chainlesschain 0.166.81 → 0.166.83
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/linux-subreaper/linux-arm64/manifest.json +1 -1
- package/src/assets/linux-subreaper/linux-x64/manifest.json +1 -1
- package/src/assets/web-panel/assets/{AIOps-BZPs-bVl.js → AIOps-D5a4i3N7.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-BP1iLSwn.js → ActionButton-DxsRk5n1.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-WxwtPqcd.js → Analytics-D-7EvH4S.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-DvcgeNkP.js → AppLayout-DDEWVr-d.js} +5 -5
- package/src/assets/web-panel/assets/{Artifacts-CtYGOtnT.js → Artifacts-CAlJAjRB.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-BBzFYWMk.js → Audit-tuaWDOAM.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-Dl9wCt0Y.js → BackgroundAgents-CIwRFJ50.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-CAL13Eak.js → Backup-CpBepJlH.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-f_lncpU_.js → BaseInput-eKguCzQc.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-DLEbkiqI.js → Chat-CFrUfXDV.js} +4 -4
- package/src/assets/web-panel/assets/{ChatBubbleRenderer-1tCmIvv1.js → ChatBubbleRenderer-DaRtY87l.js} +1 -1
- package/src/assets/web-panel/assets/{Checkbox-SNvgd2UG.js → Checkbox-o5FEn6a1.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-BG0D-INl.js → Codegen-Dih8HogC.js} +1 -1
- package/src/assets/web-panel/assets/{Col-DUBBZz0e.js → Col-D5r-6WZe.js} +1 -1
- package/src/assets/web-panel/assets/{Community-8LfuBDPs.js → Community-jeQ6bGey.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-GWs7Ka6o.js → Compact-CEP7QsWp.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-CXRhB-KC.js → Compliance-B8NydOHd.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-OpW8dLKg.js → Cowork-Bmi1zuPy.js} +2 -2
- package/src/assets/web-panel/assets/{Cron-6e47Syc_.js → Cron-BAL9bwSp.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-D-vGH4nC.js → Crosschain-D8WUi2dS.js} +1 -1
- package/src/assets/web-panel/assets/{DID-zEIa2jNx.js → DID-CQCkYcLY.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-BfUbZxsl.js → Dashboard-DogXg6kh.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-BeCD8gf0.js → Dropdown-BJBJyQJn.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-apOBM2r2.js → EmailListRenderer-CxPWquJx.js} +1 -1
- package/src/assets/web-panel/assets/{EvolutionSettings-C2dK36po.js → EvolutionSettings-BijH1x8G.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-DPMLWPTR.js → FamilyGuardDashboard-DStwf-00.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-CivI2pzm.js → Federation-LU9_sNxC.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-B3zrwBDw.js → FormItemContext-DNfWhj7P.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-jGgdcPck.js → GenericCardRenderer-DWYsPjzk.js} +1 -1
- package/src/assets/web-panel/assets/{Git-Cpfkrasd.js → Git-CA0O9ulB.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-DG8_7YS3.js → Governance-ECShL7BJ.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-TQgUqPXx.js → Inference-9GA2542e.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-By657fHZ.js → KnowledgeGraph-nC5mTbEH.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-CFJ_70-E.js → Logs-B7IqgnDy.js} +2 -2
- package/src/assets/web-panel/assets/MarkdownRenderer-BqlqrJkY.js +1 -0
- package/src/assets/web-panel/assets/{Marketplace-BWC3xler.js → Marketplace-CEr0suM8.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-B0NkUzeJ.js → McpTools-DhTPQKOE.js} +3 -3
- package/src/assets/web-panel/assets/{Memory-DBnXwjWD.js → Memory-mHRZP1w4.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-BNWDrEJ4.js → MobileBridge-BNfX6Rdo.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-BaE94B_N.js → MobileProjects-8zG8f5li.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-Bp3OAEYb.js → Mtc-CzkV3wsn.js} +5 -5
- package/src/assets/web-panel/assets/{MtcAudit-_1T24zO3.js → MtcAudit-DWYljJik.js} +4 -4
- package/src/assets/web-panel/assets/{Multisig-C2i6Yi0B.js → Multisig-kHgtvZY_.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-CBq3ujGE.js → NLProgramming-56uQiPiY.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-BxRmci_W.js → Notes-DxGOZqRf.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-CqBLgOdv.js → NotificationSettings-DabaCpmo.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-cODPlpK7.js → OrderTableRenderer-SzeQNlGT.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-DlTnQBlp.js → Organization-D9gnPpK2.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-Df2jS2Ep.js → Overflow-Cqp_bXm4.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-BwieT0aA.js → P2P-DTeK-r0G.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-Btw816xR.js → PdhVaultBrowser-CsjG85GT.js} +3 -3
- package/src/assets/web-panel/assets/{Permissions-BhVYLcqT.js → Permissions-B4ofZq3j.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-CPbNKPCA.js → PersonalDataHub-Cvas5cYd.js} +4 -4
- package/src/assets/web-panel/assets/{Pipeline-B1BuhoY3.js → Pipeline-Er3wmAew.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-BWeikVbU.js → Privacy-DMFYeZox.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-BVXcimHE.js → ProjectInit-DaVaYPy0.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-CX87x3rT.js → ProjectSettings-dPx5q2Ac.js} +2 -2
- package/src/assets/web-panel/assets/Projects-N0JNeCui.js +1 -0
- package/src/assets/web-panel/assets/{Providers-CGm0Wuer.js → Providers-BPQo_gQa.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-DJxv7J64.js → QrScannerModal-CD-sEsWh.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-DOXBReRk.js → QuickAsk-LOKRJiN_.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend--MiLwmLM.js → Recommend-BwPXqcWy.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-D6LoXH94.js → RemoteSession-BjotndxF.js} +3 -3
- package/src/assets/web-panel/assets/{Reputation-G2jToiqh.js → Reputation-BlZ2gcbU.js} +1 -1
- package/src/assets/web-panel/assets/{Row-B79ZV_lZ.js → Row-D399HM-d.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-CHQN68w_.js → RssFeed-C7H-ibnY.js} +2 -2
- package/src/assets/web-panel/assets/{Search-j7v5S2Fa.js → Search-CNHCVmM1.js} +1 -1
- package/src/assets/web-panel/assets/{Security-B2ykFZJH.js → Security-C70eDGYY.js} +3 -3
- package/src/assets/web-panel/assets/{Services-H8BK8uX0.js → Services-6xfq9G-H.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-XEUUrOTK.js → Skeleton-BbY2zeZV.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-B387UJPk.js → Skills-DYtyZ7_6.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-CWWBujpe.js → Sla-CkWcl5OH.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-Q2AOv9DV.js → SpeechSettings-CiknYk2P.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-OSNyOUr8.js → SyncSettings-D-8JVip4.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-DfUDrno5.js → Tasks-B2KnR9XC.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-CSTkzDRS.js → Templates-CipXkOE-.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-BR__gCTG.js → Tenant-DbwgIMmx.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-Bv4DM5OI.js → Terminal-OtCriHEE.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-DUnZuAtn.js → TimelineRenderer-B9LU5qrl.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-Dm0sczRV.js → Tokens-B4iFVxvH.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-DsSOp0Ob.js → Trigger-BegM2v8q.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-C0HJtFwV.js → Trust-COkqnnFj.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-BbqIZiX-.js → UkeySign-BzqKPChj.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-D7dDTt3D.js → VideoEditing-D-F2XL3C.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-DJGu0KWq.js → Wallet-22PzRY9T.js} +2 -2
- package/src/assets/web-panel/assets/{WebAuthn-x8OoogZU.js → WebAuthn-eSMq_qtz.js} +4 -4
- package/src/assets/web-panel/assets/{WorkflowEditor-CC3jIUqx.js → WorkflowEditor-BvdAakpq.js} +1 -1
- package/src/assets/web-panel/assets/{chat-Bqgv5oe8.js → chat-C6jhpF7E.js} +1 -1
- package/src/assets/web-panel/assets/{colors-CbAMqLFr.js → colors-BbbW2Pfv.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-B-zIDFg3.js → compact-item-CVFeLTqt.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-D_s1ZLtZ.js → createContext-DS0m7NZc.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-D50gzPS9.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-Cj_J9Eb9.js → hasIn-DVouP0t6.js} +1 -1
- package/src/assets/web-panel/assets/{index-BtJzqXCQ.js → index--AdzSlH7.js} +1 -1
- package/src/assets/web-panel/assets/{index-D_igPAXH.js → index-54aHB4FL.js} +1 -1
- package/src/assets/web-panel/assets/{index-BivQyKes.js → index-8QbwhE3i.js} +1 -1
- package/src/assets/web-panel/assets/{index-3o8DjHHt.js → index-B5JRIZXi.js} +1 -1
- package/src/assets/web-panel/assets/{index-0je3TLmx.js → index-B8a_c_qZ.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cz0blpuN.js → index-BE_u-rXr.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cu3FtpNc.js → index-BOOsA080.js} +1 -1
- package/src/assets/web-panel/assets/{index-KY7udrs-.js → index-BXw6ivB8.js} +1 -1
- package/src/assets/web-panel/assets/{index-J4JLWUMK.js → index-BaUah6TO.js} +1 -1
- package/src/assets/web-panel/assets/{index-C6bAvixK.js → index-Be12HoCS.js} +3 -3
- package/src/assets/web-panel/assets/{index-Cy3XOp7S.js → index-BhvnNFfd.js} +1 -1
- package/src/assets/web-panel/assets/{index-C6lO24YD.js → index-BrIv7C9a.js} +1 -1
- package/src/assets/web-panel/assets/{index-BPdH-XTd.js → index-BvCgzl0X.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cg0oYTy5.js → index-Bw-ZGQCH.js} +1 -1
- package/src/assets/web-panel/assets/{index-ab6FUgkq.js → index-BwZnPiFd.js} +1 -1
- package/src/assets/web-panel/assets/{index-DuqVIsHL.js → index-BwozzG6k.js} +1 -1
- package/src/assets/web-panel/assets/{index-B6qoyCkx.js → index-ByLsH9CL.js} +1 -1
- package/src/assets/web-panel/assets/{index-BjfpW5s3.js → index-BzL3LWFX.js} +1 -1
- package/src/assets/web-panel/assets/{index-BFNO6ycz.js → index-C4xmLgLa.js} +1 -1
- package/src/assets/web-panel/assets/{index-dg0JqQDA.js → index-C6q2MkIG.js} +1 -1
- package/src/assets/web-panel/assets/{index-DtplHZ5o.js → index-C74emdKD.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bi1E5h9V.js → index-CIg4nqyp.js} +1 -1
- package/src/assets/web-panel/assets/{index-C-Z13SCI.js → index-CIk7mg6G.js} +1 -1
- package/src/assets/web-panel/assets/{index-JdSkHz64.js → index-CPS8E-J6.js} +1 -1
- package/src/assets/web-panel/assets/index-CgensV-n.js +1 -0
- package/src/assets/web-panel/assets/{index-DVjFV2mg.js → index-CkdYB0_g.js} +1 -1
- package/src/assets/web-panel/assets/{index-CsHTqPTg.js → index-CpTbnbUI.js} +1 -1
- package/src/assets/web-panel/assets/index-Ctkf06Z_.js +1 -0
- package/src/assets/web-panel/assets/{index-nEWJobH4.js → index-CwYFDMn_.js} +1 -1
- package/src/assets/web-panel/assets/{index-BN5sO3kL.js → index-DGYX17YW.js} +1 -1
- package/src/assets/web-panel/assets/{index-CD_A-N7c.js → index-DR6TDz5H.js} +1 -1
- package/src/assets/web-panel/assets/{index-ggeC-_dR.js → index-DUBkae39.js} +1 -1
- package/src/assets/web-panel/assets/{index-D9CqHOgd.js → index-DotRL126.js} +1 -1
- package/src/assets/web-panel/assets/{index-CSzWODfj.js → index-DpReoAGb.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bw88Mk2t.js → index-Ds4wbrgk.js} +1 -1
- package/src/assets/web-panel/assets/{index-BGxI_Qxg.js → index-DuVjTDcW.js} +1 -1
- package/src/assets/web-panel/assets/{index-BLwUCYDf.js → index-JeJqXbUU.js} +1 -1
- package/src/assets/web-panel/assets/{index-D1t2IqNU.js → index-SdFkCtiC.js} +1 -1
- package/src/assets/web-panel/assets/{index-CVPI_iIh.js → index-eaXEQFuP.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-DDIGXQ08.js → initDefaultProps-CB5cUnQ5.js} +1 -1
- package/src/assets/web-panel/assets/{motion-97SXSYE4.js → motion-bn_fWhQV.js} +1 -1
- package/src/assets/web-panel/assets/{move-CfDQMFRe.js → move-DY4-Odtu.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-qLYCGwY8.js → mtc-parser-B5l5mjoh.js} +1 -1
- package/src/assets/web-panel/assets/{omit-xVqy1mCI.js → omit-rJ-Ci8CW.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-DCGlFyfv.js → pickAttrs-BMwGJd6C.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-DYgPuMk3.js → placementArrow-CaiQyGXG.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-7zUIdAWH.js → responsiveObserve-DQTO5wDH.js} +1 -1
- package/src/assets/web-panel/assets/{slide-TyaiZlSh.js → slide-Dg6ZDtbz.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-BPd13Fae.js → statusUtils-bvjrktKc.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-A44XGgQ0.js → styleChecker-C1geIU_x.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-ZX5EUa8B.js → useFlexGapSupport-BzU3RJRi.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-Sh_fBPqz.js → useFs-B7l6OwLe.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-DhR6Izei.js → usePersonalDataHub-BYF1L_8x.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-DC8g6oRh.js → vnode-6c2GVkT-.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-DRjJmOwj.js → zoom-BYAY6QeD.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/commands/agent.js +21 -10
- package/src/data/changelog.json +39 -0
- package/src/gateways/ws/session-protocol.js +19 -5
- package/src/gateways/ws/ws-agent-handler.js +6 -0
- package/src/gateways/ws/ws-session-gateway.js +133 -21
- package/src/lib/agent-execution-policy.js +177 -0
- package/src/lib/authority-launch-environment.js +99 -0
- package/src/lib/auto-mode-config.js +129 -45
- package/src/lib/claude-project-auto-memory.js +7 -30
- package/src/lib/context-memory-kernel/task-worklog-port.js +73 -24
- package/src/lib/diagnostic-excerpt.js +91 -10
- package/src/lib/diagnostic-workflow.js +2 -0
- package/src/lib/evolution/volcengine-function-replay-store.js +38 -28
- package/src/lib/git-tool-input.js +7 -0
- package/src/lib/host-tool-policy-authority.js +151 -0
- package/src/lib/permission-authority.js +67 -5
- package/src/lib/plugin-runtime/settings.js +9 -1
- package/src/lib/remote-read-loop-guard.js +59 -14
- package/src/lib/sandbox-egress-authority-monitor.js +3 -2
- package/src/lib/session-manager.js +20 -5
- package/src/lib/settings-loader.cjs +175 -19
- package/src/lib/settings-source-observation.cjs +270 -0
- package/src/lib/sub-agent-context.js +36 -18
- package/src/lib/task-progress-tracker.js +112 -12
- package/src/lib/unattended-action-policy.js +456 -38
- package/src/repl/agent-repl.js +58 -29
- package/src/repl/repl-tool-executor.js +39 -0
- package/src/runtime/agent-core.js +272 -5
- package/src/runtime/agent-runtime.js +15 -3
- package/src/runtime/headless-runner.js +13 -4
- package/src/runtime/headless-stream.js +9 -0
- package/src/runtime/policies/agent-policy.js +5 -4
- package/src/assets/web-panel/assets/MarkdownRenderer-BpRWpN1E.js +0 -1
- package/src/assets/web-panel/assets/Projects-3Qo2Mvxb.js +0 -1
- package/src/assets/web-panel/assets/devWarning-QMSC9xgn.js +0 -1
- package/src/assets/web-panel/assets/index-DVq7UuW0.js +0 -1
- package/src/assets/web-panel/assets/index-DYVepsIV.js +0 -1
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/** Host-owned policy snapshots and synchronous, irreversible revisions.
|
|
2
|
+
* Revisions cover commits through this owner in this process. Persisting a
|
|
3
|
+
* session's latest policy does not make these revisions a durable journal.
|
|
4
|
+
*/
|
|
5
|
+
import { randomUUID } from "node:crypto";
|
|
6
|
+
import { snapshotMcpJsonRpcInput } from "./mcp-call-ledger.js";
|
|
7
|
+
|
|
8
|
+
const AUTHORITIES = new WeakSet();
|
|
9
|
+
const CHILD_AUTHORITIES = new WeakMap();
|
|
10
|
+
|
|
11
|
+
function unavailable() {
|
|
12
|
+
const error = new Error("Host tool policy authority is unavailable");
|
|
13
|
+
error.code = "CC_HOST_TOOL_POLICY_AUTHORITY_UNAVAILABLE";
|
|
14
|
+
return error;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function capturePolicy(policy) {
|
|
18
|
+
if (policy == null) return null;
|
|
19
|
+
try {
|
|
20
|
+
const snapshot = snapshotMcpJsonRpcInput(policy);
|
|
21
|
+
if (typeof snapshot !== "object" || Array.isArray(snapshot))
|
|
22
|
+
throw unavailable();
|
|
23
|
+
for (const field of ["tools", "toolPolicies"]) {
|
|
24
|
+
const policies = snapshot[field];
|
|
25
|
+
if (policies == null) continue;
|
|
26
|
+
if (typeof policies !== "object" || Array.isArray(policies))
|
|
27
|
+
throw unavailable();
|
|
28
|
+
for (const entry of Object.values(policies)) {
|
|
29
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry))
|
|
30
|
+
throw unavailable();
|
|
31
|
+
for (const flag of [
|
|
32
|
+
"allowed",
|
|
33
|
+
"requiresConfirmation",
|
|
34
|
+
"requiresPlanApproval",
|
|
35
|
+
"isReadOnly",
|
|
36
|
+
]) {
|
|
37
|
+
if (entry[flag] !== undefined && typeof entry[flag] !== "boolean")
|
|
38
|
+
throw unavailable();
|
|
39
|
+
}
|
|
40
|
+
if (entry.decision !== undefined && typeof entry.decision !== "string")
|
|
41
|
+
throw unavailable();
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
if (
|
|
45
|
+
snapshot.toolDefinitions !== undefined &&
|
|
46
|
+
!Array.isArray(snapshot.toolDefinitions)
|
|
47
|
+
)
|
|
48
|
+
throw unavailable();
|
|
49
|
+
return snapshot;
|
|
50
|
+
} catch {
|
|
51
|
+
throw unavailable();
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function captureHostToolPolicyAuthority(authority) {
|
|
56
|
+
if (authority == null) return null;
|
|
57
|
+
if (!AUTHORITIES.has(authority)) throw unavailable();
|
|
58
|
+
return authority;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** The host retains the controller; consumers receive only `authority`. */
|
|
62
|
+
export function createHostToolPolicyAuthority(policy = null) {
|
|
63
|
+
const ownerId = randomUUID();
|
|
64
|
+
let snapshot = Object.freeze({
|
|
65
|
+
schema: "chainlesschain.host-tool-policy-authority/v1",
|
|
66
|
+
ownerId,
|
|
67
|
+
revision: 0,
|
|
68
|
+
policy: capturePolicy(policy),
|
|
69
|
+
});
|
|
70
|
+
let invalid = false;
|
|
71
|
+
const listeners = new Set();
|
|
72
|
+
function publish(nextPolicy) {
|
|
73
|
+
if (snapshot.revision >= Number.MAX_SAFE_INTEGER) throw unavailable();
|
|
74
|
+
snapshot = Object.freeze({
|
|
75
|
+
...snapshot,
|
|
76
|
+
revision: snapshot.revision + 1,
|
|
77
|
+
policy: nextPolicy,
|
|
78
|
+
});
|
|
79
|
+
// Commit before notifying. A failing observer cannot hide a revision from
|
|
80
|
+
// the remaining execution monitors.
|
|
81
|
+
for (const listener of [...listeners]) {
|
|
82
|
+
try {
|
|
83
|
+
listener(snapshot);
|
|
84
|
+
} catch {
|
|
85
|
+
// Observer delivery does not roll back committed authority.
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return snapshot;
|
|
89
|
+
}
|
|
90
|
+
const authority = Object.freeze({
|
|
91
|
+
getSnapshot() {
|
|
92
|
+
if (invalid) throw unavailable();
|
|
93
|
+
return snapshot;
|
|
94
|
+
},
|
|
95
|
+
subscribePolicyRevision(listener) {
|
|
96
|
+
if (typeof listener !== "function")
|
|
97
|
+
throw new TypeError("listener required");
|
|
98
|
+
listeners.add(listener);
|
|
99
|
+
return () => listeners.delete(listener);
|
|
100
|
+
},
|
|
101
|
+
});
|
|
102
|
+
AUTHORITIES.add(authority);
|
|
103
|
+
return Object.freeze({
|
|
104
|
+
authority,
|
|
105
|
+
commit(nextPolicy) {
|
|
106
|
+
const captured = capturePolicy(nextPolicy);
|
|
107
|
+
if (
|
|
108
|
+
!invalid &&
|
|
109
|
+
JSON.stringify(captured) === JSON.stringify(snapshot.policy)
|
|
110
|
+
)
|
|
111
|
+
return snapshot;
|
|
112
|
+
invalid = false;
|
|
113
|
+
return publish(captured);
|
|
114
|
+
},
|
|
115
|
+
invalidate() {
|
|
116
|
+
invalid = true;
|
|
117
|
+
return publish(snapshot.policy);
|
|
118
|
+
},
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/** Inherit the live ceiling without introducing host definitions into a child. */
|
|
123
|
+
export function hostToolPolicyAuthorityForChild(value) {
|
|
124
|
+
const parent = captureHostToolPolicyAuthority(value);
|
|
125
|
+
if (!parent) return null;
|
|
126
|
+
if (CHILD_AUTHORITIES.has(parent)) return CHILD_AUTHORITIES.get(parent);
|
|
127
|
+
let previous = null;
|
|
128
|
+
let projected = null;
|
|
129
|
+
const child = Object.freeze({
|
|
130
|
+
getSnapshot() {
|
|
131
|
+
const current = parent.getSnapshot();
|
|
132
|
+
if (current !== previous) {
|
|
133
|
+
projected = Object.freeze({
|
|
134
|
+
...current,
|
|
135
|
+
policy: current.policy
|
|
136
|
+
? Object.freeze({
|
|
137
|
+
...current.policy,
|
|
138
|
+
toolDefinitions: Object.freeze([]),
|
|
139
|
+
})
|
|
140
|
+
: null,
|
|
141
|
+
});
|
|
142
|
+
previous = current;
|
|
143
|
+
}
|
|
144
|
+
return projected;
|
|
145
|
+
},
|
|
146
|
+
subscribePolicyRevision: parent.subscribePolicyRevision,
|
|
147
|
+
});
|
|
148
|
+
AUTHORITIES.add(child);
|
|
149
|
+
CHILD_AUTHORITIES.set(parent, child);
|
|
150
|
+
return child;
|
|
151
|
+
}
|
|
@@ -1,14 +1,42 @@
|
|
|
1
1
|
/** Resolve static settings plus CLI-owned workspace-scoped permission rules. */
|
|
2
2
|
|
|
3
3
|
import settingsLoader from "./settings-loader.cjs";
|
|
4
|
+
import path from "node:path";
|
|
4
5
|
import { ScopedPermissionStore } from "./scoped-permission-store.js";
|
|
5
6
|
|
|
6
|
-
const {
|
|
7
|
+
const {
|
|
8
|
+
applyManagedPermissionPolicy,
|
|
9
|
+
loadSettings,
|
|
10
|
+
getSettingsPermissionRevision,
|
|
11
|
+
subscribeSettingsPermissionRevision,
|
|
12
|
+
} = settingsLoader;
|
|
7
13
|
const KINDS = ["allow", "ask", "deny"];
|
|
14
|
+
const providers = new WeakMap();
|
|
15
|
+
const settingsAuthority = Object.freeze({
|
|
16
|
+
getSnapshot: getSettingsPermissionRevision,
|
|
17
|
+
subscribePolicyRevision: subscribeSettingsPermissionRevision,
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
function freezeSnapshot(value) {
|
|
21
|
+
if (value && typeof value === "object") {
|
|
22
|
+
for (const child of Object.values(value)) freezeSnapshot(child);
|
|
23
|
+
Object.freeze(value);
|
|
24
|
+
}
|
|
25
|
+
return value;
|
|
26
|
+
}
|
|
8
27
|
|
|
9
28
|
function cloneRules(rules) {
|
|
10
29
|
return Object.fromEntries(
|
|
11
|
-
KINDS.map((kind) =>
|
|
30
|
+
KINDS.map((kind) => {
|
|
31
|
+
const entries = rules?.[kind] ?? [];
|
|
32
|
+
if (
|
|
33
|
+
!Array.isArray(entries) ||
|
|
34
|
+
entries.some((entry) => typeof entry !== "string")
|
|
35
|
+
) {
|
|
36
|
+
throw new TypeError(`permission ${kind} rules must be a string array`);
|
|
37
|
+
}
|
|
38
|
+
return [kind, [...entries]];
|
|
39
|
+
}),
|
|
12
40
|
);
|
|
13
41
|
}
|
|
14
42
|
|
|
@@ -24,6 +52,7 @@ export function loadPermissionAuthority({
|
|
|
24
52
|
baseRules = null,
|
|
25
53
|
scopedStore = null,
|
|
26
54
|
} = {}) {
|
|
55
|
+
const settingsRevision = getSettingsPermissionRevision();
|
|
27
56
|
const loaded = loadSettings({
|
|
28
57
|
cwd,
|
|
29
58
|
settingsFile,
|
|
@@ -63,15 +92,48 @@ export function loadPermissionAuthority({
|
|
|
63
92
|
}
|
|
64
93
|
}
|
|
65
94
|
|
|
66
|
-
|
|
95
|
+
if (getSettingsPermissionRevision() !== settingsRevision) {
|
|
96
|
+
const error = new Error(
|
|
97
|
+
"settings changed while permission authority was loading",
|
|
98
|
+
);
|
|
99
|
+
error.code = "CC_SETTINGS_PERMISSION_AUTHORITY_CHANGED";
|
|
100
|
+
throw error;
|
|
101
|
+
}
|
|
102
|
+
return freezeSnapshot({
|
|
67
103
|
...loaded,
|
|
68
104
|
rules,
|
|
69
105
|
sources,
|
|
70
106
|
scoped,
|
|
71
107
|
hasRules: hasRules(rules),
|
|
72
|
-
|
|
108
|
+
settingsRevision,
|
|
109
|
+
});
|
|
73
110
|
}
|
|
74
111
|
|
|
75
112
|
export function createPermissionRulesProvider(options = {}) {
|
|
76
|
-
|
|
113
|
+
const cwd = path.resolve(options.cwd || process.cwd());
|
|
114
|
+
const captured = Object.freeze({
|
|
115
|
+
cwd,
|
|
116
|
+
settingsFile: options.settingsFile
|
|
117
|
+
? path.resolve(cwd, options.settingsFile)
|
|
118
|
+
: null,
|
|
119
|
+
managedSettingsFile: options.managedSettingsFile
|
|
120
|
+
? path.resolve(options.managedSettingsFile)
|
|
121
|
+
: null,
|
|
122
|
+
// Environment kill switches retain live sampling semantics. The source
|
|
123
|
+
// object cannot be replaced through later changes to the options object.
|
|
124
|
+
env: options.env || process.env,
|
|
125
|
+
baseRules: options.baseRules
|
|
126
|
+
? freezeSnapshot(cloneRules(options.baseRules))
|
|
127
|
+
: null,
|
|
128
|
+
scopedStore: options.scopedStore || null,
|
|
129
|
+
});
|
|
130
|
+
const provider = () => loadPermissionAuthority(captured);
|
|
131
|
+
providers.set(provider, settingsAuthority);
|
|
132
|
+
return Object.freeze(provider);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// A legacy callback cannot advertise a trusted revision subscription simply
|
|
136
|
+
// by copying properties onto itself. It still receives sampled revalidation.
|
|
137
|
+
export function permissionRulesProviderAuthority(provider) {
|
|
138
|
+
return providers.get(provider) || null;
|
|
77
139
|
}
|
|
@@ -17,6 +17,10 @@
|
|
|
17
17
|
import fs from "fs";
|
|
18
18
|
import { discoverPlugins } from "./scopes.js";
|
|
19
19
|
import { partitionByTrust, warnUntrustedOnce } from "./trust.js";
|
|
20
|
+
import {
|
|
21
|
+
isPluginAuthorityEnvironmentKey,
|
|
22
|
+
isValidEnvironmentKey,
|
|
23
|
+
} from "../authority-launch-environment.js";
|
|
20
24
|
|
|
21
25
|
export const _deps = { readFileSync: fs.readFileSync };
|
|
22
26
|
|
|
@@ -68,7 +72,11 @@ export function collectPluginSettings(opts = {}) {
|
|
|
68
72
|
!Array.isArray(parsed.env)
|
|
69
73
|
) {
|
|
70
74
|
for (const [k, v] of Object.entries(parsed.env)) {
|
|
71
|
-
if (
|
|
75
|
+
if (
|
|
76
|
+
typeof v === "string" &&
|
|
77
|
+
isValidEnvironmentKey(k) &&
|
|
78
|
+
!isPluginAuthorityEnvironmentKey(k)
|
|
79
|
+
) {
|
|
72
80
|
env[k] = v;
|
|
73
81
|
contributed = true;
|
|
74
82
|
}
|
|
@@ -18,6 +18,19 @@ function pullRequestTarget(repo, id = "list") {
|
|
|
18
18
|
return { ...githubTarget(repo, "pulls", id), pullRequest: true };
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
+
function issueTarget(repo, id = "list") {
|
|
22
|
+
return { ...githubTarget(repo, "issues", id), issue: true };
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const ISSUE_WORKFLOW_GUIDANCE =
|
|
26
|
+
"Issue investigation: retain the issue, linked PR and workflow run as separate targets, with verified revisions and observed test outcomes. " +
|
|
27
|
+
"Use gh issue view for an issue and gh pr view for its linked PR; their numbers need not match. " +
|
|
28
|
+
"Use gh run view <run-id> for one run; gh run list accepts filters, not a positional run ID. " +
|
|
29
|
+
"For unknown JSON fields, inspect stderr/help and request supported fields without jq or shell pipelines first. " +
|
|
30
|
+
"A completed metadata read remains evidence after an unrelated command fails; do not restart the same investigation. " +
|
|
31
|
+
"Once the failing test and source are known, state a falsifiable hypothesis, run the focused reproduction, then make and validate the authorized fix. " +
|
|
32
|
+
"A merged PR or a passing diagnostic retry alone does not resolve the reported failure. Perform issue closure only when authorized and supported by the requested resolution evidence. ";
|
|
33
|
+
|
|
21
34
|
function commandFlag(command, names) {
|
|
22
35
|
return new RegExp(
|
|
23
36
|
`(?:^|\\s)(?:${names})(?:=|\\s+)(?:"([^"]*)"|'([^']*)'|([^\\s;&|]+))`,
|
|
@@ -27,13 +40,16 @@ function commandFlag(command, names) {
|
|
|
27
40
|
.find((value) => value !== undefined);
|
|
28
41
|
}
|
|
29
42
|
|
|
30
|
-
function
|
|
43
|
+
function discussionCommandTarget(command) {
|
|
31
44
|
const match =
|
|
32
|
-
/(?:^|[\s;&|])gh(?:\.exe)?\s+pr\s+(list|view|diff)\b([^;&|\r\n]*)/i.exec(
|
|
45
|
+
/(?:^|[\s;&|])gh(?:\.exe)?\s+(pr|issue)\s+(list|view|diff)\b([^;&|\r\n]*)/i.exec(
|
|
33
46
|
command,
|
|
34
47
|
);
|
|
35
48
|
if (!match) return null;
|
|
36
|
-
const
|
|
49
|
+
const target =
|
|
50
|
+
match[1].toLowerCase() === "issue" ? issueTarget : pullRequestTarget;
|
|
51
|
+
const tail = match[3];
|
|
52
|
+
const fields = commandFlag(tail, "--json")?.split(",");
|
|
37
53
|
// Lightweight status polling is intentional waiting, not repeated review.
|
|
38
54
|
if (
|
|
39
55
|
fields?.length &&
|
|
@@ -44,18 +60,20 @@ function pullRequestCommandTarget(command) {
|
|
|
44
60
|
)
|
|
45
61
|
)
|
|
46
62
|
return null;
|
|
47
|
-
const repo = commandFlag(
|
|
48
|
-
if (match[
|
|
63
|
+
const repo = commandFlag(tail, "--repo|-R") || "current-repo";
|
|
64
|
+
if (match[2].toLowerCase() === "list") return target(repo);
|
|
49
65
|
const prUrl =
|
|
50
|
-
/https:\/\/github\.com\/([^/\s]+\/[^/\s]+)\/pull\/(\d+)\b/i.exec(
|
|
51
|
-
|
|
66
|
+
/https:\/\/github\.com\/([^/\s]+\/[^/\s]+)\/(?:pull|issues)\/(\d+)\b/i.exec(
|
|
67
|
+
tail,
|
|
68
|
+
);
|
|
69
|
+
if (prUrl) return target(prUrl[1], prUrl[2]);
|
|
52
70
|
// Remove flags that consume values before finding the positional PR number.
|
|
53
|
-
const positional =
|
|
71
|
+
const positional = tail.replace(
|
|
54
72
|
/(?:^|\s)(?:--repo|-R|--json|--jq|-q|--template|-t|--color)(?:=|\s+)(?:"[^"]*"|'[^']*'|[^\s]+)/g,
|
|
55
73
|
" ",
|
|
56
74
|
);
|
|
57
75
|
const number = /(?:^|\s)["']?(\d+)["']?(?=\s|$)/.exec(positional)?.[1];
|
|
58
|
-
return number ?
|
|
76
|
+
return number ? target(repo, number) : null;
|
|
59
77
|
}
|
|
60
78
|
|
|
61
79
|
function urlTarget(value) {
|
|
@@ -69,6 +87,10 @@ function urlTarget(value) {
|
|
|
69
87
|
};
|
|
70
88
|
}
|
|
71
89
|
if (url.hostname === "github.com") {
|
|
90
|
+
const issue = /^\/([^/]+\/[^/]+)\/issues(?:\/(\d+))?\/?$/.exec(
|
|
91
|
+
url.pathname,
|
|
92
|
+
);
|
|
93
|
+
if (issue) return issueTarget(issue[1], issue[2]);
|
|
72
94
|
const pr =
|
|
73
95
|
/^\/([^/]+\/[^/]+)\/(?:pulls\/?|pull\/(\d+)(?:\/(?:files|commits|checks))?\/?|pull\/(\d+)\.(?:diff|patch))$/.exec(
|
|
74
96
|
url.pathname,
|
|
@@ -87,6 +109,11 @@ function urlTarget(value) {
|
|
|
87
109
|
);
|
|
88
110
|
}
|
|
89
111
|
if (url.hostname === "api.github.com") {
|
|
112
|
+
const issue =
|
|
113
|
+
/^\/repos\/([^/]+\/[^/]+)\/issues(?:\/(\d+)(?:\/(?:comments|events|timeline))?)?\/?$/.exec(
|
|
114
|
+
url.pathname,
|
|
115
|
+
);
|
|
116
|
+
if (issue) return issueTarget(issue[1], issue[2]);
|
|
90
117
|
const pr =
|
|
91
118
|
/^\/repos\/([^/]+\/[^/]+)\/pulls(?:\/(\d+)(?:\/(?:files|commits|reviews|comments))?)?\/?$/.exec(
|
|
92
119
|
url.pathname,
|
|
@@ -144,8 +171,8 @@ export function remoteReadTarget(tool, args = {}) {
|
|
|
144
171
|
}
|
|
145
172
|
if (tool !== "run_shell") return null;
|
|
146
173
|
const command = typeof args.command === "string" ? args.command : "";
|
|
147
|
-
const
|
|
148
|
-
if (
|
|
174
|
+
const discussion = discussionCommandTarget(command);
|
|
175
|
+
if (discussion) return discussion;
|
|
149
176
|
const view = /(?:^|[\s;&|])gh(?:\.exe)?\s+run\s+view\s+([^;&|\r\n]+)/i.exec(
|
|
150
177
|
command,
|
|
151
178
|
);
|
|
@@ -172,6 +199,11 @@ export function remoteReadTarget(tool, args = {}) {
|
|
|
172
199
|
/(?:^|\s)(?:(?:--field|--raw-field|--input)(?:=|\s)|-[fF])/.test(command)
|
|
173
200
|
)
|
|
174
201
|
return null;
|
|
202
|
+
const issueApi =
|
|
203
|
+
/(?:https:\/\/api\.github\.com\/)?\/?repos\/([\w.-]+\/[\w.-]+)\/issues(?:\/(\d+)(?:\/(?:comments|events|timeline))?)?(?=[?\s"']|$)/.exec(
|
|
204
|
+
command,
|
|
205
|
+
);
|
|
206
|
+
if (issueApi) return issueTarget(issueApi[1], issueApi[2]);
|
|
175
207
|
const prApi =
|
|
176
208
|
/(?:https:\/\/api\.github\.com\/)?\/?repos\/([\w.-]+\/[\w.-]+)\/pulls(?:\/(\d+)(?:\/(?:files|commits|reviews|comments))?)?(?=[?\s"']|$)/.exec(
|
|
177
209
|
command,
|
|
@@ -369,6 +401,14 @@ export class RemoteReadLoopGuard {
|
|
|
369
401
|
get recoveryHint() {
|
|
370
402
|
if (!(this.targets.get(this.activeKey)?.repeats >= RECOVERY_AFTER))
|
|
371
403
|
return null;
|
|
404
|
+
if (this.targets.get(this.activeKey)?.issue) {
|
|
405
|
+
return (
|
|
406
|
+
"Remote-read loop recovery: repeated issue reads returned failures or already-seen evidence. " +
|
|
407
|
+
"Use the retained observations; identify the exact missing fact before a new focused read. " +
|
|
408
|
+
ISSUE_WORKFLOW_GUIDANCE +
|
|
409
|
+
DIAGNOSTIC_WORKFLOW_GUIDANCE
|
|
410
|
+
);
|
|
411
|
+
}
|
|
372
412
|
if (this.targets.get(this.activeKey)?.shellPolicy) {
|
|
373
413
|
return (
|
|
374
414
|
"Tool-policy loop recovery: the same execution policy keeps rejecting commands, even when their arguments change. " +
|
|
@@ -449,6 +489,8 @@ export class RemoteReadLoopGuard {
|
|
|
449
489
|
}
|
|
450
490
|
|
|
451
491
|
get workflowHint() {
|
|
492
|
+
if (this.targets.get(this.activeKey)?.issue)
|
|
493
|
+
return ISSUE_WORKFLOW_GUIDANCE + DIAGNOSTIC_WORKFLOW_GUIDANCE;
|
|
452
494
|
const active = this.targets.get(this.activeKey);
|
|
453
495
|
if (active?.github && !active.pullRequest) {
|
|
454
496
|
return (
|
|
@@ -505,6 +547,7 @@ export class RemoteReadLoopGuard {
|
|
|
505
547
|
page,
|
|
506
548
|
localLog,
|
|
507
549
|
pullRequest,
|
|
550
|
+
issue,
|
|
508
551
|
shellPolicy,
|
|
509
552
|
gitRepository,
|
|
510
553
|
}) => ({
|
|
@@ -515,9 +558,11 @@ export class RemoteReadLoopGuard {
|
|
|
515
558
|
: github
|
|
516
559
|
? pullRequest
|
|
517
560
|
? "GitHub pull requests"
|
|
518
|
-
:
|
|
519
|
-
? "
|
|
520
|
-
:
|
|
561
|
+
: issue
|
|
562
|
+
? "GitHub issues"
|
|
563
|
+
: localLog
|
|
564
|
+
? "saved GitHub Actions log"
|
|
565
|
+
: "GitHub Actions logs"
|
|
521
566
|
: tool === "web_search"
|
|
522
567
|
? "web_search"
|
|
523
568
|
: "web_fetch",
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Recheck the live shell authority while a Docker egress session exists. This
|
|
3
|
-
*
|
|
4
|
-
*
|
|
3
|
+
* uses bounded polling as a fallback. Trusted policy owners also call revoke()
|
|
4
|
+
* synchronously on official revisions; unobserved external changes reverted
|
|
5
|
+
* between polls remain outside that event guarantee.
|
|
5
6
|
*/
|
|
6
7
|
export function createDockerEgressAuthorityMonitor({
|
|
7
8
|
revalidate,
|
|
@@ -108,7 +108,7 @@ export function saveMessages(db, sessionId, messages, metadata) {
|
|
|
108
108
|
/**
|
|
109
109
|
* Get a session by ID
|
|
110
110
|
*/
|
|
111
|
-
export function getSession(db, sessionId) {
|
|
111
|
+
export function getSession(db, sessionId, { strictMetadata = false } = {}) {
|
|
112
112
|
ensureSessionsTable(db);
|
|
113
113
|
|
|
114
114
|
// Try exact match first, then prefix match
|
|
@@ -127,16 +127,31 @@ export function getSession(db, sessionId) {
|
|
|
127
127
|
|
|
128
128
|
if (!session) return null;
|
|
129
129
|
|
|
130
|
+
let metadata;
|
|
131
|
+
if (strictMetadata) {
|
|
132
|
+
metadata =
|
|
133
|
+
typeof session.metadata === "string"
|
|
134
|
+
? JSON.parse(session.metadata)
|
|
135
|
+
: (session.metadata ?? {});
|
|
136
|
+
if (!metadata || typeof metadata !== "object" || Array.isArray(metadata)) {
|
|
137
|
+
const error = new TypeError("Session authority metadata is invalid");
|
|
138
|
+
error.code = "CC_SESSION_AUTHORITY_METADATA_INVALID";
|
|
139
|
+
throw error;
|
|
140
|
+
}
|
|
141
|
+
} else {
|
|
142
|
+
metadata =
|
|
143
|
+
typeof session.metadata === "string"
|
|
144
|
+
? safeJsonParse(session.metadata, {})
|
|
145
|
+
: session.metadata || {};
|
|
146
|
+
}
|
|
147
|
+
|
|
130
148
|
return {
|
|
131
149
|
...session,
|
|
132
150
|
// A single corrupt (non-JSON, non-empty) column must not make the whole
|
|
133
151
|
// session unloadable — that would block resume of the user's own data.
|
|
134
152
|
// Matches the safeJsonParse fallback listSessions already uses.
|
|
135
153
|
messages: safeJsonParse(session.messages, []),
|
|
136
|
-
metadata
|
|
137
|
-
typeof session.metadata === "string"
|
|
138
|
-
? safeJsonParse(session.metadata, {})
|
|
139
|
-
: session.metadata || {},
|
|
154
|
+
metadata,
|
|
140
155
|
};
|
|
141
156
|
}
|
|
142
157
|
|