chainlesschain 0.162.124 → 0.162.125
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/assets/web-panel/.build-hash +1 -1
- package/src/assets/web-panel/assets/AIOps-DcqNhZhA.js +1 -0
- package/src/assets/web-panel/assets/{ActionButton-COjZZKWG.js → ActionButton-BigtmPoq.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-4ZDZE7lc.js → Analytics-1J_X_HF4.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-CYH5k2Rp.js → AppLayout-DlcDG_a_.js} +5 -5
- package/src/assets/web-panel/assets/Audit-Do-y8_3w.js +1 -0
- package/src/assets/web-panel/assets/{Backup-CiX61Qc2.js → Backup-S2Df-50Y.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-CDxIQokd.js → BaseInput-D5kgWJfk.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-CaKgdRab.js → Chat-aUcp3kN1.js} +5 -5
- package/src/assets/web-panel/assets/ChatBubbleRenderer-Pg7dIsiE.js +1 -0
- package/src/assets/web-panel/assets/{Checkbox-CjiWfu4s.js → Checkbox-DTg1U7Xs.js} +1 -1
- package/src/assets/web-panel/assets/Codegen-YO9ZZ4Ky.js +1 -0
- package/src/assets/web-panel/assets/{Col-ZKWREyNs.js → Col-BHonE9fU.js} +1 -1
- package/src/assets/web-panel/assets/Community-DA2AlEFh.js +1 -0
- package/src/assets/web-panel/assets/{Compact-DqknM98n.js → Compact-DULxLRNg.js} +1 -1
- package/src/assets/web-panel/assets/Compliance-BZqfuuZL.js +1 -0
- package/src/assets/web-panel/assets/{Cowork-Dp29kHsC.js → Cowork-CJe3vyMS.js} +2 -2
- package/src/assets/web-panel/assets/{Cron-zmEJAetz.js → Cron-CeV8AtRu.js} +2 -2
- package/src/assets/web-panel/assets/Crosschain-CAg66zS5.js +1 -0
- package/src/assets/web-panel/assets/{DID-D_UFNzJ5.js → DID-Dtup5JZm.js} +2 -2
- package/src/assets/web-panel/assets/Dashboard-DAR_8PNy.js +3 -0
- package/src/assets/web-panel/assets/{Dropdown-CH7l3KCs.js → Dropdown-K5bTaCYN.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-Bud2M3XX.js → EmailListRenderer-C890uErx.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-Bd2_8uME.js → FamilyGuardDashboard-B-Tj_8yM.js} +1 -1
- package/src/assets/web-panel/assets/Federation-C6WZ98ni.js +1 -0
- package/src/assets/web-panel/assets/{FormItemContext-CpSH464Y.js → FormItemContext-D-LTfGWd.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-CANo8O-y.js → GenericCardRenderer-C80DK4W7.js} +1 -1
- package/src/assets/web-panel/assets/{Git-hvuZVoD3.js → Git-Cjvvv3zW.js} +2 -2
- package/src/assets/web-panel/assets/Governance-C0BND77H.js +1 -0
- package/src/assets/web-panel/assets/Inference-BL9kTtSu.js +1 -0
- package/src/assets/web-panel/assets/KnowledgeGraph-CnVTBmJf.js +1 -0
- package/src/assets/web-panel/assets/{Logs-DDkTnedu.js → Logs-CbCbg7K6.js} +2 -2
- package/src/assets/web-panel/assets/Marketplace-CG5On-fH.js +1 -0
- package/src/assets/web-panel/assets/{McpTools-Qx78XyOT.js → McpTools-AYYDZZK7.js} +5 -5
- package/src/assets/web-panel/assets/{Memory-CXYDO1oT.js → Memory-tMWbMDQq.js} +2 -2
- package/src/assets/web-panel/assets/MobileBridge-BAOsf4wB.js +1 -0
- package/src/assets/web-panel/assets/{MobileProjects-DEDbWNIk.js → MobileProjects-BT-2komQ.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-Dwa_vlR8.js → Mtc-BFwfC63n.js} +5 -5
- package/src/assets/web-panel/assets/{MtcAudit-DAVkxhJv.js → MtcAudit-DYG3CWdH.js} +4 -4
- package/src/assets/web-panel/assets/{Multisig-C3upmgPN.js → Multisig-KJwd0yWT.js} +3 -3
- package/src/assets/web-panel/assets/NLProgramming-BmL5dNWm.js +1 -0
- package/src/assets/web-panel/assets/{Notes-B1Oy3FbC.js → Notes-DmkaVaMc.js} +4 -4
- package/src/assets/web-panel/assets/{NotificationSettings-Bs4-Fc6b.js → NotificationSettings-tmVQszDZ.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-CEy1pIeq.js → OrderTableRenderer-BtrR7anf.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-D4cJ1UNo.js → Organization-pppktQyW.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-fdzvlF7x.js → Overflow-D3lBoQDA.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-Y13PwXBA.js → P2P-BgE2qGlZ.js} +2 -2
- package/src/assets/web-panel/assets/PdhVaultBrowser-D26Bz5gS.js +7 -0
- package/src/assets/web-panel/assets/{Permissions-CvEXP6LC.js → Permissions-BR8no2Xe.js} +3 -3
- package/src/assets/web-panel/assets/{PersonalDataHub-JeP7IWMW.js → PersonalDataHub-DabbyQEF.js} +3 -3
- package/src/assets/web-panel/assets/Pipeline-n4sNpEz8.js +1 -0
- package/src/assets/web-panel/assets/Privacy-CGNp4n8M.js +1 -0
- package/src/assets/web-panel/assets/{ProjectInit-DeWd9UcS.js → ProjectInit-CAVA5VsX.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-DUpVuU9F.js → ProjectSettings-GCgkfM7A.js} +2 -2
- package/src/assets/web-panel/assets/Projects-BYK17p52.js +1 -0
- package/src/assets/web-panel/assets/{Providers-DECW00ek.js → Providers-DpUT5W-d.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-Dl-pK9Io.js → QuickAsk-ZJxTb7vi.js} +1 -1
- package/src/assets/web-panel/assets/Recommend-CIzFRDk1.js +1 -0
- package/src/assets/web-panel/assets/Reputation-lQ_WDNZn.js +1 -0
- package/src/assets/web-panel/assets/Row-1MEtrgtF.js +1 -0
- package/src/assets/web-panel/assets/{RssFeed-iyQT5q9l.js → RssFeed-SSgcPPl4.js} +3 -3
- package/src/assets/web-panel/assets/Search-CcTMOGNY.js +1 -0
- package/src/assets/web-panel/assets/{Security-c4Jxf5Ih.js → Security-BA1KMaDx.js} +4 -4
- package/src/assets/web-panel/assets/{Services-CRuisolj.js → Services-_aj7czZn.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-CCnzJkb-.js → Skeleton-BWgg_0Zr.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-CZURCwZg.js → Skills-C2ZIziYM.js} +1 -1
- package/src/assets/web-panel/assets/Sla-CYxp4axY.js +1 -0
- package/src/assets/web-panel/assets/SpeechSettings-C7Csp1jV.js +1 -0
- package/src/assets/web-panel/assets/{SyncSettings-CJWVtd87.js → SyncSettings-svGeswiw.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-61lrQ_lS.js → Tasks-ClISj6oK.js} +1 -1
- package/src/assets/web-panel/assets/Templates-CmO-Fp7r.js +1 -0
- package/src/assets/web-panel/assets/Tenant-a3Ac3lxz.js +1 -0
- package/src/assets/web-panel/assets/{Terminal-CP15hcNS.js → Terminal-DyJIRh81.js} +2 -2
- package/src/assets/web-panel/assets/TimelineRenderer-acXS5N5h.js +1 -0
- package/src/assets/web-panel/assets/Tokens-DJW0KqV4.js +1 -0
- package/src/assets/web-panel/assets/{Trigger-BLCQEOF2.js → Trigger-0HeTi4r6.js} +1 -1
- package/src/assets/web-panel/assets/Trust-aFym34eo.js +1 -0
- package/src/assets/web-panel/assets/{UkeySign-Dwp0zXQ6.js → UkeySign-CYhszHJv.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-CIHA55Sx.js → VideoEditing-BbJxPF9X.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-Hjajvnto.js → Wallet-DDthEwiu.js} +3 -3
- package/src/assets/web-panel/assets/{WebAuthn-DqSURUvI.js → WebAuthn-DQ6McYPg.js} +4 -4
- package/src/assets/web-panel/assets/{WorkflowEditor-B5bHRwUG.js → WorkflowEditor-CnF8zRsi.js} +1 -1
- package/src/assets/web-panel/assets/{chat-DsheLKkG.js → chat-Dzkx2gTP.js} +1 -1
- package/src/assets/web-panel/assets/{colors-CST2UkgL.js → colors-35T51Oo5.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-yqEoy1L7.js → compact-item-TtzNrlu1.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-Jwp78HFY.js → createContext-Y1LkWrYb.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-D_lwLZ6O.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-CVS5mFEP.js → hasIn-BOYw1BgS.js} +1 -1
- package/src/assets/web-panel/assets/{index-Crk4KWLW.js → index-8jffdb6b.js} +1 -1
- package/src/assets/web-panel/assets/{index-CDAPnZUs.js → index-B7aKAZzS.js} +1 -1
- package/src/assets/web-panel/assets/index-BGPEaeB1.js +1 -0
- package/src/assets/web-panel/assets/index-BKRlWHUp.js +1 -0
- package/src/assets/web-panel/assets/{index-BCHAUdel.js → index-BSpFe6j5.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cjig5i3a.js → index-BWgNn9As.js} +28 -26
- package/src/assets/web-panel/assets/{index-Dxljh9Fu.js → index-Bs69SI96.js} +2 -2
- package/src/assets/web-panel/assets/{index-BLTUVd0V.js → index-BtlJWaSP.js} +2 -2
- package/src/assets/web-panel/assets/index-Bw1iOGhM.js +1 -0
- package/src/assets/web-panel/assets/{index-CjfN2CpJ.js → index-BxWUEFKy.js} +1 -1
- package/src/assets/web-panel/assets/index-C7Wt5feN.js +1 -0
- package/src/assets/web-panel/assets/index-CEwuCM44.js +1 -0
- package/src/assets/web-panel/assets/index-CLqC2sRT.js +3 -0
- package/src/assets/web-panel/assets/{index-Dgyn8-wN.js → index-CPGNc2tF.js} +2 -2
- package/src/assets/web-panel/assets/{index-AnW25bEq.js → index-CVrUs6rf.js} +1 -1
- package/src/assets/web-panel/assets/index-CZ16GlOs.js +1 -0
- package/src/assets/web-panel/assets/{index-Cy0dNzkp.js → index-CdyFg4FI.js} +1 -1
- package/src/assets/web-panel/assets/{index-C0FZScMe.js → index-CqhwG1ox.js} +4 -4
- package/src/assets/web-panel/assets/index-Cs82BHAj.js +1 -0
- package/src/assets/web-panel/assets/{index-CRBbVS43.js → index-D2od7Bgx.js} +1 -1
- package/src/assets/web-panel/assets/{index-DxaU_lza.js → index-D47robkX.js} +1 -1
- package/src/assets/web-panel/assets/{index-CSBPc-sI.js → index-D4BgCBa3.js} +2 -2
- package/src/assets/web-panel/assets/index-D6tG4B25.js +55 -0
- package/src/assets/web-panel/assets/index-D7dCBw_M.js +1 -0
- package/src/assets/web-panel/assets/index-DAizH273.js +21 -0
- package/src/assets/web-panel/assets/{index-CzsKK0tQ.js → index-DFhlelzN.js} +2 -2
- package/src/assets/web-panel/assets/index-DhMSOd_2.js +1 -0
- package/src/assets/web-panel/assets/index-Dhfw-BXs.js +12 -0
- package/src/assets/web-panel/assets/index-DjeUZxE5.js +1 -0
- package/src/assets/web-panel/assets/{index-DDAigsel.js → index-DmkG479D.js} +2 -2
- package/src/assets/web-panel/assets/index-Dof7lWA_.js +13 -0
- package/src/assets/web-panel/assets/{index-De600Jjm.js → index-Dx0hIfC-.js} +1 -1
- package/src/assets/web-panel/assets/index-DzzgU4IU.js +1 -0
- package/src/assets/web-panel/assets/index-G4ClSmJc.js +1 -0
- package/src/assets/web-panel/assets/{index-CtyEOGuj.js → index-KgXrlfNF.js} +1 -1
- package/src/assets/web-panel/assets/{index-SjxCCf5h.js → index-NM9Oke2k.js} +2 -2
- package/src/assets/web-panel/assets/{index-R-VGFpi6.js → index-XIuZV9by.js} +3 -3
- package/src/assets/web-panel/assets/{index-BzetOasL.js → index-hyqEKkBT.js} +1 -1
- package/src/assets/web-panel/assets/{index-BuI27WSx.js → index-zF77jnI2.js} +2 -2
- package/src/assets/web-panel/assets/{initDefaultProps-ClAjrsQ-.js → initDefaultProps-DD5y5msp.js} +1 -1
- package/src/assets/web-panel/assets/motion-BuoutMia.js +11 -0
- package/src/assets/web-panel/assets/{move-DMelzIW-.js → move-DkpjqOXg.js} +1 -1
- package/src/assets/web-panel/assets/mtc-parser-BmGJD-v-.js +1 -0
- package/src/assets/web-panel/assets/{omit-oxecfU3b.js → omit-DftUE0Sz.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-DJ5F5M6x.js → pickAttrs-DDRb9FIu.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-DdbKsant.js → placementArrow-DPgtxOOD.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-BUuM5cmS.js → responsiveObserve-BQFRzFeZ.js} +1 -1
- package/src/assets/web-panel/assets/{slide-DSV0ccvR.js → slide-DQ4lmUSz.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-B-6oLAXf.js → statusUtils-C1TR4ZiQ.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-DdCAHtsZ.js → styleChecker-B-Suj978.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-ZORkcoZd.js → useFlexGapSupport-Q7bDZ3EI.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-BFp46LoP.js → useFs-DHjRLyQH.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-H7rxgbdW.js → usePersonalDataHub-lQOvCvCr.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-eIq4Tk0J.js → vnode-Ie0g4-p3.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-CTNrv8-H.js → zoom-CHP0YEoH.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/commands/agent.js +10 -0
- package/src/commands/ide.js +60 -5
- package/src/commands/incentive.js +22 -22
- package/src/commands/mtc.js +5 -1
- package/src/gateways/ws/message-dispatcher.js +4 -1
- package/src/gateways/ws/ws-session-gateway.js +56 -4
- package/src/harness/background-task-manager.js +5 -1
- package/src/harness/jsonl-session-store.js +51 -0
- package/src/harness/mcp-client.js +62 -21
- package/src/harness/prompt-compressor.js +24 -5
- package/src/harness/worktree-isolator.js +5 -1
- package/src/lib/agent-core.js +1 -0
- package/src/lib/autonomous-agent.js +29 -0
- package/src/lib/chat-intent-service.js +14 -2
- package/src/lib/checkpoint-store.js +31 -4
- package/src/lib/claude-code-bridge.js +48 -3
- package/src/lib/cost-budget.js +13 -2
- package/src/lib/credential-guard.js +38 -0
- package/src/lib/git-integration.js +11 -2
- package/src/lib/goal-store.js +11 -0
- package/src/lib/hook-manager.js +4 -0
- package/src/lib/jetbrains-bridge.js +147 -0
- package/src/lib/jsonl-session-store.js +1 -0
- package/src/lib/llm-pricing.js +11 -4
- package/src/lib/llm-providers.js +11 -1
- package/src/lib/mcp-oauth.js +12 -0
- package/src/lib/mcp-serve.js +29 -0
- package/src/lib/orchestrator.js +38 -2
- package/src/lib/repl-bang-memorize.js +9 -1
- package/src/lib/runnable-provider.js +7 -1
- package/src/lib/session-insights.js +13 -6
- package/src/lib/session-usage.js +21 -3
- package/src/lib/status-line.cjs +4 -1
- package/src/lib/sub-agent-context.js +9 -0
- package/src/repl/agent-repl.js +29 -0
- package/src/runtime/agent-core.js +4 -2
- package/src/runtime/headless-runner.js +13 -0
- package/src/runtime/headless-stream.js +9 -0
- package/src/runtime/mcp-config.js +72 -3
- package/src/runtime/pipe-safety.js +51 -0
- package/src/runtime/policies/agent-policy.js +3 -0
- package/src/assets/web-panel/assets/AIOps-BsDJlD_l.js +0 -1
- package/src/assets/web-panel/assets/Audit-BKlFbLdl.js +0 -1
- package/src/assets/web-panel/assets/ChatBubbleRenderer-DgvpwU5o.js +0 -1
- package/src/assets/web-panel/assets/Codegen-tKWGzajw.js +0 -1
- package/src/assets/web-panel/assets/Community-CmLuPBUU.js +0 -1
- package/src/assets/web-panel/assets/Compliance-PZw0aBLI.js +0 -1
- package/src/assets/web-panel/assets/Crosschain-Sb-uM7Ty.js +0 -1
- package/src/assets/web-panel/assets/Dashboard-Btag698v.js +0 -3
- package/src/assets/web-panel/assets/Federation-1jhstF5P.js +0 -1
- package/src/assets/web-panel/assets/Governance-BQrWksKt.js +0 -1
- package/src/assets/web-panel/assets/Inference-jEBTp12v.js +0 -1
- package/src/assets/web-panel/assets/KnowledgeGraph-tDiV2taf.js +0 -1
- package/src/assets/web-panel/assets/Marketplace-9Yi32avt.js +0 -1
- package/src/assets/web-panel/assets/MobileBridge-MfUfkHA0.js +0 -1
- package/src/assets/web-panel/assets/NLProgramming-DO3CZ0_z.js +0 -1
- package/src/assets/web-panel/assets/PdhVaultBrowser-Ck1zCLe6.js +0 -7
- package/src/assets/web-panel/assets/Pipeline-BZ7wRzG1.js +0 -1
- package/src/assets/web-panel/assets/Privacy-BJ6qj9Hv.js +0 -1
- package/src/assets/web-panel/assets/Projects-y1WbI337.js +0 -1
- package/src/assets/web-panel/assets/Recommend-Bju04wTd.js +0 -1
- package/src/assets/web-panel/assets/Reputation-BUPa3nEk.js +0 -1
- package/src/assets/web-panel/assets/Row-WYqqaq_5.js +0 -1
- package/src/assets/web-panel/assets/Search-CrfwJF0R.js +0 -1
- package/src/assets/web-panel/assets/Sla-CrMzaEi2.js +0 -1
- package/src/assets/web-panel/assets/SpeechSettings-jTDOM5eY.js +0 -1
- package/src/assets/web-panel/assets/Templates-Cg-iF2g1.js +0 -1
- package/src/assets/web-panel/assets/Tenant-DrDv1FFc.js +0 -1
- package/src/assets/web-panel/assets/TimelineRenderer-Sl5uXrkN.js +0 -1
- package/src/assets/web-panel/assets/Tokens-yRIZTVsR.js +0 -1
- package/src/assets/web-panel/assets/Trust-D5xq8z6s.js +0 -1
- package/src/assets/web-panel/assets/community-parser-Cuyslaku.js +0 -3
- package/src/assets/web-panel/assets/devWarning-C8to6gQk.js +0 -1
- package/src/assets/web-panel/assets/index-BeblNJDH.js +0 -1
- package/src/assets/web-panel/assets/index-C130LLPq.js +0 -1
- package/src/assets/web-panel/assets/index-C8rq85gu.js +0 -1
- package/src/assets/web-panel/assets/index-CIE2n8k_.js +0 -1
- package/src/assets/web-panel/assets/index-CTd3lDxp.js +0 -13
- package/src/assets/web-panel/assets/index-CUWj5L2s.js +0 -1
- package/src/assets/web-panel/assets/index-ChRL6rgA.js +0 -3
- package/src/assets/web-panel/assets/index-CrlRa054.js +0 -1
- package/src/assets/web-panel/assets/index-Cu6Ql64n.js +0 -1
- package/src/assets/web-panel/assets/index-Cx_t5rWw.js +0 -12
- package/src/assets/web-panel/assets/index-DZfnYE6e.js +0 -1
- package/src/assets/web-panel/assets/index-D_1b7uh6.js +0 -55
- package/src/assets/web-panel/assets/index-D_e9gwfn.js +0 -1
- package/src/assets/web-panel/assets/index-DbxAlpPC.js +0 -21
- package/src/assets/web-panel/assets/index-DiK4vSZa.js +0 -1
- package/src/assets/web-panel/assets/index-DpYn5v2k.js +0 -1
- package/src/assets/web-panel/assets/index-diWkx2Wq.js +0 -1
- package/src/assets/web-panel/assets/motion-BalXv_60.js +0 -11
- package/src/assets/web-panel/assets/mtc-parser-CEQffxds.js +0 -1
|
@@ -482,8 +482,8 @@ export function registerIncentiveCommand(program) {
|
|
|
482
482
|
.command("register-account-v2 <account-id>")
|
|
483
483
|
.description("V2: register a tracked account (tags ACTIVE)")
|
|
484
484
|
.option("-m, --metadata <meta>", "Metadata (JSON)")
|
|
485
|
-
.action((accountId, opts) => {
|
|
486
|
-
const ctx = bootstrap();
|
|
485
|
+
.action(async (accountId, opts) => {
|
|
486
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
487
487
|
try {
|
|
488
488
|
const db = _dbFromCtx(ctx);
|
|
489
489
|
const metadata = parseJsonOption(opts.metadata, "--metadata");
|
|
@@ -511,8 +511,8 @@ export function registerIncentiveCommand(program) {
|
|
|
511
511
|
.description("V2: transition account status (active|frozen|closed)")
|
|
512
512
|
.option("-r, --reason <reason>", "Reason")
|
|
513
513
|
.option("-m, --metadata <meta>", "Metadata (JSON)")
|
|
514
|
-
.action((accountId, status, opts) => {
|
|
515
|
-
const ctx = bootstrap();
|
|
514
|
+
.action(async (accountId, status, opts) => {
|
|
515
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
516
516
|
try {
|
|
517
517
|
const db = _dbFromCtx(ctx);
|
|
518
518
|
const metadata = parseJsonOption(opts.metadata, "--metadata");
|
|
@@ -530,8 +530,8 @@ export function registerIncentiveCommand(program) {
|
|
|
530
530
|
.command("freeze-account <account-id>")
|
|
531
531
|
.description("V2: shortcut → frozen")
|
|
532
532
|
.option("-r, --reason <reason>", "Reason")
|
|
533
|
-
.action((accountId, opts) => {
|
|
534
|
-
const ctx = bootstrap();
|
|
533
|
+
.action(async (accountId, opts) => {
|
|
534
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
535
535
|
try {
|
|
536
536
|
const db = _dbFromCtx(ctx);
|
|
537
537
|
const entry = freezeAccount(db, accountId, opts.reason);
|
|
@@ -545,8 +545,8 @@ export function registerIncentiveCommand(program) {
|
|
|
545
545
|
.command("unfreeze-account <account-id>")
|
|
546
546
|
.description("V2: shortcut → active (from frozen)")
|
|
547
547
|
.option("-r, --reason <reason>", "Reason")
|
|
548
|
-
.action((accountId, opts) => {
|
|
549
|
-
const ctx = bootstrap();
|
|
548
|
+
.action(async (accountId, opts) => {
|
|
549
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
550
550
|
try {
|
|
551
551
|
const db = _dbFromCtx(ctx);
|
|
552
552
|
const entry = unfreezeAccount(db, accountId, opts.reason);
|
|
@@ -560,8 +560,8 @@ export function registerIncentiveCommand(program) {
|
|
|
560
560
|
.command("close-account <account-id>")
|
|
561
561
|
.description("V2: shortcut → closed")
|
|
562
562
|
.option("-r, --reason <reason>", "Reason")
|
|
563
|
-
.action((accountId, opts) => {
|
|
564
|
-
const ctx = bootstrap();
|
|
563
|
+
.action(async (accountId, opts) => {
|
|
564
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
565
565
|
try {
|
|
566
566
|
const db = _dbFromCtx(ctx);
|
|
567
567
|
const entry = closeAccount(db, accountId, opts.reason);
|
|
@@ -578,8 +578,8 @@ export function registerIncentiveCommand(program) {
|
|
|
578
578
|
.requiredOption("-a, --amount <amount>", "Amount")
|
|
579
579
|
.option("-c, --contribution <id>", "Contribution ID")
|
|
580
580
|
.option("-m, --metadata <meta>", "Metadata (JSON)")
|
|
581
|
-
.action((claimId, opts) => {
|
|
582
|
-
const ctx = bootstrap();
|
|
581
|
+
.action(async (claimId, opts) => {
|
|
582
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
583
583
|
try {
|
|
584
584
|
const db = _dbFromCtx(ctx);
|
|
585
585
|
const metadata = parseJsonOption(opts.metadata, "--metadata");
|
|
@@ -613,8 +613,8 @@ export function registerIncentiveCommand(program) {
|
|
|
613
613
|
.description("V2: transition claim (pending|approved|paid|rejected)")
|
|
614
614
|
.option("-r, --reason <reason>", "Reason")
|
|
615
615
|
.option("-m, --metadata <meta>", "Metadata (JSON)")
|
|
616
|
-
.action((claimId, status, opts) => {
|
|
617
|
-
const ctx = bootstrap();
|
|
616
|
+
.action(async (claimId, status, opts) => {
|
|
617
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
618
618
|
try {
|
|
619
619
|
const db = _dbFromCtx(ctx);
|
|
620
620
|
const metadata = parseJsonOption(opts.metadata, "--metadata");
|
|
@@ -632,8 +632,8 @@ export function registerIncentiveCommand(program) {
|
|
|
632
632
|
.command("approve-claim <claim-id>")
|
|
633
633
|
.description("V2: shortcut → approved")
|
|
634
634
|
.option("-r, --reason <reason>", "Reason")
|
|
635
|
-
.action((claimId, opts) => {
|
|
636
|
-
const ctx = bootstrap();
|
|
635
|
+
.action(async (claimId, opts) => {
|
|
636
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
637
637
|
try {
|
|
638
638
|
const db = _dbFromCtx(ctx);
|
|
639
639
|
const entry = approveClaim(db, claimId, opts.reason);
|
|
@@ -647,8 +647,8 @@ export function registerIncentiveCommand(program) {
|
|
|
647
647
|
.command("reject-claim <claim-id>")
|
|
648
648
|
.description("V2: shortcut → rejected")
|
|
649
649
|
.option("-r, --reason <reason>", "Reason")
|
|
650
|
-
.action((claimId, opts) => {
|
|
651
|
-
const ctx = bootstrap();
|
|
650
|
+
.action(async (claimId, opts) => {
|
|
651
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
652
652
|
try {
|
|
653
653
|
const db = _dbFromCtx(ctx);
|
|
654
654
|
const entry = rejectClaim(db, claimId, opts.reason);
|
|
@@ -662,8 +662,8 @@ export function registerIncentiveCommand(program) {
|
|
|
662
662
|
.command("pay-claim <claim-id>")
|
|
663
663
|
.description("V2: shortcut → paid (stamps paidAt)")
|
|
664
664
|
.option("-r, --reason <reason>", "Reason")
|
|
665
|
-
.action((claimId, opts) => {
|
|
666
|
-
const ctx = bootstrap();
|
|
665
|
+
.action(async (claimId, opts) => {
|
|
666
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
667
667
|
try {
|
|
668
668
|
const db = _dbFromCtx(ctx);
|
|
669
669
|
const entry = payClaim(db, claimId, opts.reason);
|
|
@@ -676,8 +676,8 @@ export function registerIncentiveCommand(program) {
|
|
|
676
676
|
inc
|
|
677
677
|
.command("auto-expire-unclaimed-claims")
|
|
678
678
|
.description("V2: bulk-reject stale PENDING claims")
|
|
679
|
-
.action(() => {
|
|
680
|
-
const ctx = bootstrap();
|
|
679
|
+
.action(async () => {
|
|
680
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
681
681
|
try {
|
|
682
682
|
const db = _dbFromCtx(ctx);
|
|
683
683
|
const expired = autoExpireUnclaimedClaims(db);
|
package/src/commands/mtc.js
CHANGED
|
@@ -595,7 +595,11 @@ export function registerMtcCommand(program) {
|
|
|
595
595
|
return;
|
|
596
596
|
}
|
|
597
597
|
if (result.ok) {
|
|
598
|
-
|
|
598
|
+
// `verify` reads envelope+landmark from disk and creates no signer,
|
|
599
|
+
// so signerInfo is never defined here — referencing it threw a
|
|
600
|
+
// ReferenceError that the catch reported as "verify failed" on a
|
|
601
|
+
// SUCCESSFUL verify. Use the stopgap banner directly.
|
|
602
|
+
logger.log(STOPGAP_BANNER);
|
|
599
603
|
logger.success(`Envelope verified`);
|
|
600
604
|
logger.log(
|
|
601
605
|
` ${chalk.bold("Subject:")} ${result.leaf.subject || "(no subject)"}`,
|
|
@@ -23,7 +23,10 @@ export function createWsMessageDispatcher(server) {
|
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
const client = server.clients.get(clientId);
|
|
26
|
-
|
|
26
|
+
// client may be undefined if it was removed mid-dispatch (heartbeat sweep
|
|
27
|
+
// / disconnect); treat a missing client as unauthenticated rather than
|
|
28
|
+
// throwing a TypeError (every sibling call site null-checks the client).
|
|
29
|
+
if (server.token && !client?.authenticated && type !== "auth") {
|
|
27
30
|
server._send(ws, {
|
|
28
31
|
id,
|
|
29
32
|
type: "error",
|
|
@@ -109,6 +109,20 @@ export class WSSessionManager {
|
|
|
109
109
|
this.maxSessions = Number.isFinite(options.maxSessions)
|
|
110
110
|
? options.maxSessions
|
|
111
111
|
: 100;
|
|
112
|
+
|
|
113
|
+
// Cap patch bookkeeping per session. `patchHistory` is append-only (every
|
|
114
|
+
// applyPatch/rejectPatch pushes, nothing trims) and `pendingPatches` grows
|
|
115
|
+
// with each proposePatch — both are serialized in full by
|
|
116
|
+
// _persistSessionState on every session op, so unbounded growth means
|
|
117
|
+
// O(n) memory AND an O(n²) rewrite cost over a long session (and a tool
|
|
118
|
+
// proposing many large before/after/diff blobs amplifies it). Keep a
|
|
119
|
+
// recent window; oldest entries fall off (FIFO).
|
|
120
|
+
this.maxPatchHistory = Number.isFinite(options.maxPatchHistory)
|
|
121
|
+
? options.maxPatchHistory
|
|
122
|
+
: 200;
|
|
123
|
+
this.maxPendingPatches = Number.isFinite(options.maxPendingPatches)
|
|
124
|
+
? options.maxPendingPatches
|
|
125
|
+
: 50;
|
|
112
126
|
}
|
|
113
127
|
|
|
114
128
|
_normalizeEnabledToolNames(enabledToolNames) {
|
|
@@ -499,9 +513,7 @@ export class WSSessionManager {
|
|
|
499
513
|
permanentMemory,
|
|
500
514
|
reviewState: metadata.reviewState || null,
|
|
501
515
|
pendingPatches: this._hydratePendingPatches(metadata.pendingPatches),
|
|
502
|
-
patchHistory:
|
|
503
|
-
? metadata.patchHistory
|
|
504
|
-
: [],
|
|
516
|
+
patchHistory: this._boundPatchHistory(metadata.patchHistory),
|
|
505
517
|
taskGraph: this._hydrateTaskGraph(metadata.taskGraph),
|
|
506
518
|
interaction: null,
|
|
507
519
|
createdAt: dbSession.created_at,
|
|
@@ -889,6 +901,15 @@ export class WSSessionManager {
|
|
|
889
901
|
if (!(session.pendingPatches instanceof Map)) {
|
|
890
902
|
session.pendingPatches = new Map();
|
|
891
903
|
}
|
|
904
|
+
// Bound un-reviewed proposals: evict the oldest pending (Map preserves
|
|
905
|
+
// insertion order) once at cap. 50 unresolved patches is already
|
|
906
|
+
// pathological; dropping the stalest keeps memory + persisted state bounded
|
|
907
|
+
// without blocking a legitimate new proposal.
|
|
908
|
+
while (session.pendingPatches.size >= this.maxPendingPatches) {
|
|
909
|
+
const oldestKey = session.pendingPatches.keys().next().value;
|
|
910
|
+
if (oldestKey === undefined) break;
|
|
911
|
+
session.pendingPatches.delete(oldestKey);
|
|
912
|
+
}
|
|
892
913
|
session.pendingPatches.set(patchId, patch);
|
|
893
914
|
session.lastActivity = now;
|
|
894
915
|
this._persistSessionState(sessionId);
|
|
@@ -917,11 +938,35 @@ export class WSSessionManager {
|
|
|
917
938
|
session.patchHistory = [];
|
|
918
939
|
}
|
|
919
940
|
session.patchHistory.push(patch);
|
|
941
|
+
this._trimPatchHistory(session);
|
|
920
942
|
session.lastActivity = patch.resolvedAt;
|
|
921
943
|
this._persistSessionState(sessionId);
|
|
922
944
|
return patch;
|
|
923
945
|
}
|
|
924
946
|
|
|
947
|
+
/**
|
|
948
|
+
* Keep `patchHistory` bounded to the most recent maxPatchHistory entries.
|
|
949
|
+
* Oldest fall off the front (FIFO) so memory + persisted JSON stay bounded.
|
|
950
|
+
*/
|
|
951
|
+
_trimPatchHistory(session) {
|
|
952
|
+
if (!Array.isArray(session.patchHistory)) return;
|
|
953
|
+
const overflow = session.patchHistory.length - this.maxPatchHistory;
|
|
954
|
+
if (overflow > 0) {
|
|
955
|
+
session.patchHistory.splice(0, overflow);
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
/**
|
|
960
|
+
* Bound a (possibly persisted/tampered) patchHistory array on load, keeping
|
|
961
|
+
* the most recent entries. Returns a fresh array (never the input).
|
|
962
|
+
*/
|
|
963
|
+
_boundPatchHistory(list) {
|
|
964
|
+
if (!Array.isArray(list)) return [];
|
|
965
|
+
return list.length > this.maxPatchHistory
|
|
966
|
+
? list.slice(list.length - this.maxPatchHistory)
|
|
967
|
+
: list.slice();
|
|
968
|
+
}
|
|
969
|
+
|
|
925
970
|
/**
|
|
926
971
|
* Discard a pending patch. Same bookkeeping as applyPatch but records a
|
|
927
972
|
* "rejected" decision instead.
|
|
@@ -944,6 +989,7 @@ export class WSSessionManager {
|
|
|
944
989
|
session.patchHistory = [];
|
|
945
990
|
}
|
|
946
991
|
session.patchHistory.push(patch);
|
|
992
|
+
this._trimPatchHistory(session);
|
|
947
993
|
session.lastActivity = patch.resolvedAt;
|
|
948
994
|
this._persistSessionState(sessionId);
|
|
949
995
|
return patch;
|
|
@@ -1339,7 +1385,13 @@ export class WSSessionManager {
|
|
|
1339
1385
|
_hydratePendingPatches(list) {
|
|
1340
1386
|
const map = new Map();
|
|
1341
1387
|
if (Array.isArray(list)) {
|
|
1342
|
-
|
|
1388
|
+
// Trim from the front so the most recent proposals survive a reload even
|
|
1389
|
+
// if a prior (or tampered) persisted state exceeded the cap.
|
|
1390
|
+
const bounded =
|
|
1391
|
+
list.length > this.maxPendingPatches
|
|
1392
|
+
? list.slice(list.length - this.maxPendingPatches)
|
|
1393
|
+
: list;
|
|
1394
|
+
for (const patch of bounded) {
|
|
1343
1395
|
if (patch && patch.patchId) {
|
|
1344
1396
|
map.set(patch.patchId, patch);
|
|
1345
1397
|
}
|
|
@@ -194,7 +194,11 @@ export class BackgroundTaskManager extends EventEmitter {
|
|
|
194
194
|
return history;
|
|
195
195
|
}
|
|
196
196
|
|
|
197
|
-
|
|
197
|
+
// offset + null === offset, so an offset-only page (no limit) used to
|
|
198
|
+
// slice(offset, offset) === [] and lose every item past the offset. Use
|
|
199
|
+
// history.length as the end when limit is null.
|
|
200
|
+
const end = limit === null ? history.length : offset + limit;
|
|
201
|
+
const items = history.slice(offset, end);
|
|
198
202
|
return {
|
|
199
203
|
items,
|
|
200
204
|
total: history.length,
|
|
@@ -21,7 +21,32 @@ function getSessionsDir() {
|
|
|
21
21
|
return dir;
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
+
/**
|
|
25
|
+
* A session id must be a single safe path segment. Ids are generated as
|
|
26
|
+
* `session-<ts>-<hex>`, but also arrive from CLI args (`cc agent --resume <id>`,
|
|
27
|
+
* `cc insights <id>`, `cc session show <id>`), so an id like `../../etc/x` would
|
|
28
|
+
* otherwise let sessionPath() read / append / delete a .jsonl OUTSIDE the
|
|
29
|
+
* sessions dir. Reject any separator or `..` (mirrors goal-store's
|
|
30
|
+
* isUnsafeGoalId / FileUploadService.isUnsafeSegment).
|
|
31
|
+
*/
|
|
32
|
+
export function isUnsafeSessionId(id) {
|
|
33
|
+
return (
|
|
34
|
+
id == null ||
|
|
35
|
+
id === "" ||
|
|
36
|
+
typeof id !== "string" ||
|
|
37
|
+
id.includes("/") ||
|
|
38
|
+
id.includes("\\") ||
|
|
39
|
+
id.includes("..")
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
24
43
|
export function sessionPath(sessionId) {
|
|
44
|
+
// Fail closed for path building: every write/delete goes through here, so a
|
|
45
|
+
// traversal id can never escape the sessions dir. Reads guard separately and
|
|
46
|
+
// degrade to not-found instead of throwing.
|
|
47
|
+
if (isUnsafeSessionId(sessionId)) {
|
|
48
|
+
throw new Error(`unsafe session id: ${String(sessionId).slice(0, 60)}`);
|
|
49
|
+
}
|
|
25
50
|
return join(getSessionsDir(), `${sessionId}.jsonl`);
|
|
26
51
|
}
|
|
27
52
|
|
|
@@ -69,6 +94,7 @@ export function appendCompactEvent(sessionId, stats) {
|
|
|
69
94
|
}
|
|
70
95
|
|
|
71
96
|
export function readEvents(sessionId) {
|
|
97
|
+
if (isUnsafeSessionId(sessionId)) return []; // traversal id → treat as empty
|
|
72
98
|
const filePath = sessionPath(sessionId);
|
|
73
99
|
if (!existsSync(filePath)) return [];
|
|
74
100
|
|
|
@@ -184,6 +210,7 @@ export function forkSession(sourceId) {
|
|
|
184
210
|
}
|
|
185
211
|
|
|
186
212
|
export function sessionExists(sessionId) {
|
|
213
|
+
if (isUnsafeSessionId(sessionId)) return false; // never resolve a traversal id
|
|
187
214
|
return existsSync(sessionPath(sessionId));
|
|
188
215
|
}
|
|
189
216
|
|
|
@@ -266,6 +293,15 @@ export function migrateLegacySessionFile(filePath, options = {}) {
|
|
|
266
293
|
}
|
|
267
294
|
|
|
268
295
|
export function validateJsonlSession(sessionId) {
|
|
296
|
+
if (isUnsafeSessionId(sessionId)) {
|
|
297
|
+
return {
|
|
298
|
+
sessionId,
|
|
299
|
+
valid: false,
|
|
300
|
+
reason: "invalid session id",
|
|
301
|
+
malformedLines: 0,
|
|
302
|
+
eventCount: 0,
|
|
303
|
+
};
|
|
304
|
+
}
|
|
269
305
|
const filePath = sessionPath(sessionId);
|
|
270
306
|
if (!existsSync(filePath)) {
|
|
271
307
|
return {
|
|
@@ -335,6 +371,21 @@ function performLegacySessionMigration(sourcePath, options) {
|
|
|
335
371
|
const legacy = normalizeLegacySession(parsed, basename(sourcePath, ".json"));
|
|
336
372
|
const sessionId = legacy.id;
|
|
337
373
|
|
|
374
|
+
// A legacy file carries its OWN `id` (payload.id), so a crafted file could
|
|
375
|
+
// name a traversal target like "../../evil". sessionPath() throws on write
|
|
376
|
+
// (the backstop), but fail-fast HERE with a clear reason so the migration
|
|
377
|
+
// doesn't burn retry attempts on a deterministic error.
|
|
378
|
+
if (isUnsafeSessionId(sessionId)) {
|
|
379
|
+
return {
|
|
380
|
+
file: sourcePath,
|
|
381
|
+
sessionId,
|
|
382
|
+
migrated: false,
|
|
383
|
+
failed: true,
|
|
384
|
+
dryRun: Boolean(options.dryRun),
|
|
385
|
+
reason: "unsafe session id in legacy file",
|
|
386
|
+
};
|
|
387
|
+
}
|
|
388
|
+
|
|
338
389
|
if (!options.force && sessionExists(sessionId)) {
|
|
339
390
|
return {
|
|
340
391
|
file: sourcePath,
|
|
@@ -814,19 +814,18 @@ export class MCPClient extends EventEmitter {
|
|
|
814
814
|
const contentType = response.headers?.get
|
|
815
815
|
? String(response.headers.get("content-type") || "").toLowerCase()
|
|
816
816
|
: "";
|
|
817
|
+
// Bound the response body the same way the stdio path bounds its line
|
|
818
|
+
// buffer (per-server maxBufferChars override; 0 disables).
|
|
819
|
+
const cap = Number.isFinite(entry.config?.maxBufferChars)
|
|
820
|
+
? entry.config.maxBufferChars
|
|
821
|
+
: MCP_MAX_BUFFER_CHARS;
|
|
817
822
|
|
|
818
823
|
let envelope;
|
|
819
824
|
if (contentType.includes("text/event-stream")) {
|
|
820
|
-
envelope = await _extractSseResponse(response, id);
|
|
825
|
+
envelope = await _extractSseResponse(response, id, cap);
|
|
821
826
|
} else {
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
? await response.json()
|
|
825
|
-
: JSON.parse(
|
|
826
|
-
typeof response.text === "function"
|
|
827
|
-
? await response.text()
|
|
828
|
-
: "",
|
|
829
|
-
);
|
|
827
|
+
const text = await _readBodyCapped(response, cap);
|
|
828
|
+
envelope = text ? JSON.parse(text) : null;
|
|
830
829
|
}
|
|
831
830
|
|
|
832
831
|
if (!envelope || typeof envelope !== "object") {
|
|
@@ -957,28 +956,70 @@ export class MCPClient extends EventEmitter {
|
|
|
957
956
|
}
|
|
958
957
|
|
|
959
958
|
/**
|
|
960
|
-
*
|
|
961
|
-
*
|
|
962
|
-
*
|
|
959
|
+
* Read an HTTP response body to text with a hard size cap, mirroring the stdio
|
|
960
|
+
* transport's MCP_MAX_BUFFER_CHARS guard. The per-call timeout bounds TIME but
|
|
961
|
+
* not MEMORY: a malicious/buggy HTTP MCP server could stream a multi-GB body
|
|
962
|
+
* within the timeout and OOM the client. When the body is a readable stream
|
|
963
|
+
* (real fetch) we accumulate with a running cap and cancel on overflow; for a
|
|
964
|
+
* non-stream response (test mocks) we fall back to text() + a post-hoc check.
|
|
965
|
+
* A declared Content-Length over the cap is rejected before any read. cap<=0
|
|
966
|
+
* disables the limit.
|
|
963
967
|
*/
|
|
964
|
-
async function
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
968
|
+
async function _readBodyCapped(response, cap) {
|
|
969
|
+
if (
|
|
970
|
+
cap > 0 &&
|
|
971
|
+
response.headers &&
|
|
972
|
+
typeof response.headers.get === "function"
|
|
973
|
+
) {
|
|
974
|
+
const declared = Number(response.headers.get("content-length"));
|
|
975
|
+
if (Number.isFinite(declared) && declared > cap) {
|
|
976
|
+
throw new Error(
|
|
977
|
+
`MCP HTTP response exceeds the ${cap}-byte cap (content-length ${declared})`,
|
|
978
|
+
);
|
|
979
|
+
}
|
|
980
|
+
}
|
|
981
|
+
if (response.body && typeof response.body.getReader === "function") {
|
|
969
982
|
const reader = response.body.getReader();
|
|
970
983
|
const decoder = new TextDecoder("utf-8");
|
|
971
984
|
const chunks = [];
|
|
972
|
-
|
|
985
|
+
let total = 0;
|
|
986
|
+
for (;;) {
|
|
973
987
|
const { value, done } = await reader.read();
|
|
974
988
|
if (done) break;
|
|
989
|
+
total += value && value.length ? value.length : 0;
|
|
990
|
+
if (cap > 0 && total > cap) {
|
|
991
|
+
try {
|
|
992
|
+
await reader.cancel();
|
|
993
|
+
} catch {
|
|
994
|
+
/* best-effort — the throw below is what matters */
|
|
995
|
+
}
|
|
996
|
+
throw new Error(
|
|
997
|
+
`MCP HTTP response exceeded the ${cap}-byte cap (runaway server)`,
|
|
998
|
+
);
|
|
999
|
+
}
|
|
975
1000
|
chunks.push(decoder.decode(value, { stream: true }));
|
|
976
1001
|
}
|
|
977
1002
|
chunks.push(decoder.decode());
|
|
978
|
-
|
|
979
|
-
}
|
|
980
|
-
|
|
1003
|
+
return chunks.join("");
|
|
1004
|
+
}
|
|
1005
|
+
if (typeof response.text !== "function") {
|
|
1006
|
+
throw new Error("HTTP response is not readable");
|
|
981
1007
|
}
|
|
1008
|
+
const text = await response.text();
|
|
1009
|
+
if (cap > 0 && text.length > cap) {
|
|
1010
|
+
throw new Error(`MCP HTTP response exceeded the ${cap}-char cap`);
|
|
1011
|
+
}
|
|
1012
|
+
return text;
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
/**
|
|
1016
|
+
* Parse a `text/event-stream` HTTP response body and return the first
|
|
1017
|
+
* JSON-RPC envelope whose id matches `requestId`. Tolerates multiple
|
|
1018
|
+
* `data:` chunks, comments, and non-JSON-RPC events. `cap` bounds the body
|
|
1019
|
+
* size (see _readBodyCapped).
|
|
1020
|
+
*/
|
|
1021
|
+
async function _extractSseResponse(response, requestId, cap = 0) {
|
|
1022
|
+
const text = await _readBodyCapped(response, cap);
|
|
982
1023
|
|
|
983
1024
|
// Split into events on blank line, parse each event's concatenated `data:` lines.
|
|
984
1025
|
const events = text.split(/\r?\n\r?\n/);
|
|
@@ -12,6 +12,12 @@
|
|
|
12
12
|
import { createHash } from "node:crypto";
|
|
13
13
|
import { feature, featureVariant } from "../lib/feature-flags.js";
|
|
14
14
|
|
|
15
|
+
// Bounds for the fuzzy near-dup pass in _deduplicate (see there). Generous
|
|
16
|
+
// enough that real near-dups are still caught; small enough that compaction of
|
|
17
|
+
// a long, tool-heavy history stays sub-second instead of O(n²·content).
|
|
18
|
+
const DEDUP_JACCARD_MAX_CHARS = 4000;
|
|
19
|
+
const DEDUP_FUZZY_WINDOW = 100;
|
|
20
|
+
|
|
15
21
|
export function estimateTokens(text) {
|
|
16
22
|
if (!text) return 0;
|
|
17
23
|
const chineseChars = (text.match(/[\u4e00-\u9fa5]/g) || []).length;
|
|
@@ -320,8 +326,18 @@ export class PromptCompressor {
|
|
|
320
326
|
const last = [...messages].reverse().find((m) => m.role === "user");
|
|
321
327
|
const rest = messages.filter((m) => m.role !== "system" && m !== last);
|
|
322
328
|
|
|
329
|
+
// Bound the fuzzy pass so it stays usable on the very conversations
|
|
330
|
+
// compaction targets (long + tool-heavy). The naive version compared every
|
|
331
|
+
// message against EVERY prior one (O(n²)) and ran jaccard over full content
|
|
332
|
+
// (O(content) per compare) — 300 msgs × ~8 KB tool results blocked ~3.4 s.
|
|
333
|
+
// • Exact dedup (the md5 hash set) still covers ALL messages.
|
|
334
|
+
// • The fuzzy near-dup compare uses a capped prefix (char-set jaccard is
|
|
335
|
+
// alphabet-bounded, so a prefix barely changes the decision) and only the
|
|
336
|
+
// most-recent kept entries (near-dups cluster; this can only UNDER-dedup,
|
|
337
|
+
// never drop a genuinely-distinct message).
|
|
323
338
|
const seen = new Map();
|
|
324
339
|
const deduped = [];
|
|
340
|
+
const recent = []; // rolling window of recent kept {capped} contents
|
|
325
341
|
|
|
326
342
|
for (const msg of rest) {
|
|
327
343
|
const content = getContent(msg);
|
|
@@ -329,12 +345,13 @@ export class PromptCompressor {
|
|
|
329
345
|
|
|
330
346
|
if (seen.has(hash)) continue;
|
|
331
347
|
|
|
348
|
+
const capped =
|
|
349
|
+
content.length > DEDUP_JACCARD_MAX_CHARS
|
|
350
|
+
? content.slice(0, DEDUP_JACCARD_MAX_CHARS)
|
|
351
|
+
: content;
|
|
332
352
|
let isDup = false;
|
|
333
|
-
for (const
|
|
334
|
-
if (
|
|
335
|
-
jaccardSimilarity(content, getContent(existing)) >=
|
|
336
|
-
this.similarityThreshold
|
|
337
|
-
) {
|
|
353
|
+
for (const prev of recent) {
|
|
354
|
+
if (jaccardSimilarity(capped, prev) >= this.similarityThreshold) {
|
|
338
355
|
isDup = true;
|
|
339
356
|
break;
|
|
340
357
|
}
|
|
@@ -343,6 +360,8 @@ export class PromptCompressor {
|
|
|
343
360
|
if (!isDup) {
|
|
344
361
|
seen.set(hash, msg);
|
|
345
362
|
deduped.push(msg);
|
|
363
|
+
recent.push(capped);
|
|
364
|
+
if (recent.length > DEDUP_FUZZY_WINDOW) recent.shift();
|
|
346
365
|
}
|
|
347
366
|
}
|
|
348
367
|
|
|
@@ -462,7 +462,11 @@ export function applyWorktreeAutomationCandidate(
|
|
|
462
462
|
const commitMessage =
|
|
463
463
|
options.commitMessage ||
|
|
464
464
|
`Resolve ${filePath} via ${candidate.label || candidateId}`;
|
|
465
|
-
|
|
465
|
+
// Free-text message → argv (no shell) so `$()` / backticks / quotes in a
|
|
466
|
+
// caller-supplied commitMessage can't inject a command. The prior
|
|
467
|
+
// `-m "${msg.replace(/"/g,'\\"')}"` only neutralized double quotes — the
|
|
468
|
+
// same gap already fixed in mergeWorktree.
|
|
469
|
+
gitExecArgs(["commit", "-m", commitMessage], worktree.path);
|
|
466
470
|
|
|
467
471
|
const diff = diffWorktree(repoDir, branchName, { baseBranch });
|
|
468
472
|
const filesChanged = diff.summary?.filesChanged || 0;
|
package/src/lib/agent-core.js
CHANGED
|
@@ -48,6 +48,12 @@ export class CLIAutonomousAgent extends EventEmitter {
|
|
|
48
48
|
this._initialized = false;
|
|
49
49
|
this._maxIterations = 20;
|
|
50
50
|
this._maxRetries = 3;
|
|
51
|
+
// Cap retained goal history. submitGoal stores every goal in _goals and
|
|
52
|
+
// nothing removes it; the REPL keeps one long-lived agent for the whole
|
|
53
|
+
// session, so goals (each holding steps/errors/result) accumulate without
|
|
54
|
+
// bound — and listGoals() returns the whole growing Map. Prune the oldest
|
|
55
|
+
// TERMINAL goals beyond this cap; running/pending/paused goals are kept.
|
|
56
|
+
this._maxGoals = 200;
|
|
51
57
|
}
|
|
52
58
|
|
|
53
59
|
/**
|
|
@@ -58,12 +64,14 @@ export class CLIAutonomousAgent extends EventEmitter {
|
|
|
58
64
|
toolExecutor,
|
|
59
65
|
hookManager,
|
|
60
66
|
maxIterations,
|
|
67
|
+
maxGoals,
|
|
61
68
|
iterationBudget,
|
|
62
69
|
} = {}) {
|
|
63
70
|
this._llmChat = llmChat || null;
|
|
64
71
|
this._toolExecutor = toolExecutor || null;
|
|
65
72
|
this._hookManager = hookManager || null;
|
|
66
73
|
if (maxIterations) this._maxIterations = maxIterations;
|
|
74
|
+
if (Number.isFinite(maxGoals) && maxGoals > 0) this._maxGoals = maxGoals;
|
|
67
75
|
this._iterationBudget = iterationBudget || null; // shared budget from caller
|
|
68
76
|
this._initialized = true;
|
|
69
77
|
}
|
|
@@ -96,6 +104,7 @@ export class CLIAutonomousAgent extends EventEmitter {
|
|
|
96
104
|
};
|
|
97
105
|
|
|
98
106
|
this._goals.set(goalId, goal);
|
|
107
|
+
this._pruneGoals();
|
|
99
108
|
this.emit("goal:submitted", { goalId, description });
|
|
100
109
|
|
|
101
110
|
// Start the ReAct loop asynchronously
|
|
@@ -108,6 +117,26 @@ export class CLIAutonomousAgent extends EventEmitter {
|
|
|
108
117
|
return { goalId };
|
|
109
118
|
}
|
|
110
119
|
|
|
120
|
+
/**
|
|
121
|
+
* Bound retained goal history to _maxGoals. Evicts the oldest TERMINAL goals
|
|
122
|
+
* (completed/failed/cancelled) FIFO; a still-active goal (pending/running/
|
|
123
|
+
* paused) is never dropped, so a burst of concurrent goals can briefly exceed
|
|
124
|
+
* the cap rather than losing live work.
|
|
125
|
+
*/
|
|
126
|
+
_pruneGoals() {
|
|
127
|
+
if (this._goals.size <= this._maxGoals) return;
|
|
128
|
+
for (const [id, g] of this._goals) {
|
|
129
|
+
if (this._goals.size <= this._maxGoals) break;
|
|
130
|
+
if (
|
|
131
|
+
g.status === GoalStatus.COMPLETED ||
|
|
132
|
+
g.status === GoalStatus.FAILED ||
|
|
133
|
+
g.status === GoalStatus.CANCELLED
|
|
134
|
+
) {
|
|
135
|
+
this._goals.delete(id);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
111
140
|
/**
|
|
112
141
|
* Pause a running goal.
|
|
113
142
|
*/
|
|
@@ -319,7 +319,12 @@ const FOLLOWUP_RULES = {
|
|
|
319
319
|
/(颜色|字体|大小|位置|标题).*(是|用|为)/,
|
|
320
320
|
/^.{1,20}(应该|具体)(是|为)/,
|
|
321
321
|
/^数据(来源|是)/,
|
|
322
|
-
|
|
322
|
+
// De-catastrophized: the old `/.*(用|采用).*(字体…)/` had a leading greedy
|
|
323
|
+
// `.*` overlapping `(用)`, causing exponential backtracking (a 2 KB input
|
|
324
|
+
// of "用" took ~3 s). `.test()` already searches anywhere, so the leading
|
|
325
|
+
// `.*` is redundant; a lazy middle drops it to quadratic, and the input
|
|
326
|
+
// cap in ruleBasedClassify bounds that.
|
|
327
|
+
/(用|采用)[\s\S]*?(字体|颜色|大小)/,
|
|
323
328
|
],
|
|
324
329
|
},
|
|
325
330
|
CANCEL_TASK: {
|
|
@@ -334,8 +339,15 @@ const FOLLOWUP_RULES = {
|
|
|
334
339
|
},
|
|
335
340
|
};
|
|
336
341
|
|
|
342
|
+
// A follow-up intent (continue / modify / clarify / cancel) is determinable
|
|
343
|
+
// from the start of the message; cap the text the heuristic regexes see so a
|
|
344
|
+
// huge paste can't drive their (quadratic) backtracking into a DoS. The full
|
|
345
|
+
// input is still forwarded to the LLM path / downstream — only this local
|
|
346
|
+
// classification is truncated.
|
|
347
|
+
const MAX_CLASSIFY_INPUT = 2000;
|
|
348
|
+
|
|
337
349
|
function ruleBasedClassify(userInput) {
|
|
338
|
-
const input = (userInput || "").trim();
|
|
350
|
+
const input = (userInput || "").trim().slice(0, MAX_CLASSIFY_INPUT);
|
|
339
351
|
|
|
340
352
|
if (input.length === 0) {
|
|
341
353
|
return {
|