chainlesschain 0.162.95 → 0.162.97
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 +1 -1
- package/bin/chainlesschain.js +8 -18
- package/package.json +1 -1
- package/src/assets/web-panel/assets/{AIOps-DLvvkb9x.js → AIOps-C1YvIYh5.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-BxhQTwqd.js → ActionButton-jqcXEakj.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-C4oUXzhG.js → Analytics-C8Xwqd3_.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-CjzKJdv3.js → AppLayout-Dx5ypTfA.js} +5 -5
- package/src/assets/web-panel/assets/{Audit-B-MrugUs.js → Audit-1GZ4Tv2f.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-ecIfEpIZ.js → Backup-CexR2hyk.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-BUcSfWnx.js → BaseInput-DwAruKdy.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-DIc1n_Lk.js → Chat-W2FkU5BY.js} +6 -6
- package/src/assets/web-panel/assets/{ChatBubbleRenderer-BGzg7oVA.js → ChatBubbleRenderer-BCLnhLfu.js} +1 -1
- package/src/assets/web-panel/assets/{Checkbox-BQ3Ueff-.js → Checkbox-Dkh5JY5B.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-CvSGLnqE.js → Codegen-LfYOgsdA.js} +1 -1
- package/src/assets/web-panel/assets/{Col-Br9IXZcE.js → Col-DEgketAC.js} +1 -1
- package/src/assets/web-panel/assets/{Community-tPuLOf0r.js → Community-LeFQMaZm.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-vMWH73BE.js → Compact-CrlKYvBc.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-Ct9z699x.js → Compliance-ca_4PGYF.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-Bxs1kL1y.js → Cowork-BcMWqh2a.js} +3 -3
- package/src/assets/web-panel/assets/{Cron-QEpr2k4U.js → Cron-pVVEA3dw.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-u4xbUw6O.js → Crosschain-CiKSGTqp.js} +1 -1
- package/src/assets/web-panel/assets/{DID-DXTEvyrW.js → DID-B2bGzdOj.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-C4pZgzkg.js → Dashboard-sQ6jcgCn.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-LRuqREGe.js → Dropdown-rFx_8P04.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-D7XI1yBM.js → EmailListRenderer-smw2hh6f.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-3y_SuAMQ.js → FamilyGuardDashboard-CZhg2_S2.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-DbjbJJCW.js → Federation-DQE-368G.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-WPpMZcUw.js → FormItemContext-2XPb9izr.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-nNT9Tn4E.js → GenericCardRenderer-DCGMz5E6.js} +1 -1
- package/src/assets/web-panel/assets/{Git-DqDaVzwV.js → Git-BFwrxK4Q.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-BquBZpcg.js → Governance-ChxBRwG1.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-RcPPYijs.js → Inference-C7kyknu_.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-ByXfZV13.js → KnowledgeGraph-BsMd4XAt.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-CHT3DUcq.js → Logs-Bs3yrpow.js} +2 -2
- package/src/assets/web-panel/assets/{Marketplace-Bi0HJ9pN.js → Marketplace-Cc29jdBv.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-QCBxPRRc.js → McpTools-zYpYsUsl.js} +4 -4
- package/src/assets/web-panel/assets/{Memory-D4RlxGXn.js → Memory-x9r4yKtS.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-CU_PV7e-.js → MobileBridge-CjUo8prx.js} +2 -2
- package/src/assets/web-panel/assets/{MobileProjects-c1CMFD8z.js → MobileProjects-Cmz4wVrF.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-BezDB1-s.js → Mtc-Y-SBnNxm.js} +5 -5
- package/src/assets/web-panel/assets/{MtcAudit-CgLpiHpE.js → MtcAudit-ogiKnrxj.js} +2 -2
- package/src/assets/web-panel/assets/{Multisig-vx6bz-bA.js → Multisig-C1hygkFR.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-CGOagg3-.js → NLProgramming-fWWYiTsY.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-BcuDGYRS.js → Notes-CkCNF4gC.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-ZAGCDUpc.js → NotificationSettings-DThB7WQG.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-B5onE0eX.js → OrderTableRenderer-Dh64aMq6.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-y_y20-5Y.js → Organization-BSY16zvP.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-BEICS31D.js → Overflow-C_TDh18K.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-D_WRMzh4.js → P2P-CvxZk6og.js} +2 -2
- package/src/assets/web-panel/assets/PdhVaultBrowser-0JZ5JF4M.js +7 -0
- package/src/assets/web-panel/assets/{Permissions-3aqCc_4f.js → Permissions-DV8ubpjM.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-DT3R9g9Y.js → PersonalDataHub-g2Dgls8e.js} +2 -2
- package/src/assets/web-panel/assets/{Pipeline-MiMWIhw0.js → Pipeline-DGp8zQ0C.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-C3JsLuDa.js → Privacy-C0uHrjWR.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-EhHVi1ff.js → ProjectInit-2XGICaFt.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-D_dNEA37.js → ProjectSettings-Cfy72e0Q.js} +2 -2
- package/src/assets/web-panel/assets/{Projects-CXydUXp8.js → Projects-Xsvk_kKT.js} +1 -1
- package/src/assets/web-panel/assets/{Providers-CPEhi2iZ.js → Providers-vuvDVg7U.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-BFQpMleX.js → QuickAsk-BUy7kPyQ.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-DzbovRC8.js → Recommend-DlzpC_JE.js} +1 -1
- package/src/assets/web-panel/assets/{Reputation-DoBeV3TD.js → Reputation-BiWFwPMh.js} +1 -1
- package/src/assets/web-panel/assets/{Row-DthhwX_B.js → Row-dmfZBK-k.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-Dsf5U0lP.js → RssFeed-Cf6tlRKq.js} +2 -2
- package/src/assets/web-panel/assets/{Search-CJPB8kKl.js → Search-DhzXDRAJ.js} +1 -1
- package/src/assets/web-panel/assets/{Security-DI9MfpdD.js → Security-C-bTfgeJ.js} +3 -3
- package/src/assets/web-panel/assets/{Services-BdG0TFGf.js → Services-D3KNwBqu.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-DSYazY_B.js → Skeleton-CHH8i1xY.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-BD309VTT.js → Skills-DHiONX0z.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-CaUlw4_6.js → Sla-DwWMb-Ui.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-CZAPkR7H.js → SpeechSettings-DAq64iWB.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-CFRSowN3.js → SyncSettings-DJU-kHko.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-Di8EOluc.js → Tasks-RAIzA3u-.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-B5FTziwi.js → Templates-By4cbadT.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-Dj3XYfBg.js → Tenant-kCAXsxWu.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-CznH_rJT.js → Terminal-D8pup_CO.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-DTHzaXPt.js → TimelineRenderer-Bk2c9TUI.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-Bo56nuML.js → Tokens-B0wTiZRa.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-pyVUVBwb.js → Trigger-Ca_9t0gk.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-Cp7jMkKN.js → Trust-BnfNDDDm.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-aPcZCdpz.js → UkeySign-fnlF2-Yr.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-DAv5j--w.js → VideoEditing-Cfshhzyw.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-DRhK7IDR.js → Wallet-BWMpDOLG.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-B2yQJuq8.js → WebAuthn-B6xeXZvw.js} +3 -3
- package/src/assets/web-panel/assets/{WorkflowEditor-D98DhR54.js → WorkflowEditor-Cb3-FPCE.js} +1 -1
- package/src/assets/web-panel/assets/{chat-BXrRNi8C.js → chat-CP_opuMK.js} +1 -1
- package/src/assets/web-panel/assets/{colors-jQ94T_qn.js → colors-RLzfjxRQ.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-KqUJLlF2.js → compact-item-BTRloNKV.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-DxM3zuQW.js → createContext-gXgXTLlU.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-CYlmApo_.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-BGm946KV.js → hasIn-CrIpxkRC.js} +1 -1
- package/src/assets/web-panel/assets/{index-CbQvgx2e.js → index-AsI1SqnE.js} +1 -1
- package/src/assets/web-panel/assets/{index-C5NQai7O.js → index-B3L5vTSs.js} +1 -1
- package/src/assets/web-panel/assets/{index-Crw76vIF.js → index-B5sN_pwd.js} +1 -1
- package/src/assets/web-panel/assets/{index-1P0sXNhG.js → index-BEcRPNHv.js} +1 -1
- package/src/assets/web-panel/assets/{index-Ce-YoL4x.js → index-BVrcoJe2.js} +1 -1
- package/src/assets/web-panel/assets/{index-BaU_fSK7.js → index-BewxG36A.js} +1 -1
- package/src/assets/web-panel/assets/{index-BGjCjsf1.js → index-Bi87H9yk.js} +1 -1
- package/src/assets/web-panel/assets/{index-DZpVYD4j.js → index-Bl4OQ--4.js} +1 -1
- package/src/assets/web-panel/assets/{index-yavsyHif.js → index-BlE2lCS-.js} +1 -1
- package/src/assets/web-panel/assets/{index-B4b7KjRq.js → index-BlS91Ac0.js} +1 -1
- package/src/assets/web-panel/assets/{index-BbS0cBU9.js → index-Bm0cNeeQ.js} +1 -1
- package/src/assets/web-panel/assets/{index-LWTZAud0.js → index-Bo2Fl2j7.js} +1 -1
- package/src/assets/web-panel/assets/{index-DZMzj4ay.js → index-Bsz3X-aU.js} +1 -1
- package/src/assets/web-panel/assets/{index-DOYcemym.js → index-BurvDWMS.js} +1 -1
- package/src/assets/web-panel/assets/{index-T4UJiTi3.js → index-BzBLtthb.js} +1 -1
- package/src/assets/web-panel/assets/{index-C7Z2m28C.js → index-C0wmEN2j.js} +1 -1
- package/src/assets/web-panel/assets/{index-DSzb9VOG.js → index-C4bBR-Vd.js} +1 -1
- package/src/assets/web-panel/assets/{index-CfE3jxrT.js → index-COhACL0I.js} +1 -1
- package/src/assets/web-panel/assets/{index-FSYz5aww.js → index-CQ-cnhZM.js} +1 -1
- package/src/assets/web-panel/assets/{index-BXMZLfd8.js → index-CRr99L7q.js} +1 -1
- package/src/assets/web-panel/assets/{index-DWcd5zDG.js → index-CaTkUkJp.js} +3 -3
- package/src/assets/web-panel/assets/{index-D1VWL6Lc.js → index-CfOjPxso.js} +1 -1
- package/src/assets/web-panel/assets/index-Cf_SLgtj.js +1 -0
- package/src/assets/web-panel/assets/{index-mZNLT2SP.js → index-Ch_TYQNW.js} +1 -1
- package/src/assets/web-panel/assets/{index-B-2HpfWo.js → index-CisGGaF3.js} +1 -1
- package/src/assets/web-panel/assets/{index-CghRL1dh.js → index-DA-_2qTs.js} +1 -1
- package/src/assets/web-panel/assets/index-DYBIDVGn.js +1 -0
- package/src/assets/web-panel/assets/{index-Dgzw6eKr.js → index-DZxK7MW9.js} +1 -1
- package/src/assets/web-panel/assets/{index-CreXAr5Y.js → index-DiXnGU0A.js} +1 -1
- package/src/assets/web-panel/assets/{index-aOLXuHpG.js → index-DsiyfyBE.js} +1 -1
- package/src/assets/web-panel/assets/{index-DboUBf6R.js → index-G_OgtHiC.js} +1 -1
- package/src/assets/web-panel/assets/{index-BnEZfi5D.js → index-IjV-dnE8.js} +1 -1
- package/src/assets/web-panel/assets/{index-BAPk6ntc.js → index-Mltfj22U.js} +1 -1
- package/src/assets/web-panel/assets/{index-DTUs9t5F.js → index-U-kmEJdu.js} +1 -1
- package/src/assets/web-panel/assets/{index-BMp41Q1Y.js → index-cgf-OVry.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cz_SFSix.js → index-ch8zcf-h.js} +1 -1
- package/src/assets/web-panel/assets/{index-DRzsaWQy.js → index-cqOT5RiJ.js} +1 -1
- package/src/assets/web-panel/assets/{index-C7sBLech.js → index-cywkZJ-I.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dib0FsTT.js → index-sE_I7gVL.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-BDoZJI7g.js → initDefaultProps-DMwnVZzH.js} +1 -1
- package/src/assets/web-panel/assets/{motion-sceLjgp-.js → motion-Dnpjpvo7.js} +1 -1
- package/src/assets/web-panel/assets/{move-CTWQpFa7.js → move-iuRh5U9Y.js} +1 -1
- package/src/assets/web-panel/assets/{omit-dr-NCrHZ.js → omit-Bl4WRV59.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-DK-GX_Z2.js → pickAttrs-B9KSXaep.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-BIPf--18.js → placementArrow-TEGIow4r.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-BzrWT5tV.js → responsiveObserve--BECM92v.js} +1 -1
- package/src/assets/web-panel/assets/{slide-D5XhxlET.js → slide-Ca8G7XbL.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-6DI-k-9E.js → statusUtils-8QPG_T3D.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-DMw-eyn9.js → styleChecker-p3FsA6mg.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-D4oVdliM.js → useFlexGapSupport-Ct0yIzrp.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-D3PPZnvy.js → useFs-3nlByl_3.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-Gh5ba4KP.js → usePersonalDataHub-N3OsoSRM.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-CVEM1u4x.js → vnode-Cfe3ywq3.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-ClT_-9RZ.js → zoom-DVBuDS_3.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/commands/activitypub.js +4 -3
- package/src/commands/agent.js +25 -0
- package/src/commands/agents.js +18 -4
- package/src/commands/ask.js +13 -0
- package/src/commands/command.js +12 -2
- package/src/commands/config.js +17 -1
- package/src/commands/cost.js +6 -1
- package/src/commands/dao.js +4 -3
- package/src/commands/dlp.js +2 -1
- package/src/commands/hub.js +138 -0
- package/src/commands/mtc.js +37 -1
- package/src/gateways/discord/discord-formatter.js +18 -1
- package/src/gateways/telegram/telegram-formatter.js +6 -3
- package/src/gateways/ws/ws-server.js +19 -1
- package/src/harness/plugin-manager.js +20 -0
- package/src/harness/worktree-isolator.js +17 -1
- package/src/index.js +2 -0
- package/src/lib/__tests__/model-deprecation.test.js +194 -0
- package/src/lib/a2a-protocol.js +24 -9
- package/src/lib/agent-core.js +4 -5
- package/src/lib/audit-mtc.js +40 -2
- package/src/lib/chat-core.js +146 -18
- package/src/lib/cli-anything-bridge.js +5 -0
- package/src/lib/cli-numeric.js +38 -0
- package/src/lib/config-manager.js +37 -6
- package/src/lib/cowork/ab-comparator-cli.js +6 -2
- package/src/lib/cowork-cron.js +45 -34
- package/src/lib/cowork-learning.js +31 -1
- package/src/lib/cowork-share.js +39 -3
- package/src/lib/cowork-template-marketplace.js +54 -2
- package/src/lib/cowork-workflow.js +33 -6
- package/src/lib/cross-chain-mtc.js +41 -12
- package/src/lib/crypto-manager.js +12 -2
- package/src/lib/dbevo.js +19 -8
- package/src/lib/downloader.js +24 -9
- package/src/lib/evomap-governance.js +4 -4
- package/src/lib/evomap-manager.js +54 -7
- package/src/lib/fatal-handler.js +48 -0
- package/src/lib/file-checkpoint.js +26 -0
- package/src/lib/git-integration.js +28 -0
- package/src/lib/goal-store.js +36 -10
- package/src/lib/hierarchical-memory.js +6 -0
- package/src/lib/llm-pricing.js +52 -2
- package/src/lib/mcp-oauth.js +37 -7
- package/src/lib/memory-manager.js +23 -2
- package/src/lib/model-deprecation.js +249 -0
- package/src/lib/pdh-feedback-ledger.js +131 -0
- package/src/lib/permanent-memory.js +30 -2
- package/src/lib/permission-rules.cjs +11 -3
- package/src/lib/personal-data-hub-aichat-wizard.js +33 -3
- package/src/lib/personal-data-hub-wiring.js +39 -16
- package/src/lib/project-instructions.js +10 -4
- package/src/lib/runnable-provider.js +62 -31
- package/src/lib/session-usage.js +34 -1
- package/src/lib/skill-loader.js +4 -1
- package/src/lib/skill-packs/generator.js +25 -2
- package/src/lib/stream-retry.js +56 -0
- package/src/lib/sync-credentials.js +53 -17
- package/src/lib/token-tracker.js +12 -2
- package/src/lib/with-file-lock.js +87 -0
- package/src/repl/agent-repl.js +47 -7
- package/src/repl/chat-repl.js +19 -0
- package/src/repl/session-cost.js +31 -3
- package/src/runtime/agent-core.js +478 -53
- package/src/runtime/coding-agent-contract-shared.cjs +58 -1
- package/src/runtime/coding-agent-policy.cjs +10 -0
- package/src/runtime/file-ref-expander.js +29 -1
- package/src/runtime/headless-runner.js +12 -1
- package/src/runtime/headless-stream.js +200 -9
- package/src/skills/video-editing/tools/commit.js +15 -1
- package/src/assets/web-panel/assets/PdhVaultBrowser-C8f5NbbI.js +0 -7
- package/src/assets/web-panel/assets/devWarning-Bo1r5_zX.js +0 -1
- package/src/assets/web-panel/assets/index-BCU6dVQ-.js +0 -1
- package/src/assets/web-panel/assets/index-N_oOFphx.js +0 -1
package/src/lib/a2a-protocol.js
CHANGED
|
@@ -5,6 +5,21 @@
|
|
|
5
5
|
* capability negotiation, and peer discovery.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
+
|
|
9
|
+
// Safe JSON-array parse for values read back from the DB (capabilities / skills /
|
|
10
|
+
// history / artifacts are stored as JSON strings). Written via JSON.stringify so
|
|
11
|
+
// normally valid, but a corrupted or externally-edited DB must not crash agent
|
|
12
|
+
// discovery / task lifecycle with an uncaught SyntaxError — fall back to [].
|
|
13
|
+
function parseJsonArray(value) {
|
|
14
|
+
if (value == null || value === "") return [];
|
|
15
|
+
try {
|
|
16
|
+
const parsed = JSON.parse(value);
|
|
17
|
+
return Array.isArray(parsed) ? parsed : [];
|
|
18
|
+
} catch {
|
|
19
|
+
return [];
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
8
23
|
// ─── Task statuses ───────────────────────────────────────────────
|
|
9
24
|
export const TASK_STATUS = {
|
|
10
25
|
SUBMITTED: "submitted",
|
|
@@ -165,8 +180,8 @@ export function discoverAgents(db, filter = {}) {
|
|
|
165
180
|
// Parse JSON fields
|
|
166
181
|
rows = rows.map((r) => ({
|
|
167
182
|
...r,
|
|
168
|
-
capabilities:
|
|
169
|
-
skills:
|
|
183
|
+
capabilities: parseJsonArray(r.capabilities),
|
|
184
|
+
skills: parseJsonArray(r.skills),
|
|
170
185
|
}));
|
|
171
186
|
|
|
172
187
|
if (filter.capability) {
|
|
@@ -220,7 +235,7 @@ export function completeTask(db, taskId, output, artifacts = []) {
|
|
|
220
235
|
|
|
221
236
|
const now = nowISO();
|
|
222
237
|
const task = _getTask(db, taskId);
|
|
223
|
-
const history =
|
|
238
|
+
const history = parseJsonArray(task.history);
|
|
224
239
|
history.push({ status: TASK_STATUS.COMPLETED, timestamp: now });
|
|
225
240
|
|
|
226
241
|
db.prepare(
|
|
@@ -249,7 +264,7 @@ export function failTask(db, taskId, error) {
|
|
|
249
264
|
|
|
250
265
|
const now = nowISO();
|
|
251
266
|
const task = _getTask(db, taskId);
|
|
252
|
-
const history =
|
|
267
|
+
const history = parseJsonArray(task.history);
|
|
253
268
|
history.push({ status: TASK_STATUS.FAILED, timestamp: now });
|
|
254
269
|
|
|
255
270
|
db.prepare(
|
|
@@ -276,8 +291,8 @@ export function getTaskStatus(db, taskId) {
|
|
|
276
291
|
const task = _getTask(db, taskId);
|
|
277
292
|
return {
|
|
278
293
|
...task,
|
|
279
|
-
history:
|
|
280
|
-
artifacts:
|
|
294
|
+
history: parseJsonArray(task.history),
|
|
295
|
+
artifacts: parseJsonArray(task.artifacts),
|
|
281
296
|
};
|
|
282
297
|
}
|
|
283
298
|
|
|
@@ -305,7 +320,7 @@ export function negotiateCapability(db, agentId, requiredCapabilities) {
|
|
|
305
320
|
.get(agentId);
|
|
306
321
|
if (!card) throw new Error(`Agent not found: ${agentId}`);
|
|
307
322
|
|
|
308
|
-
const agentCaps =
|
|
323
|
+
const agentCaps = parseJsonArray(card.capabilities);
|
|
309
324
|
const supported = requiredCapabilities.filter((c) => agentCaps.includes(c));
|
|
310
325
|
const missing = requiredCapabilities.filter((c) => !agentCaps.includes(c));
|
|
311
326
|
|
|
@@ -329,8 +344,8 @@ export function listPeers(db) {
|
|
|
329
344
|
.all();
|
|
330
345
|
return rows.map((r) => ({
|
|
331
346
|
...r,
|
|
332
|
-
capabilities:
|
|
333
|
-
skills:
|
|
347
|
+
capabilities: parseJsonArray(r.capabilities),
|
|
348
|
+
skills: parseJsonArray(r.skills),
|
|
334
349
|
}));
|
|
335
350
|
}
|
|
336
351
|
|
package/src/lib/agent-core.js
CHANGED
|
@@ -1,8 +1,4 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @deprecated Re-export shim; canonical impl is `../runtime/agent-core.js`
|
|
3
|
-
* (CLI Runtime Convergence, Phase 6b). Import from there in new code.
|
|
4
|
-
*/
|
|
5
|
-
|
|
1
|
+
/** @deprecated Thin re-export shim; canonical impl is `../runtime/agent-core.js` (CLI Runtime Convergence, Phase 6b) — import from there in new code. */
|
|
6
2
|
export {
|
|
7
3
|
AGENT_TOOLS,
|
|
8
4
|
AGENT_TOOL_REGISTRY,
|
|
@@ -16,6 +12,8 @@ export {
|
|
|
16
12
|
buildSystemPrompt,
|
|
17
13
|
executeTool,
|
|
18
14
|
computeProposedEdit,
|
|
15
|
+
editNotebookCell,
|
|
16
|
+
renderNotebook,
|
|
19
17
|
classifyError,
|
|
20
18
|
isValidPackageName,
|
|
21
19
|
chatWithTools,
|
|
@@ -33,6 +31,7 @@ export {
|
|
|
33
31
|
_streamErrorDisposition,
|
|
34
32
|
_isRetryableStreamError,
|
|
35
33
|
_retryStreamingChat,
|
|
34
|
+
_iterateStreamWithStall,
|
|
36
35
|
_toAnthropicMessages,
|
|
37
36
|
_anthropicThinkingParams,
|
|
38
37
|
_normalizeAnthropicResponse,
|
package/src/lib/audit-mtc.js
CHANGED
|
@@ -22,6 +22,7 @@ import path from "node:path";
|
|
|
22
22
|
import crypto from "node:crypto";
|
|
23
23
|
import { ed25519 as nobleEd25519 } from "@noble/curves/ed25519.js";
|
|
24
24
|
import mtcLib from "@chainlesschain/core-mtc";
|
|
25
|
+
import { withFileLock } from "./with-file-lock.js";
|
|
25
26
|
|
|
26
27
|
const { sha256, jcs, encodeHashStr, ed25519, assembleBatch } = mtcLib;
|
|
27
28
|
|
|
@@ -70,6 +71,30 @@ function ensureDirs(dir) {
|
|
|
70
71
|
}
|
|
71
72
|
}
|
|
72
73
|
|
|
74
|
+
/**
|
|
75
|
+
* Atomically write a UTF-8 file (temp sibling + rename). Used for the config
|
|
76
|
+
* and per-event staging records: a crash mid-write would truncate config.json
|
|
77
|
+
* or leave a partial event that closeBatch silently drops as malformed. Temp
|
|
78
|
+
* names end in `.tmp` (never `.json`), so a hard-crash leftover is ignored by
|
|
79
|
+
* listStagingEvents' `.json` filter. NOTE: the issuer key (flag "wx" exclusive
|
|
80
|
+
* create) and closeBatch (whole tmp-dir rename) have their own atomicity and
|
|
81
|
+
* are intentionally left untouched.
|
|
82
|
+
*/
|
|
83
|
+
function atomicWriteFileSync(filePath, data) {
|
|
84
|
+
const tmp = `${filePath}.${process.pid}.${Math.random().toString(36).slice(2, 8)}.tmp`;
|
|
85
|
+
try {
|
|
86
|
+
fs.writeFileSync(tmp, data, "utf-8");
|
|
87
|
+
fs.renameSync(tmp, filePath);
|
|
88
|
+
} catch (err) {
|
|
89
|
+
try {
|
|
90
|
+
if (fs.existsSync(tmp)) fs.unlinkSync(tmp);
|
|
91
|
+
} catch {
|
|
92
|
+
/* best-effort temp cleanup */
|
|
93
|
+
}
|
|
94
|
+
throw err;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
73
98
|
// ─────────────────────────────────────────────────────────────────────
|
|
74
99
|
// Config
|
|
75
100
|
// ─────────────────────────────────────────────────────────────────────
|
|
@@ -107,7 +132,7 @@ export function saveAuditMtcConfig(dir, patch) {
|
|
|
107
132
|
if (typeof merged.issuer !== "string" || !merged.issuer) {
|
|
108
133
|
throw new TypeError("config.issuer must be non-empty string");
|
|
109
134
|
}
|
|
110
|
-
|
|
135
|
+
atomicWriteFileSync(configPath(dir), JSON.stringify(merged, null, 2));
|
|
111
136
|
return merged;
|
|
112
137
|
}
|
|
113
138
|
|
|
@@ -227,7 +252,7 @@ export function emitEvent(dir, body, opts = {}) {
|
|
|
227
252
|
};
|
|
228
253
|
|
|
229
254
|
const filePath = path.join(stagingDir(dir), `${eventId}.json`);
|
|
230
|
-
|
|
255
|
+
atomicWriteFileSync(filePath, JSON.stringify(record, null, 2));
|
|
231
256
|
return { eventId, path: filePath, record };
|
|
232
257
|
}
|
|
233
258
|
|
|
@@ -293,6 +318,19 @@ function listStagingEvents(dir) {
|
|
|
293
318
|
* @param {string} [opts.issuer] - override config.issuer
|
|
294
319
|
*/
|
|
295
320
|
export function closeBatch(dir, opts = {}) {
|
|
321
|
+
// Serialize the whole staging-scan → batch-write → staging-cleanup section
|
|
322
|
+
// across processes. Without it, two concurrent closes (e.g. overlapping
|
|
323
|
+
// `cc audit mtc reconcile` runs) both scan the same staging events, allocate
|
|
324
|
+
// the same batch id, and the defensive rmSync(finalDir) lets the second
|
|
325
|
+
// silently overwrite the first's audit batch. Best-effort lock (never hangs);
|
|
326
|
+
// generous timeout since the section includes Merkle assembly + signing.
|
|
327
|
+
ensureDirs(dir);
|
|
328
|
+
return withFileLock(batchesDir(dir), () => _closeBatchImpl(dir, opts), {
|
|
329
|
+
timeoutMs: 15000,
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
function _closeBatchImpl(dir, opts = {}) {
|
|
296
334
|
ensureDirs(dir);
|
|
297
335
|
const cfg = loadAuditMtcConfig(dir);
|
|
298
336
|
const events = listStagingEvents(dir);
|
package/src/lib/chat-core.js
CHANGED
|
@@ -11,6 +11,11 @@
|
|
|
11
11
|
|
|
12
12
|
import { BUILT_IN_PROVIDERS } from "./llm-providers.js";
|
|
13
13
|
import { appendTokenUsage } from "../harness/jsonl-session-store.js";
|
|
14
|
+
import {
|
|
15
|
+
isRetryableStreamError,
|
|
16
|
+
STREAM_RETRY_MAX,
|
|
17
|
+
STREAM_RETRY_BASE_MS,
|
|
18
|
+
} from "./stream-retry.js";
|
|
14
19
|
|
|
15
20
|
// A streaming chat call must not hang forever if the API accepts the connection
|
|
16
21
|
// but then goes silent (TCP up, no bytes). Abort the request if no data arrives
|
|
@@ -19,18 +24,48 @@ import { appendTokenUsage } from "../harness/jsonl-session-store.js";
|
|
|
19
24
|
// generous (and env-overridable) so a slow local model's first token isn't cut
|
|
20
25
|
// off; raise CC_CHAT_STALL_MS if you run very large local models.
|
|
21
26
|
export const STREAM_STALL_MS = Number(process.env.CC_CHAT_STALL_MS) || 180000;
|
|
27
|
+
// Before the hard abort above, surface a "waiting for API response" hint once
|
|
28
|
+
// per silent gap (cc agent parity — agent-core STREAM_STALL_MS). Purely
|
|
29
|
+
// informational; never aborts. Env-overridable; disabled if it would exceed the
|
|
30
|
+
// abort threshold.
|
|
31
|
+
export const STREAM_STALL_HINT_MS =
|
|
32
|
+
Number(process.env.CC_CHAT_STALL_HINT_MS) || 20000;
|
|
22
33
|
|
|
23
|
-
function makeStallGuard(
|
|
34
|
+
function makeStallGuard(
|
|
35
|
+
stallMs = STREAM_STALL_MS,
|
|
36
|
+
{ onHint, hintMs = STREAM_STALL_HINT_MS } = {},
|
|
37
|
+
) {
|
|
24
38
|
const controller = new AbortController();
|
|
25
39
|
let timer = null;
|
|
40
|
+
let hintTimer = null;
|
|
41
|
+
const hintArmed =
|
|
42
|
+
typeof onHint === "function" && hintMs > 0 && hintMs < stallMs;
|
|
26
43
|
const bump = () => {
|
|
27
44
|
if (timer) clearTimeout(timer);
|
|
28
45
|
timer = setTimeout(() => controller.abort(), stallMs);
|
|
29
46
|
if (timer && typeof timer.unref === "function") timer.unref();
|
|
47
|
+
if (hintArmed) {
|
|
48
|
+
if (hintTimer) clearTimeout(hintTimer);
|
|
49
|
+
hintTimer = setTimeout(() => {
|
|
50
|
+
try {
|
|
51
|
+
// 2nd arg = the hard abort deadline (stallMs) so the hint can tell
|
|
52
|
+
// the user when the silent request will time out. Note the chat path
|
|
53
|
+
// surfaces a stall error rather than auto-retrying (see the dispatch
|
|
54
|
+
// loop in chatStream), so callers should word it "will time out",
|
|
55
|
+
// not "will retry".
|
|
56
|
+
onHint(hintMs, stallMs);
|
|
57
|
+
} catch {
|
|
58
|
+
/* hint is best-effort — never affect the stream */
|
|
59
|
+
}
|
|
60
|
+
}, hintMs);
|
|
61
|
+
if (hintTimer && typeof hintTimer.unref === "function") hintTimer.unref();
|
|
62
|
+
}
|
|
30
63
|
};
|
|
31
64
|
const stop = () => {
|
|
32
65
|
if (timer) clearTimeout(timer);
|
|
66
|
+
if (hintTimer) clearTimeout(hintTimer);
|
|
33
67
|
timer = null;
|
|
68
|
+
hintTimer = null;
|
|
34
69
|
};
|
|
35
70
|
return {
|
|
36
71
|
signal: controller.signal,
|
|
@@ -40,13 +75,35 @@ function makeStallGuard(stallMs = STREAM_STALL_MS) {
|
|
|
40
75
|
};
|
|
41
76
|
}
|
|
42
77
|
|
|
78
|
+
// OpenAI-compatible cached-prompt-token count (mirrors agent-core's
|
|
79
|
+
// _openaiCachedTokens). OpenAI/volcengine report it as
|
|
80
|
+
// usage.prompt_tokens_details.cached_tokens; DeepSeek as
|
|
81
|
+
// usage.prompt_cache_hit_tokens. prompt_tokens INCLUDES the cached count, so
|
|
82
|
+
// callers subtract it to recover uncached input (avoids over-pricing in
|
|
83
|
+
// `cc cost` for cc chat sessions).
|
|
84
|
+
function _cachedPromptTokens(usage) {
|
|
85
|
+
if (!usage || typeof usage !== "object") return 0;
|
|
86
|
+
const detailed = Number(usage.prompt_tokens_details?.cached_tokens);
|
|
87
|
+
if (Number.isFinite(detailed) && detailed > 0) return detailed;
|
|
88
|
+
const deepseek = Number(usage.prompt_cache_hit_tokens);
|
|
89
|
+
if (Number.isFinite(deepseek) && deepseek > 0) return deepseek;
|
|
90
|
+
return 0;
|
|
91
|
+
}
|
|
92
|
+
|
|
43
93
|
/**
|
|
44
94
|
* Stream a response from Ollama.
|
|
45
95
|
* If `onUsage` is provided, it's called with `{inputTokens, outputTokens}`
|
|
46
96
|
* derived from Ollama's terminal `prompt_eval_count` / `eval_count` fields.
|
|
47
97
|
*/
|
|
48
|
-
export async function streamOllama(
|
|
49
|
-
|
|
98
|
+
export async function streamOllama(
|
|
99
|
+
messages,
|
|
100
|
+
model,
|
|
101
|
+
baseUrl,
|
|
102
|
+
onToken,
|
|
103
|
+
onUsage,
|
|
104
|
+
onStall,
|
|
105
|
+
) {
|
|
106
|
+
const guard = makeStallGuard(STREAM_STALL_MS, { onHint: onStall });
|
|
50
107
|
guard.bump();
|
|
51
108
|
let response;
|
|
52
109
|
try {
|
|
@@ -129,8 +186,9 @@ export async function streamOpenAI(
|
|
|
129
186
|
apiKey,
|
|
130
187
|
onToken,
|
|
131
188
|
onUsage,
|
|
189
|
+
onStall,
|
|
132
190
|
) {
|
|
133
|
-
const guard = makeStallGuard();
|
|
191
|
+
const guard = makeStallGuard(STREAM_STALL_MS, { onHint: onStall });
|
|
134
192
|
guard.bump();
|
|
135
193
|
let response;
|
|
136
194
|
try {
|
|
@@ -189,10 +247,14 @@ export async function streamOpenAI(
|
|
|
189
247
|
onToken(content);
|
|
190
248
|
}
|
|
191
249
|
if (json.usage && onUsage) {
|
|
192
|
-
|
|
250
|
+
// Split cached prompt tokens out of prompt_tokens so cc cost
|
|
251
|
+
// prices the cached prefix correctly (matches agent-core).
|
|
252
|
+
const cacheReadTokens = _cachedPromptTokens(json.usage);
|
|
253
|
+
const prompt = Number(json.usage.prompt_tokens) || 0;
|
|
254
|
+
const inputTokens = Math.max(0, prompt - cacheReadTokens);
|
|
193
255
|
const outputTokens = Number(json.usage.completion_tokens) || 0;
|
|
194
|
-
if (inputTokens || outputTokens) {
|
|
195
|
-
onUsage({ inputTokens, outputTokens });
|
|
256
|
+
if (inputTokens || outputTokens || cacheReadTokens) {
|
|
257
|
+
onUsage({ inputTokens, outputTokens, cacheReadTokens });
|
|
196
258
|
}
|
|
197
259
|
}
|
|
198
260
|
} catch {
|
|
@@ -224,6 +286,7 @@ export async function streamAnthropic(
|
|
|
224
286
|
apiKey,
|
|
225
287
|
onToken,
|
|
226
288
|
onUsage,
|
|
289
|
+
onStall,
|
|
227
290
|
) {
|
|
228
291
|
// Split out a leading system prompt (Anthropic requires it as top-level
|
|
229
292
|
// `system`, not an OpenAI-style role=system message).
|
|
@@ -237,7 +300,7 @@ export async function streamAnthropic(
|
|
|
237
300
|
}
|
|
238
301
|
}
|
|
239
302
|
|
|
240
|
-
const guard = makeStallGuard();
|
|
303
|
+
const guard = makeStallGuard(STREAM_STALL_MS, { onHint: onStall });
|
|
241
304
|
guard.bump();
|
|
242
305
|
let response;
|
|
243
306
|
try {
|
|
@@ -279,6 +342,8 @@ export async function streamAnthropic(
|
|
|
279
342
|
let buf = "";
|
|
280
343
|
let inputTokens = 0;
|
|
281
344
|
let outputTokens = 0;
|
|
345
|
+
let cacheReadTokens = 0;
|
|
346
|
+
let cacheCreationTokens = 0;
|
|
282
347
|
|
|
283
348
|
try {
|
|
284
349
|
while (true) {
|
|
@@ -302,10 +367,13 @@ export async function streamAnthropic(
|
|
|
302
367
|
onToken(delta);
|
|
303
368
|
}
|
|
304
369
|
} else if (obj.type === "message_start") {
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
outputTokens =
|
|
308
|
-
|
|
370
|
+
const u = obj.message?.usage || {};
|
|
371
|
+
inputTokens = Number(u.input_tokens) || inputTokens;
|
|
372
|
+
outputTokens = Number(u.output_tokens) || outputTokens;
|
|
373
|
+
cacheReadTokens =
|
|
374
|
+
Number(u.cache_read_input_tokens) || cacheReadTokens;
|
|
375
|
+
cacheCreationTokens =
|
|
376
|
+
Number(u.cache_creation_input_tokens) || cacheCreationTokens;
|
|
309
377
|
} else if (obj.type === "message_delta") {
|
|
310
378
|
outputTokens = Number(obj.usage?.output_tokens) || outputTokens;
|
|
311
379
|
}
|
|
@@ -324,8 +392,16 @@ export async function streamAnthropic(
|
|
|
324
392
|
guard.stop();
|
|
325
393
|
}
|
|
326
394
|
|
|
327
|
-
if (
|
|
328
|
-
onUsage
|
|
395
|
+
if (
|
|
396
|
+
onUsage &&
|
|
397
|
+
(inputTokens || outputTokens || cacheReadTokens || cacheCreationTokens)
|
|
398
|
+
) {
|
|
399
|
+
onUsage({
|
|
400
|
+
inputTokens,
|
|
401
|
+
outputTokens,
|
|
402
|
+
cacheReadTokens,
|
|
403
|
+
cacheCreationTokens,
|
|
404
|
+
});
|
|
329
405
|
}
|
|
330
406
|
|
|
331
407
|
return fullResponse;
|
|
@@ -362,7 +438,12 @@ export async function* chatStream(messages, options) {
|
|
|
362
438
|
waiter = null;
|
|
363
439
|
w();
|
|
364
440
|
};
|
|
441
|
+
// Count of tokens the provider has actually emitted this turn. A connection
|
|
442
|
+
// drop is only safe to retry while this is 0 — once any token has been
|
|
443
|
+
// streamed, re-issuing would duplicate visible output.
|
|
444
|
+
let tokensEmitted = 0;
|
|
365
445
|
const onToken = (token) => {
|
|
446
|
+
tokensEmitted++;
|
|
366
447
|
queue.push(token);
|
|
367
448
|
wake();
|
|
368
449
|
};
|
|
@@ -376,7 +457,7 @@ export async function* chatStream(messages, options) {
|
|
|
376
457
|
let providerCall;
|
|
377
458
|
if (provider === "ollama") {
|
|
378
459
|
providerCall = () =>
|
|
379
|
-
streamOllama(messages, model, baseUrl, onToken, onUsage);
|
|
460
|
+
streamOllama(messages, model, baseUrl, onToken, onUsage, options.onStall);
|
|
380
461
|
} else if (provider === "anthropic") {
|
|
381
462
|
const providerDef = BUILT_IN_PROVIDERS.anthropic;
|
|
382
463
|
const url =
|
|
@@ -392,7 +473,15 @@ export async function* chatStream(messages, options) {
|
|
|
392
473
|
);
|
|
393
474
|
}
|
|
394
475
|
providerCall = () =>
|
|
395
|
-
streamAnthropic(
|
|
476
|
+
streamAnthropic(
|
|
477
|
+
messages,
|
|
478
|
+
model,
|
|
479
|
+
url,
|
|
480
|
+
key,
|
|
481
|
+
onToken,
|
|
482
|
+
onUsage,
|
|
483
|
+
options.onStall,
|
|
484
|
+
);
|
|
396
485
|
} else {
|
|
397
486
|
const providerDef = BUILT_IN_PROVIDERS[provider];
|
|
398
487
|
const url =
|
|
@@ -408,7 +497,15 @@ export async function* chatStream(messages, options) {
|
|
|
408
497
|
);
|
|
409
498
|
}
|
|
410
499
|
providerCall = () =>
|
|
411
|
-
streamOpenAI(
|
|
500
|
+
streamOpenAI(
|
|
501
|
+
messages,
|
|
502
|
+
model,
|
|
503
|
+
url,
|
|
504
|
+
key,
|
|
505
|
+
onToken,
|
|
506
|
+
onUsage,
|
|
507
|
+
options.onStall,
|
|
508
|
+
);
|
|
412
509
|
}
|
|
413
510
|
|
|
414
511
|
// Run the provider stream concurrently with the queue-drain loop. finally
|
|
@@ -416,7 +513,36 @@ export async function* chatStream(messages, options) {
|
|
|
416
513
|
// any token still terminates the generator cleanly.
|
|
417
514
|
const streamPromise = (async () => {
|
|
418
515
|
try {
|
|
419
|
-
|
|
516
|
+
let attempt = 0;
|
|
517
|
+
for (;;) {
|
|
518
|
+
try {
|
|
519
|
+
return await providerCall();
|
|
520
|
+
} catch (err) {
|
|
521
|
+
// Retry ONLY a transient connection drop that hit before any token
|
|
522
|
+
// reached the user (tokensEmitted === 0) — otherwise the re-issue
|
|
523
|
+
// would duplicate the visible answer. Mirrors agent-core's
|
|
524
|
+
// zero-output retry safety (cc agent parity). Stall aborts (180s) and
|
|
525
|
+
// HTTP/auth errors are not retryable and surface immediately.
|
|
526
|
+
if (
|
|
527
|
+
attempt >= STREAM_RETRY_MAX ||
|
|
528
|
+
tokensEmitted > 0 ||
|
|
529
|
+
!isRetryableStreamError(err)
|
|
530
|
+
) {
|
|
531
|
+
throw err;
|
|
532
|
+
}
|
|
533
|
+
attempt++;
|
|
534
|
+
if (typeof options.onStreamRetry === "function") {
|
|
535
|
+
try {
|
|
536
|
+
options.onStreamRetry(attempt, err);
|
|
537
|
+
} catch {
|
|
538
|
+
/* retry notice is best-effort */
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
await new Promise((r) =>
|
|
542
|
+
setTimeout(r, STREAM_RETRY_BASE_MS * 2 ** (attempt - 1)),
|
|
543
|
+
);
|
|
544
|
+
}
|
|
545
|
+
}
|
|
420
546
|
} finally {
|
|
421
547
|
done = true;
|
|
422
548
|
wake();
|
|
@@ -451,6 +577,8 @@ export async function* chatStream(messages, options) {
|
|
|
451
577
|
usage: {
|
|
452
578
|
input_tokens: capturedUsage.inputTokens,
|
|
453
579
|
output_tokens: capturedUsage.outputTokens,
|
|
580
|
+
cache_read_input_tokens: capturedUsage.cacheReadTokens || 0,
|
|
581
|
+
cache_creation_input_tokens: capturedUsage.cacheCreationTokens || 0,
|
|
454
582
|
},
|
|
455
583
|
});
|
|
456
584
|
} catch {
|
|
@@ -258,6 +258,11 @@ module.exports = {
|
|
|
258
258
|
async execute(task, context) {
|
|
259
259
|
const input = (task?.params?.input || task?.action || "").trim();
|
|
260
260
|
if (!input) return { success: false, error: "No input provided" };
|
|
261
|
+
// Security: input is interpolated into a shell command, so reject shell
|
|
262
|
+
// metacharacters to prevent command injection from untrusted skill input.
|
|
263
|
+
if (/[;&|\`$<>()\\n\\r]/.test(input)) {
|
|
264
|
+
return { success: false, error: "Input contains unsafe shell characters; refused." };
|
|
265
|
+
}
|
|
261
266
|
try {
|
|
262
267
|
const output = execSync(\`${command} \${input}\`, {
|
|
263
268
|
encoding: "utf-8",
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* cli-numeric — validated parsing for user-supplied numeric CLI options.
|
|
3
|
+
*
|
|
4
|
+
* `parseInt`/`Number` on a bad arg yields NaN that silently corrupts whatever
|
|
5
|
+
* consumes it (a `LIMIT NaN`, a `Date.now()+NaN` deadline, a stored NaN config).
|
|
6
|
+
* This centralizes parse → validate → fall-back-or-throw so a command either
|
|
7
|
+
* fails LOUDLY with a clear message or falls back to a sane default — never
|
|
8
|
+
* threads NaN downstream.
|
|
9
|
+
*
|
|
10
|
+
* @param {*} raw the option value (string from commander, or undefined)
|
|
11
|
+
* @param {object} [opts]
|
|
12
|
+
* @param {string} [opts.name="value"] label for error messages (e.g. "--limit")
|
|
13
|
+
* @param {boolean} [opts.integer=false] parse as integer (parseInt) vs float (Number)
|
|
14
|
+
* @param {number} [opts.min] inclusive lower bound
|
|
15
|
+
* @param {number} [opts.max] inclusive upper bound
|
|
16
|
+
* @param {number} [opts.fallback] returned for missing/invalid input; when
|
|
17
|
+
* omitted, invalid input THROWS instead
|
|
18
|
+
* @returns {number}
|
|
19
|
+
*/
|
|
20
|
+
export function numericOption(raw, opts = {}) {
|
|
21
|
+
const { name = "value", integer = false, min, max } = opts;
|
|
22
|
+
const hasFallback = Object.prototype.hasOwnProperty.call(opts, "fallback");
|
|
23
|
+
const fail = (msg) => {
|
|
24
|
+
if (hasFallback) return opts.fallback;
|
|
25
|
+
throw new Error(msg);
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
if (raw == null || raw === "") return fail(`${name} is required`);
|
|
29
|
+
const n = integer ? parseInt(raw, 10) : Number(raw);
|
|
30
|
+
if (!Number.isFinite(n)) {
|
|
31
|
+
return fail(
|
|
32
|
+
`${name} must be a ${integer ? "whole " : ""}number (got ${JSON.stringify(String(raw))})`,
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
if (min != null && n < min) return fail(`${name} must be >= ${min}`);
|
|
36
|
+
if (max != null && n > max) return fail(`${name} must be <= ${max}`);
|
|
37
|
+
return n;
|
|
38
|
+
}
|
|
@@ -1,7 +1,15 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
readFileSync,
|
|
3
|
+
writeFileSync,
|
|
4
|
+
existsSync,
|
|
5
|
+
mkdirSync,
|
|
6
|
+
renameSync,
|
|
7
|
+
unlinkSync,
|
|
8
|
+
} from "node:fs";
|
|
2
9
|
import { dirname } from "node:path";
|
|
3
10
|
import { getConfigPath } from "./paths.js";
|
|
4
11
|
import { DEFAULT_CONFIG } from "../constants.js";
|
|
12
|
+
import { withFileLock } from "./with-file-lock.js";
|
|
5
13
|
|
|
6
14
|
export function loadConfig() {
|
|
7
15
|
const configPath = getConfigPath();
|
|
@@ -20,7 +28,24 @@ export function loadConfig() {
|
|
|
20
28
|
export function saveConfig(config) {
|
|
21
29
|
const configPath = getConfigPath();
|
|
22
30
|
mkdirSync(dirname(configPath), { recursive: true });
|
|
23
|
-
|
|
31
|
+
// Atomic write: config.json holds API keys + LLM settings, so a crash (or two
|
|
32
|
+
// concurrent `cc config set`) mid-write must never leave a truncated/corrupt
|
|
33
|
+
// file — that would break every cc command and could lose credentials. Write a
|
|
34
|
+
// temp sibling, then rename over the target (rename is atomic within a
|
|
35
|
+
// filesystem), so a reader/crash sees either the old file or the complete new
|
|
36
|
+
// one, never a half-written one.
|
|
37
|
+
const tmp = `${configPath}.${process.pid}.${Math.random().toString(36).slice(2, 8)}.tmp`;
|
|
38
|
+
try {
|
|
39
|
+
writeFileSync(tmp, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
40
|
+
renameSync(tmp, configPath);
|
|
41
|
+
} catch (err) {
|
|
42
|
+
try {
|
|
43
|
+
if (existsSync(tmp)) unlinkSync(tmp);
|
|
44
|
+
} catch {
|
|
45
|
+
/* best-effort temp cleanup */
|
|
46
|
+
}
|
|
47
|
+
throw err;
|
|
48
|
+
}
|
|
24
49
|
}
|
|
25
50
|
|
|
26
51
|
export function getConfigValue(key) {
|
|
@@ -29,10 +54,16 @@ export function getConfigValue(key) {
|
|
|
29
54
|
}
|
|
30
55
|
|
|
31
56
|
export function setConfigValue(key, value) {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
57
|
+
// Serialize the read-modify-write across processes: two concurrent
|
|
58
|
+
// `cc config set` invocations would otherwise each load, mutate, and write
|
|
59
|
+
// back, silently losing one update. Best-effort lock (see with-file-lock):
|
|
60
|
+
// on contention timeout it proceeds anyway, so the CLI never hangs.
|
|
61
|
+
return withFileLock(getConfigPath(), () => {
|
|
62
|
+
const config = loadConfig();
|
|
63
|
+
setNestedValue(config, key, parseValue(value));
|
|
64
|
+
saveConfig(config);
|
|
65
|
+
return config;
|
|
66
|
+
});
|
|
36
67
|
}
|
|
37
68
|
|
|
38
69
|
export function resetConfig() {
|
|
@@ -160,13 +160,17 @@ REASON: (1-2 sentence justification)`;
|
|
|
160
160
|
};
|
|
161
161
|
}
|
|
162
162
|
|
|
163
|
-
function parseScores(text, variants, criteria) {
|
|
163
|
+
export function parseScores(text, variants, criteria) {
|
|
164
164
|
const scores = [];
|
|
165
165
|
for (let i = 0; i < variants.length; i++) {
|
|
166
166
|
const variantScores = {};
|
|
167
167
|
for (const c of criteria) {
|
|
168
|
+
// The criterion is matched LITERALLY — escape regex metacharacters so a
|
|
169
|
+
// name like "latency(ms)" or "f.score" doesn't silently mis-parse (and an
|
|
170
|
+
// unbalanced one like "a)b(" doesn't throw and crash the whole compare).
|
|
171
|
+
const cEsc = String(c).replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
168
172
|
const pattern = new RegExp(
|
|
169
|
-
`variant\\s*${i + 1}[^\\n]*${
|
|
173
|
+
`variant\\s*${i + 1}[^\\n]*${cEsc}\\s*=\\s*(\\d+)`,
|
|
170
174
|
"i",
|
|
171
175
|
);
|
|
172
176
|
const match = text.match(pattern);
|