Skip to content

feat(agent): add AgentGUI live event protocol#1634

Merged
SingleMai merged 6 commits into
mainfrom
codex/agent-live-protocol
Jul 25, 2026
Merged

feat(agent): add AgentGUI live event protocol#1634
SingleMai merged 6 commits into
mainfrom
codex/agent-live-protocol

Conversation

@SingleMai

@SingleMai SingleMai commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

What changed

  • Add a schema-first AgentGUI live-event protocol with one exact revision shared by Go and TypeScript. The revision covers event schemas, protobuf field numbers, delivery-kind values, and JSON control shapes.
  • Keep the shared-agent fast lane as a Go-owned adapter on both peers: typed recipient projection, bounded batching/coalescing, replay/resume, gap detection, typed rejection, and oversize discontinuity handling.
  • Preserve opaque renderer business payloads with json.RawMessage, including file paths and large JSON integers; only closed workspace, Session, and Turn identities are projected.
  • Make message_delta.turnId required, close Turn terminal/nonterminal invariants, and coalesce only pure adjacent append_text deltas.
  • Normalize cumulative provider snapshots semantically: the first snapshot uses set, a strict prefix extension emits only the suffix as append_text, duplicates are dropped, and real rewrites/backtracks use set. This applies to Claude SDK assistant and reasoning streams as well as existing ACP/Codex deltas.
  • Extend the existing agent.activity.updated v2 contract so normalized local provider text/reasoning deltas are published over the existing /v1/events/ws business-event WebSocket.
  • Bridge runtime deltas synchronously before durable-report enqueue so a later canonical terminal update cannot overtake its optimistic prefix. Observer failure is logged and cannot suppress the existing runtime fan-out or canonical persistence.
  • Keep canonical message_update for terminal confirmation, tools, audits, imports, and any message that was not represented by a delta. Suppress only redundant nonterminal runtime text/reasoning snapshots.
  • Add a strict activity-core transport cleaner and a renderer-local optimistic projection module. The renderer exposes ordinary canonical-shaped messages to AgentGUI, reconciles on terminal/canonical reads, and falls back to authoritative reads on reconnect, invalid payloads, or unanchored appends.
  • Correct architecture documentation that still described Agent activity as SSE; this path uses the shared business-event WebSocket.

Why

Shared and local Agent streaming need a lossy optimistic fast lane without making peer or WebSocket data authoritative and without teaching renderers separate transport protocols. The runtime already produces normalized AgentGUI deltas, so both the device-link adapter and the local business-event bridge now carry that same semantic event. Canonical local/cloud reads remain the source of truth.

This removes repeated full message_update payloads from high-frequency text/reasoning streaming. Cumulative provider snapshots are reduced at the provider normalizer boundary, so monotonic text growth is linear in transmitted text size instead of repeatedly sending the full accumulated message.

Impact and risk

  • Intentional breaking API change to the unpublished optimistic overlay: reconcile, project, and reset require an explicit workspace/Session scope.
  • The local Agent realtime path now exposes message_delta; renderer UI remains unaware of whether the source was local or shared.
  • Protocol revisions are exact-match. A shared-device mismatch produces a typed rejection and falls back to canonical reconciliation; no compatibility branch is added.
  • Frames are not fragmented. A shared delivery over the safe limit becomes a delivery_too_large discontinuity with reconcile keys so the final oversized event cannot disappear silently.
  • No persistence, local database, cloud schema, or canonical report format changes.
  • Event-stream backpressure remains nonblocking for runtime/canonical progress: a slow WebSocket client is disconnected and reconciles after reconnect.

Validation

  • pnpm check:changed -- --push-ready — 32 lanes passed in the pre-push gate
  • pnpm --filter @tutti-os/desktop build
  • Desktop unit tests — 1581 total, 1578 passed, 3 skipped, 0 failed
  • activity-core typecheck and 268 tests
  • go test ./packages/agent/daemon/runtime ./services/tuttid/service/eventstream ./services/tuttid/service/agent ./services/tuttid
  • Claude cumulative-snapshot and normalizer set/append/rewrite tests under the race detector, repeated 50 times
  • targeted runtime/eventstream/tuttid race tests, repeated up to 20 times
  • cd services/tuttid && go build ./...
  • generated event protocol and Agent live protocol checks

Exploratory full-package go test -race ./runtime -count=1 still reports the existing CodexAppServerAdapter.setSessionActiveTurnID race. The same race reproduces on the untouched baseline; the modified runtime tests pass repeatedly under the race detector.

Signed-off-by: liying <422107224@qq.com>
Signed-off-by: liying <422107224@qq.com>
Signed-off-by: liying <422107224@qq.com>
Signed-off-by: liying <422107224@qq.com>
Signed-off-by: liying <422107224@qq.com>
@SingleMai
SingleMai marked this pull request as ready for review July 25, 2026 01:32
@SingleMai
SingleMai merged commit 3fba361 into main Jul 25, 2026
11 of 12 checks passed

@devin-ai-integration devin-ai-integration 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.

Devin Review found 2 potential issues.

View 3 additional findings in Devin Review.

Open in Devin Review

Comment on lines +500 to +505
func (n *acpTurnNormalizer) AppendToolOutputDelta(
session Session,
turnID string,
rawToolCallID string,
delta string,
) []activityshared.Event {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Provider normalizer source file grows past the mandated size limit

New streaming/tool-output logic is added to the provider normalizer file (AppendToolOutputDelta at packages/agent/daemon/runtime/acp_turn_normalizer.go:500), pushing it from 786 lines to 930 lines, past the repository's 800-line limit for business-code files.

Impact: The file becomes harder to maintain and violates an explicit repository size rule that signals decomposition is required.

Why this violates the repository rule

AGENTS.md and CONTRIBUTING.md state: "Business-code files should stay at or below 800 lines. Prefer decomposition before adding more logic." The base file (b7053fb) was 786 lines and under the limit; this PR adds AppendToolOutputDelta, attachTextLiveOperation, attachToolOutputLiveOperation, and related logic, crossing 800 (now 930). Per the rule, the new logic should be introduced together with a decomposition of the file.

Prompt for agents
packages/agent/daemon/runtime/acp_turn_normalizer.go crossed the repository's 800-line business-file limit (786 -> 930) after adding tool-output delta and live-operation attachment helpers. The AGENTS.md/CONTRIBUTING.md rule requires decomposing an oversized business file before adding more logic. Consider extracting the live-operation attachment helpers (attachTextLiveOperation, attachToolOutputLiveOperation, and the metadata-key constants) and/or the tool-output delta handling into a separate file within the same package (for example acp_turn_normalizer_live.go) so each file stays at or below 800 lines.
Open in Devin Review

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

Comment on lines +132 to +146
protected reconcileOptimisticMessages(
workspaceId: string,
agentSessionId: string
): void {
const normalizedWorkspaceId = normalizeWorkspaceId(workspaceId);
const normalizedAgentSessionId = agentSessionId.trim();
if (!normalizedAgentSessionId) return;
const canonicalMessages =
this.entry(normalizedWorkspaceId).engine.getSnapshot().sessionMessages
.messagesBySessionId[normalizedAgentSessionId] ?? [];
this.optimisticProjection.reconcile(
normalizedWorkspaceId,
normalizedAgentSessionId,
canonicalMessages
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Reconcile bridge source file remains well over the mandated size limit and grows further

New optimistic-projection wiring is added to the reconcile bridge file (reconcileOptimisticMessages at packages/agent/desktop reconcile bridge, apps/desktop/src/renderer/src/features/workspace-agent/services/internal/workspaceAgentActivityReconcileBridge.ts:132), enlarging an already over-limit file from 893 to 993 lines instead of decomposing it first.

Impact: The file drifts further past the repository's 800-line business-file limit, compounding a maintenance hotspot the rule is meant to prevent.

Why this violates the repository rule

AGENTS.md and CONTRIBUTING.md state: "Business-code files should stay at or below 800 lines. Prefer decomposition before adding more logic." The file was already 893 lines at the merge base and this PR adds the optimistic projection methods (subscribeActivitySnapshot, reconcileOptimisticMessages, message_delta handling) rather than decomposing before adding logic.

Prompt for agents
apps/desktop/src/renderer/src/features/workspace-agent/services/internal/workspaceAgentActivityReconcileBridge.ts is already above the repository's 800-line business-file limit (893 at base, 993 after this PR). The AGENTS.md/CONTRIBUTING.md rule requires decomposition before adding more logic. Consider extracting the optimistic-projection bridging responsibilities (activitySnapshot projection wiring, subscribeActivitySnapshot, reconcileOptimisticMessages, and the message_delta observation branch) into a dedicated collaborator module so the reconcile bridge returns to or below 800 lines.
Open in Devin Review

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

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