chainlesschain 0.162.163 → 0.162.164
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-BqRkaHGb.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-Dr1MDANe.js → ActionButton-DWO7OZLC.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-CZ5vsaXW.js → Analytics-C5oaVezM.js} +2 -2
- package/src/assets/web-panel/assets/{AppLayout-BIBga881.js → AppLayout-CsUESDoZ.js} +3 -3
- package/src/assets/web-panel/assets/{Artifacts-vpXHRU3u.js → Artifacts-Dragqu5k.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-B8exuoub.js → Audit-CQsaHctY.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-923evRRN.js → BackgroundAgents-DdG1jNSM.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-sr_sswlQ.js → Backup-DN6sjK4z.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-CnW-4XKT.js → BaseInput-Cxm_ZTCl.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-FZCsTZIL.js → Chat-DVXGmA56.js} +5 -5
- package/src/assets/web-panel/assets/{ChatBubbleRenderer-DDPd9qT0.js → ChatBubbleRenderer-BdaCL_iE.js} +1 -1
- package/src/assets/web-panel/assets/{Checkbox-DFrxB59H.js → Checkbox-DEc4vD_A.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-_b6WEel8.js → Codegen-CKiTcv36.js} +1 -1
- package/src/assets/web-panel/assets/{Col-CkbnnZ-K.js → Col-Br9UUNN0.js} +1 -1
- package/src/assets/web-panel/assets/{Community-BxM_82zW.js → Community-NeUH_70T.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-DYinP45R.js → Compact-BqR-z2Df.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-DyXp3sSP.js → Compliance-DcWB_SgZ.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-BEr2DhW7.js → Cowork-BfhgcudF.js} +2 -2
- package/src/assets/web-panel/assets/{Cron-B-Vwo1hN.js → Cron-LaU29LgD.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-DbLxKddd.js → Crosschain-B2ZB9uwW.js} +1 -1
- package/src/assets/web-panel/assets/{DID-BsQ_LFw9.js → DID-BuHl38A2.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-lVlXQHu5.js → Dashboard-m627yqvZ.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-Beo_foO7.js → Dropdown-C7bxF8F5.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-DszHIXED.js → EmailListRenderer-DPLPQzLp.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-DcAwoh81.js → FamilyGuardDashboard-cOiH6ike.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-CuJLa2eb.js → Federation-Bvkzg88z.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-Cxd1lxhc.js → FormItemContext-BTsiA7h_.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-BBvq5Jsw.js → GenericCardRenderer-BGnVE5qu.js} +1 -1
- package/src/assets/web-panel/assets/{Git-WqPKetx6.js → Git-DS_j6r25.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-CWYtY3ke.js → Governance-CKRG3Rsl.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-BnX1iiNV.js → Inference-DW8INoNQ.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-BJuM8GN5.js → KnowledgeGraph-BhEYeFhC.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-DqLMNntm.js → Logs-wqi0uxuR.js} +2 -2
- package/src/assets/web-panel/assets/{MarkdownRenderer-BqYwEYou.js → MarkdownRenderer-DS8dE8ty.js} +1 -1
- package/src/assets/web-panel/assets/{Marketplace-_vGfmhNl.js → Marketplace-CMlTuPLT.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-BeJDOkDj.js → McpTools-CSx4m5JJ.js} +3 -3
- package/src/assets/web-panel/assets/{Memory-Da0EEZDO.js → Memory-vnQCUy-_.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-DSuzcCCy.js → MobileBridge-BV2NA742.js} +1 -1
- package/src/assets/web-panel/assets/MobileProjects-Bw0k8e1B.js +1 -0
- package/src/assets/web-panel/assets/{Mtc-BE9YgdA0.js → Mtc-CzK_FPB1.js} +2 -2
- package/src/assets/web-panel/assets/{MtcAudit-fC2BpO5S.js → MtcAudit-Cb7G9oL3.js} +2 -2
- package/src/assets/web-panel/assets/{Multisig-D24Xxqw3.js → Multisig-qJxlUyzr.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-Bs3oGvS8.js → NLProgramming-DqaBJ7Te.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-BFjmAA-3.js → Notes-CwhTJkRi.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-BX_9Bhv0.js → NotificationSettings-YDs4Fal4.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-wjkZwJXI.js → OrderTableRenderer-CvEALpq7.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-DBcdsVEP.js → Organization-CEmQm9n8.js} +2 -2
- package/src/assets/web-panel/assets/{Overflow-DDyaF9vC.js → Overflow-zby_P_LA.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-BB78L4he.js → P2P-DsmDNFNT.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-DqTiZFum.js → PdhVaultBrowser-FWwFlZGF.js} +3 -3
- package/src/assets/web-panel/assets/{Permissions-INbnWv4j.js → Permissions-DuzQ6me5.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-BST-VmTq.js → PersonalDataHub-DZbeN613.js} +2 -2
- package/src/assets/web-panel/assets/{Pipeline-CE7_tS2k.js → Pipeline-CoNSVAUS.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-hK5L8dOB.js → Privacy-OYJEAaMC.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-BvkkUoDn.js → ProjectInit-CWIapV--.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-u7_EUtkm.js → ProjectSettings-CX8xMxZn.js} +2 -2
- package/src/assets/web-panel/assets/{Projects-5Fmt_8Nw.js → Projects-BLCN8YyY.js} +1 -1
- package/src/assets/web-panel/assets/{Providers-B9eanarR.js → Providers-BV_sxp75.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-C0dX5N2f.js → QrScannerModal-BTnRrHRp.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-CKTpG-PZ.js → QuickAsk-D1KZg018.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-Px_w_XQM.js → Recommend-COm5cuNg.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-Di0fnYkM.js → RemoteSession-0saWEkYI.js} +2 -2
- package/src/assets/web-panel/assets/{Reputation-vesnWjXt.js → Reputation-Dr7iKLS2.js} +1 -1
- package/src/assets/web-panel/assets/{Row-exbY6LkT.js → Row-C1vpPBcr.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-B3O3x70t.js → RssFeed-CxlWrtNj.js} +2 -2
- package/src/assets/web-panel/assets/{Search-uRA0DhNz.js → Search-BT2V8dPN.js} +1 -1
- package/src/assets/web-panel/assets/{Security-CViqdH6K.js → Security-bm9V3c5k.js} +4 -4
- package/src/assets/web-panel/assets/{Services-CkXS97WN.js → Services-CocH1yyX.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-DxW5WYnE.js → Skeleton-DHItlMdd.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-CLxmGFse.js → Skills-C59sLc4w.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-Cr5fziXq.js → Sla-Crj4BMc8.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-H0JSb3cp.js → SpeechSettings-DX4KFceY.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-DMYWMae7.js → SyncSettings-BD_iwQLr.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-CbYyDdF4.js → Tasks-BmCTzYsH.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-Cpc8JjiK.js → Templates-BGUaxZYl.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-CRh77Jda.js → Tenant-BS0tJx2O.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-4D8tNBO5.js → Terminal--tHSAsld.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-D41jzzbG.js → TimelineRenderer-D9-MFxS4.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-C_jWEy6b.js → Tokens-CiCB485f.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-Dk1ZUhsy.js → Trigger-SmCFuW_l.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-CLVy4l7m.js → Trust-DkK3Rqe0.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-C-FeqFDj.js → UkeySign-CDM4LGdB.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-CqVZzyMm.js → VideoEditing-IDB7DJXt.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-DX9BW9pu.js → Wallet-Bikj5cmt.js} +2 -2
- package/src/assets/web-panel/assets/{WebAuthn-7hQDpWw_.js → WebAuthn-C9abWlAw.js} +2 -2
- package/src/assets/web-panel/assets/{WorkflowEditor-6XqUAO0j.js → WorkflowEditor-DrbRKVky.js} +1 -1
- package/src/assets/web-panel/assets/{chat-5R_vkaJG.js → chat-CccCSDQf.js} +1 -1
- package/src/assets/web-panel/assets/{colors-huUPZdC-.js → colors-CXObXLhb.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-CfJlgisE.js → compact-item-B5kfYAxC.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-jO8heofr.js → createContext-DmiPBfip.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-CvKrADNa.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-DY8Vxqx7.js → hasIn-Q6lYteAc.js} +1 -1
- package/src/assets/web-panel/assets/{index-BtEXSoPL.js → index-25Wlqps4.js} +1 -1
- package/src/assets/web-panel/assets/{index-BsBtRWbA.js → index-33U29UNV.js} +1 -1
- package/src/assets/web-panel/assets/{index-BQS-uPOK.js → index-4oXBDczR.js} +1 -1
- package/src/assets/web-panel/assets/{index-DXBcvUnf.js → index-BGYXvMc5.js} +1 -1
- package/src/assets/web-panel/assets/{index-C2iVSFpy.js → index-BJCCa0s_.js} +1 -1
- package/src/assets/web-panel/assets/{index-A48PtXw9.js → index-BJJ5DhKI.js} +1 -1
- package/src/assets/web-panel/assets/{index-Db1cLWir.js → index-BLOzViJ6.js} +1 -1
- package/src/assets/web-panel/assets/{index-vXs3B8GJ.js → index-BRrbUMoG.js} +1 -1
- package/src/assets/web-panel/assets/{index-BHE8OIls.js → index-BUFpT17Q.js} +1 -1
- package/src/assets/web-panel/assets/{index-BmqGerpC.js → index-BkRDiqGc.js} +1 -1
- package/src/assets/web-panel/assets/{index-CzKmteNS.js → index-BsfvW6my.js} +1 -1
- package/src/assets/web-panel/assets/{index-BAON7TzU.js → index-By-kfPED.js} +1 -1
- package/src/assets/web-panel/assets/{index-DoI2sZm7.js → index-BzFTcQsc.js} +1 -1
- package/src/assets/web-panel/assets/{index-j3O2EEJe.js → index-C2FXdboq.js} +1 -1
- package/src/assets/web-panel/assets/{index-CRElRhyS.js → index-C5XOlTHN.js} +1 -1
- package/src/assets/web-panel/assets/{index-KRHroryI.js → index-CLhBDS2w.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bxax9vQ6.js → index-CMq7UeoT.js} +1 -1
- package/src/assets/web-panel/assets/{index-BfCx4pFT.js → index-CVMTWNKl.js} +1 -1
- package/src/assets/web-panel/assets/{index-CA6sO5VD.js → index-Caghi-yL.js} +1 -1
- package/src/assets/web-panel/assets/{index-CA90TZxG.js → index-CdB6FuSH.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cbe9G0V4.js → index-CoK8-kWQ.js} +1 -1
- package/src/assets/web-panel/assets/{index-D0pet5Uy.js → index-CsO3GUAY.js} +1 -1
- package/src/assets/web-panel/assets/{index-ChvHubX3.js → index-CwZBa20Z.js} +1 -1
- package/src/assets/web-panel/assets/{index-BRXOP09X.js → index-D-kASucw.js} +1 -1
- package/src/assets/web-panel/assets/{index-B74OXFxC.js → index-D8eBw7vW.js} +1 -1
- package/src/assets/web-panel/assets/{index-D1VLuww0.js → index-D9wgifk4.js} +1 -1
- package/src/assets/web-panel/assets/{index-p85YaaKw.js → index-DAYCTc07.js} +1 -1
- package/src/assets/web-panel/assets/{index-BliaT02i.js → index-DF6Q8l2h.js} +1 -1
- package/src/assets/web-panel/assets/index-DTdFUWc_.js +1 -0
- package/src/assets/web-panel/assets/{index-Cjxbu68P.js → index-DgTLApS9.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cis_Ip7H.js → index-DgxVJ8pg.js} +1 -1
- package/src/assets/web-panel/assets/{index-v3pTaEQm.js → index-DlJvIqoR.js} +1 -1
- package/src/assets/web-panel/assets/{index-BYLBPpt8.js → index-Dw4eHfsG.js} +1 -1
- package/src/assets/web-panel/assets/{index-B300RPnT.js → index-Rh-ytwow.js} +3 -3
- package/src/assets/web-panel/assets/{index-CLhBh-qE.js → index-StAEt2pq.js} +1 -1
- package/src/assets/web-panel/assets/{index-d1xkbtNz.js → index-bzfw951k.js} +1 -1
- package/src/assets/web-panel/assets/{index-BCqxyvqp.js → index-fSUMbMvv.js} +1 -1
- package/src/assets/web-panel/assets/{index-dKx57io7.js → index-tFHy5Wol.js} +1 -1
- package/src/assets/web-panel/assets/index-zQ0M_kz-.js +1 -0
- package/src/assets/web-panel/assets/{initDefaultProps-MCB7-raZ.js → initDefaultProps-BNq7X72N.js} +1 -1
- package/src/assets/web-panel/assets/{motion-UWDF9swV.js → motion-DOf9viTc.js} +1 -1
- package/src/assets/web-panel/assets/{move-CsTmgXZU.js → move-CRHTcW4x.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-fQy6KGLg.js → mtc-parser-Cyoozgm2.js} +1 -1
- package/src/assets/web-panel/assets/{omit-woPnEjd1.js → omit-6-TjFNru.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-Bs2dZ1gf.js → pickAttrs-DlUmO1zi.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-C7EVgPkg.js → placementArrow-CFOL8mIR.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-BH2kYCTN.js → responsiveObserve-BtaFK5zd.js} +1 -1
- package/src/assets/web-panel/assets/{slide-BFBP2V_e.js → slide-BIbc2bur.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-xdDxDYse.js → statusUtils-BXX9d6-w.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-aAWgPPQf.js → styleChecker-BGv10Bpn.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-DkGSAWoO.js → useFlexGapSupport-bKnKT8tw.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-BrOih40N.js → useFs-FY4IL4jn.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-eIYrgMqO.js → usePersonalDataHub-Bwl9TEgu.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-B8lutNSE.js → vnode-BM3OHxAT.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-CffNre7V.js → zoom-D1fbFiHB.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/commands/agenda.js +71 -14
- 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 +5 -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/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 +152 -38
- package/src/runtime/agent-core.js +191 -13
- package/src/runtime/diagnostics.js +60 -0
- package/src/runtime/headless-runner.js +126 -1
- package/src/assets/web-panel/assets/MobileProjects-D0RAk4e8.js +0 -1
- 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
package/src/repl/agent-repl.js
CHANGED
|
@@ -2188,7 +2188,7 @@ export async function startAgentRepl(options = {}) {
|
|
|
2188
2188
|
` ${chalk.cyan("/export")} Save this conversation to a Markdown file (/export [path])`,
|
|
2189
2189
|
);
|
|
2190
2190
|
logger.log(
|
|
2191
|
-
` ${chalk.cyan("/rewind")} Rewind to an earlier turn (double-Esc lists); ${chalk.cyan("/rewind clear")} restores a /clear`,
|
|
2191
|
+
` ${chalk.cyan("/rewind")} Rewind to an earlier turn (double-Esc lists); ${chalk.cyan("/rewind <n> --files|--conversation")} scopes it; ${chalk.cyan("/rewind <n> --branch")} forks a session; ${chalk.cyan("/rewind clear")} restores a /clear`,
|
|
2192
2192
|
);
|
|
2193
2193
|
logger.log(
|
|
2194
2194
|
` ${chalk.cyan("/goal <cond>")} Set a session goal checked after each turn (exit-zero:/file-exists:/contains:/regex:/text); ${chalk.cyan("/goal")} shows, ${chalk.cyan("/goal clear")} drops`,
|
|
@@ -3020,9 +3020,17 @@ export async function startAgentRepl(options = {}) {
|
|
|
3020
3020
|
restoreClearedConversation,
|
|
3021
3021
|
buildRewindPlan,
|
|
3022
3022
|
renderRewindWarnings,
|
|
3023
|
+
buildBranchPlan,
|
|
3024
|
+
renderBranchPlan,
|
|
3025
|
+
parseRewindArg,
|
|
3026
|
+
RESTORE_SCOPE,
|
|
3023
3027
|
} = await import("../lib/repl-rewind.js");
|
|
3024
3028
|
const arg = trimmed.slice("/rewind".length).trim();
|
|
3025
|
-
|
|
3029
|
+
// Parse the turn number + optional restore scope (--conversation /
|
|
3030
|
+
// --files / --both). Claude-Code parity: a rewind can restore the
|
|
3031
|
+
// conversation only, the files only, or both.
|
|
3032
|
+
const parsed = parseRewindArg(arg);
|
|
3033
|
+
if (parsed.command === "clear") {
|
|
3026
3034
|
// Restore the conversation stashed by /clear (Claude-Code 2.1.191).
|
|
3027
3035
|
const r = restoreClearedConversation(
|
|
3028
3036
|
messages,
|
|
@@ -3045,13 +3053,86 @@ export async function startAgentRepl(options = {}) {
|
|
|
3045
3053
|
prompt();
|
|
3046
3054
|
return;
|
|
3047
3055
|
}
|
|
3048
|
-
if (
|
|
3056
|
+
if (parsed.command === "branch") {
|
|
3057
|
+
// "从这里分支" (P0-3's fourth restore action): fork a NEW independent
|
|
3058
|
+
// session that keeps history up to the chosen turn and diverges from
|
|
3059
|
+
// there. The origin session is NEVER truncated (preservesParent), so
|
|
3060
|
+
// the live conversation continues unchanged.
|
|
3061
|
+
const targetTurn = listUserTurns(messages, { limit: 1000 }).find(
|
|
3062
|
+
(t) => t.n === parsed.n,
|
|
3063
|
+
);
|
|
3064
|
+
if (!targetTurn) {
|
|
3065
|
+
logger.error(`No such turn: ${parsed.n} — run /rewind to list.`);
|
|
3066
|
+
} else if (!sessionId) {
|
|
3067
|
+
logger.error(
|
|
3068
|
+
"Branch needs a persisted session — none is active yet.",
|
|
3069
|
+
);
|
|
3070
|
+
} else {
|
|
3071
|
+
const turnIndex = targetTurn.index;
|
|
3072
|
+
const plan = buildBranchPlan(
|
|
3073
|
+
messages,
|
|
3074
|
+
_checkpointMarks,
|
|
3075
|
+
turnIndex,
|
|
3076
|
+
{
|
|
3077
|
+
parentSessionId: sessionId,
|
|
3078
|
+
writeIntent: parsed.writeIntent,
|
|
3079
|
+
},
|
|
3080
|
+
);
|
|
3081
|
+
if (!plan || !plan.branchSessionId) {
|
|
3082
|
+
logger.error("Could not plan a branch from that turn.");
|
|
3083
|
+
} else {
|
|
3084
|
+
try {
|
|
3085
|
+
const { createBranchSession } =
|
|
3086
|
+
await import("../harness/jsonl-session-store.js");
|
|
3087
|
+
const res = createBranchSession({
|
|
3088
|
+
branchSessionId: plan.branchSessionId,
|
|
3089
|
+
parentSessionId: sessionId,
|
|
3090
|
+
parentTurnId: plan.parentTurnId,
|
|
3091
|
+
messages: messages.slice(0, turnIndex),
|
|
3092
|
+
meta: { title: `Branch of ${sessionId}` },
|
|
3093
|
+
});
|
|
3094
|
+
logger.log(
|
|
3095
|
+
chalk.green(
|
|
3096
|
+
res.created
|
|
3097
|
+
? `🌿 branched to ${res.branchSessionId} — ${res.messages} message(s) kept; parent ${sessionId} untouched`
|
|
3098
|
+
: `🌿 branch ${res.branchSessionId} already exists; parent ${sessionId} untouched`,
|
|
3099
|
+
),
|
|
3100
|
+
);
|
|
3101
|
+
for (const line of renderBranchPlan(plan))
|
|
3102
|
+
logger.log(chalk.yellow(line));
|
|
3103
|
+
logger.log(
|
|
3104
|
+
chalk.gray(
|
|
3105
|
+
` resume it with cc agent --resume ${res.branchSessionId}` +
|
|
3106
|
+
(plan.requiresWorktree
|
|
3107
|
+
? " (write task → branch into an isolated worktree)"
|
|
3108
|
+
: ""),
|
|
3109
|
+
),
|
|
3110
|
+
);
|
|
3111
|
+
} catch (e) {
|
|
3112
|
+
logger.error(`branch failed: ${e.message}`);
|
|
3113
|
+
}
|
|
3114
|
+
}
|
|
3115
|
+
}
|
|
3116
|
+
prompt();
|
|
3117
|
+
return;
|
|
3118
|
+
}
|
|
3119
|
+
if (parsed.command === "list") {
|
|
3049
3120
|
logger.log(chalk.bold("\nRewind — pick a user turn (newest first):"));
|
|
3050
3121
|
logger.log(renderTurnList(listUserTurns(messages)));
|
|
3051
3122
|
const fileHint = _checkpointMarks.length
|
|
3052
3123
|
? " (restores files to that point too — checkpoints are on)"
|
|
3053
3124
|
: " (conversation only — start with --checkpoint / git to also rewind files)";
|
|
3054
3125
|
logger.log(chalk.gray(`Usage: /rewind <n>${fileHint}`));
|
|
3126
|
+
logger.log(
|
|
3127
|
+
chalk.gray(
|
|
3128
|
+
" scope: /rewind <n> --conversation | --files | --both (default)",
|
|
3129
|
+
),
|
|
3130
|
+
);
|
|
3131
|
+
logger.log(
|
|
3132
|
+
chalk.gray(
|
|
3133
|
+
" branch: /rewind <n> --branch [--write] forks an independent session (parent kept)",
|
|
3134
|
+
),
|
|
3135
|
+
);
|
|
3055
3136
|
if (_clearedConversation) {
|
|
3056
3137
|
logger.log(
|
|
3057
3138
|
chalk.gray(
|
|
@@ -3060,50 +3141,67 @@ export async function startAgentRepl(options = {}) {
|
|
|
3060
3141
|
);
|
|
3061
3142
|
}
|
|
3062
3143
|
} else {
|
|
3063
|
-
|
|
3064
|
-
|
|
3144
|
+
const scope = parsed.scope;
|
|
3145
|
+
const rewindConversation = scope !== RESTORE_SCOPE.FILES;
|
|
3146
|
+
const restoreFiles = scope !== RESTORE_SCOPE.CONVERSATION;
|
|
3147
|
+
// Snapshot + locate the target turn BEFORE any truncation:
|
|
3148
|
+
// buildRewindPlan needs the turn still present to bind its checkpoint,
|
|
3149
|
+
// and a files-only rewind keeps the conversation intact entirely.
|
|
3065
3150
|
const preRewind = messages.slice();
|
|
3066
|
-
const
|
|
3067
|
-
|
|
3068
|
-
|
|
3151
|
+
const targetTurn = listUserTurns(messages, { limit: 1000 }).find(
|
|
3152
|
+
(t) => t.n === parsed.n,
|
|
3153
|
+
);
|
|
3154
|
+
if (!targetTurn) {
|
|
3155
|
+
logger.error(`No such turn: ${parsed.n} — run /rewind to list.`);
|
|
3069
3156
|
} else {
|
|
3070
|
-
|
|
3071
|
-
|
|
3072
|
-
|
|
3073
|
-
)
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
|
|
3157
|
+
const turnIndex = targetTurn.index;
|
|
3158
|
+
// 1) Conversation rewind (skipped for files-only).
|
|
3159
|
+
if (rewindConversation) {
|
|
3160
|
+
const res = rewindToTurn(messages, parsed.n);
|
|
3161
|
+
logger.log(
|
|
3162
|
+
chalk.yellow(
|
|
3163
|
+
`⎌ rewound — dropped ${res.removed} message(s); edit and resend below`,
|
|
3164
|
+
),
|
|
3165
|
+
);
|
|
3166
|
+
} else {
|
|
3167
|
+
logger.log(
|
|
3168
|
+
chalk.yellow("⎌ files-only rewind — conversation left intact"),
|
|
3169
|
+
);
|
|
3170
|
+
}
|
|
3171
|
+
// 2) Coverage-aware honesty for the CHOSEN scope (P1 turn/checkpoint
|
|
3172
|
+
// binding): print what a restore can and cannot promise — a shell/
|
|
3173
|
+
// external side-effect → PARTIAL, no checkpoint → files can't be
|
|
3174
|
+
// restored, and a scope-specific conversation/files drift. Derived
|
|
3175
|
+
// from the REPL's existing marks (no new agent-loop events);
|
|
3080
3176
|
// best-effort so the advisory never blocks the rewind itself.
|
|
3081
3177
|
try {
|
|
3082
3178
|
const plan = buildRewindPlan(
|
|
3083
3179
|
preRewind,
|
|
3084
3180
|
_checkpointMarks,
|
|
3085
|
-
|
|
3181
|
+
turnIndex,
|
|
3182
|
+
scope,
|
|
3086
3183
|
);
|
|
3087
3184
|
for (const line of renderRewindWarnings(plan))
|
|
3088
3185
|
logger.log(chalk.yellow(line));
|
|
3089
3186
|
} catch {
|
|
3090
3187
|
/* advisory only — a rewind must never fail over its own warning */
|
|
3091
3188
|
}
|
|
3092
|
-
//
|
|
3093
|
-
//
|
|
3094
|
-
//
|
|
3095
|
-
//
|
|
3096
|
-
|
|
3097
|
-
|
|
3189
|
+
// 3) File restore (skipped for conversation-only). Match the turn to
|
|
3190
|
+
// the snapshot taken just before it first mutated the tree, then
|
|
3191
|
+
// offer to roll the working tree back to it (undoable — the restore
|
|
3192
|
+
// takes its own safety checkpoint first). Prune dropped-turn marks
|
|
3193
|
+
// only when the conversation was actually truncated.
|
|
3194
|
+
const cp = restoreFiles
|
|
3195
|
+
? pickCheckpointForTurn(_checkpointMarks, turnIndex)
|
|
3196
|
+
: null;
|
|
3197
|
+
if (rewindConversation)
|
|
3198
|
+
pruneMarksAfter(_checkpointMarks, turnIndex);
|
|
3098
3199
|
if (cp) {
|
|
3099
3200
|
const q = (p) => new Promise((r) => rl.question(p, r));
|
|
3100
|
-
const
|
|
3101
|
-
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
),
|
|
3105
|
-
)
|
|
3106
|
-
)
|
|
3201
|
+
const promptText = rewindConversation
|
|
3202
|
+
? ` Also restore files to before this turn? (checkpoint ${cp.id}) [Y/n] `
|
|
3203
|
+
: ` Restore files to before this turn? (checkpoint ${cp.id}) [Y/n] `;
|
|
3204
|
+
const ans = (await q(chalk.yellow(promptText)))
|
|
3107
3205
|
.trim()
|
|
3108
3206
|
.toLowerCase();
|
|
3109
3207
|
if (ans === "" || ans === "y" || ans === "yes") {
|
|
@@ -3120,7 +3218,7 @@ export async function startAgentRepl(options = {}) {
|
|
|
3120
3218
|
);
|
|
3121
3219
|
} catch (e) {
|
|
3122
3220
|
logger.error(
|
|
3123
|
-
` file restore skipped: ${e.message} (conversation already rewound)`,
|
|
3221
|
+
` file restore skipped: ${e.message}${rewindConversation ? " (conversation already rewound)" : ""}`,
|
|
3124
3222
|
);
|
|
3125
3223
|
}
|
|
3126
3224
|
} else {
|
|
@@ -3130,9 +3228,19 @@ export async function startAgentRepl(options = {}) {
|
|
|
3130
3228
|
),
|
|
3131
3229
|
);
|
|
3132
3230
|
}
|
|
3231
|
+
} else if (restoreFiles && scope === RESTORE_SCOPE.FILES) {
|
|
3232
|
+
logger.log(
|
|
3233
|
+
chalk.gray(
|
|
3234
|
+
" no checkpoint captured for that turn — nothing to restore",
|
|
3235
|
+
),
|
|
3236
|
+
);
|
|
3133
3237
|
}
|
|
3134
3238
|
prompt();
|
|
3135
|
-
|
|
3239
|
+
// Prefill the input line with the turn's text only when the
|
|
3240
|
+
// conversation was rewound (edit-and-resend); files-only leaves the
|
|
3241
|
+
// conversation and the prompt untouched.
|
|
3242
|
+
if (rewindConversation && typeof targetTurn.content === "string")
|
|
3243
|
+
rl.write(targetTurn.content);
|
|
3136
3244
|
return;
|
|
3137
3245
|
}
|
|
3138
3246
|
}
|
|
@@ -3437,7 +3545,9 @@ export async function startAgentRepl(options = {}) {
|
|
|
3437
3545
|
if (!_asyncHookSupervisor) {
|
|
3438
3546
|
const { AsyncHookSupervisor } =
|
|
3439
3547
|
await import("../lib/async-hook-supervisor.cjs");
|
|
3440
|
-
_asyncHookSupervisor = new AsyncHookSupervisor(
|
|
3548
|
+
_asyncHookSupervisor = new AsyncHookSupervisor({
|
|
3549
|
+
persistStats: true,
|
|
3550
|
+
});
|
|
3441
3551
|
}
|
|
3442
3552
|
dispatchAsyncHooks(_settingsHooks, "ConfigChange", payload, {
|
|
3443
3553
|
cwd,
|
|
@@ -4813,7 +4923,9 @@ export async function startAgentRepl(options = {}) {
|
|
|
4813
4923
|
if (!_asyncHookSupervisor) {
|
|
4814
4924
|
const { AsyncHookSupervisor } =
|
|
4815
4925
|
await import("../lib/async-hook-supervisor.cjs");
|
|
4816
|
-
_asyncHookSupervisor = new AsyncHookSupervisor(
|
|
4926
|
+
_asyncHookSupervisor = new AsyncHookSupervisor({
|
|
4927
|
+
persistStats: true,
|
|
4928
|
+
});
|
|
4817
4929
|
}
|
|
4818
4930
|
dispatchAsyncHooks(
|
|
4819
4931
|
_settingsHooks,
|
|
@@ -5100,7 +5212,7 @@ export async function startAgentRepl(options = {}) {
|
|
|
5100
5212
|
if (_settingsHooks && !_asyncHookSupervisor) {
|
|
5101
5213
|
const { AsyncHookSupervisor } =
|
|
5102
5214
|
await import("../lib/async-hook-supervisor.cjs");
|
|
5103
|
-
_asyncHookSupervisor = new AsyncHookSupervisor();
|
|
5215
|
+
_asyncHookSupervisor = new AsyncHookSupervisor({ persistStats: true });
|
|
5104
5216
|
}
|
|
5105
5217
|
const {
|
|
5106
5218
|
content: response,
|
|
@@ -5442,7 +5554,9 @@ export async function startAgentRepl(options = {}) {
|
|
|
5442
5554
|
if (!_asyncHookSupervisor) {
|
|
5443
5555
|
const { AsyncHookSupervisor } =
|
|
5444
5556
|
await import("../lib/async-hook-supervisor.cjs");
|
|
5445
|
-
_asyncHookSupervisor = new AsyncHookSupervisor(
|
|
5557
|
+
_asyncHookSupervisor = new AsyncHookSupervisor({
|
|
5558
|
+
persistStats: true,
|
|
5559
|
+
});
|
|
5446
5560
|
}
|
|
5447
5561
|
dispatchAsyncHooks(
|
|
5448
5562
|
_settingsHooks,
|
|
@@ -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
|
|
@@ -2100,6 +2125,26 @@ async function _withPostEditDiagnostics(result, filePath, cwd) {
|
|
|
2100
2125
|
return newDiagnostics ? { ...result, newDiagnostics } : result;
|
|
2101
2126
|
}
|
|
2102
2127
|
|
|
2128
|
+
/**
|
|
2129
|
+
* Drop skills whose `paths:` frontmatter scopes them to a different subtree than
|
|
2130
|
+
* the agent's cwd (large-monorepo lazy skill surface). A skill with no `paths`
|
|
2131
|
+
* applies everywhere, so a skill set with none is returned unchanged. Best-effort:
|
|
2132
|
+
* any failure (or an unlocatable project root → relCwd "") falls open to the full
|
|
2133
|
+
* list, never hiding a skill spuriously.
|
|
2134
|
+
*/
|
|
2135
|
+
async function filterSkillsByCwd(skills, cwd) {
|
|
2136
|
+
try {
|
|
2137
|
+
if (!Array.isArray(skills) || skills.every((s) => !s?.paths)) return skills;
|
|
2138
|
+
const { filterSkillsByRelCwd, relCwdForCwd } =
|
|
2139
|
+
await import("../lib/skill-path-scope.js");
|
|
2140
|
+
const { findProjectRoot } = await import("../lib/project-detector.js");
|
|
2141
|
+
const root = (cwd && findProjectRoot(cwd)) || cwd || "";
|
|
2142
|
+
return filterSkillsByRelCwd(skills, relCwdForCwd(cwd, root));
|
|
2143
|
+
} catch {
|
|
2144
|
+
return skills; // fail-open: never hide a skill because scoping errored
|
|
2145
|
+
}
|
|
2146
|
+
}
|
|
2147
|
+
|
|
2103
2148
|
async function executeToolInner(
|
|
2104
2149
|
name,
|
|
2105
2150
|
args,
|
|
@@ -2323,6 +2368,30 @@ async function executeToolInner(
|
|
|
2323
2368
|
replaceAll,
|
|
2324
2369
|
);
|
|
2325
2370
|
if (count === 0) {
|
|
2371
|
+
// Idempotent replay (P0-2 "Diff Apply 内容哈希"): a resumed edit whose
|
|
2372
|
+
// old_string is already gone but whose new_string is present already
|
|
2373
|
+
// landed. Report a no-op success (NO write occurs — zero data risk)
|
|
2374
|
+
// rather than a misleading "not found" error, so a recovered worker
|
|
2375
|
+
// doesn't get stuck re-issuing an edit that already happened.
|
|
2376
|
+
const replay = classifyEditReplay({
|
|
2377
|
+
content,
|
|
2378
|
+
oldString: args.old_string,
|
|
2379
|
+
newString: args.new_string,
|
|
2380
|
+
});
|
|
2381
|
+
if (replay === EDIT_REPLAY.ALREADY_APPLIED) {
|
|
2382
|
+
return attachDescriptor({
|
|
2383
|
+
success: true,
|
|
2384
|
+
path: filePath,
|
|
2385
|
+
alreadyApplied: true,
|
|
2386
|
+
idempotencyKey: editIdempotencyKey({
|
|
2387
|
+
path: args.path,
|
|
2388
|
+
oldString: args.old_string,
|
|
2389
|
+
newString: args.new_string,
|
|
2390
|
+
replaceAll,
|
|
2391
|
+
}),
|
|
2392
|
+
note: "edit already applied (new_string present, old_string absent) — no change made",
|
|
2393
|
+
});
|
|
2394
|
+
}
|
|
2326
2395
|
return attachDescriptor({ error: "old_string not found in file" });
|
|
2327
2396
|
}
|
|
2328
2397
|
// Require a UNIQUE match (Claude-Code Edit parity) — replacing the first
|
|
@@ -3220,6 +3289,16 @@ async function executeToolInner(
|
|
|
3220
3289
|
goalMaxTokens: args.goal_max_tokens ?? null,
|
|
3221
3290
|
goalMaxCost: args.goal_max_cost ?? null,
|
|
3222
3291
|
goalMaxTime: args.goal_max_time ?? null,
|
|
3292
|
+
// P1-8: action classes this unattended task may still perform (e.g.
|
|
3293
|
+
// "publish", "external_message"). Accepts an array or a comma list.
|
|
3294
|
+
unattendedAllowlist: Array.isArray(args.unattended_allow)
|
|
3295
|
+
? args.unattended_allow
|
|
3296
|
+
: typeof args.unattended_allow === "string"
|
|
3297
|
+
? args.unattended_allow
|
|
3298
|
+
.split(",")
|
|
3299
|
+
.map((s) => s.trim())
|
|
3300
|
+
.filter(Boolean)
|
|
3301
|
+
: null,
|
|
3223
3302
|
};
|
|
3224
3303
|
if (action === "wakeup") {
|
|
3225
3304
|
if (!args.prompt) {
|
|
@@ -3620,7 +3699,10 @@ async function executeToolInner(
|
|
|
3620
3699
|
}
|
|
3621
3700
|
|
|
3622
3701
|
case "run_skill": {
|
|
3623
|
-
const allSkills =
|
|
3702
|
+
const allSkills = await filterSkillsByCwd(
|
|
3703
|
+
skillLoader.getResolvedSkills().filter(skillAllowed),
|
|
3704
|
+
cwd,
|
|
3705
|
+
);
|
|
3624
3706
|
if (allSkills.length === 0) {
|
|
3625
3707
|
return attachDescriptor({
|
|
3626
3708
|
error: _skillAllowlist
|
|
@@ -3766,7 +3848,10 @@ async function executeToolInner(
|
|
|
3766
3848
|
}
|
|
3767
3849
|
|
|
3768
3850
|
case "list_skills": {
|
|
3769
|
-
let skills =
|
|
3851
|
+
let skills = await filterSkillsByCwd(
|
|
3852
|
+
skillLoader.getResolvedSkills().filter(skillAllowed),
|
|
3853
|
+
cwd,
|
|
3854
|
+
);
|
|
3770
3855
|
if (skills.length === 0) {
|
|
3771
3856
|
return attachDescriptor({
|
|
3772
3857
|
error: _skillAllowlist
|
|
@@ -6379,6 +6464,22 @@ function extractInitialPromptText(messages) {
|
|
|
6379
6464
|
return "";
|
|
6380
6465
|
}
|
|
6381
6466
|
|
|
6467
|
+
/**
|
|
6468
|
+
* Stable id for a permission GATE decision, so a denied/gated tool span can be
|
|
6469
|
+
* correlated with the decision that blocked it. Only gated results carry a
|
|
6470
|
+
* `policy` (allow-path tools execute with no distinct decision), so this returns
|
|
6471
|
+
* null when there's nothing to identify. Deterministic (call id + gate) — no
|
|
6472
|
+
* clock/RNG — so a consumer holding the tool-result's policy + tool_call_id can
|
|
6473
|
+
* recompute it. Not exported (no shim-parity impact) — only the telemetry seam
|
|
6474
|
+
* uses it.
|
|
6475
|
+
*/
|
|
6476
|
+
function permissionDecisionId(callId, policy) {
|
|
6477
|
+
if (!policy || typeof policy !== "object" || !policy.decision) return null;
|
|
6478
|
+
const gate = policy.via || policy.decision;
|
|
6479
|
+
const base = callId || "call";
|
|
6480
|
+
return `${base}:perm:${gate}`;
|
|
6481
|
+
}
|
|
6482
|
+
|
|
6382
6483
|
export async function* agentLoop(messages, options) {
|
|
6383
6484
|
// Shared iteration budget — replaces hardcoded MAX_ITERATIONS.
|
|
6384
6485
|
// When options.iterationBudget is provided (e.g. from parent agent),
|
|
@@ -6858,6 +6959,18 @@ export async function* agentLoop(messages, options) {
|
|
|
6858
6959
|
}
|
|
6859
6960
|
}
|
|
6860
6961
|
|
|
6962
|
+
// Per-span unified ids (P2 observability): the run-level default attributes
|
|
6963
|
+
// stamp session/agent/workflow ids on every span, but turn/prompt ids are
|
|
6964
|
+
// per-iteration. A "turn" is one model iteration of the run; "prompt" is the
|
|
6965
|
+
// model request within that turn. Normalized through buildTelemetryAttributes
|
|
6966
|
+
// so they get the same charset-sanitized + cardinality-bounded treatment as
|
|
6967
|
+
// the run-level ids (turn.id correlates with the agent.iteration counter).
|
|
6968
|
+
const modelIdAttrs = recorder
|
|
6969
|
+
? buildTelemetryAttributes({
|
|
6970
|
+
turnId: `${runId}:t${budget.consumed}`,
|
|
6971
|
+
promptId: `${runId}:t${budget.consumed}:p`,
|
|
6972
|
+
})
|
|
6973
|
+
: null;
|
|
6861
6974
|
const result = await _withSpan(
|
|
6862
6975
|
recorder,
|
|
6863
6976
|
"agent.model",
|
|
@@ -6865,6 +6978,7 @@ export async function* agentLoop(messages, options) {
|
|
|
6865
6978
|
"gen_ai.system": options.provider || "ollama",
|
|
6866
6979
|
"gen_ai.request.model": options.model || "unknown",
|
|
6867
6980
|
"agent.iteration": budget.consumed,
|
|
6981
|
+
...(modelIdAttrs || {}),
|
|
6868
6982
|
},
|
|
6869
6983
|
() => llmCall(callMessages, options),
|
|
6870
6984
|
(span, r) => {
|
|
@@ -6886,6 +7000,21 @@ export async function* agentLoop(messages, options) {
|
|
|
6886
7000
|
Array.isArray(r?.message?.tool_calls) &&
|
|
6887
7001
|
r.message.tool_calls.length > 0,
|
|
6888
7002
|
);
|
|
7003
|
+
// response CONTENT is opt-in (--otlp-content): stamped only when
|
|
7004
|
+
// enabled, redacted + length-capped through the same normalizer; the
|
|
7005
|
+
// field is entirely absent by default so default OTLP stays unchanged.
|
|
7006
|
+
if (options.otlpIncludeContent === true && r?.message?.content) {
|
|
7007
|
+
const respAttrs = buildTelemetryAttributes(
|
|
7008
|
+
{ response: r.message.content },
|
|
7009
|
+
{ includeContent: true },
|
|
7010
|
+
);
|
|
7011
|
+
if (respAttrs["content.response"] != null) {
|
|
7012
|
+
span.setAttribute(
|
|
7013
|
+
"content.response",
|
|
7014
|
+
respAttrs["content.response"],
|
|
7015
|
+
);
|
|
7016
|
+
}
|
|
7017
|
+
}
|
|
6889
7018
|
},
|
|
6890
7019
|
"model_error",
|
|
6891
7020
|
);
|
|
@@ -7163,17 +7292,66 @@ export async function* agentLoop(messages, options) {
|
|
|
7163
7292
|
|
|
7164
7293
|
let toolResult;
|
|
7165
7294
|
let toolError = null;
|
|
7295
|
+
// Per-span unified ids (P2 observability): a tool span carries its turn.id
|
|
7296
|
+
// (correlating with the model span of the same iteration), the provider's
|
|
7297
|
+
// tool_use.id (so a tool-result event can be tied back to its call), and —
|
|
7298
|
+
// when auto-checkpoint fired before a mutating tool — the checkpoint.id the
|
|
7299
|
+
// user could restore to. All normalized through buildTelemetryAttributes.
|
|
7300
|
+
// tool_arguments CONTENT is opt-in (--otlp-content): the alias key is only
|
|
7301
|
+
// present when explicitly enabled, so by default the field is omitted
|
|
7302
|
+
// entirely (byte-identical default OTLP), and even opted-in it's length-
|
|
7303
|
+
// capped by redactContent. Mirrors the run-level content.prompt opt-in.
|
|
7304
|
+
const toolContentOptIn = options.otlpIncludeContent === true;
|
|
7305
|
+
const toolIdAttrs = recorder
|
|
7306
|
+
? buildTelemetryAttributes(
|
|
7307
|
+
{
|
|
7308
|
+
turnId: `${runId}:t${budget.consumed}`,
|
|
7309
|
+
toolUseId: call.id,
|
|
7310
|
+
checkpointId: cpId || undefined,
|
|
7311
|
+
...(toolContentOptIn ? { toolArguments: toolArgs } : {}),
|
|
7312
|
+
},
|
|
7313
|
+
{ includeContent: toolContentOptIn },
|
|
7314
|
+
)
|
|
7315
|
+
: null;
|
|
7166
7316
|
try {
|
|
7167
7317
|
toolResult = await _withSpan(
|
|
7168
7318
|
recorder,
|
|
7169
7319
|
"agent.tool",
|
|
7170
|
-
{ "tool.name": toolName },
|
|
7320
|
+
{ "tool.name": toolName, ...(toolIdAttrs || {}) },
|
|
7171
7321
|
() => executeTool(toolName, toolArgs, toolContext),
|
|
7172
7322
|
(span, r) => {
|
|
7173
7323
|
span.setAttribute(
|
|
7174
7324
|
"tool.is_error",
|
|
7175
7325
|
!!(r && typeof r === "object" && r.error),
|
|
7176
7326
|
);
|
|
7327
|
+
// permission.decision_id (P2): a GATED tool result carries a
|
|
7328
|
+
// `policy` (deny / ask-fail / host-block / sandbox); allow-path
|
|
7329
|
+
// tools execute with no distinct decision. Stamp a stable id
|
|
7330
|
+
// (derived from the call + gate, so it's recomputable from the
|
|
7331
|
+
// tool-result's policy + tool_call_id) plus the low-cardinality
|
|
7332
|
+
// decision, letting a blocked tool span be tied to its decision.
|
|
7333
|
+
const decId =
|
|
7334
|
+
r && typeof r === "object"
|
|
7335
|
+
? permissionDecisionId(call.id, r.policy)
|
|
7336
|
+
: null;
|
|
7337
|
+
if (decId) {
|
|
7338
|
+
const permAttrs = buildTelemetryAttributes({
|
|
7339
|
+
decisionId: decId,
|
|
7340
|
+
"permission.decision": r.policy.decision,
|
|
7341
|
+
});
|
|
7342
|
+
if (permAttrs["permission.decision_id"]) {
|
|
7343
|
+
span.setAttribute(
|
|
7344
|
+
"permission.decision_id",
|
|
7345
|
+
permAttrs["permission.decision_id"],
|
|
7346
|
+
);
|
|
7347
|
+
}
|
|
7348
|
+
if (permAttrs["permission.decision"]) {
|
|
7349
|
+
span.setAttribute(
|
|
7350
|
+
"permission.decision",
|
|
7351
|
+
permAttrs["permission.decision"],
|
|
7352
|
+
);
|
|
7353
|
+
}
|
|
7354
|
+
}
|
|
7177
7355
|
},
|
|
7178
7356
|
"tool_error",
|
|
7179
7357
|
);
|
|
@@ -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
|
*
|