chainlesschain 0.162.95 → 0.162.96
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-Bo9Ux24z.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-BxhQTwqd.js → ActionButton-CExgXSQT.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-C4oUXzhG.js → Analytics-BNnZhRTZ.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-CjzKJdv3.js → AppLayout-D9xfqFCT.js} +5 -5
- package/src/assets/web-panel/assets/{Audit-B-MrugUs.js → Audit-Cpgu8uCw.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-ecIfEpIZ.js → Backup-CHdEJnNE.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-BUcSfWnx.js → BaseInput-Cbw-PxBq.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-DIc1n_Lk.js → Chat-ChHTZK_6.js} +5 -5
- package/src/assets/web-panel/assets/{ChatBubbleRenderer-BGzg7oVA.js → ChatBubbleRenderer-Dl6z6ZH7.js} +1 -1
- package/src/assets/web-panel/assets/{Checkbox-BQ3Ueff-.js → Checkbox-Cj8d9bru.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-CvSGLnqE.js → Codegen-C2YobW4j.js} +1 -1
- package/src/assets/web-panel/assets/{Col-Br9IXZcE.js → Col-CxfV5FGT.js} +1 -1
- package/src/assets/web-panel/assets/{Community-tPuLOf0r.js → Community-CiwOASMj.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-vMWH73BE.js → Compact-CTg_2iKW.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-Ct9z699x.js → Compliance-HMbsTmtw.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-Bxs1kL1y.js → Cowork-DyNDqGR2.js} +3 -3
- package/src/assets/web-panel/assets/{Cron-QEpr2k4U.js → Cron-9-x5M7qy.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-u4xbUw6O.js → Crosschain-4msgDaYJ.js} +1 -1
- package/src/assets/web-panel/assets/{DID-DXTEvyrW.js → DID--2Beb5Po.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-C4pZgzkg.js → Dashboard-T6nq0Lzs.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-LRuqREGe.js → Dropdown-D_Q6T10v.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-D7XI1yBM.js → EmailListRenderer-TnN21bJx.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-3y_SuAMQ.js → FamilyGuardDashboard-kky_uIij.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-DbjbJJCW.js → Federation-C1GkNY4B.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-WPpMZcUw.js → FormItemContext-5-_Fc5YV.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-nNT9Tn4E.js → GenericCardRenderer-CmHiB7sr.js} +1 -1
- package/src/assets/web-panel/assets/{Git-DqDaVzwV.js → Git-Cuci_BPI.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-BquBZpcg.js → Governance-BU0cO2LD.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-RcPPYijs.js → Inference-BDFNgEcx.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-ByXfZV13.js → KnowledgeGraph-DITXUzHz.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-CHT3DUcq.js → Logs-Ea-07CRd.js} +2 -2
- package/src/assets/web-panel/assets/{Marketplace-Bi0HJ9pN.js → Marketplace-D6OBbRVG.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-QCBxPRRc.js → McpTools-eiKpIJMi.js} +4 -4
- package/src/assets/web-panel/assets/{Memory-D4RlxGXn.js → Memory-M-X-T8UF.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-CU_PV7e-.js → MobileBridge-xcZb9Ekv.js} +2 -2
- package/src/assets/web-panel/assets/{MobileProjects-c1CMFD8z.js → MobileProjects-DCfoTPRC.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-BezDB1-s.js → Mtc-BWfGDzD8.js} +5 -5
- package/src/assets/web-panel/assets/{MtcAudit-CgLpiHpE.js → MtcAudit-imBuld-U.js} +2 -2
- package/src/assets/web-panel/assets/{Multisig-vx6bz-bA.js → Multisig-C2kcsq7I.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-CGOagg3-.js → NLProgramming-DObvXSXP.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-BcuDGYRS.js → Notes-C-6OGJMe.js} +4 -4
- package/src/assets/web-panel/assets/{NotificationSettings-ZAGCDUpc.js → NotificationSettings-ub7Ozxci.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-B5onE0eX.js → OrderTableRenderer-CR2W89Vk.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-y_y20-5Y.js → Organization-DNA_Wty7.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-BEICS31D.js → Overflow-rT4Wb4rQ.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-D_WRMzh4.js → P2P-CK-hARKv.js} +2 -2
- package/src/assets/web-panel/assets/PdhVaultBrowser-C2pbtNbT.js +7 -0
- package/src/assets/web-panel/assets/{Permissions-3aqCc_4f.js → Permissions-B8EIEqGJ.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-DT3R9g9Y.js → PersonalDataHub-BxNVNU3s.js} +2 -2
- package/src/assets/web-panel/assets/{Pipeline-MiMWIhw0.js → Pipeline-CT9pbkyY.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-C3JsLuDa.js → Privacy-DhCNd2CS.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-EhHVi1ff.js → ProjectInit-Qngnx-vQ.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-D_dNEA37.js → ProjectSettings-Dfn-nQc1.js} +2 -2
- package/src/assets/web-panel/assets/Projects-BKdiSaPR.js +1 -0
- package/src/assets/web-panel/assets/{Providers-CPEhi2iZ.js → Providers-4o9B_n-X.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-BFQpMleX.js → QuickAsk-bs1qvF30.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-DzbovRC8.js → Recommend-CuEnKA2K.js} +1 -1
- package/src/assets/web-panel/assets/{Reputation-DoBeV3TD.js → Reputation-DbfvmRb2.js} +1 -1
- package/src/assets/web-panel/assets/{Row-DthhwX_B.js → Row-DvMJGAI3.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-Dsf5U0lP.js → RssFeed-oJ0BZvmA.js} +2 -2
- package/src/assets/web-panel/assets/{Search-CJPB8kKl.js → Search-DnIVrY9O.js} +1 -1
- package/src/assets/web-panel/assets/{Security-DI9MfpdD.js → Security-CIWhI_Tc.js} +3 -3
- package/src/assets/web-panel/assets/{Services-BdG0TFGf.js → Services-DLrnpjwm.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-DSYazY_B.js → Skeleton-CYmqgtVb.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-BD309VTT.js → Skills-BUzCkoeu.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-CaUlw4_6.js → Sla-DYRE4bnS.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-CZAPkR7H.js → SpeechSettings-_6MurdOg.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-CFRSowN3.js → SyncSettings-UIdnF5Li.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-Di8EOluc.js → Tasks-COx2CjT8.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-B5FTziwi.js → Templates-BcHAmkZo.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-Dj3XYfBg.js → Tenant-CjYQS8yZ.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-CznH_rJT.js → Terminal-BmfOPcWt.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-DTHzaXPt.js → TimelineRenderer-D5HuarNQ.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-Bo56nuML.js → Tokens-C_fbProA.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-pyVUVBwb.js → Trigger-JPulpRx8.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-Cp7jMkKN.js → Trust-CnO-062O.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-aPcZCdpz.js → UkeySign-HpNovvau.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-DAv5j--w.js → VideoEditing-yMncxH7y.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-DRhK7IDR.js → Wallet-Cug-t7Np.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-B2yQJuq8.js → WebAuthn--U1-NR-3.js} +2 -2
- package/src/assets/web-panel/assets/{WorkflowEditor-D98DhR54.js → WorkflowEditor-R-XvSJ3S.js} +1 -1
- package/src/assets/web-panel/assets/{chat-BXrRNi8C.js → chat-CCI1i6Rp.js} +1 -1
- package/src/assets/web-panel/assets/{colors-jQ94T_qn.js → colors-C-mqoaLt.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-KqUJLlF2.js → compact-item-De_N2iRR.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-DxM3zuQW.js → createContext-DbTM6sjZ.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-OcLPHSl5.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-BGm946KV.js → hasIn-D0Adylyc.js} +1 -1
- package/src/assets/web-panel/assets/index-7E_mHkDU.js +1 -0
- package/src/assets/web-panel/assets/{index-Dib0FsTT.js → index-AY937BxS.js} +1 -1
- package/src/assets/web-panel/assets/{index-C5NQai7O.js → index-B6hgy5VC.js} +1 -1
- package/src/assets/web-panel/assets/{index-LWTZAud0.js → index-B7XnpLHk.js} +1 -1
- package/src/assets/web-panel/assets/{index-BXMZLfd8.js → index-B7gw-tDm.js} +1 -1
- package/src/assets/web-panel/assets/{index-DTUs9t5F.js → index-BBgDgkwW.js} +1 -1
- package/src/assets/web-panel/assets/{index-Ce-YoL4x.js → index-BMmeiw88.js} +1 -1
- package/src/assets/web-panel/assets/{index-BaU_fSK7.js → index-BU6XtnSx.js} +1 -1
- package/src/assets/web-panel/assets/{index-BMp41Q1Y.js → index-Bi7VUG0R.js} +1 -1
- package/src/assets/web-panel/assets/{index-CbQvgx2e.js → index-BtE3cw9m.js} +1 -1
- package/src/assets/web-panel/assets/{index-DWcd5zDG.js → index-C9ej-MF0.js} +3 -3
- package/src/assets/web-panel/assets/{index-BbS0cBU9.js → index-CIEiQl1j.js} +1 -1
- package/src/assets/web-panel/assets/{index-D1VWL6Lc.js → index-CKgaW9E5.js} +1 -1
- package/src/assets/web-panel/assets/{index-DRzsaWQy.js → index-CY0KKR5f.js} +1 -1
- package/src/assets/web-panel/assets/index-CbV3-WlB.js +1 -0
- package/src/assets/web-panel/assets/{index-DSzb9VOG.js → index-Co1UkcFX.js} +1 -1
- package/src/assets/web-panel/assets/{index-B-2HpfWo.js → index-CzP5d5LW.js} +1 -1
- package/src/assets/web-panel/assets/{index-DboUBf6R.js → index-D0NJ5lRi.js} +1 -1
- package/src/assets/web-panel/assets/{index-DZMzj4ay.js → index-D0gBsqTX.js} +1 -1
- package/src/assets/web-panel/assets/{index-CghRL1dh.js → index-D5F6shtn.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cz_SFSix.js → index-DVtamtlm.js} +1 -1
- package/src/assets/web-panel/assets/{index-T4UJiTi3.js → index-DWT18hDG.js} +1 -1
- package/src/assets/web-panel/assets/{index-CreXAr5Y.js → index-DaXb4B9P.js} +1 -1
- package/src/assets/web-panel/assets/{index-C7Z2m28C.js → index-DjQA0Vez.js} +1 -1
- package/src/assets/web-panel/assets/{index-Crw76vIF.js → index-Ds8NJVEL.js} +1 -1
- package/src/assets/web-panel/assets/{index-mZNLT2SP.js → index-DuP-8HWu.js} +1 -1
- package/src/assets/web-panel/assets/{index-BnEZfi5D.js → index-DwLTiJ31.js} +1 -1
- package/src/assets/web-panel/assets/{index-1P0sXNhG.js → index-T3VhH8cO.js} +1 -1
- package/src/assets/web-panel/assets/{index-B4b7KjRq.js → index-TDY1o0w3.js} +1 -1
- package/src/assets/web-panel/assets/{index-DOYcemym.js → index-TRy2Gc3V.js} +1 -1
- package/src/assets/web-panel/assets/{index-BAPk6ntc.js → index-W1OMxCmI.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dgzw6eKr.js → index-ftFRZbuc.js} +1 -1
- package/src/assets/web-panel/assets/{index-C7sBLech.js → index-iGPGaORA.js} +1 -1
- package/src/assets/web-panel/assets/{index-FSYz5aww.js → index-jhy5KQvg.js} +1 -1
- package/src/assets/web-panel/assets/{index-DZpVYD4j.js → index-qUSZJcgw.js} +1 -1
- package/src/assets/web-panel/assets/{index-yavsyHif.js → index-w3f6lqkD.js} +1 -1
- package/src/assets/web-panel/assets/{index-CfE3jxrT.js → index-xdMc71Kh.js} +1 -1
- package/src/assets/web-panel/assets/{index-aOLXuHpG.js → index-xhX5eg2Z.js} +1 -1
- package/src/assets/web-panel/assets/{index-BGjCjsf1.js → index-zkTiW6d6.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-BDoZJI7g.js → initDefaultProps-CIHqouWk.js} +1 -1
- package/src/assets/web-panel/assets/{motion-sceLjgp-.js → motion-D1iUKftk.js} +1 -1
- package/src/assets/web-panel/assets/{move-CTWQpFa7.js → move-YOpWjmjL.js} +1 -1
- package/src/assets/web-panel/assets/{omit-dr-NCrHZ.js → omit-C_1357UY.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-DK-GX_Z2.js → pickAttrs-CDb4dq6o.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-BIPf--18.js → placementArrow-D-OGYyhd.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-BzrWT5tV.js → responsiveObserve-hS-j69A0.js} +1 -1
- package/src/assets/web-panel/assets/{slide-D5XhxlET.js → slide-tnjuvphU.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-6DI-k-9E.js → statusUtils-BERfMMcU.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-DMw-eyn9.js → styleChecker-DKpl2K9E.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-D4oVdliM.js → useFlexGapSupport-C_sX4D3N.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-D3PPZnvy.js → useFs-DBcxC1vK.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-Gh5ba4KP.js → usePersonalDataHub-CeoDf2XI.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-CVEM1u4x.js → vnode-DC9Pmj_u.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-ClT_-9RZ.js → zoom-CZA58J7M.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/commands/activitypub.js +19 -3
- package/src/commands/agent.js +25 -0
- package/src/commands/ask.js +13 -0
- package/src/commands/config.js +17 -1
- package/src/commands/cost.js +6 -1
- package/src/commands/dao.js +16 -3
- package/src/commands/dlp.js +7 -1
- package/src/commands/hub.js +13 -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/lib/__tests__/model-deprecation.test.js +194 -0
- package/src/lib/a2a-protocol.js +23 -9
- package/src/lib/agent-core.js +2 -5
- package/src/lib/audit-mtc.js +40 -2
- package/src/lib/chat-core.js +141 -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 +36 -13
- 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 +39 -7
- package/src/repl/chat-repl.js +10 -0
- package/src/repl/session-cost.js +31 -3
- package/src/runtime/agent-core.js +474 -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 +131 -5
- 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/Projects-CXydUXp8.js +0 -1
- 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
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PDH feedback ledger (design module 101 §3.5.13) — durable, cross-session
|
|
3
|
+
* persistence of the self-learning feedback the personal-data chat collects.
|
|
4
|
+
*
|
|
5
|
+
* The Android 纠正卡 sends {type:feedback,turn_id,kind,comment} to the cc agent;
|
|
6
|
+
* headless-stream.js consumes it within the session (ack + correction turn /
|
|
7
|
+
* preference note). That is in-session only. This ledger appends every feedback
|
|
8
|
+
* to <home>/pdh-feedback.jsonl so future sessions can learn the user's standing
|
|
9
|
+
* preferences — the "越用越聪明" flywheel (§3.5.13/§8.2). Append-only and
|
|
10
|
+
* best-effort: a learning ledger must never break the live chat.
|
|
11
|
+
*
|
|
12
|
+
* Pure CLI, no Android dependency, fully unit-testable through `_deps`.
|
|
13
|
+
*/
|
|
14
|
+
import fs from "node:fs";
|
|
15
|
+
import path from "node:path";
|
|
16
|
+
import { getHomeDir } from "./paths.js";
|
|
17
|
+
|
|
18
|
+
const VALID_KINDS = new Set(["positive", "negative", "correction"]);
|
|
19
|
+
|
|
20
|
+
export const _deps = {
|
|
21
|
+
homeDir: () => getHomeDir(),
|
|
22
|
+
appendFile: (p, s) => fs.appendFileSync(p, s, "utf-8"),
|
|
23
|
+
readFile: (p) => fs.readFileSync(p, "utf-8"),
|
|
24
|
+
exists: (p) => fs.existsSync(p),
|
|
25
|
+
mkdir: (d) => fs.mkdirSync(d, { recursive: true }),
|
|
26
|
+
now: () => Date.now(),
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
/** `<home>/pdh-feedback.jsonl`. */
|
|
30
|
+
export function feedbackLedgerPath(deps = _deps) {
|
|
31
|
+
return path.join(deps.homeDir(), "pdh-feedback.jsonl");
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Append one feedback entry. Best-effort: never throws (a learning ledger must
|
|
36
|
+
* not break the live chat). Returns the written record, or null on skip/failure.
|
|
37
|
+
* A `comment` is only kept for `correction` (thumbs carry no ground truth).
|
|
38
|
+
*/
|
|
39
|
+
export function appendFeedback(entry, deps = _deps) {
|
|
40
|
+
const kind = String(entry?.kind || "").toLowerCase();
|
|
41
|
+
if (!VALID_KINDS.has(kind)) return null;
|
|
42
|
+
const rec = {
|
|
43
|
+
ts: deps.now(),
|
|
44
|
+
sessionId: entry.sessionId != null ? String(entry.sessionId) : null,
|
|
45
|
+
turnId: entry.turnId != null ? String(entry.turnId) : null,
|
|
46
|
+
kind,
|
|
47
|
+
comment:
|
|
48
|
+
kind === "correction" &&
|
|
49
|
+
typeof entry.comment === "string" &&
|
|
50
|
+
entry.comment
|
|
51
|
+
? entry.comment
|
|
52
|
+
: null,
|
|
53
|
+
};
|
|
54
|
+
try {
|
|
55
|
+
const file = feedbackLedgerPath(deps);
|
|
56
|
+
const dir = path.dirname(file);
|
|
57
|
+
if (!deps.exists(dir)) deps.mkdir(dir);
|
|
58
|
+
deps.appendFile(file, JSON.stringify(rec) + "\n");
|
|
59
|
+
return rec;
|
|
60
|
+
} catch {
|
|
61
|
+
return null; // persistence is best-effort; the live turn carries on
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** Read all ledger entries (oldest-first), skipping malformed/invalid lines. */
|
|
66
|
+
export function readFeedback(deps = _deps) {
|
|
67
|
+
const file = feedbackLedgerPath(deps);
|
|
68
|
+
let raw;
|
|
69
|
+
try {
|
|
70
|
+
if (!deps.exists(file)) return [];
|
|
71
|
+
raw = deps.readFile(file);
|
|
72
|
+
} catch {
|
|
73
|
+
return [];
|
|
74
|
+
}
|
|
75
|
+
const out = [];
|
|
76
|
+
for (const line of raw.split("\n")) {
|
|
77
|
+
const t = line.trim();
|
|
78
|
+
if (!t) continue;
|
|
79
|
+
try {
|
|
80
|
+
const o = JSON.parse(t);
|
|
81
|
+
if (o && VALID_KINDS.has(o.kind)) out.push(o);
|
|
82
|
+
} catch {
|
|
83
|
+
/* skip malformed */
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return out;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Summarize the ledger into the user's standing preferences:
|
|
91
|
+
* { total, positive, negative, corrections:[newest-first], sentiment }
|
|
92
|
+
* Pure over a list (caller passes readFeedback()). Corrections are the most
|
|
93
|
+
* actionable ground truth, so the newest few are surfaced verbatim.
|
|
94
|
+
*/
|
|
95
|
+
export function summarizeFeedback(entries, { maxCorrections = 5 } = {}) {
|
|
96
|
+
const list = Array.isArray(entries) ? entries : [];
|
|
97
|
+
let positive = 0;
|
|
98
|
+
let negative = 0;
|
|
99
|
+
const corrections = [];
|
|
100
|
+
for (const e of list) {
|
|
101
|
+
if (e.kind === "positive") positive++;
|
|
102
|
+
else if (e.kind === "negative") negative++;
|
|
103
|
+
else if (e.kind === "correction" && e.comment) corrections.push(e.comment);
|
|
104
|
+
}
|
|
105
|
+
return {
|
|
106
|
+
total: list.length,
|
|
107
|
+
positive,
|
|
108
|
+
negative,
|
|
109
|
+
corrections: corrections.slice(-maxCorrections).reverse(),
|
|
110
|
+
sentiment: positive - negative,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* A compact Chinese system-note from a summary, for re-injecting learned
|
|
116
|
+
* preferences into a new session's prompt. "" when nothing has been learned.
|
|
117
|
+
*/
|
|
118
|
+
export function feedbackSystemNote(summary) {
|
|
119
|
+
if (!summary || !summary.total) return "";
|
|
120
|
+
const parts = [];
|
|
121
|
+
if (summary.corrections.length) {
|
|
122
|
+
parts.push(
|
|
123
|
+
"用户过往纠正(请遵循):" +
|
|
124
|
+
summary.corrections.map((c) => `「${c}」`).join(";"),
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
if (summary.sentiment > 0) parts.push("用户总体认可你的回复风格。");
|
|
128
|
+
else if (summary.sentiment < 0)
|
|
129
|
+
parts.push("用户对过往回复多有不满,请更严谨、更贴合需求。");
|
|
130
|
+
return parts.join(" ");
|
|
131
|
+
}
|
|
@@ -17,6 +17,34 @@ export const _deps = {
|
|
|
17
17
|
BM25Search,
|
|
18
18
|
};
|
|
19
19
|
|
|
20
|
+
/**
|
|
21
|
+
* Atomically rewrite a UTF-8 file via _deps.fs. MEMORY.md is the memory index
|
|
22
|
+
* (re-read every session); a crash mid-rewrite truncates it and loses the
|
|
23
|
+
* index. Temp sibling + rename (atomic within a filesystem). Graceful-degrade
|
|
24
|
+
* to a direct write when the injected fs has no renameSync (partial test mocks);
|
|
25
|
+
* the real fs always has it, so production is always atomic.
|
|
26
|
+
*/
|
|
27
|
+
function _atomicWriteFile(filePath, data) {
|
|
28
|
+
const fsx = _deps.fs;
|
|
29
|
+
if (typeof fsx.renameSync !== "function") {
|
|
30
|
+
fsx.writeFileSync(filePath, data, "utf-8");
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
const tmp = `${filePath}.${process.pid}.${Math.random().toString(36).slice(2, 8)}.tmp`;
|
|
34
|
+
try {
|
|
35
|
+
fsx.writeFileSync(tmp, data, "utf-8");
|
|
36
|
+
fsx.renameSync(tmp, filePath);
|
|
37
|
+
} catch (err) {
|
|
38
|
+
try {
|
|
39
|
+
if (fsx.existsSync && fsx.existsSync(tmp) && fsx.unlinkSync)
|
|
40
|
+
fsx.unlinkSync(tmp);
|
|
41
|
+
} catch {
|
|
42
|
+
/* best-effort temp cleanup */
|
|
43
|
+
}
|
|
44
|
+
throw err;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
20
48
|
export class CLIPermanentMemory {
|
|
21
49
|
/**
|
|
22
50
|
* @param {object} options
|
|
@@ -148,13 +176,13 @@ export class CLIPermanentMemory {
|
|
|
148
176
|
existing.slice(0, sectionIdx) +
|
|
149
177
|
`${sectionHeader}\n\n${content}\n` +
|
|
150
178
|
existing.slice(endIdx);
|
|
151
|
-
|
|
179
|
+
_atomicWriteFile(filePath, newContent);
|
|
152
180
|
} else {
|
|
153
181
|
// Append new section
|
|
154
182
|
const append = existing
|
|
155
183
|
? `\n${sectionHeader}\n\n${content}\n`
|
|
156
184
|
: `# Memory\n\n${sectionHeader}\n\n${content}\n`;
|
|
157
|
-
|
|
185
|
+
_atomicWriteFile(filePath, existing + append);
|
|
158
186
|
}
|
|
159
187
|
|
|
160
188
|
this._loadMemoryFile();
|
|
@@ -113,9 +113,17 @@ function globToRegExp(glob) {
|
|
|
113
113
|
const c = s[i];
|
|
114
114
|
if (c === "*") {
|
|
115
115
|
if (s[i + 1] === "*") {
|
|
116
|
-
|
|
117
|
-
i
|
|
118
|
-
|
|
116
|
+
i++; // consume the second `*`
|
|
117
|
+
if (s[i + 1] === "/") {
|
|
118
|
+
i++; // consume the `/` too
|
|
119
|
+
// `**/` = zero or more WHOLE path segments. Must be boundary-aware:
|
|
120
|
+
// `.*` here would let `**/secret` match `notsecret` (no `/` before the
|
|
121
|
+
// suffix), over-matching permission patterns. `(?:.*/)?` requires the
|
|
122
|
+
// suffix to start a path segment (or sit at the root).
|
|
123
|
+
re += "(?:.*/)?";
|
|
124
|
+
} else {
|
|
125
|
+
re += ".*"; // bare `**` (no trailing slash) — matches across segments
|
|
126
|
+
}
|
|
119
127
|
} else {
|
|
120
128
|
re += "[^/]*";
|
|
121
129
|
}
|
|
@@ -15,12 +15,42 @@
|
|
|
15
15
|
* Reference: docs/design/Personal_Data_Hub_Phase_10_3_AIChat_WebView_Wizard.md §2 §11
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
|
-
import {
|
|
18
|
+
import {
|
|
19
|
+
readFileSync,
|
|
20
|
+
writeFileSync,
|
|
21
|
+
mkdirSync,
|
|
22
|
+
unlinkSync,
|
|
23
|
+
renameSync,
|
|
24
|
+
existsSync,
|
|
25
|
+
} from "node:fs";
|
|
19
26
|
import { join } from "node:path";
|
|
20
27
|
import { createRequire } from "node:module";
|
|
21
28
|
|
|
22
29
|
const _require = createRequire(import.meta.url);
|
|
23
30
|
|
|
31
|
+
/**
|
|
32
|
+
* Atomically persist the accounts JSON at mode 0600. The store holds AI-chat
|
|
33
|
+
* vendor API keys, and `_readAll` swallows a parse error → returns {}, so a
|
|
34
|
+
* crash mid-write would silently drop every saved vendor. Temp sibling (created
|
|
35
|
+
* 0600 so the secret is never world-readable) + rename, atomic within a
|
|
36
|
+
* filesystem. (Mirrors the config / mcp-oauth / sync-credentials / PDH-account
|
|
37
|
+
* atomic-write fixes.) Exported as a test seam.
|
|
38
|
+
*/
|
|
39
|
+
export function _atomicWriteAccounts(filePath, data) {
|
|
40
|
+
const tmp = `${filePath}.${process.pid}.${Math.random().toString(36).slice(2, 8)}.tmp`;
|
|
41
|
+
try {
|
|
42
|
+
writeFileSync(tmp, data, { mode: 0o600 });
|
|
43
|
+
renameSync(tmp, filePath);
|
|
44
|
+
} catch (err) {
|
|
45
|
+
try {
|
|
46
|
+
if (existsSync(tmp)) unlinkSync(tmp);
|
|
47
|
+
} catch {
|
|
48
|
+
/* best-effort temp cleanup */
|
|
49
|
+
}
|
|
50
|
+
throw err;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
24
54
|
// Lazy-load `@chainlesschain/personal-data-hub/adapters/ai-chat-history` —
|
|
25
55
|
// older nested copies of the hub package (e.g. PDH 0.2.0 left in
|
|
26
56
|
// `packages/cli/node_modules/@chainlesschain/personal-data-hub/` by a stale
|
|
@@ -76,7 +106,7 @@ export function createAccountsStore({ hubDir }) {
|
|
|
76
106
|
} catch (err) {
|
|
77
107
|
if (!err || err.code !== "EEXIST") throw err;
|
|
78
108
|
}
|
|
79
|
-
|
|
109
|
+
_atomicWriteAccounts(filePath, JSON.stringify(all, null, 2));
|
|
80
110
|
});
|
|
81
111
|
return writeChain;
|
|
82
112
|
}
|
|
@@ -93,7 +123,7 @@ export function createAccountsStore({ hubDir }) {
|
|
|
93
123
|
/* missing is fine */
|
|
94
124
|
}
|
|
95
125
|
} else {
|
|
96
|
-
|
|
126
|
+
_atomicWriteAccounts(filePath, JSON.stringify(all, null, 2));
|
|
97
127
|
}
|
|
98
128
|
});
|
|
99
129
|
return writeChain;
|
|
@@ -126,7 +126,13 @@ const {
|
|
|
126
126
|
runAnalysisSkill,
|
|
127
127
|
ANALYSIS_SKILL_NAMES,
|
|
128
128
|
} = hub;
|
|
129
|
-
import {
|
|
129
|
+
import {
|
|
130
|
+
readFileSync,
|
|
131
|
+
writeFileSync,
|
|
132
|
+
existsSync,
|
|
133
|
+
renameSync,
|
|
134
|
+
unlinkSync,
|
|
135
|
+
} from "node:fs";
|
|
130
136
|
import { getElectronUserDataDir } from "./paths.js";
|
|
131
137
|
|
|
132
138
|
import {
|
|
@@ -1382,11 +1388,34 @@ function loadWechatAccounts(filePath) {
|
|
|
1382
1388
|
}
|
|
1383
1389
|
}
|
|
1384
1390
|
|
|
1391
|
+
/**
|
|
1392
|
+
* Atomically persist a JSON state file at mode 0600. The PDH account stores hold
|
|
1393
|
+
* login credentials (cookies/tokens), so a crash mid-write must not truncate the
|
|
1394
|
+
* file — loadXAccounts swallows a parse error and returns [], silently dropping
|
|
1395
|
+
* every saved account. Temp sibling (created 0600 so the secret is never world-
|
|
1396
|
+
* readable) + rename, which is atomic within a filesystem. Exported as a test
|
|
1397
|
+
* seam. (Mirrors the config / mcp-oauth / sync-credentials atomic-write fixes.)
|
|
1398
|
+
*/
|
|
1399
|
+
export function _atomicWriteJson600(filePath, obj) {
|
|
1400
|
+
const tmp = `${filePath}.${process.pid}.${Math.random().toString(36).slice(2, 8)}.tmp`;
|
|
1401
|
+
try {
|
|
1402
|
+
writeFileSync(tmp, JSON.stringify(obj, null, 2), {
|
|
1403
|
+
encoding: "utf-8",
|
|
1404
|
+
mode: 0o600,
|
|
1405
|
+
});
|
|
1406
|
+
renameSync(tmp, filePath);
|
|
1407
|
+
} catch (err) {
|
|
1408
|
+
try {
|
|
1409
|
+
if (existsSync(tmp)) unlinkSync(tmp);
|
|
1410
|
+
} catch {
|
|
1411
|
+
/* best-effort temp cleanup */
|
|
1412
|
+
}
|
|
1413
|
+
throw err;
|
|
1414
|
+
}
|
|
1415
|
+
}
|
|
1416
|
+
|
|
1385
1417
|
function saveWechatAccounts(filePath, accounts) {
|
|
1386
|
-
|
|
1387
|
-
encoding: "utf-8",
|
|
1388
|
-
mode: 0o600,
|
|
1389
|
-
});
|
|
1418
|
+
_atomicWriteJson600(filePath, accounts);
|
|
1390
1419
|
}
|
|
1391
1420
|
|
|
1392
1421
|
// ─── Email account persistence (Phase 5.6) ───────────────────────────────
|
|
@@ -1403,10 +1432,7 @@ function loadEmailAccounts(filePath) {
|
|
|
1403
1432
|
}
|
|
1404
1433
|
|
|
1405
1434
|
function saveEmailAccounts(filePath, accounts) {
|
|
1406
|
-
|
|
1407
|
-
encoding: "utf-8",
|
|
1408
|
-
mode: 0o600,
|
|
1409
|
-
});
|
|
1435
|
+
_atomicWriteJson600(filePath, accounts);
|
|
1410
1436
|
}
|
|
1411
1437
|
|
|
1412
1438
|
// ─── Alipay account persistence (Phase 6) ───────────────────────────────
|
|
@@ -1423,10 +1449,7 @@ function loadAlipayAccounts(filePath) {
|
|
|
1423
1449
|
}
|
|
1424
1450
|
|
|
1425
1451
|
function saveAlipayAccounts(filePath, accounts) {
|
|
1426
|
-
|
|
1427
|
-
encoding: "utf-8",
|
|
1428
|
-
mode: 0o600,
|
|
1429
|
-
});
|
|
1452
|
+
_atomicWriteJson600(filePath, accounts);
|
|
1430
1453
|
}
|
|
1431
1454
|
|
|
1432
1455
|
export async function getHub() {
|
|
@@ -133,9 +133,11 @@ export function findInstructionFiles(opts = {}) {
|
|
|
133
133
|
for (const d of chain) {
|
|
134
134
|
push(firstExisting(fs, path, d, PROJECT_FILE_NAMES), "project");
|
|
135
135
|
push(firstExisting(fs, path, d, LOCAL_FILE_NAMES), "local");
|
|
136
|
-
//
|
|
137
|
-
//
|
|
138
|
-
|
|
136
|
+
// NOTE: `.chainlesschain/rules.md` is intentionally NOT loaded here — it is
|
|
137
|
+
// already injected by buildSystemPrompt() (as "## Project Rules"), which is
|
|
138
|
+
// the base of every composeSystemPrompt() call. Loading it here too sent it
|
|
139
|
+
// TWICE in the system prompt on every turn. Path-scoped `.claude/rules/*.md`
|
|
140
|
+
// below are NOT covered by buildSystemPrompt, so they stay.
|
|
139
141
|
// Path-scoped rule files (`.claude/rules/*.md`, YAML frontmatter `paths:`
|
|
140
142
|
// globs). Glob filtering happens at LOAD time where content is available.
|
|
141
143
|
try {
|
|
@@ -211,7 +213,11 @@ export function ruleApplies(globs, relCwd) {
|
|
|
211
213
|
const star = g.search(/[*?[]/);
|
|
212
214
|
const prefix = (star === -1 ? g : g.slice(0, star)).replace(/\/+$/, "");
|
|
213
215
|
if (!prefix) return true; // "**/*.js" — applies everywhere
|
|
214
|
-
if (
|
|
216
|
+
if (
|
|
217
|
+
cwd === prefix ||
|
|
218
|
+
cwd.startsWith(`${prefix}/`) ||
|
|
219
|
+
prefix.startsWith(`${cwd}/`)
|
|
220
|
+
) {
|
|
215
221
|
return true;
|
|
216
222
|
}
|
|
217
223
|
}
|
|
@@ -92,48 +92,79 @@ export function makeRunnableProviderFallback(
|
|
|
92
92
|
chatFn,
|
|
93
93
|
{ env = process.env, onFallback } = {},
|
|
94
94
|
) {
|
|
95
|
+
// Notify at most once per from→to pair: in an agent loop the same fallback
|
|
96
|
+
// would otherwise re-warn on every turn.
|
|
97
|
+
const notified = new Set();
|
|
95
98
|
const notify = (info) => {
|
|
96
|
-
if (typeof onFallback
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
99
|
+
if (typeof onFallback !== "function") return;
|
|
100
|
+
const key = `${info.from}→${info.to}`;
|
|
101
|
+
if (notified.has(key)) return;
|
|
102
|
+
notified.add(key);
|
|
103
|
+
try {
|
|
104
|
+
onFallback(info);
|
|
105
|
+
} catch {
|
|
106
|
+
/* notification is best-effort */
|
|
102
107
|
}
|
|
103
108
|
};
|
|
104
|
-
return async function runnableProviderFallback(messages, options = {}) {
|
|
105
|
-
try {
|
|
106
|
-
return await chatFn(messages, options);
|
|
107
|
-
} catch (err) {
|
|
108
|
-
if (!isAuthError(err)) throw err;
|
|
109
|
-
const failed = options.provider;
|
|
110
109
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
110
|
+
// Compute the fallback for these options WITHOUT calling the model — the
|
|
111
|
+
// resolution is deterministic (baseUrl-inferred provider, then env-keyed
|
|
112
|
+
// provider). Returns { to, reason, override } or null. Pure so we can both
|
|
113
|
+
// react to an auth failure AND pre-empt the known-bad attempt on later turns.
|
|
114
|
+
const computeFallback = (options) => {
|
|
115
|
+
const failed = options.provider;
|
|
116
|
+
const inferred = inferProviderFromBaseUrl(options.baseUrl);
|
|
117
|
+
if (inferred && inferred !== failed) {
|
|
118
|
+
return {
|
|
119
|
+
to: inferred,
|
|
120
|
+
reason: "baseurl-mismatch",
|
|
121
|
+
override: {
|
|
117
122
|
provider: inferred,
|
|
118
123
|
model: defaultModelFor(inferred) || options.model,
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
124
|
+
},
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
for (const [name, def] of Object.entries(BUILT_IN_PROVIDERS)) {
|
|
128
|
+
if (name === failed) continue;
|
|
129
|
+
if (def.apiKeyEnv && nonEmpty(env[def.apiKeyEnv])) {
|
|
130
|
+
return {
|
|
131
|
+
to: name,
|
|
132
|
+
reason: "env-key",
|
|
133
|
+
override: {
|
|
129
134
|
provider: name,
|
|
130
135
|
baseUrl: def.baseUrl,
|
|
131
136
|
apiKey: envKey(name, env),
|
|
132
137
|
model: defaultModelFor(name) || options.model,
|
|
133
|
-
}
|
|
134
|
-
}
|
|
138
|
+
},
|
|
139
|
+
};
|
|
135
140
|
}
|
|
136
|
-
|
|
141
|
+
}
|
|
142
|
+
return null;
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
// Providers we have already proven need a fallback — later turns route
|
|
146
|
+
// straight to the working provider instead of re-attempting the broken one.
|
|
147
|
+
const stickyFrom = new Set();
|
|
148
|
+
|
|
149
|
+
return async function runnableProviderFallback(messages, options = {}) {
|
|
150
|
+
// Known-bad provider: skip the wasted failed attempt and route directly.
|
|
151
|
+
if (stickyFrom.has(options.provider)) {
|
|
152
|
+
const fb = computeFallback(options);
|
|
153
|
+
if (fb) {
|
|
154
|
+
notify({ from: options.provider, to: fb.to, reason: fb.reason });
|
|
155
|
+
return await chatFn(messages, { ...options, ...fb.override });
|
|
156
|
+
}
|
|
157
|
+
// Resolution no longer available — fall through to a normal attempt.
|
|
158
|
+
}
|
|
159
|
+
try {
|
|
160
|
+
return await chatFn(messages, options);
|
|
161
|
+
} catch (err) {
|
|
162
|
+
if (!isAuthError(err)) throw err;
|
|
163
|
+
const fb = computeFallback(options);
|
|
164
|
+
if (!fb) throw err; // nowhere runnable — surface the clear error
|
|
165
|
+
stickyFrom.add(options.provider);
|
|
166
|
+
notify({ from: options.provider, to: fb.to, reason: fb.reason });
|
|
167
|
+
return await chatFn(messages, { ...options, ...fb.override });
|
|
137
168
|
}
|
|
138
169
|
};
|
|
139
170
|
}
|
package/src/lib/session-usage.js
CHANGED
|
@@ -49,8 +49,23 @@ export function extractUsage(event) {
|
|
|
49
49
|
const totalTokens = toNumber(
|
|
50
50
|
raw.total_tokens ?? raw.totalTokens ?? inputTokens + outputTokens,
|
|
51
51
|
);
|
|
52
|
+
// Prompt-cache token counts (Anthropic caching): read tokens are billed at
|
|
53
|
+
// ~10% and creation/write at ~1.25× the input rate, so they are tracked
|
|
54
|
+
// separately for accurate cost. Absent on non-caching providers → 0.
|
|
55
|
+
const cacheReadTokens = toNumber(
|
|
56
|
+
raw.cache_read_input_tokens ?? raw.cacheReadTokens ?? 0,
|
|
57
|
+
);
|
|
58
|
+
const cacheCreationTokens = toNumber(
|
|
59
|
+
raw.cache_creation_input_tokens ?? raw.cacheCreationTokens ?? 0,
|
|
60
|
+
);
|
|
52
61
|
|
|
53
|
-
if (
|
|
62
|
+
if (
|
|
63
|
+
inputTokens === 0 &&
|
|
64
|
+
outputTokens === 0 &&
|
|
65
|
+
totalTokens === 0 &&
|
|
66
|
+
cacheReadTokens === 0 &&
|
|
67
|
+
cacheCreationTokens === 0
|
|
68
|
+
) {
|
|
54
69
|
return null;
|
|
55
70
|
}
|
|
56
71
|
|
|
@@ -60,6 +75,8 @@ export function extractUsage(event) {
|
|
|
60
75
|
inputTokens,
|
|
61
76
|
outputTokens,
|
|
62
77
|
totalTokens,
|
|
78
|
+
cacheReadTokens,
|
|
79
|
+
cacheCreationTokens,
|
|
63
80
|
timestamp: event.timestamp || null,
|
|
64
81
|
};
|
|
65
82
|
}
|
|
@@ -73,6 +90,8 @@ export function aggregateUsage(events) {
|
|
|
73
90
|
inputTokens: 0,
|
|
74
91
|
outputTokens: 0,
|
|
75
92
|
totalTokens: 0,
|
|
93
|
+
cacheReadTokens: 0,
|
|
94
|
+
cacheCreationTokens: 0,
|
|
76
95
|
calls: 0,
|
|
77
96
|
};
|
|
78
97
|
|
|
@@ -83,6 +102,8 @@ export function aggregateUsage(events) {
|
|
|
83
102
|
total.inputTokens += u.inputTokens;
|
|
84
103
|
total.outputTokens += u.outputTokens;
|
|
85
104
|
total.totalTokens += u.totalTokens;
|
|
105
|
+
total.cacheReadTokens += u.cacheReadTokens || 0;
|
|
106
|
+
total.cacheCreationTokens += u.cacheCreationTokens || 0;
|
|
86
107
|
total.calls += 1;
|
|
87
108
|
|
|
88
109
|
const key = `${u.provider || "?"}/${u.model || "?"}`;
|
|
@@ -92,11 +113,15 @@ export function aggregateUsage(events) {
|
|
|
92
113
|
inputTokens: 0,
|
|
93
114
|
outputTokens: 0,
|
|
94
115
|
totalTokens: 0,
|
|
116
|
+
cacheReadTokens: 0,
|
|
117
|
+
cacheCreationTokens: 0,
|
|
95
118
|
calls: 0,
|
|
96
119
|
};
|
|
97
120
|
entry.inputTokens += u.inputTokens;
|
|
98
121
|
entry.outputTokens += u.outputTokens;
|
|
99
122
|
entry.totalTokens += u.totalTokens;
|
|
123
|
+
entry.cacheReadTokens += u.cacheReadTokens || 0;
|
|
124
|
+
entry.cacheCreationTokens += u.cacheCreationTokens || 0;
|
|
100
125
|
entry.calls += 1;
|
|
101
126
|
byKey.set(key, entry);
|
|
102
127
|
}
|
|
@@ -129,6 +154,8 @@ export function allSessionsUsage({ limit = 1000 } = {}) {
|
|
|
129
154
|
inputTokens: 0,
|
|
130
155
|
outputTokens: 0,
|
|
131
156
|
totalTokens: 0,
|
|
157
|
+
cacheReadTokens: 0,
|
|
158
|
+
cacheCreationTokens: 0,
|
|
132
159
|
calls: 0,
|
|
133
160
|
};
|
|
134
161
|
const byKey = new Map();
|
|
@@ -137,6 +164,8 @@ export function allSessionsUsage({ limit = 1000 } = {}) {
|
|
|
137
164
|
total.inputTokens += s.total.inputTokens;
|
|
138
165
|
total.outputTokens += s.total.outputTokens;
|
|
139
166
|
total.totalTokens += s.total.totalTokens;
|
|
167
|
+
total.cacheReadTokens += s.total.cacheReadTokens || 0;
|
|
168
|
+
total.cacheCreationTokens += s.total.cacheCreationTokens || 0;
|
|
140
169
|
total.calls += s.total.calls;
|
|
141
170
|
for (const row of s.byModel) {
|
|
142
171
|
const key = `${row.provider || "?"}/${row.model || "?"}`;
|
|
@@ -146,11 +175,15 @@ export function allSessionsUsage({ limit = 1000 } = {}) {
|
|
|
146
175
|
inputTokens: 0,
|
|
147
176
|
outputTokens: 0,
|
|
148
177
|
totalTokens: 0,
|
|
178
|
+
cacheReadTokens: 0,
|
|
179
|
+
cacheCreationTokens: 0,
|
|
149
180
|
calls: 0,
|
|
150
181
|
};
|
|
151
182
|
entry.inputTokens += row.inputTokens;
|
|
152
183
|
entry.outputTokens += row.outputTokens;
|
|
153
184
|
entry.totalTokens += row.totalTokens;
|
|
185
|
+
entry.cacheReadTokens += row.cacheReadTokens || 0;
|
|
186
|
+
entry.cacheCreationTokens += row.cacheCreationTokens || 0;
|
|
154
187
|
entry.calls += row.calls;
|
|
155
188
|
byKey.set(key, entry);
|
|
156
189
|
}
|
package/src/lib/skill-loader.js
CHANGED
|
@@ -199,7 +199,10 @@ export function substituteArguments(body, args) {
|
|
|
199
199
|
// Protect literal $$
|
|
200
200
|
const MARKER = "\u0000DOLLAR\u0000";
|
|
201
201
|
let out = body.replace(/\$\$/g, MARKER);
|
|
202
|
-
|
|
202
|
+
// Insert the args via a function so $-sequences in the user's arguments
|
|
203
|
+
// ($&, $`, $', $$) are placed literally instead of being interpreted as
|
|
204
|
+
// String.replace patterns (which would otherwise mangle the args).
|
|
205
|
+
out = out.replace(/\$ARGUMENTS\b/g, () => full);
|
|
203
206
|
out = out.replace(/\$(\d+)/g, (match, idx) => {
|
|
204
207
|
const i = parseInt(idx, 10) - 1;
|
|
205
208
|
if (i < 0 || i >= positional.length) return match;
|
|
@@ -158,7 +158,7 @@ ${commandDocs}
|
|
|
158
158
|
/**
|
|
159
159
|
* Generate handler.js content for a direct-execution pack
|
|
160
160
|
*/
|
|
161
|
-
function generateDirectHandler(domainKey, domainDef) {
|
|
161
|
+
export function generateDirectHandler(domainKey, domainDef) {
|
|
162
162
|
const commandList = Object.keys(domainDef.commands).join('", "');
|
|
163
163
|
const commandGuide = Object.entries(domainDef.commands)
|
|
164
164
|
.map(
|
|
@@ -241,6 +241,17 @@ ${commandGuide}
|
|
|
241
241
|
const useJson = JSON_SUPPORTED_COMMANDS.has(command) && !cliArgs.includes("--json");
|
|
242
242
|
if (useJson) cliArgs.push("--json");
|
|
243
243
|
|
|
244
|
+
// Security: spawnSync uses shell:true (needed to run the chainlesschain.cmd
|
|
245
|
+
// shim on Windows), which joins cliArgs into a shell command. Reject shell
|
|
246
|
+
// metacharacters so a crafted skill input can't inject commands.
|
|
247
|
+
const unsafeArg = cliArgs.find((a) => /[;&|\`$<>()\\n\\r]/.test(a));
|
|
248
|
+
if (unsafeArg) {
|
|
249
|
+
return {
|
|
250
|
+
success: false,
|
|
251
|
+
error: "参数包含不安全的 shell 字符,已拒绝执行: " + JSON.stringify(unsafeArg),
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
|
|
244
255
|
// Execute via child process
|
|
245
256
|
const result = spawnSync("chainlesschain", cliArgs, {
|
|
246
257
|
encoding: "utf-8",
|
|
@@ -371,7 +382,7 @@ module.exports = handler;
|
|
|
371
382
|
/**
|
|
372
383
|
* Generate handler.js content for hybrid-mode pack
|
|
373
384
|
*/
|
|
374
|
-
function generateHybridHandler(domainKey, domainDef) {
|
|
385
|
+
export function generateHybridHandler(domainKey, domainDef) {
|
|
375
386
|
const agentCmds = Object.entries(domainDef.commands)
|
|
376
387
|
.filter(([cmd, info]) => info.isAgentMode || AGENT_MODE_COMMANDS.has(cmd))
|
|
377
388
|
.map(([cmd]) => `"${cmd}"`)
|
|
@@ -458,6 +469,18 @@ const handler = {
|
|
|
458
469
|
|
|
459
470
|
// Direct execution for other commands
|
|
460
471
|
const cliArgs = [...args, "--quiet"];
|
|
472
|
+
|
|
473
|
+
// Security: spawnSync uses shell:true (needed to run the chainlesschain.cmd
|
|
474
|
+
// shim on Windows), which joins cliArgs into a shell command. Reject shell
|
|
475
|
+
// metacharacters so a crafted skill input can't inject commands.
|
|
476
|
+
const unsafeArg = cliArgs.find((a) => /[;&|\`$<>()\\n\\r]/.test(a));
|
|
477
|
+
if (unsafeArg) {
|
|
478
|
+
return {
|
|
479
|
+
success: false,
|
|
480
|
+
error: "参数包含不安全的 shell 字符,已拒绝执行: " + JSON.stringify(unsafeArg),
|
|
481
|
+
};
|
|
482
|
+
}
|
|
483
|
+
|
|
461
484
|
const result = spawnSync("chainlesschain", cliArgs, {
|
|
462
485
|
encoding: "utf-8",
|
|
463
486
|
shell: true,
|