@letrquan/book 0.2.0 → 0.3.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.
- package/CHANGELOG.md +2747 -40
- package/README.md +132 -1311
- package/dist/additional-roots-H7UNAEHS.js +31 -0
- package/dist/{app-MOXXWEOY.js → app-GE4OHJOC.js} +5740 -4045
- package/dist/app-GE4OHJOC.js.map +1 -0
- package/dist/{chunk-5GDZ22YP.js → chunk-224FJZLH.js} +99 -108
- package/dist/chunk-224FJZLH.js.map +1 -0
- package/dist/chunk-2AAOF5B5.js +28 -0
- package/dist/chunk-2AAOF5B5.js.map +1 -0
- package/dist/{chunk-CAGOB2N4.js → chunk-4AS7LKKQ.js} +6 -4
- package/dist/chunk-4AS7LKKQ.js.map +1 -0
- package/dist/chunk-525CAZPY.js +120 -0
- package/dist/chunk-525CAZPY.js.map +1 -0
- package/dist/{chunk-URLQAXTR.js → chunk-56SIEMDX.js} +2 -2
- package/dist/chunk-56SIEMDX.js.map +1 -0
- package/dist/{chunk-XIFFTATU.js → chunk-5YHTKWJN.js} +5420 -4031
- package/dist/chunk-5YHTKWJN.js.map +1 -0
- package/dist/chunk-62QL5QUP.js +283 -0
- package/dist/chunk-62QL5QUP.js.map +1 -0
- package/dist/{chunk-KWHQ2XXL.js → chunk-67UZEWWF.js} +3 -3
- package/dist/chunk-67UZEWWF.js.map +1 -0
- package/dist/chunk-6OQQOMZR.js +785 -0
- package/dist/chunk-6OQQOMZR.js.map +1 -0
- package/dist/{chunk-GTVB4HEM.js → chunk-AQEQX4FF.js} +344 -315
- package/dist/chunk-AQEQX4FF.js.map +1 -0
- package/dist/chunk-C7ADUNPR.js +273 -0
- package/dist/chunk-C7ADUNPR.js.map +1 -0
- package/dist/{chunk-E75IPST5.js → chunk-EFJ2NH6E.js} +12 -58
- package/dist/chunk-EFJ2NH6E.js.map +1 -0
- package/dist/{chunk-5KLVH3PY.js → chunk-HQI7NYGX.js} +5 -3
- package/dist/chunk-HQI7NYGX.js.map +1 -0
- package/dist/chunk-IME7R2UI.js +6541 -0
- package/dist/chunk-IME7R2UI.js.map +1 -0
- package/dist/chunk-J4F2QLM2.js +273 -0
- package/dist/chunk-J4F2QLM2.js.map +1 -0
- package/dist/{chunk-MAQXCUR4.js → chunk-J62VZYOB.js} +444 -112
- package/dist/chunk-J62VZYOB.js.map +1 -0
- package/dist/chunk-JTPOI3KU.js +826 -0
- package/dist/chunk-JTPOI3KU.js.map +1 -0
- package/dist/{chunk-WKBO5R4O.js → chunk-KBQS35OV.js} +2 -2
- package/dist/chunk-KBQS35OV.js.map +1 -0
- package/dist/chunk-KXAYUIKP.js +90 -0
- package/dist/chunk-KXAYUIKP.js.map +1 -0
- package/dist/{chunk-VZQXX3WW.js → chunk-QYBD22H5.js} +1 -1
- package/dist/chunk-QYBD22H5.js.map +1 -0
- package/dist/{chunk-V4BOYR52.js → chunk-RFJ6Q6LE.js} +763 -198
- package/dist/chunk-RFJ6Q6LE.js.map +1 -0
- package/dist/{chunk-7N4J557C.js → chunk-RT5SATF7.js} +1 -1
- package/dist/chunk-RT5SATF7.js.map +1 -0
- package/dist/{chunk-C7D4NJ2D.js → chunk-S4JMID4G.js} +68 -26
- package/dist/chunk-S4JMID4G.js.map +1 -0
- package/dist/{chunk-ANSJPYGI.js → chunk-SOW4WRQP.js} +3 -3
- package/dist/chunk-SOW4WRQP.js.map +1 -0
- package/dist/{chunk-J7MIZ5CA.js → chunk-TLX3H2CL.js} +518 -79
- package/dist/chunk-TLX3H2CL.js.map +1 -0
- package/dist/{chunk-DGMQEI6Y.js → chunk-VPNLCMVR.js} +22 -9
- package/dist/chunk-VPNLCMVR.js.map +1 -0
- package/dist/{chunk-BTZZUCLI.js → chunk-W3XFHEF4.js} +173 -29
- package/dist/chunk-W3XFHEF4.js.map +1 -0
- package/dist/{chunk-574C6MQR.js → chunk-X4NJT436.js} +130 -60
- package/dist/chunk-X4NJT436.js.map +1 -0
- package/dist/chunk-X66I2NQO.js +137 -0
- package/dist/chunk-X66I2NQO.js.map +1 -0
- package/dist/chunk-XHAO3K7I.js +18 -0
- package/dist/chunk-XHAO3K7I.js.map +1 -0
- package/dist/{chunk-3U5IBM24.js → chunk-Y2VHN7TC.js} +2 -2
- package/dist/chunk-Y2VHN7TC.js.map +1 -0
- package/dist/chunk-YC6W4EJH.js +947 -0
- package/dist/chunk-YC6W4EJH.js.map +1 -0
- package/dist/{chunk-24AXE6SP.js → chunk-ZPSZZUWF.js} +1 -1
- package/dist/chunk-ZPSZZUWF.js.map +1 -0
- package/dist/{command-approvals-6Y57VCWZ.js → command-approvals-VBIU6BLM.js} +6 -5
- package/dist/context-CBOBDY5Q.js +38 -0
- package/dist/{hook-approvals-CTUFKEJT.js → hook-approvals-ZVT4V7IM.js} +5 -5
- package/dist/index.js +996 -155
- package/dist/index.js.map +1 -1
- package/dist/{interactive-assets-CCAAWVKH.js → interactive-assets-T4I4SEHU.js} +13 -9
- package/dist/interactive-assets-T4I4SEHU.js.map +1 -0
- package/dist/job-runner.js +159 -111
- package/dist/job-runner.js.map +1 -1
- package/dist/job-supervisor.d.ts +2 -0
- package/dist/job-supervisor.js +112 -0
- package/dist/job-supervisor.js.map +1 -0
- package/dist/loader-S77OVTHH.js +18 -0
- package/dist/{mcp-approvals-WNPH65BK.js → mcp-approvals-HJ2VDYMC.js} +5 -5
- package/dist/{mcp-config-YIJIES2C.js → mcp-config-RNB5LEVE.js} +3 -3
- package/dist/memory-store-DZUT5HUH.js +75 -0
- package/dist/{model-window-store-YFVNXCIP.js → model-window-store-MTSQ5JDJ.js} +8 -4
- package/dist/openai-compatible-2OB54YST.js +14 -0
- package/dist/{permission-approvals-76PVREFD.js → permission-approvals-YLV6UC5W.js} +5 -5
- package/dist/permissions-DHOKF3W2.js +45 -0
- package/dist/sandbox-WRYTZH3D.js +41 -0
- package/dist/{scrollback-LUKLIBVI.js → scrollback-35ME2GFX.js} +32 -22
- package/dist/scrollback-35ME2GFX.js.map +1 -0
- package/dist/sdk.d.ts +2219 -2361
- package/dist/sdk.js +53 -38
- package/dist/sdk.js.map +1 -1
- package/dist/settings-loader-JUNPGAAM.js +35 -0
- package/dist/settings-loader-JUNPGAAM.js.map +1 -0
- package/dist/{shell-selection-V6QAYXPX.js → shell-selection-CDHGJ4GC.js} +2 -2
- package/dist/shell-selection-CDHGJ4GC.js.map +1 -0
- package/package.json +23 -22
- package/dist/app-MOXXWEOY.js.map +0 -1
- package/dist/chunk-24AXE6SP.js.map +0 -1
- package/dist/chunk-3U5IBM24.js.map +0 -1
- package/dist/chunk-574C6MQR.js.map +0 -1
- package/dist/chunk-5GDZ22YP.js.map +0 -1
- package/dist/chunk-5KLVH3PY.js.map +0 -1
- package/dist/chunk-7N4J557C.js.map +0 -1
- package/dist/chunk-A7JLBK2Y.js +0 -3506
- package/dist/chunk-A7JLBK2Y.js.map +0 -1
- package/dist/chunk-ANSJPYGI.js.map +0 -1
- package/dist/chunk-BTZZUCLI.js.map +0 -1
- package/dist/chunk-C7D4NJ2D.js.map +0 -1
- package/dist/chunk-CAGOB2N4.js.map +0 -1
- package/dist/chunk-DGMQEI6Y.js.map +0 -1
- package/dist/chunk-E75IPST5.js.map +0 -1
- package/dist/chunk-EYGD6X2T.js +0 -180
- package/dist/chunk-EYGD6X2T.js.map +0 -1
- package/dist/chunk-GTVB4HEM.js.map +0 -1
- package/dist/chunk-J7MIZ5CA.js.map +0 -1
- package/dist/chunk-KWHQ2XXL.js.map +0 -1
- package/dist/chunk-L6LK2QIQ.js +0 -657
- package/dist/chunk-L6LK2QIQ.js.map +0 -1
- package/dist/chunk-MAQXCUR4.js.map +0 -1
- package/dist/chunk-S4XL7HOM.js +0 -402
- package/dist/chunk-S4XL7HOM.js.map +0 -1
- package/dist/chunk-URLQAXTR.js.map +0 -1
- package/dist/chunk-V4BOYR52.js.map +0 -1
- package/dist/chunk-VZQXX3WW.js.map +0 -1
- package/dist/chunk-WKBO5R4O.js.map +0 -1
- package/dist/chunk-XIFFTATU.js.map +0 -1
- package/dist/context-ZC5IFEAX.js +0 -25
- package/dist/interactive-assets-CCAAWVKH.js.map +0 -1
- package/dist/loader-4IDS2RIE.js +0 -15
- package/dist/openai-compatible-RHZUBHV6.js +0 -10
- package/dist/permissions-CHCKTFQQ.js +0 -28
- package/dist/sandbox-CNVAQDA7.js +0 -23
- package/dist/scrollback-LUKLIBVI.js.map +0 -1
- package/dist/settings-loader-M2QP5TX2.js +0 -21
- package/patches/ink+6.8.0.patch +0 -13
- package/scripts/apply-ink-patch.mjs +0 -29
- package/scripts/ink-patch.mjs +0 -59
- package/scripts/verify-ink-patch.mjs +0 -11
- /package/dist/{command-approvals-6Y57VCWZ.js.map → additional-roots-H7UNAEHS.js.map} +0 -0
- /package/dist/{context-ZC5IFEAX.js.map → command-approvals-VBIU6BLM.js.map} +0 -0
- /package/dist/{hook-approvals-CTUFKEJT.js.map → context-CBOBDY5Q.js.map} +0 -0
- /package/dist/{loader-4IDS2RIE.js.map → hook-approvals-ZVT4V7IM.js.map} +0 -0
- /package/dist/{mcp-approvals-WNPH65BK.js.map → loader-S77OVTHH.js.map} +0 -0
- /package/dist/{mcp-config-YIJIES2C.js.map → mcp-approvals-HJ2VDYMC.js.map} +0 -0
- /package/dist/{model-window-store-YFVNXCIP.js.map → mcp-config-RNB5LEVE.js.map} +0 -0
- /package/dist/{openai-compatible-RHZUBHV6.js.map → memory-store-DZUT5HUH.js.map} +0 -0
- /package/dist/{permission-approvals-76PVREFD.js.map → model-window-store-MTSQ5JDJ.js.map} +0 -0
- /package/dist/{permissions-CHCKTFQQ.js.map → openai-compatible-2OB54YST.js.map} +0 -0
- /package/dist/{sandbox-CNVAQDA7.js.map → permission-approvals-YLV6UC5W.js.map} +0 -0
- /package/dist/{settings-loader-M2QP5TX2.js.map → permissions-DHOKF3W2.js.map} +0 -0
- /package/dist/{shell-selection-V6QAYXPX.js.map → sandbox-WRYTZH3D.js.map} +0 -0
|
@@ -5,57 +5,82 @@ import {
|
|
|
5
5
|
runHostReview,
|
|
6
6
|
toElicitationFields,
|
|
7
7
|
validateElicitationContent
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-RFJ6Q6LE.js";
|
|
9
9
|
import {
|
|
10
|
+
SessionRuntime,
|
|
10
11
|
buildAgentCompletionMessage,
|
|
11
12
|
buildPlanApprovalQuestion,
|
|
13
|
+
classifyAbortReason,
|
|
12
14
|
createAgentRunContext,
|
|
13
15
|
createSessionHistoryTools,
|
|
14
16
|
createTerminalOutcome,
|
|
17
|
+
finalAnswerText,
|
|
15
18
|
getOrCreateAgentManager,
|
|
19
|
+
maskAtGate,
|
|
16
20
|
normalizePersistedTodos,
|
|
17
21
|
planApprovalFromUserQuestionResponse,
|
|
18
22
|
planStopDecision,
|
|
19
|
-
|
|
23
|
+
separateInlineReasoning,
|
|
20
24
|
takeAgentCompletionBatch,
|
|
21
|
-
|
|
22
|
-
} from "./chunk-
|
|
25
|
+
uniqueAgentDisplayName
|
|
26
|
+
} from "./chunk-5YHTKWJN.js";
|
|
23
27
|
import {
|
|
28
|
+
consoleMcpDiagnosticSink,
|
|
29
|
+
resolveMcpServerList
|
|
30
|
+
} from "./chunk-Y2VHN7TC.js";
|
|
31
|
+
import {
|
|
32
|
+
TRUSTED_SETTINGS_LOCATION,
|
|
33
|
+
compactionGate,
|
|
24
34
|
createBuiltinCommandRegistry,
|
|
25
35
|
estimateUsageCost,
|
|
26
36
|
getPackageVersion,
|
|
27
37
|
planWasLost,
|
|
28
38
|
resolveContextLimit,
|
|
29
|
-
resolvePermissionMode
|
|
30
|
-
|
|
39
|
+
resolvePermissionMode,
|
|
40
|
+
usageAtGate,
|
|
41
|
+
usagePressureTokens
|
|
42
|
+
} from "./chunk-IME7R2UI.js";
|
|
43
|
+
import {
|
|
44
|
+
formatIgnoredWorkspaceSandboxKey,
|
|
45
|
+
ignoredWorkspaceSandboxKeys,
|
|
46
|
+
loadSettingsFile,
|
|
47
|
+
settingsLayerPaths,
|
|
48
|
+
workspaceLayerThatEnabledSandbox
|
|
49
|
+
} from "./chunk-W3XFHEF4.js";
|
|
50
|
+
import {
|
|
51
|
+
onAbort,
|
|
52
|
+
seedObservationLedger,
|
|
53
|
+
throwIfAborted
|
|
54
|
+
} from "./chunk-J62VZYOB.js";
|
|
31
55
|
import {
|
|
32
56
|
commandEnforcementContext,
|
|
33
57
|
discoverCommands,
|
|
34
58
|
parseSlashInput,
|
|
35
59
|
resolveCommandBody
|
|
36
|
-
} from "./chunk-
|
|
60
|
+
} from "./chunk-EFJ2NH6E.js";
|
|
37
61
|
import {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
62
|
+
buildChildEnv
|
|
63
|
+
} from "./chunk-XHAO3K7I.js";
|
|
64
|
+
import {
|
|
65
|
+
collectDeclaredDirectories,
|
|
66
|
+
partitionProjectDirectories
|
|
67
|
+
} from "./chunk-525CAZPY.js";
|
|
41
68
|
import {
|
|
42
|
-
|
|
69
|
+
getPrimaryArg,
|
|
43
70
|
toolFailure,
|
|
71
|
+
toolResultErrorMessage,
|
|
44
72
|
toolSuccess
|
|
45
|
-
} from "./chunk-
|
|
46
|
-
import {
|
|
47
|
-
loadSettingsFile
|
|
48
|
-
} from "./chunk-BTZZUCLI.js";
|
|
73
|
+
} from "./chunk-224FJZLH.js";
|
|
49
74
|
import {
|
|
50
75
|
partitionProjectAllowRules
|
|
51
|
-
} from "./chunk-
|
|
76
|
+
} from "./chunk-KBQS35OV.js";
|
|
52
77
|
import {
|
|
53
78
|
collectDeclaredHooks,
|
|
54
79
|
partitionProjectHooks
|
|
55
|
-
} from "./chunk-
|
|
80
|
+
} from "./chunk-SOW4WRQP.js";
|
|
56
81
|
import {
|
|
57
82
|
resolveBookHome
|
|
58
|
-
} from "./chunk-
|
|
83
|
+
} from "./chunk-ZPSZZUWF.js";
|
|
59
84
|
|
|
60
85
|
// src/run-status.ts
|
|
61
86
|
import {
|
|
@@ -326,7 +351,7 @@ async function resolvePrintCommand(input, env, deps = {}) {
|
|
|
326
351
|
const commands = deps.commands ?? discoverCommands(env.config.workspace);
|
|
327
352
|
const context = printBuiltinContext(env, commands);
|
|
328
353
|
const resolved = builtins.resolve(parsed.name, context);
|
|
329
|
-
if (resolved) {
|
|
354
|
+
if (resolved && !resolved.definition.tuiOnly) {
|
|
330
355
|
if (!resolved.definition.nonInteractive) {
|
|
331
356
|
throw unsupported(
|
|
332
357
|
parsed.name,
|
|
@@ -406,12 +431,17 @@ var EVENT_TYPES = /* @__PURE__ */ new Set([
|
|
|
406
431
|
"agent_message",
|
|
407
432
|
"agent_completion",
|
|
408
433
|
"agent_permission",
|
|
434
|
+
"agent_notice",
|
|
409
435
|
"evidence_update",
|
|
436
|
+
"attempt_discarded",
|
|
437
|
+
"retry",
|
|
410
438
|
"agent_apply",
|
|
411
439
|
"hook_event",
|
|
412
440
|
"mode_change",
|
|
413
441
|
"plan_approval",
|
|
442
|
+
"command_result",
|
|
414
443
|
"prompt_suggestions",
|
|
444
|
+
"notice",
|
|
415
445
|
"error",
|
|
416
446
|
"result",
|
|
417
447
|
"done"
|
|
@@ -490,6 +520,66 @@ function createStreamParser(onEvent, options = {}) {
|
|
|
490
520
|
};
|
|
491
521
|
}
|
|
492
522
|
|
|
523
|
+
// src/reader-gone.ts
|
|
524
|
+
import { Writable } from "stream";
|
|
525
|
+
var READER_GONE_CODES = /* @__PURE__ */ new Set([
|
|
526
|
+
"EPIPE",
|
|
527
|
+
"EOF",
|
|
528
|
+
"ERR_STREAM_DESTROYED",
|
|
529
|
+
"ECONNRESET"
|
|
530
|
+
]);
|
|
531
|
+
function isReaderGoneError(error) {
|
|
532
|
+
const code = error?.code;
|
|
533
|
+
return code !== void 0 && READER_GONE_CODES.has(code);
|
|
534
|
+
}
|
|
535
|
+
var READER_FLUSH_CAP_MS = 1e3;
|
|
536
|
+
function createReaderFlush(stdout, options = {}) {
|
|
537
|
+
if (!(stdout instanceof Writable)) return void 0;
|
|
538
|
+
const capMs = options.capMs ?? READER_FLUSH_CAP_MS;
|
|
539
|
+
const controller = new AbortController();
|
|
540
|
+
let pending = 0;
|
|
541
|
+
let slow = false;
|
|
542
|
+
const waiters = /* @__PURE__ */ new Set();
|
|
543
|
+
const drained = () => {
|
|
544
|
+
if (pending > 0) return;
|
|
545
|
+
slow = false;
|
|
546
|
+
for (const release of [...waiters]) release();
|
|
547
|
+
waiters.clear();
|
|
548
|
+
};
|
|
549
|
+
return {
|
|
550
|
+
signal: controller.signal,
|
|
551
|
+
write(line) {
|
|
552
|
+
pending++;
|
|
553
|
+
stdout.write(line, (error) => {
|
|
554
|
+
if (error && isReaderGoneError(error)) controller.abort();
|
|
555
|
+
pending--;
|
|
556
|
+
drained();
|
|
557
|
+
});
|
|
558
|
+
},
|
|
559
|
+
hold(signal) {
|
|
560
|
+
if (pending === 0 || slow || signal?.aborted) return Promise.resolve();
|
|
561
|
+
return new Promise((resolve) => {
|
|
562
|
+
let settled = false;
|
|
563
|
+
const finish = () => {
|
|
564
|
+
if (settled) return;
|
|
565
|
+
settled = true;
|
|
566
|
+
clearTimeout(timer);
|
|
567
|
+
signal?.removeEventListener("abort", finish);
|
|
568
|
+
waiters.delete(finish);
|
|
569
|
+
resolve();
|
|
570
|
+
};
|
|
571
|
+
const timer = setTimeout(() => {
|
|
572
|
+
slow = true;
|
|
573
|
+
finish();
|
|
574
|
+
}, capMs);
|
|
575
|
+
waiters.add(finish);
|
|
576
|
+
signal?.addEventListener("abort", finish, { once: true });
|
|
577
|
+
if (signal?.aborted) finish();
|
|
578
|
+
});
|
|
579
|
+
}
|
|
580
|
+
};
|
|
581
|
+
}
|
|
582
|
+
|
|
493
583
|
// src/headless.ts
|
|
494
584
|
function carriedCostUsd(usage, models) {
|
|
495
585
|
let worst = null;
|
|
@@ -509,17 +599,63 @@ async function readPipedPrompt(stream) {
|
|
|
509
599
|
const prompt = chunks.join("").trim();
|
|
510
600
|
return prompt || void 0;
|
|
511
601
|
}
|
|
602
|
+
function destroyOnAbort(stream, signal) {
|
|
603
|
+
if (!signal) return () => {
|
|
604
|
+
};
|
|
605
|
+
return onAbort(signal, () => {
|
|
606
|
+
const reason = signal.reason instanceof Error ? signal.reason : new Error("Aborted");
|
|
607
|
+
stream.destroy?.(reason);
|
|
608
|
+
});
|
|
609
|
+
}
|
|
610
|
+
async function raceAbort(promise, signal) {
|
|
611
|
+
if (!signal) {
|
|
612
|
+
await promise;
|
|
613
|
+
return true;
|
|
614
|
+
}
|
|
615
|
+
if (signal.aborted) return false;
|
|
616
|
+
let release;
|
|
617
|
+
const aborted = new Promise((resolve) => {
|
|
618
|
+
release = onAbort(signal, () => resolve(false));
|
|
619
|
+
});
|
|
620
|
+
try {
|
|
621
|
+
return await Promise.race([promise.then(() => true), aborted]);
|
|
622
|
+
} finally {
|
|
623
|
+
release?.();
|
|
624
|
+
}
|
|
625
|
+
}
|
|
512
626
|
async function runHeadless(config, registry, opts) {
|
|
513
627
|
const mode = resolvePermissionMode(config.settings, opts.mode);
|
|
514
628
|
const stdout = opts.stdout ?? process.stdout;
|
|
629
|
+
const flush = opts.outputFormat === "stream-json" ? createReaderFlush(stdout) : void 0;
|
|
630
|
+
const runSignal = flush && opts.signal ? AbortSignal.any([opts.signal, flush.signal]) : flush?.signal ?? opts.signal;
|
|
515
631
|
const emit = (obj) => {
|
|
516
632
|
if (obj && typeof obj === "object" && "type" in obj) {
|
|
517
633
|
opts.onEvent?.(obj);
|
|
518
634
|
}
|
|
519
|
-
|
|
635
|
+
const line = JSON.stringify(obj) + "\n";
|
|
636
|
+
if (flush) flush.write(line);
|
|
637
|
+
else stdout.write(line);
|
|
638
|
+
};
|
|
639
|
+
const eventState = {
|
|
640
|
+
childLabels: /* @__PURE__ */ new Map(),
|
|
641
|
+
errorReported: false
|
|
520
642
|
};
|
|
521
643
|
let disposeCrashHandlers;
|
|
522
|
-
|
|
644
|
+
let sessionDisposed = false;
|
|
645
|
+
let startedSessionId;
|
|
646
|
+
const agentSession = new AgentSession({
|
|
647
|
+
// The transcript is the rendered conversation and still carries the tool
|
|
648
|
+
// records compaction summarized out of the context history, so prefer it
|
|
649
|
+
// when a resume supplies both.
|
|
650
|
+
runtime: new SessionRuntime({
|
|
651
|
+
history: opts.transcript?.length ? opts.transcript : opts.history
|
|
652
|
+
})
|
|
653
|
+
});
|
|
654
|
+
const disposeSession = () => {
|
|
655
|
+
if (sessionDisposed) return;
|
|
656
|
+
sessionDisposed = true;
|
|
657
|
+
agentSession.dispose("headless_complete");
|
|
658
|
+
};
|
|
523
659
|
const runtime = agentSession.getRuntime();
|
|
524
660
|
runtime.todos.push(...normalizePersistedTodos(opts.plan?.todos));
|
|
525
661
|
if (opts.plan?.tasks?.length) runtime.tasks.push(...opts.plan.tasks);
|
|
@@ -544,7 +680,11 @@ async function runHeadless(config, registry, opts) {
|
|
|
544
680
|
let sessionName = opts.sessionName;
|
|
545
681
|
let sessionCreated = opts.sessionCreated === true;
|
|
546
682
|
if (store && !sessionId) {
|
|
547
|
-
sessionId = store.create({
|
|
683
|
+
sessionId = store.create({
|
|
684
|
+
cwd: config.workspace,
|
|
685
|
+
name: sessionName,
|
|
686
|
+
driver: config.sessionDriver
|
|
687
|
+
});
|
|
548
688
|
sessionCreated = true;
|
|
549
689
|
}
|
|
550
690
|
if (store && sessionId && !sessionName) {
|
|
@@ -567,6 +707,7 @@ async function runHeadless(config, registry, opts) {
|
|
|
567
707
|
onHookEvent: createHookEventHandler(opts, emit)
|
|
568
708
|
}
|
|
569
709
|
);
|
|
710
|
+
startedSessionId = sessionId;
|
|
570
711
|
}
|
|
571
712
|
const runtimeSessionId = sessionId ?? crypto.randomUUID();
|
|
572
713
|
const statusWriter = new RunStatusWriter({
|
|
@@ -577,7 +718,10 @@ async function runHeadless(config, registry, opts) {
|
|
|
577
718
|
startedAt: Date.now()
|
|
578
719
|
});
|
|
579
720
|
disposeCrashHandlers = statusWriter.installCrashHandlers();
|
|
580
|
-
const permissionRequired = async () =>
|
|
721
|
+
const permissionRequired = async () => ({
|
|
722
|
+
result: "deny",
|
|
723
|
+
reason: "no_approver"
|
|
724
|
+
});
|
|
581
725
|
const userQuestionStatus = opts.onUserQuestionRequired ? "pending" : "unavailable";
|
|
582
726
|
const askUser = async (request, context) => {
|
|
583
727
|
try {
|
|
@@ -596,14 +740,26 @@ async function runHeadless(config, registry, opts) {
|
|
|
596
740
|
if (mode === "bypassPermissions") return "approve";
|
|
597
741
|
if (!opts.onUserQuestionRequired) return planStopDecision("approval_unavailable");
|
|
598
742
|
const request = buildPlanApprovalQuestion(plan, crypto.randomUUID());
|
|
599
|
-
emitAgentEvent(
|
|
600
|
-
|
|
601
|
-
|
|
743
|
+
emitAgentEvent(
|
|
744
|
+
{ type: "user_question", request, status: userQuestionStatus },
|
|
745
|
+
opts,
|
|
746
|
+
emit,
|
|
747
|
+
eventState
|
|
748
|
+
);
|
|
749
|
+
const response = await askUser(request, { signal: runSignal });
|
|
750
|
+
emitAgentEvent(
|
|
751
|
+
{ type: "user_question_result", requestId: request.id, response },
|
|
752
|
+
opts,
|
|
753
|
+
emit,
|
|
754
|
+
eventState
|
|
755
|
+
);
|
|
602
756
|
return planApprovalFromUserQuestionResponse(request, response);
|
|
603
757
|
};
|
|
604
758
|
const planNotApplied = {};
|
|
605
759
|
let lastUsage = null;
|
|
606
760
|
let lastOutcome = null;
|
|
761
|
+
let mayReclassify = true;
|
|
762
|
+
let finalRunOpeningId;
|
|
607
763
|
const runResults = [];
|
|
608
764
|
const recordRunResult = (result2) => {
|
|
609
765
|
const existing = runResults.findIndex(
|
|
@@ -668,15 +824,7 @@ async function runHeadless(config, registry, opts) {
|
|
|
668
824
|
const contextHistory = [...opts.history];
|
|
669
825
|
const transcript = [...opts.transcript ?? opts.history];
|
|
670
826
|
const compactBoundaries = [...opts.compactBoundaries ?? []];
|
|
671
|
-
|
|
672
|
-
for (const observation of message.fileObservations ?? []) {
|
|
673
|
-
const key = observationKey(observation.workspaceId, observation.path);
|
|
674
|
-
const current = runtime.fileObservationLedger.get(key);
|
|
675
|
-
if (!current || current.timestamp <= observation.timestamp) {
|
|
676
|
-
runtime.fileObservationLedger.set(key, observation);
|
|
677
|
-
}
|
|
678
|
-
}
|
|
679
|
-
}
|
|
827
|
+
seedObservationLedger(runtime.fileObservationLedger, transcript);
|
|
680
828
|
const loopConfig = {
|
|
681
829
|
...config,
|
|
682
830
|
maxTurns: opts.maxTurns ?? config.maxTurns
|
|
@@ -688,7 +836,7 @@ async function runHeadless(config, registry, opts) {
|
|
|
688
836
|
lastOutcome = event.outcome;
|
|
689
837
|
onOutcome(event.outcome);
|
|
690
838
|
}
|
|
691
|
-
emitAgentEvent(event, opts, emit);
|
|
839
|
+
emitAgentEvent(event, opts, emit, eventState);
|
|
692
840
|
},
|
|
693
841
|
// No header rewrite at turn boundaries: load() derives updatedAt from
|
|
694
842
|
// the last appended record, so the on-disk header is never authoritative.
|
|
@@ -702,6 +850,15 @@ async function runHeadless(config, registry, opts) {
|
|
|
702
850
|
emit({ type: "mode_change", mode: newMode });
|
|
703
851
|
}
|
|
704
852
|
},
|
|
853
|
+
/**
|
|
854
|
+
* The hold at a tool boundary. The `tool_use` records for the calls about
|
|
855
|
+
* to run have been written by now, so waiting for them to be taken is
|
|
856
|
+
* what turns a reader that has gone into an abort BEFORE the call runs
|
|
857
|
+
* (#340) instead of one call later. Free when the reader has caught up,
|
|
858
|
+
* bounded when it has not, and absent — so a no-op — for every other
|
|
859
|
+
* output format and for a host sink with no callback to answer.
|
|
860
|
+
*/
|
|
861
|
+
beforeToolExecution: flush ? () => flush.hold(runSignal) : void 0,
|
|
705
862
|
onPlanApprovalRequired: async (plan) => {
|
|
706
863
|
const decision = await decidePlanApproval(plan);
|
|
707
864
|
if (typeof decision === "object" && decision.decision === "stop") {
|
|
@@ -719,6 +876,24 @@ async function runHeadless(config, registry, opts) {
|
|
|
719
876
|
},
|
|
720
877
|
onUserQuestionRequired: askUser,
|
|
721
878
|
userQuestionStatus,
|
|
879
|
+
onRetry: (phase, attempt, max, delayMs, reason) => {
|
|
880
|
+
const limit = max < 0 ? null : max;
|
|
881
|
+
if (opts.outputFormat === "stream-json") {
|
|
882
|
+
emit({
|
|
883
|
+
type: "retry",
|
|
884
|
+
phase,
|
|
885
|
+
attempt,
|
|
886
|
+
max: limit,
|
|
887
|
+
delay_ms: delayMs,
|
|
888
|
+
...reason ? { reason } : {}
|
|
889
|
+
});
|
|
890
|
+
} else if (opts.quiet !== true) {
|
|
891
|
+
process.stderr.write(
|
|
892
|
+
`retry: ${phase} attempt ${attempt}${limit !== null ? `/${limit}` : ""} in ${Math.round(delayMs / 1e3)}s${reason ? ` (${reason})` : ""}
|
|
893
|
+
`
|
|
894
|
+
);
|
|
895
|
+
}
|
|
896
|
+
},
|
|
722
897
|
onHookEvent: createHookEventHandler(opts, emit),
|
|
723
898
|
onUsage: (nextUsage) => {
|
|
724
899
|
lastUsage = nextUsage;
|
|
@@ -739,7 +914,50 @@ async function runHeadless(config, registry, opts) {
|
|
|
739
914
|
...hints,
|
|
740
915
|
trigger: "auto",
|
|
741
916
|
preContextTokens: usage ? usagePressureTokens(usage) : void 0,
|
|
742
|
-
signal:
|
|
917
|
+
signal: runSignal,
|
|
918
|
+
onHookEvent: createHookEventHandler(opts, emit)
|
|
919
|
+
}
|
|
920
|
+
});
|
|
921
|
+
if (outcome2.result.status === "compacted") {
|
|
922
|
+
emitCompactBoundary(opts.outputFormat, emit, outcome2.result);
|
|
923
|
+
lastUsage = null;
|
|
924
|
+
}
|
|
925
|
+
return outcome2.result;
|
|
926
|
+
},
|
|
927
|
+
// Deferred compaction (plans/async-compaction-plan.md): same plumbing,
|
|
928
|
+
// reducer on the snapshot first, judge and record at the next boundary.
|
|
929
|
+
prepareCompact: async (snapshot, usage, hints) => {
|
|
930
|
+
const outcome2 = await agentSession.prepareCompact({
|
|
931
|
+
config,
|
|
932
|
+
history: snapshot,
|
|
933
|
+
compactBoundaries,
|
|
934
|
+
sessionId,
|
|
935
|
+
transcriptOrdinal: transcript.length,
|
|
936
|
+
runContext,
|
|
937
|
+
runtime,
|
|
938
|
+
options: {
|
|
939
|
+
...hints,
|
|
940
|
+
trigger: "auto",
|
|
941
|
+
preContextTokens: usage ? usagePressureTokens(usage) : void 0,
|
|
942
|
+
signal: hints?.signal ?? runSignal,
|
|
943
|
+
onHookEvent: createHookEventHandler(opts, emit)
|
|
944
|
+
}
|
|
945
|
+
});
|
|
946
|
+
return outcome2.status === "prepared" ? outcome2.prepared : outcome2.result;
|
|
947
|
+
},
|
|
948
|
+
commitCompact: async (prepared, history, hints) => {
|
|
949
|
+
const outcome2 = await agentSession.commitCompact({
|
|
950
|
+
prepared,
|
|
951
|
+
history,
|
|
952
|
+
config,
|
|
953
|
+
sessionId,
|
|
954
|
+
transcriptOrdinal: transcript.length,
|
|
955
|
+
runContext,
|
|
956
|
+
runtime,
|
|
957
|
+
timelineStore: store,
|
|
958
|
+
onCommitted: (_result, boundary) => compactBoundaries.push(boundary),
|
|
959
|
+
options: {
|
|
960
|
+
signal: hints?.signal ?? runSignal,
|
|
743
961
|
onHookEvent: createHookEventHandler(opts, emit)
|
|
744
962
|
}
|
|
745
963
|
});
|
|
@@ -752,12 +970,19 @@ async function runHeadless(config, registry, opts) {
|
|
|
752
970
|
onAssistantMessageComplete: (message) => {
|
|
753
971
|
if (!transcript.some((item) => item.id === message.id)) transcript.push(message);
|
|
754
972
|
if (opts.outputFormat === "stream-json" && message.content) {
|
|
755
|
-
emit({
|
|
973
|
+
emit({
|
|
974
|
+
type: "assistant",
|
|
975
|
+
text: message.content,
|
|
976
|
+
complete: true,
|
|
977
|
+
...message.hostNotice ? { host_notice: true } : {}
|
|
978
|
+
});
|
|
756
979
|
}
|
|
757
980
|
}
|
|
758
981
|
});
|
|
759
982
|
const runParentTurn = async (prompt, displayMessage, userMessage, runContext, commandContext) => {
|
|
983
|
+
eventState.errorReported = false;
|
|
760
984
|
let runOutcome;
|
|
985
|
+
finalRunOpeningId = userMessage.id;
|
|
761
986
|
lastUsage = null;
|
|
762
987
|
let updated;
|
|
763
988
|
try {
|
|
@@ -770,7 +995,7 @@ async function runHeadless(config, registry, opts) {
|
|
|
770
995
|
mode,
|
|
771
996
|
sessionId: runtimeSessionId,
|
|
772
997
|
timelineStore: store,
|
|
773
|
-
signal:
|
|
998
|
+
signal: runSignal,
|
|
774
999
|
callbacks: createRunCallbacks(runContext, (outcome2) => {
|
|
775
1000
|
runOutcome = outcome2;
|
|
776
1001
|
}),
|
|
@@ -783,6 +1008,9 @@ async function runHeadless(config, registry, opts) {
|
|
|
783
1008
|
userFileObservations: userMessage.fileObservations,
|
|
784
1009
|
userAttachments: userMessage.attachments,
|
|
785
1010
|
userMessageKind: userMessage.kind,
|
|
1011
|
+
// The answer walk is bounded by `openingMessageId`, so a derived opening
|
|
1012
|
+
// message no longer lets it walk past the run.
|
|
1013
|
+
userMessageDerived: userMessage.derivedContent,
|
|
786
1014
|
manageSessionHooks: sessionId ? false : void 0,
|
|
787
1015
|
// Completion messages are host-generated notifications, not user
|
|
788
1016
|
// prompts. Keep UserPromptSubmit hooks from rewriting or blocking them.
|
|
@@ -832,7 +1060,7 @@ async function runHeadless(config, registry, opts) {
|
|
|
832
1060
|
config,
|
|
833
1061
|
mode,
|
|
834
1062
|
sessionId: runtimeSessionId,
|
|
835
|
-
signal:
|
|
1063
|
+
signal: runSignal,
|
|
836
1064
|
agents: {
|
|
837
1065
|
// Built only when an agent-backed command (`/review`) asks for it,
|
|
838
1066
|
// and attached to this session's runtime so it is disposed with the
|
|
@@ -843,7 +1071,7 @@ async function runHeadless(config, registry, opts) {
|
|
|
843
1071
|
permissionMode: mode,
|
|
844
1072
|
eventSink: (event) => {
|
|
845
1073
|
if (event.type === "agent_result") recordManagedRunResult(event.agent);
|
|
846
|
-
emitAgentEvent(event, opts, emit);
|
|
1074
|
+
emitAgentEvent(event, opts, emit, eventState);
|
|
847
1075
|
},
|
|
848
1076
|
hookEventSink: createHookEventHandler(opts, emit)
|
|
849
1077
|
}),
|
|
@@ -854,7 +1082,17 @@ async function runHeadless(config, registry, opts) {
|
|
|
854
1082
|
let commandUsage = null;
|
|
855
1083
|
const prompts = [];
|
|
856
1084
|
if (opts.inputFormat === "text") {
|
|
857
|
-
|
|
1085
|
+
let prompt = opts.prompt;
|
|
1086
|
+
if (prompt === void 0) {
|
|
1087
|
+
const stream = opts.stdin ?? process.stdin;
|
|
1088
|
+
const release = destroyOnAbort(stream, runSignal);
|
|
1089
|
+
try {
|
|
1090
|
+
prompt = await readPipedPrompt(stream);
|
|
1091
|
+
} finally {
|
|
1092
|
+
release();
|
|
1093
|
+
}
|
|
1094
|
+
throwIfAborted(runSignal);
|
|
1095
|
+
}
|
|
858
1096
|
if (!prompt) {
|
|
859
1097
|
throw new Error(
|
|
860
1098
|
"print mode requires a prompt: pass one to -p, pipe one on stdin, or use --input-format stream-json"
|
|
@@ -873,9 +1111,15 @@ async function runHeadless(config, registry, opts) {
|
|
|
873
1111
|
onDiagnostic: (diagnostic) => diagnostics.push(diagnostic)
|
|
874
1112
|
}
|
|
875
1113
|
);
|
|
876
|
-
|
|
877
|
-
|
|
1114
|
+
const release = destroyOnAbort(stream, runSignal);
|
|
1115
|
+
try {
|
|
1116
|
+
for await (const chunk of stream) {
|
|
1117
|
+
parser.feed(chunk);
|
|
1118
|
+
}
|
|
1119
|
+
} finally {
|
|
1120
|
+
release();
|
|
878
1121
|
}
|
|
1122
|
+
throwIfAborted(runSignal);
|
|
879
1123
|
parser.flush();
|
|
880
1124
|
if (diagnostics.length > 0) throw new Error(diagnostics[0]?.message);
|
|
881
1125
|
if (opts.prompt) prompts.unshift(opts.prompt);
|
|
@@ -896,6 +1140,9 @@ async function runHeadless(config, registry, opts) {
|
|
|
896
1140
|
resumedFromRunId: runResults.length === 0 ? opts.resumedFromRunId ?? (opts.history.length > 0 ? [...opts.history].reverse().find((message) => message.role === "user")?.id : void 0) : void 0
|
|
897
1141
|
});
|
|
898
1142
|
runtime.runAccounting.startRoot(runContext, opts.maxBudgetUsd);
|
|
1143
|
+
if (store && sessionId) {
|
|
1144
|
+
agentSession.trackRunUsage(runContext.rootRunId, { sessionId, timelineStore: store });
|
|
1145
|
+
}
|
|
899
1146
|
if (carriedSpend) {
|
|
900
1147
|
runtime.runAccounting.seedRoot(runContext.rootRunId, carriedSpend);
|
|
901
1148
|
}
|
|
@@ -932,6 +1179,12 @@ async function runHeadless(config, registry, opts) {
|
|
|
932
1179
|
} else if (opts.outputFormat === "stream-json" && (dispatch.output !== void 0 || dispatch.data !== void 0)) {
|
|
933
1180
|
emit({ type: "command_result", ...handled });
|
|
934
1181
|
}
|
|
1182
|
+
carriedSpend = {
|
|
1183
|
+
usage: accounting.inclusiveUsage,
|
|
1184
|
+
costUsd: accounting.inclusiveCostUsd,
|
|
1185
|
+
persistedUsage: runtime.runAccounting.persistedUsage(runContext.rootRunId),
|
|
1186
|
+
fromRootRunId: runContext.rootRunId
|
|
1187
|
+
};
|
|
935
1188
|
continue;
|
|
936
1189
|
}
|
|
937
1190
|
const prompt = dispatch.prompt;
|
|
@@ -948,9 +1201,19 @@ async function runHeadless(config, registry, opts) {
|
|
|
948
1201
|
kind: "conversation",
|
|
949
1202
|
timestamp: Date.now()
|
|
950
1203
|
};
|
|
1204
|
+
if (config.autoCompactEnabled !== false && usageAtGate(lastUsage, config)) {
|
|
1205
|
+
const pressure = usagePressureTokens(lastUsage);
|
|
1206
|
+
const gate = compactionGate(config);
|
|
1207
|
+
const masked = maskAtGate(contextHistory, gate, pressure, gate);
|
|
1208
|
+
if (masked.maskedCount > 0) {
|
|
1209
|
+
contextHistory.length = 0;
|
|
1210
|
+
contextHistory.push(...masked.history);
|
|
1211
|
+
if (masked.underLine) lastUsage = null;
|
|
1212
|
+
}
|
|
1213
|
+
}
|
|
951
1214
|
const contextLimit = resolveContextLimit(config);
|
|
952
1215
|
const hostCompactAttemptKey = `${usagePressureTokens(lastUsage)}:${contextHistory.length}`;
|
|
953
|
-
if (config.autoCompactEnabled !== false && contextLimit != null &&
|
|
1216
|
+
if (config.autoCompactEnabled !== false && contextLimit != null && usageAtGate(lastUsage, config) && lastHostCompactAttemptKey !== hostCompactAttemptKey) {
|
|
954
1217
|
lastHostCompactAttemptKey = hostCompactAttemptKey;
|
|
955
1218
|
try {
|
|
956
1219
|
const outcome2 = await agentSession.compact({
|
|
@@ -971,7 +1234,7 @@ async function runHeadless(config, registry, opts) {
|
|
|
971
1234
|
options: {
|
|
972
1235
|
trigger: "auto",
|
|
973
1236
|
preContextTokens: usagePressureTokens(lastUsage),
|
|
974
|
-
signal:
|
|
1237
|
+
signal: runSignal,
|
|
975
1238
|
upcomingUserIntent: prompt,
|
|
976
1239
|
onHookEvent: createHookEventHandler(opts, emit)
|
|
977
1240
|
}
|
|
@@ -991,7 +1254,7 @@ async function runHeadless(config, registry, opts) {
|
|
|
991
1254
|
timelineStore: store && sessionId ? store : void 0,
|
|
992
1255
|
expandShellInput: false,
|
|
993
1256
|
runtime,
|
|
994
|
-
signal:
|
|
1257
|
+
signal: runSignal
|
|
995
1258
|
});
|
|
996
1259
|
sessionName = recorded.sessionName;
|
|
997
1260
|
const contextMessage = recorded.contextMessage;
|
|
@@ -1001,7 +1264,12 @@ async function runHeadless(config, registry, opts) {
|
|
|
1001
1264
|
const spentSoFar = runtime.runAccounting.snapshotRoot(runContext.rootRunId);
|
|
1002
1265
|
carriedSpend = {
|
|
1003
1266
|
usage: spentSoFar.inclusiveUsage,
|
|
1004
|
-
costUsd: spentSoFar.inclusiveCostUsd
|
|
1267
|
+
costUsd: spentSoFar.inclusiveCostUsd,
|
|
1268
|
+
persistedUsage: runtime.runAccounting.persistedUsage(runContext.rootRunId),
|
|
1269
|
+
// Naming the root the totals came from: the next root's first record
|
|
1270
|
+
// carries this one's remainder, so this one is closed rather than left to
|
|
1271
|
+
// write the same tokens a second time at the end of the session (#336).
|
|
1272
|
+
fromRootRunId: runContext.rootRunId
|
|
1005
1273
|
};
|
|
1006
1274
|
if (planNotApplied.outcome) break;
|
|
1007
1275
|
}
|
|
@@ -1013,7 +1281,11 @@ async function runHeadless(config, registry, opts) {
|
|
|
1013
1281
|
)
|
|
1014
1282
|
);
|
|
1015
1283
|
while (true) {
|
|
1016
|
-
await managedAgentManager.waitForIdle();
|
|
1284
|
+
const idle = !runSignal?.aborted && await raceAbort(managedAgentManager.waitForIdle(), runSignal);
|
|
1285
|
+
if (!idle) {
|
|
1286
|
+
if (mayReclassify) lastOutcome = classifyAbortReason(runSignal?.reason, true);
|
|
1287
|
+
break;
|
|
1288
|
+
}
|
|
1017
1289
|
const pending = (await managedAgentManager.listPendingCompletions()).filter(
|
|
1018
1290
|
(notification) => notification.parentSessionId === runtimeSessionId
|
|
1019
1291
|
);
|
|
@@ -1055,7 +1327,7 @@ async function runHeadless(config, registry, opts) {
|
|
|
1055
1327
|
timelineStore: store && sessionId ? store : void 0,
|
|
1056
1328
|
expandShellInput: false,
|
|
1057
1329
|
runtime,
|
|
1058
|
-
signal:
|
|
1330
|
+
signal: runSignal
|
|
1059
1331
|
});
|
|
1060
1332
|
transcript.push(userMessage);
|
|
1061
1333
|
const parentContext = batch[0]?.rootRunId ? rootContexts.get(batch[0].rootRunId) : void 0;
|
|
@@ -1079,6 +1351,10 @@ async function runHeadless(config, registry, opts) {
|
|
|
1079
1351
|
}));
|
|
1080
1352
|
runResults.sort((left, right) => left.context.startedAt - right.context.startedAt);
|
|
1081
1353
|
const reportedUsage = lastUsage ?? commandUsage;
|
|
1354
|
+
mayReclassify = lastOutcome?.status !== "failed";
|
|
1355
|
+
const lastText = finalRunOpeningId === void 0 ? "" : finalAnswerText(contextHistory, finalRunOpeningId);
|
|
1356
|
+
const inline = separateInlineReasoning(lastText);
|
|
1357
|
+
const answer = inline.found ? inline.content.trimEnd() : lastText;
|
|
1082
1358
|
const result = {
|
|
1083
1359
|
messages: contextHistory,
|
|
1084
1360
|
transcript,
|
|
@@ -1089,12 +1365,12 @@ async function runHeadless(config, registry, opts) {
|
|
|
1089
1365
|
runs: runResults,
|
|
1090
1366
|
sessionId,
|
|
1091
1367
|
plan: planNotApplied.outcome,
|
|
1092
|
-
commandResults
|
|
1368
|
+
commandResults,
|
|
1369
|
+
answer
|
|
1093
1370
|
};
|
|
1094
1371
|
if (opts.jsonSchema) {
|
|
1095
|
-
const text = lastAssistantText(contextHistory);
|
|
1096
1372
|
try {
|
|
1097
|
-
result.structured = JSON.parse(
|
|
1373
|
+
result.structured = JSON.parse(answer);
|
|
1098
1374
|
} catch {
|
|
1099
1375
|
result.structuredError = "Failed to parse JSON from assistant output";
|
|
1100
1376
|
}
|
|
@@ -1105,7 +1381,7 @@ async function runHeadless(config, registry, opts) {
|
|
|
1105
1381
|
config,
|
|
1106
1382
|
registry,
|
|
1107
1383
|
contextHistory,
|
|
1108
|
-
|
|
1384
|
+
runSignal,
|
|
1109
1385
|
store?.readImageAttachment ? (attachment) => store.readImageAttachment(runtimeSessionId, attachment) : void 0
|
|
1110
1386
|
);
|
|
1111
1387
|
if (suggestions.length > 0) {
|
|
@@ -1122,8 +1398,7 @@ async function runHeadless(config, registry, opts) {
|
|
|
1122
1398
|
${stopped.message}
|
|
1123
1399
|
`);
|
|
1124
1400
|
} else {
|
|
1125
|
-
|
|
1126
|
-
if (last) stdout.write(last + "\n");
|
|
1401
|
+
if (answer) stdout.write(answer + "\n");
|
|
1127
1402
|
}
|
|
1128
1403
|
} else if (opts.outputFormat === "json") {
|
|
1129
1404
|
emit({
|
|
@@ -1137,15 +1412,25 @@ ${stopped.message}
|
|
|
1137
1412
|
structured: result.structured,
|
|
1138
1413
|
structuredError: result.structuredError,
|
|
1139
1414
|
plan: result.plan,
|
|
1140
|
-
commandResults
|
|
1415
|
+
commandResults,
|
|
1416
|
+
answer
|
|
1141
1417
|
}
|
|
1142
1418
|
});
|
|
1143
1419
|
} else if (opts.outputFormat === "stream-json") {
|
|
1144
1420
|
emit({
|
|
1145
1421
|
type: "result",
|
|
1422
|
+
// At the top level, beside `stopReason`, because that is where a
|
|
1423
|
+
// supervised loop reads the outcome from: `jq -e 'select(.type=="result")
|
|
1424
|
+
// | .outcome.reason=="objective_complete"'` was written against this
|
|
1425
|
+
// record and read a null. `result.outcome` stays for hosts already
|
|
1426
|
+
// reading it there.
|
|
1146
1427
|
stopReason: outcome.reason,
|
|
1428
|
+
outcome,
|
|
1147
1429
|
result: {
|
|
1148
|
-
|
|
1430
|
+
// Off by default: a long run's conversation is half a megabyte of JSONL
|
|
1431
|
+
// on one line, which every reader has to buffer before it can look at
|
|
1432
|
+
// the field it came for. `--include-result-messages` asks for it back.
|
|
1433
|
+
...opts.includeResultMessages ? { messages: contextHistory } : {},
|
|
1149
1434
|
usage: reportedUsage,
|
|
1150
1435
|
accounting: result.accounting,
|
|
1151
1436
|
outcome,
|
|
@@ -1154,27 +1439,41 @@ ${stopped.message}
|
|
|
1154
1439
|
structured: result.structured,
|
|
1155
1440
|
structuredError: result.structuredError,
|
|
1156
1441
|
plan: result.plan,
|
|
1157
|
-
commandResults
|
|
1442
|
+
commandResults,
|
|
1443
|
+
answer
|
|
1158
1444
|
}
|
|
1159
1445
|
});
|
|
1160
1446
|
}
|
|
1447
|
+
if (opts.outputFormat !== "stream-json" && opts.runSource !== "sdk" && outcome.status === "failed" && outcome.message && !answer && !planNotApplied.outcome && !eventState.errorReported) {
|
|
1448
|
+
process.stderr.write(`error: ${outcome.message}
|
|
1449
|
+
`);
|
|
1450
|
+
}
|
|
1161
1451
|
if (sessionId) {
|
|
1162
|
-
|
|
1163
|
-
|
|
1452
|
+
const sessionEndReason = outcome.status === "completed" ? "completion" : outcome.status === "failed" ? "error" : outcome.status === "cancelled" || runSignal?.aborted ? "aborted" : "completion";
|
|
1453
|
+
disposeSession();
|
|
1454
|
+
await agentSession.endLifecycle(config, sessionId, sessionEndReason, {
|
|
1455
|
+
onHookEvent: createHookEventHandler(opts, emit),
|
|
1456
|
+
endOutcome: outcome
|
|
1164
1457
|
});
|
|
1165
1458
|
}
|
|
1166
1459
|
return result;
|
|
1460
|
+
} catch (error) {
|
|
1461
|
+
if (startedSessionId) {
|
|
1462
|
+
disposeSession();
|
|
1463
|
+
await agentSession.endLifecycle(config, startedSessionId, runSignal?.aborted ? "aborted" : "error", {
|
|
1464
|
+
onHookEvent: createHookEventHandler(opts, emit),
|
|
1465
|
+
endOutcome: runSignal?.aborted ? classifyAbortReason(runSignal.reason, false) : createTerminalOutcome("failed", "runtime_error", { partialOutput: false })
|
|
1466
|
+
}).catch((hookError) => {
|
|
1467
|
+
console.warn(
|
|
1468
|
+
`SessionEnd hook failed: ${hookError instanceof Error ? hookError.message : String(hookError)}`
|
|
1469
|
+
);
|
|
1470
|
+
});
|
|
1471
|
+
}
|
|
1472
|
+
throw error;
|
|
1167
1473
|
} finally {
|
|
1168
1474
|
disposeCrashHandlers?.();
|
|
1169
|
-
|
|
1170
|
-
}
|
|
1171
|
-
}
|
|
1172
|
-
function lastAssistantText(history) {
|
|
1173
|
-
for (let i = history.length - 1; i >= 0; i--) {
|
|
1174
|
-
const m = history[i];
|
|
1175
|
-
if (m.role === "assistant" && m.content) return m.content;
|
|
1475
|
+
disposeSession();
|
|
1176
1476
|
}
|
|
1177
|
-
return "";
|
|
1178
1477
|
}
|
|
1179
1478
|
function planApprovalStatus(decision) {
|
|
1180
1479
|
return typeof decision === "string" ? decision : decision.decision;
|
|
@@ -1183,16 +1482,34 @@ function createHookEventHandler(opts, emit) {
|
|
|
1183
1482
|
if (!opts.includeHookEvents || opts.outputFormat !== "stream-json") return void 0;
|
|
1184
1483
|
return (event, payload) => emit({ type: "hook_event", event, ...payload });
|
|
1185
1484
|
}
|
|
1186
|
-
function emitAgentEvent(event, opts, emit) {
|
|
1485
|
+
function emitAgentEvent(event, opts, emit, state) {
|
|
1187
1486
|
if (event.type === "terminal") return;
|
|
1188
1487
|
if (event.type === "agent_text_delta" && opts.forwardSubagentText !== true) return;
|
|
1189
1488
|
opts.onAgentEvent?.(event);
|
|
1489
|
+
if (event.type === "notice") {
|
|
1490
|
+
if (opts.outputFormat === "stream-json") emit({ type: "notice", message: event.message });
|
|
1491
|
+
else console.warn(event.message);
|
|
1492
|
+
return;
|
|
1493
|
+
}
|
|
1494
|
+
if (event.type === "agent_notice") {
|
|
1495
|
+
if (opts.outputFormat === "stream-json") {
|
|
1496
|
+
emit({ type: "agent_notice", agentId: event.agentId, message: event.message });
|
|
1497
|
+
} else {
|
|
1498
|
+
process.stderr.write(`notice: ${progressLine(event.message, PROGRESS_NOTICE_MAX)}
|
|
1499
|
+
`);
|
|
1500
|
+
}
|
|
1501
|
+
return;
|
|
1502
|
+
}
|
|
1190
1503
|
if (event.type === "error") {
|
|
1504
|
+
state.errorReported = true;
|
|
1191
1505
|
if (opts.outputFormat === "stream-json") emit({ type: "error", error: event.error });
|
|
1192
1506
|
else process.stderr.write(`error: ${event.error}
|
|
1193
1507
|
`);
|
|
1194
1508
|
return;
|
|
1195
1509
|
}
|
|
1510
|
+
if (opts.outputFormat === "text" && opts.quiet !== true) {
|
|
1511
|
+
writeTextProgress(event, opts, state);
|
|
1512
|
+
}
|
|
1196
1513
|
if (opts.outputFormat !== "stream-json") return;
|
|
1197
1514
|
switch (event.type) {
|
|
1198
1515
|
case "run_started":
|
|
@@ -1249,6 +1566,67 @@ function emitAgentEvent(event, opts, emit) {
|
|
|
1249
1566
|
break;
|
|
1250
1567
|
}
|
|
1251
1568
|
}
|
|
1569
|
+
var PROGRESS_ARG_MAX = 120;
|
|
1570
|
+
var PROGRESS_ERROR_MAX = 160;
|
|
1571
|
+
var PROGRESS_NOTICE_MAX = 400;
|
|
1572
|
+
function progressLine(text, max) {
|
|
1573
|
+
const first = text.trim().split(/\r?\n/, 1)[0];
|
|
1574
|
+
const printable = Array.from(first, (char) => {
|
|
1575
|
+
const code = char.codePointAt(0) ?? 0;
|
|
1576
|
+
const control = code < 32 || code >= 127 && code <= 159 || code === 1564 || code === 8206 || code === 8207 || code >= 8232 && code <= 8238 || code >= 8294 && code <= 8297;
|
|
1577
|
+
return control ? " " : char;
|
|
1578
|
+
}).join("").trimEnd();
|
|
1579
|
+
const chars = Array.from(printable);
|
|
1580
|
+
return chars.length > max ? `${chars.slice(0, max - 1).join("")}\u2026` : printable;
|
|
1581
|
+
}
|
|
1582
|
+
function writeTextProgress(event, opts, state) {
|
|
1583
|
+
if (event.type === "tool_use") {
|
|
1584
|
+
process.stderr.write(`${toolCallProgress(event.toolCall)}
|
|
1585
|
+
`);
|
|
1586
|
+
return;
|
|
1587
|
+
}
|
|
1588
|
+
if (event.type === "tool_result" && opts.verbose === true) {
|
|
1589
|
+
process.stderr.write(` ${toolResultProgress(event.toolResult)}
|
|
1590
|
+
`);
|
|
1591
|
+
return;
|
|
1592
|
+
}
|
|
1593
|
+
if (event.type === "agent_start") {
|
|
1594
|
+
if (!state.childLabels.has(event.agent.id)) {
|
|
1595
|
+
state.childLabels.set(
|
|
1596
|
+
event.agent.id,
|
|
1597
|
+
uniqueAgentDisplayName(event.agent.profile ?? event.agent.name, state.childLabels.values())
|
|
1598
|
+
);
|
|
1599
|
+
}
|
|
1600
|
+
return;
|
|
1601
|
+
}
|
|
1602
|
+
if (event.type === "agent_activity") {
|
|
1603
|
+
writeChildToolProgress(event.agentId, event.activity, opts, state);
|
|
1604
|
+
}
|
|
1605
|
+
}
|
|
1606
|
+
function writeChildToolProgress(agentId, activity, opts, state) {
|
|
1607
|
+
if (activity.kind !== "tool" || !activity.toolCall) return;
|
|
1608
|
+
if (activity.status === "running") {
|
|
1609
|
+
const child = progressLine(state.childLabels.get(agentId) ?? "agent", PROGRESS_ARG_MAX);
|
|
1610
|
+
process.stderr.write(` [${child}] ${toolCallProgress(activity.toolCall)}
|
|
1611
|
+
`);
|
|
1612
|
+
return;
|
|
1613
|
+
}
|
|
1614
|
+
if (opts.verbose === true && activity.result) {
|
|
1615
|
+
process.stderr.write(` ${toolResultProgress(activity.result)}
|
|
1616
|
+
`);
|
|
1617
|
+
}
|
|
1618
|
+
}
|
|
1619
|
+
function toolCallProgress(call) {
|
|
1620
|
+
const arg = progressLine(getPrimaryArg(call.arguments), PROGRESS_ARG_MAX);
|
|
1621
|
+
return `[${progressLine(call.name, PROGRESS_ARG_MAX)}]${arg ? ` ${arg}` : ""}`;
|
|
1622
|
+
}
|
|
1623
|
+
function toolResultProgress(result) {
|
|
1624
|
+
const duration = result.metrics?.durationMs;
|
|
1625
|
+
const target = progressLine(result.presentation?.target ?? "", PROGRESS_ARG_MAX);
|
|
1626
|
+
const error = result.status === "success" ? "" : progressLine(toolResultErrorMessage(result) ?? "", PROGRESS_ERROR_MAX);
|
|
1627
|
+
const detail = [target, error].filter(Boolean).join(": ");
|
|
1628
|
+
return `\u2192 ${result.status}${duration !== void 0 ? ` ${Math.round(duration)}ms` : ""}${detail ? ` ${detail}` : ""}`;
|
|
1629
|
+
}
|
|
1252
1630
|
function emitCompactBoundary(outputFormat, emit, result) {
|
|
1253
1631
|
if (outputFormat !== "stream-json") return;
|
|
1254
1632
|
emit({
|
|
@@ -1260,6 +1638,7 @@ function emitCompactBoundary(outputFormat, emit, result) {
|
|
|
1260
1638
|
generation: result.generation,
|
|
1261
1639
|
pre_messages: result.preMessageCount,
|
|
1262
1640
|
post_messages: result.replacementHistory.length,
|
|
1641
|
+
carried_messages: result.carriedCount,
|
|
1263
1642
|
post_tokens: result.postContextTokens,
|
|
1264
1643
|
checkpoint_version: 2,
|
|
1265
1644
|
strategy: result.strategy,
|
|
@@ -1269,12 +1648,17 @@ function emitCompactBoundary(outputFormat, emit, result) {
|
|
|
1269
1648
|
// This generation's status above; the conversation's accumulated one here.
|
|
1270
1649
|
coverage_lifetime_status: result.checkpoint.coverage?.lifetime?.status ?? "complete",
|
|
1271
1650
|
coverage: result.checkpoint.coverage,
|
|
1272
|
-
warning: result.warning
|
|
1651
|
+
warning: result.warning,
|
|
1652
|
+
// The reducer audit, when the host has something to report: suspect inputs
|
|
1653
|
+
// found in the summarized span and inherited constraints the reducer let go of.
|
|
1654
|
+
...result.checkpoint.audit ? { audit: result.checkpoint.audit } : {},
|
|
1655
|
+
// Deferred compaction: the judge's verdict on the steps taken while the reducer ran.
|
|
1656
|
+
...result.judge ? { judge: result.judge } : {}
|
|
1273
1657
|
});
|
|
1274
1658
|
}
|
|
1275
1659
|
async function generatePromptSuggestions(config, _registry, history, signal, resolveAttachment) {
|
|
1276
|
-
const { chatCompletionStream } = await import("./openai-compatible-
|
|
1277
|
-
const { buildMessages } = await import("./context-
|
|
1660
|
+
const { chatCompletionStream } = await import("./openai-compatible-2OB54YST.js");
|
|
1661
|
+
const { buildMessages } = await import("./context-CBOBDY5Q.js");
|
|
1278
1662
|
const suggestionMessages = await buildMessages(
|
|
1279
1663
|
config,
|
|
1280
1664
|
[
|
|
@@ -1398,7 +1782,7 @@ var StdioProcessTransport = class {
|
|
|
1398
1782
|
return Promise.reject(new Error(`MCP server ${this.name} is not a stdio server`));
|
|
1399
1783
|
}
|
|
1400
1784
|
const child = spawn(this.config.command, this.config.args ?? [], {
|
|
1401
|
-
env:
|
|
1785
|
+
env: buildChildEnv(process.env, this.config.env),
|
|
1402
1786
|
cwd: this.config.cwd,
|
|
1403
1787
|
stdio: ["pipe", "pipe", "pipe"]
|
|
1404
1788
|
});
|
|
@@ -1433,7 +1817,6 @@ var StdioProcessTransport = class {
|
|
|
1433
1817
|
child.stdout?.removeListener("data", onStdoutData);
|
|
1434
1818
|
child.stderr?.removeListener("data", onStderrData);
|
|
1435
1819
|
child.removeListener("error", onChildError);
|
|
1436
|
-
child.stdin?.removeListener("error", onStdinError);
|
|
1437
1820
|
child.removeListener("exit", onExit);
|
|
1438
1821
|
child.removeListener("close", onClose);
|
|
1439
1822
|
this.cleanupListeners = () => {
|
|
@@ -1653,7 +2036,7 @@ function registerElicitationHandler(client, name, options) {
|
|
|
1653
2036
|
}
|
|
1654
2037
|
);
|
|
1655
2038
|
}
|
|
1656
|
-
async function connectMcpServer(name, cfg, options) {
|
|
2039
|
+
async function connectMcpServer(name, cfg, options, failure) {
|
|
1657
2040
|
let handle;
|
|
1658
2041
|
let liveConnection;
|
|
1659
2042
|
let transportClosed = false;
|
|
@@ -1741,6 +2124,7 @@ async function connectMcpServer(name, cfg, options) {
|
|
|
1741
2124
|
}
|
|
1742
2125
|
return conn;
|
|
1743
2126
|
} catch (error) {
|
|
2127
|
+
if (failure) failure.error = error;
|
|
1744
2128
|
const message = redact(error instanceof Error ? error.message : String(error));
|
|
1745
2129
|
options.onDiagnostic({
|
|
1746
2130
|
level: "warn",
|
|
@@ -1758,6 +2142,23 @@ async function connectMcpServer(name, cfg, options) {
|
|
|
1758
2142
|
return null;
|
|
1759
2143
|
}
|
|
1760
2144
|
}
|
|
2145
|
+
function rejectedStreamableHttp(error) {
|
|
2146
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2147
|
+
return /\b(404|405)\b|Not Found|Method Not Allowed/i.test(message);
|
|
2148
|
+
}
|
|
2149
|
+
async function connectRemoteWithSseFallback(name, cfg, options) {
|
|
2150
|
+
const eligible = isRemoteConfig(cfg) && cfg.type !== "sse";
|
|
2151
|
+
if (!eligible) return connectMcpServer(name, cfg, options);
|
|
2152
|
+
const failure = {};
|
|
2153
|
+
const connection = await connectMcpServer(name, cfg, options, failure);
|
|
2154
|
+
if (connection || !rejectedStreamableHttp(failure.error)) return connection;
|
|
2155
|
+
options.onDiagnostic({
|
|
2156
|
+
level: "warn",
|
|
2157
|
+
message: `MCP server "${name}" rejected Streamable HTTP; retrying as legacy SSE. Set "type": "sse" to skip this.`,
|
|
2158
|
+
server: name
|
|
2159
|
+
});
|
|
2160
|
+
return connectMcpServer(name, { ...cfg, type: "sse" }, options);
|
|
2161
|
+
}
|
|
1761
2162
|
function buildToolDefinition(conn, tool, requestTimeoutMs) {
|
|
1762
2163
|
const prefixedName = `mcp__${conn.name}__${tool.name}`;
|
|
1763
2164
|
const live = conn;
|
|
@@ -1836,7 +2237,7 @@ async function connectMcpServers(workspace, options = {}) {
|
|
|
1836
2237
|
servers = declared.filter((server) => server.source === "user");
|
|
1837
2238
|
}
|
|
1838
2239
|
const results = await Promise.allSettled(
|
|
1839
|
-
servers.map(({ name, config }) =>
|
|
2240
|
+
servers.map(({ name, config }) => connectRemoteWithSseFallback(name, config, resolved))
|
|
1840
2241
|
);
|
|
1841
2242
|
const connections = [];
|
|
1842
2243
|
for (const [index, result] of results.entries()) {
|
|
@@ -1868,7 +2269,8 @@ async function disconnectMcpServers(connections) {
|
|
|
1868
2269
|
import { join as join2 } from "path";
|
|
1869
2270
|
function collectWithheldProjectNotices(input) {
|
|
1870
2271
|
if (!input.settingsEnabled) return [];
|
|
1871
|
-
const
|
|
2272
|
+
const projectPath = join2(input.workspace, ".book", "settings.json");
|
|
2273
|
+
const projectSettings = loadSettingsFile(projectPath);
|
|
1872
2274
|
const notices = [];
|
|
1873
2275
|
const allow = partitionProjectAllowRules(
|
|
1874
2276
|
projectSettings?.permissions?.allow ?? [],
|
|
@@ -1893,15 +2295,52 @@ function collectWithheldProjectNotices(input) {
|
|
|
1893
2295
|
`\u26A0 Ignoring ${hooks.pending.length} project-declared hook(s) (${summary}): hooks require approval. Run \`book doctor\` to approve them.`
|
|
1894
2296
|
);
|
|
1895
2297
|
}
|
|
2298
|
+
const directories = partitionProjectDirectories(
|
|
2299
|
+
collectDeclaredDirectories(input.workspace, projectSettings?.additionalDirectories ?? []),
|
|
2300
|
+
input.settings.projectDirectories
|
|
2301
|
+
);
|
|
2302
|
+
for (const directory of directories.pending) {
|
|
2303
|
+
notices.push(
|
|
2304
|
+
`\u26A0 Ignoring project-declared additionalDirectories entry "${directory.declared}" (really ${directory.realPath}): it requires approval. Run \`book doctor\` to see how to grant it.`
|
|
2305
|
+
);
|
|
2306
|
+
}
|
|
2307
|
+
const ignoredSandboxKeys = ignoredWorkspaceSandboxKeys(projectSettings ?? {});
|
|
2308
|
+
if (ignoredSandboxKeys.length > 0) {
|
|
2309
|
+
const listed = ignoredSandboxKeys.map(formatIgnoredWorkspaceSandboxKey).join(", ");
|
|
2310
|
+
notices.push(
|
|
2311
|
+
`\u26A0 Ignoring sandbox settings declared by ${projectPath}: ${listed}. A file inside the workspace may turn the sandbox on and add deny entries, but not loosen it. ` + TRUSTED_SETTINGS_LOCATION
|
|
2312
|
+
);
|
|
2313
|
+
}
|
|
2314
|
+
const localPath = join2(input.workspace, ".book", "settings.local.json");
|
|
2315
|
+
const localSettings = loadSettingsFile(localPath);
|
|
2316
|
+
const ignoredLocalSandboxKeys = ignoredWorkspaceSandboxKeys(localSettings ?? {});
|
|
2317
|
+
if (ignoredLocalSandboxKeys.length > 0) {
|
|
2318
|
+
const listed = ignoredLocalSandboxKeys.map(formatIgnoredWorkspaceSandboxKey).join(", ");
|
|
2319
|
+
notices.push(
|
|
2320
|
+
`\u26A0 Ignoring sandbox settings declared by ${localPath}: ${listed}. A file inside the workspace may turn the sandbox on and add deny entries, but not loosen it. ` + TRUSTED_SETTINGS_LOCATION
|
|
2321
|
+
);
|
|
2322
|
+
}
|
|
2323
|
+
const userPath = settingsLayerPaths(input.workspace)[0];
|
|
2324
|
+
const activation = workspaceLayerThatEnabledSandbox([
|
|
2325
|
+
{ path: userPath, trust: "trusted", document: loadSettingsFile(userPath) },
|
|
2326
|
+
{ path: projectPath, trust: "repository", document: projectSettings },
|
|
2327
|
+
{ path: localPath, trust: "local", document: localSettings }
|
|
2328
|
+
]);
|
|
2329
|
+
if (activation) {
|
|
2330
|
+
notices.push(
|
|
2331
|
+
`\u26A0 ${activation} declared sandbox.enabled=true, so sandbox.autoAllowBashIfSandboxed is false for this session: no Bash command is auto-approved. ${TRUSTED_SETTINGS_LOCATION}`
|
|
2332
|
+
);
|
|
2333
|
+
}
|
|
1896
2334
|
return notices;
|
|
1897
2335
|
}
|
|
1898
2336
|
|
|
1899
2337
|
export {
|
|
1900
2338
|
readRunStatus,
|
|
2339
|
+
isReaderGoneError,
|
|
1901
2340
|
runHeadless,
|
|
1902
2341
|
buildMcpToolDefinitions,
|
|
1903
2342
|
connectMcpServers,
|
|
1904
2343
|
disconnectMcpServers,
|
|
1905
2344
|
collectWithheldProjectNotices
|
|
1906
2345
|
};
|
|
1907
|
-
//# sourceMappingURL=chunk-
|
|
2346
|
+
//# sourceMappingURL=chunk-TLX3H2CL.js.map
|