chainlesschain 0.162.36 → 0.162.38
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/package.json +3 -2
- package/src/assets/web-panel/assets/{AIOps-vAVAFNJ4.js → AIOps-DV0Q9zKL.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-BnRHFCKM.js → ActionButton-C6vH8rhL.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-BOjwqWqG.js → Analytics-BvPDc2ui.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-Dc0D1Txn.js → AppLayout-CWnyqTqY.js} +5 -5
- package/src/assets/web-panel/assets/{Audit-dd_2efaZ.js → Audit-BzenidV4.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-HF1jgm8G.js → Backup-CSl7bNwK.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-CCtzmoKe.js → BaseInput-DAY3iHIq.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-BNfH1c3p.js → Chat-Jyhm9fgk.js} +6 -6
- package/src/assets/web-panel/assets/{ChatBubbleRenderer-DCWFqmI4.js → ChatBubbleRenderer-CwlAnVjy.js} +1 -1
- package/src/assets/web-panel/assets/{Checkbox-BOr-NscK.js → Checkbox-D4rwURAi.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-DE058N7-.js → Codegen-DYdjTEfC.js} +1 -1
- package/src/assets/web-panel/assets/{Col-SOREo1XE.js → Col-DsVyZ_fS.js} +1 -1
- package/src/assets/web-panel/assets/{Community-sOvNZo9f.js → Community-CjCpl27Q.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-DnBe558D.js → Compact-kt18dsjm.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-o-r6CUbg.js → Compliance-BV5urquU.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-D6_k9mHP.js → Cowork-C4SovPWC.js} +3 -3
- package/src/assets/web-panel/assets/{Cron-CEV3Xkrm.js → Cron-uuNs_xzA.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-eJ1lQWKU.js → Crosschain-DR5a65tR.js} +1 -1
- package/src/assets/web-panel/assets/{DID-B-WqM9Hp.js → DID-B1KTf2-5.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-ZnKPcsHN.js → Dashboard-Dkj7XgED.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-B8uLWDIP.js → Dropdown-BhXCuJ19.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-Jmj2Y7aH.js → EmailListRenderer-DG8365Iv.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-Cb2xetG-.js → FamilyGuardDashboard-BdHGPu39.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-C_07GXoq.js → Federation-Dwvxl0zR.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-D3kbYrMU.js → FormItemContext-BVmhCVWU.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-9xgqvGPg.js → GenericCardRenderer-DDPjvF2s.js} +1 -1
- package/src/assets/web-panel/assets/{Git-BlwWlMMB.js → Git-foK6WTSr.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-DxN3wQZ_.js → Governance-CfqMdu6Y.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-ls7pSw_D.js → Inference-BKrLO4GO.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-_n9hYuPI.js → KnowledgeGraph-6o6Q-mmF.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-CvEVY5TK.js → Logs-L5ZIW0Dz.js} +2 -2
- package/src/assets/web-panel/assets/{Marketplace-C3qvQJT7.js → Marketplace-BWkfEocP.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-DiwKpnKx.js → McpTools-BPebQbWU.js} +4 -4
- package/src/assets/web-panel/assets/{Memory-CIBPi_da.js → Memory-C0Dq-X3C.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-D-v0Se8y.js → MobileBridge-DRBoutTY.js} +2 -2
- package/src/assets/web-panel/assets/{MobileProjects-cP1apTQD.js → MobileProjects-BMP6eLp1.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-BMFWrI65.js → Mtc-Cj3QPM9p.js} +4 -4
- package/src/assets/web-panel/assets/{MtcAudit-2s8LaHtR.js → MtcAudit-rBQYbfQR.js} +2 -2
- package/src/assets/web-panel/assets/{Multisig-dL_nvj7d.js → Multisig-Dbuy4OY4.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-BbrJp06R.js → NLProgramming-CMnt1se-.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-jR9irwy3.js → Notes-BX9tSCiF.js} +4 -4
- package/src/assets/web-panel/assets/{NotificationSettings-Dk-STCIX.js → NotificationSettings-BFeirVRq.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-CqqfY6zq.js → OrderTableRenderer-ybiMlKQW.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-BCK5jylo.js → Organization-kTfRxKqk.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-BRAY7Smt.js → Overflow-CtuCAzwV.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-BltVRGjb.js → P2P-KfbciaP3.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-CV8UbXHe.js → PdhVaultBrowser-bqEUFhgC.js} +5 -5
- package/src/assets/web-panel/assets/{Permissions-_tNl47Qh.js → Permissions-BgMypz-z.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-Cgc4HjpX.js → PersonalDataHub-C3zUE-1z.js} +2 -2
- package/src/assets/web-panel/assets/{Pipeline-Bn_QU4mu.js → Pipeline-iX-pYHpC.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-jzJowp5P.js → Privacy-B01uzeFM.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-B_1pJ8qd.js → ProjectInit-TsfbzJp7.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-CPVZpXzs.js → ProjectSettings-iGvMp8sM.js} +2 -2
- package/src/assets/web-panel/assets/{Projects-CQsHOWnT.js → Projects-Be9k29iQ.js} +1 -1
- package/src/assets/web-panel/assets/{Providers-CzzMiLC0.js → Providers-C9Pc8dqo.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-MxBKIn9o.js → QuickAsk-DN_yFiVO.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-D8lN6Lis.js → Recommend-CvSNgl7H.js} +1 -1
- package/src/assets/web-panel/assets/{Reputation-CfYK-IrV.js → Reputation-S6BCz8xH.js} +1 -1
- package/src/assets/web-panel/assets/{Row-Bg7NZDP9.js → Row-CTRYCaqP.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-BOVNJhj0.js → RssFeed-Cu8_P5ll.js} +3 -3
- package/src/assets/web-panel/assets/{Search-B38qzmhY.js → Search-rZ1Xza_U.js} +1 -1
- package/src/assets/web-panel/assets/{Security-CjqleZpe.js → Security-CF43IJHX.js} +4 -4
- package/src/assets/web-panel/assets/{Services-Bu9JSJap.js → Services-BobNHzne.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-B2RvRkaX.js → Skeleton-DWJ2kfuI.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-_h42mxMN.js → Skills-AmEZgHYr.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-BssLs56D.js → Sla-DTS-fBiY.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-DCxFYHsd.js → SpeechSettings-DEr6MHRU.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-D2xQuNLE.js → SyncSettings-CVs9alv_.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-DhpOGOlo.js → Tasks-BcVDAxdi.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-CYG-R-aS.js → Templates-CTNjZRKA.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-BQRYLsvP.js → Tenant-DPbXg0Pg.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-imKU7N5j.js → Terminal-DhKXcPw2.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-BIZzBftk.js → TimelineRenderer-B0DMZOpk.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-uMLH5p_a.js → Tokens-RvWuBXgg.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-BzS6XPqx.js → Trigger-2O-BaTQG.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-R4zhHufZ.js → Trust-6qY35L-C.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-DATQCoGe.js → UkeySign-DhV1wYtQ.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-ClUmKOtS.js → VideoEditing-DgqA5UZm.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-DzJTbQzD.js → Wallet-DJRYdUAK.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-CrXrLmzQ.js → WebAuthn-C2W-x0cg.js} +5 -5
- package/src/assets/web-panel/assets/{WorkflowEditor-CpvZ0Tma.js → WorkflowEditor-BP2tkDHe.js} +1 -1
- package/src/assets/web-panel/assets/{chat-a6wpYmVL.js → chat-CGVfeoTn.js} +1 -1
- package/src/assets/web-panel/assets/{colors-CXJADb1t.js → colors-BmjRolM1.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-CL2pohS_.js → compact-item-BvJJkjZE.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-xFi_1G5_.js → createContext-DyhlvRYs.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-CetO0WH0.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-Bchh1rAi.js → hasIn-BoBMR89s.js} +1 -1
- package/src/assets/web-panel/assets/{index-C2eMYASq.js → index-39VDXdn6.js} +1 -1
- package/src/assets/web-panel/assets/{index-CR3kFPuC.js → index-81tWFqfN.js} +1 -1
- package/src/assets/web-panel/assets/{index-CTRd7vkq.js → index-BT1SQ9nj.js} +1 -1
- package/src/assets/web-panel/assets/index-BZVz-WfV.js +1 -0
- package/src/assets/web-panel/assets/{index-D-TT9Swq.js → index-Beh7jDbS.js} +1 -1
- package/src/assets/web-panel/assets/{index-BrbJBnT-.js → index-Bm_MmdwP.js} +1 -1
- package/src/assets/web-panel/assets/{index-dsLc7t6W.js → index-BqGNmoKy.js} +1 -1
- package/src/assets/web-panel/assets/{index-DEYcLAl7.js → index-BuQrONgf.js} +1 -1
- package/src/assets/web-panel/assets/{index-KCib1PTw.js → index-BvvNnWXe.js} +1 -1
- package/src/assets/web-panel/assets/{index-DxahxRP7.js → index-ByWpNjTj.js} +1 -1
- package/src/assets/web-panel/assets/{index-B6NehWty.js → index-BycpeGfj.js} +1 -1
- package/src/assets/web-panel/assets/{index-DTEu7TSF.js → index-C0xn6hOr.js} +1 -1
- package/src/assets/web-panel/assets/{index-BH9t10pe.js → index-C1t-r7yV.js} +1 -1
- package/src/assets/web-panel/assets/{index-B7wT5VRi.js → index-CDPMHKQi.js} +1 -1
- package/src/assets/web-panel/assets/{index-majCS3s2.js → index-CIaGw7vl.js} +1 -1
- package/src/assets/web-panel/assets/{index-EPERz4Pu.js → index-CQJVedQ3.js} +1 -1
- package/src/assets/web-panel/assets/{index-IkvkNxbc.js → index-CSgbOGaP.js} +1 -1
- package/src/assets/web-panel/assets/{index-DQ_hw_5P.js → index-Cbh-lCxq.js} +1 -1
- package/src/assets/web-panel/assets/{index-CMybtJY6.js → index-CzDVBBcg.js} +1 -1
- package/src/assets/web-panel/assets/{index-B4zNisy9.js → index-Czsbrn75.js} +1 -1
- package/src/assets/web-panel/assets/{index-M8SZI11a.js → index-D-93XwJd.js} +1 -1
- package/src/assets/web-panel/assets/index-D0-bvFy3.js +1 -0
- package/src/assets/web-panel/assets/{index-TxbHusq2.js → index-D0YToIi_.js} +1 -1
- package/src/assets/web-panel/assets/{index-B7knYOpm.js → index-DIPZ6hbJ.js} +1 -1
- package/src/assets/web-panel/assets/{index-CGq4HQno.js → index-DeeLHcMY.js} +1 -1
- package/src/assets/web-panel/assets/{index-CdU8BwRW.js → index-DgbWSwr5.js} +1 -1
- package/src/assets/web-panel/assets/{index-C4yBRKT4.js → index-DtKdCXHW.js} +1 -1
- package/src/assets/web-panel/assets/{index-jMcv1u5o.js → index-DwTgvhOL.js} +1 -1
- package/src/assets/web-panel/assets/{index-B3Tpv7-d.js → index-DyS4I4L-.js} +1 -1
- package/src/assets/web-panel/assets/{index-u8K1y_lh.js → index-FKFT-QTk.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cua_P8St.js → index-Te0ruvY_.js} +1 -1
- package/src/assets/web-panel/assets/{index-DVo1GJoj.js → index-VXVukhBA.js} +1 -1
- package/src/assets/web-panel/assets/{index-BPH5ESqs.js → index-Y1b8i0NV.js} +3 -3
- package/src/assets/web-panel/assets/{index-DsbMVBj1.js → index-ZNIms1nA.js} +1 -1
- package/src/assets/web-panel/assets/{index-BoaRB-4a.js → index-n-N19np-.js} +1 -1
- package/src/assets/web-panel/assets/{index-BmsIKzyu.js → index-vF1pR00A.js} +1 -1
- package/src/assets/web-panel/assets/{index-CuehgDOp.js → index-wLAjVpmJ.js} +1 -1
- package/src/assets/web-panel/assets/{index-DjdOL159.js → index-xPSzUoWT.js} +1 -1
- package/src/assets/web-panel/assets/{index-BF4xx1_b.js → index-xZdOioVg.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-DYn3Gc09.js → initDefaultProps-BLKSE8he.js} +1 -1
- package/src/assets/web-panel/assets/{motion-ZS3eolb9.js → motion-Bb59qqLK.js} +1 -1
- package/src/assets/web-panel/assets/{move-CEw4uqr3.js → move-CB3pYCk6.js} +1 -1
- package/src/assets/web-panel/assets/{omit-DlHFZnPp.js → omit-iImQWuU7.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-eZQvV5fA.js → pickAttrs-DRP2Chqo.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-B31jQwa-.js → placementArrow-BrlfD4tF.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-DAsNmVto.js → responsiveObserve-Cqxkuh5H.js} +1 -1
- package/src/assets/web-panel/assets/{slide-gPQPrYZC.js → slide-nxKEuLMj.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-DwWKX5co.js → statusUtils-30E47KSk.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-B3VOtXuH.js → styleChecker-Dn2_-5bn.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-6ADctM2r.js → useFlexGapSupport-DkZ00X6F.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-6Zx1SSKs.js → useFs-ByrwSCOr.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-BzReowln.js → usePersonalDataHub-BDY6jtUD.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-C8IpEQbD.js → vnode-BL2q5BLv.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-ruc9vHr0.js → zoom-BSkPKE42.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/commands/agent.js +31 -0
- package/src/commands/cli-anything.js +14 -6
- package/src/commands/loop.js +450 -0
- package/src/commands/mcp.js +236 -6
- package/src/harness/mcp-client.js +70 -1
- package/src/index.js +2 -0
- package/src/lib/loop.js +198 -0
- package/src/lib/settings-hooks.cjs +1 -0
- package/src/repl/agent-repl.js +57 -20
- package/src/repl/mcp-prompt.js +122 -0
- package/src/runtime/agent-core.js +123 -17
- package/src/runtime/headless-runner.js +34 -9
- package/src/runtime/mcp-config.js +118 -9
- package/src/runtime/policies/agent-policy.js +3 -0
- package/src/assets/web-panel/assets/devWarning-BtmELbtB.js +0 -1
- package/src/assets/web-panel/assets/index-B4l4vLTB.js +0 -1
- package/src/assets/web-panel/assets/index-B7Ek5iiY.js +0 -1
package/src/repl/agent-repl.js
CHANGED
|
@@ -74,6 +74,7 @@ import { expandFileRefs } from "../runtime/file-ref-expander.js";
|
|
|
74
74
|
import { composeSystemPrompt } from "../runtime/system-prompt.js";
|
|
75
75
|
import { makeFallbackChatFn } from "../runtime/fallback-model.js";
|
|
76
76
|
import { resolveSlashMacro } from "./slash-macro.js";
|
|
77
|
+
import { expandMcpPrompt, renderMcpSurface } from "./mcp-prompt.js";
|
|
77
78
|
|
|
78
79
|
/**
|
|
79
80
|
* Reference to the runtime DB for hook execution (set during startAgentRepl)
|
|
@@ -129,7 +130,8 @@ async function _persistAlwaysAllow(tool, args) {
|
|
|
129
130
|
scope: "local",
|
|
130
131
|
});
|
|
131
132
|
if (!_permissionRules) _permissionRules = { allow: [], ask: [], deny: [] };
|
|
132
|
-
if (!_permissionRules.allow.includes(rule))
|
|
133
|
+
if (!_permissionRules.allow.includes(rule))
|
|
134
|
+
_permissionRules.allow.push(rule);
|
|
133
135
|
return { rule, file };
|
|
134
136
|
} catch (err) {
|
|
135
137
|
process.stderr.write(` always-allow persist failed: ${err.message}\n`);
|
|
@@ -504,9 +506,8 @@ export async function startAgentRepl(options = {}) {
|
|
|
504
506
|
// settings.json SessionStart hooks → inject session context (observe-only).
|
|
505
507
|
if (_settingsHooks) {
|
|
506
508
|
try {
|
|
507
|
-
const { runSessionStartHooks } =
|
|
508
|
-
"../lib/settings-hook-events.cjs"
|
|
509
|
-
);
|
|
509
|
+
const { runSessionStartHooks } =
|
|
510
|
+
await import("../lib/settings-hook-events.cjs");
|
|
510
511
|
const ctx = runSessionStartHooks(_settingsHooks, {
|
|
511
512
|
source: "startup",
|
|
512
513
|
cwd: process.cwd(),
|
|
@@ -630,6 +631,11 @@ export async function startAgentRepl(options = {}) {
|
|
|
630
631
|
mcpConfigPath: options.mcpConfig || null,
|
|
631
632
|
db: db?.getDatabase?.() || null,
|
|
632
633
|
includeRegistered: options.useRegisteredMcp !== false,
|
|
634
|
+
// IDE bridge: auto-connect a running editor's MCP server when inside
|
|
635
|
+
// an IDE integrated terminal. --ide forces it, --no-ide disables it
|
|
636
|
+
// (parity with headless; auto-detect already works via process.env).
|
|
637
|
+
ide: options.ide,
|
|
638
|
+
cwd: process.cwd(),
|
|
633
639
|
},
|
|
634
640
|
{ writeErr: (s) => process.stderr.write(s) },
|
|
635
641
|
);
|
|
@@ -800,7 +806,8 @@ export async function startAgentRepl(options = {}) {
|
|
|
800
806
|
if (_statusLineEnabled && _renderStatus) {
|
|
801
807
|
const line = _renderStatus();
|
|
802
808
|
// Built-in line is dimmed; a custom command may carry its own ANSI.
|
|
803
|
-
if (line)
|
|
809
|
+
if (line)
|
|
810
|
+
process.stdout.write((_customStatus ? line : chalk.dim(line)) + "\n");
|
|
804
811
|
}
|
|
805
812
|
rl.setPrompt(getPrompt());
|
|
806
813
|
rl.prompt();
|
|
@@ -1019,15 +1026,16 @@ export async function startAgentRepl(options = {}) {
|
|
|
1019
1026
|
logger.info("Status line: on");
|
|
1020
1027
|
} else {
|
|
1021
1028
|
// bare / "show" → report state + a one-off render
|
|
1022
|
-
const line =
|
|
1029
|
+
const line =
|
|
1030
|
+
_statusLineEnabled && _renderStatus ? _renderStatus() : null;
|
|
1023
1031
|
if (line) {
|
|
1024
|
-
logger.info(
|
|
1025
|
-
`Status line: ${_customStatus ? line : chalk.dim(line)}`,
|
|
1026
|
-
);
|
|
1032
|
+
logger.info(`Status line: ${_customStatus ? line : chalk.dim(line)}`);
|
|
1027
1033
|
} else {
|
|
1028
1034
|
logger.info(
|
|
1029
1035
|
`Status line: ${_statusLineEnabled ? "on (no content yet)" : "off"}` +
|
|
1030
|
-
(_statusLineEnabled
|
|
1036
|
+
(_statusLineEnabled
|
|
1037
|
+
? ""
|
|
1038
|
+
: ` — enable with ${chalk.cyan("/statusline on")}`),
|
|
1031
1039
|
);
|
|
1032
1040
|
}
|
|
1033
1041
|
if (_customStatus) {
|
|
@@ -1041,9 +1049,8 @@ export async function startAgentRepl(options = {}) {
|
|
|
1041
1049
|
if (trimmed === "/output-style" || trimmed.startsWith("/output-style ")) {
|
|
1042
1050
|
const arg = trimmed.slice("/output-style".length).trim();
|
|
1043
1051
|
try {
|
|
1044
|
-
const { discoverOutputStyles, getOutputStyle } =
|
|
1045
|
-
"../lib/output-styles.js"
|
|
1046
|
-
);
|
|
1052
|
+
const { discoverOutputStyles, getOutputStyle } =
|
|
1053
|
+
await import("../lib/output-styles.js");
|
|
1047
1054
|
if (!arg) {
|
|
1048
1055
|
logger.log(chalk.bold("Output styles:"));
|
|
1049
1056
|
for (const s of discoverOutputStyles(process.cwd())) {
|
|
@@ -1796,15 +1803,47 @@ export async function startAgentRepl(options = {}) {
|
|
|
1796
1803
|
return;
|
|
1797
1804
|
}
|
|
1798
1805
|
|
|
1806
|
+
// `/mcp` — overview of connected MCP servers' resources + prompts.
|
|
1807
|
+
if (trimmed === "/mcp" || trimmed === "/mcp ") {
|
|
1808
|
+
const mcpClient = _adhocMcp?.mcpClient || _bundleMcpClient;
|
|
1809
|
+
logger.log(renderMcpSurface(mcpClient));
|
|
1810
|
+
prompt();
|
|
1811
|
+
return;
|
|
1812
|
+
}
|
|
1813
|
+
|
|
1799
1814
|
// User-defined slash-command macros (.claude/commands/*.md), Claude-Code
|
|
1800
1815
|
// parity. resolveSlashMacro maps a leading /name to a command macro and
|
|
1801
1816
|
// expands its template; a non-match returns the line unchanged so a literal
|
|
1802
1817
|
// prompt like "/etc/hosts" still reaches the LLM. Wire is unit-tested.
|
|
1803
1818
|
let promptText = trimmed;
|
|
1819
|
+
|
|
1820
|
+
// MCP server-provided prompts (Claude-Code parity): `/mcp__<server>__<name>
|
|
1821
|
+
// [json-args]` fetches a rendered prompt template from the connected MCP
|
|
1822
|
+
// server and uses its text as this turn's input. Falls through unchanged
|
|
1823
|
+
// when the line isn't an MCP prompt command.
|
|
1824
|
+
if (promptText.startsWith("/mcp__")) {
|
|
1825
|
+
try {
|
|
1826
|
+
const expanded = await expandMcpPrompt(
|
|
1827
|
+
promptText,
|
|
1828
|
+
_adhocMcp?.mcpClient || _bundleMcpClient,
|
|
1829
|
+
);
|
|
1830
|
+
if (expanded != null) {
|
|
1831
|
+
promptText = expanded;
|
|
1832
|
+
logger.log(chalk.gray(`[mcp] prompt expanded`));
|
|
1833
|
+
}
|
|
1834
|
+
} catch (err) {
|
|
1835
|
+
logger.info(
|
|
1836
|
+
chalk.yellow(`[mcp] prompt expansion failed: ${err.message}`),
|
|
1837
|
+
);
|
|
1838
|
+
prompt();
|
|
1839
|
+
return;
|
|
1840
|
+
}
|
|
1841
|
+
}
|
|
1804
1842
|
try {
|
|
1805
1843
|
const macro = await resolveSlashMacro(trimmed, { cwd: process.cwd() });
|
|
1806
1844
|
if (macro.matched) {
|
|
1807
|
-
for (const w of macro.warnings)
|
|
1845
|
+
for (const w of macro.warnings)
|
|
1846
|
+
logger.info(chalk.yellow(`[@ref] ${w}`));
|
|
1808
1847
|
promptText = macro.promptText;
|
|
1809
1848
|
logger.log(
|
|
1810
1849
|
chalk.gray(`[/${macro.name}] macro expanded (${macro.scope})`),
|
|
@@ -1859,9 +1898,8 @@ export async function startAgentRepl(options = {}) {
|
|
|
1859
1898
|
// is observe-only). block → abort the turn; context → inject before the turn.
|
|
1860
1899
|
if (_settingsHooks) {
|
|
1861
1900
|
try {
|
|
1862
|
-
const { runUserPromptSubmitHooks } =
|
|
1863
|
-
"../lib/settings-hook-events.cjs"
|
|
1864
|
-
);
|
|
1901
|
+
const { runUserPromptSubmitHooks } =
|
|
1902
|
+
await import("../lib/settings-hook-events.cjs");
|
|
1865
1903
|
const ups = runUserPromptSubmitHooks(_settingsHooks, {
|
|
1866
1904
|
prompt: userContent,
|
|
1867
1905
|
cwd: process.cwd(),
|
|
@@ -2131,9 +2169,8 @@ export async function startAgentRepl(options = {}) {
|
|
|
2131
2169
|
// settings.json SessionEnd hooks (observe-only) when the REPL exits.
|
|
2132
2170
|
if (_settingsHooks) {
|
|
2133
2171
|
try {
|
|
2134
|
-
const { runObserveHooks } =
|
|
2135
|
-
"../lib/settings-hook-events.cjs"
|
|
2136
|
-
);
|
|
2172
|
+
const { runObserveHooks } =
|
|
2173
|
+
await import("../lib/settings-hook-events.cjs");
|
|
2137
2174
|
runObserveHooks(
|
|
2138
2175
|
_settingsHooks,
|
|
2139
2176
|
"SessionEnd",
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP prompt + resource surfacing for the agent REPL (Claude-Code parity).
|
|
3
|
+
*
|
|
4
|
+
* MCP servers can expose "prompts" — server-defined, parameterized prompt
|
|
5
|
+
* templates — which Claude Code surfaces as slash commands of the form
|
|
6
|
+
* `/mcp__<server>__<prompt>`. This module is the pure, unit-testable core:
|
|
7
|
+
* it parses such a command, fetches the rendered prompt from the connected
|
|
8
|
+
* MCP client, and flattens it into plain text to use as the user's turn.
|
|
9
|
+
*
|
|
10
|
+
* It also renders a `/mcp` overview of every connected server's resources and
|
|
11
|
+
* prompts. The REPL (agent-repl.js) wires these in; all the logic lives here so
|
|
12
|
+
* it can be tested without spinning up a readline session.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Parse a `/mcp__<server>__<prompt> [args]` line. Returns `{ server, name,
|
|
17
|
+
* args }` or `null` when the line is not an MCP prompt invocation.
|
|
18
|
+
*
|
|
19
|
+
* `args` is parsed from a trailing JSON object when present; a non-JSON tail is
|
|
20
|
+
* passed as `{ input: "<tail>" }` so simple one-arg prompts stay ergonomic.
|
|
21
|
+
*/
|
|
22
|
+
export function parseMcpPromptCommand(line) {
|
|
23
|
+
const trimmed = (line || "").trim();
|
|
24
|
+
if (!trimmed.startsWith("/mcp__")) return null;
|
|
25
|
+
|
|
26
|
+
const sp = trimmed.search(/\s/);
|
|
27
|
+
const token = sp === -1 ? trimmed : trimmed.slice(0, sp);
|
|
28
|
+
const rest = sp === -1 ? "" : trimmed.slice(sp + 1).trim();
|
|
29
|
+
|
|
30
|
+
const full = token.slice(1); // drop leading "/"
|
|
31
|
+
const parts = full.split("__"); // ["mcp", "<server>", "<prompt...>"]
|
|
32
|
+
if (parts.length < 3 || parts[0] !== "mcp") return null;
|
|
33
|
+
const server = parts[1];
|
|
34
|
+
const name = parts.slice(2).join("__");
|
|
35
|
+
if (!server || !name) return null;
|
|
36
|
+
|
|
37
|
+
let args = {};
|
|
38
|
+
if (rest) {
|
|
39
|
+
try {
|
|
40
|
+
const parsed = JSON.parse(rest);
|
|
41
|
+
args =
|
|
42
|
+
parsed && typeof parsed === "object" && !Array.isArray(parsed)
|
|
43
|
+
? parsed
|
|
44
|
+
: { input: rest };
|
|
45
|
+
} catch {
|
|
46
|
+
args = { input: rest };
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return { server, name, args };
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Flatten an MCP `prompts/get` result (`{ messages: [...] }`) into plain text.
|
|
54
|
+
* Handles text blocks and embedded text resources; ignores binary blocks.
|
|
55
|
+
*/
|
|
56
|
+
export function renderPromptMessages(result) {
|
|
57
|
+
const messages = Array.isArray(result?.messages) ? result.messages : [];
|
|
58
|
+
const out = [];
|
|
59
|
+
for (const msg of messages) {
|
|
60
|
+
const blocks = Array.isArray(msg?.content) ? msg.content : [msg?.content];
|
|
61
|
+
for (const b of blocks) {
|
|
62
|
+
if (!b) continue;
|
|
63
|
+
if (b.type === "text" && typeof b.text === "string") {
|
|
64
|
+
out.push(b.text);
|
|
65
|
+
} else if (
|
|
66
|
+
b.type === "resource" &&
|
|
67
|
+
b.resource &&
|
|
68
|
+
typeof b.resource.text === "string"
|
|
69
|
+
) {
|
|
70
|
+
out.push(b.resource.text);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return out.join("\n\n");
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Expand a `/mcp__server__prompt` line into prompt text by calling the MCP
|
|
79
|
+
* client. Returns the rendered text, or `null` when the line is not an MCP
|
|
80
|
+
* prompt command. Throws if the underlying `getPrompt` fails (caller decides
|
|
81
|
+
* whether to surface or swallow).
|
|
82
|
+
*/
|
|
83
|
+
export async function expandMcpPrompt(line, mcpClient) {
|
|
84
|
+
const cmd = parseMcpPromptCommand(line);
|
|
85
|
+
if (!cmd) return null;
|
|
86
|
+
if (!mcpClient || typeof mcpClient.getPrompt !== "function") {
|
|
87
|
+
throw new Error("No MCP servers are connected this session.");
|
|
88
|
+
}
|
|
89
|
+
const result = await mcpClient.getPrompt(cmd.server, cmd.name, cmd.args);
|
|
90
|
+
return renderPromptMessages(result);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Render a human overview of all connected MCP resources + prompts for `/mcp`.
|
|
95
|
+
*/
|
|
96
|
+
export function renderMcpSurface(mcpClient) {
|
|
97
|
+
if (!mcpClient) return "No MCP servers are connected this session.";
|
|
98
|
+
const resources =
|
|
99
|
+
typeof mcpClient.listResources === "function"
|
|
100
|
+
? mcpClient.listResources()
|
|
101
|
+
: [];
|
|
102
|
+
const prompts =
|
|
103
|
+
typeof mcpClient.listPrompts === "function" ? mcpClient.listPrompts() : [];
|
|
104
|
+
|
|
105
|
+
const lines = [];
|
|
106
|
+
lines.push(`MCP resources (${resources.length}):`);
|
|
107
|
+
for (const r of resources) {
|
|
108
|
+
lines.push(` ${r.uri} [${r.server}]${r.name ? " — " + r.name : ""}`);
|
|
109
|
+
}
|
|
110
|
+
lines.push("");
|
|
111
|
+
lines.push(`MCP prompts (${prompts.length}):`);
|
|
112
|
+
for (const p of prompts) {
|
|
113
|
+
lines.push(
|
|
114
|
+
` /mcp__${p.server}__${p.name}${p.description ? " — " + p.description : ""}`,
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
if (resources.length === 0 && prompts.length === 0) {
|
|
118
|
+
lines.push("");
|
|
119
|
+
lines.push("(connected servers expose no resources or prompts)");
|
|
120
|
+
}
|
|
121
|
+
return lines.join("\n");
|
|
122
|
+
}
|
|
@@ -222,7 +222,10 @@ async function runSettingsPreToolUseHooks(name, args, context, cwd) {
|
|
|
222
222
|
cwd,
|
|
223
223
|
session_id: context.sessionId || null,
|
|
224
224
|
};
|
|
225
|
-
const outcome = runCommandHooks(matched, payload, {
|
|
225
|
+
const outcome = runCommandHooks(matched, payload, {
|
|
226
|
+
cwd,
|
|
227
|
+
event: "PreToolUse",
|
|
228
|
+
});
|
|
226
229
|
if (outcome.decision === "block") {
|
|
227
230
|
return { blocked: true, reason: outcome.reason, hook: outcome.hook };
|
|
228
231
|
}
|
|
@@ -234,8 +237,7 @@ async function runSettingsPreToolUseHooks(name, args, context, cwd) {
|
|
|
234
237
|
tool: name,
|
|
235
238
|
args,
|
|
236
239
|
rule: `hook:${outcome.hook}`,
|
|
237
|
-
reason:
|
|
238
|
-
outcome.reason || "a PreToolUse hook requests confirmation",
|
|
240
|
+
reason: outcome.reason || "a PreToolUse hook requests confirmation",
|
|
239
241
|
})
|
|
240
242
|
: false;
|
|
241
243
|
return ok
|
|
@@ -730,7 +732,11 @@ export async function executeTool(name, args, context = {}) {
|
|
|
730
732
|
if (!ok) {
|
|
731
733
|
return {
|
|
732
734
|
error: `[Permission] Tool "${name}" requires confirmation (settings rule: ${settingsVerdict.rule}) — denied.`,
|
|
733
|
-
policy: {
|
|
735
|
+
policy: {
|
|
736
|
+
decision: "ask",
|
|
737
|
+
rule: settingsVerdict.rule,
|
|
738
|
+
via: "settings",
|
|
739
|
+
},
|
|
734
740
|
};
|
|
735
741
|
}
|
|
736
742
|
ruleAllowed = true; // confirmed → treat like allow downstream
|
|
@@ -889,6 +895,41 @@ export async function executeTool(name, args, context = {}) {
|
|
|
889
895
|
}
|
|
890
896
|
}
|
|
891
897
|
|
|
898
|
+
// settings.json SubagentStop hooks: fire when a `spawn_sub_agent` tool call
|
|
899
|
+
// finishes (Claude-Code SubagentStop parity). The subagent has already
|
|
900
|
+
// returned its summary, so this is observe + feedback rather than force-
|
|
901
|
+
// continue: a `block` reason is surfaced to the PARENT agent as hookFeedback
|
|
902
|
+
// so it can react (e.g. re-spawn or adjust), mirroring PostToolUse.
|
|
903
|
+
if (
|
|
904
|
+
name === "spawn_sub_agent" &&
|
|
905
|
+
context.settingsHooks &&
|
|
906
|
+
toolResult &&
|
|
907
|
+
typeof toolResult === "object"
|
|
908
|
+
) {
|
|
909
|
+
try {
|
|
910
|
+
const outcome = runObserveHooks(
|
|
911
|
+
context.settingsHooks,
|
|
912
|
+
"SubagentStop",
|
|
913
|
+
{
|
|
914
|
+
stop_hook_active: false,
|
|
915
|
+
session_id: context.sessionId || null,
|
|
916
|
+
subagent_response:
|
|
917
|
+
typeof toolResult === "object"
|
|
918
|
+
? JSON.stringify(toolResult).substring(0, 2000)
|
|
919
|
+
: String(toolResult).substring(0, 2000),
|
|
920
|
+
},
|
|
921
|
+
{ cwd },
|
|
922
|
+
);
|
|
923
|
+
if (outcome.decision === "block" && outcome.reason) {
|
|
924
|
+
toolResult.hookFeedback = toolResult.hookFeedback
|
|
925
|
+
? `${toolResult.hookFeedback}\n${outcome.reason}`
|
|
926
|
+
: outcome.reason;
|
|
927
|
+
}
|
|
928
|
+
} catch (_err) {
|
|
929
|
+
// SubagentStop hooks are best-effort
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
|
|
892
933
|
return toolResult;
|
|
893
934
|
}
|
|
894
935
|
|
|
@@ -1240,12 +1281,8 @@ async function executeToolInner(
|
|
|
1240
1281
|
...(task.error ? { error: task.error } : {}),
|
|
1241
1282
|
stdout: out.text.substring(0, 30000),
|
|
1242
1283
|
stderr: err.text.substring(0, 30000),
|
|
1243
|
-
...(out.droppedGap
|
|
1244
|
-
|
|
1245
|
-
: {}),
|
|
1246
|
-
...(err.droppedGap
|
|
1247
|
-
? { stderr_dropped_bytes: err.droppedGap }
|
|
1248
|
-
: {}),
|
|
1284
|
+
...(out.droppedGap ? { stdout_dropped_bytes: out.droppedGap } : {}),
|
|
1285
|
+
...(err.droppedGap ? { stderr_dropped_bytes: err.droppedGap } : {}),
|
|
1249
1286
|
...(killed ? { killed: true } : {}),
|
|
1250
1287
|
startedAt: task.startedAt,
|
|
1251
1288
|
endedAt: task.endedAt,
|
|
@@ -1635,6 +1672,50 @@ async function executeToolInner(
|
|
|
1635
1672
|
}
|
|
1636
1673
|
|
|
1637
1674
|
default:
|
|
1675
|
+
if (localToolExecutor?.kind === "mcp-resource") {
|
|
1676
|
+
if (!mcpClient) {
|
|
1677
|
+
return attachDescriptor({
|
|
1678
|
+
error: `MCP client is unavailable for tool: ${name}`,
|
|
1679
|
+
});
|
|
1680
|
+
}
|
|
1681
|
+
try {
|
|
1682
|
+
if (localToolExecutor.op === "list") {
|
|
1683
|
+
const resources =
|
|
1684
|
+
typeof mcpClient.listResources === "function"
|
|
1685
|
+
? mcpClient.listResources(args?.server || undefined)
|
|
1686
|
+
: [];
|
|
1687
|
+
return attachDescriptor({ count: resources.length, resources });
|
|
1688
|
+
}
|
|
1689
|
+
// op === "read"
|
|
1690
|
+
const uri = args?.uri;
|
|
1691
|
+
if (!uri || typeof uri !== "string") {
|
|
1692
|
+
return attachDescriptor({
|
|
1693
|
+
error: "read_mcp_resource requires a string 'uri' argument.",
|
|
1694
|
+
});
|
|
1695
|
+
}
|
|
1696
|
+
let server = args?.server;
|
|
1697
|
+
if (!server && typeof mcpClient.listResources === "function") {
|
|
1698
|
+
const match = mcpClient
|
|
1699
|
+
.listResources()
|
|
1700
|
+
.find((r) => r && r.uri === uri);
|
|
1701
|
+
server = match?.server;
|
|
1702
|
+
}
|
|
1703
|
+
if (!server) {
|
|
1704
|
+
return attachDescriptor({
|
|
1705
|
+
error: `Could not resolve which MCP server owns resource "${uri}". Pass 'server' explicitly.`,
|
|
1706
|
+
});
|
|
1707
|
+
}
|
|
1708
|
+
const result = await mcpClient.readResource(server, uri);
|
|
1709
|
+
return attachDescriptor(
|
|
1710
|
+
result && typeof result === "object" ? result : { result },
|
|
1711
|
+
);
|
|
1712
|
+
} catch (err) {
|
|
1713
|
+
return attachDescriptor({
|
|
1714
|
+
error: `MCP resource access failed: ${err.message}`,
|
|
1715
|
+
});
|
|
1716
|
+
}
|
|
1717
|
+
}
|
|
1718
|
+
|
|
1638
1719
|
if (localToolExecutor?.kind === "mcp") {
|
|
1639
1720
|
if (!mcpClient || typeof mcpClient.callTool !== "function") {
|
|
1640
1721
|
return attachDescriptor({
|
|
@@ -2601,7 +2682,13 @@ export function _accumulateAnthropicStream(lines, onToken) {
|
|
|
2601
2682
|
return _anthropicFinalize(state);
|
|
2602
2683
|
}
|
|
2603
2684
|
|
|
2604
|
-
async function _chatAnthropicStreaming(
|
|
2685
|
+
async function _chatAnthropicStreaming(
|
|
2686
|
+
apiUrl,
|
|
2687
|
+
body,
|
|
2688
|
+
extraHeaders,
|
|
2689
|
+
onToken,
|
|
2690
|
+
signal,
|
|
2691
|
+
) {
|
|
2605
2692
|
const response = await fetch(apiUrl, {
|
|
2606
2693
|
method: "POST",
|
|
2607
2694
|
headers: { "Content-Type": "application/json", ...extraHeaders },
|
|
@@ -2663,10 +2750,12 @@ function _openaiReduceLine(state, raw, onToken) {
|
|
|
2663
2750
|
if (Array.isArray(delta?.tool_calls)) {
|
|
2664
2751
|
for (const tc of delta.tool_calls) {
|
|
2665
2752
|
const idx = tc.index ?? 0;
|
|
2666
|
-
if (!state.tools[idx])
|
|
2753
|
+
if (!state.tools[idx])
|
|
2754
|
+
state.tools[idx] = { id: undefined, name: "", args: "" };
|
|
2667
2755
|
if (tc.id) state.tools[idx].id = tc.id;
|
|
2668
2756
|
if (tc.function?.name) state.tools[idx].name = tc.function.name;
|
|
2669
|
-
if (tc.function?.arguments)
|
|
2757
|
+
if (tc.function?.arguments)
|
|
2758
|
+
state.tools[idx].args += tc.function.arguments;
|
|
2670
2759
|
}
|
|
2671
2760
|
}
|
|
2672
2761
|
if (obj.usage) {
|
|
@@ -2702,7 +2791,14 @@ export function _accumulateOpenAIStream(lines, onToken) {
|
|
|
2702
2791
|
return _openaiFinalize(state);
|
|
2703
2792
|
}
|
|
2704
2793
|
|
|
2705
|
-
async function _chatOpenAIStreaming(
|
|
2794
|
+
async function _chatOpenAIStreaming(
|
|
2795
|
+
apiUrl,
|
|
2796
|
+
body,
|
|
2797
|
+
apiKey,
|
|
2798
|
+
onToken,
|
|
2799
|
+
signal,
|
|
2800
|
+
provider,
|
|
2801
|
+
) {
|
|
2706
2802
|
const response = await fetch(apiUrl, {
|
|
2707
2803
|
method: "POST",
|
|
2708
2804
|
headers: {
|
|
@@ -2843,7 +2939,11 @@ function _intensityToEffort(want) {
|
|
|
2843
2939
|
* RUNTIME-UNVERIFIED — no Anthropic key to validate the wire shape live.
|
|
2844
2940
|
* Exported for tests.
|
|
2845
2941
|
*/
|
|
2846
|
-
export function _anthropicThinkingParams(
|
|
2942
|
+
export function _anthropicThinkingParams(
|
|
2943
|
+
model,
|
|
2944
|
+
options = {},
|
|
2945
|
+
maxTokens = 8192,
|
|
2946
|
+
) {
|
|
2847
2947
|
const want = options?.thinking;
|
|
2848
2948
|
if (!want) return null; // off by default → request unchanged
|
|
2849
2949
|
const m = String(model || "").toLowerCase();
|
|
@@ -3181,7 +3281,11 @@ export async function* agentLoop(messages, options) {
|
|
|
3181
3281
|
}
|
|
3182
3282
|
}
|
|
3183
3283
|
if (preCompactBlocked) {
|
|
3184
|
-
yield {
|
|
3284
|
+
yield {
|
|
3285
|
+
type: "compaction-skipped",
|
|
3286
|
+
runId,
|
|
3287
|
+
reason: preCompactReason,
|
|
3288
|
+
};
|
|
3185
3289
|
}
|
|
3186
3290
|
const { messages: compacted, stats } = preCompactBlocked
|
|
3187
3291
|
? { messages, stats: { saved: 0 } }
|
|
@@ -3400,7 +3504,9 @@ export function formatToolArgs(name, args) {
|
|
|
3400
3504
|
case "edit_file_hashed":
|
|
3401
3505
|
return `${args.path} @${args.anchor_hash}`;
|
|
3402
3506
|
case "run_shell":
|
|
3403
|
-
return args.run_in_background
|
|
3507
|
+
return args.run_in_background
|
|
3508
|
+
? `${args.command} (background)`
|
|
3509
|
+
: args.command;
|
|
3404
3510
|
case "check_shell":
|
|
3405
3511
|
return args.task_id
|
|
3406
3512
|
? `${args.task_id}${args.kill ? " (kill)" : ""}`
|
|
@@ -176,6 +176,34 @@ export function resolveHeadlessSession(options = {}, store = {}, fallbackId) {
|
|
|
176
176
|
return { sessionId: id, resumeId, persist, wantLatest };
|
|
177
177
|
}
|
|
178
178
|
|
|
179
|
+
/**
|
|
180
|
+
* Apply `--fork-session`: when a session has been resolved (resume/continue) and
|
|
181
|
+
* a fork is requested, branch its JSONL transcript into a NEW id so the original
|
|
182
|
+
* stays untouched (Claude-Code `--fork-session` parity). The copy carries the
|
|
183
|
+
* full prior history, so a later `--resume <newId>` replays the whole branch.
|
|
184
|
+
*
|
|
185
|
+
* Returns the id to use downstream + the source (`forkedFrom`), or the original
|
|
186
|
+
* id unchanged with `missing:true` when there is no transcript to fork. Pure
|
|
187
|
+
* apart from the injected store's side effect; both `sessionExists`/`forkSession`
|
|
188
|
+
* are injection seams so this is unit-testable without disk.
|
|
189
|
+
*
|
|
190
|
+
* @param {{forkSession?:boolean, sessionId?:string|null}} opts
|
|
191
|
+
* @param {{sessionExists?:Function, forkSession?:Function}} store
|
|
192
|
+
* @returns {{ sessionId:string|null, forkedFrom:string|null, missing:boolean }}
|
|
193
|
+
*/
|
|
194
|
+
export function applyForkSession(opts = {}, store = {}) {
|
|
195
|
+
const want = opts.forkSession === true;
|
|
196
|
+
const id = opts.sessionId || null;
|
|
197
|
+
if (!want || !id) return { sessionId: id, forkedFrom: null, missing: false };
|
|
198
|
+
if (typeof store.sessionExists === "function" && !store.sessionExists(id)) {
|
|
199
|
+
return { sessionId: id, forkedFrom: null, missing: true };
|
|
200
|
+
}
|
|
201
|
+
const newId =
|
|
202
|
+
typeof store.forkSession === "function" ? store.forkSession(id) : null;
|
|
203
|
+
if (!newId) return { sessionId: id, forkedFrom: null, missing: false };
|
|
204
|
+
return { sessionId: newId, forkedFrom: id, missing: false };
|
|
205
|
+
}
|
|
206
|
+
|
|
179
207
|
/**
|
|
180
208
|
* Run a single headless agentic turn.
|
|
181
209
|
*
|
|
@@ -404,9 +432,8 @@ export async function runAgentHeadless(options = {}, deps = {}) {
|
|
|
404
432
|
// settings.json UserPromptSubmit hooks. block → abort the run; context → inject.
|
|
405
433
|
if (settingsHooks) {
|
|
406
434
|
try {
|
|
407
|
-
const { runUserPromptSubmitHooks } =
|
|
408
|
-
"../lib/settings-hook-events.cjs"
|
|
409
|
-
);
|
|
435
|
+
const { runUserPromptSubmitHooks } =
|
|
436
|
+
await import("../lib/settings-hook-events.cjs");
|
|
410
437
|
const ups = runUserPromptSubmitHooks(settingsHooks, {
|
|
411
438
|
prompt: userContent,
|
|
412
439
|
cwd,
|
|
@@ -434,9 +461,8 @@ export async function runAgentHeadless(options = {}, deps = {}) {
|
|
|
434
461
|
let sessionStartContext = null;
|
|
435
462
|
if (settingsHooks) {
|
|
436
463
|
try {
|
|
437
|
-
const { runSessionStartHooks } =
|
|
438
|
-
"../lib/settings-hook-events.cjs"
|
|
439
|
-
);
|
|
464
|
+
const { runSessionStartHooks } =
|
|
465
|
+
await import("../lib/settings-hook-events.cjs");
|
|
440
466
|
sessionStartContext = runSessionStartHooks(settingsHooks, {
|
|
441
467
|
source: resumeId ? "resume" : "startup",
|
|
442
468
|
cwd,
|
|
@@ -766,9 +792,8 @@ export async function runAgentHeadless(options = {}, deps = {}) {
|
|
|
766
792
|
// settings.json SessionEnd hooks (observe-only) when the run finishes.
|
|
767
793
|
if (settingsHooks) {
|
|
768
794
|
try {
|
|
769
|
-
const { runObserveHooks } =
|
|
770
|
-
"../lib/settings-hook-events.cjs"
|
|
771
|
-
);
|
|
795
|
+
const { runObserveHooks } =
|
|
796
|
+
await import("../lib/settings-hook-events.cjs");
|
|
772
797
|
runObserveHooks(
|
|
773
798
|
settingsHooks,
|
|
774
799
|
"SessionEnd",
|