sealbridge 0.2.2 → 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 (251) hide show
  1. package/README.ja.md +492 -30
  2. package/README.md +522 -109
  3. package/dist/cli/dist/accept.d.ts +13 -0
  4. package/dist/cli/dist/accept.js +115 -0
  5. package/dist/cli/dist/accept.js.map +1 -0
  6. package/dist/cli/dist/approval-summary.d.ts +1 -1
  7. package/dist/cli/dist/board.d.ts +10 -9
  8. package/dist/cli/dist/board.js +62 -31
  9. package/dist/cli/dist/board.js.map +1 -1
  10. package/dist/cli/dist/command-identity.d.ts +6 -0
  11. package/dist/cli/dist/command-identity.js +13 -0
  12. package/dist/cli/dist/command-identity.js.map +1 -0
  13. package/dist/cli/dist/config.d.ts +3 -0
  14. package/dist/cli/dist/config.js +29 -0
  15. package/dist/cli/dist/config.js.map +1 -0
  16. package/dist/cli/dist/connections.d.ts +15 -0
  17. package/dist/cli/dist/connections.js +191 -0
  18. package/dist/cli/dist/connections.js.map +1 -0
  19. package/dist/cli/dist/contacts.d.ts +4 -3
  20. package/dist/cli/dist/contacts.js +39 -28
  21. package/dist/cli/dist/contacts.js.map +1 -1
  22. package/dist/cli/dist/doctor.d.ts +9 -0
  23. package/dist/cli/dist/doctor.js +46 -5
  24. package/dist/cli/dist/doctor.js.map +1 -1
  25. package/dist/cli/dist/error-messages.js +22 -0
  26. package/dist/cli/dist/error-messages.js.map +1 -1
  27. package/dist/cli/dist/index.js +44 -6
  28. package/dist/cli/dist/index.js.map +1 -1
  29. package/dist/cli/dist/invite-output.d.ts +14 -0
  30. package/dist/cli/dist/invite-output.js +66 -0
  31. package/dist/cli/dist/invite-output.js.map +1 -0
  32. package/dist/cli/dist/invite-share.d.ts +6 -3
  33. package/dist/cli/dist/invite-share.js +40 -15
  34. package/dist/cli/dist/invite-share.js.map +1 -1
  35. package/dist/cli/dist/invites-migrate.d.ts +3 -0
  36. package/dist/cli/dist/invites-migrate.js +33 -0
  37. package/dist/cli/dist/invites-migrate.js.map +1 -0
  38. package/dist/cli/dist/mcp.d.ts +7 -5
  39. package/dist/cli/dist/mcp.js +23 -18
  40. package/dist/cli/dist/mcp.js.map +1 -1
  41. package/dist/cli/dist/network.d.ts +11 -1
  42. package/dist/cli/dist/network.js +150 -87
  43. package/dist/cli/dist/network.js.map +1 -1
  44. package/dist/cli/dist/pending.d.ts +8 -0
  45. package/dist/cli/dist/pending.js +50 -0
  46. package/dist/cli/dist/pending.js.map +1 -0
  47. package/dist/cli/dist/public-settings.d.ts +5 -4
  48. package/dist/cli/dist/public-settings.js +13 -15
  49. package/dist/cli/dist/public-settings.js.map +1 -1
  50. package/dist/cli/dist/receipt.d.ts +1 -1
  51. package/dist/cli/dist/relay-auth.d.ts +3 -0
  52. package/dist/cli/dist/relay-auth.js +23 -0
  53. package/dist/cli/dist/relay-auth.js.map +1 -0
  54. package/dist/cli/dist/requests.d.ts +10 -0
  55. package/dist/cli/dist/requests.js +91 -0
  56. package/dist/cli/dist/requests.js.map +1 -0
  57. package/dist/cli/dist/sdk.d.ts +10 -0
  58. package/dist/cli/dist/sdk.js +7 -0
  59. package/dist/cli/dist/sdk.js.map +1 -0
  60. package/dist/cli/dist/setup-files.d.ts +24 -0
  61. package/dist/cli/dist/setup-files.js +190 -0
  62. package/dist/cli/dist/setup-files.js.map +1 -0
  63. package/dist/cli/dist/setup-hosts.d.ts +29 -0
  64. package/dist/cli/dist/setup-hosts.js +75 -0
  65. package/dist/cli/dist/setup-hosts.js.map +1 -0
  66. package/dist/cli/dist/setup.d.ts +61 -0
  67. package/dist/cli/dist/setup.js +243 -0
  68. package/dist/cli/dist/setup.js.map +1 -0
  69. package/dist/cli/dist/task-input.d.ts +1 -1
  70. package/dist/cli/dist/wake-hook.d.ts +1 -1
  71. package/dist/cli/dist/worker-json.d.ts +4 -0
  72. package/dist/cli/dist/worker-json.js +87 -0
  73. package/dist/cli/dist/worker-json.js.map +1 -0
  74. package/dist/examples/two-owner-demo/dist/run-demo.d.ts +13 -13
  75. package/dist/package.json +1 -1
  76. package/dist/packages/adapters/dist/command.d.ts +2 -2
  77. package/dist/packages/adapters/dist/credential-proxy.d.ts +2 -2
  78. package/dist/packages/adapters/dist/index.d.ts +1 -1
  79. package/dist/packages/adapters/dist/mcp.d.ts +2 -2
  80. package/dist/packages/adapters/dist/openai.d.ts +2 -2
  81. package/dist/packages/adapters/dist/sandbox.d.ts +2 -2
  82. package/dist/packages/core/dist/connection.d.ts +58 -0
  83. package/dist/packages/core/dist/connection.js +132 -0
  84. package/dist/packages/core/dist/connection.js.map +1 -0
  85. package/dist/packages/core/dist/delivery-status.d.ts +22 -0
  86. package/dist/packages/core/dist/delivery-status.js +53 -0
  87. package/dist/packages/core/dist/delivery-status.js.map +1 -0
  88. package/dist/packages/core/dist/discovery.d.ts +7 -6
  89. package/dist/packages/core/dist/discovery.js +19 -20
  90. package/dist/packages/core/dist/discovery.js.map +1 -1
  91. package/dist/packages/core/dist/edge.d.ts +1 -0
  92. package/dist/packages/core/dist/edge.js +1 -0
  93. package/dist/packages/core/dist/edge.js.map +1 -1
  94. package/dist/packages/core/dist/index.d.ts +3 -0
  95. package/dist/packages/core/dist/index.js +3 -0
  96. package/dist/packages/core/dist/index.js.map +1 -1
  97. package/dist/packages/core/dist/protocol.d.ts +24 -0
  98. package/dist/packages/core/dist/protocol.js +54 -0
  99. package/dist/packages/core/dist/protocol.js.map +1 -0
  100. package/dist/packages/core/dist/receipts.d.ts +1 -0
  101. package/dist/packages/core/dist/receipts.js +1 -1
  102. package/dist/packages/core/dist/receipts.js.map +1 -1
  103. package/dist/packages/core/dist/schema.d.ts +1 -0
  104. package/dist/packages/core/dist/schema.js +13 -2
  105. package/dist/packages/core/dist/schema.js.map +1 -1
  106. package/dist/packages/ledger/dist/index.d.ts +1 -1
  107. package/dist/packages/relay/dist/admission.d.ts +14 -0
  108. package/dist/packages/relay/dist/admission.js +70 -0
  109. package/dist/packages/relay/dist/admission.js.map +1 -0
  110. package/dist/packages/relay/dist/fetch.d.ts +8 -2
  111. package/dist/packages/relay/dist/fetch.js +41 -12
  112. package/dist/packages/relay/dist/fetch.js.map +1 -1
  113. package/dist/packages/relay/dist/index.d.ts +23 -12
  114. package/dist/packages/relay/dist/index.js +130 -78
  115. package/dist/packages/relay/dist/index.js.map +1 -1
  116. package/dist/packages/relay/dist/journal.d.ts +1 -1
  117. package/dist/packages/relay/dist/notifications.d.ts +1 -1
  118. package/dist/packages/relay/dist/retry.d.ts +23 -0
  119. package/dist/packages/relay/dist/retry.js +43 -1
  120. package/dist/packages/relay/dist/retry.js.map +1 -1
  121. package/dist/packages/relay/dist/ssrf.d.ts +3 -2
  122. package/dist/packages/relay/dist/ssrf.js.map +1 -1
  123. package/dist/packages/relay/dist/worker.js +1 -1
  124. package/dist/packages/relay/dist/worker.js.map +1 -1
  125. package/dist/packages/runtime/dist/a2a.d.ts +1 -1
  126. package/dist/packages/runtime/dist/a2a.js +1 -1
  127. package/dist/packages/runtime/dist/adapter.d.ts +2 -2
  128. package/dist/packages/runtime/dist/application-config.d.ts +20 -0
  129. package/dist/packages/runtime/dist/application-config.js +218 -0
  130. package/dist/packages/runtime/dist/application-config.js.map +1 -0
  131. package/dist/packages/runtime/dist/approval-providers.d.ts +24 -3
  132. package/dist/packages/runtime/dist/approval-providers.js +79 -3
  133. package/dist/packages/runtime/dist/approval-providers.js.map +1 -1
  134. package/dist/packages/runtime/dist/connection-admission-review.d.ts +39 -0
  135. package/dist/packages/runtime/dist/connection-admission-review.js +86 -0
  136. package/dist/packages/runtime/dist/connection-admission-review.js.map +1 -0
  137. package/dist/packages/runtime/dist/connection-persistence.d.ts +35 -0
  138. package/dist/packages/runtime/dist/connection-persistence.js +181 -0
  139. package/dist/packages/runtime/dist/connection-persistence.js.map +1 -0
  140. package/dist/packages/runtime/dist/connection-records.d.ts +167 -0
  141. package/dist/packages/runtime/dist/connection-records.js +132 -0
  142. package/dist/packages/runtime/dist/connection-records.js.map +1 -0
  143. package/dist/packages/runtime/dist/connection-router.d.ts +45 -0
  144. package/dist/packages/runtime/dist/connection-router.js +130 -0
  145. package/dist/packages/runtime/dist/connection-router.js.map +1 -0
  146. package/dist/packages/runtime/dist/connection-service.d.ts +159 -0
  147. package/dist/packages/runtime/dist/connection-service.js +239 -0
  148. package/dist/packages/runtime/dist/connection-service.js.map +1 -0
  149. package/dist/packages/runtime/dist/connection-transport.d.ts +14 -0
  150. package/dist/packages/runtime/dist/connection-transport.js +89 -0
  151. package/dist/packages/runtime/dist/connection-transport.js.map +1 -0
  152. package/dist/packages/runtime/dist/delivery-records.d.ts +180 -0
  153. package/dist/packages/runtime/dist/delivery-records.js +205 -0
  154. package/dist/packages/runtime/dist/delivery-records.js.map +1 -0
  155. package/dist/packages/runtime/dist/delivery-status-router.d.ts +27 -0
  156. package/dist/packages/runtime/dist/delivery-status-router.js +150 -0
  157. package/dist/packages/runtime/dist/delivery-status-router.js.map +1 -0
  158. package/dist/packages/runtime/dist/delivery-status.d.ts +43 -0
  159. package/dist/packages/runtime/dist/delivery-status.js +193 -0
  160. package/dist/packages/runtime/dist/delivery-status.js.map +1 -0
  161. package/dist/packages/runtime/dist/delivery-store.d.ts +125 -0
  162. package/dist/packages/runtime/dist/delivery-store.js +1047 -0
  163. package/dist/packages/runtime/dist/delivery-store.js.map +1 -0
  164. package/dist/packages/runtime/dist/gateway.d.ts +4 -3
  165. package/dist/packages/runtime/dist/gateway.js.map +1 -1
  166. package/dist/packages/runtime/dist/held-request-review.d.ts +49 -0
  167. package/dist/packages/runtime/dist/held-request-review.js +122 -0
  168. package/dist/packages/runtime/dist/held-request-review.js.map +1 -0
  169. package/dist/packages/runtime/dist/held-request-store.d.ts +112 -0
  170. package/dist/packages/runtime/dist/held-request-store.js +290 -0
  171. package/dist/packages/runtime/dist/held-request-store.js.map +1 -0
  172. package/dist/packages/runtime/dist/idempotency-store.d.ts +53 -0
  173. package/dist/packages/runtime/dist/idempotency-store.js +331 -0
  174. package/dist/packages/runtime/dist/idempotency-store.js.map +1 -0
  175. package/dist/packages/runtime/dist/inbox.d.ts +1 -1
  176. package/dist/packages/runtime/dist/index.d.ts +27 -1
  177. package/dist/packages/runtime/dist/index.js +26 -0
  178. package/dist/packages/runtime/dist/index.js.map +1 -1
  179. package/dist/packages/runtime/dist/invite-store.d.ts +76 -0
  180. package/dist/packages/runtime/dist/invite-store.js +337 -0
  181. package/dist/packages/runtime/dist/invite-store.js.map +1 -0
  182. package/dist/packages/runtime/dist/key-store.d.ts +77 -0
  183. package/dist/packages/runtime/dist/key-store.js +226 -0
  184. package/dist/packages/runtime/dist/key-store.js.map +1 -0
  185. package/dist/packages/runtime/dist/local-identity.d.ts +33 -0
  186. package/dist/packages/runtime/dist/local-identity.js +279 -0
  187. package/dist/packages/runtime/dist/local-identity.js.map +1 -0
  188. package/dist/packages/runtime/dist/mcp-rpc.d.ts +19 -0
  189. package/dist/packages/runtime/dist/mcp-rpc.js +175 -0
  190. package/dist/packages/runtime/dist/mcp-rpc.js.map +1 -0
  191. package/dist/packages/runtime/dist/mcp-server.d.ts +46 -19
  192. package/dist/packages/runtime/dist/mcp-server.js +427 -184
  193. package/dist/packages/runtime/dist/mcp-server.js.map +1 -1
  194. package/dist/packages/runtime/dist/mcp-tool-catalog.d.ts +1 -1
  195. package/dist/packages/runtime/dist/mcp-tool-catalog.js +172 -13
  196. package/dist/packages/runtime/dist/mcp-tool-catalog.js.map +1 -1
  197. package/dist/packages/runtime/dist/mcp.d.ts +2 -2
  198. package/dist/packages/runtime/dist/message-status.d.ts +32 -0
  199. package/dist/packages/runtime/dist/message-status.js +29 -0
  200. package/dist/packages/runtime/dist/message-status.js.map +1 -0
  201. package/dist/packages/runtime/dist/outbox.d.ts +91 -0
  202. package/dist/packages/runtime/dist/outbox.js +462 -0
  203. package/dist/packages/runtime/dist/outbox.js.map +1 -0
  204. package/dist/packages/runtime/dist/policy.d.ts +1 -1
  205. package/dist/packages/runtime/dist/protocol-peer-store.d.ts +40 -0
  206. package/dist/packages/runtime/dist/protocol-peer-store.js +176 -0
  207. package/dist/packages/runtime/dist/protocol-peer-store.js.map +1 -0
  208. package/dist/packages/runtime/dist/protocol-release.d.ts +26 -0
  209. package/dist/packages/runtime/dist/protocol-release.js +76 -0
  210. package/dist/packages/runtime/dist/protocol-release.js.map +1 -0
  211. package/dist/packages/runtime/dist/quota.d.ts +1 -1
  212. package/dist/packages/runtime/dist/relay-selection.d.ts +20 -0
  213. package/dist/packages/runtime/dist/relay-selection.js +34 -0
  214. package/dist/packages/runtime/dist/relay-selection.js.map +1 -0
  215. package/dist/packages/runtime/dist/requester-pending-store.d.ts +59 -0
  216. package/dist/packages/runtime/dist/requester-pending-store.js +266 -0
  217. package/dist/packages/runtime/dist/requester-pending-store.js.map +1 -0
  218. package/dist/packages/runtime/dist/secret-store.d.ts +19 -0
  219. package/dist/packages/runtime/dist/secret-store.js +51 -0
  220. package/dist/packages/runtime/dist/secret-store.js.map +1 -0
  221. package/dist/packages/runtime/dist/session-audit.d.ts +29 -7
  222. package/dist/packages/runtime/dist/session-audit.js +149 -29
  223. package/dist/packages/runtime/dist/session-audit.js.map +1 -1
  224. package/dist/packages/runtime/dist/session.d.ts +101 -9
  225. package/dist/packages/runtime/dist/session.js +696 -96
  226. package/dist/packages/runtime/dist/session.js.map +1 -1
  227. package/dist/packages/runtime/dist/task-journal.d.ts +18 -3
  228. package/dist/packages/runtime/dist/task-journal.js +271 -18
  229. package/dist/packages/runtime/dist/task-journal.js.map +1 -1
  230. package/dist/packages/runtime/dist/verifier.d.ts +3 -2
  231. package/dist/packages/runtime/dist/verifier.js +1 -0
  232. package/dist/packages/runtime/dist/verifier.js.map +1 -1
  233. package/dist/packages/runtime/dist/worker-trust-store.d.ts +111 -0
  234. package/dist/packages/runtime/dist/worker-trust-store.js +592 -0
  235. package/dist/packages/runtime/dist/worker-trust-store.js.map +1 -0
  236. package/dist/packages/sandbox/dist/container.d.ts +1 -1
  237. package/dist/packages/sandbox/dist/index.d.ts +1 -1
  238. package/dist/spec/README.ja.md +21 -1
  239. package/dist/spec/README.md +20 -1
  240. package/dist/spec/agent-card.schema.json +11 -1
  241. package/dist/spec/delivery-status.schema.json +106 -0
  242. package/dist/spec/examples/protocol-versions/sample-agent-card.json +35 -0
  243. package/dist/spec/examples/sample-delivery-status.json +21 -0
  244. package/dist/spec/examples/sample-task-manifest.0.1.0.json +173 -0
  245. package/dist/spec/examples/sample-task-manifest.0.2.0.json +173 -0
  246. package/dist/spec/task-manifest.0.1.0.schema.json +422 -0
  247. package/dist/spec/task-manifest.0.2.0.schema.json +422 -0
  248. package/dist/spec/task-manifest.schema.json +9 -418
  249. package/docs/sdk.ja.md +133 -0
  250. package/docs/sdk.md +138 -0
  251. package/package.json +15 -3
@@ -0,0 +1,592 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ import * as fs from 'node:fs';
3
+ import { join, dirname } from 'node:path';
4
+ import { createHash, randomUUID } from 'node:crypto';
5
+ import { digest, signPayload, verifySigned } from '../../core/dist/index.js';
6
+ import { resolveDataRoot, privateDirectory, checkPath, checkStat, inspectDirectory } from './local-identity.js';
7
+ import { emptyConnectionData, validConnectionData, pruneConnectionData, assertConnectionCapacity, projectConnectionState, projectConnectionTrust, validConnectionPeer, validConnectionOffer, validConnectionOutbox, assertConnectionCommit, assertConnectionOwnerDid, connectionInviteDigest, CONNECTION_REFUSAL_TTL_MS } from './connection-records.js';
8
+ import { validStoredControls } from './connection-persistence.js';
9
+ // A finite grant is evidence of a past decision, never a connection.
10
+ function priorApprovedPeers(state) {
11
+ const peers = new Map((state.version === 2 ? state.priorApprovedPeers : [])
12
+ .map(p => [p.peerOwnerDid, structuredClone(p)]));
13
+ const add = (did, trust, requestId) => {
14
+ const p = peers.get(did) ?? { peerOwnerDid: did, sourceRequestIds: [], legacyTrust: trust };
15
+ if (trust === 'trusted')
16
+ p.legacyTrust = trust;
17
+ if (requestId && !p.sourceRequestIds.includes(requestId))
18
+ p.sourceRequestIds.push(requestId);
19
+ // Bound per-peer evidence without removing the peer or inferring an approval timestamp.
20
+ p.sourceRequestIds = p.sourceRequestIds.slice(-200);
21
+ peers.set(did, p);
22
+ };
23
+ for (const r of state.requests)
24
+ if (r.state === 'approved')
25
+ add(r.requesterOwnerDid, r.trust ?? 'limited', r.id);
26
+ for (const g of state.grants)
27
+ add(g.did, g.trust);
28
+ if (state.version === 2)
29
+ for (const p of peers.values()) {
30
+ const name = state.connections.find(c => c.peerOwnerDid === p.peerOwnerDid)?.peerDisplayName;
31
+ if (name !== undefined)
32
+ p.peerDisplayName ??= name;
33
+ }
34
+ return [...peers.values()];
35
+ }
36
+ const ceiling = 1024 * 1024;
37
+ export const PENDING_MESSAGE = 'The worker owner must review this requester. No task was accepted. Do not retry automatically.';
38
+ export function workerIntakePath(ownerDid, options = {}) {
39
+ return join(resolveDataRoot(options), 'workers', createHash('sha256').update(ownerDid).digest('hex'), 'intake.json');
40
+ }
41
+ /** JSON output also escapes controls; store previews never carry terminal control sequences. */
42
+ function preview(value, length) {
43
+ return value.slice(0, length).replace(/[\p{Cc}\p{Cf}]/gu, c => '\\u' + c.charCodeAt(0).toString(16).padStart(4, '0'));
44
+ }
45
+ export class WorkerTrustStore {
46
+ owner;
47
+ options;
48
+ state;
49
+ times = [];
50
+ io;
51
+ platform;
52
+ now;
53
+ constructor(owner, options = {}) {
54
+ this.owner = owner;
55
+ this.options = options;
56
+ this.io = options.fs ?? fs;
57
+ this.platform = options.platform ?? process.platform;
58
+ this.now = options.now ?? Date.now;
59
+ this.state = { version: 1, workerOwnerDid: owner.did, requests: [], grants: [], denials: [] };
60
+ }
61
+ read() {
62
+ if (!this.options.path)
63
+ return structuredClone(this.state);
64
+ const path = this.options.path, io = this.io;
65
+ let fd, observed = false;
66
+ try {
67
+ for (const directory of [dirname(dirname(dirname(path))), dirname(dirname(path)), dirname(path)]) {
68
+ try {
69
+ inspectDirectory(directory, io, this.platform);
70
+ }
71
+ catch (e) {
72
+ if (e.code !== 'ENOENT')
73
+ throw e;
74
+ }
75
+ }
76
+ checkPath(path, io, this.platform);
77
+ const before = io.lstatSync(path);
78
+ observed = true;
79
+ checkStat(before);
80
+ if (before.size > ceiling)
81
+ throw new Error();
82
+ fd = io.openSync(path, fs.constants.O_RDONLY | (fs.constants.O_NOFOLLOW ?? 0) | (fs.constants.O_NONBLOCK ?? 0));
83
+ const stat = io.fstatSync(fd);
84
+ checkStat(stat);
85
+ if (stat.ino !== before.ino || stat.dev !== before.dev || stat.size > ceiling)
86
+ throw new Error();
87
+ const bytes = Buffer.alloc(ceiling + 1);
88
+ let size = 0;
89
+ while (size < bytes.length) {
90
+ const count = io.readSync(fd, bytes, size, bytes.length - size, null);
91
+ if (!count)
92
+ break;
93
+ size += count;
94
+ }
95
+ if (size > ceiling)
96
+ throw new Error();
97
+ const signed = JSON.parse(bytes.subarray(0, size).toString('utf8'));
98
+ const s = signed.payload;
99
+ if (!verifySigned(signed, this.owner.did) || ![1, 2].includes(s.version) || s.workerOwnerDid !== this.owner.did ||
100
+ !Array.isArray(s.requests) || !Array.isArray(s.grants) || !Array.isArray(s.denials) ||
101
+ s.requests.length > 200 || s.requests.some(r => !r || typeof r.id !== 'string' ||
102
+ !['pending', 'approved', 'denied', 'expired'].includes(r.state) ||
103
+ !Number.isFinite(r.expiresAt) || typeof r.requesterOwnerDid !== 'string' ||
104
+ Buffer.byteLength(JSON.stringify(r)) > 4096) ||
105
+ s.grants.some(g => !g || !['limited', 'trusted'].includes(g.trust) ||
106
+ typeof g.did !== 'string' || !Number.isFinite(g.expiresAt)) ||
107
+ s.denials.some(d => typeof d !== 'string') ||
108
+ (s.version === 2 && ((!validConnectionData(s, this.owner.did) ||
109
+ (s.controls !== undefined && !validStoredControls(s.controls, this.owner.did))) ||
110
+ s.connections.some(c => s.denials.includes(c.peerOwnerDid)) ||
111
+ s.outbox.some(e => s.denials.includes(e.peerOwnerDid)))))
112
+ throw new Error();
113
+ return s;
114
+ }
115
+ catch (e) {
116
+ if (e.code === 'ENOENT' && !observed)
117
+ return { version: 1, workerOwnerDid: this.owner.did, requests: [], grants: [], denials: [] };
118
+ throw new Error('INTAKE_STORE_INVALID');
119
+ }
120
+ finally {
121
+ if (fd !== undefined)
122
+ io.closeSync(fd);
123
+ }
124
+ }
125
+ mutate(change, connectionMutation = false, reserveRoom = false) {
126
+ const path = this.options.path, io = this.io;
127
+ let locked = false, temporary;
128
+ try {
129
+ if (path) {
130
+ // Apply P1's private-directory policy to every store-owned directory.
131
+ privateDirectory(dirname(dirname(dirname(path))), io, this.platform);
132
+ privateDirectory(dirname(dirname(path)), io, this.platform);
133
+ privateDirectory(dirname(path), io, this.platform);
134
+ try {
135
+ io.writeFileSync(path + '.lock', '', { flag: 'wx', mode: 0o600 });
136
+ locked = true;
137
+ }
138
+ catch (e) {
139
+ if (e.code === 'EEXIST')
140
+ throw new Error('INTAKE_STORE_BUSY');
141
+ throw e;
142
+ }
143
+ }
144
+ let state = this.read();
145
+ const now = this.now();
146
+ if (connectionMutation && state.version === 1)
147
+ state = { ...state, ...emptyConnectionData(), version: 2, priorApprovedPeers: priorApprovedPeers(state) };
148
+ if (state.version === 2) {
149
+ state.priorApprovedPeers = priorApprovedPeers(state);
150
+ }
151
+ state.grants = state.grants.filter(g => g.expiresAt > now);
152
+ for (const r of state.requests)
153
+ if (r.state === 'pending' && r.expiresAt <= now)
154
+ r.state = 'expired';
155
+ const result = change(state);
156
+ state.requests = [...state.requests.filter(r => r.state !== 'pending').slice(-100),
157
+ ...state.requests.filter(r => r.state === 'pending')];
158
+ if (state.version === 2) {
159
+ if (state.controls)
160
+ state.controls = state.controls.filter(r => Date.parse(r.offer.expiresAt) > now);
161
+ if (state.controls && !validStoredControls(state.controls, this.owner.did))
162
+ throw new Error('INTAKE_STORE_INVALID');
163
+ state.priorApprovedPeers = priorApprovedPeers(state);
164
+ pruneConnectionData(state, now);
165
+ assertConnectionCapacity(state);
166
+ if (new Set([...state.requests.filter(r => r.state === 'pending').map(r => r.requesterOwnerDid),
167
+ ...state.connections.filter(c => c.state === 'pending').map(c => c.peerOwnerDid)]).size > 100)
168
+ throw new Error('CONNECTION_STORE_FULL');
169
+ if (!validConnectionData(state, this.owner.did))
170
+ throw new Error('INTAKE_STORE_INVALID');
171
+ }
172
+ const bytes = JSON.stringify(signPayload(state, this.owner, new Date(now).toISOString())) + '\n';
173
+ // Leave room for owner decisions when intake fills the queue.
174
+ if (Buffer.byteLength(bytes) > ceiling - (reserveRoom ? 32 * 1024 : 0))
175
+ throw new Error(connectionMutation ? 'CONNECTION_STORE_FULL' : 'INTAKE_QUEUE_FULL');
176
+ if (path) {
177
+ temporary = path + '.' + randomUUID() + '.tmp';
178
+ const fd = io.openSync(temporary, 'wx', 0o600);
179
+ try {
180
+ io.writeFileSync(fd, bytes);
181
+ io.fsyncSync(fd);
182
+ }
183
+ finally {
184
+ io.closeSync(fd);
185
+ }
186
+ checkPath(path, io, this.platform);
187
+ io.renameSync(temporary, path);
188
+ }
189
+ else
190
+ this.state = state;
191
+ return result;
192
+ }
193
+ catch (e) {
194
+ if (e instanceof Error && /^(INTAKE_|REQUEST_|CONNECTION_)/.test(e.message))
195
+ throw e;
196
+ throw new Error('INTAKE_STORE_UNWRITABLE');
197
+ }
198
+ finally {
199
+ if (temporary)
200
+ try {
201
+ io.unlinkSync(temporary);
202
+ }
203
+ catch (e) {
204
+ if (e.code !== 'ENOENT')
205
+ throw e;
206
+ }
207
+ if (locked)
208
+ io.unlinkSync(path + '.lock');
209
+ }
210
+ }
211
+ get ownerDid() { return this.owner.did; }
212
+ controlSnapshot() {
213
+ const s = this.read();
214
+ return s.version === 2 ? s.controls ?? [] : [];
215
+ }
216
+ updateControls(change) { return this.mutateConnections(change); }
217
+ level(did) {
218
+ const state = this.read();
219
+ if (state.denials.includes(did))
220
+ return 'denied';
221
+ return state.grants.find(g => g.did === did && g.expiresAt > this.now())?.trust ?? 'unknown';
222
+ }
223
+ hasExplicitLegacyGrant(did) {
224
+ const s = this.read();
225
+ return !s.denials.includes(did) && s.grants.some(g => g.did === did && g.trust === 'trusted' && g.origin === 'explicit-legacy' && g.expiresAt > this.now());
226
+ }
227
+ hasTrustedGrant() {
228
+ const s = this.read();
229
+ return s.grants.some(g => g.trust === 'trusted' && g.origin === 'explicit-legacy' && g.expiresAt > this.now() && !s.denials.includes(g.did));
230
+ }
231
+ list() {
232
+ return this.read().requests.map(r => r.state === 'pending' && r.expiresAt <= this.now() ? { ...r, state: 'expired' } : r);
233
+ }
234
+ pendingTask(did, taskId, _proposalDigest) {
235
+ // A reviewed entry still identifies an admission-only attempt; never replay it.
236
+ return this.read().requests.some(r => r.requesterOwnerDid === did && r.taskId === taskId);
237
+ }
238
+ /** Connection projection is separate from level(), which retains legacy grant semantics. */
239
+ connectionState(peerOwnerDid) {
240
+ const state = this.read();
241
+ return projectConnectionState(state.version === 2 ? state : emptyConnectionData(), state.denials, peerOwnerDid, this.now());
242
+ }
243
+ connectionLevel(peerOwnerDid) {
244
+ return projectConnectionTrust(this.connectionState(peerOwnerDid));
245
+ }
246
+ /** Read one local snapshot without polling, writing, pruning or changing consent. */
247
+ connectionStatus(peerOwnerDid) {
248
+ assertConnectionOwnerDid(peerOwnerDid);
249
+ const snapshot = this.read(), now = this.now();
250
+ const data = snapshot.version === 2 ? snapshot : emptyConnectionData();
251
+ const state = projectConnectionState(data, snapshot.denials, peerOwnerDid, now);
252
+ const connection = data.connections.find(c => c.peerOwnerDid === peerOwnerDid);
253
+ const controls = snapshot.version === 2 ? (snapshot.controls ?? []).filter(r => (r.offer.fromOwnerDid === this.owner.did ? r.offer.toOwnerDid : r.offer.fromOwnerDid) === peerOwnerDid) : [];
254
+ // Prefer the exchange that established this connection, then the current offer.
255
+ const control = state === 'connected' ? controls.find(r => r.active && r.accepted &&
256
+ r.accepted.payload.connectionId === connection?.connectionId &&
257
+ (connection.remoteAcceptanceDigest ? digest(r.accepted) === connection.remoteAcceptanceDigest :
258
+ r.consentDigest === connection.localConsentDigest)) :
259
+ controls.find(r => r.offer.requestId === connection?.outgoing?.requestId) ??
260
+ controls.find(r => r.offer.requestId === connection?.incoming?.requestId) ?? controls.at(-1);
261
+ const offer = control?.offer;
262
+ const legacyOffer = connection?.outgoing ?? connection?.incoming;
263
+ const direction = control?.direction ?? (connection?.outgoing ? 'outgoing' : connection?.incoming ? 'incoming' : undefined);
264
+ const expiresAt = offer ? Date.parse(offer.expiresAt) : legacyOffer?.expiresAt;
265
+ const status = state === 'blocked' ? 'blocked' : state === 'connected' ? 'connected' :
266
+ control && !control.active ? 'inactive' : expiresAt !== undefined && expiresAt <= now ? 'expired' :
267
+ direction === 'outgoing' ? 'pending_peer' : direction === 'incoming' ? 'pending_owner' : 'none';
268
+ const peerDisplayName = connection?.peerDisplayName ?? offer?.peerDisplayName;
269
+ return { peerOwnerDid, state, status,
270
+ ...(peerDisplayName === undefined ? {} : { peerDisplayName }),
271
+ ...(offer ? { requestId: offer.requestId, direction: control.direction,
272
+ createdAt: Date.parse(offer.createdAt), expiresAt } : legacyOffer ? {
273
+ requestId: legacyOffer.requestId, direction, expiresAt,
274
+ ...('createdAt' in legacyOffer ? { createdAt: legacyOffer.createdAt } : {}),
275
+ } : {}),
276
+ ...(state === 'connected' ? { connectionId: connection.connectionId, connectedAt: connection.connectedAt } : {}),
277
+ ...(state === 'connected' && control?.accepted ? { acknowledgementPending: control.outbox.some(b => b.envelope.payload.eventType === (control.direction === 'outgoing' ? 'CONNECTION_ACK' : 'CONNECTION_ACCEPTED')) } : {}), };
278
+ }
279
+ listConnections() {
280
+ const state = this.read(), data = state.version === 2 ? state : emptyConnectionData();
281
+ pruneConnectionData(data, this.now());
282
+ return [...data.connections.filter(c => !state.denials.includes(c.peerOwnerDid)),
283
+ ...state.denials.map(peerOwnerDid => ({ peerOwnerDid, state: 'blocked' }))];
284
+ }
285
+ listPriorApprovedPeers() { return priorApprovedPeers(this.read()); }
286
+ mutateConnections(change, reserveRoom = false) {
287
+ return this.mutate(s => change(s), true, reserveRoom);
288
+ }
289
+ assertAvailable(s, peer, incoming = false) {
290
+ assertConnectionOwnerDid(peer);
291
+ if (s.denials.includes(peer))
292
+ throw new Error('CONNECTION_ALREADY_DECIDED');
293
+ if (incoming && s.refusals.some(r => r.peerOwnerDid === peer && r.refuseUntil > this.now()))
294
+ throw new Error('CONNECTION_REQUEST_COOLDOWN');
295
+ }
296
+ recordOffer(input, outgoing) {
297
+ this.mutateConnections(s => {
298
+ pruneConnectionData(s, this.now());
299
+ const offer = 'outgoing' in input ? input.outgoing : input.incoming;
300
+ if (!validConnectionPeer(input) || !validConnectionOffer(offer, outgoing))
301
+ throw new Error('INTAKE_STORE_INVALID');
302
+ const start = 'createdAt' in offer ? offer.createdAt : offer.receivedAt;
303
+ if (offer.expiresAt <= this.now())
304
+ throw new Error('CONNECTION_REQUEST_EXPIRED');
305
+ if (start > this.now())
306
+ throw new Error('INTAKE_STORE_INVALID');
307
+ this.assertAvailable(s, input.peerOwnerDid, !outgoing);
308
+ let c = s.connections.find(c => c.peerOwnerDid === input.peerOwnerDid);
309
+ if (c?.state === 'connected')
310
+ throw new Error('CONNECTION_ALREADY_DECIDED');
311
+ const previous = outgoing ? c?.outgoing : c?.incoming;
312
+ if (previous) {
313
+ if (digest(previous) === digest(offer) && c.relayUrl === input.relayUrl) {
314
+ if (input.peerDisplayName !== undefined)
315
+ c.peerDisplayName ??= input.peerDisplayName;
316
+ return;
317
+ }
318
+ throw new Error('CONNECTION_ALREADY_DECIDED');
319
+ }
320
+ if (c && c.relayUrl !== input.relayUrl)
321
+ throw new Error('INTAKE_STORE_INVALID');
322
+ if (!c) {
323
+ c = { peerOwnerDid: input.peerOwnerDid, relayUrl: input.relayUrl, state: 'pending' };
324
+ s.connections.push(c);
325
+ }
326
+ if (input.peerAgentDid)
327
+ c.peerAgentDid = input.peerAgentDid;
328
+ if (input.peerDisplayName !== undefined)
329
+ c.peerDisplayName ??= input.peerDisplayName;
330
+ if ('outgoing' in input)
331
+ c.outgoing = structuredClone(input.outgoing);
332
+ else
333
+ c.incoming = structuredClone(input.incoming);
334
+ if (new Set([...s.requests.filter(r => r.state === 'pending').map(r => r.requesterOwnerDid),
335
+ ...s.connections.filter(c => c.state === 'pending').map(c => c.peerOwnerDid)]).size > 100)
336
+ throw new Error('CONNECTION_STORE_FULL');
337
+ }, true);
338
+ }
339
+ recordOutgoing(input) { this.recordOffer(input, true); }
340
+ recordIncoming(input) { this.recordOffer(input, false); }
341
+ queueAcceptance(s, peer, event, deadline) {
342
+ if (!validConnectionOutbox(event, this.owner.did) || event.peerOwnerDid !== peer.peerOwnerDid ||
343
+ event.relayUrl !== peer.relayUrl || event.state !== 'pending' ||
344
+ event.deliveryDeadline <= this.now() || event.deliveryDeadline > deadline)
345
+ throw new Error('INTAKE_STORE_INVALID');
346
+ const existing = s.outbox.find(e => e.eventId === event.eventId && e.deliveryDeadline > this.now());
347
+ if (existing)
348
+ throw new Error('CONNECTION_ALREADY_DECIDED');
349
+ s.outbox.push(structuredClone(event));
350
+ }
351
+ approveConnection(input) {
352
+ this.mutateConnections(s => {
353
+ this.assertAvailable(s, input.peerOwnerDid);
354
+ assertConnectionCommit(input.connectionId, input.localConsentDigest);
355
+ const c = s.connections.find(c => c.peerOwnerDid === input.peerOwnerDid);
356
+ if (!c?.incoming)
357
+ throw new Error('CONNECTION_NOT_FOUND');
358
+ if (c.incoming.requestId !== input.requestId || c.incoming.requestDigest !== input.requestDigest)
359
+ throw new Error('CONNECTION_ALREADY_DECIDED');
360
+ if (c.state === 'connected') {
361
+ if (c.connectionId === input.connectionId && c.localConsentDigest === input.localConsentDigest)
362
+ return;
363
+ throw new Error('CONNECTION_ALREADY_DECIDED');
364
+ }
365
+ if (c.incoming.expiresAt <= this.now())
366
+ throw new Error('CONNECTION_REQUEST_EXPIRED');
367
+ if (input.source !== undefined && !['request', 'prior-approval'].includes(input.source))
368
+ throw new Error('INTAKE_STORE_INVALID');
369
+ if (input.source === 'prior-approval' && !s.priorApprovedPeers.some(p => p.peerOwnerDid === c.peerOwnerDid))
370
+ throw new Error('CONNECTION_NOT_FOUND');
371
+ this.queueAcceptance(s, c, input.outbox, c.incoming.expiresAt);
372
+ Object.assign(c, { state: 'connected', connectionId: input.connectionId, connectedAt: this.now(),
373
+ source: input.source ?? 'request', localConsentDigest: input.localConsentDigest });
374
+ });
375
+ }
376
+ applyAccepted(input) {
377
+ this.mutateConnections(s => {
378
+ this.assertAvailable(s, input.peerOwnerDid);
379
+ assertConnectionCommit(input.connectionId, input.remoteAcceptanceDigest);
380
+ const c = s.connections.find(c => c.peerOwnerDid === input.peerOwnerDid);
381
+ if (!c?.outgoing)
382
+ throw new Error('CONNECTION_NOT_FOUND');
383
+ if (c.outgoing.requestId !== input.requestId || c.outgoing.requestDigest !== input.requestDigest)
384
+ throw new Error('CONNECTION_ALREADY_DECIDED');
385
+ if (c.state === 'connected') {
386
+ if (c.connectionId === input.connectionId && c.remoteAcceptanceDigest === input.remoteAcceptanceDigest)
387
+ return;
388
+ throw new Error('CONNECTION_ALREADY_DECIDED');
389
+ }
390
+ if (c.outgoing.expiresAt <= this.now())
391
+ throw new Error('CONNECTION_REQUEST_EXPIRED');
392
+ Object.assign(c, { state: 'connected', connectionId: input.connectionId, connectedAt: this.now(),
393
+ source: 'request', localConsentDigest: c.outgoing.ownerConsentDigest, remoteAcceptanceDigest: input.remoteAcceptanceDigest });
394
+ });
395
+ }
396
+ rejectConnection(peerOwnerDid) {
397
+ this.mutateConnections(s => {
398
+ this.assertAvailable(s, peerOwnerDid);
399
+ const c = s.connections.find(c => c.peerOwnerDid === peerOwnerDid);
400
+ if (!c?.incoming)
401
+ throw new Error('CONNECTION_NOT_FOUND');
402
+ if (c.state !== 'pending')
403
+ throw new Error('CONNECTION_ALREADY_DECIDED');
404
+ if (c.incoming.expiresAt <= this.now())
405
+ throw new Error('CONNECTION_REQUEST_EXPIRED');
406
+ this.removeConnection(s, peerOwnerDid, false);
407
+ s.refusals = s.refusals.filter(r => r.peerOwnerDid !== peerOwnerDid);
408
+ s.refusals.push({ peerOwnerDid, refuseUntil: this.now() + CONNECTION_REFUSAL_TTL_MS });
409
+ });
410
+ }
411
+ removeConnection(s, peerOwnerDid, removeLegacyGrants = true) {
412
+ assertConnectionOwnerDid(peerOwnerDid);
413
+ s.connections = s.connections.filter(c => c.peerOwnerDid !== peerOwnerDid);
414
+ for (const r of s.controls ?? [])
415
+ if (r.offer.fromOwnerDid === peerOwnerDid || r.offer.toOwnerDid === peerOwnerDid) {
416
+ r.active = false;
417
+ r.outbox = [];
418
+ }
419
+ if (removeLegacyGrants)
420
+ s.grants = s.grants.filter(g => g.did !== peerOwnerDid);
421
+ s.outbox = s.outbox.filter(e => e.peerOwnerDid !== peerOwnerDid);
422
+ }
423
+ withdrawConnection(peerOwnerDid, requestId) {
424
+ this.mutateConnections(s => {
425
+ const c = s.connections.find(c => c.peerOwnerDid === peerOwnerDid);
426
+ if (!c?.outgoing || c.outgoing.requestId !== requestId || c.state !== 'pending')
427
+ throw new Error('CONNECTION_NOT_FOUND');
428
+ for (const r of s.controls ?? [])
429
+ if (r.offer.requestId === requestId) {
430
+ r.active = false;
431
+ r.outbox = [];
432
+ }
433
+ delete c.outgoing;
434
+ if (!c.incoming)
435
+ s.connections = s.connections.filter(r => r !== c);
436
+ });
437
+ }
438
+ finishRequestReview(id, discard = false) {
439
+ this.mutate(s => {
440
+ const r = s.requests.find(r => r.id === id);
441
+ if (!r)
442
+ throw new Error('REQUEST_NOT_FOUND');
443
+ if (r.state !== 'pending')
444
+ throw new Error('REQUEST_ALREADY_DECIDED');
445
+ r.state = discard ? 'expired' : 'approved';
446
+ if (!discard)
447
+ r.trust = 'limited';
448
+ });
449
+ }
450
+ disconnect(peerOwnerDid) { this.mutateConnections(s => this.removeConnection(s, peerOwnerDid)); }
451
+ block(peerOwnerDid) {
452
+ this.mutateConnections(s => {
453
+ this.removeConnection(s, peerOwnerDid);
454
+ if (!s.denials.includes(peerOwnerDid))
455
+ s.denials.push(peerOwnerDid);
456
+ });
457
+ }
458
+ unblock(peerOwnerDid) {
459
+ this.mutateConnections(s => {
460
+ this.removeConnection(s, peerOwnerDid);
461
+ s.denials = s.denials.filter(d => d !== peerOwnerDid);
462
+ });
463
+ }
464
+ /** A validated task can redeem inviter consent without granting execution or recipient consent. */
465
+ redeemTaskInvite(invite, peer, connectionId) {
466
+ return this.mutateConnections(s => {
467
+ const inviteDigest = connectionInviteDigest(invite), deadline = Math.min(Date.parse(invite.expiresAt), Date.parse(invite.agentCard.payload.expiresAt));
468
+ if (invite.ownerDid !== this.owner.did || deadline <= this.now() || !validConnectionPeer(peer))
469
+ throw new Error('INTAKE_STORE_INVALID');
470
+ const binding = s.inviteBindings.find(b => b.inviteDigest === inviteDigest);
471
+ if (binding)
472
+ return binding.requesterOwnerDid !== peer.peerOwnerDid ? 'other' :
473
+ s.connections.some(c => c.peerOwnerDid === peer.peerOwnerDid && c.state === 'connected') ? 'same' : 'inactive';
474
+ this.assertAvailable(s, peer.peerOwnerDid);
475
+ s.inviteBindings.push({ inviteDigest, requesterOwnerDid: peer.peerOwnerDid, boundAt: this.now(), tokenExpiresAt: deadline, connectionId });
476
+ const previous = s.connections.find(c => c.peerOwnerDid === peer.peerOwnerDid);
477
+ if (previous?.state !== 'connected') {
478
+ const peerDisplayName = previous?.peerDisplayName ?? peer.peerDisplayName;
479
+ s.connections = s.connections.filter(c => c.peerOwnerDid !== peer.peerOwnerDid);
480
+ s.connections.push({ ...peer, ...(peerDisplayName !== undefined ? { peerDisplayName } : {}),
481
+ state: 'connected', connectionId, connectedAt: this.now(), source: 'invite', localConsentDigest: inviteDigest });
482
+ }
483
+ return 'first';
484
+ });
485
+ }
486
+ bindInviteFirstUse(input) {
487
+ return this.mutateConnections(s => {
488
+ const inviteDigest = connectionInviteDigest(input.invite);
489
+ const deadline = Math.min(Date.parse(input.invite.expiresAt), Date.parse(input.invite.agentCard.payload.expiresAt));
490
+ if (input.invite.ownerDid !== this.owner.did || !Number.isFinite(deadline))
491
+ throw new Error('INTAKE_STORE_INVALID');
492
+ if (deadline <= this.now())
493
+ throw new Error('CONNECTION_REQUEST_EXPIRED');
494
+ if (!validConnectionPeer(input))
495
+ throw new Error('INTAKE_STORE_INVALID');
496
+ const existing = s.inviteBindings.find(b => b.inviteDigest === inviteDigest);
497
+ if (existing)
498
+ return existing.requesterOwnerDid === input.peerOwnerDid ? 'same' : 'other';
499
+ this.assertAvailable(s, input.peerOwnerDid, true);
500
+ assertConnectionCommit(input.connectionId, input.localConsentDigest);
501
+ const previous = s.connections.find(c => c.peerOwnerDid === input.peerOwnerDid);
502
+ const connected = previous?.state === 'connected' ? previous : undefined;
503
+ if (connected && connected.connectionId !== input.connectionId)
504
+ throw new Error('CONNECTION_ALREADY_DECIDED');
505
+ this.queueAcceptance(s, input, input.outbox, deadline);
506
+ if (!connected) {
507
+ const peerDisplayName = previous?.peerDisplayName ?? input.peerDisplayName;
508
+ s.connections = s.connections.filter(c => c.peerOwnerDid !== input.peerOwnerDid);
509
+ s.connections.push({ peerOwnerDid: input.peerOwnerDid, ...(input.peerAgentDid ? { peerAgentDid: input.peerAgentDid } : {}),
510
+ ...(peerDisplayName !== undefined ? { peerDisplayName } : {}),
511
+ relayUrl: input.relayUrl, state: 'connected', connectionId: input.connectionId, connectedAt: this.now(),
512
+ source: 'invite', localConsentDigest: input.localConsentDigest });
513
+ }
514
+ s.inviteBindings.push({ inviteDigest, requesterOwnerDid: input.peerOwnerDid, boundAt: this.now(),
515
+ tokenExpiresAt: deadline, connectionId: input.connectionId });
516
+ return 'first';
517
+ });
518
+ }
519
+ listConnectionOutbox() {
520
+ const s = this.read();
521
+ return s.version === 2 ? s.outbox.filter(e => e.state === 'pending' && e.deliveryDeadline > this.now()) : [];
522
+ }
523
+ markConnectionEventDelivered(eventId) {
524
+ this.mutateConnections(s => {
525
+ const e = s.outbox.find(e => e.eventId === eventId);
526
+ if (!e)
527
+ throw new Error('CONNECTION_NOT_FOUND');
528
+ e.state = 'delivered';
529
+ });
530
+ }
531
+ /** One process-local limiter shared by task and connection intake on this store. */
532
+ consumeAdmission(did, id) {
533
+ const now = this.now();
534
+ this.times = this.times.filter(t => now - t.at < 60_000);
535
+ if (this.times.some(t => t.did === did && t.id === id))
536
+ return;
537
+ if (this.times.length >= 60 || this.times.filter(t => t.did === did).length >= 5)
538
+ throw new Error('INTAKE_RATE_LIMITED');
539
+ this.times.push({ did, at: now, id });
540
+ }
541
+ enqueue(manifest, input) {
542
+ const now = this.now(), did = manifest.requester.ownerDid;
543
+ this.consumeAdmission(did, manifest.taskId);
544
+ this.mutate(s => {
545
+ if (s.denials.includes(did))
546
+ throw new Error('REQUEST_DENIED');
547
+ if (s.requests.some(r => r.requesterOwnerDid === did &&
548
+ (r.state === 'pending' || (r.taskId === manifest.taskId && r.digest === digest({ manifest, input })))))
549
+ return;
550
+ if (s.requests.filter(r => r.state === 'pending').length >= 100 ||
551
+ Buffer.byteLength(JSON.stringify(s)) > ceiling - 32 * 1024)
552
+ throw new Error('INTAKE_QUEUE_FULL');
553
+ const entry = { id: randomUUID(), requesterOwnerDid: did,
554
+ requesterAgentDid: manifest.requester.agentDid, taskId: manifest.taskId,
555
+ digest: digest({ manifest, input }),
556
+ scope: { ...(manifest.task.skillId ? { skillId: preview(manifest.task.skillId, 64) } : {}),
557
+ ...(manifest.task.taskType ? { taskType: preview(manifest.task.taskType, 64) } : {}) }, budget: manifest.budget,
558
+ capabilities: manifest.permissions.grantedCapabilities.length, receivedAt: now,
559
+ expiresAt: Math.min(Date.parse(manifest.deadline), now + 24 * 60 * 60_000), state: 'pending' };
560
+ if (Buffer.byteLength(JSON.stringify(entry)) > 4096)
561
+ throw new Error('INTAKE_QUEUE_FULL');
562
+ s.requests.push(entry);
563
+ });
564
+ }
565
+ decide(id, decision, trust = 'limited') {
566
+ this.mutate(s => {
567
+ const r = s.requests.find(r => r.id === id);
568
+ if (!r)
569
+ throw new Error('REQUEST_NOT_FOUND');
570
+ if (decision === 'deny') {
571
+ if (!s.denials.includes(r.requesterOwnerDid))
572
+ s.denials.push(r.requesterOwnerDid);
573
+ if (s.version === 2)
574
+ this.removeConnection(s, r.requesterOwnerDid);
575
+ s.grants = s.grants.filter(g => g.did !== r.requesterOwnerDid);
576
+ r.state = 'denied';
577
+ return;
578
+ }
579
+ if (r.state === 'approved' && r.trust === trust)
580
+ return;
581
+ if (r.state === 'expired')
582
+ throw new Error('REQUEST_EXPIRED');
583
+ if (r.state !== 'pending' || s.denials.includes(r.requesterOwnerDid))
584
+ throw new Error('REQUEST_ALREADY_DECIDED');
585
+ s.grants = s.grants.filter(g => g.did !== r.requesterOwnerDid);
586
+ s.grants.push({ did: r.requesterOwnerDid, trust, origin: 'explicit-legacy', expiresAt: this.now() + 24 * 60 * 60_000 });
587
+ r.state = 'approved';
588
+ r.trust = trust;
589
+ });
590
+ }
591
+ }
592
+ //# sourceMappingURL=worker-trust-store.js.map