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/data/changelog.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"releases": [
|
|
4
4
|
{
|
|
5
5
|
"productVersion": "Unreleased",
|
|
6
|
-
"cliVersion": "0.162.
|
|
6
|
+
"cliVersion": "0.162.164",
|
|
7
7
|
"cliVersions": [
|
|
8
8
|
"0.4.46",
|
|
9
9
|
"0.4.51",
|
|
@@ -21,14 +21,15 @@
|
|
|
21
21
|
"0.162.160",
|
|
22
22
|
"0.162.161",
|
|
23
23
|
"0.162.162",
|
|
24
|
-
"0.162.163"
|
|
24
|
+
"0.162.163",
|
|
25
|
+
"0.162.164"
|
|
25
26
|
],
|
|
26
27
|
"date": null,
|
|
27
28
|
"title": "",
|
|
28
29
|
"sections": [
|
|
29
30
|
{
|
|
30
|
-
"heading": "Added — cc CLI 0.162.
|
|
31
|
-
"body": "> `chainlesschain` 0.162.162 → **0.162.163** 发 npm `latest`(经 `npm-publish.yml`,`--provenance --access public`)。纯 `packages/cli/src` 增量;未触 `pdh/lib` → 无 Android cc bundle rollover / 无 USR_VERSION 改动。命令面只加子命令(`cc agenda prune`、`cc plugin consent`、`cc hook replay`/`events-log`、`cc context --sources`)与 REPL slash(`/goal`)+ 工具参数/flag,**顶层命令数 175 不变**。本版把 0.162.162 announce 的多数纯核落成真实**运行时接线**,并补齐 `docs/CLAUDE_CODE_CLI_INCREMENTAL_GAP_ANALYSIS_2026-07-12.md` 各节的 last-mile「仍欠」。全套本地三层全绿(unit+integration 25,008 + e2e 628,0 真失败)。\n\n- **Subagent 契约全轴强制(P1)**:`_executeSpawnSubAgent` 真正消费 `resolveSubagentContract`(spawnArgs > agent-file > 父级天花板),tighten-only 强制 skills/mcp/hooks/memory capability INTERSECT、permissionMode(plan→只读工具集)、budget/effort/context、worktree fail-closed、递归深度/宽度上限;非 plan confirmer 线程化 + headless 与交互 REPL 均以运行模式播种子级天花板;子级专属无 confirmer ApprovalGate 强制 run_shell/run_code/browser_act 会话策略。\n- **跨 Agent 授权边界接线(P0)**:本地 headless 审批链路携带并校验 `tool_call_id + normalized_arguments + policy_digest` 绑定(重放/参数替换/错投的批准判 `binding-mismatch` 拒绝);入站 channel 事件显式打 steer authority(其载荷里的 \"approved\" 只是文本,不能代表用户批准)。\n- **会话完成条件引擎接线(P1)**:`--goal-condition <spec>`(exit-zero/file-exists/contains/regex/model)驱动 headless 外层回合,跨进程 `--resume` 续跑(持久 `goal_snapshot`),REPL `/goal <condition>` 交互入口;`goal_started/evaluated/completed/exhausted` 事件。\n- **Turn↔Checkpoint 绑定持久化 + coverage-aware /rewind(P1)**:绑定作为链式会话事件持久化;`/rewind` 打印诚实的 coverage/警告(副作用 / 缺 checkpoint / 对话-文件漂移),永不过度承诺可恢复范围。\n- **副作用台账崩溃恢复(P0 纯核)**:`side-effect-ledger.js` 两阶段记账 `prepare→start→commit|fail|unknown` + `reconcileSideEffects` 分桶 redo/inspect/skip(file-write/git-push/package-install 中途强杀不重复副作用)。\n- **持久 Scheduler/Monitor 扩展(P1)**:`cc agenda prune`(回收终态条目)+ 过期退休(`expiresAt`/`retireExpired`)+ Monitor 文件/HTTP/mtime-change 源(替每 tick spawn shell)+ agent `schedule` 工具透传 `expires`/`jitter` + 每任务 permission-mode/worktree/turn 预算。\n- **Plugin 能力 consent 生命周期(P1)**:consent 存储 + load-path chokepoint enforcement(opt-in)+ `cc plugin consent` + `add`/`upgrade` 渲染能力清单+diff+re-consent 提示 + doctor 未同意/坏 manifest 告警。\n- **Monorepo 上下文(P1)**:`instructionExcludes` 从分层 settings 接进 `composeSystemPrompt`(排除 legacy/vendor/generated 子树,含 @import)+ `cc context --sources`(逐来源 token 归因)+ 懒加载子树指令纯核。\n- **Hooks 持久事件日志 + Replay(P2)**:哈希链 JSONL 事件日志(opt-in `CC_HOOK_EVENT_LOG`)+ `cc hook replay`(决策类事件必显式 `--sandbox`,绝不 re-gate 真流程)/`cc hook events-log`(列出/校验链)。\n- **JSON Schema(P2)**:`--json-schema` 接受 inline JSON + 与 `stream-json` 兼容(终局 `structured_result` NDJSON 行)+ format/if-then-else 断言 + 约束式重试改用富校验器(带 RFC 6901 JSON Pointer 提示)。\n- **多 Agent Review(P2)**:`cc review --multi`(correctness/security/performance/tests 分维 finder 扇出)+ `--verify`(skeptic 复现剔除 refuted)+ high effort 自动双开。\n- **OTel(P2)**:`--otlp-content` opt-in 输出 prompt/response/tool_arguments(默认脱敏 `[redacted]`)+ 每 span 归一化 session/agent id。"
|
|
31
|
+
"heading": "Added — cc CLI 0.162.164:IDE 增量 gap-analysis (2026-07-13) 全批接线落地——凭据脱敏三导出面 / /rewind 从这里分支 / cc doctor 执行位置 / cc session pr-status / 复杂 Diff 行评论锚定 / 终端上下文策略 / worktree 清理安全闸 / 崩溃恢复台账 / 无人值守动作门 / 跨设备操作指纹(CLI-only npm 发版)",
|
|
32
|
+
"body": "> `chainlesschain` 0.162.163 → **0.162.164** 发 npm `latest`(经 `npm-publish.yml`,`--provenance --access public`)。纯 `packages/cli/src` 增量;未触 `pdh/lib` → 无 Android cc bundle rollover / 无 USR_VERSION 改动。命令面只加 **session 子命令 `cc session pr-status`** 与 REPL `/rewind <n> --branch`、`cc doctor` 新增「Execution context」诊断段 + 工具 flag,**顶层命令数 175 不变**。本版把 `docs/CLAUDE_CODE_IDE_INCREMENTAL_GAP_ANALYSIS_2026-07-13.md` 的可 Windows 落地项整批接线(纯核已建、本版补真实运行时/命令面接线,默认路径保持字节不变)。\n\n- **8.1 凭据脱敏三导出面全闸**:一份导出物离机必须朝「不泄漏」失败——`cc session export` 导出前对渲染 transcript 跑召回优先 secret-scan(命中替 `[REDACTED]`,脱敏计数写 stderr,`--no-redact` 保原值);OTLP `toOtlp()` 对 span/event 字符串属性(含 exception message)默认脱敏(`cc agent/team --otlp`、`cc eval`);诊断包 `cc doctor --export-bundle` 强制脱敏(P1-9)。三面共用同一 recall-first 扫描器。\n- **P0-2 崩溃恢复副作用台账**:两阶段记账 `prepare→start→commit|fail|unknown` 接进 headless 与 IDE/Bridge/Extension-Host resume——`--resume` reconcile 台账,started-未落定-非幂等 op 注入去标识「Recovery notice」而非盲目重跑;Diff-Apply 内容哈希幂等(old 已改+new 已在→幂等 no-op 不重写);外部操作 Idempotency Key + 0-重复副作用度量。\n- **P0-3「从这里分支」进 `/rewind`**:`/rewind <n> --branch [--write]`(`--fork` 别名)从选中 turn 派生独立会话,保留分支点之前历史并从此分叉,**父会话绝不被截断**;确定性分支 id + `session_branch` 血统事件 + 幂等重放 + traversal fail-closed。\n- **P1-1 复杂 Diff 行评论锚定**:review finding 锚到它所指的**代码**(file+baseHash+锚定行原文+上下文)而非裸坐标;agent 改文件后可 `reanchorComment` 重定位/标 outdated,绝不复用旧行号(接进 `review-pipeline` finding 输出 + `cc review` 尽力填 fileContents)。\n- **P1-2 终端上下文策略 + 后台任务脱敏**:`selectTerminalContextForModel` fail-closed(非显式 opt-in 绝不把终端 scrollback 塞进模型上下文)+ ANSI-strip→secret-redact→scrollback-cap;`check_shell {list}` / `/tasks` 后台任务快照对命令串脱敏并暴露 PID(stoppable 仅在握真 PID 时)。\n- **P1-4 PR/CI 监控 + 受控合并**:新子命令 `cc session pr-status [id] [--pr N] [--checks-file <json>] [--enable] [--json]`——从 PR-link ledger 取会话关联 PR,`gh pr view` 或 `--checks-file` 采信号,渲染状态条 + **默认关**的 auto-merge 合格性判定(穷举列未满足项:分支保护/Review/待审批/失败或 pending check/缺席必需 check)。\n- **P1-5 worktree 清理安全闸**:后台 agent worktree reaper 清理前过 fail-closed 安全闸——未提交/未追踪/未 push/关联 PR 任一命中即**保留**(旧「全清」逃生门 `force:true` 保留);`git` 读不出→保留(绝不销毁无法核实的工作)。\n- **P1-6 会话级 agent 工具准入 + 归因**:extension 层工具(run_skill/spawn_sub_agent/browser/schedule/publish_artifact/MCP)需 Capability+Policy+权限+预算+UI 五闸全过;每次调用记只带来源的安全归因(可落 transcript)。\n- **P1-7 执行位置一等属性**:`cc doctor` 新增「Execution context」诊断段——检测 ambient location(Codespaces/SSH/Container/WSL/Local),remote/unknown fail-closed 到只读地板并列 policy 违规;凭据只显示来源/作用域不显示值。\n- **P1-8 无人值守动作门**:`cc agenda` 定时/事件任务默认拦截 notify/publish_artifact 等高危外呼(`unattendedAllowlist` 显式放回);action-class 分级 fail-closed。\n- **P1-9 脱敏诊断包**:`cc doctor --export-bundle` 一键导出去标识诊断包(版本/平台/Capability/连接/脱敏事件/进程·端口·锁文件·worktree 摘要;默认不含源码/Key/Cookie/完整环境变量;导出前强制 secret-scan)。\n- **8.2 跨设备操作指纹**:远程审批 `remote-approval-bridge` 升级到全元组 `OperationApprovalRegistry`(工具名/规范化参数/目标环境/workspace/session/策略版本/有效期);`permission.request` 发完整指纹 + 短标识 + 无秘密摘要,`approval.resolve` 失败关闭(mismatch/superseded/duplicate/过窗)。\n- **Monorepo / P2 硬化**:worktree `sparsePaths` 稀疏检出 + `symlinkDirectories` 逃逸守卫接进 team runner;`cc doctor` 新增沙箱真能力/静默降级、重复 skill 影子、settings.json hook 配置静态校验、async-hook 可靠性健康检查;LSP 服务器可选指数退避重启;每 span 归一 OTel session/agent id + `permission.decision_id`。\n\n> `chainlesschain` 0.162.162 → **0.162.163** 发 npm `latest`(经 `npm-publish.yml`,`--provenance --access public`)。纯 `packages/cli/src` 增量;未触 `pdh/lib` → 无 Android cc bundle rollover / 无 USR_VERSION 改动。命令面只加子命令(`cc agenda prune`、`cc plugin consent`、`cc hook replay`/`events-log`、`cc context --sources`)与 REPL slash(`/goal`)+ 工具参数/flag,**顶层命令数 175 不变**。本版把 0.162.162 announce 的多数纯核落成真实**运行时接线**,并补齐 `docs/CLAUDE_CODE_CLI_INCREMENTAL_GAP_ANALYSIS_2026-07-12.md` 各节的 last-mile「仍欠」。全套本地三层全绿(unit+integration 25,008 + e2e 628,0 真失败)。\n\n- **Subagent 契约全轴强制(P1)**:`_executeSpawnSubAgent` 真正消费 `resolveSubagentContract`(spawnArgs > agent-file > 父级天花板),tighten-only 强制 skills/mcp/hooks/memory capability INTERSECT、permissionMode(plan→只读工具集)、budget/effort/context、worktree fail-closed、递归深度/宽度上限;非 plan confirmer 线程化 + headless 与交互 REPL 均以运行模式播种子级天花板;子级专属无 confirmer ApprovalGate 强制 run_shell/run_code/browser_act 会话策略。\n- **跨 Agent 授权边界接线(P0)**:本地 headless 审批链路携带并校验 `tool_call_id + normalized_arguments + policy_digest` 绑定(重放/参数替换/错投的批准判 `binding-mismatch` 拒绝);入站 channel 事件显式打 steer authority(其载荷里的 \"approved\" 只是文本,不能代表用户批准)。\n- **会话完成条件引擎接线(P1)**:`--goal-condition <spec>`(exit-zero/file-exists/contains/regex/model)驱动 headless 外层回合,跨进程 `--resume` 续跑(持久 `goal_snapshot`),REPL `/goal <condition>` 交互入口;`goal_started/evaluated/completed/exhausted` 事件。\n- **Turn↔Checkpoint 绑定持久化 + coverage-aware /rewind(P1)**:绑定作为链式会话事件持久化;`/rewind` 打印诚实的 coverage/警告(副作用 / 缺 checkpoint / 对话-文件漂移),永不过度承诺可恢复范围。\n- **副作用台账崩溃恢复(P0 纯核)**:`side-effect-ledger.js` 两阶段记账 `prepare→start→commit|fail|unknown` + `reconcileSideEffects` 分桶 redo/inspect/skip(file-write/git-push/package-install 中途强杀不重复副作用)。\n- **持久 Scheduler/Monitor 扩展(P1)**:`cc agenda prune`(回收终态条目)+ 过期退休(`expiresAt`/`retireExpired`)+ Monitor 文件/HTTP/mtime-change 源(替每 tick spawn shell)+ agent `schedule` 工具透传 `expires`/`jitter` + 每任务 permission-mode/worktree/turn 预算。\n- **Plugin 能力 consent 生命周期(P1)**:consent 存储 + load-path chokepoint enforcement(opt-in)+ `cc plugin consent` + `add`/`upgrade` 渲染能力清单+diff+re-consent 提示 + doctor 未同意/坏 manifest 告警。\n- **Monorepo 上下文(P1)**:`instructionExcludes` 从分层 settings 接进 `composeSystemPrompt`(排除 legacy/vendor/generated 子树,含 @import)+ `cc context --sources`(逐来源 token 归因)+ 懒加载子树指令纯核。\n- **Hooks 持久事件日志 + Replay(P2)**:哈希链 JSONL 事件日志(opt-in `CC_HOOK_EVENT_LOG`)+ `cc hook replay`(决策类事件必显式 `--sandbox`,绝不 re-gate 真流程)/`cc hook events-log`(列出/校验链)。\n- **JSON Schema(P2)**:`--json-schema` 接受 inline JSON + 与 `stream-json` 兼容(终局 `structured_result` NDJSON 行)+ format/if-then-else 断言 + 约束式重试改用富校验器(带 RFC 6901 JSON Pointer 提示)。\n- **多 Agent Review(P2)**:`cc review --multi`(correctness/security/performance/tests 分维 finder 扇出)+ `--verify`(skeptic 复现剔除 refuted)+ high effort 自动双开。\n- **OTel(P2)**:`--otlp-content` opt-in 输出 prompt/response/tool_arguments(默认脱敏 `[redacted]`)+ 每 span 归一化 session/agent id。"
|
|
32
33
|
},
|
|
33
34
|
{
|
|
34
35
|
"heading": "Added — cc CLI 0.162.162:增量 gap-analysis 收尾(P1 Subagent 契约/Turn-Checkpoint 绑定/Plugin 能力 Schema + P2 Hooks 事件总线/JSON Schema/多 Agent Review + LSP 诊断/Doctor+文档+OTel)+ IDE gap P0(远程审批指纹/会话生命周期)(CLI-only npm 发版)",
|
|
@@ -5,6 +5,46 @@ import {
|
|
|
5
5
|
CODING_AGENT_EVENT_TYPES,
|
|
6
6
|
} from "../../runtime/runtime-events.js";
|
|
7
7
|
import { createSessionRecord } from "../../runtime/contracts/session-record.js";
|
|
8
|
+
import { loadSideEffectLedger } from "../../lib/side-effect-ledger-store.js";
|
|
9
|
+
import { reconcileSideEffects } from "../../lib/side-effect-ledger.js";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* P0-2: on a bridge/IDE resume, surface any irreversible tool that was in flight
|
|
13
|
+
* when the prior worker died. Returns a de-identified recovery descriptor (kind
|
|
14
|
+
* + short key + reason, never argument values) plus a human-readable notice, or
|
|
15
|
+
* null when nothing needs verification. PURE apart from reading the ledger.
|
|
16
|
+
*/
|
|
17
|
+
function buildResumeRecovery(sessionId) {
|
|
18
|
+
try {
|
|
19
|
+
const ledger = loadSideEffectLedger(sessionId);
|
|
20
|
+
const plan = reconcileSideEffects(ledger);
|
|
21
|
+
if (!plan.inspect.length) return null;
|
|
22
|
+
const items = plan.plans
|
|
23
|
+
.filter((p) => p.action === "inspect")
|
|
24
|
+
.map((p) => {
|
|
25
|
+
const op = ledger.get(p.opId) || {};
|
|
26
|
+
return {
|
|
27
|
+
kind: op.kind || "unknown",
|
|
28
|
+
key: op.key || null,
|
|
29
|
+
reason: p.reason,
|
|
30
|
+
};
|
|
31
|
+
});
|
|
32
|
+
const notice =
|
|
33
|
+
"Recovery notice — the previous run was interrupted while these " +
|
|
34
|
+
"irreversible operations were in flight; their outcome is UNKNOWN. Do " +
|
|
35
|
+
"NOT blindly re-run them. Verify whether each already took effect before " +
|
|
36
|
+
"repeating it, and ask the user if unsure:\n" +
|
|
37
|
+
items
|
|
38
|
+
.map(
|
|
39
|
+
(it) =>
|
|
40
|
+
` • [${it.kind}]${it.key ? ` (${it.key})` : ""} — ${it.reason}`,
|
|
41
|
+
)
|
|
42
|
+
.join("\n");
|
|
43
|
+
return { count: items.length, items, notice };
|
|
44
|
+
} catch {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
8
48
|
|
|
9
49
|
// Build a unified envelope for a solicited WS response. The bridge correlates
|
|
10
50
|
// by `requestId` (the inbound request's id) and unwraps `payload` for callers
|
|
@@ -226,6 +266,14 @@ export async function handleSessionResume(server, id, ws, message) {
|
|
|
226
266
|
status: "resumed",
|
|
227
267
|
});
|
|
228
268
|
|
|
269
|
+
// P0-2: reconcile the crash-safe side-effect ledger. If a dangerous tool was
|
|
270
|
+
// in flight when the prior worker died, warn the IDE client AND inject a
|
|
271
|
+
// system note so the resumed model does not silently replay it.
|
|
272
|
+
const recovery = buildResumeRecovery(session.id);
|
|
273
|
+
if (recovery && Array.isArray(session.messages)) {
|
|
274
|
+
session.messages.push({ role: "system", content: recovery.notice });
|
|
275
|
+
}
|
|
276
|
+
|
|
229
277
|
server.emit(
|
|
230
278
|
RUNTIME_EVENTS.SESSION_RESUME,
|
|
231
279
|
createRuntimeEvent(
|
|
@@ -235,6 +283,7 @@ export async function handleSessionResume(server, id, ws, message) {
|
|
|
235
283
|
historyCount: history.length,
|
|
236
284
|
sessionType: session.type || null,
|
|
237
285
|
record,
|
|
286
|
+
...(recovery ? { recovery } : {}),
|
|
238
287
|
},
|
|
239
288
|
{ kind: "server", sessionId: session.id },
|
|
240
289
|
),
|
|
@@ -249,6 +298,7 @@ export async function handleSessionResume(server, id, ws, message) {
|
|
|
249
298
|
sessionId: session.id,
|
|
250
299
|
history,
|
|
251
300
|
record,
|
|
301
|
+
...(recovery ? { recovery } : {}),
|
|
252
302
|
},
|
|
253
303
|
session.id,
|
|
254
304
|
),
|
|
@@ -12,6 +12,12 @@
|
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
import { agentLoop, formatToolArgs } from "../../runtime/agent-core.js";
|
|
15
|
+
import { classifyToolSideEffect } from "../../lib/side-effect-ledger.js";
|
|
16
|
+
import {
|
|
17
|
+
loadSideEffectLedger,
|
|
18
|
+
persistSideEffectLedger,
|
|
19
|
+
} from "../../lib/side-effect-ledger-store.js";
|
|
20
|
+
import { operationIdempotencyKey } from "../../lib/idempotency.js";
|
|
15
21
|
import {
|
|
16
22
|
detectTaskType,
|
|
17
23
|
selectModelForTask,
|
|
@@ -35,6 +41,9 @@ export class WSAgentHandler {
|
|
|
35
41
|
this._processing = false;
|
|
36
42
|
this._abortController = null;
|
|
37
43
|
this._activeRequestId = null;
|
|
44
|
+
// P0-2: monotonic sequence for crash-safe side-effect ledger op ids. The
|
|
45
|
+
// session id + a per-op nonce keep ids unique across turns and processes.
|
|
46
|
+
this._sideEffectSeq = 0;
|
|
38
47
|
}
|
|
39
48
|
|
|
40
49
|
/**
|
|
@@ -120,6 +129,14 @@ export class WSAgentHandler {
|
|
|
120
129
|
signal: abortController.signal,
|
|
121
130
|
};
|
|
122
131
|
|
|
132
|
+
// P0-2: crash-safe side-effect ledger for the IDE/bridge path — mirrors
|
|
133
|
+
// the headless runner. A dangerous tool is recorded prepare→start (snapshot
|
|
134
|
+
// persisted BEFORE it settles) and commit/fail on its result, so a bridge
|
|
135
|
+
// worker killed mid-flight is surfaced for verification on resume (see
|
|
136
|
+
// handleSessionResume) instead of being blindly replayed.
|
|
137
|
+
const sideEffectLedger = loadSideEffectLedger(session.id);
|
|
138
|
+
let currentSideEffectOpId = null;
|
|
139
|
+
|
|
123
140
|
for await (const event of agentLoop(session.messages, loopOptions)) {
|
|
124
141
|
switch (event.type) {
|
|
125
142
|
case "slot-filling":
|
|
@@ -137,6 +154,28 @@ export class WSAgentHandler {
|
|
|
137
154
|
args: event.args,
|
|
138
155
|
display: formatToolArgs(event.tool, event.args),
|
|
139
156
|
});
|
|
157
|
+
currentSideEffectOpId = null;
|
|
158
|
+
{
|
|
159
|
+
const se = classifyToolSideEffect(event.tool, event.args);
|
|
160
|
+
if (se) {
|
|
161
|
+
const opId = `ws:${session.id}:${Date.now()}:${this._sideEffectSeq++}`;
|
|
162
|
+
currentSideEffectOpId = opId;
|
|
163
|
+
sideEffectLedger
|
|
164
|
+
.prepare(opId, {
|
|
165
|
+
kind: se.kind,
|
|
166
|
+
key: se.key,
|
|
167
|
+
meta: {
|
|
168
|
+
tool: event.tool,
|
|
169
|
+
idempotencyKey: operationIdempotencyKey({
|
|
170
|
+
tool: event.tool,
|
|
171
|
+
args: event.args,
|
|
172
|
+
}),
|
|
173
|
+
},
|
|
174
|
+
})
|
|
175
|
+
.start(opId);
|
|
176
|
+
persistSideEffectLedger(session.id, sideEffectLedger);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
140
179
|
break;
|
|
141
180
|
|
|
142
181
|
case "tool-result":
|
|
@@ -146,6 +185,17 @@ export class WSAgentHandler {
|
|
|
146
185
|
result: event.result,
|
|
147
186
|
error: event.error,
|
|
148
187
|
});
|
|
188
|
+
if (currentSideEffectOpId) {
|
|
189
|
+
const err = event.error || event.result?.error || null;
|
|
190
|
+
if (err)
|
|
191
|
+
sideEffectLedger.fail(
|
|
192
|
+
currentSideEffectOpId,
|
|
193
|
+
String(err).slice(0, 200),
|
|
194
|
+
);
|
|
195
|
+
else sideEffectLedger.commit(currentSideEffectOpId);
|
|
196
|
+
persistSideEffectLedger(session.id, sideEffectLedger);
|
|
197
|
+
currentSideEffectOpId = null;
|
|
198
|
+
}
|
|
149
199
|
break;
|
|
150
200
|
|
|
151
201
|
case "response-complete":
|
|
@@ -385,6 +385,66 @@ export function forkSession(sourceId) {
|
|
|
385
385
|
return newId;
|
|
386
386
|
}
|
|
387
387
|
|
|
388
|
+
/**
|
|
389
|
+
* Create an independent BRANCH session ("从这里分支" — P0-3's fourth restore
|
|
390
|
+
* action) that keeps a parent's conversation up to a chosen turn and diverges
|
|
391
|
+
* from there. Unlike forkSession() (whole-session copy, random id, no lineage),
|
|
392
|
+
* this writes ONLY the caller-supplied pre-branch messages under a deterministic
|
|
393
|
+
* id (see deriveBranchSessionId) and records parent lineage — so the origin
|
|
394
|
+
* session is never touched (preservesParent) and a replayed branch request
|
|
395
|
+
* resolves to the SAME file instead of a duplicate (idempotent).
|
|
396
|
+
*
|
|
397
|
+
* @param {object} params
|
|
398
|
+
* @param {string} params.branchSessionId deterministic branch id
|
|
399
|
+
* @param {string|null} [params.parentSessionId]
|
|
400
|
+
* @param {string|null} [params.parentTurnId]
|
|
401
|
+
* @param {Array<{role:string,content:any}>} [params.messages] pre-branch turns
|
|
402
|
+
* @param {{title?:string,provider?:string,model?:string}} [params.meta]
|
|
403
|
+
* @returns {{branchSessionId:string, created:boolean, messages:number}}
|
|
404
|
+
*/
|
|
405
|
+
export function createBranchSession({
|
|
406
|
+
branchSessionId,
|
|
407
|
+
parentSessionId = null,
|
|
408
|
+
parentTurnId = null,
|
|
409
|
+
messages = [],
|
|
410
|
+
meta = {},
|
|
411
|
+
} = {}) {
|
|
412
|
+
if (isUnsafeSessionId(branchSessionId)) {
|
|
413
|
+
throw new Error(
|
|
414
|
+
`unsafe branch session id: ${String(branchSessionId).slice(0, 60)}`,
|
|
415
|
+
);
|
|
416
|
+
}
|
|
417
|
+
// Idempotent: a replayed branch request resolves to the existing branch
|
|
418
|
+
// rather than doubling it (matches deriveBranchId's determinism).
|
|
419
|
+
if (existsSync(sessionPath(branchSessionId))) {
|
|
420
|
+
return { branchSessionId, created: false, messages: 0 };
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
startSession(branchSessionId, {
|
|
424
|
+
title: meta.title || `Branch of ${parentSessionId ?? "session"}`,
|
|
425
|
+
provider: meta.provider || "",
|
|
426
|
+
model: meta.model || "",
|
|
427
|
+
});
|
|
428
|
+
appendEvent(branchSessionId, "session_branch", {
|
|
429
|
+
parentSessionId: parentSessionId == null ? null : String(parentSessionId),
|
|
430
|
+
parentTurnId: parentTurnId == null ? null : String(parentTurnId),
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
let count = 0;
|
|
434
|
+
for (const m of Array.isArray(messages) ? messages : []) {
|
|
435
|
+
if (!m || m.role == null) continue;
|
|
436
|
+
if (m.role === "user") {
|
|
437
|
+
appendUserMessage(branchSessionId, m.content);
|
|
438
|
+
count += 1;
|
|
439
|
+
} else if (m.role === "assistant") {
|
|
440
|
+
appendAssistantMessage(branchSessionId, m.content);
|
|
441
|
+
count += 1;
|
|
442
|
+
}
|
|
443
|
+
// system prompt + tool scaffolding are re-established on resume; skip here.
|
|
444
|
+
}
|
|
445
|
+
return { branchSessionId, created: true, messages: count };
|
|
446
|
+
}
|
|
447
|
+
|
|
388
448
|
export function sessionExists(sessionId) {
|
|
389
449
|
if (isUnsafeSessionId(sessionId)) return false; // never resolve a traversal id
|
|
390
450
|
return existsSync(sessionPath(sessionId));
|
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import { execSync } from "node:child_process";
|
|
11
|
-
import { existsSync, rmSync } from "node:fs";
|
|
12
|
-
import { resolve } from "node:path";
|
|
11
|
+
import { existsSync, rmSync, symlinkSync, mkdirSync } from "node:fs";
|
|
12
|
+
import { resolve, dirname } from "node:path";
|
|
13
13
|
import {
|
|
14
14
|
isGitRepo,
|
|
15
15
|
gitExec,
|
|
@@ -17,10 +17,15 @@ import {
|
|
|
17
17
|
assertSafeGitRef,
|
|
18
18
|
assertSafeGitPath,
|
|
19
19
|
} from "../lib/git-integration.js";
|
|
20
|
+
import { evaluateWorktreeCleanup } from "../lib/worktree-cleanup-safety.js";
|
|
21
|
+
import {
|
|
22
|
+
normalizeSparsePaths,
|
|
23
|
+
planSymlinkDirectories,
|
|
24
|
+
} from "../lib/worktree-sparse.js";
|
|
20
25
|
|
|
21
26
|
const WORKTREE_DIR = ".worktrees";
|
|
22
27
|
|
|
23
|
-
export function createWorktree(repoDir, branchName, baseBranch) {
|
|
28
|
+
export function createWorktree(repoDir, branchName, baseBranch, options = {}) {
|
|
24
29
|
assertSafeGitRef(branchName, "branch name");
|
|
25
30
|
if (baseBranch) assertSafeGitRef(baseBranch, "base branch");
|
|
26
31
|
if (!isGitRepo(repoDir)) {
|
|
@@ -40,7 +45,46 @@ export function createWorktree(repoDir, branchName, baseBranch) {
|
|
|
40
45
|
const base = baseBranch || "HEAD";
|
|
41
46
|
gitExec(`worktree add "${worktreePath}" -b "${branchName}" ${base}`, repoDir);
|
|
42
47
|
|
|
43
|
-
|
|
48
|
+
const result = { path: worktreePath, branch: branchName };
|
|
49
|
+
|
|
50
|
+
// Sparse checkout: only materialize the packages this task needs. For a
|
|
51
|
+
// large monorepo this cuts worktree creation time and disk. Cone mode wants
|
|
52
|
+
// directory paths — our sparsePaths are package dirs. Fail-closed on unsafe
|
|
53
|
+
// paths (normalizeSparsePaths drops them); empty/absent → full checkout,
|
|
54
|
+
// byte-identical to the no-sparse path.
|
|
55
|
+
const sparsePaths = normalizeSparsePaths(options.sparsePaths);
|
|
56
|
+
if (sparsePaths) {
|
|
57
|
+
for (const p of sparsePaths) assertSafeGitPath(p, "sparse path");
|
|
58
|
+
gitExecArgs(["sparse-checkout", "set", ...sparsePaths], worktreePath);
|
|
59
|
+
result.sparsePaths = sparsePaths;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Dependency reuse: link explicitly-approved directories (e.g. node_modules)
|
|
63
|
+
// from the main checkout into the worktree so the task does not reinstall.
|
|
64
|
+
// planSymlinkDirectories throws (fail-closed) on any junction/symlink escape.
|
|
65
|
+
if (options.symlinkDirectories != null) {
|
|
66
|
+
const plan = planSymlinkDirectories(options.symlinkDirectories, {
|
|
67
|
+
repoDir,
|
|
68
|
+
worktreePath,
|
|
69
|
+
});
|
|
70
|
+
const linked = [];
|
|
71
|
+
for (const { name, source, dest } of plan) {
|
|
72
|
+
if (!existsSync(source)) continue; // approved but absent — nothing to reuse
|
|
73
|
+
if (existsSync(dest)) rmSync(dest, { recursive: true, force: true });
|
|
74
|
+
mkdirSync(dirname(dest), { recursive: true });
|
|
75
|
+
// 'junction' on Windows needs no admin and works for directories;
|
|
76
|
+
// 'dir' elsewhere. Both resolve to an absolute source we validated.
|
|
77
|
+
symlinkSync(
|
|
78
|
+
source,
|
|
79
|
+
dest,
|
|
80
|
+
process.platform === "win32" ? "junction" : "dir",
|
|
81
|
+
);
|
|
82
|
+
linked.push(name);
|
|
83
|
+
}
|
|
84
|
+
if (linked.length > 0) result.symlinkedDirectories = linked;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return result;
|
|
44
88
|
}
|
|
45
89
|
|
|
46
90
|
export function removeWorktree(repoDir, worktreePath, options = {}) {
|
|
@@ -145,12 +189,103 @@ export async function isolateTask(repoDir, taskId, fn) {
|
|
|
145
189
|
}
|
|
146
190
|
}
|
|
147
191
|
|
|
148
|
-
|
|
192
|
+
/**
|
|
193
|
+
* Gather the cleanup-safety state of one worktree for
|
|
194
|
+
* [[worktree-cleanup-safety.js]] `evaluateWorktreeCleanup`. Fail-closed: any git
|
|
195
|
+
* command that cannot be read leaves the worktree marked unverifiable (→ kept).
|
|
196
|
+
* `linkedPrs` is not known at this layer (no PR store here) and is left empty;
|
|
197
|
+
* a higher layer that knows about PRs can call `evaluateWorktreeCleanup` itself.
|
|
198
|
+
*/
|
|
199
|
+
function _worktreeCleanupState(repoDir, wt) {
|
|
200
|
+
const state = {
|
|
201
|
+
readable: true,
|
|
202
|
+
uncommitted: false,
|
|
203
|
+
untracked: false,
|
|
204
|
+
unpushed: false,
|
|
205
|
+
linkedPrs: [],
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
// Dirty state: separate tracked modifications from untracked files.
|
|
209
|
+
try {
|
|
210
|
+
const porcelain = execSync("git status --porcelain", {
|
|
211
|
+
cwd: wt.path,
|
|
212
|
+
encoding: "utf-8",
|
|
213
|
+
maxBuffer: 64 * 1024 * 1024,
|
|
214
|
+
});
|
|
215
|
+
const lines = porcelain.split("\n").filter((l) => l.trim().length > 0);
|
|
216
|
+
state.untracked = lines.some((l) => l.startsWith("??"));
|
|
217
|
+
state.uncommitted = lines.some((l) => !l.startsWith("??"));
|
|
218
|
+
} catch (_e) {
|
|
219
|
+
state.readable = false; // cannot verify → fail-closed keep
|
|
220
|
+
return state;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Unpushed: only relevant if the branch has commits beyond the repo HEAD; if
|
|
224
|
+
// it does, they are unpushed unless a remote branch contains them.
|
|
225
|
+
try {
|
|
226
|
+
assertSafeGitRef(wt.branch, "branch name");
|
|
227
|
+
if (_hasBranchCommits(repoDir, wt.branch)) {
|
|
228
|
+
const remotes = gitExecArgs(
|
|
229
|
+
["branch", "-r", "--contains", wt.branch],
|
|
230
|
+
repoDir,
|
|
231
|
+
);
|
|
232
|
+
state.unpushed = remotes.trim().length === 0;
|
|
233
|
+
}
|
|
234
|
+
} catch (_e) {
|
|
235
|
+
state.unpushed = true; // cannot prove the commits are pushed → keep
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
return state;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Assess (read-only) which `agent/*` worktrees are safe to remove. Returns the
|
|
243
|
+
* per-worktree decision from [[worktree-cleanup-safety.js]] without deleting
|
|
244
|
+
* anything — the "清理前检查未提交修改、未追踪文件、未 Push Commit 和关联 PR"
|
|
245
|
+
* report from IDE gap P1-5.
|
|
246
|
+
*/
|
|
247
|
+
export function assessAgentWorktreeCleanup(repoDir) {
|
|
248
|
+
const worktrees = listWorktrees(repoDir);
|
|
249
|
+
const assessments = [];
|
|
250
|
+
for (const wt of worktrees) {
|
|
251
|
+
if (!(wt.branch && wt.branch.startsWith("agent/"))) continue;
|
|
252
|
+
const state = _worktreeCleanupState(repoDir, wt);
|
|
253
|
+
const decision = evaluateWorktreeCleanup(state);
|
|
254
|
+
assessments.push({
|
|
255
|
+
path: wt.path,
|
|
256
|
+
branch: wt.branch,
|
|
257
|
+
safeToRemove: decision.safeToRemove,
|
|
258
|
+
blockers: decision.blockers,
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
return {
|
|
262
|
+
assessments,
|
|
263
|
+
removable: assessments.filter((a) => a.safeToRemove).length,
|
|
264
|
+
kept: assessments.filter((a) => !a.safeToRemove).length,
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Remove `agent/*` worktrees. By DEFAULT this is now fail-closed: a worktree
|
|
270
|
+
* with uncommitted / untracked / unpushed work (or one whose state cannot be
|
|
271
|
+
* read) is KEPT, not force-deleted — the P1-5 data-loss guard. Pass
|
|
272
|
+
* `{ force: true }` for the legacy "wipe every agent worktree" behavior.
|
|
273
|
+
*
|
|
274
|
+
* @returns {number} count of worktrees actually removed
|
|
275
|
+
*/
|
|
276
|
+
export function cleanupAgentWorktrees(repoDir, options = {}) {
|
|
277
|
+
const force = options.force === true;
|
|
149
278
|
const worktrees = listWorktrees(repoDir);
|
|
150
279
|
let cleaned = 0;
|
|
151
280
|
|
|
152
281
|
for (const wt of worktrees) {
|
|
153
282
|
if (wt.branch && wt.branch.startsWith("agent/")) {
|
|
283
|
+
if (!force) {
|
|
284
|
+
const decision = evaluateWorktreeCleanup(
|
|
285
|
+
_worktreeCleanupState(repoDir, wt),
|
|
286
|
+
);
|
|
287
|
+
if (!decision.safeToRemove) continue; // keep work we cannot safely lose
|
|
288
|
+
}
|
|
154
289
|
try {
|
|
155
290
|
removeWorktree(repoDir, wt.path, { deleteBranch: true });
|
|
156
291
|
cleaned++;
|
|
@@ -26,6 +26,7 @@ import { randomUUID } from "crypto";
|
|
|
26
26
|
import { isEntryExpired, effectiveFireAt } from "./schedule-planner.js";
|
|
27
27
|
import { SUBAGENT_PERMISSION_MODES } from "./subagent-contract.js";
|
|
28
28
|
import { parseGoalCondition } from "./goal-condition-engine.js";
|
|
29
|
+
import { normalizeActionClass } from "./unattended-action-policy.js";
|
|
29
30
|
|
|
30
31
|
export const SCHEDULE_KINDS = Object.freeze(["wakeup", "cron", "monitor"]);
|
|
31
32
|
|
|
@@ -83,6 +84,7 @@ export function normalizeRunPolicy({
|
|
|
83
84
|
goalMaxCost = null,
|
|
84
85
|
goalMaxTime = null,
|
|
85
86
|
maxOuterTurns = null,
|
|
87
|
+
unattendedAllowlist = null,
|
|
86
88
|
} = {}) {
|
|
87
89
|
const policy = {};
|
|
88
90
|
if (
|
|
@@ -119,6 +121,17 @@ export function normalizeRunPolicy({
|
|
|
119
121
|
if (Number.isFinite(outer) && outer > 0) policy.maxOuterTurns = outer;
|
|
120
122
|
}
|
|
121
123
|
}
|
|
124
|
+
|
|
125
|
+
// P1-8: action classes this UNATTENDED task may still perform (e.g. "publish",
|
|
126
|
+
// "external_message"). Validated against the known classes; unknowns dropped.
|
|
127
|
+
// Absent → the scheduled run gets the default-deny high-risk tool set.
|
|
128
|
+
if (Array.isArray(unattendedAllowlist)) {
|
|
129
|
+
const cleaned = [
|
|
130
|
+
...new Set(unattendedAllowlist.map(normalizeActionClass).filter(Boolean)),
|
|
131
|
+
];
|
|
132
|
+
if (cleaned.length > 0) policy.unattendedAllowlist = cleaned;
|
|
133
|
+
}
|
|
134
|
+
|
|
122
135
|
return Object.keys(policy).length > 0 ? policy : null;
|
|
123
136
|
}
|
|
124
137
|
|
|
@@ -575,7 +588,13 @@ export class AgentScheduleStore {
|
|
|
575
588
|
*/
|
|
576
589
|
recordMonitorCheck(
|
|
577
590
|
id,
|
|
578
|
-
{
|
|
591
|
+
{
|
|
592
|
+
matched = false,
|
|
593
|
+
atMs = null,
|
|
594
|
+
mtimeMs = null,
|
|
595
|
+
eventId = null,
|
|
596
|
+
authority = null,
|
|
597
|
+
} = {},
|
|
579
598
|
) {
|
|
580
599
|
const now = atMs != null ? atMs : this._now();
|
|
581
600
|
return this._mutate("monitor", id, (entry) => {
|
|
@@ -590,6 +609,16 @@ export class AgentScheduleStore {
|
|
|
590
609
|
if (matched) {
|
|
591
610
|
entry.status = "matched";
|
|
592
611
|
entry.matchedAt = now;
|
|
612
|
+
// Audit record of the firing: the deterministic monitor-event id + the
|
|
613
|
+
// (always SYSTEM/steer) authority the event was delivered under. Durable
|
|
614
|
+
// on the entry so `cc agenda list --json` and a future resident daemon
|
|
615
|
+
// can recognise / dedup the exact observation. Only stamped when the
|
|
616
|
+
// caller supplies them, so legacy callers stay byte-identical.
|
|
617
|
+
if (eventId != null) {
|
|
618
|
+
entry.lastEventId = String(eventId);
|
|
619
|
+
entry.lastEventAt = now;
|
|
620
|
+
}
|
|
621
|
+
if (authority != null) entry.lastAuthority = String(authority);
|
|
593
622
|
} else if (entry.maxChecks != null && entry.checks >= entry.maxChecks) {
|
|
594
623
|
entry.status = "exhausted";
|
|
595
624
|
} else {
|
|
@@ -22,8 +22,31 @@ import {
|
|
|
22
22
|
mergeWorktree as _mergeWorktree,
|
|
23
23
|
} from "../../harness/worktree-isolator.js";
|
|
24
24
|
import { isGitRepo as _isGitRepo } from "../../lib/git-integration.js";
|
|
25
|
+
import { normalizeSparsePaths } from "../../lib/worktree-sparse.js";
|
|
25
26
|
import { execFileSync, spawn } from "node:child_process";
|
|
26
27
|
|
|
28
|
+
/**
|
|
29
|
+
* Resolve the sparse-checkout / dependency-symlink options for a task, letting a
|
|
30
|
+
* per-task value (from the plan) override the coordinator-wide default. Returns
|
|
31
|
+
* `undefined` when nothing is configured so `createWorktree` takes its unchanged
|
|
32
|
+
* full-checkout path (byte-identical to before this feature).
|
|
33
|
+
*/
|
|
34
|
+
function resolveWorktreeOptions(task, defaults) {
|
|
35
|
+
const t = task || {};
|
|
36
|
+
const sparse = normalizeSparsePaths(
|
|
37
|
+
t.sparsePaths ?? t.metadata?.sparsePaths ?? defaults.sparsePaths ?? null,
|
|
38
|
+
);
|
|
39
|
+
const symlink =
|
|
40
|
+
t.symlinkDirectories ??
|
|
41
|
+
t.metadata?.symlinkDirectories ??
|
|
42
|
+
defaults.symlinkDirectories ??
|
|
43
|
+
null;
|
|
44
|
+
const opts = {};
|
|
45
|
+
if (sparse) opts.sparsePaths = sparse;
|
|
46
|
+
if (symlink != null) opts.symlinkDirectories = symlink;
|
|
47
|
+
return Object.keys(opts).length ? opts : undefined;
|
|
48
|
+
}
|
|
49
|
+
|
|
27
50
|
function defaultRunShell(command, cwd) {
|
|
28
51
|
return new Promise((resolve, reject) => {
|
|
29
52
|
const child = spawn(command, { cwd, shell: true, env: process.env });
|
|
@@ -72,9 +95,15 @@ export const _deps = {
|
|
|
72
95
|
};
|
|
73
96
|
|
|
74
97
|
export class TeamWorktreeCoordinator {
|
|
75
|
-
constructor(repoDir) {
|
|
98
|
+
constructor(repoDir, options = {}) {
|
|
76
99
|
this.repoDir = repoDir;
|
|
77
100
|
this._created = new Map(); // key → { branch, path, committed }
|
|
101
|
+
// Coordinator-wide sparse-checkout / dependency-symlink defaults; a per-task
|
|
102
|
+
// value (task.sparsePaths / task.metadata.sparsePaths) overrides these.
|
|
103
|
+
this._worktreeDefaults = {
|
|
104
|
+
sparsePaths: options.sparsePaths ?? null,
|
|
105
|
+
symlinkDirectories: options.symlinkDirectories ?? null,
|
|
106
|
+
};
|
|
78
107
|
}
|
|
79
108
|
|
|
80
109
|
isGitRepo() {
|
|
@@ -114,7 +143,13 @@ export class TeamWorktreeCoordinator {
|
|
|
114
143
|
}
|
|
115
144
|
this._created.delete(key);
|
|
116
145
|
}
|
|
117
|
-
const
|
|
146
|
+
const wtOptions = resolveWorktreeOptions(task, this._worktreeDefaults);
|
|
147
|
+
const { path: worktreePath } = _deps.createWorktree(
|
|
148
|
+
this.repoDir,
|
|
149
|
+
branch,
|
|
150
|
+
null,
|
|
151
|
+
wtOptions,
|
|
152
|
+
);
|
|
118
153
|
this._created.set(key, { branch, path: worktreePath, committed: false });
|
|
119
154
|
if (typeof runInWorktree === "function") {
|
|
120
155
|
await runInWorktree({ key, task, holder, cwd: worktreePath });
|