Skip to content

feat(continuity): Add file-based continuity command handling to orchestrator#331

Merged
khaliqgant merged 10 commits intomainfrom
feature/continuity-file-persistence
Jan 28, 2026
Merged

feat(continuity): Add file-based continuity command handling to orchestrator#331
khaliqgant merged 10 commits intomainfrom
feature/continuity-file-persistence

Conversation

@khaliqgant
Copy link
Copy Markdown
Member

@khaliqgant khaliqgant commented Jan 28, 2026

Previously, file-based continuity commands (KIND: continuity) were parsed by relay-pty and output to stderr, but the orchestrator only processed relay_command types, causing continuity commands to be silently dropped.

Changes:

  • Add handleRustContinuityCommand() in relay-pty-orchestrator.ts to process continuity JSON from relay-pty stderr
  • Map Rust ContinuityCommand format to TypeScript ContinuityCommand
  • Forward to ContinuityManager.handleCommand() for persistence
  • Add 5 tests for continuity command handling
  • Document continuity file format in relay-pty-schemas.ts

This enables proper session persistence via the file-based protocol:
KIND: continuity
ACTION: save/load/uncertain


Open with Devin

Copy link
Copy Markdown
Contributor

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

Choose a reason for hiding this comment

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

Devin Review found 2 potential issues.

View issues and 4 additional flags in Devin Review.

Open in Devin Review

Comment on lines +1167 to +1173
this.messageQueue.push({
from: 'system',
body: response,
messageId: `continuity-${Date.now()}`,
thread: 'continuity-response',
});
this.log(`Queued continuity response for injection`);
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.

🟡 Missing processMessageQueue() call after queuing continuity response

When a continuity command (like load) returns a response, the response is pushed to messageQueue but processMessageQueue() is never called to trigger injection.

Click to expand

How the bug is triggered

When an agent sends a load continuity command and handleCommand() returns a response (e.g., previous session context), the code at lines 1167-1172 queues the response but doesn't call processMessageQueue().

Actual vs Expected

  • Actual: The response sits in the queue until either the queue monitor runs (every 5 seconds per QUEUE_MONITOR_INTERVAL_MS) or another message arrives.
  • Expected: The response should be injected immediately, as done in tmux-wrapper.ts:1177 which calls this.checkForInjectionOpportunity() after pushing.

Impact

Continuity responses (session context on load) will be delayed by up to 5 seconds, degrading user experience when agents request their previous session state.

Recommendation: Add this.processMessageQueue(); after line 1173, similar to how other message queue operations in this file call it (e.g., line 2037).

Open in Devin Review

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

Comment on lines +1150 to +1152
if (command.content && this.processedContinuityCommands.has(cmdHash)) {
this.log(`Continuity command already processed: ${cmdHash}`);
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.

🟡 Deduplication check fails for 'uncertain' commands due to wrong field check

The deduplication condition checks command.content but for uncertain commands, the content is stored in command.item, causing these commands to never be deduplicated.

Click to expand

How the bug is triggered

For an uncertain command with content "API rate limit handling unclear":

  1. Line 1145 sets command.item = "API rate limit handling unclear"
  2. command.content remains undefined
  3. Line 1149 creates hash: "uncertain:API rate limit handling unclear" (correct)
  4. Line 1150 checks: if (command.content && this.processedContinuityCommands.has(cmdHash)) - but command.content is falsy!
  5. The duplicate check is skipped, and duplicate uncertain commands are processed.

Actual vs Expected

  • Actual: uncertain commands are never deduplicated because command.content is undefined for them.
  • Expected: The check should use (command.content || command.item) to match the hash computation, consistent with tmux-wrapper.ts:1150-1154 which uses hasContent = command.content || command.query || command.item.

Impact

Duplicate uncertain commands will be processed multiple times, potentially recording the same uncertainty item repeatedly in the continuity ledger.

Recommendation: Change line 1150 from if (command.content && this.processedContinuityCommands.has(cmdHash)) to if ((command.content || command.item) && this.processedContinuityCommands.has(cmdHash)) to match the hash computation on line 1149.

Open in Devin Review

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

…strator

Previously, file-based continuity commands (KIND: continuity) were parsed
by relay-pty and output to stderr, but the orchestrator only processed
relay_command types, causing continuity commands to be silently dropped.

Changes:
- Add handleRustContinuityCommand() in relay-pty-orchestrator.ts to process
  continuity JSON from relay-pty stderr
- Map Rust ContinuityCommand format to TypeScript ContinuityCommand
- Forward to ContinuityManager.handleCommand() for persistence
- Add 5 tests for continuity command handling
- Document continuity file format in relay-pty-schemas.ts

This enables proper session persistence via the file-based protocol:
  KIND: continuity
  ACTION: save/load/uncertain

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@khaliqgant khaliqgant force-pushed the feature/continuity-file-persistence branch from 27a36c4 to dc8c07c Compare January 28, 2026 10:53
khaliqgant and others added 4 commits January 28, 2026 12:21
Fixes CI failures caused by npm bug #4828 where optional dependencies
(@rollup/rollup-linux-x64-gnu) are not installed correctly.

Added 'npm install --no-save rollup || true' step after npm ci in:
- test.yml (test, coverage, lint jobs)
- node-compat.yml (install-test job)
- sqlite-migrations.yml
- publish.yml (build job, after both npm ci and clean reinstall)

This ensures vitest's rollup dependency has platform-specific binaries
available even when npm's optional dependency handling fails.
Create shared request/response handling helpers to ensure SDK and MCP
clients stay in lockstep and prevent future inconsistencies.

Added:
- Type aliases: SpawnResult, ReleaseResult
- createRequestHandler() - shared request/response logic
- createRequestEnvelope() - envelope creation helper
- isMatchingResponse() - response matching logic
- handleResponse() - response resolution logic
- toSpawnResult() / toReleaseResult() - conversion functions
- generateRequestId() - ID generation helper

This provides a single source of truth for request/response handling
and ensures both clients behave identically.
Fix all inconsistencies between MCP and SDK client implementations:

1. spawn() - Now waits for SPAWN_RESULT like SDK, uses shared helpers
2. release() - Returns ReleaseResultPayload with success/name/error
3. sendAndWait() - Returns full AckPayload instead of fabricated object
4. send() - Supports kind/data options like SDK sendMessage
5. Envelopes - All spawn/release include 'from: agentName' field

Refactored to use shared client helpers from @agent-relay/utils,
reducing code duplication and ensuring consistency going forward.

Updated tests to use AckPayload structure.
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

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

Choose a reason for hiding this comment

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

Devin Review found 1 new potential issue.

View issue and 9 additional flags in Devin Review.

Open in Devin Review

Comment thread packages/mcp/src/client.ts
khaliqgant and others added 5 commits January 28, 2026 12:27
Fixes npm ci failures in CI by including optional dependency entries
for all platforms (esbuild, rollup, turbo platform binaries).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Aligns hybrid-client.ts and simple.ts with the new AckPayload return
type from the SDK/MCP consistency changes.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Since spawn() now waits for SPAWN_RESULT like SDK, tests need to mock
the socket to send back a SPAWN_RESULT response. Updated both failing
tests to properly mock the response.
… deps

The createRequestHandler in client-helpers imports createConnection from
node:net. The test mock needs to properly preserve the original module
while overriding createConnection.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@khaliqgant khaliqgant merged commit ceccbe5 into main Jan 28, 2026
27 checks passed
@khaliqgant khaliqgant deleted the feature/continuity-file-persistence branch January 28, 2026 11:52
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