super-dsh 0.1.5 → 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.
Files changed (79) hide show
  1. package/client/index.js +1010 -345
  2. package/cordis.patch.yml +22 -5
  3. package/package.json +8 -3
  4. package/superd-hub/dist/a2a-delivery.d.ts +189 -0
  5. package/superd-hub/dist/a2a-delivery.js +240 -0
  6. package/superd-hub/dist/a2a-host.d.ts +76 -0
  7. package/superd-hub/dist/a2a-host.js +145 -0
  8. package/superd-hub/dist/a2a-ingest.d.ts +31 -0
  9. package/superd-hub/dist/a2a-ingest.js +636 -0
  10. package/superd-hub/dist/a2a-log.d.ts +17 -0
  11. package/superd-hub/dist/a2a-log.js +42 -0
  12. package/superd-hub/dist/a2a-row.d.ts +65 -0
  13. package/superd-hub/dist/a2a-row.js +162 -0
  14. package/superd-hub/dist/a2a-service.d.ts +357 -0
  15. package/superd-hub/dist/a2a-service.js +980 -0
  16. package/superd-hub/dist/a2a-wire.d.ts +220 -0
  17. package/superd-hub/dist/a2a-wire.js +305 -0
  18. package/superd-hub/dist/agent-ids.d.ts +10 -0
  19. package/superd-hub/dist/agent-ids.js +68 -0
  20. package/superd-hub/dist/agent-roster.d.ts +32 -17
  21. package/superd-hub/dist/agent-roster.js +54 -15
  22. package/superd-hub/dist/agent-status.d.ts +119 -0
  23. package/superd-hub/dist/agent-status.js +386 -0
  24. package/superd-hub/dist/client/mesh-api.d.ts +134 -0
  25. package/superd-hub/dist/client/mesh-api.js +219 -0
  26. package/superd-hub/dist/client/mesh-groups.d.ts +63 -0
  27. package/superd-hub/dist/client/mesh-groups.js +66 -0
  28. package/superd-hub/dist/client/roster.d.ts +48 -0
  29. package/superd-hub/dist/client/roster.js +96 -0
  30. package/superd-hub/dist/exec-pty-channel.d.ts +58 -0
  31. package/superd-hub/dist/exec-pty-channel.js +162 -0
  32. package/superd-hub/dist/exec-pty-framing.d.ts +32 -0
  33. package/superd-hub/dist/exec-pty-framing.js +81 -0
  34. package/superd-hub/dist/exec-pty-rpc.d.ts +21 -0
  35. package/superd-hub/dist/exec-pty-rpc.js +128 -0
  36. package/superd-hub/dist/exec-pty-session.d.ts +76 -0
  37. package/superd-hub/dist/exec-pty-session.js +234 -0
  38. package/superd-hub/dist/exec.d.ts +56 -0
  39. package/superd-hub/dist/exec.js +225 -0
  40. package/superd-hub/dist/fusion-transport.d.ts +23 -21
  41. package/superd-hub/dist/fusion-transport.js +12 -40
  42. package/superd-hub/dist/gateway.d.ts +39 -2
  43. package/superd-hub/dist/gateway.js +62 -3
  44. package/superd-hub/dist/hub-tool.d.ts +174 -0
  45. package/superd-hub/dist/hub-tool.js +464 -0
  46. package/superd-hub/dist/index.d.ts +18 -4
  47. package/superd-hub/dist/index.js +19 -3
  48. package/superd-hub/dist/machines.d.ts +45 -0
  49. package/superd-hub/dist/machines.js +125 -0
  50. package/superd-hub/dist/mesh-federate.d.ts +138 -0
  51. package/superd-hub/dist/mesh-federate.js +223 -0
  52. package/superd-hub/dist/mesh-gateway.d.ts +67 -0
  53. package/superd-hub/dist/mesh-gateway.js +408 -0
  54. package/superd-hub/dist/mesh-link.d.ts +274 -0
  55. package/superd-hub/dist/mesh-link.js +441 -0
  56. package/superd-hub/dist/mesh-rpc.d.ts +123 -0
  57. package/superd-hub/dist/mesh-rpc.js +250 -0
  58. package/superd-hub/dist/mux-client.d.ts +71 -0
  59. package/superd-hub/dist/mux-client.js +281 -0
  60. package/superd-hub/dist/pack-version.d.ts +2 -0
  61. package/superd-hub/dist/pack-version.js +30 -0
  62. package/superd-hub/dist/roster.d.ts +10 -0
  63. package/superd-hub/dist/roster.js +9 -1
  64. package/superd-hub/dist/rpc.d.ts +18 -2
  65. package/superd-hub/dist/rpc.js +34 -4
  66. package/superd-hub/dist/spawn-world.js +3 -1
  67. package/superd-hub/dist/switch-core.d.ts +98 -21
  68. package/superd-hub/dist/switch-core.js +123 -28
  69. package/superd-hub/dist/system-head.d.ts +22 -12
  70. package/superd-hub/dist/system-head.js +100 -84
  71. package/superd-hub/dist/toast-core.d.ts +43 -0
  72. package/superd-hub/dist/toast-core.js +63 -0
  73. package/superd-hub/dist/wire-endpoints.d.ts +264 -0
  74. package/superd-hub/dist/wire-endpoints.js +180 -0
  75. package/superd-hub/dist/world-join.js +8 -3
  76. package/superd-hub/dist/world-mount.d.ts +27 -0
  77. package/superd-hub/dist/world-mount.js +67 -0
  78. package/superd-hub/lib/client/index.js +1010 -345
  79. package/superd-hub/package.json +11 -2
package/cordis.patch.yml CHANGED
@@ -21,6 +21,10 @@
21
21
  # On/off: row `disabled: true`, or config
22
22
  # `trustedPageAuthorities: []` (inert). Default
23
23
  # derives from the trusted-hosts env.
24
+ # - super-dsh/a2a the agent-facing A2A tool face (mesh P3): the
25
+ # single `superd-hub` model tool on ctx0's tools
26
+ # service (dormant without tools; world trees
27
+ # never compose this row — see a2a-row.ts).
24
28
  #
25
29
  # Profile-layer values (webserver port/host, settings path) stay in the USER's
26
30
  # profile patch layer, never here.
@@ -33,17 +37,30 @@
33
37
  - id: superd-web-trust
34
38
  name: 'super-dsh/web-trust'
35
39
  # Native UI fusion transport (plan Task 3): the ctx0 page-level state atom
36
- # + five call-time patches, flag-gated (SUPERD_UI_FUSION, default off).
40
+ # + five call-time patches. Composing this row IS the switch — there is no
41
+ # environment variable in the decision (2026-09-27 user ruling: a release
42
+ # gate living in an env var is deployment state no patch layer can see).
37
43
  # ROW ORDER IS LOAD ORDER and the row MUST stay after superd-web-trust:
38
44
  # the trust script's non-merging `if(!window.__DSH_TRANSPORT__)` verdict
39
45
  # must be evaluated before the fusion script could own that global
40
46
  # (spec §3.4, hard; precedent world-web-server.ts renderIndex).
41
- # On/off (Task 5 formalization): env `SUPERD_UI_FUSION` derives the
42
- # schema default (off when unset); a profile layer may restate this row
43
- # (same id, same name) with `config: {enabled: true|false}` to override.
44
- # Off = the component registers nothing: no script, no flag global.
47
+ # To turn the fused surface off: restate this row (same id, same name) with
48
+ # `config: {enabled: false}` or `disabled: true`, or drop it from the
49
+ # composition. Off = the component registers nothing: no script, no global.
45
50
  - id: superd-fusion-transport
46
51
  name: 'super-dsh/fusion-transport'
52
+ # A2A tool face (mesh P3 Task 4): composes the single model-facing
53
+ # `superd-hub` tool onto ctx0's tools service and closes the a2a-host
54
+ # nativeGateway seam with the hub process's own typertGateway. Subpath row
55
+ # BY RULING (a2a-row.ts header): world trees provide a tools service too,
56
+ # so riding the bare superd-hub row would mount a mis-wired tool inside
57
+ # every world; this row lives only HERE, which world profiles never
58
+ # compose — ctx0 only, exactly the P3 initiator surface (foreign runtimes
59
+ # get their own injection in P5, spec §10). Dormant without tools.
60
+ # Off: restate with `disabled: true` or drop the row — nothing else
61
+ # registers the tool.
62
+ - id: superd-a2a
63
+ name: 'super-dsh/a2a'
47
64
  - id: agent-omp
48
65
  name: 'super-dsh/agents/omp'
49
66
  - id: agent-codex
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "super-dsh",
3
- "version": "0.1.5",
3
+ "version": "0.1.9",
4
4
  "description": "Super D for the DeepSeek Harness (DSH) — one dsh plugin that mounts the superd hub (runtime selector, roster, per-runtime agents) and joins local foreign-agent runtimes (OMP, Codex, Claude, Pi, Hermes, Antigravity) into the DSH Web UI. Each runtime keeps its native home and session store; the hub only bridges.",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
7
  "repository": {
8
8
  "type": "git",
9
- "url": "git+https://github.com/pgmi-builds/superd.git"
9
+ "url": "git+https://github.com/pgmi-builds/super-dsh.git"
10
10
  },
11
11
  "exports": {
12
12
  ".": "./superd-hub/dist/index.js",
@@ -22,7 +22,8 @@
22
22
  "./cordis.patch.yml": "./cordis.patch.yml",
23
23
  "./package.json": "./package.json",
24
24
  "./web-trust": "./superd-hub/dist/web-trust.js",
25
- "./fusion-transport": "./superd-hub/dist/fusion-transport.js"
25
+ "./fusion-transport": "./superd-hub/dist/fusion-transport.js",
26
+ "./a2a": "./superd-hub/dist/a2a-row.js"
26
27
  },
27
28
  "files": [
28
29
  "cordis.patch.yml",
@@ -87,6 +88,7 @@
87
88
  "@deepseek-ai/dsh-scope": ">=0.1.5-0 <0.3.0-0",
88
89
  "@deepseek-ai/dsh-session": ">=0.1.5-0 <0.3.0-0",
89
90
  "@deepseek-ai/dsh-session-persistence": ">=0.1.5-0 <0.3.0-0",
91
+ "@deepseek-ai/dsh-tools": ">=0.1.5-0 <0.3.0-0",
90
92
  "@deepseek-ai/dsh-typert-protocol": ">=0.1.5-0 <0.3.0-0",
91
93
  "@deepseek-ai/dsh-user-approval": ">=0.1.5-0 <0.3.0-0",
92
94
  "@deepseek-ai/dsh-user-questions": ">=0.1.5-0 <0.3.0-0"
@@ -125,6 +127,9 @@
125
127
  "@deepseek-ai/dsh-session-persistence": {
126
128
  "optional": true
127
129
  },
130
+ "@deepseek-ai/dsh-tools": {
131
+ "optional": true
132
+ },
128
133
  "@deepseek-ai/dsh-typert-protocol": {
129
134
  "optional": true
130
135
  },
@@ -0,0 +1,189 @@
1
+ import type { ContentBlock } from '@deepseek-ai/dsh-llm';
2
+ import { A2A_NOTICE_SUMMARY_MAX, type A2aNoticeReason } from './a2a-wire.js';
3
+ export { A2A_NOTICE_SUMMARY_MAX, type A2aNoticeReason };
4
+ import type { A2aConversationRef, A2aInitiator, A2aService } from './a2a-service.js';
5
+ /** Durable attribution for one peer reply relayed into the initiator's conversation. */
6
+ export interface A2aRelaySource {
7
+ readonly kind: 'a2a-relay';
8
+ /** A message another agent addressed to this one (`relay` context form). */
9
+ readonly form: 'relay';
10
+ /** Machine id (m4) of the replying peer. */
11
+ readonly senderMachine: string;
12
+ /** Agent id (a6) of the replying peer. */
13
+ readonly senderAgent: string;
14
+ /** Conversation shortId the reply belongs to. */
15
+ readonly conversationId: string;
16
+ /** Durable identity of this relay (minted at delivery, one per fired completion). */
17
+ readonly messageId: string;
18
+ }
19
+ /** Why a runtime-owned notice was produced — the taxonomy now lives on the
20
+ * protocol layer (a2a-wire.ts, imported + re-exported above): 0.1.7-h R2
21
+ * made it a wire fact, and the model-facing notice line renders it. */
22
+ /**
23
+ * Durable attribution for the hub's own account of an A2A conversation event
24
+ * (a failed send, a dead completion stream, a stop) — deliberately a notice,
25
+ * not a relay: the hub states what became of the conversation and never
26
+ * credits the peer with words it did not write.
27
+ */
28
+ export interface A2aNoticeSource {
29
+ readonly kind: 'a2a-notice';
30
+ /** A runtime account shown without expanding the row (`notice` context form). */
31
+ readonly form: 'notice';
32
+ /** One-line account of what happened, bounded to {@link A2A_NOTICE_SUMMARY_MAX}. */
33
+ readonly summary: string;
34
+ /** Conversation shortId the notice is about, when one is known. */
35
+ readonly conversationId?: string;
36
+ /** Which failure this notice reports. */
37
+ readonly reason: A2aNoticeReason;
38
+ }
39
+ declare module '@deepseek-ai/dsh-llm' {
40
+ interface MessageSourceMap {
41
+ 'a2a-relay': A2aRelaySource;
42
+ 'a2a-notice': A2aNoticeSource;
43
+ }
44
+ }
45
+ /** Bound for a notice summary — owned by a2a-wire.ts since R1 (see the re-export above). */
46
+ /**
47
+ * Bound one notice summary to {@link A2A_NOTICE_SUMMARY_MAX} — the same
48
+ * ellipsizing as upstream `boundContextSummary` (dsh-llm message.ts).
49
+ * @param summary - the producer's one-line account, of any length.
50
+ * @returns the account, ellipsized when it exceeds the bound.
51
+ */
52
+ export declare function boundNoticeSummary(summary: string): string;
53
+ /** Reply-routing choice over `ctx.agents`; peer semantics default to followup (spec §7.3). */
54
+ export type A2aReplyDelivery = 'followup' | 'steer';
55
+ /** One delivered (or buffered) reply/notice outcome. */
56
+ export type A2aDeliveryOutcome = {
57
+ delivered: 'followup';
58
+ } | {
59
+ delivered: 'steer';
60
+ } | {
61
+ delivered: 'buffered';
62
+ } | {
63
+ delivered: 'dropped';
64
+ reason: 'no-conversation';
65
+ };
66
+ /** Structural mirror of upstream `createUserMessage` output (dsh-llm message.ts) —
67
+ * fresh identity, role 'user', content blocks, producer source; detached and
68
+ * deep-frozen before publication. The id mirrors upstream's branded MessageId
69
+ * as a plain string (brands are compile-time only; the hub keeps zero dsh-llm
70
+ * runtime dependencies). */
71
+ export interface A2aDeliveredMessage {
72
+ readonly id: string;
73
+ readonly role: 'user';
74
+ readonly content: readonly ContentBlock[];
75
+ readonly source: A2aRelaySource | A2aNoticeSource;
76
+ }
77
+ /**
78
+ * The agent-handle face this layer consumes — structural subset of upstream
79
+ * `Agent` (packages/core/agent/src/runtime-types.ts): lifecycle status plus
80
+ * the three inbox boundaries followup / steer / inject.
81
+ */
82
+ export interface A2aAgentHandle {
83
+ /** Lifecycle state ('idle' | 'running' upstream); peer routing does NOT branch on it. */
84
+ readonly status: string;
85
+ followup(message: A2aDeliveredMessage): void;
86
+ steer(message: A2aDeliveredMessage): void;
87
+ inject(message: A2aDeliveredMessage): void;
88
+ }
89
+ /**
90
+ * The delivery context: the live-agent registry face plus Task 2's bounded
91
+ * unread-buffer leg (the persistence policy for initiators that are not live).
92
+ */
93
+ export interface A2aDeliveryCtx {
94
+ /** Structural subset of `ctx.agents` (AgentRegistry): `get` → live handle or undefined. */
95
+ readonly agents: {
96
+ get(sessionId: string): A2aAgentHandle | undefined;
97
+ };
98
+ /** Task 2's `A2aService.buffer` leg — where undeliverable messages wait. */
99
+ readonly buffer: A2aService['buffer'];
100
+ }
101
+ /** Producer attribution for one relayed peer reply. */
102
+ export interface A2aReplyMeta {
103
+ senderMachine: string;
104
+ senderAgent: string;
105
+ /** Conversation shortId the reply belongs to. */
106
+ conversationId: string;
107
+ /** Durable identity of this relay (one per fired completion). */
108
+ messageId: string;
109
+ /** Conversation title for the relay prefix (untitled placeholder when absent). */
110
+ title?: string;
111
+ /** D20: true = the runtime's transcription of the peer's turn-end last output (the peer never sent it through the channel) — renders the provenance prefix instead of "replied". */
112
+ transcribed?: boolean;
113
+ }
114
+ /** Producer attribution for one runtime notice. */
115
+ export interface A2aNoticeMeta {
116
+ senderMachine: string;
117
+ senderAgent: string;
118
+ /** Conversation shortId the notice is about; absent notices cannot buffer. */
119
+ conversationId?: string;
120
+ reason: A2aNoticeReason;
121
+ }
122
+ /**
123
+ * The model-facing reply envelope (0.1.7-h R1 — one stable machine-readable
124
+ * vocabulary, replacing the v3 prose prefixes per the operator's §8 ruling:
125
+ * provenance the model had to recover from English sentences is now stated):
126
+ * a header line of `k=v` facts, a blank line, then the reply body. `form`
127
+ * keeps the D20 provenance split — `relay` = the peer chose to send through
128
+ * the channel; `transcript` = the peer never sent, the runtime is delivering
129
+ * its last output at turn end (the note spells out that observable fact per
130
+ * the user's no-jargon redline).
131
+ * @param meta - the reply attribution.
132
+ * @param content - the reply text (channel message or transcribed last output).
133
+ * @returns `[superd-hub:a2a] kind=reply form=… conversation=… from=… message=…[ note="…"]\n\n<body>`.
134
+ */
135
+ export declare function renderA2aRelayText(meta: A2aReplyMeta, content: string): string;
136
+ /** R2: the three model-facing cause values — one wire fact maps to exactly one
137
+ * model value, so the notice line and its summary can never disagree. */
138
+ export type A2aModelNoticeReason = 'peer-silent' | 'peer-error' | 'stopped-by-initiator' | 'delivered' | 'undelivered';
139
+ /** Map the wire cause taxonomy onto the model-facing one. */
140
+ export declare function modelNoticeReason(reason: A2aNoticeReason): A2aModelNoticeReason;
141
+ /**
142
+ * Render the model-visible notice text (0.1.7-h R1/R2): ONE line in the same
143
+ * envelope vocabulary as relays, plus the R2 cause and the bounded summary.
144
+ * Inner double quotes are sanitized to single quotes (legacy peer summaries
145
+ * embed `conversation "x"`) so the k=v line stays parseable; the wire/source
146
+ * summary keeps its verbatim text.
147
+ */
148
+ export declare function renderA2aNoticeText(meta: A2aNoticeMeta, summary: string): string;
149
+ /**
150
+ * Deliver one peer reply into the initiating agent's conversation.
151
+ * @param ctx - live-agent face + buffer leg.
152
+ * @param initiator - who asked (its sessionId keys the buffer).
153
+ * @param content - the peer's final reply text.
154
+ * @param meta - relay attribution (sender address, conversation, identity, title).
155
+ * @param opts - `delivery:'steer'` overrides the followup default.
156
+ * @returns where the reply landed.
157
+ */
158
+ export declare function deliverReply(ctx: A2aDeliveryCtx, initiator: A2aInitiator, content: string, meta: A2aReplyMeta, opts?: {
159
+ delivery?: A2aReplyDelivery;
160
+ }): A2aDeliveryOutcome;
161
+ /**
162
+ * Deliver one runtime-owned notice (bounded one-line account) into the
163
+ * initiating agent's conversation.
164
+ * @param ctx - live-agent face + buffer leg.
165
+ * @param initiator - who asked (its sessionId keys the buffer).
166
+ * @param summary - the one-line account, any length (bounded here).
167
+ * @param meta - notice attribution; a notice without a conversation cannot buffer.
168
+ * @param opts - `delivery:'steer'` overrides the followup default.
169
+ * @returns where the notice landed.
170
+ */
171
+ export declare function deliverNotice(ctx: A2aDeliveryCtx, initiator: A2aInitiator, summary: string, meta: A2aNoticeMeta, opts?: {
172
+ delivery?: A2aReplyDelivery;
173
+ }): A2aDeliveryOutcome;
174
+ /** The Task-2 service surface the glue consumes. */
175
+ export type A2aCompletionSource = Pick<A2aService, 'subscribeCompletion' | 'listConversations'>;
176
+ /**
177
+ * Wire one conversation's completion stream to the initiator's delivery face:
178
+ * every fired completion becomes one relayed reply (titled from the registry
179
+ * row); a completion-stream death becomes one bounded stream-dead notice.
180
+ * Wire one conversation's completion stream to the initiator's delivery face:
181
+ * every fired completion becomes one relayed reply (titled from the registry
182
+ * row) — EXCEPT a turn that ended non-completed with NO captured text, which
183
+ * degrades to a bounded 'aborted' notice (D3: a content-free relay is noise
184
+ * and can wake an idle initiator for nothing); a completion-stream death
185
+ * becomes one bounded stream-dead notice.
186
+ */
187
+ export declare function wireCompletionToDelivery(ctx: A2aDeliveryCtx, service: A2aCompletionSource, initiator: A2aInitiator, conv: A2aConversationRef, opts?: {
188
+ requestId?: string;
189
+ }): () => void;
@@ -0,0 +1,240 @@
1
+ /**
2
+ * A2A reply-delivery face (mesh P3 Task 3): how a peer agent's reply arrives
3
+ * back in the INITIATING agent's conversation — the two producer source kinds
4
+ * for `@deepseek-ai/dsh-llm`'s MessageSourceMap, the peer-routing rules over
5
+ * `ctx.agents`, and the subscribe→deliver glue.
6
+ *
7
+ * Upstream anchors (`upstream/deepseek-harness` @ dsh-v0.1.7-rc.1):
8
+ * - the continuation-message pattern: `packages/subagent/subagent/src/
9
+ * continuation-messages.ts` — each producer declares its own kinds into
10
+ * MessageSourceMap via `declare module '@deepseek-ai/dsh-llm'` with a
11
+ * type-only import of the target package, and builds user-role messages
12
+ * through `createUserMessage` (fresh identity, detached, deep-frozen).
13
+ * That constructor is not re-exported by any package the hub may consume,
14
+ * so this module constructs the SAME structural shape locally
15
+ * ({@link A2aDeliveredMessage}) and keeps a zero-dependency runtime —
16
+ * the type-only import below anchors the augmentation exactly like the
17
+ * upstream precedent.
18
+ * - the delivery routing template: `continuation-activation.ts`
19
+ * notifySettlement (:871-888) — idle→queue, busy→steer, closing
20
+ * teardown→inject, missing→nothing. That is the PARENT/child contract.
21
+ * A2A peers are not parents (spec §7.3 ruling): a peer reply is ordinary
22
+ * queued input, so idle AND busy both deliver via `followup` — never
23
+ * interrupting the initiator's active turn — and `steer` exists ONLY for
24
+ * explicit `delivery:'steer'` requests; a missing initiator (not live,
25
+ * torn down) lands in Task 2's bounded unread buffer instead of being
26
+ * injected or dropped.
27
+ * - notice summaries are bounded like upstream `boundContextSummary`
28
+ * (CONTEXT_SUMMARY_MAX_CHARS = 120): 119 chars + '…'.
29
+ */
30
+ import { randomUUID } from 'node:crypto';
31
+ import { A2A_NOTICE_SUMMARY_MAX } from './a2a-wire.js';
32
+ // compat re-exports (postoffice R1 / 0.1.7-h R2): both moved to the protocol
33
+ // layer (a2a-wire.ts) — the bound so the envelope guard enforces the same
34
+ // number this module bounds with, the reason type because it now crosses the
35
+ // wire; existing importers keep compiling.
36
+ export { A2A_NOTICE_SUMMARY_MAX };
37
+ /** Bound for a notice summary — owned by a2a-wire.ts since R1 (see the re-export above). */
38
+ /**
39
+ * Bound one notice summary to {@link A2A_NOTICE_SUMMARY_MAX} — the same
40
+ * ellipsizing as upstream `boundContextSummary` (dsh-llm message.ts).
41
+ * @param summary - the producer's one-line account, of any length.
42
+ * @returns the account, ellipsized when it exceeds the bound.
43
+ */
44
+ export function boundNoticeSummary(summary) {
45
+ return summary.length <= A2A_NOTICE_SUMMARY_MAX
46
+ ? summary
47
+ : `${summary.slice(0, A2A_NOTICE_SUMMARY_MAX - 1)}…`;
48
+ }
49
+ /** Recursive freeze, mirroring upstream `deepFreeze(structuredClone(...))` publication. */
50
+ function deepFreeze(value) {
51
+ if (value !== null && typeof value === 'object') {
52
+ if (Array.isArray(value)) {
53
+ for (const item of value)
54
+ deepFreeze(item);
55
+ }
56
+ else {
57
+ for (const key of Object.keys(value))
58
+ deepFreeze(value[key]);
59
+ }
60
+ Object.freeze(value);
61
+ }
62
+ return value;
63
+ }
64
+ /**
65
+ * Build one identified, immutable user-role message — the local equivalent of
66
+ * upstream `createUserMessage`: fresh uuid identity, structuredClone-detached,
67
+ * deep-frozen before publication.
68
+ * @param content - model-visible message blocks.
69
+ * @param source - the producer attribution (our declared kinds).
70
+ * @returns the frozen user message handed to the initiator's inbox.
71
+ */
72
+ function createA2aUserMessage(content, source) {
73
+ return deepFreeze(structuredClone({
74
+ id: randomUUID(),
75
+ role: 'user',
76
+ content,
77
+ source,
78
+ }));
79
+ }
80
+ /**
81
+ * The model-facing reply envelope (0.1.7-h R1 — one stable machine-readable
82
+ * vocabulary, replacing the v3 prose prefixes per the operator's §8 ruling:
83
+ * provenance the model had to recover from English sentences is now stated):
84
+ * a header line of `k=v` facts, a blank line, then the reply body. `form`
85
+ * keeps the D20 provenance split — `relay` = the peer chose to send through
86
+ * the channel; `transcript` = the peer never sent, the runtime is delivering
87
+ * its last output at turn end (the note spells out that observable fact per
88
+ * the user's no-jargon redline).
89
+ * @param meta - the reply attribution.
90
+ * @param content - the reply text (channel message or transcribed last output).
91
+ * @returns `[superd-hub:a2a] kind=reply form=… conversation=… from=… message=…[ note="…"]\n\n<body>`.
92
+ */
93
+ export function renderA2aRelayText(meta, content) {
94
+ const header = meta.transcribed === true
95
+ ? `[superd-hub:a2a] kind=reply form=transcript conversation=${meta.conversationId} from=${meta.senderMachine}-${meta.senderAgent} message=${meta.messageId} note="the peer never sent through the channel — this is the runtime delivering the peer's last output at turn end"`
96
+ : `[superd-hub:a2a] kind=reply form=relay conversation=${meta.conversationId} from=${meta.senderMachine}-${meta.senderAgent} message=${meta.messageId}`;
97
+ return `${header}\n\n${content}`;
98
+ }
99
+ /** Map the wire cause taxonomy onto the model-facing one. */
100
+ export function modelNoticeReason(reason) {
101
+ switch (reason) {
102
+ case 'stop': return 'stopped-by-initiator';
103
+ case 'peer': return 'peer-silent';
104
+ case 'delivered': return 'delivered';
105
+ case 'undelivered': return 'undelivered';
106
+ default: return 'peer-error'; // send-failed / stream-dead / aborted — the peer side ended abnormally
107
+ }
108
+ }
109
+ /**
110
+ * Render the model-visible notice text (0.1.7-h R1/R2): ONE line in the same
111
+ * envelope vocabulary as relays, plus the R2 cause and the bounded summary.
112
+ * Inner double quotes are sanitized to single quotes (legacy peer summaries
113
+ * embed `conversation "x"`) so the k=v line stays parseable; the wire/source
114
+ * summary keeps its verbatim text.
115
+ */
116
+ export function renderA2aNoticeText(meta, summary) {
117
+ const conv = meta.conversationId === undefined ? '' : ` conversation=${meta.conversationId}`;
118
+ return `[superd-hub:a2a] kind=notice${conv} from=${meta.senderMachine}-${meta.senderAgent} reason=${modelNoticeReason(meta.reason)} summary="${summary.replace(/"/g, "'")}"`;
119
+ }
120
+ /**
121
+ * Route one built message to the initiator: live → followup (or steer when
122
+ * explicitly requested); not live → the buffer leg when the input has a
123
+ * conversation to anchor it, else best-effort nothing.
124
+ */
125
+ function route(ctx, initiator, message, delivery, buffered) {
126
+ const agent = ctx.agents.get(initiator.sessionId);
127
+ if (agent === undefined) {
128
+ if (buffered === undefined)
129
+ return { delivered: 'dropped', reason: 'no-conversation' };
130
+ ctx.buffer(initiator.sessionId, buffered);
131
+ return { delivered: 'buffered' };
132
+ }
133
+ if (delivery === 'steer')
134
+ agent.steer(message);
135
+ else
136
+ agent.followup(message);
137
+ return { delivered: delivery };
138
+ }
139
+ /**
140
+ * Deliver one peer reply into the initiating agent's conversation.
141
+ * @param ctx - live-agent face + buffer leg.
142
+ * @param initiator - who asked (its sessionId keys the buffer).
143
+ * @param content - the peer's final reply text.
144
+ * @param meta - relay attribution (sender address, conversation, identity, title).
145
+ * @param opts - `delivery:'steer'` overrides the followup default.
146
+ * @returns where the reply landed.
147
+ */
148
+ export function deliverReply(ctx, initiator, content, meta, opts = {}) {
149
+ const text = renderA2aRelayText(meta, content);
150
+ const message = createA2aUserMessage([{ type: 'text', text }], {
151
+ kind: 'a2a-relay',
152
+ form: 'relay',
153
+ senderMachine: meta.senderMachine,
154
+ senderAgent: meta.senderAgent,
155
+ conversationId: meta.conversationId,
156
+ messageId: meta.messageId,
157
+ });
158
+ return route(ctx, initiator, message, opts.delivery ?? 'followup', {
159
+ messageId: meta.messageId,
160
+ from: { machine: meta.senderMachine, id: meta.senderAgent },
161
+ conversationId: meta.conversationId,
162
+ text,
163
+ });
164
+ }
165
+ /**
166
+ * Deliver one runtime-owned notice (bounded one-line account) into the
167
+ * initiating agent's conversation.
168
+ * @param ctx - live-agent face + buffer leg.
169
+ * @param initiator - who asked (its sessionId keys the buffer).
170
+ * @param summary - the one-line account, any length (bounded here).
171
+ * @param meta - notice attribution; a notice without a conversation cannot buffer.
172
+ * @param opts - `delivery:'steer'` overrides the followup default.
173
+ * @returns where the notice landed.
174
+ */
175
+ export function deliverNotice(ctx, initiator, summary, meta, opts = {}) {
176
+ const bounded = boundNoticeSummary(summary);
177
+ // R1: a notice is an injected turn like any other — its model-facing text
178
+ // is the envelope line (provenance + cause + summary); the buffered copy
179
+ // carries the same line so a later sweep injects identical text.
180
+ const noticeText = renderA2aNoticeText(meta, bounded);
181
+ const message = createA2aUserMessage([{ type: 'text', text: noticeText }], {
182
+ kind: 'a2a-notice',
183
+ form: 'notice',
184
+ summary: bounded,
185
+ ...(meta.conversationId === undefined ? {} : { conversationId: meta.conversationId }),
186
+ reason: meta.reason,
187
+ });
188
+ const buffered = meta.conversationId === undefined ? undefined : {
189
+ messageId: `a2a-notice-${randomUUID()}`,
190
+ from: { machine: meta.senderMachine, id: meta.senderAgent },
191
+ conversationId: meta.conversationId,
192
+ text: noticeText,
193
+ };
194
+ return route(ctx, initiator, message, opts.delivery ?? 'followup', buffered);
195
+ }
196
+ /**
197
+ * Wire one conversation's completion stream to the initiator's delivery face:
198
+ * every fired completion becomes one relayed reply (titled from the registry
199
+ * row); a completion-stream death becomes one bounded stream-dead notice.
200
+ * Wire one conversation's completion stream to the initiator's delivery face:
201
+ * every fired completion becomes one relayed reply (titled from the registry
202
+ * row) — EXCEPT a turn that ended non-completed with NO captured text, which
203
+ * degrades to a bounded 'aborted' notice (D3: a content-free relay is noise
204
+ * and can wake an idle initiator for nothing); a completion-stream death
205
+ * becomes one bounded stream-dead notice.
206
+ */
207
+ export function wireCompletionToDelivery(ctx, service, initiator, conv, opts = {}) {
208
+ const titleOf = (conversationId) => service.listConversations().find((r) => r.shortId === conversationId || r.sessionId === conversationId)?.title;
209
+ return service.subscribeCompletion(conv, (completion) => {
210
+ // D3: a terminal turn that produced NO reply must not relay — aborted
211
+ // WITH captured text still relays (the words exist); only the empty
212
+ // non-completed end becomes a bounded notice.
213
+ if (completion.reason !== 'completed' && completion.text === '') {
214
+ deliverNotice(ctx, initiator, `conversation ${completion.conversationId}: turn aborted, no reply`, {
215
+ senderMachine: conv.to.machine,
216
+ senderAgent: conv.to.id,
217
+ conversationId: completion.conversationId,
218
+ reason: 'aborted',
219
+ });
220
+ return;
221
+ }
222
+ deliverReply(ctx, initiator, completion.text, {
223
+ senderMachine: conv.to.machine,
224
+ senderAgent: conv.to.id,
225
+ conversationId: completion.conversationId,
226
+ messageId: `a2a-relay-${randomUUID()}`,
227
+ title: titleOf(completion.conversationId),
228
+ });
229
+ }, {
230
+ requestId: opts.requestId,
231
+ onError: (error) => {
232
+ deliverNotice(ctx, initiator, `A2A completion stream from ${conv.to.machine}-${conv.to.id} (conversation "${conv.conversationId}") failed: ${error.message}`, {
233
+ senderMachine: conv.to.machine,
234
+ senderAgent: conv.to.id,
235
+ conversationId: conv.conversationId,
236
+ reason: 'stream-dead',
237
+ });
238
+ },
239
+ });
240
+ }
@@ -0,0 +1,76 @@
1
+ import type { A2aAddress, A2aGateway, A2aInitiator, A2aService } from './a2a-service.js';
2
+ /** Host wiring inputs: the hub home plus the local native session-plane face. */
3
+ export interface A2aHostOptions {
4
+ /** Hub home (a6 id store + machines registry live under `<home>/superd/`). */
5
+ home: string;
6
+ /**
7
+ * ctx0's own RAW in-process typertGateway instance — serves the LOCAL
8
+ * 'native' row (its a6); injected by the plugin wiring, undefined before
9
+ * the hub process has its own face. Bridged to T2's face by the resolver
10
+ * ({@link bridgeTypertGateway}).
11
+ */
12
+ nativeGateway?: RawTypertGatewayFace;
13
+ }
14
+ /**
15
+ * The RAW typertGateway service face — upstream canonical stream order
16
+ * `openWireStream(endpoint, payload, uplink, peer, signal, control)`
17
+ * (dsh-api-gateway/src/index.ts:434; the 0.1.3-j positional-misbind lesson).
18
+ * LOCAL worlds and ctx0's own row hand out RAW instances (world-join.ts
19
+ * registers `ctxW.get('typertGateway')` verbatim); FEDERATED rows hand out
20
+ * mesh-gateway wire adapters (dual-face, 3rd-argument dispatch).
21
+ */
22
+ export interface RawTypertGatewayFace {
23
+ dispatchRpc(endpoint: string, payload: unknown, signal: AbortSignal): Promise<unknown>;
24
+ openWireStream(endpoint: string, payload: unknown, uplink: undefined, peer: undefined, signal: AbortSignal, control: AbortController): Promise<AsyncIterable<unknown>> | AsyncIterable<unknown>;
25
+ }
26
+ /**
27
+ * Bridge one RAW typertGateway instance to Task 2's 3-arg `A2aGateway` face
28
+ * (fix round 1, 2026-09-28 review: the positional mismatch stranded completion
29
+ * streams). `uplink: undefined` is the canonical NO-LANE spelling upstream —
30
+ * the gateway substitutes `EMPTY_ASYNC_ITERABLE` itself
31
+ * (dsh-api-gateway/src/index.ts:712) — and `dispatchRpc` passes through as-is.
32
+ *
33
+ * FEDERATED WIRE ADAPTERS MUST NOT BE WRAPPED: mesh-gateway's dual-face
34
+ * dispatch reads the THIRD argument (`isUplinkLane(undefined)` → false), so a
35
+ * canonical 6-arg call would take its 3-arg branch and read the abort signal
36
+ * from position 3 (undefined) — silently losing cancellation. The resolver
37
+ * therefore passes federated rows through untouched.
38
+ * @param real - a raw in-process typertGateway instance (ctx0's own or a local world's).
39
+ * @returns the gateway Task 2's service consumes.
40
+ */
41
+ export declare function bridgeTypertGateway(real: RawTypertGatewayFace): A2aGateway;
42
+ /**
43
+ * Build the gatewayFor resolver Task 2's service consumes: federated table
44
+ * first, shared target registry second, local id-map inversion third,
45
+ * injected native face last. LOCAL rows (runtime-key targets + the native
46
+ * seam) are RAW instances and get {@link bridgeTypertGateway}; FEDERATED rows
47
+ * (federation memo + a6-keyed registry entries) are wire adapters and pass
48
+ * through UNTOWRAPPED (see the bridge's JSDoc for why wrapping them loses
49
+ * the abort signal).
50
+ *
51
+ * A MISS THROWS the typed {@link A2aAddressError} (D7): a roster-known a6 whose
52
+ * plane is down → 'unreachable'; an a6 no store has ever seen → 'unknown-agent'
53
+ * when the addressed machine is self or paired, else 'unknown-machine' — the
54
+ * WHY survives the resolver boundary for future consumers (P4 exec, P5
55
+ * injection) to branch on.
56
+ * @param options - hub home + optional raw in-process native gateway.
57
+ * @returns the resolver (a gateway, or it throws A2aAddressError).
58
+ */
59
+ export declare function resolveA2aGatewayFor(options: A2aHostOptions): (addr: A2aAddress) => A2aGateway;
60
+ /**
61
+ * Build the LOCAL machine's initiator identity from the calling world's
62
+ * roster key and the live DSH session driving the tool call.
63
+ * @param home - hub home (self m4 + a6 id store).
64
+ * @param agentKey - roster key of the calling world ('native' for ctx0's own agent).
65
+ * @param sessionId - the live DSH session id making the call (T4: the tool call's agent context).
66
+ * @returns the initiator envelope Task 2's service teaches the peer.
67
+ */
68
+ export declare function localA2aInitiator(home: string, agentKey: string, sessionId: string): A2aInitiator;
69
+ /**
70
+ * Assemble the full A2A service on the hub's live surfaces: Task 2's pure
71
+ * core with the host gatewayFor resolution (delivery glue is per-conversation
72
+ * — T4's tool wires `wireCompletionToDelivery` after each send).
73
+ * @param options - hub home + optional in-process native gateway.
74
+ * @returns the A2A conversation service.
75
+ */
76
+ export declare function createA2aHost(options: A2aHostOptions): A2aService;