@creatornader/agent-bridge 0.2.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (115) hide show
  1. package/CHANGELOG.md +86 -4
  2. package/README.md +567 -55
  3. package/ROADMAP.md +114 -0
  4. package/SECURITY.md +90 -0
  5. package/SKILL.md +68 -2
  6. package/bin/agent-bridge +2 -2
  7. package/clients/claude-desktop.json +3 -1
  8. package/dist/archive.d.ts +163 -0
  9. package/dist/archive.js +50 -0
  10. package/dist/archive.js.map +1 -0
  11. package/dist/bridge-domain-CBX3IVqC.d.ts +153 -0
  12. package/dist/{bridge-service-CfqMcMAn.d.ts → bridge-service-hX1ZQp4W.d.ts} +15 -5
  13. package/dist/bridge-store-DPVSCwfs.d.ts +102 -0
  14. package/dist/bridge.d.ts +76 -17
  15. package/dist/bridge.js +52 -24
  16. package/dist/chunk-2DFNOLMO.js +618 -0
  17. package/dist/chunk-2DFNOLMO.js.map +1 -0
  18. package/dist/{chunk-KVR4JBRF.js → chunk-2EVLG3YA.js} +2 -2
  19. package/dist/{chunk-KVR4JBRF.js.map → chunk-2EVLG3YA.js.map} +1 -1
  20. package/dist/chunk-45BZGB77.js +709 -0
  21. package/dist/chunk-45BZGB77.js.map +1 -0
  22. package/dist/chunk-4VB44KJU.js +230 -0
  23. package/dist/chunk-4VB44KJU.js.map +1 -0
  24. package/dist/chunk-5GMB3A77.js +1013 -0
  25. package/dist/chunk-5GMB3A77.js.map +1 -0
  26. package/dist/chunk-A6W4QALF.js +1258 -0
  27. package/dist/chunk-A6W4QALF.js.map +1 -0
  28. package/dist/chunk-BZLMF5O5.js +328 -0
  29. package/dist/chunk-BZLMF5O5.js.map +1 -0
  30. package/dist/{chunk-AXL2I7FH.js → chunk-CEXBMOI5.js} +240 -79
  31. package/dist/chunk-CEXBMOI5.js.map +1 -0
  32. package/dist/chunk-GADH244D.js +527 -0
  33. package/dist/chunk-GADH244D.js.map +1 -0
  34. package/dist/chunk-MBPZXTJA.js +2428 -0
  35. package/dist/chunk-MBPZXTJA.js.map +1 -0
  36. package/dist/chunk-NCO7RLBM.js +146 -0
  37. package/dist/chunk-NCO7RLBM.js.map +1 -0
  38. package/dist/chunk-Q6PPMXJC.js +1050 -0
  39. package/dist/chunk-Q6PPMXJC.js.map +1 -0
  40. package/dist/{chunk-4KAOBC5X.js → chunk-SPSMZOCJ.js} +190 -165
  41. package/dist/chunk-SPSMZOCJ.js.map +1 -0
  42. package/dist/chunk-UAHMP6GC.js +54 -0
  43. package/dist/chunk-UAHMP6GC.js.map +1 -0
  44. package/dist/{chunk-YD6YJATN.js → chunk-VOQIDTCJ.js} +4 -2
  45. package/dist/chunk-VOQIDTCJ.js.map +1 -0
  46. package/dist/chunk-WAI2IYIQ.js +554 -0
  47. package/dist/chunk-WAI2IYIQ.js.map +1 -0
  48. package/dist/cli.d.ts +2 -1
  49. package/dist/cli.js +3555 -214
  50. package/dist/cli.js.map +1 -1
  51. package/dist/client-config.d.ts +1 -1
  52. package/dist/client-config.js +1 -1
  53. package/dist/client-runtime.d.ts +10 -6
  54. package/dist/client-runtime.js +9 -6
  55. package/dist/contracts/generate.d.ts +2 -0
  56. package/dist/contracts/generate.js +247 -0
  57. package/dist/contracts/generate.js.map +1 -0
  58. package/dist/dr.d.ts +324 -0
  59. package/dist/dr.js +124 -0
  60. package/dist/dr.js.map +1 -0
  61. package/dist/gateway-CggQ8vTC.d.ts +78 -0
  62. package/dist/gateway-main.js +190 -8
  63. package/dist/gateway-main.js.map +1 -1
  64. package/dist/gateway.d.ts +4 -3
  65. package/dist/gateway.js +4 -3
  66. package/dist/index.js +11 -8
  67. package/dist/index.js.map +1 -1
  68. package/dist/postgres-bridge-store-Bfd3hRId.d.ts +49 -0
  69. package/dist/server.d.ts +3 -2
  70. package/dist/server.js +13 -8
  71. package/dist/sqlite.d.ts +94 -12
  72. package/dist/sqlite.js +7 -4
  73. package/docs/architecture-v2.md +394 -0
  74. package/docs/decisions/0001-protocol-layers-and-acknowledgment-semantics.md +85 -0
  75. package/docs/decisions/0002-canonical-operation-contract-registry.md +46 -0
  76. package/docs/decisions/0003-host-adapters-and-consumer-instance-keys.md +88 -0
  77. package/docs/ecosystem.md +86 -0
  78. package/docs/postmortems/2026-07-08-wrapper-source-drift.md +82 -0
  79. package/docs/troubleshooting.md +89 -0
  80. package/llms.txt +107 -6
  81. package/openapi/agent-bridge-v2.openapi.json +9610 -0
  82. package/package.json +24 -4
  83. package/schemas/agent-bridge-v2.capabilities.json +1829 -0
  84. package/schemas/agent-bridge-v2.mcp.json +2074 -0
  85. package/schemas/agent-bridge-v2.schema.json +4560 -0
  86. package/sql/migrations/008_message_projects.sql +36 -0
  87. package/sql/migrations/009_mailbox_query_indexes.sql +28 -0
  88. package/sql/migrations/010_publisher_delivery_policy.sql +210 -0
  89. package/sql/migrations/011_credential_security.sql +875 -0
  90. package/sql/migrations/012_request_authority.sql +177 -0
  91. package/sql/migrations/013_row_isolation.sql +495 -0
  92. package/sql/migrations/014_owner_control_plane.sql +1641 -0
  93. package/sql/migrations/015_portable_archives.sql +1813 -0
  94. package/sql/migrations/016_native_dr_fence.sql +1005 -0
  95. package/bin/CLAUDE.md +0 -11
  96. package/dist/bridge-domain-BcnBeHOn.d.ts +0 -97
  97. package/dist/bridge-store-BuGjxTn4.d.ts +0 -61
  98. package/dist/chunk-4KAOBC5X.js.map +0 -1
  99. package/dist/chunk-6MAZ6YLZ.js +0 -146
  100. package/dist/chunk-6MAZ6YLZ.js.map +0 -1
  101. package/dist/chunk-AXL2I7FH.js.map +0 -1
  102. package/dist/chunk-BV6W4AJK.js +0 -211
  103. package/dist/chunk-BV6W4AJK.js.map +0 -1
  104. package/dist/chunk-CKSKUBDQ.js +0 -153
  105. package/dist/chunk-CKSKUBDQ.js.map +0 -1
  106. package/dist/chunk-JOUBDRYX.js +0 -272
  107. package/dist/chunk-JOUBDRYX.js.map +0 -1
  108. package/dist/chunk-JTC5YURG.js +0 -49
  109. package/dist/chunk-JTC5YURG.js.map +0 -1
  110. package/dist/chunk-O26W5QHS.js +0 -409
  111. package/dist/chunk-O26W5QHS.js.map +0 -1
  112. package/dist/chunk-WEULPMUY.js +0 -988
  113. package/dist/chunk-WEULPMUY.js.map +0 -1
  114. package/dist/chunk-YD6YJATN.js.map +0 -1
  115. package/dist/gateway-BULozv3K.d.ts +0 -54
@@ -0,0 +1,394 @@
1
+ # Agent Bridge v2 architecture
2
+
3
+ Status: living architecture. Version 0.3.0 was released on July 15, 2026.
4
+
5
+ ## Product boundary
6
+
7
+ Agent Bridge is the durable, pull-first mailbox and work-delivery control plane for agents that run in different clients, processes, sessions, and machines. It supports three operating modes: local SQLite, the authenticated PostgreSQL gateway, and the legacy Supabase adapter.
8
+
9
+ History visibility is caller-relative. `inbox` is the default and preserves broadcast-plus-targeted visibility; `sent` is source equal to the caller; `all` is their union. Receipt state (`any`, `unread`, `read`) is valid only for inbox and is always evaluated for the authenticated caller. Opaque v2 cursors bind workspace, caller, mailbox, and normalized filters. Readers temporarily accept v1 sequence cursors but emit only v2. After an edge cache contract upgrade, the gateway resets the authoritative pull cursor and replays `all` visibility; the publication outbox is never treated as sent history.
10
+
11
+ These authorization guarantees apply to local v2 and the authenticated gateway. The legacy Supabase adapter can enforce them only cooperatively because its publishable key can call the underlying PostgREST table and receipt RPC directly.
12
+
13
+ - Local mode runs without an account or network connection.
14
+ - Shared mode uses a remote service so agents on different machines see the same history and delivery state.
15
+
16
+ The protocol must support informational context and executable work without treating them as the same thing. A2A and application task semantics sit above Agent Bridge. MCP, CLI, HTTPS, and the Node library are access surfaces. Agent harnesses and host applications use those surfaces through host adapters or direct integration. Optional transports may sit below the core, but they cannot replace authoritative cursor replay or durable delivery state.
17
+
18
+ [ADR-0001](decisions/0001-protocol-layers-and-acknowledgment-semantics.md) defines these layers and the distinct meanings of receipts, claims, leases, delivery settlement, and external task completion.
19
+
20
+ [ADR-0002](decisions/0002-canonical-operation-contract-registry.md) defines the canonical cross-surface operation contract, deterministic artifacts, capability discovery, and protocol negotiation.
21
+
22
+ [ADR-0003](decisions/0003-host-adapters-and-consumer-instance-keys.md) separates harnesses, hosts, adapters, and access surfaces and defines the consumer instance-key contract.
23
+
24
+ ## Decisions
25
+
26
+ ### PostgreSQL is the shared source of truth
27
+
28
+ PostgreSQL stores shared messages, identities, receipts, deliveries, and migration state. Supabase remains a supported managed PostgreSQL provider, but clients do not depend on Supabase-specific behavior.
29
+
30
+ A standalone Agent Bridge API sits between remote clients and PostgreSQL. Clients authenticate to the API with scoped credentials. They do not receive a database password or rely on a shared Supabase publishable key for identity.
31
+
32
+ Supabase can still host the database. Existing direct PostgREST deployments remain available through an explicitly named legacy adapter while they migrate.
33
+
34
+ ### SQLite is local storage
35
+
36
+ SQLite WAL provides local-only operation, a durable outbox, an inbox cache, cursor state, and a cheap pending-work check. Outbox writes use `synchronous=FULL`. It is not the canonical multi-machine database.
37
+
38
+ The gateway outbox queues immutable message publication. Receipt and lease mutations still require the remote authority because replaying them after ownership or identity changes can settle the wrong work.
39
+
40
+ Long-lived gateway MCP clients own a cancellable transport loop that replays publications and refreshes the inbox cache with bounded exponential backoff. It is transport maintenance, not agent monitoring. Manual MCP and CLI sync use the same replay path. A publication timeout after the gateway may have committed remains queued under its stable idempotency key. A retry resolves to the original immutable message instead of publishing a duplicate.
41
+
42
+ Local initialization must be idempotent. Every connection uses a bounded busy timeout. The minimum supported SQLite build must include the WAL-reset corruption fix. Node 22.23.1 on the current development machine embeds SQLite 3.51.3, which includes that fix.
43
+
44
+ ### Portable archives cross storage engines without copying runtime state
45
+
46
+ A portable archive is a canonical NDJSON snapshot of one workspace's immutable
47
+ messages and read receipts. Export takes a consistent store snapshot. Verification
48
+ checks strict framing, canonical JSON, record order, counts, and the SHA-256 digest.
49
+ Import opens one private, regular archive file without following links. It performs a
50
+ format pass, a message pass, and a receipt pass through the same descriptor. Each pass
51
+ recomputes framing, order, counts, and the client-computed digest. Message and receipt
52
+ batches have row limits and a 4 MiB byte budget. Import runs as a rollback-backed dry
53
+ run unless the operator passes `--apply`; `--dry-run` and `--apply` are mutually
54
+ exclusive. Replaying identical records is idempotent. Conflicting message or receipt
55
+ content fails the import.
56
+
57
+ The portable format excludes deliveries, delivery events, presence, credentials,
58
+ owner-control state, security events, migration records, and edge synchronization
59
+ state. Those records have different authority and recovery semantics. The digest
60
+ detects changed bytes. It provides neither encryption nor authentication, so operators
61
+ must protect the archive as message data and use a separate trusted transfer channel.
62
+
63
+ Archive administration stays outside MCP, HTTPS, and the canonical operation registry.
64
+ Local commands open only a canonical local Agent Bridge database. PostgreSQL commands
65
+ use a separate restricted archive login supplied only through
66
+ `AGENT_BRIDGE_ARCHIVE_DATABASE_URL`. Export holds one store snapshot while it streams
67
+ canonical records and an incremental digest to a private temporary file. It fsyncs the
68
+ file, publishes without replacement unless `--force` is explicit, verifies the private
69
+ path, and fsyncs the directory. Only then does it complete the database audit. A
70
+ publication failure abandons the operation with a bounded code. The footer digest is
71
+ client-verified and caller-attested. It is not independent server proof.
72
+
73
+ Every export has a caller-visible request ID. The canonical header stores it as
74
+ `exportRequestId`, and the digest binds that header to the archive. Verification and
75
+ import return the same provenance. The request ID for an import operation remains a
76
+ separate destination-side identifier. An exact completed export retry verifies the
77
+ private file against the terminal export request ID, workspace, digest, and counts. A
78
+ retry of a started export never streams a new snapshot or force-replaces the file. It
79
+ verifies the retained file and uses the separate reconciliation operation, or leaves
80
+ the request started when the file is missing or mismatched. Temporary and backup paths
81
+ are deterministic same-directory names derived from `exportRequestId`. Force
82
+ replacement first makes a durable private hard-link backup. A pre-durable failure
83
+ restores and fsyncs the original before abandonment. An unproved restore, retained
84
+ artifact, or lost audit response returns its path and state without claiming a clean
85
+ failure. Reconciliation proves durability through the file descriptor that performed
86
+ verification and checks pathname identity around file and directory synchronization.
87
+
88
+ Portable v1 carries only records that satisfy the current message domain. New API
89
+ writes use lowercase UUIDs. Archive UUIDs must be lowercase, and archive timestamps
90
+ use UTC with six fractional digits. The archive path does not trim, deduplicate,
91
+ default, or rewrite message content. A legacy or direct database row outside this
92
+ domain fails export and requires native database recovery.
93
+
94
+ ### Native DR preserves one storage authority
95
+
96
+ Native disaster recovery is separate from portable workspace archives. A native DR
97
+ bundle preserves one full local SQLite authority or the shared PostgreSQL authority in
98
+ its native schema. It does not translate between providers. Gateway edge SQLite files
99
+ are excluded because they contain a replayable outbox and cache, not the shared source
100
+ of truth.
101
+
102
+ The common framed format has a canonical manifest, provider-specific entries, and
103
+ per-entry SHA-256 hashes. Commands calculate and return a separate whole-bundle hash.
104
+ Readers validate strict framing, size bounds, entry order, provider kind, and exact
105
+ schema metadata. The hashes detect changed bytes but do not authenticate or encrypt
106
+ the backup. Paths must pass the private-path
107
+ policy. Publication uses an exclusive adjacent file, file synchronization, a no-replace
108
+ hard link, post-publication verification, and directory synchronization where the
109
+ platform supports it. Deterministic stage names include the operation UUID. Existing
110
+ stages fail closed and are returned as recovery paths. Node cannot prove parent-directory
111
+ entry synchronization on Windows. Successful Windows results report that directory
112
+ durability as unavailable while still requiring file-content synchronization and
113
+ post-publication verification.
114
+
115
+ Local backup accepts only a current or upgradeable local-authority SQLite database. It
116
+ uses the online backup API in a worker with a hard deadline, then verifies health and
117
+ the exact schema contract before framing the snapshot. Restore extracts through one
118
+ exclusive descriptor, verifies the contract, and publishes only to a new target. Edge
119
+ cache and legacy edge files are rejected. Worker termination that cannot be proved is
120
+ reported with the possible residue instead of being treated as a clean timeout.
121
+
122
+ PostgreSQL backup takes a repeatable-read exported snapshot while holding the native DR
123
+ advisory fence. `pg_dump` captures only the `agent_bridge` schema. A separate canonical
124
+ role inventory records the bounded role shells, membership options, and global or
125
+ schema-scoped default ACLs that a schema-only dump omits. The dump excludes data for
126
+ agent instances, rate-limit buckets, request authorities, and archive transaction
127
+ authorizations. The manifest binds migration inventory, table counts, claimed-delivery
128
+ count, tool major, role inventory, and the security, row-isolation, owner-control, and
129
+ portable-archive readiness definitions.
130
+
131
+ PostgreSQL restore requires a dedicated fresh database, the same database name and
132
+ server major, an exact-major `pg_restore`, and superuser authority. It restores role
133
+ shells as `NOLOGIN`, then schema objects, memberships, and default privileges. Claimed
134
+ deliveries become retrying deliveries with cleared leases. The restore checks migrations,
135
+ counts, role inventory, and every readiness attestation before success. External
136
+ principals remain `NOLOGIN`. A failure after target mutation begins disables new target
137
+ connections. If that offlining step fails, the command reports the target as unsafe and
138
+ lists residual role shells.
139
+
140
+ PostgreSQL source and target URLs come only from
141
+ `AGENT_BRIDGE_DR_SOURCE_DATABASE_URL` and
142
+ `AGENT_BRIDGE_DR_TARGET_DATABASE_URL`. Restore requires explicit acceptance that a
143
+ trusted PostgreSQL dump contains executable SQL. Source and restored target must never
144
+ run concurrently as authorities. Native DR supports PostgreSQL 15 through 18
145
+ and rejects a tool or server major mismatch.
146
+
147
+ ### Messages are immutable
148
+
149
+ The protocol stores message content and routing fields in an immutable message record. Read and execution state live in separate tables.
150
+
151
+ Each message has:
152
+
153
+ - A client-generated UUIDv7 ID.
154
+ - A backend-assigned sequence number for the authoritative shared cursor.
155
+ - A workspace and source principal.
156
+ - A display source for v1 compatibility.
157
+ - Category, kind, and priority.
158
+ - Target principals or broadcast scope.
159
+ - Thread, reply, correlation, and causation IDs.
160
+ - Content and optional structured or referenced payload data.
161
+ - Creation and expiry timestamps.
162
+ - An optional idempotency key.
163
+ - An optional validated project label that is immutable with the message. It does not affect workspace, identity, delivery, or cursor authority.
164
+ - Optional atrib receipt and informed-by references.
165
+
166
+ Frequently queried fields are columns with indexes. Extension data remains JSON.
167
+
168
+ The v2 envelope uses fields that can be mapped to CloudEvents without changing stored message identity or payload data. Agent Bridge fields such as workspace, targets, thread, priority, expiry, and causal references remain protocol-specific extensions. A concrete CloudEvents serializer is deferred until an external integration needs one.
169
+
170
+ ### Receipts and deliveries are different records
171
+
172
+ A receipt records that a principal read a message.
173
+
174
+ A publisher stores an immutable `deliveryPolicy` with each message. Untargeted messages default to mailbox mode. Targeted messages default to leased mode with `maxAttempts: 5`, `retryBaseDelayMs: 1000`, `retryMaxDelayMs: 60000`, and `retryJitterRatio: 0.2`. Leased policy may also set `notBefore`. Mailbox messages create no deliveries, and legacy mode rejects leased policy.
175
+
176
+ A leased delivery records executable work for a recipient. Its state is one of `pending`, `claimed`, `acked`, `retrying`, `dead`, or `cancelled`. `attempt` is lifetime-monotonic, while `cycleAttempt` resets on publisher requeue and `requeueCount` increments. Stored policy owns retry and exhaustion. Claims order due work by urgent, high, then info priority, followed by availability, message creation time, and the stable delivery ID tie-break.
177
+
178
+ Consumer-side `maxAttempts` on claim and `retryPolicy` on nack are compatibility inputs for one release. The service validates them and ignores them. This keeps older clients working without letting a consumer change immutable publisher intent.
179
+
180
+ Migration 010 uses `nack_retry` as a conservative compatibility mapping for a legacy `claimed` to `retrying` event. In v0.2 the same stored transition could come from an explicit nack or lease-expiry recovery, so the migrated action describes the retry transition and does not prove which cause produced it. When a legacy settlement event has no lease owner, migration recovers its actor from the preceding claim for the same delivery attempt.
181
+
182
+ A receipt does not change delivery state. A claim or settlement does not create a receipt. Lease renewal proves only that the current owner retained its claim. External task completion belongs to A2A or the application layer and must be recorded separately when a workflow needs both task state and delivery settlement.
183
+
184
+ Each delivery transition also appends an audit event. Instance presence uses a separate leased record keyed by workspace, principal, and caller-supplied instance key. Presence requires that key and can carry a host or harness type and declared capabilities. Supported installers generate and persist a stable key for each installed client. Direct clients that use presence must provide a stable key for each intended consumer. Processes that share a key also share its presence row. The gateway does not bind the key to an installer registration, and the row does not identify a unique live process or PID. Expired rows are pruned during normal presence operations. Each principal is limited to 128 active instance keys, and each workspace is limited to 4,096.
185
+
186
+ The system promises at-least-once delivery. It does not promise exactly-once execution. Idempotency keys prevent duplicate insertion, and consumers must make side effects idempotent.
187
+
188
+ Project participates in the idempotency fingerprint. Reads without a project filter span labeled and unlabeled messages inside the credential-bound workspace. An exact filter narrows that same cursor authority. Migration 008 adds the PostgreSQL column and index. Existing local and edge SQLite databases add their project columns during initialization. Migration 006 is not rewritten. A schema-owner command dry-runs or reconciles its rows into workspace `agent-bridge` in one transaction. It preserves IDs, timestamps, receipts, and row counts, and it verifies that no delivery exists before or after the change.
189
+
190
+ The legacy Supabase schema has no tenant workspace column. Its adapter reports workspace `*` and uses the legacy `project` column only as a message label. It rejects per-command workspace overrides because assigning a caller-selected workspace to global rows would create a false isolation boundary.
191
+
192
+ The delivery API supports:
193
+
194
+ - Atomic claim with row locking.
195
+ - Publisher-only cancel and dead/cancelled requeue, both fencing prior leases. Publisher and recipient can page deliveries and audit events; unrelated callers learn no existence information.
196
+ - Lease renewal for long-running work.
197
+ - Acknowledgment after successful processing.
198
+ - Negative acknowledgment with a bounded error message.
199
+ - Retry scheduling with exponential backoff and jitter.
200
+ - Maximum attempts and dead-letter state.
201
+ - Recovery of expired leases.
202
+
203
+ PostgreSQL claims use `FOR UPDATE SKIP LOCKED` so workers do not wait on rows that another worker already owns. SQLite uses a short immediate transaction to provide the same store contract on one machine.
204
+
205
+ ### Cursor pull is authoritative
206
+
207
+ Reads use an opaque cursor backed by the backend sequence number. UUIDv7 improves index locality and offline identity, but it does not replace the server cursor because client clocks can drift. Filters run in the storage layer. Clients never fetch an arbitrary recent sample and filter it in memory.
208
+
209
+ During gateway outages, normal inbox and pending reads return the locally cached candidate set with explicit degraded and stale metadata. Because receipt state is remote authority and is not mirrored, an offline `unacknowledgedBy` result reports acknowledgement state as unknown; it must not be interpreted as proof that every returned message is unread.
210
+
211
+ Realtime, hooks, webhooks, and desktop notifications may wake a client. The client still pulls from its last durable cursor. This closes notification gaps and makes reconnect behavior testable.
212
+
213
+ Supabase documents that Realtime does not guarantee every database change. Agent Bridge therefore treats it as an optional delivery hint, not a message log.
214
+
215
+ ### Identity is bound at the client boundary
216
+
217
+ The identity model separates:
218
+
219
+ - Workspace.
220
+ - Agent principal.
221
+ - Human-readable name.
222
+ - Host adapter and harness type.
223
+ - Consumer instance key.
224
+ - Live process identity, which the released protocol does not represent separately.
225
+ - Session or thread context.
226
+ - Optional role.
227
+ - Authentication subject and credential.
228
+
229
+ Remote credentials bind the workspace and principal. Each installed client gets a separate owner-only backend file containing only its bound token and backend settings. The shared config does not hold a gateway token. A client cannot claim another source label. Local mode binds identity through its process configuration. `AGENT_BRIDGE_AGENT` remains the compatibility field for MCP and CLI clients.
230
+
231
+ `AGENT_BRIDGE_INSTANCE` is an optional caller-supplied stable consumer key. Unless
232
+ `AGENT_BRIDGE_CURSOR` sets an explicit path, the instance key selects cursor storage.
233
+ The key also selects delivery lease ownership and instance presence. Supported
234
+ installers generate and persist it; direct clients may manage it themselves. When the
235
+ key and explicit cursor path are both omitted, cursor storage uses the `default` path
236
+ component. Delivery lease ownership falls back to the principal, and presence is
237
+ unavailable. The gateway does not treat the key as registered authority. It is not a
238
+ PID, conversation, or session identifier. A future per-process presence feature must
239
+ add a separate identity instead of changing the meaning of `instance` and invalidating
240
+ existing cursor or lease ownership.
241
+
242
+ ### The core is provider-neutral
243
+
244
+ Protocol logic depends on a `BridgeStore` interface, not a provider SDK. The initial stores are:
245
+
246
+ - SQLite for local mode and edge state.
247
+ - PostgreSQL for a standalone shared deployment.
248
+ - Supabase legacy REST for v1 compatibility during migration.
249
+ - Agent Bridge HTTP API for normal remote clients.
250
+
251
+ Host and wakeup adapters are independent from storage. A client manifest declares identity injection, supported wake modes, config locations, health checks, and install actions for Codex, Claude Code, Claude Desktop, OpenClaw, or a generic MCP client. The v1 manifest key named `runtime` identifies an installation target for compatibility. It does not identify a unique running process. The `codex` adapter configures the profile shared by the Codex CLI and the Codex surface in the ChatGPT desktop app. Scripts, CI jobs, daemons, and application services may instead call the CLI or HTTPS API or embed the Node library without pretending to be a harness adapter.
252
+
253
+ ### v1 compatibility is additive
254
+
255
+ The MCP tools `post_context`, `get_context`, and `ack_context` remain available. Existing CLI verbs and flags remain accepted. Text responses remain present while v2 adds structured result data.
256
+
257
+ Legacy Supabase rows remain readable as broadcast messages. A migration command checks schema state, backfills v2 records, and verifies counts before activation. Secure shared mode cannot silently inherit the old permissive RLS policies.
258
+
259
+ ## Security boundary
260
+
261
+ Shared mode uses a token-authenticated API. The service stores only token hashes. Credentials bind a workspace and principal, carry canonical operation scopes, can expire, and can be revoked. Capabilities requires an active credential but no named scope. Local and legacy providers do not claim gateway scope enforcement.
262
+
263
+ Migration 011 grants every pre-migration credential the full compatibility scope set, including expired and revoked rows, without rewriting lifecycle metadata. Migration 014 preserves those rows but changes new raw inserts to empty scopes. Owner provisioning selects an immutable named scope set. Database constraints reject unknown, duplicated, or unsorted scopes.
264
+
265
+ A successor holds an immutable link to one predecessor in the same workspace and principal. Revocation and ordinary expiry always win. A grace cutoff applies only after a successor exists and can only shorten the predecessor's remaining lifetime. Narrow owner functions commit replacement or revocation with their security event. Expected replacement failures that identify a predecessor also produce a fixed failure event.
266
+
267
+ Schema migration and provisioning use a database-owner connection. The running gateway uses a different login that inherits a database-specific runtime role. The role name includes a digest of the database name so permission does not bleed between Agent Bridge databases on one PostgreSQL cluster. That role can read runtime state, insert immutable messages and receipts, transition deliveries, append delivery events, and maintain leased presence. It cannot change workspaces, agents, credentials, message content, schema objects, or migration records. Gateway startup does not run migrations.
268
+
269
+ Migration 014 adds a separate offline owner control plane. Database-specific no-login control-owner, operator, and auditor roles have no superuser or RLS bypass powers. The control owner owns append-only request, event, membership, and catalog-attestation records. It can select only the non-secret credential columns used by its security-definer functions. Operators can provision several principals in a name-compatible workspace, rotate credentials, revoke credentials, and read safe inventory. Auditors can only read inventory.
270
+
271
+ The owner CLI connects only through `AGENT_BRIDGE_OPERATOR_DATABASE_URL` and remains
272
+ outside MCP, HTTP, and the versioned operation registry. Provision and rotation write
273
+ a revisioned enrollment file with exact gateway, workspace, principal, runtime, and
274
+ instance inputs before any database call. The file moves through `pending`, `ready`,
275
+ `consuming`, and `consumed` states under one exclusive per-file operation lock. Every
276
+ transition compares the current disk revision, state, request, operation, and token
277
+ before durable atomic replacement. Stale lock recovery is explicit and requires
278
+ same-host ownership, a minimum age, and proof that the recorded process has stopped.
279
+ Only the local process computes the token hash. PostgreSQL never receives the raw
280
+ token. The installer registers a provisioned client once. Provision stores nonsecret
281
+ credential, principal, and instance metadata in the client backend. Rotation requires
282
+ that metadata plus an exact live host registration before it replaces the token. An
283
+ opaque inventory cursor includes its workspace authority so it cannot be reused under
284
+ another filter.
285
+
286
+ Enrollment deletion and lock release report durability separately. A failure before
287
+ unlink leaves a retained consumed file. A failure after unlink reports unknown directory
288
+ durability and never reports a retained path. On Windows, enrollment roots, every path
289
+ component, enrollment files, temporary files, locks, and credential backends must be
290
+ owned by the current account SID or the active token's default owner SID before the
291
+ process applies policy. The process rejects every other owner, sets the account SID as
292
+ owner, and verifies one protected account-SID FullControl rule. Verification-only paths
293
+ must already satisfy that final policy. Node identity and file-type checks run before
294
+ and after the native policy check. Native reparse attributes
295
+ reject symlinks, junctions, and other reparse objects before and after DACL work.
296
+
297
+ The schema owner is the trusted offline role administrator. It holds all three control roles with the admin option and registers eligible login roles through `register_control_member`; `revoke_control_member` removes them. Those functions are not available through HTTP or MCP. Each call is idempotent by request UUID and appends the database session actor to the membership ledger. Runtime readiness compares the active registry with the full `pg_has_role` closure and the direct `pg_auth_members` edges. It rejects unregistered operator or auditor holders, every external owner holder, missing registered grants, unsafe login attributes, extra roles inherited by a registered member, and roles that inherit a registered member. Direct edges must retain their expected grantor, admin, inherit, and set options. Only the schema owner is exempt from the broad closure check because a PostgreSQL superuser reports membership across roles. A direct `GRANT` is never a valid deployment shortcut.
298
+
299
+ Every protected operation first locks a member-global key for `session_user`, followed by capability keys in operator-before-auditor order. It then checks the active registry, direct membership edge, safe login attributes, and complete upstream and downstream role closure. Inventory accepts either registered capability. Registration and revocation use the same global-first lock hierarchy. Opposite operator and auditor changes for one member therefore cannot deadlock. A revocation waits for an operation that already passed authorization, while every operation begun after revocation observes the new state. A session that remains in a revoked role through `SET ROLE` cannot use that stale authorization.
300
+
301
+ Every mutation then locks its request UUID before reading the request ledger or lifecycle rows. The ledger stores a SHA-256 fingerprint of the canonical request. Timestamp inputs use epoch microseconds in that fingerprint, so session time zones cannot change replay identity. Identical concurrent calls return the first committed result, including a provisioning replay after its requested expiry. Changed content under the same UUID fails. Rotation accepts credentials without an expiry. A null grace cutoff invalidates the predecessor as soon as the successor exists. Audit actors come from `session_user`; results, audit rows, and sanitized constraint errors exclude credential digest material. Text fields at the control boundary reject surrounding whitespace, control characters, and values over 128 characters.
302
+
303
+ Rotation includes the expected workspace and principal in its request fingerprint. The
304
+ function checks both against the predecessor while holding its row lock. Its initial
305
+ and replay results return the canonical workspace and principal from the stored
306
+ request result. The owner CLI uses that result directly instead of performing a
307
+ bounded inventory lookup after rotation.
308
+
309
+ Inventory orders credentials by millisecond-normalized creation time and credential ID. Callers may filter by workspace, continue after a `(created_at, credential_id)` cursor, and request up to 1,000 rows. The default page contains 100 rows. Fixed-origin `date_bin` expression indexes cover both global and workspace ordering. The millisecond normalization matches the timestamp precision preserved by common PostgreSQL JavaScript clients.
310
+
311
+ Before recording an attestation, the migration restores the canonical row-isolation helper functions and verifies the existing row-isolation attestation. An independent credential-security catalog checks exact relation, column, constraint, index, trigger, and function definitions against a separately certified digest for PostgreSQL 15, 16, 17, and 18. An unknown future major has no expected digest and fails closed. Runtime readiness repeats the explicit major-version gate on every call. An already-migrated database therefore becomes unavailable after an upgrade to an uncertified PostgreSQL major. The migration also checks relation ownership, RLS state, protected ACLs, default privileges, and control-role ownership. It refuses to establish a healthy baseline over dependency drift, privilege paths to untrusted roles, or objects unexpectedly owned by a control role.
312
+
313
+ The stored definition is scoped to the control plane and its dependencies. It includes the live credential-security prerequisite catalog, protected function bodies and owners, trigger and index definitions, column types and defaults, RLS and persistence flags, role attributes, relevant default privileges, and the allowed schema, relation, column, sequence, and function grants. Runtime readiness compares the live definition with that record and checks registered membership separately. It fails when a protected function body or same-name constraint changes, a ledger trigger is disabled, a credential default drifts, a control role inherits a data role, or an untrusted role receives schema access, data access, function execution, or a future-object default grant. An unrelated owner-only table remains outside the definition and does not cause a false readiness failure.
314
+
315
+ Migration 012 removes direct runtime reads of credential digest material and establishes transaction-bound request authority. Node computes the credential hash, so PostgreSQL never receives the raw bearer credential. Request authority, security accounting, and domain work use one checked-out PostgreSQL client and one explicit transaction. A security-definer opener matches the credential ID and hash against current revocation, expiry, successor-grace, agent, and workspace state in one locked statement, then records the database-derived credential, workspace, principal, and scopes for that backend and transaction. Instance remains validated same-principal attribution and never establishes authority. Claim, cancel, and requeue reuse the outer transaction without nested `BEGIN`.
316
+
317
+ The domain savepoint separates expected rejected domain work from security effects: expected scope and rate accounting can commit while failed domain mutations are rolled back. An abort before commit rolls the whole request back. Once commit dispatch begins, the gateway never retries and reports `mutation_outcome_unknown` if the outcome is ambiguous. A failed rollback discards the pooled connection.
318
+
319
+ Migration 013 enables and forces RLS on the five domain tables: messages, receipts, deliveries, delivery events, and agent instances. A database-specific no-login role owns those tables. Two more no-login roles isolate request-context reads and delivery-event writes. The gateway login inherits only the runtime role, which has `NOBYPASSRLS` and cannot inherit any of the owner roles. Zero-argument stable context functions read the workspace and principal from the authority row bound to the current backend, transaction ID, and session user. Policies call those functions through scalar subqueries so PostgreSQL evaluates them as InitPlans rather than once per row.
320
+
321
+ Message policies allow the source, broadcast readers, and named targets. Receipt rows belong to one principal. Delivery and event visibility is limited to the publisher or recipient. Presence is visible across one workspace, while writes belong to the active principal. A delivery actor trigger blocks publishers from claim or settlement actions and blocks recipients from cancel or requeue. It does not validate every lifecycle transition or lease token. Those checks remain in the service. Target-to-delivery membership also remains application-enforced; the database proves the delivery's message and publisher, not that its recipient appears in the target array.
322
+
323
+ The migration stores the server's own deparsed catalog definition after creating the protected objects. Runtime readiness compares current policies, constraints, trigger bindings, and security functions with that baseline. This avoids a PostgreSQL-version-specific source hash. The runtime can read the attestation but cannot change it. Owner-approved schema changes must replace the baseline in the same migration. The gateway coalesces readiness probes, caches their result for one second, and runs them through a separate one-connection pool. A capabilities request therefore never waits for a pool slot that its request-authority transaction already holds. Capabilities report row isolation only when request authority exists and every readiness check passes. Superusers and `BYPASSRLS` roles bypass PostgreSQL RLS by design and are forbidden for the gateway login.
324
+
325
+ The database denies direct message updates. Constrained operations handle insertion, receipts, claims, renewals, acknowledgments, and negative acknowledgments. Every query includes workspace scope, and recipient visibility is enforced in storage rather than by client-side filtering.
326
+
327
+ Limits apply to content bytes, payload bytes, metadata depth, target count, batch size, lease duration, and page size. Network calls have connect and total deadlines. Every authenticated operation consumes a database-timed credential-wide bucket and a separate operation bucket. Numeric bucket state, deterministic row locking, and atomic denial events keep concurrent decisions consistent. Missing or disabled policy state fails closed. Errors have stable codes and do not return secrets or raw database messages.
328
+
329
+ ## Operations and observability
330
+
331
+ The CLI provides `init`, `doctor`, `status`, `demo`, `send`, `inbox`, `history`, `claim`, `ack`, `nack`, `watch`, `sync`, portable archive, and migration commands. Existing `post` and `get` aliases remain available.
332
+
333
+ `/readyz` reports storage and schema readiness. Authenticated HTTP status reports the bound principal, provider schema, delivery counts, and the oldest due delivery. CLI `doctor` and `status` use a separate client-status contract that also reports local edge state and remote gateway reachability.
334
+
335
+ Gateway responses carry a request ID, including stable error envelopes. Scope failures return required scopes under `error.details`. Rate failures return a rounded retry delay in both the HTTP header and error details. Authenticated Prometheus output counts requests, errors, timeouts, and authentication failures. Responses, metrics, and append-only security events exclude bearer tokens, hashes, database URLs, payload bodies, arbitrary metadata, and credential material.
336
+
337
+ ## Packaging and release
338
+
339
+ The unscoped npm name `agent-bridge` belongs to another project. This repository uses `@creatornader/agent-bridge`. Tagged builds always produce a package artifact. Publishing stays gated by the protected `npm` environment, npm's OIDC trusted-publisher binding to `release.yml`, and the `NPM_PUBLISH_ENABLED` repository variable.
340
+
341
+ The package must contain built runtime files, migrations, client manifests, maintained
342
+ public documentation, the license, README, roadmap, security policy, and changelog. It
343
+ exports the portable archive API from `@creatornader/agent-bridge/archive`. A clean
344
+ tarball install runs archive and CLI smoke checks in CI. Releases use one version
345
+ source, a tag-to-version check, and npm provenance. The npm environment does not
346
+ currently require a human reviewer, so the release process must not claim a manual
347
+ approval gate.
348
+
349
+ GitHub immutable releases are enabled, and GitHub reports release `v0.3.0` as
350
+ immutable. A tag ruleset also blocks update and deletion of `v*` tags with no bypass
351
+ actor. Future recovery dispatches must run the workflow at the release tag ref so the
352
+ provenance workflow identity and checked-out source commit do not diverge.
353
+
354
+ ## Acceptance checks
355
+
356
+ The TypeBox 1.x registry validates closed requests before domain semantics. Response schemas require known fields and accept additive properties. MCP and HTTP 2.1 claim and delivery-control results use object envelopes with nullable `delivery` fields. HTTP 2.0 and the unversioned CLI keep their released direct or null shapes.
357
+
358
+ Compatibility is asymmetric. An upgraded gateway serves released headerless and explicit 2.0 clients. A new 2.1 client probes before mutation and accepts the gateway only when complete, consistent response headers select 2.1 and advertise 2.1 support. A headerless response, selected 2.0 response, or partial negotiation means the gateway must be upgraded. The client rejects mutation instead of using the d8184fe 2.0 contract. Deploy the gateway before 2.1 clients.
359
+
360
+ The OpenAPI paths describe protocol 2.1. Embedded 2.0 vendor extensions carry only the frozen compatibility schemas and metadata needed to document released clients; they do not form a second OpenAPI description. `npm run contracts:check` proves that JSON Schema 2020-12, OpenAPI 3.1.2, MCP manifest, and capability artifacts match the registry. All schema references are local. Protocol, npm package, MCP implementation, and migration versions are independent. Gateway scope enforcement comes from the registry. Provider-neutral artifacts report enforcement separately for gateway, local, and legacy modes.
361
+
362
+ The v2 implementation is not accepted until all of these checks pass:
363
+
364
+ 1. Unit tests and the production build.
365
+ 2. One store contract suite against SQLite and PostgreSQL.
366
+ 3. Concurrent claim tests that prove one active lease per delivery.
367
+ 4. Crash-after-claim recovery through lease expiry.
368
+ 5. Idempotent insertion and retry tests.
369
+ 6. Cursor replay with equal timestamps and expiry filtering.
370
+ 7. Workspace and recipient isolation tests.
371
+ 8. HTTP timeout, authentication, redaction, and malformed-input tests.
372
+ 9. MCP transport tests for the three v1 tools and all added v2 tools.
373
+ 10. CLI config precedence, identity, local demo, doctor, and package-install tests.
374
+ 11. Upgrade tests from the current `shared_context` schema.
375
+ 12. A two-client local run and a two-client PostgreSQL run.
376
+
377
+ ## Deferred work
378
+
379
+ NATS, JetStream, SLIM, or another transport stays optional until measured throughput, wakeup, queue-group, or geographic requirements justify it. Any transport adapter must preserve cursored replay, idempotency, identity binding, and delivery leases. A dashboard also waits. The CLI and structured health surface come first.
380
+
381
+ End-to-end payload encryption needs a separate threat model and key-management design. The current ciphertext field does not imply that routing metadata is encrypted. OpenClaw and generic MCP config mutation remains operator-managed because their host config formats are not stable enough for blind edits.
382
+
383
+ ## Source notes
384
+
385
+ - agmsg current architecture and implementation: <https://github.com/fujibee/agmsg>
386
+ - SQLite WAL behavior: <https://sqlite.org/wal.html>
387
+ - SQLite WAL-reset bug: <https://sqlite.org/forum/forumpost/0b7fd7f028>
388
+ - PostgreSQL row locking and `SKIP LOCKED`: <https://www.postgresql.org/docs/current/sql-select.html#SQL-FOR-UPDATE-SHARE>
389
+ - Supabase self-hosting responsibilities: <https://supabase.com/docs/guides/self-hosting>
390
+ - Supabase Realtime delivery limitations: <https://supabase.com/docs/reference/self-hosting-realtime>
391
+ - MCP tools specification: <https://modelcontextprotocol.io/specification/2025-11-25/server/tools>
392
+ - MCP transport specification: <https://modelcontextprotocol.io/specification/2025-11-25/basic/transports>
393
+ - UUIDv7: <https://www.rfc-editor.org/rfc/rfc9562.html#name-uuid-version-7>
394
+ - CloudEvents core attributes: <https://github.com/cloudevents/spec/blob/ce%40stable/cloudevents/spec.md#required-attributes>
@@ -0,0 +1,85 @@
1
+ # ADR-0001: Define protocol layers and acknowledgment semantics
2
+
3
+ ## Status
4
+
5
+ Accepted
6
+
7
+ ## Date
8
+
9
+ 2026-07-14
10
+
11
+ ## Context
12
+
13
+ Agent Bridge overlaps with agent protocols, client interfaces, message transports, and task systems. Without a firm boundary, an access adapter can become the protocol, transport state can be mistaken for task state, and different acknowledgments can acquire conflicting meanings.
14
+
15
+ Agent Bridge needs to serve agents across clients, sessions, processes, and machines without owning every protocol above or transport below it.
16
+
17
+ ## Decision
18
+
19
+ Agent Bridge is the durable, pull-first mailbox and work-delivery control plane for intermittently connected agent runtimes.
20
+
21
+ Its protocol owns:
22
+
23
+ - Immutable messages and cursored history.
24
+ - Read receipts.
25
+ - Targeted work deliveries.
26
+ - Claims, leases, retries, settlement, and dead-letter state.
27
+ - Principal-bound identity and workspace isolation.
28
+ - Durable replay after disconnection.
29
+
30
+ The surrounding layers have separate responsibilities:
31
+
32
+ - A2A and application protocols sit above Agent Bridge. They own task, conversation, artifact, and domain lifecycle semantics.
33
+ - MCP, CLI, HTTPS, and the Node library are access surfaces. They expose Agent Bridge operations without defining its stored protocol.
34
+ - Optional transports such as SLIM or NATS may sit below the core as wakeup or transport adapters. They must not replace authoritative cursor replay or durable delivery state.
35
+ - agmsg is a reference for adapters, interoperability, and client experience. Its patterns may inform clients, but it does not define Agent Bridge identity, persistence, or delivery semantics.
36
+
37
+ A2A task IDs or application status may travel through Agent Bridge fields and payloads. Agent Bridge does not infer a task lifecycle from message or delivery state.
38
+
39
+ ## Acknowledgment semantics
40
+
41
+ | Operation | Meaning | State changed | It does not mean |
42
+ | --- | --- | --- | --- |
43
+ | Read receipt | A principal records that it has read or handled a message. | Receipt record | Work ownership, delivery settlement, or task completion |
44
+ | Delivery claim | One principal and instance own a delivery until its lease expires. | Delivery state and lease | The message was read or the work succeeded |
45
+ | Lease extension | The active owner renews its claim. | Lease expiry | Progress, success, or task completion |
46
+ | Delivery acknowledgment | The worker finished processing the delivery and Agent Bridge may stop redelivery. | Delivery becomes `acked` | A read receipt or an external task status |
47
+ | Delivery negative acknowledgment | The worker releases the delivery for retry or marks it dead. | Delivery becomes `retrying` or `dead` | An A2A task failure, cancellation, or rejection |
48
+ | External task completion | An application or A2A task reaches a domain-defined terminal state. | State owned above Agent Bridge | Any automatic receipt or delivery transition |
49
+
50
+ The interfaces use overlapping names, so documentation must identify the surface:
51
+
52
+ - MCP `ack_context` and CLI `acknowledge` write read receipts.
53
+ - MCP `acknowledge` and CLI `ack` settle claimed deliveries.
54
+ - MCP `negative_acknowledge` and CLI `nack` negatively settle claimed deliveries.
55
+ - MCP `extend` and CLI `extend` renew a lease.
56
+
57
+ Read receipts and delivery settlement remain independent in both directions. Recording a receipt does not consume or settle work. Settling a delivery does not create a receipt.
58
+
59
+ ## Consequences
60
+
61
+ Agent Bridge can carry A2A messages without implementing the A2A task state machine. A client that needs both task completion and delivery settlement must record both operations explicitly.
62
+
63
+ MCP remains replaceable by another access surface. A host or transport integration must preserve cursors, idempotency, identity binding, and delivery leases.
64
+
65
+ Push notifications and future brokers can reduce wakeup latency. Pull and replay remain the recovery path.
66
+
67
+ Adapters inspired by agmsg must map into Agent Bridge semantics rather than creating a second receipt, identity, or delivery model.
68
+
69
+ ## Alternatives considered
70
+
71
+ ### Make MCP the Agent Bridge protocol
72
+
73
+ Rejected because MCP is one client interface. CLI, HTTPS, and future clients need the same stored semantics.
74
+
75
+ ### Own the full task lifecycle
76
+
77
+ Rejected because A2A and applications define richer task and artifact state. Duplicating that state would create conflicting authorities.
78
+
79
+ ### Require a broker as the shared authority
80
+
81
+ Rejected for the current architecture. PostgreSQL already provides durable shared state, while cursored pull supports intermittent clients without another required service.
82
+
83
+ ### Adopt agmsg semantics as the core
84
+
85
+ Rejected because Agent Bridge must persist messages across machines, bind principals to credentials, replay from cursors, and manage leased deliveries. agmsg remains useful as an adapter and interface reference.
@@ -0,0 +1,46 @@
1
+ # ADR-0002: Canonical operation contract registry
2
+
3
+ ## Status
4
+
5
+ Accepted
6
+
7
+ ## Date
8
+
9
+ 2026-07-14
10
+
11
+ ## Context
12
+
13
+ Agent Bridge v2 exposed the same operations through MCP, HTTPS, and the CLI, but schemas and surface metadata were maintained separately. That allowed request shapes, capability descriptions, and generated documentation to drift. Package, MCP implementation, protocol, and database migration versions also have different compatibility lifecycles.
14
+
15
+ ## Decision
16
+
17
+ `src/contracts/registry.ts` is the canonical v2 operation registry. Each operation owns its request and response schemas, reserved authorization scope metadata, provider availability, and surface metadata. The generator writes checked-in JSON Schema 2020-12, OpenAPI 3.1.2, MCP manifest, and capability documents from that registry.
18
+
19
+ External requests pass closed structural schema validation before domain validation. Responses require their known fields and accept additive unknown properties. MCP and HTTP 2.1 claim responses use `{ delivery, leaseToken? }`, and delivery controls use `{ delivery }`. HTTP 2.0 and the unversioned CLI retain direct or null delivery results. Public send requests may supply an immutable message `id`, but never a workspace. An optional `source` is an identity assertion and must match the active principal.
20
+
21
+ Authenticated HTTP operations negotiate with `X-Agent-Bridge-Protocol-Version`. Version 2.1 is current. A missing request header selects 2.0, and the gateway accepts exactly 2.0 and 2.1. Other versions return 426 with `unsupported_protocol_version`. An upgraded gateway returns the selected version in `X-Agent-Bridge-Protocol-Version` and its supported versions in `X-Agent-Bridge-Supported-Protocol-Versions`. Gateway errors use one envelope with a stable code and request ID.
22
+
23
+ The HTTP 2.1 client performs and caches a read-only status probe before its first mutation. Negotiation succeeds only when `X-Agent-Bridge-Protocol-Version` equals `2.1` and `X-Agent-Bridge-Supported-Protocol-Versions` includes `2.1`. A headerless response is classified as a released d8184fe 2.0 gateway. A selected 2.0 response, either missing response header, or contradictory values cause the client to reject mutation with a gateway-upgrade error. The client does not downgrade to the 2.0 wire contract.
24
+
25
+ Compatibility runs in the other direction. An upgraded gateway continues to serve released headerless and explicit 2.0 clients with their original message, delivery, and direct or null response shapes. This makes gateway-first deployment mandatory: deploy the upgraded gateway, verify 2.0 clients, and only then install or start 2.1 clients. Capability output reports current, selected, and supported versions separately.
26
+
27
+ The OpenAPI paths require protocol 2.1. The `x-agent-bridge-protocol-2.0` and `x-agent-bridge-schemas-2.0` vendor extensions provide limited metadata and frozen schemas for the released compatibility contract. They do not define a second set of paths or a separate OpenAPI description.
28
+
29
+ Capability documents are filtered by surface and provider. HTTP capability discovery requires a valid credential but has no named scope. The exact scope names are reserved metadata until a later security migration. Gateway credentials grant access as a whole, local mode uses process identity, and legacy mode uses its configured key. Capability documents and OpenAPI set `scopeEnforcement` to false.
30
+
31
+ Package version, MCP implementation version, Agent Bridge protocol version, and migration version remain independent. The MCP server reports the explicit `MCP_IMPLEMENTATION_VERSION` constant instead of the npm package version.
32
+
33
+ ## Consequences
34
+
35
+ - `npm run contracts:generate` updates artifacts and `npm run contracts:check` rejects drift.
36
+ - The npm package includes the generated schema and OpenAPI directories.
37
+ - New v2 operations must be added to the registry before surface wiring.
38
+ - Legacy `post_context`, `get_context`, and `ack_context` remain separate frozen compatibility contracts.
39
+ - Protocol 2.1 deployments upgrade and verify the gateway before upgrading clients.
40
+ - This decision adds no task semantics and requires no database migration.
41
+
42
+ ## Related
43
+
44
+ - [ADR-0001](0001-protocol-layers-and-acknowledgment-semantics.md) defines the protocol layers and acknowledgment meanings.
45
+ - [Architecture v2](../architecture-v2.md) describes the system that follows these decisions.
46
+ - The [README](../../README.md) documents public setup and upgrade behavior.
@@ -0,0 +1,88 @@
1
+ # ADR-0003: Separate host adapters from consumer instance keys
2
+
3
+ ## Status
4
+
5
+ Accepted
6
+
7
+ ## Date
8
+
9
+ 2026-07-15
10
+
11
+ ## Context
12
+
13
+ Agent products often combine an agent harness with a host application. Codex runs in
14
+ the Codex CLI and in the Codex surface of the ChatGPT desktop app. Claude Code and
15
+ Claude Desktop are separate hosts. OpenClaw, Hermes, Pi, scripts, CI jobs, and services
16
+ have different host and access options.
17
+
18
+ The released protocol also uses `AGENT_BRIDGE_INSTANCE` in several places. Supported
19
+ installers generate one value for each installed client, but the gateway accepts the
20
+ value from the caller. Calling it a registered process identity would promise an
21
+ authority check that the server does not perform.
22
+
23
+ ## Decision
24
+
25
+ The integration model separates these concepts:
26
+
27
+ - A harness runs an agent loop.
28
+ - A host is a CLI, desktop application, daemon, job, or service that contains a
29
+ harness or calls Agent Bridge.
30
+ - A host adapter configures a host to use Agent Bridge.
31
+ - MCP over stdio, the CLI, the HTTPS API, and the Node library are access surfaces.
32
+ - Scripts, CI jobs, and services may use an access surface directly without becoming
33
+ agent harnesses.
34
+
35
+ The `codex` adapter configures the profile shared by the Codex CLI and the Codex
36
+ surface in the ChatGPT desktop app. Claude Code and Claude Desktop have separate
37
+ adapters. Their supported installation flows create separate host registrations and
38
+ instance keys. OpenClaw and generic MCP manifests are operator-managed. Hermes, Pi,
39
+ and other harnesses have no dedicated compatibility or conformance claim until an
40
+ adapter is tested.
41
+
42
+ `AGENT_BRIDGE_INSTANCE` is an optional caller-supplied stable consumer key. Supported
43
+ installers generate and persist one for each installed client. Direct clients should
44
+ generate and persist a value when they need separate consumer state or presence. The
45
+ gateway does not bind the value to an installer registration.
46
+
47
+ Unless `AGENT_BRIDGE_CURSOR` sets an explicit path, a present instance key selects
48
+ cursor storage. The key also selects delivery lease ownership and presence. Processes
49
+ that share one key also share those resources. When the key and explicit cursor path
50
+ are both absent, cursor storage uses the `default` path component. Delivery ownership
51
+ falls back to the principal, and presence is unavailable. The key is not a PID,
52
+ live-process identity, session, or conversation. Per-process presence requires a
53
+ separate additive identity.
54
+
55
+ The v1 client manifest key named `runtime` remains a compatibility field for the host
56
+ installation target. A future manifest version may rename it without changing the
57
+ meaning of `AGENT_BRIDGE_INSTANCE`.
58
+
59
+ ## Consequences
60
+
61
+ The architecture diagram and compatibility matrix can list desktop apps, CLIs,
62
+ daemons, and direct callers without presenting them as one category.
63
+
64
+ Installer-generated keys work without a new protocol field. Direct client authors who
65
+ use a key are responsible for stable storage and uniqueness within a principal. Reusing
66
+ a key intentionally creates one shared delivery consumer and presence identity.
67
+
68
+ The current gateway does not prevent two callers from choosing the same key. Clients
69
+ must not treat an instance value as proof of process identity or server registration.
70
+
71
+ ## Alternatives considered
72
+
73
+ ### Treat the adapter name as the harness identity
74
+
75
+ Rejected because one adapter may configure more than one host surface. The `codex`
76
+ adapter is the current example.
77
+
78
+ ### Treat every live process as an instance
79
+
80
+ Rejected because the released instance key already selects cursor paths, lease
81
+ ownership, and presence. Changing it on every process start would alter consumer
82
+ ownership and strand existing state.
83
+
84
+ ### Bind instance keys to server-side registrations now
85
+
86
+ Deferred. Binding could prevent accidental key collisions, but it needs a migration,
87
+ credential rules, and direct-client enrollment semantics. The current gateway accepts
88
+ the caller-supplied key and must be documented honestly.