Skip to content

feat(cli): reflex syncs to relayhistory-cloud in-process via ai-hist SDK - #1233

Merged
khaliqgant merged 12 commits into
mainfrom
feat/reflex-cloud-autosync
Jul 8, 2026
Merged

feat(cli): reflex syncs to relayhistory-cloud in-process via ai-hist SDK#1233
khaliqgant merged 12 commits into
mainfrom
feat/reflex-cloud-autosync

Conversation

@khaliqgant

@khaliqgant khaliqgant commented Jul 6, 2026

Copy link
Copy Markdown
Member

Why

agent-relay reflex on wrote a flag and did a one-time login but nothing ever pushed history — the flag was read by nothing at runtime.

What

The long-running agent-relay up host now periodically pushes new local history to relayhistory-cloud in-process, gated on the reflex flag. It does this via the ai-hist SDK's pushToCloud, which drives the real ai-hist push Rust binary (relayhistory#38) — no CLI shell-out to schedule launchd/cron, no TypeScript re-port of the push logic.

  • @agent-relay/configreflex-config.ts: single source of truth for ~/.agentworkforce/reflex.json (isReflexEnabled, read/write). reflex.ts uses it.
  • reflex on persists the rth_at_ session where the binary reads it — $RELAYHISTORY_HOME/auth.json (default ~/.agentworkforce/relayhistory/auth.json), Rust snake_case shape — so the spawned ai-hist push authenticates. Only prints "History syncs automatically…" when cloud login actually succeeded.
  • reflex-capture.ts: unref'd periodic loop (mirrors the telemetry client) started after the fleet sidecar and stopped (final flush) in shutdownOnce. Re-checks isEnabled() each tick so reflex off/on take effect immediately; interval starts only after the initial delay. Loads ai-hist/cloud via a lazy dynamic import → silent no-op if the SDK/binary is unavailable or unauthed.

Dependency / ordering

ai-hist is intentionally not a hard dependency yet: ai-hist@0.4.0 (relayhistory#38, which ships pushToCloud) isn't published, and pinning an unpublished version broke npm ci (ETARGET) across all CI. The capture loop reaches it via the lazy dynamic import and no-ops when absent. Once 0.4.0 is published, a follow-up adds ai-hist@^0.4.0 as a dependency so real installs pick it up (needs the ai-hist binary present too, via install.sh / PATH).

Testing

  • vitest reflex + reflex-capture ✅ 14 passed
  • tsc (cli + config) ✅, eslint ✅, prettier --check

🤖 Generated with Claude Code

`agent-relay reflex on` previously only flipped a flag and did a one-time
cloud login — nothing ever pushed history to relayhistory-cloud, so "Reflex is
on" never actually synced anything.

Now `reflex on` also schedules the ai-hist background services (local `sync`
+ cloud `push`, via `ai-hist <cmd> --install-service`), `reflex off` removes
the push service (leaving local capture in place), and `reflex status` reports
whether cloud push is scheduled. Scheduling is best-effort: if ai-hist isn't
on PATH the flag still flips and a clear hint is printed, matching the existing
cloud-login resilience.

Wiring goes through injectable ReflexDependencies (installCloudSync /
uninstallCloudSync / cloudSyncInstalled) so it stays unit-testable without
shelling out. Requires ai-hist with `push --install-service`
(AgentWorkforce/relayhistory#38).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@khaliqgant
khaliqgant requested a review from willwashburn as a code owner July 6, 2026 05:59
@cursor

cursor Bot commented Jul 6, 2026

Copy link
Copy Markdown

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

@coderabbitai

coderabbitai Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds shared Reflex state helpers, an ai-hist binary resolver, an in-process Reflex capture loop, and CLI/broker wiring so Reflex state is persisted and capture starts and stops with agent-relay up.

Changes

Reflex capture lifecycle

Layer / File(s) Summary
Shared Reflex state storage
packages/config/src/reflex-config.ts, packages/config/src/index.ts
Adds ReflexState and helpers to resolve, read, write, and check the persisted Reflex toggle file, then re-exports the module from the config package entrypoint.
ai-hist binary resolution
packages/cli/src/cli/lib/ai-hist-path.ts, packages/cli/src/cli/lib/ai-hist-path.test.ts
Adds optional-dependency name resolution and runtime binary lookup with env override, bundled, local-install, and fallback paths, plus tests for those lookup cases.
Reflex capture loop
packages/cli/src/cli/lib/reflex-capture.ts, packages/cli/src/cli/lib/reflex-capture.test.ts
Adds startReflexCapture, reflexSyncAndPush, periodic scheduling with enablement checks and de-duplication, stop/flush handling, and tests for enabled, disabled, failure, and idle-flush behavior.
Reflex command state updates
packages/cli/src/cli/commands/reflex.ts, packages/cli/src/cli/commands/reflex.test.ts, packages/cli/src/cli/lib/broker-lifecycle.ts
reflex on/off/status now use the shared config helpers, cloud-login persistence comments and paths are updated, tests cover the new automatic-sync log conditions, and agent-relay up starts and stops Reflex capture during broker lifecycle.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related PRs

  • AgentWorkforce/relay#1207: Also changes the Reflex CLI command flow and state persistence in packages/cli/src/cli/commands/reflex.ts.
  • AgentWorkforce/relay#1218: Also touches packages/cli/src/cli/lib/broker-lifecycle.ts and the agent-relay up startup/shutdown path.

Suggested reviewers: willwashburn

Poem

A rabbit hops through code all day,
With Reflex state tucked snug away.
The history purrs, the timers chime,
And syncs keep pace in perfect time.
🐇✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: in-process reflex syncing to relayhistory-cloud via ai-hist.
Description check ✅ Passed The description is substantive and covers summary, rationale, implementation, dependency notes, and testing, though it doesn’t follow the template headings exactly.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/reflex-cloud-autosync

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request integrates background services for ai-hist (automatic local history sync and cloud push) into the reflex command, allowing scheduling on reflex on, removal on reflex off, and status reporting on reflex status. The review feedback recommends improving platform compatibility on non-Darwin systems by allowing the status check to return 'unknown' instead of a false negative, updating the status output accordingly, and gracefully handling missing ai-hist binaries during uninstallation.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread packages/cli/src/cli/commands/reflex.ts Outdated
Comment on lines 306 to 310
? 'Cloud push service: scheduled.'
: 'Cloud push service: not scheduled — run `agent-relay reflex on`.'
);
return;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Handle the 'unknown' status gracefully on non-Darwin platforms. This avoids showing a misleading message to Linux users telling them to run agent-relay reflex on when the service might already be scheduled.

Suggested change
? 'Cloud push service: scheduled.'
: 'Cloud push service: not scheduled — run `agent-relay reflex on`.'
);
return;
}
const syncInstalled = deps.cloudSyncInstalled();
if (syncInstalled === 'unknown') {
deps.log('Cloud push service: scheduled (status check not supported on this platform).');
} else {
deps.log(
syncInstalled
? 'Cloud push service: scheduled.'
: 'Cloud push service: not scheduled — run \'agent-relay reflex on\'.'
);
}

Comment thread packages/cli/src/cli/commands/reflex.ts Outdated
Comment on lines +30 to +31
/** Whether the automatic cloud push service is currently scheduled. */
cloudSyncInstalled: () => boolean;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

To prevent false negatives on non-Darwin platforms (like Linux) where we cannot easily check the status of the cron job synchronously, let's allow cloudSyncInstalled to return 'unknown' instead of forcing a boolean false.

Suggested change
/** Whether the automatic cloud push service is currently scheduled. */
cloudSyncInstalled: () => boolean;
/** Whether the automatic cloud push service is currently scheduled. */
cloudSyncInstalled: () => boolean | 'unknown';

Comment thread packages/cli/src/cli/commands/reflex.ts Outdated
Comment on lines +167 to +176
async function defaultUninstallCloudSync(): Promise<ServiceResult> {
// Only remove cloud upload; leave local `sync` capture in place.
const args = ['push', '--uninstall-service'];
try {
await execFileAsync('ai-hist', args);
return { ok: true };
} catch (err) {
return { ok: false, error: aiHistFailure(err, args) };
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

If ai-hist is not found on the PATH (i.e., ENOENT), it means ai-hist is not installed, so the background service cannot be installed or running anyway. Treating this as a success avoids printing a confusing error message to the user when they run agent-relay reflex off.

async function defaultUninstallCloudSync(): Promise<ServiceResult> {
  // Only remove cloud upload; leave local 'sync' capture in place.
  const args = ['push', '--uninstall-service'];
  try {
    await execFileAsync('ai-hist', args);
    return { ok: true };
  } catch (err) {
    if (err && typeof err === 'object' && (err as NodeJS.ErrnoException).code === 'ENOENT') {
      return { ok: true };
    }
    return { ok: false, error: aiHistFailure(err, args) };
  }
}

Comment thread packages/cli/src/cli/commands/reflex.ts Outdated
Comment on lines 178 to 187
function defaultCloudSyncInstalled(fsImpl: typeof fs, homedir: () => string): boolean {
if (process.platform !== 'darwin') {
// On Linux the push job lives in crontab; we don't shell out just to report
// status, so report unknown (false) rather than guess.
return false;
}
return fsImpl.existsSync(path.join(homedir(), 'Library', 'LaunchAgents', 'com.ai-hist.push.plist'));
}

function withDefaults(overrides: Partial<ReflexDependencies> = {}): ReflexDependencies {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Update defaultCloudSyncInstalled to return 'unknown' on non-Darwin platforms instead of false. This allows the CLI to distinguish between a service that is definitely not installed and a service whose status cannot be verified on the current platform.

Suggested change
function defaultCloudSyncInstalled(fsImpl: typeof fs, homedir: () => string): boolean {
if (process.platform !== 'darwin') {
// On Linux the push job lives in crontab; we don't shell out just to report
// status, so report unknown (false) rather than guess.
return false;
}
return fsImpl.existsSync(path.join(homedir(), 'Library', 'LaunchAgents', 'com.ai-hist.push.plist'));
}
function withDefaults(overrides: Partial<ReflexDependencies> = {}): ReflexDependencies {
function defaultCloudSyncInstalled(fsImpl: typeof fs, homedir: () => string): boolean | 'unknown' {
if (process.platform !== 'darwin') {
// On Linux the push job lives in crontab; we don't shell out just to report
// status, so report unknown rather than guess.
return 'unknown';
}
return fsImpl.existsSync(
path.join(homedir(), 'Library', 'LaunchAgents', 'com.ai-hist.push.plist')
);
}

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 3 potential issues.

Open in Devin Review

Comment thread packages/cli/src/cli/commands/reflex.ts Outdated
Comment on lines 268 to 273
deps.log('Scheduled automatic history sync + cloud push.');
} else {
deps.log(`Reflex is enabled, but automatic sync could not be scheduled: ${cloudSync.error}`);
}

deps.log('Reflex is on.');

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changelog not updated with the new cloud sync scheduling feature

The changelog is not updated to reflect the new cloud sync scheduling behavior added by this PR (CHANGELOG.md:57), so the release narrative omits that reflex on now schedules background sync services, reflex off removes them, and reflex status reports their state.

Impact: The release notes will not document the new cloud sync scheduling capability for users.

AGENTS.md rule and missing changelog detail

AGENTS.md states: "Curate [Unreleased] in CHANGELOG.md as you land PRs. The root changelog is the cross-package, user-facing release narrative for Relay."

The existing [Unreleased] entry at CHANGELOG.md:57 reads:
- \agent-relay reflex on|off|status` manages Reflex history sync with a consent prompt and persisted `~/.agentworkforce/reflex.json` state.`

This PR adds three significant user-visible behaviors:

  1. reflex on now schedules ai-hist sync and ai-hist push background services (packages/cli/src/cli/commands/reflex.ts:268-273)
  2. reflex off now uninstalls the cloud push service (packages/cli/src/cli/commands/reflex.ts:284-287)
  3. reflex status now reports whether the cloud push service is scheduled (packages/cli/src/cli/commands/reflex.ts:306-310)

None of these are reflected in the changelog entry.

Prompt for agents
AGENTS.md requires updating the [Unreleased] section of CHANGELOG.md when landing PRs. The existing entry at CHANGELOG.md:57 for reflex on/off/status should be updated (or a new bullet added) to mention that reflex on now schedules ai-hist background sync and push services (launchd on macOS, cron on Linux), reflex off removes the cloud push service, and reflex status reports whether the cloud push service is currently scheduled. This is a user-visible behavior change that should be documented in the Added section of the changelog.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment thread packages/cli/src/cli/commands/reflex.ts Outdated
Comment on lines 179 to 187
if (process.platform !== 'darwin') {
// On Linux the push job lives in crontab; we don't shell out just to report
// status, so report unknown (false) rather than guess.
return false;
}
return fsImpl.existsSync(path.join(homedir(), 'Library', 'LaunchAgents', 'com.ai-hist.push.plist'));
}

function withDefaults(overrides: Partial<ReflexDependencies> = {}): ReflexDependencies {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔍 Cloud push status always reports 'not scheduled' on Linux

The defaultCloudSyncInstalled function at packages/cli/src/cli/commands/reflex.ts:179-187 returns false on all non-macOS platforms because it only knows how to check for the launchd plist. On Linux, even after a successful reflex on that installs the cron-based push service, reflex status will always show 'Cloud push service: not scheduled — run agent-relay reflex on.' This is documented in the comment but creates a confusing UX for Linux users who just successfully ran reflex on and then immediately check reflex status.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment thread packages/cli/src/cli/commands/reflex.ts Outdated
Comment on lines +167 to +176
async function defaultUninstallCloudSync(): Promise<ServiceResult> {
// Only remove cloud upload; leave local `sync` capture in place.
const args = ['push', '--uninstall-service'];
try {
await execFileAsync('ai-hist', args);
return { ok: true };
} catch (err) {
return { ok: false, error: aiHistFailure(err, args) };
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔍 Install schedules both sync and push, but uninstall only removes push

The defaultInstallCloudSync at packages/cli/src/cli/commands/reflex.ts:156-165 installs both sync --install-service and push --install-service, but defaultUninstallCloudSync at packages/cli/src/cli/commands/reflex.ts:167-176 only removes push. The comment says this is intentional ('leave local sync capture in place'), but it means reflex offreflex onreflex off will accumulate a sync service that is never cleaned up. If the intent is that local capture persists even when Reflex is off, this is fine, but it's worth confirming that's the desired behavior since users might expect reflex off to fully undo reflex on.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2cd9390e54

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/cli/src/cli/commands/reflex.ts Outdated
// ai-hist background services (launchd on macOS, cron on Linux).
const CLOUD_SYNC_STAGES: readonly string[][] = [
['sync', '--install-service'],
['push', '--install-service'],

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Gate cloud scheduling on supported ai-hist flags

This new stage assumes ai-hist push accepts --install-service, but the current public relayhistory CLI only defines service flags on sync; push is declared with --limit, --incognito, and --json (checked upstream crates/ai-hist/src/main.rs). With current installs, reflex on installs local sync and then push --install-service fails as an unexpected argument, so cloud push is never scheduled while Reflex still writes enabled state. Gate this on a supported ai-hist version or land/update the dependency before enabling it here.

Useful? React with 👍 / 👎.

Comment thread packages/cli/src/cli/commands/reflex.ts Outdated
Comment on lines +179 to +182
if (process.platform !== 'darwin') {
// On Linux the push job lives in crontab; we don't shell out just to report
// status, so report unknown (false) rather than guess.
return false;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Report Linux cron status instead of always unscheduled

On Linux, reflex on schedules the push job through cron, but this status probe short-circuits every non-macOS platform to false. After a successful enable on Linux, agent-relay reflex status will still print Cloud push service: not scheduled — run agent-relay reflex on, causing users to rerun setup even though the cron job may already exist. Check the managed crontab entry or report an explicit unknown state instead of treating Linux as unscheduled.

Useful? React with 👍 / 👎.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

4 issues found and verified against the latest diff

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="packages/cli/src/cli/commands/reflex.test.ts">

<violation number="1" location="packages/cli/src/cli/commands/reflex.test.ts:111">
P3: The `reflex off` test only covers the happy path where `uninstallCloudSync` succeeds. The implementation also handles failure (`!removal.ok`) by logging a distinct warning message, but that branch is untested. Adding a test with a failing mock (e.g., `{ ok: false, error: 'ai-hist was not found on your PATH.' }`) would close the gap and mirror the coverage pattern already used for `installCloudSync` in the "reflex on still enables when scheduling the sync service fails" test.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread packages/cli/src/cli/commands/reflex.ts Outdated
Comment thread packages/cli/src/cli/commands/reflex.ts Outdated
expect(outputLines(deps)).toContain('Reflex is on.');
});

it('reflex off writes disabled state, removes the push service, and prints confirmation', async () => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: The reflex off test only covers the happy path where uninstallCloudSync succeeds. The implementation also handles failure (!removal.ok) by logging a distinct warning message, but that branch is untested. Adding a test with a failing mock (e.g., { ok: false, error: 'ai-hist was not found on your PATH.' }) would close the gap and mirror the coverage pattern already used for installCloudSync in the "reflex on still enables when scheduling the sync service fails" test.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/cli/src/cli/commands/reflex.test.ts, line 111:

<comment>The `reflex off` test only covers the happy path where `uninstallCloudSync` succeeds. The implementation also handles failure (`!removal.ok`) by logging a distinct warning message, but that branch is untested. Adding a test with a failing mock (e.g., `{ ok: false, error: 'ai-hist was not found on your PATH.' }`) would close the gap and mirror the coverage pattern already used for `installCloudSync` in the "reflex on still enables when scheduling the sync service fails" test.</comment>

<file context>
@@ -78,21 +81,40 @@ describe('registerReflexCommands', () => {
+    expect(outputLines(deps)).toContain('Reflex is on.');
+  });
+
+  it('reflex off writes disabled state, removes the push service, and prints confirmation', async () => {
     const { program, deps } = createHarness();
 
</file context>

Comment thread packages/cli/src/cli/commands/reflex.ts Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
packages/cli/src/cli/commands/reflex.ts (2)

138-165: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Consider adding a timeout to ai-hist subprocess calls.

execFileAsync('ai-hist', args) has no timeout, so if the external ai-hist binary hangs, reflex on/reflex off will block indefinitely with no feedback to the user.

♻️ Proposed fix
-      await execFileAsync('ai-hist', args);
+      await execFileAsync('ai-hist', args, { timeout: 30_000 });

Also applies to: 167-176

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/cli/src/cli/commands/reflex.ts` around lines 138 - 165, The
`defaultInstallCloudSync` flow currently calls `execFileAsync('ai-hist', args)`
without any timeout, so a hung `ai-hist` process can block `reflex on`/`reflex
off` indefinitely. Update the `execFileAsync` invocation (and any other
`ai-hist` subprocess calls in this area) to enforce a reasonable timeout and
surface a clear failure through `aiHistFailure`, so the user gets a
deterministic error instead of a stuck command.

187-204: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Redundant placeholder default for cloudSyncInstalled.

The inline cloudSyncInstalled: () => false is always overwritten — either by overrides.cloudSyncInstalled via the spread, or by the subsequent if (!overrides.cloudSyncInstalled) block. It never survives as the final value, so it's dead code that adds a bit of reading friction.

♻️ Optional simplification
   const deps: ReflexDependencies = {
     fs,
     homedir: os.homedir,
     readRelayAuth: defaultReadRelayAuth,
     loginToCloud: defaultLoginToCloud,
     prompt: promptYesNo,
     log: (...args: unknown[]) => console.log(...args),
     installCloudSync: defaultInstallCloudSync,
     uninstallCloudSync: defaultUninstallCloudSync,
-    cloudSyncInstalled: () => false,
+    cloudSyncInstalled: overrides.cloudSyncInstalled ?? (() => false),
     ...overrides,
   };
   if (!overrides.cloudSyncInstalled) {
     // Probe the launchd plist using the same homedir the rest of the deps use.
     deps.cloudSyncInstalled = () => defaultCloudSyncInstalled(deps.fs, deps.homedir);
   }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/cli/src/cli/commands/reflex.ts` around lines 187 - 204, The
withDefaults helper in reflex.ts has a redundant placeholder for
cloudSyncInstalled that is always replaced and should be removed. Update the
ReflexDependencies construction in withDefaults so it no longer sets the inline
false stub, and keep the existing override spread plus the fallback assignment
that uses defaultCloudSyncInstalled when overrides.cloudSyncInstalled is
missing.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/cli/src/cli/commands/reflex.ts`:
- Around line 178-185: `defaultCloudSyncInstalled()` currently hardcodes `false`
for every non-darwin platform, which makes `reflex status` report the cloud push
service as unscheduled on Linux even when the cron job exists. Update the
platform check in `reflex.ts` so Linux also performs a real installation probe,
using the existing cron-based setup or an `ai-hist` status command rather than
returning false unconditionally. Keep the macOS LaunchAgents check as-is, but
branch Linux separately and make the result reflect the actual cloud sync state.

---

Nitpick comments:
In `@packages/cli/src/cli/commands/reflex.ts`:
- Around line 138-165: The `defaultInstallCloudSync` flow currently calls
`execFileAsync('ai-hist', args)` without any timeout, so a hung `ai-hist`
process can block `reflex on`/`reflex off` indefinitely. Update the
`execFileAsync` invocation (and any other `ai-hist` subprocess calls in this
area) to enforce a reasonable timeout and surface a clear failure through
`aiHistFailure`, so the user gets a deterministic error instead of a stuck
command.
- Around line 187-204: The withDefaults helper in reflex.ts has a redundant
placeholder for cloudSyncInstalled that is always replaced and should be
removed. Update the ReflexDependencies construction in withDefaults so it no
longer sets the inline false stub, and keep the existing override spread plus
the fallback assignment that uses defaultCloudSyncInstalled when
overrides.cloudSyncInstalled is missing.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 265733fa-47ad-4f52-a839-2a5f09615406

📥 Commits

Reviewing files that changed from the base of the PR and between 12f4eec and 8c3586a.

📒 Files selected for processing (2)
  • packages/cli/src/cli/commands/reflex.test.ts
  • packages/cli/src/cli/commands/reflex.ts

Comment thread packages/cli/src/cli/commands/reflex.ts Outdated
Comment on lines +178 to +185
function defaultCloudSyncInstalled(fsImpl: typeof fs, homedir: () => string): boolean {
if (process.platform !== 'darwin') {
// On Linux the push job lives in crontab; we don't shell out just to report
// status, so report unknown (false) rather than guess.
return false;
}
return fsImpl.existsSync(path.join(homedir(), 'Library', 'LaunchAgents', 'com.ai-hist.push.plist'));
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check whether ai-hist exposes a status/introspection command usable for Linux detection.
rg -n "ai-hist" --type=ts -C3
rg -n "crontab|cron" --type=ts -C2

Repository: AgentWorkforce/relay

Length of output: 158


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Map the relevant file and inspect the surrounding implementation.
ast-grep outline packages/cli/src/cli/commands/reflex.ts --view expanded || true
sed -n '1,260p' packages/cli/src/cli/commands/reflex.ts | cat -n

Repository: AgentWorkforce/relay

Length of output: 13122


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the status command and related control flow.
sed -n '260,420p' packages/cli/src/cli/commands/reflex.ts | cat -n

Repository: AgentWorkforce/relay

Length of output: 2283


cloudSyncInstalled() needs a Linux probe

packages/cli/src/cli/commands/reflex.ts:178-185 returns false on every non-macOS platform, so reflex status will always report “Cloud push service: not scheduled” on Linux even after reflex on installs the cron job. Check the cron entry or an ai-hist status command instead of hardcoding false.

🧰 Tools
🪛 ast-grep (0.44.1)

[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { execFile } from 'node:child_process';
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/cli/src/cli/commands/reflex.ts` around lines 178 - 185,
`defaultCloudSyncInstalled()` currently hardcodes `false` for every non-darwin
platform, which makes `reflex status` report the cloud push service as
unscheduled on Linux even when the cron job exists. Update the platform check in
`reflex.ts` so Linux also performs a real installation probe, using the existing
cron-based setup or an `ai-hist` status command rather than returning false
unconditionally. Keep the macOS LaunchAgents check as-is, but branch Linux
separately and make the result reflect the actual cloud sync state.

@agent-relay-code

Copy link
Copy Markdown
Contributor

pr-reviewer applied fixes — committed and pushed 8c3586a to this PR. The notes below describe what changed.

Both changed files are formatting-only edits. Let me review the overall logic quality to leave any advisory notes, then write the review.

Review of PR #1233: feat(cli): reflex on/off schedules automatic cloud sync

Summary of changes

The PR extends the reflex CLI command so that reflex on schedules ai-hist sync and ai-hist push background services, reflex off removes the push service, and reflex status reports whether the push service is scheduled. New injectable deps (installCloudSync, uninstallCloudSync, cloudSyncInstalled) keep the code testable, with real defaults that shell out via execFile (no shell, fixed args — no injection risk) and probe the launchd plist on macOS.

Verification (run against the current checkout, the way CI runs)

  • npm ci — installed cleanly.
  • vitest run reflex.test.ts — 10/10 pass.
  • CLI typecheck (tsc --noEmit) — passes.
  • eslint on both files — clean.
  • prettier --check — FAILED on both files (CI's format:check step would go red).

Fix applied (mechanical only)

  • Ran prettier --write on reflex.ts and reflex.test.ts. The only changes were collapsing three multi-line calls that fit within the configured print width — purely non-semantic formatting. Re-verified afterward: tests still 10/10, typecheck clean, prettier --check now passes.
    • packages/cli/src/cli/commands/reflex.ts:184 (existsSync call collapsed to one line)
    • packages/cli/src/cli/commands/reflex.test.ts:150 (writeFileSync call collapsed to one line)

Correctness notes (verified, no action needed)

  • withDefaults correctly preserves a caller-supplied cloudSyncInstalled (via spread) and only substitutes the real launchd probe when none was provided. The initial () => false placeholder is harmless.
  • registerReflexCommands is called with no overrides in bootstrap.ts:302; all new deps have defaults, so no caller breakage.
  • Failure of installCloudSync/uninstallCloudSync is non-fatal and does not block enabling/disabling Reflex — reasonable fail-soft behavior for an auxiliary scheduling step, and covered by tests.

Addressed comments

  • No bot or human reviewer comments were present in .workforce/context.json or the workforce inputs, so there were none to address.

Advisory Notes

  • defaultCloudSyncInstalled returns false on Linux (where the push job lives in crontab) so reflex status will report "not scheduled" even when it is. This is documented in the code comment and is a deliberate trade-off, but the status line "not scheduled — run agent-relay reflex on" could mislead Linux users into re-running unnecessarily. Consider a "unknown on this platform" wording. Non-blocking; out of scope to change without human judgment on the desired UX.
  • reflex off uninstalls only the push service and intentionally leaves the local sync service running (per the code comment). Worth confirming this asymmetry is the intended product behavior, since a user disabling Reflex may expect local capture to stop too. Advisory only — a behavior decision for a human.

The only file changes I made are mechanical prettier formatting required for CI's format:check to pass. All build/test/typecheck/lint/format steps that CI runs now pass locally. The two advisory items are behavior/UX questions that need human judgment; I left that code unchanged. Since those are non-blocking advisories rather than pending work, and CI status/mergeability are post-harness facts I can't confirm from here, I'm not declaring the PR fully ready.

…hell-out)

Replaces the earlier shell-out approach. `agent-relay reflex on` no longer
spawns `ai-hist ... --install-service`; instead the long-running `agent-relay
up` host pushes new local session history to relayhistory-cloud in-process,
gated on the reflex flag.

- `@agent-relay/config`: `reflex-config.ts` is the single source of truth for
  the `~/.agentworkforce/reflex.json` shape/location — `isReflexEnabled()`,
  `readReflexState()`, `writeReflexState()`. `reflex.ts` now uses it and drops
  its private copy + the CLI shell-out deps; `reflex on` = flip flag + cloud
  login only.
- `reflex-capture.ts`: an unref'd periodic push loop (mirrors the telemetry
  client) started after the fleet sidecar in `broker-lifecycle.ts` and stopped
  (with a final flush) in `shutdownOnce`. It calls `ai-hist/cloud`'s
  `pushToCloud` via a lazy, non-analyzable dynamic import so the CLI does not
  statically depend on it — a silent no-op if ai-hist is unavailable or the
  user isn't authed.
- Adds `ai-hist@^0.4.0` (the SDK gains `pushToCloud` in that release) and marks
  `ai-hist`/`sql.js` external in the esbuild library bundle.

Auth is self-consistent: reflex login and the SDK push both use
~/.config/ai-hist/auth.json.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@cursor

cursor Bot commented Jul 6, 2026

Copy link
Copy Markdown

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

@khaliqgant khaliqgant changed the title feat(cli): reflex on/off schedules automatic cloud sync feat(cli): reflex syncs to relayhistory-cloud in-process via ai-hist SDK Jul 6, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/cli/src/cli/commands/reflex.ts (1)

159-174: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Cloud-sync message is unconditional even when login is skipped/failed.

Lines 171-173 always claim history "syncs to relayhistory-cloud automatically," even in the branches above where the user isn't logged in or cloud login failed. This contradicts the warnings just printed and misleads users about whether their data is actually being synced.

🐛 Proposed fix
       const relayAuth = await deps.readRelayAuth();
+      let cloudSyncActive = false;
       if (!relayAuth) {
         deps.log(
           'Not logged in to Agent Relay. Run `agent-relay login` first to sync Reflex history to the cloud.'
         );
       } else {
         const result = await deps.loginToCloud(relayAuth.accessToken);
         if (!result.ok) {
           deps.log(`Reflex is enabled locally, but cloud login did not complete: ${result.error}`);
+        } else {
+          cloudSyncActive = true;
         }
       }

       deps.log('Reflex is on.');
-      deps.log('History syncs to relayhistory-cloud automatically while `agent-relay up` is running.');
+      if (cloudSyncActive) {
+        deps.log('History syncs to relayhistory-cloud automatically while `agent-relay up` is running.');
+      }
       deps.log('State file: ~/.agentworkforce/reflex.json');
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/cli/src/cli/commands/reflex.ts` around lines 159 - 174, The sync
status message in the Reflex command is unconditional and can contradict the
login warnings. Update the logic in the `reflex` command so the history-sync
message is only shown when `deps.readRelayAuth()` succeeds and
`deps.loginToCloud()` returns ok, and use a separate fallback message when cloud
login is skipped or fails. Keep the main flow around `readRelayAuth`,
`loginToCloud`, and the final `deps.log` calls consistent with the user’s actual
sync state.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/cli/package.json`:
- Line 57: The dependency declaration for ai-hist in the CLI package is pinned
to a version that is not available on the default npm registry. Update the
ai-hist entry in package.json to a published version that exists on npm, or
otherwise ensure the referenced 0.4.0 release is published and resolvable before
merging; use the ai-hist dependency key in the CLI package manifest to locate
and adjust it.

In `@packages/cli/src/cli/lib/reflex-capture.ts`:
- Around line 75-108: The capture loop in startReflexCapture only checks
deps.isEnabled() once, so “reflex off” does not stop an already-running
interval. Update tick() to re-check deps.isEnabled() before calling deps.push(),
and skip/log nothing when disabled mid-run; keep the existing stop handling and
inFlight dedup logic intact. Use the startReflexCapture and tick symbols to
locate the change, and add a reflex-capture.test.ts case for disabling after
startup to verify no further pushes occur.

---

Outside diff comments:
In `@packages/cli/src/cli/commands/reflex.ts`:
- Around line 159-174: The sync status message in the Reflex command is
unconditional and can contradict the login warnings. Update the logic in the
`reflex` command so the history-sync message is only shown when
`deps.readRelayAuth()` succeeds and `deps.loginToCloud()` returns ok, and use a
separate fallback message when cloud login is skipped or fails. Keep the main
flow around `readRelayAuth`, `loginToCloud`, and the final `deps.log` calls
consistent with the user’s actual sync state.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: c676e5fc-cfbd-4308-b2b8-81603ea7c15f

📥 Commits

Reviewing files that changed from the base of the PR and between 8c3586a and e5db418.

📒 Files selected for processing (9)
  • packages/cli/package.json
  • packages/cli/scripts/build-cjs.mjs
  • packages/cli/src/cli/commands/reflex.test.ts
  • packages/cli/src/cli/commands/reflex.ts
  • packages/cli/src/cli/lib/broker-lifecycle.ts
  • packages/cli/src/cli/lib/reflex-capture.test.ts
  • packages/cli/src/cli/lib/reflex-capture.ts
  • packages/config/src/index.ts
  • packages/config/src/reflex-config.ts

Comment thread packages/cli/package.json Outdated
Comment thread packages/cli/src/cli/lib/reflex-capture.ts Outdated

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

3 issues found across 9 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="packages/cli/src/cli/commands/reflex.test.ts">

<violation number="1" location="packages/cli/src/cli/commands/reflex.test.ts:111">
P3: The `reflex off` test only covers the happy path where `uninstallCloudSync` succeeds. The implementation also handles failure (`!removal.ok`) by logging a distinct warning message, but that branch is untested. Adding a test with a failing mock (e.g., `{ ok: false, error: 'ai-hist was not found on your PATH.' }`) would close the gap and mirror the coverage pattern already used for `installCloudSync` in the "reflex on still enables when scheduling the sync service fails" test.</violation>
</file>

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

Comment thread packages/cli/src/cli/lib/reflex-capture.ts
Comment thread packages/cli/src/cli/commands/reflex.ts Outdated
Comment thread packages/cli/src/cli/lib/reflex-capture.ts Outdated
- Drop the `ai-hist@^0.4.0` dependency: that version isn't published yet
  (it ships in relayhistory#38), so `npm ci` failed with ETARGET across every
  CI job. The capture loop already loads `ai-hist/cloud` via a lazy dynamic
  import and no-ops gracefully when absent, so it's a runtime-optional peer;
  declare it as a real dependency once 0.4.0 is published. Reverts the now-moot
  esbuild external entry too.
- reflex-capture: re-check `isEnabled()` on every tick so `reflex off` (or `on`)
  takes effect immediately in a running `agent-relay up`, and start the interval
  only after the initial delay so the first push can't fire before
  initialDelayMs when intervalMs is smaller. Adds tests for both.
- reflex on: only print "History syncs automatically…" when cloud login
  actually succeeded, so it no longer contradicts the not-logged-in / login-
  failed warnings. Adds negative assertions.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@cursor

cursor Bot commented Jul 6, 2026

Copy link
Copy Markdown

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

@khaliqgant

Copy link
Copy Markdown
Member Author

Thanks all — addressed the feedback in 1e0… (latest push).

CI root cause (all install/test jobs): npm ci failed with ETARGET — no matching version for ai-hist@^0.4.0. That version isn't published yet (it ships in AgentWorkforce/relayhistory#38). Since the capture loop already loads ai-hist/cloud via a lazy dynamic import and no-ops gracefully when it's absent, I removed the hard dependency — it's a runtime-optional peer for now, to be declared as a real dependency once ai-hist@0.4.0 is published. This realigns package.json with the lockfile and unblocks every job. (Also reverted the now-moot esbuild external entry.)

Addressed on the current (in-process SDK) code:

  • reflex-capture: re-check isEnabled() every tick so reflex off/on take effect immediately in a running agent-relay up (cubic P1 / CodeRabbit) — + test.
  • reflex-capture: start the interval only after the initial delay so the first push can't precede initialDelayMs when intervalMs is smaller (cubic P2) — + test.
  • reflex on: only print "History syncs automatically…" when cloud login actually succeeded (CodeRabbit major / cubic P2) — + negative assertions.

Not applicable: several comments (execFileAsync timeout, cloudSyncInstalled placeholder, Linux cron probe, uninstallCloudSync failure-branch test) target the earlier shell-out revision, which was fully replaced by the in-process SDK approach — that code no longer exists.

@agent-relay-code

Copy link
Copy Markdown
Contributor

I made no code edits — the PR is clean, well-tested, and all mechanical checks pass. Here is my review.


Review: PR #1233feat/reflex-cloud-autosync

Summary

This PR moves Reflex state management (~/.agentworkforce/reflex.json) out of the reflex command into a shared @agent-relay/config module (reflex-config.ts), and adds an in-process cloud sync loop (reflex-capture.ts) that agent-relay up starts/stops. The reflex on command now only claims automatic sync happens when cloud auth actually succeeds.

Traced the full impact of the diff: the removed ReflexDependencies.fs field and the deleted local read/writeReflexState/getReflexDir helpers have no remaining consumers outside reflex.ts/reflex.test.ts (both updated in the diff). startReflexCapture/RunningReflexCapture are consumed only by broker-lifecycle.ts (integrated correctly) and its test. The new @agent-relay/config barrel export (reflex-config.js) introduces no name collisions with other config modules.

Verification (CI-equivalent, run end-to-end with the checkout as-is)

  • npm run build:config — passes (config barrel + new module compile).
  • npm run typecheck — passes (builds all core packages, then tsc --noEmit on cli; catches the config→cli import surface and the broker-lifecycle integration).
  • npx vitest run on both changed test files — 14/14 pass.
  • eslint on the three changed cli sources — clean.
  • prettier --check on all 7 changed files — clean.

No code changes were needed; I did not edit any files.

Observations (no action required)

  • fail-open/fail-closed preserved: The reflex on change is a strict improvement — cloudSyncActive starts false and is only set true on result.ok, so the "syncs automatically" message can no longer contradict a login-failure/not-logged-in warning. The two new negative test assertions guard exactly this. No safety default was weakened.
  • Capture loop is safe: startReflexCapture uses unref'd timers (won't hold the process open), dedups concurrent ticks via inFlight, re-checks isEnabled() every tick (so reflex off during a running up takes effect), and swallows push errors. stop() clears timers and best-effort flushes. The reflexCapture?.stop() call in shutdownOnce is optional-chained and additive — it does not alter existing broker/fleet teardown ordering or lifecycle semantics.
  • Lazy ai-hist/cloud import is a silent no-op when the SDK is absent or unauthenticated, as documented; it is not yet a declared dependency, which is intentional per the module comment.

Advisory Notes

  • Changelog: CLAUDE.md asks contributors to curate [Unreleased] in CHANGELOG.md as PRs land. This PR ships a user-visible behavior — history now syncs to relayhistory-cloud automatically in-process while agent-relay up runs (replacing any CLI shell-out/cron path) — but adds no changelog entry. Suggest adding one bullet under ### Added (or ### Changed), e.g.: "When Reflex is enabled, agent-relay up now pushes new local session history to relayhistory-cloud in-process on an interval (no CLI shell-out); reflex on only promises automatic sync once cloud login succeeds." I did not edit CHANGELOG.md because the release-narrative wording is a human/judgment call, not a mechanical fix.

Addressed comments

  • No bot or reviewer comments are present in .workforce/context.json (no comments/review payload was provided), and no review-comment file exists in .workforce/. There were therefore no existing threads to validate against the current checkout or resolve.

The PR is functionally sound and passes all mechanical/build/test/typecheck gates I can run locally. The only open item (changelog entry) is advisory and requires human wording, and I cannot confirm the state of remote CI checks or GitHub mergeability from this sandbox, so I am not declaring it ready to merge.

Follows the SDK pivot to a binary-backed push (relayhistory sdk-ts). Since the
in-process push now spawns `ai-hist push`, reflex login must persist the
rth_at_ session where that binary reads it: $RELAYHISTORY_HOME/auth.json
(default ~/.agentworkforce/relayhistory/auth.json), in the Rust snake_case
shape (base_url/access_token/refresh_token) — not the old camelCase
~/.config/ai-hist path.

The capture loop no longer pre-checks auth via the TS SDK; it just calls
pushToCloud, which drives the binary and returns null when the binary is
missing or the user isn't logged in.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@cursor

cursor Bot commented Jul 6, 2026

Copy link
Copy Markdown

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

Toward `agent-relay reflex on` "just working" with no extra user commands:

- Add `ai-hist-path.ts`: resolves the ai-hist binary like the broker resolves
  its own — `$AI_HIST_RUST_BIN` -> the per-platform optional-dep package
  (`ai-hist-bin-<platform>-<arch>`) -> the install.sh location -> `ai-hist` on
  PATH. Once the binary ships as an optional dependency, a plain agent-relay
  install has it with zero setup.
- Rework the capture loop to drive that binary directly: it now runs
  `ai-hist sync` (populate the local DB from the user's agent history) then
  `ai-hist push --json` each tick — previously it only pushed, so a fresh
  machine had nothing to upload. Dropped the lazy `ai-hist/cloud` npm import, so
  relay has no dependency to publish/resolve; unavailable binary or missing
  auth is a silent no-op.

Tests cover the resolver (override + package-name mapping) and sync→push
(happy path, binary-unavailable skip, not-authenticated, hard failure).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@cursor

cursor Bot commented Jul 7, 2026

Copy link
Copy Markdown

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/cli/src/cli/lib/reflex-capture.ts`:
- Around line 47-119: Add a timeout to the process launched by runAiHist so hung
ai-hist sync/push invocations can’t keep the promise pending forever and block
later capture ticks or stop(). Update the spawnFn call inside runAiHist to pass
a timeout option supported by the Node 20+ runtime, and make sure the existing
error/close handling still resolves or rejects correctly for ReflexPushOptions
and reflexSyncAndPush.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: a7386155-f7f9-4cb6-ba5a-38d195a6880c

📥 Commits

Reviewing files that changed from the base of the PR and between 664c6b3 and b4b86ac.

📒 Files selected for processing (4)
  • packages/cli/src/cli/lib/ai-hist-path.test.ts
  • packages/cli/src/cli/lib/ai-hist-path.ts
  • packages/cli/src/cli/lib/reflex-capture.test.ts
  • packages/cli/src/cli/lib/reflex-capture.ts

Comment thread packages/cli/src/cli/lib/reflex-capture.ts Outdated

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 issues found across 4 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="packages/cli/src/cli/commands/reflex.test.ts">

<violation number="1" location="packages/cli/src/cli/commands/reflex.test.ts:111">
P3: The `reflex off` test only covers the happy path where `uninstallCloudSync` succeeds. The implementation also handles failure (`!removal.ok`) by logging a distinct warning message, but that branch is untested. Adding a test with a failing mock (e.g., `{ ok: false, error: 'ai-hist was not found on your PATH.' }`) would close the gap and mirror the coverage pattern already used for `installCloudSync` in the "reflex on still enables when scheduling the sync service fails" test.</violation>
</file>

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

Comment thread packages/cli/src/cli/lib/ai-hist-path.ts Outdated
Comment thread packages/cli/src/cli/lib/reflex-capture.ts Outdated
Replaces the spawn-based capture with an in-process call to the `ai-hist-native`
napi addon's `syncAndPush()` — no `ai-hist` subprocess at all, per the
requirement not to shell out to the CLI. The addon is lazy-loaded via a
non-analyzable dynamic import (so it stays out of the esbuild bundle and
resolves from its per-platform optional-dependency package), and is a silent
no-op when unavailable or the user isn't authenticated.

Removes ai-hist-path.ts (the binary resolver) and the spawn plumbing. Tests now
inject the native addon instead of a fake child process.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@cursor

cursor Bot commented Jul 7, 2026

Copy link
Copy Markdown

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 4 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="packages/cli/src/cli/commands/reflex.test.ts">

<violation number="1" location="packages/cli/src/cli/commands/reflex.test.ts:111">
P3: The `reflex off` test only covers the happy path where `uninstallCloudSync` succeeds. The implementation also handles failure (`!removal.ok`) by logging a distinct warning message, but that branch is untested. Adding a test with a failing mock (e.g., `{ ok: false, error: 'ai-hist was not found on your PATH.' }`) would close the gap and mirror the coverage pattern already used for `installCloudSync` in the "reflex on still enables when scheduling the sync service fails" test.</violation>
</file>

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

Comment thread packages/cli/src/cli/lib/reflex-capture.ts Outdated
Now that ai-hist-native@0.4.1 is published, declare it as an optional
dependency so a plain `agent-relay` install pulls the addon (and npm
auto-selects the matching per-platform binary via os/cpu). The reflex capture
loop loads it and calls syncAndPush() in-process; it stays a graceful no-op if
the addon isn't available for a platform. Verified: npm install resolves
ai-hist-native + ai-hist-native-darwin-arm64 and `require('ai-hist-native')`
exposes syncAndPush.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@cursor

cursor Bot commented Jul 7, 2026

Copy link
Copy Markdown

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

…osync

# Conflicts:
#	package-lock.json
#	packages/cli/src/cli/lib/broker-lifecycle.ts
@cursor

cursor Bot commented Jul 7, 2026

Copy link
Copy Markdown

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

…op'ing

reflex-capture's loadNative() caught every dynamic-import error and returned
null, so an installed-but-broken addon (ABI mismatch, missing system lib, init
failure) looked identical to "not installed" and the capture loop silently did
nothing. Now only ERR_MODULE_NOT_FOUND / MODULE_NOT_FOUND is treated as a
no-op; any other error is rethrown so the loop logs it
(`[reflex] cloud sync failed: …`).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@cursor

cursor Bot commented Jul 7, 2026

Copy link
Copy Markdown

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

@khaliqgant

Copy link
Copy Markdown
Member Author

Went through all the review threads. This PR evolved through several approaches (shell-out → ai-hist SDK → bundled binary spawn → in-process napi addon), so most inline comments target code from earlier commits that no longer exists.

Addressed (current code):

  • loadNative() swallowed every dynamic-import error, so an installed-but-broken addon (ABI mismatch, missing lib, init failure) looked the same as "not installed" and silently no-op'd (cubic P2 on the merge commit). Now only ERR_MODULE_NOT_FOUND/MODULE_NOT_FOUND → no-op; any other error is rethrown and logged as [reflex] cloud sync failed: ….

Already handled in the current code (no change needed):

  • Re-check isEnabled() every tick so reflex off takes effect immediately — present (reflex-capture.ts tick()).
  • First push can't fire before initialDelayMs regardless of interval — the interval starts inside the kickoff timer.

Moot — target deleted code (the shell-out / bundled-binary-spawn eras):

  • defaultCloudSyncInstalled / Linux cron status / 'unknown' handling, defaultInstallCloudSync/defaultUninstallCloudSync + ENOENT, --install-service flag gating, ai-hist-path.ts resolution + spawn timeout, the ai-hist (SDK) dependency not-published note, changelog for "scheduling services".

The final design is: reflex on flips the flag + logs in; the agent-relay up host calls ai-hist-native's syncAndPush() in-process (no subprocess), gated on the flag. Depends on ai-hist-native@^0.4.1 (published).

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 1 file (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="packages/cli/src/cli/lib/reflex-capture.ts">

<violation number="1" location="packages/cli/src/cli/lib/reflex-capture.ts:62">
P2: The broad `MODULE_NOT_FOUND` / `ERR_MODULE_NOT_FOUND` catch returns `null` for *any* module resolution failure, not just when `ai-hist-native` itself is absent. This means a broken installation (e.g., missing native binding, missing transitive dependency, corrupted `node_modules`) would silently no-op, contradicting the adjacent comment that says ABI mismatches and addon init failures should be rethrown so the caller logs them.

Node.js throws these same error codes both for top-level package absence and for nested internal requires within an already-resolved package. Consider narrowing the check so it only swallows errors that are specifically about the `ai-hist-native` specifier itself — for example, by also inspecting the error message for `ai-hist-native` or `requireStack` — so that real broken-install problems still surface.</violation>
</file>

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

// mismatch, missing system lib, addon init failure) is a real problem —
// rethrow so the caller logs it instead of silently doing nothing.
const code = (err as NodeJS.ErrnoException | undefined)?.code;
if (code === 'ERR_MODULE_NOT_FOUND' || code === 'MODULE_NOT_FOUND') {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: The broad MODULE_NOT_FOUND / ERR_MODULE_NOT_FOUND catch returns null for any module resolution failure, not just when ai-hist-native itself is absent. This means a broken installation (e.g., missing native binding, missing transitive dependency, corrupted node_modules) would silently no-op, contradicting the adjacent comment that says ABI mismatches and addon init failures should be rethrown so the caller logs them.

Node.js throws these same error codes both for top-level package absence and for nested internal requires within an already-resolved package. Consider narrowing the check so it only swallows errors that are specifically about the ai-hist-native specifier itself — for example, by also inspecting the error message for ai-hist-native or requireStack — so that real broken-install problems still surface.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/cli/src/cli/lib/reflex-capture.ts, line 62:

<comment>The broad `MODULE_NOT_FOUND` / `ERR_MODULE_NOT_FOUND` catch returns `null` for *any* module resolution failure, not just when `ai-hist-native` itself is absent. This means a broken installation (e.g., missing native binding, missing transitive dependency, corrupted `node_modules`) would silently no-op, contradicting the adjacent comment that says ABI mismatches and addon init failures should be rethrown so the caller logs them.

Node.js throws these same error codes both for top-level package absence and for nested internal requires within an already-resolved package. Consider narrowing the check so it only swallows errors that are specifically about the `ai-hist-native` specifier itself — for example, by also inspecting the error message for `ai-hist-native` or `requireStack` — so that real broken-install problems still surface.</comment>

<file context>
@@ -46,18 +46,26 @@ export interface NativeAiHist {
+    // mismatch, missing system lib, addon init failure) is a real problem —
+    // rethrow so the caller logs it instead of silently doing nothing.
+    const code = (err as NodeJS.ErrnoException | undefined)?.code;
+    if (code === 'ERR_MODULE_NOT_FOUND' || code === 'MODULE_NOT_FOUND') {
+      return null;
+    }
</file context>

@khaliqgant
khaliqgant merged commit eef01b9 into main Jul 8, 2026
47 checks passed
@khaliqgant
khaliqgant deleted the feat/reflex-cloud-autosync branch July 8, 2026 07:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant