chainlesschain 0.162.156 → 0.162.158
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/package.json +2 -2
- package/src/assets/web-panel/.build-hash +1 -1
- package/src/assets/web-panel/assets/{AIOps-DjBXlypQ.js → AIOps-BwpTJTN_.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-Cv9er9bp.js → ActionButton-BtQOoo3l.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-CLKDjSNs.js → Analytics-Duf_rXZa.js} +3 -3
- package/src/assets/web-panel/assets/AppLayout-XQK3wjvB.js +9 -0
- package/src/assets/web-panel/assets/Artifacts-CTVt96zW.css +1 -0
- package/src/assets/web-panel/assets/Artifacts-DQvJoQ6E.js +1 -0
- package/src/assets/web-panel/assets/{Audit-CXafD178.js → Audit-DNjOGQLh.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-CP5c-GSd.js → BackgroundAgents-FH9RAfB5.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-BbYLQfyt.js → Backup-ZIbrR4-U.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-Bqe0MwZL.js → BaseInput-DSt3NaND.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-jNA_yUPH.js → Chat-CY-wvbjv.js} +6 -6
- package/src/assets/web-panel/assets/Chat-KSGPtU0L.css +1 -0
- package/src/assets/web-panel/assets/{ChatBubbleRenderer-BOFLF01s.js → ChatBubbleRenderer-BAV9V-t-.js} +1 -1
- package/src/assets/web-panel/assets/{Checkbox-Dlzz-zD1.js → Checkbox-8JVadE_B.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-BAsDQjGf.js → Codegen-CN83nPYI.js} +1 -1
- package/src/assets/web-panel/assets/{Col-Ju0HrAuS.js → Col-xPSvqdN8.js} +1 -1
- package/src/assets/web-panel/assets/{Community-DynJd43Z.js → Community-D78cnIjG.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-CabbZVww.js → Compact-CSw6Csbp.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-DW4BmAoh.js → Compliance-E_HhjcYR.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-Bk1S_7X-.js → Cowork-DybeQjmm.js} +3 -3
- package/src/assets/web-panel/assets/{Cron-By4sh_NM.js → Cron-DS0BJeLY.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-DdZdbS1C.js → Crosschain-BOwVgkbs.js} +1 -1
- package/src/assets/web-panel/assets/{DID-CkL-sQyS.js → DID-DRUr9YuX.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-g_mOpumg.js → Dashboard-Cht-yUKW.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-DtHyE7Ao.js → Dropdown-BLr_EJ9X.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-Kpvov5Wr.js → EmailListRenderer-Drexuh5W.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-C4kZoePR.js → FamilyGuardDashboard-D9Q2Kq63.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-BAwWoK2N.js → Federation-C-Z1xI-T.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-DeI0i5f8.js → FormItemContext-DLHPMkQF.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-CkcKRtos.js → GenericCardRenderer-DxxEdn2r.js} +1 -1
- package/src/assets/web-panel/assets/{Git-kX6jjI9y.js → Git-syATcaBt.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-BXJCBm6x.js → Governance-CYe_r8Zf.js} +1 -1
- package/src/assets/web-panel/assets/Inference-GWxErB7k.js +1 -0
- package/src/assets/web-panel/assets/{KnowledgeGraph-3Dnuf1Wh.js → KnowledgeGraph-qQvVCZoi.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-lVAZCtUB.js → Logs-BenAU2W8.js} +2 -2
- package/src/assets/web-panel/assets/MarkdownRenderer-7agE3Or1.js +1 -0
- package/src/assets/web-panel/assets/MarkdownRenderer-DbzhFwvz.css +1 -0
- package/src/assets/web-panel/assets/Marketplace-D4Jn5fpD.js +1 -0
- package/src/assets/web-panel/assets/{McpTools-CqjzefRU.js → McpTools-DtAwoqiQ.js} +4 -4
- package/src/assets/web-panel/assets/{Memory-BFiPAxk6.js → Memory-BI_O50I8.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-D0O43VwF.js → MobileBridge-CQWE2lqx.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-B3now48t.js → MobileProjects-CVtTqgtg.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-DybhtaCS.js → Mtc-Dnr16qsn.js} +2 -2
- package/src/assets/web-panel/assets/{MtcAudit-D7MmY4F0.js → MtcAudit-CQ5Xe4-a.js} +5 -5
- package/src/assets/web-panel/assets/{Multisig-wZvGJnB8.js → Multisig-Bjr8O-wy.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-BNPpgfvn.js → NLProgramming-CEe-KirU.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-B6FS3KBj.js → Notes-spKJIKF9.js} +4 -4
- package/src/assets/web-panel/assets/{NotificationSettings-CK0yla9U.js → NotificationSettings-DRp83LFG.js} +1 -1
- package/src/assets/web-panel/assets/OrderTableRenderer-h1A0XB2a.js +1 -0
- package/src/assets/web-panel/assets/{Organization-geqgwN1T.js → Organization-BlgB0IYg.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-BWJYZFbC.js → Overflow-a_35HqZI.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-DnU1505F.js → P2P-CSNHCNPC.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-BGY74KnC.js → PdhVaultBrowser-C8M1JH-1.js} +5 -5
- package/src/assets/web-panel/assets/{Permissions-CgArrWtQ.js → Permissions-DmjJi9Ms.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-C2vYE61a.js → PersonalDataHub-DfuwRyf4.js} +2 -2
- package/src/assets/web-panel/assets/{Pipeline-CBYLI4yW.js → Pipeline-B2-S4ekE.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-B-PdW9aN.js → Privacy-B1dOiBLC.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-CuWQegbV.js → ProjectInit-_pNmc-DX.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-4d32m_cS.js → ProjectSettings-DmwG2Ii1.js} +2 -2
- package/src/assets/web-panel/assets/{Projects-VLA7ttwd.js → Projects-COATmHxF.js} +1 -1
- package/src/assets/web-panel/assets/{Providers-B_Izb_oa.js → Providers-C6HSjg1d.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-C6B_ndXV.js → QrScannerModal-CrDmS7l1.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-C6tavL-y.js → QuickAsk-CfKvWt-x.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-CF-LaZ6x.js → Recommend-DMa6Kuzv.js} +1 -1
- package/src/assets/web-panel/assets/RemoteSession-DrktUo5x.js +4 -0
- package/src/assets/web-panel/assets/Reputation-BTPQ1vjL.js +1 -0
- package/src/assets/web-panel/assets/{Row-DO0fd48A.js → Row-B6Q7QkMG.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-BV5LDzFR.js → RssFeed-B1IJhAmX.js} +3 -3
- package/src/assets/web-panel/assets/Search-xbea1kGD.js +1 -0
- package/src/assets/web-panel/assets/{Security-DwSrWRCw.js → Security-7rs0cO3F.js} +4 -4
- package/src/assets/web-panel/assets/{Services-BEf1QyM4.js → Services-0fjixK0S.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-LZXTpovy.js → Skeleton-B_rVq9Cu.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-SKgc0Zug.js → Skills-C5uM90b9.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-C_QVOtkN.js → Sla-U4pYCIZH.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-v5TW2V-X.js → SpeechSettings-Cdz4yTWx.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-CQPjHdqU.js → SyncSettings-Me3aetHv.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-DCd-uYcI.js → Tasks-Cf6aNkL_.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-62VDIjtj.js → Templates-CYatk4tU.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-BnFQ0pNy.js → Tenant-DIMKLw7o.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-D3v0MGSj.js → Terminal-C_cp-uSJ.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-BI1Ka-cu.js → TimelineRenderer-cUPVNADp.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-BwX1jXYP.js → Tokens-CiaEIXVf.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-CSA8TqNx.js → Trigger-DB1WU5lQ.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-Bn3IjCx7.js → Trust-Cfht9Ijy.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-pUbOAqNi.js → UkeySign-K3P4Vt9S.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-Db2RercF.js → VideoEditing-DWjhjXRh.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-CpR1BVAl.js → Wallet-D9-Q1Kan.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-Yxtkn7Zp.js → WebAuthn-BDszy-oV.js} +5 -5
- package/src/assets/web-panel/assets/{WorkflowEditor-CYLe9a2E.js → WorkflowEditor-DIhij4rA.js} +1 -1
- package/src/assets/web-panel/assets/{chat-gsOGdak0.js → chat-D7VCiKbh.js} +1 -1
- package/src/assets/web-panel/assets/{colors-D9fN299a.js → colors-BvOd9pVn.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-vkd7s-ay.js → compact-item-DoqXBiAO.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-C7YkbMyy.js → createContext-CrKRzUtp.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-nDB--2xr.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-ig7reRSU.js → hasIn-BWAev4WC.js} +1 -1
- package/src/assets/web-panel/assets/{icons-DEmcnxBf.js → icons-DG5UD-6K.js} +4 -4
- package/src/assets/web-panel/assets/{index-W52sHRdr.js → index-1poMpMU0.js} +1 -1
- package/src/assets/web-panel/assets/{index-DZAxCnjS.js → index-8g5TJCZ3.js} +1 -1
- package/src/assets/web-panel/assets/{index-9TxdMYju.js → index-B0vGEbJT.js} +1 -1
- package/src/assets/web-panel/assets/index-B1_pRTtB.js +1 -0
- package/src/assets/web-panel/assets/{index-DhdXbsXX.js → index-B30NvR21.js} +1 -1
- package/src/assets/web-panel/assets/{index-B7QRpKif.js → index-BCyP071m.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bt-9ZhIu.js → index-BHOcVYrx.js} +1 -1
- package/src/assets/web-panel/assets/{index-BYe9HRSt.js → index-BN9m4Ux2.js} +1 -1
- package/src/assets/web-panel/assets/{index-BkP17Z95.js → index-BWWsaimP.js} +1 -1
- package/src/assets/web-panel/assets/{index-CDWTycrg.js → index-BXyl8O23.js} +1 -1
- package/src/assets/web-panel/assets/{index-CspGymmg.js → index-BZGk8AbZ.js} +1 -1
- package/src/assets/web-panel/assets/{index-DFfxF7Vt.js → index-Bf9YlbDn.js} +1 -1
- package/src/assets/web-panel/assets/{index-DQ8U7kzC.js → index-Bj7EgmOp.js} +1 -1
- package/src/assets/web-panel/assets/{index-D2yhtk6a.js → index-BkgnGX7v.js} +1 -1
- package/src/assets/web-panel/assets/{index-FzGr6CVk.js → index-BoYrS_L2.js} +1 -1
- package/src/assets/web-panel/assets/{index-BMw48d19.js → index-Bq_ctRpz.js} +1 -1
- package/src/assets/web-panel/assets/{index-CAkz1f1r.js → index-C5kRmj3P.js} +1 -1
- package/src/assets/web-panel/assets/{index-SDrcQXO1.js → index-CDGLdn12.js} +1 -1
- package/src/assets/web-panel/assets/{index-gj8kxcAi.js → index-CEPHMSih.js} +1 -1
- package/src/assets/web-panel/assets/{index-PkH39qsp.js → index-CEXfKSiI.js} +1 -1
- package/src/assets/web-panel/assets/{index-BHHN_KTS.js → index-CSb-tnIo.js} +1 -1
- package/src/assets/web-panel/assets/{index-gpurplyq.js → index-CmJ6xKBO.js} +1 -1
- package/src/assets/web-panel/assets/{index-BfpVcXKa.js → index-CoD31Ndm.js} +1 -1
- package/src/assets/web-panel/assets/{index-CGzGe5tv.js → index-Cx7M7PSF.js} +1 -1
- package/src/assets/web-panel/assets/{index-BhftELHI.js → index-D23L1V76.js} +3 -3
- package/src/assets/web-panel/assets/{index-DPguq3X7.js → index-DV-Rfmfx.js} +4 -4
- package/src/assets/web-panel/assets/{index-BpGlLbRv.js → index-DlGu7As6.js} +1 -1
- package/src/assets/web-panel/assets/{index-C8HZMygt.js → index-DvDOpsJB.js} +1 -1
- package/src/assets/web-panel/assets/{index-DuwbKHoS.js → index-HwHdTusY.js} +1 -1
- package/src/assets/web-panel/assets/{index-C7AY39sg.js → index-ISvPZ05r.js} +1 -1
- package/src/assets/web-panel/assets/{index-nX8YE6cj.js → index-JzUmJMvn.js} +1 -1
- package/src/assets/web-panel/assets/{index-DvCJXmrx.js → index-KR1-Em9C.js} +1 -1
- package/src/assets/web-panel/assets/{index-Co2NYyew.js → index-PDxGjKRu.js} +1 -1
- package/src/assets/web-panel/assets/index-TMM9FuW-.js +1 -0
- package/src/assets/web-panel/assets/{index-D6s_kbjt.js → index-YGDy7nNq.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dy_TKZ9h.js → index-gmxqu5Kk.js} +1 -1
- package/src/assets/web-panel/assets/{index-DFM1oNe1.js → index-oY45XrkG.js} +1 -1
- package/src/assets/web-panel/assets/{index-bVbgYBhv.js → index-ww1QtLnX.js} +1 -1
- package/src/assets/web-panel/assets/{index-BvDEukXe.js → index-zH-tuuGZ.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-BrgfVRg9.js → initDefaultProps-XW-OR3mL.js} +1 -1
- package/src/assets/web-panel/assets/{motion-D_3xmwac.js → motion-CeuEHh20.js} +1 -1
- package/src/assets/web-panel/assets/{move-DDDJdlxw.js → move-Dva79Keg.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-BK2uEWwn.js → mtc-parser-C0B6bD2j.js} +1 -1
- package/src/assets/web-panel/assets/{omit-BPHKbDcf.js → omit-AkZvO-a2.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-LNnTscuz.js → pickAttrs-B4r55qGc.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-CvmTZll5.js → placementArrow-BLH-z7YE.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-eBj7qi2J.js → responsiveObserve-ksEGZ2Wf.js} +1 -1
- package/src/assets/web-panel/assets/{slide-D68hHkrM.js → slide-Cth4OD1Z.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-daTlaeo0.js → statusUtils-9S83ezxw.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-DtyKMtHB.js → styleChecker-7yQmQGZ4.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-ekFwFs9h.js → useFlexGapSupport-DpGnMmPc.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-jRaSEoF4.js → useFs-C4zDZIXH.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-DD5nGzjA.js → usePersonalDataHub-CoLICdUZ.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-KcgLUiom.js → vnode-CKyH-35X.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-Cyk2PQ-s.js → zoom-DRDGit17.js} +1 -1
- package/src/assets/web-panel/index.html +2 -2
- package/src/command-manifest.json +8 -1
- package/src/commands/agent.js +49 -5
- package/src/commands/artifacts.js +161 -0
- package/src/commands/browse.js +204 -2
- package/src/data/changelog.json +13 -2
- package/src/gateways/ws/artifact-protocol.js +219 -0
- package/src/gateways/ws/message-dispatcher.js +6 -0
- package/src/gateways/ws/ws-server.js +32 -0
- package/src/index.js +6 -0
- package/src/lib/accessibility.js +55 -0
- package/src/lib/artifact-store.js +236 -0
- package/src/lib/chrome-connector.js +349 -0
- package/src/lib/hook-runner.cjs +47 -7
- package/src/lib/ide-context.js +84 -0
- package/src/lib/permission-rules.cjs +26 -3
- package/src/lib/plugin-runtime/scopes.js +12 -0
- package/src/lib/remote-control.js +35 -0
- package/src/lib/safe-mode.js +40 -0
- package/src/lib/shell-selector.cjs +203 -0
- package/src/lib/skill-loader.js +19 -0
- package/src/lib/slash-dispatch.js +34 -0
- package/src/lib/web-ui-server.js +120 -7
- package/src/repl/agent-repl.js +330 -116
- package/src/repl/remote-approval.js +111 -0
- package/src/runtime/agent-core.js +129 -8
- package/src/runtime/coding-agent-contract-shared.cjs +105 -0
- package/src/runtime/coding-agent-policy.cjs +31 -0
- package/src/runtime/headless-stream.js +51 -1
- package/src/runtime/policies/agent-policy.js +4 -0
- package/src/assets/web-panel/assets/AppLayout-CfyZNzmJ.js +0 -9
- package/src/assets/web-panel/assets/Chat-DDh58PCf.css +0 -1
- package/src/assets/web-panel/assets/Inference-ujD2JSKo.js +0 -1
- package/src/assets/web-panel/assets/Marketplace-qnzNPm-A.js +0 -1
- package/src/assets/web-panel/assets/OrderTableRenderer-CXfPHIpy.js +0 -1
- package/src/assets/web-panel/assets/RemoteSession-DBqTTQ9_.js +0 -4
- package/src/assets/web-panel/assets/Reputation-B01FALUi.js +0 -1
- package/src/assets/web-panel/assets/Search-CTti4_MP.js +0 -1
- package/src/assets/web-panel/assets/devWarning-DEcR2h6k.js +0 -1
- package/src/assets/web-panel/assets/index-BEibanjg.js +0 -1
- package/src/assets/web-panel/assets/index-z_25tU3p.js +0 -1
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* REPL-side remote-approval glue (gap-analysis 批26 — 批17/18 REPL 遗留收口).
|
|
3
|
+
*
|
|
4
|
+
* Interactive sessions race the LOCAL terminal prompt against a paired
|
|
5
|
+
* device's decision (headless gates on the remote decision alone):
|
|
6
|
+
*
|
|
7
|
+
* - local answer first → settle the device card too (resolveLocally
|
|
8
|
+
* publishes permission.resolved so the phone/web UI clears it)
|
|
9
|
+
* - remote answer first → cancel the local readline prompt and print
|
|
10
|
+
* the outcome
|
|
11
|
+
* - remote leg times out or the bridge closes mid-ask → NOT a decision:
|
|
12
|
+
* keep waiting on the terminal. A silent phone must never auto-deny a
|
|
13
|
+
* user sitting at the keyboard — the fail-closed timeout is headless
|
|
14
|
+
* semantics only.
|
|
15
|
+
*
|
|
16
|
+
* Pure over an injected bridge + local prompt handle ({promise, cancel}),
|
|
17
|
+
* so the race is unit-testable without a WS server or readline.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Normalize the shapes the REPL's two confirmers receive (ApprovalGate
|
|
22
|
+
* `{tool, args, riskLevel}` / settings-hook `{tool, args, rule, reason}`)
|
|
23
|
+
* into the bridge's `{tool, action, detail}` ask payload.
|
|
24
|
+
*/
|
|
25
|
+
export function describeAskContext({
|
|
26
|
+
tool,
|
|
27
|
+
args,
|
|
28
|
+
rule = null,
|
|
29
|
+
reason = null,
|
|
30
|
+
riskLevel = null,
|
|
31
|
+
} = {}) {
|
|
32
|
+
const detail =
|
|
33
|
+
typeof args?.command === "string"
|
|
34
|
+
? args.command
|
|
35
|
+
: typeof args?.path === "string"
|
|
36
|
+
? args.path
|
|
37
|
+
: args
|
|
38
|
+
? JSON.stringify(args).slice(0, 2000)
|
|
39
|
+
: null;
|
|
40
|
+
return {
|
|
41
|
+
tool: tool || null,
|
|
42
|
+
action: rule
|
|
43
|
+
? `ask-rule:${rule}`
|
|
44
|
+
: reason || (riskLevel ? `${riskLevel}-risk` : null),
|
|
45
|
+
detail,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Remote outcomes that are NOT a decision for an interactive session: the
|
|
50
|
+
// devices went silent (timeout) or the bridge was torn down mid-ask (closed).
|
|
51
|
+
const NON_DECISIVE_REMOTE = new Set(["timeout", "closed"]);
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Race the local interactive prompt against the paired-device decision.
|
|
55
|
+
*
|
|
56
|
+
* @param {object} opts
|
|
57
|
+
* @param {import("../lib/remote-approval-bridge.js").RemoteApprovalBridge} opts.bridge
|
|
58
|
+
* @param {{tool?, action?, detail?}} opts.ask - describeAskContext() payload
|
|
59
|
+
* @param {{promise: Promise<boolean>, cancel?: () => void}} opts.local -
|
|
60
|
+
* the already-started local prompt; `cancel` closes its readline when the
|
|
61
|
+
* device answers first (implementations must mute their idle-timeout
|
|
62
|
+
* message after cancel).
|
|
63
|
+
* @param {(text: string) => void} [opts.writeOut]
|
|
64
|
+
* @returns {Promise<boolean>} approved
|
|
65
|
+
*/
|
|
66
|
+
export async function raceLocalAndRemote({
|
|
67
|
+
bridge,
|
|
68
|
+
ask = {},
|
|
69
|
+
local,
|
|
70
|
+
writeOut = () => {},
|
|
71
|
+
}) {
|
|
72
|
+
let requestId = null;
|
|
73
|
+
const remoteLeg = bridge
|
|
74
|
+
.requestDecision({
|
|
75
|
+
...ask,
|
|
76
|
+
onRequestId: (id) => {
|
|
77
|
+
requestId = id;
|
|
78
|
+
},
|
|
79
|
+
})
|
|
80
|
+
.then((decision) => ({ src: "remote", decision }));
|
|
81
|
+
const localLeg = local.promise.then(
|
|
82
|
+
(approved) => ({ src: "local", approved: approved === true }),
|
|
83
|
+
() => ({ src: "local", approved: false }),
|
|
84
|
+
);
|
|
85
|
+
let winner = await Promise.race([remoteLeg, localLeg]);
|
|
86
|
+
if (
|
|
87
|
+
winner.src === "remote" &&
|
|
88
|
+
NON_DECISIVE_REMOTE.has(winner.decision?.via)
|
|
89
|
+
) {
|
|
90
|
+
winner = await localLeg;
|
|
91
|
+
}
|
|
92
|
+
if (winner.src === "local") {
|
|
93
|
+
if (requestId) {
|
|
94
|
+
try {
|
|
95
|
+
bridge.resolveLocally(requestId, winner.approved);
|
|
96
|
+
} catch {
|
|
97
|
+
// clearing the device card is best-effort
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return winner.approved;
|
|
101
|
+
}
|
|
102
|
+
try {
|
|
103
|
+
local.cancel?.();
|
|
104
|
+
} catch {
|
|
105
|
+
// readline already closed
|
|
106
|
+
}
|
|
107
|
+
writeOut(
|
|
108
|
+
`\n ✓ ${winner.decision.approved ? "allowed" : "denied"} from paired device\n`,
|
|
109
|
+
);
|
|
110
|
+
return winner.decision.approved === true;
|
|
111
|
+
}
|
|
@@ -1041,6 +1041,7 @@ async function tryIdeDiffApprovalForEdit(
|
|
|
1041
1041
|
hasIdeOpenDiff,
|
|
1042
1042
|
requestIdeDiffApproval,
|
|
1043
1043
|
formatReviewComments,
|
|
1044
|
+
summarizeUserAmendments,
|
|
1044
1045
|
} = await import("../lib/ide-context.js");
|
|
1045
1046
|
const mcpLike = {
|
|
1046
1047
|
mcpClient: context.mcpClient,
|
|
@@ -1056,15 +1057,22 @@ async function tryIdeDiffApprovalForEdit(
|
|
|
1056
1057
|
title: `cc agent: ${name} ${path.basename(proposal.filePath)}`,
|
|
1057
1058
|
});
|
|
1058
1059
|
if (verdict?.outcome === "accepted") {
|
|
1060
|
+
// When the reviewer amended the proposal in the diff before accepting,
|
|
1061
|
+
// hand the agent the actual -/+ delta — not just a flag — so its model
|
|
1062
|
+
// of the file matches what was really written (gap #4: the agent
|
|
1063
|
+
// perceives the user's edits).
|
|
1064
|
+
const amendments =
|
|
1065
|
+
verdict.finalText != null && verdict.finalText !== proposal.newContent
|
|
1066
|
+
? summarizeUserAmendments(proposal.newContent, verdict.finalText)
|
|
1067
|
+
: null;
|
|
1059
1068
|
return {
|
|
1060
1069
|
outcome: "accepted",
|
|
1061
1070
|
result: {
|
|
1062
1071
|
success: true,
|
|
1063
1072
|
path: proposal.filePath,
|
|
1064
1073
|
appliedVia: "ide-diff",
|
|
1065
|
-
...(
|
|
1066
|
-
|
|
1067
|
-
? { userEdited: true }
|
|
1074
|
+
...(amendments
|
|
1075
|
+
? { userEdited: true, userAmendments: amendments }
|
|
1068
1076
|
: {}),
|
|
1069
1077
|
policy: { decision: "allow", rule, via: "ide-diff" },
|
|
1070
1078
|
},
|
|
@@ -2431,6 +2439,24 @@ async function executeToolInner(
|
|
|
2431
2439
|
}
|
|
2432
2440
|
}
|
|
2433
2441
|
|
|
2442
|
+
// P1 #8 Windows/PowerShell first-class: per-call args.shell or the
|
|
2443
|
+
// configured settings `shell.windowsDefault` may route this command
|
|
2444
|
+
// through PowerShell via explicit argv (`powershell.exe -NoProfile
|
|
2445
|
+
// [-ExecutionPolicy <p>] -Command <cmd>`). The unconfigured path keeps
|
|
2446
|
+
// the historical default shell byte-identical (useDefaultShell=true).
|
|
2447
|
+
const { resolveShellInvocation } =
|
|
2448
|
+
await import("../lib/shell-selector.cjs");
|
|
2449
|
+
const shellInv = resolveShellInvocation({
|
|
2450
|
+
command: args.command,
|
|
2451
|
+
requested: args.shell,
|
|
2452
|
+
cwd: args.cwd || cwd,
|
|
2453
|
+
});
|
|
2454
|
+
const shellMeta = shellInv.useDefaultShell
|
|
2455
|
+
? shellInv.note
|
|
2456
|
+
? { shell_note: shellInv.note }
|
|
2457
|
+
: {}
|
|
2458
|
+
: { shell: shellInv.kind };
|
|
2459
|
+
|
|
2434
2460
|
// Background: spawn, register, return a task_id immediately. The agent
|
|
2435
2461
|
// polls output + completion via check_shell. No timeout — that's the whole
|
|
2436
2462
|
// point of backgrounding (builds, test suites, dev servers).
|
|
@@ -2463,9 +2489,9 @@ async function executeToolInner(
|
|
|
2463
2489
|
child: null,
|
|
2464
2490
|
};
|
|
2465
2491
|
try {
|
|
2466
|
-
const
|
|
2492
|
+
const bgSpawnOpts = {
|
|
2467
2493
|
cwd: task.cwd,
|
|
2468
|
-
shell:
|
|
2494
|
+
shell: shellInv.useDefaultShell,
|
|
2469
2495
|
windowsHide: true,
|
|
2470
2496
|
// Same agent-identity env as the foreground path: CLAUDECODE marks
|
|
2471
2497
|
// "running under the agent"; the session id correlates work to the
|
|
@@ -2484,7 +2510,12 @@ async function executeToolInner(
|
|
|
2484
2510
|
// the whole tree (shell + its grandchild command). No-op on Windows
|
|
2485
2511
|
// where the tree is killed via taskkill /T instead.
|
|
2486
2512
|
detached: process.platform !== "win32",
|
|
2487
|
-
}
|
|
2513
|
+
};
|
|
2514
|
+
// PowerShell route: explicit argv (shell:false above); default route
|
|
2515
|
+
// is the historical spawn(command, {shell:true}) byte-for-byte.
|
|
2516
|
+
const child = shellInv.useDefaultShell
|
|
2517
|
+
? spawn(args.command, bgSpawnOpts)
|
|
2518
|
+
: spawn(shellInv.file, shellInv.argv, bgSpawnOpts);
|
|
2488
2519
|
task.child = child;
|
|
2489
2520
|
if (child.stdout) {
|
|
2490
2521
|
child.stdout.setEncoding("utf8");
|
|
@@ -2532,6 +2563,7 @@ async function executeToolInner(
|
|
|
2532
2563
|
task_id: id,
|
|
2533
2564
|
status: task.status,
|
|
2534
2565
|
command: task.command,
|
|
2566
|
+
...shellMeta,
|
|
2535
2567
|
hint: "Poll output and completion with the check_shell tool using this task_id. Kill long-lived servers with check_shell { task_id, kill: true } when done.",
|
|
2536
2568
|
shellCommandPolicy: shellPolicy,
|
|
2537
2569
|
approval: approvalOutcome,
|
|
@@ -2630,7 +2662,7 @@ async function executeToolInner(
|
|
|
2630
2662
|
}
|
|
2631
2663
|
|
|
2632
2664
|
try {
|
|
2633
|
-
const
|
|
2665
|
+
const fgExecOpts = {
|
|
2634
2666
|
cwd: args.cwd || cwd,
|
|
2635
2667
|
encoding: "utf8",
|
|
2636
2668
|
timeout: _resolveShellTimeout(args.timeout),
|
|
@@ -2649,10 +2681,35 @@ async function executeToolInner(
|
|
|
2649
2681
|
}
|
|
2650
2682
|
: {}),
|
|
2651
2683
|
},
|
|
2652
|
-
}
|
|
2684
|
+
};
|
|
2685
|
+
let output;
|
|
2686
|
+
if (shellInv.useDefaultShell) {
|
|
2687
|
+
output = execSync(args.command, fgExecOpts);
|
|
2688
|
+
} else {
|
|
2689
|
+
// PowerShell route: explicit argv, no intermediate default shell.
|
|
2690
|
+
// Reproduce execSync's contract so the shared catch shapes errors
|
|
2691
|
+
// identically: throw on spawn error; throw an Error carrying
|
|
2692
|
+
// status/stdout/stderr on non-zero exit.
|
|
2693
|
+
const res = spawnSync(shellInv.file, shellInv.argv, {
|
|
2694
|
+
...fgExecOpts,
|
|
2695
|
+
windowsHide: true,
|
|
2696
|
+
});
|
|
2697
|
+
if (res.error) throw res.error;
|
|
2698
|
+
if (res.status !== 0) {
|
|
2699
|
+
const e = new Error(
|
|
2700
|
+
`Command failed (${shellInv.kind}, exit ${res.status}): ${args.command}`,
|
|
2701
|
+
);
|
|
2702
|
+
e.status = res.status;
|
|
2703
|
+
e.stdout = res.stdout;
|
|
2704
|
+
e.stderr = res.stderr;
|
|
2705
|
+
throw e;
|
|
2706
|
+
}
|
|
2707
|
+
output = res.stdout ?? "";
|
|
2708
|
+
}
|
|
2653
2709
|
return attachDescriptor(
|
|
2654
2710
|
{
|
|
2655
2711
|
stdout: output.substring(0, 30000),
|
|
2712
|
+
...shellMeta,
|
|
2656
2713
|
shellCommandPolicy: shellPolicy,
|
|
2657
2714
|
approval: approvalOutcome,
|
|
2658
2715
|
},
|
|
@@ -2672,6 +2729,7 @@ async function executeToolInner(
|
|
|
2672
2729
|
: {}),
|
|
2673
2730
|
stderr: (err.stderr || "").substring(0, 2000),
|
|
2674
2731
|
exitCode: err.status,
|
|
2732
|
+
...shellMeta,
|
|
2675
2733
|
shellCommandPolicy: shellPolicy,
|
|
2676
2734
|
approval: approvalOutcome,
|
|
2677
2735
|
},
|
|
@@ -3104,6 +3162,65 @@ async function executeToolInner(
|
|
|
3104
3162
|
}
|
|
3105
3163
|
}
|
|
3106
3164
|
|
|
3165
|
+
case "publish_artifact": {
|
|
3166
|
+
// P1 #10: copy a finished deliverable into the user's artifact store.
|
|
3167
|
+
// Only METADATA returns to the conversation (the transcript never
|
|
3168
|
+
// carries the file body); `cc artifacts` lists/inspects/cleans.
|
|
3169
|
+
try {
|
|
3170
|
+
const { ArtifactStore } = await import("../lib/artifact-store.js");
|
|
3171
|
+
const store = new ArtifactStore();
|
|
3172
|
+
const entry = store.publish({
|
|
3173
|
+
filePath: path.resolve(cwd, String(args.path || "")),
|
|
3174
|
+
title: args.title,
|
|
3175
|
+
kind: args.kind,
|
|
3176
|
+
ttlDays: args.ttl_days,
|
|
3177
|
+
sessionId: sessionId ? String(sessionId) : null,
|
|
3178
|
+
});
|
|
3179
|
+
return attachDescriptor({
|
|
3180
|
+
published: entry,
|
|
3181
|
+
hint: "The user can browse this with `cc artifacts list` / `cc artifacts show <id>`.",
|
|
3182
|
+
});
|
|
3183
|
+
} catch (err) {
|
|
3184
|
+
return attachDescriptor({
|
|
3185
|
+
error: `publish_artifact failed: ${err.message}`,
|
|
3186
|
+
});
|
|
3187
|
+
}
|
|
3188
|
+
}
|
|
3189
|
+
|
|
3190
|
+
case "browser_state": {
|
|
3191
|
+
// P1 #8: first-class Chrome CDP observation — the agent side of the
|
|
3192
|
+
// "locate error → fix → verify" web loop against the user's real,
|
|
3193
|
+
// logged-in browser (`cc browse chrome launch`). DOM is capped harder
|
|
3194
|
+
// than the CLI command's 150k default so a page dump cannot flood the
|
|
3195
|
+
// conversation; screenshots go to a generated temp file (never an
|
|
3196
|
+
// agent-chosen path) to keep the tool strictly read-only.
|
|
3197
|
+
try {
|
|
3198
|
+
const { captureState } = await import("../lib/chrome-connector.js");
|
|
3199
|
+
const screenshotPath = args.screenshot
|
|
3200
|
+
? path.join(os.tmpdir(), `cc-browser-state-${Date.now()}.png`)
|
|
3201
|
+
: null;
|
|
3202
|
+
const state = await captureState({
|
|
3203
|
+
port: args.port != null ? Number(args.port) : undefined,
|
|
3204
|
+
tab: args.tab != null ? Number(args.tab) : undefined,
|
|
3205
|
+
reload: args.reload === true,
|
|
3206
|
+
watchMs: args.watch_ms != null ? Number(args.watch_ms) : undefined,
|
|
3207
|
+
includeDom: args.include_dom !== false,
|
|
3208
|
+
domCap: args.dom_cap != null ? Number(args.dom_cap) : 40000,
|
|
3209
|
+
screenshotPath,
|
|
3210
|
+
});
|
|
3211
|
+
if (!state.ok) {
|
|
3212
|
+
return attachDescriptor({
|
|
3213
|
+
error: `browser_state failed: ${state.error}`,
|
|
3214
|
+
});
|
|
3215
|
+
}
|
|
3216
|
+
return attachDescriptor(state);
|
|
3217
|
+
} catch (err) {
|
|
3218
|
+
return attachDescriptor({
|
|
3219
|
+
error: `browser_state failed: ${err.message}`,
|
|
3220
|
+
});
|
|
3221
|
+
}
|
|
3222
|
+
}
|
|
3223
|
+
|
|
3107
3224
|
case "search_files": {
|
|
3108
3225
|
// An explicit directory scopes the search to one root; otherwise span
|
|
3109
3226
|
// cwd plus any --add-dir roots so cross-package searches find matches.
|
|
@@ -6438,6 +6555,10 @@ export function formatToolArgs(name, args) {
|
|
|
6438
6555
|
return `"${(args.query || "").substring(0, 60)}"`;
|
|
6439
6556
|
case "notify":
|
|
6440
6557
|
return `${args.level || "info"}: ${(args.title || "").substring(0, 50)}`;
|
|
6558
|
+
case "publish_artifact":
|
|
6559
|
+
return `${args.kind || "other"}: ${(args.title || args.path || "").substring(0, 60)}`;
|
|
6560
|
+
case "browser_state":
|
|
6561
|
+
return `tab=${args.tab ?? 0} port=${args.port ?? 9222}${args.reload ? " reload" : ""}`;
|
|
6441
6562
|
case "schedule":
|
|
6442
6563
|
return args.action === "cron"
|
|
6443
6564
|
? `cron ${args.cron || ""}`.trim()
|
|
@@ -238,6 +238,12 @@ const CODING_AGENT_TOOL_CONTRACTS = Object.freeze([
|
|
|
238
238
|
description:
|
|
239
239
|
"Foreground (synchronous) timeout in milliseconds. Default 60000, max 600000. Ignored when run_in_background is true.",
|
|
240
240
|
},
|
|
241
|
+
shell: {
|
|
242
|
+
type: "string",
|
|
243
|
+
enum: ["default", "cmd", "powershell", "pwsh"],
|
|
244
|
+
description:
|
|
245
|
+
"Shell to run the command with (optional). default = the platform shell (cmd.exe on Windows, /bin/sh elsewhere) or the configured settings shell.windowsDefault. Use powershell (Windows PowerShell 5.1) or pwsh (PowerShell 7+) when the command uses PowerShell syntax: cmdlets, object pipelines, $variables.",
|
|
246
|
+
},
|
|
241
247
|
},
|
|
242
248
|
required: ["command"],
|
|
243
249
|
},
|
|
@@ -953,6 +959,105 @@ const CODING_AGENT_TOOL_CONTRACTS = Object.freeze([
|
|
|
953
959
|
tags: ["tool:schedule", "contract:coding-agent", "tier:extension"],
|
|
954
960
|
},
|
|
955
961
|
},
|
|
962
|
+
{
|
|
963
|
+
name: "publish_artifact",
|
|
964
|
+
title: "Publish Artifact",
|
|
965
|
+
kind: "artifact",
|
|
966
|
+
tier: "extension",
|
|
967
|
+
description:
|
|
968
|
+
"Publish a finished deliverable file (Markdown report, patch, screenshot, test log, findings JSON) as a durable artifact the user can find later with `cc artifacts`. The file is copied into the user's artifact store with metadata; only the metadata enters the conversation. Use at the END of a task for its key outputs — not for intermediate scratch files.",
|
|
969
|
+
inputSchema: {
|
|
970
|
+
type: "object",
|
|
971
|
+
properties: {
|
|
972
|
+
path: {
|
|
973
|
+
type: "string",
|
|
974
|
+
description:
|
|
975
|
+
"Path of the file to publish (relative to cwd or absolute)",
|
|
976
|
+
},
|
|
977
|
+
title: {
|
|
978
|
+
type: "string",
|
|
979
|
+
description: "Human title for the artifact (default: the file name)",
|
|
980
|
+
},
|
|
981
|
+
kind: {
|
|
982
|
+
type: "string",
|
|
983
|
+
enum: ["report", "patch", "screenshot", "log", "data", "other"],
|
|
984
|
+
description: "What kind of deliverable this is (default: other)",
|
|
985
|
+
},
|
|
986
|
+
ttl_days: {
|
|
987
|
+
type: "number",
|
|
988
|
+
description:
|
|
989
|
+
"Days to retain before `cc artifacts clean` may remove it (default 30)",
|
|
990
|
+
},
|
|
991
|
+
},
|
|
992
|
+
required: ["path"],
|
|
993
|
+
},
|
|
994
|
+
...TOOL_POLICY_METADATA.publish_artifact,
|
|
995
|
+
permissions: {
|
|
996
|
+
level: "standard",
|
|
997
|
+
scopes: ["filesystem:read", "artifact:write"],
|
|
998
|
+
},
|
|
999
|
+
telemetry: {
|
|
1000
|
+
category: "artifact",
|
|
1001
|
+
tags: [
|
|
1002
|
+
"tool:publish_artifact",
|
|
1003
|
+
"contract:coding-agent",
|
|
1004
|
+
"tier:extension",
|
|
1005
|
+
],
|
|
1006
|
+
},
|
|
1007
|
+
},
|
|
1008
|
+
{
|
|
1009
|
+
name: "browser_state",
|
|
1010
|
+
title: "Browser State",
|
|
1011
|
+
kind: "browser",
|
|
1012
|
+
tier: "extension",
|
|
1013
|
+
description:
|
|
1014
|
+
"Capture live state from the user's real Chrome over CDP: current URL/title, open tabs, console messages and failed/error network requests observed during a short watch window, a capped DOM snapshot, and optionally a screenshot (saved to a temp file whose path is returned). Requires a debuggable Chrome — the user starts one with `cc browse chrome launch`. Console/network capture starts at attach time (CDP keeps no history) — set reload=true to observe load-time errors. Use to verify web changes in the user's logged-in browser.",
|
|
1015
|
+
inputSchema: {
|
|
1016
|
+
type: "object",
|
|
1017
|
+
properties: {
|
|
1018
|
+
port: {
|
|
1019
|
+
type: "number",
|
|
1020
|
+
description: "CDP port of the debuggable Chrome (default 9222)",
|
|
1021
|
+
},
|
|
1022
|
+
tab: {
|
|
1023
|
+
type: "number",
|
|
1024
|
+
description: "Tab index to observe (default 0)",
|
|
1025
|
+
},
|
|
1026
|
+
reload: {
|
|
1027
|
+
type: "boolean",
|
|
1028
|
+
description:
|
|
1029
|
+
"Reload the page first to capture load-time console/network (default false)",
|
|
1030
|
+
},
|
|
1031
|
+
watch_ms: {
|
|
1032
|
+
type: "number",
|
|
1033
|
+
description: "Observation window in milliseconds (default 3000)",
|
|
1034
|
+
},
|
|
1035
|
+
include_dom: {
|
|
1036
|
+
type: "boolean",
|
|
1037
|
+
description: "Include a capped HTML snapshot (default true)",
|
|
1038
|
+
},
|
|
1039
|
+
dom_cap: {
|
|
1040
|
+
type: "number",
|
|
1041
|
+
description: "Max HTML characters to return (default 40000)",
|
|
1042
|
+
},
|
|
1043
|
+
screenshot: {
|
|
1044
|
+
type: "boolean",
|
|
1045
|
+
description:
|
|
1046
|
+
"Save a screenshot to a generated temp file and return its path (default false)",
|
|
1047
|
+
},
|
|
1048
|
+
},
|
|
1049
|
+
required: [],
|
|
1050
|
+
},
|
|
1051
|
+
...TOOL_POLICY_METADATA.browser_state,
|
|
1052
|
+
permissions: {
|
|
1053
|
+
level: "standard",
|
|
1054
|
+
scopes: ["browser:read"],
|
|
1055
|
+
},
|
|
1056
|
+
telemetry: {
|
|
1057
|
+
category: "browser",
|
|
1058
|
+
tags: ["tool:browser_state", "contract:coding-agent", "tier:extension"],
|
|
1059
|
+
},
|
|
1060
|
+
},
|
|
956
1061
|
]);
|
|
957
1062
|
|
|
958
1063
|
const CODING_AGENT_MVP_TOOL_NAMES = Object.freeze(
|
|
@@ -289,6 +289,37 @@ const TOOL_POLICY_METADATA = Object.freeze({
|
|
|
289
289
|
approvalFlow: "auto",
|
|
290
290
|
isReadOnly: false,
|
|
291
291
|
},
|
|
292
|
+
// Copies a finished deliverable (report/patch/screenshot/log) into the
|
|
293
|
+
// user's own ~/.chainlesschain/artifacts store with metadata. Reads a
|
|
294
|
+
// workspace file, writes only under the user's config dir — no third-party
|
|
295
|
+
// egress. Blocked in plan mode (publishing is a side effect of DONE work,
|
|
296
|
+
// not analysis).
|
|
297
|
+
publish_artifact: {
|
|
298
|
+
riskLevel: RISK_LEVELS.LOW,
|
|
299
|
+
category: TOOL_CATEGORIES.WRITE,
|
|
300
|
+
availableInPlanMode: false,
|
|
301
|
+
planModeBehavior: "blocked",
|
|
302
|
+
requiresPlanApproval: false,
|
|
303
|
+
requiresConfirmation: false,
|
|
304
|
+
approvalFlow: "auto",
|
|
305
|
+
isReadOnly: false,
|
|
306
|
+
},
|
|
307
|
+
// Attaches to the user's own debuggable Chrome over loopback CDP and
|
|
308
|
+
// observes one tab (console/network/DOM/optional screenshot) for a bounded
|
|
309
|
+
// window. Observation only — it never clicks, types, or navigates; the
|
|
310
|
+
// optional reload re-runs the page the user already has open, and the
|
|
311
|
+
// screenshot goes to a generated temp path (never an agent-chosen one), so
|
|
312
|
+
// the tool stays read-only and plan-mode safe.
|
|
313
|
+
browser_state: {
|
|
314
|
+
riskLevel: RISK_LEVELS.LOW,
|
|
315
|
+
category: TOOL_CATEGORIES.READ,
|
|
316
|
+
availableInPlanMode: true,
|
|
317
|
+
planModeBehavior: "allow",
|
|
318
|
+
requiresPlanApproval: false,
|
|
319
|
+
requiresConfirmation: false,
|
|
320
|
+
approvalFlow: "auto",
|
|
321
|
+
isReadOnly: true,
|
|
322
|
+
},
|
|
292
323
|
});
|
|
293
324
|
|
|
294
325
|
function normalizeGitCommand(command) {
|
|
@@ -181,6 +181,47 @@ export function planSnapshot(pm) {
|
|
|
181
181
|
}
|
|
182
182
|
return { active: pm.isActive(), state: pm.state || null, items, risk };
|
|
183
183
|
}
|
|
184
|
+
|
|
185
|
+
const MAX_PLAN_REVIEW_SNAPSHOT_CHARS = 24000;
|
|
186
|
+
|
|
187
|
+
function planReviewFromInput(obj) {
|
|
188
|
+
const review =
|
|
189
|
+
obj && obj.review && typeof obj.review === "object" ? obj.review : null;
|
|
190
|
+
const snapshot =
|
|
191
|
+
typeof obj?.snapshot === "string"
|
|
192
|
+
? obj.snapshot
|
|
193
|
+
: typeof review?.snapshot === "string"
|
|
194
|
+
? review.snapshot
|
|
195
|
+
: "";
|
|
196
|
+
if (!snapshot.trim()) return null;
|
|
197
|
+
const text =
|
|
198
|
+
snapshot.length > MAX_PLAN_REVIEW_SNAPSHOT_CHARS
|
|
199
|
+
? snapshot.slice(0, MAX_PLAN_REVIEW_SNAPSHOT_CHARS) +
|
|
200
|
+
`\n\n[review snapshot truncated: ${snapshot.length - MAX_PLAN_REVIEW_SNAPSHOT_CHARS} chars omitted]`
|
|
201
|
+
: snapshot;
|
|
202
|
+
return {
|
|
203
|
+
action: String(review?.action || obj.action || "").toLowerCase(),
|
|
204
|
+
reviewedAt:
|
|
205
|
+
typeof review?.reviewedAt === "string" ? review.reviewedAt : null,
|
|
206
|
+
conversationId:
|
|
207
|
+
typeof review?.conversationId === "string" ? review.conversationId : null,
|
|
208
|
+
snapshot: text,
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function planReviewSystemMessage(review) {
|
|
213
|
+
if (!review?.snapshot) return null;
|
|
214
|
+
const meta = [
|
|
215
|
+
review.action ? `action=${review.action}` : null,
|
|
216
|
+
review.reviewedAt ? `reviewedAt=${review.reviewedAt}` : null,
|
|
217
|
+
review.conversationId ? `conversationId=${review.conversationId}` : null,
|
|
218
|
+
]
|
|
219
|
+
.filter(Boolean)
|
|
220
|
+
.join(" ");
|
|
221
|
+
return (
|
|
222
|
+
"[PLAN REVIEW SNAPSHOT]" + (meta ? ` ${meta}` : "") + "\n" + review.snapshot
|
|
223
|
+
);
|
|
224
|
+
}
|
|
184
225
|
import { withQuietStdout } from "./quiet-stdout.js";
|
|
185
226
|
|
|
186
227
|
/**
|
|
@@ -199,7 +240,9 @@ export function parseInputEvent(line) {
|
|
|
199
240
|
// {"type":"plan","action":"enter"|"approve"|"reject"}
|
|
200
241
|
if (obj && typeof obj === "object" && obj.type === "plan") {
|
|
201
242
|
const action = String(obj.action || "").toLowerCase();
|
|
202
|
-
|
|
243
|
+
if (!action) return null;
|
|
244
|
+
const planReview = planReviewFromInput(obj);
|
|
245
|
+
return planReview ? { plan: action, planReview } : { plan: action };
|
|
203
246
|
}
|
|
204
247
|
// Turn interrupt (panel Stop / Claude-Code Esc parity): aborts the
|
|
205
248
|
// in-flight turn without ending the conversation. {"type":"interrupt"}
|
|
@@ -1304,6 +1347,13 @@ export async function runAgentHeadlessStream(options = {}, deps = {}) {
|
|
|
1304
1347
|
// exits plan mode. Every control answers with a `plan_update` event.
|
|
1305
1348
|
if (parsed.plan) {
|
|
1306
1349
|
const pm = getPlanModeManager();
|
|
1350
|
+
const reviewMessage =
|
|
1351
|
+
parsed.plan === "approve" || parsed.plan === "reject"
|
|
1352
|
+
? planReviewSystemMessage(parsed.planReview)
|
|
1353
|
+
: null;
|
|
1354
|
+
if (reviewMessage) {
|
|
1355
|
+
messages.push({ role: "system", content: reviewMessage });
|
|
1356
|
+
}
|
|
1307
1357
|
if (parsed.plan === "enter") {
|
|
1308
1358
|
if (!pm.isActive()) {
|
|
1309
1359
|
pm.enterPlanMode({ title: "Agent Plan" });
|
|
@@ -55,6 +55,10 @@ export function resolveAgentPolicy({
|
|
|
55
55
|
fallbackModels: overrides.fallbackModels,
|
|
56
56
|
pdh: overrides.pdh,
|
|
57
57
|
outputStyle: overrides.outputStyle,
|
|
58
|
+
disableSlashCommands: overrides.disableSlashCommands === true,
|
|
59
|
+
// --remote-control also applies interactively: the REPL starts the
|
|
60
|
+
// paired-device approval bridge at startup (批26).
|
|
61
|
+
remoteControl: overrides.remoteControl === true,
|
|
58
62
|
};
|
|
59
63
|
}
|
|
60
64
|
|