chainlesschain 0.162.124 → 0.162.126
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-BrTOcqIJ.js +1 -0
- package/src/assets/web-panel/assets/{ActionButton-COjZZKWG.js → ActionButton-na9cocmf.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-4ZDZE7lc.js → Analytics-BtDygkpt.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-CYH5k2Rp.js → AppLayout-D_t5CS18.js} +5 -5
- package/src/assets/web-panel/assets/Audit-BYMZccCy.js +1 -0
- package/src/assets/web-panel/assets/{Backup-CiX61Qc2.js → Backup-D2qDfAdL.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-CDxIQokd.js → BaseInput-kj456Ju9.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-CaKgdRab.js → Chat-BY0A0ZLE.js} +4 -4
- package/src/assets/web-panel/assets/ChatBubbleRenderer-D1VvrOPQ.js +1 -0
- package/src/assets/web-panel/assets/{Checkbox-CjiWfu4s.js → Checkbox-DiBhtXsh.js} +1 -1
- package/src/assets/web-panel/assets/Codegen-B1ER0L0j.js +1 -0
- package/src/assets/web-panel/assets/{Col-ZKWREyNs.js → Col-CjLmza7D.js} +1 -1
- package/src/assets/web-panel/assets/Community-BNId05U7.js +1 -0
- package/src/assets/web-panel/assets/{Compact-DqknM98n.js → Compact-BuuMj7K1.js} +1 -1
- package/src/assets/web-panel/assets/Compliance-DUuDycaJ.js +1 -0
- package/src/assets/web-panel/assets/{Cowork-Dp29kHsC.js → Cowork-BKHiF6uL.js} +2 -2
- package/src/assets/web-panel/assets/{Cron-zmEJAetz.js → Cron-C7p-lHnC.js} +2 -2
- package/src/assets/web-panel/assets/Crosschain-BvSOx8a6.js +1 -0
- package/src/assets/web-panel/assets/{DID-D_UFNzJ5.js → DID-D1k9jgHv.js} +2 -2
- package/src/assets/web-panel/assets/Dashboard-B9mCCnqY.js +3 -0
- package/src/assets/web-panel/assets/{Dropdown-CH7l3KCs.js → Dropdown-ge9UHSXF.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-Bud2M3XX.js → EmailListRenderer-TZO7ppXi.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-Bd2_8uME.js → FamilyGuardDashboard-DkhLJmzx.js} +1 -1
- package/src/assets/web-panel/assets/Federation-JLuA_8zp.js +1 -0
- package/src/assets/web-panel/assets/{FormItemContext-CpSH464Y.js → FormItemContext-C4w-rzNg.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-CANo8O-y.js → GenericCardRenderer-Btns4sV9.js} +1 -1
- package/src/assets/web-panel/assets/{Git-hvuZVoD3.js → Git-BheXMjzC.js} +2 -2
- package/src/assets/web-panel/assets/Governance-D9rLlatH.js +1 -0
- package/src/assets/web-panel/assets/Inference-DiklIMcd.js +1 -0
- package/src/assets/web-panel/assets/KnowledgeGraph-C70EWL03.js +1 -0
- package/src/assets/web-panel/assets/{Logs-DDkTnedu.js → Logs-Zt_MLI10.js} +2 -2
- package/src/assets/web-panel/assets/Marketplace-suQxES99.js +1 -0
- package/src/assets/web-panel/assets/{McpTools-Qx78XyOT.js → McpTools-CclpCDpY.js} +3 -3
- package/src/assets/web-panel/assets/{Memory-CXYDO1oT.js → Memory-B2lHOUwF.js} +2 -2
- package/src/assets/web-panel/assets/MobileBridge-BMXKoDAD.js +1 -0
- package/src/assets/web-panel/assets/{MobileProjects-DEDbWNIk.js → MobileProjects-a6mWVdBV.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-Dwa_vlR8.js → Mtc-BFpM4Fkj.js} +2 -2
- package/src/assets/web-panel/assets/{MtcAudit-DAVkxhJv.js → MtcAudit-DDQkxkbS.js} +4 -4
- package/src/assets/web-panel/assets/{Multisig-C3upmgPN.js → Multisig-Crkd_zKQ.js} +3 -3
- package/src/assets/web-panel/assets/NLProgramming-Dpk5An7B.js +1 -0
- package/src/assets/web-panel/assets/{Notes-B1Oy3FbC.js → Notes-BrFnOMNz.js} +4 -4
- package/src/assets/web-panel/assets/{NotificationSettings-Bs4-Fc6b.js → NotificationSettings-CM0iApFM.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-CEy1pIeq.js → OrderTableRenderer-XXjeqkdz.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-D4cJ1UNo.js → Organization-CHFW_38T.js} +2 -2
- package/src/assets/web-panel/assets/{Overflow-fdzvlF7x.js → Overflow-C-vl3EjV.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-Y13PwXBA.js → P2P-BT7Slavq.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-Ck1zCLe6.js → PdhVaultBrowser-DRUaULap.js} +3 -3
- package/src/assets/web-panel/assets/{Permissions-CvEXP6LC.js → Permissions-DkhOAvMz.js} +2 -2
- package/src/assets/web-panel/assets/{PersonalDataHub-JeP7IWMW.js → PersonalDataHub-Dl7dSOvV.js} +3 -3
- package/src/assets/web-panel/assets/Pipeline-B3nVI4p6.js +1 -0
- package/src/assets/web-panel/assets/Privacy-Dv1SXx1Q.js +1 -0
- package/src/assets/web-panel/assets/{ProjectInit-DeWd9UcS.js → ProjectInit-DrAhVi5p.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-DUpVuU9F.js → ProjectSettings-h-ggCYN_.js} +2 -2
- package/src/assets/web-panel/assets/Projects-CM91hYdr.js +1 -0
- package/src/assets/web-panel/assets/{Providers-DECW00ek.js → Providers-rfkZel3N.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-Dl-pK9Io.js → QuickAsk-XepZB7h_.js} +1 -1
- package/src/assets/web-panel/assets/Recommend-DzQWsh1a.js +1 -0
- package/src/assets/web-panel/assets/Reputation-CDeaal0j.js +1 -0
- package/src/assets/web-panel/assets/Row-BN4aKhFH.js +1 -0
- package/src/assets/web-panel/assets/{RssFeed-iyQT5q9l.js → RssFeed-INP6VYAA.js} +3 -3
- package/src/assets/web-panel/assets/Search-ChPbwatu.js +1 -0
- package/src/assets/web-panel/assets/{Security-c4Jxf5Ih.js → Security-C97ZCY8N.js} +4 -4
- package/src/assets/web-panel/assets/{Services-CRuisolj.js → Services-Cbtl2Ajs.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-CCnzJkb-.js → Skeleton-B13sFxBQ.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-CZURCwZg.js → Skills-vI9zSIKX.js} +1 -1
- package/src/assets/web-panel/assets/Sla-Cr3oBH39.js +1 -0
- package/src/assets/web-panel/assets/SpeechSettings-BwemqPPV.js +1 -0
- package/src/assets/web-panel/assets/{SyncSettings-CJWVtd87.js → SyncSettings-CLA78P-Z.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-61lrQ_lS.js → Tasks-DBjQ_DOM.js} +1 -1
- package/src/assets/web-panel/assets/Templates-BkSLDkBs.js +1 -0
- package/src/assets/web-panel/assets/Tenant-Dn0nSlib.js +1 -0
- package/src/assets/web-panel/assets/{Terminal-CP15hcNS.js → Terminal-C8f4boru.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-Sl5uXrkN.js → TimelineRenderer-DW_rMtJR.js} +1 -1
- package/src/assets/web-panel/assets/Tokens-BdffrKYV.js +1 -0
- package/src/assets/web-panel/assets/{Trigger-BLCQEOF2.js → Trigger-CX4CUg-Q.js} +1 -1
- package/src/assets/web-panel/assets/Trust-Cq9Bl7Od.js +1 -0
- package/src/assets/web-panel/assets/{UkeySign-Dwp0zXQ6.js → UkeySign-BQy18_VY.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-CIHA55Sx.js → VideoEditing-h5hzK7Vw.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-Hjajvnto.js → Wallet-BLotT3gw.js} +3 -3
- package/src/assets/web-panel/assets/{WebAuthn-DqSURUvI.js → WebAuthn-8cCANYLE.js} +4 -4
- package/src/assets/web-panel/assets/{WorkflowEditor-B5bHRwUG.js → WorkflowEditor-Db6NwtAv.js} +1 -1
- package/src/assets/web-panel/assets/{chat-DsheLKkG.js → chat-BPk1LbpL.js} +1 -1
- package/src/assets/web-panel/assets/{colors-CST2UkgL.js → colors-BnNal71p.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-yqEoy1L7.js → compact-item-W2VC0tcy.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-Jwp78HFY.js → createContext-DceVsgnZ.js} +1 -1
- package/src/assets/web-panel/assets/devWarning--rwFDBhx.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-CVS5mFEP.js → hasIn-BEq6EDcp.js} +1 -1
- package/src/assets/web-panel/assets/{index-BLTUVd0V.js → index-5gIBUFBn.js} +2 -2
- package/src/assets/web-panel/assets/index-APCHxOkM.js +3 -0
- package/src/assets/web-panel/assets/index-B2dMhwZi.js +1 -0
- package/src/assets/web-panel/assets/{index-C0FZScMe.js → index-B8cPjrEH.js} +4 -4
- package/src/assets/web-panel/assets/{index-CjfN2CpJ.js → index-BDC5HsxZ.js} +1 -1
- package/src/assets/web-panel/assets/index-BPFuPGKi.js +1 -0
- package/src/assets/web-panel/assets/{index-SjxCCf5h.js → index-BUkDcIwJ.js} +2 -2
- package/src/assets/web-panel/assets/{index-Cjig5i3a.js → index-BWcCyH6-.js} +28 -26
- package/src/assets/web-panel/assets/{index-AnW25bEq.js → index-BgZikQoh.js} +1 -1
- package/src/assets/web-panel/assets/index-Bwjus13Y.js +1 -0
- package/src/assets/web-panel/assets/index-BzSzRWsw.js +55 -0
- package/src/assets/web-panel/assets/index-C1K9CsGr.js +1 -0
- package/src/assets/web-panel/assets/{index-DDAigsel.js → index-CBZVISK6.js} +2 -2
- package/src/assets/web-panel/assets/index-CEJN-R2L.js +1 -0
- package/src/assets/web-panel/assets/index-CG5dGC9E.js +1 -0
- package/src/assets/web-panel/assets/{index-DxaU_lza.js → index-CMGLpstm.js} +1 -1
- package/src/assets/web-panel/assets/index-CWrGCndd.js +1 -0
- package/src/assets/web-panel/assets/index-CbcvfpCV.js +1 -0
- package/src/assets/web-panel/assets/{index-De600Jjm.js → index-Ceqv4lI2.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cy0dNzkp.js → index-CsT6frT8.js} +1 -1
- package/src/assets/web-panel/assets/{index-CDAPnZUs.js → index-D8PQKsDT.js} +1 -1
- package/src/assets/web-panel/assets/{index-Crk4KWLW.js → index-DGncdA-j.js} +1 -1
- package/src/assets/web-panel/assets/{index-R-VGFpi6.js → index-DHanQHO0.js} +3 -3
- package/src/assets/web-panel/assets/index-DaUBk28E.js +12 -0
- package/src/assets/web-panel/assets/{index-BzetOasL.js → index-Di2J4b4V.js} +1 -1
- package/src/assets/web-panel/assets/index-DtuLvWZN.js +21 -0
- package/src/assets/web-panel/assets/{index-Dgyn8-wN.js → index-DvS1J8xc.js} +2 -2
- package/src/assets/web-panel/assets/{index-CtyEOGuj.js → index-DxePJdwP.js} +1 -1
- package/src/assets/web-panel/assets/{index-CzsKK0tQ.js → index-Et4XvL49.js} +2 -2
- package/src/assets/web-panel/assets/{index-BuI27WSx.js → index-GlFxgcj4.js} +2 -2
- package/src/assets/web-panel/assets/index-HV119Oui.js +1 -0
- package/src/assets/web-panel/assets/index-InGW87pj.js +13 -0
- package/src/assets/web-panel/assets/{index-Dxljh9Fu.js → index-P1-s8JR7.js} +2 -2
- package/src/assets/web-panel/assets/{index-CSBPc-sI.js → index-SdABCNoi.js} +2 -2
- package/src/assets/web-panel/assets/{index-BCHAUdel.js → index-WTAZbN-c.js} +1 -1
- package/src/assets/web-panel/assets/index-_2I-KzOj.js +1 -0
- package/src/assets/web-panel/assets/index-m1sVaWVU.js +1 -0
- package/src/assets/web-panel/assets/index-s37wwyeN.js +1 -0
- package/src/assets/web-panel/assets/{index-CRBbVS43.js → index-tyWABqp9.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-ClAjrsQ-.js → initDefaultProps-DzrCDb3j.js} +1 -1
- package/src/assets/web-panel/assets/motion-BQERVnE3.js +11 -0
- package/src/assets/web-panel/assets/{move-DMelzIW-.js → move-I9ACA5XJ.js} +1 -1
- package/src/assets/web-panel/assets/mtc-parser-Dd2Pw-tr.js +1 -0
- package/src/assets/web-panel/assets/{omit-oxecfU3b.js → omit-DwzYRzWV.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-DJ5F5M6x.js → pickAttrs-BSGULyIL.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-DdbKsant.js → placementArrow-tSYYjAts.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-BUuM5cmS.js → responsiveObserve-Dy2lqikT.js} +1 -1
- package/src/assets/web-panel/assets/{slide-DSV0ccvR.js → slide-RaB4Uq0c.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-B-6oLAXf.js → statusUtils-CBpC_wZg.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-DdCAHtsZ.js → styleChecker-BEKwPWt5.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-ZORkcoZd.js → useFlexGapSupport-C4CnYJH1.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-BFp46LoP.js → useFs-n24jutw5.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-H7rxgbdW.js → usePersonalDataHub-DMgS8F6G.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-eIq4Tk0J.js → vnode-BSp2KYcj.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-CTNrv8-H.js → zoom-7UfQtTPh.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/commands/agent.js +51 -2
- package/src/commands/compliance.js +3 -1
- package/src/commands/ide.js +60 -5
- package/src/commands/incentive.js +22 -22
- package/src/commands/mtc.js +5 -1
- package/src/commands/review.js +47 -17
- package/src/commands/session.js +11 -2
- package/src/gateways/ws/message-dispatcher.js +149 -166
- package/src/gateways/ws/ws-session-gateway.js +95 -9
- package/src/harness/background-task-manager.js +5 -1
- package/src/harness/jsonl-session-store.js +90 -12
- package/src/harness/mcp-client.js +62 -21
- package/src/harness/prompt-compressor.js +24 -5
- package/src/harness/worktree-isolator.js +10 -1
- package/src/lib/agent-core.js +2 -0
- package/src/lib/audit-logger.js +3 -1
- package/src/lib/autonomous-agent.js +29 -0
- package/src/lib/chat-core.js +5 -2
- package/src/lib/chat-intent-service.js +82 -23
- package/src/lib/checkpoint-store.js +31 -4
- package/src/lib/claude-code-bridge.js +48 -3
- package/src/lib/config-manager.js +25 -1
- package/src/lib/cost-budget.js +13 -2
- package/src/lib/credential-guard.js +81 -1
- package/src/lib/git-integration.js +16 -2
- package/src/lib/goal-store.js +11 -0
- package/src/lib/hook-manager.js +4 -0
- package/src/lib/interaction-adapter.js +32 -11
- package/src/lib/jetbrains-bridge.js +147 -0
- package/src/lib/jsonl-session-store.js +2 -0
- package/src/lib/llm-config-defaults.js +37 -0
- package/src/lib/llm-pricing.js +11 -4
- package/src/lib/llm-providers.js +11 -1
- package/src/lib/mcp-oauth.js +88 -10
- package/src/lib/mcp-serve.js +29 -0
- package/src/lib/orchestrator.js +38 -2
- package/src/lib/permission-engine.js +4 -1
- package/src/lib/pipeline-orchestrator.js +20 -2
- package/src/lib/project-instructions.js +13 -0
- package/src/lib/repl-bang-memorize.js +9 -1
- package/src/lib/repl-denials.js +137 -0
- package/src/lib/runnable-provider.js +7 -1
- package/src/lib/safe-json.js +22 -0
- package/src/lib/sandbox-v2.js +7 -6
- package/src/lib/search-command.js +65 -0
- package/src/lib/session-insights.js +13 -6
- package/src/lib/session-usage.js +21 -3
- package/src/lib/settings-hooks.cjs +133 -0
- package/src/lib/settings-loader.cjs +16 -7
- package/src/lib/social-graph.js +3 -1
- package/src/lib/status-line.cjs +4 -1
- package/src/lib/stream-retry.js +27 -0
- package/src/lib/sub-agent-context.js +9 -0
- package/src/lib/turn-context.js +15 -5
- package/src/repl/agent-repl.js +110 -8
- package/src/runtime/__tests__/message-roles.test.js +36 -3
- package/src/runtime/agent-core.js +179 -52
- package/src/runtime/coding-agent-contract-shared.cjs +9 -2
- package/src/runtime/coding-agent-shell-policy.cjs +23 -2
- package/src/runtime/file-ref-expander.js +51 -7
- package/src/runtime/headless-runner.js +96 -2
- package/src/runtime/headless-stream.js +78 -3
- package/src/runtime/mcp-config.js +114 -4
- package/src/runtime/message-roles.js +14 -1
- 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/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/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
|
@@ -50,6 +50,11 @@ import { mergeConsecutiveMessages } from "./message-roles.js";
|
|
|
50
50
|
import { isHeadlessConfigCommand } from "../lib/headless-config-command.js";
|
|
51
51
|
import { withQuietStdout } from "./quiet-stdout.js";
|
|
52
52
|
import { CostBudget } from "../lib/cost-budget.js";
|
|
53
|
+
import {
|
|
54
|
+
classifyDenial,
|
|
55
|
+
recordDenial,
|
|
56
|
+
formatDenials,
|
|
57
|
+
} from "../lib/repl-denials.js";
|
|
53
58
|
|
|
54
59
|
/** Tools that cannot mutate the filesystem or run commands. */
|
|
55
60
|
export const READ_ONLY_TOOLS = Object.freeze([
|
|
@@ -69,6 +74,11 @@ const VALID_PERMISSION_MODES = Object.freeze([
|
|
|
69
74
|
|
|
70
75
|
const VALID_OUTPUT_FORMATS = Object.freeze(["text", "json", "stream-json"]);
|
|
71
76
|
|
|
77
|
+
// EPIPE guard for `cc agent -p … | head`. Lives in pipe-safety.js (shared with
|
|
78
|
+
// the stream-json driver + REPL); re-exported here for existing importers.
|
|
79
|
+
export { installPipeSafety } from "./pipe-safety.js";
|
|
80
|
+
import { installPipeSafety } from "./pipe-safety.js";
|
|
81
|
+
|
|
72
82
|
/**
|
|
73
83
|
* Resolve a --permission-mode string into the session-policy tier + a
|
|
74
84
|
* non-interactive confirmer + whether to clamp tools to the read-only set.
|
|
@@ -289,17 +299,46 @@ export async function runAgentHeadless(options = {}, deps = {}) {
|
|
|
289
299
|
let settingsHooks = options.settingsHooks || null;
|
|
290
300
|
if (!settingsHooks) {
|
|
291
301
|
try {
|
|
292
|
-
const { loadHooks } =
|
|
302
|
+
const { loadHooks, projectHookTrustNotice } =
|
|
303
|
+
await import("../lib/settings-hooks.cjs");
|
|
293
304
|
const loaded = loadHooks({ cwd, settingsFile: options.settingsFile });
|
|
294
305
|
settingsHooks =
|
|
295
306
|
loaded.hooks && Object.keys(loaded.hooks).length > 0
|
|
296
307
|
? loaded.hooks
|
|
297
308
|
: null;
|
|
309
|
+
// First-run trust notice for an untrusted/cloned repo's shell-running
|
|
310
|
+
// hooks (Claude-Code 2.1.195 parity). Best-effort, stderr-only.
|
|
311
|
+
try {
|
|
312
|
+
const notice = projectHookTrustNotice({
|
|
313
|
+
cwd,
|
|
314
|
+
settingsFile: options.settingsFile,
|
|
315
|
+
});
|
|
316
|
+
if (notice) process.stderr.write(notice + "\n");
|
|
317
|
+
} catch {
|
|
318
|
+
/* trust notice is best-effort */
|
|
319
|
+
}
|
|
298
320
|
} catch {
|
|
299
321
|
settingsHooks = null; // fail-open
|
|
300
322
|
}
|
|
301
323
|
}
|
|
302
324
|
|
|
325
|
+
// autoMode.classifyAllShell (Claude-Code 2.1.193): route the built-in
|
|
326
|
+
// verification allowlist through the shell-policy classifier instead of
|
|
327
|
+
// fast-pathing it. Explicit option wins; otherwise read settings.json.
|
|
328
|
+
let classifyAllShell = options.classifyAllShell || false;
|
|
329
|
+
if (!classifyAllShell) {
|
|
330
|
+
try {
|
|
331
|
+
const { readBooleanSetting } = await import("../lib/settings-loader.cjs");
|
|
332
|
+
classifyAllShell =
|
|
333
|
+
readBooleanSetting("autoMode.classifyAllShell", {
|
|
334
|
+
cwd,
|
|
335
|
+
settingsFile: options.settingsFile,
|
|
336
|
+
}) === true;
|
|
337
|
+
} catch {
|
|
338
|
+
classifyAllShell = false; // fail-open
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
303
342
|
const runLoop = deps.agentLoop || coreAgentLoop;
|
|
304
343
|
const doBootstrap = deps.bootstrap || bootstrap;
|
|
305
344
|
const getApprovalGate =
|
|
@@ -312,6 +351,12 @@ export async function runAgentHeadless(options = {}, deps = {}) {
|
|
|
312
351
|
// trace so `cc agent -p ... > out.txt` keeps `out.txt` clean.
|
|
313
352
|
const writeOut = deps.writeOut || ((s) => process.stdout.write(s));
|
|
314
353
|
const writeErr = deps.writeErr || ((s) => process.stderr.write(s));
|
|
354
|
+
// Only when we own the real stdout/stderr (no injected seams = production,
|
|
355
|
+
// not tests): guard against a downstream `| head` closing the pipe, which
|
|
356
|
+
// would otherwise crash with an unhandled EPIPE. Idempotent across calls.
|
|
357
|
+
if (!deps.writeOut && !deps.writeErr) {
|
|
358
|
+
installPipeSafety();
|
|
359
|
+
}
|
|
315
360
|
// Session persistence seam (file-based JSONL; DB-free, like the rest of
|
|
316
361
|
// headless). Defaults to the real store; tests inject fakes.
|
|
317
362
|
const store = {
|
|
@@ -667,6 +712,7 @@ export async function runAgentHeadless(options = {}, deps = {}) {
|
|
|
667
712
|
strict: options.strictMcpConfig === true,
|
|
668
713
|
ide: options.ide,
|
|
669
714
|
pdh: options.pdh,
|
|
715
|
+
jetbrains: options.jetbrains,
|
|
670
716
|
cwd: options.cwd || process.cwd(),
|
|
671
717
|
// advertise the session id to spawned stdio MCP servers
|
|
672
718
|
sessionId,
|
|
@@ -676,6 +722,7 @@ export async function runAgentHeadless(options = {}, deps = {}) {
|
|
|
676
722
|
loadMcpConfig: deps.loadMcpConfig,
|
|
677
723
|
loadRegisteredMcp: deps.loadRegisteredMcp,
|
|
678
724
|
loadIdeMcp: deps.loadIdeMcp,
|
|
725
|
+
loadJetbrainsMcp: deps.loadJetbrainsMcp,
|
|
679
726
|
},
|
|
680
727
|
);
|
|
681
728
|
if (mcp && isText) {
|
|
@@ -763,6 +810,7 @@ export async function runAgentHeadless(options = {}, deps = {}) {
|
|
|
763
810
|
approvalGate,
|
|
764
811
|
permissionRules,
|
|
765
812
|
settingsHooks,
|
|
813
|
+
classifyAllShell,
|
|
766
814
|
enabledToolNames,
|
|
767
815
|
disabledTools,
|
|
768
816
|
iterationBudget: budget,
|
|
@@ -834,6 +882,10 @@ export async function runAgentHeadless(options = {}, deps = {}) {
|
|
|
834
882
|
|
|
835
883
|
const startedAt = deps.now ? deps.now() : Date.now();
|
|
836
884
|
const toolCalls = [];
|
|
885
|
+
// Policy denials (blocked tool calls) collected for an end-of-run summary,
|
|
886
|
+
// so a non-interactive run surfaces what got blocked the way the REPL's
|
|
887
|
+
// `/permissions denials` does (Claude-Code 2.1.193 denial reasons).
|
|
888
|
+
const denials = [];
|
|
837
889
|
const usage = {
|
|
838
890
|
input_tokens: 0,
|
|
839
891
|
output_tokens: 0,
|
|
@@ -911,6 +963,26 @@ export async function runAgentHeadless(options = {}, deps = {}) {
|
|
|
911
963
|
if (toolCalls.length > 0) {
|
|
912
964
|
toolCalls[toolCalls.length - 1].is_error = Boolean(err);
|
|
913
965
|
}
|
|
966
|
+
// Track policy denials (not plain tool failures) for the end-of-run
|
|
967
|
+
// summary. The preceding tool-executing pushed the args.
|
|
968
|
+
if (err) {
|
|
969
|
+
const last = toolCalls[toolCalls.length - 1];
|
|
970
|
+
const denial = classifyDenial({
|
|
971
|
+
tool: event.tool,
|
|
972
|
+
result: event.result,
|
|
973
|
+
error: event.error,
|
|
974
|
+
argsSummary:
|
|
975
|
+
last && last.tool === event.tool
|
|
976
|
+
? formatToolArgs(event.tool, last.args)
|
|
977
|
+
: "",
|
|
978
|
+
});
|
|
979
|
+
if (denial) {
|
|
980
|
+
recordDenial(denials, {
|
|
981
|
+
...denial,
|
|
982
|
+
at: deps.now ? deps.now() : Date.now(),
|
|
983
|
+
});
|
|
984
|
+
}
|
|
985
|
+
}
|
|
914
986
|
break;
|
|
915
987
|
}
|
|
916
988
|
case "token-usage": {
|
|
@@ -1000,6 +1072,7 @@ export async function runAgentHeadless(options = {}, deps = {}) {
|
|
|
1000
1072
|
usage,
|
|
1001
1073
|
numTurns: budget.consumed,
|
|
1002
1074
|
durationMs: (deps.now ? deps.now() : Date.now()) - startedAt,
|
|
1075
|
+
denials,
|
|
1003
1076
|
}),
|
|
1004
1077
|
) + "\n",
|
|
1005
1078
|
);
|
|
@@ -1119,6 +1192,21 @@ export async function runAgentHeadless(options = {}, deps = {}) {
|
|
|
1119
1192
|
}
|
|
1120
1193
|
}
|
|
1121
1194
|
|
|
1195
|
+
// End-of-run policy-denial summary so a non-interactive run surfaces what was
|
|
1196
|
+
// blocked (mirrors the REPL's `/permissions denials`). Text → stderr lines;
|
|
1197
|
+
// stream → a `denials_summary` event before the final result.
|
|
1198
|
+
if (denials.length) {
|
|
1199
|
+
if (isText) {
|
|
1200
|
+
writeErr(
|
|
1201
|
+
`\n ${denials.length} tool call(s) were denied by policy this run:\n` +
|
|
1202
|
+
formatDenials(denials, { now: deps.now ? deps.now() : Date.now() }) +
|
|
1203
|
+
"\n",
|
|
1204
|
+
);
|
|
1205
|
+
} else if (isStream) {
|
|
1206
|
+
emitStream({ type: "denials_summary", count: denials.length, denials });
|
|
1207
|
+
}
|
|
1208
|
+
}
|
|
1209
|
+
|
|
1122
1210
|
if (isStream) {
|
|
1123
1211
|
emitStream({
|
|
1124
1212
|
type: "result",
|
|
@@ -1142,6 +1230,7 @@ export async function runAgentHeadless(options = {}, deps = {}) {
|
|
|
1142
1230
|
usage,
|
|
1143
1231
|
numTurns: budget.consumed,
|
|
1144
1232
|
durationMs,
|
|
1233
|
+
denials,
|
|
1145
1234
|
}),
|
|
1146
1235
|
) + "\n",
|
|
1147
1236
|
);
|
|
@@ -1162,8 +1251,9 @@ function buildResultEnvelope({
|
|
|
1162
1251
|
usage,
|
|
1163
1252
|
numTurns,
|
|
1164
1253
|
durationMs,
|
|
1254
|
+
denials,
|
|
1165
1255
|
}) {
|
|
1166
|
-
|
|
1256
|
+
const env = {
|
|
1167
1257
|
type: "result",
|
|
1168
1258
|
subtype,
|
|
1169
1259
|
is_error: isError,
|
|
@@ -1174,4 +1264,8 @@ function buildResultEnvelope({
|
|
|
1174
1264
|
tool_calls: toolCalls,
|
|
1175
1265
|
usage,
|
|
1176
1266
|
};
|
|
1267
|
+
// Only present when something was blocked — keeps the no-denial envelope
|
|
1268
|
+
// byte-identical to before (Claude-Code 2.1.193 denial reasons, json mode).
|
|
1269
|
+
if (Array.isArray(denials) && denials.length) env.denials = denials;
|
|
1270
|
+
return env;
|
|
1177
1271
|
}
|
|
@@ -35,6 +35,7 @@ import {
|
|
|
35
35
|
resolvePermissionMode,
|
|
36
36
|
resolveEnabledTools,
|
|
37
37
|
parseToolList,
|
|
38
|
+
installPipeSafety,
|
|
38
39
|
} from "./headless-runner.js";
|
|
39
40
|
import {
|
|
40
41
|
startSession as jsonlStartSession,
|
|
@@ -317,9 +318,26 @@ export function sanitizeLlmHint(raw) {
|
|
|
317
318
|
/**
|
|
318
319
|
* Yield NDJSON lines from a byte/string stream (stdin). Splits on "\n" and
|
|
319
320
|
* flushes any trailing partial line when the stream ends.
|
|
321
|
+
*
|
|
322
|
+
* A single line with no terminating "\n" must never accumulate without bound:
|
|
323
|
+
* a stuck / garbage producer (or a runaway multi-MB inline payload) that never
|
|
324
|
+
* emits a newline would otherwise grow `buf` unbounded and OOM this long-lived
|
|
325
|
+
* stream process. When a line exceeds the cap, a short head is yielded (so the
|
|
326
|
+
* caller surfaces it as an invalid-JSON error rather than dropping it silently)
|
|
327
|
+
* and the rest of that monster line is discarded until its newline — the stream
|
|
328
|
+
* resyncs cleanly to the next well-formed line. Cap precedence:
|
|
329
|
+
* `opts.maxLineLength` > `CC_MAX_INPUT_LINE_BYTES` env > 16 MB default.
|
|
320
330
|
*/
|
|
321
|
-
export async function* readJsonLines(input) {
|
|
331
|
+
export async function* readJsonLines(input, opts = {}) {
|
|
332
|
+
const envCap = Number(process.env.CC_MAX_INPUT_LINE_BYTES);
|
|
333
|
+
const maxLineLength =
|
|
334
|
+
Number.isFinite(opts.maxLineLength) && opts.maxLineLength > 0
|
|
335
|
+
? opts.maxLineLength
|
|
336
|
+
: Number.isFinite(envCap) && envCap > 0
|
|
337
|
+
? envCap
|
|
338
|
+
: 16 * 1024 * 1024;
|
|
322
339
|
let buf = "";
|
|
340
|
+
let overflow = false; // discarding the tail of an over-long line until its \n
|
|
323
341
|
// Reuse ONE streaming decoder across chunks so a multi-byte UTF-8 character
|
|
324
342
|
// (e.g. a 3-byte Chinese char) split across two Buffer chunks is reassembled
|
|
325
343
|
// rather than corrupted. process.stdin yields Buffers with no setEncoding, so
|
|
@@ -331,14 +349,34 @@ export async function* readJsonLines(input) {
|
|
|
331
349
|
typeof chunk === "string"
|
|
332
350
|
? chunk
|
|
333
351
|
: decoder.decode(chunk, { stream: true });
|
|
352
|
+
// Still skipping past an over-long line: drop everything up to (and
|
|
353
|
+
// including) the next newline, then resume normal line parsing. While no
|
|
354
|
+
// newline has arrived, keep `buf` empty so we don't re-grow it.
|
|
355
|
+
if (overflow) {
|
|
356
|
+
const nl = buf.indexOf("\n");
|
|
357
|
+
if (nl < 0) {
|
|
358
|
+
buf = "";
|
|
359
|
+
continue;
|
|
360
|
+
}
|
|
361
|
+
buf = buf.slice(nl + 1);
|
|
362
|
+
overflow = false;
|
|
363
|
+
}
|
|
334
364
|
let idx;
|
|
335
365
|
while ((idx = buf.indexOf("\n")) >= 0) {
|
|
336
366
|
yield buf.slice(0, idx);
|
|
337
367
|
buf = buf.slice(idx + 1);
|
|
338
368
|
}
|
|
369
|
+
// No newline in the remaining buffer and it has blown past the cap — this is
|
|
370
|
+
// a pathological unterminated line. Yield a short head (an invalid-JSON
|
|
371
|
+
// error for the caller) and discard the rest until the next newline.
|
|
372
|
+
if (buf.length > maxLineLength) {
|
|
373
|
+
yield buf.slice(0, 200);
|
|
374
|
+
buf = "";
|
|
375
|
+
overflow = true;
|
|
376
|
+
}
|
|
339
377
|
}
|
|
340
378
|
buf += decoder.decode(); // flush any bytes held from a final partial char
|
|
341
|
-
if (buf.trim()) yield buf;
|
|
379
|
+
if (!overflow && buf.trim()) yield buf;
|
|
342
380
|
}
|
|
343
381
|
|
|
344
382
|
/**
|
|
@@ -496,23 +534,57 @@ export async function runAgentHeadlessStream(options = {}, deps = {}) {
|
|
|
496
534
|
let settingsHooks = options.settingsHooks || null;
|
|
497
535
|
if (!settingsHooks) {
|
|
498
536
|
try {
|
|
499
|
-
const { loadHooks } =
|
|
537
|
+
const { loadHooks, projectHookTrustNotice } =
|
|
538
|
+
await import("../lib/settings-hooks.cjs");
|
|
500
539
|
const loaded = loadHooks({ cwd, settingsFile: options.settingsFile });
|
|
501
540
|
settingsHooks =
|
|
502
541
|
loaded.hooks && Object.keys(loaded.hooks).length > 0
|
|
503
542
|
? loaded.hooks
|
|
504
543
|
: null;
|
|
544
|
+
// First-run trust notice for an untrusted/cloned repo's shell-running
|
|
545
|
+
// hooks (Claude-Code 2.1.195 parity). stderr keeps the NDJSON stdout clean.
|
|
546
|
+
try {
|
|
547
|
+
const notice = projectHookTrustNotice({
|
|
548
|
+
cwd,
|
|
549
|
+
settingsFile: options.settingsFile,
|
|
550
|
+
});
|
|
551
|
+
if (notice) process.stderr.write(notice + "\n");
|
|
552
|
+
} catch {
|
|
553
|
+
/* trust notice is best-effort */
|
|
554
|
+
}
|
|
505
555
|
} catch {
|
|
506
556
|
settingsHooks = null; // fail-open
|
|
507
557
|
}
|
|
508
558
|
}
|
|
509
559
|
|
|
560
|
+
// autoMode.classifyAllShell (Claude-Code 2.1.193): classify the built-in
|
|
561
|
+
// verification allowlist through the shell-policy instead of fast-pathing it.
|
|
562
|
+
let classifyAllShell = options.classifyAllShell || false;
|
|
563
|
+
if (!classifyAllShell) {
|
|
564
|
+
try {
|
|
565
|
+
const { readBooleanSetting } = await import("../lib/settings-loader.cjs");
|
|
566
|
+
classifyAllShell =
|
|
567
|
+
readBooleanSetting("autoMode.classifyAllShell", {
|
|
568
|
+
cwd,
|
|
569
|
+
settingsFile: options.settingsFile,
|
|
570
|
+
}) === true;
|
|
571
|
+
} catch {
|
|
572
|
+
classifyAllShell = false; // fail-open
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
|
|
510
576
|
const input = deps.input || process.stdin;
|
|
511
577
|
const runLoop = deps.agentLoop || coreAgentLoop;
|
|
512
578
|
const doBootstrap = deps.bootstrap || bootstrap;
|
|
513
579
|
const doExpand = deps.expandFileRefs || expandFileRefsAsync;
|
|
514
580
|
const writeOut = deps.writeOut || ((s) => process.stdout.write(s));
|
|
515
581
|
const writeErr = deps.writeErr || ((s) => process.stderr.write(s));
|
|
582
|
+
// Guard the real stdout/stderr against a downstream `| head` closing the pipe
|
|
583
|
+
// (unhandled async EPIPE → crash). Only when we own the streams (no injected
|
|
584
|
+
// seams = production, not tests). Idempotent; shared with the -p runner.
|
|
585
|
+
if (!deps.writeOut && !deps.writeErr) {
|
|
586
|
+
installPipeSafety();
|
|
587
|
+
}
|
|
516
588
|
// Batch consecutive partial-message text/thinking deltas into one stream_event
|
|
517
589
|
// line (Claude-Code 2.1.191 streaming-CPU optimization). `emit` flushes any
|
|
518
590
|
// pending deltas before writing a non-delta line, so ordering is preserved;
|
|
@@ -832,6 +904,7 @@ export async function runAgentHeadlessStream(options = {}, deps = {}) {
|
|
|
832
904
|
strict: options.strictMcpConfig === true,
|
|
833
905
|
ide: options.ide,
|
|
834
906
|
pdh: options.pdh,
|
|
907
|
+
jetbrains: options.jetbrains,
|
|
835
908
|
cwd: options.cwd || process.cwd(),
|
|
836
909
|
// advertise the session id to spawned stdio MCP servers
|
|
837
910
|
sessionId,
|
|
@@ -841,6 +914,7 @@ export async function runAgentHeadlessStream(options = {}, deps = {}) {
|
|
|
841
914
|
loadMcpConfig: deps.loadMcpConfig,
|
|
842
915
|
loadRegisteredMcp: deps.loadRegisteredMcp,
|
|
843
916
|
loadIdeMcp: deps.loadIdeMcp,
|
|
917
|
+
loadJetbrainsMcp: deps.loadJetbrainsMcp,
|
|
844
918
|
},
|
|
845
919
|
);
|
|
846
920
|
} catch (err) {
|
|
@@ -905,6 +979,7 @@ export async function runAgentHeadlessStream(options = {}, deps = {}) {
|
|
|
905
979
|
approvalGate,
|
|
906
980
|
permissionRules,
|
|
907
981
|
settingsHooks,
|
|
982
|
+
classifyAllShell,
|
|
908
983
|
enabledToolNames,
|
|
909
984
|
disabledTools,
|
|
910
985
|
// --interactive-approvals: settings/hook `ask` (and, with an IDE bridge,
|
|
@@ -28,6 +28,11 @@ import {
|
|
|
28
28
|
pdhServerToMcpConfig,
|
|
29
29
|
isInPdhTerminal,
|
|
30
30
|
} from "../lib/pdh-bridge.js";
|
|
31
|
+
import {
|
|
32
|
+
discoverJetbrainsServer,
|
|
33
|
+
jetbrainsServerToMcpConfig,
|
|
34
|
+
isInJetbrainsContext,
|
|
35
|
+
} from "../lib/jetbrains-bridge.js";
|
|
31
36
|
|
|
32
37
|
/**
|
|
33
38
|
* Normalize a parsed config object into a `{ name: serverConfig }` map.
|
|
@@ -60,6 +65,22 @@ export function mcpToolName(server, tool) {
|
|
|
60
65
|
return `mcp__${server}__${tool}`;
|
|
61
66
|
}
|
|
62
67
|
|
|
68
|
+
/**
|
|
69
|
+
* Case-insensitive "does this header map already carry `name`?". HTTP header
|
|
70
|
+
* names are case-insensitive, so a caller-supplied `AUTHORIZATION` must count
|
|
71
|
+
* the same as `Authorization` — otherwise the OAuth-injection guard below would
|
|
72
|
+
* not see it and would add a SECOND, conflicting auth header, leaking our
|
|
73
|
+
* stored token where the user explicitly pinned their own credential.
|
|
74
|
+
*/
|
|
75
|
+
export function hasHeaderInsensitive(headers, name) {
|
|
76
|
+
if (!headers || typeof headers !== "object") return false;
|
|
77
|
+
const want = String(name).toLowerCase();
|
|
78
|
+
for (const k of Object.keys(headers)) {
|
|
79
|
+
if (k.toLowerCase() === want) return true;
|
|
80
|
+
}
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
|
|
63
84
|
/**
|
|
64
85
|
* When an MCP connect fails with an authentication error (HTTP 401/403, or an
|
|
65
86
|
* explicit Unauthorized/Forbidden) on a URL-based server, return an actionable
|
|
@@ -138,7 +159,7 @@ export async function setupMcpFromConfig(servers, deps = {}) {
|
|
|
138
159
|
// unless the config already supplies an Authorization header. Best-effort:
|
|
139
160
|
// no token / a refresh failure just connects unauthenticated.
|
|
140
161
|
let connectCfg = cfg;
|
|
141
|
-
if (cfg.url && !cfg.headers
|
|
162
|
+
if (cfg.url && !hasHeaderInsensitive(cfg.headers, "Authorization")) {
|
|
142
163
|
try {
|
|
143
164
|
const { ensureValidToken } = await import("../lib/mcp-oauth.js");
|
|
144
165
|
const token = await ensureValidToken(cfg.url);
|
|
@@ -147,6 +168,31 @@ export async function setupMcpFromConfig(servers, deps = {}) {
|
|
|
147
168
|
...cfg,
|
|
148
169
|
headers: { ...cfg.headers, Authorization: `Bearer ${token}` },
|
|
149
170
|
};
|
|
171
|
+
// Auto-recover from a mid-session 401/403 (server-side token rotation
|
|
172
|
+
// / revocation while the locally-stored copy still looks unexpired):
|
|
173
|
+
// register a reconnector that force-refreshes the bearer, so the
|
|
174
|
+
// mcp-client's callTool retry path reconnects and re-runs the call
|
|
175
|
+
// once with a fresh token (Claude-Code 2.1.193: "MCP auth re-runs and
|
|
176
|
+
// reconnects automatically on 401/403"). Best-effort, and skipped for
|
|
177
|
+
// a server whose config carries its own Authorization header (handled
|
|
178
|
+
// by the guard above) — an explicit IDE-bridge reconnector registered
|
|
179
|
+
// after setup still wins on any name clash.
|
|
180
|
+
if (typeof mcpClient.setReconnector === "function") {
|
|
181
|
+
const baseCfg = cfg;
|
|
182
|
+
mcpClient.setReconnector(name, async () => {
|
|
183
|
+
const { ensureValidToken: reauth } =
|
|
184
|
+
await import("../lib/mcp-oauth.js");
|
|
185
|
+
const fresh = await reauth(baseCfg.url, { forceRefresh: true });
|
|
186
|
+
if (!fresh) return null;
|
|
187
|
+
return {
|
|
188
|
+
...baseCfg,
|
|
189
|
+
headers: {
|
|
190
|
+
...baseCfg.headers,
|
|
191
|
+
Authorization: `Bearer ${fresh}`,
|
|
192
|
+
},
|
|
193
|
+
};
|
|
194
|
+
});
|
|
195
|
+
}
|
|
150
196
|
}
|
|
151
197
|
} catch {
|
|
152
198
|
// OAuth wiring is best-effort
|
|
@@ -463,6 +509,47 @@ export async function loadPdhMcp(opts = {}, deps = {}) {
|
|
|
463
509
|
return out;
|
|
464
510
|
}
|
|
465
511
|
|
|
512
|
+
/**
|
|
513
|
+
* Connect IntelliJ IDEA's built-in MCP server (IDEA >= 2025.2) as the reserved
|
|
514
|
+
* server `idea` -> tools `mcp__idea__*`. The ChainlessChain JetBrains plugin
|
|
515
|
+
* injects the exact endpoint (CHAINLESSCHAIN_JETBRAINS_MCP_URL + optional
|
|
516
|
+
* _TOKEN) into the cc it spawns ONLY when the IDE actually supports + enables
|
|
517
|
+
* MCP; absent that, this is a no-op (we never scan/guess/spawn a proxy — if
|
|
518
|
+
* the IDE's MCP is unsupported, we don't connect). Best-effort: returns
|
|
519
|
+
* `deps.into` (or null) on nothing found / bad config, never throws. An explicit
|
|
520
|
+
* user registration of a server named `idea` wins (yield + one-line warning).
|
|
521
|
+
*
|
|
522
|
+
* @param {object} opts { env? }
|
|
523
|
+
* @param {object} [deps] { writeErr, into, discoverJetbrainsServer, jetbrainsServerToMcpConfig }
|
|
524
|
+
*/
|
|
525
|
+
export async function loadJetbrainsMcp(opts = {}, deps = {}) {
|
|
526
|
+
const discover = deps.discoverJetbrainsServer || discoverJetbrainsServer;
|
|
527
|
+
const toCfg = deps.jetbrainsServerToMcpConfig || jetbrainsServerToMcpConfig;
|
|
528
|
+
const found = discover({ env: opts.env });
|
|
529
|
+
if (!found) return deps.into || null;
|
|
530
|
+
if (deps.into?.mcpClient?.servers?.has?.("idea")) {
|
|
531
|
+
(deps.writeErr || (() => {}))(
|
|
532
|
+
` idea: server "idea" already registered explicitly — ` +
|
|
533
|
+
`skipping IDEA built-in MCP auto-connect\n`,
|
|
534
|
+
);
|
|
535
|
+
return deps.into;
|
|
536
|
+
}
|
|
537
|
+
const cfg = toCfg(found);
|
|
538
|
+
if (!cfg) return deps.into || null;
|
|
539
|
+
const out = await setupMcpFromConfig({ idea: cfg }, deps);
|
|
540
|
+
// Hot reconnect: re-read the injected env on a failed mcp__idea__* call (the
|
|
541
|
+
// IDE may have restarted its MCP server on a new port/token, and the plugin
|
|
542
|
+
// re-injects a fresh CHAINLESSCHAIN_JETBRAINS_MCP_URL into the next spawn —
|
|
543
|
+
// though within one run the env is fixed, so this just re-validates it).
|
|
544
|
+
if (out?.mcpClient?.setReconnector) {
|
|
545
|
+
out.mcpClient.setReconnector("idea", () => {
|
|
546
|
+
const fresh = discover({ env: opts.env });
|
|
547
|
+
return fresh ? toCfg(fresh) : null;
|
|
548
|
+
});
|
|
549
|
+
}
|
|
550
|
+
return out;
|
|
551
|
+
}
|
|
552
|
+
|
|
466
553
|
/**
|
|
467
554
|
* Discover + connect a project-scoped `.mcp.json` (Claude-Code parity). Reads a
|
|
468
555
|
* `.mcp.json` from the git project root (walked up from cwd, same as the
|
|
@@ -534,10 +621,13 @@ export async function loadProjectMcp(opts = {}, deps = {}) {
|
|
|
534
621
|
* there's nothing. Throws only on a bad `--mcp-config` file (explicit request).
|
|
535
622
|
*
|
|
536
623
|
* IDE discovery: default ON only inside an IDE integrated terminal; `ide:true`
|
|
537
|
-
* (`--ide`) forces it; `ide:false` (`--no-ide`) disables it.
|
|
624
|
+
* (`--ide`) forces it; `ide:false` (`--no-ide`) disables it. IDEA built-in MCP
|
|
625
|
+
* (reserved `idea`): default ON only when the JetBrains plugin injected
|
|
626
|
+
* `CHAINLESSCHAIN_JETBRAINS_MCP_URL`; `jetbrains:true` (`--jetbrains`) forces;
|
|
627
|
+
* `jetbrains:false` (`--no-jetbrains`) disables.
|
|
538
628
|
*
|
|
539
|
-
* @param {object} args { mcpConfigPath?, db?, includeRegistered=true, allRegistered=false, ide?, pdh?, cwd?, env? }
|
|
540
|
-
* @param {object} [deps] { writeErr, loadMcpConfig, loadRegisteredMcp, loadIdeMcp, loadPdhMcp, isInIdeTerminal, isInPdhTerminal }
|
|
629
|
+
* @param {object} args { mcpConfigPath?, db?, includeRegistered=true, allRegistered=false, ide?, pdh?, jetbrains?, cwd?, env? }
|
|
630
|
+
* @param {object} [deps] { writeErr, loadMcpConfig, loadRegisteredMcp, loadIdeMcp, loadPdhMcp, loadJetbrainsMcp, isInIdeTerminal, isInPdhTerminal, isInJetbrainsContext }
|
|
541
631
|
*/
|
|
542
632
|
export async function resolveAgentMcp(args = {}, deps = {}) {
|
|
543
633
|
const doFile = deps.loadMcpConfig || loadMcpConfig;
|
|
@@ -545,6 +635,7 @@ export async function resolveAgentMcp(args = {}, deps = {}) {
|
|
|
545
635
|
const doProject = deps.loadProjectMcp || loadProjectMcp;
|
|
546
636
|
const doIde = deps.loadIdeMcp || loadIdeMcp;
|
|
547
637
|
const doPdh = deps.loadPdhMcp || loadPdhMcp;
|
|
638
|
+
const doJetbrains = deps.loadJetbrainsMcp || loadJetbrainsMcp;
|
|
548
639
|
// Thread the agent session id down to setupMcpFromConfig so spawned stdio MCP
|
|
549
640
|
// servers get CC_SESSION_ID / CLAUDE_CODE_SESSION_ID (Claude-Code parity).
|
|
550
641
|
const fwd =
|
|
@@ -601,6 +692,25 @@ export async function resolveAgentMcp(args = {}, deps = {}) {
|
|
|
601
692
|
);
|
|
602
693
|
}
|
|
603
694
|
}
|
|
695
|
+
// IntelliJ IDEA built-in MCP (IDEA >= 2025.2), reserved name `idea` ->
|
|
696
|
+
// mcp__idea__*. Scoped to the JetBrains plugin context: the plugin injects
|
|
697
|
+
// CHAINLESSCHAIN_JETBRAINS_MCP_URL only when the IDE supports + enables MCP,
|
|
698
|
+
// so default-ON is gated on that env being present (isInJetbrainsContext).
|
|
699
|
+
// `jetbrains:true` (--jetbrains) forces the attempt; `jetbrains:false`
|
|
700
|
+
// (--no-jetbrains) disables it. No URL = no connect (unsupported IDE).
|
|
701
|
+
// Skipped under --strict-mcp-config (returned above).
|
|
702
|
+
if (args.jetbrains !== false) {
|
|
703
|
+
const env = args.env || process.env;
|
|
704
|
+
const inJetbrains = (deps.isInJetbrainsContext || isInJetbrainsContext)(
|
|
705
|
+
env,
|
|
706
|
+
);
|
|
707
|
+
if (args.jetbrains === true || inJetbrains) {
|
|
708
|
+
result = await doJetbrains(
|
|
709
|
+
{ env },
|
|
710
|
+
{ ...fwd, into: result || undefined },
|
|
711
|
+
);
|
|
712
|
+
}
|
|
713
|
+
}
|
|
604
714
|
return result;
|
|
605
715
|
}
|
|
606
716
|
|
|
@@ -64,6 +64,17 @@ function joinContent(a, b) {
|
|
|
64
64
|
*/
|
|
65
65
|
const MERGEABLE_ROLES = new Set(["user", "assistant"]);
|
|
66
66
|
|
|
67
|
+
/**
|
|
68
|
+
* Does this message carry tool calls? An assistant turn with `tool_calls` is a
|
|
69
|
+
* STRUCTURED turn whose calls must stay paired with their `tool` results —
|
|
70
|
+
* folding it loses one side's tool_calls (orphaning results / unanswered calls)
|
|
71
|
+
* and the strict-API 400s. Such turns are never merged (left to alternate; the
|
|
72
|
+
* tool-pair fix lives in sanitizeToolPairs, not here).
|
|
73
|
+
*/
|
|
74
|
+
function hasToolCalls(msg) {
|
|
75
|
+
return Array.isArray(msg && msg.tool_calls) && msg.tool_calls.length > 0;
|
|
76
|
+
}
|
|
77
|
+
|
|
67
78
|
/**
|
|
68
79
|
* Merge consecutive same-role messages so the conversation alternates roles for
|
|
69
80
|
* the provider. Only `user`/`assistant` turns fold; `tool` and `system` turns
|
|
@@ -85,7 +96,9 @@ export function mergeConsecutiveMessages(messages) {
|
|
|
85
96
|
msg &&
|
|
86
97
|
typeof msg.role === "string" &&
|
|
87
98
|
MERGEABLE_ROLES.has(msg.role) &&
|
|
88
|
-
prev.role === msg.role
|
|
99
|
+
prev.role === msg.role &&
|
|
100
|
+
!hasToolCalls(prev) && // never fold a structured tool-call turn — merging
|
|
101
|
+
!hasToolCalls(msg) // drops one side's tool_calls and breaks pairing
|
|
89
102
|
) {
|
|
90
103
|
out[out.length - 1] = {
|
|
91
104
|
...prev,
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* pipe-safety — shared EPIPE guard for cc's stdout/stderr writers.
|
|
3
|
+
*
|
|
4
|
+
* A downstream consumer that closes the pipe (e.g. `cc agent -p … | head`)
|
|
5
|
+
* makes the next write fail ASYNCHRONOUSLY via the stream's `error` event,
|
|
6
|
+
* which the try/catch around a write call cannot catch. With no `error`
|
|
7
|
+
* listener that is an unhandled EPIPE → the process crashes with a stack trace
|
|
8
|
+
* instead of stopping cleanly. `installPipeSafety` adds an idempotent listener
|
|
9
|
+
* that treats EPIPE as "consumer done" (the Unix pipeline convention) and
|
|
10
|
+
* surfaces other stream errors best-effort.
|
|
11
|
+
*
|
|
12
|
+
* Used by the headless `-p` runner, the stream-json driver, and the REPL.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
// Global-registry symbol so the guard is installed at most once per stream even
|
|
16
|
+
// across the modules that share it.
|
|
17
|
+
const _PIPE_SAFE = Symbol.for("cc.headless.pipeSafe");
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Install an EPIPE-safe `error` listener on the given write streams (default:
|
|
21
|
+
* process.stdout + process.stderr). Idempotent. `onEpipe` defaults to a clean
|
|
22
|
+
* `process.exit(0)`; the REPL passes a graceful-shutdown callback instead.
|
|
23
|
+
*
|
|
24
|
+
* @param {Array<NodeJS.WriteStream>} [streams]
|
|
25
|
+
* @param {() => void} [onEpipe]
|
|
26
|
+
*/
|
|
27
|
+
export function installPipeSafety(streams, onEpipe) {
|
|
28
|
+
const targets = streams || [process.stdout, process.stderr];
|
|
29
|
+
const handleEpipe = onEpipe || (() => process.exit(0));
|
|
30
|
+
for (const stream of targets) {
|
|
31
|
+
if (!stream || typeof stream.on !== "function" || stream[_PIPE_SAFE]) {
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
stream[_PIPE_SAFE] = true;
|
|
35
|
+
stream.on("error", (err) => {
|
|
36
|
+
if (err && err.code === "EPIPE") {
|
|
37
|
+
handleEpipe();
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
// Non-EPIPE stream error: surface best-effort (never onto the stream that
|
|
41
|
+
// just errored, to avoid a loop) and otherwise swallow.
|
|
42
|
+
try {
|
|
43
|
+
if (stream !== process.stderr) {
|
|
44
|
+
process.stderr.write(`stream error: ${err?.message || err}\n`);
|
|
45
|
+
}
|
|
46
|
+
} catch {
|
|
47
|
+
/* nothing more we can do */
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -38,6 +38,9 @@ export function resolveAgentPolicy({
|
|
|
38
38
|
// IDE bridge tri-state (undefined=auto / true=--ide / false=--no-ide); the
|
|
39
39
|
// REPL forwards it to resolveAgentMcp so --ide/--no-ide work interactively.
|
|
40
40
|
ide: overrides.ide,
|
|
41
|
+
// IDEA built-in MCP tri-state (undefined=auto when the JetBrains plugin
|
|
42
|
+
// injected the endpoint / true=--jetbrains / false=--no-jetbrains).
|
|
43
|
+
jetbrains: overrides.jetbrains,
|
|
41
44
|
};
|
|
42
45
|
}
|
|
43
46
|
|