chainlesschain 0.162.163 → 0.162.165
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 -1
- package/src/assets/web-panel/assets/{AIOps-BQ0kgfA1.js → AIOps-DgGZr3PR.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-Dr1MDANe.js → ActionButton-BAOgBIc_.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-CZ5vsaXW.js → Analytics-Ds4GAvNF.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-BIBga881.js → AppLayout-CoJvCkoF.js} +4 -4
- package/src/assets/web-panel/assets/{Artifacts-vpXHRU3u.js → Artifacts-6EgUaE1T.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-B8exuoub.js → Audit-BYie_i74.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-923evRRN.js → BackgroundAgents-D5gxOhjd.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-sr_sswlQ.js → Backup-CIsVK2pT.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-CnW-4XKT.js → BaseInput-ClorP55M.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-FZCsTZIL.js → Chat-CnhTPGGA.js} +6 -6
- package/src/assets/web-panel/assets/ChatBubbleRenderer-Bl1VXgwp.js +1 -0
- package/src/assets/web-panel/assets/{Checkbox-DFrxB59H.js → Checkbox-BRavl7TP.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-_b6WEel8.js → Codegen-BXg_ErNT.js} +1 -1
- package/src/assets/web-panel/assets/{Col-CkbnnZ-K.js → Col-CfDPzFPq.js} +1 -1
- package/src/assets/web-panel/assets/{Community-BxM_82zW.js → Community-Cgo0httP.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-DYinP45R.js → Compact-DU5JcEQM.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-DyXp3sSP.js → Compliance-DCH_TVBl.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-BEr2DhW7.js → Cowork-IGb5O2IH.js} +2 -2
- package/src/assets/web-panel/assets/{Cron-B-Vwo1hN.js → Cron-CPp_Xp9F.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-DbLxKddd.js → Crosschain-Cwrqklzw.js} +1 -1
- package/src/assets/web-panel/assets/{DID-BsQ_LFw9.js → DID-D55sBQwM.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-lVlXQHu5.js → Dashboard-CedXhGmP.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-Beo_foO7.js → Dropdown-Dm6_v3V4.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-DszHIXED.js → EmailListRenderer-CWjnuE3G.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-DcAwoh81.js → FamilyGuardDashboard-DubTh7y-.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-CuJLa2eb.js → Federation-DlSM4uJv.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-Cxd1lxhc.js → FormItemContext-BYhg0vtG.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-BBvq5Jsw.js → GenericCardRenderer-Bkh7qjfK.js} +1 -1
- package/src/assets/web-panel/assets/{Git-WqPKetx6.js → Git-DSLWPKvn.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-CWYtY3ke.js → Governance-DIj9O8BB.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-BnX1iiNV.js → Inference-Dhep3e5Y.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-BJuM8GN5.js → KnowledgeGraph-Cj8vzxB_.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-DqLMNntm.js → Logs-Ba4SsPja.js} +2 -2
- package/src/assets/web-panel/assets/MarkdownRenderer-DUjyuVB3.js +1 -0
- package/src/assets/web-panel/assets/{Marketplace-_vGfmhNl.js → Marketplace-CGs9IIbf.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-BeJDOkDj.js → McpTools-BTPAoX4K.js} +4 -4
- package/src/assets/web-panel/assets/{Memory-Da0EEZDO.js → Memory-BJ6ISf2c.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-DSuzcCCy.js → MobileBridge-Ca6n3Dcn.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-D0RAk4e8.js → MobileProjects-lvilHuLt.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-BE9YgdA0.js → Mtc-CMo0acBV.js} +2 -2
- package/src/assets/web-panel/assets/{MtcAudit-fC2BpO5S.js → MtcAudit-Hz5wBvFX.js} +4 -4
- package/src/assets/web-panel/assets/{Multisig-D24Xxqw3.js → Multisig-BK2aKWE6.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-Bs3oGvS8.js → NLProgramming-CExpnYMI.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-BFjmAA-3.js → Notes-B7IFbuBo.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-BX_9Bhv0.js → NotificationSettings-VrGchtb0.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-wjkZwJXI.js → OrderTableRenderer-CyrNTxkU.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-DBcdsVEP.js → Organization-I0C17GBP.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-DDyaF9vC.js → Overflow-CXcWvERe.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-BB78L4he.js → P2P-FFSCXrZq.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-DqTiZFum.js → PdhVaultBrowser-1PB-p8Zk.js} +3 -3
- package/src/assets/web-panel/assets/{Permissions-INbnWv4j.js → Permissions-C1YHejsb.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-BST-VmTq.js → PersonalDataHub-CEMPpgf8.js} +2 -2
- package/src/assets/web-panel/assets/{Pipeline-CE7_tS2k.js → Pipeline-FoIScdLr.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-hK5L8dOB.js → Privacy-DDfbEzEj.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-BvkkUoDn.js → ProjectInit-Dso4kB0O.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-u7_EUtkm.js → ProjectSettings-WvDoydDc.js} +2 -2
- package/src/assets/web-panel/assets/{Projects-5Fmt_8Nw.js → Projects-NJp0kzkd.js} +1 -1
- package/src/assets/web-panel/assets/{Providers-B9eanarR.js → Providers-BuJXnOLI.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-C0dX5N2f.js → QrScannerModal-Bb3lMvGG.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-CKTpG-PZ.js → QuickAsk-Ddxra3fK.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-Px_w_XQM.js → Recommend-BW-dR9qw.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-Di0fnYkM.js → RemoteSession-Hnm7-Qkm.js} +2 -2
- package/src/assets/web-panel/assets/{Reputation-vesnWjXt.js → Reputation-DKz6meHE.js} +1 -1
- package/src/assets/web-panel/assets/{Row-exbY6LkT.js → Row-BuAsoNTS.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-B3O3x70t.js → RssFeed-_do9DMIH.js} +2 -2
- package/src/assets/web-panel/assets/{Search-uRA0DhNz.js → Search-CMlcw7ND.js} +1 -1
- package/src/assets/web-panel/assets/{Security-CViqdH6K.js → Security-DhAkfSpM.js} +3 -3
- package/src/assets/web-panel/assets/{Services-CkXS97WN.js → Services-5zKLhy46.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-DxW5WYnE.js → Skeleton-DCMZ3wfv.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-CLxmGFse.js → Skills-DxQgYCC_.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-Cr5fziXq.js → Sla-CJmE4cSN.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-H0JSb3cp.js → SpeechSettings-BgpRTPQp.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-DMYWMae7.js → SyncSettings-DkogtYbB.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-CbYyDdF4.js → Tasks-D5OrwAl9.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-Cpc8JjiK.js → Templates-DAZIu5nN.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-CRh77Jda.js → Tenant-BRJMI-su.js} +1 -1
- package/src/assets/web-panel/assets/Terminal-CMXLqQKF.js +3 -0
- package/src/assets/web-panel/assets/{TimelineRenderer-D41jzzbG.js → TimelineRenderer-D4z9aSGD.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-C_jWEy6b.js → Tokens-zgYA4UOI.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-Dk1ZUhsy.js → Trigger-DMOgHX4m.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-CLVy4l7m.js → Trust-CEtoNwcT.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-C-FeqFDj.js → UkeySign-DFx2pQfO.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-CqVZzyMm.js → VideoEditing-DLssy-7B.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-DX9BW9pu.js → Wallet-DQHc6gTl.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-7hQDpWw_.js → WebAuthn-DgKcODkn.js} +2 -2
- package/src/assets/web-panel/assets/{WorkflowEditor-6XqUAO0j.js → WorkflowEditor-cPEWfMOf.js} +1 -1
- package/src/assets/web-panel/assets/{chat-5R_vkaJG.js → chat-Coj7rSO1.js} +1 -1
- package/src/assets/web-panel/assets/{colors-huUPZdC-.js → colors-DiyGfr-6.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-CfJlgisE.js → compact-item-DB63aMXh.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-jO8heofr.js → createContext-CENpsYHg.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-XvRaIiyt.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-DY8Vxqx7.js → hasIn-B9NOmk0K.js} +1 -1
- package/src/assets/web-panel/assets/{index-B300RPnT.js → index-76GMjcbq.js} +3 -3
- package/src/assets/web-panel/assets/{index-CA6sO5VD.js → index-7IMjHwGU.js} +1 -1
- package/src/assets/web-panel/assets/{index-D1VLuww0.js → index-B0C6zl0y.js} +1 -1
- package/src/assets/web-panel/assets/{index-D0pet5Uy.js → index-B45osqnZ.js} +1 -1
- package/src/assets/web-panel/assets/{index-BHE8OIls.js → index-BXzMKVLp.js} +1 -1
- package/src/assets/web-panel/assets/{index-vXs3B8GJ.js → index-BkoqtJzV.js} +1 -1
- package/src/assets/web-panel/assets/{index-d1xkbtNz.js → index-BpmTl8QC.js} +1 -1
- package/src/assets/web-panel/assets/{index-BfCx4pFT.js → index-BramLq29.js} +1 -1
- package/src/assets/web-panel/assets/{index-p85YaaKw.js → index-BvQq6gX2.js} +1 -1
- package/src/assets/web-panel/assets/{index-BsBtRWbA.js → index-Bvj07Ab1.js} +1 -1
- package/src/assets/web-panel/assets/{index-KRHroryI.js → index-BxvNtndl.js} +1 -1
- package/src/assets/web-panel/assets/{index-BmqGerpC.js → index-C9d8hdgY.js} +1 -1
- package/src/assets/web-panel/assets/{index-CA90TZxG.js → index-CAXouogg.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bxax9vQ6.js → index-CKNt2vfs.js} +1 -1
- package/src/assets/web-panel/assets/{index-BYLBPpt8.js → index-CRhEJ8JO.js} +1 -1
- package/src/assets/web-panel/assets/{index-DXBcvUnf.js → index-CTuX_MU0.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cjxbu68P.js → index-CXRknr9W.js} +1 -1
- package/src/assets/web-panel/assets/index-CbO1zSXV.js +1 -0
- package/src/assets/web-panel/assets/{index-Cbe9G0V4.js → index-ClEy1v1o.js} +1 -1
- package/src/assets/web-panel/assets/{index-CRElRhyS.js → index-CmNCDN5t.js} +1 -1
- package/src/assets/web-panel/assets/{index-v3pTaEQm.js → index-CvTJ-x6b.js} +1 -1
- package/src/assets/web-panel/assets/{index-ChvHubX3.js → index-CwkK9Kl2.js} +1 -1
- package/src/assets/web-panel/assets/{index-CLhBh-qE.js → index-CxVfbuj9.js} +1 -1
- package/src/assets/web-panel/assets/{index-BQS-uPOK.js → index-DAMKhjtT.js} +1 -1
- package/src/assets/web-panel/assets/{index-C2iVSFpy.js → index-DANoIqXP.js} +1 -1
- package/src/assets/web-panel/assets/{index-B74OXFxC.js → index-D_rZCxxI.js} +1 -1
- package/src/assets/web-panel/assets/{index-dKx57io7.js → index-Dg0v-CXZ.js} +1 -1
- package/src/assets/web-panel/assets/{index-BRXOP09X.js → index-DqLC0iE4.js} +1 -1
- package/src/assets/web-panel/assets/{index-BtEXSoPL.js → index-DsPDEHOg.js} +1 -1
- package/src/assets/web-panel/assets/{index-DoI2sZm7.js → index-KDCuTzdx.js} +1 -1
- package/src/assets/web-panel/assets/{index-BAON7TzU.js → index-Kp2AETEw.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cis_Ip7H.js → index-U_snupDl.js} +1 -1
- package/src/assets/web-panel/assets/{index-j3O2EEJe.js → index-VDFbduzG.js} +1 -1
- package/src/assets/web-panel/assets/{index-BliaT02i.js → index-Y_UKQpsV.js} +1 -1
- package/src/assets/web-panel/assets/{index-A48PtXw9.js → index-Yb9EfUnY.js} +1 -1
- package/src/assets/web-panel/assets/index-aCu2gbUb.js +1 -0
- package/src/assets/web-panel/assets/{index-CzKmteNS.js → index-mTK_ZHhE.js} +1 -1
- package/src/assets/web-panel/assets/{index-BCqxyvqp.js → index-nio98wGC.js} +1 -1
- package/src/assets/web-panel/assets/{index-Db1cLWir.js → index-t6hXF2Cw.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-MCB7-raZ.js → initDefaultProps-GTwJVIOz.js} +1 -1
- package/src/assets/web-panel/assets/{motion-UWDF9swV.js → motion-BJQLsPx8.js} +1 -1
- package/src/assets/web-panel/assets/{move-CsTmgXZU.js → move-DVQM0bdG.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-fQy6KGLg.js → mtc-parser-CMXI1I2y.js} +1 -1
- package/src/assets/web-panel/assets/{omit-woPnEjd1.js → omit-CAc8MDw9.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-Bs2dZ1gf.js → pickAttrs-BgqzoyP7.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-C7EVgPkg.js → placementArrow-B9ebCdRy.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-BH2kYCTN.js → responsiveObserve-Dy8D0D3n.js} +1 -1
- package/src/assets/web-panel/assets/{slide-BFBP2V_e.js → slide-Bcrc8J39.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-xdDxDYse.js → statusUtils-CDW6FOlc.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-aAWgPPQf.js → styleChecker-CWhJrDON.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-DkGSAWoO.js → useFlexGapSupport-C82c33XI.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-BrOih40N.js → useFs-A6m--mih.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-eIYrgMqO.js → usePersonalDataHub-m-1laJY5.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-B8lutNSE.js → vnode-CL9oKKRa.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-CffNre7V.js → zoom-DrOyaof3.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/commands/agenda.js +71 -14
- package/src/commands/agent.js +14 -0
- package/src/commands/doctor.js +85 -1
- package/src/commands/plugin.js +226 -9
- package/src/commands/review.js +17 -1
- package/src/commands/session.js +191 -0
- package/src/commands/team.js +19 -1
- package/src/data/changelog.json +10 -4
- package/src/gateways/ws/session-protocol.js +50 -0
- package/src/gateways/ws/ws-agent-handler.js +50 -0
- package/src/harness/jsonl-session-store.js +60 -0
- package/src/harness/worktree-isolator.js +140 -5
- package/src/lib/agent-schedule-store.js +30 -1
- package/src/lib/agent-team/team-worktree.js +37 -2
- package/src/lib/agent-tool-admission.js +125 -0
- package/src/lib/app-preview.js +360 -0
- package/src/lib/async-hook-supervisor.cjs +54 -0
- package/src/lib/capability-manifest.js +319 -0
- package/src/lib/capability-negotiation.js +23 -12
- package/src/lib/diagnostic-bundle.js +230 -0
- package/src/lib/doctor-checkup.js +223 -4
- package/src/lib/execution-location.js +286 -0
- package/src/lib/headless-manifest.js +13 -40
- package/src/lib/hook-stats-store.cjs +197 -0
- package/src/lib/idempotency.js +109 -0
- package/src/lib/jsonl-session-store.js +1 -0
- package/src/lib/lsp/__tests__/lsp-manager.test.js +58 -0
- package/src/lib/lsp/lsp-manager.js +56 -0
- package/src/lib/monitor-event.js +290 -0
- package/src/lib/operation-fingerprint.js +217 -0
- package/src/lib/plugin-runtime/bin.js +37 -0
- package/src/lib/plugin-runtime/capabilities.js +19 -0
- package/src/lib/plugin-runtime/capability-consent.js +28 -0
- package/src/lib/plugin-runtime/hooks.js +37 -0
- package/src/lib/plugin-runtime/lsp.js +38 -0
- package/src/lib/plugin-runtime/mcp.js +40 -0
- package/src/lib/plugin-runtime/monitors.js +37 -0
- package/src/lib/plugin-runtime/plugin-options.js +151 -0
- package/src/lib/pr-automation-policy.js +224 -0
- package/src/lib/project-instructions.js +9 -1
- package/src/lib/remote-approval-bridge.js +66 -13
- package/src/lib/repl-rewind.js +101 -0
- package/src/lib/review-comment-anchor.js +241 -0
- package/src/lib/review-pipeline.js +46 -7
- package/src/lib/runtime-checkup.js +221 -0
- package/src/lib/secret-scan.js +409 -0
- package/src/lib/session-branch.js +0 -0
- package/src/lib/side-effect-ledger.js +105 -0
- package/src/lib/skill-loader.js +32 -0
- package/src/lib/skill-path-scope.js +63 -0
- package/src/lib/sub-agent-context.js +36 -4
- package/src/lib/sub-agent-registry.js +19 -0
- package/src/lib/telemetry/span-recorder.js +17 -4
- package/src/lib/terminal-context.js +195 -0
- package/src/lib/unattended-action-policy.js +182 -0
- package/src/lib/worktree-cleanup-safety.js +138 -0
- package/src/lib/worktree-isolator.js +8 -0
- package/src/lib/worktree-sparse.js +131 -0
- package/src/repl/agent-repl.js +169 -40
- package/src/runtime/agent-core.js +220 -24
- package/src/runtime/diagnostics.js +60 -0
- package/src/runtime/headless-runner.js +135 -2
- package/src/runtime/headless-stream.js +10 -1
- package/src/runtime/policies/agent-policy.js +5 -0
- package/src/runtime/system-prompt.js +21 -8
- package/src/assets/web-panel/assets/ChatBubbleRenderer-DDPd9qT0.js +0 -1
- package/src/assets/web-panel/assets/MarkdownRenderer-BqYwEYou.js +0 -1
- package/src/assets/web-panel/assets/Terminal-4D8tNBO5.js +0 -3
- package/src/assets/web-panel/assets/devWarning-DeIrCfH-.js +0 -1
- package/src/assets/web-panel/assets/index-BxUKMRpP.js +0 -1
- package/src/assets/web-panel/assets/index-DscU9iPI.js +0 -1
|
@@ -26,6 +26,7 @@ import sharedHookRunner from "../lib/hook-runner.cjs";
|
|
|
26
26
|
import sharedHookEvents from "../lib/settings-hook-events.cjs";
|
|
27
27
|
import { mergeProviderOptions } from "../lib/provider-options.js";
|
|
28
28
|
import { applyCredentialProxy } from "../lib/credential-proxy.js";
|
|
29
|
+
import { describeBackgroundCommand } from "../lib/terminal-context.js";
|
|
29
30
|
import { buildTelemetryAttributes } from "../lib/telemetry-ids.js";
|
|
30
31
|
import { getPlanModeManager } from "../lib/plan-mode.js";
|
|
31
32
|
import { CLISkillLoader } from "../lib/skill-loader.js";
|
|
@@ -45,6 +46,11 @@ import {
|
|
|
45
46
|
import { createToolContext } from "../tools/tool-context.js";
|
|
46
47
|
import { createToolTelemetryRecord } from "../tools/tool-telemetry.js";
|
|
47
48
|
import { isAbortError, throwIfAborted } from "../lib/abort-utils.js";
|
|
49
|
+
import {
|
|
50
|
+
classifyEditReplay,
|
|
51
|
+
editIdempotencyKey,
|
|
52
|
+
EDIT_REPLAY,
|
|
53
|
+
} from "../lib/idempotency.js";
|
|
48
54
|
import { buildSearchCommand } from "../lib/search-command.js";
|
|
49
55
|
import { discoverCommands } from "../lib/slash-commands.js";
|
|
50
56
|
import {
|
|
@@ -138,18 +144,37 @@ function _readBgStream(stream) {
|
|
|
138
144
|
}
|
|
139
145
|
|
|
140
146
|
/**
|
|
141
|
-
* Snapshot of background shell tasks (for REPL/host status surfaces
|
|
142
|
-
*
|
|
147
|
+
* Snapshot of background shell tasks (for REPL/host status surfaces AND the
|
|
148
|
+
* model-facing `check_shell { list:true }` tool).
|
|
149
|
+
*
|
|
150
|
+
* P1-2 (terminal-context): the `command` string can carry an inline secret
|
|
151
|
+
* (`FOO=token cmd`, `curl -H "Authorization: Bearer …"`); this list is shown to
|
|
152
|
+
* the model and rendered in `/tasks`, so run it through
|
|
153
|
+
* `describeBackgroundCommand` to secret-redact the command and expose the PID.
|
|
154
|
+
* The task's own lifecycle `status` (running/exited/failed/error) is preserved
|
|
155
|
+
* — that is distinct from a health check, so it is NOT replaced. `stoppable` is
|
|
156
|
+
* gated on the task still running (a valid pid alone doesn't make a dead task
|
|
157
|
+
* stoppable).
|
|
158
|
+
* @returns {Array<{id:string,status:string,command:string,pid:number|null,
|
|
159
|
+
* stoppable:boolean,exitCode:number|null,startedAt:string,endedAt:string}>}
|
|
143
160
|
*/
|
|
144
161
|
export function listBackgroundShellTasks() {
|
|
145
|
-
return Array.from(_backgroundShellTasks.values()).map((t) =>
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
162
|
+
return Array.from(_backgroundShellTasks.values()).map((t) => {
|
|
163
|
+
const desc = describeBackgroundCommand({
|
|
164
|
+
command: t.command,
|
|
165
|
+
pid: t.child?.pid,
|
|
166
|
+
});
|
|
167
|
+
return {
|
|
168
|
+
id: t.id,
|
|
169
|
+
status: t.status,
|
|
170
|
+
command: desc.command, // P1-2: secret-redacted
|
|
171
|
+
pid: desc.pid, // P1-2: PID surfaced
|
|
172
|
+
stoppable: t.status === "running" && desc.stoppable,
|
|
173
|
+
exitCode: t.exitCode,
|
|
174
|
+
startedAt: t.startedAt,
|
|
175
|
+
endedAt: t.endedAt,
|
|
176
|
+
};
|
|
177
|
+
});
|
|
153
178
|
}
|
|
154
179
|
|
|
155
180
|
// Kill a background task's whole process tree. Because tasks are spawned with
|
|
@@ -826,20 +851,38 @@ export function buildSystemPrompt(cwd, opts = {}) {
|
|
|
826
851
|
// Non-critical — skill loader may not be available
|
|
827
852
|
}
|
|
828
853
|
|
|
829
|
-
// Append rules.md
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
854
|
+
// Append rules.md — unless the caller opted into a lean/off prompt. `rules.md`
|
|
855
|
+
// is coding-convention DETAIL (the entry cc.md/CLAUDE.md references it), so it
|
|
856
|
+
// is shed both when project memory is fully off (`--no-project-memory` →
|
|
857
|
+
// projectMemory === false) AND in entry-only lean mode (projectMemory ===
|
|
858
|
+
// "lean", or the env signal CC_PROJECT_MEMORY=lean when no explicit value is
|
|
859
|
+
// threaded). NOTE: legacy `CC_PROJECT_MEMORY=0` intentionally still KEEPS
|
|
860
|
+
// rules.md (its long-standing contract only dropped the instruction block) —
|
|
861
|
+
// so we only honor the "lean" env here, never "0".
|
|
862
|
+
const _pm = opts.projectMemory;
|
|
863
|
+
const _envLean =
|
|
864
|
+
process.env.CC_PROJECT_MEMORY === "lean" ||
|
|
865
|
+
process.env.CC_PROJECT_MEMORY === "entry";
|
|
866
|
+
const _dropRules =
|
|
867
|
+
_pm === false ||
|
|
868
|
+
_pm === "lean" ||
|
|
869
|
+
_pm === "entry" ||
|
|
870
|
+
(_pm == null && _envLean);
|
|
871
|
+
if (!_dropRules) {
|
|
872
|
+
try {
|
|
873
|
+
const projectRoot = findProjectRoot(dir);
|
|
874
|
+
if (projectRoot) {
|
|
875
|
+
const rulesPath = path.join(projectRoot, ".chainlesschain", "rules.md");
|
|
876
|
+
if (fs.existsSync(rulesPath)) {
|
|
877
|
+
const content = fs.readFileSync(rulesPath, "utf-8");
|
|
878
|
+
if (content.trim()) {
|
|
879
|
+
prompt += `\n\n## Project Rules\n${content}`;
|
|
880
|
+
}
|
|
838
881
|
}
|
|
839
882
|
}
|
|
883
|
+
} catch {
|
|
884
|
+
// Non-critical
|
|
840
885
|
}
|
|
841
|
-
} catch {
|
|
842
|
-
// Non-critical
|
|
843
886
|
}
|
|
844
887
|
|
|
845
888
|
// Advertise extra workspace roots (--add-dir) so the model knows it may
|
|
@@ -2100,6 +2143,26 @@ async function _withPostEditDiagnostics(result, filePath, cwd) {
|
|
|
2100
2143
|
return newDiagnostics ? { ...result, newDiagnostics } : result;
|
|
2101
2144
|
}
|
|
2102
2145
|
|
|
2146
|
+
/**
|
|
2147
|
+
* Drop skills whose `paths:` frontmatter scopes them to a different subtree than
|
|
2148
|
+
* the agent's cwd (large-monorepo lazy skill surface). A skill with no `paths`
|
|
2149
|
+
* applies everywhere, so a skill set with none is returned unchanged. Best-effort:
|
|
2150
|
+
* any failure (or an unlocatable project root → relCwd "") falls open to the full
|
|
2151
|
+
* list, never hiding a skill spuriously.
|
|
2152
|
+
*/
|
|
2153
|
+
async function filterSkillsByCwd(skills, cwd) {
|
|
2154
|
+
try {
|
|
2155
|
+
if (!Array.isArray(skills) || skills.every((s) => !s?.paths)) return skills;
|
|
2156
|
+
const { filterSkillsByRelCwd, relCwdForCwd } =
|
|
2157
|
+
await import("../lib/skill-path-scope.js");
|
|
2158
|
+
const { findProjectRoot } = await import("../lib/project-detector.js");
|
|
2159
|
+
const root = (cwd && findProjectRoot(cwd)) || cwd || "";
|
|
2160
|
+
return filterSkillsByRelCwd(skills, relCwdForCwd(cwd, root));
|
|
2161
|
+
} catch {
|
|
2162
|
+
return skills; // fail-open: never hide a skill because scoping errored
|
|
2163
|
+
}
|
|
2164
|
+
}
|
|
2165
|
+
|
|
2103
2166
|
async function executeToolInner(
|
|
2104
2167
|
name,
|
|
2105
2168
|
args,
|
|
@@ -2323,6 +2386,30 @@ async function executeToolInner(
|
|
|
2323
2386
|
replaceAll,
|
|
2324
2387
|
);
|
|
2325
2388
|
if (count === 0) {
|
|
2389
|
+
// Idempotent replay (P0-2 "Diff Apply 内容哈希"): a resumed edit whose
|
|
2390
|
+
// old_string is already gone but whose new_string is present already
|
|
2391
|
+
// landed. Report a no-op success (NO write occurs — zero data risk)
|
|
2392
|
+
// rather than a misleading "not found" error, so a recovered worker
|
|
2393
|
+
// doesn't get stuck re-issuing an edit that already happened.
|
|
2394
|
+
const replay = classifyEditReplay({
|
|
2395
|
+
content,
|
|
2396
|
+
oldString: args.old_string,
|
|
2397
|
+
newString: args.new_string,
|
|
2398
|
+
});
|
|
2399
|
+
if (replay === EDIT_REPLAY.ALREADY_APPLIED) {
|
|
2400
|
+
return attachDescriptor({
|
|
2401
|
+
success: true,
|
|
2402
|
+
path: filePath,
|
|
2403
|
+
alreadyApplied: true,
|
|
2404
|
+
idempotencyKey: editIdempotencyKey({
|
|
2405
|
+
path: args.path,
|
|
2406
|
+
oldString: args.old_string,
|
|
2407
|
+
newString: args.new_string,
|
|
2408
|
+
replaceAll,
|
|
2409
|
+
}),
|
|
2410
|
+
note: "edit already applied (new_string present, old_string absent) — no change made",
|
|
2411
|
+
});
|
|
2412
|
+
}
|
|
2326
2413
|
return attachDescriptor({ error: "old_string not found in file" });
|
|
2327
2414
|
}
|
|
2328
2415
|
// Require a UNIQUE match (Claude-Code Edit parity) — replacing the first
|
|
@@ -3220,6 +3307,16 @@ async function executeToolInner(
|
|
|
3220
3307
|
goalMaxTokens: args.goal_max_tokens ?? null,
|
|
3221
3308
|
goalMaxCost: args.goal_max_cost ?? null,
|
|
3222
3309
|
goalMaxTime: args.goal_max_time ?? null,
|
|
3310
|
+
// P1-8: action classes this unattended task may still perform (e.g.
|
|
3311
|
+
// "publish", "external_message"). Accepts an array or a comma list.
|
|
3312
|
+
unattendedAllowlist: Array.isArray(args.unattended_allow)
|
|
3313
|
+
? args.unattended_allow
|
|
3314
|
+
: typeof args.unattended_allow === "string"
|
|
3315
|
+
? args.unattended_allow
|
|
3316
|
+
.split(",")
|
|
3317
|
+
.map((s) => s.trim())
|
|
3318
|
+
.filter(Boolean)
|
|
3319
|
+
: null,
|
|
3223
3320
|
};
|
|
3224
3321
|
if (action === "wakeup") {
|
|
3225
3322
|
if (!args.prompt) {
|
|
@@ -3620,7 +3717,10 @@ async function executeToolInner(
|
|
|
3620
3717
|
}
|
|
3621
3718
|
|
|
3622
3719
|
case "run_skill": {
|
|
3623
|
-
const allSkills =
|
|
3720
|
+
const allSkills = await filterSkillsByCwd(
|
|
3721
|
+
skillLoader.getResolvedSkills().filter(skillAllowed),
|
|
3722
|
+
cwd,
|
|
3723
|
+
);
|
|
3624
3724
|
if (allSkills.length === 0) {
|
|
3625
3725
|
return attachDescriptor({
|
|
3626
3726
|
error: _skillAllowlist
|
|
@@ -3766,7 +3866,10 @@ async function executeToolInner(
|
|
|
3766
3866
|
}
|
|
3767
3867
|
|
|
3768
3868
|
case "list_skills": {
|
|
3769
|
-
let skills =
|
|
3869
|
+
let skills = await filterSkillsByCwd(
|
|
3870
|
+
skillLoader.getResolvedSkills().filter(skillAllowed),
|
|
3871
|
+
cwd,
|
|
3872
|
+
);
|
|
3770
3873
|
if (skills.length === 0) {
|
|
3771
3874
|
return attachDescriptor({
|
|
3772
3875
|
error: _skillAllowlist
|
|
@@ -6379,6 +6482,22 @@ function extractInitialPromptText(messages) {
|
|
|
6379
6482
|
return "";
|
|
6380
6483
|
}
|
|
6381
6484
|
|
|
6485
|
+
/**
|
|
6486
|
+
* Stable id for a permission GATE decision, so a denied/gated tool span can be
|
|
6487
|
+
* correlated with the decision that blocked it. Only gated results carry a
|
|
6488
|
+
* `policy` (allow-path tools execute with no distinct decision), so this returns
|
|
6489
|
+
* null when there's nothing to identify. Deterministic (call id + gate) — no
|
|
6490
|
+
* clock/RNG — so a consumer holding the tool-result's policy + tool_call_id can
|
|
6491
|
+
* recompute it. Not exported (no shim-parity impact) — only the telemetry seam
|
|
6492
|
+
* uses it.
|
|
6493
|
+
*/
|
|
6494
|
+
function permissionDecisionId(callId, policy) {
|
|
6495
|
+
if (!policy || typeof policy !== "object" || !policy.decision) return null;
|
|
6496
|
+
const gate = policy.via || policy.decision;
|
|
6497
|
+
const base = callId || "call";
|
|
6498
|
+
return `${base}:perm:${gate}`;
|
|
6499
|
+
}
|
|
6500
|
+
|
|
6382
6501
|
export async function* agentLoop(messages, options) {
|
|
6383
6502
|
// Shared iteration budget — replaces hardcoded MAX_ITERATIONS.
|
|
6384
6503
|
// When options.iterationBudget is provided (e.g. from parent agent),
|
|
@@ -6858,6 +6977,18 @@ export async function* agentLoop(messages, options) {
|
|
|
6858
6977
|
}
|
|
6859
6978
|
}
|
|
6860
6979
|
|
|
6980
|
+
// Per-span unified ids (P2 observability): the run-level default attributes
|
|
6981
|
+
// stamp session/agent/workflow ids on every span, but turn/prompt ids are
|
|
6982
|
+
// per-iteration. A "turn" is one model iteration of the run; "prompt" is the
|
|
6983
|
+
// model request within that turn. Normalized through buildTelemetryAttributes
|
|
6984
|
+
// so they get the same charset-sanitized + cardinality-bounded treatment as
|
|
6985
|
+
// the run-level ids (turn.id correlates with the agent.iteration counter).
|
|
6986
|
+
const modelIdAttrs = recorder
|
|
6987
|
+
? buildTelemetryAttributes({
|
|
6988
|
+
turnId: `${runId}:t${budget.consumed}`,
|
|
6989
|
+
promptId: `${runId}:t${budget.consumed}:p`,
|
|
6990
|
+
})
|
|
6991
|
+
: null;
|
|
6861
6992
|
const result = await _withSpan(
|
|
6862
6993
|
recorder,
|
|
6863
6994
|
"agent.model",
|
|
@@ -6865,6 +6996,7 @@ export async function* agentLoop(messages, options) {
|
|
|
6865
6996
|
"gen_ai.system": options.provider || "ollama",
|
|
6866
6997
|
"gen_ai.request.model": options.model || "unknown",
|
|
6867
6998
|
"agent.iteration": budget.consumed,
|
|
6999
|
+
...(modelIdAttrs || {}),
|
|
6868
7000
|
},
|
|
6869
7001
|
() => llmCall(callMessages, options),
|
|
6870
7002
|
(span, r) => {
|
|
@@ -6886,6 +7018,21 @@ export async function* agentLoop(messages, options) {
|
|
|
6886
7018
|
Array.isArray(r?.message?.tool_calls) &&
|
|
6887
7019
|
r.message.tool_calls.length > 0,
|
|
6888
7020
|
);
|
|
7021
|
+
// response CONTENT is opt-in (--otlp-content): stamped only when
|
|
7022
|
+
// enabled, redacted + length-capped through the same normalizer; the
|
|
7023
|
+
// field is entirely absent by default so default OTLP stays unchanged.
|
|
7024
|
+
if (options.otlpIncludeContent === true && r?.message?.content) {
|
|
7025
|
+
const respAttrs = buildTelemetryAttributes(
|
|
7026
|
+
{ response: r.message.content },
|
|
7027
|
+
{ includeContent: true },
|
|
7028
|
+
);
|
|
7029
|
+
if (respAttrs["content.response"] != null) {
|
|
7030
|
+
span.setAttribute(
|
|
7031
|
+
"content.response",
|
|
7032
|
+
respAttrs["content.response"],
|
|
7033
|
+
);
|
|
7034
|
+
}
|
|
7035
|
+
}
|
|
6889
7036
|
},
|
|
6890
7037
|
"model_error",
|
|
6891
7038
|
);
|
|
@@ -7163,17 +7310,66 @@ export async function* agentLoop(messages, options) {
|
|
|
7163
7310
|
|
|
7164
7311
|
let toolResult;
|
|
7165
7312
|
let toolError = null;
|
|
7313
|
+
// Per-span unified ids (P2 observability): a tool span carries its turn.id
|
|
7314
|
+
// (correlating with the model span of the same iteration), the provider's
|
|
7315
|
+
// tool_use.id (so a tool-result event can be tied back to its call), and —
|
|
7316
|
+
// when auto-checkpoint fired before a mutating tool — the checkpoint.id the
|
|
7317
|
+
// user could restore to. All normalized through buildTelemetryAttributes.
|
|
7318
|
+
// tool_arguments CONTENT is opt-in (--otlp-content): the alias key is only
|
|
7319
|
+
// present when explicitly enabled, so by default the field is omitted
|
|
7320
|
+
// entirely (byte-identical default OTLP), and even opted-in it's length-
|
|
7321
|
+
// capped by redactContent. Mirrors the run-level content.prompt opt-in.
|
|
7322
|
+
const toolContentOptIn = options.otlpIncludeContent === true;
|
|
7323
|
+
const toolIdAttrs = recorder
|
|
7324
|
+
? buildTelemetryAttributes(
|
|
7325
|
+
{
|
|
7326
|
+
turnId: `${runId}:t${budget.consumed}`,
|
|
7327
|
+
toolUseId: call.id,
|
|
7328
|
+
checkpointId: cpId || undefined,
|
|
7329
|
+
...(toolContentOptIn ? { toolArguments: toolArgs } : {}),
|
|
7330
|
+
},
|
|
7331
|
+
{ includeContent: toolContentOptIn },
|
|
7332
|
+
)
|
|
7333
|
+
: null;
|
|
7166
7334
|
try {
|
|
7167
7335
|
toolResult = await _withSpan(
|
|
7168
7336
|
recorder,
|
|
7169
7337
|
"agent.tool",
|
|
7170
|
-
{ "tool.name": toolName },
|
|
7338
|
+
{ "tool.name": toolName, ...(toolIdAttrs || {}) },
|
|
7171
7339
|
() => executeTool(toolName, toolArgs, toolContext),
|
|
7172
7340
|
(span, r) => {
|
|
7173
7341
|
span.setAttribute(
|
|
7174
7342
|
"tool.is_error",
|
|
7175
7343
|
!!(r && typeof r === "object" && r.error),
|
|
7176
7344
|
);
|
|
7345
|
+
// permission.decision_id (P2): a GATED tool result carries a
|
|
7346
|
+
// `policy` (deny / ask-fail / host-block / sandbox); allow-path
|
|
7347
|
+
// tools execute with no distinct decision. Stamp a stable id
|
|
7348
|
+
// (derived from the call + gate, so it's recomputable from the
|
|
7349
|
+
// tool-result's policy + tool_call_id) plus the low-cardinality
|
|
7350
|
+
// decision, letting a blocked tool span be tied to its decision.
|
|
7351
|
+
const decId =
|
|
7352
|
+
r && typeof r === "object"
|
|
7353
|
+
? permissionDecisionId(call.id, r.policy)
|
|
7354
|
+
: null;
|
|
7355
|
+
if (decId) {
|
|
7356
|
+
const permAttrs = buildTelemetryAttributes({
|
|
7357
|
+
decisionId: decId,
|
|
7358
|
+
"permission.decision": r.policy.decision,
|
|
7359
|
+
});
|
|
7360
|
+
if (permAttrs["permission.decision_id"]) {
|
|
7361
|
+
span.setAttribute(
|
|
7362
|
+
"permission.decision_id",
|
|
7363
|
+
permAttrs["permission.decision_id"],
|
|
7364
|
+
);
|
|
7365
|
+
}
|
|
7366
|
+
if (permAttrs["permission.decision"]) {
|
|
7367
|
+
span.setAttribute(
|
|
7368
|
+
"permission.decision",
|
|
7369
|
+
permAttrs["permission.decision"],
|
|
7370
|
+
);
|
|
7371
|
+
}
|
|
7372
|
+
}
|
|
7177
7373
|
},
|
|
7178
7374
|
"tool_error",
|
|
7179
7375
|
);
|
|
@@ -288,6 +288,66 @@ export async function collectDoctorReport() {
|
|
|
288
288
|
};
|
|
289
289
|
}
|
|
290
290
|
|
|
291
|
+
/**
|
|
292
|
+
* Parse `git worktree list --porcelain` output into `{ path, branch }` entries.
|
|
293
|
+
*
|
|
294
|
+
* Pure (no I/O) so it can be unit-tested against fixed fixtures. A detached
|
|
295
|
+
* worktree has no `branch` line and yields `branch: null`.
|
|
296
|
+
*/
|
|
297
|
+
export function parseGitWorktrees(porcelain) {
|
|
298
|
+
if (!porcelain || typeof porcelain !== "string") return [];
|
|
299
|
+
const entries = [];
|
|
300
|
+
let cur = null;
|
|
301
|
+
for (const line of porcelain.split("\n")) {
|
|
302
|
+
const trimmed = line.trimEnd();
|
|
303
|
+
if (trimmed.startsWith("worktree ")) {
|
|
304
|
+
if (cur) entries.push(cur);
|
|
305
|
+
cur = { path: trimmed.slice("worktree ".length).trim(), branch: null };
|
|
306
|
+
} else if (trimmed.startsWith("branch ") && cur) {
|
|
307
|
+
cur.branch = trimmed
|
|
308
|
+
.slice("branch ".length)
|
|
309
|
+
.replace("refs/heads/", "")
|
|
310
|
+
.trim();
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
if (cur) entries.push(cur);
|
|
314
|
+
return entries;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/** Best-effort live git worktree list for the current repo. */
|
|
318
|
+
export function collectWorktrees() {
|
|
319
|
+
return parseGitWorktrees(safeExec("git worktree list --porcelain"));
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Map a doctor report + ambient runtime signals into the input contract of
|
|
324
|
+
* `buildDiagnosticBundle` (src/lib/diagnostic-bundle.js).
|
|
325
|
+
*
|
|
326
|
+
* Pure & deterministic aside from reading this process's own platform tags:
|
|
327
|
+
* the caller gathers live signals (env, worktrees, lockfiles) and passes them
|
|
328
|
+
* in, so the mapping itself is unit-testable. Session-runtime fields
|
|
329
|
+
* (connectionState, reconnectHistory, trace, redactionEvents) are absent from a
|
|
330
|
+
* cold `cc doctor` invocation and left to the bundle builder's safe defaults.
|
|
331
|
+
*/
|
|
332
|
+
export function buildDoctorDiagnosticInput(report, signals = {}) {
|
|
333
|
+
const r = report || {};
|
|
334
|
+
return {
|
|
335
|
+
version: r.version || null,
|
|
336
|
+
platform: {
|
|
337
|
+
os: process.platform,
|
|
338
|
+
arch: process.arch,
|
|
339
|
+
node: process.versions?.node || null,
|
|
340
|
+
},
|
|
341
|
+
ports: (Array.isArray(r.ports) ? r.ports : [])
|
|
342
|
+
.filter((p) => p && p.open)
|
|
343
|
+
.map((p) => ({ port: p.port, proto: p.name })),
|
|
344
|
+
worktrees: Array.isArray(signals.worktrees) ? signals.worktrees : [],
|
|
345
|
+
lockfiles: Array.isArray(signals.lockfiles) ? signals.lockfiles : [],
|
|
346
|
+
env: signals.env && typeof signals.env === "object" ? signals.env : {},
|
|
347
|
+
notes: signals.notes != null ? signals.notes : null,
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
|
|
291
351
|
/**
|
|
292
352
|
* Collect a machine-readable status report.
|
|
293
353
|
*
|
|
@@ -51,6 +51,13 @@ import {
|
|
|
51
51
|
getLastSessionId as jsonlGetLastSessionId,
|
|
52
52
|
verifySession as jsonlVerifySession,
|
|
53
53
|
} from "../harness/jsonl-session-store.js";
|
|
54
|
+
import {
|
|
55
|
+
SideEffectLedger,
|
|
56
|
+
reconcileSideEffects,
|
|
57
|
+
classifyToolSideEffect,
|
|
58
|
+
} from "../lib/side-effect-ledger.js";
|
|
59
|
+
import { SIDE_EFFECT_LEDGER_EVENT } from "../lib/side-effect-ledger-store.js";
|
|
60
|
+
import { operationIdempotencyKey } from "../lib/idempotency.js";
|
|
54
61
|
import { expandFileRefsAsync } from "./file-ref-expander.js";
|
|
55
62
|
import { composeSystemPrompt } from "./system-prompt.js";
|
|
56
63
|
import { buildUserContent } from "../lib/image-input.js";
|
|
@@ -688,6 +695,81 @@ export async function runAgentHeadless(options = {}, deps = {}) {
|
|
|
688
695
|
}
|
|
689
696
|
}
|
|
690
697
|
|
|
698
|
+
// ── P0-2: crash-safe side-effect ledger ────────────────────────────────
|
|
699
|
+
// Dangerous tools (file writes, opaque shell, git push, publish/schedule/
|
|
700
|
+
// notify/browser actions) are recorded prepare→start→commit|fail and the
|
|
701
|
+
// full snapshot persisted before the effect settles, so a worker killed
|
|
702
|
+
// mid-flight does NOT blindly replay an effect that may already have landed.
|
|
703
|
+
// Only active when persisting (an ephemeral/one-shot run can't be resumed);
|
|
704
|
+
// the byte-for-byte default path is untouched when no dangerous tool runs.
|
|
705
|
+
const sideEffectRunNonce = String(deps.now ? deps.now() : Date.now());
|
|
706
|
+
let sideEffectLedger = new SideEffectLedger({ clock: deps.now || null });
|
|
707
|
+
let sideEffectSeq = 0;
|
|
708
|
+
let currentSideEffectOpId = null;
|
|
709
|
+
let resumeSideEffectContext = null;
|
|
710
|
+
const persistSideEffectLedger = () => {
|
|
711
|
+
if (!persist) return;
|
|
712
|
+
try {
|
|
713
|
+
store.appendEvent(
|
|
714
|
+
sessionId,
|
|
715
|
+
SIDE_EFFECT_LEDGER_EVENT,
|
|
716
|
+
sideEffectLedger.toJSON(),
|
|
717
|
+
);
|
|
718
|
+
} catch {
|
|
719
|
+
// best-effort — never fail the run over ledger persistence
|
|
720
|
+
}
|
|
721
|
+
};
|
|
722
|
+
if (persist) {
|
|
723
|
+
try {
|
|
724
|
+
const events = store.readEvents(sessionId) || [];
|
|
725
|
+
for (let i = events.length - 1; i >= 0; i--) {
|
|
726
|
+
const e = events[i];
|
|
727
|
+
if (
|
|
728
|
+
e &&
|
|
729
|
+
e.type === SIDE_EFFECT_LEDGER_EVENT &&
|
|
730
|
+
e.data &&
|
|
731
|
+
Array.isArray(e.data.ops)
|
|
732
|
+
) {
|
|
733
|
+
sideEffectLedger = SideEffectLedger.fromJSON(e.data, {
|
|
734
|
+
clock: deps.now || null,
|
|
735
|
+
});
|
|
736
|
+
break;
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
} catch {
|
|
740
|
+
// fresh ledger — best effort
|
|
741
|
+
}
|
|
742
|
+
// On resume, surface any operation that was in flight when the prior run
|
|
743
|
+
// died: its outcome is UNKNOWN, so the model is told to VERIFY before any
|
|
744
|
+
// replay rather than silently re-issue an irreversible effect.
|
|
745
|
+
if (resumeId) {
|
|
746
|
+
try {
|
|
747
|
+
const plan = reconcileSideEffects(sideEffectLedger);
|
|
748
|
+
if (plan.inspect.length > 0) {
|
|
749
|
+
const lines = plan.plans
|
|
750
|
+
.filter((p) => p.action === "inspect")
|
|
751
|
+
.map((p) => {
|
|
752
|
+
const op = sideEffectLedger.get(p.opId);
|
|
753
|
+
const kind = op?.kind || "unknown";
|
|
754
|
+
const key = op?.key ? ` (${op.key})` : "";
|
|
755
|
+
return ` • [${kind}]${key} — ${p.reason}`;
|
|
756
|
+
});
|
|
757
|
+
resumeSideEffectContext =
|
|
758
|
+
"Recovery notice — the previous run was interrupted while these " +
|
|
759
|
+
"irreversible operations were in flight; their outcome is UNKNOWN. " +
|
|
760
|
+
"Do NOT blindly re-run them. Verify whether each already took " +
|
|
761
|
+
"effect before repeating it, and ask the user if unsure:\n" +
|
|
762
|
+
lines.join("\n");
|
|
763
|
+
writeErr(
|
|
764
|
+
`⚠ ${plan.inspect.length} interrupted side-effect(s) need verification before replay (resume ${resumeId}).\n`,
|
|
765
|
+
);
|
|
766
|
+
}
|
|
767
|
+
} catch {
|
|
768
|
+
resumeSideEffectContext = null;
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
|
|
691
773
|
// ── Wire the persistent ApprovalGate with our non-interactive confirmer
|
|
692
774
|
// and force the session-policy tier dictated by --permission-mode. ──────
|
|
693
775
|
let approvalGate = null;
|
|
@@ -770,13 +852,21 @@ export async function runAgentHeadless(options = {}, deps = {}) {
|
|
|
770
852
|
}
|
|
771
853
|
}
|
|
772
854
|
|
|
855
|
+
// --no-project-memory (options.projectMemory === false): lean prompt — skip
|
|
856
|
+
// rules.md (in buildSystemPrompt) + the cc.md/CLAUDE.md block. Absent flag
|
|
857
|
+
// (undefined) leaves both paths byte-identical.
|
|
858
|
+
const _leanNoProjectMemory = options.projectMemory === false;
|
|
773
859
|
const systemContent = composeSystemPrompt(
|
|
774
|
-
buildSystemPrompt(cwd, {
|
|
860
|
+
buildSystemPrompt(cwd, {
|
|
861
|
+
additionalDirectories,
|
|
862
|
+
projectMemory: options.projectMemory,
|
|
863
|
+
}),
|
|
775
864
|
{
|
|
776
865
|
systemPrompt: options.systemPrompt,
|
|
777
866
|
appendSystemPrompt: options.appendSystemPrompt,
|
|
778
867
|
outputStyle: outputStyleBody,
|
|
779
868
|
instructionExcludes,
|
|
869
|
+
projectMemory: _leanNoProjectMemory ? false : undefined,
|
|
780
870
|
},
|
|
781
871
|
);
|
|
782
872
|
|
|
@@ -863,6 +953,9 @@ export async function runAgentHeadless(options = {}, deps = {}) {
|
|
|
863
953
|
...(sessionStartContext
|
|
864
954
|
? [{ role: "system", content: sessionStartContext }]
|
|
865
955
|
: []),
|
|
956
|
+
...(resumeSideEffectContext
|
|
957
|
+
? [{ role: "system", content: resumeSideEffectContext }]
|
|
958
|
+
: []),
|
|
866
959
|
...history,
|
|
867
960
|
{ role: "user", content: userMessageContent },
|
|
868
961
|
]);
|
|
@@ -1067,7 +1160,7 @@ export async function runAgentHeadless(options = {}, deps = {}) {
|
|
|
1067
1160
|
try {
|
|
1068
1161
|
const { AsyncHookSupervisor } =
|
|
1069
1162
|
await import("../lib/async-hook-supervisor.cjs");
|
|
1070
|
-
_hookSupervisor = new AsyncHookSupervisor();
|
|
1163
|
+
_hookSupervisor = new AsyncHookSupervisor({ persistStats: true });
|
|
1071
1164
|
} catch {
|
|
1072
1165
|
_hookSupervisor = null; // async hooks are best-effort
|
|
1073
1166
|
}
|
|
@@ -1492,10 +1585,50 @@ export async function runAgentHeadless(options = {}, deps = {}) {
|
|
|
1492
1585
|
args: event.args,
|
|
1493
1586
|
});
|
|
1494
1587
|
toolCalls.push({ tool: event.tool, args: event.args });
|
|
1588
|
+
// P0-2: record an irreversible effect as STARTED (persisted before
|
|
1589
|
+
// it settles) so a crash before the matching tool-result leaves a
|
|
1590
|
+
// reconcilable "in flight" marker instead of a silent replay.
|
|
1591
|
+
currentSideEffectOpId = null;
|
|
1592
|
+
if (persist) {
|
|
1593
|
+
const se = classifyToolSideEffect(event.tool, event.args);
|
|
1594
|
+
if (se) {
|
|
1595
|
+
const opId = `${sideEffectRunNonce}:${sideEffectSeq++}`;
|
|
1596
|
+
currentSideEffectOpId = opId;
|
|
1597
|
+
sideEffectLedger
|
|
1598
|
+
.prepare(opId, {
|
|
1599
|
+
kind: se.kind,
|
|
1600
|
+
key: se.key,
|
|
1601
|
+
// Content-addressed key: a resumed replay of the SAME effect
|
|
1602
|
+
// derives the SAME key, so an external provider can de-dupe
|
|
1603
|
+
// and countDuplicateCommittedEffects can measure `0` repeats.
|
|
1604
|
+
meta: {
|
|
1605
|
+
tool: event.tool,
|
|
1606
|
+
idempotencyKey: operationIdempotencyKey({
|
|
1607
|
+
tool: event.tool,
|
|
1608
|
+
args: event.args,
|
|
1609
|
+
}),
|
|
1610
|
+
},
|
|
1611
|
+
})
|
|
1612
|
+
.start(opId);
|
|
1613
|
+
persistSideEffectLedger();
|
|
1614
|
+
}
|
|
1615
|
+
}
|
|
1495
1616
|
break;
|
|
1496
1617
|
}
|
|
1497
1618
|
case "tool-result": {
|
|
1498
1619
|
const err = event.error || event.result?.error || null;
|
|
1620
|
+
// P0-2: settle the in-flight side-effect (commit on success, fail on
|
|
1621
|
+
// a clean error) and persist the updated ledger snapshot.
|
|
1622
|
+
if (persist && currentSideEffectOpId) {
|
|
1623
|
+
if (err)
|
|
1624
|
+
sideEffectLedger.fail(
|
|
1625
|
+
currentSideEffectOpId,
|
|
1626
|
+
String(err).slice(0, 200),
|
|
1627
|
+
);
|
|
1628
|
+
else sideEffectLedger.commit(currentSideEffectOpId);
|
|
1629
|
+
persistSideEffectLedger();
|
|
1630
|
+
currentSideEffectOpId = null;
|
|
1631
|
+
}
|
|
1499
1632
|
if (isText && err) writeErr(` Error: ${err}\n`);
|
|
1500
1633
|
emitStream({
|
|
1501
1634
|
type: "tool_result",
|
|
@@ -1073,8 +1073,16 @@ export async function runAgentHeadlessStream(options = {}, deps = {}) {
|
|
|
1073
1073
|
}
|
|
1074
1074
|
}
|
|
1075
1075
|
|
|
1076
|
+
// --no-project-memory (options.projectMemory === false) → lean system prompt:
|
|
1077
|
+
// skip both the rules.md append inside buildSystemPrompt AND the auto-loaded
|
|
1078
|
+
// cc.md/CLAUDE.md instruction block. Only `=== false` changes anything; when
|
|
1079
|
+
// the flag is absent (undefined) both paths stay byte-identical.
|
|
1080
|
+
const _leanNoProjectMemory = options.projectMemory === false;
|
|
1076
1081
|
const systemContent = composeSystemPrompt(
|
|
1077
|
-
buildSystemPrompt(cwd, {
|
|
1082
|
+
buildSystemPrompt(cwd, {
|
|
1083
|
+
additionalDirectories,
|
|
1084
|
+
projectMemory: options.projectMemory,
|
|
1085
|
+
}),
|
|
1078
1086
|
{
|
|
1079
1087
|
systemPrompt: options.systemPrompt,
|
|
1080
1088
|
appendSystemPrompt: _jsonSchema
|
|
@@ -1084,6 +1092,7 @@ export async function runAgentHeadlessStream(options = {}, deps = {}) {
|
|
|
1084
1092
|
: options.appendSystemPrompt,
|
|
1085
1093
|
outputStyle: outputStyleBody,
|
|
1086
1094
|
instructionExcludes,
|
|
1095
|
+
projectMemory: _leanNoProjectMemory ? false : undefined,
|
|
1087
1096
|
},
|
|
1088
1097
|
);
|
|
1089
1098
|
const messages = [{ role: "system", content: systemContent }];
|
|
@@ -49,6 +49,11 @@ export function resolveAgentPolicy({
|
|
|
49
49
|
// before 2026-07-09, breaking --vim/--think/--thinking-budget/
|
|
50
50
|
// --fallback-model/--pdh/--permission-mode for interactive sessions.
|
|
51
51
|
permissionMode: overrides.permissionMode,
|
|
52
|
+
// --no-project-memory tri-state (undefined=default-on / false=lean prompt).
|
|
53
|
+
// Plain passthrough consumed by startAgentRepl → composeSystemPrompt /
|
|
54
|
+
// buildSystemPrompt; without this allowlist entry the flag would be silently
|
|
55
|
+
// dropped for interactive sessions (same class as the vim/think/pdh fixes).
|
|
56
|
+
projectMemory: overrides.projectMemory,
|
|
52
57
|
vimMode: overrides.vimMode,
|
|
53
58
|
thinking: overrides.thinking,
|
|
54
59
|
thinkingBudget: overrides.thinkingBudget,
|