Skip to content

feat: structured agent frames + durable sessions for the AI bridge (0392) - #623

Merged
crs48 merged 8 commits into
mainfrom
claude/0392-ai-harness-architectures-and-xnet-connectivity
Jul 23, 2026
Merged

feat: structured agent frames + durable sessions for the AI bridge (0392)#623
crs48 merged 8 commits into
mainfrom
claude/0392-ai-harness-architectures-and-xnet-connectivity

Conversation

@crs48

@crs48 crs48 commented Jul 23, 2026

Copy link
Copy Markdown
Owner

Implements the backend protocol foundation from exploration 0392 — the "keystone" the doc says everything else hangs off. Scoped deliberately to the pieces that are fully unit-testable in-repo; the agent-integration and UI items ship as follow-ups (see below).

What landed (5/10 implementation, 4/7 validation)

  • AgentFrame vocabulary + frame reducer (@xnetjs/devkit) — an ACP-aligned union (session, delta, tool_call, tool_result, permission_request, cost, result) and foldStreamJsonFrames, the frame-emitting counterpart of reduceStreamJsonLine. The Claude streaming agent gains streamTurnFrames; the delta-only streamTurn is untouched so the OpenAI endpoint is byte-for-byte unchanged.
  • Framed endpoint POST /v1/agent/stream — streams the frames as SSE with the same Host/Origin/pairing-token hardening as /v1/chat/completions (which is unchanged). Plain agents get synthesized delta+result frames.
  • Durable bridge sessionscreateBridgeSessionStore accepts a SessionPersistence; fileSessionPersistence writes the fingerprint→session-id map to disk, and xnet bridge serve --agent claude wires it under the bridge cwd so --resume sessions survive a daemon restart. In-memory stays the default.
  • models.dev catalog consumer (@xnetjs/plugins) — fetchModelsDevCatalog with a vendored snapshot fallback (never hangs on an outage), plus OpenRouter HTTP-Referer/X-Title app-attribution headers on OpenRouter-bound requests only.

Changeset: devkit minor, plugins minor, cli patch. All additive — no breaking API changes.

Verification

  • packages/devkit, packages/plugins/src/ai, packages/cli bridge, electron bridge manager — 200 tests green.
  • Typecheck green across devkit, cli, plugins, and downstream consumers (electron, web) — 40/40 turbo tasks.
  • New tests: 14 frame-reducer cases, 6 framed-endpoint cases, durable-session round-trip across a simulated restart, models.dev live/snapshot/outage fallback, OpenRouter attribution gating.

Deliberately deferred to follow-up PRs

These need a real vendor binary, the running React app, or are explicitly a separate PR in the exploration — they can't be responsibly built-and-merged here without integration verification:

  • codexAppServerChatAgent (JSON-RPC to codex app-server) + its resume/interrupt validation
  • gemini --experimental-acp agent
  • Panel: tool-call rendering + in-chat approval UI wired to permission_request (and the "confirmed apply" / consent-ceiling validation that depends on it)
  • Wiring the model-lane generateWithTools + AiSurfaceService loop to emit the same frames
  • Extending bridge install beyond launchd (marked "separate PR" in the doc)

Exploration 0392 stays [_] until the follow-ups land.

🤖 Generated with Claude Code

xNet Test and others added 8 commits July 22, 2026 19:39
…vity

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: xNet Test <test@xnet.dev>
Adds the ACP-aligned AgentFrame union and foldStreamJsonFrames, the
frame-emitting counterpart of reduceStreamJsonLine. cliStreamingChatAgent
gains streamTurnFrames (tool_call/tool_result/permission_request/cost/session
frames) while streamTurn stays on the delta reducer so the OpenAI-compatible
endpoint is byte-for-byte unchanged.

Signed-off-by: xNet Test <test@xnet.dev>
Streams structured AgentFrames (session, delta, tool_call, tool_result,
permission_request, cost, result) as SSE, with the same pairing-token, Host,
and Origin discipline as /v1/chat/completions, which is unchanged. Framed
agents forward their native frames; plain agents get synthesized delta+result
frames. Session planning/recording is shared with the OpenAI endpoint.

Signed-off-by: xNet Test <test@xnet.dev>
createBridgeSessionStore accepts a SessionPersistence; fileSessionPersistence
writes the fingerprint to session-id map as JSON. The CLI wires it under the
Claude bridge cwd so a restart continues --resume sessions instead of
re-seeding every open conversation. In-memory remains the default.

Signed-off-by: xNet Test <test@xnet.dev>
Adds fetchModelsDevCatalog/parseModelsDevCatalog with a vendored snapshot
fallback so cloud-key/local model pickers stop drifting against hand-kept
tables and never hang on a models.dev outage. OpenAICompatibleProvider now
sends HTTP-Referer + X-Title app-attribution headers to OpenRouter only.

Signed-off-by: xNet Test <test@xnet.dev>
Signed-off-by: xNet Test <test@xnet.dev>
Signed-off-by: xNet Test <test@xnet.dev>
Signed-off-by: xNet Test <test@xnet.dev>
@crs48
crs48 temporarily deployed to pr-623 July 23, 2026 02:58 — with GitHub Actions Inactive
github-actions Bot added a commit that referenced this pull request Jul 23, 2026
@github-actions

github-actions Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Preview removed for PR #623.

github-actions Bot added a commit that referenced this pull request Jul 23, 2026
@crs48
crs48 merged commit 5c708a4 into main Jul 23, 2026
19 checks passed
@crs48
crs48 deleted the claude/0392-ai-harness-architectures-and-xnet-connectivity branch July 23, 2026 03:07
github-actions Bot added a commit that referenced this pull request Jul 23, 2026
crs48 added a commit that referenced this pull request Jul 29, 2026
…nt (0394 phase 2) (#656)

Exploration 0394's deliberately-deferred phase 2, built with the design
pass it asked for — and the unblocking of 0406's last implementation
item.

**The in-chat approval ceremony** (`ai-chat-ceremony.ts`): write tools
ride the existing risk-tiered `AgentAuditRecorder` (0337). Reads bypass
(no audit-node flood from searches); low-risk writes execute with an
audit trail; **medium** parks on the one-time code — `APPROVE <code>`
typed in chat rides the real nonce machinery (SHA-256 match, TTL,
single-use); **high/critical refuse the chat path** structurally (no
nonce exists) and need the deliberate in-app approval, stamped with the
operator's DID, with the change reviewed before the button enables.
Denial and expiry resolve the model's tool call honestly — the turn
never hangs. 9 ceremony tests + 5 gating tests.

**Doubly-gated write tools** (`ai-chat-write-tools.ts`): the tier must
be `reliable` (`writeModeFor` = agentic) AND the operator must flip the
new "Allow edits" toggle (default off). The badge escalates honestly:
reads workspace → searches workspace → **edits with approval**.

**The framed bridge client** (`ai-bridge-frames.ts`): the panel's bridge
tier now speaks `/v1/agent/stream` — the endpoint #623 shipped and
nothing consumed. Tool activity, per-turn cost, and permission asks
arrive structured instead of flattened away.

**Desktop MCP ceremony**: `agent-mcp-server.ts` now passes `agentAudit`,
closing a real hole — a bridged agent could previously run
`xnet_apply_page_markdown` with no gate but the system prompt.

**Verified live on the desktop**: with writes armed, a chat turn through
the bridged Claude Code created a real Page node in the desktop SQLite
store (`Ceremony Test 0394`), and the panel showed "Last turn · $0.1581
· 495 output tokens" — cost frames only exist on the framed wire, so
that line is proof of the new transport. With writes off, the agent
still refuses (verified in the same session).

Doc movement: 0394 → 9/10 impl, 3/7 val. **0406 → 23/23 impl, 9/10 val**
(only the cold-open item remains, in flight separately).

🤖 Generated with [Claude Code](https://claude.com/claude-code)
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