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
@@ -1,28 +1,30 @@
1
1
  /**
2
2
  * The hub's own agent roster row (AW-B, URL scheme).
3
3
  *
4
- * The selector is pure navigation: every runtime owns a mount path, so the UI
5
- * needs exactly one fact — which paths exist — and nothing else. The hub bakes
6
- * that fact into every page it can reach (ctx0's index and every mounted
7
- * world's index), so no world implements any selector code and no server-side
8
- * selection value exists anywhere.
4
+ * The selector is pure navigation: every runtime owns an addressing surface,
5
+ * so the UI needs exactly one fact — which surfaces exist — and nothing else.
6
+ * The hub bakes that fact into every page it can reach (ctx0's index and
7
+ * every mounted world's index), so no world implements any selector code and
8
+ * no server-side selection value exists anywhere.
9
9
  *
10
- * / → native DSH
11
- * /omp/ → the OMP world's mount
12
- * /codex/ → the Codex world's mount
10
+ * / → native DSH
11
+ * /omp/ → the OMP world's mount (local worlds: mount roots)
12
+ * /hub/?agent=<a6> → federated rows (mesh P2 Task 3; demux family)
13
13
  */
14
14
  import type { IndexInjection } from '@deepseek-ai/dsh-host-webserver';
15
15
  /** One selectable runtime: its key, its label, the path that addresses it, its machine. */
16
16
  export interface AgentLink {
17
- /** `native` for ctx0, otherwise the adapter's runtime key. */
17
+ /** Stable short id (a6) minted once per home (`superd/agent-ids.json`); for federated rows the peer-minted a6 adopted verbatim (never re-minted locally). */
18
+ id: string;
19
+ /** `native` for ctx0, the adapter's runtime key for local worlds, or the federated row's a6. */
18
20
  key: string;
19
- /** Chip/menu label (`DSH`, `OMP`, `Codex`, …). */
21
+ /** Chip/menu label (`DSH`, `OMP`, `Codex · dev3`, …). */
20
22
  label: string;
21
- /** Mount root, always trailing-slashed except the native `/`. */
23
+ /** Addressing surface, always trailing-slashed except the native `/`: a mount root for local worlds, `/hub/?agent=<a6>` for federated rows (informational chip only — switch traffic rides the `/hub` demux; there is no navigable page since T-C). */
22
24
  path: string;
23
- /** Machine the runtime runs on — the Hub UI's grouping key. */
25
+ /** Machine the runtime runs on — the Hub UI's grouping key: `local`, or the owning machine's m4 for federated rows. */
24
26
  machine: string;
25
- /** Whether the world's data plane is up (spawn resolved). Native is always ready. */
27
+ /** Whether the world's data plane is up (spawn resolved / probe reachable). Native is always ready. */
26
28
  ready: boolean;
27
29
  /** Whether the world's spawn REJECTED (mount stays for diagnosis). */
28
30
  failed: boolean;
@@ -30,11 +32,20 @@ export interface AgentLink {
30
32
  /** The global this module publishes the roster under. */
31
33
  export declare const AGENT_ROSTER_GLOBAL = "__DSH_AGENT_ROSTER__";
32
34
  /**
33
- * Every runtime that currently owns a mount, native first.
35
+ * Every runtime that currently owns an addressable surface — native first,
36
+ * then LOCAL worlds in roster order, then FEDERATED rows sorted by machine
37
+ * label (the paired machine's display label from its `machines.json` row; the
38
+ * m4 itself when no row survives) then agent label (key as the final tiebreak).
34
39
  *
35
- * A registered agent without a host mount has no addressable path yet (its
36
- * world is still spawning) and is therefore not selectable.
40
+ * A LOCAL agent without a host mount has no addressable path yet (its world
41
+ * is still spawning) and is therefore not selectable. A FEDERATED row
42
+ * (`machine` set, mesh P2 Task 3) needs NO mount: it is addressed by its a6
43
+ * through the `/hub` demux family, and the a6 IS the row's stable id —
44
+ * owner-minted at the peer and adopted verbatim, never re-minted locally.
45
+ * The id store lives under `home` — resolved per call (world-join precedent,
46
+ * zero module state); callers that already hold a home (rpc.ts) pass it through.
47
+ * @param home - the hub home backing the a6 id store and machine labels; defaults to `resolveDshHome()`.
37
48
  */
38
- export declare function agentLinks(): AgentLink[];
49
+ export declare function agentLinks(home?: string): AgentLink[];
39
50
  /** The structured index row that publishes {@link agentLinks} to the page. */
40
- export declare function agentRosterRow(): IndexInjection;
51
+ export declare function agentRosterRow(home?: string): IndexInjection;
@@ -1,29 +1,68 @@
1
+ import { resolveDshHome } from '@deepseek-ai/dsh-home-paths';
1
2
  import { listAgents } from './roster.js';
3
+ import { ensureAgentId } from './agent-ids.js';
2
4
  import { takeHostMount } from './world-host.js';
3
- /** Machine of every roster row today — the local constant; the extension point a multi-machine registry replaces. */
5
+ import { listRemotes } from './machines.js';
6
+ /** Machine of every LOCAL roster row; federated rows carry their owning m4 (mesh P2 Task 3). */
4
7
  const LOCAL_MACHINE = 'local';
5
- /** The native runtime's synthetic entry (no adapter plugin backs it). */
6
- const NATIVE = { key: 'native', label: 'DSH', path: '/', machine: LOCAL_MACHINE, ready: true, failed: false };
8
+ /** The native runtime's synthetic base row (no adapter plugin backs it); its
9
+ * id is minted per-home like every other LOCAL row's. */
10
+ const NATIVE_BASE = { key: 'native', label: 'DSH', path: '/', machine: LOCAL_MACHINE, ready: true, failed: false };
7
11
  /** The global this module publishes the roster under. */
8
12
  export const AGENT_ROSTER_GLOBAL = '__DSH_AGENT_ROSTER__';
9
13
  /**
10
- * Every runtime that currently owns a mount, native first.
14
+ * Every runtime that currently owns an addressable surface — native first,
15
+ * then LOCAL worlds in roster order, then FEDERATED rows sorted by machine
16
+ * label (the paired machine's display label from its `machines.json` row; the
17
+ * m4 itself when no row survives) then agent label (key as the final tiebreak).
11
18
  *
12
- * A registered agent without a host mount has no addressable path yet (its
13
- * world is still spawning) and is therefore not selectable.
19
+ * A LOCAL agent without a host mount has no addressable path yet (its world
20
+ * is still spawning) and is therefore not selectable. A FEDERATED row
21
+ * (`machine` set, mesh P2 Task 3) needs NO mount: it is addressed by its a6
22
+ * through the `/hub` demux family, and the a6 IS the row's stable id —
23
+ * owner-minted at the peer and adopted verbatim, never re-minted locally.
24
+ * The id store lives under `home` — resolved per call (world-join precedent,
25
+ * zero module state); callers that already hold a home (rpc.ts) pass it through.
26
+ * @param home - the hub home backing the a6 id store and machine labels; defaults to `resolveDshHome()`.
14
27
  */
15
- export function agentLinks() {
16
- const links = [NATIVE];
28
+ export function agentLinks(home = resolveDshHome()) {
29
+ const links = [{ id: ensureAgentId(home, 'native'), ...NATIVE_BASE }];
30
+ const locals = [];
31
+ const federated = [];
32
+ const machineLabels = new Map(listRemotes(home).map((row) => [row.id, row.label !== '' ? row.label : row.name]));
17
33
  for (const agent of listAgents()) {
18
- const mount = takeHostMount(agent.key);
19
- if (mount === undefined)
20
- continue;
21
- const path = mount.labelPath.endsWith('/') ? mount.labelPath : `${mount.labelPath}/`;
22
- links.push({ key: agent.key, label: agent.label, path, machine: LOCAL_MACHINE, ready: agent.ready === true, failed: agent.failed === true });
34
+ if ((agent.machine ?? LOCAL_MACHINE) === LOCAL_MACHINE) {
35
+ const mount = takeHostMount(agent.key);
36
+ if (mount === undefined)
37
+ continue;
38
+ const path = mount.labelPath.endsWith('/') ? mount.labelPath : `${mount.labelPath}/`;
39
+ locals.push({ id: ensureAgentId(home, agent.key), key: agent.key, label: agent.label, path, machine: LOCAL_MACHINE, ready: agent.ready === true, failed: agent.failed === true });
40
+ }
41
+ else {
42
+ federated.push({
43
+ id: agent.key,
44
+ key: agent.key,
45
+ label: agent.label,
46
+ path: `/hub/?agent=${encodeURIComponent(agent.key)}`,
47
+ machine: agent.machine,
48
+ ready: agent.ready === true,
49
+ failed: agent.failed === true,
50
+ });
51
+ }
23
52
  }
53
+ federated.sort((a, b) => {
54
+ const machineA = machineLabels.get(a.machine) ?? a.machine;
55
+ const machineB = machineLabels.get(b.machine) ?? b.machine;
56
+ if (machineA !== machineB)
57
+ return machineA < machineB ? -1 : 1;
58
+ if (a.label !== b.label)
59
+ return a.label < b.label ? -1 : 1;
60
+ return a.key < b.key ? -1 : a.key > b.key ? 1 : 0;
61
+ });
62
+ links.push(...locals, ...federated);
24
63
  return links;
25
64
  }
26
65
  /** The structured index row that publishes {@link agentLinks} to the page. */
27
- export function agentRosterRow() {
28
- return { kind: 'global', name: AGENT_ROSTER_GLOBAL, value: { agents: agentLinks() } };
66
+ export function agentRosterRow(home = resolveDshHome()) {
67
+ return { kind: 'global', name: AGENT_ROSTER_GLOBAL, value: { agents: agentLinks(home) } };
29
68
  }
@@ -0,0 +1,119 @@
1
+ import { type MachineRow } from './machines.js';
2
+ import { type ProbeResult } from './mesh-link.js';
3
+ import { type A2aAddress, type A2aGateway } from './a2a-service.js';
4
+ import type { A2aIngest } from './a2a-ingest.js';
5
+ /** One collected session row — exactly what session/list exposes (see the header's upstream anchor). */
6
+ export interface AgentStatusRow {
7
+ /** Full DSH session id (the wire identity; R6 truncates for display). */
8
+ sessionId: string;
9
+ /** `SessionSummary.updatedAt` (epoch ms). */
10
+ mtime: number;
11
+ /** `running` mapped directly: true → 'busy', false → 'idle', unobservable → 'unknown'. */
12
+ status: 'busy' | 'idle' | 'unknown';
13
+ /** `SessionSummary.agentAvailable` — whether the session owns a live Agent. */
14
+ agentAvailable: boolean;
15
+ }
16
+ /** One workspace join entry — exactly the title-join fields the R6 ruling needs (WorkspaceView, workspace-controller types.ts:17-29). */
17
+ export interface AgentWorkspaceEntry {
18
+ workspaceId: string;
19
+ /** User-visible title (non-null upstream — the session fallback display title). */
20
+ title: string;
21
+ sessionIds: string[];
22
+ }
23
+ /** One agent's collected window (bounded, payload order kept). */
24
+ export interface AgentStatusAgent {
25
+ a6: string;
26
+ rows: AgentStatusRow[];
27
+ /**
28
+ * Workspace membership for the title join (R6 title-source ruling): the
29
+ * `workspace/follow` baseline's WorkspaceViews, trimmed to the join fields.
30
+ * Absent when the workspace leg failed (the error note says so).
31
+ */
32
+ workspaces?: AgentWorkspaceEntry[];
33
+ /** Per-agent collection failure (face miss / session-list rejection / workspace-follow death). */
34
+ error?: string;
35
+ }
36
+ /** One machine's snapshot. */
37
+ export interface AgentStatusMachine {
38
+ /** Epoch ms of the LAST SUCCESSFUL collection (kept on failure — the data's age, not the attempt's). */
39
+ collectedAt: number;
40
+ agents: AgentStatusAgent[];
41
+ /** Machine-level collection failure note (set on failure, cleared on success). */
42
+ error?: string;
43
+ }
44
+ /** The whole cache file. */
45
+ export interface AgentStatusFile {
46
+ machines: Record<string, AgentStatusMachine>;
47
+ }
48
+ /** Rows kept per agent (payload order; the D18 ≤5 window is R6's query-time concern). */
49
+ export declare const AGENT_STATUS_ROWS_MAX = 32;
50
+ /** Read the cache; a missing or unreadable/corrupt file degrades to empty (never throws). */
51
+ export declare function loadAgentStatus(home: string): AgentStatusFile;
52
+ /** Record a SUCCESSFUL machine collection — replaces the rows wholesale (empty included), clears the error. */
53
+ export declare function recordMachineCollection(home: string, machine: string, agents: AgentStatusAgent[], now?: () => number): void;
54
+ /**
55
+ * Record a machine-level collection FAILURE — the previous snapshot stays
56
+ * (rows AND its collectedAt: the data's age is what it is) and the error
57
+ * note says what happened.
58
+ */
59
+ export declare function recordMachineFailure(home: string, machine: string, error: string): void;
60
+ /**
61
+ * `workspace/follow` — the workspace face's reconnect-safe STATE verb
62
+ * (workspace-controller index.ts:199 `@Remote({ mode: 'stream' }) follow(signal)`:
63
+ * signal-only, no request parameter — the wire payload is a bare `{args:{}}`).
64
+ * Every generation starts with EXACTLY ONE
65
+ * `{type:'baseline', value:{items: WorkspaceView[], ...}}` frame; increments
66
+ * after it are mutations we do not consume. All other verbs on the controller
67
+ * are mutations (create / rename / delete / insert-before / archive / pin) — excluded.
68
+ */
69
+ export declare const WORKSPACE_FOLLOW = "workspace/follow";
70
+ /** Workspaces kept per agent (join-sized; a host has few). */
71
+ export declare const AGENT_WORKSPACES_MAX = 32;
72
+ /** How a remote agent's presentation-lane face is resolved (injectable for tests). */
73
+ export type RemoteFaceOf = (a6: string) => A2aGateway | undefined;
74
+ /** The production resolver: the federation memo first, the shared target registry second (the /hub demux order). */
75
+ export declare function federatedFaceOf(a6: string): A2aGateway | undefined;
76
+ /**
77
+ * Collect one REACHABLE machine's session lists over the presentation lane.
78
+ * Never throws: a machine-level failure records the error and keeps the
79
+ * previous snapshot; a per-agent failure (no face, rejected list) marks that
80
+ * agent with an error and empty rows — an honest miss, never a guess.
81
+ */
82
+ export declare function collectRemoteMachine(home: string, remote: MachineRow, probe: ProbeResult, opts?: {
83
+ faceOf?: RemoteFaceOf;
84
+ }): Promise<void>;
85
+ /**
86
+ * Collect the LOCAL machine's agents (worlds + native — every a6 in the id
87
+ * store) through their in-process faces, resolved by the a2a-host local leg.
88
+ */
89
+ export declare function collectLocalMachine(home: string, gatewayFor: (addr: A2aAddress) => A2aGateway): Promise<void>;
90
+ /**
91
+ * Claim (and clear, peer-side) the envelopes buffered for THIS machine and
92
+ * deliver each through the SAME ingest handler the HTTP endpoint uses — in
93
+ * order, no dedupe layer (claim&clear is the peer's guarantee). A per-
94
+ * envelope failure degrades; the claim itself throwing propagates to the
95
+ * sweep's leg-level swallow.
96
+ */
97
+ export declare function pullOutboxFor(home: string, remote: MachineRow, ingest: A2aIngest, opts?: {
98
+ fetchImpl?: typeof fetch;
99
+ }): Promise<number>;
100
+ /** The assembled legs a sweep runs (the shape mesh-link's SweepCollectors takes). */
101
+ export interface StatusSweepLegs {
102
+ onLocalCollect(): Promise<void>;
103
+ onRemoteProbed(info: {
104
+ remote: MachineRow;
105
+ probe: ProbeResult;
106
+ }): Promise<void>;
107
+ }
108
+ /**
109
+ * Compose the dual-responsibility legs over one assembly (a2a-row's single
110
+ * resolver + shared ingest). Every leg degrades internally — a failure never
111
+ * breaks probe accounting or the sweep loop.
112
+ */
113
+ export declare function statusSweepLegs(deps: {
114
+ home: string;
115
+ gatewayFor: (addr: A2aAddress) => A2aGateway;
116
+ ingest: A2aIngest;
117
+ remoteFaceOf?: RemoteFaceOf;
118
+ fetchImpl?: typeof fetch;
119
+ }): StatusSweepLegs;
@@ -0,0 +1,386 @@
1
+ /**
2
+ * Agent status cache + the sweep's dual responsibility legs (postoffice R5;
3
+ * D18, plan 2026-09-28-machine-mesh-postoffice-rework task board).
4
+ *
5
+ * THE CACHE (`<home>/superd/agent-status.json`, 0600 atomic writes —
6
+ * machines.ts discipline): per machine `{collectedAt, agents: [{a6, rows}]}`,
7
+ * flat and additive. Rows carry EXACTLY what the session-list payload
8
+ * exposes — upstream `SessionSummary`
9
+ * (packages/api/session-controller/src/types.ts:177) is
10
+ * `{agentAvailable, sessionId, updatedAt, running, blank, parentSessionId?,
11
+ * origin?, cwd?, projections?}`: status maps `running` directly (true →
12
+ * 'busy', false → 'idle'), anything else → 'unknown' (Orchestra unknown
13
+ * discipline — never guess). NOTE: session/list carries NO title — the D18
14
+ * title overlay is a QUERY-time concern (R6), not a collection fact.
15
+ *
16
+ * THE LEGS (both ride the EXISTING reprobe sweep — mesh-link's single timer,
17
+ * no second probing path):
18
+ * - OUTBOX PULL: after each SUCCESSFUL reprobe, claim the peer's buffered
19
+ * envelopes (`GET /api/superd/a2a/outbox?machine=<selfId>`, cookie 随行)
20
+ * and feed each into the SAME ingest handler the HTTP endpoint uses —
21
+ * claim&clear semantics, in order, no dedupe layer. A pull failure
22
+ * degrades (log, keep sweeping).
23
+ * - SESSION-LIST COLLECTION: per reachable machine, every roster agent's
24
+ * session list through the PRESENTATION lane (the federation memo's wire
25
+ * faces — D16 presentation exemption, the same lane the /hub family
26
+ * browses); local agents (worlds + native) collect the same shape through
27
+ * their in-process faces. Failure retention: a machine's rows are only
28
+ * replaced by a SUCCESSFUL collection (empty included); a failure keeps
29
+ * the previous snapshot and records an error note.
30
+ */
31
+ import { chmodSync, mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs';
32
+ import { join } from 'node:path';
33
+ import { ensureSelf } from './machines.js';
34
+ import { cookieOf } from './mesh-link.js';
35
+ import { agentIdMap } from './agent-ids.js';
36
+ import { fetchA2aOutbox } from './a2a-wire.js';
37
+ import { WIRE } from './wire-endpoints.js';
38
+ import { sessionRpc } from './a2a-service.js';
39
+ import { federatedGatewayOf } from './mesh-federate.js';
40
+ import { getTarget } from './targets.js';
41
+ /** Rows kept per agent (payload order; the D18 ≤5 window is R6's query-time concern). */
42
+ export const AGENT_STATUS_ROWS_MAX = 32;
43
+ function statusPath(home) {
44
+ return join(home, 'superd', 'agent-status.json');
45
+ }
46
+ function isRecord(v) {
47
+ return typeof v === 'object' && v !== null && !Array.isArray(v);
48
+ }
49
+ function coerceFile(v) {
50
+ const out = { machines: {} };
51
+ const machines = isRecord(v) ? v['machines'] : undefined;
52
+ if (!isRecord(machines))
53
+ return out;
54
+ for (const [m4, raw] of Object.entries(machines)) {
55
+ if (m4 === '' || !isRecord(raw) || !Array.isArray(raw['agents']))
56
+ continue;
57
+ const agents = [];
58
+ for (const a of raw['agents']) {
59
+ if (!isRecord(a) || typeof a['a6'] !== 'string' || a['a6'] === '' || !Array.isArray(a['rows']))
60
+ continue;
61
+ let workspaces;
62
+ if (Array.isArray(a['workspaces'])) {
63
+ workspaces = [];
64
+ for (const w of a['workspaces']) {
65
+ if (!isRecord(w) || typeof w['workspaceId'] !== 'string' || w['workspaceId'] === '' || typeof w['title'] !== 'string')
66
+ continue;
67
+ workspaces.push({
68
+ workspaceId: w['workspaceId'],
69
+ title: w['title'],
70
+ sessionIds: Array.isArray(w['sessionIds']) ? w['sessionIds'].filter((sid) => typeof sid === 'string') : [],
71
+ });
72
+ }
73
+ }
74
+ const rows = [];
75
+ for (const r of a['rows']) {
76
+ if (!isRecord(r) || typeof r['sessionId'] !== 'string' || r['sessionId'] === '')
77
+ continue;
78
+ const status = r['status'] === 'busy' || r['status'] === 'idle' || r['status'] === 'unknown' ? r['status'] : 'unknown';
79
+ rows.push({
80
+ sessionId: r['sessionId'],
81
+ mtime: typeof r['mtime'] === 'number' ? r['mtime'] : 0,
82
+ status,
83
+ agentAvailable: r['agentAvailable'] === true,
84
+ });
85
+ }
86
+ agents.push({
87
+ a6: a['a6'],
88
+ rows,
89
+ ...(workspaces === undefined ? {} : { workspaces }),
90
+ ...(typeof a['error'] === 'string' && a['error'] !== '' ? { error: a['error'] } : {}),
91
+ });
92
+ }
93
+ const machine = {
94
+ collectedAt: typeof raw['collectedAt'] === 'number' ? raw['collectedAt'] : 0,
95
+ agents,
96
+ };
97
+ if (typeof raw['error'] === 'string' && raw['error'] !== '')
98
+ machine.error = raw['error'];
99
+ out.machines[m4] = machine;
100
+ }
101
+ return out;
102
+ }
103
+ /** Read the cache; a missing or unreadable/corrupt file degrades to empty (never throws). */
104
+ export function loadAgentStatus(home) {
105
+ let raw;
106
+ try {
107
+ raw = readFileSync(statusPath(home), 'utf8');
108
+ }
109
+ catch {
110
+ return { machines: {} };
111
+ }
112
+ try {
113
+ return coerceFile(JSON.parse(raw));
114
+ }
115
+ catch {
116
+ return { machines: {} };
117
+ }
118
+ }
119
+ /** Persist the cache: recursive mkdir, tmp write, chmod 0600, atomic rename. */
120
+ function saveAgentStatus(home, file) {
121
+ const dir = join(home, 'superd');
122
+ mkdirSync(dir, { recursive: true });
123
+ const target = statusPath(home);
124
+ const tmp = `${target}.tmp`;
125
+ writeFileSync(tmp, JSON.stringify(file, null, 2) + '\n', { encoding: 'utf8', mode: 0o600 });
126
+ chmodSync(tmp, 0o600);
127
+ renameSync(tmp, target);
128
+ }
129
+ /** Record a SUCCESSFUL machine collection — replaces the rows wholesale (empty included), clears the error. */
130
+ export function recordMachineCollection(home, machine, agents, now = Date.now) {
131
+ if (machine === '')
132
+ return;
133
+ const file = loadAgentStatus(home);
134
+ file.machines[machine] = { collectedAt: now(), agents };
135
+ saveAgentStatus(home, file);
136
+ }
137
+ /**
138
+ * Record a machine-level collection FAILURE — the previous snapshot stays
139
+ * (rows AND its collectedAt: the data's age is what it is) and the error
140
+ * note says what happened.
141
+ */
142
+ export function recordMachineFailure(home, machine, error) {
143
+ if (machine === '')
144
+ return;
145
+ const file = loadAgentStatus(home);
146
+ const prior = file.machines[machine];
147
+ if (prior === undefined) {
148
+ file.machines[machine] = { collectedAt: 0, agents: [], error };
149
+ }
150
+ else {
151
+ file.machines[machine] = { ...prior, error };
152
+ }
153
+ saveAgentStatus(home, file);
154
+ }
155
+ // ---------------------------------------------------------------- collection
156
+ /**
157
+ * `workspace/follow` — the workspace face's reconnect-safe STATE verb
158
+ * (workspace-controller index.ts:199 `@Remote({ mode: 'stream' }) follow(signal)`:
159
+ * signal-only, no request parameter — the wire payload is a bare `{args:{}}`).
160
+ * Every generation starts with EXACTLY ONE
161
+ * `{type:'baseline', value:{items: WorkspaceView[], ...}}` frame; increments
162
+ * after it are mutations we do not consume. All other verbs on the controller
163
+ * are mutations (create / rename / delete / insert-before / archive / pin) — excluded.
164
+ */
165
+ export const WORKSPACE_FOLLOW = 'workspace/follow';
166
+ /** Workspaces kept per agent (join-sized; a host has few). */
167
+ export const AGENT_WORKSPACES_MAX = 32;
168
+ /** One agent's workspace baseline over one face: first frame, abort, join fields only. */
169
+ async function workspaceBaselineOf(face, timeoutMs = 10_000) {
170
+ const controller = new AbortController();
171
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
172
+ try {
173
+ const iterable = await face.openWireStream(WORKSPACE_FOLLOW, { args: {} }, controller.signal);
174
+ const iterator = (await iterable)[Symbol.asyncIterator]();
175
+ for (;;) {
176
+ const next = await iterator.next();
177
+ if (next.done)
178
+ return [];
179
+ const frame = next.value;
180
+ if (!isRecord(frame) || frame['type'] !== 'baseline')
181
+ continue;
182
+ const value = isRecord(frame['value']) ? frame['value'] : {};
183
+ const items = Array.isArray(value['items']) ? value['items'] : [];
184
+ const out = [];
185
+ for (const item of items) {
186
+ if (!isRecord(item) || typeof item['workspaceId'] !== 'string' || item['workspaceId'] === '' || typeof item['title'] !== 'string')
187
+ continue;
188
+ out.push({
189
+ workspaceId: item['workspaceId'],
190
+ title: item['title'],
191
+ sessionIds: Array.isArray(item['sessionIds']) ? item['sessionIds'].filter((sid) => typeof sid === 'string') : [],
192
+ });
193
+ if (out.length >= AGENT_WORKSPACES_MAX)
194
+ break;
195
+ }
196
+ controller.abort(); // baseline consumed — drop the stream now
197
+ return out;
198
+ }
199
+ }
200
+ finally {
201
+ controller.abort();
202
+ clearTimeout(timer);
203
+ }
204
+ }
205
+ function errMsg(err) {
206
+ return err instanceof Error ? err.message : String(err);
207
+ }
208
+ /** One session-list item → the honest cache row (unknown when the payload doesn't say). */
209
+ function rowOf(item) {
210
+ if (!isRecord(item) || typeof item['sessionId'] !== 'string' || item['sessionId'] === '')
211
+ return undefined;
212
+ const running = item['running'];
213
+ return {
214
+ sessionId: item['sessionId'],
215
+ mtime: typeof item['updatedAt'] === 'number' ? item['updatedAt'] : 0,
216
+ status: running === true ? 'busy' : running === false ? 'idle' : 'unknown',
217
+ agentAvailable: item['agentAvailable'] === true,
218
+ };
219
+ }
220
+ /**
221
+ * Collect one agent fully: the session list AND the workspace baseline (the
222
+ * R6 title source). A workspace-leg failure keeps the fresh rows and notes
223
+ * the error — the title join then falls back honestly ('[unhosted]').
224
+ */
225
+ async function collectAgent(face, a6) {
226
+ const rows = await collectAgentRows(face);
227
+ try {
228
+ return { a6, rows, workspaces: await workspaceBaselineOf(face) };
229
+ }
230
+ catch (error) {
231
+ return { a6, rows, error: `workspace follow failed — ${errMsg(error)}` };
232
+ }
233
+ }
234
+ /** One agent's session list over one face (sessionRpc unwraps {ok,value} and applies wireArgsOf's `_request` key). */
235
+ async function collectAgentRows(face) {
236
+ const value = await sessionRpc(face, WIRE.sessionList, {});
237
+ const items = isRecord(value) && Array.isArray(value['items']) ? value['items'] : [];
238
+ const rows = [];
239
+ for (const item of items) {
240
+ const row = rowOf(item);
241
+ if (row !== undefined)
242
+ rows.push(row);
243
+ if (rows.length >= AGENT_STATUS_ROWS_MAX)
244
+ break;
245
+ }
246
+ return rows;
247
+ }
248
+ /** The production resolver: the federation memo first, the shared target registry second (the /hub demux order). */
249
+ export function federatedFaceOf(a6) {
250
+ return federatedGatewayOf(a6) ?? getTarget(a6)?.gateway;
251
+ }
252
+ /**
253
+ * Record a collection's outcome under the ONE rule this cache already states
254
+ * for machines: only a SUCCESSFUL collection replaces rows.
255
+ *
256
+ * `sessionListFailures` counts the agents whose SESSION LIST leg died (no face,
257
+ * or the list call rejected) — the leg the rows come from.
258
+ * `AgentStatusAgent.error` alone cannot carry that verdict: a failed WORKSPACE
259
+ * (title) leg sets it too while the rows are perfectly fresh. So a collection
260
+ * in which the session list died for EVERY agent is recorded as a machine-level
261
+ * FAILURE (O1b, 0.1.8-e): writing it as fresh `rows: []` would let "could not
262
+ * collect" masquerade as "this agent has no sessions", and would wipe a prior
263
+ * snapshot that was real. An empty-but-collected machine IS a success.
264
+ */
265
+ function recordCollection(home, machine, agents, sessionListFailures) {
266
+ if (agents.length > 0 && sessionListFailures === agents.length) {
267
+ const why = agents.find((a) => a.error !== undefined)?.error ?? 'unknown error';
268
+ recordMachineFailure(home, machine, `collection failed for every agent (${agents.length}) — ${why}`);
269
+ return;
270
+ }
271
+ recordMachineCollection(home, machine, agents);
272
+ }
273
+ /**
274
+ * Collect one REACHABLE machine's session lists over the presentation lane.
275
+ * Never throws: a machine-level failure records the error and keeps the
276
+ * previous snapshot; a per-agent failure (no face, rejected list) marks that
277
+ * agent with an error and empty rows — an honest miss, never a guess.
278
+ */
279
+ export async function collectRemoteMachine(home, remote, probe, opts = {}) {
280
+ const faceOf = opts.faceOf ?? federatedFaceOf;
281
+ if (!probe.ok || !Array.isArray(probe.agents)) {
282
+ // (unreachable probes never reach the collector — the sweep only calls it
283
+ // after a SUCCESSFUL reprobe — but the guard stays honest for direct callers)
284
+ recordMachineFailure(home, remote.id, 'probe did not carry a roster');
285
+ return;
286
+ }
287
+ const agents = [];
288
+ let sessionListFailures = 0;
289
+ for (const agent of probe.agents) {
290
+ if (typeof agent?.id !== 'string' || agent.id === '')
291
+ continue;
292
+ try {
293
+ const face = faceOf(agent.id);
294
+ if (face === undefined) {
295
+ agents.push({ a6: agent.id, rows: [], error: 'no gateway face for the session list' });
296
+ sessionListFailures += 1;
297
+ continue;
298
+ }
299
+ agents.push(await collectAgent(face, agent.id));
300
+ }
301
+ catch (error) {
302
+ agents.push({ a6: agent.id, rows: [], error: errMsg(error) });
303
+ sessionListFailures += 1;
304
+ }
305
+ }
306
+ // only the SESSION LIST leg counts here: a dead workspace (title) leg keeps
307
+ // the fresh rows and must not demote the whole collection to a failure
308
+ recordCollection(home, remote.id, agents, sessionListFailures);
309
+ }
310
+ /**
311
+ * Collect the LOCAL machine's agents (worlds + native — every a6 in the id
312
+ * store) through their in-process faces, resolved by the a2a-host local leg.
313
+ */
314
+ export async function collectLocalMachine(home, gatewayFor) {
315
+ const self = ensureSelf(home, 'local');
316
+ const agents = [];
317
+ let sessionListFailures = 0;
318
+ for (const [, a6] of Object.entries(agentIdMap(home))) {
319
+ try {
320
+ const face = gatewayFor({ machine: self.id, id: a6 });
321
+ agents.push(await collectAgent(face, a6));
322
+ }
323
+ catch (error) {
324
+ agents.push({ a6, rows: [], error: errMsg(error) });
325
+ sessionListFailures += 1;
326
+ }
327
+ }
328
+ recordCollection(home, self.id, agents, sessionListFailures);
329
+ }
330
+ // ---------------------------------------------------------------- outbox pull
331
+ /**
332
+ * Claim (and clear, peer-side) the envelopes buffered for THIS machine and
333
+ * deliver each through the SAME ingest handler the HTTP endpoint uses — in
334
+ * order, no dedupe layer (claim&clear is the peer's guarantee). A per-
335
+ * envelope failure degrades; the claim itself throwing propagates to the
336
+ * sweep's leg-level swallow.
337
+ */
338
+ export async function pullOutboxFor(home, remote, ingest, opts = {}) {
339
+ const self = ensureSelf(home, 'local');
340
+ const envelopes = await fetchA2aOutbox(remote.origin, self.id, {
341
+ cookie: cookieOf(remote.id, home),
342
+ fetchImpl: opts.fetchImpl,
343
+ });
344
+ let delivered = 0;
345
+ for (const envelope of envelopes) {
346
+ try {
347
+ await ingest.ingest(envelope);
348
+ delivered += 1;
349
+ }
350
+ catch {
351
+ // per-envelope degradation — the claim already consumed it peer-side
352
+ }
353
+ }
354
+ return delivered;
355
+ }
356
+ /**
357
+ * Compose the dual-responsibility legs over one assembly (a2a-row's single
358
+ * resolver + shared ingest). Every leg degrades internally — a failure never
359
+ * breaks probe accounting or the sweep loop.
360
+ */
361
+ export function statusSweepLegs(deps) {
362
+ return {
363
+ async onLocalCollect() {
364
+ try {
365
+ await collectLocalMachine(deps.home, deps.gatewayFor);
366
+ }
367
+ catch {
368
+ // the local leg degrades — the sweep keeps probing remotes
369
+ }
370
+ },
371
+ async onRemoteProbed({ remote, probe }) {
372
+ try {
373
+ await pullOutboxFor(deps.home, remote, deps.ingest, { fetchImpl: deps.fetchImpl });
374
+ }
375
+ catch {
376
+ // pull failure degrades — never breaks probe accounting
377
+ }
378
+ try {
379
+ await collectRemoteMachine(deps.home, remote, probe, { faceOf: deps.remoteFaceOf });
380
+ }
381
+ catch (error) {
382
+ recordMachineFailure(deps.home, remote.id, errMsg(error));
383
+ }
384
+ },
385
+ };
386
+ }