Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
d5e8c2c
chore(vibecode): add branch scratchpad template
0xTomDaniel Nov 29, 2025
5f31fd0
docs(adr): record A2A and AG-UI decisions
0xTomDaniel Nov 29, 2025
6dcc0b3
docs(adr): rename state-control to engram
0xTomDaniel Nov 29, 2025
39f0426
docs(adr): clarify config views via artifacts
0xTomDaniel Nov 29, 2025
1f558a3
docs(adr): note backward compatible paths
0xTomDaniel Nov 29, 2025
20496d0
docs(vibecode): log a2a adr alignment gaps
0xTomDaniel Nov 29, 2025
e068378
docs(vibecode): add a2a alignment prd
0xTomDaniel Nov 29, 2025
586cc6c
feat(a2a): add run options and shared state projection
0xTomDaniel Nov 29, 2025
08b16a7
docs(vibecode): update a2a troubleshooting log
0xTomDaniel Nov 29, 2025
478c60a
test(a2a): broaden agent utils coverage
0xTomDaniel Nov 29, 2025
f954d2e
test(a2a): add agent integration coverage
0xTomDaniel Nov 30, 2025
43f2359
test(a2a): add resubscribe streaming e2e
0xTomDaniel Nov 30, 2025
06d7d97
test(a2a): cover legacy streaming output
0xTomDaniel Nov 30, 2025
6d8b25a
test(a2a): extend conversion coverage
0xTomDaniel Nov 30, 2025
f7b1fc0
docs(vibecode): add HITL interrupt ADR
0xTomDaniel Dec 2, 2025
bd36678
docs(vibecode): refine HITL interrupt flow
0xTomDaniel Dec 2, 2025
ca0024b
docs(vibecode): mark a2a adr set accepted
0xTomDaniel Dec 2, 2025
aae2cab
feat(a2a): handle hitl interrupts and resume
0xTomDaniel Dec 2, 2025
749169f
test(client): strengthen subscriber lifecycle
0xTomDaniel Dec 2, 2025
9f56026
docs(vibecode): refresh a2a troubleshooting log
0xTomDaniel Dec 2, 2025
ba2ac4d
fix(a2a): align context aware event projection
0xTomDaniel Dec 2, 2025
48c29c9
docs(vibecode): clarify HITL shared state language
0xTomDaniel Dec 2, 2025
377714c
fix(a2a): gate engram header and cover hitl resume
0xTomDaniel Dec 2, 2025
33b388f
docs(vibecode): update input-required bridge plan
0xTomDaniel Dec 2, 2025
4b7a163
fix(a2a): align input-required handling
0xTomDaniel Dec 2, 2025
3a2b89e
docs(vibecode): capture stateless bridge plan
0xTomDaniel Dec 4, 2025
de41e76
build(a2a): add lint tooling
0xTomDaniel Dec 4, 2025
80be3e4
feat(client): support deferred thread binding
0xTomDaniel Dec 4, 2025
d11d879
fix(langgraph): default regenerate thread id
0xTomDaniel Dec 4, 2025
2456314
fix(a2a): harden stateless run handling
0xTomDaniel Dec 4, 2025
46061e9
docs(vibecode): add engram v0.1 adr set
0xTomDaniel Dec 5, 2025
41c4fca
docs(vibecode): expand engram prd and notes
0xTomDaniel Dec 6, 2025
8b24db5
build(sdks): add eslint configs for packages
0xTomDaniel Dec 6, 2025
e0389fc
feat(a2a): add engram run handling
0xTomDaniel Dec 6, 2025
1aeb6bf
docs(vibecode): add engram snapshot/delta plan
0xTomDaniel Dec 6, 2025
48735c5
fix(a2a): hydrate engram snapshots from tasks
0xTomDaniel Dec 6, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# ADR 0001: Interface Surfaces (A2A vs AG-UI vs REST)

**Status**
Accepted

**Date**
2025-11-29

## Context

The platform needs clear, canonical interaction surfaces for humans and machines without fragmenting the cognitive surface across protocols.

## Decision

- A2A is the canonical machine interface; other agents, CLIs, and jobs interact only via A2A Messages/Tasks and extensions.
- AG-UI is the canonical human interface; frontends use Runs, events, and shared state to talk to agents.
- No bespoke REST/gRPC for cognitive/agent behavior; REST/gRPC are reserved for platform/admin/infra (multi-tenant ops, analytics, internal tools).

## Consequences

- Single machine interface simplifies interoperability and extension discovery.
- Human flows stay on AG-UI abstractions without leaking UI concerns into A2A.
- Avoids split-brain behavior between REST and A2A for core agent semantics.

24 changes: 24 additions & 0 deletions .vibecode/feat-improve-a2a-support/adr/0002-run-task-mapping.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# ADR 0002: Run <-> Task Mapping

**Status**
Accepted

**Date**
2025-11-29

## Context

Long-lived workflows need a stable mapping between AG-UI Runs and A2A Tasks while supporting mid-flight injections and reconnect/resubscribe flows.

## Decision

- Treat one long-lived A2A Task as mapped to one long-lived AG-UI Run subscription; the Run streams Task updates for the Task’s non-terminal life.
- Mid-flight injections are new Messages on the same Task, sent via short-lived control Runs; the long-lived subscription Run stays open.
- On reconnect, start a new Run and resync via StateSnapshot/MessagesSnapshot/ActivitySnapshot rather than reopening the old Run.

## Consequences

- Supports continuous jobs with uninterrupted streaming and provenance.
- Keeps injections lightweight while preserving a single Task timeline.
- Reconnect semantics rely on snapshots, avoiding fragile run resurrection.

26 changes: 26 additions & 0 deletions .vibecode/feat-improve-a2a-support/adr/0003-run-modes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# ADR 0003: Run Invocation Modes (send vs stream)

**Status**
Accepted

**Date**
2025-11-29

## Context

We need a unified Run API that maps cleanly to A2A message.send and message.stream while covering new and existing Tasks.

## Decision

- Expose RunOptions with `mode: "send" | "stream"`, `threadId`, optional `taskId`, optional `message` (A2A-shaped), and optional `state`.
- `mode: "stream", taskId: undefined` → A2A `message.stream` creates a new Task and a long-lived AG-UI Run subscription.
- `mode: "send", taskId: undefined` → A2A `message.send` creates a new Task and returns when the Task reaches completion; the Run is short-lived or synchronous.
- `mode: "send", taskId: existing` → A2A `message.send` as injection/control; short-lived control Run, while the subscription Run for that Task stays open.
- `mode: "stream", taskId: existing` → optional secondary subscription to an existing Task.
- For backwards compatibility, `mode: "send", taskId: undefined` mirrors the current one-shot behavior (single message, blocking or short-lived run); richer modes add streaming/subscription without changing that baseline.

## Consequences

- Clear mapping between UI invocation modes and A2A semantics.
- Supports both synchronous one-offs and long-lived streaming jobs.
- Enables injections without disrupting primary subscriptions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# ADR 0004: Canonical Input as Messages

**Status**
Accepted

**Date**
2025-11-29

## Context

Inputs that affect agent behavior (from users or agents) must be normalized to A2A semantics to keep auditability and multi-agent consistency.

## Decision

- Everything that matters to agent behavior is expressed as an A2A Message with appropriate `parts`, `extensions`, and `metadata`.
- Covers conversational input, input-required approvals/rejections, mid-flight injections, config/setting changes, and workflow toggles/filters.
- Clients send the current message only; do not resend the full conversation transcript. The A2A service owns task history and context storage.
- Optional cues (system/developer/config, input resume payloads) may be attached to the current message when explicitly enabled, but are not a substitute for server-side history.
- Long-lived task/domain state is represented via Tasks + Artifacts; AG-UI does not send state blobs directly as truth.

## Consequences

- Uniform ingestion path simplifies reasoning, auditing, and replay.
- Prevents hidden state mutations outside the A2A message log.
- Aligns UI and other agents on the same contract.
20 changes: 20 additions & 0 deletions .vibecode/feat-improve-a2a-support/adr/0005-engram-extension.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# ADR 0005: Config/Control via Engram Extension

**Status**
Superseded by ADR 0014 & ADR 0020

**Date**
2025-11-29

## Context

We need a structured way to mutate agent/task configuration via A2A without overloading Secure Passport or leaking UI specifics.

## Decision

- Superseded: ADR 0014 redefines Engram as the domain-state extension with canonical RPC surface and task-based streaming; ADR 0020 sets the versioned extension URI and activation model.
- Previous guidance (URN-style Engram, message-only config lane) is retained here for historical context but is not normative after 2025-12-05.

## Consequences

- Historical only; see ADR 0014–0020 for the current Engram design and extension URI.
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# ADR 0006: AG-UI Shared State as Projection

**Status**
Accepted

**Date**
2025-11-29

## Context

AG-UI maintains shared state, but agent behavior must be driven by canonical A2A state/config, not by UI-local mutations.

## Decision

- Treat AG-UI shared state as a projection/view, not the source of truth.
- Partition AG-UI state into a config slice (mirrors agent config), a view/data slice (mirrors Task/domain state), and UI-only fields.
- When AG-UI state changes, the bridge computes semantic deltas for the config slice and emits Engram messages (ADR 0005); view/data stay driven by Task/Artifact updates; UI-only fields stay local.

## Consequences

- Agent behavior stays anchored to A2A state while UI remains responsive.
- Config changes are explicit, auditable messages; view data flows from Tasks/Artifacts.
- UI-only concerns avoid polluting agent-facing protocols.
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# ADR 0007: Metadata Layering (Keep AG-UI Details Internal)

**Status**
Accepted

**Date**
2025-11-29

## Context

We must prevent AG-UI-specific metadata from leaking into external A2A interactions while still routing messages correctly inside the agent.

## Decision

- Keep AG-UI-specific metadata (for example, source, threadId, runId) inside the AG-UI <-> A2A bridge.
- External A2A clients see plain messages and known extensions (such as Engram), not AG-UI markers.
- Inside the agent, routing uses semantic markers (extension URIs, DataPart `kind`) rather than origin-specific UI metadata.

## Consequences

- Clean separation between UI session details and A2A semantics.
- Reduces coupling and surprises for third-party agents.
- Agent internals route on stable protocol markers instead of UI provenance.
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# ADR 0008: LLM Lane vs Config Lane

**Status**
Accepted

**Date**
2025-11-29

## Context

Agent inputs may be conversational or imperative config changes; these require different handling paths. The A2A surface exposes a single message API (`message.send` / `message.stream`) plus Task/Artifact outputs, so lane selection must be encoded in the message itself (via extensions/metadata) without inventing a second protocol.

## Decision

- Messages without the Engram extension flow through the LLM/planner/workflow lane as conversational or high-level control input—these ride the standard A2A message pipeline unchanged.
- Messages with the Engram extension flow through a config lane: parse `agent_state_update`, apply config/task updates directly on the A2A server/agent state, and optionally emit derived context/system cues to the LLM (still via the A2A message stream, not a side channel).
- Task/domain state and UI projections return via Tasks + Artifacts, not via Engram output messages; the same applies to config views that the agent chooses to surface back to AG-UI, preserving the A2A output contract.
- If no Engram extension is present, inputs stay in the LLM/conversational lane and default projection semantics apply; adding Engram is opt-in for explicit config control and is the only way to request state/config mutation over A2A.

## Consequences

- Protects config mutations from LLM hallucinations and preserves intent.
- Keeps conversational inputs and config updates independent yet coordinated.
- Output side remains artifact-driven for consistent UI projections.
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# ADR 0009: Audit, Replay, and Multi-Agent Consistency

**Status**
Accepted

**Date**
2025-11-29

## Context

We need strong provenance and replay for config and domain changes across humans and agents.

## Decision

- Treat A2A Message history plus Task lifecycle (including Artifacts) as the canonical audit log.
- All meaningful config changes become Engram messages; no silent in-process mutations.
- Domain/view changes are represented in Task status and Artifacts, not hidden UI state.
- Other agents use the same Engram mechanism as AG-UI for consistency.
- Message-only agents remain valid: when only `kind: "message"` text parts are emitted, they continue to render as assistant messages; artifact/status handling is additive for richer agents.

## Consequences

- Full provenance of who/when/what for config and domain state.
- Reliable replay from initial state plus message/Artifact history.
- Shared contract enables multi-agent orchestration without special-casing UI.
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# ADR 0010: A2A -> AG-UI Shared State via Tasks and Artifacts

**Status**
Accepted (amended by ADR 0016/0018)

**Date**
2025-11-29

## Context

AG-UI shared state must be driven from A2A outputs without treating artifacts as implicit commands.

## Decision

- Tasks + Artifacts are the primary source for projecting A2A outputs into AG-UI shared state.
- Text artifacts stream as assistant messages by default (one per artifactId, honoring `append` and `lastChunk`).
- JSON/structured artifacts map to shared-state paths (for example, `/view/portfolio`) as snapshots or append deltas based on `append` and `lastChunk`.
- Artifacts are projections of task/domain state or config views; they are not config-mutation commands. Per ADR 0016/0018, config mutations use the Engram RPC surface (`engram/set`, `engram/patch`, etc.), with message-embedded Engram ops treated as optional/non-normative sugar.
- Backwards-compatible baseline: if agents only emit `kind: "message"` text parts, projection stays as the current text-message path; artifact-aware projection is additive when agents provide status/artifact events.

## Consequences

- Shared state stays aligned with task/artifact history.
- Streaming semantics (`append`, `lastChunk`) control snapshot vs append behavior.
- Clear separation between config mutation (Engram messages) and view projection (artifacts).
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# ADR 0011: Implicit vs Explicit Semantics for A2A Agents

**Status**
Accepted

**Date**
2025-11-29

## Context

The AG-UI gateway must provide sensible defaults for arbitrary A2A agents while allowing richer projections for agents that opt in to metadata conventions.

## Decision

- Support implicit defaults for generic agents: first text artifact renders as the primary assistant message; other text artifacts become additional messages/results; JSON artifacts render as generic result/JSON panels and are projected into shared state via safe defaults (for example, `/view/artifacts/<artifactId>` or `/view/results/...`). `append: false` snapshots replace the projection subtree; `append: true` appends; `lastChunk: true` closes streaming for that artifact.
- Support explicit semantics for cooperative agents: artifacts may carry metadata (kind/scope/path/uiKind) indicating projection targets and snapshot vs append behavior.
- Agent config/knobs remain explicit: use Engram extension Messages for config mutation; artifacts do not implicitly change agent configuration.

## Consequences

- Safe, useful baseline experience for any A2A agent.
- Rich, precise shared-state projection when agents provide explicit metadata.
- Prevents accidental config mutations via artifacts while enabling structured views.
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# ADR 0012: Input Interrupts via A2A `input_required` + AG-UI Activity/State

**Status**
Accepted

**Date**
2025-12-01

## Context

- We now need first-class input-required handling while keeping A2A the source of truth and AG-UI as a projection (ADR 0004, ADR 0006, ADR 0010).
- A2A expresses input pauses via `TaskState.input_required` and DataPart payloads; AG-UI surfaces pauses via interrupts/resume plus Activity/State events.
- Web check (`@ag-ui/core@0.0.41` on unpkg) confirms Activity event types we can emit:
- `ACTIVITY_SNAPSHOT` `{ type, messageId, activityType, content, replace? }`
- `ACTIVITY_DELTA` `{ type, messageId, activityType, patch }`
- PRD requires Activity/State to keep `/view/tasks/<taskId>` as the canonical task map and track pending interrupts for input-required decisions without leaking AG-UI-only identifiers to A2A.
- Goal: combine the minimal interrupt/resume flow (no frontend tools) with richer Activity/State projection for timeline and pending approvals.

## Decision

- **A2A input expression**: On pause, the agent sets `TaskState.input_required` and emits a status message containing a `TextPart` (user-facing explanation) plus a `DataPart { type: "a2a.input.request", requestId?, title?, description?, fields?, metadata? }`. Resumption arrives as a follow-up message on the same `taskId` with `DataPart { type: "a2a.input.response", requestId?, values, metadata? }`, which the agent validates before moving to `working` and then `succeeded`/`failed`.
- **AG-UI bridge behavior**: Maintain `{ threadId, runId } -> taskId` mapping. When `input_required` appears, stop streaming for the run, emit the final assistant `MESSAGE_CREATED` with the explanatory text, then emit `RUN_FINISHED` with `outcome: "interrupt"` and payload `{ taskId, contextId, interruptId, request }` where `interruptId = "input-<taskId>-<n>"` (per-task monotonic counter). To resume, the frontend opens a new run in the same `threadId` with `resume { interruptId, payload }`; the bridge sends `a2a.input.response` to the original `taskId`, does not reopen the prior run, and continues normal streaming to completion.
- **Activity timeline**: On interrupt, emit `ACTIVITY_SNAPSHOT` with `activityType: "INPUT_REQUEST"`, `messageId = interruptId`, and `content` carrying `stage: "awaiting_input"`, `taskId`, the request schema/payload, and the explanation text. On resume or completion, emit `ACTIVITY_DELTA` JSON Patch updates for `stage` (`working` -> `completed`), `decision` (`provided`/`approved`/`rejected`), and optional summarized input values so the UI can render pending/approved/rejected inputs.
- **Shared state model**: Maintain `/view/tasks/<taskId>` as the canonical task map (status, lastRunId, lastInterruptId, summaries). Track pending interrupts under `/view/pendingInterrupts` as array/map entries `{ interruptId, taskId, requestId, reason }`. Emit one `STATE_SNAPSHOT` on init and `STATE_DELTA` JSON Patch arrays as tasks and interrupts change (additions, updates, removals).
- **Replay and layering**: Input requests/responses stay in the A2A message timeline for audit; Activity/State emissions are AG-UI projections only. Keep AG-UI identifiers out of A2A payloads and continue streaming normal text/tool output until `input_required` ends the run with `outcome: "interrupt"`; the resumed run carries the remainder of the task.

## Consequences

- Clear interrupt/resume semantics: A2A owns truth; AG-UI presents via interrupt + Activity/State events.
- UI can show both chat bubble (assistant explanation) and “pending approvals” timeline without frontend-specific tools.
- Resumptions stay auditable (messages + Activity/State deltas) and keep task history intact; task map stays stable around a single canonical location.
34 changes: 34 additions & 0 deletions .vibecode/feat-improve-a2a-support/adr/0013-stateless-bridge.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# ADR 0013: Stateless A2A Bridge

**Status**
Accepted

**Date**
2025-12-03

## Context

Recent defects showed the A2A bridge reused client-held tracker state across reconnects and generated local contextIds. This caused repeated interruptIds, missing pending interrupt cleanup, and protocol divergence (server owns contextId/taskId). Prior ADR 0002 mandates reconnects resync from snapshots instead of reviving prior runs; this ADR makes the stateless requirement explicit for the bridge implementation.

## Decision

- Treat each bridge run/connection as stateless: hydrate solely from server-delivered task snapshots + subsequent stream deltas; discard tracker state when the stream ends.
- A2A is the source of truth for contextId and taskId. If the caller supplies a contextId, use it; otherwise omit it and accept the server-generated contextId. Binding happens once per agent instance; the instance keeps that bound threadId/contextId for its lifetime, consistent with AbstractAgent’s contract. To start a new context, construct a new agent instance—no internal multi-context cache.
- For A2A-only scenarios, treat the server contextId as the conversation identifier (`contextId === threadId`) exposed to AG-UI. The caller (UI/orchestrator) persists and reuses that identifier on subsequent calls; the bridge remains stateless in its per-run projection/tracker.
- Derive deterministic interruptIds from server identifiers (taskId + requestId/status messageId) instead of local counters.
- On reconnect/resubscribe, always fetch the task snapshot first, then apply streaming deltas; rebuild pending interrupts and artifacts from snapshot + live events.

## Consequences

- Reconnects and resumes produce consistent projections without cross-run residue.
- Interrupt handling remains deterministic across disconnects; input responses can always match pending entries.
- Bridge behavior stays protocol-compliant: server is the source of truth for contextId/taskId; client never invents them.
- Statelessness is preserved: any persistence of contextId/threadId happens in the caller, not inside the bridge.
- Simpler failure recovery: dropping a connection only requires refetching snapshot + streaming, with no persisted client state to reconcile.

## Notes on first-turn identity

- AG-UI currently treats threadId as required in RunAgentInput, so late-binding threadId to the server contextId requires either deferring threadId assignment for A2A agents or treating the first emitted threadId as authoritative. This is consistent with patterns where the server returns the conversation ID on first call (for example, AGUIChatClient auto-generates and returns thread_id to use on later calls; see Microsoft Learn example¹).

---
¹ https://learn.microsoft.com/zh-cn/python/api/agent-framework-core/agent_framework.ag_ui.aguichatclient?view=agent-framework-python-latest&utm_source=chatgpt.com
Loading