chainlesschain 0.166.77 → 0.166.79
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 +9 -8
- package/completions/_cc +1 -1
- package/completions/cc.bash +1 -1
- package/completions/cc.fish +1 -1
- package/completions/cc.ps1 +1 -1
- package/package.json +1 -1
- package/src/assets/linux-subreaper/linux-arm64/manifest.json +10 -0
- package/src/assets/linux-subreaper/linux-arm64/supervisor +0 -0
- package/src/assets/linux-subreaper/linux-x64/manifest.json +10 -0
- package/src/assets/linux-subreaper/linux-x64/supervisor +0 -0
- package/src/assets/web-panel/assets/{AIOps-CpiCBhRh.js → AIOps-CwD6Pm_Y.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-CnU-Xvx-.js → ActionButton-Du5Rp-O6.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-BJW-CNtf.js → Analytics-DY9l72ma.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-UFQJL3G9.js → AppLayout-7t1WIaCJ.js} +5 -5
- package/src/assets/web-panel/assets/{Artifacts-Dr_IUDoY.js → Artifacts-BLIA5yny.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-aglWo6rr.js → Audit-BC_N0QTg.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-Dm_TCiXC.js → BackgroundAgents-CA7SxDtg.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-DUdX4-gA.js → Backup-BZ4AzS1_.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-BTcLjy1o.js → BaseInput-BhiggGaH.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-Bhy9qZrl.js → Chat-CDdBoIBI.js} +5 -5
- package/src/assets/web-panel/assets/ChatBubbleRenderer-hsBsrl31.js +1 -0
- package/src/assets/web-panel/assets/{Checkbox-CblrPa21.js → Checkbox-asoMKSEK.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-SEAHzOlr.js → Codegen-B0gElLdm.js} +1 -1
- package/src/assets/web-panel/assets/{Col-DJz8iFOR.js → Col-DrNzLpF6.js} +1 -1
- package/src/assets/web-panel/assets/{Community-RufKhcQt.js → Community-DCzB5qkI.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-Dlifx08U.js → Compact-Db-Eua_j.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-DdhQWcHh.js → Compliance-B76NByjl.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-BLNn6oOm.js → Cowork-C-nK6Qpk.js} +3 -3
- package/src/assets/web-panel/assets/{Cron-Dv-JWY2d.js → Cron-C-GlQO19.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-k7Pz7O-b.js → Crosschain-Cuuz9IIh.js} +1 -1
- package/src/assets/web-panel/assets/{DID-C_10syUW.js → DID-gwFaT973.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-CMyH6Szx.js → Dashboard-DHDt5jEf.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-DjmD2Saa.js → Dropdown-CJ9kog-Y.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-36_IRUSo.js → EmailListRenderer-DOSXFRiQ.js} +1 -1
- package/src/assets/web-panel/assets/{EvolutionSettings-CmtU0XmM.js → EvolutionSettings-CHYMP7rR.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-DEgf9kyP.js → FamilyGuardDashboard-C1gzG5EK.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-DGO1wQls.js → Federation-CEdNVWUw.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-Doj1FmKW.js → FormItemContext-CwVOWPX8.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-GzOU27iT.js → GenericCardRenderer-CgNUs3kT.js} +1 -1
- package/src/assets/web-panel/assets/{Git-Dd7D1EDK.js → Git-DM4MNnQo.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-6IY8LcjI.js → Governance-DdOtjkvd.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-BB9gl8q1.js → Inference-28PidFfr.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-CeCed5wd.js → KnowledgeGraph-Bpj_qNUK.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-CYOeHuaI.js → Logs-C6Ix19Ay.js} +2 -2
- package/src/assets/web-panel/assets/{MarkdownRenderer-Bk8Zqc2R.js → MarkdownRenderer-Bj6mtPbf.js} +1 -1
- package/src/assets/web-panel/assets/{Marketplace-D-_xgHgM.js → Marketplace-DqkhRSMP.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-NnaUTF5t.js → McpTools-1WQ2SjaE.js} +3 -3
- package/src/assets/web-panel/assets/{Memory-CndN9pMt.js → Memory-BWTi7v6R.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-BXblFmhY.js → MobileBridge-DB4wu3UE.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-CC26Dm2k.js → MobileProjects-Bh9pWx2f.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-BhaqWCiW.js → Mtc-BVF1Wrno.js} +4 -4
- package/src/assets/web-panel/assets/{MtcAudit-D6fktM2w.js → MtcAudit-DtNfslPf.js} +5 -5
- package/src/assets/web-panel/assets/{Multisig-CFvahz7s.js → Multisig-4qcXP0bf.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-Co73Ivya.js → NLProgramming-BRJl5Ehw.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-B4g33L1o.js → Notes-BRY_bgMk.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-lXd17EbN.js → NotificationSettings-DirXMFz4.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-BiOB0GIi.js → OrderTableRenderer-BlrY8lwu.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-JQ7_wQIA.js → Organization-CVzQ1vO-.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-gwgJgozQ.js → Overflow-A3rXTPG4.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-D9L_0gie.js → P2P-Db9ABWz0.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-CbOPsFE9.js → PdhVaultBrowser-CPv0H2OZ.js} +3 -3
- package/src/assets/web-panel/assets/{Permissions-Cz42HNQf.js → Permissions-COnW4sIR.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-DjorYtrF.js → PersonalDataHub-C1hG7rp7.js} +5 -5
- package/src/assets/web-panel/assets/{Pipeline-DyWPhmm5.js → Pipeline---GLRDEe.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-DyfLMl2j.js → Privacy-aaN9R3Lu.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-CyqDx1OE.js → ProjectInit-C-kw49hE.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-DsBw0Csy.js → ProjectSettings-y5tTkBTC.js} +2 -2
- package/src/assets/web-panel/assets/Projects-DZHlhyPB.js +1 -0
- package/src/assets/web-panel/assets/{Providers-CCI_klN-.js → Providers-LqxnV72o.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-DNsnlDdS.js → QrScannerModal-D_q-Y5F0.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-B9r2iaee.js → QuickAsk-_E_KB5o8.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-BJDe03GR.js → Recommend-BN8S5WJA.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-J7JR3rRo.js → RemoteSession-BFzrY_aW.js} +3 -3
- package/src/assets/web-panel/assets/{Reputation-DR39AJeV.js → Reputation-C1HCLaTV.js} +1 -1
- package/src/assets/web-panel/assets/{Row-Dh77m3NZ.js → Row-B8T2-Qv7.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-BUdg28nJ.js → RssFeed-CNQLk8v1.js} +3 -3
- package/src/assets/web-panel/assets/{Search-BNC7sGhj.js → Search-BOx720Kc.js} +1 -1
- package/src/assets/web-panel/assets/{Security-DISENGI7.js → Security-Ds8z6PF6.js} +4 -4
- package/src/assets/web-panel/assets/{Services-BBBGpJ1-.js → Services-C0-YkRSF.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-DMcIoIv2.js → Skeleton-DUHlSySD.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-QAyFbI3r.js → Skills-C8CBbbh0.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-CSg5wLH7.js → Sla-lQltyYfB.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-DMPxTc5x.js → SpeechSettings-B5vyOma-.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-CPG7l30y.js → SyncSettings-BKDPu_y-.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-Y1kOVvVL.js → Tasks-Bgvf7LTx.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-jt9NagL0.js → Templates-BMZyYYS7.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-DauuJICx.js → Tenant-7PYTig8f.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-E9rnKeGq.js → Terminal-C2W0LMdR.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-Cc7z2APG.js → TimelineRenderer-CqREew_l.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-CV0UthRA.js → Tokens-BhTyxTuD.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-BFcJJGfu.js → Trigger-DxNSSZy_.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-BAydbNJA.js → Trust-Dy_wfbIh.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-DcOS6sqL.js → UkeySign-DJC5ANqx.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-BLHJZvif.js → VideoEditing-CNFevrO4.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-4QFLKHwW.js → Wallet-CXzuS1YK.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-BocEOOSj.js → WebAuthn-BWS6HP8c.js} +5 -5
- package/src/assets/web-panel/assets/{WorkflowEditor-DCfF7D0B.js → WorkflowEditor-swiN_7Vj.js} +1 -1
- package/src/assets/web-panel/assets/{chat-CxvfsYGr.js → chat-NwH7qlBs.js} +1 -1
- package/src/assets/web-panel/assets/{colors-KtQB9DmQ.js → colors-h6nRhBTw.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-Dx4J6arN.js → compact-item-BQ6B6rP2.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-Cq0olgzo.js → createContext-CQkJuRcG.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-Bz6vqBGV.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-Bf9-h3D-.js → hasIn-Diwl1WyI.js} +1 -1
- package/src/assets/web-panel/assets/{index-CpO6DKuu.js → index-0ZztPzIm.js} +1 -1
- package/src/assets/web-panel/assets/index-1b7DEw-w.js +1 -0
- package/src/assets/web-panel/assets/{index-DQDR-_ai.js → index-AzeSQJDK.js} +1 -1
- package/src/assets/web-panel/assets/{index-BMcC4cbj.js → index-B3QgcUBV.js} +1 -1
- package/src/assets/web-panel/assets/{index-BO6XvO-C.js → index-B5gj_uCC.js} +1 -1
- package/src/assets/web-panel/assets/{index-ClUfSChI.js → index-BI3ALCgN.js} +1 -1
- package/src/assets/web-panel/assets/{index-B3xfiItB.js → index-BRl3KUj0.js} +1 -1
- package/src/assets/web-panel/assets/{index-DFdEDvFn.js → index-BYYb8oeV.js} +1 -1
- package/src/assets/web-panel/assets/{index-CqnggDos.js → index-BcKDUGYf.js} +1 -1
- package/src/assets/web-panel/assets/{index-B_8UcDan.js → index-BcjlIMP9.js} +1 -1
- package/src/assets/web-panel/assets/{index-CmQuG0fr.js → index-BheCvudN.js} +1 -1
- package/src/assets/web-panel/assets/{index-iE9tqEN5.js → index-BjcARUtr.js} +1 -1
- package/src/assets/web-panel/assets/{index-nmyDOh_b.js → index-BnQUin2K.js} +1 -1
- package/src/assets/web-panel/assets/{index-Br1mDMfO.js → index-Bs6HyCfP.js} +1 -1
- package/src/assets/web-panel/assets/{index-DZWjYfWt.js → index-BvPz6vZ8.js} +1 -1
- package/src/assets/web-panel/assets/{index-0fi8DIVQ.js → index-C3Ye1ISy.js} +1 -1
- package/src/assets/web-panel/assets/{index-hSzyoXri.js → index-C7-T-u0L.js} +3 -3
- package/src/assets/web-panel/assets/{index-DfrFNMvz.js → index-CRWpixu2.js} +1 -1
- package/src/assets/web-panel/assets/{index-C9yaUHm9.js → index-CatBQDSQ.js} +1 -1
- package/src/assets/web-panel/assets/index-CfD4tMuW.js +1 -0
- package/src/assets/web-panel/assets/{index-DMR-keCL.js → index-CqH-oOR3.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cy865Hhq.js → index-CwsoVDi-.js} +1 -1
- package/src/assets/web-panel/assets/{index-DI5bRqNG.js → index-CyBtEVLM.js} +1 -1
- package/src/assets/web-panel/assets/{index-gt5N6VM_.js → index-D5P498Rk.js} +1 -1
- package/src/assets/web-panel/assets/{index-6dDDSCHD.js → index-DKaB4xTf.js} +1 -1
- package/src/assets/web-panel/assets/{index-BKtQyb2o.js → index-DQYZa1Ci.js} +1 -1
- package/src/assets/web-panel/assets/{index-CaIclw-E.js → index-DZ9CNcbJ.js} +1 -1
- package/src/assets/web-panel/assets/{index-_FIz-0tp.js → index-DZAsByy4.js} +1 -1
- package/src/assets/web-panel/assets/{index-DBRlZdZj.js → index-D_HY0nyN.js} +1 -1
- package/src/assets/web-panel/assets/{index-DsCdl2Od.js → index-Da8frO_A.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cfsn7N18.js → index-DjB8IC9D.js} +1 -1
- package/src/assets/web-panel/assets/{index-CeYP_kT9.js → index-Dwpthe2b.js} +1 -1
- package/src/assets/web-panel/assets/{index-B8WuPI-h.js → index-Fm95dO4u.js} +1 -1
- package/src/assets/web-panel/assets/{index-CK2U_AHi.js → index-GTefQSbY.js} +1 -1
- package/src/assets/web-panel/assets/{index-DnU-dUWd.js → index-OEWSrqit.js} +1 -1
- package/src/assets/web-panel/assets/{index-DDAe1UmZ.js → index-TYMmUsxZ.js} +1 -1
- package/src/assets/web-panel/assets/{index-D2BfSIrn.js → index-iHNOr_wL.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cz7f4Jaq.js → index-mxxZtsHp.js} +1 -1
- package/src/assets/web-panel/assets/{index-BHmdMKYl.js → index-pYwYBVeQ.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-DvzKJ6o5.js → initDefaultProps-BeUrpC_H.js} +1 -1
- package/src/assets/web-panel/assets/{motion-D1yTvmxW.js → motion-CHW5EXt1.js} +1 -1
- package/src/assets/web-panel/assets/{move-DxtSIF8K.js → move-CWaoWJvF.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-BDsQgIHY.js → mtc-parser-CEC6Us1u.js} +1 -1
- package/src/assets/web-panel/assets/{omit-UVBJW8kh.js → omit-1D-kP_Qj.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-D4QqtTyX.js → pickAttrs-CsS0myLl.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-QXmu6vBf.js → placementArrow-bMThOvkI.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-D-PmXO9M.js → responsiveObserve-B72knbW8.js} +1 -1
- package/src/assets/web-panel/assets/{slide-CKbGxEZJ.js → slide-C3bI_2yy.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-BbgXPkXm.js → statusUtils-0-FmcCAS.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-DOr9rk68.js → styleChecker-DXUyqoWY.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-B2wO2r36.js → useFlexGapSupport-BSSnL3f1.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-SeVGBK34.js → useFs-B0Cvvl0S.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-DUJBCxjD.js → usePersonalDataHub-6D-d4WU5.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-CF4EYsig.js → vnode-zjIrlxT_.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-pRUs5pqI.js → zoom-Hw7NEisu.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/command-help-index.json +1 -1
- package/src/command-manifest.json +1 -1
- package/src/commands/checkpoint.js +42 -7
- package/src/commands/orchestrate.js +85 -25
- package/src/commands/session-location.js +5 -0
- package/src/commands/session-show.js +109 -1
- package/src/commands/session.js +26 -2
- package/src/commands/tokens.js +9 -4
- package/src/data/changelog.json +26 -0
- package/src/generated/cc-agent-protocol.schema.json +71 -0
- package/src/harness/jsonl-session-store.js +357 -114
- package/src/harness/mcp-client.js +44 -1
- package/src/lib/agent-router.js +35 -7
- package/src/lib/agent-sandbox.js +302 -2
- package/src/lib/app-server/server.js +14 -0
- package/src/lib/background-agent-supervisor.js +14 -4
- package/src/lib/causal-observability.js +64 -1
- package/src/lib/checkpoint-summary-projection.js +98 -0
- package/src/lib/checkpoint-timeline-authority.js +15 -51
- package/src/lib/claude-code-bridge.js +302 -60
- package/src/lib/codex-app-server-adapter.js +161 -35
- package/src/lib/cost-budget.js +4 -0
- package/src/lib/doctor-checkup.js +31 -2
- package/src/lib/durable-security-store.js +6 -1
- package/src/lib/evolution/evolution-artifact-identity.js +21 -0
- package/src/lib/evolution/evolution-artifact-ports.js +5 -11
- package/src/lib/evolution/evolution-eval-cohort-receipt-reconciliation.js +389 -0
- package/src/lib/execution-location-failure-site.js +63 -0
- package/src/lib/execution-location-target.js +102 -8
- package/src/lib/external-agent-adapters.js +2 -0
- package/src/lib/image-file-boundary.js +154 -0
- package/src/lib/image-input.js +16 -3
- package/src/lib/llm-pricing.js +114 -8
- package/src/lib/lsp/code-intelligence.js +7 -1
- package/src/lib/mcp-call-ledger-store.js +7 -2
- package/src/lib/model-capabilities.js +21 -10
- package/src/lib/model-context-catalog.js +60 -1
- package/src/lib/openai-responses.js +7 -1
- package/src/lib/orchestrator.js +8 -2
- package/src/lib/process-execution-broker/index.js +93 -1
- package/src/lib/process-execution-broker/linux-subreaper-artifact.js +120 -0
- package/src/lib/process-execution-broker/linux-subreaper-helper.js +344 -0
- package/src/lib/process-execution-broker/linux-subreaper-process.js +326 -0
- package/src/lib/process-execution-broker/linux-subreaper-supervisor.c +360 -0
- package/src/lib/process-execution-broker/owned-posix-process-group-worker.mjs +131 -0
- package/src/lib/process-execution-broker/owned-posix-process-group.js +267 -0
- package/src/lib/process-execution-broker/process-ownership-journal.js +297 -0
- package/src/lib/process-execution-broker/process-ownership-quarantine.js +91 -0
- package/src/lib/runtime-usage-ledger.js +3 -0
- package/src/lib/sandbox-docker-egress.js +507 -0
- package/src/lib/sandbox-egress-proxy.js +167 -10
- package/src/lib/sandbox-egress-worker-thread.js +38 -0
- package/src/lib/sandbox-egress-worker.js +196 -0
- package/src/lib/secure-fs.js +158 -42
- package/src/lib/session-branch-history.js +240 -0
- package/src/lib/session-history-origins.js +285 -0
- package/src/lib/session-input-receipt.js +110 -0
- package/src/lib/session-resource-budget.js +8 -8
- package/src/lib/session-transcript-history.js +456 -0
- package/src/lib/session-transcript-page.js +164 -0
- package/src/lib/session-transcript-reference.js +38 -0
- package/src/lib/session-usage.js +8 -0
- package/src/lib/token-tracker.js +32 -54
- package/src/lib/usage-pricing-context.js +93 -0
- package/src/runtime/agent-core.js +141 -21
- package/src/runtime/headless-stream.js +202 -4
- package/src/assets/web-panel/assets/ChatBubbleRenderer-D0WrJQaQ.js +0 -1
- package/src/assets/web-panel/assets/Projects-CRaSI25Z.js +0 -1
- package/src/assets/web-panel/assets/devWarning-CmgDNkoO.js +0 -1
- package/src/assets/web-panel/assets/index-BpIZ2S01.js +0 -1
- package/src/assets/web-panel/assets/index-C-VmKuyv.js +0 -1
package/src/lib/agent-router.js
CHANGED
|
@@ -228,12 +228,14 @@ export class AgentRouter extends EventEmitter {
|
|
|
228
228
|
*/
|
|
229
229
|
static autoDetect(options = {}) {
|
|
230
230
|
const backends = [];
|
|
231
|
+
const claude = options.cliDetections?.claude || detectClaudeCode();
|
|
232
|
+
const codex = options.cliDetections?.codex || detectCodex();
|
|
231
233
|
|
|
232
|
-
if (
|
|
233
|
-
backends.push({ type: BACKEND_TYPE.CLAUDE, weight: 3 });
|
|
234
|
+
if (claude.found) {
|
|
235
|
+
backends.push({ type: BACKEND_TYPE.CLAUDE, weight: 3, installed: true });
|
|
234
236
|
}
|
|
235
|
-
if (
|
|
236
|
-
backends.push({ type: BACKEND_TYPE.CODEX, weight: 2 });
|
|
237
|
+
if (codex.found) {
|
|
238
|
+
backends.push({ type: BACKEND_TYPE.CODEX, weight: 2, installed: true });
|
|
237
239
|
}
|
|
238
240
|
if (process.env.GEMINI_API_KEY) {
|
|
239
241
|
backends.push({ type: BACKEND_TYPE.GEMINI, weight: 2 });
|
|
@@ -263,7 +265,7 @@ export class AgentRouter extends EventEmitter {
|
|
|
263
265
|
const { cwd = process.cwd() } = options;
|
|
264
266
|
if (this._backends.length === 0) {
|
|
265
267
|
throw new Error(
|
|
266
|
-
"No agent backends available.
|
|
268
|
+
"No agent backends available. Use cc agent for tool execution; orchestrate API backends produce text only.",
|
|
267
269
|
);
|
|
268
270
|
}
|
|
269
271
|
const evolutionIngress =
|
|
@@ -277,8 +279,7 @@ export class AgentRouter extends EventEmitter {
|
|
|
277
279
|
error.code = "CC_AGENT_EVOLUTION_INGRESS_FAILED";
|
|
278
280
|
throw error;
|
|
279
281
|
}
|
|
280
|
-
const backends =
|
|
281
|
-
this._backends.filter((backend) => !backend.isCLI);
|
|
282
|
+
const backends = this._backends.filter((backend) => !backend.isCLI);
|
|
282
283
|
|
|
283
284
|
if (backends.length === 0) {
|
|
284
285
|
const error = new Error(
|
|
@@ -475,6 +476,7 @@ export class AgentRouter extends EventEmitter {
|
|
|
475
476
|
return {
|
|
476
477
|
type,
|
|
477
478
|
isCLI: true,
|
|
479
|
+
installed: cfg.installed ?? null,
|
|
478
480
|
weight: cfg.weight || 1,
|
|
479
481
|
_pool: pool,
|
|
480
482
|
timeout: cfg.timeout || 300_000,
|
|
@@ -530,8 +532,34 @@ export class AgentRouter extends EventEmitter {
|
|
|
530
532
|
kind: b.isCLI ? "cli" : "api",
|
|
531
533
|
provider: b.provider || b.type,
|
|
532
534
|
weight: b.weight,
|
|
535
|
+
installed: b.isCLI ? (b.installed ?? null) : null,
|
|
536
|
+
configured: true,
|
|
537
|
+
governanceAdmitted: b.isCLI ? false : null,
|
|
538
|
+
requiresAuthenticatedRun: true,
|
|
539
|
+
dispatchable: !b.isCLI,
|
|
540
|
+
runnable: false,
|
|
541
|
+
executionMode: b.isCLI ? "blocked" : "text-only",
|
|
542
|
+
blockedReason: b.isCLI
|
|
543
|
+
? AGENT_ROUTER_ERROR.EXTERNAL_MODEL_INGRESS_UNATTESTED
|
|
544
|
+
: "API_TEXT_ONLY_NO_TOOL_EXECUTION",
|
|
545
|
+
alternative: "cc agent",
|
|
533
546
|
}));
|
|
534
547
|
}
|
|
548
|
+
|
|
549
|
+
/** Configuration preflight only; dispatch still authenticates each Run. */
|
|
550
|
+
assertDispatchAvailable() {
|
|
551
|
+
if (this._backends.some((backend) => !backend.isCLI)) return;
|
|
552
|
+
const error = new Error(
|
|
553
|
+
this._backends.length
|
|
554
|
+
? "External Claude/Codex CLIs are blocked: per-request model governance is unattested. Use cc agent for tool execution."
|
|
555
|
+
: "No agent backends available. Use cc agent for tool execution.",
|
|
556
|
+
);
|
|
557
|
+
error.code = this._backends.length
|
|
558
|
+
? AGENT_ROUTER_ERROR.EXTERNAL_MODEL_INGRESS_UNATTESTED
|
|
559
|
+
: "AGENT_ROUTER_NO_BACKENDS";
|
|
560
|
+
error.backends = this.summary();
|
|
561
|
+
throw error;
|
|
562
|
+
}
|
|
535
563
|
}
|
|
536
564
|
|
|
537
565
|
// ===== V2 Surface: Agent Router governance overlay (CLI v0.132.0) =====
|
package/src/lib/agent-sandbox.js
CHANGED
|
@@ -13,6 +13,9 @@ export const AGENT_SANDBOX_MODES = Object.freeze([
|
|
|
13
13
|
]);
|
|
14
14
|
export const AGENT_SANDBOX_CAPABILITY_SCHEMA =
|
|
15
15
|
"chainlesschain.agent-sandbox-capabilities/v1";
|
|
16
|
+
const dockerEgressReceipts = new WeakMap();
|
|
17
|
+
const PINNED_DOCKER_IMAGE =
|
|
18
|
+
/^[a-zA-Z0-9][a-zA-Z0-9./:_-]*@sha256:[a-f0-9]{64}$/;
|
|
16
19
|
export const _deps = {
|
|
17
20
|
spawnSync: (...args) => executionBroker.spawnSync(...args),
|
|
18
21
|
host: () => ({
|
|
@@ -249,12 +252,21 @@ export function normalizeAgentSandbox(value, options = {}) {
|
|
|
249
252
|
return {
|
|
250
253
|
engine: effectiveSettings.engine || "docker",
|
|
251
254
|
image: effectiveSettings.image || image,
|
|
255
|
+
...(effectiveSettings.engine === "docker-egress"
|
|
256
|
+
? { relayImage: effectiveSettings.relayImage || null }
|
|
257
|
+
: {}),
|
|
252
258
|
cwd: path.resolve(options.cwd || process.cwd()),
|
|
253
259
|
network: managedNetworkDisabled
|
|
254
260
|
? false
|
|
255
261
|
: options.network === true || effectiveSettings.network === true,
|
|
256
262
|
policy: normalizeSandboxPolicy(
|
|
257
|
-
effectiveSettings
|
|
263
|
+
effectiveSettings.engine === "docker-egress"
|
|
264
|
+
? {
|
|
265
|
+
...effectiveSettings,
|
|
266
|
+
allowUnsandboxedCommands: false,
|
|
267
|
+
failIfUnavailable: true,
|
|
268
|
+
}
|
|
269
|
+
: effectiveSettings,
|
|
258
270
|
options.cwd || process.cwd(),
|
|
259
271
|
),
|
|
260
272
|
};
|
|
@@ -340,6 +352,149 @@ function addUniqueCapability(target, entry) {
|
|
|
340
352
|
}
|
|
341
353
|
}
|
|
342
354
|
|
|
355
|
+
function egressBinding(sandbox) {
|
|
356
|
+
return JSON.stringify({
|
|
357
|
+
engine: sandbox.engine,
|
|
358
|
+
image: sandbox.image,
|
|
359
|
+
relayImage: sandbox.relayImage,
|
|
360
|
+
cwd: sandbox.cwd,
|
|
361
|
+
network: sandbox.network,
|
|
362
|
+
mode: sandbox.mode || null,
|
|
363
|
+
policy: sandbox.policy || {},
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
function assessDockerEgressCapabilities(sandbox, options, host) {
|
|
368
|
+
const policy = sandbox.policy || {};
|
|
369
|
+
const domainRules =
|
|
370
|
+
(policy.allowedDomains?.length || 0) + (policy.deniedDomains?.length || 0);
|
|
371
|
+
const requested = [
|
|
372
|
+
capability("isolation.container"),
|
|
373
|
+
capability("filesystem.workspace-read-write"),
|
|
374
|
+
capability("network.domain-policy", { entries: domainRules }),
|
|
375
|
+
];
|
|
376
|
+
const unsupported = [];
|
|
377
|
+
const reject = (id, reason, message) =>
|
|
378
|
+
unsupported.push(capability(id, { reason, message }));
|
|
379
|
+
if (policy.excludedCommands?.length)
|
|
380
|
+
reject(
|
|
381
|
+
"execution.excluded-commands",
|
|
382
|
+
"docker_egress_exclusions_prohibited",
|
|
383
|
+
"Docker egress does not allow commands to bypass its execution route.",
|
|
384
|
+
);
|
|
385
|
+
if (host.platform !== "linux")
|
|
386
|
+
reject(
|
|
387
|
+
"backend.platform",
|
|
388
|
+
"docker_egress_requires_linux",
|
|
389
|
+
"Docker egress requires a Linux host.",
|
|
390
|
+
);
|
|
391
|
+
if (!["x64", "arm64"].includes(host.arch))
|
|
392
|
+
reject(
|
|
393
|
+
"backend.architecture",
|
|
394
|
+
"docker_egress_architecture_unsupported",
|
|
395
|
+
"Docker egress requires x64 or arm64.",
|
|
396
|
+
);
|
|
397
|
+
if (sandbox.mode === "strict" || sandbox.network !== true)
|
|
398
|
+
reject(
|
|
399
|
+
"network.domain-policy",
|
|
400
|
+
"docker_egress_network_disabled",
|
|
401
|
+
"Docker egress requires network=true and is prohibited in strict mode.",
|
|
402
|
+
);
|
|
403
|
+
if (!domainRules)
|
|
404
|
+
reject(
|
|
405
|
+
"network.domain-policy",
|
|
406
|
+
"docker_egress_domain_policy_required",
|
|
407
|
+
"Docker egress requires explicit domain rules.",
|
|
408
|
+
);
|
|
409
|
+
for (const field of ["image", "relayImage"]) {
|
|
410
|
+
if (
|
|
411
|
+
typeof sandbox[field] !== "string" ||
|
|
412
|
+
!PINNED_DOCKER_IMAGE.test(sandbox[field])
|
|
413
|
+
)
|
|
414
|
+
reject(
|
|
415
|
+
`backend.${field}`,
|
|
416
|
+
"docker_egress_image_digest_required",
|
|
417
|
+
`Docker egress ${field} must be pinned to a sha256 digest.`,
|
|
418
|
+
);
|
|
419
|
+
}
|
|
420
|
+
for (const field of ["allowRead", "denyRead", "allowWrite", "denyWrite"]) {
|
|
421
|
+
if (policy[field]?.length)
|
|
422
|
+
reject(
|
|
423
|
+
`filesystem.${field}`,
|
|
424
|
+
"docker_fine_grained_filesystem_unsupported",
|
|
425
|
+
"Docker egress does not support additional or masked filesystem paths.",
|
|
426
|
+
);
|
|
427
|
+
}
|
|
428
|
+
const enforceable = unsupported.length
|
|
429
|
+
? []
|
|
430
|
+
: [
|
|
431
|
+
capability("isolation.container", { enforcement: "docker-container" }),
|
|
432
|
+
capability("filesystem.workspace-read-write", {
|
|
433
|
+
enforcement: "docker-bind-mount",
|
|
434
|
+
}),
|
|
435
|
+
capability("network.domain-policy", {
|
|
436
|
+
enforcement: "docker-none-network-uds-proxy-seccomp",
|
|
437
|
+
}),
|
|
438
|
+
];
|
|
439
|
+
const receipt = options.execution?.receipt;
|
|
440
|
+
const registered =
|
|
441
|
+
receipt && typeof receipt === "object"
|
|
442
|
+
? dockerEgressReceipts.get(receipt)
|
|
443
|
+
: null;
|
|
444
|
+
const started =
|
|
445
|
+
unsupported.length === 0 &&
|
|
446
|
+
registered?.binding === egressBinding(sandbox) &&
|
|
447
|
+
registered.host.platform === host.platform &&
|
|
448
|
+
registered.host.arch === host.arch;
|
|
449
|
+
const attempted = started || options.execution?.attempted === true;
|
|
450
|
+
const outcomeUnknown = options.execution?.outcomeUnknown === true;
|
|
451
|
+
const checked = started || options.availability != null;
|
|
452
|
+
const available = started
|
|
453
|
+
? true
|
|
454
|
+
: checked
|
|
455
|
+
? options.availability?.available === true
|
|
456
|
+
: null;
|
|
457
|
+
return {
|
|
458
|
+
schema: AGENT_SANDBOX_CAPABILITY_SCHEMA,
|
|
459
|
+
host: {
|
|
460
|
+
platform: String(host.platform),
|
|
461
|
+
release: String(host.release),
|
|
462
|
+
arch: String(host.arch),
|
|
463
|
+
},
|
|
464
|
+
backend: {
|
|
465
|
+
engine: "docker-egress",
|
|
466
|
+
isolationLevel: "container",
|
|
467
|
+
availabilityChecked: checked,
|
|
468
|
+
available,
|
|
469
|
+
reason: started
|
|
470
|
+
? null
|
|
471
|
+
: outcomeUnknown
|
|
472
|
+
? "docker_egress_outcome_unknown"
|
|
473
|
+
: options.availability?.reason || null,
|
|
474
|
+
},
|
|
475
|
+
status: unsupported.length
|
|
476
|
+
? "unsupported"
|
|
477
|
+
: started
|
|
478
|
+
? "applied"
|
|
479
|
+
: outcomeUnknown
|
|
480
|
+
? "outcome-unknown"
|
|
481
|
+
: attempted
|
|
482
|
+
? "failed-to-start"
|
|
483
|
+
: checked && !available
|
|
484
|
+
? "unavailable"
|
|
485
|
+
: "ready",
|
|
486
|
+
execution: {
|
|
487
|
+
observed: Boolean(started || options.execution),
|
|
488
|
+
attempted,
|
|
489
|
+
started: Boolean(started),
|
|
490
|
+
},
|
|
491
|
+
requested,
|
|
492
|
+
enforceable,
|
|
493
|
+
applied: started ? enforceable.map((entry) => ({ ...entry })) : [],
|
|
494
|
+
unsupported,
|
|
495
|
+
};
|
|
496
|
+
}
|
|
497
|
+
|
|
343
498
|
/**
|
|
344
499
|
* Describe the exact legacy agent-shell sandbox request without claiming that
|
|
345
500
|
* a command has run. `enforceable` is backend capability; `applied` is only
|
|
@@ -351,6 +506,8 @@ export function assessAgentSandboxCapabilities(sandbox, options = {}) {
|
|
|
351
506
|
release: os.release(),
|
|
352
507
|
arch: process.arch,
|
|
353
508
|
};
|
|
509
|
+
if (sandbox?.engine === "docker-egress")
|
|
510
|
+
return assessDockerEgressCapabilities(sandbox, options, host);
|
|
354
511
|
const execution = options.execution || null;
|
|
355
512
|
const availability = options.availability || null;
|
|
356
513
|
const requested = [];
|
|
@@ -594,6 +751,22 @@ function attachSandboxCapabilityReport(
|
|
|
594
751
|
}
|
|
595
752
|
|
|
596
753
|
export function executeSandboxedShell(command, sandbox, options = {}) {
|
|
754
|
+
if (sandbox?.engine === "docker-egress") {
|
|
755
|
+
return {
|
|
756
|
+
stdout: "",
|
|
757
|
+
stderr: "Docker egress requires the asynchronous execution route",
|
|
758
|
+
exitCode: 1,
|
|
759
|
+
failedToStart: true,
|
|
760
|
+
sandboxCapabilities: assessAgentSandboxCapabilities(sandbox, {
|
|
761
|
+
host: _deps.host(),
|
|
762
|
+
execution: { attempted: true },
|
|
763
|
+
availability: {
|
|
764
|
+
available: false,
|
|
765
|
+
reason: "docker_egress_async_execution_required",
|
|
766
|
+
},
|
|
767
|
+
}),
|
|
768
|
+
};
|
|
769
|
+
}
|
|
597
770
|
if (!sandbox || !["docker", "bubblewrap"].includes(sandbox.engine)) {
|
|
598
771
|
throw new Error("A supported agent sandbox configuration is required");
|
|
599
772
|
}
|
|
@@ -783,7 +956,7 @@ function executeBubblewrapShell(command, sandbox, options, hostCwd, policy) {
|
|
|
783
956
|
export function isolationLevel(sandbox) {
|
|
784
957
|
if (!sandbox) return "policy-only";
|
|
785
958
|
if (sandbox.engine === "bubblewrap") return "os-sandbox";
|
|
786
|
-
if (
|
|
959
|
+
if (["docker", "docker-egress"].includes(sandbox.engine)) return "container";
|
|
787
960
|
return "policy-only";
|
|
788
961
|
}
|
|
789
962
|
|
|
@@ -836,6 +1009,11 @@ export function assertSandboxAvailable(sandbox, deps = _deps) {
|
|
|
836
1009
|
if (!sandbox || sandbox.policy?.failIfUnavailable !== true) return;
|
|
837
1010
|
const probe = probeSandboxAvailability(sandbox, deps);
|
|
838
1011
|
if (!probe.available) {
|
|
1012
|
+
if (sandbox.engine === "docker-egress") {
|
|
1013
|
+
throw new Error(
|
|
1014
|
+
"Docker egress is unavailable; this backend never falls back to unsandboxed execution.",
|
|
1015
|
+
);
|
|
1016
|
+
}
|
|
839
1017
|
throw new Error(
|
|
840
1018
|
`sandbox.failIfUnavailable: ${sandbox.engine} sandbox is unavailable (${probe.reason}) — refusing to start. Install/start ${sandbox.engine === "bubblewrap" ? "bubblewrap" : "Docker"}, or unset failIfUnavailable to allow per-command degradation.`,
|
|
841
1019
|
);
|
|
@@ -860,5 +1038,127 @@ export function sandboxSummary(sandbox) {
|
|
|
860
1038
|
},
|
|
861
1039
|
};
|
|
862
1040
|
if (sandbox.mode) summary.mode = sandbox.mode;
|
|
1041
|
+
if (sandbox.engine === "docker-egress")
|
|
1042
|
+
summary.relayImage = sandbox.relayImage;
|
|
863
1043
|
return summary;
|
|
864
1044
|
}
|
|
1045
|
+
|
|
1046
|
+
/** Own the asynchronous session and mint execution evidence only after its
|
|
1047
|
+
* real implementation reports a terminal result and cleanup completes. */
|
|
1048
|
+
export async function executeDockerEgressShell(command, sandbox, options = {}) {
|
|
1049
|
+
if (sandbox?.engine !== "docker-egress")
|
|
1050
|
+
throw new TypeError("docker-egress configuration is required");
|
|
1051
|
+
const host = _deps.host();
|
|
1052
|
+
const snapshot = JSON.parse(JSON.stringify(sandbox));
|
|
1053
|
+
const preflight = assessAgentSandboxCapabilities(snapshot, { host });
|
|
1054
|
+
let startAuthorized = false;
|
|
1055
|
+
let observedResult;
|
|
1056
|
+
const failure = () => ({
|
|
1057
|
+
stdout:
|
|
1058
|
+
startAuthorized && typeof observedResult?.stdout === "string"
|
|
1059
|
+
? observedResult.stdout
|
|
1060
|
+
: "",
|
|
1061
|
+
stderr: startAuthorized
|
|
1062
|
+
? "Docker egress execution outcome or cleanup could not be verified; do not retry automatically."
|
|
1063
|
+
: preflight.unsupported.length
|
|
1064
|
+
? preflight.unsupported.map((entry) => entry.message).join("; ")
|
|
1065
|
+
: "Docker egress execution is unavailable or did not complete safely.",
|
|
1066
|
+
exitCode: 1,
|
|
1067
|
+
...(startAuthorized
|
|
1068
|
+
? {
|
|
1069
|
+
executionOutcome: observedResult
|
|
1070
|
+
? "completed-cleanup-unknown"
|
|
1071
|
+
: "unknown",
|
|
1072
|
+
retrySafe: false,
|
|
1073
|
+
}
|
|
1074
|
+
: { failedToStart: true }),
|
|
1075
|
+
sandboxCapabilities: assessAgentSandboxCapabilities(snapshot, {
|
|
1076
|
+
host,
|
|
1077
|
+
execution: { attempted: true, outcomeUnknown: startAuthorized },
|
|
1078
|
+
...(startAuthorized
|
|
1079
|
+
? {}
|
|
1080
|
+
: {
|
|
1081
|
+
availability: {
|
|
1082
|
+
available: false,
|
|
1083
|
+
reason: "docker_egress_execution_unavailable",
|
|
1084
|
+
},
|
|
1085
|
+
}),
|
|
1086
|
+
}),
|
|
1087
|
+
});
|
|
1088
|
+
if (preflight.unsupported.length) return failure();
|
|
1089
|
+
let session;
|
|
1090
|
+
let authorityError;
|
|
1091
|
+
try {
|
|
1092
|
+
const { startDockerEgressSession } =
|
|
1093
|
+
await import("./sandbox-docker-egress.js");
|
|
1094
|
+
session = await startDockerEgressSession({
|
|
1095
|
+
brokerSocketPath: options.brokerSocketPath,
|
|
1096
|
+
relayImage: snapshot.relayImage,
|
|
1097
|
+
targetImage: snapshot.image,
|
|
1098
|
+
workspaceRoot: options.cwd || snapshot.cwd,
|
|
1099
|
+
auditContext: options.auditContext,
|
|
1100
|
+
});
|
|
1101
|
+
try {
|
|
1102
|
+
options.onSession?.(session);
|
|
1103
|
+
} catch (error) {
|
|
1104
|
+
authorityError = error;
|
|
1105
|
+
throw error;
|
|
1106
|
+
}
|
|
1107
|
+
const result = await session.run(command, {
|
|
1108
|
+
timeoutMs: options.timeoutMs || options.timeout,
|
|
1109
|
+
auditContext: options.auditContext,
|
|
1110
|
+
env: options.env,
|
|
1111
|
+
async beforeStart() {
|
|
1112
|
+
try {
|
|
1113
|
+
if (egressBinding(sandbox) !== egressBinding(snapshot))
|
|
1114
|
+
throw Object.assign(
|
|
1115
|
+
new Error("Sandbox policy changed during Docker startup"),
|
|
1116
|
+
{ code: "CC_SHELL_POLICY_AUTHORITY_CHANGED" },
|
|
1117
|
+
);
|
|
1118
|
+
await options.beforeStart?.();
|
|
1119
|
+
if (egressBinding(sandbox) !== egressBinding(snapshot))
|
|
1120
|
+
throw Object.assign(
|
|
1121
|
+
new Error("Sandbox policy changed during Docker startup"),
|
|
1122
|
+
{ code: "CC_SHELL_POLICY_AUTHORITY_CHANGED" },
|
|
1123
|
+
);
|
|
1124
|
+
startAuthorized = true;
|
|
1125
|
+
} catch (error) {
|
|
1126
|
+
authorityError = error;
|
|
1127
|
+
throw error;
|
|
1128
|
+
}
|
|
1129
|
+
},
|
|
1130
|
+
});
|
|
1131
|
+
observedResult = result;
|
|
1132
|
+
await session.close();
|
|
1133
|
+
if (
|
|
1134
|
+
!startAuthorized ||
|
|
1135
|
+
!Number.isInteger(result?.exitCode) ||
|
|
1136
|
+
typeof result.stdout !== "string" ||
|
|
1137
|
+
typeof result.stderr !== "string"
|
|
1138
|
+
) {
|
|
1139
|
+
throw new Error("Docker egress execution proof is incomplete");
|
|
1140
|
+
}
|
|
1141
|
+
const receipt = Object.freeze({ kind: "docker-egress-execution/v1" });
|
|
1142
|
+
dockerEgressReceipts.set(receipt, {
|
|
1143
|
+
binding: egressBinding(snapshot),
|
|
1144
|
+
host: { ...host },
|
|
1145
|
+
});
|
|
1146
|
+
return {
|
|
1147
|
+
...result,
|
|
1148
|
+
sandboxCapabilities: assessAgentSandboxCapabilities(snapshot, {
|
|
1149
|
+
host,
|
|
1150
|
+
execution: { attempted: true, receipt },
|
|
1151
|
+
}),
|
|
1152
|
+
};
|
|
1153
|
+
} catch (error) {
|
|
1154
|
+
if (session) {
|
|
1155
|
+
try {
|
|
1156
|
+
await session.close();
|
|
1157
|
+
} catch (cleanupError) {
|
|
1158
|
+
error.cleanupError = cleanupError;
|
|
1159
|
+
}
|
|
1160
|
+
}
|
|
1161
|
+
if (authorityError) throw authorityError;
|
|
1162
|
+
return failure();
|
|
1163
|
+
}
|
|
1164
|
+
}
|
|
@@ -1463,6 +1463,20 @@ export class CcAppServer {
|
|
|
1463
1463
|
contextRevision: Number.isSafeInteger(event.context_revision)
|
|
1464
1464
|
? event.context_revision
|
|
1465
1465
|
: null,
|
|
1466
|
+
// Preserve the MCP form/URL vocabulary for native host review. This is
|
|
1467
|
+
// question metadata, not an approval decision or host execution authority.
|
|
1468
|
+
...(event.metadata?.kind === "mcp_elicitation"
|
|
1469
|
+
? {
|
|
1470
|
+
metadata: {
|
|
1471
|
+
kind: "mcp_elicitation",
|
|
1472
|
+
mode: event.metadata.mode || "form",
|
|
1473
|
+
server: event.metadata.server ?? null,
|
|
1474
|
+
requestedSchema: event.metadata.requestedSchema ?? null,
|
|
1475
|
+
url: event.metadata.url ?? null,
|
|
1476
|
+
elicitationId: event.metadata.elicitationId ?? null,
|
|
1477
|
+
},
|
|
1478
|
+
}
|
|
1479
|
+
: {}),
|
|
1466
1480
|
expiresAt: new Date(this.now() + this.requestTimeoutMs).toISOString(),
|
|
1467
1481
|
};
|
|
1468
1482
|
}
|
|
@@ -2174,14 +2174,22 @@ function isBackgroundAgentAfterCursor(state, cursor) {
|
|
|
2174
2174
|
return String(state.id).localeCompare(cursor.id) > 0;
|
|
2175
2175
|
}
|
|
2176
2176
|
|
|
2177
|
+
function observeBackgroundAgentList(options, source, entryCount) {
|
|
2178
|
+
// Optional in-process measurement only; telemetry failures cannot change a read.
|
|
2179
|
+
try {
|
|
2180
|
+
options.indexObserver?.(Object.freeze({ source, entryCount }));
|
|
2181
|
+
} catch {
|
|
2182
|
+
// Keep observers outside the authoritative result.
|
|
2183
|
+
}
|
|
2184
|
+
}
|
|
2185
|
+
|
|
2177
2186
|
/**
|
|
2178
2187
|
* Return one stable, bounded page from the newest-first background session
|
|
2179
2188
|
* feed. The cursor is an opaque ordering fence, not an authority token.
|
|
2180
2189
|
*
|
|
2181
|
-
*
|
|
2182
|
-
*
|
|
2183
|
-
*
|
|
2184
|
-
* caller.
|
|
2190
|
+
* The index still enumerates and stats every authority file; selected rows are
|
|
2191
|
+
* read again for lifecycle reconciliation. It reduces content reads, not the
|
|
2192
|
+
* O(N) inventory fence. indexObserver receives the chosen read path and count.
|
|
2185
2193
|
*/
|
|
2186
2194
|
export function listBackgroundAgentsPage(options = {}) {
|
|
2187
2195
|
const limit = normalizeBackgroundAgentPageLimit(options.limit);
|
|
@@ -2223,6 +2231,7 @@ export function listBackgroundAgentsPage(options = {}) {
|
|
|
2223
2231
|
projected.length > sessions.length
|
|
2224
2232
|
? encodeBackgroundAgentPageCursor(sessions.at(-1), options.all)
|
|
2225
2233
|
: null;
|
|
2234
|
+
observeBackgroundAgentList(options, indexed.source, indexed.entries.length);
|
|
2226
2235
|
return Object.freeze({
|
|
2227
2236
|
sessions: Object.freeze(sessions),
|
|
2228
2237
|
nextCursor,
|
|
@@ -2238,6 +2247,7 @@ export function listBackgroundAgentsPage(options = {}) {
|
|
|
2238
2247
|
eligible.length > sessions.length
|
|
2239
2248
|
? encodeBackgroundAgentPageCursor(sessions.at(-1), options.all)
|
|
2240
2249
|
: null;
|
|
2250
|
+
observeBackgroundAgentList(options, "full-scan", eligible.length);
|
|
2241
2251
|
return Object.freeze({
|
|
2242
2252
|
sessions: Object.freeze(sessions),
|
|
2243
2253
|
nextCursor,
|
|
@@ -8,6 +8,10 @@
|
|
|
8
8
|
|
|
9
9
|
import crypto from "node:crypto";
|
|
10
10
|
import { PRICE_TABLE, priceRollup } from "./llm-pricing.js";
|
|
11
|
+
import {
|
|
12
|
+
projectUsagePricingContext,
|
|
13
|
+
mergeUsagePricingBucket,
|
|
14
|
+
} from "./usage-pricing-context.js";
|
|
11
15
|
import { canonicalDeliveryJson } from "./delivery-evidence.js";
|
|
12
16
|
import { createSessionTranscriptStructureProjection } from "./session-transcript-structure.js";
|
|
13
17
|
|
|
@@ -251,7 +255,64 @@ function normalizePricingTable(table = PRICE_TABLE) {
|
|
|
251
255
|
) {
|
|
252
256
|
throw new Error("pricing rates must be finite non-negative numbers");
|
|
253
257
|
}
|
|
254
|
-
|
|
258
|
+
const result = { match, in: input, out: output };
|
|
259
|
+
if (Object.hasOwn(entry, "explicitOverride")) {
|
|
260
|
+
if (typeof entry.explicitOverride !== "boolean")
|
|
261
|
+
throw new Error("pricing explicitOverride must be boolean");
|
|
262
|
+
result.explicitOverride = entry.explicitOverride;
|
|
263
|
+
}
|
|
264
|
+
if (Object.hasOwn(entry, "exact")) {
|
|
265
|
+
if (typeof entry.exact !== "boolean")
|
|
266
|
+
throw new Error("pricing exact must be boolean");
|
|
267
|
+
result.exact = entry.exact;
|
|
268
|
+
}
|
|
269
|
+
if (Object.hasOwn(entry, "terms")) {
|
|
270
|
+
const terms = entry.terms;
|
|
271
|
+
const nonNegative = (value) => {
|
|
272
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value < 0)
|
|
273
|
+
throw new Error("invalid pricing multiplier");
|
|
274
|
+
return value;
|
|
275
|
+
};
|
|
276
|
+
assertKnownOwnKeys(
|
|
277
|
+
terms,
|
|
278
|
+
new Set([
|
|
279
|
+
"cacheReadMultiplier",
|
|
280
|
+
"cacheWriteMultiplier",
|
|
281
|
+
"longContext",
|
|
282
|
+
"serviceMultipliers",
|
|
283
|
+
]),
|
|
284
|
+
"pricing terms",
|
|
285
|
+
);
|
|
286
|
+
assertKnownOwnKeys(
|
|
287
|
+
terms.longContext,
|
|
288
|
+
new Set(["threshold", "inputMultiplier", "outputMultiplier"]),
|
|
289
|
+
"pricing long context",
|
|
290
|
+
);
|
|
291
|
+
const threshold = nonNegative(terms.longContext.threshold);
|
|
292
|
+
if (!Number.isSafeInteger(threshold))
|
|
293
|
+
throw new Error("invalid pricing threshold");
|
|
294
|
+
assertKnownOwnKeys(
|
|
295
|
+
terms.serviceMultipliers,
|
|
296
|
+
new Set(["standard", "default", "auto", "batch", "flex", "fast"]),
|
|
297
|
+
"pricing service multipliers",
|
|
298
|
+
);
|
|
299
|
+
result.terms = {
|
|
300
|
+
cacheReadMultiplier: nonNegative(terms.cacheReadMultiplier),
|
|
301
|
+
cacheWriteMultiplier: nonNegative(terms.cacheWriteMultiplier),
|
|
302
|
+
longContext: {
|
|
303
|
+
threshold,
|
|
304
|
+
inputMultiplier: nonNegative(terms.longContext.inputMultiplier),
|
|
305
|
+
outputMultiplier: nonNegative(terms.longContext.outputMultiplier),
|
|
306
|
+
},
|
|
307
|
+
serviceMultipliers: Object.fromEntries(
|
|
308
|
+
Object.entries(terms.serviceMultipliers).map(([key, value]) => [
|
|
309
|
+
key,
|
|
310
|
+
nonNegative(value),
|
|
311
|
+
]),
|
|
312
|
+
),
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
return result;
|
|
255
316
|
});
|
|
256
317
|
normalized.set(providerKey, normalizedEntries);
|
|
257
318
|
}
|
|
@@ -672,6 +733,7 @@ function strictUsage(event) {
|
|
|
672
733
|
totalTokens: expectedTotal,
|
|
673
734
|
cacheReadTokens,
|
|
674
735
|
cacheCreationTokens,
|
|
736
|
+
...projectUsagePricingContext(raw),
|
|
675
737
|
};
|
|
676
738
|
}
|
|
677
739
|
|
|
@@ -1101,6 +1163,7 @@ export function createVerifiedSessionObservabilityProjection(
|
|
|
1101
1163
|
}),
|
|
1102
1164
|
);
|
|
1103
1165
|
addUsageSums(row, usage, "session usage model");
|
|
1166
|
+
mergeUsagePricingBucket(row, usage);
|
|
1104
1167
|
}
|
|
1105
1168
|
|
|
1106
1169
|
if (event?.type === "tool_call") {
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import {
|
|
2
|
+
buildExtractiveHandoff,
|
|
3
|
+
formatStructuredHandoff,
|
|
4
|
+
} from "../harness/structured-handoff.js";
|
|
5
|
+
import {
|
|
6
|
+
DURABLE_SYSTEM_MESSAGE_KINDS,
|
|
7
|
+
getDurableSystemMessageProvenance,
|
|
8
|
+
markDurableSystemMessage,
|
|
9
|
+
projectCanonicalResumeMessages,
|
|
10
|
+
} from "./session-message-provenance.js";
|
|
11
|
+
|
|
12
|
+
export const HISTORY_SUMMARY_SCHEMA =
|
|
13
|
+
"chainlesschain.session-history-summary/v1";
|
|
14
|
+
|
|
15
|
+
function summaryMessage(messages, action, turnId) {
|
|
16
|
+
const handoff = buildExtractiveHandoff(messages, {
|
|
17
|
+
maxFallbackSourceChars: 24_000,
|
|
18
|
+
});
|
|
19
|
+
return markDurableSystemMessage(
|
|
20
|
+
{
|
|
21
|
+
role: "system",
|
|
22
|
+
content:
|
|
23
|
+
`[Conversation Summary: ${action} ${turnId}]\n` +
|
|
24
|
+
formatStructuredHandoff(handoff),
|
|
25
|
+
},
|
|
26
|
+
DURABLE_SYSTEM_MESSAGE_KINDS.CHECKPOINT_SUMMARY,
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Deterministic v1 rewrite shared by the planner and verified history reader.
|
|
32
|
+
* Source indexes describe the conservative dependency of each output, not a
|
|
33
|
+
* claim that the bounded extractive summary quotes every source. Changes to
|
|
34
|
+
* this transform/formatter require a new history schema or a v1 reader.
|
|
35
|
+
*/
|
|
36
|
+
export function projectCheckpointSummary({
|
|
37
|
+
messages,
|
|
38
|
+
action,
|
|
39
|
+
turnId,
|
|
40
|
+
start,
|
|
41
|
+
end,
|
|
42
|
+
}) {
|
|
43
|
+
if (
|
|
44
|
+
!Array.isArray(messages) ||
|
|
45
|
+
typeof turnId !== "string" ||
|
|
46
|
+
!turnId ||
|
|
47
|
+
!Number.isSafeInteger(start) ||
|
|
48
|
+
!Number.isSafeInteger(end) ||
|
|
49
|
+
start < 0 ||
|
|
50
|
+
start >= end ||
|
|
51
|
+
end > messages.length ||
|
|
52
|
+
!["summary-from", "summary-to"].includes(action)
|
|
53
|
+
)
|
|
54
|
+
throw new TypeError("Invalid checkpoint summary source range");
|
|
55
|
+
const copied = (begin, finish) =>
|
|
56
|
+
messages.slice(begin, finish).map((message, index) => ({
|
|
57
|
+
message,
|
|
58
|
+
sourceIndexes: [begin + index],
|
|
59
|
+
}));
|
|
60
|
+
const summarized = (entries) => ({
|
|
61
|
+
message: summaryMessage(
|
|
62
|
+
entries.map((entry) => entry.message),
|
|
63
|
+
action,
|
|
64
|
+
turnId,
|
|
65
|
+
),
|
|
66
|
+
sourceIndexes: entries.flatMap((entry) => entry.sourceIndexes),
|
|
67
|
+
});
|
|
68
|
+
if (action === "summary-from") {
|
|
69
|
+
if (end !== messages.length || messages[start]?.role !== "user")
|
|
70
|
+
throw new TypeError("Invalid checkpoint summary suffix");
|
|
71
|
+
return [...copied(0, start), summarized(copied(start, end))];
|
|
72
|
+
}
|
|
73
|
+
if (
|
|
74
|
+
start !== (messages[0]?.role === "system" ? 1 : 0) ||
|
|
75
|
+
(end < messages.length && messages[end]?.role !== "user")
|
|
76
|
+
)
|
|
77
|
+
throw new TypeError("Invalid checkpoint summary prefix");
|
|
78
|
+
|
|
79
|
+
const prefix = messages.slice(start, end);
|
|
80
|
+
// Validate/sanitize before inspecting roles. Unmarked systems must neither
|
|
81
|
+
// survive the rewrite nor be quoted into a newly authorized summary.
|
|
82
|
+
const canonical = projectCanonicalResumeMessages(prefix, { strict: true });
|
|
83
|
+
const indexes = prefix.flatMap((message, index) =>
|
|
84
|
+
message.role !== "system" || getDurableSystemMessageProvenance(message)
|
|
85
|
+
? [start + index]
|
|
86
|
+
: [],
|
|
87
|
+
);
|
|
88
|
+
const entries = canonical.map((message, index) => ({
|
|
89
|
+
message,
|
|
90
|
+
sourceIndexes: [indexes[index]],
|
|
91
|
+
}));
|
|
92
|
+
return [
|
|
93
|
+
...copied(0, start),
|
|
94
|
+
...entries.filter((entry) => entry.message.role === "system"),
|
|
95
|
+
summarized(entries.filter((entry) => entry.message.role !== "system")),
|
|
96
|
+
...copied(end, messages.length),
|
|
97
|
+
];
|
|
98
|
+
}
|