chainlesschain 0.162.193 → 0.162.198
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/README.md +29 -6
- package/completions/_cc +2 -2
- package/completions/cc.bash +2 -2
- package/completions/cc.fish +2 -2
- package/completions/cc.ps1 +2 -2
- package/package.json +1 -1
- package/src/assets/web-panel/assets/{AIOps-DBLkaTje.js → AIOps-CSN-D2xb.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-BAy-Qebo.js → ActionButton-CBSMlGzv.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-Dfb1Mlau.js → Analytics-3yr3Nt2t.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-BbFkN8m0.js → AppLayout-1b-o3lMH.js} +4 -4
- package/src/assets/web-panel/assets/{Artifacts-kM4-cEEH.js → Artifacts-Dr8rJi17.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-BSQfYWf3.js → Audit-2V689HC2.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-CWDbI_fG.js → BackgroundAgents-DIxkNrD_.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-BxLPbq_Q.js → Backup-Pg6BcwrE.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-DuIbdrp6.js → BaseInput-CLUpqyfH.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-NEmjGahU.js → Chat-qVS8rD4G.js} +5 -5
- package/src/assets/web-panel/assets/{ChatBubbleRenderer-DfJEo-5_.js → ChatBubbleRenderer-ti49X12Q.js} +1 -1
- package/src/assets/web-panel/assets/{Checkbox-CaoeG3Ns.js → Checkbox-Bs9AaEay.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-BFf_g3xY.js → Codegen-Dvc1mq8q.js} +1 -1
- package/src/assets/web-panel/assets/{Col-CNOd8mdQ.js → Col-C4_biO7A.js} +1 -1
- package/src/assets/web-panel/assets/{Community-BhpDqz-i.js → Community-al25XQUV.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-CQ42T7Z6.js → Compact-D-ZDB8M1.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-DI5PcLMN.js → Compliance-6lIfDz1W.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-wYYZe0wR.js → Cowork-DdI48XMJ.js} +3 -3
- package/src/assets/web-panel/assets/{Cron-Dlq03DD_.js → Cron-DF5HZd2g.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-BzqS8PP3.js → Crosschain-DuxeSvt3.js} +1 -1
- package/src/assets/web-panel/assets/{DID-It55Yem7.js → DID-B9yHkUTd.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-9HkH6oxn.js → Dashboard-Z5EuI3IM.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-00e2YWRB.js → Dropdown-vuRfO3dY.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-By-ugRDL.js → EmailListRenderer-QFLCc1uL.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-C396yu5m.js → FamilyGuardDashboard-BZ7EXkFJ.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-BME5sbZY.js → Federation-DycIEg2s.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-B4TEZCr-.js → FormItemContext-Ba0H_7yG.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-_9CX9ieY.js → GenericCardRenderer--JgSTK-C.js} +1 -1
- package/src/assets/web-panel/assets/{Git-BrS6WVSr.js → Git-DEvTbldE.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-BMdCWnKF.js → Governance-BJIljy18.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-Bcp3vzcA.js → Inference-CVyW3G3q.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-DlLuqGY5.js → KnowledgeGraph-v8-borge.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-D-W_6x09.js → Logs-BCdS7gGo.js} +2 -2
- package/src/assets/web-panel/assets/{MarkdownRenderer-DElGPxCZ.js → MarkdownRenderer-CU-JB2Ns.js} +1 -1
- package/src/assets/web-panel/assets/{Marketplace-Dzn2guvI.js → Marketplace-AN6tGA2B.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-uPict-KL.js → McpTools-DGViXASN.js} +6 -6
- package/src/assets/web-panel/assets/{Memory-BCw43kOb.js → Memory-Bpvmx9X4.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-DmV8Wv7F.js → MobileBridge-CTP-DOTC.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-C-HaCmSM.js → MobileProjects-C8qfktxn.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-C6jxJ34Y.js → Mtc-ndYr0M1s.js} +5 -5
- package/src/assets/web-panel/assets/{MtcAudit-Za8ExwhQ.js → MtcAudit-Ci0-YK7W.js} +5 -5
- package/src/assets/web-panel/assets/{Multisig-KKDbuEgb.js → Multisig-BehSTFW7.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-Dj97zbCk.js → NLProgramming-B91_YBLv.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-DBXvMraU.js → Notes-BQlJ0-gq.js} +4 -4
- package/src/assets/web-panel/assets/{NotificationSettings-DOxITLYl.js → NotificationSettings-CDgYMjYK.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-7DVjZwMM.js → OrderTableRenderer-BJkSlKeH.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-D1i73mjb.js → Organization-B-b3cWCp.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-kW6Q3rvg.js → Overflow-CILUL4nL.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-C-v_8p2v.js → P2P-BTCFBkF0.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-tUbbY64W.js → PdhVaultBrowser-BAR6AUx5.js} +5 -5
- package/src/assets/web-panel/assets/{Permissions-DkHxEZ58.js → Permissions-RRf6HbWJ.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-DWuMPGrK.js → PersonalDataHub-BGTMiICa.js} +4 -4
- package/src/assets/web-panel/assets/{Pipeline-30IYXWWm.js → Pipeline-945qY9YF.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-DZNQ4RrH.js → Privacy-DPZwI6lJ.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-De2MCElf.js → ProjectInit-BPH2NRKc.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-5uHk9wsC.js → ProjectSettings-BZCRNCvb.js} +2 -2
- package/src/assets/web-panel/assets/{Projects-XjDfrFLb.js → Projects-BTI__tBt.js} +1 -1
- package/src/assets/web-panel/assets/{Providers-Bgwj6gcE.js → Providers-DDCf_nIw.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-gKql9umv.js → QrScannerModal-N6rbqt1r.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-BWvmwkh7.js → QuickAsk-DrwjPS7M.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-Uok4hZPd.js → Recommend-Cog9Wd99.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-DLJzTk9a.js → RemoteSession-DC9pynjM.js} +3 -3
- package/src/assets/web-panel/assets/{Reputation-ZVwIPMSO.js → Reputation-D4th4gKS.js} +1 -1
- package/src/assets/web-panel/assets/{Row-CN04HxWR.js → Row-ogiWR6v9.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-BdSrEG4g.js → RssFeed-uYlk-2Oj.js} +3 -3
- package/src/assets/web-panel/assets/{Search-5PwVd0IR.js → Search-DYzzwvtM.js} +1 -1
- package/src/assets/web-panel/assets/{Security-DdBo4zjN.js → Security-Da7YOyX6.js} +4 -4
- package/src/assets/web-panel/assets/{Services-D3letU7M.js → Services-CLI1VM1X.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton--Jb83f1v.js → Skeleton-DK77dzDC.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-Bg-CulHX.js → Skills-C_z7Lmhl.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-7QOa7S-X.js → Sla-D6413Q8L.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-DQwppjRw.js → SpeechSettings-C07ydbj2.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-dNPQGL-s.js → SyncSettings-DGOm2W22.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-Ggs-Z48q.js → Tasks-Cr_PVyLM.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-78TYHgRO.js → Templates-BhL2mZ0t.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-xHwwv9vO.js → Tenant-CPk5oyUr.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-BJXSJfm4.js → Terminal-ClhqUsqV.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-fLbjRhJw.js → TimelineRenderer-BCC7MV5a.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-oMSBSBmB.js → Tokens-DD1Jt4oO.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-CfnY_wX_.js → Trigger-BCydFvN2.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-AdyTgYKO.js → Trust-DDHT1noa.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-C5K_81dS.js → UkeySign-CWPZC7oI.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-vcGVDqki.js → VideoEditing-9Ea0nJR0.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-hhOc3hlY.js → Wallet-BjxhLxTf.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-CVloC11s.js → WebAuthn-C6uCye67.js} +4 -4
- package/src/assets/web-panel/assets/{WorkflowEditor-CYeE32R5.js → WorkflowEditor-d2g4reYo.js} +1 -1
- package/src/assets/web-panel/assets/{chat-CNROR5bN.js → chat-frTc6ScX.js} +1 -1
- package/src/assets/web-panel/assets/{colors-Cjx-cnwU.js → colors-B5K6lG3v.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-DOIfelvD.js → compact-item-Cdn46r8D.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-DfA7UZ8u.js → createContext-DU1jqVQi.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-CP-jEUyw.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-BMSCzcfM.js → hasIn-DdNH-aW_.js} +1 -1
- package/src/assets/web-panel/assets/{index-CAnvhwg6.js → index-32ajXmTb.js} +1 -1
- package/src/assets/web-panel/assets/{index-BEMwBBHk.js → index-7ehytj1K.js} +1 -1
- package/src/assets/web-panel/assets/{index-CVL27kXC.js → index-BC4eGkar.js} +1 -1
- package/src/assets/web-panel/assets/{index-FBc_CJBd.js → index-BVLL-mq_.js} +1 -1
- package/src/assets/web-panel/assets/{index-dCd8cSUq.js → index-BXKhhx9V.js} +1 -1
- package/src/assets/web-panel/assets/{index-BemJ2u_E.js → index-BcGwGIaR.js} +1 -1
- package/src/assets/web-panel/assets/{index-CVm1_i3t.js → index-Be4OtL-N.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cs6l7uRu.js → index-BoFak7le.js} +1 -1
- package/src/assets/web-panel/assets/{index-DPAWBrMf.js → index-BwlIxAmX.js} +1 -1
- package/src/assets/web-panel/assets/{index-DLmn0BoK.js → index-CGiwfwB9.js} +1 -1
- package/src/assets/web-panel/assets/{index-B7gf77CJ.js → index-CHmkkdfT.js} +1 -1
- package/src/assets/web-panel/assets/{index-m2vG9gG_.js → index-CQorP1Ng.js} +1 -1
- package/src/assets/web-panel/assets/{index-ClNMrqEY.js → index-CUoNAmE8.js} +1 -1
- package/src/assets/web-panel/assets/{index-DHtpHZAQ.js → index-Cj6P9Nst.js} +1 -1
- package/src/assets/web-panel/assets/{index-C2K9SNVX.js → index-CtRPi4YC.js} +1 -1
- package/src/assets/web-panel/assets/index-CzrCHrFy.js +1 -0
- package/src/assets/web-panel/assets/{index-BolaOQxx.js → index-D1qIC4GR.js} +1 -1
- package/src/assets/web-panel/assets/{index-BM1y_9hM.js → index-D23xGYjM.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cyweh0Hy.js → index-D4l82Me1.js} +1 -1
- package/src/assets/web-panel/assets/{index-By61ojUx.js → index-D4miNkWv.js} +1 -1
- package/src/assets/web-panel/assets/{index-BbQ2eQ8H.js → index-DIVyTmZ3.js} +1 -1
- package/src/assets/web-panel/assets/{index-Icdoqmzt.js → index-DM5FmPZU.js} +1 -1
- package/src/assets/web-panel/assets/{index-DjEsxO5h.js → index-DRzBBdQ0.js} +1 -1
- package/src/assets/web-panel/assets/{index-B4iYzpkL.js → index-Dca_i7H8.js} +1 -1
- package/src/assets/web-panel/assets/{index-C3L5gdiD.js → index-DftZuyA7.js} +1 -1
- package/src/assets/web-panel/assets/{index-CdYBye8B.js → index-DtBCCkC4.js} +1 -1
- package/src/assets/web-panel/assets/{index-AWg-0M1S.js → index-HjCvXHf5.js} +1 -1
- package/src/assets/web-panel/assets/{index-CEKMcOyU.js → index-LaivuXyF.js} +1 -1
- package/src/assets/web-panel/assets/{index-BxOk9vkG.js → index-MM4Zz2OE.js} +1 -1
- package/src/assets/web-panel/assets/{index-CNC5onm4.js → index-NmEXVjzt.js} +1 -1
- package/src/assets/web-panel/assets/index-OJsGskZw.js +1 -0
- package/src/assets/web-panel/assets/{index-BUonmKSE.js → index-PjAS0sIZ.js} +1 -1
- package/src/assets/web-panel/assets/{index-B5Udeb_6.js → index-VDM2KKo9.js} +1 -1
- package/src/assets/web-panel/assets/{index-5-HXgOX5.js → index-cYaWV4Mq.js} +1 -1
- package/src/assets/web-panel/assets/{index-DBpWXFed.js → index-gg3QPC3i.js} +3 -3
- package/src/assets/web-panel/assets/{index-C7xn0Nub.js → index-kVO_MDm-.js} +1 -1
- package/src/assets/web-panel/assets/{index-ti_lnTWl.js → index-mgU3Q-lN.js} +1 -1
- package/src/assets/web-panel/assets/{index-CshQbilz.js → index-t_OBzIP4.js} +1 -1
- package/src/assets/web-panel/assets/{index-mTpHoEzr.js → index-yZRuZpwK.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-Bu8k7H1Y.js → initDefaultProps-DwLTRNdo.js} +1 -1
- package/src/assets/web-panel/assets/{motion-s3iVK41k.js → motion-BchZDXnh.js} +1 -1
- package/src/assets/web-panel/assets/{move-B6B-E5Jz.js → move-e8M-zZFl.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-BiYgTidA.js → mtc-parser-Ci40fQb3.js} +1 -1
- package/src/assets/web-panel/assets/{omit-pU1RdUDA.js → omit-C7110_f2.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-Cdoq_SeM.js → pickAttrs-skteMgnM.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-DIuvAn2K.js → placementArrow-MGZ-lh2B.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-8w16YR2D.js → responsiveObserve-N4Sd7uy-.js} +1 -1
- package/src/assets/web-panel/assets/{slide-VZ01cZe3.js → slide-C7HDGRaa.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-lh8YMN8Z.js → statusUtils-BhRexlgy.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-CmDyreec.js → styleChecker-ChEoQkN2.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-hiJWMU5y.js → useFlexGapSupport-CDbG1s2Y.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-Thnu-Iaj.js → useFs-CUIx0tcJ.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-LBdOxjwY.js → usePersonalDataHub-bkO7L7ec.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-Cg6lFY_X.js → vnode-B_ZvABIG.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-DVfsR1YO.js → zoom-D5shjpS8.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/command-help-index.json +4 -4
- package/src/command-manifest.json +214 -50
- package/src/command-surface-policy.js +115 -0
- package/src/commands/agent.js +3 -3
- package/src/commands/background-session.js +145 -0
- package/src/commands/mcp.js +125 -7
- package/src/commands/serve.js +1 -1
- package/src/commands/session-show.js +131 -0
- package/src/commands/session.js +50 -106
- package/src/commands/skill.js +87 -50
- package/src/data/changelog.json +18 -5
- package/src/gateways/ws/ws-agent-handler.js +8 -1
- package/src/harness/jsonl-session-store.js +428 -25
- package/src/harness/mcp-client.js +2652 -404
- package/src/harness/session-list-index.js +65 -7
- package/src/lazy-dispatch.js +76 -8
- package/src/lib/abort-utils.js +68 -0
- package/src/lib/agent-core.js +1 -59
- package/src/lib/chat-core.js +24 -6
- package/src/lib/cowork-mcp-tools.js +47 -2
- package/src/lib/cowork-task-runner.js +11 -3
- package/src/lib/doctor-checkup.js +9 -7
- package/src/lib/learning/skill-improver.js +21 -2
- package/src/lib/mcp-call-ledger-store.js +116 -3
- package/src/lib/mcp-call-ledger.js +59 -14
- package/src/lib/mcp-client.js +1 -0
- package/src/lib/mcp-stdio-executable-identity.js +598 -0
- package/src/lib/mcp-stdio-execution-authority.js +363 -0
- package/src/lib/mcp-tool-metadata.js +436 -0
- package/src/lib/mcp-tool-result.js +367 -0
- package/src/lib/observability/index.js +71 -0
- package/src/lib/pr-link-ledger.js +8 -27
- package/src/lib/pr-link-store.js +26 -0
- package/src/lib/process-execution-broker/index.js +14 -0
- package/src/lib/process-tree-termination.js +253 -0
- package/src/lib/public-ide-capability-manifest.js +6 -0
- package/src/lib/session-host-snapshot.js +9 -5
- package/src/lib/session-projection.js +5 -1
- package/src/lib/session-tail.js +31 -1
- package/src/lib/skill-budget.js +222 -0
- package/src/lib/skill-execution-authority.js +381 -0
- package/src/lib/skill-execution-identity.js +39 -2
- package/src/lib/skill-loader.js +322 -86
- package/src/lib/skill-mcp.js +43 -1
- package/src/repl/agent-repl.js +349 -46
- package/src/repl/bg-dashboard.js +81 -28
- package/src/repl/chat-repl.js +77 -12
- package/src/repl/prompt-interactions.js +67 -7
- package/src/repl/slash-command-registry.js +3 -4
- package/src/runtime/agent-core.js +340 -108
- package/src/runtime/agent-runtime.js +26 -3
- package/src/runtime/headless-runner.js +150 -23
- package/src/runtime/headless-stream.js +309 -59
- package/src/runtime/mcp-config.js +157 -10
- package/src/runtime/mcp-tool-search.js +0 -0
- package/src/runtime/output-backpressure.js +366 -0
- package/src/runtime/pipe-safety.js +56 -21
- package/src/assets/web-panel/assets/devWarning-ChcCBGs7.js +0 -1
- package/src/assets/web-panel/assets/index-Q2IajL54.js +0 -1
- package/src/assets/web-panel/assets/index-bRkHgOMc.js +0 -1
- package/src/lib/skill-process-broker.js +0 -85
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
appendFileSync,
|
|
12
12
|
existsSync,
|
|
13
13
|
readFileSync,
|
|
14
|
+
rmSync,
|
|
14
15
|
writeFileSync,
|
|
15
16
|
} from "node:fs";
|
|
16
17
|
import { join } from "node:path";
|
|
@@ -19,6 +20,7 @@ import { ensurePrivateFile } from "../lib/secure-fs.js";
|
|
|
19
20
|
|
|
20
21
|
export const SESSION_INDEX_SCHEMA = 2;
|
|
21
22
|
export const SESSION_INDEX_FILE = ".sessions-index-v2.ndjson";
|
|
23
|
+
export const SESSION_TOMBSTONE_MARKER_SUFFIX = ".tombstone";
|
|
22
24
|
|
|
23
25
|
// Used only by an opted-in child in the independent session-scale crash gate.
|
|
24
26
|
// Keeping the injection immediately around the production sidecar/journal
|
|
@@ -41,6 +43,10 @@ export function sessionIndexPath(dir) {
|
|
|
41
43
|
return join(dir, SESSION_INDEX_FILE);
|
|
42
44
|
}
|
|
43
45
|
|
|
46
|
+
export function sessionTombstoneMarkerPath(dir, sessionId) {
|
|
47
|
+
return join(dir, `${sessionId}${SESSION_TOMBSTONE_MARKER_SUFFIX}`);
|
|
48
|
+
}
|
|
49
|
+
|
|
44
50
|
export function emptySessionMeta(sessionId) {
|
|
45
51
|
return {
|
|
46
52
|
schema: SESSION_INDEX_SCHEMA,
|
|
@@ -108,6 +114,18 @@ export function applyEventToSessionMeta(meta, event, lastHash) {
|
|
|
108
114
|
|
|
109
115
|
function writeMetaSnapshot(dir, meta) {
|
|
110
116
|
const filePath = sessionMetaPath(dir, meta.id);
|
|
117
|
+
const tombstoneMarker = sessionTombstoneMarkerPath(dir, meta.id);
|
|
118
|
+
// The marker is the durable per-session namespace witness that makes a
|
|
119
|
+
// tombstoned identity discoverable without reading every live sidecar on
|
|
120
|
+
// `--continue`; the meta snapshot remains the content/status authority.
|
|
121
|
+
// Publish the marker before the tombstone snapshot, and fail the delete if
|
|
122
|
+
// that publication fails. Remove it only after a live generation snapshot
|
|
123
|
+
// commits, so crashes can cause an extra candidate read but never hide a
|
|
124
|
+
// successfully committed tombstone.
|
|
125
|
+
if (meta.deleted === true) {
|
|
126
|
+
writeFileSync(tombstoneMarker, "", { encoding: "utf8", mode: 0o600 });
|
|
127
|
+
ensurePrivateFile(tombstoneMarker);
|
|
128
|
+
}
|
|
111
129
|
// The owning transcript lock serializes this derived snapshot. It need not
|
|
112
130
|
// use a second temp+rename protocol: interruption can only corrupt a cache,
|
|
113
131
|
// which readSessionMeta rejects and rebuildSessionMeta regenerates.
|
|
@@ -116,6 +134,7 @@ function writeMetaSnapshot(dir, meta) {
|
|
|
116
134
|
mode: 0o600,
|
|
117
135
|
});
|
|
118
136
|
ensurePrivateFile(filePath);
|
|
137
|
+
if (meta.deleted !== true) rmSync(tombstoneMarker, { force: true });
|
|
119
138
|
}
|
|
120
139
|
|
|
121
140
|
function appendActivity(dir, meta) {
|
|
@@ -135,9 +154,20 @@ function appendActivity(dir, meta) {
|
|
|
135
154
|
}
|
|
136
155
|
|
|
137
156
|
/** Called while the owning transcript lock is held. */
|
|
138
|
-
export function recordSessionEvent(
|
|
139
|
-
|
|
140
|
-
|
|
157
|
+
export function recordSessionEvent(
|
|
158
|
+
dir,
|
|
159
|
+
sessionId,
|
|
160
|
+
event,
|
|
161
|
+
lastHash,
|
|
162
|
+
{ resetGeneration = false } = {},
|
|
163
|
+
) {
|
|
164
|
+
const existing = readSessionMeta(dir, sessionId);
|
|
165
|
+
// Only the transcript writer may authorize a new generation after checking
|
|
166
|
+
// the tombstone and physical file under its lock. The index cannot infer
|
|
167
|
+
// that authority from an attacker-controlled genesis-shaped event alone.
|
|
168
|
+
const current = resetGeneration
|
|
169
|
+
? emptySessionMeta(sessionId)
|
|
170
|
+
: existing || emptySessionMeta(sessionId);
|
|
141
171
|
current.id = sessionId;
|
|
142
172
|
const next = applyEventToSessionMeta(current, event, lastHash);
|
|
143
173
|
writeMetaSnapshot(dir, next);
|
|
@@ -224,13 +254,29 @@ export function publicSessionMeta(meta) {
|
|
|
224
254
|
*/
|
|
225
255
|
export function listIndexedSessions(
|
|
226
256
|
dir,
|
|
227
|
-
{
|
|
257
|
+
{
|
|
258
|
+
limit = 20,
|
|
259
|
+
hasSession = () => true,
|
|
260
|
+
includeDeleted = () => false,
|
|
261
|
+
onScanComplete = null,
|
|
262
|
+
} = {},
|
|
228
263
|
) {
|
|
229
264
|
const filePath = sessionIndexPath(dir);
|
|
230
|
-
if (!existsSync(filePath))
|
|
265
|
+
if (!existsSync(filePath)) {
|
|
266
|
+
if (typeof onScanComplete === "function") {
|
|
267
|
+
onScanComplete({
|
|
268
|
+
exhausted: true,
|
|
269
|
+
seenSessionIds: new Set(),
|
|
270
|
+
latestBySessionId: new Map(),
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
return [];
|
|
274
|
+
}
|
|
231
275
|
const wanted = Math.max(0, Number(limit) || 0);
|
|
232
276
|
const seen = new Set();
|
|
277
|
+
const latestBySessionId = new Map();
|
|
233
278
|
const result = [];
|
|
279
|
+
let exhausted = true;
|
|
234
280
|
for (const { line } of iterateFileLinesReverseSync(filePath)) {
|
|
235
281
|
let meta;
|
|
236
282
|
try {
|
|
@@ -246,9 +292,21 @@ export function listIndexedSessions(
|
|
|
246
292
|
continue;
|
|
247
293
|
}
|
|
248
294
|
seen.add(meta.id);
|
|
249
|
-
|
|
295
|
+
latestBySessionId.set(meta.id, meta);
|
|
296
|
+
if (
|
|
297
|
+
(meta.deleted && !includeDeleted(meta.id, meta)) ||
|
|
298
|
+
!hasSession(meta.id)
|
|
299
|
+
) {
|
|
300
|
+
continue;
|
|
301
|
+
}
|
|
250
302
|
result.push(publicSessionMeta(meta));
|
|
251
|
-
if (wanted > 0 && result.length >= wanted)
|
|
303
|
+
if (wanted > 0 && result.length >= wanted) {
|
|
304
|
+
exhausted = false;
|
|
305
|
+
break;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
if (typeof onScanComplete === "function") {
|
|
309
|
+
onScanComplete({ exhausted, seenSessionIds: seen, latestBySessionId });
|
|
252
310
|
}
|
|
253
311
|
return result;
|
|
254
312
|
}
|
package/src/lazy-dispatch.js
CHANGED
|
@@ -337,6 +337,43 @@ export function resolveCommandLifecycleInvocation(
|
|
|
337
337
|
};
|
|
338
338
|
}
|
|
339
339
|
|
|
340
|
+
/**
|
|
341
|
+
* Build the bounded, content-free lifecycle usage dimensions for a migrated
|
|
342
|
+
* command. The original argv is inspected only to distinguish the retained
|
|
343
|
+
* top-level spelling from its virtual namespace replacement; arguments and
|
|
344
|
+
* nested command names are never copied into telemetry.
|
|
345
|
+
*/
|
|
346
|
+
export function resolveCommandLifecycleTelemetry(
|
|
347
|
+
argv,
|
|
348
|
+
manifestData = manifest,
|
|
349
|
+
) {
|
|
350
|
+
const invocation = resolveCommandLifecycleInvocation(argv, manifestData);
|
|
351
|
+
let entry = null;
|
|
352
|
+
let route = null;
|
|
353
|
+
if (invocation.kind === "namespace-rewrite") {
|
|
354
|
+
entry = invocation.entry;
|
|
355
|
+
route = "replacement";
|
|
356
|
+
} else if (invocation.kind === "passthrough") {
|
|
357
|
+
const commandName = resolveCommandToken(argv);
|
|
358
|
+
entry = findManifestEntry(commandName, manifestData);
|
|
359
|
+
route = "legacy";
|
|
360
|
+
}
|
|
361
|
+
if (
|
|
362
|
+
entry?.lifecycle?.state !== "deprecated" ||
|
|
363
|
+
typeof entry.name !== "string" ||
|
|
364
|
+
!entry.replacement
|
|
365
|
+
) {
|
|
366
|
+
return null;
|
|
367
|
+
}
|
|
368
|
+
return Object.freeze({
|
|
369
|
+
command: entry.name,
|
|
370
|
+
route,
|
|
371
|
+
version: packageMetadata.version,
|
|
372
|
+
deprecatedSince: entry.lifecycle.deprecatedSince,
|
|
373
|
+
removalNotBefore: entry.lifecycle.removalNotBefore,
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
|
|
340
377
|
export function formatCommandDeprecationWarning(entry, invokedName) {
|
|
341
378
|
const lifecycle = entry?.lifecycle;
|
|
342
379
|
if (lifecycle?.state !== "deprecated" || !entry.replacement) return null;
|
|
@@ -691,7 +728,7 @@ export async function dispatchCli(
|
|
|
691
728
|
{ manifestData = manifest, ...dispatchOptions } = {},
|
|
692
729
|
) {
|
|
693
730
|
const commandName = resolveCommandToken(argv);
|
|
694
|
-
|
|
731
|
+
let entry = findManifestEntry(commandName, manifestData);
|
|
695
732
|
if (!entry) {
|
|
696
733
|
const program = await (
|
|
697
734
|
dispatchOptions.loadFullProgram || defaultLoadFullProgram
|
|
@@ -699,20 +736,35 @@ export async function dispatchCli(
|
|
|
699
736
|
await program.parseAsync(argv);
|
|
700
737
|
return;
|
|
701
738
|
}
|
|
739
|
+
const commandLocation = findCommandTokenLocation(argv);
|
|
740
|
+
if (entry.name === "session" && argv[commandLocation?.index + 1] === "show") {
|
|
741
|
+
entry = {
|
|
742
|
+
...entry,
|
|
743
|
+
module: "./commands/session-show.js",
|
|
744
|
+
register: "registerSessionShowCommand",
|
|
745
|
+
};
|
|
746
|
+
}
|
|
702
747
|
await dispatchManifestEntry(argv, entry, dispatchOptions);
|
|
703
748
|
}
|
|
704
749
|
|
|
705
750
|
export function isFastReadOnlyInvocation(argv, env = process.env) {
|
|
706
751
|
const args = argv.slice(2);
|
|
707
|
-
|
|
752
|
+
const explicitOtlp =
|
|
753
|
+
args.includes("--otlp-endpoint") ||
|
|
754
|
+
args.some((token) => token.startsWith("--otlp-endpoint=")) ||
|
|
755
|
+
env.OTEL_EXPORTER_OTLP_ENDPOINT ||
|
|
756
|
+
env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT ||
|
|
757
|
+
env.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT;
|
|
758
|
+
const quickStatus =
|
|
708
759
|
resolveCommandToken(argv) === "status" &&
|
|
709
760
|
!args.includes("--deep") &&
|
|
710
|
-
!
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
761
|
+
!explicitOtlp;
|
|
762
|
+
const commandLocation = findCommandTokenLocation(argv);
|
|
763
|
+
const sessionShow =
|
|
764
|
+
commandLocation?.token === "session" &&
|
|
765
|
+
argv[commandLocation.index + 1] === "show" &&
|
|
766
|
+
!explicitOtlp;
|
|
767
|
+
return quickStatus || sessionShow;
|
|
716
768
|
}
|
|
717
769
|
|
|
718
770
|
let processHandlersInstalled = false;
|
|
@@ -767,6 +819,10 @@ async function withInvocationOutputContext(argv, task) {
|
|
|
767
819
|
}
|
|
768
820
|
|
|
769
821
|
export async function runCli(argv, options = {}) {
|
|
822
|
+
const lifecycleTelemetry = resolveCommandLifecycleTelemetry(
|
|
823
|
+
argv,
|
|
824
|
+
options.manifestData || manifest,
|
|
825
|
+
);
|
|
770
826
|
const prepared = await prepareInvocation(argv, options);
|
|
771
827
|
if (prepared.handled) {
|
|
772
828
|
if (prepared.exitCode) {
|
|
@@ -799,12 +855,24 @@ export async function runCli(argv, options = {}) {
|
|
|
799
855
|
}
|
|
800
856
|
|
|
801
857
|
const observability = await initializeCommandRuntime(dispatchArgv);
|
|
858
|
+
const lifecycleStartedAt = performance.now();
|
|
859
|
+
let lifecycleOutcome = "completed";
|
|
802
860
|
try {
|
|
803
861
|
return await withDefaultEventRuntimeLifecycle(
|
|
804
862
|
() => dispatchCli(dispatchArgv, options),
|
|
805
863
|
options,
|
|
806
864
|
);
|
|
865
|
+
} catch (error) {
|
|
866
|
+
lifecycleOutcome = "error";
|
|
867
|
+
throw error;
|
|
807
868
|
} finally {
|
|
869
|
+
if (lifecycleTelemetry) {
|
|
870
|
+
observability.exportCommandLifecycleInvocation({
|
|
871
|
+
...lifecycleTelemetry,
|
|
872
|
+
outcome: lifecycleOutcome,
|
|
873
|
+
durationMs: performance.now() - lifecycleStartedAt,
|
|
874
|
+
});
|
|
875
|
+
}
|
|
808
876
|
await observability.shutdown().catch(() => {});
|
|
809
877
|
}
|
|
810
878
|
});
|
package/src/lib/abort-utils.js
CHANGED
|
@@ -18,3 +18,71 @@ export function throwIfAborted(signal, message = "Agent loop interrupted") {
|
|
|
18
18
|
throw signal.reason || createAbortError(message);
|
|
19
19
|
}
|
|
20
20
|
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Await a value while making a host AbortSignal authoritative over settlement.
|
|
24
|
+
* The source promise is always observed after cancellation, so a late rejection
|
|
25
|
+
* cannot become unhandled and a late success cannot revive the cancelled path.
|
|
26
|
+
*/
|
|
27
|
+
export function raceWithAbort(
|
|
28
|
+
value,
|
|
29
|
+
signal,
|
|
30
|
+
message = "Agent loop interrupted",
|
|
31
|
+
) {
|
|
32
|
+
if (!signal) return Promise.resolve(value);
|
|
33
|
+
try {
|
|
34
|
+
throwIfAborted(signal, message);
|
|
35
|
+
} catch (error) {
|
|
36
|
+
return Promise.reject(error);
|
|
37
|
+
}
|
|
38
|
+
if (typeof signal.addEventListener !== "function") {
|
|
39
|
+
return Promise.resolve(value);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const source = Promise.resolve(value);
|
|
43
|
+
return new Promise((resolve, reject) => {
|
|
44
|
+
let settled = false;
|
|
45
|
+
const cleanup = () => {
|
|
46
|
+
try {
|
|
47
|
+
signal.removeEventListener?.("abort", onAbort);
|
|
48
|
+
} catch {
|
|
49
|
+
// Listener cleanup cannot change the already-authoritative settlement.
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
const onAbort = () => {
|
|
53
|
+
if (settled) return;
|
|
54
|
+
settled = true;
|
|
55
|
+
cleanup();
|
|
56
|
+
reject(signal.reason || createAbortError(message));
|
|
57
|
+
};
|
|
58
|
+
try {
|
|
59
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
60
|
+
} catch (error) {
|
|
61
|
+
settled = true;
|
|
62
|
+
// Observe a later source rejection even though the signal contract was
|
|
63
|
+
// malformed and this wrapper must fail immediately.
|
|
64
|
+
source.catch(() => {});
|
|
65
|
+
reject(error);
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
source.then(
|
|
69
|
+
(result) => {
|
|
70
|
+
if (settled) return;
|
|
71
|
+
settled = true;
|
|
72
|
+
cleanup();
|
|
73
|
+
resolve(result);
|
|
74
|
+
},
|
|
75
|
+
(error) => {
|
|
76
|
+
if (settled) return;
|
|
77
|
+
settled = true;
|
|
78
|
+
cleanup();
|
|
79
|
+
reject(error);
|
|
80
|
+
},
|
|
81
|
+
);
|
|
82
|
+
// AbortSignal does not replay an event that fired immediately before the
|
|
83
|
+
// listener was attached. Re-check after both observers are installed so
|
|
84
|
+
// that narrow registration race still settles as cancellation, while the
|
|
85
|
+
// source remains observed if it finishes later.
|
|
86
|
+
if (signal.aborted) onAbort();
|
|
87
|
+
});
|
|
88
|
+
}
|
package/src/lib/agent-core.js
CHANGED
|
@@ -1,60 +1,2 @@
|
|
|
1
1
|
/** @deprecated Thin re-export shim; canonical impl is `../runtime/agent-core.js` (CLI Runtime Convergence, Phase 6b) — import from there in new code. */
|
|
2
|
-
export
|
|
3
|
-
AGENT_TOOLS,
|
|
4
|
-
AGENT_TOOL_REGISTRY,
|
|
5
|
-
MAX_SUB_AGENT_DEPTH,
|
|
6
|
-
MAX_SUB_AGENTS_PER_RUN,
|
|
7
|
-
MAX_TOOL_RESULT_CHARS,
|
|
8
|
-
capToolResultString,
|
|
9
|
-
safeStringifyToolResult,
|
|
10
|
-
tokenizeShellWords,
|
|
11
|
-
reloadSkills,
|
|
12
|
-
getAgentToolDefinitions,
|
|
13
|
-
getAgentToolDescriptors,
|
|
14
|
-
getCachedPython,
|
|
15
|
-
_resetCachedPythonForTests,
|
|
16
|
-
getEnvironmentInfo,
|
|
17
|
-
_environmentProcessDeps,
|
|
18
|
-
_agentToolProcessDeps,
|
|
19
|
-
getBaseSystemPrompt,
|
|
20
|
-
buildSystemPrompt,
|
|
21
|
-
executeTool,
|
|
22
|
-
computeProposedEdit,
|
|
23
|
-
editNotebookCell,
|
|
24
|
-
renderNotebook,
|
|
25
|
-
classifyError,
|
|
26
|
-
isValidPackageName,
|
|
27
|
-
chatWithTools,
|
|
28
|
-
agentLoop,
|
|
29
|
-
formatToolArgs,
|
|
30
|
-
getActiveMcpServers,
|
|
31
|
-
listBackgroundShellTasks,
|
|
32
|
-
killBackgroundShellTask,
|
|
33
|
-
killAllBackgroundShellTasks,
|
|
34
|
-
killAllBackgroundShellTasksSync,
|
|
35
|
-
_backgroundProcessDeps,
|
|
36
|
-
_runBackgroundTaskkill,
|
|
37
|
-
_gitProcessDeps,
|
|
38
|
-
reapIdleBackgroundShellTasks,
|
|
39
|
-
_ingestSearchOutput,
|
|
40
|
-
writeFileVerified,
|
|
41
|
-
formatProviderHttpError,
|
|
42
|
-
_accumulateOllamaStream,
|
|
43
|
-
_accumulateOpenAIStream,
|
|
44
|
-
_accumulateAnthropicStream,
|
|
45
|
-
_streamErrorDisposition,
|
|
46
|
-
_isRetryableStreamError,
|
|
47
|
-
_retryStreamingChat,
|
|
48
|
-
_iterateStreamWithStall,
|
|
49
|
-
_toAnthropicMessages,
|
|
50
|
-
_anthropicThinkingParams,
|
|
51
|
-
_normalizeAnthropicResponse,
|
|
52
|
-
_resetFileFreshness,
|
|
53
|
-
_withPostEditDiagnostics,
|
|
54
|
-
_getSharedCodeIntel,
|
|
55
|
-
disposeSharedCodeIntel,
|
|
56
|
-
emitToolHookLifecycle,
|
|
57
|
-
emitToolBatchHookLifecycle,
|
|
58
|
-
buildSubAgentHandoffContext,
|
|
59
|
-
_resetSubtreeInstructionLoaders,
|
|
60
|
-
} from "../runtime/agent-core.js";
|
|
2
|
+
export * from "../runtime/agent-core.js";
|
package/src/lib/chat-core.js
CHANGED
|
@@ -90,6 +90,21 @@ function _cachedPromptTokens(usage) {
|
|
|
90
90
|
return 0;
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
+
async function _deliverToken(onToken, token, guard) {
|
|
94
|
+
if (typeof onToken !== "function") return;
|
|
95
|
+
// Terminal backpressure is not provider silence. Suspend the network stall
|
|
96
|
+
// timer while the UI sink drains, then re-arm it before reading more bytes.
|
|
97
|
+
guard.stop();
|
|
98
|
+
try {
|
|
99
|
+
await onToken(token);
|
|
100
|
+
} catch (error) {
|
|
101
|
+
if (error?.isOutputBackpressureFailure === true) throw error;
|
|
102
|
+
// Generic UI callbacks retain their historical best-effort semantics.
|
|
103
|
+
} finally {
|
|
104
|
+
guard.bump();
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
93
108
|
/**
|
|
94
109
|
* Stream a response from Ollama.
|
|
95
110
|
* If `onUsage` is provided, it's called with `{inputTokens, outputTokens}`
|
|
@@ -156,7 +171,7 @@ export async function streamOllama(
|
|
|
156
171
|
const json = JSON.parse(line);
|
|
157
172
|
if (json.message?.content) {
|
|
158
173
|
fullResponse += json.message.content;
|
|
159
|
-
onToken
|
|
174
|
+
await _deliverToken(onToken, json.message.content, guard);
|
|
160
175
|
}
|
|
161
176
|
if (json.done && onUsage) {
|
|
162
177
|
const inputTokens = Number(json.prompt_eval_count) || 0;
|
|
@@ -165,7 +180,8 @@ export async function streamOllama(
|
|
|
165
180
|
onUsage({ inputTokens, outputTokens });
|
|
166
181
|
}
|
|
167
182
|
}
|
|
168
|
-
} catch {
|
|
183
|
+
} catch (error) {
|
|
184
|
+
if (error?.isOutputBackpressureFailure === true) throw error;
|
|
169
185
|
// Partial JSON, skip
|
|
170
186
|
}
|
|
171
187
|
}
|
|
@@ -257,7 +273,7 @@ export async function streamOpenAI(
|
|
|
257
273
|
const content = json.choices?.[0]?.delta?.content;
|
|
258
274
|
if (content) {
|
|
259
275
|
fullResponse += content;
|
|
260
|
-
onToken
|
|
276
|
+
await _deliverToken(onToken, content, guard);
|
|
261
277
|
}
|
|
262
278
|
if (json.usage && onUsage) {
|
|
263
279
|
// Split cached prompt tokens out of prompt_tokens so cc cost
|
|
@@ -270,7 +286,8 @@ export async function streamOpenAI(
|
|
|
270
286
|
onUsage({ inputTokens, outputTokens, cacheReadTokens });
|
|
271
287
|
}
|
|
272
288
|
}
|
|
273
|
-
} catch {
|
|
289
|
+
} catch (error) {
|
|
290
|
+
if (error?.isOutputBackpressureFailure === true) throw error;
|
|
274
291
|
// Partial data
|
|
275
292
|
}
|
|
276
293
|
}
|
|
@@ -377,7 +394,7 @@ export async function streamAnthropic(
|
|
|
377
394
|
const delta = obj.delta?.text;
|
|
378
395
|
if (delta) {
|
|
379
396
|
fullResponse += delta;
|
|
380
|
-
onToken
|
|
397
|
+
await _deliverToken(onToken, delta, guard);
|
|
381
398
|
}
|
|
382
399
|
} else if (obj.type === "message_start") {
|
|
383
400
|
const u = obj.message?.usage || {};
|
|
@@ -390,7 +407,8 @@ export async function streamAnthropic(
|
|
|
390
407
|
} else if (obj.type === "message_delta") {
|
|
391
408
|
outputTokens = Number(obj.usage?.output_tokens) || outputTokens;
|
|
392
409
|
}
|
|
393
|
-
} catch {
|
|
410
|
+
} catch (error) {
|
|
411
|
+
if (error?.isOutputBackpressureFailure === true) throw error;
|
|
394
412
|
/* skip malformed */
|
|
395
413
|
}
|
|
396
414
|
}
|
|
@@ -21,6 +21,10 @@
|
|
|
21
21
|
*/
|
|
22
22
|
import { validateMcpServerConfig } from "./skill-mcp.js";
|
|
23
23
|
import { mcpEffectDescriptorFields } from "./mcp-effect-contract.js";
|
|
24
|
+
import {
|
|
25
|
+
MCP_STDIO_LOCAL_CODE_TRUST_REQUIRED_CODE,
|
|
26
|
+
issueMcpStdioExecutionAuthority,
|
|
27
|
+
} from "./mcp-stdio-execution-authority.js";
|
|
24
28
|
|
|
25
29
|
export const _deps = {
|
|
26
30
|
importMcpClient: async () => {
|
|
@@ -90,6 +94,7 @@ export function toAgentTool(serverName, tool) {
|
|
|
90
94
|
* @param {{ mcpServers?: Array<object> }} template
|
|
91
95
|
* @param {object} [opts]
|
|
92
96
|
* @param {(msg: string, err?: Error) => void} [opts.onWarn]
|
|
97
|
+
* @param {(request: object) => boolean|Promise<boolean>} [opts.approveLocalCodeExecution]
|
|
93
98
|
* @returns {Promise<{
|
|
94
99
|
* mcpClient: object|null,
|
|
95
100
|
* mounted: string[],
|
|
@@ -121,15 +126,55 @@ export async function mountTemplateMcpTools(template, opts = {}) {
|
|
|
121
126
|
.filter(Boolean);
|
|
122
127
|
if (validated.length === 0) return empty;
|
|
123
128
|
|
|
129
|
+
const skipped = [];
|
|
130
|
+
const authorized = [];
|
|
131
|
+
for (const server of validated) {
|
|
132
|
+
let approved = false;
|
|
133
|
+
try {
|
|
134
|
+
approved =
|
|
135
|
+
typeof opts.approveLocalCodeExecution === "function" &&
|
|
136
|
+
(await opts.approveLocalCodeExecution({
|
|
137
|
+
templateId: template?.id || null,
|
|
138
|
+
serverName: server.name,
|
|
139
|
+
command: server.command,
|
|
140
|
+
args: [...server.args],
|
|
141
|
+
cwd: server.cwd || null,
|
|
142
|
+
envKeys: Object.keys(server.env || {}).sort(),
|
|
143
|
+
})) === true;
|
|
144
|
+
} catch (error) {
|
|
145
|
+
const message = error?.message || String(error);
|
|
146
|
+
skipped.push({ name: server.name, error: message });
|
|
147
|
+
opts.onWarn?.(
|
|
148
|
+
`[cowork-mcp] Local-code approval failed for "${server.name}": ${message}`,
|
|
149
|
+
error,
|
|
150
|
+
);
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
if (!approved) {
|
|
154
|
+
const message = `${MCP_STDIO_LOCAL_CODE_TRUST_REQUIRED_CODE}: template MCP server requires explicit local-code approval`;
|
|
155
|
+
skipped.push({ name: server.name, error: message });
|
|
156
|
+
opts.onWarn?.(`[cowork-mcp] Skipped "${server.name}": ${message}`);
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
const config = { ...server };
|
|
160
|
+
config.mcpStdioExecutionAuthority = issueMcpStdioExecutionAuthority({
|
|
161
|
+
serverName: server.name,
|
|
162
|
+
config,
|
|
163
|
+
approvalKind: "explicit-config",
|
|
164
|
+
approvalSource: `cowork-template:${template?.id || "unknown"}:${server.name}`,
|
|
165
|
+
});
|
|
166
|
+
authorized.push(config);
|
|
167
|
+
}
|
|
168
|
+
if (authorized.length === 0) return { ...empty, skipped };
|
|
169
|
+
|
|
124
170
|
const MCPClient = await _deps.importMcpClient();
|
|
125
171
|
const mcpClient = new MCPClient();
|
|
126
172
|
const mounted = [];
|
|
127
|
-
const skipped = [];
|
|
128
173
|
const extraToolDefinitions = [];
|
|
129
174
|
const externalToolDescriptors = {};
|
|
130
175
|
const externalToolExecutors = {};
|
|
131
176
|
|
|
132
|
-
for (const server of
|
|
177
|
+
for (const server of authorized) {
|
|
133
178
|
try {
|
|
134
179
|
await mcpClient.connect(server.name, server);
|
|
135
180
|
mounted.push(server.name);
|
|
@@ -313,6 +313,7 @@ export function prepareCoworkMcpRuntime(mcp, options = {}) {
|
|
|
313
313
|
}
|
|
314
314
|
let recoveryNotice = null;
|
|
315
315
|
let recoveryState = null;
|
|
316
|
+
let recoveryAuthority = null;
|
|
316
317
|
let bindingAllowed = false;
|
|
317
318
|
let expectedHeadHash = null;
|
|
318
319
|
try {
|
|
@@ -321,6 +322,7 @@ export function prepareCoworkMcpRuntime(mcp, options = {}) {
|
|
|
321
322
|
String(options.templateId || "free"),
|
|
322
323
|
);
|
|
323
324
|
const recovery = projected.recovery;
|
|
325
|
+
recoveryAuthority = recovery;
|
|
324
326
|
expectedHeadHash = projected.expectedHeadHash;
|
|
325
327
|
bindingAllowed = projected.bindingAllowed;
|
|
326
328
|
recoveryNotice = formatMcpLedgerRecoveryNotice(recovery);
|
|
@@ -361,9 +363,12 @@ export function prepareCoworkMcpRuntime(mcp, options = {}) {
|
|
|
361
363
|
}
|
|
362
364
|
}
|
|
363
365
|
|
|
364
|
-
const sink = createSessionMcpLedgerSink(
|
|
365
|
-
|
|
366
|
-
|
|
366
|
+
const sink = createSessionMcpLedgerSink(
|
|
367
|
+
sessionId,
|
|
368
|
+
_deps.appendSessionEvent === appendSessionEvent
|
|
369
|
+
? { recovery: recoveryAuthority }
|
|
370
|
+
: { appendEvent: _deps.appendSessionEvent },
|
|
371
|
+
);
|
|
367
372
|
const recoveryController =
|
|
368
373
|
createMcpRecoveryAdmissionController(recoveryState);
|
|
369
374
|
return {
|
|
@@ -395,6 +400,7 @@ export function prepareCoworkMcpRuntime(mcp, options = {}) {
|
|
|
395
400
|
* @param {number} [options.maxIterations] - Override iteration limit
|
|
396
401
|
* @param {number} [options.tokenBudget] - Override token budget
|
|
397
402
|
* @param {string} [options.mcpSessionId] - Stable MCP ledger session for resume
|
|
403
|
+
* @param {(request: object) => boolean|Promise<boolean>} [options.approveMcpLocalCodeExecution]
|
|
398
404
|
* @returns {Promise<{ taskId: string, status: string, result: object }>}
|
|
399
405
|
*/
|
|
400
406
|
export async function runCoworkTask(options = {}) {
|
|
@@ -410,6 +416,7 @@ export async function runCoworkTask(options = {}) {
|
|
|
410
416
|
onProgress = null,
|
|
411
417
|
signal = null,
|
|
412
418
|
mcpSessionId = null,
|
|
419
|
+
approveMcpLocalCodeExecution = null,
|
|
413
420
|
} = options;
|
|
414
421
|
|
|
415
422
|
if (!userMessage || typeof userMessage !== "string") {
|
|
@@ -458,6 +465,7 @@ export async function runCoworkTask(options = {}) {
|
|
|
458
465
|
|
|
459
466
|
// Mount template-declared MCP servers (best-effort, failures are tolerated)
|
|
460
467
|
const mcp = await mountTemplateMcpTools(template, {
|
|
468
|
+
approveLocalCodeExecution: approveMcpLocalCodeExecution,
|
|
461
469
|
onWarn: (msg) => {
|
|
462
470
|
if (onProgress) onProgress({ type: "mcp-warning", message: msg });
|
|
463
471
|
},
|
|
@@ -604,22 +604,24 @@ async function transcriptSection(_opts, _deps2) {
|
|
|
604
604
|
const { verifyAllSessions } =
|
|
605
605
|
await import("../harness/jsonl-session-store.js");
|
|
606
606
|
const results = verifyAllSessions({ limit: 200 });
|
|
607
|
-
const
|
|
608
|
-
|
|
607
|
+
const integrityFailures = results.filter((result) =>
|
|
608
|
+
["tampered", "missing", "conflict"].includes(result.status),
|
|
609
|
+
);
|
|
610
|
+
if (integrityFailures.length > 0) {
|
|
609
611
|
checks.push(
|
|
610
612
|
check(
|
|
611
613
|
"transcript-tamper",
|
|
612
614
|
"Transcript integrity",
|
|
613
615
|
CHECK_LEVELS.ERR,
|
|
614
|
-
`${
|
|
616
|
+
`${integrityFailures.length} transcript integrity failure(s): ${integrityFailures
|
|
615
617
|
.slice(0, 5)
|
|
616
|
-
.map((
|
|
617
|
-
.join(", ")}${
|
|
618
|
+
.map((result) => `${result.sessionId} [${result.status}]`)
|
|
619
|
+
.join(", ")}${integrityFailures.length > 5 ? ", …" : ""}`,
|
|
618
620
|
{
|
|
619
621
|
id: "transcript-tamper",
|
|
620
622
|
safe: false,
|
|
621
623
|
description: "Inspect the flagged transcripts",
|
|
622
|
-
command: `cc session verify ${
|
|
624
|
+
command: `cc session verify ${integrityFailures[0].sessionId}`,
|
|
623
625
|
},
|
|
624
626
|
),
|
|
625
627
|
);
|
|
@@ -629,7 +631,7 @@ async function transcriptSection(_opts, _deps2) {
|
|
|
629
631
|
"transcript-tamper",
|
|
630
632
|
"Transcript integrity",
|
|
631
633
|
CHECK_LEVELS.OK,
|
|
632
|
-
`${results.length} session(s) checked, no
|
|
634
|
+
`${results.length} session(s) checked, no integrity failures`,
|
|
633
635
|
),
|
|
634
636
|
);
|
|
635
637
|
}
|