super-dsh 0.1.6 → 0.1.9
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/client/index.js +741 -256
- package/cordis.patch.yml +16 -0
- package/package.json +7 -2
- 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/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 +96 -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 +741 -256
- 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/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-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
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// Operational journal for the A2A/mesh surface (0.1.7-b headliner; the
|
|
2
|
+
// 2026-09-29 first-person field pass found the hub emits ZERO journal lines
|
|
3
|
+
// — every field question forced registry forensics). One line per machine
|
|
4
|
+
// event, greppable `a2a.<event>` vocabulary, no secrets (messageIds and
|
|
5
|
+
// session/conversation ids only — never envelope bodies, never cookies).
|
|
6
|
+
//
|
|
7
|
+
// The pure modules (a2a-service/a2a-ingest) stay runtime-agnostic: they take
|
|
8
|
+
// an optional A2aLogFn and only FORMAT lines; the cordis host (a2a-row.ts)
|
|
9
|
+
// wires it onto ctx.logger so lines land in the unit journal (4999 rig /
|
|
10
|
+
// dev3 dsh.service). Tests inject a collector.
|
|
11
|
+
/**
|
|
12
|
+
* Render one journal-friendly line: `superd-hub: a2a.<event> k=v k=v`.
|
|
13
|
+
* Values are quoted (JSON) only when they contain whitespace, so the common
|
|
14
|
+
* case stays grep-friendly: `a2a.completion.transcript conversation=abc123 bytes=42`.
|
|
15
|
+
*/
|
|
16
|
+
export function formatA2aLogLine(event, fields = {}) {
|
|
17
|
+
const parts = [`superd-hub: a2a.${event}`];
|
|
18
|
+
for (const [key, value] of Object.entries(fields)) {
|
|
19
|
+
if (value === undefined)
|
|
20
|
+
continue;
|
|
21
|
+
const text = String(value);
|
|
22
|
+
parts.push(`${key}=${/\s/.test(text) ? JSON.stringify(text) : text}`);
|
|
23
|
+
}
|
|
24
|
+
return parts.join(' ');
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* The host wiring both rows share: level → process console. NOT ctx.logger —
|
|
28
|
+
* the pack composes no cordis logger-console exporter, so LoggerService
|
|
29
|
+
* records go nowhere (verified live 2026-09-29); console matches the
|
|
30
|
+
* world-join / world-provision precedent and lands in every unit log.
|
|
31
|
+
*/
|
|
32
|
+
export function consoleA2aLog() {
|
|
33
|
+
return (level, event, fields) => {
|
|
34
|
+
const line = formatA2aLogLine(event, fields);
|
|
35
|
+
if (level === 'warn')
|
|
36
|
+
console.warn(line);
|
|
37
|
+
else if (level === 'error')
|
|
38
|
+
console.error(line);
|
|
39
|
+
else
|
|
40
|
+
console.log(line);
|
|
41
|
+
};
|
|
42
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `super-dsh/a2a` — the SuperD Hub TOOL row (mesh P3 Task 4, spec §9):
|
|
3
|
+
* composes the single model-facing `superd-hub` tool onto ctx0's `tools`
|
|
4
|
+
* service and closes T3's `nativeGateway` seam with the hub process's OWN
|
|
5
|
+
* in-process session-plane gateway.
|
|
6
|
+
*
|
|
7
|
+
* WHY A SUBPATH ROW (documented ruling, not riding the bare `superd-hub`
|
|
8
|
+
* row): world trees compose the `superd-hub` row too (world-mount.ts
|
|
9
|
+
* `WORLD_HUB_ROW`) and they DO provide a `tools` service (the base web-app
|
|
10
|
+
* composition's `tools` row is not in the wave-1/wave-2 disable lists) — so
|
|
11
|
+
* riding gateway.ts's apply would register a mis-wired A2A tool inside every
|
|
12
|
+
* world (its `nativeGateway` would be THAT WORLD's typertGateway, while the
|
|
13
|
+
* 'native' a6 belongs to ctx0's agent). The pack's precedent for an
|
|
14
|
+
* explicitly-composed capability is the subpath row (web-trust,
|
|
15
|
+
* fusion-transport — "composing this row IS the switch"): this row lives
|
|
16
|
+
* only in the pack's own cordis.patch.yml, which world profiles never
|
|
17
|
+
* compose (world-provision.ts `WORLD_BASE_BUNDLES`), so it mounts in ctx0
|
|
18
|
+
* only — exactly the P3 initiator surface (native DSH sessions; foreign
|
|
19
|
+
* runtimes get their own injection in P5, spec §10). Individually
|
|
20
|
+
* toggleable per the pack's row discipline.
|
|
21
|
+
*
|
|
22
|
+
* Dormancy (better-dash plugin.ts discipline): zero static injects; the
|
|
23
|
+
* dynamic `ctx.inject(['tools', 'typertGateway'])` keeps the row dormant —
|
|
24
|
+
* and side-effect-free — until BOTH the tool registry and ctx0's session
|
|
25
|
+
* plane exist. `tools.register` scopes the registration to the calling
|
|
26
|
+
* fiber, so the tool unregisters with the row.
|
|
27
|
+
*
|
|
28
|
+
* The stream-face bridge: T2's `A2aGateway` declares a 3-arg
|
|
29
|
+
* `openWireStream(endpoint, payload, signal)`, but a RAW typertGateway
|
|
30
|
+
* instance speaks the upstream canonical 6-arg order
|
|
31
|
+
* `openWireStream(endpoint, payload, uplink, peer, signal, control)`
|
|
32
|
+
* (dsh-api-gateway/src/index.ts:434; the 0.1.3-j lesson — positional
|
|
33
|
+
* misbinding strands streams forever). This row hands the RAW ctx0
|
|
34
|
+
* instance to `createA2aHost`. NOTE: T3's resolver hands LOCAL world rows
|
|
35
|
+
* from the shared target registry RAW (world-join.ts registers the instance
|
|
36
|
+
* verbatim) — fixed in round 1 (2026-09-28 review): the resolver now bridges
|
|
37
|
+
* ALL local rows + the native seam through the shared `bridgeTypertGateway`
|
|
38
|
+
* (a2a-host.ts), and FEDERATED wire adapters pass through unwrapped.
|
|
39
|
+
*/
|
|
40
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
41
|
+
/** Stable Cordis plugin (row) name. */
|
|
42
|
+
export declare const name = "superd-a2a";
|
|
43
|
+
/** Zero static injects — the tools/typertGateway legs are dynamic (dormancy). */
|
|
44
|
+
export declare const inject: string[];
|
|
45
|
+
/** Injection seam for tests: a tmpdir home instead of the ambient/prod DSH_HOME (rpc.ts discipline). */
|
|
46
|
+
export interface HubToolRowOptions {
|
|
47
|
+
/** Hub home backing the registries; defaults to `resolveDshHome()` at wiring time. */
|
|
48
|
+
home?: string;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Wire the tool row: build T2+T3's service with ctx0's own gateway injected
|
|
52
|
+
* at the native seam, then register the `superd-hub` tool once the `tools`
|
|
53
|
+
* service and the session plane are both live.
|
|
54
|
+
* @param ctx - the hub host root context (ctx0).
|
|
55
|
+
* @param opts - optional home override (tests inject a tmpdir).
|
|
56
|
+
*/
|
|
57
|
+
export declare function wireHubToolRow(ctx: Context, opts?: HubToolRowOptions): void;
|
|
58
|
+
/** MUST stay an arrow (RM0/better-dash note: function declarations get `new`-called, disposers lost). */
|
|
59
|
+
export declare const apply: (ctx: Context) => void;
|
|
60
|
+
declare const _default: {
|
|
61
|
+
name: string;
|
|
62
|
+
inject: string[];
|
|
63
|
+
apply: (ctx: Context) => void;
|
|
64
|
+
};
|
|
65
|
+
export default _default;
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { resolveDshHome } from '@deepseek-ai/dsh-home-paths';
|
|
2
|
+
import { createSuperdHubTool } from './hub-tool.js';
|
|
3
|
+
import { createA2aIngest } from './a2a-ingest.js';
|
|
4
|
+
import { statusSweepLegs, loadAgentStatus } from './agent-status.js';
|
|
5
|
+
import { setSweepCollectors } from './mesh-link.js';
|
|
6
|
+
import { consoleA2aLog } from './a2a-log.js';
|
|
7
|
+
import { createA2aService } from './a2a-service.js';
|
|
8
|
+
import { resolveA2aGatewayFor, localA2aInitiator } from './a2a-host.js';
|
|
9
|
+
import { registerA2aRpc, registerExecRpc } from './mesh-rpc.js';
|
|
10
|
+
import { runOneShotExec, postOneShotExec } from './exec.js';
|
|
11
|
+
import { ensureSelf, listRemotes } from './machines.js';
|
|
12
|
+
import { cookieOf } from './mesh-link.js';
|
|
13
|
+
import { PtyPool } from './exec-pty-session.js';
|
|
14
|
+
import { LocalScriptChannel, WsPtyChannel } from './exec-pty-channel.js';
|
|
15
|
+
import { PTY_EXEC_PATH } from './exec-pty-rpc.js';
|
|
16
|
+
import { agentLinks } from './agent-roster.js';
|
|
17
|
+
/** Stable Cordis plugin (row) name. */
|
|
18
|
+
export const name = 'superd-a2a';
|
|
19
|
+
/** Zero static injects — the tools/typertGateway legs are dynamic (dormancy). */
|
|
20
|
+
export const inject = [];
|
|
21
|
+
/**
|
|
22
|
+
* Resolve one initiator's live agent handle. Read LAZILY at delivery time
|
|
23
|
+
* (`ctx.get`, no declaration): the registry's availability must not gate the
|
|
24
|
+
* row, and the runtime Agent object carries followup/steer/inject even though
|
|
25
|
+
* its public type surface says `{id}` (dsh-agent runtime-types.ts interface
|
|
26
|
+
* merge — compile-time only, one object at runtime; T3's A2aAgentHandle is
|
|
27
|
+
* its structural subset).
|
|
28
|
+
*/
|
|
29
|
+
function liveAgentHandle(ctx, sessionId) {
|
|
30
|
+
const registry = ctx.get('agents');
|
|
31
|
+
const agent = registry?.get(sessionId);
|
|
32
|
+
return agent === undefined || agent === null ? undefined : agent;
|
|
33
|
+
}
|
|
34
|
+
/** The default roster view over the hub home (the /api/agent-runtime facts). */
|
|
35
|
+
function rosterViewOf(home) {
|
|
36
|
+
return {
|
|
37
|
+
self: ensureSelf(home, 'local'),
|
|
38
|
+
remotes: listRemotes(home).map((row) => ({ id: row.id, label: row.label !== '' ? row.label : row.name })),
|
|
39
|
+
agents: agentLinks(home),
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Wire the tool row: build T2+T3's service with ctx0's own gateway injected
|
|
44
|
+
* at the native seam, then register the `superd-hub` tool once the `tools`
|
|
45
|
+
* service and the session plane are both live.
|
|
46
|
+
* @param ctx - the hub host root context (ctx0).
|
|
47
|
+
* @param opts - optional home override (tests inject a tmpdir).
|
|
48
|
+
*/
|
|
49
|
+
export function wireHubToolRow(ctx, opts = {}) {
|
|
50
|
+
const home = opts.home ?? resolveDshHome();
|
|
51
|
+
ctx.inject(['tools', 'typertGateway'], (t) => {
|
|
52
|
+
// RAW instance in, bridged at the resolver (fix round 1: one shared
|
|
53
|
+
// bridge in a2a-host.ts covers the native seam AND all local world rows).
|
|
54
|
+
const raw = t.get('typertGateway');
|
|
55
|
+
// postoffice R4: ONE resolver, ONE ingest, TWO consumers — the initiator
|
|
56
|
+
// service's self-routing leg calls the very same ingest handler the HTTP
|
|
57
|
+
// endpoint calls (protocol-identical by construction). The service takes
|
|
58
|
+
// a LAZY provider: the ingest's delivery face buffers through the
|
|
59
|
+
// service's own buffer leg, so it can only be built after the service.
|
|
60
|
+
const gatewayFor = resolveA2aGatewayFor({ home, nativeGateway: raw });
|
|
61
|
+
// Operational journal: the pure modules only format lines; this is
|
|
62
|
+
// where they meet the process console and land in the unit log (4999 rig
|
|
63
|
+
// / dev3 dsh.service journal). NOT ctx.logger: the pack composes no
|
|
64
|
+
// cordis logger-console exporter, so ctx.logger records go nowhere
|
|
65
|
+
// (verified live 2026-09-29 — zero a2a lines through a full envelope
|
|
66
|
+
// flow). Console matches the world-join/world-provision precedent for
|
|
67
|
+
// operational lines. One line per machine event, no secrets.
|
|
68
|
+
const alog = consoleA2aLog();
|
|
69
|
+
let ingestRef;
|
|
70
|
+
const service = createA2aService({ home, gatewayFor, ingest: () => ingestRef, log: alog });
|
|
71
|
+
const delivery = {
|
|
72
|
+
agents: { get: (sessionId) => liveAgentHandle(ctx, sessionId) },
|
|
73
|
+
buffer: (initiatorSessionId, message) => { service.buffer(initiatorSessionId, message); },
|
|
74
|
+
};
|
|
75
|
+
ingestRef = createA2aIngest({ home, gatewayFor, delivery, log: alog });
|
|
76
|
+
// postoffice R5: the sweep's dual-responsibility legs (outbox pull +
|
|
77
|
+
// session-list collection) over THIS assembly — registered process-level;
|
|
78
|
+
// gateway.ts's existing sweep timer picks them up (no second timer).
|
|
79
|
+
setSweepCollectors(statusSweepLegs({ home, gatewayFor, ingest: ingestRef }));
|
|
80
|
+
// the a2a endpoints (POST /api/superd/a2a + GET outbox) ride the shared
|
|
81
|
+
// /api fence like the machines route; registered on the same inject scope
|
|
82
|
+
// (they live and die with the typertGateway seam).
|
|
83
|
+
registerA2aRpc(t, ingestRef);
|
|
84
|
+
// P4: the exec endpoint rides the same shared /api fence (spec §8.1).
|
|
85
|
+
registerExecRpc(t, alog);
|
|
86
|
+
// P4 exec face: the local executing side + the wire leg over the pairing
|
|
87
|
+
// cookie. The initiator-side per-machine knob (execEnabled) is honored
|
|
88
|
+
// here, before any bytes leave (spec §5).
|
|
89
|
+
const execFace = {
|
|
90
|
+
selfM4: () => ensureSelf(home, 'local').id,
|
|
91
|
+
run: (input) => runOneShotExec(input, alog),
|
|
92
|
+
post: async (machineId, input) => {
|
|
93
|
+
const row = listRemotes(home).find((r) => r.id === machineId);
|
|
94
|
+
if (row === undefined)
|
|
95
|
+
throw new Error(`unknown machine "${machineId}" — call agents for the roster`);
|
|
96
|
+
if (row.execEnabled === false)
|
|
97
|
+
throw new Error(`exec is disabled for machine "${machineId}" on this registry row (execEnabled=false)`);
|
|
98
|
+
alog?.('info', 'exec.remote', { machine: machineId, cmd: input.cmd, timeoutSec: input.timeoutSec ?? 120 });
|
|
99
|
+
const result = await postOneShotExec(row.origin, input, { cookie: cookieOf(row.id, home) });
|
|
100
|
+
// F6 (first-person 0.1.7-d finding): the initiator side needs its own
|
|
101
|
+
// completion line — without it a remote result lives only in the tool
|
|
102
|
+
// response and the initiator's journal stays blind to the outcome.
|
|
103
|
+
alog?.('info', 'exec.remote.done', {
|
|
104
|
+
machine: machineId,
|
|
105
|
+
exit: result.exit ?? 'none',
|
|
106
|
+
durationMs: result.durationMs,
|
|
107
|
+
...(result.outcome === 'exited' ? {} : { outcome: result.outcome }),
|
|
108
|
+
...(result.truncated ? { truncated: true } : {}),
|
|
109
|
+
});
|
|
110
|
+
return result;
|
|
111
|
+
},
|
|
112
|
+
};
|
|
113
|
+
// P4 §8.2 initiator half: the PTY pool. Session key = (calling DSH
|
|
114
|
+
// session, machine) — per-agent direct, no central allocation (D12).
|
|
115
|
+
const ptyPool = new PtyPool({ idleTtlSec: 600 });
|
|
116
|
+
ctx.effect(() => () => { void ptyPool.disposeAll(); }, 'superd-a2a: pty pool teardown');
|
|
117
|
+
const execPty = async (sessionKey, machine, cmd, opts = {}) => {
|
|
118
|
+
const channelOf = () => {
|
|
119
|
+
if (machine === ensureSelf(home, 'local').id)
|
|
120
|
+
return new LocalScriptChannel();
|
|
121
|
+
const row = listRemotes(home).find((r) => r.id === machine);
|
|
122
|
+
if (row === undefined)
|
|
123
|
+
throw new Error(`unknown machine "${machine}" — call agents for the roster`);
|
|
124
|
+
if (row.execEnabled === false)
|
|
125
|
+
throw new Error(`exec is disabled for machine "${machine}" on this registry row (execEnabled=false)`);
|
|
126
|
+
alog?.('info', 'exec.pty.dial', { machine, session: sessionKey });
|
|
127
|
+
return new WsPtyChannel(`${row.origin.replace(/^http/, 'ws')}${PTY_EXEC_PATH}`, { cookie: cookieOf(row.id, home) });
|
|
128
|
+
};
|
|
129
|
+
const session = ptyPool.getOrCreate(`${sessionKey}|m:${machine}`, channelOf);
|
|
130
|
+
const result = await session.dispatch(cmd, { ...(opts.timeoutSec === undefined ? {} : { timeoutSec: opts.timeoutSec }) });
|
|
131
|
+
alog?.('info', 'exec.pty.run', {
|
|
132
|
+
machine,
|
|
133
|
+
cmd: cmd.length > 120 ? `${cmd.slice(0, 117)}...` : cmd,
|
|
134
|
+
exit: result.exit ?? 'none',
|
|
135
|
+
...(result.cwd !== null ? { cwd: result.cwd } : {}),
|
|
136
|
+
durationMs: result.durationMs,
|
|
137
|
+
...(result.reconnected ? { reconnected: true } : {}),
|
|
138
|
+
...(result.timedOut ? { timedOut: true } : {}),
|
|
139
|
+
});
|
|
140
|
+
return result;
|
|
141
|
+
};
|
|
142
|
+
const execFaceFull = { ...execFace, pty: execPty };
|
|
143
|
+
// the a2a endpoints (POST /api/superd/a2a + GET /api/superd/a2a/outbox) ride the shared
|
|
144
|
+
t.tools.register(createSuperdHubTool({
|
|
145
|
+
service,
|
|
146
|
+
delivery,
|
|
147
|
+
exec: execFaceFull,
|
|
148
|
+
// R6: the agent_status leg reads the R5 cache (the sweep's legs fill it)
|
|
149
|
+
status: () => loadAgentStatus(home),
|
|
150
|
+
roster: () => rosterViewOf(home),
|
|
151
|
+
// This row mounts in ctx0 only (header ruling): every caller is a
|
|
152
|
+
// native DSH session, so the initiator's a6 is the 'native' row's.
|
|
153
|
+
initiatorOf: (sessionId) => localA2aInitiator(home, 'native', sessionId),
|
|
154
|
+
onWatch: (dispose) => { ctx.effect(() => dispose, 'superd-a2a: a2a completion watch'); },
|
|
155
|
+
}));
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
/** MUST stay an arrow (RM0/better-dash note: function declarations get `new`-called, disposers lost). */
|
|
159
|
+
export const apply = (ctx) => {
|
|
160
|
+
wireHubToolRow(ctx);
|
|
161
|
+
};
|
|
162
|
+
export default { name, inject, apply };
|
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
import { A2aAddressError } from './a2a-wire.js';
|
|
2
|
+
import type { A2aIngest } from './a2a-ingest.js';
|
|
3
|
+
import type { A2aLogFn } from './a2a-log.js';
|
|
4
|
+
export { A2aAddressError };
|
|
5
|
+
/** One tracked conversation = one target-side DSH session (spec §2). */
|
|
6
|
+
export interface ConversationRow {
|
|
7
|
+
/** Display/tool alias — first 6 chars of the session id's uuid part (the constant `session-` prefix stripped), collision-lengthened 6→8→10; legacy rows keep older spellings verbatim. */
|
|
8
|
+
shortId: string;
|
|
9
|
+
/** Machine id (m4) the conversation lives on. */
|
|
10
|
+
machine: string;
|
|
11
|
+
/** Agent id (a6) the conversation belongs to. */
|
|
12
|
+
agent: string;
|
|
13
|
+
/** Full DSH session id (the wire identity). */
|
|
14
|
+
sessionId: string;
|
|
15
|
+
/** Operator-facing title (optional; never an identity). */
|
|
16
|
+
title?: string;
|
|
17
|
+
/**
|
|
18
|
+
* R4 amendment (2026-09-28 review): present ONLY on MIRROR rows (the peer
|
|
19
|
+
* hosted the conversation here) = the LOCAL a6 hosting the session.
|
|
20
|
+
* Presence identifies the row as locally-hosted and routes continuations
|
|
21
|
+
* to our own ingest. Absent = initiator-row semantics (the session lives
|
|
22
|
+
* at {machine, agent}). Additive optional; no migration.
|
|
23
|
+
*/
|
|
24
|
+
hostAgent?: string;
|
|
25
|
+
/**
|
|
26
|
+
* The initiator identity's full session id — the peer→initiator asynchronous
|
|
27
|
+
* delivery address. R4: persisted at create/first-send on INITIATOR rows.
|
|
28
|
+
* 0.1.7-i: MIRROR rows persist it at adoption too (the opener's session
|
|
29
|
+
* from the envelope) — the send-failed notice must address the original
|
|
30
|
+
* initiator even when the write-back dies before the initiator-side ingest
|
|
31
|
+
* ever learned the conversation. Additive; absent = pre-R4 rows / not yet
|
|
32
|
+
* sent.
|
|
33
|
+
*/
|
|
34
|
+
initiatorSession?: string;
|
|
35
|
+
/**
|
|
36
|
+
* D20 (2026-09-29): present ONLY on MIRROR rows = the hosted peer has sent
|
|
37
|
+
* through the channel at least once in this conversation. Monotonic per
|
|
38
|
+
* conversation — once set, the completion watch NEVER transcribes again
|
|
39
|
+
* (the channel reply IS the reply; unsent turn-end output is the peer's
|
|
40
|
+
* own business). Additive optional; no migration.
|
|
41
|
+
*/
|
|
42
|
+
channelUsed?: true;
|
|
43
|
+
/** D20: epoch ms of the peer's LAST channel send on this row — the watermark the completion watch compares against its arm time to detect "sent during this turn's window" (suppresses the artifact when the send already was it). */
|
|
44
|
+
lastSentAt?: number;
|
|
45
|
+
/**
|
|
46
|
+
* 0.1.7-h R2: epoch ms when a stop envelope cancelled a LIVE turn here.
|
|
47
|
+
* The completion watch compares it against its arm watermark — a turn that
|
|
48
|
+
* ends because the INITIATOR stopped it must surface as
|
|
49
|
+
* reason=stopped-by-initiator, not as the peer going silent. Additive
|
|
50
|
+
* optional; no migration.
|
|
51
|
+
*/
|
|
52
|
+
stoppedAt?: number;
|
|
53
|
+
/** Epoch ms when the row was created. */
|
|
54
|
+
createdAt: number;
|
|
55
|
+
/** Epoch ms of the last successful send. */
|
|
56
|
+
lastActivityAt: number;
|
|
57
|
+
/**
|
|
58
|
+
* D30 (2026-09-30): the peer's last output delivered into this conversation
|
|
59
|
+
* (a channel reply or a turn-end transcript), recorded when this hub
|
|
60
|
+
* RECEIVES it. Queryable through agent_status by the conversation's
|
|
61
|
+
* INITIATOR only (the ownership gate). Additive optional; no migration.
|
|
62
|
+
*/
|
|
63
|
+
lastOutput?: string;
|
|
64
|
+
/** Epoch ms of lastOutput. */
|
|
65
|
+
lastOutputAt?: number;
|
|
66
|
+
}
|
|
67
|
+
/** The whole registry file. */
|
|
68
|
+
export interface ConversationsFile {
|
|
69
|
+
conversations: ConversationRow[];
|
|
70
|
+
}
|
|
71
|
+
/** Read the registry; a missing or unreadable/corrupt file degrades to empty (never throws). */
|
|
72
|
+
export declare function loadConversations(home: string): ConversationsFile;
|
|
73
|
+
/** Persist the registry: recursive mkdir, tmp write, chmod 0600, atomic rename (machines.ts discipline). */
|
|
74
|
+
export declare function saveConversations(home: string, file: ConversationsFile): void;
|
|
75
|
+
/**
|
|
76
|
+
* shortId mint (D2 fix): DSH session ids are `session-<uuid>`, so slicing the
|
|
77
|
+
* raw id only ever captures the constant prefix (the live registry degenerated
|
|
78
|
+
* to "session-", "session-3", …). Strip the prefix, mint the first 6 chars of
|
|
79
|
+
* the uuid part, collision-lengthen 6→8→10… (step 2, spec §11). Ids without the
|
|
80
|
+
* prefix (older/foreign spellings) mint from the whole id the same way. A
|
|
81
|
+
* re-seen sessionId keeps its row's shortId (stable across restarts); stored
|
|
82
|
+
* shortIds are reserved as taken — DEGENERATE ones are re-minted once by
|
|
83
|
+
* {@link migrateConversations}, all others stay verbatim forever.
|
|
84
|
+
*/
|
|
85
|
+
export declare function shortIdFor(sessionId: string, rows: readonly ConversationRow[]): string;
|
|
86
|
+
/**
|
|
87
|
+
* Degenerate-pattern predicate (D2 migration): a stored shortId that still
|
|
88
|
+
* carries the `session-` prefix AND whose remainder is shorter than the new
|
|
89
|
+
* mint length — exactly the ids the old prefix-slicing bug produced. Such ids
|
|
90
|
+
* carry (almost) no information and teach peers to echo garbage.
|
|
91
|
+
*/
|
|
92
|
+
export declare function isDegenerateShortId(shortId: string): boolean;
|
|
93
|
+
/**
|
|
94
|
+
* One-time registry migration (D2 amended, review 2026-09-28): rows matching
|
|
95
|
+
* the degenerate pattern are re-minted with the current algorithm and written
|
|
96
|
+
* back; non-degenerate rows are never touched. Peers still holding an old
|
|
97
|
+
* degenerate id in their context is accepted collateral — the registry holds
|
|
98
|
+
* test data today, which is the cheapest migration window there will ever be.
|
|
99
|
+
* @returns the re-minted rows (empty when nothing needed migrating).
|
|
100
|
+
*/
|
|
101
|
+
export declare function migrateConversations(home: string): ConversationRow[];
|
|
102
|
+
/**
|
|
103
|
+
* THE teaching envelope — one template constant (D20 v3, 2026-09-29: the
|
|
104
|
+
* SINGLE merged text, no variants). Filled with the INITIATOR's identity
|
|
105
|
+
* (machine-agent address = the write-back target), the conversation title,
|
|
106
|
+
* the body, and the shortId a superd-hub tool addresses. Tail = the user's
|
|
107
|
+
* 2026-09-29 redline, four short paragraphs, each carrying one load:
|
|
108
|
+
* provenance + channel-only principle / heterogeneity disclosure (tool
|
|
109
|
+
* availability genuinely differs per runtime — this is what stops a
|
|
110
|
+
* tool-less model from hunting for one) / how+when to send (sending does
|
|
111
|
+
* not end the turn) / the fallback, phrased from the RECEIVER's operational
|
|
112
|
+
* truth ("the hub receive no hub message" — intentionally or prevented;
|
|
113
|
+
* wording per user ruling 0.1.7-h: the receiver is the hub, the message
|
|
114
|
+
* class is a hub message). The "if available" conditional is evaluated by
|
|
115
|
+
* the MODEL against its own tool
|
|
116
|
+
* list; never pre-select variants on unknowable runtime state. Never a
|
|
117
|
+
* separate user prompt (the OMP 20-tool-call incident).
|
|
118
|
+
*/
|
|
119
|
+
export declare const A2A_MESSAGE_TEMPLATE = "A2A message from {machine}-{agent} (conversation \"{title}\"):\n\n{message}\n\nThis conversation is initiated through superd-hub by the sender. The sender sees only what arrives through this conversation.\n\nAs superd-hub hosts various kinds of agent runtimes, integration into each runtime and superd-hub tool availability in each runtime session might differ.\n\nTo reply, send a message back: with a superd-hub tool, if available, {action:\"send\", agent:\"{machine}-{agent}\", conversationId:\"{shortId}\", message:\"\u2026\"}. You can send at any point, more than once \u2014 sending does not end your turn.\n\nIf the hub receive no hub message from you (intentionally or whatever situation prevents so), your last output at this turn's end is delivered to the sender as your reply.";
|
|
120
|
+
/** Title shown when the conversation has none. */
|
|
121
|
+
export declare const A2A_UNTITLED = "(untitled)";
|
|
122
|
+
/**
|
|
123
|
+
* Render the teaching envelope for one send — a SINGLE pass over the template
|
|
124
|
+
* (injection fix, 2026-09-28): each placeholder is expanded exactly once from
|
|
125
|
+
* the fields, and user-supplied values (title, message) are never re-scanned,
|
|
126
|
+
* so a title containing the literal `{shortId}` or `{message}` lands verbatim
|
|
127
|
+
* instead of being expanded by a later replaceAll round.
|
|
128
|
+
*/
|
|
129
|
+
export declare function renderA2aEnvelope(fields: {
|
|
130
|
+
machine: string;
|
|
131
|
+
agent: string;
|
|
132
|
+
title?: string;
|
|
133
|
+
shortId: string;
|
|
134
|
+
message: string;
|
|
135
|
+
}): string;
|
|
136
|
+
/** Bounded unread buffer size per initiator session. */
|
|
137
|
+
export declare const A2A_BUFFER_MAX = 32;
|
|
138
|
+
/** D28: the sender's synchronous receipt window. If the end-to-end wire has not settled by then the hub answers `accepted` and lets the SAME in-flight request finish in the background. */
|
|
139
|
+
export declare const A2A_SYNC_RECEIPT_MS = 10000;
|
|
140
|
+
/** D28: the TOTAL wire budget for one send (120s). The POST timeout is set from the request start, so after the 10s synchronous window the SAME in-flight request still has ~110s left to settle; the peer may spend it bringing the runtime up (spawn OR resume — indistinguishable at the process level). */
|
|
141
|
+
export declare const A2A_WIRE_TOTAL_BUDGET_MS = 120000;
|
|
142
|
+
/** stop('session') degradation notice — the session plane has no close RPC upstream. */
|
|
143
|
+
export declare const A2A_NO_SESSION_CLOSE_REASON = "no session-close endpoint upstream";
|
|
144
|
+
/** Target address: machine id (m4) + agent id (a6) — the `gatewayFor` key. */
|
|
145
|
+
export interface A2aAddress {
|
|
146
|
+
machine: string;
|
|
147
|
+
id: string;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Initiator identity envelope (spec §7.3), opaque in T2 — Task 3 gives it its
|
|
151
|
+
* real shape. T2 uses `machine`/`agent` for the teaching template and
|
|
152
|
+
* `sessionId` as the unread-buffer key.
|
|
153
|
+
*/
|
|
154
|
+
export interface A2aInitiator {
|
|
155
|
+
machine: string;
|
|
156
|
+
agent: string;
|
|
157
|
+
sessionId: string;
|
|
158
|
+
}
|
|
159
|
+
/** Prompt delivery mode (upstream `SessionPromptRequest.mode`); default 'queue' — peer semantics, never interrupting. */
|
|
160
|
+
export type A2aDelivery = 'queue' | 'steer';
|
|
161
|
+
/**
|
|
162
|
+
* Receipt taxonomy (D28): 'delivered' on every successful prompt submission
|
|
163
|
+
* (end-to-end delivery complete); 'failed' carries an executable error body.
|
|
164
|
+
* ('accepted' / 'spawn-failed' land with the 0.1.8 poll mechanism, T5.)
|
|
165
|
+
*/
|
|
166
|
+
export type A2aReceiptOutcome = 'delivered' | 'accepted' | 'failed';
|
|
167
|
+
/** One failed-send error body, verbatim from the gateway (wire error shape). */
|
|
168
|
+
export interface A2aReceiptError {
|
|
169
|
+
code: string;
|
|
170
|
+
message: string;
|
|
171
|
+
details?: unknown;
|
|
172
|
+
}
|
|
173
|
+
/** send()'s immediate return (spec §2: inbox-acceptance receipt). */
|
|
174
|
+
export interface A2aReceipt {
|
|
175
|
+
outcome: A2aReceiptOutcome;
|
|
176
|
+
/** The client-minted requestId — the durable identity of the injected user message. */
|
|
177
|
+
messageId: string;
|
|
178
|
+
/** Conversation shortId (absent when no conversation resolved). */
|
|
179
|
+
conversationId?: string;
|
|
180
|
+
/** Target session ('' when the failure predates session resolution). */
|
|
181
|
+
sessionId: string;
|
|
182
|
+
/** Present iff outcome === 'failed'. */
|
|
183
|
+
error?: A2aReceiptError;
|
|
184
|
+
/** Model-facing guidance riding a non-terminal receipt (D28: an `accepted` receipt explains that the same in-flight request keeps running). */
|
|
185
|
+
note?: string;
|
|
186
|
+
}
|
|
187
|
+
/** One send request (spec §7.2): no conversationId ≡ new conversation. */
|
|
188
|
+
export interface A2aSendRequest {
|
|
189
|
+
/**
|
|
190
|
+
* Target agent. REQUIRED to open a new conversation (no conversationId);
|
|
191
|
+
* on a continuation it may be omitted — the hub derives the route from the
|
|
192
|
+
* registry row + the caller's session (D24/T3). When supplied on a
|
|
193
|
+
* continuation it is a checked assertion: a mismatch is refused.
|
|
194
|
+
*/
|
|
195
|
+
to?: A2aAddress;
|
|
196
|
+
conversationId?: string;
|
|
197
|
+
title?: string;
|
|
198
|
+
message: string;
|
|
199
|
+
delivery?: A2aDelivery;
|
|
200
|
+
}
|
|
201
|
+
/** Conversation reference: address + shortId. */
|
|
202
|
+
export interface A2aConversationRef {
|
|
203
|
+
to: A2aAddress;
|
|
204
|
+
conversationId: string;
|
|
205
|
+
}
|
|
206
|
+
/** One stop request (D24/T3): `to` is optional — the hub derives the session owner from the row. */
|
|
207
|
+
export interface A2aStopRequest {
|
|
208
|
+
conversationId: string;
|
|
209
|
+
/** The session owner's address; optional (derived from the row), checked when supplied. */
|
|
210
|
+
to?: A2aAddress;
|
|
211
|
+
}
|
|
212
|
+
/** One captured turn completion. */
|
|
213
|
+
export interface A2aCompletion {
|
|
214
|
+
conversationId: string;
|
|
215
|
+
sessionId: string;
|
|
216
|
+
turn: number;
|
|
217
|
+
/** `turn/end.data.reason.kind` (TurnEndReason). */
|
|
218
|
+
reason: string;
|
|
219
|
+
/** Text blocks of the turn's LAST non-interrupted assistant/message, joined with '\n\n'. */
|
|
220
|
+
text: string;
|
|
221
|
+
}
|
|
222
|
+
export type A2aCompletionCb = (completion: A2aCompletion) => void;
|
|
223
|
+
export type A2aErrorCb = (error: {
|
|
224
|
+
code: string;
|
|
225
|
+
message: string;
|
|
226
|
+
}) => void;
|
|
227
|
+
export interface A2aSubscribeOptions {
|
|
228
|
+
/**
|
|
229
|
+
* Correlate one send: the requestId used on `session/prompt`. The cb fires
|
|
230
|
+
* exactly once — for the turn that admitted this request's user message
|
|
231
|
+
* (matched by `data.source.rpcId`) — then the subscription auto-disposes.
|
|
232
|
+
* Omit to observe every turn completion while subscribed (one cb per turn).
|
|
233
|
+
*/
|
|
234
|
+
requestId?: string;
|
|
235
|
+
/** Death/failure notice path (missing conversation/gateway, second stream death). */
|
|
236
|
+
onError?: A2aErrorCb;
|
|
237
|
+
}
|
|
238
|
+
export type A2aStopScope = 'turn' | 'session';
|
|
239
|
+
/** stop() idle note — the honest answer when the pre-cancel liveness snapshot showed no live turn (D5). */
|
|
240
|
+
export declare const A2A_NO_TURN_ACTIVE_NOTE = "no turn was active";
|
|
241
|
+
/** stop() result: 'turn' answers a turn-scope stop; 'turn-only' reports the session-scope degradation.
|
|
242
|
+
* `cancelled:false` is the honest idle answer (D5): the cancel RPC still fired (today's wire behavior
|
|
243
|
+
* is unchanged), but the pre-cancel liveness read showed no turn to stop — the note says so. */
|
|
244
|
+
export type A2aStopResult = {
|
|
245
|
+
scope: 'turn';
|
|
246
|
+
cancelled: true;
|
|
247
|
+
} | {
|
|
248
|
+
scope: 'turn';
|
|
249
|
+
cancelled: false;
|
|
250
|
+
note: typeof A2A_NO_TURN_ACTIVE_NOTE;
|
|
251
|
+
} | {
|
|
252
|
+
scope: 'turn-only';
|
|
253
|
+
cancelled: true;
|
|
254
|
+
reason: typeof A2A_NO_SESSION_CLOSE_REASON;
|
|
255
|
+
} | {
|
|
256
|
+
scope: 'turn-only';
|
|
257
|
+
cancelled: false;
|
|
258
|
+
note: typeof A2A_NO_TURN_ACTIVE_NOTE;
|
|
259
|
+
reason: typeof A2A_NO_SESSION_CLOSE_REASON;
|
|
260
|
+
};
|
|
261
|
+
/** One buffered (undelivered) inbound message for an initiator session. */
|
|
262
|
+
export interface A2aBufferedMessage {
|
|
263
|
+
messageId: string;
|
|
264
|
+
initiatorSessionId: string;
|
|
265
|
+
from: A2aAddress;
|
|
266
|
+
conversationId: string;
|
|
267
|
+
text: string;
|
|
268
|
+
bufferedAt: number;
|
|
269
|
+
}
|
|
270
|
+
/** Input half of a buffer push (Task 3's delivery layer calls this on reply-delivery failure). */
|
|
271
|
+
export type A2aBufferInput = Omit<A2aBufferedMessage, 'initiatorSessionId' | 'bufferedAt'>;
|
|
272
|
+
/** The gateway face this service consumes — structural subset of `GatewayFace` (targets.ts). */
|
|
273
|
+
export interface A2aGateway {
|
|
274
|
+
dispatchRpc(endpoint: string, payload: unknown, signal: AbortSignal): Promise<unknown>;
|
|
275
|
+
openWireStream(endpoint: string, payload: unknown, signal: AbortSignal): AsyncIterable<unknown> | Promise<AsyncIterable<unknown>>;
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* Typed address-resolution failure (D7) — the class lives in a2a-wire.ts
|
|
279
|
+
* since postoffice R1 (the wire helpers classify transport failures with
|
|
280
|
+
* it); re-exported above for this module's existing consumers.
|
|
281
|
+
*/
|
|
282
|
+
export interface A2aServiceDeps {
|
|
283
|
+
home: string;
|
|
284
|
+
/** Still the LOCAL leg's resolver (subscribeCompletion + the ingest assembly source); the send/stop paths never resolve a REMOTE gateway since R4 — remote routing is envelope-wire only. */
|
|
285
|
+
gatewayFor(addr: A2aAddress): A2aGateway;
|
|
286
|
+
/**
|
|
287
|
+
* R4: the ingest handler shared with the HTTP endpoint — the self-routing
|
|
288
|
+
* leg. LAZY (resolved at send time, never construction time: the ingest's
|
|
289
|
+
* delivery face buffers through THIS service's buffer leg, so the instance
|
|
290
|
+
* can only exist after the service does).
|
|
291
|
+
*/
|
|
292
|
+
ingest?(): A2aIngest | undefined;
|
|
293
|
+
/** R4: fetch override for the remote wire leg (tests inject fixtures). */
|
|
294
|
+
fetchImpl?: typeof fetch;
|
|
295
|
+
/** Operational journal (0.1.7-b): one greppable line per machine event; the cordis host wires it onto ctx.logger, tests inject a collector. */
|
|
296
|
+
log?: A2aLogFn;
|
|
297
|
+
now?(): number;
|
|
298
|
+
/** D28: override the synchronous receipt window (tests run it small; production uses A2A_SYNC_RECEIPT_MS). */
|
|
299
|
+
syncReceiptMs?: number;
|
|
300
|
+
}
|
|
301
|
+
export interface A2aService {
|
|
302
|
+
/** Create-or-resume + inject one user turn; receipt semantics (never throws — failures are 'failed' receipts). */
|
|
303
|
+
send(req: A2aSendRequest, initiator: A2aInitiator): Promise<A2aReceipt>;
|
|
304
|
+
/** Subscribe turn completions of one conversation over session/follow; returns the dispose fn. */
|
|
305
|
+
subscribeCompletion(conv: A2aConversationRef, cb: A2aCompletionCb, opts?: A2aSubscribeOptions): () => void;
|
|
306
|
+
/** Cancel the active turn; 'session' scope degrades to turn-only with the typed reason. cancelled:false + note is the honest idle answer (D5 — the cancel still fires; a pre-cancel snapshot read proved no turn was live). */
|
|
307
|
+
stop(req: A2aStopRequest, scope: A2aStopScope): Promise<A2aStopResult>;
|
|
308
|
+
/** Buffer one undelivered message for an initiator (bounded A2A_BUFFER_MAX, oldest dropped). */
|
|
309
|
+
buffer(initiatorSessionId: string, message: A2aBufferInput): void;
|
|
310
|
+
/** Sweep the unread buffer — one initiator, or all; swept entries are returned and cleared. */
|
|
311
|
+
read(initiatorSessionId?: string): A2aBufferedMessage[];
|
|
312
|
+
/** Snapshot of the registry rows. */
|
|
313
|
+
listConversations(): ConversationRow[];
|
|
314
|
+
}
|
|
315
|
+
/** Map one typed address failure to its wire error body (D7): the error's own
|
|
316
|
+
* message; the code keeps 'a2a/gateway-unavailable' for the unreachable branch
|
|
317
|
+
* (today's code) and names the kind otherwise. */
|
|
318
|
+
export declare function addressErrorBody(error: A2aAddressError): {
|
|
319
|
+
code: string;
|
|
320
|
+
message: string;
|
|
321
|
+
};
|
|
322
|
+
/**
|
|
323
|
+
* D5 turn-liveness read: open the follow stream, replay the FIRST snapshot
|
|
324
|
+
* frame's records, and report whether a turn is still open (a turn/start not
|
|
325
|
+
* followed by its turn/end). Read manually (no for-await) and aborted BEFORE
|
|
326
|
+
* returning — closing an async generator suspended at an await must not gate
|
|
327
|
+
* the answer (the close promise can reject on abort and would eat the result).
|
|
328
|
+
* @returns true (a turn is live), false (idle), or undefined when the plane
|
|
329
|
+
* could not answer (stream death, stream end, no snapshot, read timeout) —
|
|
330
|
+
* callers treat undefined conservatively: cancel anyway, keep today's shape.
|
|
331
|
+
*/
|
|
332
|
+
export declare function turnLivenessOf(gateway: A2aGateway, sessionId: string): Promise<boolean | undefined>;
|
|
333
|
+
/**
|
|
334
|
+
* One session-plane RPC through the gateway: payload via `wireArgsOf`,
|
|
335
|
+
* `{ok:false,error}` rethrown as a typed EnvelopeError (error body verbatim),
|
|
336
|
+
* transport throws pass through untouched (they are already gateway/* typed).
|
|
337
|
+
*/
|
|
338
|
+
export declare function sessionRpc<T>(gateway: A2aGateway, endpoint: string, request: unknown): Promise<T>;
|
|
339
|
+
/** A directly-addressed session to watch — the ingest leg (postoffice R3)
|
|
340
|
+
* subscribes the LOCAL gateway without the initiator-side registry checks. */
|
|
341
|
+
export interface A2aSessionSource {
|
|
342
|
+
/** The session-plane gateway owning the session (local leg on the ingest side). */
|
|
343
|
+
gateway: A2aGateway;
|
|
344
|
+
/** The session to follow. */
|
|
345
|
+
sessionId: string;
|
|
346
|
+
/** Conversation id reported in fired completions (the registry shortId). */
|
|
347
|
+
conversationId: string;
|
|
348
|
+
}
|
|
349
|
+
/**
|
|
350
|
+
* The completion watcher behind `subscribeCompletion` (extracted verbatim in
|
|
351
|
+
* postoffice R3): one follow stream over the addressed session, turn-text
|
|
352
|
+
* reduction, requestId arming, one resubscribe, snapshot-history fire.
|
|
353
|
+
* Same contract as the service method — every fired completion is one cb,
|
|
354
|
+
* armed subscriptions auto-dispose after their turn fires.
|
|
355
|
+
*/
|
|
356
|
+
export declare function subscribeSessionCompletion(source: A2aSessionSource, cb: A2aCompletionCb, opts?: A2aSubscribeOptions): () => void;
|
|
357
|
+
export declare function createA2aService(deps: A2aServiceDeps): A2aService;
|