super-dsh 0.1.6 → 0.1.10-e

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 (260) hide show
  1. package/README.md +25 -3
  2. package/agent-agy/cordis.patch.yml +7 -4
  3. package/agent-agy/dist/agent.d.ts +12 -11
  4. package/agent-agy/dist/agy-auth-gateway.d.ts +124 -0
  5. package/agent-agy/dist/agy-auth-gateway.js +233 -0
  6. package/agent-agy/dist/agy-auth.d.ts +153 -0
  7. package/agent-agy/dist/agy-auth.js +179 -0
  8. package/agent-agy/dist/agy-cli-client.d.ts +39 -9
  9. package/agent-agy/dist/agy-cli-client.js +82 -18
  10. package/agent-agy/dist/agy-store.d.ts +8 -0
  11. package/agent-agy/dist/index.d.ts +46 -16
  12. package/agent-agy/dist/index.js +82 -56
  13. package/agent-agy/dist/models.d.ts +70 -0
  14. package/agent-agy/dist/models.js +102 -3
  15. package/agent-agy/package.json +7 -5
  16. package/agent-agy-sdk/cordis.patch.yml +58 -0
  17. package/agent-agy-sdk/dist/adapter.d.ts +15 -0
  18. package/agent-agy-sdk/dist/adapter.js +42 -0
  19. package/agent-agy-sdk/dist/agent-preset-agy.d.ts +27 -0
  20. package/agent-agy-sdk/dist/agent-preset-agy.js +32 -0
  21. package/agent-agy-sdk/dist/agent.d.ts +138 -0
  22. package/agent-agy-sdk/dist/agent.js +494 -0
  23. package/{agent-agy → agent-agy-sdk}/dist/agy-client.d.ts +26 -6
  24. package/{agent-agy → agent-agy-sdk}/dist/agy-client.js +43 -25
  25. package/agent-agy-sdk/dist/agy-sessions.d.ts +25 -0
  26. package/agent-agy-sdk/dist/agy-sessions.js +107 -0
  27. package/agent-agy-sdk/dist/agy-store.d.ts +39 -0
  28. package/agent-agy-sdk/dist/agy-store.js +83 -0
  29. package/agent-agy-sdk/dist/inbox.d.ts +26 -0
  30. package/agent-agy-sdk/dist/inbox.js +53 -0
  31. package/agent-agy-sdk/dist/index.d.ts +93 -0
  32. package/agent-agy-sdk/dist/index.js +476 -0
  33. package/agent-agy-sdk/dist/models.d.ts +41 -0
  34. package/agent-agy-sdk/dist/models.js +74 -0
  35. package/agent-agy-sdk/dist/sdk-venv.d.ts +76 -0
  36. package/agent-agy-sdk/dist/sdk-venv.js +173 -0
  37. package/agent-agy-sdk/dist/world-plugin.d.ts +10 -0
  38. package/agent-agy-sdk/dist/world-plugin.js +82 -0
  39. package/agent-agy-sdk/package.json +64 -0
  40. package/agent-claude/dist/adapter.js +106 -31
  41. package/agent-claude/dist/agent.js +154 -17
  42. package/agent-claude/dist/claude-client.js +84 -43
  43. package/agent-claude/dist/claude-launch.js +101 -0
  44. package/agent-claude/dist/claude-native-config.js +169 -0
  45. package/agent-claude/dist/claude-provider-gateway.js +340 -0
  46. package/agent-claude/dist/claude-providers.js +391 -0
  47. package/agent-claude/dist/claude-route.js +152 -0
  48. package/agent-claude/dist/index.js +214 -37
  49. package/agent-claude/dist/models.js +14 -0
  50. package/agent-claude/dist/sdk-runtime.js +320 -0
  51. package/agent-claude/package.json +6 -4
  52. package/agent-codex/README.md +141 -36
  53. package/agent-codex/cordis.patch.yml +14 -10
  54. package/agent-codex/dist/adapter.js +64 -28
  55. package/agent-codex/dist/agent.js +255 -81
  56. package/agent-codex/dist/app-server-approvals.js +165 -0
  57. package/agent-codex/dist/app-server-client.js +723 -0
  58. package/agent-codex/dist/app-server-events.js +432 -0
  59. package/agent-codex/dist/app-server-protocol.js +95 -0
  60. package/agent-codex/dist/app-server-wire.js +299 -0
  61. package/agent-codex/dist/codex-launch.js +78 -0
  62. package/agent-codex/dist/codex-native-config.js +392 -0
  63. package/agent-codex/dist/codex-provider-gateway.js +402 -0
  64. package/agent-codex/dist/codex-providers.js +376 -0
  65. package/agent-codex/dist/codex-route.js +86 -0
  66. package/agent-codex/dist/index.js +256 -115
  67. package/agent-codex/dist/permission.js +75 -1
  68. package/agent-codex/package.json +1 -2
  69. package/agent-codex-sdk/README.md +79 -0
  70. package/agent-codex-sdk/cordis.patch.yml +81 -0
  71. package/agent-codex-sdk/dist/adapter.js +91 -0
  72. package/agent-codex-sdk/dist/agent-preset-codex.js +32 -0
  73. package/agent-codex-sdk/dist/agent.js +1085 -0
  74. package/agent-codex-sdk/dist/codex-native-config.js +392 -0
  75. package/agent-codex-sdk/dist/codex-provider-gateway.js +402 -0
  76. package/agent-codex-sdk/dist/codex-providers.js +376 -0
  77. package/agent-codex-sdk/dist/codex-route.js +86 -0
  78. package/{agent-codex/dist/codex-client.js → agent-codex-sdk/dist/codex-sdk-client.js} +253 -32
  79. package/agent-codex-sdk/dist/codex-sdk-launch.js +92 -0
  80. package/agent-codex-sdk/dist/codex-store.js +180 -0
  81. package/agent-codex-sdk/dist/inbox.js +53 -0
  82. package/agent-codex-sdk/dist/index.js +739 -0
  83. package/agent-codex-sdk/dist/permission.js +71 -0
  84. package/agent-codex-sdk/dist/sdk-runtime.js +307 -0
  85. package/agent-codex-sdk/dist/session-map.js +167 -0
  86. package/agent-codex-sdk/dist/world-plugin.js +93 -0
  87. package/agent-codex-sdk/package.json +79 -0
  88. package/agent-codex-sdk/scripts/fetch-sdk-runtime.mjs +57 -0
  89. package/agent-omp/README.md +72 -55
  90. package/agent-omp/dist/agent.js +36 -11
  91. package/agent-omp/dist/index.js +92 -52
  92. package/agent-omp/dist/lazy-rpc.js +11 -4
  93. package/agent-omp/dist/omp-cli.js +114 -97
  94. package/agent-omp/dist/omp-store.js +12 -4
  95. package/agent-omp/dist/permission.js +70 -5
  96. package/agent-omp/dist/rpc-client.js +409 -0
  97. package/agent-omp/dist/world-plugin.js +36 -3
  98. package/agent-omp/package.json +2 -6
  99. package/agent-omp-sdk/README.md +103 -0
  100. package/agent-omp-sdk/cordis.patch.yml +108 -0
  101. package/agent-omp-sdk/dist/adapter.d.ts +21 -0
  102. package/agent-omp-sdk/dist/adapter.js +36 -0
  103. package/agent-omp-sdk/dist/agent-preset-omp.d.ts +31 -0
  104. package/agent-omp-sdk/dist/agent-preset-omp.js +32 -0
  105. package/agent-omp-sdk/dist/agent.d.ts +64 -0
  106. package/agent-omp-sdk/dist/agent.js +1178 -0
  107. package/agent-omp-sdk/dist/discovery.d.ts +57 -0
  108. package/agent-omp-sdk/dist/discovery.js +116 -0
  109. package/agent-omp-sdk/dist/inbox.d.ts +28 -0
  110. package/agent-omp-sdk/dist/inbox.js +53 -0
  111. package/agent-omp-sdk/dist/index.d.ts +60 -0
  112. package/agent-omp-sdk/dist/index.js +632 -0
  113. package/agent-omp-sdk/dist/knobs.d.ts +34 -0
  114. package/agent-omp-sdk/dist/knobs.js +50 -0
  115. package/agent-omp-sdk/dist/lazy-rpc.d.ts +98 -0
  116. package/agent-omp-sdk/dist/lazy-rpc.js +178 -0
  117. package/agent-omp-sdk/dist/mobile/zoom-guard.d.ts +187 -0
  118. package/agent-omp-sdk/dist/mobile/zoom-guard.js +294 -0
  119. package/agent-omp-sdk/dist/mobile-boot.d.ts +49 -0
  120. package/agent-omp-sdk/dist/mobile-boot.js +38 -0
  121. package/agent-omp-sdk/dist/models.d.ts +39 -0
  122. package/agent-omp-sdk/dist/models.js +299 -0
  123. package/agent-omp-sdk/dist/omp-cli.d.ts +67 -0
  124. package/agent-omp-sdk/dist/omp-cli.js +164 -0
  125. package/agent-omp-sdk/dist/omp-disk-discovery.d.ts +27 -0
  126. package/agent-omp-sdk/dist/omp-disk-discovery.js +178 -0
  127. package/agent-omp-sdk/dist/omp-executable.d.ts +2 -0
  128. package/agent-omp-sdk/dist/omp-executable.js +30 -0
  129. package/agent-omp-sdk/dist/omp-store.d.ts +134 -0
  130. package/agent-omp-sdk/dist/omp-store.js +526 -0
  131. package/agent-omp-sdk/dist/pairing.d.ts +7 -0
  132. package/agent-omp-sdk/dist/pairing.js +45 -0
  133. package/agent-omp-sdk/dist/permission.d.ts +138 -0
  134. package/agent-omp-sdk/dist/permission.js +161 -0
  135. package/agent-omp-sdk/dist/protocol.d.ts +189 -0
  136. package/agent-omp-sdk/dist/rpc-types.d.ts +77 -0
  137. package/agent-omp-sdk/dist/rpc-types.js +1 -0
  138. package/agent-omp-sdk/dist/sdk-client.d.ts +160 -0
  139. package/{agent-omp → agent-omp-sdk}/dist/sdk-client.js +108 -56
  140. package/agent-omp-sdk/dist/sdk-runtime.d.ts +211 -0
  141. package/agent-omp-sdk/dist/sdk-runtime.js +418 -0
  142. package/agent-omp-sdk/dist/sidecar-client.d.ts +87 -0
  143. package/agent-omp-sdk/dist/sidecar-client.js +197 -0
  144. package/agent-omp-sdk/dist/store/adapter.d.ts +17 -0
  145. package/agent-omp-sdk/dist/store/adapter.js +47 -0
  146. package/agent-omp-sdk/dist/store/db.d.ts +70 -0
  147. package/agent-omp-sdk/dist/store/db.js +210 -0
  148. package/agent-omp-sdk/dist/store/index.d.ts +23 -0
  149. package/agent-omp-sdk/dist/store/index.js +59 -0
  150. package/agent-omp-sdk/dist/store/reconcile.d.ts +15 -0
  151. package/agent-omp-sdk/dist/store/reconcile.js +51 -0
  152. package/agent-omp-sdk/dist/store/schema.d.ts +8 -0
  153. package/agent-omp-sdk/dist/store/schema.js +40 -0
  154. package/agent-omp-sdk/dist/transport.d.ts +49 -0
  155. package/agent-omp-sdk/dist/transport.js +35 -0
  156. package/agent-omp-sdk/dist/world-home.d.ts +4 -0
  157. package/agent-omp-sdk/dist/world-home.js +23 -0
  158. package/agent-omp-sdk/dist/world-plugin.d.ts +10 -0
  159. package/agent-omp-sdk/dist/world-plugin.js +137 -0
  160. package/agent-omp-sdk/package.json +87 -0
  161. package/client/index.js +1883 -287
  162. package/cordis.patch.yml +80 -0
  163. package/package.json +23 -9
  164. package/superd-hub/dist/a2a-delivery.d.ts +189 -0
  165. package/superd-hub/dist/a2a-delivery.js +240 -0
  166. package/superd-hub/dist/a2a-host.d.ts +76 -0
  167. package/superd-hub/dist/a2a-host.js +145 -0
  168. package/superd-hub/dist/a2a-ingest.d.ts +31 -0
  169. package/superd-hub/dist/a2a-ingest.js +636 -0
  170. package/superd-hub/dist/a2a-log.d.ts +17 -0
  171. package/superd-hub/dist/a2a-log.js +42 -0
  172. package/superd-hub/dist/a2a-row.d.ts +65 -0
  173. package/superd-hub/dist/a2a-row.js +162 -0
  174. package/superd-hub/dist/a2a-service.d.ts +357 -0
  175. package/superd-hub/dist/a2a-service.js +980 -0
  176. package/superd-hub/dist/a2a-wire.d.ts +220 -0
  177. package/superd-hub/dist/a2a-wire.js +305 -0
  178. package/superd-hub/dist/agent-ids.d.ts +10 -0
  179. package/superd-hub/dist/agent-ids.js +68 -0
  180. package/superd-hub/dist/agent-roster.d.ts +29 -18
  181. package/superd-hub/dist/agent-roster.js +54 -15
  182. package/superd-hub/dist/agent-status.d.ts +119 -0
  183. package/superd-hub/dist/agent-status.js +386 -0
  184. package/superd-hub/dist/client/agent-settings.d.ts +210 -0
  185. package/superd-hub/dist/client/agent-settings.js +300 -0
  186. package/superd-hub/dist/client/mesh-api.d.ts +134 -0
  187. package/superd-hub/dist/client/mesh-api.js +219 -0
  188. package/superd-hub/dist/client/mesh-groups.d.ts +63 -0
  189. package/superd-hub/dist/client/mesh-groups.js +66 -0
  190. package/superd-hub/dist/client/roster.d.ts +48 -0
  191. package/superd-hub/dist/client/roster.js +102 -0
  192. package/superd-hub/dist/client/runtime-settings-api.d.ts +76 -0
  193. package/superd-hub/dist/client/runtime-settings-api.js +137 -0
  194. package/superd-hub/dist/client/switch.d.ts +165 -0
  195. package/superd-hub/dist/client/switch.js +295 -0
  196. package/superd-hub/dist/exec-pty-channel.d.ts +58 -0
  197. package/superd-hub/dist/exec-pty-channel.js +162 -0
  198. package/superd-hub/dist/exec-pty-framing.d.ts +32 -0
  199. package/superd-hub/dist/exec-pty-framing.js +81 -0
  200. package/superd-hub/dist/exec-pty-rpc.d.ts +21 -0
  201. package/superd-hub/dist/exec-pty-rpc.js +128 -0
  202. package/superd-hub/dist/exec-pty-session.d.ts +76 -0
  203. package/superd-hub/dist/exec-pty-session.js +234 -0
  204. package/superd-hub/dist/exec.d.ts +56 -0
  205. package/superd-hub/dist/exec.js +225 -0
  206. package/superd-hub/dist/gateway.d.ts +39 -2
  207. package/superd-hub/dist/gateway.js +62 -3
  208. package/superd-hub/dist/hub-tool.d.ts +174 -0
  209. package/superd-hub/dist/hub-tool.js +464 -0
  210. package/superd-hub/dist/index.d.ts +15 -1
  211. package/superd-hub/dist/index.js +16 -0
  212. package/superd-hub/dist/machines.d.ts +45 -0
  213. package/superd-hub/dist/machines.js +125 -0
  214. package/superd-hub/dist/mesh-federate.d.ts +138 -0
  215. package/superd-hub/dist/mesh-federate.js +223 -0
  216. package/superd-hub/dist/mesh-gateway.d.ts +67 -0
  217. package/superd-hub/dist/mesh-gateway.js +408 -0
  218. package/superd-hub/dist/mesh-link.d.ts +274 -0
  219. package/superd-hub/dist/mesh-link.js +441 -0
  220. package/superd-hub/dist/mesh-rpc.d.ts +123 -0
  221. package/superd-hub/dist/mesh-rpc.js +250 -0
  222. package/superd-hub/dist/mux-client.d.ts +71 -0
  223. package/superd-hub/dist/mux-client.js +281 -0
  224. package/superd-hub/dist/pack-version.d.ts +2 -0
  225. package/superd-hub/dist/pack-version.js +30 -0
  226. package/superd-hub/dist/roster.d.ts +6 -0
  227. package/superd-hub/dist/rpc.d.ts +18 -2
  228. package/superd-hub/dist/rpc.js +34 -4
  229. package/superd-hub/dist/spawn-world.js +3 -1
  230. package/superd-hub/dist/switch-core.d.ts +62 -25
  231. package/superd-hub/dist/switch-core.js +53 -30
  232. package/superd-hub/dist/system-head.d.ts +22 -12
  233. package/superd-hub/dist/system-head.js +100 -84
  234. package/superd-hub/dist/wire-endpoints.d.ts +264 -0
  235. package/superd-hub/dist/wire-endpoints.js +180 -0
  236. package/superd-hub/lib/client/index.js +1883 -287
  237. package/superd-hub/package.json +11 -2
  238. package/agent-agy/dist/agent-preset-projection.d.ts +0 -31
  239. package/agent-agy/dist/agent-preset-projection.js +0 -33
  240. package/agent-claude/dist/agent-preset-projection.js +0 -30
  241. package/agent-codex/dist/agent-preset-projection.js +0 -37
  242. package/agent-codex/dist/app-home.js +0 -65
  243. package/agent-codex/dist/models.js +0 -152
  244. package/agent-codex/dist/spike.js +0 -46
  245. package/agent-hermes/dist/agent-preset-projection.js +0 -34
  246. package/agent-omp/dist/agent-preset-projection.js +0 -36
  247. package/agent-omp/dist/app-home.js +0 -86
  248. package/agent-omp/dist/sidecar-client.js +0 -195
  249. package/agent-pi/dist/agent-preset-projection.js +0 -29
  250. package/superd-hub/dist/client-shim.d.ts +0 -28
  251. package/superd-hub/dist/client-shim.js +0 -251
  252. package/superd-hub/dist/index-pass.d.ts +0 -22
  253. package/superd-hub/dist/index-pass.js +0 -47
  254. package/superd-hub/dist/labels.d.ts +0 -45
  255. package/superd-hub/dist/labels.js +0 -83
  256. /package/{agent-agy → agent-agy-sdk}/bridge/agy_bridge.py +0 -0
  257. /package/{agent-agy → agent-agy-sdk}/bridge/gemini_openai_server.mjs +0 -0
  258. /package/{agent-codex/dist/codex-events.js → agent-codex-sdk/dist/codex-sdk-events.js} +0 -0
  259. /package/{agent-omp → agent-omp-sdk}/dist/protocol.js +0 -0
  260. /package/{agent-omp → agent-omp-sdk}/sidecar/main.ts +0 -0
@@ -0,0 +1,220 @@
1
+ /**
2
+ * Hub-to-hub A2A envelope protocol v1 (postoffice R1; ADR 0011, spec
3
+ * machine-mesh-a2a D16–D19, plan 2026-09-28-machine-mesh-postoffice-rework
4
+ * 契约要点).
5
+ *
6
+ * THE only versioned cross-machine contract in the mesh (D16): everything
7
+ * hub-to-hub rides these envelopes — opaque payloads addressed by
8
+ * `<m4>-<a6>`; the session plane never crosses a machine boundary. Local
9
+ * delivery is the SAME protocol short-circuited in-process (R4), so wire
10
+ * shape = local shape by construction.
11
+ *
12
+ * Frame: `{v: 1, envelope}`. Request envelopes (initiator hub → target hub):
13
+ * new-conversation — create + inject (conversationId = initiator-minted
14
+ * shortId, title already normalized to the
15
+ * '[undefined]' placeholder when unspecified, D17)
16
+ * message — resume + inject on the adopted shortId
17
+ * stop — cancel the target machine's local turn
18
+ * Return envelopes (target hub → initiator hub):
19
+ * reply — final assistant text of a captured completion (D3: never empty;
20
+ * an aborted turn without text is a notice instead)
21
+ * notice — bounded state account (summary ≤120, mirrors upstream
22
+ * CONTEXT_SUMMARY_MAX_CHARS)
23
+ *
24
+ * Endpoints (registered by mesh-rpc in R3, consumed here):
25
+ * POST /api/superd/a2a body = frame → receipt
26
+ * GET /api/superd/a2a/outbox?machine=<m4> → claim & clear the buffered
27
+ * envelopes addressed to that machine
28
+ * (declarative claim inside the trust
29
+ * model, D1 — the consumer holds full
30
+ * authority over its own outbox).
31
+ *
32
+ * Receipts (contract): new/message → `{outcome,messageId,conversationId,
33
+ * sessionId}` ('delivered' on every successful prompt submission — end-to-end
34
+ * delivery complete; 'failed' carries an error body); stop → `{stopped,scope,reason?}`.
35
+ *
36
+ * Guards are STRICT: exact key sets per kind, non-empty strings everywhere,
37
+ * no unknown fields. Version skew is the outer `v`'s job; within v1 a
38
+ * malformed envelope is a protocol violation and fails loud (the collision
39
+ * case — adopting a foreign shortId — likewise rejects loud at ingest, R3).
40
+ */
41
+ import type { WireError } from './envelope.js';
42
+ /** The wire-protocol version this module speaks (and its guards accept). */
43
+ export declare const A2A_WIRE_VERSION = 1;
44
+ /** Registry/wire title placeholder when the operator specified none (D17). */
45
+ export declare const A2A_TITLE_PLACEHOLDER = "[undefined]";
46
+ /** The a2a ingest endpoint (fence + cookie gate ride the connection channel). */
47
+ export declare const A2A_ENDPOINT_PATH = "/api/superd/a2a";
48
+ /** The outbox claim endpoint — `?machine=<m4>` names the CLAIMING machine. */
49
+ export declare const A2A_OUTBOX_PATH = "/api/superd/a2a/outbox";
50
+ /** Bound for a notice summary — mirrors upstream CONTEXT_SUMMARY_MAX_CHARS (119 chars + '…' when bounded). */
51
+ export declare const A2A_NOTICE_SUMMARY_MAX = 120;
52
+ /** Wire call budget — matches mesh-link's FETCH_TIMEOUT_MS (10s). */
53
+ export declare const A2A_WIRE_TIMEOUT_MS = 10000;
54
+ /** Request-side destination: machine id (m4) + agent id (a6). */
55
+ export interface A2aWireTarget {
56
+ machine: string;
57
+ id: string;
58
+ }
59
+ /** Return-side destination: the initiator's machine/agent/session (FULL-length sessionId, D6). */
60
+ export interface A2aWireOrigin {
61
+ machine: string;
62
+ agent: string;
63
+ sessionId: string;
64
+ }
65
+ /** Prompt delivery mode (upstream `SessionPromptRequest.mode`); default 'queue' — peer semantics. */
66
+ export type A2aWireDelivery = 'queue' | 'steer';
67
+ export interface A2aNewConversationEnvelope {
68
+ kind: 'new-conversation';
69
+ to: A2aWireTarget;
70
+ /** Initiator-minted shortId; the target adopts it verbatim (mirror row). */
71
+ conversationId: string;
72
+ /** Already normalized: '[undefined]' when the operator specified none (D17). */
73
+ title: string;
74
+ message: string;
75
+ from: A2aWireOrigin;
76
+ messageId: string;
77
+ delivery?: A2aWireDelivery;
78
+ }
79
+ export interface A2aMessageEnvelope {
80
+ kind: 'message';
81
+ to: A2aWireTarget;
82
+ conversationId: string;
83
+ message: string;
84
+ from: A2aWireOrigin;
85
+ messageId: string;
86
+ delivery?: A2aWireDelivery;
87
+ }
88
+ export interface A2aStopEnvelope {
89
+ kind: 'stop';
90
+ to: A2aWireTarget;
91
+ conversationId: string;
92
+ scope: 'turn' | 'session';
93
+ }
94
+ /**
95
+ * Sender identity on a return envelope — the machine+agent that PRODUCED the
96
+ * reply/notice. Additive v1 field: absent on older peers, in which case the
97
+ * receiver falls back to registry attribution (the local row for the
98
+ * conversation points at the peer). Never the receiver's own id.
99
+ */
100
+ export interface A2aReturnSender {
101
+ readonly machine: string;
102
+ readonly agent: string;
103
+ }
104
+ export interface A2aReplyEnvelope {
105
+ kind: 'reply';
106
+ to: A2aWireOrigin;
107
+ /** Conversation shortId (requests say `conversationId`; returns say `conversation` — contract asymmetry, kept verbatim). */
108
+ conversation: string;
109
+ text: string;
110
+ messageId: string;
111
+ /** Who replied — the hosting machine + host agent (additive v1). */
112
+ sender?: A2aReturnSender;
113
+ /** D20 (additive v1): present iff the text is the runtime's TRANSCRIPTION of the peer's turn-end last output — the peer never sent it through the channel (latch never set). The delivering side renders the provenance prefix from this flag; absent = a channel reply the peer chose to send. */
114
+ transcribed?: true;
115
+ }
116
+ /**
117
+ * Why a runtime-owned notice was produced — THE cause taxonomy of the a2a
118
+ * face (0.1.7-h R2: this crosses the wire now; the additive `reason` field
119
+ * rides the notice envelope and the receiver renders it into the model-facing
120
+ * envelope line, so "turn ended, no reply" stops being three different facts
121
+ * wearing one sentence). 'peer' = an account minted by the PEER hub and
122
+ * relayed in; absent wire reason (older peer) falls back to 'peer'.
123
+ */
124
+ export type A2aNoticeReason = 'send-failed' | 'stream-dead' | 'stop' | 'aborted' | 'peer' | 'delivered' | 'undelivered';
125
+ export interface A2aNoticeEnvelope {
126
+ kind: 'notice';
127
+ to: A2aWireOrigin;
128
+ conversation: string;
129
+ /** Bounded ≤ {@link A2A_NOTICE_SUMMARY_MAX}. */
130
+ summary: string;
131
+ messageId: string;
132
+ /** Who noticed — the hosting machine + host agent (additive v1). */
133
+ sender?: A2aReturnSender;
134
+ /** R2 (additive 0.1.7-h): the cause — rendered into the model-facing notice line; absent = pre-0.1.7-h peer, receiver falls back to 'peer'. */
135
+ reason?: A2aNoticeReason;
136
+ }
137
+ export type A2aEnvelope = A2aNewConversationEnvelope | A2aMessageEnvelope | A2aStopEnvelope | A2aReplyEnvelope | A2aNoticeEnvelope;
138
+ /** The v1 outer frame — the versioned unit that crosses the wire. */
139
+ export interface A2aWireFrame {
140
+ v: 1;
141
+ envelope: A2aEnvelope;
142
+ }
143
+ /** Receipt for new-conversation / message envelopes (inbox-acceptance). */
144
+ export interface A2aSendReceipt {
145
+ outcome: 'delivered' | 'failed';
146
+ messageId: string;
147
+ conversationId?: string;
148
+ /** Target session ('' when the failure predates session resolution). */
149
+ sessionId: string;
150
+ /** Present iff outcome === 'failed'. */
151
+ error?: WireError;
152
+ }
153
+ /** Receipt for stop envelopes; 'turn-only' reports the session-scope degradation honestly (no session-close RPC upstream). */
154
+ export interface A2aStopReceipt {
155
+ stopped: boolean;
156
+ scope: 'turn' | 'turn-only';
157
+ reason?: string;
158
+ }
159
+ export type A2aWireReceipt = A2aSendReceipt | A2aStopReceipt;
160
+ /**
161
+ * Typed address-resolution failure (D7): THROWN by resolvers and wire
162
+ * helpers so the WHY survives the boundary — consumers (P4 exec, P5
163
+ * injection) branch on `kind` instead of parsing message strings. The three
164
+ * kinds name the actual problem: a typo'd a6 on a known machine is not a
165
+ * pairing issue, and an unpaired machine is not an agent problem.
166
+ *
167
+ * Postoffice R1 note: the class lives here (the protocol layer) since the
168
+ * wire helpers classify transport failures with it; `a2a-service.js`
169
+ * re-exports it unchanged for its existing consumers.
170
+ */
171
+ export declare class A2aAddressError extends Error {
172
+ /** Which failure this is. */
173
+ readonly kind: 'unknown-agent' | 'unknown-machine' | 'unreachable';
174
+ /** The address that failed to resolve. */
175
+ readonly address: A2aWireTarget;
176
+ /** The machine the blame lands on (m4; equals `address.machine`). */
177
+ readonly machine: string;
178
+ constructor(kind: 'unknown-agent' | 'unknown-machine' | 'unreachable', address: A2aWireTarget);
179
+ /** The honest branch text — one place, shared by every consumer of the error. */
180
+ static messageOf(kind: 'unknown-agent' | 'unknown-machine' | 'unreachable', address: A2aWireTarget): string;
181
+ }
182
+ /** The target address of any envelope (request `to` directly; return `to` folded agent→id). */
183
+ export declare function a2aAddressOf(envelope: A2aEnvelope): A2aWireTarget;
184
+ /** Strict v1 envelope guard: exact key sets, non-empty strings, bounded notice summaries. */
185
+ export declare function isA2aEnvelope(v: unknown): v is A2aEnvelope;
186
+ /** Strict v1 frame guard: `{v: 1, envelope}` with a valid envelope inside. */
187
+ export declare function isA2aWireFrame(v: unknown): v is A2aWireFrame;
188
+ /** Decode one unknown value as a frame; errors name the failure for the ingest side's 400s. */
189
+ export declare function decodeA2aWireFrame(v: unknown): {
190
+ ok: true;
191
+ envelope: A2aEnvelope;
192
+ } | {
193
+ ok: false;
194
+ error: string;
195
+ };
196
+ /** Wrap one envelope in the v1 outer frame. */
197
+ export declare function a2aWireFrameOf(envelope: A2aEnvelope): A2aWireFrame;
198
+ /** D17 title normalization: unspecified and empty become the placeholder; a given title stays verbatim. */
199
+ export declare function normalizeA2aTitle(title: string | undefined): string;
200
+ /** Wire-helper options: the cookie riding along, an injectable fetch, and a timeout budget. */
201
+ export interface A2aWireOptions {
202
+ /** The claiming/paired machine's request cookie (empty → header omitted, mesh-link discipline). */
203
+ cookie?: string;
204
+ fetchImpl?: typeof fetch;
205
+ timeoutMs?: number;
206
+ }
207
+ /**
208
+ * POST one envelope to a hub's a2a ingest and decode the receipt.
209
+ * Transport failures (network error, non-2xx, 401 cookie rejection) map to
210
+ * {@link A2aAddressError} kind 'unreachable' with the cause attached; a 200
211
+ * whose body is not a v1 receipt shape is a loud protocol error (never a
212
+ * guessed receipt).
213
+ */
214
+ export declare function postA2aEnvelope(origin: string, envelope: A2aEnvelope, opts?: A2aWireOptions): Promise<A2aWireReceipt>;
215
+ /**
216
+ * Claim (and clear, server-side) the envelopes buffered for THIS machine:
217
+ * `GET <origin>/api/superd/a2a/outbox?machine=<m4>` with the cookie riding
218
+ * along. Same failure taxonomy as {@link postA2aEnvelope}.
219
+ */
220
+ export declare function fetchA2aOutbox(origin: string, machine: string, opts?: A2aWireOptions): Promise<A2aEnvelope[]>;
@@ -0,0 +1,305 @@
1
+ /** The wire-protocol version this module speaks (and its guards accept). */
2
+ export const A2A_WIRE_VERSION = 1;
3
+ /** Registry/wire title placeholder when the operator specified none (D17). */
4
+ export const A2A_TITLE_PLACEHOLDER = '[undefined]';
5
+ /** The a2a ingest endpoint (fence + cookie gate ride the connection channel). */
6
+ export const A2A_ENDPOINT_PATH = '/api/superd/a2a';
7
+ /** The outbox claim endpoint — `?machine=<m4>` names the CLAIMING machine. */
8
+ export const A2A_OUTBOX_PATH = '/api/superd/a2a/outbox';
9
+ /** Bound for a notice summary — mirrors upstream CONTEXT_SUMMARY_MAX_CHARS (119 chars + '…' when bounded). */
10
+ export const A2A_NOTICE_SUMMARY_MAX = 120;
11
+ /** Wire call budget — matches mesh-link's FETCH_TIMEOUT_MS (10s). */
12
+ export const A2A_WIRE_TIMEOUT_MS = 10_000;
13
+ // ---------------------------------------------------------------- address error
14
+ /**
15
+ * Typed address-resolution failure (D7): THROWN by resolvers and wire
16
+ * helpers so the WHY survives the boundary — consumers (P4 exec, P5
17
+ * injection) branch on `kind` instead of parsing message strings. The three
18
+ * kinds name the actual problem: a typo'd a6 on a known machine is not a
19
+ * pairing issue, and an unpaired machine is not an agent problem.
20
+ *
21
+ * Postoffice R1 note: the class lives here (the protocol layer) since the
22
+ * wire helpers classify transport failures with it; `a2a-service.js`
23
+ * re-exports it unchanged for its existing consumers.
24
+ */
25
+ export class A2aAddressError extends Error {
26
+ /** Which failure this is. */
27
+ kind;
28
+ /** The address that failed to resolve. */
29
+ address;
30
+ /** The machine the blame lands on (m4; equals `address.machine`). */
31
+ machine;
32
+ constructor(kind, address) {
33
+ super(A2aAddressError.messageOf(kind, address));
34
+ this.name = 'A2aAddressError';
35
+ this.kind = kind;
36
+ this.address = address;
37
+ this.machine = address.machine;
38
+ }
39
+ /** The honest branch text — one place, shared by every consumer of the error. */
40
+ static messageOf(kind, address) {
41
+ if (kind === 'unknown-agent') {
42
+ return `a2a: unknown-agent ${address.machine}-${address.id} (no such agent on machine ${address.machine})`;
43
+ }
44
+ if (kind === 'unknown-machine') {
45
+ // 0.1.7-h R3 (dev3 first-person 0.1.7-g §6): a malformed address such as
46
+ // "no-such-agent-zzz" parses machine="no" — an error naming only "no"
47
+ // sends the model hunting for a machine it never typed. Name the
48
+ // fragment AND the full address; split-at-first-dash concat is the raw
49
+ // input, so this reconstruction is exact.
50
+ return `a2a: unknown-machine "${address.machine}" — address "${address.machine}-${address.id}" is not a paired machine (addresses are "<m4>-<a6>")`;
51
+ }
52
+ return `a2a: no gateway for ${address.machine}-${address.id} — machine unreachable or not paired`;
53
+ }
54
+ }
55
+ /** The target address of any envelope (request `to` directly; return `to` folded agent→id). */
56
+ export function a2aAddressOf(envelope) {
57
+ const to = envelope.to;
58
+ return 'id' in to ? { machine: to.machine, id: to.id } : { machine: to.machine, id: to.agent };
59
+ }
60
+ // ---------------------------------------------------------------- guards
61
+ function isRecord(v) {
62
+ return typeof v === 'object' && v !== null && !Array.isArray(v);
63
+ }
64
+ function nonEmptyString(v) {
65
+ return typeof v === 'string' && v !== '';
66
+ }
67
+ /** R2 (0.1.7-i): the wire notice reason vocabulary — mirror of the type union. */
68
+ const NOTICE_REASONS = new Set(['send-failed', 'stream-dead', 'stop', 'aborted', 'peer', 'delivered', 'undelivered']);
69
+ function isNoticeReason(v) {
70
+ return typeof v === 'string' && NOTICE_REASONS.has(v);
71
+ }
72
+ function hasExactKeys(v, keys, optional = []) {
73
+ const expected = new Set([...keys, ...optional]);
74
+ for (const key of Object.keys(v)) {
75
+ if (!expected.has(key))
76
+ return false;
77
+ }
78
+ for (const key of keys) {
79
+ if (!(key in v))
80
+ return false;
81
+ }
82
+ return true;
83
+ }
84
+ function isTarget(v) {
85
+ return isRecord(v) && hasExactKeys(v, ['machine', 'id']) && nonEmptyString(v.machine) && nonEmptyString(v.id);
86
+ }
87
+ function isReturnSender(v) {
88
+ return isRecord(v) && hasExactKeys(v, ['machine', 'agent']) && nonEmptyString(v.machine) && nonEmptyString(v.agent);
89
+ }
90
+ function isOrigin(v) {
91
+ return isRecord(v) && hasExactKeys(v, ['machine', 'agent', 'sessionId']) &&
92
+ nonEmptyString(v.machine) && nonEmptyString(v.agent) && nonEmptyString(v.sessionId);
93
+ }
94
+ function isDelivery(v) {
95
+ return v === undefined || v === 'queue' || v === 'steer';
96
+ }
97
+ /** Strict v1 envelope guard: exact key sets, non-empty strings, bounded notice summaries. */
98
+ export function isA2aEnvelope(v) {
99
+ if (!isRecord(v) || typeof v['kind'] !== 'string')
100
+ return false;
101
+ switch (v['kind']) {
102
+ case 'new-conversation':
103
+ return hasExactKeys(v, ['kind', 'to', 'conversationId', 'title', 'message', 'from', 'messageId'], ['delivery']) &&
104
+ isTarget(v['to']) && nonEmptyString(v['conversationId']) && nonEmptyString(v['title']) &&
105
+ nonEmptyString(v['message']) && isOrigin(v['from']) && nonEmptyString(v['messageId']) && isDelivery(v['delivery']);
106
+ case 'message':
107
+ return hasExactKeys(v, ['kind', 'to', 'conversationId', 'message', 'from', 'messageId'], ['delivery']) &&
108
+ isTarget(v['to']) && nonEmptyString(v['conversationId']) && nonEmptyString(v['message']) &&
109
+ isOrigin(v['from']) && nonEmptyString(v['messageId']) && isDelivery(v['delivery']);
110
+ case 'stop':
111
+ return hasExactKeys(v, ['kind', 'to', 'conversationId', 'scope']) &&
112
+ isTarget(v['to']) && nonEmptyString(v['conversationId']) && (v['scope'] === 'turn' || v['scope'] === 'session');
113
+ case 'reply':
114
+ return hasExactKeys(v, ['kind', 'to', 'conversation', 'text', 'messageId'], ['sender', 'transcribed']) &&
115
+ isOrigin(v['to']) && nonEmptyString(v['conversation']) && nonEmptyString(v['text']) && nonEmptyString(v['messageId']) &&
116
+ (isReturnSender(v['sender']) || v['sender'] === undefined) &&
117
+ (v['transcribed'] === undefined || v['transcribed'] === true);
118
+ case 'notice':
119
+ // 0.1.7-i R2: `reason` rides additively (the send-failed notice made it
120
+ // load-bearing) — older peers omit it, the guard validates the vocabulary.
121
+ return hasExactKeys(v, ['kind', 'to', 'conversation', 'summary', 'messageId'], ['sender', 'reason']) &&
122
+ isOrigin(v['to']) && nonEmptyString(v['conversation']) && nonEmptyString(v['summary']) &&
123
+ typeof v['summary'] === 'string' && v['summary'].length <= A2A_NOTICE_SUMMARY_MAX && nonEmptyString(v['messageId']) &&
124
+ (isReturnSender(v['sender']) || v['sender'] === undefined) &&
125
+ (isNoticeReason(v['reason']) || v['reason'] === undefined);
126
+ default:
127
+ return false;
128
+ }
129
+ }
130
+ /** Strict v1 frame guard: `{v: 1, envelope}` with a valid envelope inside. */
131
+ export function isA2aWireFrame(v) {
132
+ return isRecord(v) && v['v'] === A2A_WIRE_VERSION && isA2aEnvelope(v['envelope']);
133
+ }
134
+ /** Decode one unknown value as a frame; errors name the failure for the ingest side's 400s. */
135
+ export function decodeA2aWireFrame(v) {
136
+ if (!isRecord(v))
137
+ return { ok: false, error: 'a2a-wire: frame must be a JSON object' };
138
+ if (v['v'] !== A2A_WIRE_VERSION) {
139
+ return { ok: false, error: `a2a-wire: unsupported frame version ${JSON.stringify(v['v'])} (protocol v${A2A_WIRE_VERSION})` };
140
+ }
141
+ if (!isA2aEnvelope(v['envelope'])) {
142
+ return { ok: false, error: `a2a-wire: malformed v${A2A_WIRE_VERSION} envelope (strict shape check failed)` };
143
+ }
144
+ return { ok: true, envelope: v['envelope'] };
145
+ }
146
+ /** Wrap one envelope in the v1 outer frame. */
147
+ export function a2aWireFrameOf(envelope) {
148
+ return { v: A2A_WIRE_VERSION, envelope };
149
+ }
150
+ /** D17 title normalization: unspecified and empty become the placeholder; a given title stays verbatim. */
151
+ export function normalizeA2aTitle(title) {
152
+ return title === undefined || title === '' ? A2A_TITLE_PLACEHOLDER : title;
153
+ }
154
+ // ---------------------------------------------------------------- receipts (guards)
155
+ /**
156
+ * D28 rollout tolerance (0.1.8-b): the pre-0.1.8 packed `injected` names the
157
+ * SAME fact as `delivered`. A mesh always has mixed-version windows during a
158
+ * rollout, so an unknown-but-legible outcome must DEGRADE, never throw. The
159
+ * guard accepts both; the boundary normalizes the legacy word.
160
+ */
161
+ const LEGACY_RECEIPT_OUTCOMES = new Set(['injected']);
162
+ function isReceiptOutcome(v) {
163
+ return v === 'delivered' || v === 'failed' || (typeof v === 'string' && LEGACY_RECEIPT_OUTCOMES.has(v));
164
+ }
165
+ /** Rewrite a legacy receipt outcome in place (the parsed body is ours after the guard). */
166
+ function normalizeReceiptOutcome(body) {
167
+ const raw = body.outcome;
168
+ if (typeof raw === 'string' && LEGACY_RECEIPT_OUTCOMES.has(raw))
169
+ body.outcome = 'delivered';
170
+ }
171
+ function isWireError(v) {
172
+ return isRecord(v) && nonEmptyString(v['code']) && nonEmptyString(v['message']);
173
+ }
174
+ function isSendReceipt(v) {
175
+ if (!isRecord(v))
176
+ return false;
177
+ if (!hasExactKeys(v, ['outcome', 'messageId', 'sessionId'], ['conversationId', 'error']))
178
+ return false;
179
+ if (!isReceiptOutcome(v['outcome']))
180
+ return false;
181
+ if (!nonEmptyString(v['messageId']))
182
+ return false;
183
+ if (typeof v['sessionId'] !== 'string')
184
+ return false;
185
+ if ('conversationId' in v && !nonEmptyString(v['conversationId']))
186
+ return false;
187
+ if ('error' in v && !isWireError(v['error']))
188
+ return false;
189
+ return true;
190
+ }
191
+ function isStopReceipt(v) {
192
+ if (!isRecord(v))
193
+ return false;
194
+ if (!hasExactKeys(v, ['stopped', 'scope'], ['reason']))
195
+ return false;
196
+ if (typeof v['stopped'] !== 'boolean')
197
+ return false;
198
+ if (v['scope'] !== 'turn' && v['scope'] !== 'turn-only')
199
+ return false;
200
+ if ('reason' in v && !nonEmptyString(v['reason']))
201
+ return false;
202
+ return true;
203
+ }
204
+ function errMsg(err) {
205
+ return err instanceof Error ? err.message : String(err);
206
+ }
207
+ /** Throw one classified unreachable failure with the underlying cause attached (the canonical message stays the classification's). */
208
+ function throwUnreachable(envelope, cause) {
209
+ const error = new A2aAddressError('unreachable', a2aAddressOf(envelope));
210
+ error.cause = cause;
211
+ throw error;
212
+ }
213
+ /**
214
+ * POST one envelope to a hub's a2a ingest and decode the receipt.
215
+ * Transport failures (network error, non-2xx, 401 cookie rejection) map to
216
+ * {@link A2aAddressError} kind 'unreachable' with the cause attached; a 200
217
+ * whose body is not a v1 receipt shape is a loud protocol error (never a
218
+ * guessed receipt).
219
+ */
220
+ export async function postA2aEnvelope(origin, envelope, opts = {}) {
221
+ const fetchImpl = opts.fetchImpl ?? globalThis.fetch;
222
+ const cookie = opts.cookie ?? '';
223
+ let res;
224
+ try {
225
+ res = await fetchImpl(`${origin}${A2A_ENDPOINT_PATH}`, {
226
+ method: 'POST',
227
+ headers: { 'content-type': 'application/json', ...(cookie === '' ? {} : { cookie }) },
228
+ body: JSON.stringify(a2aWireFrameOf(envelope)),
229
+ signal: AbortSignal.timeout(opts.timeoutMs ?? A2A_WIRE_TIMEOUT_MS),
230
+ });
231
+ }
232
+ catch (err) {
233
+ throwUnreachable(envelope, err);
234
+ }
235
+ if (res.status !== 200) {
236
+ throwUnreachable(envelope, `ingest POST answered status ${res.status}${res.status === 401 ? ' (cookie rejected)' : ''}`);
237
+ }
238
+ let body;
239
+ try {
240
+ body = await res.json();
241
+ }
242
+ catch (err) {
243
+ throw new Error(`a2a-wire: ingest receipt from ${origin} is not JSON — ${errMsg(err)}`);
244
+ }
245
+ if (isSendReceipt(body)) {
246
+ normalizeReceiptOutcome(body);
247
+ return body;
248
+ }
249
+ if (isStopReceipt(body))
250
+ return body;
251
+ throw new Error(`a2a-wire: ingest receipt from ${origin} is not a v1 receipt shape — ${JSON.stringify(body)}`);
252
+ }
253
+ /** The outbox claim body: `{v: 1, envelopes}` — envelopes validated on arrival, fail-loud on any malformed entry. */
254
+ function decodeOutboxBody(v, origin) {
255
+ if (!isRecord(v) || v['v'] !== A2A_WIRE_VERSION || !Array.isArray(v['envelopes'])) {
256
+ throw new Error(`a2a-wire: outbox body from ${origin} is not {v: 1, envelopes: []} — ${JSON.stringify(v)}`);
257
+ }
258
+ const envelopes = [];
259
+ for (const [index, entry] of v['envelopes'].entries()) {
260
+ if (!isA2aEnvelope(entry)) {
261
+ throw new Error(`a2a-wire: outbox entry #${index} from ${origin} is not a v1 envelope — ${JSON.stringify(entry)}`);
262
+ }
263
+ envelopes.push(entry);
264
+ }
265
+ return envelopes;
266
+ }
267
+ /**
268
+ * Claim (and clear, server-side) the envelopes buffered for THIS machine:
269
+ * `GET <origin>/api/superd/a2a/outbox?machine=<m4>` with the cookie riding
270
+ * along. Same failure taxonomy as {@link postA2aEnvelope}.
271
+ */
272
+ export async function fetchA2aOutbox(origin, machine, opts = {}) {
273
+ if (!nonEmptyString(machine)) {
274
+ throw new Error('a2a-wire: outbox claim requires a non-empty machine id');
275
+ }
276
+ const fetchImpl = opts.fetchImpl ?? globalThis.fetch;
277
+ const cookie = opts.cookie ?? '';
278
+ let res;
279
+ try {
280
+ res = await fetchImpl(`${origin}${A2A_OUTBOX_PATH}?machine=${encodeURIComponent(machine)}`, {
281
+ method: 'GET',
282
+ headers: cookie === '' ? {} : { cookie },
283
+ signal: AbortSignal.timeout(opts.timeoutMs ?? A2A_WIRE_TIMEOUT_MS),
284
+ });
285
+ }
286
+ catch (err) {
287
+ const error = new A2aAddressError('unreachable', { machine, id: '' });
288
+ error.cause = err;
289
+ throw error;
290
+ }
291
+ if (res.status !== 200) {
292
+ const error = new A2aAddressError('unreachable', { machine, id: '' });
293
+ error.cause =
294
+ `outbox claim answered status ${res.status}${res.status === 401 ? ' (cookie rejected)' : ''}`;
295
+ throw error;
296
+ }
297
+ let body;
298
+ try {
299
+ body = await res.json();
300
+ }
301
+ catch (err) {
302
+ throw new Error(`a2a-wire: outbox body from ${origin} is not JSON — ${errMsg(err)}`);
303
+ }
304
+ return decodeOutboxBody(body, origin);
305
+ }
@@ -0,0 +1,10 @@
1
+ /** The persisted map: roster key (`native` for ctx0, else the adapter key) → a6 id. */
2
+ export type AgentIdMap = Record<string, string>;
3
+ /** Read the id map; a missing or unreadable/corrupt file degrades to `{}` (never throws). */
4
+ export declare function agentIdMap(home: string): AgentIdMap;
5
+ /**
6
+ * Lazily mint + persist one agent's a6 id, deduped against every id already in
7
+ * the map; an already-minted label is returned unchanged (idempotent). The
8
+ * `native` label is minted like any other.
9
+ */
10
+ export declare function ensureAgentId(home: string, label: string): string;
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Agent short ids (mesh P1, spec §3.2): one stable a6 id per roster row,
3
+ * owner-minted and persisted in `<home>/superd/agent-ids.json`.
4
+ *
5
+ * Pure module — no cordis dependencies. Same persistence discipline as
6
+ * machines.ts (0600, atomic tmp+rename writes, no in-process cache): every
7
+ * mint is a read-modify-write of the whole map, deduped against every id
8
+ * already in the file. A missing or unreadable/corrupt file reads as `{}`
9
+ * (never throws), so a corrupted map simply re-mints on next need. The mint
10
+ * itself is machines.ts's `mintShortId` (shared alphabet + collision re-roll);
11
+ * this module only adds the a6 length and its own store.
12
+ */
13
+ import { chmodSync, mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs';
14
+ import { join } from 'node:path';
15
+ import { mintShortId } from './machines.js';
16
+ function agentIdsPath(home) {
17
+ return join(home, 'superd', 'agent-ids.json');
18
+ }
19
+ /** Read the id map; a missing or unreadable/corrupt file degrades to `{}` (never throws). */
20
+ export function agentIdMap(home) {
21
+ let raw;
22
+ try {
23
+ raw = readFileSync(agentIdsPath(home), 'utf8');
24
+ }
25
+ catch {
26
+ return {};
27
+ }
28
+ let parsed;
29
+ try {
30
+ parsed = JSON.parse(raw);
31
+ }
32
+ catch {
33
+ return {};
34
+ }
35
+ if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed))
36
+ return {};
37
+ const out = {};
38
+ for (const [key, id] of Object.entries(parsed)) {
39
+ if (key !== '' && typeof id === 'string' && id !== '')
40
+ out[key] = id;
41
+ }
42
+ return out;
43
+ }
44
+ /** Persist the whole map: recursive mkdir, tmp write, chmod 0600, atomic rename. */
45
+ function saveAgentIds(home, map) {
46
+ const dir = join(home, 'superd');
47
+ mkdirSync(dir, { recursive: true });
48
+ const target = agentIdsPath(home);
49
+ const tmp = `${target}.tmp`;
50
+ writeFileSync(tmp, JSON.stringify(map, null, 2) + '\n', { encoding: 'utf8', mode: 0o600 });
51
+ chmodSync(tmp, 0o600);
52
+ renameSync(tmp, target);
53
+ }
54
+ /**
55
+ * Lazily mint + persist one agent's a6 id, deduped against every id already in
56
+ * the map; an already-minted label is returned unchanged (idempotent). The
57
+ * `native` label is minted like any other.
58
+ */
59
+ export function ensureAgentId(home, label) {
60
+ const map = agentIdMap(home);
61
+ const existing = map[label];
62
+ if (existing !== undefined)
63
+ return existing;
64
+ const id = mintShortId(6, Object.values(map));
65
+ map[label] = id;
66
+ saveAgentIds(home, map);
67
+ return id;
68
+ }