chainlesschain 0.165.5 → 0.165.8
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 +2 -1
- package/src/assets/web-panel/assets/{AIOps-CEeYTeDB.js → AIOps-B3gWmXjo.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-BeRUQ3V2.js → ActionButton-G9pdT-0e.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-DrV718XF.js → Analytics-Ca30J7m2.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-Cd2Frt7Q.js → AppLayout-BtVTm8PU.js} +5 -5
- package/src/assets/web-panel/assets/{Artifacts-CUJMlORU.js → Artifacts-BhViNa6u.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-CXJcfT6p.js → Audit-DhKq-FnY.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-DOv6Z7H9.js → BackgroundAgents-BL0SVbnE.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-BwdDyBI0.js → Backup-BWkblnFi.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-BmLn4kYH.js → BaseInput-ClaRNrF8.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-COg8pAW2.js → Chat-sZE2Z77o.js} +4 -4
- package/src/assets/web-panel/assets/ChatBubbleRenderer-BAfKOnRe.js +1 -0
- package/src/assets/web-panel/assets/{Checkbox-BCP4uoU8.js → Checkbox-Cvhay5WM.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-pRt6Y9JI.js → Codegen-BpejL-4N.js} +1 -1
- package/src/assets/web-panel/assets/{Col-DLaprKhS.js → Col-g2Ki6g_1.js} +1 -1
- package/src/assets/web-panel/assets/{Community-DyATsz5f.js → Community-D2xKbNo9.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-BXAaupZK.js → Compact-DbL1Pds3.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-DpcJBELr.js → Compliance-DGtZCV4c.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-DG4EFWyd.js → Cowork-4kQpoNsI.js} +3 -3
- package/src/assets/web-panel/assets/{Cron-F6Oy5n2P.js → Cron-BxTYXXAN.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-BOdgtalX.js → Crosschain-k0J0LT2N.js} +1 -1
- package/src/assets/web-panel/assets/{DID-DT1dSkjH.js → DID-BaqhoPgN.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-NBUIqWzC.js → Dashboard-T4RVPIM1.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-CSpHeMYB.js → Dropdown-DszOToWq.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-CEG0xISr.js → EmailListRenderer-C9KJU7HF.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-BTZ0BZna.js → FamilyGuardDashboard-Cmr0JnsA.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-BuuCTn2V.js → Federation-BI-_2xZX.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-KAVceevk.js → FormItemContext-DsnDEPsl.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-DnHpcQvW.js → GenericCardRenderer-BLncAKKK.js} +1 -1
- package/src/assets/web-panel/assets/{Git-C_B_0HwB.js → Git-BPQhGXDn.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-DPSZuzuu.js → Governance-CyElbUQW.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-BV-06V7q.js → Inference-CcSntBQD.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-wpDuupn6.js → KnowledgeGraph-p6e7HZ2r.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-Bseca1_I.js → Logs-CNEtr-wz.js} +2 -2
- package/src/assets/web-panel/assets/MarkdownRenderer-Bj9xI0YH.js +1 -0
- package/src/assets/web-panel/assets/{Marketplace-d3kM4DDL.js → Marketplace-Dd2notAJ.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-DQ_xZ23i.js → McpTools-BBsN2CSW.js} +4 -4
- package/src/assets/web-panel/assets/{Memory-CRIeYO4L.js → Memory-Dg2uTvwx.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-BsllIa9l.js → MobileBridge-r1YMRiJV.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-BUuQy30X.js → MobileProjects-C-89kSm4.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-BOFDIzrC.js → Mtc-CrPZcVgf.js} +5 -5
- package/src/assets/web-panel/assets/{MtcAudit-BQg-vyvG.js → MtcAudit-sxi7hqJb.js} +5 -5
- package/src/assets/web-panel/assets/{Multisig-Bo1jrPdN.js → Multisig-CAgOMfF1.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-Bmod8alw.js → NLProgramming-DafaPts3.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-CGGFL8w-.js → Notes-CJM-xUW_.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-BsZvCJxC.js → NotificationSettings-L0ZjfAh_.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-C1-Quo_h.js → OrderTableRenderer-BB1CvCK0.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-BjOeDYOi.js → Organization-DeLtgAD8.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-DwVV3l5A.js → Overflow-CKC2b10z.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-Cu3DQT9W.js → P2P-eyf2rzY1.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-Cg0gR7dH.js → PdhVaultBrowser-5upNIOW8.js} +3 -3
- package/src/assets/web-panel/assets/{Permissions-CsAXxRN5.js → Permissions-B8p3W6JK.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-C-nNmnYp.js → PersonalDataHub-BnRMxTq3.js} +5 -5
- package/src/assets/web-panel/assets/{Pipeline-BOnW7nNc.js → Pipeline-DjCNI1li.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-ClLSyGKt.js → Privacy-u6gXLbWT.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-DuLvjjq7.js → ProjectInit-BZAw4tkM.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-DQYYbDD9.js → ProjectSettings-B1Tr72q7.js} +2 -2
- package/src/assets/web-panel/assets/{Projects-Drw8MBGK.js → Projects-DO7HFuBQ.js} +1 -1
- package/src/assets/web-panel/assets/{Providers-ckqHQe6K.js → Providers-BjCztyhj.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-DW5BXk-G.js → QrScannerModal-ZWyCUbR1.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-DDLnhNTp.js → QuickAsk-DhDjeW4G.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-PJQ2C91U.js → Recommend-R_CN2MuU.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-s6wmda34.js → RemoteSession-uxFcujeu.js} +2 -2
- package/src/assets/web-panel/assets/{Reputation-DC2sb-I3.js → Reputation-DX8D7rks.js} +1 -1
- package/src/assets/web-panel/assets/{Row-hPGk1eGj.js → Row-BYahQILD.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-RIvfZql0.js → RssFeed-CUSQ2tm-.js} +3 -3
- package/src/assets/web-panel/assets/{Search-DFg1G1eR.js → Search-CnN_S8Cs.js} +1 -1
- package/src/assets/web-panel/assets/{Security-D6H5p8Dp.js → Security-DcZlh-O4.js} +4 -4
- package/src/assets/web-panel/assets/{Services-nxYiFV_w.js → Services-ZhBOjzIX.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-BmgFNAdB.js → Skeleton-D5Qnq_lI.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-CsaaD2av.js → Skills-BC9cZ3Be.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-yM5y9yvK.js → Sla-DauJ3E8_.js} +1 -1
- package/src/assets/web-panel/assets/SpeechSettings-DbNeZ_fh.js +1 -0
- package/src/assets/web-panel/assets/{SyncSettings-CFVhEyyr.js → SyncSettings-DUvJbSct.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-DdO02Inn.js → Tasks-BXa-5noQ.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-Eo2d8Llu.js → Templates-JxaiUt_3.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-ChESOXgy.js → Tenant-BW4ERQN8.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-BJ7oDxTI.js → Terminal-DltUbdtv.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-hWX0fUvz.js → TimelineRenderer-CMoZz0Tc.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-DVBZQnd-.js → Tokens-D_VEV60K.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-D89cQfVq.js → Trigger-CCXTCs0R.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-BIVSb4-V.js → Trust-lkGRIPcG.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-B58gWopl.js → UkeySign-BfGeQ9HX.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-DhHI7bmG.js → VideoEditing-BEC7pYvc.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-C4FheWS_.js → Wallet-BZdG24o0.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-DnPRq-Bx.js → WebAuthn-yoEddedp.js} +3 -3
- package/src/assets/web-panel/assets/{WorkflowEditor-BerRPjpJ.js → WorkflowEditor-BXZEprOW.js} +1 -1
- package/src/assets/web-panel/assets/{chat-BzZ52iKr.js → chat-gg0bzZal.js} +1 -1
- package/src/assets/web-panel/assets/{colors-BI_E4BAn.js → colors-DjRVRuin.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-d50mr3yk.js → compact-item-su-dieAc.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-C6FsQ1IH.js → createContext-Ci3S8oKF.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-CxbFX9uH.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-DLZEMgLP.js → hasIn-D8F10heH.js} +1 -1
- package/src/assets/web-panel/assets/{index-D-aBumtY.js → index-11wtYQiF.js} +1 -1
- package/src/assets/web-panel/assets/{index-CjuKZBqJ.js → index-1IgxgALJ.js} +1 -1
- package/src/assets/web-panel/assets/{index-9H_M2jju.js → index-1agckWSq.js} +1 -1
- package/src/assets/web-panel/assets/{index-DE0V6cp-.js → index-5JqhujnR.js} +1 -1
- package/src/assets/web-panel/assets/{index-Y2-q7QLm.js → index-6Tniqk1s.js} +1 -1
- package/src/assets/web-panel/assets/{index-DoFtCVZ7.js → index-8Z-8BIlN.js} +1 -1
- package/src/assets/web-panel/assets/{index-KNGgO4Wm.js → index-BCI5v0yB.js} +1 -1
- package/src/assets/web-panel/assets/{index-DY4j5zkF.js → index-BDJntYmJ.js} +1 -1
- package/src/assets/web-panel/assets/{index-BZjjdQ62.js → index-BGzixOGZ.js} +1 -1
- package/src/assets/web-panel/assets/{index-BTpIOTfR.js → index-BMMOVoLD.js} +1 -1
- package/src/assets/web-panel/assets/{index-BB9SqVSt.js → index-BTFpdBtt.js} +1 -1
- package/src/assets/web-panel/assets/{index-AHr7ryWf.js → index-Ba_HyPnE.js} +1 -1
- package/src/assets/web-panel/assets/{index-Pvf30m8Q.js → index-BbF1IHof.js} +1 -1
- package/src/assets/web-panel/assets/{index-CRN5TRTQ.js → index-BgT9su_e.js} +1 -1
- package/src/assets/web-panel/assets/{index-CUaEHfQ0.js → index-BzPB6kGZ.js} +1 -1
- package/src/assets/web-panel/assets/{index-CdKt9fOd.js → index-C6ygzplr.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dec0Kf_Z.js → index-CC3ZDjzY.js} +1 -1
- package/src/assets/web-panel/assets/{index-IPkwSAW_.js → index-CEmXRhAE.js} +1 -1
- package/src/assets/web-panel/assets/index-CJGOlduY.js +1 -0
- package/src/assets/web-panel/assets/{index-C3k8kevG.js → index-CMPqd5Cv.js} +1 -1
- package/src/assets/web-panel/assets/{index-A5CDWP7y.js → index-COWd8uGx.js} +1 -1
- package/src/assets/web-panel/assets/{index-CO8DAF7U.js → index-CPIYSp4H.js} +1 -1
- package/src/assets/web-panel/assets/{index-CVzuawCX.js → index-CR0h6seT.js} +1 -1
- package/src/assets/web-panel/assets/{index-Co7s9LsB.js → index-CUmDhC4j.js} +1 -1
- package/src/assets/web-panel/assets/index-CkBCN5yH.js +1 -0
- package/src/assets/web-panel/assets/{index-CLoTQr0W.js → index-CusgZPtc.js} +1 -1
- package/src/assets/web-panel/assets/{index-B81mo1Et.js → index-DBAETXft.js} +1 -1
- package/src/assets/web-panel/assets/{index-BcSUl9fA.js → index-DIJ2qFOC.js} +1 -1
- package/src/assets/web-panel/assets/{index-Q5Tn735s.js → index-Dbq4NMET.js} +1 -1
- package/src/assets/web-panel/assets/{index-GXE0D8Nx.js → index-DkMEF5VT.js} +1 -1
- package/src/assets/web-panel/assets/{index-TWG-_D1M.js → index-DkfQR3hM.js} +1 -1
- package/src/assets/web-panel/assets/{index-BAE3hwev.js → index-DnLucOn9.js} +1 -1
- package/src/assets/web-panel/assets/{index-CgkoH3_g.js → index-DugB_aKa.js} +1 -1
- package/src/assets/web-panel/assets/{index-BRGx3w6T.js → index-DyLUarhZ.js} +3 -3
- package/src/assets/web-panel/assets/{index-BeM4hfht.js → index-T6_6ygJZ.js} +1 -1
- package/src/assets/web-panel/assets/{index-DsscwxPE.js → index-Tu8eWqh-.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bmr_vxXD.js → index-W7XUgdVL.js} +1 -1
- package/src/assets/web-panel/assets/{index-BoNZlzhQ.js → index-dHZinfko.js} +1 -1
- package/src/assets/web-panel/assets/{index-DqkbHHg8.js → index-jyNgp5FI.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-D-4Hzgej.js → initDefaultProps-Cv4p8e4w.js} +1 -1
- package/src/assets/web-panel/assets/{motion-D4Wl4xU7.js → motion-B8EwmMOw.js} +1 -1
- package/src/assets/web-panel/assets/{move-Cd5eQvL0.js → move-B8R9F8TK.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-B4ilslCW.js → mtc-parser-DoFYz5Bp.js} +1 -1
- package/src/assets/web-panel/assets/{omit-Ckarx-tf.js → omit-CHWYLBjI.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-QJ0bpCmc.js → pickAttrs-CXuPDO77.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-CKHm-tq8.js → placementArrow-BaAtUUon.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-CV6a_w_Y.js → responsiveObserve-DCc4zcDD.js} +1 -1
- package/src/assets/web-panel/assets/{slide-mHpeQFgk.js → slide-B_lx89oZ.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-DIaQNQWA.js → statusUtils-DE8DPmR0.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-PJpzavV8.js → styleChecker-1ktXOsL_.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-BH7zTw8S.js → useFlexGapSupport-xH89-wjh.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-BDNG3JDd.js → useFs-DscvfW5p.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-CY2VWdUg.js → usePersonalDataHub-RdMKQta7.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-D0kw9jhz.js → vnode-Z3wTni2T.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-B5g7TN7r.js → zoom-B0iChCsm.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/command-help-index.json +3 -3
- package/src/commands/agent.js +105 -62
- package/src/commands/background-session.js +100 -10
- package/src/commands/git.js +39 -0
- package/src/commands/mcp.js +5 -0
- package/src/commands/plugin.js +160 -29
- package/src/commands/remote-control.js +295 -110
- package/src/commands/session-group.js +131 -0
- package/src/commands/session-location.js +48 -0
- package/src/commands/session-message.js +171 -0
- package/src/commands/session.js +22 -0
- package/src/commands/sync-providers.js +4 -1
- package/src/commands/update.js +1 -0
- package/src/data/changelog.json +41 -2
- package/src/gateways/ws/ws-agent-handler.js +20 -12
- package/src/harness/jsonl-session-store.js +279 -37
- package/src/harness/mcp-client.js +832 -91
- package/src/harness/session-index.js +15 -4
- package/src/lib/agent-worktree.js +8 -1
- package/src/lib/auto-mode-safety-classifier.js +3 -17
- package/src/lib/background-agent-keeper-protocol.js +26 -6
- package/src/lib/background-agent-supervisor.js +17 -1
- package/src/lib/background-session-transport.js +153 -27
- package/src/lib/browser-evidence.js +1405 -0
- package/src/lib/chrome-connector.js +465 -79
- package/src/lib/claude-project-auto-memory.js +481 -0
- package/src/lib/claude-project-storage-layout.js +104 -0
- package/src/lib/cloud/cloud-client.js +40 -0
- package/src/lib/execution-location-local-supervisor.mjs +226 -0
- package/src/lib/execution-location-runner-lifecycle.js +991 -0
- package/src/lib/execution-location-target-preflight.js +333 -0
- package/src/lib/execution-location-target.js +783 -13
- package/src/lib/file-lines.js +6 -1
- package/src/lib/gateway-client-resilience.js +571 -0
- package/src/lib/gitlab-mr-worktree.js +176 -0
- package/src/lib/hooks-v2-producers.js +119 -15
- package/src/lib/hooks-v2-workspace-context.js +44 -1
- package/src/lib/host-resource-budget.js +295 -0
- package/src/lib/ide-context.js +15 -2
- package/src/lib/llm-config-defaults.js +43 -0
- package/src/lib/mcp-client.js +2 -0
- package/src/lib/mcp-headers-helper.js +47 -5
- package/src/lib/mcp-lifecycle-authority.js +887 -0
- package/src/lib/mcp-oauth.js +156 -26
- package/src/lib/mcp-tls.js +330 -0
- package/src/lib/output-styles.js +12 -0
- package/src/lib/paths.js +260 -32
- package/src/lib/permission-request.js +48 -0
- package/src/lib/plugin-runtime/bin.js +3 -1
- package/src/lib/plugin-runtime/hooks.js +3 -1
- package/src/lib/plugin-runtime/install.js +730 -71
- package/src/lib/plugin-runtime/lsp.js +4 -4
- package/src/lib/plugin-runtime/marketplace-archive-source.js +366 -0
- package/src/lib/plugin-runtime/marketplace-catalog.js +63 -6
- package/src/lib/plugin-runtime/marketplace-command-descriptor.js +343 -0
- package/src/lib/plugin-runtime/marketplace-command-source.js +580 -0
- package/src/lib/plugin-runtime/marketplace-remote-artifacts.js +1 -0
- package/src/lib/plugin-runtime/marketplace-source-adapter.js +188 -0
- package/src/lib/plugin-runtime/marketplace-source-cache.js +11 -1
- package/src/lib/plugin-runtime/mcp.js +3 -1
- package/src/lib/plugin-runtime/monitors.js +3 -1
- package/src/lib/plugin-runtime/remote-source.js +583 -58
- package/src/lib/plugin-runtime/settings.js +3 -1
- package/src/lib/plugin-runtime/trust.js +274 -13
- package/src/lib/plugin-security.js +393 -41
- package/src/lib/plugin-source-identity.js +850 -0
- package/src/lib/process-execution-broker/credential-agent.js +210 -41
- package/src/lib/process-execution-broker/index.js +15 -1
- package/src/lib/process-execution-broker/linux-cgroup-v2.js +391 -0
- package/src/lib/process-execution-broker/platform-sandbox.js +50 -28
- package/src/lib/project-mcp-trust.js +165 -10
- package/src/lib/remote-approval-bridge.js +136 -46
- package/src/lib/remote-control.js +217 -31
- package/src/lib/resume-session.js +9 -6
- package/src/lib/session-core-singletons.js +26 -11
- package/src/lib/session-host-lease.js +52 -16
- package/src/lib/session-lifecycle-scope.js +153 -0
- package/src/lib/session-message-fabric.js +1252 -0
- package/src/lib/session-projection.js +286 -3
- package/src/lib/session-runtime-retention.js +155 -0
- package/src/lib/session-store-guard.js +3 -1
- package/src/lib/session-workbench-store.js +466 -0
- package/src/lib/settings-loader.cjs +29 -0
- package/src/lib/spellcheck.js +211 -0
- package/src/lib/sub-agent-context.js +9 -9
- package/src/lib/sync-cli-db.js +485 -129
- package/src/lib/sync-credentials.js +31 -20
- package/src/lib/web-fetch.js +112 -40
- package/src/lib/web-search.js +127 -61
- package/src/lib/workspace-trust.js +793 -0
- package/src/repl/agent-repl.js +175 -19
- package/src/repl/permission-prompt.js +29 -8
- package/src/repl/repl-keybindings.js +55 -0
- package/src/runtime/agent-core.js +362 -20
- package/src/runtime/bootstrap.js +37 -13
- package/src/runtime/coding-agent-contract-shared.cjs +11 -4
- package/src/runtime/headless-runner.js +281 -102
- package/src/runtime/headless-stream.js +199 -30
- package/src/runtime/mcp-config.js +298 -6
- package/src/runtime/policies/agent-policy.js +10 -0
- package/src/runtime/status-diagnostics-lite.js +9 -1
- package/src/assets/web-panel/assets/ChatBubbleRenderer-4gGkdGgA.js +0 -1
- package/src/assets/web-panel/assets/MarkdownRenderer-B4RLp2zs.js +0 -1
- package/src/assets/web-panel/assets/SpeechSettings-DPy1XMYh.js +0 -1
- package/src/assets/web-panel/assets/devWarning-CagEcIex.js +0 -1
- package/src/assets/web-panel/assets/index-pzT9PpOU.js +0 -1
- package/src/assets/web-panel/assets/index-z1kFadaX.js +0 -1
package/src/repl/agent-repl.js
CHANGED
|
@@ -119,6 +119,7 @@ import {
|
|
|
119
119
|
} from "../lib/task-model-selector.js";
|
|
120
120
|
import { runnableTaskModel, hasUsableKey } from "../lib/runnable-provider.js";
|
|
121
121
|
import { CLIPermanentMemory } from "../lib/permanent-memory.js";
|
|
122
|
+
import { resolveClaudeProjectAutoMemory } from "../lib/claude-project-auto-memory.js";
|
|
122
123
|
import { CLIAutonomousAgent, GoalStatus } from "../lib/autonomous-agent.js";
|
|
123
124
|
import {
|
|
124
125
|
estimateMessagesTokens,
|
|
@@ -185,6 +186,11 @@ import {
|
|
|
185
186
|
createPromptInteractionSurface,
|
|
186
187
|
mergeClipboardImageChips,
|
|
187
188
|
} from "./prompt-interactions.js";
|
|
189
|
+
import {
|
|
190
|
+
isReadlineWordRuboutKey,
|
|
191
|
+
readlineWordRubout,
|
|
192
|
+
resolveReplKeybindingFlavor,
|
|
193
|
+
} from "./repl-keybindings.js";
|
|
188
194
|
import { createSystemClipboardImageBinding } from "./clipboard-image.js";
|
|
189
195
|
import {
|
|
190
196
|
buildPermissionPrompt,
|
|
@@ -2760,15 +2766,11 @@ export async function runReplStartupBoundary(options, startupDependencies) {
|
|
|
2760
2766
|
"CC_REPL_STARTUP_DEPENDENCY_INVALID",
|
|
2761
2767
|
);
|
|
2762
2768
|
}
|
|
2763
|
-
const root = openBudgetRoot(
|
|
2764
|
-
|
|
2765
|
-
options.
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
signal: combineReplSignals(options.signal, leaseScope.lease.signal),
|
|
2769
|
-
table: options.sessionBudgetRoot.table,
|
|
2770
|
-
},
|
|
2771
|
-
);
|
|
2769
|
+
const root = openBudgetRoot(leaseSessionId, options.sessionBudgetRoot, {
|
|
2770
|
+
persist: true,
|
|
2771
|
+
signal: combineReplSignals(options.signal, leaseScope.lease.signal),
|
|
2772
|
+
table: options.sessionBudgetRoot.table,
|
|
2773
|
+
});
|
|
2772
2774
|
if (
|
|
2773
2775
|
root?.enabled !== true ||
|
|
2774
2776
|
!root.budget ||
|
|
@@ -2824,10 +2826,7 @@ export function startReplJsonlSession(
|
|
|
2824
2826
|
...meta,
|
|
2825
2827
|
...(observabilityScope != null ? { observabilityScope } : {}),
|
|
2826
2828
|
});
|
|
2827
|
-
if (
|
|
2828
|
-
requireDurable &&
|
|
2829
|
-
(typeof sessionId !== "string" || !sessionId)
|
|
2830
|
-
) {
|
|
2829
|
+
if (requireDurable && (typeof sessionId !== "string" || !sessionId)) {
|
|
2831
2830
|
throw new Error("JSONL session creation returned no durable session id");
|
|
2832
2831
|
}
|
|
2833
2832
|
return sessionId || null;
|
|
@@ -2847,6 +2846,46 @@ export function startReplJsonlSession(
|
|
|
2847
2846
|
}
|
|
2848
2847
|
}
|
|
2849
2848
|
|
|
2849
|
+
/**
|
|
2850
|
+
* Resolve the automatic-memory location at the same lifecycle boundary that
|
|
2851
|
+
* opens the durable REPL session. This is intentionally exported as a narrow
|
|
2852
|
+
* test seam; startAgentReplInWorkspaceOwned consumes it below rather than
|
|
2853
|
+
* letting a standalone helper drift away from real session behavior.
|
|
2854
|
+
*/
|
|
2855
|
+
export function resolveReplAutomaticMemoryPlan(
|
|
2856
|
+
options = {},
|
|
2857
|
+
cwd = process.cwd(),
|
|
2858
|
+
) {
|
|
2859
|
+
return resolveClaudeProjectAutoMemory({
|
|
2860
|
+
cwd,
|
|
2861
|
+
launchEnv: options.claudeStorageLaunchEnv || undefined,
|
|
2862
|
+
});
|
|
2863
|
+
}
|
|
2864
|
+
|
|
2865
|
+
/**
|
|
2866
|
+
* Resolve the concrete permanent-memory backend used by the live REPL. Claude
|
|
2867
|
+
* project mode is deliberately file-only: the legacy bootstrap SQLite DB is
|
|
2868
|
+
* process/global state and would otherwise let project A retrieve facts that
|
|
2869
|
+
* project B summarized. Legacy mode retains its historical DB behavior.
|
|
2870
|
+
*/
|
|
2871
|
+
export function resolveReplPermanentMemoryStorage(
|
|
2872
|
+
options = {},
|
|
2873
|
+
db = null,
|
|
2874
|
+
cwd = process.cwd(),
|
|
2875
|
+
) {
|
|
2876
|
+
const automaticMemory = resolveReplAutomaticMemoryPlan(options, cwd);
|
|
2877
|
+
if (!automaticMemory.enabled) return null;
|
|
2878
|
+
const dataDir = process.env.CHAINLESSCHAIN_DATA_DIR || cwd;
|
|
2879
|
+
return Object.freeze({
|
|
2880
|
+
db: automaticMemory.mode === "project" ? null : db,
|
|
2881
|
+
memoryDir:
|
|
2882
|
+
automaticMemory.mode === "project"
|
|
2883
|
+
? automaticMemory.memoryDir
|
|
2884
|
+
: path.join(dataDir, "memory"),
|
|
2885
|
+
automaticMemory,
|
|
2886
|
+
});
|
|
2887
|
+
}
|
|
2888
|
+
|
|
2850
2889
|
/** Start the agentic REPL with non-overridable production bindings. */
|
|
2851
2890
|
export async function startAgentRepl(options = {}) {
|
|
2852
2891
|
const sessionHostLeaseScope = { lease: null };
|
|
@@ -3187,6 +3226,7 @@ async function startAgentReplInWorkspaceOwned(
|
|
|
3187
3226
|
let _visionModel;
|
|
3188
3227
|
let _promptInteractionConfig = {};
|
|
3189
3228
|
let _persistPromptSuggestionsEnabled = null;
|
|
3229
|
+
let _keybindingFlavor = "classic";
|
|
3190
3230
|
// Hard stream inactivity timeout override (config.llm.streamStallTimeoutMs,
|
|
3191
3231
|
// ms): a stream silent that long is aborted + retried instead of hanging.
|
|
3192
3232
|
// Unset → agent-core's 180s default (matches cc chat/ask). Set to 0 to
|
|
@@ -3218,6 +3258,23 @@ async function startAgentReplInWorkspaceOwned(
|
|
|
3218
3258
|
} catch {
|
|
3219
3259
|
/* optional — resolveVisionLlm falls back to DEFAULT_VISION_MODEL */
|
|
3220
3260
|
}
|
|
3261
|
+
try {
|
|
3262
|
+
const { readStringSetting } = await import("../lib/settings-loader.cjs");
|
|
3263
|
+
const configuredFlavor = readStringSetting("keybindingFlavor", {
|
|
3264
|
+
cwd: process.cwd(),
|
|
3265
|
+
settingsFile: options.settingsFile || null,
|
|
3266
|
+
managedSettingsFile: options.managedSettingsFile || null,
|
|
3267
|
+
onWarn: (message) => logger.warn(message),
|
|
3268
|
+
});
|
|
3269
|
+
const resolvedFlavor = resolveReplKeybindingFlavor(configuredFlavor);
|
|
3270
|
+
_keybindingFlavor = resolvedFlavor.flavor;
|
|
3271
|
+
if (resolvedFlavor.error) {
|
|
3272
|
+
logger.warn(`settings: ${resolvedFlavor.error}; using classic`);
|
|
3273
|
+
}
|
|
3274
|
+
} catch (error) {
|
|
3275
|
+
if (error?.code === "CC_MANAGED_SETTINGS_INVALID") throw error;
|
|
3276
|
+
logger.warn(`settings: could not load keybindingFlavor (${error.message})`);
|
|
3277
|
+
}
|
|
3221
3278
|
// Extra workspace roots (--add-dir): advertised in the system prompt and
|
|
3222
3279
|
// spanned by search_files.
|
|
3223
3280
|
const additionalDirectories = Array.isArray(options.additionalDirectories)
|
|
@@ -3328,10 +3385,15 @@ async function startAgentReplInWorkspaceOwned(
|
|
|
3328
3385
|
// Initialize permanent memory
|
|
3329
3386
|
let permanentMemory = null;
|
|
3330
3387
|
try {
|
|
3331
|
-
const
|
|
3332
|
-
|
|
3333
|
-
|
|
3334
|
-
|
|
3388
|
+
const permanentMemoryStorage = resolveReplPermanentMemoryStorage(
|
|
3389
|
+
options,
|
|
3390
|
+
db,
|
|
3391
|
+
process.cwd(),
|
|
3392
|
+
);
|
|
3393
|
+
if (permanentMemoryStorage) {
|
|
3394
|
+
permanentMemory = new CLIPermanentMemory(permanentMemoryStorage);
|
|
3395
|
+
permanentMemory.initialize();
|
|
3396
|
+
}
|
|
3335
3397
|
} catch (_err) {
|
|
3336
3398
|
// Non-critical
|
|
3337
3399
|
}
|
|
@@ -3767,7 +3829,13 @@ async function startAgentReplInWorkspaceOwned(
|
|
|
3767
3829
|
try {
|
|
3768
3830
|
const { getSessionManager } =
|
|
3769
3831
|
await import("../lib/session-core-singletons.js");
|
|
3770
|
-
|
|
3832
|
+
// Keep durable lifecycle sidecars on the same launch-authoritative Claude
|
|
3833
|
+
// project bucket as the JSONL transcript. Project settings may not switch
|
|
3834
|
+
// this map/path after the REPL has admitted its launch environment.
|
|
3835
|
+
_sessionMgr = getSessionManager({
|
|
3836
|
+
launchEnv: options.claudeStorageLaunchEnv || undefined,
|
|
3837
|
+
cwd: process.cwd(),
|
|
3838
|
+
});
|
|
3771
3839
|
if (sessionId) {
|
|
3772
3840
|
if (options.sessionId && !_sessionMgr.has(sessionId)) {
|
|
3773
3841
|
// Try unparking; no-op if nothing parked with that id
|
|
@@ -3815,6 +3883,8 @@ async function startAgentReplInWorkspaceOwned(
|
|
|
3815
3883
|
agentSessionId:
|
|
3816
3884
|
sessionId || `repl-${process.pid}-${Date.now().toString(36)}`,
|
|
3817
3885
|
isText: false, // the REPL prints its own pairing lines below
|
|
3886
|
+
allowLan: options.remoteControlAllowLan === true,
|
|
3887
|
+
env: process.env,
|
|
3818
3888
|
});
|
|
3819
3889
|
_remoteApproval = {
|
|
3820
3890
|
bridge: started.bridge,
|
|
@@ -5201,6 +5271,25 @@ async function startAgentReplInWorkspaceOwned(
|
|
|
5201
5271
|
return;
|
|
5202
5272
|
}
|
|
5203
5273
|
|
|
5274
|
+
// 1.875) The opt-in readline flavor makes Ctrl+W a whitespace-delimited
|
|
5275
|
+
// unix-word-rubout. Classic stays byte-identical and lets Node readline
|
|
5276
|
+
// own the chord; Vim NORMAL mode continues to use the existing engine.
|
|
5277
|
+
if (
|
|
5278
|
+
!_turnAbort &&
|
|
5279
|
+
(!_vimEnabled || !_vim) &&
|
|
5280
|
+
_keybindingFlavor === "readline" &&
|
|
5281
|
+
isReadlineWordRuboutKey(_str, k)
|
|
5282
|
+
) {
|
|
5283
|
+
_consumeReadlineKeypress(key);
|
|
5284
|
+
const next = readlineWordRubout(rl.line, rl.cursor);
|
|
5285
|
+
if (next.changed) {
|
|
5286
|
+
rl.line = next.line;
|
|
5287
|
+
rl.cursor = next.cursor;
|
|
5288
|
+
rl._refreshLine?.();
|
|
5289
|
+
}
|
|
5290
|
+
return;
|
|
5291
|
+
}
|
|
5292
|
+
|
|
5204
5293
|
// 2) Vim mode: modal editing on the current input line.
|
|
5205
5294
|
if (_vimEnabled && !_turnAbort) {
|
|
5206
5295
|
if (!_vim) {
|
|
@@ -5625,7 +5714,7 @@ async function startAgentReplInWorkspaceOwned(
|
|
|
5625
5714
|
` ${chalk.cyan("/voice")} Speech dictation — local-first STT (/voice hold|tap|off)`,
|
|
5626
5715
|
);
|
|
5627
5716
|
logger.log(
|
|
5628
|
-
` ${chalk.cyan("/output-style")} Response persona (/output-style <name|list>; explanatory/learning built-in)`,
|
|
5717
|
+
` ${chalk.cyan("/output-style")} Response persona (/output-style <name|list>; concise/explanatory/learning built-in)`,
|
|
5629
5718
|
);
|
|
5630
5719
|
logger.log(
|
|
5631
5720
|
` ${chalk.cyan("/editor")} Edit a draft prompt in an external editor`,
|
|
@@ -6275,6 +6364,73 @@ async function startAgentReplInWorkspaceOwned(
|
|
|
6275
6364
|
return;
|
|
6276
6365
|
}
|
|
6277
6366
|
|
|
6367
|
+
// `/spellcheck` uses an installed local dictionary only. It never sends
|
|
6368
|
+
// prompt text to a provider, and fenced code is suppressed by the adapter
|
|
6369
|
+
// before the executable receives it.
|
|
6370
|
+
{
|
|
6371
|
+
const {
|
|
6372
|
+
parseSpellcheckCommand,
|
|
6373
|
+
resolveSpellcheckEnabled,
|
|
6374
|
+
spellcheckText,
|
|
6375
|
+
} = await import("../lib/spellcheck.js");
|
|
6376
|
+
const spellcheck = parseSpellcheckCommand(trimmed);
|
|
6377
|
+
if (spellcheck) {
|
|
6378
|
+
let configured;
|
|
6379
|
+
try {
|
|
6380
|
+
const { getConfigValue } = await import("../lib/config-manager.js");
|
|
6381
|
+
configured = getConfigValue("cli.spellcheck");
|
|
6382
|
+
} catch {
|
|
6383
|
+
configured = undefined;
|
|
6384
|
+
}
|
|
6385
|
+
if (spellcheck.action === "on" || spellcheck.action === "off") {
|
|
6386
|
+
try {
|
|
6387
|
+
const { setConfigValue } = await import("../lib/config-manager.js");
|
|
6388
|
+
setConfigValue("cli.spellcheck", spellcheck.action === "on");
|
|
6389
|
+
} catch {
|
|
6390
|
+
// The current-session diagnostic remains useful if config storage
|
|
6391
|
+
// is unavailable; don't turn a local optional feature into a
|
|
6392
|
+
// REPL failure.
|
|
6393
|
+
}
|
|
6394
|
+
logger.info(
|
|
6395
|
+
`Spellcheck: ${spellcheck.action === "on" ? "enabled" : "disabled"}.`,
|
|
6396
|
+
);
|
|
6397
|
+
} else if (spellcheck.action === "status") {
|
|
6398
|
+
logger.info(
|
|
6399
|
+
`Spellcheck: ${
|
|
6400
|
+
resolveSpellcheckEnabled({
|
|
6401
|
+
config: { cli: { spellcheck: configured } },
|
|
6402
|
+
})
|
|
6403
|
+
? "enabled"
|
|
6404
|
+
: "disabled"
|
|
6405
|
+
}. ` + "Use /spellcheck <text> to check local prose.",
|
|
6406
|
+
);
|
|
6407
|
+
} else {
|
|
6408
|
+
try {
|
|
6409
|
+
const result = spellcheckText(spellcheck.text, {
|
|
6410
|
+
config: { cli: { spellcheck: configured } },
|
|
6411
|
+
});
|
|
6412
|
+
if (!result.enabled) {
|
|
6413
|
+
logger.info("Spellcheck is disabled.");
|
|
6414
|
+
} else if (!result.available) {
|
|
6415
|
+
logger.info(
|
|
6416
|
+
"No local spellchecker found (aspell, hunspell, or ispell).",
|
|
6417
|
+
);
|
|
6418
|
+
} else if (result.words.length === 0) {
|
|
6419
|
+
logger.info("Spellcheck: no unknown prose words.");
|
|
6420
|
+
} else {
|
|
6421
|
+
logger.info(
|
|
6422
|
+
`Spellcheck (${result.adapter}): ${result.words.join(", ")}`,
|
|
6423
|
+
);
|
|
6424
|
+
}
|
|
6425
|
+
} catch (error) {
|
|
6426
|
+
logger.error(`/spellcheck failed: ${error.message}`);
|
|
6427
|
+
}
|
|
6428
|
+
}
|
|
6429
|
+
prompt();
|
|
6430
|
+
return;
|
|
6431
|
+
}
|
|
6432
|
+
}
|
|
6433
|
+
|
|
6278
6434
|
// `/tui` — fullscreen no-flicker view (Claude-Code parity). `fullscreen`
|
|
6279
6435
|
// enters the alternate screen buffer; `default` returns to streaming
|
|
6280
6436
|
// scrollback. No arg prints the current mode. Persisted to `cli.tuiMode`.
|
|
@@ -14,15 +14,36 @@
|
|
|
14
14
|
*
|
|
15
15
|
* @returns {string} the prompt header (no styling, no trailing "Proceed?")
|
|
16
16
|
*/
|
|
17
|
+
import { normalizePermissionRequest } from "../lib/permission-request.js";
|
|
18
|
+
import { redactSecrets } from "../lib/secret-scan.js";
|
|
19
|
+
|
|
20
|
+
export function visualizePermissionText(value) {
|
|
21
|
+
return String(value ?? "").replace(
|
|
22
|
+
/[\u0000-\u001f\u007f-\u009f\u00ad\u034f\u061c\u115f\u1160\u17b4\u17b5\u180b-\u180f\u200b-\u200f\u202a-\u202e\u2060-\u206f\u3164\ufeff\uffa0]/gu,
|
|
23
|
+
(character) => {
|
|
24
|
+
if (character === "\t") return "<TAB>";
|
|
25
|
+
if (character === "\n") return "<LF>";
|
|
26
|
+
if (character === "\r") return "<CR>";
|
|
27
|
+
return `<U+${character.codePointAt(0).toString(16).toUpperCase().padStart(4, "0")}>`;
|
|
28
|
+
},
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
17
32
|
export function buildPermissionPrompt({ tool, args, rule, reason } = {}) {
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
if (rule)
|
|
24
|
-
|
|
25
|
-
|
|
33
|
+
const request = normalizePermissionRequest({ tool, args });
|
|
34
|
+
const visibleTool = visualizePermissionText(request.tool);
|
|
35
|
+
const rawDetail = request.detail;
|
|
36
|
+
const visibleDetail = visualizePermissionText(redactSecrets(rawDetail));
|
|
37
|
+
const detail = rawDetail ? ` ${visibleDetail}` : "";
|
|
38
|
+
if (rule) {
|
|
39
|
+
return `[Permission] rule "${visualizePermissionText(redactSecrets(String(rule)))}" asks before ${visibleTool}:${detail}`;
|
|
40
|
+
}
|
|
41
|
+
if (reason) {
|
|
42
|
+
const reasonText = String(reason);
|
|
43
|
+
const missingTarget = rawDetail && !reasonText.includes(String(rawDetail));
|
|
44
|
+
return `[Permission] ${visualizePermissionText(redactSecrets(reasonText))}${missingTarget ? `:${detail}` : ""}`;
|
|
45
|
+
}
|
|
46
|
+
return `[Permission] confirm ${visibleTool}:${detail}`;
|
|
26
47
|
}
|
|
27
48
|
|
|
28
49
|
/**
|
|
@@ -1,5 +1,60 @@
|
|
|
1
1
|
/** Declarative, validated keybindings for interactive prompt actions. */
|
|
2
2
|
|
|
3
|
+
export const DEFAULT_REPL_KEYBINDING_FLAVOR = "classic";
|
|
4
|
+
export const REPL_KEYBINDING_FLAVORS = Object.freeze([
|
|
5
|
+
DEFAULT_REPL_KEYBINDING_FLAVOR,
|
|
6
|
+
"readline",
|
|
7
|
+
]);
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Resolve the prompt editing flavor. Unknown values stay on the compatible
|
|
11
|
+
* classic behavior and include a diagnostic for the caller to surface.
|
|
12
|
+
*/
|
|
13
|
+
export function resolveReplKeybindingFlavor(value) {
|
|
14
|
+
if (value == null || String(value).trim() === "") {
|
|
15
|
+
return { flavor: DEFAULT_REPL_KEYBINDING_FLAVOR, error: null };
|
|
16
|
+
}
|
|
17
|
+
const flavor = String(value).trim().toLowerCase();
|
|
18
|
+
if (REPL_KEYBINDING_FLAVORS.includes(flavor)) {
|
|
19
|
+
return { flavor, error: null };
|
|
20
|
+
}
|
|
21
|
+
return {
|
|
22
|
+
flavor: DEFAULT_REPL_KEYBINDING_FLAVOR,
|
|
23
|
+
error: `keybindingFlavor must be classic or readline (got "${String(value)}")`,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** True for the terminal Ctrl+W chord used by readline's unix-word-rubout. */
|
|
28
|
+
export function isReadlineWordRuboutKey(input, key = {}) {
|
|
29
|
+
if (key.meta || key.alt || key.shift) return false;
|
|
30
|
+
const name = String(key.name || "").toLowerCase();
|
|
31
|
+
return (
|
|
32
|
+
key.ctrl === true &&
|
|
33
|
+
(name === "w" || input === "\u0017" || key.sequence === "\u0017")
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Delete the token immediately left of the cursor using whitespace-only word
|
|
39
|
+
* boundaries. This intentionally treats a full path as one token, unlike the
|
|
40
|
+
* classic path-segment behavior. Text right of the cursor is preserved.
|
|
41
|
+
*/
|
|
42
|
+
export function readlineWordRubout(line, cursor) {
|
|
43
|
+
const text = String(line || "");
|
|
44
|
+
const point = Math.max(
|
|
45
|
+
0,
|
|
46
|
+
Math.min(Number.isInteger(cursor) ? cursor : text.length, text.length),
|
|
47
|
+
);
|
|
48
|
+
let start = point;
|
|
49
|
+
while (start > 0 && /\s/u.test(text[start - 1])) start -= 1;
|
|
50
|
+
while (start > 0 && !/\s/u.test(text[start - 1])) start -= 1;
|
|
51
|
+
return {
|
|
52
|
+
line: text.slice(0, start) + text.slice(point),
|
|
53
|
+
cursor: start,
|
|
54
|
+
changed: start !== point,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
3
58
|
export const REPL_KEYBINDING_ACTIONS = Object.freeze([
|
|
4
59
|
"prompt.edit",
|
|
5
60
|
"prompt.stash",
|