@creatornader/agent-bridge 0.2.0

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 (74) hide show
  1. package/CHANGELOG.md +60 -0
  2. package/LICENSE +216 -0
  3. package/README.md +338 -0
  4. package/SKILL.md +24 -0
  5. package/bin/CLAUDE.md +11 -0
  6. package/bin/agent-bridge +7 -0
  7. package/clients/claude-code.json +20 -0
  8. package/clients/claude-desktop.json +20 -0
  9. package/clients/codex.json +20 -0
  10. package/clients/generic-mcp.json +13 -0
  11. package/clients/openclaw.json +13 -0
  12. package/dist/bridge-domain-BcnBeHOn.d.ts +97 -0
  13. package/dist/bridge-service-CfqMcMAn.d.ts +28 -0
  14. package/dist/bridge-store-BuGjxTn4.d.ts +61 -0
  15. package/dist/bridge.d.ts +102 -0
  16. package/dist/bridge.js +83 -0
  17. package/dist/bridge.js.map +1 -0
  18. package/dist/chunk-4KAOBC5X.js +773 -0
  19. package/dist/chunk-4KAOBC5X.js.map +1 -0
  20. package/dist/chunk-6MAZ6YLZ.js +146 -0
  21. package/dist/chunk-6MAZ6YLZ.js.map +1 -0
  22. package/dist/chunk-AXL2I7FH.js +423 -0
  23. package/dist/chunk-AXL2I7FH.js.map +1 -0
  24. package/dist/chunk-BV6W4AJK.js +211 -0
  25. package/dist/chunk-BV6W4AJK.js.map +1 -0
  26. package/dist/chunk-CKSKUBDQ.js +153 -0
  27. package/dist/chunk-CKSKUBDQ.js.map +1 -0
  28. package/dist/chunk-JOUBDRYX.js +272 -0
  29. package/dist/chunk-JOUBDRYX.js.map +1 -0
  30. package/dist/chunk-JTC5YURG.js +49 -0
  31. package/dist/chunk-JTC5YURG.js.map +1 -0
  32. package/dist/chunk-KVR4JBRF.js +80 -0
  33. package/dist/chunk-KVR4JBRF.js.map +1 -0
  34. package/dist/chunk-O26W5QHS.js +409 -0
  35. package/dist/chunk-O26W5QHS.js.map +1 -0
  36. package/dist/chunk-WEULPMUY.js +988 -0
  37. package/dist/chunk-WEULPMUY.js.map +1 -0
  38. package/dist/chunk-YD6YJATN.js +48 -0
  39. package/dist/chunk-YD6YJATN.js.map +1 -0
  40. package/dist/cli.d.ts +3 -0
  41. package/dist/cli.js +670 -0
  42. package/dist/cli.js.map +1 -0
  43. package/dist/client-config.d.ts +30 -0
  44. package/dist/client-config.js +10 -0
  45. package/dist/client-config.js.map +1 -0
  46. package/dist/client-runtime.d.ts +15 -0
  47. package/dist/client-runtime.js +15 -0
  48. package/dist/client-runtime.js.map +1 -0
  49. package/dist/gateway-BULozv3K.d.ts +54 -0
  50. package/dist/gateway-main.d.ts +2 -0
  51. package/dist/gateway-main.js +105 -0
  52. package/dist/gateway-main.js.map +1 -0
  53. package/dist/gateway.d.ts +4 -0
  54. package/dist/gateway.js +10 -0
  55. package/dist/gateway.js.map +1 -0
  56. package/dist/index.d.ts +2 -0
  57. package/dist/index.js +18 -0
  58. package/dist/index.js.map +1 -0
  59. package/dist/server.d.ts +34 -0
  60. package/dist/server.js +19 -0
  61. package/dist/server.js.map +1 -0
  62. package/dist/sqlite.d.ts +156 -0
  63. package/dist/sqlite.js +24 -0
  64. package/dist/sqlite.js.map +1 -0
  65. package/llms.txt +39 -0
  66. package/package.json +104 -0
  67. package/sql/migrations/001_schema_state.sql +34 -0
  68. package/sql/migrations/002_workspaces_agents_credentials.sql +77 -0
  69. package/sql/migrations/003_messages_receipts_deliveries.sql +116 -0
  70. package/sql/migrations/004_message_query_indexes.sql +31 -0
  71. package/sql/migrations/005_delivery_events_presence.sql +79 -0
  72. package/sql/migrations/006_legacy_shared_context_import.sql +139 -0
  73. package/sql/migrations/007_runtime_role.sql +59 -0
  74. package/sql/setup.sql +68 -0
@@ -0,0 +1,156 @@
1
+ import { b as BridgeMessage, c as BridgePrincipal, B as BridgeDelivery, D as DeliveryState, R as RetryPolicy, A as AgentPresence, a as BridgeDeliveryEvent } from './bridge-domain-BcnBeHOn.js';
2
+ import { b as MessageQuery, M as MessagePage, B as BridgeDiagnostics, I as InsertMessageResult, a as BridgeStore, C as ClaimOptions } from './bridge-store-BuGjxTn4.js';
3
+
4
+ type PendingMessage = Omit<BridgeMessage, "sequence" | "createdAt">;
5
+ interface EdgeScope {
6
+ endpoint: string;
7
+ principal: BridgePrincipal;
8
+ }
9
+ interface EdgeOutboxRecord {
10
+ position: number;
11
+ draft: PendingMessage;
12
+ payloadHash: string;
13
+ attempts: number;
14
+ leaseToken: string;
15
+ createdAt: string;
16
+ }
17
+ interface EdgeEnqueueResult {
18
+ draft: PendingMessage;
19
+ created: boolean;
20
+ }
21
+ interface EdgeStats {
22
+ pending: number;
23
+ blocked: number;
24
+ cached: number;
25
+ pullCursor?: string;
26
+ lastSyncAt?: string;
27
+ lastError?: string;
28
+ }
29
+ declare class EdgeConflictError extends Error {
30
+ readonly code = "edge_idempotency_conflict";
31
+ }
32
+ declare function edgeMessageFingerprint(message: PendingMessage | BridgeMessage): string;
33
+ declare function stableIdempotency(message: PendingMessage): PendingMessage;
34
+ declare function edgeScopeKey(scope: EdgeScope): string;
35
+ declare class SQLiteEdgeStore {
36
+ private readonly path;
37
+ private readonly scope;
38
+ private readonly busyTimeoutMs;
39
+ private readonly db;
40
+ private readonly key;
41
+ private initialized;
42
+ constructor(path: string, scope: EdgeScope, busyTimeoutMs?: number);
43
+ private restrictFiles;
44
+ get scopeKey(): string;
45
+ initialize(): Promise<void>;
46
+ private ready;
47
+ enqueue(input: PendingMessage, now?: Date): Promise<EdgeEnqueueResult>;
48
+ claimNext(now?: Date, leaseMs?: number): Promise<EdgeOutboxRecord | undefined>;
49
+ retry(record: EdgeOutboxRecord, error: string, availableAt: Date): Promise<void>;
50
+ block(record: EdgeOutboxRecord, error: string): Promise<void>;
51
+ commit(record: EdgeOutboxRecord, message: BridgeMessage, cacheVisible: boolean, now?: Date): Promise<void>;
52
+ private cacheOne;
53
+ cachePage(messages: BridgeMessage[], cursor: string | undefined, now?: Date): Promise<void>;
54
+ cacheLatest(messages: BridgeMessage[], now?: Date): Promise<void>;
55
+ list(query?: MessageQuery): Promise<MessagePage>;
56
+ pullCursor(): Promise<string | undefined>;
57
+ noteError(error: string): Promise<void>;
58
+ stats(): Promise<EdgeStats>;
59
+ close(): Promise<void>;
60
+ }
61
+
62
+ interface SyncInsertResult extends InsertMessageResult {
63
+ disposition: "committed" | "queued";
64
+ authoritative: boolean;
65
+ }
66
+ interface CachedMessagePage extends MessagePage {
67
+ source: "remote" | "cache";
68
+ stale: boolean;
69
+ lastSyncedAt?: string;
70
+ }
71
+ interface SyncReport {
72
+ online: boolean;
73
+ pushed: number;
74
+ deduplicated: number;
75
+ pulled: number;
76
+ pending: number;
77
+ blocked: number;
78
+ cached: number;
79
+ cursor?: string;
80
+ lastSyncedAt?: string;
81
+ lastError?: string;
82
+ failureRetryable?: boolean;
83
+ }
84
+ interface SyncDiagnostics extends BridgeDiagnostics {
85
+ remoteReachable: boolean;
86
+ outboxPending: number;
87
+ outboxBlocked: number;
88
+ cachedMessages: number;
89
+ lastSyncAt?: string;
90
+ lastSyncError?: string;
91
+ }
92
+ interface SyncingBridgeStoreOptions {
93
+ now?: () => Date;
94
+ random?: () => number;
95
+ baseDelayMs?: number;
96
+ maxDelayMs?: number;
97
+ leaseMs?: number;
98
+ }
99
+ declare function isRetryableSyncError(error: unknown): boolean;
100
+ declare class SyncingBridgeStore implements BridgeStore {
101
+ private readonly edge;
102
+ private readonly remote;
103
+ private readonly principal;
104
+ private readonly now;
105
+ private readonly random;
106
+ private readonly baseDelayMs;
107
+ private readonly maxDelayMs;
108
+ private readonly leaseMs;
109
+ private remoteReady;
110
+ constructor(edge: SQLiteEdgeStore, remote: BridgeStore, principal: BridgePrincipal, options?: SyncingBridgeStoreOptions);
111
+ initialize(): Promise<void>;
112
+ verifyRemote(): Promise<void>;
113
+ private ensureRemote;
114
+ private assertPrincipal;
115
+ private delay;
116
+ private flushOne;
117
+ insertMessage(message: PendingMessage): Promise<SyncInsertResult>;
118
+ private pull;
119
+ sync(options?: {
120
+ maxPush?: number;
121
+ maxPages?: number;
122
+ }): Promise<SyncReport>;
123
+ listMessages(principal: BridgePrincipal, query?: MessageQuery): Promise<CachedMessagePage>;
124
+ recordReceipt(workspace: string, messageIds: string[], principal: string, readAt?: Date): Promise<number>;
125
+ claimDelivery(principal: BridgePrincipal, options: ClaimOptions): Promise<BridgeDelivery | null>;
126
+ renewDelivery(principal: BridgePrincipal, deliveryId: string, leaseToken: string, leaseMs: number): Promise<BridgeDelivery | null>;
127
+ settleDelivery(principal: BridgePrincipal, deliveryId: string, leaseToken: string, state: Extract<DeliveryState, "acked" | "retrying" | "dead">, error: string | undefined, retryPolicy: RetryPolicy): Promise<BridgeDelivery | null>;
128
+ diagnostics(principal: BridgePrincipal): Promise<SyncDiagnostics>;
129
+ heartbeat(principal: BridgePrincipal, leaseMs: number, runtimeType?: string, capabilities?: string[]): Promise<AgentPresence>;
130
+ listPresence(principal: BridgePrincipal): Promise<AgentPresence[]>;
131
+ close(): Promise<void>;
132
+ }
133
+
134
+ declare class SQLiteBridgeStore implements BridgeStore {
135
+ private readonly path;
136
+ private readonly busyTimeoutMs;
137
+ private readonly db;
138
+ private initialized;
139
+ constructor(path?: string, busyTimeoutMs?: number);
140
+ private restrictFiles;
141
+ initialize(): Promise<void>;
142
+ private ready;
143
+ insertMessage(input: Omit<BridgeMessage, "sequence" | "createdAt">): Promise<InsertMessageResult>;
144
+ listMessages(principal: BridgePrincipal, query?: MessageQuery): Promise<MessagePage>;
145
+ recordReceipt(workspace: string, messageIds: string[], principal: string, readAt?: Date): Promise<number>;
146
+ claimDelivery(principal: BridgePrincipal, options: ClaimOptions): Promise<BridgeDelivery | null>;
147
+ renewDelivery(principal: BridgePrincipal, id: string, token: string, leaseMs: number): Promise<BridgeDelivery | null>;
148
+ settleDelivery(principal: BridgePrincipal, id: string, token: string, state: "acked" | "retrying" | "dead", error: string | undefined, retryPolicy: RetryPolicy): Promise<BridgeDelivery | null>;
149
+ diagnostics(principal: BridgePrincipal): Promise<BridgeDiagnostics>;
150
+ heartbeat(principal: BridgePrincipal, leaseMs: number, runtimeType?: string, capabilities?: string[]): Promise<AgentPresence>;
151
+ listPresence(principal: BridgePrincipal): Promise<AgentPresence[]>;
152
+ listDeliveryEvents(deliveryId: string): Promise<BridgeDeliveryEvent[]>;
153
+ close(): Promise<void>;
154
+ }
155
+
156
+ export { type CachedMessagePage, EdgeConflictError, type EdgeEnqueueResult, type EdgeOutboxRecord, type EdgeScope, type EdgeStats, type PendingMessage, SQLiteBridgeStore, SQLiteEdgeStore, type SyncDiagnostics, type SyncInsertResult, type SyncReport, SyncingBridgeStore, type SyncingBridgeStoreOptions, edgeMessageFingerprint, edgeScopeKey, isRetryableSyncError, stableIdempotency };
package/dist/sqlite.js ADDED
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ EdgeConflictError,
4
+ SQLiteBridgeStore,
5
+ SQLiteEdgeStore,
6
+ SyncingBridgeStore,
7
+ edgeMessageFingerprint,
8
+ edgeScopeKey,
9
+ isRetryableSyncError,
10
+ stableIdempotency
11
+ } from "./chunk-WEULPMUY.js";
12
+ import "./chunk-YD6YJATN.js";
13
+ import "./chunk-BV6W4AJK.js";
14
+ export {
15
+ EdgeConflictError,
16
+ SQLiteBridgeStore,
17
+ SQLiteEdgeStore,
18
+ SyncingBridgeStore,
19
+ edgeMessageFingerprint,
20
+ edgeScopeKey,
21
+ isRetryableSyncError,
22
+ stableIdempotency
23
+ };
24
+ //# sourceMappingURL=sqlite.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
package/llms.txt ADDED
@@ -0,0 +1,39 @@
1
+ # Agent Bridge
2
+
3
+ Repository: https://github.com/creatornader/agent-bridge
4
+ Package: @creatornader/agent-bridge
5
+
6
+ Agent Bridge carries durable context and executable work between AI agents in different clients, processes, sessions, and machines.
7
+
8
+ ## Modes
9
+
10
+ - local: SQLite WAL, no account or daemon
11
+ - gateway: authenticated HTTPS, PostgreSQL authority, SQLite edge outbox and cache
12
+ - legacy-supabase: compatibility with the v1 shared_context schema
13
+
14
+ ## Commands
15
+
16
+ - agent-bridge init --provider local
17
+ - agent-bridge demo
18
+ - agent-bridge doctor
19
+ - agent-bridge pending
20
+ - agent-bridge send, inbox, history, acknowledge
21
+ - agent-bridge claim, extend, ack, nack
22
+ - agent-bridge sync, watch, join, presence
23
+ - agent-bridge clients install <runtime> --identity <principal>
24
+
25
+ ## MCP tools
26
+
27
+ Compatibility: post_context, get_context, ack_context.
28
+ Reliable messaging: send, history, claim, extend, acknowledge, negative_acknowledge, heartbeat, presence.
29
+
30
+ ## Identity rule
31
+
32
+ The active runtime supplies AGENT_BRIDGE_AGENT and AGENT_BRIDGE_INSTANCE. Do not hardcode another client name and do not store agent identity in the shared backend config. Gateway installations use a separate principal-bound token and owner-only backend file for each client.
33
+
34
+ ## References
35
+
36
+ - README.md: setup, commands, security, and provider behavior
37
+ - docs/architecture-v2.md: protocol, storage, identity, delivery, and acceptance decisions
38
+ - clients/*.json: runtime adapter manifests
39
+ - SKILL.md: concise agent operating instructions
package/package.json ADDED
@@ -0,0 +1,104 @@
1
+ {
2
+ "name": "@creatornader/agent-bridge",
3
+ "version": "0.2.0",
4
+ "description": "Shared context MCP server for heterogeneous AI agent coordination",
5
+ "type": "module",
6
+ "license": "Apache-2.0",
7
+ "author": "Nader Helmy",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/creatornader/agent-bridge.git"
11
+ },
12
+ "homepage": "https://github.com/creatornader/agent-bridge#readme",
13
+ "bugs": {
14
+ "url": "https://github.com/creatornader/agent-bridge/issues"
15
+ },
16
+ "engines": {
17
+ "node": ">=22.23.1"
18
+ },
19
+ "main": "dist/bridge.js",
20
+ "bin": {
21
+ "agent-bridge": "bin/agent-bridge",
22
+ "agent-bridge-mcp": "dist/index.js",
23
+ "agent-bridge-gateway": "dist/gateway-main.js"
24
+ },
25
+ "files": [
26
+ "dist",
27
+ "bin",
28
+ "clients",
29
+ "sql/migrations",
30
+ "sql/setup.sql",
31
+ "llms.txt",
32
+ "SKILL.md",
33
+ "README.md",
34
+ "CHANGELOG.md",
35
+ "LICENSE"
36
+ ],
37
+ "exports": {
38
+ ".": {
39
+ "types": "./dist/bridge.d.ts",
40
+ "import": "./dist/bridge.js"
41
+ },
42
+ "./server": {
43
+ "types": "./dist/server.d.ts",
44
+ "import": "./dist/server.js"
45
+ },
46
+ "./bridge": {
47
+ "types": "./dist/bridge.d.ts",
48
+ "import": "./dist/bridge.js"
49
+ },
50
+ "./gateway": {
51
+ "types": "./dist/gateway.d.ts",
52
+ "import": "./dist/gateway.js"
53
+ },
54
+ "./sqlite": {
55
+ "types": "./dist/sqlite.d.ts",
56
+ "import": "./dist/sqlite.js"
57
+ },
58
+ "./client": {
59
+ "types": "./dist/client-runtime.d.ts",
60
+ "import": "./dist/client-runtime.js"
61
+ },
62
+ "./config": {
63
+ "types": "./dist/client-config.d.ts",
64
+ "import": "./dist/client-config.js"
65
+ }
66
+ },
67
+ "scripts": {
68
+ "build": "tsup",
69
+ "dev": "tsup --watch",
70
+ "start": "node dist/index.js",
71
+ "start:gateway": "node dist/gateway-main.js",
72
+ "migrate": "node dist/gateway-main.js --migrate-only",
73
+ "prepack": "npm run build",
74
+ "pretest": "npm run build",
75
+ "test": "vitest run",
76
+ "typecheck": "tsc --noEmit",
77
+ "check": "npm run typecheck && npm test"
78
+ },
79
+ "dependencies": {
80
+ "@modelcontextprotocol/sdk": "^1.12.1",
81
+ "pg": "^8.22.0"
82
+ },
83
+ "devDependencies": {
84
+ "@types/node": "^26.1.0",
85
+ "@types/pg": "^8.20.0",
86
+ "tsup": "^8.4.0",
87
+ "typescript": "^6.0.3",
88
+ "vitest": "^4.1.9"
89
+ },
90
+ "overrides": {
91
+ "esbuild": "0.28.1"
92
+ },
93
+ "directories": {
94
+ "doc": "docs",
95
+ "test": "test"
96
+ },
97
+ "keywords": [
98
+ "agents",
99
+ "mcp",
100
+ "messaging",
101
+ "coordination"
102
+ ],
103
+ "types": "./dist/bridge.d.ts"
104
+ }
@@ -0,0 +1,34 @@
1
+ begin;
2
+
3
+ select pg_advisory_xact_lock(1646705660);
4
+
5
+ create schema if not exists agent_bridge;
6
+ revoke all on schema agent_bridge from public;
7
+
8
+ create table if not exists agent_bridge.schema_migrations (
9
+ version integer primary key,
10
+ name text not null unique,
11
+ checksum char(64) not null,
12
+ applied_at timestamptz not null default now(),
13
+ constraint schema_migrations_checksum check (checksum ~ '^[0-9a-f]{64}$')
14
+ );
15
+
16
+ insert into agent_bridge.schema_migrations (version, name, checksum)
17
+ values (1, 'schema_state', '__AGENT_BRIDGE_MIGRATION_CHECKSUM__')
18
+ on conflict (version) do update set applied_at=agent_bridge.schema_migrations.applied_at
19
+ where agent_bridge.schema_migrations.name=excluded.name
20
+ and agent_bridge.schema_migrations.checksum=excluded.checksum;
21
+
22
+ do $migration$
23
+ begin
24
+ if not exists (
25
+ select 1 from agent_bridge.schema_migrations
26
+ where version=1 and name='schema_state'
27
+ and checksum='__AGENT_BRIDGE_MIGRATION_CHECKSUM__'
28
+ ) then
29
+ raise exception 'migration 1_schema_state conflicts with recorded schema state';
30
+ end if;
31
+ end
32
+ $migration$;
33
+
34
+ commit;
@@ -0,0 +1,77 @@
1
+ begin;
2
+
3
+ select pg_advisory_xact_lock(1646705660);
4
+
5
+ create table if not exists agent_bridge.workspaces (
6
+ id text primary key,
7
+ name text not null,
8
+ disabled_at timestamptz,
9
+ created_at timestamptz not null default now()
10
+ );
11
+
12
+ create table if not exists agent_bridge.agents (
13
+ id uuid primary key default gen_random_uuid(),
14
+ workspace_id text not null references agent_bridge.workspaces(id),
15
+ principal text not null,
16
+ display_name text,
17
+ runtime_type text,
18
+ disabled_at timestamptz,
19
+ created_at timestamptz not null default now(),
20
+ unique (workspace_id, principal),
21
+ unique (workspace_id, id)
22
+ );
23
+
24
+ create table if not exists agent_bridge.credentials (
25
+ id uuid primary key default gen_random_uuid(),
26
+ workspace_id text not null,
27
+ agent_id uuid not null,
28
+ token_hash char(64) not null unique,
29
+ label text,
30
+ expires_at timestamptz,
31
+ revoked_at timestamptz,
32
+ created_at timestamptz not null default now(),
33
+ last_used_at timestamptz,
34
+ foreign key (workspace_id, agent_id)
35
+ references agent_bridge.agents(workspace_id, id),
36
+ constraint credentials_hash check (token_hash ~ '^[0-9a-f]{64}$')
37
+ );
38
+
39
+ create index if not exists credentials_active_hash
40
+ on agent_bridge.credentials(token_hash)
41
+ where revoked_at is null;
42
+
43
+ revoke all on all tables in schema agent_bridge from public;
44
+ revoke all on all sequences in schema agent_bridge from public;
45
+
46
+ do $roles$
47
+ declare role_name text;
48
+ begin
49
+ foreach role_name in array array['anon', 'authenticated'] loop
50
+ if exists (select 1 from pg_roles where rolname=role_name) then
51
+ execute format('revoke all on schema agent_bridge from %I', role_name);
52
+ execute format('revoke all on all tables in schema agent_bridge from %I', role_name);
53
+ execute format('revoke all on all sequences in schema agent_bridge from %I', role_name);
54
+ end if;
55
+ end loop;
56
+ end
57
+ $roles$;
58
+
59
+ insert into agent_bridge.schema_migrations (version, name, checksum)
60
+ values (2, 'workspaces_agents_credentials', '__AGENT_BRIDGE_MIGRATION_CHECKSUM__')
61
+ on conflict (version) do update set applied_at=agent_bridge.schema_migrations.applied_at
62
+ where agent_bridge.schema_migrations.name=excluded.name
63
+ and agent_bridge.schema_migrations.checksum=excluded.checksum;
64
+
65
+ do $migration$
66
+ begin
67
+ if not exists (
68
+ select 1 from agent_bridge.schema_migrations
69
+ where version=2 and name='workspaces_agents_credentials'
70
+ and checksum='__AGENT_BRIDGE_MIGRATION_CHECKSUM__'
71
+ ) then
72
+ raise exception 'migration 2_workspaces_agents_credentials conflicts with recorded schema state';
73
+ end if;
74
+ end
75
+ $migration$;
76
+
77
+ commit;
@@ -0,0 +1,116 @@
1
+ begin;
2
+
3
+ select pg_advisory_xact_lock(1646705660);
4
+
5
+ create table if not exists agent_bridge.messages (
6
+ sequence bigint generated always as identity primary key,
7
+ id uuid not null unique,
8
+ workspace text not null references agent_bridge.workspaces(id),
9
+ source text not null,
10
+ type text not null,
11
+ content text not null,
12
+ content_type text not null default 'text/plain',
13
+ data jsonb,
14
+ targets jsonb not null default '[]'::jsonb,
15
+ thread_id text,
16
+ reply_to_id text,
17
+ correlation_id text,
18
+ causation_id text,
19
+ priority text not null default 'info',
20
+ expires_at timestamptz,
21
+ idempotency_key text,
22
+ atrib_receipt_id text,
23
+ informed_by jsonb,
24
+ metadata jsonb,
25
+ created_at timestamptz not null default now(),
26
+ unique (workspace, id),
27
+ check (jsonb_typeof(targets)='array'),
28
+ check (priority in ('info','high','urgent'))
29
+ );
30
+ create unique index if not exists messages_idempotency
31
+ on agent_bridge.messages(workspace, source, idempotency_key)
32
+ where idempotency_key is not null;
33
+ create index if not exists messages_cursor on agent_bridge.messages(workspace, sequence);
34
+ create index if not exists messages_expiry on agent_bridge.messages(workspace, expires_at)
35
+ where expires_at is not null;
36
+
37
+ create table if not exists agent_bridge.receipts (
38
+ workspace text not null,
39
+ message_id uuid not null,
40
+ principal text not null,
41
+ read_at timestamptz not null default now(),
42
+ primary key (workspace, message_id, principal),
43
+ foreign key (workspace, message_id)
44
+ references agent_bridge.messages(workspace, id) on delete cascade
45
+ );
46
+
47
+ create table if not exists agent_bridge.deliveries (
48
+ id uuid primary key,
49
+ message_id uuid not null,
50
+ workspace text not null,
51
+ recipient text not null,
52
+ state text not null,
53
+ attempt integer not null default 0,
54
+ available_at timestamptz not null default now(),
55
+ lease_token uuid,
56
+ lease_owner text,
57
+ lease_expires_at timestamptz,
58
+ last_error text,
59
+ unique (message_id, recipient),
60
+ foreign key (workspace, message_id)
61
+ references agent_bridge.messages(workspace, id) on delete cascade,
62
+ check (state in ('pending','claimed','acked','retrying','dead')),
63
+ check (attempt >= 0),
64
+ check ((state='claimed' and lease_token is not null and lease_expires_at is not null)
65
+ or (state<>'claimed' and lease_token is null and lease_owner is null and lease_expires_at is null))
66
+ );
67
+ create index if not exists deliveries_claim
68
+ on agent_bridge.deliveries(workspace, recipient, state, available_at);
69
+
70
+ create or replace function agent_bridge.reject_message_mutation() returns trigger
71
+ language plpgsql set search_path = '' as $$
72
+ begin
73
+ raise exception 'bridge messages are immutable';
74
+ end
75
+ $$;
76
+ drop trigger if exists messages_immutable on agent_bridge.messages;
77
+ create trigger messages_immutable before update or delete on agent_bridge.messages
78
+ for each row execute function agent_bridge.reject_message_mutation();
79
+
80
+ revoke all on all tables in schema agent_bridge from public;
81
+ revoke all on all sequences in schema agent_bridge from public;
82
+ revoke all on all functions in schema agent_bridge from public;
83
+
84
+ do $roles$
85
+ declare role_name text;
86
+ begin
87
+ foreach role_name in array array['anon', 'authenticated'] loop
88
+ if exists (select 1 from pg_roles where rolname=role_name) then
89
+ execute format('revoke all on schema agent_bridge from %I', role_name);
90
+ execute format('revoke all on all tables in schema agent_bridge from %I', role_name);
91
+ execute format('revoke all on all sequences in schema agent_bridge from %I', role_name);
92
+ execute format('revoke all on all functions in schema agent_bridge from %I', role_name);
93
+ end if;
94
+ end loop;
95
+ end
96
+ $roles$;
97
+
98
+ insert into agent_bridge.schema_migrations (version, name, checksum)
99
+ values (3, 'messages_receipts_deliveries', '__AGENT_BRIDGE_MIGRATION_CHECKSUM__')
100
+ on conflict (version) do update set applied_at=agent_bridge.schema_migrations.applied_at
101
+ where agent_bridge.schema_migrations.name=excluded.name
102
+ and agent_bridge.schema_migrations.checksum=excluded.checksum;
103
+
104
+ do $migration$
105
+ begin
106
+ if not exists (
107
+ select 1 from agent_bridge.schema_migrations
108
+ where version=3 and name='messages_receipts_deliveries'
109
+ and checksum='__AGENT_BRIDGE_MIGRATION_CHECKSUM__'
110
+ ) then
111
+ raise exception 'migration 3_messages_receipts_deliveries conflicts with recorded schema state';
112
+ end if;
113
+ end
114
+ $migration$;
115
+
116
+ commit;
@@ -0,0 +1,31 @@
1
+ begin;
2
+
3
+ select pg_advisory_xact_lock(1646705660);
4
+
5
+ create index if not exists messages_source
6
+ on agent_bridge.messages(workspace, source, sequence);
7
+ create index if not exists messages_thread
8
+ on agent_bridge.messages(workspace, thread_id, sequence)
9
+ where thread_id is not null;
10
+ create index if not exists messages_created
11
+ on agent_bridge.messages(workspace, created_at, sequence);
12
+
13
+ insert into agent_bridge.schema_migrations (version, name, checksum)
14
+ values (4, 'message_query_indexes', '__AGENT_BRIDGE_MIGRATION_CHECKSUM__')
15
+ on conflict (version) do update set applied_at=agent_bridge.schema_migrations.applied_at
16
+ where agent_bridge.schema_migrations.name=excluded.name
17
+ and agent_bridge.schema_migrations.checksum=excluded.checksum;
18
+
19
+ do $migration$
20
+ begin
21
+ if not exists (
22
+ select 1 from agent_bridge.schema_migrations
23
+ where version=4 and name='message_query_indexes'
24
+ and checksum='__AGENT_BRIDGE_MIGRATION_CHECKSUM__'
25
+ ) then
26
+ raise exception 'migration 4_message_query_indexes conflicts with recorded schema state';
27
+ end if;
28
+ end
29
+ $migration$;
30
+
31
+ commit;
@@ -0,0 +1,79 @@
1
+ begin;
2
+
3
+ select pg_advisory_xact_lock(1646705660);
4
+
5
+ create table if not exists agent_bridge.delivery_events (
6
+ sequence bigint generated always as identity primary key,
7
+ delivery_id uuid not null,
8
+ message_id uuid not null,
9
+ workspace text not null,
10
+ recipient text not null,
11
+ from_state text,
12
+ to_state text not null,
13
+ attempt integer not null,
14
+ lease_owner text,
15
+ error text,
16
+ created_at timestamptz not null default now()
17
+ );
18
+ create index if not exists delivery_events_lookup
19
+ on agent_bridge.delivery_events(workspace, delivery_id, sequence);
20
+
21
+ create or replace function agent_bridge.record_delivery_event() returns trigger
22
+ language plpgsql set search_path = '' as $$
23
+ begin
24
+ if tg_op = 'INSERT' or old.state is distinct from new.state or old.attempt is distinct from new.attempt then
25
+ insert into agent_bridge.delivery_events (
26
+ delivery_id, message_id, workspace, recipient, from_state, to_state,
27
+ attempt, lease_owner, error
28
+ ) values (
29
+ new.id, new.message_id, new.workspace, new.recipient,
30
+ case when tg_op = 'INSERT' then null else old.state end,
31
+ new.state, new.attempt, new.lease_owner, new.last_error
32
+ );
33
+ end if;
34
+ return new;
35
+ end
36
+ $$;
37
+ drop trigger if exists deliveries_record_event on agent_bridge.deliveries;
38
+ create trigger deliveries_record_event
39
+ after insert or update on agent_bridge.deliveries
40
+ for each row execute function agent_bridge.record_delivery_event();
41
+
42
+ create table if not exists agent_bridge.agent_instances (
43
+ workspace text not null,
44
+ agent text not null,
45
+ instance text not null,
46
+ runtime_type text,
47
+ capabilities jsonb not null default '[]'::jsonb,
48
+ lease_expires_at timestamptz not null,
49
+ last_seen_at timestamptz not null default now(),
50
+ primary key (workspace, agent, instance),
51
+ foreign key (workspace) references agent_bridge.workspaces(id) on delete cascade,
52
+ check (jsonb_typeof(capabilities)='array')
53
+ );
54
+ create index if not exists agent_instances_active
55
+ on agent_bridge.agent_instances(workspace, lease_expires_at);
56
+
57
+ revoke all on all tables in schema agent_bridge from public;
58
+ revoke all on all sequences in schema agent_bridge from public;
59
+ revoke all on all functions in schema agent_bridge from public;
60
+
61
+ insert into agent_bridge.schema_migrations (version, name, checksum)
62
+ values (5, 'delivery_events_presence', '__AGENT_BRIDGE_MIGRATION_CHECKSUM__')
63
+ on conflict (version) do update set applied_at=agent_bridge.schema_migrations.applied_at
64
+ where agent_bridge.schema_migrations.name=excluded.name
65
+ and agent_bridge.schema_migrations.checksum=excluded.checksum;
66
+
67
+ do $migration$
68
+ begin
69
+ if not exists (
70
+ select 1 from agent_bridge.schema_migrations
71
+ where version=5 and name='delivery_events_presence'
72
+ and checksum='__AGENT_BRIDGE_MIGRATION_CHECKSUM__'
73
+ ) then
74
+ raise exception 'migration 5_delivery_events_presence conflicts with recorded schema state';
75
+ end if;
76
+ end
77
+ $migration$;
78
+
79
+ commit;