@code-yeongyu/senpi 2026.10.25-3 → 2026.10.26-1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +40 -0
- package/dist/bundle/chunks/{anthropic-messages-WBASBTIW.js → anthropic-messages-M235FJQV.js} +3 -3
- package/dist/bundle/chunks/{args-B444OQBK.js → args-FBNJOJSR.js} +1 -1
- package/dist/bundle/chunks/azure-openai-responses-YQRTQ7H6.js +2 -0
- package/dist/bundle/chunks/bedrock-converse-stream.js +3 -3
- package/dist/bundle/chunks/{chunk-FFQOEC76.js → chunk-3A3XCLNH.js} +3 -3
- package/dist/bundle/chunks/{chunk-EHQXLHRB.js → chunk-3LPWILUZ.js} +2 -2
- package/dist/bundle/chunks/{chunk-3BTN337S.js → chunk-3WP5RFQR.js} +1 -1
- package/dist/bundle/chunks/{chunk-WLHNEUGD.js → chunk-5FQMNHXV.js} +1 -1
- package/dist/bundle/chunks/{chunk-HVJI5T75.js → chunk-5KZH6EYQ.js} +2 -2
- package/dist/bundle/chunks/{chunk-4P7WARQZ.js → chunk-5LWUUXMH.js} +1 -1
- package/dist/bundle/chunks/{chunk-ODTGC6YU.js → chunk-6U3TBFBA.js} +1 -1
- package/dist/bundle/chunks/chunk-6VWIMHMH.js +2 -0
- package/dist/bundle/chunks/{chunk-BWA4FVFW.js → chunk-7KOUQSBX.js} +2 -2
- package/dist/bundle/chunks/{chunk-7CNOCJCN.js → chunk-ADQAPDPV.js} +1 -1
- package/dist/bundle/chunks/{chunk-ITGYN22E.js → chunk-CDQPA4FP.js} +1 -1
- package/dist/bundle/chunks/{chunk-WDPQ6CZJ.js → chunk-DMFAVE5N.js} +1 -1
- package/dist/bundle/chunks/{chunk-XF3LJC3O.js → chunk-DYWIFF2Q.js} +1 -1
- package/dist/bundle/chunks/{chunk-YRF4I43T.js → chunk-EZP6AYW2.js} +37 -31
- package/dist/bundle/chunks/chunk-GQJYNFMO.js +2 -0
- package/dist/bundle/chunks/{chunk-O3Q2ET6W.js → chunk-GWGNVYPB.js} +1 -1
- package/dist/bundle/chunks/{chunk-Z3UYNSVR.js → chunk-H4WJ4LY3.js} +1 -1
- package/dist/bundle/chunks/{chunk-UQNRW3VU.js → chunk-HWTD4QF3.js} +1 -1
- package/dist/bundle/chunks/{chunk-WD6WBYC5.js → chunk-I7QWTWVZ.js} +1 -1
- package/dist/bundle/chunks/{chunk-SWJXRC2I.js → chunk-J2YAYTB2.js} +1 -1
- package/dist/bundle/chunks/{chunk-TTU2RNVX.js → chunk-J5FCH7TI.js} +1 -1
- package/dist/bundle/chunks/chunk-JFQUWEN7.js +13 -0
- package/dist/bundle/chunks/{chunk-JA2R2ZSE.js → chunk-L2O6K3SJ.js} +1 -1
- package/dist/bundle/chunks/{chunk-4AOMNOGY.js → chunk-LO5MYLGF.js} +1 -1
- package/dist/bundle/chunks/{chunk-P3M2PF6K.js → chunk-MSHRIZGT.js} +8 -6
- package/dist/bundle/chunks/{chunk-T36LJ6MW.js → chunk-MY4ZD3EW.js} +1 -1
- package/dist/bundle/chunks/{chunk-UFJ2K7Q7.js → chunk-OQ3WDMB3.js} +1 -1
- package/dist/bundle/chunks/{chunk-7GZUFOM3.js → chunk-PH3JDRB2.js} +1 -1
- package/dist/bundle/chunks/{chunk-E6WYH3ZL.js → chunk-QREKAXKX.js} +7 -5
- package/dist/bundle/chunks/{chunk-MJ5NNWX4.js → chunk-QYFOHFPS.js} +1 -1
- package/dist/bundle/chunks/chunk-SOXQ42VS.js +2 -0
- package/dist/bundle/chunks/{chunk-FHXR4ZAC.js → chunk-UDM7VO6O.js} +1 -1
- package/dist/bundle/chunks/{chunk-OLCDIITS.js → chunk-UW6M3EXY.js} +1 -1
- package/dist/bundle/chunks/chunk-VICDRG3J.js +410 -0
- package/dist/bundle/chunks/chunk-VL7O5PO4.js +5 -0
- package/dist/bundle/chunks/chunk-XMJKZK5H.js +2 -0
- package/dist/bundle/chunks/{chunk-C5UFJ7B3.js → chunk-XMNPK472.js} +1 -1
- package/dist/bundle/chunks/{chunk-GSSD3R5X.js → chunk-XQ45JMGN.js} +1 -1
- package/dist/bundle/chunks/{client-WTYEICLH.js → chunk-Y3TMJV3J.js} +3 -3
- package/dist/bundle/chunks/{chunk-KXB5WIS5.js → chunk-Z6SAQ5O3.js} +1 -1
- package/dist/bundle/chunks/{chunk-CNL6QSUB.js → chunk-ZAUVY6ZC.js} +3 -3
- package/dist/bundle/chunks/{chunk-E4LBKOZG.js → chunk-ZLIVZQDY.js} +4 -4
- package/dist/bundle/chunks/{chunk-6AOVUTUR.js → chunk-ZXHLH4MW.js} +2 -2
- package/dist/bundle/chunks/cli-main-55EQA5PS.js +3 -0
- package/dist/bundle/chunks/client-LOS6GSEW.js +2 -0
- package/dist/bundle/chunks/{cloudflare-workers-ai-system-one-K5H6BJP3.js → cloudflare-workers-ai-system-one-ZJQGIIRC.js} +1 -1
- package/dist/bundle/chunks/cursor-agent.js +3 -3
- package/dist/bundle/chunks/devin-agent.js +3 -3
- package/dist/bundle/chunks/dispatch-C2IBP5VV.js +9 -0
- package/dist/bundle/chunks/google-generative-ai-5G4RIQA6.js +2 -0
- package/dist/bundle/chunks/google-vertex-ALD4P27O.js +2 -0
- package/dist/bundle/chunks/{help-fast-path-3Z26DQSV.js → help-fast-path-PEIKGCKZ.js} +1 -1
- package/dist/bundle/chunks/host-command-657VCQ34.js +2 -0
- package/dist/bundle/chunks/{host-lifecycle-ONQIMWMX.js → host-lifecycle-HX3EGRRW.js} +1 -1
- package/dist/bundle/chunks/host-lifecycle.js +2 -2
- package/dist/bundle/chunks/{host-session-command-J65ENSV5.js → host-session-command-OV5XDOJK.js} +1 -1
- package/dist/bundle/chunks/{host-socket-request-3CWE4265.js → host-socket-request-K4LCUOUD.js} +1 -1
- package/dist/bundle/chunks/interactive-mode-MLXSYK2H.js +2 -0
- package/dist/bundle/chunks/{mcp-presenter-bootstrap-XUSCQVRC.js → mcp-presenter-bootstrap-FOMKF4TU.js} +1 -1
- package/dist/bundle/chunks/{mistral-conversations-DH44NOYV.js → mistral-conversations-RAE4K2XY.js} +1 -1
- package/dist/bundle/chunks/{multi-session-host-YI2WZOET.js → multi-session-host-774MACJU.js} +5 -6
- package/dist/bundle/chunks/{openai-codex-responses-SERAFKAS.js → openai-codex-responses-3CW4R7TG.js} +1 -1
- package/dist/bundle/chunks/{openai-completions-BZB5O45A.js → openai-completions-5UIJXQUF.js} +1 -1
- package/dist/bundle/chunks/openai-responses-2BO44MRW.js +3 -0
- package/dist/bundle/chunks/{package-manager-cli-GHZPJMRF.js → package-manager-cli-MZHVCX2S.js} +1 -1
- package/dist/bundle/chunks/{pi-messages-VXATX45B.js → pi-messages-RF6GER4R.js} +2 -2
- package/dist/bundle/chunks/{role-JVSBNKGE.js → role-ETSFSDTS.js} +1 -1
- package/dist/bundle/chunks/rotation-stream-ZMQB4GPO.js +2 -0
- package/dist/bundle/chunks/rpc-mode-UJXOIZCW.js +2 -0
- package/dist/bundle/chunks/{rpc-monitor-source-FFEQS744.js → rpc-monitor-source-DRZXJHTL.js} +1 -1
- package/dist/bundle/chunks/{schedule-command-YWG6D6ZF.js → schedule-command-KA7LFFNK.js} +1 -1
- package/dist/bundle/chunks/session-control-endpoint-MFWXUR7Z.js +3 -0
- package/dist/bundle/chunks/session-picker-BP33NRSL.js +2 -0
- package/dist/bundle/chunks/session-worker.js +212 -203
- package/dist/bundle/chunks/{typesafe-system-one-P75WJ767.js → typesafe-system-one-GNNZ5O5U.js} +1 -1
- package/dist/bundle/cli.js +2 -2
- package/dist/bundle/host-supervisor.js +2 -2
- package/dist/bundle/index.js +1 -1
- package/dist/bundle/rpc-entry.js +1 -1
- package/dist/bundle/runtime-manifest.json +1 -1
- package/dist/cli/host-command-args.d.ts +1 -0
- package/dist/cli/host-command-args.js +5 -0
- package/dist/cli/host-command.js +7 -1
- package/dist/cli.js +21 -3
- package/dist/core/extensions/builtin/mcp/catalog-cache.d.ts +2 -1
- package/dist/core/extensions/builtin/mcp/catalog-cache.js +19 -6
- package/dist/core/extensions/builtin/mcp/catalog.d.ts +3 -2
- package/dist/core/extensions/builtin/mcp/catalog.js +20 -3
- package/dist/core/extensions/builtin/mcp/elicitation.js +2 -4
- package/dist/core/extensions/builtin/mcp/expose/call.d.ts +2 -1
- package/dist/core/extensions/builtin/mcp/expose/call.js +2 -1
- package/dist/core/extensions/builtin/mcp/expose/register.js +1 -1
- package/dist/core/extensions/builtin/mcp/expose/schema-compat.d.ts +2 -2
- package/dist/core/extensions/builtin/mcp/expose/schema-compat.js +11 -1
- package/dist/core/extensions/builtin/mcp/invocation.js +16 -2
- package/dist/core/extensions/builtin/mcp/modern/request.d.ts +1 -1
- package/dist/core/extensions/builtin/mcp/modern/request.js +1 -1
- package/dist/core/extensions/builtin/mcp/modern/sdk-bridge.js +18 -3
- package/dist/core/extensions/builtin/mcp/modern/stdio-probe.js +23 -0
- package/dist/core/extensions/builtin/mcp/modern/subscriptions.d.ts +14 -0
- package/dist/core/extensions/builtin/mcp/modern/subscriptions.js +226 -0
- package/dist/core/extensions/builtin/mcp/modern/tool-schema.d.ts +13 -0
- package/dist/core/extensions/builtin/mcp/modern/tool-schema.js +75 -0
- package/dist/core/extensions/builtin/mcp/modern/tools.d.ts +107 -0
- package/dist/core/extensions/builtin/mcp/modern/tools.js +53 -0
- package/dist/core/extensions/builtin/mcp/resources.js +4 -0
- package/dist/core/extensions/builtin/mcp/tool-client.d.ts +13 -0
- package/dist/core/extensions/builtin/mcp/tool-client.js +17 -0
- package/dist/core/extensions/builtin/mcp/tool-contracts.d.ts +19 -0
- package/dist/core/extensions/builtin/mcp/tool-contracts.js +2 -0
- package/dist/core/extensions/builtin/mcp/wrap.js +2 -2
- package/dist/core/extensions/builtin/permission-system/bash-command-segments.d.ts +37 -0
- package/dist/core/extensions/builtin/permission-system/bash-command-segments.js +899 -0
- package/dist/core/extensions/builtin/permission-system/parsers.d.ts +2 -0
- package/dist/core/extensions/builtin/permission-system/parsers.js +44 -10
- package/dist/core/session-control-actions.js +0 -2
- package/dist/modes/interactive/session-control-attach.d.ts +20 -0
- package/dist/modes/interactive/session-control-attach.js +13 -0
- package/dist/modes/interactive/session-control-commands.js +37 -10
- package/dist/modes/interactive/session-control-registry.d.ts +2 -1
- package/dist/modes/interactive/session-control-registry.js +3 -2
- package/dist/modes/interactive/session-control-rpc-commands.d.ts +4 -2
- package/dist/modes/interactive/session-control-rpc-commands.js +133 -4
- package/dist/modes/interactive/session-control-server.js +9 -3
- package/dist/modes/rpc/compact-content-store.d.ts +96 -0
- package/dist/modes/rpc/compact-content-store.js +269 -0
- package/dist/modes/rpc/compact-session-state.d.ts +111 -0
- package/dist/modes/rpc/compact-session-state.js +206 -0
- package/dist/modes/rpc/compact-spool-dir.d.ts +5 -0
- package/dist/modes/rpc/compact-spool-dir.js +61 -0
- package/dist/modes/rpc/compact-storage-io.d.ts +27 -0
- package/dist/modes/rpc/compact-storage-io.js +19 -0
- package/dist/modes/rpc/connection-handler.d.ts +2 -0
- package/dist/modes/rpc/connection-handler.js +45 -47
- package/dist/modes/rpc/connection-question-bridge.d.ts +2 -1
- package/dist/modes/rpc/connection-question-bridge.js +6 -2
- package/dist/modes/rpc/custom-capability.d.ts +6 -0
- package/dist/modes/rpc/custom-capability.js +6 -0
- package/dist/modes/rpc/extension-ui-response.d.ts +7 -2
- package/dist/modes/rpc/extension-ui-response.js +27 -7
- package/dist/modes/rpc/get-entries-command.d.ts +13 -0
- package/dist/modes/rpc/get-entries-command.js +61 -0
- package/dist/modes/rpc/get-entries-page.d.ts +22 -0
- package/dist/modes/rpc/get-entries-page.js +41 -2
- package/dist/modes/rpc/get-snapshot.d.ts +18 -0
- package/dist/modes/rpc/get-snapshot.js +43 -0
- package/dist/modes/rpc/host-capabilities.js +7 -1
- package/dist/modes/rpc/host-ensure-start.js +1 -0
- package/dist/modes/rpc/host-ensure-types.d.ts +2 -0
- package/dist/modes/rpc/host-ensure.js +1 -0
- package/dist/modes/rpc/host-handoff.d.ts +2 -0
- package/dist/modes/rpc/host-handover-request.d.ts +1 -1
- package/dist/modes/rpc/host-handover-request.js +4 -3
- package/dist/modes/rpc/host-handover-wire.js +6 -0
- package/dist/modes/rpc/host-idle-handover.d.ts +2 -0
- package/dist/modes/rpc/host-idle-handover.js +3 -1
- package/dist/modes/rpc/host-observe-request.d.ts +2 -1
- package/dist/modes/rpc/host-observe-request.js +5 -1
- package/dist/modes/rpc/host-runner.d.ts +1 -0
- package/dist/modes/rpc/host-runner.js +9 -7
- package/dist/modes/rpc/host-successor.js +4 -0
- package/dist/modes/rpc/json-normal-value.d.ts +6 -0
- package/dist/modes/rpc/json-normal-value.js +100 -0
- package/dist/modes/rpc/mcp-presenter-bootstrap.js +5 -0
- package/dist/modes/rpc/media-placeholders.d.ts +9 -3
- package/dist/modes/rpc/media-placeholders.js +12 -4
- package/dist/modes/rpc/rpc-host-command-types.d.ts +2 -0
- package/dist/modes/rpc/rpc-question-types.d.ts +1 -0
- package/dist/modes/rpc/rpc-read-response-types.d.ts +9 -0
- package/dist/modes/rpc/rpc-session-command-types.d.ts +14 -1
- package/dist/modes/rpc/rpc-session-state-types.d.ts +3 -0
- package/dist/modes/rpc/rpc-session-state.js +2 -0
- package/dist/modes/rpc/rpc-types.d.ts +1 -0
- package/dist/modes/rpc/session-attachment-gate.js +1 -0
- package/dist/modes/rpc/session-binding.js +21 -18
- package/dist/modes/rpc/session-command-activity.js +1 -0
- package/dist/modes/rpc/session-event-fanout.d.ts +1 -1
- package/dist/modes/rpc/session-event-fanout.js +17 -9
- package/dist/modes/rpc/session-event-writer.js +22 -5
- package/dist/modes/rpc/session-extension-ui-requests.d.ts +2 -1
- package/dist/modes/rpc/session-extension-ui-requests.js +15 -10
- package/dist/modes/rpc/session-held.js +1 -0
- package/dist/modes/rpc/session-status-stream.d.ts +2 -1
- package/dist/modes/rpc/session-status-stream.js +8 -2
- package/dist/modes/rpc/session-status-subscriptions.d.ts +2 -1
- package/dist/modes/rpc/session-status-subscriptions.js +4 -0
- package/dist/modes/rpc/session-worker.js +5 -2
- package/dist/modes/rpc/snapshot-types.d.ts +29 -0
- package/dist/modes/rpc/snapshot-types.js +2 -0
- package/dist/modes/rpc/ui-answer-ledger.d.ts +13 -0
- package/dist/modes/rpc/ui-answer-ledger.js +34 -0
- package/dist/senpi +21 -3
- package/docs/mcp.md +20 -0
- package/docs/rpc.md +87 -11
- package/package.json +7 -6
- package/dist/bundle/chunks/azure-openai-responses-7I45DI72.js +0 -2
- package/dist/bundle/chunks/chunk-3LNSLJ7N.js +0 -2
- package/dist/bundle/chunks/chunk-6O4SEW2W.js +0 -5
- package/dist/bundle/chunks/chunk-7W7OJDIY.js +0 -410
- package/dist/bundle/chunks/chunk-DMI55M2N.js +0 -2
- package/dist/bundle/chunks/chunk-H3HNMY2W.js +0 -13
- package/dist/bundle/chunks/chunk-MSQCTH6V.js +0 -2
- package/dist/bundle/chunks/cli-main-HEKFFB5S.js +0 -3
- package/dist/bundle/chunks/dispatch-NYAULNVW.js +0 -9
- package/dist/bundle/chunks/google-generative-ai-UM5Y7T6B.js +0 -2
- package/dist/bundle/chunks/google-vertex-OKU2VQHL.js +0 -2
- package/dist/bundle/chunks/host-command-Z3WPHJOW.js +0 -2
- package/dist/bundle/chunks/interactive-mode-DEPQC6TX.js +0 -2
- package/dist/bundle/chunks/openai-responses-TNHQ6SJS.js +0 -3
- package/dist/bundle/chunks/rotation-stream-ZPVCFOVD.js +0 -2
- package/dist/bundle/chunks/rpc-mode-ORHBGBBV.js +0 -2
- package/dist/bundle/chunks/session-control-endpoint-36HAQSQO.js +0 -3
- package/dist/bundle/chunks/session-picker-25H5KNYP.js +0 -2
|
@@ -14,14 +14,9 @@ export async function createRpcSessionBinding(sessionId, entry, writer, requestC
|
|
|
14
14
|
// session is inside is what the loop-lag watchdog blames for a stall. The record
|
|
15
15
|
// stream already carries that transition; no extra subscription is needed.
|
|
16
16
|
const toolSpans = createToolAttributionSpans(sessionId);
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
continue;
|
|
21
|
-
const record = JSON.parse(line);
|
|
22
|
-
toolSpans.observe(record);
|
|
23
|
-
writer.enqueue(sessionId, record);
|
|
24
|
-
}
|
|
17
|
+
const enqueueRecord = (record) => {
|
|
18
|
+
toolSpans.observe(record);
|
|
19
|
+
writer.enqueue(sessionId, record);
|
|
25
20
|
};
|
|
26
21
|
// Attachments share one entry, so resolve the host from the live runtime. In
|
|
27
22
|
// particular, switch_session must use the entry's replacement-aware method
|
|
@@ -48,16 +43,25 @@ export async function createRpcSessionBinding(sessionId, entry, writer, requestC
|
|
|
48
43
|
// an unawaited prompt (senpi#2871).
|
|
49
44
|
let droppedAfterClose = false;
|
|
50
45
|
const handler = await runWithProviderScope(entry.scope, async () => {
|
|
51
|
-
const writeInScope = bindToProviderScope(
|
|
46
|
+
const writeInScope = bindToProviderScope(enqueueRecord);
|
|
47
|
+
const dropAfterClose = () => {
|
|
48
|
+
if (!droppedAfterClose)
|
|
49
|
+
process.stderr.write(`senpi rpc host: dropped a record for closed session ${sessionId} at ${new Date().toISOString()}\n`);
|
|
50
|
+
droppedAfterClose = true;
|
|
51
|
+
};
|
|
52
|
+
const writeRecord = (record) => {
|
|
53
|
+
if (entry.scope.state === "closed")
|
|
54
|
+
return dropAfterClose();
|
|
55
|
+
writeInScope(record);
|
|
56
|
+
};
|
|
52
57
|
const taggedSink = {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
return;
|
|
59
|
-
|
|
60
|
-
writeInScope(chunk);
|
|
58
|
+
writeRecord,
|
|
59
|
+
// A late raw reply after the scope closed is dropped like any record (senpi#2871); while the scope is
|
|
60
|
+
// open, records arrive only through writeRecord, so a raw write is a wiring bug.
|
|
61
|
+
writeRaw: () => {
|
|
62
|
+
if (entry.scope.state === "closed")
|
|
63
|
+
return dropAfterClose();
|
|
64
|
+
throw new Error("Shared session bindings deliver records through writeRecord, never writeRaw");
|
|
61
65
|
},
|
|
62
66
|
waitForBackpressure: bindToProviderScope(async () => { }),
|
|
63
67
|
};
|
|
@@ -65,7 +69,6 @@ export async function createRpcSessionBinding(sessionId, entry, writer, requestC
|
|
|
65
69
|
sessionId,
|
|
66
70
|
shutdownHandler: bindToProviderScope(requestClose),
|
|
67
71
|
disposeRuntime: false,
|
|
68
|
-
eventFlushScheduler: (flush) => flush(),
|
|
69
72
|
publishLoadedSurfacesChanged: (previous, digest) => {
|
|
70
73
|
if (entry.scope.state !== "closed")
|
|
71
74
|
writer.publishLoadedSurfacesChanged(sessionId, previous, digest);
|
|
@@ -50,7 +50,7 @@ export declare class SessionEventFanout {
|
|
|
50
50
|
pendingQuestionCount(sessionId: string): number;
|
|
51
51
|
/** Deliver one line to the connections attached to a session, never to the rest of the fanout. */
|
|
52
52
|
deliverToSession(sessionId: string, line: string): void;
|
|
53
|
-
rememberSnapshot(sessionId: string, value: Record<string, unknown>, line: string, placeholderLine?: string
|
|
53
|
+
rememberSnapshot(sessionId: string, value: Record<string, unknown>, line: string, placeholderLine?: string): void;
|
|
54
54
|
forgetSession(sessionId: string): void;
|
|
55
55
|
private replaySnapshot;
|
|
56
56
|
private resyncSnapshot;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { MEDIA_PLACEHOLDERS_CAPABILITY } from "./custom-capability.js";
|
|
2
2
|
import { serializeJsonLine } from "./jsonl.js";
|
|
3
|
-
import {
|
|
3
|
+
import { carriesMedia, omitNormalInlineMedia } from "./media-placeholders.js";
|
|
4
4
|
import { SocketEventSinkActor } from "./socket-event-fanout.js";
|
|
5
5
|
import { EVENT_RESYNC_CAPABILITY } from "./socket-peer-recovery.js";
|
|
6
6
|
const BROADCAST_LIFECYCLE_RECORDS = new Set([
|
|
@@ -15,9 +15,11 @@ const BROADCAST_LIFECYCLE_RECORDS = new Set([
|
|
|
15
15
|
function snapshotPlaceholderLine(record) {
|
|
16
16
|
if (record.placeholderLine !== undefined)
|
|
17
17
|
return record.placeholderLine;
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
if (!record.mayCarryMedia)
|
|
19
|
+
return record.line;
|
|
20
|
+
const source = JSON.parse(record.line);
|
|
21
|
+
const redacted = omitNormalInlineMedia(source);
|
|
22
|
+
record.placeholderLine = redacted === source ? record.line : serializeJsonLine(redacted);
|
|
21
23
|
return record.placeholderLine;
|
|
22
24
|
}
|
|
23
25
|
export class SessionEventFanout {
|
|
@@ -147,13 +149,20 @@ export class SessionEventFanout {
|
|
|
147
149
|
if (this.connectionSessions.get(id)?.has(sessionId))
|
|
148
150
|
actor.enqueue(line);
|
|
149
151
|
}
|
|
150
|
-
rememberSnapshot(sessionId, value, line, placeholderLine
|
|
152
|
+
rememberSnapshot(sessionId, value, line, placeholderLine) {
|
|
153
|
+
const record = {
|
|
154
|
+
line,
|
|
155
|
+
placeholderLine,
|
|
156
|
+
hasMessage: value.message != null,
|
|
157
|
+
mayCarryMedia: carriesMedia(value),
|
|
158
|
+
};
|
|
151
159
|
if (value.type === "session_lifecycle") {
|
|
152
|
-
this.lifecycleSnapshots.set(sessionId,
|
|
160
|
+
this.lifecycleSnapshots.set(sessionId, record);
|
|
153
161
|
return;
|
|
154
162
|
}
|
|
155
163
|
if (value.type === "extension_ui_request" && value.method === "question" && typeof value.id === "string") {
|
|
156
164
|
const pending = this.pendingQuestions.get(sessionId) ?? new Map();
|
|
165
|
+
// Writer acceptance already detached and JSON-normalized the frame.
|
|
157
166
|
pending.set(value.id, { ...value });
|
|
158
167
|
this.pendingQuestions.set(sessionId, pending);
|
|
159
168
|
return;
|
|
@@ -169,7 +178,6 @@ export class SessionEventFanout {
|
|
|
169
178
|
return;
|
|
170
179
|
}
|
|
171
180
|
const event = value.assistantMessageEvent;
|
|
172
|
-
const record = { line, placeholderLine, source };
|
|
173
181
|
if (value.type === "message_start" || (value.type === "message_update" && event?.type === "text_start")) {
|
|
174
182
|
this.sessionSnapshots.set(sessionId, [record]);
|
|
175
183
|
}
|
|
@@ -200,7 +208,7 @@ export class SessionEventFanout {
|
|
|
200
208
|
const current = this.connectionSessions.get(id) ?? new Set();
|
|
201
209
|
return [...new Set([...attached, ...current])].map((sessionId) => {
|
|
202
210
|
const records = this.sessionSnapshots.get(sessionId) ?? [];
|
|
203
|
-
const latest = records.findLast((record) => record.
|
|
211
|
+
const latest = records.findLast((record) => record.hasMessage);
|
|
204
212
|
// The producer may mutate its partial object after emission. The serialized
|
|
205
213
|
// record, not that object, fixes the recovery cut before later deltas enqueue.
|
|
206
214
|
const frozen = latest === undefined ? undefined : JSON.parse(latest.line);
|
|
@@ -211,7 +219,7 @@ export class SessionEventFanout {
|
|
|
211
219
|
message: typeof frozen === "object" && frozen !== null && "message" in frozen ? frozen.message : null,
|
|
212
220
|
...(!current.has(sessionId) ? { closed: true } : {}),
|
|
213
221
|
};
|
|
214
|
-
return placeholders ?
|
|
222
|
+
return placeholders ? omitNormalInlineMedia(record) : record;
|
|
215
223
|
});
|
|
216
224
|
}
|
|
217
225
|
}
|
|
@@ -2,8 +2,9 @@ import { AsyncLocalStorage } from "node:async_hooks";
|
|
|
2
2
|
import { bindPrivateMcpForms } from "../../core/mcp-form-types.js";
|
|
3
3
|
import { McpPresenterAuthority } from "./mcp-presenter-authority.js";
|
|
4
4
|
import { MEDIA_PLACEHOLDERS_CAPABILITY } from "./custom-capability.js";
|
|
5
|
+
import { normalizeJsonValue } from "./json-normal-value.js";
|
|
5
6
|
import { serializeJsonLine } from "./jsonl.js";
|
|
6
|
-
import {
|
|
7
|
+
import { omitNormalInlineMedia } from "./media-placeholders.js";
|
|
7
8
|
import { RpcSecurityPresentations } from "./security-presentations.js";
|
|
8
9
|
import { SessionEventFanout } from "./session-event-fanout.js";
|
|
9
10
|
import { SessionOpenTurns } from "./session-open-turns.js";
|
|
@@ -330,7 +331,7 @@ export class SessionEventWriter {
|
|
|
330
331
|
if (this.sealedSessions.has(sessionId))
|
|
331
332
|
return false;
|
|
332
333
|
const targetId = this.connectionContext.getStore();
|
|
333
|
-
const record = value;
|
|
334
|
+
const record = normalizeJsonValue(value).value;
|
|
334
335
|
const isTargeted = record.type === "response" ||
|
|
335
336
|
record.type === "bash_execution_update" ||
|
|
336
337
|
// Relocation records belong to the one connection that offers it (session-relocation-channel.ts).
|
|
@@ -338,8 +339,22 @@ export class SessionEventWriter {
|
|
|
338
339
|
record.type === "relocation_due" ||
|
|
339
340
|
(record.type === "extension_ui_request" &&
|
|
340
341
|
["select", "confirm", "input", "editor"].includes(String(record.method)));
|
|
341
|
-
const tagged = { ...
|
|
342
|
+
const tagged = { ...record, sessionId };
|
|
342
343
|
const line = serializeJsonLine(tagged);
|
|
344
|
+
// Observation records are per-subscription opt-ins on the subscriber's own actor FIFO: never attachments,
|
|
345
|
+
// the attach replay, response broadcast, or the stdio lane, which cannot subscribe.
|
|
346
|
+
if (record.type === "snapshot_marker" ||
|
|
347
|
+
record.type === "ui_request_pending" ||
|
|
348
|
+
record.type === "ui_request_settled") {
|
|
349
|
+
const targets = record.type === "snapshot_marker"
|
|
350
|
+
? this.status.mirrorTargets(sessionId, "snapshot_marker").filter((id) => id === targetId)
|
|
351
|
+
: this.status.dialogTargets(sessionId);
|
|
352
|
+
for (const target of targets)
|
|
353
|
+
this.fanout.get(target)?.actor.enqueue(line);
|
|
354
|
+
if (targets.length > 0)
|
|
355
|
+
this.requestFlush();
|
|
356
|
+
return true;
|
|
357
|
+
}
|
|
343
358
|
if (this.fanout.isEmpty() && this.exceedsStdioCapacity(line)) {
|
|
344
359
|
this.closeSession(sessionId, {
|
|
345
360
|
type: "response",
|
|
@@ -357,10 +372,12 @@ export class SessionEventWriter {
|
|
|
357
372
|
// A record is only walked and re-serialized when a target asked for placeholders;
|
|
358
373
|
// otherwise this is byte-for-byte today's path, with serializeJsonLine called once.
|
|
359
374
|
const hasPlaceholderTarget = targets.some((target) => this.fanout.connectionHas(target, MEDIA_PLACEHOLDERS_CAPABILITY));
|
|
360
|
-
const redacted = hasPlaceholderTarget
|
|
375
|
+
const redacted = hasPlaceholderTarget
|
|
376
|
+
? omitNormalInlineMedia(tagged, this.mediaPersisters.get(sessionId))
|
|
377
|
+
: tagged;
|
|
361
378
|
const placeholderLine = redacted === tagged ? undefined : serializeJsonLine(redacted);
|
|
362
379
|
if (!isTargeted)
|
|
363
|
-
this.fanout.rememberSnapshot(sessionId, tagged, line, placeholderLine
|
|
380
|
+
this.fanout.rememberSnapshot(sessionId, tagged, line, placeholderLine);
|
|
364
381
|
for (const target of targets) {
|
|
365
382
|
if (target !== undefined && !this.fanout.get(target))
|
|
366
383
|
continue;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { UiRequestChange } from "./snapshot-types.ts";
|
|
1
2
|
import type { RpcExtensionUIRequest, RpcExtensionUIResponse } from "./rpc-types.ts";
|
|
2
3
|
type PendingRequest = {
|
|
3
4
|
resolve: (value: RpcExtensionUIResponse) => void;
|
|
@@ -9,7 +10,7 @@ export declare const sessionDialogRequests: WeakMap<object, SessionExtensionUiRe
|
|
|
9
10
|
export declare class SessionExtensionUiRequests {
|
|
10
11
|
private readonly pending;
|
|
11
12
|
private readonly changed;
|
|
12
|
-
constructor(changed?: () => void);
|
|
13
|
+
constructor(changed?: (change?: UiRequestChange) => void);
|
|
13
14
|
set(id: string, request: PendingRequest): void;
|
|
14
15
|
delete(id: string): void;
|
|
15
16
|
snapshot(): RpcExtensionUIRequest[];
|
|
@@ -7,11 +7,13 @@ export class SessionExtensionUiRequests {
|
|
|
7
7
|
}
|
|
8
8
|
set(id, request) {
|
|
9
9
|
this.pending.set(id, request);
|
|
10
|
-
this.changed?.(
|
|
10
|
+
this.changed?.(request.frame
|
|
11
|
+
? { type: "ui_request_pending", uiRequestId: id, method: request.frame.method, request: request.frame }
|
|
12
|
+
: undefined);
|
|
11
13
|
}
|
|
12
14
|
delete(id) {
|
|
13
|
-
this.pending.delete(id)
|
|
14
|
-
|
|
15
|
+
if (this.pending.delete(id))
|
|
16
|
+
this.changed?.({ type: "ui_request_settled", uiRequestId: id, disposition: "cancelled" });
|
|
15
17
|
}
|
|
16
18
|
snapshot() {
|
|
17
19
|
return [...this.pending.values()].flatMap((request) => (request.frame ? [request.frame] : []));
|
|
@@ -22,22 +24,25 @@ export class SessionExtensionUiRequests {
|
|
|
22
24
|
return false;
|
|
23
25
|
beforeAnswer?.();
|
|
24
26
|
this.pending.delete(response.id);
|
|
25
|
-
this.changed?.(
|
|
27
|
+
this.changed?.({
|
|
28
|
+
type: "ui_request_settled",
|
|
29
|
+
uiRequestId: response.id,
|
|
30
|
+
disposition: "cancelled" in response ? "cancelled" : "answered",
|
|
31
|
+
});
|
|
26
32
|
request.resolve(response);
|
|
27
33
|
return true;
|
|
28
34
|
}
|
|
29
35
|
cancelAll() {
|
|
30
|
-
for (const [id, request] of this.pending)
|
|
36
|
+
for (const [id, request] of this.pending) {
|
|
37
|
+
this.delete(id);
|
|
31
38
|
request.resolve({ type: "extension_ui_response", id, cancelled: true });
|
|
32
|
-
|
|
33
|
-
this.changed?.();
|
|
39
|
+
}
|
|
34
40
|
}
|
|
35
41
|
close() {
|
|
36
|
-
for (const request of this.pending
|
|
42
|
+
for (const [id, request] of this.pending) {
|
|
43
|
+
this.delete(id);
|
|
37
44
|
request.reject(new Error("Extension UI request cancelled: session closed"));
|
|
38
45
|
}
|
|
39
|
-
this.pending.clear();
|
|
40
|
-
this.changed?.();
|
|
41
46
|
}
|
|
42
47
|
}
|
|
43
48
|
//# sourceMappingURL=session-extension-ui-requests.js.map
|
|
@@ -9,6 +9,7 @@ import { RpcSessionRegistryError } from "./session-registry-types.js";
|
|
|
9
9
|
// a read or a control needed to finish/cancel an already-admitted turn.
|
|
10
10
|
const READ_ONLY_OR_CONTROL = new Set([
|
|
11
11
|
"get_state",
|
|
12
|
+
"get_snapshot",
|
|
12
13
|
"get_effect_status",
|
|
13
14
|
"get_pending_inputs",
|
|
14
15
|
"get_steering_messages",
|
|
@@ -67,7 +67,8 @@ export declare class SessionStatusStream {
|
|
|
67
67
|
publishEffect(sessionId: string, status: EffectStatus): void;
|
|
68
68
|
seal(sessionId: string, terminal: SessionStatusTerminal, cause: SessionStatusCause, by: SessionStatusActor): void;
|
|
69
69
|
forget(sessionId: string): void;
|
|
70
|
-
|
|
70
|
+
dialogTargets(sessionId: string): readonly string[];
|
|
71
|
+
mirrorTargets(sessionId: string, recordType: string, dialogs?: boolean): readonly string[];
|
|
71
72
|
matchingConnections(sessionId: string): ReadonlyMap<string, boolean>;
|
|
72
73
|
/** The live view, falling back to the last one seen once the registry has dropped a sealed entry. */
|
|
73
74
|
private currentView;
|
|
@@ -171,8 +171,14 @@ export class SessionStatusStream {
|
|
|
171
171
|
this.lastViews.delete(sessionId);
|
|
172
172
|
this.lastChanges.delete(sessionId);
|
|
173
173
|
}
|
|
174
|
-
|
|
175
|
-
|
|
174
|
+
dialogTargets(sessionId) {
|
|
175
|
+
return this.mirrorTargets(sessionId, "ui_request_pending", true);
|
|
176
|
+
}
|
|
177
|
+
mirrorTargets(sessionId, recordType, dialogs = false) {
|
|
178
|
+
const subscriptions = [...this.pendings.values(), ...this.subscriptions.values()].filter((subscription) => (dialogs ? subscription.options.dialogs === true : true) &&
|
|
179
|
+
(recordType === "snapshot_marker" || dialogs
|
|
180
|
+
? subscription.options.mirror.kind !== "none"
|
|
181
|
+
: mirrorsRecord(subscription.options.mirror, recordType)));
|
|
176
182
|
if (subscriptions.length === 0)
|
|
177
183
|
return [];
|
|
178
184
|
const entry = this.source?.entryView(sessionId) ?? this.lastViews.get(sessionId)?.entry;
|
|
@@ -29,6 +29,7 @@ export interface SubscriptionOptions {
|
|
|
29
29
|
readonly includeContext: boolean;
|
|
30
30
|
readonly mirror: MirrorSelection;
|
|
31
31
|
readonly effects: boolean;
|
|
32
|
+
readonly dialogs?: boolean;
|
|
32
33
|
}
|
|
33
34
|
export interface SubscriptionRefusal {
|
|
34
35
|
readonly reason: string;
|
|
@@ -36,7 +37,7 @@ export interface SubscriptionRefusal {
|
|
|
36
37
|
}
|
|
37
38
|
export declare function parseSubscription(request: Readonly<Pick<Extract<RpcCommand, {
|
|
38
39
|
type: "subscribe_sessions";
|
|
39
|
-
}>, "sessions" | "mirror" | "include_workers" | "include_context" | "effects">>): {
|
|
40
|
+
}>, "sessions" | "mirror" | "include_workers" | "include_context" | "effects" | "dialogs">>): {
|
|
40
41
|
readonly options: SubscriptionOptions;
|
|
41
42
|
} | {
|
|
42
43
|
readonly refusal: SubscriptionRefusal;
|
|
@@ -59,12 +59,16 @@ export function parseSubscription(request) {
|
|
|
59
59
|
const includeContext = optionalBoolean(request.include_context, "include_context");
|
|
60
60
|
if (typeof includeContext === "object")
|
|
61
61
|
return { refusal: includeContext };
|
|
62
|
+
const dialogs = optionalBoolean(request.dialogs, "dialogs");
|
|
63
|
+
if (typeof dialogs === "object")
|
|
64
|
+
return { refusal: dialogs };
|
|
62
65
|
const effects = optionalBoolean(request.effects, "effects");
|
|
63
66
|
if (typeof effects === "object")
|
|
64
67
|
return { refusal: effects };
|
|
65
68
|
return {
|
|
66
69
|
options: {
|
|
67
70
|
selector: selector.selector,
|
|
71
|
+
dialogs: dialogs ?? false,
|
|
68
72
|
// A childrenOf subscription exists to watch machine-driven children, which open as workers.
|
|
69
73
|
includeWorkers: includeWorkers ?? selector.selector.kind === "childrenOf",
|
|
70
74
|
includeContext: includeContext ?? false,
|
|
@@ -76,7 +76,8 @@ class WorkerEventWriter extends SessionEventWriter {
|
|
|
76
76
|
}));
|
|
77
77
|
}
|
|
78
78
|
enqueue(_sessionId, record) {
|
|
79
|
-
|
|
79
|
+
const line = JSON.stringify(record);
|
|
80
|
+
if (Buffer.byteLength(line) > SESSION_WORKER_LIMITS.outputBytes)
|
|
80
81
|
failWorker("session_worker_output_limit");
|
|
81
82
|
const session = entry?.runtime?.session;
|
|
82
83
|
if (!session)
|
|
@@ -97,7 +98,9 @@ class WorkerEventWriter extends SessionEventWriter {
|
|
|
97
98
|
failWorker("session_worker_snapshot_limit");
|
|
98
99
|
exchange((signal) => ({
|
|
99
100
|
type: "output",
|
|
100
|
-
|
|
101
|
+
// Keep one JSON-normal transfer format, including toJSON and non-cloneable fields.
|
|
102
|
+
// S12 replaces this whole-record serialization with compact records and permits.
|
|
103
|
+
record: JSON.parse(line),
|
|
101
104
|
connection: this.currentConnection(),
|
|
102
105
|
signal,
|
|
103
106
|
activity,
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { SessionEntry } from "../../core/session-manager.ts";
|
|
2
|
+
import type { RpcExtensionUIRequest } from "./rpc-question-types.ts";
|
|
3
|
+
import type { RpcSessionState } from "./rpc-session-state-types.ts";
|
|
4
|
+
export type RpcSnapshot = {
|
|
5
|
+
readonly state: RpcSessionState;
|
|
6
|
+
readonly entries: SessionEntry[];
|
|
7
|
+
readonly next_before: string | null;
|
|
8
|
+
readonly history_truncated: boolean;
|
|
9
|
+
readonly markerId: string;
|
|
10
|
+
};
|
|
11
|
+
export type RpcSnapshotMarker = {
|
|
12
|
+
readonly type: "snapshot_marker";
|
|
13
|
+
readonly sessionId: string;
|
|
14
|
+
readonly markerId: string;
|
|
15
|
+
};
|
|
16
|
+
export type UiRequestChange = {
|
|
17
|
+
readonly type: "ui_request_pending";
|
|
18
|
+
readonly uiRequestId: string;
|
|
19
|
+
readonly method: RpcExtensionUIRequest["method"];
|
|
20
|
+
readonly request: RpcExtensionUIRequest;
|
|
21
|
+
} | {
|
|
22
|
+
readonly type: "ui_request_settled";
|
|
23
|
+
readonly uiRequestId: string;
|
|
24
|
+
readonly disposition: "answered" | "cancelled";
|
|
25
|
+
};
|
|
26
|
+
export type RpcUiRequestEvent = UiRequestChange & {
|
|
27
|
+
readonly sessionId: string;
|
|
28
|
+
};
|
|
29
|
+
//# sourceMappingURL=snapshot-types.d.ts.map
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/** Session-generation receipts: deliberately never persisted to the transcript. */
|
|
2
|
+
export type UiAnswerReceipt = {
|
|
3
|
+
readonly uiRequestId: string;
|
|
4
|
+
readonly answerId?: string;
|
|
5
|
+
};
|
|
6
|
+
export declare class UiAnswerLedger {
|
|
7
|
+
private readonly answers;
|
|
8
|
+
lookup(uiRequestId: string, answerId: string | undefined): "replay" | "already_answered" | undefined;
|
|
9
|
+
record(uiRequestId: string, answerId: string | undefined): void;
|
|
10
|
+
snapshot(): UiAnswerReceipt[];
|
|
11
|
+
}
|
|
12
|
+
export declare function sessionUiAnswerLedger(session: object): UiAnswerLedger;
|
|
13
|
+
//# sourceMappingURL=ui-answer-ledger.d.ts.map
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export class UiAnswerLedger {
|
|
2
|
+
constructor() {
|
|
3
|
+
this.answers = new Map();
|
|
4
|
+
}
|
|
5
|
+
lookup(uiRequestId, answerId) {
|
|
6
|
+
const receipt = this.answers.get(uiRequestId);
|
|
7
|
+
if (!receipt)
|
|
8
|
+
return undefined;
|
|
9
|
+
return answerId !== undefined && answerId === receipt.answerId ? "replay" : "already_answered";
|
|
10
|
+
}
|
|
11
|
+
record(uiRequestId, answerId) {
|
|
12
|
+
if (this.answers.has(uiRequestId))
|
|
13
|
+
return;
|
|
14
|
+
this.answers.set(uiRequestId, { uiRequestId, ...(answerId === undefined ? {} : { answerId }) });
|
|
15
|
+
if (this.answers.size > 1024) {
|
|
16
|
+
const oldest = this.answers.keys().next();
|
|
17
|
+
if (!oldest.done)
|
|
18
|
+
this.answers.delete(oldest.value);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
snapshot() {
|
|
22
|
+
return [...this.answers.values()].slice(-64).map((receipt) => ({ ...receipt }));
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
const ledgers = new WeakMap();
|
|
26
|
+
export function sessionUiAnswerLedger(session) {
|
|
27
|
+
let ledger = ledgers.get(session);
|
|
28
|
+
if (!ledger) {
|
|
29
|
+
ledger = new UiAnswerLedger();
|
|
30
|
+
ledgers.set(session, ledger);
|
|
31
|
+
}
|
|
32
|
+
return ledger;
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=ui-answer-ledger.js.map
|
package/dist/senpi
CHANGED
|
@@ -10,7 +10,7 @@ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExte
|
|
|
10
10
|
import "./valid-cwd.js";
|
|
11
11
|
import "./internal-role.js";
|
|
12
12
|
import { spawn, spawnSync } from "node:child_process";
|
|
13
|
-
import { existsSync, realpathSync } from "node:fs";
|
|
13
|
+
import { existsSync, fstatSync, realpathSync } from "node:fs";
|
|
14
14
|
import { dirname, join } from "node:path";
|
|
15
15
|
import { fileURLToPath } from "node:url";
|
|
16
16
|
import { processBunRuntimeOptions, resolveBunReexec } from "./bun-runtime.js";
|
|
@@ -20,6 +20,24 @@ import { APP_NAME, DISPLAY_VERSION, findNodePackageDir, getAgentDir, getInstallP
|
|
|
20
20
|
import { hasInheritedInspectorOption, releaseInheritedInspectorForChild } from "./inspector-policy.js";
|
|
21
21
|
import { prepareRuntimeSnapshot } from "./runtime-snapshot/enter.js";
|
|
22
22
|
import { handleBootstrapSelfUpdate } from "./self-update-bootstrap.js";
|
|
23
|
+
/** Only a presenter-enabled launch may carry its reserved private channel through CLI re-execution. */
|
|
24
|
+
function reexecStdio() {
|
|
25
|
+
const argv = process.argv.slice(2);
|
|
26
|
+
const presenter = argv.indexOf("--mcp-presenter-fd");
|
|
27
|
+
if (presenter < 0 || argv[presenter + 1] !== "4")
|
|
28
|
+
return "inherit";
|
|
29
|
+
try {
|
|
30
|
+
const stat = fstatSync(4);
|
|
31
|
+
if (!stat.isSocket() && !stat.isFIFO())
|
|
32
|
+
return "inherit";
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
return "inherit";
|
|
36
|
+
}
|
|
37
|
+
const owner = argv.indexOf("--owner-fd");
|
|
38
|
+
const lifetime = (owner >= 0 && argv[owner + 1] === "3") || process.env.SENPI_RPC_HOST_WATCH_FD === "3";
|
|
39
|
+
return ["inherit", "inherit", "inherit", lifetime ? 3 : "ignore", 4];
|
|
40
|
+
}
|
|
23
41
|
// Upstream's `cli/setup.ts` helper is deliberately not used here: this launcher only decides the
|
|
24
42
|
// runtime and process structure, and `cli-main.ts` performs the equivalent process/title/env/http
|
|
25
43
|
// setup for both entry paths (Node launcher and the Bun binary).
|
|
@@ -52,7 +70,7 @@ function reexecUnderBunIfNeeded() {
|
|
|
52
70
|
return false;
|
|
53
71
|
}
|
|
54
72
|
const result = spawnSync(decision.bunPath, [scriptRealPath, ...process.argv.slice(2)], {
|
|
55
|
-
stdio:
|
|
73
|
+
stdio: reexecStdio(),
|
|
56
74
|
windowsHide: true,
|
|
57
75
|
});
|
|
58
76
|
if (result.signal) {
|
|
@@ -125,7 +143,7 @@ async function spawnFullCli() {
|
|
|
125
143
|
return await new Promise((resolve, reject) => {
|
|
126
144
|
const child = spawn(process.execPath, [...process.execArgv, fullCliPath, ...args], {
|
|
127
145
|
env: childEnvironment,
|
|
128
|
-
stdio:
|
|
146
|
+
stdio: reexecStdio(),
|
|
129
147
|
});
|
|
130
148
|
child.on("error", (error) => {
|
|
131
149
|
reject(error);
|
package/docs/mcp.md
CHANGED
|
@@ -386,6 +386,26 @@ above.
|
|
|
386
386
|
|
|
387
387
|
## Resources and prompts
|
|
388
388
|
|
|
389
|
+
MCP 2026-07-28 tool schemas use JSON Schema 2020-12 by default. Explicit
|
|
390
|
+
2020-12 declarations are supported; other dialect declarations produce
|
|
391
|
+
`MCP_SCHEMA_UNSUPPORTED_DIALECT`. Tool discovery, the disk cache, and MCP
|
|
392
|
+
exposure retain the full schema, including `$defs`, `$ref` siblings, and
|
|
393
|
+
`additionalProperties`. Boolean roots use an equivalent `allOf` container
|
|
394
|
+
at the object-shaped model-tool API boundary.
|
|
395
|
+
|
|
396
|
+
Modern structured results may be any JSON value, including `null`, `false`,
|
|
397
|
+
and `0`. Successful results with an `outputSchema` must contain conforming
|
|
398
|
+
`structuredContent`; execution-error results are exempt. Validation never
|
|
399
|
+
coerces values, inserts defaults, removes properties, or fetches references
|
|
400
|
+
from the network. Unresolved references are rejected. Schemas are bounded
|
|
401
|
+
to 64 nesting levels and 16,384 JSON nodes; `format` is an annotation.
|
|
402
|
+
|
|
403
|
+
Object-only model providers may require a lossy schema adaptation in the AI
|
|
404
|
+
provider layer. That wire adaptation does not change MCP's source schema
|
|
405
|
+
or the arguments sent to the server: modern calls validate the model's
|
|
406
|
+
arguments without coercion. Legacy connections retain the SDK's existing
|
|
407
|
+
schema validation and compatibility conversion.
|
|
408
|
+
|
|
389
409
|
- `mcp_list_resources` / `mcp_read_resource` register automatically when a
|
|
390
410
|
connected server lists resources.
|
|
391
411
|
- Mention `@mcp:<server>/<uri>` in your prompt to inline a resource's content
|