chainlesschain 0.166.46 → 0.166.48
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 +6 -4
- package/package.json +4 -2
- package/src/assets/web-panel/assets/{AIOps-Cp8KiMmv.js → AIOps-BLGEffkh.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-B4d9BGsm.js → ActionButton-Cz1z2VGD.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-Cf9yXTkv.js → Analytics-C3j9cf6q.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-40UkmxtP.js → AppLayout-BR9ERuCA.js} +4 -4
- package/src/assets/web-panel/assets/{Artifacts-Ckkb4oOA.js → Artifacts-CozJCm4y.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-Dnf569k5.js → Audit-DC5g9Ra2.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-CA6bLem4.js → BackgroundAgents-B1zrQ0dW.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-ChQU0I_Q.js → Backup-ChVnfjpm.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-BHeZXnNN.js → BaseInput-8_vLQsLf.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-DcFVYB2E.js → Chat-Dct9bm6L.js} +5 -5
- package/src/assets/web-panel/assets/ChatBubbleRenderer-BvoSsCKh.js +1 -0
- package/src/assets/web-panel/assets/{Checkbox-2PDbejv2.js → Checkbox-DZQIsCIC.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-DJBn1RAu.js → Codegen-CdzHxmnq.js} +1 -1
- package/src/assets/web-panel/assets/{Col-CPRq7n6y.js → Col-DdAN8LeW.js} +1 -1
- package/src/assets/web-panel/assets/{Community-axvINFMi.js → Community-CNgjT9MA.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-_OX4Qbe0.js → Compact-BtQiX6G-.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-DWAkvzRL.js → Compliance-OGX5ma3j.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-CUIJ25oj.js → Cowork-CgugQnGM.js} +3 -3
- package/src/assets/web-panel/assets/{Cron-nR0m7H3_.js → Cron-Bpg7cPFD.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-CgTLXzh_.js → Crosschain-D1hsuswf.js} +1 -1
- package/src/assets/web-panel/assets/{DID-BllzRB6Q.js → DID-miDRsMBw.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-C9f0IIBN.js → Dashboard-CGcl8htP.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-BkahetiO.js → Dropdown-BFaxTdVE.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-rartVC-k.js → EmailListRenderer-BQdGLZIO.js} +1 -1
- package/src/assets/web-panel/assets/{EvolutionSettings-BqpG3XHV.js → EvolutionSettings-bMvgBk6r.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-DgUdFvw1.js → FamilyGuardDashboard-c1WVC_--.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-oFpIQU6g.js → Federation-DyAcFrQY.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-BUH5xlyP.js → FormItemContext-CfwpfD2B.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-ilRa-ueg.js → GenericCardRenderer-DkJcbBpS.js} +1 -1
- package/src/assets/web-panel/assets/{Git-DlHHSF8j.js → Git-DgYIrPJh.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-yQiiXIWe.js → Governance-BF1tYPJ6.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-BUDRVGOP.js → Inference-BFkwRIrU.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-BaGyIoN1.js → KnowledgeGraph-DpCoqhBa.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-C1cO6zE0.js → Logs-DU2qAJmD.js} +2 -2
- package/src/assets/web-panel/assets/{MarkdownRenderer-_z_Iaoes.js → MarkdownRenderer-QWv7UJLq.js} +1 -1
- package/src/assets/web-panel/assets/{Marketplace-CM8cGL0K.js → Marketplace-Dz9NUrfo.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-CDqAMU0U.js → McpTools-B67I3d5v.js} +3 -3
- package/src/assets/web-panel/assets/{Memory-CfB0-Ofv.js → Memory-crkElJSy.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-BL18ytvB.js → MobileBridge-BO20VbdX.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-PIyyxbuJ.js → MobileProjects-Ba-3gZZd.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-BTTrFCol.js → Mtc-BAHZM084.js} +4 -4
- package/src/assets/web-panel/assets/{MtcAudit-JHZn037c.js → MtcAudit-CL3DzajL.js} +4 -4
- package/src/assets/web-panel/assets/{Multisig-CMf9NEdB.js → Multisig-BSWWzvWH.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-Dhk3qaus.js → NLProgramming-fnxqXM_3.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-6F8EarNN.js → Notes-qUZPIQXT.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-gxdaVEit.js → NotificationSettings-x9Xrixqu.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-gVgXQWdc.js → OrderTableRenderer-BtMtQUt7.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-CzvHcDNO.js → Organization-C946Hzna.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-Dclx30Vv.js → Overflow-BYf8KIts.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-CM0Wm9vZ.js → P2P-Cbqf5dnv.js} +2 -2
- package/src/assets/web-panel/assets/PdhVaultBrowser-rmxp0cuF.js +7 -0
- package/src/assets/web-panel/assets/{Permissions-B_oATenG.js → Permissions-CZBw45EW.js} +3 -3
- package/src/assets/web-panel/assets/{PersonalDataHub-CptiVBh5.js → PersonalDataHub-BKwUAt43.js} +4 -4
- package/src/assets/web-panel/assets/{Pipeline-DkHzgyyI.js → Pipeline-DmFD2QW4.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-DzZvwcKh.js → Privacy-KpuwWvt5.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-CzkCs-5G.js → ProjectInit-Bt8UheX6.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-BB8dcxRD.js → ProjectSettings-2ZeAn9CP.js} +2 -2
- package/src/assets/web-panel/assets/{Projects-B7PW9CFY.js → Projects-zBuet8Zs.js} +1 -1
- package/src/assets/web-panel/assets/{Providers-Tfx-ti1V.js → Providers-DcEAnw6L.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-EvWlNhfX.js → QrScannerModal-qF7liYG9.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-CUspgbNC.js → QuickAsk-qiESlVBJ.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-COY2g77J.js → Recommend-Bk-cQyi9.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-BzMBU6E6.js → RemoteSession-CwrekErS.js} +2 -2
- package/src/assets/web-panel/assets/{Reputation-RtYRF4YP.js → Reputation-Bmw_UQtW.js} +1 -1
- package/src/assets/web-panel/assets/{Row-Bva3l9Lc.js → Row-CPdqZIUX.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-BhjzLvW3.js → RssFeed-D_W-xxvS.js} +2 -2
- package/src/assets/web-panel/assets/{Search-BYzT2PTR.js → Search-Hu0oDFj_.js} +1 -1
- package/src/assets/web-panel/assets/{Security-Bcl4-KTv.js → Security-DeRxUwmq.js} +3 -3
- package/src/assets/web-panel/assets/{Services-BRRa4A0f.js → Services-CsNmHFkf.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-DYQ016qy.js → Skeleton-gv_Vz0tN.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-CAUKL9JA.js → Skills-Dgg-_rCB.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-BTyUqfuD.js → Sla-Bpu-bsEJ.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-CUjMihrs.js → SpeechSettings-CrhleHuF.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-CvfOSupr.js → SyncSettings-JHjUN6I9.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-EvjNXrWH.js → Tasks-DKRWKkl9.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-wPAp0EoM.js → Templates-leiWntLq.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-CP0D-q9Q.js → Tenant--1bYulvn.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-CQ0f2iFg.js → Terminal-C0y6FvSS.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-D3JrGSPb.js → TimelineRenderer-CQK4PVv4.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-yXf2PPp9.js → Tokens-DbIvF-DV.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-DfmMOoZ2.js → Trigger-CkXJ2vuq.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-BTCyMgf1.js → Trust-B02WXJPw.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-DKCwIBHi.js → UkeySign-VPMkfzmw.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-CQpDSzLO.js → VideoEditing-C5ezUnah.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-CyFrzC3K.js → Wallet-CYfvYRrY.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-D6Nr_1kd.js → WebAuthn-DtKl-kpr.js} +4 -4
- package/src/assets/web-panel/assets/{WorkflowEditor-CgR9tNrB.js → WorkflowEditor-DACITF-u.js} +1 -1
- package/src/assets/web-panel/assets/{chat-CRG_IeyP.js → chat-qnygfnLi.js} +1 -1
- package/src/assets/web-panel/assets/{colors-MdE3TStI.js → colors-D4mISLBD.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-B0LRuiNV.js → compact-item-BBPfxYGL.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-B_V5fjcX.js → createContext-Byx4ndHp.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-DBy6gsuI.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-DSL-c_hr.js → hasIn-CGjLkkn3.js} +1 -1
- package/src/assets/web-panel/assets/{index-DE7bSYmW.js → index-B8UpadHC.js} +1 -1
- package/src/assets/web-panel/assets/{index-BD0w-888.js → index-B9jH-h4X.js} +1 -1
- package/src/assets/web-panel/assets/{index-BbZZ7P_c.js → index-BN7_TSgU.js} +1 -1
- package/src/assets/web-panel/assets/{index-BIDCNwy6.js → index-BNe2lCk6.js} +1 -1
- package/src/assets/web-panel/assets/{index-CkN53KiJ.js → index-B_fHPZ8E.js} +1 -1
- package/src/assets/web-panel/assets/index-BfEDXk_E.js +1 -0
- package/src/assets/web-panel/assets/{index-BsAEd-Do.js → index-BklGH3YI.js} +1 -1
- package/src/assets/web-panel/assets/{index-CS3bOhV7.js → index-BmKYaaGP.js} +1 -1
- package/src/assets/web-panel/assets/{index-B3iW-mhn.js → index-BxQF1wCU.js} +1 -1
- package/src/assets/web-panel/assets/{index-SmQfEHjp.js → index-ByA1NyFP.js} +1 -1
- package/src/assets/web-panel/assets/{index-0200QxC4.js → index-BzeBBAl5.js} +1 -1
- package/src/assets/web-panel/assets/{index-c8OqO3rq.js → index-C3abGsSW.js} +1 -1
- package/src/assets/web-panel/assets/{index-C-aKWR2v.js → index-C9tcHs46.js} +1 -1
- package/src/assets/web-panel/assets/{index-BNhWoD6d.js → index-CD5Btq6p.js} +1 -1
- package/src/assets/web-panel/assets/{index-CVDi4rS9.js → index-CFMWjuav.js} +1 -1
- package/src/assets/web-panel/assets/{index-CEOAZm5j.js → index-CMj8qmpX.js} +1 -1
- package/src/assets/web-panel/assets/{index-C67dPW0L.js → index-CVUJjQAT.js} +1 -1
- package/src/assets/web-panel/assets/{index-D3pJBTvo.js → index-CWhI8G14.js} +1 -1
- package/src/assets/web-panel/assets/{index-DtOTppk8.js → index-CZjNamSU.js} +1 -1
- package/src/assets/web-panel/assets/{index-DRRY44XP.js → index-CmHE-lY_.js} +1 -1
- package/src/assets/web-panel/assets/{index-Tg3yl8wc.js → index-CoA8WsVr.js} +1 -1
- package/src/assets/web-panel/assets/{index-CXMeisJK.js → index-Cpg3AWRy.js} +1 -1
- package/src/assets/web-panel/assets/index-CrxUwNM8.js +1 -0
- package/src/assets/web-panel/assets/{index-Bq8-2Ibv.js → index-CsVuQsY1.js} +1 -1
- package/src/assets/web-panel/assets/{index-BHp1nSur.js → index-CydHMjwc.js} +1 -1
- package/src/assets/web-panel/assets/{index-CPYshcic.js → index-DH6iC5b2.js} +1 -1
- package/src/assets/web-panel/assets/{index-DBwBZwio.js → index-DZXhKAk9.js} +1 -1
- package/src/assets/web-panel/assets/{index-Didyuq2Z.js → index-D_g9mA1N.js} +1 -1
- package/src/assets/web-panel/assets/{index-CdHMc9LQ.js → index-DboTeuPF.js} +1 -1
- package/src/assets/web-panel/assets/{index-C5dqieYX.js → index-DfgpKd64.js} +1 -1
- package/src/assets/web-panel/assets/{index-CdplnAS9.js → index-Dmfe0L2Z.js} +1 -1
- package/src/assets/web-panel/assets/{index-KaSejHjf.js → index-Dzni_5Qw.js} +1 -1
- package/src/assets/web-panel/assets/{index-BpwGsAA-.js → index-J5JkStvY.js} +1 -1
- package/src/assets/web-panel/assets/{index-OBhlHSMN.js → index-Ne8UGllV.js} +3 -3
- package/src/assets/web-panel/assets/{index-CZICtYJ0.js → index-O09jXbir.js} +1 -1
- package/src/assets/web-panel/assets/{index-DVgTf7CZ.js → index-Y7el_3WS.js} +1 -1
- package/src/assets/web-panel/assets/{index-BQAsL19t.js → index-c_tBkIlU.js} +1 -1
- package/src/assets/web-panel/assets/{index-BMm8tO2X.js → index-lKJWAmd4.js} +1 -1
- package/src/assets/web-panel/assets/{index-CNytQuFq.js → index-xzRKTy0R.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-P8SBN6Di.js → initDefaultProps-XuhfEIhB.js} +1 -1
- package/src/assets/web-panel/assets/{motion-BubbjUT_.js → motion-Cbxh6AxO.js} +1 -1
- package/src/assets/web-panel/assets/{move-Chcykv0I.js → move-C7IEnFly.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-DyiYuGDu.js → mtc-parser-lLADx4LL.js} +1 -1
- package/src/assets/web-panel/assets/{omit-Rdwb_Neb.js → omit-mIbic_k-.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-CCr0MYHl.js → pickAttrs-_xIYEqu3.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-B1ZXz3bb.js → placementArrow-ClB-E0ny.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-BGEdwej6.js → responsiveObserve-D36Bg2ut.js} +1 -1
- package/src/assets/web-panel/assets/{slide-CpnsUMaT.js → slide-CUuO08Bl.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-C7x-TVp_.js → statusUtils-BKahwLbv.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-CC5k_791.js → styleChecker-2pT-T8vy.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-BpZFKsJW.js → useFlexGapSupport-DyH5g7Rg.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-CVq5HySy.js → useFs-CTOauB3X.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-BGkdLryu.js → usePersonalDataHub-B02JYIMe.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-uLtOUEK-.js → vnode-Do22wQrc.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-BfVC9iUs.js → zoom-BfnL7rit.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 +8 -0
- package/src/commands/background-session.js +31 -3
- package/src/commands/chat.js +5 -1
- package/src/commands/eval.js +86 -28
- package/src/commands/llm.js +104 -1
- package/src/commands/plugin.js +161 -1
- package/src/commands/sandbox.js +121 -2
- package/src/commands/serve.js +11 -2
- package/src/commands/skill.js +29 -0
- package/src/data/changelog.json +26 -0
- package/src/gateways/ws/session-protocol.js +13 -0
- package/src/gateways/ws/ws-agent-handler.js +17 -0
- package/src/gateways/ws/ws-server.js +3 -0
- package/src/gateways/ws/ws-session-state.js +131 -0
- package/src/generated/cc-agent-protocol.schema.json +48 -1
- package/src/lazy-dispatch.js +8 -1
- package/src/lib/agent-sandbox.js +337 -53
- package/src/lib/app-server/cli-agent-kernel-adapter.js +28 -0
- package/src/lib/app-server/server.js +93 -0
- package/src/lib/background-agent-keeper-client.js +2 -0
- package/src/lib/background-agent-keeper-protocol.js +7 -0
- package/src/lib/background-agent-list-index.js +222 -0
- package/src/lib/background-agent-supervisor.js +232 -47
- package/src/lib/codex-app-server-adapter.js +82 -27
- package/src/lib/context-memory-kernel/durable-memory-port.js +31 -8
- package/src/lib/context-memory-kernel/provider-context.js +118 -19
- package/src/lib/deferred-question-context.js +278 -0
- package/src/lib/eval/evidence.js +202 -64
- package/src/lib/eval/plugin-suite.js +1181 -0
- package/src/lib/eval/runner.js +10 -3
- package/src/lib/evolution/agent-evolution-runtime-composition.js +236 -1
- package/src/lib/evolution/agent-model-projection.js +97 -1
- package/src/lib/evolution/evidence-backed-wiki-maintainer.js +154 -1
- package/src/lib/evolution/evolution-artifact-ports.js +6 -0
- package/src/lib/evolution/evolution-deployment-loader.js +87 -3
- package/src/lib/evolution/evolution-eval-gate.js +8 -0
- package/src/lib/evolution/evolution-eval-runtime-composition.js +255 -0
- package/src/lib/evolution/evolution-evidence-artifact-adapter.js +58 -2
- package/src/lib/evolution/evolution-ledger-file-backend.js +23 -2
- package/src/lib/evolution/evolution-ledger-manifest-chain.js +223 -7
- package/src/lib/evolution/evolution-ledger-v2-journal.js +525 -0
- package/src/lib/evolution/evolution-ledger-v2-manifest-backend.js +46 -2
- package/src/lib/evolution/evolution-ledger.js +57 -1
- package/src/lib/evolution/evolution-run-ledger-adapter.js +93 -0
- package/src/lib/evolution/evolution-run-wiki-evidence-resolver.js +259 -0
- package/src/lib/evolution/governed-hub-llm.js +238 -5
- package/src/lib/evolution/governed-hub-resolver.js +82 -4
- package/src/lib/evolution/governed-hub-skill.js +11 -5
- package/src/lib/evolution/governed-skill-synthesis-provider-chat.js +10 -1
- package/src/lib/evolution/skill-runtime-revalidation-ledger-adapter.js +254 -0
- package/src/lib/evolution/skill-runtime-revalidation.js +530 -0
- package/src/lib/evolution/skill-target-matrix-eval.js +4 -4
- package/src/lib/evolution/wiki-evidence-commit-guard.js +366 -0
- package/src/lib/evolution/wiki-informed-skill-proposer.js +13 -1
- package/src/lib/goal-context.js +14 -5
- package/src/lib/interaction-adapter.js +166 -1
- package/src/lib/model-capabilities.js +271 -0
- package/src/lib/model-context-catalog.js +105 -0
- package/src/lib/model-context-window.js +4 -49
- package/src/lib/openai-responses.js +428 -0
- package/src/lib/plugin-runtime/scopes.js +7 -1
- package/src/lib/remote-read-loop-guard.js +2 -2
- package/src/lib/runtime-usage-ledger.js +16 -0
- package/src/lib/session-host-snapshot.js +16 -0
- package/src/lib/skill-loader.js +12 -0
- package/src/lib/with-file-lock.js +3 -1
- package/src/repl/agent-repl.js +5 -1
- package/src/runtime/agent-core.js +348 -23
- package/src/runtime/agent-runtime.js +7 -0
- package/src/runtime/coding-agent-contract-shared.cjs +13 -1
- package/src/runtime/headless-runner.js +4 -1
- package/src/runtime/headless-stream.js +283 -45
- package/src/runtime/runtime-factory.js +26 -2
- package/src/workers/background-agent-keeper.js +23 -13
- package/src/workers/background-agent-worker.js +27 -13
- package/src/assets/web-panel/assets/ChatBubbleRenderer-Bi2SY9E_.js +0 -1
- package/src/assets/web-panel/assets/PdhVaultBrowser-fYmQzmTA.js +0 -7
- package/src/assets/web-panel/assets/devWarning-BtFLt3qI.js +0 -1
- package/src/assets/web-panel/assets/index-BhuWQHeG.js +0 -1
- package/src/assets/web-panel/assets/index-D-4tFek3.js +0 -1
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { types } from "node:util";
|
|
3
|
+
import {
|
|
4
|
+
captureWikiRevisionReader,
|
|
5
|
+
WikiMaintainerLedgerAdapter,
|
|
6
|
+
} from "./wiki-maintainer-ledger-adapter.js";
|
|
7
|
+
import {
|
|
8
|
+
digestWikiState,
|
|
9
|
+
WIKI_EVIDENCE_SCHEMA,
|
|
10
|
+
WIKI_PATTERN_STATUS,
|
|
11
|
+
} from "./evidence-backed-wiki-maintainer.js";
|
|
12
|
+
import { verifyWikiRevision } from "./wiki-revision-protocol.js";
|
|
13
|
+
|
|
14
|
+
export const WIKI_EVIDENCE_COMMIT_REQUEST_SCHEMA =
|
|
15
|
+
"chainlesschain.wiki-evidence-commit-request/v1";
|
|
16
|
+
export const WIKI_EVIDENCE_COMMIT_LEASE_SCHEMA =
|
|
17
|
+
"chainlesschain.wiki-evidence-commit-lease/v1";
|
|
18
|
+
export const WIKI_EVIDENCE_COMMIT_DENIED_CODE =
|
|
19
|
+
"CC_WIKI_EVIDENCE_COMMIT_DENIED";
|
|
20
|
+
export const WIKI_EVIDENCE_COMMIT_RELEASE_FAILED_CODE =
|
|
21
|
+
"CC_WIKI_EVIDENCE_COMMIT_RELEASE_FAILED";
|
|
22
|
+
const DIGEST = /^sha256:[a-f0-9]{64}$/u;
|
|
23
|
+
const USED_LEASES = new WeakSet();
|
|
24
|
+
const TERMINAL = new Set([
|
|
25
|
+
WIKI_PATTERN_STATUS.QUARANTINED,
|
|
26
|
+
WIKI_PATTERN_STATUS.REVOKED,
|
|
27
|
+
WIKI_PATTERN_STATUS.TOMBSTONED,
|
|
28
|
+
]);
|
|
29
|
+
const EVIDENCE_KEYS = [
|
|
30
|
+
"schema",
|
|
31
|
+
"tenantId",
|
|
32
|
+
"ref",
|
|
33
|
+
"sourceDigest",
|
|
34
|
+
"projectionDigest",
|
|
35
|
+
"artifactRef",
|
|
36
|
+
"trustedProjection",
|
|
37
|
+
"trustDomain",
|
|
38
|
+
"kind",
|
|
39
|
+
"status",
|
|
40
|
+
"observedAt",
|
|
41
|
+
"expiresAt",
|
|
42
|
+
"data",
|
|
43
|
+
];
|
|
44
|
+
const LEASE_KEYS = [
|
|
45
|
+
"schema",
|
|
46
|
+
"tenantId",
|
|
47
|
+
"runId",
|
|
48
|
+
"requestDigest",
|
|
49
|
+
"evidenceBindingDigest",
|
|
50
|
+
"leaseId",
|
|
51
|
+
"assertCurrent",
|
|
52
|
+
"release",
|
|
53
|
+
];
|
|
54
|
+
function denied(message, cause) {
|
|
55
|
+
const error = new Error(message, cause === undefined ? undefined : { cause });
|
|
56
|
+
error.code = WIKI_EVIDENCE_COMMIT_DENIED_CODE;
|
|
57
|
+
return error;
|
|
58
|
+
}
|
|
59
|
+
function record(value, label, keys) {
|
|
60
|
+
if (
|
|
61
|
+
!value ||
|
|
62
|
+
typeof value !== "object" ||
|
|
63
|
+
types.isProxy(value) ||
|
|
64
|
+
![Object.prototype, null].includes(Object.getPrototypeOf(value))
|
|
65
|
+
)
|
|
66
|
+
throw denied(`${label} must be a plain non-Proxy record`);
|
|
67
|
+
const own = Reflect.ownKeys(value);
|
|
68
|
+
if (
|
|
69
|
+
(keys &&
|
|
70
|
+
(own.length !== keys.length || own.some((key) => !keys.includes(key)))) ||
|
|
71
|
+
own.some(
|
|
72
|
+
(key) =>
|
|
73
|
+
typeof key !== "string" ||
|
|
74
|
+
!Object.hasOwn(Object.getOwnPropertyDescriptor(value, key), "value"),
|
|
75
|
+
)
|
|
76
|
+
)
|
|
77
|
+
throw denied(`${label} must contain only exact own data fields`);
|
|
78
|
+
return value;
|
|
79
|
+
}
|
|
80
|
+
function syncMethod(value, name, label) {
|
|
81
|
+
const method = Object.getOwnPropertyDescriptor(value, name)?.value;
|
|
82
|
+
if (
|
|
83
|
+
typeof method !== "function" ||
|
|
84
|
+
types.isProxy(method) ||
|
|
85
|
+
types.isAsyncFunction(method) ||
|
|
86
|
+
types.isGeneratorFunction(method)
|
|
87
|
+
)
|
|
88
|
+
throw denied(`${label}.${name} must be synchronous`);
|
|
89
|
+
return (...args) => Reflect.apply(method, value, args);
|
|
90
|
+
}
|
|
91
|
+
// Copy before calling any host authority. No getters, toJSON, custom prototypes,
|
|
92
|
+
// proxies, sparse arrays or mutable aliases survive into the publish request.
|
|
93
|
+
function snapshot(value, budget = { nodes: 0, bytes: 0 }, depth = 0) {
|
|
94
|
+
if (++budget.nodes > 100_000 || depth > 32)
|
|
95
|
+
throw denied("Wiki revision exceeds its structural budget");
|
|
96
|
+
if (value === null || typeof value === "boolean") return value;
|
|
97
|
+
if (typeof value === "string") {
|
|
98
|
+
budget.bytes += Buffer.byteLength(value, "utf8");
|
|
99
|
+
if (budget.bytes > 1_048_576)
|
|
100
|
+
throw denied("Wiki revision strings exceed their byte budget");
|
|
101
|
+
return value;
|
|
102
|
+
}
|
|
103
|
+
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
104
|
+
if (typeof value !== "object" || types.isProxy(value))
|
|
105
|
+
throw denied("Wiki revision is not plain data");
|
|
106
|
+
if (Array.isArray(value)) {
|
|
107
|
+
if (
|
|
108
|
+
Object.getPrototypeOf(value) !== Array.prototype ||
|
|
109
|
+
value.length > 100_000 ||
|
|
110
|
+
Reflect.ownKeys(value).length !== value.length + 1
|
|
111
|
+
)
|
|
112
|
+
throw denied("Wiki revision array is not dense and bounded");
|
|
113
|
+
const result = [];
|
|
114
|
+
for (let i = 0; i < value.length; i++) {
|
|
115
|
+
const entry = Object.getOwnPropertyDescriptor(value, String(i));
|
|
116
|
+
if (!entry || !Object.hasOwn(entry, "value"))
|
|
117
|
+
throw denied("Wiki revision array contains an accessor or hole");
|
|
118
|
+
result.push(snapshot(entry.value, budget, depth + 1));
|
|
119
|
+
}
|
|
120
|
+
return Object.freeze(result);
|
|
121
|
+
}
|
|
122
|
+
record(value, "Wiki revision record");
|
|
123
|
+
return Object.freeze(
|
|
124
|
+
Object.fromEntries(
|
|
125
|
+
Object.keys(value).map((key) => [
|
|
126
|
+
key,
|
|
127
|
+
snapshot(
|
|
128
|
+
Object.getOwnPropertyDescriptor(value, key).value,
|
|
129
|
+
budget,
|
|
130
|
+
depth + 1,
|
|
131
|
+
),
|
|
132
|
+
]),
|
|
133
|
+
),
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
function bindingsFor(revision) {
|
|
137
|
+
const state = revision.state;
|
|
138
|
+
record(state.evidence, "Wiki evidence map");
|
|
139
|
+
record(state.patterns, "Wiki pattern map");
|
|
140
|
+
const refs = new Set();
|
|
141
|
+
const add = (ref) => {
|
|
142
|
+
if (typeof ref !== "string" || !ref || !Object.hasOwn(state.evidence, ref))
|
|
143
|
+
throw denied("Wiki dependency has no bound evidence");
|
|
144
|
+
refs.add(ref);
|
|
145
|
+
if (refs.size > 256)
|
|
146
|
+
throw denied(
|
|
147
|
+
"Wiki active dependencies exceed 256; none may be truncated",
|
|
148
|
+
);
|
|
149
|
+
};
|
|
150
|
+
// Old evidence can be reused by upsert or merged into an existing pattern.
|
|
151
|
+
// Therefore scope is the OUTPUT dependencies, not just this request's refs.
|
|
152
|
+
for (const pattern of Object.values(state.patterns)) {
|
|
153
|
+
if (TERMINAL.has(pattern.status)) continue;
|
|
154
|
+
if (!Object.values(WIKI_PATTERN_STATUS).includes(pattern.status))
|
|
155
|
+
throw denied("Wiki pattern status is invalid");
|
|
156
|
+
for (const field of ["positiveEvidence", "negativeEvidence"]) {
|
|
157
|
+
if (!Array.isArray(pattern[field]))
|
|
158
|
+
throw denied("Wiki pattern dependencies are invalid");
|
|
159
|
+
for (const ref of pattern[field]) add(ref);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
// Newly read active evidence is also admitted even when no pattern is made.
|
|
163
|
+
// Terminal cleanup may retain old tombstones without reauthorizing learning.
|
|
164
|
+
for (const ref of revision.evidenceRefs) {
|
|
165
|
+
const item = state.evidence[ref];
|
|
166
|
+
if (!item || !["active", "revoked", "deleted"].includes(item.status))
|
|
167
|
+
throw denied("Wiki request evidence status is invalid");
|
|
168
|
+
if (item.status === "active") add(ref);
|
|
169
|
+
}
|
|
170
|
+
return Object.freeze(
|
|
171
|
+
[...refs].sort().map((ref) => {
|
|
172
|
+
const item = record(
|
|
173
|
+
state.evidence[ref],
|
|
174
|
+
"Wiki evidence binding",
|
|
175
|
+
EVIDENCE_KEYS,
|
|
176
|
+
);
|
|
177
|
+
if (
|
|
178
|
+
item.schema !== WIKI_EVIDENCE_SCHEMA ||
|
|
179
|
+
item.tenantId !== revision.tenantId ||
|
|
180
|
+
item.ref !== ref ||
|
|
181
|
+
item.trustedProjection !== true ||
|
|
182
|
+
item.status !== "active" ||
|
|
183
|
+
!DIGEST.test(item.sourceDigest) ||
|
|
184
|
+
!DIGEST.test(item.projectionDigest) ||
|
|
185
|
+
typeof item.artifactRef !== "string" ||
|
|
186
|
+
!item.artifactRef ||
|
|
187
|
+
typeof item.trustDomain !== "string" ||
|
|
188
|
+
!item.trustDomain ||
|
|
189
|
+
!["tool-observation", "grader-receipt"].includes(item.kind) ||
|
|
190
|
+
!Number.isFinite(Date.parse(item.observedAt)) ||
|
|
191
|
+
!Number.isFinite(Date.parse(item.expiresAt))
|
|
192
|
+
)
|
|
193
|
+
throw denied(
|
|
194
|
+
"Wiki active dependency is not authenticated typed evidence",
|
|
195
|
+
);
|
|
196
|
+
record(item.data, "Wiki trusted metadata");
|
|
197
|
+
for (const key of [
|
|
198
|
+
"rawRecordDigest",
|
|
199
|
+
"manifestDigest",
|
|
200
|
+
"projectionReceiptDigest",
|
|
201
|
+
"schemaDigest",
|
|
202
|
+
])
|
|
203
|
+
if (!DIGEST.test(item.data[key]))
|
|
204
|
+
throw denied("Wiki evidence identity digest is missing");
|
|
205
|
+
return item;
|
|
206
|
+
}),
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Explicit host trust boundary. acquireCurrentEvidence MUST synchronously hold
|
|
212
|
+
* a cross-evidence state/principal/ACL lock (or irrevocable scoped grant) until
|
|
213
|
+
* release. A timestamp/read receipt alone is not that contract. Deployments must
|
|
214
|
+
* prove the coordinator's cross-process semantics; this wrapper does not defend
|
|
215
|
+
* against a malicious authority. The authority NEVER receives publish authority.
|
|
216
|
+
*/
|
|
217
|
+
export function createWikiEvidenceCommitGuard({
|
|
218
|
+
wikiAdapter,
|
|
219
|
+
commitCoordinator,
|
|
220
|
+
principalEnvelope,
|
|
221
|
+
clock = Date.now,
|
|
222
|
+
} = {}) {
|
|
223
|
+
if (
|
|
224
|
+
!wikiAdapter ||
|
|
225
|
+
types.isProxy(wikiAdapter) ||
|
|
226
|
+
Object.getPrototypeOf(wikiAdapter) !== WikiMaintainerLedgerAdapter.prototype
|
|
227
|
+
)
|
|
228
|
+
throw denied("a genuine production Wiki adapter is required");
|
|
229
|
+
const reader = captureWikiRevisionReader(wikiAdapter);
|
|
230
|
+
const publish = syncMethod(wikiAdapter, "commitRevision", "Wiki adapter");
|
|
231
|
+
record(commitCoordinator, "Wiki commit coordinator", [
|
|
232
|
+
"acquireCurrentEvidence",
|
|
233
|
+
]);
|
|
234
|
+
const acquire = syncMethod(
|
|
235
|
+
commitCoordinator,
|
|
236
|
+
"acquireCurrentEvidence",
|
|
237
|
+
"Wiki commit coordinator",
|
|
238
|
+
);
|
|
239
|
+
if (
|
|
240
|
+
typeof principalEnvelope !== "string" ||
|
|
241
|
+
!principalEnvelope ||
|
|
242
|
+
principalEnvelope.length > 65_536 ||
|
|
243
|
+
typeof clock !== "function" ||
|
|
244
|
+
types.isProxy(clock) ||
|
|
245
|
+
types.isAsyncFunction(clock)
|
|
246
|
+
)
|
|
247
|
+
throw denied("Wiki principal envelope and synchronous clock are required");
|
|
248
|
+
let committing = false;
|
|
249
|
+
return Object.freeze({
|
|
250
|
+
commitRevision(input) {
|
|
251
|
+
if (committing) throw denied("Wiki publication is not reentrant");
|
|
252
|
+
const owned = snapshot(input);
|
|
253
|
+
record(
|
|
254
|
+
owned,
|
|
255
|
+
"Wiki commit input",
|
|
256
|
+
Object.hasOwn(owned, "expectedLedgerHead")
|
|
257
|
+
? ["expectedStateDigest", "revision", "expectedLedgerHead"]
|
|
258
|
+
: ["expectedStateDigest", "revision"],
|
|
259
|
+
);
|
|
260
|
+
const revision = verifyWikiRevision(owned.revision, reader.descriptor);
|
|
261
|
+
if (
|
|
262
|
+
!DIGEST.test(owned.expectedStateDigest) ||
|
|
263
|
+
owned.expectedStateDigest !== revision.priorStateDigest
|
|
264
|
+
)
|
|
265
|
+
throw denied("Wiki prior state is not bound to the publication");
|
|
266
|
+
const evidenceBindings = bindingsFor(revision);
|
|
267
|
+
const requestedAt = new Date(clock()).toISOString();
|
|
268
|
+
if (
|
|
269
|
+
evidenceBindings.some(
|
|
270
|
+
(item) => Date.parse(item.expiresAt) <= Date.parse(requestedAt),
|
|
271
|
+
)
|
|
272
|
+
)
|
|
273
|
+
throw denied("Wiki evidence expired before lease acquisition");
|
|
274
|
+
const evidenceBindingDigest = digestWikiState({
|
|
275
|
+
domain: "chainlesschain.wiki-evidence-bindings/v1",
|
|
276
|
+
tenantId: reader.descriptor.tenantId,
|
|
277
|
+
runId: reader.descriptor.evolutionRunId,
|
|
278
|
+
evidenceBindings,
|
|
279
|
+
});
|
|
280
|
+
const core = Object.freeze({
|
|
281
|
+
schema: WIKI_EVIDENCE_COMMIT_REQUEST_SCHEMA,
|
|
282
|
+
tenantId: reader.descriptor.tenantId,
|
|
283
|
+
runId: reader.descriptor.evolutionRunId,
|
|
284
|
+
principalEnvelope,
|
|
285
|
+
expectedStateDigest: owned.expectedStateDigest,
|
|
286
|
+
revisionId: revision.revisionId,
|
|
287
|
+
stateDigest: revision.stateDigest,
|
|
288
|
+
evidenceRefs: revision.evidenceRefs,
|
|
289
|
+
evidenceBindings,
|
|
290
|
+
evidenceBindingDigest,
|
|
291
|
+
requestNonce: randomUUID(),
|
|
292
|
+
requestedAt,
|
|
293
|
+
});
|
|
294
|
+
const request = Object.freeze({
|
|
295
|
+
...core,
|
|
296
|
+
requestDigest: digestWikiState(core),
|
|
297
|
+
});
|
|
298
|
+
let release;
|
|
299
|
+
let receipt;
|
|
300
|
+
let failure;
|
|
301
|
+
committing = true;
|
|
302
|
+
try {
|
|
303
|
+
const lease = acquire(request);
|
|
304
|
+
record(lease, "Wiki commit lease", LEASE_KEYS);
|
|
305
|
+
if (!Object.isFrozen(lease))
|
|
306
|
+
throw denied("Wiki commit lease must be immutable");
|
|
307
|
+
if (USED_LEASES.has(lease))
|
|
308
|
+
throw denied("Wiki commit lease cannot be reused");
|
|
309
|
+
USED_LEASES.add(lease);
|
|
310
|
+
release = syncMethod(lease, "release", "Wiki commit lease");
|
|
311
|
+
const assertCurrent = syncMethod(
|
|
312
|
+
lease,
|
|
313
|
+
"assertCurrent",
|
|
314
|
+
"Wiki commit lease",
|
|
315
|
+
);
|
|
316
|
+
if (
|
|
317
|
+
lease.schema !== WIKI_EVIDENCE_COMMIT_LEASE_SCHEMA ||
|
|
318
|
+
lease.tenantId !== request.tenantId ||
|
|
319
|
+
lease.runId !== request.runId ||
|
|
320
|
+
lease.requestDigest !== request.requestDigest ||
|
|
321
|
+
lease.evidenceBindingDigest !== request.evidenceBindingDigest ||
|
|
322
|
+
typeof lease.leaseId !== "string" ||
|
|
323
|
+
!lease.leaseId ||
|
|
324
|
+
lease.leaseId.length > 256
|
|
325
|
+
)
|
|
326
|
+
throw denied("Wiki commit lease is not bound to the entire request");
|
|
327
|
+
if (assertCurrent() !== true)
|
|
328
|
+
throw denied("Wiki commit lease is not currently held");
|
|
329
|
+
// No await or host-supplied publish callback between lease and real write.
|
|
330
|
+
receipt = publish(owned);
|
|
331
|
+
} catch (error) {
|
|
332
|
+
failure = error?.code
|
|
333
|
+
? error
|
|
334
|
+
: denied("Wiki evidence commit authorization failed", error);
|
|
335
|
+
} finally {
|
|
336
|
+
let releaseFailure;
|
|
337
|
+
try {
|
|
338
|
+
if (release && release() !== undefined)
|
|
339
|
+
releaseFailure = new Error(
|
|
340
|
+
"Wiki commit lease release must be synchronous and return undefined",
|
|
341
|
+
);
|
|
342
|
+
} catch (error) {
|
|
343
|
+
releaseFailure = error;
|
|
344
|
+
}
|
|
345
|
+
if (releaseFailure) {
|
|
346
|
+
if (receipt?.committed === true) {
|
|
347
|
+
failure = new Error(
|
|
348
|
+
"Wiki was committed but its authorization lease failed to release",
|
|
349
|
+
{ cause: releaseFailure },
|
|
350
|
+
);
|
|
351
|
+
failure.code = WIKI_EVIDENCE_COMMIT_RELEASE_FAILED_CODE;
|
|
352
|
+
failure.committed = true;
|
|
353
|
+
failure.receipt = receipt;
|
|
354
|
+
} else if (!failure)
|
|
355
|
+
failure = denied(
|
|
356
|
+
"Wiki commit lease failed to release",
|
|
357
|
+
releaseFailure,
|
|
358
|
+
);
|
|
359
|
+
}
|
|
360
|
+
committing = false;
|
|
361
|
+
}
|
|
362
|
+
if (failure) throw failure;
|
|
363
|
+
return receipt;
|
|
364
|
+
},
|
|
365
|
+
});
|
|
366
|
+
}
|
|
@@ -2,6 +2,8 @@ import { createHash } from "node:crypto";
|
|
|
2
2
|
|
|
3
3
|
export const WIKI_SKILL_PROPOSAL_SCHEMA =
|
|
4
4
|
"chainlesschain.wiki-informed-skill-proposal/v2";
|
|
5
|
+
export const WIKI_SKILL_CANDIDATE_CONTENT_SCHEMA =
|
|
6
|
+
"chainlesschain.wiki-informed-skill-candidate-content/v1";
|
|
5
7
|
export const WIKI_PROPOSAL_STATUS = Object.freeze({
|
|
6
8
|
PROPOSAL: "proposal",
|
|
7
9
|
NO_PROPOSAL: "no-proposal",
|
|
@@ -42,6 +44,16 @@ export function computeWikiSkillProposalDigest(proposal) {
|
|
|
42
44
|
return hash(proposal);
|
|
43
45
|
}
|
|
44
46
|
|
|
47
|
+
function candidateContent(proposal) {
|
|
48
|
+
// Evidence digests belong to the candidate's ID-bound metadata, not its
|
|
49
|
+
// plaintext body. Keep the full proposal (and its independent digest) intact.
|
|
50
|
+
// This is a representation change, not a schema-based secret-scan exemption:
|
|
51
|
+
// PURPOSE references, machineDiff and all other body fields remain scanned.
|
|
52
|
+
const body = { ...proposal, schema: WIKI_SKILL_CANDIDATE_CONTENT_SCHEMA };
|
|
53
|
+
delete body.sourceEvidenceRefs;
|
|
54
|
+
return canonical(body);
|
|
55
|
+
}
|
|
56
|
+
|
|
45
57
|
function deepFreeze(value) {
|
|
46
58
|
if (value && typeof value === "object" && !Object.isFrozen(value)) {
|
|
47
59
|
Object.freeze(value);
|
|
@@ -408,7 +420,7 @@ export class WikiInformedSkillProposer {
|
|
|
408
420
|
proposerModel: proposal.proposerModel,
|
|
409
421
|
requestedCapabilities: proposal.requestedCapabilities,
|
|
410
422
|
sourceEvidenceRefs: proposal.sourceEvidenceRefs,
|
|
411
|
-
content:
|
|
423
|
+
content: candidateContent(proposal),
|
|
412
424
|
});
|
|
413
425
|
const created = await this._ports.createCandidate(candidateInput);
|
|
414
426
|
const candidate = created?.candidate;
|
package/src/lib/goal-context.js
CHANGED
|
@@ -63,25 +63,34 @@ export function goalPrepareCall(goal) {
|
|
|
63
63
|
|
|
64
64
|
/**
|
|
65
65
|
* Compose several `prepareCall` functions into one. Each is invoked per turn
|
|
66
|
-
* and their non-empty
|
|
67
|
-
*
|
|
66
|
+
* and their non-empty transient system/user contexts are concatenated without
|
|
67
|
+
* changing authority: `systemSuffix` stays system, `userContext` stays user.
|
|
68
|
+
* A failing member is skipped, never fatal.
|
|
68
69
|
* @param {Array<Function|null|undefined>} fns
|
|
69
70
|
* @returns {(ctx:object) => Promise<{systemSuffix:string}|null>}
|
|
70
71
|
*/
|
|
71
72
|
export function composePrepareCall(fns) {
|
|
72
73
|
const list = (fns || []).filter((f) => typeof f === "function");
|
|
73
74
|
return async (ctx) => {
|
|
74
|
-
const
|
|
75
|
+
const systemParts = [];
|
|
76
|
+
const userParts = [];
|
|
75
77
|
for (const fn of list) {
|
|
76
78
|
try {
|
|
77
79
|
const r = await fn(ctx);
|
|
78
80
|
if (r && typeof r.systemSuffix === "string" && r.systemSuffix.trim()) {
|
|
79
|
-
|
|
81
|
+
systemParts.push(r.systemSuffix);
|
|
82
|
+
}
|
|
83
|
+
if (r && typeof r.userContext === "string" && r.userContext.trim()) {
|
|
84
|
+
userParts.push(r.userContext);
|
|
80
85
|
}
|
|
81
86
|
} catch {
|
|
82
87
|
/* a failing member must not break the turn */
|
|
83
88
|
}
|
|
84
89
|
}
|
|
85
|
-
|
|
90
|
+
if (systemParts.length === 0 && userParts.length === 0) return null;
|
|
91
|
+
return {
|
|
92
|
+
...(systemParts.length ? { systemSuffix: systemParts.join("\n\n") } : {}),
|
|
93
|
+
...(userParts.length ? { userContext: userParts.join("\n\n") } : {}),
|
|
94
|
+
};
|
|
86
95
|
};
|
|
87
96
|
}
|
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
normalizeInteractionBinding,
|
|
21
21
|
sameInteractionBinding,
|
|
22
22
|
} from "./interaction-binding.js";
|
|
23
|
+
import { DeferredQuestionContext } from "./deferred-question-context.js";
|
|
23
24
|
import { createEnvelope } from "@chainlesschain/session-core";
|
|
24
25
|
|
|
25
26
|
// Phase 5: parallel service-envelope emission. Map WS agent-handler event
|
|
@@ -133,6 +134,35 @@ export class WebSocketInteractionAdapter extends InteractionAdapter {
|
|
|
133
134
|
// global default tracker.
|
|
134
135
|
this._sequenceTracker = new CodingAgentSequenceTracker();
|
|
135
136
|
this._questionSequence = 0;
|
|
137
|
+
const deferredState =
|
|
138
|
+
options.deferredState && typeof options.deferredState === "object"
|
|
139
|
+
? options.deferredState
|
|
140
|
+
: {};
|
|
141
|
+
this._contextRevision = Number.isSafeInteger(deferredState.contextRevision)
|
|
142
|
+
? deferredState.contextRevision
|
|
143
|
+
: 0;
|
|
144
|
+
this.onDeferredQuestionChange =
|
|
145
|
+
typeof options.onDeferredQuestionChange === "function"
|
|
146
|
+
? options.onDeferredQuestionChange
|
|
147
|
+
: null;
|
|
148
|
+
this._deferredQuestionContext = new DeferredQuestionContext({
|
|
149
|
+
sessionId: this.sessionId,
|
|
150
|
+
initialAnswers: Array.isArray(deferredState.deferredAnswers)
|
|
151
|
+
? deferredState.deferredAnswers.map((answer) => ({
|
|
152
|
+
...answer,
|
|
153
|
+
questionId: answer.questionId || answer.requestId,
|
|
154
|
+
}))
|
|
155
|
+
: [],
|
|
156
|
+
onConsumed: (questionIds) =>
|
|
157
|
+
this._notifyDeferredQuestion("question.deferred.consumed", {
|
|
158
|
+
questionIds,
|
|
159
|
+
}),
|
|
160
|
+
});
|
|
161
|
+
for (const pending of Array.isArray(deferredState.deferredQuestions)
|
|
162
|
+
? deferredState.deferredQuestions
|
|
163
|
+
: []) {
|
|
164
|
+
this._restoreDeferredQuestion(pending);
|
|
165
|
+
}
|
|
136
166
|
// Phase 5: parallel service-envelope emission. Opt-in (default off) so
|
|
137
167
|
// legacy callers that count ws.send invocations stay green.
|
|
138
168
|
this.enablePhase5Envelopes = options.enablePhase5Envelopes === true;
|
|
@@ -155,6 +185,54 @@ export class WebSocketInteractionAdapter extends InteractionAdapter {
|
|
|
155
185
|
}
|
|
156
186
|
}
|
|
157
187
|
|
|
188
|
+
_notifyDeferredQuestion(type, payload) {
|
|
189
|
+
if (!this.onDeferredQuestionChange) return;
|
|
190
|
+
try {
|
|
191
|
+
this.onDeferredQuestionChange({ type, payload });
|
|
192
|
+
} catch {
|
|
193
|
+
// The live interaction remains available; the host journal owns retry.
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
_restoreDeferredQuestion(record) {
|
|
198
|
+
const requestId = String(record?.requestId || "").trim();
|
|
199
|
+
const binding = normalizeInteractionBinding(record?.binding);
|
|
200
|
+
if (!requestId || binding.sessionId !== String(this.sessionId)) return;
|
|
201
|
+
this._questionSequence = Math.max(
|
|
202
|
+
this._questionSequence,
|
|
203
|
+
Number.isSafeInteger(binding.sequence) ? binding.sequence : 0,
|
|
204
|
+
);
|
|
205
|
+
this._pending.set(requestId, {
|
|
206
|
+
resolve: (answer) => {
|
|
207
|
+
const resolvedRevision = this._contextRevision;
|
|
208
|
+
this._deferredQuestionContext.record({
|
|
209
|
+
questionId: requestId,
|
|
210
|
+
question: record.question,
|
|
211
|
+
answer,
|
|
212
|
+
binding,
|
|
213
|
+
requestedRevision: record.contextRevision,
|
|
214
|
+
resolvedRevision,
|
|
215
|
+
});
|
|
216
|
+
this._notifyDeferredQuestion("question.deferred.resolved", {
|
|
217
|
+
requestId,
|
|
218
|
+
question: record.question,
|
|
219
|
+
answer,
|
|
220
|
+
binding,
|
|
221
|
+
requestedRevision: record.contextRevision,
|
|
222
|
+
resolvedRevision,
|
|
223
|
+
});
|
|
224
|
+
},
|
|
225
|
+
reject: () => {},
|
|
226
|
+
timeoutId: null,
|
|
227
|
+
kind: "question",
|
|
228
|
+
approvalRequest: false,
|
|
229
|
+
binding,
|
|
230
|
+
bindingType: "interaction",
|
|
231
|
+
requireBinding: true,
|
|
232
|
+
deferred: true,
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
|
|
158
236
|
/** Generate a unique request id */
|
|
159
237
|
_requestId() {
|
|
160
238
|
return `q-${Date.now()}-${createHash("sha256").update(Math.random().toString()).digest("hex").slice(0, 6)}`;
|
|
@@ -207,6 +285,10 @@ export class WebSocketInteractionAdapter extends InteractionAdapter {
|
|
|
207
285
|
bindingType: options.bindingType || null,
|
|
208
286
|
requireBinding: options.requireBinding === true,
|
|
209
287
|
});
|
|
288
|
+
options.onRegistered?.({
|
|
289
|
+
requestId,
|
|
290
|
+
binding: options.binding || null,
|
|
291
|
+
});
|
|
210
292
|
|
|
211
293
|
if (approvalRequest) {
|
|
212
294
|
this._notifyPendingApproval("approval.requested", {
|
|
@@ -337,7 +419,7 @@ export class WebSocketInteractionAdapter extends InteractionAdapter {
|
|
|
337
419
|
* elicitation, while exposing the stream protocol's `question_request`
|
|
338
420
|
* shape so capable UIs can render options natively.
|
|
339
421
|
*/
|
|
340
|
-
|
|
422
|
+
askUser({
|
|
341
423
|
question,
|
|
342
424
|
options = null,
|
|
343
425
|
multiSelect = false,
|
|
@@ -348,6 +430,9 @@ export class WebSocketInteractionAdapter extends InteractionAdapter {
|
|
|
348
430
|
turnId,
|
|
349
431
|
toolUseId,
|
|
350
432
|
metadata = null,
|
|
433
|
+
mode = "blocking",
|
|
434
|
+
purpose = "decision",
|
|
435
|
+
onRegistered = null,
|
|
351
436
|
} = {}) {
|
|
352
437
|
const normalizedTurnId =
|
|
353
438
|
turnId === undefined || turnId === null ? null : String(turnId);
|
|
@@ -380,6 +465,9 @@ export class WebSocketInteractionAdapter extends InteractionAdapter {
|
|
|
380
465
|
question: typeof question === "string" ? question : "",
|
|
381
466
|
options: Array.isArray(options) ? options : null,
|
|
382
467
|
multiSelect: multiSelect === true,
|
|
468
|
+
mode,
|
|
469
|
+
blocking: mode !== "deferred",
|
|
470
|
+
purpose,
|
|
383
471
|
...(defaultValue !== undefined ? { defaultValue } : {}),
|
|
384
472
|
...(onTimeout ? { onTimeout } : {}),
|
|
385
473
|
...(onReject ? { onReject } : {}),
|
|
@@ -401,10 +489,84 @@ export class WebSocketInteractionAdapter extends InteractionAdapter {
|
|
|
401
489
|
binding,
|
|
402
490
|
bindingType: "interaction",
|
|
403
491
|
...(Number(timeoutMs) > 0 ? { timeoutMs: Number(timeoutMs) } : {}),
|
|
492
|
+
...(typeof onRegistered === "function" ? { onRegistered } : {}),
|
|
404
493
|
},
|
|
405
494
|
);
|
|
406
495
|
}
|
|
407
496
|
|
|
497
|
+
/**
|
|
498
|
+
* Register an informational question without blocking the current agent loop.
|
|
499
|
+
* Its answer is retained as one-shot, user-authority context for prepareCall.
|
|
500
|
+
*/
|
|
501
|
+
deferUserQuestion(request = {}) {
|
|
502
|
+
let registration = null;
|
|
503
|
+
const requestedRevision = this._contextRevision;
|
|
504
|
+
const answer = this.askUser({
|
|
505
|
+
...request,
|
|
506
|
+
mode: "deferred",
|
|
507
|
+
onRegistered: (value) => {
|
|
508
|
+
registration = value;
|
|
509
|
+
},
|
|
510
|
+
});
|
|
511
|
+
this._notifyDeferredQuestion("question.deferred.requested", {
|
|
512
|
+
requestId: registration?.requestId,
|
|
513
|
+
question: request.question,
|
|
514
|
+
options: request.options,
|
|
515
|
+
multiSelect: request.multiSelect === true,
|
|
516
|
+
purpose: request.purpose || "information",
|
|
517
|
+
binding: registration?.binding || null,
|
|
518
|
+
contextRevision: requestedRevision,
|
|
519
|
+
});
|
|
520
|
+
answer
|
|
521
|
+
.then((value) => {
|
|
522
|
+
this._deferredQuestionContext.record({
|
|
523
|
+
questionId: registration?.requestId,
|
|
524
|
+
question: request.question,
|
|
525
|
+
answer: value,
|
|
526
|
+
binding: registration?.binding || null,
|
|
527
|
+
requestedRevision,
|
|
528
|
+
resolvedRevision: this._contextRevision,
|
|
529
|
+
});
|
|
530
|
+
this._notifyDeferredQuestion("question.deferred.resolved", {
|
|
531
|
+
requestId: registration?.requestId,
|
|
532
|
+
question: request.question,
|
|
533
|
+
answer: value,
|
|
534
|
+
binding: registration?.binding || null,
|
|
535
|
+
requestedRevision,
|
|
536
|
+
resolvedRevision: this._contextRevision,
|
|
537
|
+
});
|
|
538
|
+
})
|
|
539
|
+
.catch(() => {
|
|
540
|
+
// Timeout/disconnect simply leaves no asynchronous user context.
|
|
541
|
+
});
|
|
542
|
+
return Object.freeze({
|
|
543
|
+
questionId: registration?.requestId,
|
|
544
|
+
status: "pending",
|
|
545
|
+
mode: "deferred",
|
|
546
|
+
purpose: request.purpose || "information",
|
|
547
|
+
authorization: false,
|
|
548
|
+
binding: registration?.binding || null,
|
|
549
|
+
contextRevision: requestedRevision,
|
|
550
|
+
});
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
beginContextRevision(revision = null) {
|
|
554
|
+
this._contextRevision = Number.isSafeInteger(revision)
|
|
555
|
+
? revision
|
|
556
|
+
: this._contextRevision + 1;
|
|
557
|
+
this._notifyDeferredQuestion("question.context.revision", {
|
|
558
|
+
contextRevision: this._contextRevision,
|
|
559
|
+
});
|
|
560
|
+
return this._contextRevision;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
createDeferredPrepareCall() {
|
|
564
|
+
return () =>
|
|
565
|
+
this._deferredQuestionContext.prepareCall({
|
|
566
|
+
currentRevision: this._contextRevision,
|
|
567
|
+
});
|
|
568
|
+
}
|
|
569
|
+
|
|
408
570
|
/**
|
|
409
571
|
* Called by ws-server when a session-answer message arrives.
|
|
410
572
|
* Resolves the corresponding pending promise.
|
|
@@ -502,6 +664,9 @@ export class WebSocketInteractionAdapter extends InteractionAdapter {
|
|
|
502
664
|
}
|
|
503
665
|
pending.reject(error);
|
|
504
666
|
}
|
|
667
|
+
// Deferred questions and already-resolved answers are durable host state;
|
|
668
|
+
// a transport disconnect must not erase them. Blocking questions and
|
|
669
|
+
// approvals above are still rejected fail-closed.
|
|
505
670
|
}
|
|
506
671
|
|
|
507
672
|
emit(eventType, data) {
|