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.
- package/README.md +25 -3
- package/agent-agy/cordis.patch.yml +7 -4
- package/agent-agy/dist/agent.d.ts +12 -11
- package/agent-agy/dist/agy-auth-gateway.d.ts +124 -0
- package/agent-agy/dist/agy-auth-gateway.js +233 -0
- package/agent-agy/dist/agy-auth.d.ts +153 -0
- package/agent-agy/dist/agy-auth.js +179 -0
- package/agent-agy/dist/agy-cli-client.d.ts +39 -9
- package/agent-agy/dist/agy-cli-client.js +82 -18
- package/agent-agy/dist/agy-store.d.ts +8 -0
- package/agent-agy/dist/index.d.ts +46 -16
- package/agent-agy/dist/index.js +82 -56
- package/agent-agy/dist/models.d.ts +70 -0
- package/agent-agy/dist/models.js +102 -3
- package/agent-agy/package.json +7 -5
- package/agent-agy-sdk/cordis.patch.yml +58 -0
- package/agent-agy-sdk/dist/adapter.d.ts +15 -0
- package/agent-agy-sdk/dist/adapter.js +42 -0
- package/agent-agy-sdk/dist/agent-preset-agy.d.ts +27 -0
- package/agent-agy-sdk/dist/agent-preset-agy.js +32 -0
- package/agent-agy-sdk/dist/agent.d.ts +138 -0
- package/agent-agy-sdk/dist/agent.js +494 -0
- package/{agent-agy → agent-agy-sdk}/dist/agy-client.d.ts +26 -6
- package/{agent-agy → agent-agy-sdk}/dist/agy-client.js +43 -25
- package/agent-agy-sdk/dist/agy-sessions.d.ts +25 -0
- package/agent-agy-sdk/dist/agy-sessions.js +107 -0
- package/agent-agy-sdk/dist/agy-store.d.ts +39 -0
- package/agent-agy-sdk/dist/agy-store.js +83 -0
- package/agent-agy-sdk/dist/inbox.d.ts +26 -0
- package/agent-agy-sdk/dist/inbox.js +53 -0
- package/agent-agy-sdk/dist/index.d.ts +93 -0
- package/agent-agy-sdk/dist/index.js +476 -0
- package/agent-agy-sdk/dist/models.d.ts +41 -0
- package/agent-agy-sdk/dist/models.js +74 -0
- package/agent-agy-sdk/dist/sdk-venv.d.ts +76 -0
- package/agent-agy-sdk/dist/sdk-venv.js +173 -0
- package/agent-agy-sdk/dist/world-plugin.d.ts +10 -0
- package/agent-agy-sdk/dist/world-plugin.js +82 -0
- package/agent-agy-sdk/package.json +64 -0
- package/agent-claude/dist/adapter.js +106 -31
- package/agent-claude/dist/agent.js +154 -17
- package/agent-claude/dist/claude-client.js +84 -43
- package/agent-claude/dist/claude-launch.js +101 -0
- package/agent-claude/dist/claude-native-config.js +169 -0
- package/agent-claude/dist/claude-provider-gateway.js +340 -0
- package/agent-claude/dist/claude-providers.js +391 -0
- package/agent-claude/dist/claude-route.js +152 -0
- package/agent-claude/dist/index.js +214 -37
- package/agent-claude/dist/models.js +14 -0
- package/agent-claude/dist/sdk-runtime.js +320 -0
- package/agent-claude/package.json +6 -4
- package/agent-codex/README.md +141 -36
- package/agent-codex/cordis.patch.yml +14 -10
- package/agent-codex/dist/adapter.js +64 -28
- package/agent-codex/dist/agent.js +255 -81
- package/agent-codex/dist/app-server-approvals.js +165 -0
- package/agent-codex/dist/app-server-client.js +723 -0
- package/agent-codex/dist/app-server-events.js +432 -0
- package/agent-codex/dist/app-server-protocol.js +95 -0
- package/agent-codex/dist/app-server-wire.js +299 -0
- package/agent-codex/dist/codex-launch.js +78 -0
- package/agent-codex/dist/codex-native-config.js +392 -0
- package/agent-codex/dist/codex-provider-gateway.js +402 -0
- package/agent-codex/dist/codex-providers.js +376 -0
- package/agent-codex/dist/codex-route.js +86 -0
- package/agent-codex/dist/index.js +256 -115
- package/agent-codex/dist/permission.js +75 -1
- package/agent-codex/package.json +1 -2
- package/agent-codex-sdk/README.md +79 -0
- package/agent-codex-sdk/cordis.patch.yml +81 -0
- package/agent-codex-sdk/dist/adapter.js +91 -0
- package/agent-codex-sdk/dist/agent-preset-codex.js +32 -0
- package/agent-codex-sdk/dist/agent.js +1085 -0
- package/agent-codex-sdk/dist/codex-native-config.js +392 -0
- package/agent-codex-sdk/dist/codex-provider-gateway.js +402 -0
- package/agent-codex-sdk/dist/codex-providers.js +376 -0
- package/agent-codex-sdk/dist/codex-route.js +86 -0
- package/{agent-codex/dist/codex-client.js → agent-codex-sdk/dist/codex-sdk-client.js} +253 -32
- package/agent-codex-sdk/dist/codex-sdk-launch.js +92 -0
- package/agent-codex-sdk/dist/codex-store.js +180 -0
- package/agent-codex-sdk/dist/inbox.js +53 -0
- package/agent-codex-sdk/dist/index.js +739 -0
- package/agent-codex-sdk/dist/permission.js +71 -0
- package/agent-codex-sdk/dist/sdk-runtime.js +307 -0
- package/agent-codex-sdk/dist/session-map.js +167 -0
- package/agent-codex-sdk/dist/world-plugin.js +93 -0
- package/agent-codex-sdk/package.json +79 -0
- package/agent-codex-sdk/scripts/fetch-sdk-runtime.mjs +57 -0
- package/agent-omp/README.md +72 -55
- package/agent-omp/dist/agent.js +36 -11
- package/agent-omp/dist/index.js +92 -52
- package/agent-omp/dist/lazy-rpc.js +11 -4
- package/agent-omp/dist/omp-cli.js +114 -97
- package/agent-omp/dist/omp-store.js +12 -4
- package/agent-omp/dist/permission.js +70 -5
- package/agent-omp/dist/rpc-client.js +409 -0
- package/agent-omp/dist/world-plugin.js +36 -3
- package/agent-omp/package.json +2 -6
- package/agent-omp-sdk/README.md +103 -0
- package/agent-omp-sdk/cordis.patch.yml +108 -0
- package/agent-omp-sdk/dist/adapter.d.ts +21 -0
- package/agent-omp-sdk/dist/adapter.js +36 -0
- package/agent-omp-sdk/dist/agent-preset-omp.d.ts +31 -0
- package/agent-omp-sdk/dist/agent-preset-omp.js +32 -0
- package/agent-omp-sdk/dist/agent.d.ts +64 -0
- package/agent-omp-sdk/dist/agent.js +1178 -0
- package/agent-omp-sdk/dist/discovery.d.ts +57 -0
- package/agent-omp-sdk/dist/discovery.js +116 -0
- package/agent-omp-sdk/dist/inbox.d.ts +28 -0
- package/agent-omp-sdk/dist/inbox.js +53 -0
- package/agent-omp-sdk/dist/index.d.ts +60 -0
- package/agent-omp-sdk/dist/index.js +632 -0
- package/agent-omp-sdk/dist/knobs.d.ts +34 -0
- package/agent-omp-sdk/dist/knobs.js +50 -0
- package/agent-omp-sdk/dist/lazy-rpc.d.ts +98 -0
- package/agent-omp-sdk/dist/lazy-rpc.js +178 -0
- package/agent-omp-sdk/dist/mobile/zoom-guard.d.ts +187 -0
- package/agent-omp-sdk/dist/mobile/zoom-guard.js +294 -0
- package/agent-omp-sdk/dist/mobile-boot.d.ts +49 -0
- package/agent-omp-sdk/dist/mobile-boot.js +38 -0
- package/agent-omp-sdk/dist/models.d.ts +39 -0
- package/agent-omp-sdk/dist/models.js +299 -0
- package/agent-omp-sdk/dist/omp-cli.d.ts +67 -0
- package/agent-omp-sdk/dist/omp-cli.js +164 -0
- package/agent-omp-sdk/dist/omp-disk-discovery.d.ts +27 -0
- package/agent-omp-sdk/dist/omp-disk-discovery.js +178 -0
- package/agent-omp-sdk/dist/omp-executable.d.ts +2 -0
- package/agent-omp-sdk/dist/omp-executable.js +30 -0
- package/agent-omp-sdk/dist/omp-store.d.ts +134 -0
- package/agent-omp-sdk/dist/omp-store.js +526 -0
- package/agent-omp-sdk/dist/pairing.d.ts +7 -0
- package/agent-omp-sdk/dist/pairing.js +45 -0
- package/agent-omp-sdk/dist/permission.d.ts +138 -0
- package/agent-omp-sdk/dist/permission.js +161 -0
- package/agent-omp-sdk/dist/protocol.d.ts +189 -0
- package/agent-omp-sdk/dist/rpc-types.d.ts +77 -0
- package/agent-omp-sdk/dist/rpc-types.js +1 -0
- package/agent-omp-sdk/dist/sdk-client.d.ts +160 -0
- package/{agent-omp → agent-omp-sdk}/dist/sdk-client.js +108 -56
- package/agent-omp-sdk/dist/sdk-runtime.d.ts +211 -0
- package/agent-omp-sdk/dist/sdk-runtime.js +418 -0
- package/agent-omp-sdk/dist/sidecar-client.d.ts +87 -0
- package/agent-omp-sdk/dist/sidecar-client.js +197 -0
- package/agent-omp-sdk/dist/store/adapter.d.ts +17 -0
- package/agent-omp-sdk/dist/store/adapter.js +47 -0
- package/agent-omp-sdk/dist/store/db.d.ts +70 -0
- package/agent-omp-sdk/dist/store/db.js +210 -0
- package/agent-omp-sdk/dist/store/index.d.ts +23 -0
- package/agent-omp-sdk/dist/store/index.js +59 -0
- package/agent-omp-sdk/dist/store/reconcile.d.ts +15 -0
- package/agent-omp-sdk/dist/store/reconcile.js +51 -0
- package/agent-omp-sdk/dist/store/schema.d.ts +8 -0
- package/agent-omp-sdk/dist/store/schema.js +40 -0
- package/agent-omp-sdk/dist/transport.d.ts +49 -0
- package/agent-omp-sdk/dist/transport.js +35 -0
- package/agent-omp-sdk/dist/world-home.d.ts +4 -0
- package/agent-omp-sdk/dist/world-home.js +23 -0
- package/agent-omp-sdk/dist/world-plugin.d.ts +10 -0
- package/agent-omp-sdk/dist/world-plugin.js +137 -0
- package/agent-omp-sdk/package.json +87 -0
- package/client/index.js +1883 -287
- package/cordis.patch.yml +80 -0
- package/package.json +23 -9
- package/superd-hub/dist/a2a-delivery.d.ts +189 -0
- package/superd-hub/dist/a2a-delivery.js +240 -0
- package/superd-hub/dist/a2a-host.d.ts +76 -0
- package/superd-hub/dist/a2a-host.js +145 -0
- package/superd-hub/dist/a2a-ingest.d.ts +31 -0
- package/superd-hub/dist/a2a-ingest.js +636 -0
- package/superd-hub/dist/a2a-log.d.ts +17 -0
- package/superd-hub/dist/a2a-log.js +42 -0
- package/superd-hub/dist/a2a-row.d.ts +65 -0
- package/superd-hub/dist/a2a-row.js +162 -0
- package/superd-hub/dist/a2a-service.d.ts +357 -0
- package/superd-hub/dist/a2a-service.js +980 -0
- package/superd-hub/dist/a2a-wire.d.ts +220 -0
- package/superd-hub/dist/a2a-wire.js +305 -0
- package/superd-hub/dist/agent-ids.d.ts +10 -0
- package/superd-hub/dist/agent-ids.js +68 -0
- package/superd-hub/dist/agent-roster.d.ts +29 -18
- package/superd-hub/dist/agent-roster.js +54 -15
- package/superd-hub/dist/agent-status.d.ts +119 -0
- package/superd-hub/dist/agent-status.js +386 -0
- package/superd-hub/dist/client/agent-settings.d.ts +210 -0
- package/superd-hub/dist/client/agent-settings.js +300 -0
- package/superd-hub/dist/client/mesh-api.d.ts +134 -0
- package/superd-hub/dist/client/mesh-api.js +219 -0
- package/superd-hub/dist/client/mesh-groups.d.ts +63 -0
- package/superd-hub/dist/client/mesh-groups.js +66 -0
- package/superd-hub/dist/client/roster.d.ts +48 -0
- package/superd-hub/dist/client/roster.js +102 -0
- package/superd-hub/dist/client/runtime-settings-api.d.ts +76 -0
- package/superd-hub/dist/client/runtime-settings-api.js +137 -0
- package/superd-hub/dist/client/switch.d.ts +165 -0
- package/superd-hub/dist/client/switch.js +295 -0
- package/superd-hub/dist/exec-pty-channel.d.ts +58 -0
- package/superd-hub/dist/exec-pty-channel.js +162 -0
- package/superd-hub/dist/exec-pty-framing.d.ts +32 -0
- package/superd-hub/dist/exec-pty-framing.js +81 -0
- package/superd-hub/dist/exec-pty-rpc.d.ts +21 -0
- package/superd-hub/dist/exec-pty-rpc.js +128 -0
- package/superd-hub/dist/exec-pty-session.d.ts +76 -0
- package/superd-hub/dist/exec-pty-session.js +234 -0
- package/superd-hub/dist/exec.d.ts +56 -0
- package/superd-hub/dist/exec.js +225 -0
- package/superd-hub/dist/gateway.d.ts +39 -2
- package/superd-hub/dist/gateway.js +62 -3
- package/superd-hub/dist/hub-tool.d.ts +174 -0
- package/superd-hub/dist/hub-tool.js +464 -0
- package/superd-hub/dist/index.d.ts +15 -1
- package/superd-hub/dist/index.js +16 -0
- package/superd-hub/dist/machines.d.ts +45 -0
- package/superd-hub/dist/machines.js +125 -0
- package/superd-hub/dist/mesh-federate.d.ts +138 -0
- package/superd-hub/dist/mesh-federate.js +223 -0
- package/superd-hub/dist/mesh-gateway.d.ts +67 -0
- package/superd-hub/dist/mesh-gateway.js +408 -0
- package/superd-hub/dist/mesh-link.d.ts +274 -0
- package/superd-hub/dist/mesh-link.js +441 -0
- package/superd-hub/dist/mesh-rpc.d.ts +123 -0
- package/superd-hub/dist/mesh-rpc.js +250 -0
- package/superd-hub/dist/mux-client.d.ts +71 -0
- package/superd-hub/dist/mux-client.js +281 -0
- package/superd-hub/dist/pack-version.d.ts +2 -0
- package/superd-hub/dist/pack-version.js +30 -0
- package/superd-hub/dist/roster.d.ts +6 -0
- package/superd-hub/dist/rpc.d.ts +18 -2
- package/superd-hub/dist/rpc.js +34 -4
- package/superd-hub/dist/spawn-world.js +3 -1
- package/superd-hub/dist/switch-core.d.ts +62 -25
- package/superd-hub/dist/switch-core.js +53 -30
- package/superd-hub/dist/system-head.d.ts +22 -12
- package/superd-hub/dist/system-head.js +100 -84
- package/superd-hub/dist/wire-endpoints.d.ts +264 -0
- package/superd-hub/dist/wire-endpoints.js +180 -0
- package/superd-hub/lib/client/index.js +1883 -287
- package/superd-hub/package.json +11 -2
- package/agent-agy/dist/agent-preset-projection.d.ts +0 -31
- package/agent-agy/dist/agent-preset-projection.js +0 -33
- package/agent-claude/dist/agent-preset-projection.js +0 -30
- package/agent-codex/dist/agent-preset-projection.js +0 -37
- package/agent-codex/dist/app-home.js +0 -65
- package/agent-codex/dist/models.js +0 -152
- package/agent-codex/dist/spike.js +0 -46
- package/agent-hermes/dist/agent-preset-projection.js +0 -34
- package/agent-omp/dist/agent-preset-projection.js +0 -36
- package/agent-omp/dist/app-home.js +0 -86
- package/agent-omp/dist/sidecar-client.js +0 -195
- package/agent-pi/dist/agent-preset-projection.js +0 -29
- package/superd-hub/dist/client-shim.d.ts +0 -28
- package/superd-hub/dist/client-shim.js +0 -251
- package/superd-hub/dist/index-pass.d.ts +0 -22
- package/superd-hub/dist/index-pass.js +0 -47
- package/superd-hub/dist/labels.d.ts +0 -45
- package/superd-hub/dist/labels.js +0 -83
- /package/{agent-agy → agent-agy-sdk}/bridge/agy_bridge.py +0 -0
- /package/{agent-agy → agent-agy-sdk}/bridge/gemini_openai_server.mjs +0 -0
- /package/{agent-codex/dist/codex-events.js → agent-codex-sdk/dist/codex-sdk-events.js} +0 -0
- /package/{agent-omp → agent-omp-sdk}/dist/protocol.js +0 -0
- /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
|
|
5
|
-
* needs exactly one fact — which
|
|
6
|
-
* that fact into every page it can reach (ctx0's index and
|
|
7
|
-
* world's index), so no world implements any selector code and
|
|
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
|
-
* /
|
|
11
|
-
* /omp/
|
|
12
|
-
* /
|
|
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
|
-
/** `
|
|
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
|
-
/**
|
|
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
|
|
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
|
|
36
|
-
*
|
|
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
|
-
|
|
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
|
|
6
|
-
|
|
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
|
|
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
|
|
13
|
-
*
|
|
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 = [
|
|
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
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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
|
+
}
|