chainlesschain 0.162.180 → 0.162.182
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 +7 -3
- package/scripts/build-windows-sandbox-helper.ps1 +163 -0
- package/src/assets/web-panel/.build-hash +1 -1
- package/src/assets/web-panel/assets/{AIOps-D_TIL54O.js → AIOps-DzZ5_xoj.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-eQwy9r-f.js → ActionButton-DeLXddIg.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-D2eNuDgT.js → Analytics-rvDCmet1.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-CFhwOFsf.js → AppLayout-Yt6QVDnp.js} +5 -5
- package/src/assets/web-panel/assets/{Artifacts-CJUMw6PB.js → Artifacts-BS112K5L.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-CDB9V58o.js → Audit-0wJuQrNu.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-TBppYFjN.js → BackgroundAgents-B90PMgbn.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-CYEOUpzJ.js → Backup-TkgMglQQ.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-C88fFT2u.js → BaseInput-CQVS9p1l.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-mkr1vC3N.js → Chat-YTjQHoC7.js} +6 -6
- package/src/assets/web-panel/assets/ChatBubbleRenderer-PR074hUN.js +1 -0
- package/src/assets/web-panel/assets/{Checkbox-CVU4xqti.js → Checkbox-5bwaOabq.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-rrNcByz3.js → Codegen-BFmNQ_Xe.js} +1 -1
- package/src/assets/web-panel/assets/{Col-Cqu-zCvh.js → Col-BT_u4Dzp.js} +1 -1
- package/src/assets/web-panel/assets/{Community-RGRlSgXV.js → Community-CJP52u5b.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-DBm6epOK.js → Compact-BpzhJE9i.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-BgKw5iaW.js → Compliance-Cg7rfslp.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-BqQcNO3Z.js → Cowork-JisxL6Yy.js} +3 -3
- package/src/assets/web-panel/assets/{Cron-DO5gTFNb.js → Cron-Bu7Ug_MC.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-B8hbJNuN.js → Crosschain-CvuqoLdj.js} +1 -1
- package/src/assets/web-panel/assets/{DID-B08bFJ2C.js → DID-C2u9G8Pu.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-xNdy_ifo.js → Dashboard-BFt5Yv3d.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-D-KIZgWL.js → Dropdown-DIaT9Wxq.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-DKCvsaGo.js → EmailListRenderer-D_A601Y4.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-D_cfqama.js → FamilyGuardDashboard--IyQ8I8w.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-B-UqQD68.js → Federation-BihWHxuY.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-BWWvWuJW.js → FormItemContext-BB8dOXc_.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-CPchx6N7.js → GenericCardRenderer-DQYhj1HH.js} +1 -1
- package/src/assets/web-panel/assets/{Git-Vx4co9Ff.js → Git-B-2QZxGf.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-D4x0pp_m.js → Governance-CusSb-nG.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-T3KvwPYR.js → Inference-BAjmvPPL.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-Ddv3KUv3.js → KnowledgeGraph-DoNYWKYq.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-DfH-Dkae.js → Logs-B8fKWD-n.js} +2 -2
- package/src/assets/web-panel/assets/{MarkdownRenderer-DHW3PbCq.js → MarkdownRenderer-BlJm1J7E.js} +1 -1
- package/src/assets/web-panel/assets/{Marketplace-tjgb0R3D.js → Marketplace-CyOscbSL.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-DHAk5nYX.js → McpTools-Ex6Om5da.js} +5 -5
- package/src/assets/web-panel/assets/{Memory-A2Yx7thM.js → Memory-CDcD8l8r.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-BEmTVfN5.js → MobileBridge-xfBqoFDg.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-DuFHUgE4.js → MobileProjects-BwObMMA2.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-BDSll0OZ.js → Mtc-CIWd36OV.js} +4 -4
- package/src/assets/web-panel/assets/{MtcAudit-CuBvluaS.js → MtcAudit-CPX0ouuA.js} +4 -4
- package/src/assets/web-panel/assets/{Multisig-BmXAA-Sb.js → Multisig-BDJsIUuW.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-ci2stZAe.js → NLProgramming-DEkbuD3N.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-D-1RpqnS.js → Notes-DJpHRhH1.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-C0GyitIT.js → NotificationSettings-C1UZ-Qd5.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-U2bN-hj5.js → OrderTableRenderer-GlJtqLjT.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-BNZgpPci.js → Organization-Bbhj0fac.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-CDidKmUN.js → Overflow-1H04x3kx.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-BnreBaXq.js → P2P-CDpPe3et.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-DwJUxO9j.js → PdhVaultBrowser-MUX0Bpvw.js} +3 -3
- package/src/assets/web-panel/assets/{Permissions-BQGgJPH0.js → Permissions-A07-0Ci1.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-k_fSkCwL.js → PersonalDataHub-DhhDWxAZ.js} +5 -5
- package/src/assets/web-panel/assets/{Pipeline-WGqKMqau.js → Pipeline-CyfbJfe-.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-DtHj9Ymq.js → Privacy-CNFps3ED.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-B3wH2SFC.js → ProjectInit--T-u3Dr5.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-Cd0cgcA1.js → ProjectSettings-DJmrK2fC.js} +2 -2
- package/src/assets/web-panel/assets/Projects-Qx_MdX9k.js +1 -0
- package/src/assets/web-panel/assets/{Providers-DTpxEXur.js → Providers-DLzzbOZd.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-B2v6gR-C.js → QrScannerModal-CexOomKS.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-D3Nr9xXR.js → QuickAsk-QklGi19_.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-C9inoMky.js → Recommend-Bb9kwSw8.js} +1 -1
- package/src/assets/web-panel/assets/RemoteSession-DZuVNNN5.js +5 -0
- package/src/assets/web-panel/assets/{Reputation-41KFG646.js → Reputation-Dr5cLP0N.js} +1 -1
- package/src/assets/web-panel/assets/{Row-DqAaqqCv.js → Row-oCQHYv5t.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-CMDpCDKc.js → RssFeed-IFDEykIR.js} +3 -3
- package/src/assets/web-panel/assets/{Search-B6mtegNL.js → Search-CspVIxDc.js} +1 -1
- package/src/assets/web-panel/assets/{Security-BaePFVZ8.js → Security-Bm66o_DH.js} +3 -3
- package/src/assets/web-panel/assets/{Services-DeG3Qscl.js → Services-DvanNqXl.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-D_QP7F90.js → Skeleton-CHjEe9_O.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-DLGyUgIA.js → Skills-Cb8jWjMX.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-BN2pxW1Q.js → Sla-DxZm1491.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-BWsZPd8B.js → SpeechSettings-DWxEEclC.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-VOZRPODW.js → SyncSettings-FIvOFw5p.js} +2 -2
- package/src/assets/web-panel/assets/Tasks-CyuLnDFR.js +1 -0
- package/src/assets/web-panel/assets/{Templates-eJSob-q0.js → Templates-CaNjzV_t.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-Bdwk8Rwy.js → Tenant-DjlP6TmH.js} +1 -1
- package/src/assets/web-panel/assets/Terminal-DwzGMniO.js +3 -0
- package/src/assets/web-panel/assets/{TimelineRenderer-D5qkbPKT.js → TimelineRenderer-DoZ8y7y8.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-DKgwn8hI.js → Tokens-390cg2TC.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-HlEHniGM.js → Trigger-CLrtCj5_.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-CdPcsBpN.js → Trust-DpESfSPn.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-vAdXd-vG.js → UkeySign-CN6279O0.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-9hHBYFnH.js → VideoEditing-DPtAUlSe.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-Dxh5d1yR.js → Wallet-nKOMV_1n.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-BsFmLHkp.js → WebAuthn-HuXlSOc4.js} +5 -5
- package/src/assets/web-panel/assets/{WorkflowEditor-Cn0OIwLn.js → WorkflowEditor-CUfFjARi.js} +1 -1
- package/src/assets/web-panel/assets/chat-6Eq0PcZJ.js +1 -0
- package/src/assets/web-panel/assets/{colors-Du9jzLN0.js → colors-BP_Sf6Iy.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-DhEscfzw.js → compact-item-BufMKIh5.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-CwzEfF3i.js → createContext-BBMXxWSW.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-DgVKduH2.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-DT_GSE6J.js → hasIn-fUK_tRO2.js} +1 -1
- package/src/assets/web-panel/assets/{index-tND8-TDU.js → index-2A5ph8b2.js} +1 -1
- package/src/assets/web-panel/assets/{index-B8s5FxJY.js → index-3FuwBewR.js} +1 -1
- package/src/assets/web-panel/assets/{index-53z1YUxX.js → index-6GmBa4Kg.js} +1 -1
- package/src/assets/web-panel/assets/{index-BFD0cEna.js → index-B2vS66eI.js} +1 -1
- package/src/assets/web-panel/assets/{index-CQWxDOvM.js → index-B7Etk0Te.js} +1 -1
- package/src/assets/web-panel/assets/{index-VeHs6qH0.js → index-B9WYSn4m.js} +1 -1
- package/src/assets/web-panel/assets/{index-CGvgnfl9.js → index-BHy6yNiF.js} +1 -1
- package/src/assets/web-panel/assets/{index-D3llLkYM.js → index-BMLHv389.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dny_ZnAO.js → index-BWKoBU8R.js} +1 -1
- package/src/assets/web-panel/assets/{index-DzjzrOli.js → index-Bkg4ZePE.js} +1 -1
- package/src/assets/web-panel/assets/{index-DN60VBn5.js → index-C44atv4k.js} +1 -1
- package/src/assets/web-panel/assets/{index-v5HLYCVa.js → index-CE2DdTP2.js} +1 -1
- package/src/assets/web-panel/assets/{index-BF-ZCVGE.js → index-CGjmILDm.js} +1 -1
- package/src/assets/web-panel/assets/{index-gFjYdstW.js → index-CW_G-MXw.js} +1 -1
- package/src/assets/web-panel/assets/{index-NA2oBLSB.js → index-CZ6Yt17L.js} +1 -1
- package/src/assets/web-panel/assets/{index-Mobw3PHs.js → index-CeASi4fr.js} +1 -1
- package/src/assets/web-panel/assets/{index-DWL3SYN3.js → index-CgiECHu2.js} +1 -1
- package/src/assets/web-panel/assets/{index-1LoutPJd.js → index-Cp8vhyZy.js} +1 -1
- package/src/assets/web-panel/assets/{index-BnHhFnwt.js → index-CwSf5pnf.js} +1 -1
- package/src/assets/web-panel/assets/{index-B6QDWRLk.js → index-D4_MuGkd.js} +1 -1
- package/src/assets/web-panel/assets/{index-D-QkW1fp.js → index-D9vU25ij.js} +1 -1
- package/src/assets/web-panel/assets/{index-C4SN3by5.js → index-DASluSqY.js} +1 -1
- package/src/assets/web-panel/assets/{index-CyEhUJcu.js → index-DLpIs_P_.js} +1 -1
- package/src/assets/web-panel/assets/index-DOZTSoS7.js +1 -0
- package/src/assets/web-panel/assets/{index-BrkJ7JPm.js → index-DPdUNk1i.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cnok8mhZ.js → index-DVsqvI4M.js} +1 -1
- package/src/assets/web-panel/assets/index-DhpvjpqI.js +1 -0
- package/src/assets/web-panel/assets/{index-CAVW0cWM.js → index-DmkMXwnR.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cc9aPkv-.js → index-DocdRcbd.js} +1 -1
- package/src/assets/web-panel/assets/{index-DnVVYKCE.js → index-Dyngbd29.js} +20 -20
- package/src/assets/web-panel/assets/{index-D8gt0j7r.js → index-GwdRUZ-x.js} +1 -1
- package/src/assets/web-panel/assets/{index-BIYTjbv_.js → index-LvCBfTLo.js} +1 -1
- package/src/assets/web-panel/assets/{index-DhXay17G.js → index-cpEli44X.js} +1 -1
- package/src/assets/web-panel/assets/{index-BQWENaZf.js → index-fzaMtSJ8.js} +1 -1
- package/src/assets/web-panel/assets/{index-DfRDyY1q.js → index-pum7HYBA.js} +1 -1
- package/src/assets/web-panel/assets/{index-BgSR2WMG.js → index-t8N1tSeX.js} +1 -1
- package/src/assets/web-panel/assets/{index-D5IytTND.js → index-uCbEN7cY.js} +1 -1
- package/src/assets/web-panel/assets/{index-DtRwLhee.js → index-wKTLbKyc.js} +1 -1
- package/src/assets/web-panel/assets/{index-D4kZoXNr.js → index-wV6iGdpY.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-Cvt59c10.js → initDefaultProps-V5Fo95FM.js} +1 -1
- package/src/assets/web-panel/assets/{motion-D15pPEzl.js → motion-BYWU0hUT.js} +1 -1
- package/src/assets/web-panel/assets/{move-N55KeXJ7.js → move-Cr1M-J2h.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-CyFO1bYP.js → mtc-parser-BysQKi2N.js} +1 -1
- package/src/assets/web-panel/assets/{omit-B5IXfxU2.js → omit-D1zHU8GC.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-CpVqsulV.js → pickAttrs-CUArxtfi.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-ME36sMF3.js → placementArrow-ChoYEys0.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-BXAIR1r-.js → responsiveObserve-BQRs3Mo7.js} +1 -1
- package/src/assets/web-panel/assets/{slide-BkOX3-Dp.js → slide-B8zbsCkg.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-Cy4Aklpt.js → statusUtils-C2U8Gs6h.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-gUd2T206.js → styleChecker-Dd8MgL_V.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-CaVD4o99.js → useFlexGapSupport-hknqY5NH.js} +1 -1
- package/src/assets/web-panel/assets/{useFs--pFGl0KD.js → useFs-CLTAmcfw.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-D45iP86i.js → usePersonalDataHub-ByegHbiB.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-BayoChH_.js → vnode-DqlAy5DP.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-CpP38poi.js → zoom-DiXiztQn.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/commands/agenda.js +115 -3
- package/src/commands/agent.js +10 -1
- package/src/commands/background-session.js +2 -1
- package/src/commands/context.js +69 -2
- package/src/data/changelog.json +26 -0
- package/src/gateways/terminal/PtyManager.js +45 -4
- package/src/gateways/ws/background-agent-protocol.js +38 -0
- package/src/gateways/ws/message-dispatcher.js +1 -0
- package/src/gateways/ws/remote-session-protocol.js +65 -6
- package/src/gateways/ws/ws-server.js +18 -2
- package/src/harness/background-task-manager.js +70 -2
- package/src/harness/feature-flags.js +15 -8
- package/src/harness/mcp-client.js +618 -43
- package/src/harness/worktree-isolator.js +14 -3
- package/src/lazy-dispatch.js +30 -1
- package/src/lib/agent-authority.js +22 -0
- package/src/lib/agent-core.js +3 -0
- package/src/lib/agent-sandbox.js +161 -0
- package/src/lib/agent-schedule-store.js +45 -22
- package/src/lib/agent-team/team-runner.js +22 -0
- package/src/lib/artifact-store.js +35 -0
- package/src/lib/async-hook-supervisor.cjs +13 -2
- package/src/lib/audit-mtc.js +3 -2
- package/src/lib/background-agent-supervisor.js +104 -3
- package/src/lib/background-interaction-journal.js +407 -0
- package/src/lib/background-interaction-resolver.js +23 -34
- package/src/lib/background-session-transport.js +12 -2
- package/src/lib/capability-manifest.js +41 -3
- package/src/lib/capability-negotiation.js +1 -1
- package/src/lib/channels/channel-manager.js +33 -0
- package/src/lib/channels/telegram-channel.js +23 -5
- package/src/lib/channels/webhook-channel.js +38 -15
- package/src/lib/chrome-connector.js +169 -23
- package/src/lib/config-manager.js +23 -9
- package/src/lib/context-breakdown.js +31 -0
- package/src/lib/cowork-cron.js +274 -32
- package/src/lib/cross-chain-mtc.js +3 -2
- package/src/lib/durable-security-store.js +124 -0
- package/src/lib/event-runtime-host.js +67 -6
- package/src/lib/event-runtime-store.js +163 -0
- package/src/lib/goal-store.js +32 -12
- package/src/lib/headless-manifest.js +2 -1
- package/src/lib/hook-environment.cjs +58 -0
- package/src/lib/hook-manager.js +12 -5
- package/src/lib/hook-runner.cjs +122 -28
- package/src/lib/hook-runner.js +7 -2
- package/src/lib/hooks-v2-producers.js +36 -0
- package/src/lib/hooks-v2-runtime.js +329 -52
- package/src/lib/interaction-adapter.js +60 -19
- package/src/lib/interaction-binding.js +64 -0
- package/src/lib/json-schema-output.js +439 -14
- package/src/lib/json-schema-validate.js +514 -883
- package/src/lib/lan-pairing-tokens.js +115 -57
- package/src/lib/lsp/__tests__/lsp-client.test.js +32 -0
- package/src/lib/lsp/__tests__/lsp-manager.test.js +25 -0
- package/src/lib/lsp/lsp-client.js +4 -0
- package/src/lib/lsp/lsp-manager.js +3 -0
- package/src/lib/lsp/lsp-server-registry.js +18 -0
- package/src/lib/mcp-client.js +1 -0
- package/src/lib/mcp-oauth.js +35 -21
- package/src/lib/packer/pkg-config-generator.js +3 -0
- package/src/lib/permission-decision.js +99 -0
- package/src/lib/plugin-monitor-supervisor.js +3 -0
- package/src/lib/plugin-runtime/bin.js +1148 -70
- package/src/lib/plugin-runtime/capabilities.js +50 -0
- package/src/lib/plugin-runtime/capability-consent.js +42 -35
- package/src/lib/plugin-runtime/hooks.js +36 -9
- package/src/lib/plugin-runtime/install.js +23 -7
- package/src/lib/plugin-runtime/lsp.js +4 -0
- package/src/lib/plugin-runtime/manifest.js +112 -8
- package/src/lib/plugin-runtime/mcp.js +30 -5
- package/src/lib/plugin-runtime/monitors.js +11 -0
- package/src/lib/plugin-runtime/plugin-options.js +94 -38
- package/src/lib/plugin-runtime/policy.js +29 -2
- package/src/lib/plugin-runtime/sandbox-policy.js +84 -0
- package/src/lib/plugin-runtime/scopes.js +27 -13
- package/src/lib/plugin-runtime/trust.js +36 -31
- package/src/lib/process-execution-broker/credential-agent.js +114 -16
- package/src/lib/process-execution-broker/credential-transport-worker.js +312 -0
- package/src/lib/process-execution-broker/credential-transport.js +378 -0
- package/src/lib/process-execution-broker/index.js +765 -49
- package/src/lib/process-execution-broker/platform-sandbox.js +4233 -138
- package/src/lib/process-execution-broker/windows-sandbox-helper.dll +0 -0
- package/src/lib/process-execution-broker/windows-sandbox-helper.exe +0 -0
- package/src/lib/process-execution-broker/windows-sandbox.cs +3856 -0
- package/src/lib/project-mcp-trust.js +21 -26
- package/src/lib/protocol-replay.js +19 -12
- package/src/lib/repl-bang-memorize.js +67 -10
- package/src/lib/settings-hooks.cjs +13 -0
- package/src/lib/skill-loader.js +83 -10
- package/src/lib/slash-commands.js +27 -4
- package/src/lib/sync-credentials.js +5 -4
- package/src/lib/turn-binding.js +8 -6
- package/src/repl/agent-repl.js +117 -31
- package/src/repl/repl-turn-binding.js +5 -2
- package/src/runtime/agent-core.js +699 -70
- package/src/runtime/agent-runtime.js +13 -1
- package/src/runtime/coding-agent-contract-shared.cjs +3 -3
- package/src/runtime/headless-runner.js +105 -7
- package/src/runtime/headless-stream.js +209 -23
- package/src/runtime/mcp-config.js +29 -17
- package/src/runtime/policies/agent-policy.js +2 -0
- package/src/workers/background-agent-worker.js +128 -13
- package/src/assets/web-panel/assets/ChatBubbleRenderer-C7bofJuA.js +0 -1
- package/src/assets/web-panel/assets/Projects-BbgmyQnS.js +0 -1
- package/src/assets/web-panel/assets/RemoteSession-Bqk3EYSu.js +0 -4
- package/src/assets/web-panel/assets/Tasks-3bDAUz8K.js +0 -1
- package/src/assets/web-panel/assets/Terminal-CsMLKIoV.js +0 -3
- package/src/assets/web-panel/assets/chat-CJ19O6Dh.js +0 -1
- package/src/assets/web-panel/assets/devWarning-BBMFnsja.js +0 -1
- package/src/assets/web-panel/assets/index-Do8JMV4N.js +0 -1
- package/src/assets/web-panel/assets/index-UYAJ6iNM.js +0 -1
|
@@ -1,977 +1,608 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* 6901 JSON Pointers, schema meta-validation, a schema digest, and a
|
|
4
|
-
* `structured_result` stream event (P2 "JSON Schema 结构化输出").
|
|
2
|
+
* Draft 2020-12 JSON Schema validation for structured CLI output.
|
|
5
3
|
*
|
|
6
|
-
* The
|
|
7
|
-
*
|
|
8
|
-
* reports `$.path`-style strings. This module is the richer, protocol-facing
|
|
9
|
-
* core:
|
|
4
|
+
* The public result shape is intentionally stable even though the standards
|
|
5
|
+
* implementation is provided by Ajv:
|
|
10
6
|
*
|
|
11
|
-
* validate(value, schema)
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* buildStructuredResultEvent({schema, value})
|
|
16
|
-
* → { type:"structured_result", schema_digest,
|
|
17
|
-
* valid, value, errors? }
|
|
7
|
+
* validate(value, schema) -> {
|
|
8
|
+
* valid,
|
|
9
|
+
* errors: [{ code, keyword, instancePath, schemaPath, message }]
|
|
10
|
+
* }
|
|
18
11
|
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
* additionalProperties (bool|schema), patternProperties-free, items,
|
|
24
|
-
* prefixItems, minItems/maxItems, uniqueItems, minLength/maxLength, pattern,
|
|
25
|
-
* format (date-time/date/time/email/uri/uuid/ipv4/ipv6/hostname — asserted;
|
|
26
|
-
* unknown formats pass through as annotations), minimum/maximum/
|
|
27
|
-
* exclusiveMinimum/exclusiveMaximum, multipleOf, minProperties/maxProperties,
|
|
28
|
-
* allOf/anyOf/oneOf/not, if/then/else, and local `$ref` (`#/$defs/…`,
|
|
29
|
-
* `#/definitions/…`, or any in-document JSON Pointer).
|
|
12
|
+
* Ajv supplies the complete Draft 2020-12 vocabularies, including dynamic
|
|
13
|
+
* references and unevaluated locations. This adapter preserves the CLI's RFC
|
|
14
|
+
* 6901 pointers, stable keyword codes, schema digest and structured-result
|
|
15
|
+
* envelope.
|
|
30
16
|
*/
|
|
31
17
|
|
|
32
|
-
import crypto from "crypto";
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
"
|
|
41
|
-
"
|
|
18
|
+
import crypto from "node:crypto";
|
|
19
|
+
import Ajv2020 from "ajv/dist/2020.js";
|
|
20
|
+
import addFormats from "ajv-formats";
|
|
21
|
+
|
|
22
|
+
const ATTACHED_EXTERNAL_SCHEMAS = new WeakMap();
|
|
23
|
+
const COMPILED_VALIDATORS = new WeakMap();
|
|
24
|
+
|
|
25
|
+
const SCHEMA_KEYWORDS = new Set([
|
|
26
|
+
"$anchor",
|
|
27
|
+
"$comment",
|
|
28
|
+
"$defs",
|
|
29
|
+
"$dynamicAnchor",
|
|
30
|
+
"$dynamicRef",
|
|
31
|
+
"$id",
|
|
32
|
+
"$ref",
|
|
33
|
+
"$schema",
|
|
34
|
+
"$vocabulary",
|
|
35
|
+
"additionalProperties",
|
|
36
|
+
"allOf",
|
|
37
|
+
"anyOf",
|
|
38
|
+
"const",
|
|
39
|
+
"contains",
|
|
40
|
+
"contentEncoding",
|
|
41
|
+
"contentMediaType",
|
|
42
|
+
"contentSchema",
|
|
43
|
+
"default",
|
|
44
|
+
"dependentRequired",
|
|
45
|
+
"dependentSchemas",
|
|
46
|
+
"deprecated",
|
|
47
|
+
"description",
|
|
48
|
+
"else",
|
|
49
|
+
"enum",
|
|
50
|
+
"examples",
|
|
51
|
+
"exclusiveMaximum",
|
|
52
|
+
"exclusiveMinimum",
|
|
53
|
+
"format",
|
|
54
|
+
"if",
|
|
55
|
+
"items",
|
|
56
|
+
"maxContains",
|
|
57
|
+
"maximum",
|
|
58
|
+
"maxItems",
|
|
59
|
+
"maxLength",
|
|
60
|
+
"maxProperties",
|
|
61
|
+
"minContains",
|
|
62
|
+
"minimum",
|
|
63
|
+
"minItems",
|
|
64
|
+
"minLength",
|
|
65
|
+
"minProperties",
|
|
66
|
+
"multipleOf",
|
|
67
|
+
"not",
|
|
68
|
+
"oneOf",
|
|
69
|
+
"pattern",
|
|
70
|
+
"patternProperties",
|
|
71
|
+
"prefixItems",
|
|
72
|
+
"properties",
|
|
73
|
+
"propertyNames",
|
|
74
|
+
"readOnly",
|
|
75
|
+
"required",
|
|
76
|
+
"then",
|
|
77
|
+
"title",
|
|
78
|
+
"type",
|
|
79
|
+
"unevaluatedItems",
|
|
80
|
+
"unevaluatedProperties",
|
|
81
|
+
"uniqueItems",
|
|
82
|
+
"writeOnly",
|
|
42
83
|
]);
|
|
43
84
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
function checkTime(v) {
|
|
64
|
-
const m = /^(\d{2}):(\d{2}):(\d{2})(\.\d+)?([Zz]|[+-]\d{2}:\d{2})?$/.exec(v);
|
|
65
|
-
if (!m) return false;
|
|
66
|
-
const hh = +m[1];
|
|
67
|
-
const mm = +m[2];
|
|
68
|
-
const ss = +m[3];
|
|
69
|
-
return hh <= 23 && mm <= 59 && ss <= 60; // 60 allows a leap second
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
function checkDateTime(v) {
|
|
73
|
-
const m = /^(.+)[Tt](.+)$/.exec(v);
|
|
74
|
-
if (!m) return false;
|
|
75
|
-
// RFC 3339 date-time requires an offset/Z on the time part.
|
|
76
|
-
if (!/([Zz]|[+-]\d{2}:\d{2})$/.test(m[2])) return false;
|
|
77
|
-
return checkDate(m[1]) && checkTime(m[2]);
|
|
78
|
-
}
|
|
85
|
+
/** Formats asserted by ajv-formats in full mode. */
|
|
86
|
+
export const KNOWN_FORMATS = new Set([
|
|
87
|
+
"date",
|
|
88
|
+
"time",
|
|
89
|
+
"date-time",
|
|
90
|
+
"duration",
|
|
91
|
+
"uri",
|
|
92
|
+
"uri-reference",
|
|
93
|
+
"uri-template",
|
|
94
|
+
"url",
|
|
95
|
+
"email",
|
|
96
|
+
"hostname",
|
|
97
|
+
"ipv4",
|
|
98
|
+
"ipv6",
|
|
99
|
+
"regex",
|
|
100
|
+
"uuid",
|
|
101
|
+
"json-pointer",
|
|
102
|
+
"relative-json-pointer",
|
|
103
|
+
]);
|
|
79
104
|
|
|
80
|
-
function
|
|
81
|
-
if (
|
|
82
|
-
|
|
83
|
-
|
|
105
|
+
function isCalendarDate(year, month, day) {
|
|
106
|
+
if (month < 1 || month > 12 || day < 1) return false;
|
|
107
|
+
const leap =
|
|
108
|
+
(year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
|
|
109
|
+
const days = [
|
|
110
|
+
31,
|
|
111
|
+
leap ? 29 : 28,
|
|
112
|
+
31,
|
|
113
|
+
30,
|
|
114
|
+
31,
|
|
115
|
+
30,
|
|
116
|
+
31,
|
|
117
|
+
31,
|
|
118
|
+
30,
|
|
119
|
+
31,
|
|
120
|
+
30,
|
|
121
|
+
31,
|
|
122
|
+
];
|
|
123
|
+
return day <= days[month - 1];
|
|
84
124
|
}
|
|
85
125
|
|
|
86
|
-
function
|
|
87
|
-
const
|
|
88
|
-
return
|
|
126
|
+
function checkDate(value) {
|
|
127
|
+
const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(value);
|
|
128
|
+
return (
|
|
129
|
+
Boolean(match) &&
|
|
130
|
+
isCalendarDate(+match[1], +match[2], +match[3])
|
|
131
|
+
);
|
|
89
132
|
}
|
|
90
133
|
|
|
91
|
-
function
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
134
|
+
function checkTime(value) {
|
|
135
|
+
const match =
|
|
136
|
+
/^(\d{2}):(\d{2}):(\d{2})(\.\d+)?([Zz]|[+-]\d{2}:\d{2})$/.exec(
|
|
137
|
+
value,
|
|
138
|
+
);
|
|
139
|
+
if (!match) return false;
|
|
140
|
+
const hours = +match[1];
|
|
141
|
+
const minutes = +match[2];
|
|
142
|
+
const seconds = +match[3];
|
|
143
|
+
const offset = match[5];
|
|
144
|
+
if (hours > 23 || minutes > 59 || seconds > 60) return false;
|
|
145
|
+
if (/^[+-]/.test(offset)) {
|
|
146
|
+
const [offsetHours, offsetMinutes] = offset
|
|
147
|
+
.slice(1)
|
|
148
|
+
.split(":")
|
|
149
|
+
.map(Number);
|
|
150
|
+
if (offsetHours > 23 || offsetMinutes > 59) return false;
|
|
151
|
+
}
|
|
152
|
+
return true;
|
|
109
153
|
}
|
|
110
154
|
|
|
111
|
-
function
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
return labels.every((l) =>
|
|
115
|
-
/^[A-Za-z0-9]([A-Za-z0-9-]{0,61}[A-Za-z0-9])?$/.test(l),
|
|
116
|
-
);
|
|
155
|
+
function checkDateTime(value) {
|
|
156
|
+
const match = /^(.+)[Tt](.+)$/.exec(value);
|
|
157
|
+
return Boolean(match) && checkDate(match[1]) && checkTime(match[2]);
|
|
117
158
|
}
|
|
118
159
|
|
|
119
|
-
const FORMAT_CHECKERS = {
|
|
120
|
-
"date-time": checkDateTime,
|
|
121
|
-
date: checkDate,
|
|
122
|
-
time: checkTime,
|
|
123
|
-
email: (v) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v),
|
|
124
|
-
uri: (v) => /^[A-Za-z][A-Za-z0-9+.-]*:\S*$/.test(v),
|
|
125
|
-
uuid: (v) =>
|
|
126
|
-
/^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/.test(
|
|
127
|
-
v,
|
|
128
|
-
),
|
|
129
|
-
ipv4: checkIpv4,
|
|
130
|
-
ipv6: checkIpv6,
|
|
131
|
-
hostname: checkHostname,
|
|
132
|
-
};
|
|
133
|
-
|
|
134
|
-
/** Known format names (exported for meta-validation / callers). */
|
|
135
|
-
export const KNOWN_FORMATS = new Set(Object.keys(FORMAT_CHECKERS));
|
|
136
|
-
|
|
137
160
|
/** RFC 6901 JSON Pointer from a segment array ("" = document root). */
|
|
138
161
|
export function jsonPointer(segments) {
|
|
139
162
|
if (!segments || segments.length === 0) return "";
|
|
140
|
-
return
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
);
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
/** JSON value type per Draft 2020-12 (integer is a refinement of number). */
|
|
149
|
-
function typeOf(v) {
|
|
150
|
-
if (v === null) return "null";
|
|
151
|
-
if (Array.isArray(v)) return "array";
|
|
152
|
-
if (typeof v === "number") return Number.isInteger(v) ? "integer" : "number";
|
|
153
|
-
return typeof v;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
function deepEqual(a, b) {
|
|
157
|
-
return canonical(a) === canonical(b);
|
|
163
|
+
return `/${segments
|
|
164
|
+
.map((segment) =>
|
|
165
|
+
String(segment).replace(/~/g, "~0").replace(/\//g, "~1"),
|
|
166
|
+
)
|
|
167
|
+
.join("/")}`;
|
|
158
168
|
}
|
|
159
169
|
|
|
160
|
-
/** Canonical JSON
|
|
170
|
+
/** Canonical JSON with recursively sorted object keys. */
|
|
161
171
|
export function canonical(value) {
|
|
162
|
-
if (value === null || typeof value !== "object")
|
|
163
|
-
|
|
172
|
+
if (value === null || typeof value !== "object") {
|
|
173
|
+
return JSON.stringify(value);
|
|
174
|
+
}
|
|
175
|
+
if (Array.isArray(value)) {
|
|
176
|
+
return `[${value.map(canonical).join(",")}]`;
|
|
177
|
+
}
|
|
164
178
|
const keys = Object.keys(value).sort();
|
|
165
|
-
return `{${keys
|
|
179
|
+
return `{${keys
|
|
180
|
+
.map((key) => `${JSON.stringify(key)}:${canonical(value[key])}`)
|
|
181
|
+
.join(",")}}`;
|
|
166
182
|
}
|
|
167
183
|
|
|
168
184
|
/** "sha256:<hex>" over the canonical form of the schema. */
|
|
169
185
|
export function computeSchemaDigest(schema) {
|
|
170
|
-
const
|
|
171
|
-
|
|
172
|
-
|
|
186
|
+
const attached =
|
|
187
|
+
schema && typeof schema === "object"
|
|
188
|
+
? ATTACHED_EXTERNAL_SCHEMAS.get(schema)
|
|
189
|
+
: null;
|
|
190
|
+
const externalSchemas = attached?.externalSchemas || {};
|
|
191
|
+
const digestInput =
|
|
192
|
+
Object.keys(externalSchemas).length > 0
|
|
193
|
+
? { schema, externalSchemas }
|
|
194
|
+
: schema;
|
|
195
|
+
const hash = crypto.createHash("sha256");
|
|
196
|
+
hash.update(canonical(digestInput));
|
|
197
|
+
return `sha256:${hash.digest("hex")}`;
|
|
173
198
|
}
|
|
174
199
|
|
|
175
|
-
/** Resolve a local `#/...` JSON Pointer $ref within `root`; null if unresolved. */
|
|
176
|
-
function resolvePointer(root, frag) {
|
|
177
|
-
if (frag === "" || frag === "/") return root;
|
|
178
|
-
const parts = frag
|
|
179
|
-
.replace(/^\//, "")
|
|
180
|
-
.split("/")
|
|
181
|
-
.map((p) => p.replace(/~1/g, "/").replace(/~0/g, "~"));
|
|
182
|
-
let cur = root;
|
|
183
|
-
for (const p of parts) {
|
|
184
|
-
if (cur == null || typeof cur !== "object") return null;
|
|
185
|
-
cur = cur[p];
|
|
186
|
-
}
|
|
187
|
-
return cur === undefined ? null : cur;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
function findAnchor(root, anchor, seen = new Set()) {
|
|
191
|
-
if (!root || typeof root !== "object" || seen.has(root)) return null;
|
|
192
|
-
seen.add(root);
|
|
193
|
-
if (root.$anchor === anchor || root.$dynamicAnchor === anchor) return root;
|
|
194
|
-
for (const value of Object.values(root)) {
|
|
195
|
-
const found = findAnchor(value, anchor, seen);
|
|
196
|
-
if (found) return found;
|
|
197
|
-
}
|
|
198
|
-
return null;
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
/** Resolve local refs or a caller-provided external document registry. */
|
|
202
|
-
function resolveRef(root, ref, externalSchemas = {}) {
|
|
203
|
-
if (typeof ref !== "string") return null;
|
|
204
|
-
if (ref.startsWith("#")) {
|
|
205
|
-
const fragment = ref.slice(1);
|
|
206
|
-
if (fragment === "" || fragment.startsWith("/")) {
|
|
207
|
-
return resolvePointer(root, fragment);
|
|
208
|
-
}
|
|
209
|
-
try {
|
|
210
|
-
return findAnchor(root, decodeURIComponent(fragment), new Set());
|
|
211
|
-
} catch {
|
|
212
|
-
return findAnchor(root, fragment, new Set());
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
const hash = ref.indexOf("#");
|
|
216
|
-
const uri = hash >= 0 ? ref.slice(0, hash) : ref;
|
|
217
|
-
const document = externalSchemas?.[ref] ?? externalSchemas?.[uri];
|
|
218
|
-
if (document === undefined) return null;
|
|
219
|
-
if (hash < 0) return document;
|
|
220
|
-
const fragment = ref.slice(hash + 1);
|
|
221
|
-
if (fragment === "" || fragment.startsWith("/")) {
|
|
222
|
-
return resolvePointer(document, fragment);
|
|
223
|
-
}
|
|
224
|
-
try {
|
|
225
|
-
return findAnchor(document, decodeURIComponent(fragment), new Set());
|
|
226
|
-
} catch {
|
|
227
|
-
return findAnchor(document, fragment, new Set());
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
// ─── instance validation ─────────────────────────────────────────────────────
|
|
232
|
-
|
|
233
200
|
/**
|
|
234
|
-
*
|
|
235
|
-
*
|
|
201
|
+
* Associate resolved external documents with a root schema without mutating
|
|
202
|
+
* the JSON value (and therefore without changing its digest or model prompt).
|
|
236
203
|
*/
|
|
237
|
-
export function
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
204
|
+
export function attachExternalSchemas(
|
|
205
|
+
schema,
|
|
206
|
+
externalSchemas = {},
|
|
207
|
+
{ baseUri = null } = {},
|
|
208
|
+
) {
|
|
209
|
+
if (schema && typeof schema === "object") {
|
|
210
|
+
COMPILED_VALIDATORS.delete(schema);
|
|
211
|
+
ATTACHED_EXTERNAL_SCHEMAS.set(schema, {
|
|
212
|
+
baseUri,
|
|
213
|
+
externalSchemas: { ...externalSchemas },
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
return schema;
|
|
241
217
|
}
|
|
242
218
|
|
|
243
|
-
function
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
instancePath: jsonPointer(instSegs),
|
|
248
|
-
schemaPath: jsonPointer(schemaSegs),
|
|
249
|
-
message,
|
|
250
|
-
});
|
|
219
|
+
function decodePointerSegment(value) {
|
|
220
|
+
return String(value || "")
|
|
221
|
+
.replace(/~1/g, "/")
|
|
222
|
+
.replace(/~0/g, "~");
|
|
251
223
|
}
|
|
252
224
|
|
|
253
|
-
function
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
if (!schema || typeof schema !== "object") return;
|
|
260
|
-
if (depth > MAX_REF_DEPTH) return; // cyclic $ref guard
|
|
261
|
-
|
|
262
|
-
// $ref/$dynamicRef — local pointer/anchor or explicit external registry.
|
|
263
|
-
const refKeyword =
|
|
264
|
-
typeof schema.$ref === "string"
|
|
265
|
-
? "$ref"
|
|
266
|
-
: typeof schema.$dynamicRef === "string"
|
|
267
|
-
? "$dynamicRef"
|
|
268
|
-
: null;
|
|
269
|
-
if (refKeyword) {
|
|
270
|
-
const target = resolveRef(root, schema[refKeyword], externalSchemas);
|
|
271
|
-
if (target == null) {
|
|
272
|
-
_err(
|
|
273
|
-
errors,
|
|
274
|
-
refKeyword,
|
|
275
|
-
inst,
|
|
276
|
-
[...sch, refKeyword],
|
|
277
|
-
`cannot resolve ${refKeyword} "${schema[refKeyword]}"`,
|
|
278
|
-
);
|
|
279
|
-
} else {
|
|
280
|
-
_validate(value, target, inst, [...sch, refKeyword], root, errors, depth + 1, externalSchemas);
|
|
281
|
-
}
|
|
282
|
-
// sibling keywords still apply in 2020-12; fall through.
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
const t = typeOf(value);
|
|
286
|
-
|
|
287
|
-
if (schema.type !== undefined) {
|
|
288
|
-
const want = Array.isArray(schema.type) ? schema.type : [schema.type];
|
|
289
|
-
const ok = want.some((x) => x === t || (x === "number" && t === "integer"));
|
|
290
|
-
if (!ok) {
|
|
291
|
-
_err(
|
|
292
|
-
errors,
|
|
293
|
-
"type",
|
|
294
|
-
inst,
|
|
295
|
-
[...sch, "type"],
|
|
296
|
-
`expected type ${want.join("|")}, got ${t}`,
|
|
297
|
-
);
|
|
298
|
-
return; // type mismatch — deeper checks would be noise
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
if (schema.enum !== undefined) {
|
|
303
|
-
if (
|
|
304
|
-
!Array.isArray(schema.enum) ||
|
|
305
|
-
!schema.enum.some((e) => deepEqual(e, value))
|
|
306
|
-
) {
|
|
307
|
-
_err(
|
|
308
|
-
errors,
|
|
309
|
-
"enum",
|
|
310
|
-
inst,
|
|
311
|
-
[...sch, "enum"],
|
|
312
|
-
"value is not one of the allowed enum values",
|
|
313
|
-
);
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
if (schema.const !== undefined && !deepEqual(schema.const, value)) {
|
|
317
|
-
_err(
|
|
318
|
-
errors,
|
|
319
|
-
"const",
|
|
320
|
-
inst,
|
|
321
|
-
[...sch, "const"],
|
|
322
|
-
`value must equal ${JSON.stringify(schema.const)}`,
|
|
323
|
-
);
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
if (t === "string") _validateString(value, schema, inst, sch, errors);
|
|
327
|
-
if (t === "number" || t === "integer")
|
|
328
|
-
_validateNumber(value, schema, inst, sch, errors);
|
|
329
|
-
if (t === "array")
|
|
330
|
-
_validateArray(value, schema, inst, sch, root, errors, depth, externalSchemas);
|
|
331
|
-
if (t === "object")
|
|
332
|
-
_validateObject(value, schema, inst, sch, root, errors, depth, externalSchemas);
|
|
225
|
+
function appendPointer(pointer, segment) {
|
|
226
|
+
const encoded = String(segment)
|
|
227
|
+
.replace(/~/g, "~0")
|
|
228
|
+
.replace(/\//g, "~1");
|
|
229
|
+
return `${pointer || ""}/${encoded}`;
|
|
230
|
+
}
|
|
333
231
|
|
|
334
|
-
|
|
335
|
-
|
|
232
|
+
function schemaPathFromAjv(value) {
|
|
233
|
+
const path = String(value || "");
|
|
234
|
+
return path.startsWith("#") ? path.slice(1) : path;
|
|
336
235
|
}
|
|
337
236
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
}
|
|
359
|
-
} else if (schema.else !== undefined) {
|
|
360
|
-
_validate(value, schema.else, inst, [...sch, "else"], root, errors, depth, externalSchemas);
|
|
237
|
+
function stableMessage(error) {
|
|
238
|
+
const params = error?.params || {};
|
|
239
|
+
switch (error?.keyword) {
|
|
240
|
+
case "required":
|
|
241
|
+
return `missing required property "${params.missingProperty}"`;
|
|
242
|
+
case "additionalProperties":
|
|
243
|
+
case "unevaluatedProperties":
|
|
244
|
+
return `unexpected property "${
|
|
245
|
+
params.additionalProperty || params.unevaluatedProperty
|
|
246
|
+
}"`;
|
|
247
|
+
case "type":
|
|
248
|
+
return `expected type ${params.type}, got a different type`;
|
|
249
|
+
case "enum":
|
|
250
|
+
return "value is not one of the allowed enum values";
|
|
251
|
+
case "format":
|
|
252
|
+
return `string is not a valid ${params.format}`;
|
|
253
|
+
default:
|
|
254
|
+
return `${error?.keyword || "schema"}: ${
|
|
255
|
+
error?.message || "validation failed"
|
|
256
|
+
}`;
|
|
361
257
|
}
|
|
362
258
|
}
|
|
363
259
|
|
|
364
|
-
function
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
`string is shorter than minLength ${schema.minLength}`,
|
|
260
|
+
function normalizeValidationError(error) {
|
|
261
|
+
const keyword = error?.keyword || "schema";
|
|
262
|
+
let instancePath = String(error?.instancePath || "");
|
|
263
|
+
if (keyword === "additionalProperties") {
|
|
264
|
+
instancePath = appendPointer(
|
|
265
|
+
instancePath,
|
|
266
|
+
error?.params?.additionalProperty,
|
|
372
267
|
);
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
"maxLength",
|
|
378
|
-
inst,
|
|
379
|
-
[...sch, "maxLength"],
|
|
380
|
-
`string is longer than maxLength ${schema.maxLength}`,
|
|
268
|
+
} else if (keyword === "unevaluatedProperties") {
|
|
269
|
+
instancePath = appendPointer(
|
|
270
|
+
instancePath,
|
|
271
|
+
error?.params?.unevaluatedProperty,
|
|
381
272
|
);
|
|
382
273
|
}
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
if (re && !re.test(value)) {
|
|
391
|
-
_err(
|
|
392
|
-
errors,
|
|
393
|
-
"pattern",
|
|
394
|
-
inst,
|
|
395
|
-
[...sch, "pattern"],
|
|
396
|
-
`string does not match pattern ${schema.pattern}`,
|
|
397
|
-
);
|
|
398
|
-
}
|
|
399
|
-
}
|
|
400
|
-
if (typeof schema.format === "string") {
|
|
401
|
-
const check = FORMAT_CHECKERS[schema.format];
|
|
402
|
-
// Unknown format = annotation only (always passes), per Draft 2020-12.
|
|
403
|
-
if (check && !check(value)) {
|
|
404
|
-
_err(
|
|
405
|
-
errors,
|
|
406
|
-
"format",
|
|
407
|
-
inst,
|
|
408
|
-
[...sch, "format"],
|
|
409
|
-
`string is not a valid ${schema.format}`,
|
|
410
|
-
);
|
|
411
|
-
}
|
|
412
|
-
}
|
|
274
|
+
return {
|
|
275
|
+
code: keyword,
|
|
276
|
+
keyword,
|
|
277
|
+
instancePath,
|
|
278
|
+
schemaPath: schemaPathFromAjv(error?.schemaPath),
|
|
279
|
+
message: stableMessage(error),
|
|
280
|
+
};
|
|
413
281
|
}
|
|
414
282
|
|
|
415
|
-
function
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
"maximum",
|
|
429
|
-
inst,
|
|
430
|
-
[...sch, "maximum"],
|
|
431
|
-
`value is greater than maximum ${schema.maximum}`,
|
|
432
|
-
);
|
|
433
|
-
}
|
|
434
|
-
if (
|
|
435
|
-
Number.isFinite(schema.exclusiveMinimum) &&
|
|
436
|
-
value <= schema.exclusiveMinimum
|
|
437
|
-
) {
|
|
438
|
-
_err(
|
|
439
|
-
errors,
|
|
440
|
-
"exclusiveMinimum",
|
|
441
|
-
inst,
|
|
442
|
-
[...sch, "exclusiveMinimum"],
|
|
443
|
-
`value must be > ${schema.exclusiveMinimum}`,
|
|
444
|
-
);
|
|
445
|
-
}
|
|
446
|
-
if (
|
|
447
|
-
Number.isFinite(schema.exclusiveMaximum) &&
|
|
448
|
-
value >= schema.exclusiveMaximum
|
|
449
|
-
) {
|
|
450
|
-
_err(
|
|
451
|
-
errors,
|
|
452
|
-
"exclusiveMaximum",
|
|
453
|
-
inst,
|
|
454
|
-
[...sch, "exclusiveMaximum"],
|
|
455
|
-
`value must be < ${schema.exclusiveMaximum}`,
|
|
456
|
-
);
|
|
457
|
-
}
|
|
458
|
-
if (Number.isFinite(schema.multipleOf) && schema.multipleOf > 0) {
|
|
459
|
-
const q = value / schema.multipleOf;
|
|
460
|
-
if (Math.abs(q - Math.round(q)) > 1e-9) {
|
|
461
|
-
_err(
|
|
462
|
-
errors,
|
|
463
|
-
"multipleOf",
|
|
464
|
-
inst,
|
|
465
|
-
[...sch, "multipleOf"],
|
|
466
|
-
`value is not a multiple of ${schema.multipleOf}`,
|
|
467
|
-
);
|
|
468
|
-
}
|
|
469
|
-
}
|
|
283
|
+
function normalizeValidationErrors(errors) {
|
|
284
|
+
const normalized = (errors || []).map(normalizeValidationError);
|
|
285
|
+
// Preserve the original CLI contract: aggregate applicator failures precede
|
|
286
|
+
// their noisy branch details, so retry prompts lead with the actionable rule.
|
|
287
|
+
const aggregate = new Set(["anyOf", "oneOf", "not"]);
|
|
288
|
+
return normalized
|
|
289
|
+
.map((error, index) => ({ error, index }))
|
|
290
|
+
.sort((a, b) => {
|
|
291
|
+
const aRank = aggregate.has(a.error.keyword) ? 0 : 1;
|
|
292
|
+
const bRank = aggregate.has(b.error.keyword) ? 0 : 1;
|
|
293
|
+
return aRank - bRank || a.index - b.index;
|
|
294
|
+
})
|
|
295
|
+
.map(({ error }) => error);
|
|
470
296
|
}
|
|
471
297
|
|
|
472
|
-
function
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
errors,
|
|
485
|
-
"maxItems",
|
|
486
|
-
inst,
|
|
487
|
-
[...sch, "maxItems"],
|
|
488
|
-
`array has more than maxItems ${schema.maxItems}`,
|
|
489
|
-
);
|
|
490
|
-
}
|
|
491
|
-
if (schema.uniqueItems === true) {
|
|
492
|
-
const seen = new Set();
|
|
493
|
-
for (const item of value) {
|
|
494
|
-
const key = canonical(item);
|
|
495
|
-
if (seen.has(key)) {
|
|
496
|
-
_err(
|
|
497
|
-
errors,
|
|
498
|
-
"uniqueItems",
|
|
499
|
-
inst,
|
|
500
|
-
[...sch, "uniqueItems"],
|
|
501
|
-
"array items are not unique",
|
|
502
|
-
);
|
|
503
|
-
break;
|
|
504
|
-
}
|
|
505
|
-
seen.add(key);
|
|
506
|
-
}
|
|
507
|
-
}
|
|
508
|
-
const prefix = Array.isArray(schema.prefixItems) ? schema.prefixItems : [];
|
|
509
|
-
if (schema.contains !== undefined) {
|
|
510
|
-
const matches = value.reduce(
|
|
511
|
-
(count, item, i) =>
|
|
512
|
-
count + (_branchValid(item, schema.contains, root, depth, externalSchemas) ? 1 : 0),
|
|
513
|
-
0,
|
|
514
|
-
);
|
|
515
|
-
const min = Number.isFinite(schema.minContains) ? schema.minContains : 1;
|
|
516
|
-
const max = Number.isFinite(schema.maxContains) ? schema.maxContains : Infinity;
|
|
517
|
-
if (matches < min || matches > max) {
|
|
518
|
-
_err(
|
|
519
|
-
errors,
|
|
520
|
-
"contains",
|
|
521
|
-
inst,
|
|
522
|
-
[...sch, "contains"],
|
|
523
|
-
`array contains ${matches} matching item(s), expected ${min}${max === Infinity ? "+" : `..${max}`}`,
|
|
524
|
-
);
|
|
525
|
-
}
|
|
526
|
-
}
|
|
527
|
-
value.forEach((item, i) => {
|
|
528
|
-
if (i < prefix.length) {
|
|
529
|
-
_validate(
|
|
530
|
-
item,
|
|
531
|
-
prefix[i],
|
|
532
|
-
[...inst, i],
|
|
533
|
-
[...sch, "prefixItems", i],
|
|
534
|
-
root,
|
|
535
|
-
errors,
|
|
536
|
-
depth,
|
|
537
|
-
externalSchemas,
|
|
538
|
-
);
|
|
539
|
-
} else if (schema.items !== undefined) {
|
|
540
|
-
_validate(
|
|
541
|
-
item,
|
|
542
|
-
schema.items,
|
|
543
|
-
[...inst, i],
|
|
544
|
-
[...sch, "items"],
|
|
545
|
-
root,
|
|
546
|
-
errors,
|
|
547
|
-
depth,
|
|
548
|
-
externalSchemas,
|
|
549
|
-
);
|
|
550
|
-
}
|
|
551
|
-
});
|
|
298
|
+
function mergedExternalSchemas(schema, externalSchemas) {
|
|
299
|
+
const attached =
|
|
300
|
+
schema && typeof schema === "object"
|
|
301
|
+
? ATTACHED_EXTERNAL_SCHEMAS.get(schema)
|
|
302
|
+
: null;
|
|
303
|
+
return {
|
|
304
|
+
baseUri: attached?.baseUri || null,
|
|
305
|
+
externalSchemas: {
|
|
306
|
+
...(attached?.externalSchemas || {}),
|
|
307
|
+
...(externalSchemas || {}),
|
|
308
|
+
},
|
|
309
|
+
};
|
|
552
310
|
}
|
|
553
311
|
|
|
554
|
-
function
|
|
555
|
-
const
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
_err(
|
|
561
|
-
errors,
|
|
562
|
-
"minProperties",
|
|
563
|
-
inst,
|
|
564
|
-
[...sch, "minProperties"],
|
|
565
|
-
`object has fewer than minProperties ${schema.minProperties}`,
|
|
566
|
-
);
|
|
567
|
-
}
|
|
568
|
-
if (
|
|
569
|
-
Number.isFinite(schema.maxProperties) &&
|
|
570
|
-
keys.length > schema.maxProperties
|
|
571
|
-
) {
|
|
572
|
-
_err(
|
|
573
|
-
errors,
|
|
574
|
-
"maxProperties",
|
|
575
|
-
inst,
|
|
576
|
-
[...sch, "maxProperties"],
|
|
577
|
-
`object has more than maxProperties ${schema.maxProperties}`,
|
|
578
|
-
);
|
|
579
|
-
}
|
|
580
|
-
for (const req of schema.required || []) {
|
|
581
|
-
if (!Object.prototype.hasOwnProperty.call(value, req)) {
|
|
582
|
-
_err(
|
|
583
|
-
errors,
|
|
584
|
-
"required",
|
|
585
|
-
inst,
|
|
586
|
-
[...sch, "required"],
|
|
587
|
-
`missing required property "${req}"`,
|
|
588
|
-
);
|
|
589
|
-
}
|
|
590
|
-
}
|
|
591
|
-
const props = schema.properties || {};
|
|
592
|
-
const patterns = schema.patternProperties || {};
|
|
593
|
-
const patternEntries = Object.entries(patterns).map(([pattern, sub]) => {
|
|
594
|
-
try { return [new RegExp(pattern), pattern, sub]; } catch { return [null, pattern, sub]; }
|
|
312
|
+
function createAjv(externalSchemas = {}) {
|
|
313
|
+
const ajv = new Ajv2020({
|
|
314
|
+
allErrors: true,
|
|
315
|
+
strict: false,
|
|
316
|
+
validateFormats: true,
|
|
317
|
+
logger: false,
|
|
595
318
|
});
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
continue;
|
|
615
|
-
}
|
|
616
|
-
if (schema.additionalProperties === false || schema.unevaluatedProperties === false) {
|
|
617
|
-
_err(
|
|
618
|
-
errors,
|
|
619
|
-
schema.additionalProperties === false ? "additionalProperties" : "unevaluatedProperties",
|
|
620
|
-
[...inst, k],
|
|
621
|
-
[...sch, schema.additionalProperties === false ? "additionalProperties" : "unevaluatedProperties"],
|
|
622
|
-
`unexpected property "${k}"`,
|
|
623
|
-
);
|
|
624
|
-
} else if (schema.additionalProperties && typeof schema.additionalProperties === "object") {
|
|
625
|
-
_validate(
|
|
626
|
-
v,
|
|
627
|
-
schema.additionalProperties,
|
|
628
|
-
[...inst, k],
|
|
629
|
-
[...sch, "additionalProperties"],
|
|
630
|
-
root,
|
|
631
|
-
errors,
|
|
632
|
-
depth,
|
|
633
|
-
externalSchemas,
|
|
634
|
-
);
|
|
635
|
-
}
|
|
636
|
-
}
|
|
637
|
-
}
|
|
638
|
-
if (schema.propertyNames !== undefined) {
|
|
639
|
-
for (const key of keys) {
|
|
640
|
-
_validate(key, schema.propertyNames, [...inst, key], [...sch, "propertyNames"], root, errors, depth, externalSchemas);
|
|
641
|
-
}
|
|
642
|
-
}
|
|
643
|
-
if (schema.dependentRequired && typeof schema.dependentRequired === "object") {
|
|
644
|
-
for (const [key, required] of Object.entries(schema.dependentRequired)) {
|
|
645
|
-
if (!Object.prototype.hasOwnProperty.call(value, key)) continue;
|
|
646
|
-
for (const dep of Array.isArray(required) ? required : []) {
|
|
647
|
-
if (!Object.prototype.hasOwnProperty.call(value, dep)) {
|
|
648
|
-
_err(errors, "dependentRequired", inst, [...sch, "dependentRequired", key], `property "${key}" requires property "${dep}"`);
|
|
649
|
-
}
|
|
650
|
-
}
|
|
319
|
+
addFormats(ajv, { mode: "full" });
|
|
320
|
+
// ajv-formats historically rejects RFC 3339 leap seconds. Draft 2020-12
|
|
321
|
+
// delegates to RFC 3339, whose full-time grammar permits second 60.
|
|
322
|
+
ajv.addFormat("date", { type: "string", validate: checkDate });
|
|
323
|
+
ajv.addFormat("time", { type: "string", validate: checkTime });
|
|
324
|
+
ajv.addFormat("date-time", {
|
|
325
|
+
type: "string",
|
|
326
|
+
validate: checkDateTime,
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
const registered = new Set();
|
|
330
|
+
for (const [rawUri, document] of Object.entries(externalSchemas || {})) {
|
|
331
|
+
if (
|
|
332
|
+
document === undefined ||
|
|
333
|
+
(!document && document !== false) ||
|
|
334
|
+
(typeof document !== "object" && typeof document !== "boolean")
|
|
335
|
+
) {
|
|
336
|
+
continue;
|
|
651
337
|
}
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
338
|
+
let uri = String(rawUri);
|
|
339
|
+
try {
|
|
340
|
+
const parsed = new URL(uri);
|
|
341
|
+
parsed.hash = "";
|
|
342
|
+
uri = parsed.href;
|
|
343
|
+
} catch {
|
|
344
|
+
// Ajv also accepts caller-defined non-URL registry keys.
|
|
658
345
|
}
|
|
346
|
+
if (registered.has(uri)) continue;
|
|
347
|
+
ajv.addSchema(document, uri);
|
|
348
|
+
registered.add(uri);
|
|
659
349
|
}
|
|
350
|
+
return ajv;
|
|
660
351
|
}
|
|
661
352
|
|
|
662
|
-
function
|
|
663
|
-
const
|
|
664
|
-
|
|
665
|
-
|
|
353
|
+
function unresolvedReferenceError(cause, schema) {
|
|
354
|
+
const missingReference =
|
|
355
|
+
Boolean(cause?.missingRef) || cause?.name === "MissingRefError";
|
|
356
|
+
const dynamic =
|
|
357
|
+
String(cause?.message || "").includes("$dynamicRef") &&
|
|
358
|
+
schema?.$dynamicRef;
|
|
359
|
+
const keyword = missingReference
|
|
360
|
+
? dynamic
|
|
361
|
+
? "$dynamicRef"
|
|
362
|
+
: "$ref"
|
|
363
|
+
: "schema";
|
|
364
|
+
return {
|
|
365
|
+
code: keyword,
|
|
366
|
+
keyword,
|
|
367
|
+
instancePath: "",
|
|
368
|
+
schemaPath: keyword === "schema" ? "" : `/${keyword}`,
|
|
369
|
+
message: missingReference && cause?.missingRef
|
|
370
|
+
? `cannot resolve ${keyword} "${cause.missingRef}"`
|
|
371
|
+
: cause?.message || "cannot compile JSON Schema",
|
|
372
|
+
};
|
|
666
373
|
}
|
|
667
374
|
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
)
|
|
689
|
-
if (
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
if (
|
|
700
|
-
schema.not !== undefined &&
|
|
701
|
-
_branchValid(value, schema.not, root, depth, externalSchemas)
|
|
702
|
-
) {
|
|
703
|
-
_err(
|
|
704
|
-
errors,
|
|
705
|
-
"not",
|
|
706
|
-
inst,
|
|
707
|
-
[...sch, "not"],
|
|
708
|
-
"value must NOT match the `not` schema",
|
|
709
|
-
);
|
|
710
|
-
}
|
|
375
|
+
/**
|
|
376
|
+
* Compile a root schema and all caller-provided/attached external documents.
|
|
377
|
+
* The returned function is synchronous because every remote document must be
|
|
378
|
+
* resolved before a structured-output turn starts.
|
|
379
|
+
*/
|
|
380
|
+
export function compileSchema(schema, { externalSchemas = {} } = {}) {
|
|
381
|
+
const cacheable =
|
|
382
|
+
schema &&
|
|
383
|
+
typeof schema === "object" &&
|
|
384
|
+
Object.keys(externalSchemas || {}).length === 0;
|
|
385
|
+
const digest = cacheable ? computeSchemaDigest(schema) : null;
|
|
386
|
+
const cached = cacheable ? COMPILED_VALIDATORS.get(schema) : null;
|
|
387
|
+
if (cached?.digest === digest) {
|
|
388
|
+
return cached.validator;
|
|
389
|
+
}
|
|
390
|
+
const context = mergedExternalSchemas(schema, externalSchemas);
|
|
391
|
+
const ajv = createAjv(context.externalSchemas);
|
|
392
|
+
let validator;
|
|
393
|
+
if (context.baseUri) {
|
|
394
|
+
ajv.addSchema(schema, context.baseUri);
|
|
395
|
+
validator = ajv.getSchema(context.baseUri);
|
|
396
|
+
if (!validator) {
|
|
397
|
+
throw new Error(`could not compile schema at ${context.baseUri}`);
|
|
398
|
+
}
|
|
399
|
+
} else {
|
|
400
|
+
validator = ajv.compile(schema);
|
|
401
|
+
}
|
|
402
|
+
if (cacheable) {
|
|
403
|
+
COMPILED_VALIDATORS.set(schema, { digest, validator });
|
|
404
|
+
}
|
|
405
|
+
return validator;
|
|
711
406
|
}
|
|
712
407
|
|
|
713
|
-
// ─── schema meta-validation (startup) ────────────────────────────────────────
|
|
714
|
-
|
|
715
408
|
/**
|
|
716
|
-
*
|
|
717
|
-
* malformed schema (bad type value, non-array required, non-number numeric
|
|
718
|
-
* keyword, invalid regex, non-object properties, …) so a broken contract fails
|
|
719
|
-
* fast instead of silently mis-validating every value.
|
|
409
|
+
* Validate `value` against a Draft 2020-12 schema.
|
|
720
410
|
*
|
|
721
|
-
*
|
|
411
|
+
* Compilation failures (most notably unresolved references) are represented in
|
|
412
|
+
* the same stable error envelope instead of escaping or falling back to text.
|
|
722
413
|
*/
|
|
723
|
-
export function
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
414
|
+
export function validate(value, schema, { externalSchemas = {} } = {}) {
|
|
415
|
+
let validator;
|
|
416
|
+
try {
|
|
417
|
+
validator = compileSchema(schema, { externalSchemas });
|
|
418
|
+
} catch (cause) {
|
|
419
|
+
return {
|
|
420
|
+
valid: false,
|
|
421
|
+
errors: [unresolvedReferenceError(cause, schema)],
|
|
422
|
+
};
|
|
423
|
+
}
|
|
424
|
+
const valid = validator(value);
|
|
425
|
+
return {
|
|
426
|
+
valid: Boolean(valid),
|
|
427
|
+
errors: valid ? [] : normalizeValidationErrors(validator.errors),
|
|
428
|
+
};
|
|
727
429
|
}
|
|
728
430
|
|
|
729
|
-
function
|
|
730
|
-
|
|
431
|
+
function pointerSegments(pointer) {
|
|
432
|
+
if (!pointer) return [];
|
|
433
|
+
return String(pointer)
|
|
434
|
+
.replace(/^\//, "")
|
|
435
|
+
.split("/")
|
|
436
|
+
.map(decodePointerSegment);
|
|
731
437
|
}
|
|
732
438
|
|
|
733
|
-
function
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
return;
|
|
439
|
+
function metaCode(error) {
|
|
440
|
+
const segments = pointerSegments(error?.instancePath);
|
|
441
|
+
for (let index = segments.length - 1; index >= 0; index -= 1) {
|
|
442
|
+
if (SCHEMA_KEYWORDS.has(segments[index])) return segments[index];
|
|
738
443
|
}
|
|
739
|
-
|
|
444
|
+
return error?.keyword || "schema";
|
|
445
|
+
}
|
|
740
446
|
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
!Array.isArray(schema.required) ||
|
|
752
|
-
schema.required.some((r) => typeof r !== "string")
|
|
753
|
-
) {
|
|
754
|
-
_metaErr(
|
|
755
|
-
errors,
|
|
756
|
-
"required",
|
|
757
|
-
[...segs, "required"],
|
|
758
|
-
"required must be an array of strings",
|
|
759
|
-
);
|
|
760
|
-
}
|
|
761
|
-
}
|
|
762
|
-
if (schema.enum !== undefined && !Array.isArray(schema.enum)) {
|
|
763
|
-
_metaErr(errors, "enum", [...segs, "enum"], "enum must be an array");
|
|
764
|
-
}
|
|
765
|
-
for (const k of [
|
|
766
|
-
"minimum",
|
|
767
|
-
"maximum",
|
|
768
|
-
"exclusiveMinimum",
|
|
769
|
-
"exclusiveMaximum",
|
|
770
|
-
"multipleOf",
|
|
771
|
-
]) {
|
|
772
|
-
if (schema[k] !== undefined && typeof schema[k] !== "number") {
|
|
773
|
-
_metaErr(errors, k, [...segs, k], `${k} must be a number`);
|
|
774
|
-
}
|
|
775
|
-
}
|
|
447
|
+
function normalizeMetaErrors(errors) {
|
|
448
|
+
return (errors || []).map((error) => ({
|
|
449
|
+
code: metaCode(error),
|
|
450
|
+
schemaPath: String(error?.instancePath || ""),
|
|
451
|
+
message: error?.message || "invalid JSON Schema",
|
|
452
|
+
}));
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
function collectRegexMetaErrors(schema, segments = [], seen = new Set()) {
|
|
456
|
+
const errors = [];
|
|
776
457
|
if (
|
|
777
|
-
schema
|
|
778
|
-
|
|
779
|
-
schema
|
|
458
|
+
schema === true ||
|
|
459
|
+
schema === false ||
|
|
460
|
+
!schema ||
|
|
461
|
+
typeof schema !== "object" ||
|
|
462
|
+
seen.has(schema)
|
|
780
463
|
) {
|
|
781
|
-
|
|
782
|
-
errors,
|
|
783
|
-
"multipleOf",
|
|
784
|
-
[...segs, "multipleOf"],
|
|
785
|
-
"multipleOf must be > 0",
|
|
786
|
-
);
|
|
464
|
+
return errors;
|
|
787
465
|
}
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
schema[k] !== undefined &&
|
|
800
|
-
(!Number.isInteger(schema[k]) || schema[k] < 0)
|
|
801
|
-
) {
|
|
802
|
-
_metaErr(errors, k, [...segs, k], `${k} must be a non-negative integer`);
|
|
466
|
+
seen.add(schema);
|
|
467
|
+
|
|
468
|
+
if (typeof schema.pattern === "string") {
|
|
469
|
+
try {
|
|
470
|
+
new RegExp(schema.pattern);
|
|
471
|
+
} catch (cause) {
|
|
472
|
+
errors.push({
|
|
473
|
+
code: "pattern",
|
|
474
|
+
schemaPath: jsonPointer([...segments, "pattern"]),
|
|
475
|
+
message: `pattern is not a valid regular expression: ${cause.message}`,
|
|
476
|
+
});
|
|
803
477
|
}
|
|
804
478
|
}
|
|
805
|
-
if (
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
"pattern must be a string",
|
|
812
|
-
);
|
|
813
|
-
} else {
|
|
479
|
+
if (
|
|
480
|
+
schema.patternProperties &&
|
|
481
|
+
typeof schema.patternProperties === "object" &&
|
|
482
|
+
!Array.isArray(schema.patternProperties)
|
|
483
|
+
) {
|
|
484
|
+
for (const pattern of Object.keys(schema.patternProperties)) {
|
|
814
485
|
try {
|
|
815
|
-
new RegExp(
|
|
816
|
-
} catch (
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
486
|
+
new RegExp(pattern);
|
|
487
|
+
} catch (cause) {
|
|
488
|
+
errors.push({
|
|
489
|
+
code: "patternProperties",
|
|
490
|
+
schemaPath: jsonPointer([
|
|
491
|
+
...segments,
|
|
492
|
+
"patternProperties",
|
|
493
|
+
pattern,
|
|
494
|
+
]),
|
|
495
|
+
message: `patternProperties key is not a valid regular expression: ${cause.message}`,
|
|
496
|
+
});
|
|
823
497
|
}
|
|
824
498
|
}
|
|
825
499
|
}
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
}
|
|
850
|
-
}
|
|
851
|
-
if (schema.properties !== undefined) {
|
|
852
|
-
if (
|
|
853
|
-
typeof schema.properties !== "object" ||
|
|
854
|
-
Array.isArray(schema.properties)
|
|
855
|
-
) {
|
|
856
|
-
_metaErr(
|
|
857
|
-
errors,
|
|
858
|
-
"properties",
|
|
859
|
-
[...segs, "properties"],
|
|
860
|
-
"properties must be an object",
|
|
500
|
+
|
|
501
|
+
const single = [
|
|
502
|
+
"additionalProperties",
|
|
503
|
+
"contains",
|
|
504
|
+
"contentSchema",
|
|
505
|
+
"else",
|
|
506
|
+
"if",
|
|
507
|
+
"items",
|
|
508
|
+
"not",
|
|
509
|
+
"propertyNames",
|
|
510
|
+
"then",
|
|
511
|
+
"unevaluatedItems",
|
|
512
|
+
"unevaluatedProperties",
|
|
513
|
+
];
|
|
514
|
+
for (const keyword of single) {
|
|
515
|
+
const child = schema[keyword];
|
|
516
|
+
if (child && typeof child === "object") {
|
|
517
|
+
errors.push(
|
|
518
|
+
...collectRegexMetaErrors(
|
|
519
|
+
child,
|
|
520
|
+
[...segments, keyword],
|
|
521
|
+
seen,
|
|
522
|
+
),
|
|
861
523
|
);
|
|
862
|
-
} else {
|
|
863
|
-
for (const [k, sub] of Object.entries(schema.properties)) {
|
|
864
|
-
_metaValidate(sub, [...segs, "properties", k], errors, depth + 1);
|
|
865
|
-
}
|
|
866
|
-
}
|
|
867
|
-
}
|
|
868
|
-
for (const k of ["patternProperties", "dependentSchemas"]) {
|
|
869
|
-
if (schema[k] === undefined) continue;
|
|
870
|
-
if (typeof schema[k] !== "object" || Array.isArray(schema[k])) {
|
|
871
|
-
_metaErr(errors, k, [...segs, k], `${k} must be an object`);
|
|
872
|
-
} else {
|
|
873
|
-
for (const [name, sub] of Object.entries(schema[k])) {
|
|
874
|
-
if (k === "patternProperties") {
|
|
875
|
-
try { new RegExp(name); } catch (e) { _metaErr(errors, k, [...segs, k, name], `invalid regex: ${e.message}`); }
|
|
876
|
-
}
|
|
877
|
-
_metaValidate(sub, [...segs, k, name], errors, depth + 1);
|
|
878
|
-
}
|
|
879
524
|
}
|
|
880
525
|
}
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
_metaErr(errors, "dependentRequired", [...segs, "dependentRequired", key], "dependentRequired values must be arrays of strings");
|
|
891
|
-
}
|
|
892
|
-
}
|
|
893
|
-
}
|
|
894
|
-
}
|
|
895
|
-
if (schema.contains !== undefined) {
|
|
896
|
-
_metaValidate(schema.contains, [...segs, "contains"], errors, depth + 1);
|
|
897
|
-
}
|
|
898
|
-
if (
|
|
899
|
-
schema.additionalProperties !== undefined &&
|
|
900
|
-
typeof schema.additionalProperties === "object"
|
|
901
|
-
) {
|
|
902
|
-
_metaValidate(
|
|
903
|
-
schema.additionalProperties,
|
|
904
|
-
[...segs, "additionalProperties"],
|
|
905
|
-
errors,
|
|
906
|
-
depth + 1,
|
|
907
|
-
);
|
|
908
|
-
}
|
|
909
|
-
if (schema.items !== undefined) {
|
|
910
|
-
_metaValidate(schema.items, [...segs, "items"], errors, depth + 1);
|
|
911
|
-
}
|
|
912
|
-
if (schema.prefixItems !== undefined) {
|
|
913
|
-
if (!Array.isArray(schema.prefixItems)) {
|
|
914
|
-
_metaErr(
|
|
915
|
-
errors,
|
|
916
|
-
"prefixItems",
|
|
917
|
-
[...segs, "prefixItems"],
|
|
918
|
-
"prefixItems must be an array of schemas",
|
|
526
|
+
for (const keyword of ["allOf", "anyOf", "oneOf", "prefixItems"]) {
|
|
527
|
+
if (!Array.isArray(schema[keyword])) continue;
|
|
528
|
+
schema[keyword].forEach((child, index) => {
|
|
529
|
+
errors.push(
|
|
530
|
+
...collectRegexMetaErrors(
|
|
531
|
+
child,
|
|
532
|
+
[...segments, keyword, index],
|
|
533
|
+
seen,
|
|
534
|
+
),
|
|
919
535
|
);
|
|
920
|
-
}
|
|
921
|
-
schema.prefixItems.forEach((sub, i) =>
|
|
922
|
-
_metaValidate(sub, [...segs, "prefixItems", i], errors, depth + 1),
|
|
923
|
-
);
|
|
924
|
-
}
|
|
925
|
-
}
|
|
926
|
-
for (const k of ["allOf", "anyOf", "oneOf"]) {
|
|
927
|
-
if (schema[k] !== undefined) {
|
|
928
|
-
if (!Array.isArray(schema[k])) {
|
|
929
|
-
_metaErr(errors, k, [...segs, k], `${k} must be an array of schemas`);
|
|
930
|
-
} else {
|
|
931
|
-
schema[k].forEach((sub, i) =>
|
|
932
|
-
_metaValidate(sub, [...segs, k, i], errors, depth + 1),
|
|
933
|
-
);
|
|
934
|
-
}
|
|
935
|
-
}
|
|
936
|
-
}
|
|
937
|
-
if (schema.not !== undefined)
|
|
938
|
-
_metaValidate(schema.not, [...segs, "not"], errors, depth + 1);
|
|
939
|
-
if (schema.format !== undefined && typeof schema.format !== "string") {
|
|
940
|
-
_metaErr(errors, "format", [...segs, "format"], "format must be a string");
|
|
941
|
-
}
|
|
942
|
-
for (const k of ["if", "then", "else"]) {
|
|
943
|
-
if (schema[k] !== undefined) {
|
|
944
|
-
_metaValidate(schema[k], [...segs, k], errors, depth + 1);
|
|
945
|
-
}
|
|
536
|
+
});
|
|
946
537
|
}
|
|
947
|
-
for (const
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
538
|
+
for (const keyword of [
|
|
539
|
+
"$defs",
|
|
540
|
+
"definitions",
|
|
541
|
+
"dependentSchemas",
|
|
542
|
+
"patternProperties",
|
|
543
|
+
"properties",
|
|
544
|
+
]) {
|
|
545
|
+
const map = schema[keyword];
|
|
546
|
+
if (!map || typeof map !== "object" || Array.isArray(map)) continue;
|
|
547
|
+
for (const [name, child] of Object.entries(map)) {
|
|
548
|
+
errors.push(
|
|
549
|
+
...collectRegexMetaErrors(
|
|
550
|
+
child,
|
|
551
|
+
[...segments, keyword, name],
|
|
552
|
+
seen,
|
|
553
|
+
),
|
|
554
|
+
);
|
|
964
555
|
}
|
|
965
556
|
}
|
|
557
|
+
return errors;
|
|
966
558
|
}
|
|
967
559
|
|
|
968
|
-
|
|
560
|
+
/**
|
|
561
|
+
* Validate the schema itself against the Draft 2020-12 meta-schema, then
|
|
562
|
+
* compile it to catch invalid regular expressions and unsupported dialects.
|
|
563
|
+
* Missing external documents are left to the bounded reference resolver.
|
|
564
|
+
*/
|
|
565
|
+
export function validateSchema(schema) {
|
|
566
|
+
const ajv = createAjv();
|
|
567
|
+
let valid;
|
|
568
|
+
try {
|
|
569
|
+
valid = ajv.validateSchema(schema);
|
|
570
|
+
} catch (cause) {
|
|
571
|
+
return {
|
|
572
|
+
valid: false,
|
|
573
|
+
errors: [
|
|
574
|
+
{
|
|
575
|
+
code: "$schema",
|
|
576
|
+
schemaPath: "/$schema",
|
|
577
|
+
message: cause.message,
|
|
578
|
+
},
|
|
579
|
+
],
|
|
580
|
+
};
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
const errors = valid ? [] : normalizeMetaErrors(ajv.errors);
|
|
584
|
+
errors.push(...collectRegexMetaErrors(schema));
|
|
585
|
+
return { valid: errors.length === 0, errors };
|
|
586
|
+
}
|
|
969
587
|
|
|
970
588
|
/**
|
|
971
|
-
*
|
|
972
|
-
*
|
|
973
|
-
* when invalid — the coded/pointered errors. Never returns free text.
|
|
589
|
+
* Assert that the fully-resolved schema graph compiles. Used at startup after
|
|
590
|
+
* the bounded local/HTTPS resolver has populated the external registry.
|
|
974
591
|
*/
|
|
592
|
+
export function assertSchemaCompiles(schema, { externalSchemas = {} } = {}) {
|
|
593
|
+
try {
|
|
594
|
+
compileSchema(schema, { externalSchemas });
|
|
595
|
+
} catch (cause) {
|
|
596
|
+
const detail = unresolvedReferenceError(cause, schema);
|
|
597
|
+
const error = new Error(detail.message, { cause });
|
|
598
|
+
error.code = "JSON_SCHEMA_COMPILE_FAILED";
|
|
599
|
+
error.schemaError = detail;
|
|
600
|
+
throw error;
|
|
601
|
+
}
|
|
602
|
+
return true;
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
/** Build the terminal protocol event for one structured value. */
|
|
975
606
|
export function buildStructuredResultEvent({ schema, value }) {
|
|
976
607
|
const { valid, errors } = validate(value, schema);
|
|
977
608
|
const event = {
|