@sentropic/h2a 0.97.2 → 0.97.4

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 (94) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/.codex-plugin/plugin.json +1 -1
  3. package/dist/bin.js +13 -0
  4. package/dist/bin.js.map +1 -1
  5. package/dist/cli.d.ts +13 -0
  6. package/dist/cli.d.ts.map +1 -1
  7. package/dist/cli.js +94 -37
  8. package/dist/cli.js.map +1 -1
  9. package/dist/index.d.ts +1 -1
  10. package/dist/mcp.d.ts +1 -1
  11. package/dist/mcp.d.ts.map +1 -1
  12. package/dist/mcp.js +2 -0
  13. package/dist/mcp.js.map +1 -1
  14. package/dist/runtime/identity/bindings.d.ts +33 -0
  15. package/dist/runtime/identity/bindings.d.ts.map +1 -1
  16. package/dist/runtime/identity/bindings.js +65 -2
  17. package/dist/runtime/identity/bindings.js.map +1 -1
  18. package/dist/runtime/identity/index.d.ts +4 -4
  19. package/dist/runtime/identity/index.d.ts.map +1 -1
  20. package/dist/runtime/identity/index.js +2 -2
  21. package/dist/runtime/identity/index.js.map +1 -1
  22. package/dist/runtime/identity/live.d.ts +63 -0
  23. package/dist/runtime/identity/live.d.ts.map +1 -1
  24. package/dist/runtime/identity/live.js +346 -65
  25. package/dist/runtime/identity/live.js.map +1 -1
  26. package/dist/runtime/identity/worker.d.ts +19 -0
  27. package/dist/runtime/identity/worker.d.ts.map +1 -0
  28. package/dist/runtime/identity/worker.js +189 -0
  29. package/dist/runtime/identity/worker.js.map +1 -0
  30. package/dist/runtime/local-files/locks.d.ts +34 -0
  31. package/dist/runtime/local-files/locks.d.ts.map +1 -1
  32. package/dist/runtime/local-files/locks.js +55 -2
  33. package/dist/runtime/local-files/locks.js.map +1 -1
  34. package/dist/runtime/local-files/store.d.ts +21 -0
  35. package/dist/runtime/local-files/store.d.ts.map +1 -1
  36. package/dist/runtime/local-files/store.js +79 -10
  37. package/dist/runtime/local-files/store.js.map +1 -1
  38. package/dist/runtime/mcp/discovery-pagination.d.ts +73 -0
  39. package/dist/runtime/mcp/discovery-pagination.d.ts.map +1 -0
  40. package/dist/runtime/mcp/discovery-pagination.js +446 -0
  41. package/dist/runtime/mcp/discovery-pagination.js.map +1 -0
  42. package/dist/runtime/mcp/frame-budget.d.ts +149 -0
  43. package/dist/runtime/mcp/frame-budget.d.ts.map +1 -0
  44. package/dist/runtime/mcp/frame-budget.js +275 -0
  45. package/dist/runtime/mcp/frame-budget.js.map +1 -0
  46. package/dist/runtime/mcp/handlers.d.ts +14 -3
  47. package/dist/runtime/mcp/handlers.d.ts.map +1 -1
  48. package/dist/runtime/mcp/handlers.js +22 -1
  49. package/dist/runtime/mcp/handlers.js.map +1 -1
  50. package/dist/runtime/mcp/identity-state.d.ts +131 -0
  51. package/dist/runtime/mcp/identity-state.d.ts.map +1 -0
  52. package/dist/runtime/mcp/identity-state.js +288 -0
  53. package/dist/runtime/mcp/identity-state.js.map +1 -0
  54. package/dist/runtime/mcp/index.d.ts +2 -0
  55. package/dist/runtime/mcp/index.d.ts.map +1 -1
  56. package/dist/runtime/mcp/index.js +2 -0
  57. package/dist/runtime/mcp/index.js.map +1 -1
  58. package/dist/runtime/mcp/notifications.d.ts +13 -1
  59. package/dist/runtime/mcp/notifications.d.ts.map +1 -1
  60. package/dist/runtime/mcp/notifications.js +36 -20
  61. package/dist/runtime/mcp/notifications.js.map +1 -1
  62. package/dist/runtime/mcp/payload-store.d.ts +67 -0
  63. package/dist/runtime/mcp/payload-store.d.ts.map +1 -0
  64. package/dist/runtime/mcp/payload-store.js +242 -0
  65. package/dist/runtime/mcp/payload-store.js.map +1 -0
  66. package/dist/runtime/mcp/phase-trace.d.ts +98 -0
  67. package/dist/runtime/mcp/phase-trace.d.ts.map +1 -0
  68. package/dist/runtime/mcp/phase-trace.js +179 -0
  69. package/dist/runtime/mcp/phase-trace.js.map +1 -0
  70. package/dist/runtime/mcp/server.d.ts +36 -1
  71. package/dist/runtime/mcp/server.d.ts.map +1 -1
  72. package/dist/runtime/mcp/server.js +162 -5
  73. package/dist/runtime/mcp/server.js.map +1 -1
  74. package/dist/runtime/mcp/sessions.d.ts +12 -2
  75. package/dist/runtime/mcp/sessions.d.ts.map +1 -1
  76. package/dist/runtime/mcp/sessions.js +15 -3
  77. package/dist/runtime/mcp/sessions.js.map +1 -1
  78. package/dist/runtime/mcp/stdio.d.ts +21 -0
  79. package/dist/runtime/mcp/stdio.d.ts.map +1 -1
  80. package/dist/runtime/mcp/stdio.js +378 -98
  81. package/dist/runtime/mcp/stdio.js.map +1 -1
  82. package/dist/runtime/mcp/tools.d.ts.map +1 -1
  83. package/dist/runtime/mcp/tools.js +47 -4
  84. package/dist/runtime/mcp/tools.js.map +1 -1
  85. package/dist/runtime/mcp-central.d.ts.map +1 -1
  86. package/dist/runtime/mcp-central.js +17 -7
  87. package/dist/runtime/mcp-central.js.map +1 -1
  88. package/dist/runtime/mcp-http/hosted-mcp-server.d.ts.map +1 -1
  89. package/dist/runtime/mcp-http/hosted-mcp-server.js +16 -6
  90. package/dist/runtime/mcp-http/hosted-mcp-server.js.map +1 -1
  91. package/dist/runtime/mcp-http/readonly-allowlist.d.ts.map +1 -1
  92. package/dist/runtime/mcp-http/readonly-allowlist.js +6 -0
  93. package/dist/runtime/mcp-http/readonly-allowlist.js.map +1 -1
  94. package/package.json +3 -3
@@ -0,0 +1,131 @@
1
+ /**
2
+ * L2 — identity-independent MCP connection + asynchronous identity readiness.
3
+ *
4
+ * The MCP transport (initialize / tools/list) must answer IMMEDIATELY, without
5
+ * waiting on the shared-identity critical section (the keypair read, the
6
+ * registry parse, and above all the identity/registry lock wait). Those run in a
7
+ * dedicated, non-detached CHILD process (`../identity/worker.js`) so they never
8
+ * block the parent's event loop — a `Promise`/`setImmediate` around the sync
9
+ * lock in the PARENT would not help, because the lock wait (and the ~17 MB
10
+ * registry parse) are synchronous and would still stall `initialize`.
11
+ *
12
+ * This module owns the readiness STATE MACHINE and the single 20 s deadline
13
+ * measured from entering `identity_pending`. Activation (opening the real
14
+ * presence session, reading the private key, publishing the correlated readiness
15
+ * ACK, arming the live signer) is delegated to the caller through `activate`,
16
+ * because that side lives with the SessionRegistry in the stdio transport. The
17
+ * controller only transitions to `identity_ready` once activation reports
18
+ * success — never on the child's raw result, never with a provisional key, never
19
+ * with an early availability ACK.
20
+ */
21
+ import type { H2ASendSigner } from "../send.js";
22
+ /** The single identity deadline (ms), measured from entering identity_pending. */
23
+ export declare const MCP_IDENTITY_TIMEOUT_MS = 20000;
24
+ export type IdentityFailureCode = "identity_timeout" | "storage_readonly" | "storage_permission_denied" | "identity_worker_failed" | "identity_proof_failed" | "identity_storage_failed" | "session_open_failed" | "readiness_ack_failed";
25
+ export type McpIdentityStatus = {
26
+ state: "identity_disabled";
27
+ } | {
28
+ state: "identity_pending";
29
+ attemptId: string;
30
+ elapsedMs: number;
31
+ timeoutMs: 20000;
32
+ } | {
33
+ state: "identity_ready";
34
+ attemptId: string;
35
+ instance: string;
36
+ sessionId: string;
37
+ signingAvailable: boolean;
38
+ } | {
39
+ state: "identity_failed";
40
+ attemptId: string;
41
+ cause: IdentityFailureCode;
42
+ message: string;
43
+ retryable: false;
44
+ elapsedMs: number;
45
+ };
46
+ export interface McpIdentityController {
47
+ status(): McpIdentityStatus;
48
+ start(): void;
49
+ cancel(reason: "transport_closed" | "signal"): void;
50
+ signer(): H2ASendSigner | undefined;
51
+ }
52
+ /**
53
+ * The identity attributes the worker resolves and hands back over IPC. It
54
+ * carries NO private key material — only paths, which the parent re-validates
55
+ * against `identityKeyPaths(root, instance)` before reading.
56
+ */
57
+ export interface ResolvedIdentityMessage {
58
+ readonly instance: string;
59
+ readonly host: string;
60
+ readonly workspace?: unknown;
61
+ readonly name?: string;
62
+ readonly action: "override" | "reclaim" | "mint";
63
+ readonly providerSessionId?: string;
64
+ readonly providerSessionSource?: string;
65
+ readonly privateKeyPath: string;
66
+ readonly publicKeyPath: string;
67
+ readonly migrationNotice?: string;
68
+ readonly legacyInstance?: string;
69
+ readonly delegationEligible?: boolean;
70
+ }
71
+ /** The deferred identity request the CLI hands to the transport (no lock work). */
72
+ export interface McpIdentityRequest {
73
+ readonly root: string;
74
+ readonly host: string;
75
+ readonly cwd: string;
76
+ readonly explicitInstance?: string;
77
+ readonly name?: string;
78
+ readonly scopes?: readonly string[];
79
+ readonly declaredCapabilities?: readonly string[];
80
+ /** Provider env captured from the launching session (never re-derived in child). */
81
+ readonly providerEnv?: Readonly<Record<string, string>>;
82
+ }
83
+ /** Result of a caller-supplied activation attempt (session + ACK + signer). */
84
+ export type ActivationResult = {
85
+ ok: true;
86
+ sessionId: string;
87
+ signer?: H2ASendSigner;
88
+ } | {
89
+ ok: false;
90
+ cause: IdentityFailureCode;
91
+ message: string;
92
+ };
93
+ export interface CreateIdentityControllerOptions {
94
+ readonly request: McpIdentityRequest;
95
+ /**
96
+ * Perform the real activation once the worker resolved a valid identity and
97
+ * the deadline still holds: open the presence session, read the private key,
98
+ * publish the correlated readiness ACK, build the live signer. Returning
99
+ * `{ok:false}` fails the identity terminally (no partial availability).
100
+ */
101
+ readonly activate: (identity: ResolvedIdentityMessage) => ActivationResult;
102
+ /** Diagnostic sink (stderr). Never protocol traffic. */
103
+ readonly log?: (line: string) => void;
104
+ /** Test seam: fork a worker child. Production uses the real fork below. */
105
+ readonly spawnWorker?: (request: McpIdentityRequest, budgetMs: number) => IdentityWorkerHandle;
106
+ /** Test seam: monotonic clock in ns. Defaults to process.hrtime.bigint. */
107
+ readonly nowNs?: () => bigint;
108
+ /** Test seam: deadline in ms. Defaults to MCP_IDENTITY_TIMEOUT_MS. */
109
+ readonly timeoutMs?: number;
110
+ }
111
+ /** Minimal handle over the identity worker child (real fork or test double). */
112
+ export interface IdentityWorkerHandle {
113
+ onResolved(cb: (identity: ResolvedIdentityMessage) => void): void;
114
+ onError(cb: (cause: IdentityFailureCode, message: string) => void): void;
115
+ /** Fires when the child exits WITHOUT having produced a result. */
116
+ onExitWithoutResult(cb: () => void): void;
117
+ cancel(reason: string): void;
118
+ }
119
+ /**
120
+ * Real worker fork. Non-detached, argv-only, stdout IGNORED (it must never write
121
+ * protocol traffic), stderr piped for diagnostics, IPC typed. cwd/provider env
122
+ * are captured explicitly so the child does not inherit an artificial
123
+ * conversation. No shell, and NO PEM ever crosses IPC.
124
+ */
125
+ export declare function forkIdentityWorker(request: McpIdentityRequest, budgetMs: number, log?: (line: string) => void): IdentityWorkerHandle;
126
+ /**
127
+ * Build the identity controller for one MCP attachment. It starts inert and
128
+ * transitions `identity_pending → identity_ready | identity_failed` exactly once.
129
+ */
130
+ export declare function createIdentityController(options: CreateIdentityControllerOptions): McpIdentityController;
131
+ //# sourceMappingURL=identity-state.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identity-state.d.ts","sourceRoot":"","sources":["../../../src/runtime/mcp/identity-state.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,288 @@
1
+ /**
2
+ * L2 — identity-independent MCP connection + asynchronous identity readiness.
3
+ *
4
+ * The MCP transport (initialize / tools/list) must answer IMMEDIATELY, without
5
+ * waiting on the shared-identity critical section (the keypair read, the
6
+ * registry parse, and above all the identity/registry lock wait). Those run in a
7
+ * dedicated, non-detached CHILD process (`../identity/worker.js`) so they never
8
+ * block the parent's event loop — a `Promise`/`setImmediate` around the sync
9
+ * lock in the PARENT would not help, because the lock wait (and the ~17 MB
10
+ * registry parse) are synchronous and would still stall `initialize`.
11
+ *
12
+ * This module owns the readiness STATE MACHINE and the single 20 s deadline
13
+ * measured from entering `identity_pending`. Activation (opening the real
14
+ * presence session, reading the private key, publishing the correlated readiness
15
+ * ACK, arming the live signer) is delegated to the caller through `activate`,
16
+ * because that side lives with the SessionRegistry in the stdio transport. The
17
+ * controller only transitions to `identity_ready` once activation reports
18
+ * success — never on the child's raw result, never with a provisional key, never
19
+ * with an early availability ACK.
20
+ */
21
+ import { fork } from "node:child_process";
22
+ import { identityKeyPaths } from "../identity/live.js";
23
+ import { getActiveMcpTrace } from "./phase-trace.js";
24
+ /** The single identity deadline (ms), measured from entering identity_pending. */
25
+ export const MCP_IDENTITY_TIMEOUT_MS = 20_000;
26
+ function randomAttemptId() {
27
+ // A short, non-secret correlation id for the attempt (never a nonce/key).
28
+ return `att:${Math.random().toString(16).slice(2, 10)}${Date.now().toString(16)}`;
29
+ }
30
+ /**
31
+ * Real worker fork. Non-detached, argv-only, stdout IGNORED (it must never write
32
+ * protocol traffic), stderr piped for diagnostics, IPC typed. cwd/provider env
33
+ * are captured explicitly so the child does not inherit an artificial
34
+ * conversation. No shell, and NO PEM ever crosses IPC.
35
+ */
36
+ export function forkIdentityWorker(request, budgetMs, log) {
37
+ const capturedEnv = { ...process.env, ...(request.providerEnv ?? {}) };
38
+ let child;
39
+ let resolvedCb;
40
+ let errorCb;
41
+ let exitCb;
42
+ let sawResult = false;
43
+ let started = false;
44
+ const ensureStarted = () => {
45
+ if (started)
46
+ return;
47
+ started = true;
48
+ try {
49
+ child = fork(new URL("../identity/worker.js", import.meta.url), [], {
50
+ cwd: request.cwd,
51
+ env: capturedEnv,
52
+ stdio: ["ignore", "ignore", "pipe", "ipc"]
53
+ });
54
+ }
55
+ catch (err) {
56
+ queueMicrotask(() => errorCb?.("identity_worker_failed", err instanceof Error ? err.message : String(err)));
57
+ return;
58
+ }
59
+ // Forward the worker's stderr LINE BY LINE so a `h2a.mcp.phase …` trace span
60
+ // (L0, role=identity-child) is never split across a chunk boundary when it is
61
+ // re-emitted on the parent's stderr. Diagnostics flow through unchanged.
62
+ let stderrBuf = "";
63
+ child.stderr?.on("data", (chunk) => {
64
+ stderrBuf += String(chunk);
65
+ let nl;
66
+ while ((nl = stderrBuf.indexOf("\n")) !== -1) {
67
+ const line = stderrBuf.slice(0, nl);
68
+ stderrBuf = stderrBuf.slice(nl + 1);
69
+ if (line.length > 0)
70
+ log?.(`identity-worker: ${line}`);
71
+ }
72
+ });
73
+ child.stderr?.on("end", () => {
74
+ if (stderrBuf.trimEnd().length > 0)
75
+ log?.(`identity-worker: ${stderrBuf.trimEnd()}`);
76
+ stderrBuf = "";
77
+ });
78
+ child.on("message", (msg) => {
79
+ const m = msg;
80
+ if (!m || typeof m !== "object")
81
+ return;
82
+ if (m.kind === "identity_resolved") {
83
+ sawResult = true;
84
+ resolvedCb?.(m.identity);
85
+ }
86
+ else if (m.kind === "identity_error") {
87
+ sawResult = true;
88
+ const e = m;
89
+ errorCb?.(e.cause ?? "identity_worker_failed", e.message ?? "identity worker error");
90
+ }
91
+ // A `cancel_ack` needs no parent action beyond letting the child exit.
92
+ });
93
+ child.on("error", (err) => {
94
+ if (sawResult)
95
+ return;
96
+ errorCb?.("identity_worker_failed", err instanceof Error ? err.message : String(err));
97
+ });
98
+ child.on("exit", () => {
99
+ if (!sawResult)
100
+ exitCb?.();
101
+ });
102
+ try {
103
+ // Correlate the worker's L0 spans to THIS attempt's trace (same attemptId,
104
+ // role=identity-child) so the relocated identity/lock/registry spans stay
105
+ // joinable with the parent's server-role boot spans.
106
+ const traceAttemptId = getActiveMcpTrace()?.attemptId;
107
+ child.send({
108
+ kind: "resolve_identity",
109
+ v: 1,
110
+ request,
111
+ budgetMs,
112
+ ...(traceAttemptId !== undefined ? { traceAttemptId } : {})
113
+ });
114
+ }
115
+ catch (err) {
116
+ errorCb?.("identity_worker_failed", err instanceof Error ? err.message : String(err));
117
+ }
118
+ };
119
+ return {
120
+ onResolved(cb) {
121
+ resolvedCb = cb;
122
+ ensureStarted();
123
+ },
124
+ onError(cb) {
125
+ errorCb = cb;
126
+ },
127
+ onExitWithoutResult(cb) {
128
+ exitCb = cb;
129
+ },
130
+ cancel(reason) {
131
+ try {
132
+ child?.send({ kind: "cancel", reason });
133
+ }
134
+ catch {
135
+ /* channel already gone; the child's own finally releases its lock */
136
+ }
137
+ }
138
+ };
139
+ }
140
+ /**
141
+ * Build the identity controller for one MCP attachment. It starts inert and
142
+ * transitions `identity_pending → identity_ready | identity_failed` exactly once.
143
+ */
144
+ export function createIdentityController(options) {
145
+ const timeoutMs = options.timeoutMs ?? MCP_IDENTITY_TIMEOUT_MS;
146
+ const nowNs = options.nowNs ?? process.hrtime.bigint;
147
+ const spawnWorker = options.spawnWorker ??
148
+ ((req, budget) => forkIdentityWorker(req, budget, options.log));
149
+ let state = { state: "identity_disabled" };
150
+ let attemptId = "";
151
+ let startedNs = 0n;
152
+ let deadlineTimer;
153
+ let worker;
154
+ let liveSigner;
155
+ let terminal = false;
156
+ let cancelled = false;
157
+ const elapsedMs = () => Number((nowNs() - startedNs) / 1000000n);
158
+ const clearTimer = () => {
159
+ if (deadlineTimer) {
160
+ clearTimeout(deadlineTimer);
161
+ deadlineTimer = undefined;
162
+ }
163
+ };
164
+ const fail = (cause, message) => {
165
+ if (terminal)
166
+ return;
167
+ terminal = true;
168
+ clearTimer();
169
+ state = {
170
+ state: "identity_failed",
171
+ attemptId,
172
+ cause,
173
+ message,
174
+ retryable: false,
175
+ elapsedMs: elapsedMs()
176
+ };
177
+ liveSigner = undefined;
178
+ // L0 trace reuse: the identity lifecycle on the PARENT (the deep provider /
179
+ // registry / lock spans now run in the child, off this event loop).
180
+ getActiveMcpTrace()?.phase("identity_failed", { code: cause });
181
+ options.log?.(`identity failed (${cause}): ${message}`);
182
+ try {
183
+ worker?.cancel("failed");
184
+ }
185
+ catch {
186
+ /* best effort */
187
+ }
188
+ };
189
+ const onResolved = (identity) => {
190
+ if (terminal || cancelled)
191
+ return;
192
+ // Deadline re-check at the activation boundary: a late worker result never
193
+ // reactivates the connection.
194
+ if (elapsedMs() >= timeoutMs) {
195
+ fail("identity_timeout", `identity did not become ready within ${timeoutMs}ms`);
196
+ return;
197
+ }
198
+ // Validate the returned key paths against the canonical layout for this
199
+ // instance — NEVER read an arbitrary path the child claimed. An explicit
200
+ // `--instance` override resolves with NO key paths (presence-only, no
201
+ // signer); that is allowed and only skips this check.
202
+ if (identity.privateKeyPath) {
203
+ const expected = identityKeyPaths(options.request.root, identity.instance);
204
+ if (identity.privateKeyPath !== expected.privateKeyPath ||
205
+ identity.publicKeyPath !== expected.publicKeyPath) {
206
+ fail("identity_proof_failed", "resolved identity key paths do not match the canonical layout");
207
+ return;
208
+ }
209
+ }
210
+ let result;
211
+ try {
212
+ result = options.activate(identity);
213
+ }
214
+ catch (err) {
215
+ fail("session_open_failed", err instanceof Error ? err.message : String(err));
216
+ return;
217
+ }
218
+ if (!result.ok) {
219
+ fail(result.cause, result.message);
220
+ return;
221
+ }
222
+ // F1: a FULLY-SUCCESSFUL activation is terminal-ready. We do NOT re-check the
223
+ // deadline here: activation already opened the presence session, armed the
224
+ // signer/wake and published the correlated readiness ACK — failing now would
225
+ // leave a live ACK + presence + wake behind a `failed` state (a wedged
226
+ // connection). The deadline is enforced BEFORE activation (above) and by the
227
+ // parent timer; if activation itself fails, `activate` rolls its own partial
228
+ // work back before returning `{ok:false}`.
229
+ terminal = true;
230
+ clearTimer();
231
+ liveSigner = result.signer;
232
+ state = {
233
+ state: "identity_ready",
234
+ attemptId,
235
+ instance: identity.instance,
236
+ sessionId: result.sessionId,
237
+ signingAvailable: liveSigner !== undefined
238
+ };
239
+ getActiveMcpTrace()?.phase("identity_ready", {
240
+ code: liveSigner ? "signing" : "presence_only"
241
+ });
242
+ options.log?.(`identity ready: ${identity.instance} (signing ${liveSigner ? "available" : "unavailable"})`);
243
+ };
244
+ return {
245
+ status() {
246
+ if (state.state === "identity_pending") {
247
+ return { state: "identity_pending", attemptId, elapsedMs: elapsedMs(), timeoutMs: 20000 };
248
+ }
249
+ return state;
250
+ },
251
+ start() {
252
+ if (state.state !== "identity_disabled")
253
+ return;
254
+ attemptId = randomAttemptId();
255
+ startedNs = nowNs();
256
+ state = { state: "identity_pending", attemptId, elapsedMs: 0, timeoutMs: 20000 };
257
+ // L0 trace reuse: mark that identity entered the async pending window on the
258
+ // parent (initialize/tools-list are already answerable — identity is NOT on
259
+ // the boot critical path anymore, which is the #249 fix).
260
+ getActiveMcpTrace()?.phase("identity_pending");
261
+ deadlineTimer = setTimeout(() => {
262
+ fail("identity_timeout", `identity did not become ready within ${timeoutMs}ms`);
263
+ }, timeoutMs);
264
+ deadlineTimer.unref?.();
265
+ worker = spawnWorker(options.request, timeoutMs);
266
+ worker.onError((cause, message) => fail(cause, message));
267
+ worker.onExitWithoutResult(() => fail("identity_worker_failed", "identity worker exited without a result"));
268
+ worker.onResolved(onResolved);
269
+ },
270
+ cancel(reason) {
271
+ cancelled = true;
272
+ clearTimer();
273
+ try {
274
+ worker?.cancel(reason);
275
+ }
276
+ catch {
277
+ /* best effort */
278
+ }
279
+ // Transport is gone: no activation may follow. Keep the last observable
280
+ // state for any in-flight status read, but disarm the signer.
281
+ liveSigner = undefined;
282
+ },
283
+ signer() {
284
+ return liveSigner;
285
+ }
286
+ };
287
+ }
288
+ //# sourceMappingURL=identity-state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identity-state.js","sourceRoot":"","sources":["../../../src/runtime/mcp/identity-state.ts"],"names":[],"mappings":"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"}
@@ -5,4 +5,6 @@ export { H2A_MCP_READY_FILE_ENV, H2A_MCP_READY_KIND, H2A_MCP_READY_NONCE_ENV, ru
5
5
  export { buildH2aRunInvocation, executeH2aRun, executeH2aRunWithSpawn, handleH2aRun, recordMcpRunDelegation, H2A_RUN_API_VERSION, validateH2aRunRequest, type H2aRunExecutor, type H2aRunInvocation, type H2aRunRequest } from "./agent-launch.js";
6
6
  export { SessionRegistry, type OpenSessionRequest, type SessionRegistryOptions } from "./sessions.js";
7
7
  export { NotificationDispatcher, type McpPushNotification, type NotificationSink } from "./notifications.js";
8
+ export { createMcpTrace, getActiveMcpTrace, setActiveMcpTrace, TRACE_LINE_PREFIX, TRACE_DETAIL_ENV, type McpTrace, type McpTraceEvent, type McpTraceFields, type McpTraceOptions, type McpTraceRole, type McpTraceEventKind } from "./phase-trace.js";
9
+ export { createIdentityController, forkIdentityWorker, MCP_IDENTITY_TIMEOUT_MS, type ActivationResult, type CreateIdentityControllerOptions, type IdentityFailureCode, type IdentityWorkerHandle, type McpIdentityController, type McpIdentityRequest, type McpIdentityStatus, type ResolvedIdentityMessage } from "./identity-state.js";
8
10
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/runtime/mcp/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EACf,oBAAoB,EACpB,KAAK,sBAAsB,EAC3B,KAAK,kBAAkB,EACvB,KAAK,SAAS,EACf,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,4BAA4B,EAC5B,KAAK,iBAAiB,EACtB,KAAK,WAAW,EACjB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,KAAK,cAAc,EACnB,KAAK,aAAa,EACnB,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,sBAAsB,EACtB,kBAAkB,EAClB,uBAAuB,EACvB,WAAW,EACX,KAAK,kBAAkB,EACxB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,qBAAqB,EACrB,aAAa,EACb,sBAAsB,EACtB,YAAY,EACZ,sBAAsB,EACtB,mBAAmB,EACnB,qBAAqB,EACrB,KAAK,cAAc,EACnB,KAAK,gBAAgB,EACrB,KAAK,aAAa,EACnB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACL,eAAe,EACf,KAAK,kBAAkB,EACvB,KAAK,sBAAsB,EAC5B,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,sBAAsB,EACtB,KAAK,mBAAmB,EACxB,KAAK,gBAAgB,EACtB,MAAM,oBAAoB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/runtime/mcp/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EACf,oBAAoB,EACpB,KAAK,sBAAsB,EAC3B,KAAK,kBAAkB,EACvB,KAAK,SAAS,EACf,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,4BAA4B,EAC5B,KAAK,iBAAiB,EACtB,KAAK,WAAW,EACjB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,KAAK,cAAc,EACnB,KAAK,aAAa,EACnB,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,sBAAsB,EACtB,kBAAkB,EAClB,uBAAuB,EACvB,WAAW,EACX,KAAK,kBAAkB,EACxB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,qBAAqB,EACrB,aAAa,EACb,sBAAsB,EACtB,YAAY,EACZ,sBAAsB,EACtB,mBAAmB,EACnB,qBAAqB,EACrB,KAAK,cAAc,EACnB,KAAK,gBAAgB,EACrB,KAAK,aAAa,EACnB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACL,eAAe,EACf,KAAK,kBAAkB,EACvB,KAAK,sBAAsB,EAC5B,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,sBAAsB,EACtB,KAAK,mBAAmB,EACxB,KAAK,gBAAgB,EACtB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,cAAc,EACd,iBAAiB,EACjB,iBAAiB,EACjB,iBAAiB,EACjB,gBAAgB,EAChB,KAAK,QAAQ,EACb,KAAK,aAAa,EAClB,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,YAAY,EACjB,KAAK,iBAAiB,EACvB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACL,wBAAwB,EACxB,kBAAkB,EAClB,uBAAuB,EACvB,KAAK,gBAAgB,EACrB,KAAK,+BAA+B,EACpC,KAAK,mBAAmB,EACxB,KAAK,oBAAoB,EACzB,KAAK,qBAAqB,EAC1B,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,EACtB,KAAK,uBAAuB,EAC7B,MAAM,qBAAqB,CAAC"}
@@ -5,4 +5,6 @@ export { H2A_MCP_READY_FILE_ENV, H2A_MCP_READY_KIND, H2A_MCP_READY_NONCE_ENV, ru
5
5
  export { buildH2aRunInvocation, executeH2aRun, executeH2aRunWithSpawn, handleH2aRun, recordMcpRunDelegation, H2A_RUN_API_VERSION, validateH2aRunRequest } from "./agent-launch.js";
6
6
  export { SessionRegistry } from "./sessions.js";
7
7
  export { NotificationDispatcher } from "./notifications.js";
8
+ export { createMcpTrace, getActiveMcpTrace, setActiveMcpTrace, TRACE_LINE_PREFIX, TRACE_DETAIL_ENV } from "./phase-trace.js";
9
+ export { createIdentityController, forkIdentityWorker, MCP_IDENTITY_TIMEOUT_MS } from "./identity-state.js";
8
10
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/runtime/mcp/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EACf,oBAAoB,EAIrB,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,4BAA4B,EAG7B,MAAM,YAAY,CAAC;AAEpB,OAAO,EAGN,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,sBAAsB,EACtB,kBAAkB,EAClB,uBAAuB,EACvB,WAAW,EAEZ,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,qBAAqB,EACrB,aAAa,EACb,sBAAsB,EACtB,YAAY,EACZ,sBAAsB,EACtB,mBAAmB,EACnB,qBAAqB,EAItB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACL,eAAe,EAGhB,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,sBAAsB,EAGvB,MAAM,oBAAoB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/runtime/mcp/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EACf,oBAAoB,EAIrB,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,4BAA4B,EAG7B,MAAM,YAAY,CAAC;AAEpB,OAAO,EAGN,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,sBAAsB,EACtB,kBAAkB,EAClB,uBAAuB,EACvB,WAAW,EAEZ,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,qBAAqB,EACrB,aAAa,EACb,sBAAsB,EACtB,YAAY,EACZ,sBAAsB,EACtB,mBAAmB,EACnB,qBAAqB,EAItB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACL,eAAe,EAGhB,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,sBAAsB,EAGvB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,cAAc,EACd,iBAAiB,EACjB,iBAAiB,EACjB,iBAAiB,EACjB,gBAAgB,EAOjB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACL,wBAAwB,EACxB,kBAAkB,EAClB,uBAAuB,EASxB,MAAM,qBAAqB,CAAC"}
@@ -11,7 +11,18 @@ export interface McpPushNotification {
11
11
  data: Record<string, unknown>;
12
12
  };
13
13
  }
14
- export type NotificationSink = (notification: McpPushNotification) => void;
14
+ /**
15
+ * L1: a sink may return a receipt so the dispatcher advances a session's
16
+ * snapshot ONLY after the notification left bounded (or its intact bytes were
17
+ * persisted). `void`/`undefined` is treated as accepted, keeping existing mock
18
+ * sinks working. When a push is not accepted (persistence impossible), the
19
+ * session's snapshot is NOT advanced, so the diff is retried next tick — no ACK
20
+ * of a purely-ephemeral event is fabricated.
21
+ */
22
+ export type NotificationReceipt = {
23
+ readonly accepted: boolean;
24
+ } | void;
25
+ export type NotificationSink = (notification: McpPushNotification) => NotificationReceipt;
15
26
  /**
16
27
  * Owns the per-session diff snapshots and the periodic scan that turns
17
28
  * filesystem changes into pushed JSON-RPC notifications. One dispatcher per
@@ -42,6 +53,7 @@ export declare class NotificationDispatcher {
42
53
  * tests can drive the dispatch deterministically without a timer.
43
54
  */
44
55
  tick(): void;
56
+ /** Emit one notification; returns whether the sink accepted it (see NotificationReceipt). */
45
57
  private push;
46
58
  }
47
59
  //# sourceMappingURL=notifications.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"notifications.d.ts","sourceRoot":"","sources":["../../../src/runtime/mcp/notifications.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,2BAA2B,EACjC,MAAM,gBAAgB,CAAC;AAGxB,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAErD,kEAAkE;AAClE,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,KAAK,CAAC;IACf,MAAM,EAAE,mBAAmB,CAAC;IAC5B,MAAM,EAAE;QACN,KAAK,EAAE,2BAA2B,CAAC;QACnC,SAAS,EAAE,MAAM,CAAC;QAClB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KAC/B,CAAC;CACH;AAED,MAAM,MAAM,gBAAgB,GAAG,CAAC,YAAY,EAAE,mBAAmB,KAAK,IAAI,CAAC;AAsD3E;;;;;GAKG;AACH,qBAAa,sBAAsB;IAK/B,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,IAAI;IACZ,OAAO,CAAC,QAAQ,CAAC,UAAU;IAR7B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAuC;IACjE,OAAO,CAAC,KAAK,CAAC,CAAiC;gBAG5B,QAAQ,EAAE,eAAe,EACzB,KAAK,EAAE,UAAU,EACjB,IAAI,EAAE,MAAM,EACrB,IAAI,EAAE,gBAAgB,GAAG,SAAS,EACzB,UAAU,EAAE,MAAM;IAGrC,OAAO,CAAC,IAAI,EAAE,gBAAgB,GAAG,SAAS,GAAG,IAAI;IAIjD;;;;OAIG;IACH,OAAO,CAAC,cAAc,CAAC,CAA6B;IACpD,iBAAiB,CAAC,EAAE,EAAE,CAAC,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,SAAS,GAAG,IAAI;IAIrE,KAAK,IAAI,IAAI;IAMb,IAAI,IAAI,IAAI;IAMZ;;;OAGG;IACH,IAAI,IAAI,IAAI;IA6HZ,OAAO,CAAC,IAAI;CAab"}
1
+ {"version":3,"file":"notifications.d.ts","sourceRoot":"","sources":["../../../src/runtime/mcp/notifications.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,2BAA2B,EACjC,MAAM,gBAAgB,CAAC;AAGxB,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAErD,kEAAkE;AAClE,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,KAAK,CAAC;IACf,MAAM,EAAE,mBAAmB,CAAC;IAC5B,MAAM,EAAE;QACN,KAAK,EAAE,2BAA2B,CAAC;QACnC,SAAS,EAAE,MAAM,CAAC;QAClB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KAC/B,CAAC;CACH;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,mBAAmB,GAAG;IAAE,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAA;CAAE,GAAG,IAAI,CAAC;AAExE,MAAM,MAAM,gBAAgB,GAAG,CAAC,YAAY,EAAE,mBAAmB,KAAK,mBAAmB,CAAC;AAsD1F;;;;;GAKG;AACH,qBAAa,sBAAsB;IAK/B,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,IAAI;IACZ,OAAO,CAAC,QAAQ,CAAC,UAAU;IAR7B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAuC;IACjE,OAAO,CAAC,KAAK,CAAC,CAAiC;gBAG5B,QAAQ,EAAE,eAAe,EACzB,KAAK,EAAE,UAAU,EACjB,IAAI,EAAE,MAAM,EACrB,IAAI,EAAE,gBAAgB,GAAG,SAAS,EACzB,UAAU,EAAE,MAAM;IAGrC,OAAO,CAAC,IAAI,EAAE,gBAAgB,GAAG,SAAS,GAAG,IAAI;IAIjD;;;;OAIG;IACH,OAAO,CAAC,cAAc,CAAC,CAA6B;IACpD,iBAAiB,CAAC,EAAE,EAAE,CAAC,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,SAAS,GAAG,IAAI;IAIrE,KAAK,IAAI,IAAI;IAMb,IAAI,IAAI,IAAI;IAMZ;;;OAGG;IACH,IAAI,IAAI,IAAI;IAqIZ,6FAA6F;IAC7F,OAAO,CAAC,IAAI;CAsBb"}
@@ -92,6 +92,11 @@ export class NotificationDispatcher {
92
92
  const activeBlockages = listBlockages(this.root).filter(isActiveBlockage);
93
93
  for (const session of ownSessions) {
94
94
  const prev = this.snapshots.get(session.sessionId) ?? emptyDiffState();
95
+ // L1: a session's snapshot advances only if EVERY emitted diff was accepted
96
+ // (bounded/persisted). One unaccepted push holds the whole session snapshot
97
+ // so the diffs are retried next tick — an at-least-once guarantee that never
98
+ // fabricates an ACK for a dropped ephemeral notification.
99
+ let accepted = true;
95
100
  // 1. Presence join/leave (excluding self)
96
101
  const others = new Set([...peerIds].filter((sid) => sid !== session.sessionId));
97
102
  if (isInterestedIn(session, "presence.peer_joined")) {
@@ -99,14 +104,14 @@ export class NotificationDispatcher {
99
104
  if (!prev.peers.has(sid)) {
100
105
  const peer = freshPeers.find((p) => p.sessionId === sid);
101
106
  if (peer) {
102
- this.push(session.sessionId, "presence.peer_joined", {
107
+ accepted = this.push(session.sessionId, "presence.peer_joined", {
103
108
  peer: {
104
109
  sessionId: peer.sessionId,
105
110
  instance: peer.instance,
106
111
  host: peer.host,
107
112
  interests: peer.interests
108
113
  }
109
- });
114
+ }) && accepted;
110
115
  }
111
116
  }
112
117
  }
@@ -114,9 +119,9 @@ export class NotificationDispatcher {
114
119
  if (isInterestedIn(session, "presence.peer_left")) {
115
120
  for (const sid of prev.peers) {
116
121
  if (!others.has(sid)) {
117
- this.push(session.sessionId, "presence.peer_left", {
122
+ accepted = this.push(session.sessionId, "presence.peer_left", {
118
123
  peer: { sessionId: sid }
119
- });
124
+ }) && accepted;
120
125
  }
121
126
  }
122
127
  }
@@ -127,10 +132,10 @@ export class NotificationDispatcher {
127
132
  if (isInterestedIn(session, "inbox.envelope_arrived")) {
128
133
  for (const envelopeId of inboxIds) {
129
134
  if (!prev.inbox.envelopeIds.has(envelopeId)) {
130
- this.push(session.sessionId, "inbox.envelope_arrived", {
135
+ accepted = this.push(session.sessionId, "inbox.envelope_arrived", {
131
136
  instance: session.instance,
132
137
  envelopeId
133
- });
138
+ }) && accepted;
134
139
  }
135
140
  }
136
141
  // EVO-1 wake (bug #3): nudge the host every tick while the inbox is
@@ -148,11 +153,11 @@ export class NotificationDispatcher {
148
153
  const count = readJournalLength(this.store, negotiationId);
149
154
  const prevCount = prev.negotiations.get(negotiationId)?.count ?? 0;
150
155
  if (count > prevCount) {
151
- this.push(session.sessionId, "negotiation.event_appended", {
156
+ accepted = this.push(session.sessionId, "negotiation.event_appended", {
152
157
  negotiationId,
153
158
  newEntries: count - prevCount,
154
159
  totalEntries: count
155
- });
160
+ }) && accepted;
156
161
  }
157
162
  negotiationSnap.set(negotiationId, { count });
158
163
  }
@@ -165,40 +170,51 @@ export class NotificationDispatcher {
165
170
  if (isInterestedIn(session, "peer.blocked")) {
166
171
  for (const b of inScope) {
167
172
  if (!prev.blocked.has(b.instance)) {
168
- this.push(session.sessionId, "peer.blocked", {
173
+ accepted = this.push(session.sessionId, "peer.blocked", {
169
174
  instance: b.instance,
170
175
  scope: b.scope,
171
176
  reason: b.reason,
172
177
  ...(b.needs ? { needs: b.needs } : {})
173
- });
178
+ }) && accepted;
174
179
  }
175
180
  }
176
181
  }
177
182
  if (isInterestedIn(session, "peer.unblocked")) {
178
183
  for (const instance of prev.blocked) {
179
184
  if (!blockedNow.has(instance)) {
180
- this.push(session.sessionId, "peer.unblocked", { instance });
185
+ accepted = this.push(session.sessionId, "peer.unblocked", { instance }) && accepted;
181
186
  }
182
187
  }
183
188
  }
184
- this.snapshots.set(session.sessionId, {
185
- peers: others,
186
- inbox: { envelopeIds: new Set(inboxIds) },
187
- negotiations: negotiationSnap,
188
- blocked: blockedNow
189
- });
189
+ if (accepted) {
190
+ this.snapshots.set(session.sessionId, {
191
+ peers: others,
192
+ inbox: { envelopeIds: new Set(inboxIds) },
193
+ negotiations: negotiationSnap,
194
+ blocked: blockedNow
195
+ });
196
+ }
197
+ // else: keep the previous snapshot so the unaccepted diffs are retried.
190
198
  }
191
199
  }
200
+ /** Emit one notification; returns whether the sink accepted it (see NotificationReceipt). */
192
201
  push(sessionId, topic, data) {
193
202
  if (!this.sink)
194
- return;
203
+ return true;
195
204
  if (!H2A_SESSION_NOTIFICATION_TOPICS.includes(topic))
196
- return;
197
- this.sink({
205
+ return true;
206
+ const receipt = this.sink({
198
207
  jsonrpc: "2.0",
199
208
  method: "notifications/h2a",
200
209
  params: { topic, sessionId, data }
201
210
  });
211
+ // Accepted unless the sink explicitly returns `{ accepted: false }`. A sink
212
+ // that returns void, or any other value (e.g. a legacy `array.push` count),
213
+ // is treated as accepted so existing sinks keep advancing the snapshot.
214
+ return !(receipt !== null &&
215
+ typeof receipt === "object" &&
216
+ "accepted" in receipt &&
217
+ receipt.accepted === false);
202
218
  }
203
219
  }
204
220
  //# sourceMappingURL=notifications.js.map