@adhisang/minecraft-modding-mcp 6.2.0 → 7.0.0-rc.0

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 (98) hide show
  1. package/CHANGELOG.md +71 -0
  2. package/README.md +21 -5
  3. package/dist/cache-policy.d.ts +71 -0
  4. package/dist/cache-policy.js +83 -0
  5. package/dist/cache-registry.js +6 -6
  6. package/dist/cli.js +74 -3
  7. package/dist/compat-stdio-transport.d.ts +1 -1
  8. package/dist/compat-stdio-transport.js +13 -1
  9. package/dist/config.d.ts +3 -0
  10. package/dist/config.js +8 -2
  11. package/dist/decompiler/vineflower.d.ts +1 -0
  12. package/dist/decompiler/vineflower.js +8 -5
  13. package/dist/entry-tools/analyze-mod-service.d.ts +70 -136
  14. package/dist/entry-tools/analyze-symbol-service.d.ts +112 -150
  15. package/dist/entry-tools/compare-minecraft-service.d.ts +59 -145
  16. package/dist/entry-tools/entry-tool-schema.d.ts +38 -4
  17. package/dist/entry-tools/entry-tool-schema.js +4 -1
  18. package/dist/entry-tools/inspect-minecraft/internal.d.ts +235 -799
  19. package/dist/entry-tools/inspect-minecraft/internal.js +65 -28
  20. package/dist/entry-tools/inspect-minecraft-service.d.ts +372 -1736
  21. package/dist/entry-tools/inspect-minecraft-service.js +1 -1
  22. package/dist/entry-tools/manage-cache-service.d.ts +81 -91
  23. package/dist/entry-tools/validate-project/cases/project-summary.d.ts +7 -7
  24. package/dist/entry-tools/validate-project-service.d.ts +164 -592
  25. package/dist/entry-tools/verify-mixin-target-service.d.ts +3 -19
  26. package/dist/era-classifier.d.ts +161 -0
  27. package/dist/era-classifier.js +292 -0
  28. package/dist/error-mapping.js +9 -2
  29. package/dist/index.d.ts +42 -4
  30. package/dist/index.js +637 -475
  31. package/dist/java-process.d.ts +2 -0
  32. package/dist/java-process.js +22 -2
  33. package/dist/json-rpc-framing.d.ts +77 -1
  34. package/dist/json-rpc-framing.js +249 -13
  35. package/dist/mapping/loaders/tiny-loom-selection.d.ts +88 -0
  36. package/dist/mapping/loaders/tiny-loom-selection.js +223 -0
  37. package/dist/mapping/loaders/tiny-loom.js +45 -33
  38. package/dist/mapping/loaders/tiny-maven.js +6 -11
  39. package/dist/mapping/parsers/tiny.d.ts +57 -0
  40. package/dist/mapping/parsers/tiny.js +99 -22
  41. package/dist/mapping-service.d.ts +19 -0
  42. package/dist/mapping-service.js +93 -9
  43. package/dist/mcp-helpers.d.ts +19 -2
  44. package/dist/mcp-helpers.js +48 -6
  45. package/dist/minecraft-explorer-service.d.ts +1 -1
  46. package/dist/mixin/types.d.ts +8 -0
  47. package/dist/mod-analyzer.js +7 -7
  48. package/dist/mod-decompile-service.js +1 -0
  49. package/dist/nbt/java-nbt-codec.js +12 -2
  50. package/dist/nbt/json-patch.js +14 -3
  51. package/dist/nbt/pipeline.js +40 -3
  52. package/dist/nbt/typed-json.js +26 -1
  53. package/dist/registration-adapter.d.ts +32 -0
  54. package/dist/registration-adapter.js +52 -0
  55. package/dist/request-context.d.ts +7 -0
  56. package/dist/request-context.js +9 -0
  57. package/dist/resources.d.ts +1 -1
  58. package/dist/resources.js +25 -19
  59. package/dist/server-identity.d.ts +27 -0
  60. package/dist/server-identity.js +26 -0
  61. package/dist/source/access-validate.js +53 -0
  62. package/dist/source/artifact-resolver.d.ts +69 -1
  63. package/dist/source/artifact-resolver.js +215 -14
  64. package/dist/source/class-source.d.ts +22 -0
  65. package/dist/source/class-source.js +162 -29
  66. package/dist/source/did-you-mean.d.ts +12 -1
  67. package/dist/source/did-you-mean.js +6 -2
  68. package/dist/source/file-access.js +150 -46
  69. package/dist/source/indexer.js +1 -0
  70. package/dist/source/nested-jars.d.ts +19 -0
  71. package/dist/source/nested-jars.js +90 -21
  72. package/dist/source/shared-utils.d.ts +21 -0
  73. package/dist/source/shared-utils.js +23 -0
  74. package/dist/source-service.d.ts +12 -0
  75. package/dist/source-service.js +3 -0
  76. package/dist/stdio-supervisor.d.ts +357 -2
  77. package/dist/stdio-supervisor.js +1031 -80
  78. package/dist/storage/db.d.ts +2 -1
  79. package/dist/storage/db.js +15 -8
  80. package/dist/synthetic-decorator.d.ts +24 -0
  81. package/dist/synthetic-decorator.js +48 -0
  82. package/dist/tool-contract-manifest.js +1 -1
  83. package/dist/tool-guidance.d.ts +17 -1
  84. package/dist/tool-guidance.js +417 -13
  85. package/dist/tool-schema-registry.d.ts +2 -0
  86. package/dist/tool-schema-registry.js +4 -0
  87. package/dist/tool-schemas.d.ts +2214 -3915
  88. package/dist/tool-schemas.js +34 -7
  89. package/dist/types.d.ts +35 -0
  90. package/dist/v1-parity-schemas.d.ts +7 -0
  91. package/dist/v1-parity-schemas.js +5584 -0
  92. package/dist/version-diff-service.d.ts +33 -0
  93. package/dist/version-diff-service.js +148 -3
  94. package/dist/version-service.js +36 -14
  95. package/dist/warning-details.js +18 -1
  96. package/docs/README-ja.md +9 -3
  97. package/docs/tool-reference.md +267 -22
  98. package/package.json +12 -9
@@ -1,8 +1,26 @@
1
1
  import { type ChildProcessWithoutNullStreams } from "node:child_process";
2
- import type { JSONRPCMessage, JSONRPCResponse } from "@modelcontextprotocol/sdk/types.js";
2
+ import type { JSONRPCMessage, JSONRPCResponse } from "@modelcontextprotocol/server";
3
+ import { type ConcreteFramingMode } from "./json-rpc-framing.js";
4
+ import { type Era } from "./era-classifier.js";
3
5
  export declare function loadValidateProjectTimeoutMs(value?: string | undefined): number;
4
6
  export declare function computeWorkerStartupWatchdogMs(validateProjectTimeoutMs: number): number;
5
7
  export declare function computeRestartBackoffMs(retryIndex: number): number;
8
+ /**
9
+ * How long an adopted worker must stay ready before a CLEAN voluntary
10
+ * stand-down counts as the end of a generation that actually served.
11
+ *
12
+ * A worker stands down on stdin EOF. On a host that hands every spawned child
13
+ * a stdin which closes immediately, that EOF arrives while the worker is still
14
+ * starting: it writes its ready marker, is adopted (which clears the restart
15
+ * backoff), and exits 0 in the same breath. Answering that with the 100 ms
16
+ * restart floor respawns a ~125 MB process ten times a second forever, so a
17
+ * stand-down inside this window escalates the backoff instead.
18
+ *
19
+ * A CRASH is deliberately not covered: it may be transient and keeps the
20
+ * prompt replacement the restart suites pin, whereas a clean stand-down means
21
+ * the host closed the pipe and will close the replacement's stdin the same way.
22
+ */
23
+ export declare const MIN_HEALTHY_WORKER_READY_MS = 1000;
6
24
  export declare function terminatePosixProcessGroup(pid: number, kill?: (pid: number, signal: NodeJS.Signals) => void): boolean;
7
25
  export declare function buildWindowsTreeKillArgs(pid: number): string[];
8
26
  export declare function shouldRetainUnavailableNotification(method: string): boolean;
@@ -16,6 +34,16 @@ export declare class RestartBackoffState {
16
34
  private epoch;
17
35
  reserve(now: number): RestartReservation;
18
36
  reset(): void;
37
+ /**
38
+ * Raise the escalation level without reserving a restart.
39
+ *
40
+ * Adoption resets the index, so a generation that was adopted and then died
41
+ * without ever serving arrives here with the level already cleared. This
42
+ * restores the level that the repeated failure has earned, which is what
43
+ * stops a self-cancelling adopt/reset cycle from pinning every restart to
44
+ * the 100 ms floor.
45
+ */
46
+ escalateTo(level: number): void;
19
47
  }
20
48
  export declare function retryPosixTreeToken(pid: number, kill?: (pid: number, signal: NodeJS.Signals) => void): boolean;
21
49
  export declare function settleTreeCleanupWithin(operation: Promise<boolean>, timeoutMs: number, onTimeout?: () => void): Promise<boolean>;
@@ -25,6 +53,8 @@ export type SupervisorOptions = {
25
53
  clientWriter?: (message: JSONRPCMessage) => void;
26
54
  treeTokenRetrier?: (pid: number) => boolean | Promise<boolean>;
27
55
  treeCleanupTimeoutMs?: number;
56
+ cleanupTokenRetryBaseMs?: number;
57
+ cleanupTokenRetryCapMs?: number;
28
58
  eventWriter?: (level: "warn" | "error" | "info", event: string, details?: Record<string, unknown>) => void;
29
59
  monotonicNow?: () => number;
30
60
  timerScheduler?: (callback: () => void, delayMs: number) => NodeJS.Timeout;
@@ -58,10 +88,58 @@ export type PendingRequestSnapshot = {
58
88
  /** `true` when toolArgsRedacted contains sentinel placeholders (truncate/redact/overflow). */
59
89
  toolArgsRedactedModified?: boolean;
60
90
  startedAt: number;
91
+ /**
92
+ * Framing mode of the inbound frame that carried this request, captured at
93
+ * admission. Every terminal client-bound write for this request (forwarded
94
+ * worker response, timeout/queue-limit/restart synthesis) uses THIS mode —
95
+ * never the connection's latest inbound mode. Retained for the pending
96
+ * entry's whole lifetime (including client-cancelled tombstones) until the
97
+ * entry is discarded.
98
+ */
99
+ mode?: ConcreteFramingMode;
100
+ /**
101
+ * The supervisor's era at this request's admission ("unselected" occurs
102
+ * only for era-neutral server/discover admitted before any lock). Always
103
+ * set at runtime by createPendingRequest, the sole constructor; optional at
104
+ * the type level for tolerant synthetic-builder inputs and exported-type
105
+ * compatibility — no reply BUILDER may read it. Carve-out: the terminal
106
+ * synthetic-decoration step (writeSyntheticReply → decorateSyntheticReply)
107
+ * reads `era` — and ONLY `era` — to gate modern-era decoration; the three
108
+ * captured context fields below remain unread everywhere.
109
+ */
110
+ era?: Era;
111
+ /**
112
+ * Per-request modern protocol context, captured SHALLOW (values aliased
113
+ * as-is from params._meta — nothing may mutate an inbound params graph
114
+ * after admission, or captured snapshots would change retroactively) for
115
+ * shallow-valid modern-signal requests only; undefined for claim-less
116
+ * legacy traffic and for every initialize (initialize envelopes are
117
+ * ignored). Captured for downstream consumers; the supervisor itself must
118
+ * never read these three fields — no reply, synthesis, decoration, or
119
+ * forwarded frame may depend on them (the no-leak tests pin that).
120
+ * Synthetic decoration reads the snapshot's `era` alone, and the identity
121
+ * it stamps comes from src/server-identity.ts, never from request context.
122
+ */
123
+ protocolVersion?: string;
124
+ clientCapabilities?: Record<string, unknown>;
125
+ clientInfo?: unknown;
61
126
  lastStage?: string;
62
127
  lastStageStartedAt?: number;
63
128
  lastStageMeta?: unknown;
64
129
  };
130
+ /**
131
+ * Frozen premigration reply for a tools/call whose name is not registered
132
+ * (typo OR flag-disabled tool — indistinguishable, exactly as the v1 SDK
133
+ * answered): a SUCCESSFUL CallToolResult with isError:true and one text
134
+ * content entry embedding the v1 error string. The v2 SDK throws a raw
135
+ * -32602 instead, so the supervisor synthesizes this envelope for
136
+ * LEGACY-era registry misses (see handleClientMessage) to keep the legacy
137
+ * wire byte-identical to the premigration baseline
138
+ * (tests/fixtures/premigration/error-code-inventory.json, disabled-tool
139
+ * row). The modern era keeps the raw -32602 (sanctioned: tool absence is a
140
+ * params problem on 2026-07-28, mirroring the SDK's own not-found mapping).
141
+ */
142
+ export declare function buildUnknownToolNotFoundReply(id: RequestId, toolName: string): JSONRPCResponse;
65
143
  export declare function buildLegacyJsonRpcError(id: RequestId): JSONRPCResponse;
66
144
  export type RedactedToolArgs = {
67
145
  args: Record<string, unknown>;
@@ -106,6 +184,8 @@ export declare class StdioSupervisor {
106
184
  private readonly clientWriter;
107
185
  private readonly treeTokenRetrier;
108
186
  private readonly treeCleanupTimeoutMs;
187
+ private readonly cleanupTokenRetryBaseMs;
188
+ private readonly cleanupTokenRetryCapMs;
109
189
  private readonly eventWriter;
110
190
  private readonly monotonicNow;
111
191
  private readonly timerScheduler;
@@ -124,10 +204,59 @@ export declare class StdioSupervisor {
124
204
  private readonly unresolvedTreeTokens;
125
205
  private readonly restartBackoff;
126
206
  private readonly terminalChildren;
207
+ /**
208
+ * Monotonic worker-generation counter (incremented per successful spawn).
209
+ * Recorded into finality tombstones so retention can be bounded per
210
+ * generation; hand-injected children in tests that bypass spawnWorker run
211
+ * as generation 0.
212
+ */
213
+ private workerGeneration;
214
+ /**
215
+ * Response-finality tombstones: request key → {originating worker
216
+ * generation, originating framing mode}, recorded by writeSyntheticReply
217
+ * the moment a synthetic response is written. While a tombstone exists, any
218
+ * worker RESPONSE for that id is discarded (with a logged
219
+ * `supervisor.late_response_discarded` event) instead of being forwarded —
220
+ * the synthesized response was terminal, and exactly one response per id
221
+ * must reach the client. A tombstone is cleared when its id is re-forwarded
222
+ * to a worker (forwardRequest), because a client may legally retry with the
223
+ * same id; finality is per request INSTANCE, not per id forever.
224
+ *
225
+ * `mode` is retained deliberately even though discard writes nothing today:
226
+ * a tombstoned id keeps its originating framing mode until discard, so any
227
+ * future terminal write for that id — should the discard policy ever gain
228
+ * one — answers in the originating framing.
229
+ *
230
+ * Retention bound: entries with generation ≤ G are dropped when the
231
+ * generation-G child emits "close" (process ended AND its stdio streams
232
+ * closed — Node's guarantee for that event). This is safe: worker frames
233
+ * only enter handleWorkerMessage from the CURRENT child, the supervisor
234
+ * only forwards ids with live pending entries, and forwarding clears the
235
+ * id's tombstone — so once every generation that could have seen a
236
+ * tombstoned id is gone with streams closed, no current child can ever
237
+ * deliver a frame for it. Also cleared wholesale on shutdown.
238
+ */
239
+ private readonly syntheticTombstones;
127
240
  private child;
128
241
  private childReady;
242
+ /**
243
+ * Monotonic timestamp of the current generation's adoption, or undefined
244
+ * while no generation is adopted. Read once at exit to tell a generation
245
+ * that served from one that stood down the moment it came up.
246
+ */
247
+ private childReadyAt;
248
+ /**
249
+ * Consecutive adopted generations that stood down CLEANLY before they had
250
+ * been ready for MIN_HEALTHY_WORKER_READY_MS. Any other outcome — a crash, a
251
+ * signal, a generation that served — clears it.
252
+ */
253
+ private consecutiveImmediateStandDowns;
129
254
  private shuttingDown;
130
255
  private restartTimer;
256
+ private cleanupRetryTimer;
257
+ private cleanupRetryIndex;
258
+ private readonly cleanupRetryAttempts;
259
+ private readonly cleanupRetriesInFlight;
131
260
  private startupWatchdog;
132
261
  private validateBarrierKey;
133
262
  private runningValidateKey;
@@ -136,24 +265,173 @@ export declare class StdioSupervisor {
136
265
  private currentRetryReservation;
137
266
  private retryPaused;
138
267
  private workerStderrBuffer;
139
- private clientMode;
268
+ /**
269
+ * Framing mode of the most recently detected inbound client frame. NOT used
270
+ * for request-correlated writes (those use the originating request's
271
+ * captured mode); this is only the documented fallback for client-bound
272
+ * writes that correlate to no request id (worker-originated notifications
273
+ * and server->client requests forwarded at handleWorkerMessage, and
274
+ * untracked-id responses with no finality tombstone — tombstoned ids are
275
+ * discarded, never forwarded).
276
+ */
277
+ private lastInboundClientMode;
278
+ /**
279
+ * Per-frame framing mode of every inbound client message, keyed by message
280
+ * object identity. Lets admission (createPendingRequest), queued-message
281
+ * flushes, and initialize replay recover the ORIGINATING frame's mode long
282
+ * after the frame was parsed.
283
+ */
284
+ private readonly inboundFrameModes;
140
285
  private initializeRequest;
141
286
  private initializedNotification;
287
+ /**
288
+ * Number of queued requests that arrived before an initial initialize while
289
+ * the worker was unavailable. Only era-neutral modern discovers can occupy
290
+ * this prefix; readiness forwards it before initialize, then keeps the queue
291
+ * suffix gated until the initialization response.
292
+ */
293
+ private initializePredecessorCount;
142
294
  private clientInitialized;
143
295
  private replayingInitialization;
144
296
  private initializeSentToWorker;
297
+ /**
298
+ * Process-lifetime era state. The supervisor is the SOLE era gatekeeper:
299
+ * classification happens at admission (handleClientMessage), in stdin
300
+ * order. The first valid NON-discover era signal locks the era (initialize
301
+ * → legacy; shallow-valid modern `_meta` request → modern); the lock is
302
+ * one-way and survives worker restarts.
303
+ */
304
+ private era;
305
+ /**
306
+ * Whether `start()` attached this instance to the real process stdio. Only
307
+ * then may a teardown touch process-wide state (the framing-fatal exit
308
+ * code); white-box suites construct supervisors without starting them and
309
+ * must never have their own runner's exit status rewritten.
310
+ */
311
+ private ownsProcessStdio;
312
+ /**
313
+ * Whether a fatal fault has already armed the explicit process exit. A
314
+ * second fault must not stack another watchdog or cut the first teardown
315
+ * short.
316
+ */
317
+ private fatalExitScheduled;
318
+ /** Memoized teardown; see shutdown(). */
319
+ private shutdownRun;
145
320
  constructor(options: SupervisorOptions);
146
321
  start(): Promise<void>;
147
322
  private readonly handleClientData;
323
+ /** The captured per-frame mode of an inbound client message, if known. */
324
+ private modeForMessage;
148
325
  private readonly handleClientError;
149
326
  private readonly handleClientClosed;
150
327
  private readonly handleTerminateSignal;
328
+ /**
329
+ * Terminal fault in the supervisor process. Reported through the injected
330
+ * event writer, then routed into the ordinary shutdown so the detached
331
+ * worker group is reaped rather than orphaned by a crash.
332
+ */
333
+ private readonly handleFatalError;
151
334
  private handleClientMessage;
335
+ /**
336
+ * Era-aware notification dispatch. notifications/cancelled stays
337
+ * supervisor-side in ALL eras/states; notifications/initialized is captured
338
+ * and forwarded only within a legacy flow; every other notification is
339
+ * gated by the era rules before reaching the original forwarding path.
340
+ * Dropped variants emit NO response and never affect subsequent traffic.
341
+ */
342
+ private handleClientNotification;
343
+ /**
344
+ * Era rules for non-initialize requests, applied at admission in stdin
345
+ * order. Returns undefined when the request was terminally rejected here (a
346
+ * supervisor-produced response was written; nothing may be forwarded or
347
+ * queued). On admission it returns the admission-time era signal, which the
348
+ * caller hands to createPendingRequest so snapshot capture uses the same
349
+ * result as the gating decision (extractModernRequestContext re-checks the
350
+ * same immutable params graph defensively — a pure re-evaluation, not a
351
+ * second authority); the caller then proceeds through the original era-less
352
+ * admission path unchanged.
353
+ */
354
+ private admitEraRequest;
355
+ /**
356
+ * Deep validation of a shallow-valid modern claim's protocolVersion VALUE,
357
+ * applied to EVERY modern request at admission. Returns whether the request
358
+ * was terminally answered here.
359
+ *
360
+ * The version check used to be delegated entirely to the worker, and the SDK
361
+ * only performs it while the connection is still OPENING: once an instance
362
+ * is pinned, `processMessage` delivers straight to it without re-reading the
363
+ * envelope. So the contract ("any unsupported version string answers -32022
364
+ * with data.supported/data.requested") held for exactly one request per
365
+ * process — and whether it held at all depended on which method the client
366
+ * happened to send first, since the era-neutral `server/discover` does not
367
+ * pin. Every later request with `"1999-12-31"` was SERVED, tool handlers
368
+ * included.
369
+ *
370
+ * Ordering is deliberate and matches the SDK's own listen path: the envelope
371
+ * shape is checked first (era gating above), the era lock is committed
372
+ * BEFORE this check — an unsupported version still locks modern, exactly as
373
+ * documented — and only then is the value judged, ahead of any method-level
374
+ * rejection. The reply is built from the SDK's own
375
+ * UnsupportedProtocolVersionError, so it stays byte-identical to the answer
376
+ * the worker gives on the still-opening path.
377
+ */
378
+ private rejectUnsupportedProtocolVersion;
379
+ /**
380
+ * One-way modern lock. Purges the cached legacy lifecycle state so no
381
+ * initialize/notifications/initialized can ever reach a worker in a
382
+ * modern-locked process — across ALL later worker generations.
383
+ */
384
+ private lockModernEra;
385
+ /**
386
+ * `eraSignal` is the ADMISSION-time classification handed back by
387
+ * admitEraRequest — the initialize/replay/flush call sites omit it (an
388
+ * initialize snapshot is always era "legacy" with no modern context, even
389
+ * when the initialize carries a _meta envelope). Context is captured only
390
+ * for shallow-valid modern signals; this.era already reflects any lock
391
+ * admitEraRequest applied, so it IS the era at admission on every path.
392
+ */
152
393
  private createPendingRequest;
153
394
  private canDispatchImmediately;
154
395
  private forwardRequest;
155
396
  private writeToWorker;
156
397
  private handleCancellation;
398
+ /**
399
+ * Terminal release of a FORWARDED request the client cancelled.
400
+ *
401
+ * MCP cancellation semantics forbid a response for a cancelled id, so the
402
+ * supervisor stops waiting on the worker the moment the cancellation is
403
+ * admitted — and a worker is under no obligation to ever answer a cancelled
404
+ * request. Keeping the entry in `pendingRequests` as a suppression marker
405
+ * (the pre-repair behavior) therefore stranded EVERY dispatch barrier the
406
+ * map gates — canDispatchImmediately's empty-map requirement, drainQueue's
407
+ * validate release — for the life of the process whenever the worker never
408
+ * answered, and let a client grow the map without bound by pairing fresh
409
+ * ids with cancellations.
410
+ *
411
+ * Suppression is instead recorded as an ordinary response-finality
412
+ * tombstone, which already carries exactly the needed semantics: a late
413
+ * worker answer for the id is discarded with a logged event, and
414
+ * re-forwarding the id (a legal retry) clears it. The result is one
415
+ * mechanism for "this request instance is over" rather than two.
416
+ *
417
+ * Carve-out: an in-flight `initialize` is NOT released. Its pending entry is
418
+ * owned by the legacy handshake lifecycle (replay correlation,
419
+ * isInitializationResponse, the preserved-key rule in
420
+ * failPendingRequestsOnWorkerExit), which has its own recovery paths
421
+ * (startup watchdog, worker exit, clearInitialInitializationState) and never
422
+ * consulted the cancellation marker anyway — so handshake behavior is
423
+ * unchanged, byte for byte.
424
+ *
425
+ * Returns whether an entry was actually released.
426
+ */
427
+ private releaseCancelledRequest;
428
+ /**
429
+ * Era gating for a cancellation notification. Extracted from
430
+ * handleCancellation so supervisor-side bookkeeping (releasing the cancelled
431
+ * request) stays independent of whether the frame may reach the worker — a
432
+ * DROPPED cancellation still cancels, exactly as documented.
433
+ */
434
+ private shouldForwardCancellation;
157
435
  private handleValidateProjectDeadline;
158
436
  private drainQueue;
159
437
  private spawnWorker;
@@ -162,15 +440,81 @@ export declare class StdioSupervisor {
162
440
  private handleWorkerStderr;
163
441
  private handleWorkerProcessError;
164
442
  private handleWorkerExit;
443
+ /**
444
+ * Decide whether the generation that just ended counts as a start that
445
+ * succeeded.
446
+ *
447
+ * A worker stands down on stdin EOF, and it replays an EOF that arrived
448
+ * while it was still starting. On a host that closes a spawned child's stdin
449
+ * immediately, every generation therefore signals ready, is adopted — which
450
+ * clears the restart backoff — and exits 0 in the same breath. Left at the
451
+ * 100 ms floor that is an unbounded hot respawn loop, so a clean stand-down
452
+ * inside the healthy window re-escalates the backoff the adoption cleared.
453
+ *
454
+ * Only a CLEAN exit qualifies: a non-zero code or a signal is a crash, which
455
+ * may be transient and keeps its prompt replacement.
456
+ */
457
+ private accountForReadyGenerationExit;
165
458
  private handleWorkerMessage;
166
459
  private applyStageUpdate;
167
460
  private handleWorkerReady;
168
461
  private isInitializationResponse;
169
462
  private flushQueue;
170
463
  private failPendingRequestsOnWorkerExit;
464
+ /**
465
+ * The single terminal-synthesis writer: EVERY supervisor-synthesized
466
+ * response (queue-limit, validate-project timeout, worker-restart in all
467
+ * variants incl. the structured-restart toggle, startup-failure
468
+ * terminalization, initialize-failure -32603) routes through here.
469
+ * Responsibilities, in order:
470
+ * 1. settle the pending entry,
471
+ * 2. record the finality tombstone (id + originating worker generation +
472
+ * framing mode) so a late worker response for the id is discarded —
473
+ * ONLY when the id was actually FORWARDED to a worker (i.e. the entry
474
+ * in pendingRequests at the id IS the snapshot instance being
475
+ * answered; an id-colliding DIFFERENT live entry is left untouched).
476
+ * Never-forwarded
477
+ * rejections (queue-limit at admission and at the forward fallback,
478
+ * queue-phase validate timeout, cap-blocked replies, startup-failure
479
+ * terminalization of queued work, the retained-initialize error) record
480
+ * NO tombstone: no worker ever saw those ids, so no worker can answer
481
+ * them, and tombstoning them would let a client grow the map without
482
+ * bound inside a healthy generation,
483
+ * 3. apply modern-era decoration — `snapshot.era` is the ONLY
484
+ * captured-context field read; raw error envelopes and non-modern eras
485
+ * pass through byte-identical — and
486
+ * 4. write in the ORIGINATING request's framing.
487
+ * Admission-time era rejections (era_conflict/missing_meta/-32601) do NOT
488
+ * route through here: they reject before any forwarding could happen, so
489
+ * no worker can ever answer those ids. The initialize-error synthesis also
490
+ * records no tombstone: isInitializationResponse settles its pending entry
491
+ * before synthesizing, the answering generation is terminated, and the
492
+ * replay cache is cleared — no late-response channel remains.
493
+ */
494
+ private writeSyntheticReply;
495
+ /**
496
+ * Records a response-finality tombstone for a request instance the
497
+ * supervisor has terminally settled (synthesized reply or client
498
+ * cancellation). Insertion-order eviction at MAX_SYNTHETIC_TOMBSTONES keeps
499
+ * the map bounded even inside one healthy worker generation, where the
500
+ * per-generation purge cannot fire.
501
+ */
502
+ private recordFinalityTombstone;
503
+ /**
504
+ * Retention bound for finality tombstones: generation `generation` is gone
505
+ * with streams closed, so every tombstone recorded at or before it is
506
+ * unreachable (see syntheticTombstones doc) and may be dropped.
507
+ */
508
+ private purgeTombstonesThroughGeneration;
509
+ /**
510
+ * Writes one client-bound message. `mode` is the ORIGINATING REQUEST's
511
+ * captured framing; callers omit it only for writes that correlate to no
512
+ * request id, which fall back to the last-detected inbound mode.
513
+ */
171
514
  private writeToClient;
172
515
  private clearInitialInitializationState;
173
516
  private liveCapOccupancy;
517
+ get unresolvedTreeTokenCount(): number;
174
518
  private clearStartupWatchdog;
175
519
  private adoptActiveChild;
176
520
  private invalidateCurrentChild;
@@ -178,13 +522,24 @@ export declare class StdioSupervisor {
178
522
  private recoverTimedOutWorker;
179
523
  private beginTreeTermination;
180
524
  private finishTreeTermination;
525
+ private scheduleCleanupTokenRetry;
526
+ private runCleanupTokenRetries;
527
+ private stopCleanupTokenRetryIfIdle;
181
528
  private retryUnresolvedTreeToken;
529
+ private performUnresolvedTreeTokenRetry;
182
530
  private handleStartupFailure;
183
531
  private failQueuedRequestsOnStartupFailure;
184
532
  private scheduleRestart;
185
533
  private armRestartReservation;
186
534
  private resumePausedRestart;
535
+ /**
536
+ * Idempotent teardown. Every caller receives the SAME promise, so a caller
537
+ * that needs to know when cleanup is really finished — the fatal handler,
538
+ * which then ends the process — cannot be handed an already-resolved promise
539
+ * while another teardown is still collecting the worker's process group.
540
+ */
187
541
  private shutdown;
542
+ private runShutdown;
188
543
  }
189
544
  export declare const STDIO_WORKER_MODE_ENV = "MCP_STDIO_WORKER_MODE";
190
545
  export {};