Skip to content

feat(agent-harness): share client state and cursor recovery - #5643

Open
iscekic wants to merge 1 commit into
shared-agent-harness-3bb0-s3from
shared-agent-harness-3bb0-s4
Open

feat(agent-harness): share client state and cursor recovery#5643
iscekic wants to merge 1 commit into
shared-agent-harness-3bb0-s3from
shared-agent-harness-3bb0-s4

Conversation

@iscekic

@iscekic iscekic commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

No new behavior — the product does not use these additions.


Summary

  • Repository: Kilo-Org/cloud; worktree: /Users/igor/Projects/.worktrees/shared-agent-harness-3bb0.
  • Branch: shared-agent-harness-3bb0-s4; base: origin/shared-agent-harness-3bb0-s3.

HarnessState, HarnessAction, and HistoryPage give harnessReducer snapshot replacement, ordered event reduction, and history merging that preserves live messages and pending work.
ConnectionState and SyncFailure separate connection failures from run outcomes; SynchronizationError rejects invalid conversation data, stale snapshots, and event gaps.
Replayed events cannot restore resolved work or partial text, and missing snapshot records disappear without fabricated completion.

Files
  • packages/agent-harness/src/state.ts — Source; A (added), 136 lines (+136/-0). Adds idle initialization, indexed records, and message ordering by creation time, then identifier. Tracks active runs and stable queue order, retains interaction resolutions, and adds or removes pending actions by tool-call identifier. Validates conversation and action identifiers, ignores replayed sequences, and raises retryable errors for stale snapshots or event gaps. History changes only messages and the history cursor; connection changes do not complete runs.

createHarnessStore adds HarnessStore, validating EventPageSchema and HistoryPageSchema before applying data and replacing snapshots after cursor expiry or event gaps.
ResumeOptions requires conversation and client identifiers, HarnessTransport, and HarnessClock; optional journal and bridge hints use ChangeSource to trigger reads, never effects.
Callers own refresh, loadHistory error handling, and dispose; pollIntervalMs defaults to 1,000 milliseconds, and nonretryable failures require a new store.

Files
  • packages/agent-harness/src/resume.ts — Source; A (added), 200 lines (+200/-0). Adds snapshot-first reads, 200-event pages, and 50-message history requests; both page schemas accept at most 200 records. Validates protocol versions and conversation identity, publishes event batches atomically, coalesces refreshes, and allows one immediate snapshot replacement per pass. Retryable failures retain run state and use exponential backoff capped at 30 seconds; unknown failures become retryable connection_lost errors. Drops history responses after snapshot replacement, cursor changes, blocking, or disposal; rejected history loads preserve state. Keeps subscriptions receiver-independent; disposal cancels polls and change subscriptions, notifies listeners once, and ignores late reads.

useHarnessState exposes HarnessStore to React through useSyncExternalStore, with the same snapshot getter for client and server rendering.
The hook observes state only, so callers still own refresh and disposal; this branch does not connect any product host.

Files
  • packages/agent-harness/src/react.ts — Source; A (added), 8 lines (+8/-0). Adds a client hook that forwards the subscription and shared snapshot getter to React without starting synchronization.

Tests: 1 test file added: packages/agent-harness/src/resume.test.ts (Test; A; 566 lines, +566/-0). Covers bootstrap, replay, cursor recovery, history races, message preservation, pending work, run queues, retries, blocking, notifications, and disposal.
Generated: 0 files changed.


Verification

Manual verification: skipped. This level adds shared state and recovery modules without deployed consumers.

Visual Changes

Visual Changes: N/A

Reviewer Notes

  • The handoff reports five passing scoped checks, including 26 resume tests. This description round did not rerun those checks.

Human steps

None before merge or after merge.

Notes

E2E: This level adds shared state and recovery modules without deployed consumers. Full runtime verification runs on the completed stack tip.

Stacked PRs — merge bottom to top. Each level shows only its own diff.

Runtime verification (E2E, user advocacy, simplify) runs on the tip PR over every level.
Every level keeps its own checks, its own bot review, and its own threads; each one is answered on its own PR.
Each level is its own deliverable: it builds and passes its own checks alone.
A finding on a level is repaired on that level, then carried upward with stack.sh forward.

  1. shared-agent-harness-3bb0chore(agent-harness): register workspaces and enforce CI boundaries #5632
  2. shared-agent-harness-3bb0-s2feat(agent-harness): define portable domain and snapshots #5637
  3. shared-agent-harness-3bb0-s3feat(agent-harness): define commands tools and permission policy #5639
  4. shared-agent-harness-3bb0-s4feat(agent-harness): share client state and cursor recovery #5643 ← this PR
  5. shared-agent-harness-3bb0-s5feat(agent-harness): persist command intents and execution receipts #5647
  6. shared-agent-harness-3bb0-s6feat(db): add harness ingress grants and retirement fences #5655
  7. shared-agent-harness-3bb0-s7feat(agent-harness): deliver legacy history and project durable text #5659
  8. shared-agent-harness-3bb0-s8feat(agent-harness): authorize durable grants and registered clients #5662
  9. shared-agent-harness-3bb0-s9feat(agent-harness): fence retirement and retry payload cleanup #5667 (tip)

if (needsSnapshot) continue;
failures = 0;
delay = interval;
publish({ ...state, connection: { status: 'connected' } });

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.

WARNING: Successful polls always publish a new state object

publish({ ...state, connection: { status: 'connected' } }) allocates a new HarnessState even when connection.status is already 'connected'. publish only skips on reference equality, so every empty poll notifies subscribers. useHarnessState reads this store through useSyncExternalStore, so React will re-render on each poll interval (default 1s) with no data change.

Suggested change
publish({ ...state, connection: { status: 'connected' } });
if (state.connection.status !== 'connected') publish({ ...state, connection: { status: 'connected' } });

Reply with @kilocode-bot fix it to have Kilo Code address this issue.

@kilo-code-bot

kilo-code-bot Bot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Code Review Summary

Status: 1 Issue Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 1
SUGGESTION 0
Issue Details (click to expand)

WARNING

File Line Issue
packages/agent-harness/src/resume.ts 131 Successful polls publish a new state object and retrigger React
Files Reviewed (4 files)
  • packages/agent-harness/src/react.ts - 0 issues
  • packages/agent-harness/src/resume.ts - 1 issue
  • packages/agent-harness/src/state.ts - 0 issues
  • packages/agent-harness/src/resume.test.ts - 0 issues

Fix these issues in Kilo Cloud


Reviewed by grok-4.6 · Input: 285.1K · Output: 34.6K · Cached: 211.6K

Review guidance: REVIEW.md from base branch shared-agent-harness-3bb0-s3

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