@yalehwang/cloudpit 2.0.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/api-docs.html +3 -3
- package/dist/assets/{abnfDiagram-N423BO3Z-D5yLnMt9.js → abnfDiagram-N423BO3Z-D7Xt05gV.js} +1 -1
- package/dist/assets/{abnfDiagram-N423BO3Z-bTHtQIhR.js → abnfDiagram-N423BO3Z-dmPRRPEm.js} +1 -1
- package/dist/assets/{arc-1DmrBEyG.js → arc-B7W3R5qH.js} +1 -1
- package/dist/assets/{arc-BlGK--01.js → arc-BHMlIcaQ.js} +1 -1
- package/dist/assets/{architectureDiagram-T3A2C74G--lzbKBn9.js → architectureDiagram-T3A2C74G-CUUpAoN0.js} +1 -1
- package/dist/assets/{architectureDiagram-T3A2C74G-7lKNAbUU.js → architectureDiagram-T3A2C74G-DpXhbHxu.js} +1 -1
- package/dist/assets/{blockDiagram-VBNYF7ZC-17cdO9zs.js → blockDiagram-VBNYF7ZC-Bf0yfNxj.js} +1 -1
- package/dist/assets/{blockDiagram-VBNYF7ZC-BPavJfDj.js → blockDiagram-VBNYF7ZC-Cki9AOhc.js} +1 -1
- package/dist/assets/{c4Diagram-5PPSVZJV-Bm1-2w3b.js → c4Diagram-5PPSVZJV-DK2RqNDO.js} +1 -1
- package/dist/assets/{c4Diagram-5PPSVZJV-aHvFL3Wb.js → c4Diagram-5PPSVZJV-DyhTF0e5.js} +1 -1
- package/dist/assets/channel-DTLMNzbH.js +1 -0
- package/dist/assets/channel-DiqxUuPf.js +1 -0
- package/dist/assets/{chunk-2GRJ4B5K-D7WqZewM.js → chunk-2GRJ4B5K-BYtByJbf.js} +1 -1
- package/dist/assets/{chunk-2GRJ4B5K-DbwzBiPF.js → chunk-2GRJ4B5K-CksFgocc.js} +1 -1
- package/dist/assets/{chunk-2Q5K7J3B-DdCr89uy.js → chunk-2Q5K7J3B-D2kQa5TZ.js} +1 -1
- package/dist/assets/{chunk-2Q5K7J3B-TFd0ECKO.js → chunk-2Q5K7J3B-NSSX86FA.js} +1 -1
- package/dist/assets/{chunk-5RXB4S5H-BdhRBpEi.js → chunk-5RXB4S5H-DNNYC8_t.js} +1 -1
- package/dist/assets/{chunk-5RXB4S5H-D0SQ3nIe.js → chunk-5RXB4S5H-Rx0YaqNI.js} +1 -1
- package/dist/assets/{chunk-5VM5RSS4-B9Lv3cWR.js → chunk-5VM5RSS4-B1k4I4CX.js} +1 -1
- package/dist/assets/{chunk-5VM5RSS4-DioqTIvB.js → chunk-5VM5RSS4-CsvQj3QC.js} +1 -1
- package/dist/assets/{chunk-6Q2QTUOP-09bwlcIv.js → chunk-6Q2QTUOP-48-dEgAs.js} +1 -1
- package/dist/assets/{chunk-6Q2QTUOP-JuhPJZZL.js → chunk-6Q2QTUOP-DCMSW5_0.js} +1 -1
- package/dist/assets/{chunk-GF5L2VYU-C6f2SgCw.js → chunk-GF5L2VYU-CGyvOsdN.js} +1 -1
- package/dist/assets/{chunk-GF5L2VYU-DzD9aOyp.js → chunk-GF5L2VYU-YXe113hp.js} +1 -1
- package/dist/assets/{chunk-JWPE2WC7-BggnWNZv.js → chunk-JWPE2WC7-BT7vCW4x.js} +1 -1
- package/dist/assets/{chunk-JWPE2WC7-Dte_Ax9K.js → chunk-JWPE2WC7-D2K1Oywq.js} +1 -1
- package/dist/assets/{chunk-KBJHAD2P-9TCQV92m.js → chunk-KBJHAD2P-CQZWamtE.js} +1 -1
- package/dist/assets/{chunk-KBJHAD2P-DDnJp7kJ.js → chunk-KBJHAD2P-gpEwZrNy.js} +1 -1
- package/dist/assets/{chunk-RYQCIY6F-CE2Y4D_h.js → chunk-RYQCIY6F-BI0fMtjY.js} +1 -1
- package/dist/assets/{chunk-RYQCIY6F-DsgFSTNw.js → chunk-RYQCIY6F-vguRdb4F.js} +1 -1
- package/dist/assets/{chunk-XXDRQBXY-B1FIfsqq.js → chunk-XXDRQBXY-B99qH1_w.js} +1 -1
- package/dist/assets/{chunk-XXDRQBXY-B4YfaqTO.js → chunk-XXDRQBXY-DZLJnbBB.js} +1 -1
- package/dist/assets/classDiagram-JCYQIIEL-BaV-s6x8.js +1 -0
- package/dist/assets/classDiagram-JCYQIIEL-D4M03B4L.js +1 -0
- package/dist/assets/classDiagram-v2-OCEON4UE-BaV-s6x8.js +1 -0
- package/dist/assets/classDiagram-v2-OCEON4UE-D4M03B4L.js +1 -0
- package/dist/assets/{cose-bilkent-JH36ORCC-C1AG5Pib.js → cose-bilkent-JH36ORCC-EsVnz-E6.js} +1 -1
- package/dist/assets/{cose-bilkent-JH36ORCC-lMwWDibI.js → cose-bilkent-JH36ORCC-VI2OjCIx.js} +1 -1
- package/dist/assets/{cynefin-VYW2F7L2-BlabniRL.js → cynefin-VYW2F7L2-BVYg0ixs.js} +1 -1
- package/dist/assets/{cynefin-VYW2F7L2-W-c-z4IW.js → cynefin-VYW2F7L2-BnHmA5I2.js} +1 -1
- package/dist/assets/{cynefinDiagram-MW4NZA55-BtTep_pq.js → cynefinDiagram-MW4NZA55-B-OCRLIw.js} +1 -1
- package/dist/assets/{cynefinDiagram-MW4NZA55-Due6EIPh.js → cynefinDiagram-MW4NZA55-ItLlzjDR.js} +1 -1
- package/dist/assets/{dagre-VZM6K2ZE-CWLaFfdt.js → dagre-VZM6K2ZE-DkVDkAWJ.js} +1 -1
- package/dist/assets/{dagre-VZM6K2ZE-DZldvqiQ.js → dagre-VZM6K2ZE-bYR4z8-Z.js} +1 -1
- package/dist/assets/{diagram-7IWD3JNH-RnmUHYwN.js → diagram-7IWD3JNH-DjXMue67.js} +1 -1
- package/dist/assets/{diagram-7IWD3JNH-Di-7-nSA.js → diagram-7IWD3JNH-FNEXlm5k.js} +1 -1
- package/dist/assets/{diagram-B4RE2ZJO-Wybyn8fw.js → diagram-B4RE2ZJO-73NYiO_P.js} +1 -1
- package/dist/assets/{diagram-B4RE2ZJO-mrK79C2E.js → diagram-B4RE2ZJO-dB4QHq0D.js} +1 -1
- package/dist/assets/{diagram-LBJQPF4R-JY6FqSFu.js → diagram-LBJQPF4R-DyKT610O.js} +1 -1
- package/dist/assets/{diagram-LBJQPF4R-sWhu2VLy.js → diagram-LBJQPF4R-rMgmnjvd.js} +1 -1
- package/dist/assets/{diagram-Q27KOJAE-BJ4T0WGp.js → diagram-Q27KOJAE-BvsfmmQt.js} +1 -1
- package/dist/assets/{diagram-Q27KOJAE-DyGT9KhB.js → diagram-Q27KOJAE-c6DEjLQX.js} +1 -1
- package/dist/assets/{diagram-UB23O5K3-oXFJAndB.js → diagram-UB23O5K3-BMo2VXVb.js} +1 -1
- package/dist/assets/{diagram-UB23O5K3-tTd8qwEM.js → diagram-UB23O5K3-DRMKefnZ.js} +1 -1
- package/dist/assets/{ebnfDiagram-BXEA7PRR-DV67cBe6.js → ebnfDiagram-BXEA7PRR-BrOAekWh.js} +1 -1
- package/dist/assets/{ebnfDiagram-BXEA7PRR-OQGm8tPs.js → ebnfDiagram-BXEA7PRR-YmQkylwV.js} +1 -1
- package/dist/assets/{elk-BRx4gEGg.js → elk-Hz3WRESK.js} +1 -1
- package/dist/assets/{elk-CdAQJ-NA.js → elk-tPSZwRVB.js} +1 -1
- package/dist/assets/{erDiagram-JOGREHBK-BhD2xZ2I.js → erDiagram-JOGREHBK-BlaZnoQi.js} +1 -1
- package/dist/assets/{erDiagram-JOGREHBK-BqP8DqqW.js → erDiagram-JOGREHBK-CRCfRUXt.js} +1 -1
- package/dist/assets/{flowDiagram-UKHOOZJN-B4AjZxPJ.js → flowDiagram-UKHOOZJN-DQ1gbjxo.js} +1 -1
- package/dist/assets/{flowDiagram-UKHOOZJN-CP_1WsW4.js → flowDiagram-UKHOOZJN-Dinlbmds.js} +1 -1
- package/dist/assets/{ganttDiagram-PKOTCBZU-BMk6nngr.js → ganttDiagram-PKOTCBZU-CMGpcBZO.js} +1 -1
- package/dist/assets/{ganttDiagram-PKOTCBZU-B_ce39Ld.js → ganttDiagram-PKOTCBZU-Eh94zuPI.js} +1 -1
- package/dist/assets/{gitGraphDiagram-DS77QQ5N-CnZA42iA.js → gitGraphDiagram-DS77QQ5N-Bp8xIZbk.js} +1 -1
- package/dist/assets/{gitGraphDiagram-DS77QQ5N-DqEz_iTn.js → gitGraphDiagram-DS77QQ5N-Cu-b4YNw.js} +1 -1
- package/dist/assets/index-51e8fagd.css +32 -0
- package/dist/assets/{index-D5nLH7qP.js → index-CQE9C6k6.js} +356 -356
- package/dist/assets/{index-CqyOGerp.js → index-S8ow0XfK.js} +356 -356
- package/dist/assets/{infoDiagram-6WML65LV-BQf7m4X4.js → infoDiagram-6WML65LV-CClEEtTq.js} +1 -1
- package/dist/assets/{infoDiagram-6WML65LV-Dntf4YFM.js → infoDiagram-6WML65LV-QmHeojgl.js} +1 -1
- package/dist/assets/{ishikawaDiagram-WSZJBQD7-BDjsldAI.js → ishikawaDiagram-WSZJBQD7-BemSj9lN.js} +1 -1
- package/dist/assets/{ishikawaDiagram-WSZJBQD7-BqXpJeoL.js → ishikawaDiagram-WSZJBQD7-nw48g1vr.js} +1 -1
- package/dist/assets/{journeyDiagram-NVQOT4AX-CBcFzP2M.js → journeyDiagram-NVQOT4AX-DeNtO6OU.js} +1 -1
- package/dist/assets/{journeyDiagram-NVQOT4AX-DoWKjgKd.js → journeyDiagram-NVQOT4AX-q1lCfOYn.js} +1 -1
- package/dist/assets/{kanban-definition-27J2QSJJ-9bspl7j-.js → kanban-definition-27J2QSJJ-Bzhu6hql.js} +1 -1
- package/dist/assets/{kanban-definition-27J2QSJJ-Cjxa-Fbb.js → kanban-definition-27J2QSJJ-VfvD33YG.js} +1 -1
- package/dist/assets/{linear-YogAXmU_.js → linear-C0Kwu3Yp.js} +1 -1
- package/dist/assets/{linear-lIhI5Y8l.js → linear-CbM_4-fP.js} +1 -1
- package/dist/assets/{mermaid.core-2Spxcrg7.js → mermaid.core-G2huIsXn.js} +5 -5
- package/dist/assets/{mermaid.core-BxxInCBm.js → mermaid.core-fvTQFUX1.js} +5 -5
- package/dist/assets/{mindmap-definition-FAOFIHXS-DblO5EG4.js → mindmap-definition-FAOFIHXS-BONvuyvz.js} +1 -1
- package/dist/assets/{mindmap-definition-FAOFIHXS-_nlvJ9VK.js → mindmap-definition-FAOFIHXS-BzYawGKQ.js} +1 -1
- package/dist/assets/{pegDiagram-VL7TDLO6-BBJFPAZG.js → pegDiagram-VL7TDLO6-Df2HAKGH.js} +1 -1
- package/dist/assets/{pegDiagram-VL7TDLO6-DhRH7YRf.js → pegDiagram-VL7TDLO6-DnMwBHn_.js} +1 -1
- package/dist/assets/{pieDiagram-7S7Q4E2Y-4Qy7Nk4U.js → pieDiagram-7S7Q4E2Y-Cd3-a-dM.js} +1 -1
- package/dist/assets/{pieDiagram-7S7Q4E2Y-BUtdeYBT.js → pieDiagram-7S7Q4E2Y-LV6TOhOD.js} +1 -1
- package/dist/assets/{quadrantDiagram-CIZ2JOQS-BUoIDY1r.js → quadrantDiagram-CIZ2JOQS-8VOnA3FV.js} +1 -1
- package/dist/assets/{quadrantDiagram-CIZ2JOQS-D90kdHA1.js → quadrantDiagram-CIZ2JOQS-CnwxFPxh.js} +1 -1
- package/dist/assets/{railroadDiagram-AXF67PYL-CJf9--tj.js → railroadDiagram-AXF67PYL-CXBFj3Iu.js} +1 -1
- package/dist/assets/{railroadDiagram-AXF67PYL-D3S1oliC.js → railroadDiagram-AXF67PYL-DLoAPcsb.js} +1 -1
- package/dist/assets/{requirementDiagram-LRYGKXZP-C04jhjS6.js → requirementDiagram-LRYGKXZP-LrVlZXSz.js} +1 -1
- package/dist/assets/{requirementDiagram-LRYGKXZP-cQaGai5h.js → requirementDiagram-LRYGKXZP-pakITowN.js} +1 -1
- package/dist/assets/{sankeyDiagram-W5VNT64P-AT4cTVbY.js → sankeyDiagram-W5VNT64P-BIQQ8A1I.js} +1 -1
- package/dist/assets/{sankeyDiagram-W5VNT64P-DfFUdqYO.js → sankeyDiagram-W5VNT64P-C6QWOxNM.js} +1 -1
- package/dist/assets/{sequenceDiagram-SI44F4Z6-DbjhG030.js → sequenceDiagram-SI44F4Z6-CpHBe2CG.js} +1 -1
- package/dist/assets/{sequenceDiagram-SI44F4Z6-DKjdupix.js → sequenceDiagram-SI44F4Z6-cnLxaxcV.js} +1 -1
- package/dist/assets/{sizeCapture-X5ZJPWSS-CCcKLP8m.js → sizeCapture-X5ZJPWSS-3g-1onHD.js} +1 -1
- package/dist/assets/{sizeCapture-X5ZJPWSS-DGxRWxeo.js → sizeCapture-X5ZJPWSS-4U6b3PSL.js} +1 -1
- package/dist/assets/{stateDiagram-OKZ733FA-D7wGCNb7.js → stateDiagram-OKZ733FA-CFwVDdlY.js} +1 -1
- package/dist/assets/{stateDiagram-OKZ733FA-qdMAailp.js → stateDiagram-OKZ733FA-CKVGJcM2.js} +1 -1
- package/dist/assets/{stateDiagram-v2-UEYNNEHI-BbmmtA9w.js → stateDiagram-v2-UEYNNEHI-Dnbk8ftm.js} +1 -1
- package/dist/assets/{stateDiagram-v2-UEYNNEHI-Diz_hFvh.js → stateDiagram-v2-UEYNNEHI-rlA-woxH.js} +1 -1
- package/dist/assets/{swimlanes-SLNWSIFB-CZgIPz6I.js → swimlanes-SLNWSIFB-DCwz8-mf.js} +2 -2
- package/dist/assets/{swimlanes-SLNWSIFB-B16H-6OU.js → swimlanes-SLNWSIFB-M9Oumwnl.js} +2 -2
- package/dist/assets/swimlanesDiagram-ULZ7WXOC-CTtTyW6-.js +8 -0
- package/dist/assets/swimlanesDiagram-ULZ7WXOC-Caohx6WZ.js +8 -0
- package/dist/assets/{timeline-definition-Z64GVDOM-7r7OVlrT.js → timeline-definition-Z64GVDOM-BVmfjoWH.js} +1 -1
- package/dist/assets/{timeline-definition-Z64GVDOM-nhnkS_wy.js → timeline-definition-Z64GVDOM-DyXvto1l.js} +1 -1
- package/dist/assets/{vennDiagram-T6HMQDX7-B1piFYrz.js → vennDiagram-T6HMQDX7-CNe6zorb.js} +1 -1
- package/dist/assets/{vennDiagram-T6HMQDX7-x8SJTFhK.js → vennDiagram-T6HMQDX7-DyzOWKx3.js} +1 -1
- package/dist/assets/{wardleyDiagram-T6FBY63Y-BdD4GExj.js → wardleyDiagram-T6FBY63Y-D5YvMVwa.js} +1 -1
- package/dist/assets/{wardleyDiagram-T6FBY63Y-C90xqNVS.js → wardleyDiagram-T6FBY63Y-DXo8BCrA.js} +1 -1
- package/dist/assets/{xychartDiagram-ELKLHX3M--Ndoz0Cq.js → xychartDiagram-ELKLHX3M-CIBffVxC.js} +1 -1
- package/dist/assets/{xychartDiagram-ELKLHX3M-BAKns-7o.js → xychartDiagram-ELKLHX3M-XlsWelDb.js} +1 -1
- package/dist/build-info.json +2 -2
- package/dist/convert-icons.md +10 -46
- package/dist/favicon.png +0 -0
- package/dist/favicon.svg +1 -9
- package/dist/generate-icons.js +67 -47
- package/dist/icons/icon-128x128.png +0 -0
- package/dist/icons/icon-144x144.png +0 -0
- package/dist/icons/icon-152x152.png +0 -0
- package/dist/icons/icon-192x192.png +0 -0
- package/dist/icons/icon-384x384.png +0 -0
- package/dist/icons/icon-512x512.png +0 -0
- package/dist/icons/icon-72x72.png +0 -0
- package/dist/icons/icon-96x96.png +0 -0
- package/dist/index.html +2 -2
- package/dist/logo-128.png +0 -0
- package/dist/logo-256.png +0 -0
- package/dist/logo-32.png +0 -0
- package/dist/logo-512.png +0 -0
- package/dist/logo-64.png +0 -0
- package/dist/logo-source.png +0 -0
- package/dist/logo.svg +1 -17
- package/dist/sw.js +7 -9
- package/dist-server/build-info.json +2 -2
- package/dist-server/server/load-env.js +12 -4
- package/dist-server/server/load-env.js.map +1 -1
- package/dist-server/server/modules/browser-use/browser-use.service.js +33 -11
- package/dist-server/server/modules/browser-use/browser-use.service.js.map +1 -1
- package/dist-server/server/modules/browser-use/index.js +4 -4
- package/dist-server/server/modules/browser-use/index.js.map +1 -1
- package/dist-server/server/modules/browser-use/mcp-server-names.js +6 -4
- package/dist-server/server/modules/browser-use/mcp-server-names.js.map +1 -1
- package/dist-server/server/modules/browser-use/tests/browser-use.service.test.js +49 -1
- package/dist-server/server/modules/browser-use/tests/browser-use.service.test.js.map +1 -1
- package/dist-server/server/modules/cli/asset-migration-files.js +74 -0
- package/dist-server/server/modules/cli/asset-migration-files.js.map +1 -0
- package/dist-server/server/modules/cli/asset-migration-report.js +80 -0
- package/dist-server/server/modules/cli/asset-migration-report.js.map +1 -0
- package/dist-server/server/modules/cli/asset-migration-store.js +153 -0
- package/dist-server/server/modules/cli/asset-migration-store.js.map +1 -0
- package/dist-server/server/modules/cli/asset-migration.service.js +161 -0
- package/dist-server/server/modules/cli/asset-migration.service.js.map +1 -0
- package/dist-server/server/modules/cli/browser-use-mcp.js +9 -4
- package/dist-server/server/modules/cli/browser-use-mcp.js.map +1 -1
- package/dist-server/server/modules/cli/cli.module.js +21 -1
- package/dist-server/server/modules/cli/cli.module.js.map +1 -1
- package/dist-server/server/modules/cli/cli.service.js +6 -1
- package/dist-server/server/modules/cli/cli.service.js.map +1 -1
- package/dist-server/server/modules/cli/tests/asset-migration.service.test.js +275 -0
- package/dist-server/server/modules/cli/tests/asset-migration.service.test.js.map +1 -0
- package/dist-server/server/modules/cli/tests/browser-use-mcp-entry.test.js +157 -0
- package/dist-server/server/modules/cli/tests/browser-use-mcp-entry.test.js.map +1 -1
- package/dist-server/server/modules/cli/tests/cli-environment-bootstrap.test.js +1 -0
- package/dist-server/server/modules/cli/tests/cli-environment-bootstrap.test.js.map +1 -1
- package/dist-server/server/modules/cli/tests/cli.service.test.js +6 -1
- package/dist-server/server/modules/cli/tests/cli.service.test.js.map +1 -1
- package/dist-server/server/modules/cockpit/cockpit-brief-prompt.js +1 -1
- package/dist-server/server/modules/cockpit/cockpit-card-actions.js +19 -3
- package/dist-server/server/modules/cockpit/cockpit-card-actions.js.map +1 -1
- package/dist-server/server/modules/cockpit/cockpit-card-feedback-store.js +94 -9
- package/dist-server/server/modules/cockpit/cockpit-card-feedback-store.js.map +1 -1
- package/dist-server/server/modules/cockpit/cockpit-card-registry.js +1 -1
- package/dist-server/server/modules/cockpit/cockpit-fleet-card-projection.js +3 -0
- package/dist-server/server/modules/cockpit/cockpit-fleet-card-projection.js.map +1 -1
- package/dist-server/server/modules/cockpit/cockpit-knowledge-reading.js +16 -0
- package/dist-server/server/modules/cockpit/cockpit-knowledge-reading.js.map +1 -1
- package/dist-server/server/modules/cockpit/cockpit.module.js +1 -1
- package/dist-server/server/modules/cockpit/tests/cockpit-card-feedback.test.js +96 -1
- package/dist-server/server/modules/cockpit/tests/cockpit-card-feedback.test.js.map +1 -1
- package/dist-server/server/modules/cockpit/tests/cockpit-fleet-card-provider.test.js +2 -2
- package/dist-server/server/modules/cockpit/tests/cockpit-knowledge-decision-handoff.test.js +169 -3
- package/dist-server/server/modules/cockpit/tests/cockpit-knowledge-decision-handoff.test.js.map +1 -1
- package/dist-server/server/modules/context-events/context-event-writer.js +5 -5
- package/dist-server/server/modules/context-events/context-events.module.js +2 -2
- package/dist-server/server/modules/context-events/session-ask-stream-reader.js +1 -1
- package/dist-server/server/modules/context-events/session-blocked-events.js +3 -3
- package/dist-server/server/modules/context-events/tests/session-ask-event-writer.test.js +1 -1
- package/dist-server/server/modules/context-events/tests/session-blocked-events.test.js +3 -3
- package/dist-server/server/modules/context-events/tests/session-run-events.test.js +1 -1
- package/dist-server/server/modules/database/migrations.js +23 -1
- package/dist-server/server/modules/database/migrations.js.map +1 -1
- package/dist-server/server/modules/database/repositories/sessions.db.js +25 -4
- package/dist-server/server/modules/database/repositories/sessions.db.js.map +1 -1
- package/dist-server/server/modules/database/repositories/voice-user-identifiers.db.js +1 -1
- package/dist-server/server/modules/database/schema.js +16 -2
- package/dist-server/server/modules/database/schema.js.map +1 -1
- package/dist-server/server/modules/database/tests/migrations-ensure-columns.test.js +2 -0
- package/dist-server/server/modules/database/tests/migrations-ensure-columns.test.js.map +1 -1
- package/dist-server/server/modules/database/tests/sessions-provider-mapping.test.js +2 -2
- package/dist-server/server/modules/debug-agent/tests/debug-agent-permission-popup.test.js +2 -2
- package/dist-server/server/modules/liaison/liaison-deliver.service.js +3 -3
- package/dist-server/server/modules/liaison/liaison.module.js +1 -1
- package/dist-server/server/modules/mcp-gateway/mcp-gateway.auth.js +3 -2
- package/dist-server/server/modules/mcp-gateway/mcp-gateway.auth.js.map +1 -1
- package/dist-server/server/modules/mcp-gateway/mcp-self-target.js +2 -2
- package/dist-server/server/modules/mcp-gateway/mcp-session-send.js +7 -0
- package/dist-server/server/modules/mcp-gateway/mcp-session-send.js.map +1 -1
- package/dist-server/server/modules/mcp-gateway/mcp-tool-annotations.js +4 -4
- package/dist-server/server/modules/mcp-gateway/oauth-metadata.gate.js +4 -2
- package/dist-server/server/modules/mcp-gateway/oauth-metadata.gate.js.map +1 -1
- package/dist-server/server/modules/mcp-gateway/tests/knowledge-effective-config-proof.test.js +1 -1
- package/dist-server/server/modules/mcp-gateway/tests/knowledge-peer-correlation-proof.test.js +2 -2
- package/dist-server/server/modules/mcp-gateway/tests/mcp-approvals.test.js +3 -2
- package/dist-server/server/modules/mcp-gateway/tests/mcp-approvals.test.js.map +1 -1
- package/dist-server/server/modules/mcp-gateway/tests/mcp-audit-route.test.js +1 -1
- package/dist-server/server/modules/mcp-gateway/tests/mcp-audit.test.js +4 -4
- package/dist-server/server/modules/mcp-gateway/tests/mcp-auth.test.js +3 -1
- package/dist-server/server/modules/mcp-gateway/tests/mcp-auth.test.js.map +1 -1
- package/dist-server/server/modules/mcp-gateway/tests/mcp-cancel-queued.test.js +3 -2
- package/dist-server/server/modules/mcp-gateway/tests/mcp-cancel-queued.test.js.map +1 -1
- package/dist-server/server/modules/mcp-gateway/tests/mcp-cockpit-tools.test.js +7 -3
- package/dist-server/server/modules/mcp-gateway/tests/mcp-cockpit-tools.test.js.map +1 -1
- package/dist-server/server/modules/mcp-gateway/tests/mcp-correlation-id.test.js +8 -8
- package/dist-server/server/modules/mcp-gateway/tests/mcp-draft.test.js +3 -2
- package/dist-server/server/modules/mcp-gateway/tests/mcp-draft.test.js.map +1 -1
- package/dist-server/server/modules/mcp-gateway/tests/mcp-english-only.test.js +1 -1
- package/dist-server/server/modules/mcp-gateway/tests/mcp-gateway-wiring.test.js +2 -2
- package/dist-server/server/modules/mcp-gateway/tests/mcp-insufficient-scope.test.js +1 -1
- package/dist-server/server/modules/mcp-gateway/tests/mcp-insufficient-scope.test.js.map +1 -1
- package/dist-server/server/modules/mcp-gateway/tests/mcp-knowledge-facts.test.js +1 -1
- package/dist-server/server/modules/mcp-gateway/tests/mcp-not-found-semantics.test.js +1 -1
- package/dist-server/server/modules/mcp-gateway/tests/mcp-not-found-semantics.test.js.map +1 -1
- package/dist-server/server/modules/mcp-gateway/tests/mcp-oauth-challenge.test.js +3 -2
- package/dist-server/server/modules/mcp-gateway/tests/mcp-oauth-challenge.test.js.map +1 -1
- package/dist-server/server/modules/mcp-gateway/tests/mcp-overview.test.js +2 -2
- package/dist-server/server/modules/mcp-gateway/tests/mcp-pending-interaction.test.js +2 -2
- package/dist-server/server/modules/mcp-gateway/tests/mcp-read-tools.test.js +3 -3
- package/dist-server/server/modules/mcp-gateway/tests/mcp-read-tools.test.js.map +1 -1
- package/dist-server/server/modules/mcp-gateway/tests/mcp-run-get.test.js +1 -1
- package/dist-server/server/modules/mcp-gateway/tests/mcp-self-target.test.js +1 -1
- package/dist-server/server/modules/mcp-gateway/tests/mcp-self-target.test.js.map +1 -1
- package/dist-server/server/modules/mcp-gateway/tests/mcp-session-background.test.js +2 -2
- package/dist-server/server/modules/mcp-gateway/tests/mcp-session-background.test.js.map +1 -1
- package/dist-server/server/modules/mcp-gateway/tests/mcp-session-host-control.test.js +1 -1
- package/dist-server/server/modules/mcp-gateway/tests/mcp-session-host-control.test.js.map +1 -1
- package/dist-server/server/modules/mcp-gateway/tests/mcp-session-lifecycle.test.js +1 -1
- package/dist-server/server/modules/mcp-gateway/tests/mcp-session-lifecycle.test.js.map +1 -1
- package/dist-server/server/modules/mcp-gateway/tests/mcp-session-reconfigure.test.js +2 -2
- package/dist-server/server/modules/mcp-gateway/tests/mcp-session-reconfigure.test.js.map +1 -1
- package/dist-server/server/modules/mcp-gateway/tests/mcp-session-search-pruning.test.js +1 -1
- package/dist-server/server/modules/mcp-gateway/tests/mcp-session-search.test.js +1 -1
- package/dist-server/server/modules/mcp-gateway/tests/mcp-session-send.test.js +2 -2
- package/dist-server/server/modules/mcp-gateway/tests/mcp-session-send.test.js.map +1 -1
- package/dist-server/server/modules/mcp-gateway/tests/mcp-ui-last-opened.test.js +1 -1
- package/dist-server/server/modules/mcp-gateway/tests/mcp-usage-stats.test.js +1 -1
- package/dist-server/server/modules/mcp-gateway/tests/mcp-usage-stats.test.js.map +1 -1
- package/dist-server/server/modules/mcp-gateway/tests/mcp-write-notification.test.js +2 -2
- package/dist-server/server/modules/mcp-gateway/tests/mcp-write-notification.test.js.map +1 -1
- package/dist-server/server/modules/mcp-gateway/tests/oauth-flow.e2e.test.js +3 -3
- package/dist-server/server/modules/notifications/services/notification-orchestrator.service.js +1 -1
- package/dist-server/server/modules/oauth/access-tokens.service.js +8 -8
- package/dist-server/server/modules/oauth/oauth-consent.service.js +20 -15
- package/dist-server/server/modules/oauth/oauth-consent.service.js.map +1 -1
- package/dist-server/server/modules/oauth/tests/access-token-scopes.test.js +54 -34
- package/dist-server/server/modules/oauth/tests/access-token-scopes.test.js.map +1 -1
- package/dist-server/server/modules/oauth/tests/access-tokens.service.test.js +35 -14
- package/dist-server/server/modules/oauth/tests/access-tokens.service.test.js.map +1 -1
- package/dist-server/server/modules/oauth/tests/oauth-consent-page.test.js +27 -22
- package/dist-server/server/modules/oauth/tests/oauth-consent-page.test.js.map +1 -1
- package/dist-server/server/modules/oauth/tests/oauth-consent-ratelimit.test.js +2 -2
- package/dist-server/server/modules/oauth/tests/token-info.routes.test.js +2 -2
- package/dist-server/server/modules/oauth/tests/token-info.routes.test.js.map +1 -1
- package/dist-server/server/modules/observation/blind-spots.js +1 -1
- package/dist-server/server/modules/observation/observation-store.js +50 -17
- package/dist-server/server/modules/observation/observation-store.js.map +1 -1
- package/dist-server/server/modules/observation/observation-uri.js +31 -19
- package/dist-server/server/modules/observation/observation-uri.js.map +1 -1
- package/dist-server/server/modules/observation/observation.routes.js +1 -1
- package/dist-server/server/modules/observation/project-truth.js +1 -1
- package/dist-server/server/modules/observation/session-ask-decision-answer.js +2 -1
- package/dist-server/server/modules/observation/session-ask-decision-answer.js.map +1 -1
- package/dist-server/server/modules/observation/session-digest-llm.js +7 -1
- package/dist-server/server/modules/observation/session-digest-llm.js.map +1 -1
- package/dist-server/server/modules/observation/session-digest.js +39 -5
- package/dist-server/server/modules/observation/session-digest.js.map +1 -1
- package/dist-server/server/modules/observation/tests/architecture-delta.test.js +1 -1
- package/dist-server/server/modules/observation/tests/observation-routes.test.js +9 -0
- package/dist-server/server/modules/observation/tests/observation-routes.test.js.map +1 -1
- package/dist-server/server/modules/observation/tests/observation-store.test.js +38 -0
- package/dist-server/server/modules/observation/tests/observation-store.test.js.map +1 -1
- package/dist-server/server/modules/observation/tests/project-truth.test.js +2 -2
- package/dist-server/server/modules/observation/tests/session-ask-decision-digest.test.js +150 -0
- package/dist-server/server/modules/observation/tests/session-ask-decision-digest.test.js.map +1 -1
- package/dist-server/server/modules/observation/tests/session-digest.test.js +24 -1
- package/dist-server/server/modules/observation/tests/session-digest.test.js.map +1 -1
- package/dist-server/server/modules/providers/index.js +9 -0
- package/dist-server/server/modules/providers/index.js.map +1 -1
- package/dist-server/server/modules/providers/list/claude/claude-host-driver.provider.js +1 -1
- package/dist-server/server/modules/providers/list/codex/codex-app-server.client.js +22 -3
- package/dist-server/server/modules/providers/list/codex/codex-app-server.client.js.map +1 -1
- package/dist-server/server/modules/providers/list/codex/codex-app-server.connection.js +1 -1
- package/dist-server/server/modules/providers/list/codex/codex-host-driver.contract.js +11 -9
- package/dist-server/server/modules/providers/list/codex/codex-host-driver.contract.js.map +1 -1
- package/dist-server/server/modules/providers/list/codex/codex-host-driver.provider.js +15 -1
- package/dist-server/server/modules/providers/list/codex/codex-host-driver.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/codex/codex-models.provider.js +14 -3
- package/dist-server/server/modules/providers/list/codex/codex-models.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/codex/codex-provider-injection.js +248 -0
- package/dist-server/server/modules/providers/list/codex/codex-provider-injection.js.map +1 -0
- package/dist-server/server/modules/providers/list/codex/codex-resident-provider-launch.js +37 -0
- package/dist-server/server/modules/providers/list/codex/codex-resident-provider-launch.js.map +1 -0
- package/dist-server/server/modules/providers/list/codex/codex-runtime.provider.js +19 -1
- package/dist-server/server/modules/providers/list/codex/codex-runtime.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/cursor/cursor-models.provider.js +1 -1
- package/dist-server/server/modules/providers/list/opencode/opencode-models.provider.js +1 -1
- package/dist-server/server/modules/providers/provider.routes.js +23 -2
- package/dist-server/server/modules/providers/provider.routes.js.map +1 -1
- package/dist-server/server/modules/providers/services/provider-models.service.js +136 -106
- package/dist-server/server/modules/providers/services/provider-models.service.js.map +1 -1
- package/dist-server/server/modules/providers/services/provider-runtime.service.js +3 -0
- package/dist-server/server/modules/providers/services/provider-runtime.service.js.map +1 -1
- package/dist-server/server/modules/providers/services/session-model-resolver.js +106 -0
- package/dist-server/server/modules/providers/services/session-model-resolver.js.map +1 -0
- package/dist-server/server/modules/providers/services/session-provider-binding.js +89 -0
- package/dist-server/server/modules/providers/services/session-provider-binding.js.map +1 -0
- package/dist-server/server/modules/providers/services/sessions.service.js +5 -1
- package/dist-server/server/modules/providers/services/sessions.service.js.map +1 -1
- package/dist-server/server/modules/providers/tests/claude-resident-addressable.test.js +2 -2
- package/dist-server/server/modules/providers/tests/claude-session-name-authority.test.js +4 -4
- package/dist-server/server/modules/providers/tests/codex-app-server-protocol.test.js +1 -1
- package/dist-server/server/modules/providers/tests/codex-host-driver-persistent.test.js +2 -2
- package/dist-server/server/modules/providers/tests/codex-provider-model-binding.test.js +270 -0
- package/dist-server/server/modules/providers/tests/codex-provider-model-binding.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/codex-resident-provider-injection.test.js +484 -0
- package/dist-server/server/modules/providers/tests/codex-resident-provider-injection.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/codex-runtime-provider-injection.test.js +228 -0
- package/dist-server/server/modules/providers/tests/codex-runtime-provider-injection.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/codex-session-provider-binding.test.js +306 -0
- package/dist-server/server/modules/providers/tests/codex-session-provider-binding.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/codex-sessions.test.js +1 -1
- package/dist-server/server/modules/providers/tests/opencode-sessions.test.js +2 -2
- package/dist-server/server/modules/providers/tests/provider-models.service.test.js +15 -0
- package/dist-server/server/modules/providers/tests/provider-models.service.test.js.map +1 -1
- package/dist-server/server/modules/providers/tests/provider.routes.test.js +1 -1
- package/dist-server/server/modules/quay/index.js +1 -1
- package/dist-server/server/modules/quay/quay-fleet-cli.js +1 -1
- package/dist-server/server/modules/quay/quay-fleet-locator.js +2 -2
- package/dist-server/server/modules/quay/quay-fleet-project-id.js +7 -7
- package/dist-server/server/modules/quay/quay-fleet-registry.js +3 -3
- package/dist-server/server/modules/quay/quay-suite-fanin-reader.js +1 -1
- package/dist-server/server/modules/quay/quay-uri-resolve.js +6 -6
- package/dist-server/server/modules/quay/quay.module.js +3 -3
- package/dist-server/server/modules/quay/quay.routes.js +1 -1
- package/dist-server/server/modules/quay/quay.service.js +4 -4
- package/dist-server/server/modules/quay/tests/contracts/fleet-cli-contracts.test.js +3 -3
- package/dist-server/server/modules/quay/tests/contracts/shapes.js +9 -9
- package/dist-server/server/modules/quay/tests/quay-fleet-locator.test.js +1 -1
- package/dist-server/server/modules/quay/tests/quay-fleet-project-id.test.js +4 -4
- package/dist-server/server/modules/quay/tests/quay-uri-resolve.test.js +10 -10
- package/dist-server/server/modules/session-hosts/tests/resident-server-restart.test.js +1 -1
- package/dist-server/server/modules/settings/codex-mcp-gateway.settings.js +1 -1
- package/dist-server/server/modules/settings/codex-model-providers.schema.js +349 -0
- package/dist-server/server/modules/settings/codex-model-providers.schema.js.map +1 -0
- package/dist-server/server/modules/settings/codex-model-providers.service.js +199 -0
- package/dist-server/server/modules/settings/codex-model-providers.service.js.map +1 -0
- package/dist-server/server/modules/settings/codex-model-providers.settings.js +547 -0
- package/dist-server/server/modules/settings/codex-model-providers.settings.js.map +1 -0
- package/dist-server/server/modules/settings/codex-model-providers.store.js +200 -0
- package/dist-server/server/modules/settings/codex-model-providers.store.js.map +1 -0
- package/dist-server/server/modules/settings/index.js +10 -0
- package/dist-server/server/modules/settings/index.js.map +1 -1
- package/dist-server/server/modules/settings/server-settings-file.js +28 -32
- package/dist-server/server/modules/settings/server-settings-file.js.map +1 -1
- package/dist-server/server/modules/settings/server-settings.js +2 -2
- package/dist-server/server/modules/settings/server-settings.js.map +1 -1
- package/dist-server/server/modules/settings/settings.module.js +35 -0
- package/dist-server/server/modules/settings/settings.module.js.map +1 -1
- package/dist-server/server/modules/settings/settings.routes.js +42 -0
- package/dist-server/server/modules/settings/settings.routes.js.map +1 -1
- package/dist-server/server/modules/settings/settings.service.js +8 -21
- package/dist-server/server/modules/settings/settings.service.js.map +1 -1
- package/dist-server/server/modules/settings/tests/cockpit-session-start-settings.routes.test.js +1 -0
- package/dist-server/server/modules/settings/tests/cockpit-session-start-settings.routes.test.js.map +1 -1
- package/dist-server/server/modules/settings/tests/codex-model-providers-settings.test.js +336 -0
- package/dist-server/server/modules/settings/tests/codex-model-providers-settings.test.js.map +1 -0
- package/dist-server/server/modules/settings/tests/codex-provider-advanced.test.js +466 -0
- package/dist-server/server/modules/settings/tests/codex-provider-advanced.test.js.map +1 -0
- package/dist-server/server/modules/settings/tests/codex-provider-import.test.js +475 -0
- package/dist-server/server/modules/settings/tests/codex-provider-import.test.js.map +1 -0
- package/dist-server/server/modules/voice/tests/voice-lexicon.test.js +9 -9
- package/dist-server/server/modules/voice/tests/voice-lexicon.test.js.map +1 -1
- package/dist-server/server/modules/voice/voice-lexicon.js +1 -1
- package/dist-server/server/modules/websocket/services/chat-control.service.js +21 -1
- package/dist-server/server/modules/websocket/services/chat-control.service.js.map +1 -1
- package/dist-server/server/modules/websocket/services/chat-websocket.service.js +25 -1
- package/dist-server/server/modules/websocket/services/chat-websocket.service.js.map +1 -1
- package/dist-server/server/modules/websocket/tests/chat-background-task.test.js +18 -1
- package/dist-server/server/modules/websocket/tests/chat-background-task.test.js.map +1 -1
- package/dist-server/server/modules/websocket/tests/chat-stop-task.test.js +18 -1
- package/dist-server/server/modules/websocket/tests/chat-stop-task.test.js.map +1 -1
- package/dist-server/server/modules/worktrees/services/worktree-list.service.js +1 -1
- package/dist-server/server/modules/worktrees/services/worktree-open.service.js +1 -1
- package/dist-server/server/modules/worktrees/services/worktree-remove.service.js +1 -1
- package/dist-server/server/shared/brand.js +18 -20
- package/dist-server/server/shared/brand.js.map +1 -1
- package/dist-server/server/shared/data-root-migration.js +3 -2
- package/dist-server/server/shared/data-root-migration.js.map +1 -1
- package/dist-server/server/shared/data-root.js +54 -59
- package/dist-server/server/shared/data-root.js.map +1 -1
- package/dist-server/server/shared/mcp-contract/types.js +20 -15
- package/dist-server/server/shared/mcp-contract/types.js.map +1 -1
- package/dist-server/server/shared/ports/codex-provider-registry.port.js +66 -0
- package/dist-server/server/shared/ports/codex-provider-registry.port.js.map +1 -0
- package/dist-server/server/shared/settings-file.js +120 -6
- package/dist-server/server/shared/settings-file.js.map +1 -1
- package/dist-server/server/shared/tests/cloudpit-brand-env.test.js +76 -34
- package/dist-server/server/shared/tests/cloudpit-brand-env.test.js.map +1 -1
- package/dist-server/server/shared/tests/cloudpit-data-root.test.js +35 -18
- package/dist-server/server/shared/tests/cloudpit-data-root.test.js.map +1 -1
- package/dist-server/server/shared/tests/cloudpit-goal-exit-gate.test.js +153 -17
- package/dist-server/server/shared/tests/cloudpit-goal-exit-gate.test.js.map +1 -1
- package/dist-server/server/shared/tests/cloudpit-runtime-flip.test.js +116 -114
- package/dist-server/server/shared/tests/cloudpit-runtime-flip.test.js.map +1 -1
- package/dist-server/server/shared/tests/frontend-import-cycle-budget.test.js +9 -1
- package/dist-server/server/shared/tests/frontend-import-cycle-budget.test.js.map +1 -1
- package/dist-server/server/shared/tests/quay-test-script.test.js +40 -6
- package/dist-server/server/shared/tests/quay-test-script.test.js.map +1 -1
- package/dist-server/server/shared/tests/settings-file.test.js +58 -2
- package/dist-server/server/shared/tests/settings-file.test.js.map +1 -1
- package/dist-server/server/shared/types.js.map +1 -1
- package/dist-server/server/shared/utils.js +1 -1
- package/electron/appIdMigration.js +467 -0
- package/electron/assets/logo-macos.icns +0 -0
- package/electron/assets/logo-macos.png +0 -0
- package/electron/assets/logo-windows.ico +0 -0
- package/electron/brandEnv.js +64 -0
- package/electron/cloud.js +3 -3
- package/electron/desktopNotifications.js +1 -1
- package/electron/launcher/index.html +1 -1
- package/electron/localServer.js +41 -7
- package/electron/main.js +33 -15
- package/electron/scripts/generate-macos-icon.js +34 -28
- package/electron/scripts/generate-windows-icon.js +66 -0
- package/electron/serverInstaller.js +20 -11
- package/electron/tests/cloudpit-appid-migration.test.mjs +514 -0
- package/electron/tests/cloudpit-brand-env-wiring.test.mjs +324 -0
- package/electron/tests/cloudpit-desktop-identity.test.mjs +42 -19
- package/electron/tests/cloudpit-runtime-identity-surfaces.test.mjs +10 -4
- package/electron/tests/cloudpit-userdata-migration.test.mjs +18 -5
- package/electron/userDataMigration.js +3 -0
- package/package.json +7 -6
- package/public/api-docs.html +3 -3
- package/scripts/brand-allowlist-check.mjs +233 -12
- package/scripts/brand-allowlist-check.test.mjs +270 -1
- package/scripts/cloudpit-distribution-check.mjs +57 -23
- package/scripts/cloudpit-distribution-check.test.mjs +42 -9
- package/scripts/codex-protocol-smoke.mjs +1 -1
- package/scripts/isolated-instance.sh +1 -1
- package/scripts/mcp-smoke.mjs +2 -2
- package/scripts/release/tests/fork-release-workflows.test.mjs +45 -0
- package/scripts/release/tests/server-installer-bundle-url.test.mjs +57 -9
- package/scripts/restart-when-idle.sh +1 -1
- package/scripts/shadow-prediction-report.mjs +1 -1
- package/scripts/suite-duration-baseline.tsv +45 -0
- package/scripts/ui-scenario.mjs +195 -0
- package/scripts/ui-scenarios/codex-provider-import.json +63 -0
- package/scripts/ui-scenarios/codex-provider-settings.json +77 -0
- package/scripts/voice-capture-process-check.false-forms.test.mjs +1 -1
- package/scripts/voice-phase0-report.test.mjs +1 -1
- package/server/load-env.ts +15 -4
- package/server/modules/browser-use/browser-use.service.ts +45 -14
- package/server/modules/browser-use/index.ts +3 -4
- package/server/modules/browser-use/mcp-server-names.ts +6 -5
- package/server/modules/browser-use/tests/browser-use.service.test.ts +63 -1
- package/server/modules/cli/asset-migration-files.ts +96 -0
- package/server/modules/cli/asset-migration-report.ts +123 -0
- package/server/modules/cli/asset-migration-store.ts +228 -0
- package/server/modules/cli/asset-migration.service.ts +236 -0
- package/server/modules/cli/browser-use-mcp.ts +9 -4
- package/server/modules/cli/cli.module.ts +21 -1
- package/server/modules/cli/cli.service.ts +14 -1
- package/server/modules/cli/tests/asset-migration.service.test.ts +349 -0
- package/server/modules/cli/tests/browser-use-mcp-entry.test.ts +202 -0
- package/server/modules/cli/tests/cli-environment-bootstrap.test.ts +1 -0
- package/server/modules/cli/tests/cli.service.test.ts +6 -1
- package/server/modules/cockpit/cockpit-brief-prompt.ts +1 -1
- package/server/modules/cockpit/cockpit-card-actions.ts +20 -3
- package/server/modules/cockpit/cockpit-card-feedback-store.ts +104 -9
- package/server/modules/cockpit/cockpit-card-registry.ts +2 -2
- package/server/modules/cockpit/cockpit-fleet-card-projection.ts +3 -0
- package/server/modules/cockpit/cockpit-fleet-executor.ts +1 -1
- package/server/modules/cockpit/cockpit-knowledge-reading.ts +17 -0
- package/server/modules/cockpit/cockpit.module.ts +2 -2
- package/server/modules/cockpit/cockpit.reducer.ts +1 -1
- package/server/modules/cockpit/tests/cockpit-card-feedback.test.ts +116 -1
- package/server/modules/cockpit/tests/cockpit-fleet-card-provider.test.ts +2 -2
- package/server/modules/cockpit/tests/cockpit-knowledge-decision-handoff.test.ts +205 -3
- package/server/modules/context-events/context-event-writer.ts +6 -6
- package/server/modules/context-events/context-events.module.ts +2 -2
- package/server/modules/context-events/session-ask-stream-reader.ts +1 -1
- package/server/modules/context-events/session-blocked-events.ts +3 -3
- package/server/modules/context-events/tests/session-ask-event-writer.test.ts +1 -1
- package/server/modules/context-events/tests/session-blocked-events.test.ts +3 -3
- package/server/modules/context-events/tests/session-run-events.test.ts +1 -1
- package/server/modules/database/migrations.ts +24 -1
- package/server/modules/database/repositories/sessions.db.ts +41 -4
- package/server/modules/database/repositories/voice-user-identifiers.db.ts +1 -1
- package/server/modules/database/schema.ts +16 -2
- package/server/modules/database/tests/migrations-ensure-columns.test.ts +2 -0
- package/server/modules/database/tests/sessions-provider-mapping.test.ts +2 -2
- package/server/modules/debug-agent/tests/debug-agent-permission-popup.test.ts +2 -2
- package/server/modules/liaison/liaison-deliver.service.ts +4 -4
- package/server/modules/liaison/liaison.module.ts +1 -1
- package/server/modules/mcp-gateway/mcp-gateway.auth.ts +3 -2
- package/server/modules/mcp-gateway/mcp-self-target.ts +2 -2
- package/server/modules/mcp-gateway/mcp-session-send.ts +21 -2
- package/server/modules/mcp-gateway/mcp-tool-annotations.ts +4 -4
- package/server/modules/mcp-gateway/oauth-metadata.gate.ts +4 -5
- package/server/modules/mcp-gateway/tests/knowledge-effective-config-proof.test.ts +1 -1
- package/server/modules/mcp-gateway/tests/knowledge-peer-correlation-proof.test.ts +2 -2
- package/server/modules/mcp-gateway/tests/mcp-approvals.test.ts +3 -2
- package/server/modules/mcp-gateway/tests/mcp-audit-route.test.ts +1 -1
- package/server/modules/mcp-gateway/tests/mcp-audit.test.ts +4 -4
- package/server/modules/mcp-gateway/tests/mcp-auth.test.ts +3 -1
- package/server/modules/mcp-gateway/tests/mcp-cancel-queued.test.ts +3 -2
- package/server/modules/mcp-gateway/tests/mcp-cockpit-tools.test.ts +7 -3
- package/server/modules/mcp-gateway/tests/mcp-correlation-id.test.ts +8 -8
- package/server/modules/mcp-gateway/tests/mcp-draft.test.ts +3 -2
- package/server/modules/mcp-gateway/tests/mcp-english-only.test.ts +1 -1
- package/server/modules/mcp-gateway/tests/mcp-gateway-wiring.test.ts +2 -2
- package/server/modules/mcp-gateway/tests/mcp-insufficient-scope.test.ts +1 -1
- package/server/modules/mcp-gateway/tests/mcp-knowledge-facts.test.ts +1 -1
- package/server/modules/mcp-gateway/tests/mcp-not-found-semantics.test.ts +1 -1
- package/server/modules/mcp-gateway/tests/mcp-oauth-challenge.test.ts +3 -2
- package/server/modules/mcp-gateway/tests/mcp-overview.test.ts +2 -2
- package/server/modules/mcp-gateway/tests/mcp-pending-interaction.test.ts +2 -2
- package/server/modules/mcp-gateway/tests/mcp-read-tools.test.ts +3 -3
- package/server/modules/mcp-gateway/tests/mcp-run-get.test.ts +1 -1
- package/server/modules/mcp-gateway/tests/mcp-self-target.test.ts +1 -1
- package/server/modules/mcp-gateway/tests/mcp-session-background.test.ts +2 -2
- package/server/modules/mcp-gateway/tests/mcp-session-host-control.test.ts +1 -1
- package/server/modules/mcp-gateway/tests/mcp-session-lifecycle.test.ts +1 -1
- package/server/modules/mcp-gateway/tests/mcp-session-reconfigure.test.ts +2 -2
- package/server/modules/mcp-gateway/tests/mcp-session-search-pruning.test.ts +1 -1
- package/server/modules/mcp-gateway/tests/mcp-session-search.test.ts +1 -1
- package/server/modules/mcp-gateway/tests/mcp-session-send.test.ts +2 -2
- package/server/modules/mcp-gateway/tests/mcp-ui-last-opened.test.ts +1 -1
- package/server/modules/mcp-gateway/tests/mcp-usage-stats.test.ts +1 -1
- package/server/modules/mcp-gateway/tests/mcp-write-notification.test.ts +2 -2
- package/server/modules/mcp-gateway/tests/oauth-flow.e2e.test.ts +3 -3
- package/server/modules/notifications/services/notification-orchestrator.service.js +1 -1
- package/server/modules/oauth/access-tokens.service.ts +8 -8
- package/server/modules/oauth/oauth-consent.service.ts +20 -15
- package/server/modules/oauth/tests/access-token-scopes.test.ts +55 -34
- package/server/modules/oauth/tests/access-tokens.service.test.ts +50 -14
- package/server/modules/oauth/tests/oauth-consent-page.test.ts +29 -21
- package/server/modules/oauth/tests/oauth-consent-ratelimit.test.ts +2 -2
- package/server/modules/oauth/tests/token-info.routes.test.ts +2 -2
- package/server/modules/observation/architecture-delta.producer.ts +1 -1
- package/server/modules/observation/blind-spots.ts +1 -1
- package/server/modules/observation/observation-store.ts +54 -16
- package/server/modules/observation/observation-uri.ts +34 -21
- package/server/modules/observation/observation.routes.ts +1 -1
- package/server/modules/observation/project-truth.ts +3 -3
- package/server/modules/observation/session-ask-decision-answer.ts +2 -1
- package/server/modules/observation/session-digest-llm.ts +7 -1
- package/server/modules/observation/session-digest.ts +39 -5
- package/server/modules/observation/tests/architecture-delta.test.ts +1 -1
- package/server/modules/observation/tests/observation-routes.test.ts +13 -0
- package/server/modules/observation/tests/observation-store.test.ts +58 -0
- package/server/modules/observation/tests/project-truth.test.ts +2 -2
- package/server/modules/observation/tests/session-ask-decision-digest.test.ts +189 -0
- package/server/modules/observation/tests/session-digest.test.ts +43 -0
- package/server/modules/providers/index.ts +13 -0
- package/server/modules/providers/list/claude/claude-host-driver.provider.ts +1 -1
- package/server/modules/providers/list/codex/codex-app-server.client.ts +37 -5
- package/server/modules/providers/list/codex/codex-app-server.connection.ts +1 -1
- package/server/modules/providers/list/codex/codex-host-driver.contract.ts +18 -9
- package/server/modules/providers/list/codex/codex-host-driver.provider.ts +15 -1
- package/server/modules/providers/list/codex/codex-models.provider.ts +16 -3
- package/server/modules/providers/list/codex/codex-provider-injection.ts +323 -0
- package/server/modules/providers/list/codex/codex-resident-provider-launch.ts +71 -0
- package/server/modules/providers/list/codex/codex-runtime.provider.ts +20 -1
- package/server/modules/providers/list/cursor/cursor-models.provider.ts +1 -1
- package/server/modules/providers/list/opencode/opencode-models.provider.ts +1 -1
- package/server/modules/providers/provider.routes.ts +23 -2
- package/server/modules/providers/services/provider-models.service.ts +187 -124
- package/server/modules/providers/services/provider-runtime.service.ts +3 -0
- package/server/modules/providers/services/session-model-resolver.ts +180 -0
- package/server/modules/providers/services/session-provider-binding.ts +176 -0
- package/server/modules/providers/services/sessions.service.ts +5 -1
- package/server/modules/providers/tests/claude-resident-addressable.test.ts +2 -2
- package/server/modules/providers/tests/claude-session-name-authority.test.ts +4 -4
- package/server/modules/providers/tests/codex-app-server-protocol.test.ts +1 -1
- package/server/modules/providers/tests/codex-host-driver-persistent.test.ts +2 -2
- package/server/modules/providers/tests/codex-provider-model-binding.test.ts +345 -0
- package/server/modules/providers/tests/codex-resident-provider-injection.test.ts +561 -0
- package/server/modules/providers/tests/codex-runtime-provider-injection.test.ts +269 -0
- package/server/modules/providers/tests/codex-session-provider-binding.test.ts +386 -0
- package/server/modules/providers/tests/codex-sessions.test.ts +1 -1
- package/server/modules/providers/tests/opencode-sessions.test.ts +2 -2
- package/server/modules/providers/tests/provider-models.service.test.ts +15 -0
- package/server/modules/providers/tests/provider.routes.test.ts +1 -1
- package/server/modules/quay/index.ts +1 -1
- package/server/modules/quay/quay-dispatch-carrier.ts +1 -1
- package/server/modules/quay/quay-fleet-cli.ts +1 -1
- package/server/modules/quay/quay-fleet-locator.ts +2 -2
- package/server/modules/quay/quay-fleet-project-id.ts +14 -14
- package/server/modules/quay/quay-fleet-registry.ts +8 -8
- package/server/modules/quay/quay-suite-fanin-reader.ts +1 -1
- package/server/modules/quay/quay-uri-resolve.ts +8 -8
- package/server/modules/quay/quay.module.ts +3 -3
- package/server/modules/quay/quay.routes.ts +1 -1
- package/server/modules/quay/quay.service.ts +6 -6
- package/server/modules/quay/session-task-chain.service.ts +1 -1
- package/server/modules/quay/tests/contracts/fleet-cli-contracts.test.ts +3 -3
- package/server/modules/quay/tests/contracts/shapes.ts +10 -10
- package/server/modules/quay/tests/quay-fleet-locator.test.ts +1 -1
- package/server/modules/quay/tests/quay-fleet-project-id.test.ts +6 -6
- package/server/modules/quay/tests/quay-uri-resolve.test.ts +10 -10
- package/server/modules/session-hosts/tests/resident-server-restart.test.ts +1 -1
- package/server/modules/settings/codex-mcp-gateway.settings.ts +1 -1
- package/server/modules/settings/codex-model-providers.schema.ts +460 -0
- package/server/modules/settings/codex-model-providers.service.ts +235 -0
- package/server/modules/settings/codex-model-providers.settings.ts +713 -0
- package/server/modules/settings/codex-model-providers.store.ts +244 -0
- package/server/modules/settings/index.ts +18 -0
- package/server/modules/settings/server-settings-file.ts +29 -41
- package/server/modules/settings/server-settings.ts +2 -3
- package/server/modules/settings/settings.module.ts +41 -0
- package/server/modules/settings/settings.routes.ts +37 -0
- package/server/modules/settings/settings.service.ts +21 -26
- package/server/modules/settings/tests/cockpit-session-start-settings.routes.test.ts +1 -0
- package/server/modules/settings/tests/codex-model-providers-settings.test.ts +413 -0
- package/server/modules/settings/tests/codex-provider-advanced.test.ts +540 -0
- package/server/modules/settings/tests/codex-provider-import.test.ts +621 -0
- package/server/modules/voice/tests/voice-lexicon.test.ts +10 -10
- package/server/modules/voice/voice-lexicon.ts +1 -1
- package/server/modules/websocket/services/chat-control.service.ts +35 -2
- package/server/modules/websocket/services/chat-websocket.service.ts +25 -1
- package/server/modules/websocket/tests/chat-background-task.test.ts +17 -1
- package/server/modules/websocket/tests/chat-stop-task.test.ts +17 -1
- package/server/modules/worktrees/services/worktree-list.service.ts +1 -1
- package/server/modules/worktrees/services/worktree-open.service.ts +1 -1
- package/server/modules/worktrees/services/worktree-remove.service.ts +1 -1
- package/server/shared/brand.ts +24 -38
- package/server/shared/data-root-migration.ts +3 -2
- package/server/shared/data-root.ts +73 -79
- package/server/shared/interfaces.ts +1 -1
- package/server/shared/mcp-contract/types.ts +20 -15
- package/server/shared/ports/codex-provider-registry.port.ts +133 -0
- package/server/shared/ports/session-blocked-events.port.ts +1 -1
- package/server/shared/ports/session-run-events.port.ts +2 -2
- package/server/shared/settings-file.ts +167 -6
- package/server/shared/tests/architecture-ratchet.baseline.json +15 -15
- package/server/shared/tests/cloudpit-brand-env.test.ts +106 -36
- package/server/shared/tests/cloudpit-data-root.test.ts +42 -18
- package/server/shared/tests/cloudpit-goal-exit-gate.baseline.json +6 -2
- package/server/shared/tests/cloudpit-goal-exit-gate.test.ts +179 -17
- package/server/shared/tests/cloudpit-runtime-flip.test.ts +162 -123
- package/server/shared/tests/frontend-import-cycle-budget.baseline.json +8 -6
- package/server/shared/tests/frontend-import-cycle-budget.test.ts +9 -1
- package/server/shared/tests/quay-test-script.test.ts +40 -6
- package/server/shared/tests/settings-file.test.ts +83 -1
- package/server/shared/types.ts +121 -8
- package/server/shared/utils.ts +1 -1
- package/dist/assets/channel-D7XqNTJC.js +0 -1
- package/dist/assets/channel-sbQyI1an.js +0 -1
- package/dist/assets/classDiagram-JCYQIIEL-DHudi-zH.js +0 -1
- package/dist/assets/classDiagram-JCYQIIEL-DV7ybJJG.js +0 -1
- package/dist/assets/classDiagram-v2-OCEON4UE-DHudi-zH.js +0 -1
- package/dist/assets/classDiagram-v2-OCEON4UE-DV7ybJJG.js +0 -1
- package/dist/assets/index-BsACp0s2.css +0 -32
- package/dist/assets/swimlanesDiagram-ULZ7WXOC-CNMNp6YA.js +0 -8
- package/dist/assets/swimlanesDiagram-ULZ7WXOC-DOjtDEUW.js +0 -8
- package/dist/icons/icon-128x128.svg +0 -12
- package/dist/icons/icon-144x144.svg +0 -12
- package/dist/icons/icon-152x152.svg +0 -12
- package/dist/icons/icon-192x192.svg +0 -12
- package/dist/icons/icon-384x384.svg +0 -12
- package/dist/icons/icon-512x512.svg +0 -12
- package/dist/icons/icon-72x72.svg +0 -12
- package/dist/icons/icon-96x96.svg +0 -12
- package/dist/icons/icon-template.svg +0 -12
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-run Codex SDK provider injection (AC-464).
|
|
3
|
+
*
|
|
4
|
+
* The runtime used to always construct `new Codex()` with no options, so a
|
|
5
|
+
* model bound to a project upstream provider still dialled Codex's global
|
|
6
|
+
* default. This criterion pins the three readings that make the binding real:
|
|
7
|
+
*
|
|
8
|
+
* - POSITIVE: a model whose public config carries `codexProviderId` compiles
|
|
9
|
+
* `config.model_provider` + `config.model_providers.<id>` (base_url,
|
|
10
|
+
* env_key, wire_api) and passes the token-file secret in the child env
|
|
11
|
+
* under `env_key`. The REAL Codex SDK spawns the REAL bundled CLI, which
|
|
12
|
+
* POSTs to a local mock `/v1/responses`; the mock asserts the request's
|
|
13
|
+
* `Authorization` header is `Bearer <token-file secret>`.
|
|
14
|
+
* - ISOLATION: a host sentinel variable present in `process.env` is absent
|
|
15
|
+
* from the child env, while plumbing (PATH/HOME) survives.
|
|
16
|
+
* - NEGATIVE: a model with no `codexProviderId` still constructs `new Codex()`
|
|
17
|
+
* with neither `config` nor `env`; a model bound to an unknown provider is
|
|
18
|
+
* refused with a readable line and never falls back to the default endpoint.
|
|
19
|
+
*/
|
|
20
|
+
import assert from 'node:assert/strict';
|
|
21
|
+
import http from 'node:http';
|
|
22
|
+
import { mkdtemp, rm, writeFile } from 'node:fs/promises';
|
|
23
|
+
import type { AddressInfo } from 'node:net';
|
|
24
|
+
import os from 'node:os';
|
|
25
|
+
import path from 'node:path';
|
|
26
|
+
import test, { type TestContext } from 'node:test';
|
|
27
|
+
|
|
28
|
+
import { Codex } from '@openai/codex-sdk';
|
|
29
|
+
import type { CodexOptions, Thread, ThreadOptions } from '@openai/codex-sdk';
|
|
30
|
+
|
|
31
|
+
import { codexRuntime } from '@/modules/providers/list/codex/codex-runtime.provider.js';
|
|
32
|
+
import {
|
|
33
|
+
createCodexModelProvidersSource,
|
|
34
|
+
} from '@/modules/settings/index.js';
|
|
35
|
+
import {
|
|
36
|
+
installCodexProviderRegistryPort,
|
|
37
|
+
} from '@/shared/ports/codex-provider-registry.port.js';
|
|
38
|
+
import type { ProviderRuntimeContext } from '@/shared/index.js';
|
|
39
|
+
|
|
40
|
+
const HOST_SENTINEL_KEY = 'CC_CODEX_HOST_SENTINEL';
|
|
41
|
+
const HOST_SENTINEL_VALUE = 'host-secret-that-must-not-reach-the-child';
|
|
42
|
+
const PROVIDER_ID = 'bob';
|
|
43
|
+
const ENV_KEY = 'BOB_API_KEY';
|
|
44
|
+
const TOKEN_SECRET = 'token-file-secret-4242';
|
|
45
|
+
|
|
46
|
+
/** One request the bundled CLI made to the mock endpoint. */
|
|
47
|
+
type SeenRequest = { url: string; authorization: string | undefined };
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* A local Responses-API mock. A 400 is deliberate: Codex 0.166 treats it as a
|
|
51
|
+
* terminal client error (no 5xx retry/backoff), so the criterion settles in
|
|
52
|
+
* well under a second while still exercising the real HTTP path — including the
|
|
53
|
+
* `Authorization` header the provider's `env_key` produces.
|
|
54
|
+
*/
|
|
55
|
+
async function startMockResponsesApi(): Promise<{
|
|
56
|
+
requests: SeenRequest[];
|
|
57
|
+
baseUrl: string;
|
|
58
|
+
close(): Promise<void>;
|
|
59
|
+
}> {
|
|
60
|
+
const requests: SeenRequest[] = [];
|
|
61
|
+
const server = http.createServer((request, response) => {
|
|
62
|
+
requests.push({
|
|
63
|
+
url: request.url ?? '',
|
|
64
|
+
authorization: typeof request.headers.authorization === 'string'
|
|
65
|
+
? request.headers.authorization
|
|
66
|
+
: undefined,
|
|
67
|
+
});
|
|
68
|
+
request.resume();
|
|
69
|
+
response.writeHead(400, { 'content-type': 'application/json' });
|
|
70
|
+
response.end(JSON.stringify({ error: { message: 'mock: provider injection criterion' } }));
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
await new Promise<void>((resolve) => server.listen(0, '127.0.0.1', resolve));
|
|
74
|
+
const { port } = server.address() as AddressInfo;
|
|
75
|
+
return {
|
|
76
|
+
requests,
|
|
77
|
+
baseUrl: `http://127.0.0.1:${port}/v1`,
|
|
78
|
+
close: () => new Promise<void>((resolve, reject) => {
|
|
79
|
+
server.close((error) => (error ? reject(error) : resolve()));
|
|
80
|
+
}),
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Writes the production settings document + token file, then installs the real
|
|
86
|
+
* settings source in the shared registry port. Nothing about the resolution is
|
|
87
|
+
* stubbed: the runtime reads the JSON and the 0600 token file through the same
|
|
88
|
+
* `resolve()` the production composition root installs.
|
|
89
|
+
*/
|
|
90
|
+
async function withProviderRegistry(
|
|
91
|
+
baseUrl: string,
|
|
92
|
+
run: (home: string) => Promise<void>,
|
|
93
|
+
): Promise<void> {
|
|
94
|
+
const directory = await mkdtemp(path.join(os.tmpdir(), 'codex-perrun-injection-'));
|
|
95
|
+
const settingsPath = path.join(directory, 'codex.settings.json');
|
|
96
|
+
const tokenFile = path.join(directory, 'bob.token');
|
|
97
|
+
await writeFile(tokenFile, TOKEN_SECRET, { encoding: 'utf8', mode: 0o600 });
|
|
98
|
+
await writeFile(
|
|
99
|
+
settingsPath,
|
|
100
|
+
JSON.stringify({
|
|
101
|
+
modelProviders: {
|
|
102
|
+
[PROVIDER_ID]: {
|
|
103
|
+
name: 'Bob Upstream',
|
|
104
|
+
baseUrl,
|
|
105
|
+
envKey: ENV_KEY,
|
|
106
|
+
tokenFile,
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
}),
|
|
110
|
+
'utf8',
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
const source = createCodexModelProvidersSource({ settingsPath, env: {}, homeDir: directory });
|
|
114
|
+
installCodexProviderRegistryPort({
|
|
115
|
+
read: () => ({
|
|
116
|
+
providers: source.read().providers.map((provider) => ({ id: provider.id })),
|
|
117
|
+
reservedIds: [],
|
|
118
|
+
}),
|
|
119
|
+
resolve: (id) => source.resolve(id),
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
try {
|
|
123
|
+
await run(directory);
|
|
124
|
+
} finally {
|
|
125
|
+
await rm(directory, { recursive: true, force: true });
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/** A context whose single model option carries (or omits) a provider binding. */
|
|
130
|
+
function contextForModel(
|
|
131
|
+
modelId: string,
|
|
132
|
+
codexProviderId?: string,
|
|
133
|
+
): ProviderRuntimeContext {
|
|
134
|
+
return {
|
|
135
|
+
resolveProviderSessionId: () => null,
|
|
136
|
+
resolveResumeModel: async () => modelId,
|
|
137
|
+
getProviderModels: async () => ({
|
|
138
|
+
OPTIONS: [{
|
|
139
|
+
value: modelId,
|
|
140
|
+
label: modelId,
|
|
141
|
+
isCustom: true,
|
|
142
|
+
config: codexProviderId === undefined
|
|
143
|
+
? { env: [] }
|
|
144
|
+
: { codexProviderId, env: [] },
|
|
145
|
+
}],
|
|
146
|
+
DEFAULT: modelId,
|
|
147
|
+
}),
|
|
148
|
+
normalizeMessage: () => [],
|
|
149
|
+
isProviderInstalled: async () => true,
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Captures the options the runtime handed the REAL SDK constructor while
|
|
155
|
+
* letting the original `startThread` run, so the CLI still starts for real.
|
|
156
|
+
*/
|
|
157
|
+
function captureCodexOptions(t: TestContext): { options: () => CodexOptions } {
|
|
158
|
+
let captured: CodexOptions | undefined;
|
|
159
|
+
const originalStartThread = Codex.prototype.startThread;
|
|
160
|
+
t.mock.method(Codex.prototype, 'startThread', function startThread(
|
|
161
|
+
this: Codex,
|
|
162
|
+
options?: ThreadOptions,
|
|
163
|
+
): Thread {
|
|
164
|
+
captured = (this as unknown as { options: CodexOptions }).options;
|
|
165
|
+
return originalStartThread.call(this, options);
|
|
166
|
+
});
|
|
167
|
+
return {
|
|
168
|
+
options: () => {
|
|
169
|
+
assert.ok(captured, 'the runtime must construct the SDK through startThread');
|
|
170
|
+
return captured;
|
|
171
|
+
},
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
test('AC-464: a bound model launches the real SDK under its provider and the token file supplies Authorization', async (t) => {
|
|
176
|
+
const mock = await startMockResponsesApi();
|
|
177
|
+
const previousHome = process.env.HOME;
|
|
178
|
+
process.env[HOST_SENTINEL_KEY] = HOST_SENTINEL_VALUE;
|
|
179
|
+
|
|
180
|
+
try {
|
|
181
|
+
await withProviderRegistry(mock.baseUrl, async (home) => {
|
|
182
|
+
// Keep the real CLI's session/config writes inside the criterion's temp
|
|
183
|
+
// directory instead of the operator's ~/.codex.
|
|
184
|
+
process.env.HOME = home;
|
|
185
|
+
const capture = captureCodexOptions(t);
|
|
186
|
+
|
|
187
|
+
const messages: any[] = [];
|
|
188
|
+
await codexRuntime.run('hello provider', {
|
|
189
|
+
sessionId: 'codex-perrun-injection',
|
|
190
|
+
model: 'bound-model',
|
|
191
|
+
cwd: home,
|
|
192
|
+
permissionMode: 'bypassPermissions',
|
|
193
|
+
}, { isWebSocketWriter: true, send: (message) => messages.push(message) }, contextForModel('bound-model', PROVIDER_ID));
|
|
194
|
+
|
|
195
|
+
const options = capture.options();
|
|
196
|
+
assert.deepEqual(options.config, {
|
|
197
|
+
model_provider: PROVIDER_ID,
|
|
198
|
+
model_providers: {
|
|
199
|
+
[PROVIDER_ID]: {
|
|
200
|
+
name: 'Bob Upstream',
|
|
201
|
+
base_url: mock.baseUrl,
|
|
202
|
+
env_key: ENV_KEY,
|
|
203
|
+
wire_api: 'responses',
|
|
204
|
+
},
|
|
205
|
+
},
|
|
206
|
+
});
|
|
207
|
+
assert.equal(options.env?.[ENV_KEY], TOKEN_SECRET);
|
|
208
|
+
assert.equal(options.env?.[HOST_SENTINEL_KEY], undefined, 'a host sentinel must not reach the child');
|
|
209
|
+
|
|
210
|
+
// The real bundled CLI actually spoke to the mock: the secret arrived as
|
|
211
|
+
// the bearer token, which is the end-to-end reading the config alone
|
|
212
|
+
// cannot prove.
|
|
213
|
+
assert.ok(mock.requests.length >= 1, `the real CLI must call the mock; messages=${JSON.stringify(messages)}`);
|
|
214
|
+
assert.equal(mock.requests[0].url, '/v1/responses');
|
|
215
|
+
assert.equal(mock.requests[0].authorization, `Bearer ${TOKEN_SECRET}`);
|
|
216
|
+
});
|
|
217
|
+
} finally {
|
|
218
|
+
if (previousHome === undefined) delete process.env.HOME;
|
|
219
|
+
else process.env.HOME = previousHome;
|
|
220
|
+
delete process.env[HOST_SENTINEL_KEY];
|
|
221
|
+
await mock.close();
|
|
222
|
+
}
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
test('AC-464: an unbound model keeps the previous no-options construction', async (t) => {
|
|
226
|
+
let captured: CodexOptions | undefined;
|
|
227
|
+
const fakeThread = {
|
|
228
|
+
id: 'native-thread',
|
|
229
|
+
async runStreamed() {
|
|
230
|
+
return { events: (async function* () {})() };
|
|
231
|
+
},
|
|
232
|
+
} as unknown as Thread;
|
|
233
|
+
// No CLI is needed for the negative reading: the question is what the runtime
|
|
234
|
+
// handed the constructor, and the SDK stores it on the instance.
|
|
235
|
+
t.mock.method(Codex.prototype, 'startThread', function startThread(this: Codex) {
|
|
236
|
+
captured = (this as unknown as { options: CodexOptions }).options;
|
|
237
|
+
return fakeThread;
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
await codexRuntime.run('hello default', {
|
|
241
|
+
model: 'unbound-model',
|
|
242
|
+
cwd: os.tmpdir(),
|
|
243
|
+
}, { isWebSocketWriter: true, send: () => {} }, contextForModel('unbound-model'));
|
|
244
|
+
|
|
245
|
+
assert.ok(captured, 'the runtime must construct the SDK through startThread');
|
|
246
|
+
assert.equal(captured.config, undefined);
|
|
247
|
+
assert.equal(captured.env, undefined);
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
test('AC-464: a model bound to an unknown provider fails readably and never dials the default', async (t) => {
|
|
251
|
+
const mock = await startMockResponsesApi();
|
|
252
|
+
try {
|
|
253
|
+
await withProviderRegistry(mock.baseUrl, async (home) => {
|
|
254
|
+
const messages: any[] = [];
|
|
255
|
+
await codexRuntime.run('hello nobody', {
|
|
256
|
+
model: 'dangling-model',
|
|
257
|
+
cwd: home,
|
|
258
|
+
}, { isWebSocketWriter: true, send: (message) => messages.push(message) }, contextForModel('dangling-model', 'nobody'));
|
|
259
|
+
|
|
260
|
+
const error = messages.find((message) => message.kind === 'error');
|
|
261
|
+
assert.ok(error, `an unresolvable provider must surface an error; messages=${JSON.stringify(messages)}`);
|
|
262
|
+
assert.match(String(error.content), /nobody/);
|
|
263
|
+
assert.match(String(error.content), /not configured/);
|
|
264
|
+
assert.equal(mock.requests.length, 0, 'a refusal must not spawn the CLI against any endpoint');
|
|
265
|
+
});
|
|
266
|
+
} finally {
|
|
267
|
+
await mock.close();
|
|
268
|
+
}
|
|
269
|
+
});
|
|
@@ -0,0 +1,386 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import { once } from 'node:events';
|
|
3
|
+
import { mkdir, mkdtemp, rm, writeFile } from 'node:fs/promises';
|
|
4
|
+
import type { AddressInfo } from 'node:net';
|
|
5
|
+
import os from 'node:os';
|
|
6
|
+
import path from 'node:path';
|
|
7
|
+
import test from 'node:test';
|
|
8
|
+
|
|
9
|
+
import express, { type NextFunction, type Request, type Response } from 'express';
|
|
10
|
+
|
|
11
|
+
import {
|
|
12
|
+
closeConnection,
|
|
13
|
+
getConnection,
|
|
14
|
+
initializeDatabase,
|
|
15
|
+
providerModelsDb,
|
|
16
|
+
sessionsDb,
|
|
17
|
+
} from '@/modules/database/index.js';
|
|
18
|
+
import providerRouter from '@/modules/providers/provider.routes.js';
|
|
19
|
+
import { providerModelsService } from '@/modules/providers/index.js';
|
|
20
|
+
import { sessionsService } from '@/modules/providers/services/sessions.service.js';
|
|
21
|
+
import { providerRegistry } from '@/modules/providers/provider.registry.js';
|
|
22
|
+
import {
|
|
23
|
+
installCodexProviderRegistryPort,
|
|
24
|
+
type CodexProviderRegistryReading,
|
|
25
|
+
} from '@/shared/ports/codex-provider-registry.port.js';
|
|
26
|
+
import type { IProviderFork } from '@/shared/interfaces.js';
|
|
27
|
+
import { AppError } from '@/shared/utils.js';
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* A session records the upstream provider its model is bound to (AC-462).
|
|
31
|
+
*
|
|
32
|
+
* Legs:
|
|
33
|
+
* 1. the sessions row gains `model_provider_id`, and the first send records the
|
|
34
|
+
* provider the selected model entry declares (`codexProviderId`);
|
|
35
|
+
* 2. a model that declares no provider leaves the session unbound;
|
|
36
|
+
* 3. moving a bound session to a selection that runs under a DIFFERENT provider
|
|
37
|
+
* is refused with the fork/new-session action, and nothing is written; the
|
|
38
|
+
* same provider with another model is an ordinary switch;
|
|
39
|
+
* 4. a provider that was deleted or renamed is MARKED — the recorded model is
|
|
40
|
+
* still the one the conversation ran with, never the catalog default — and a
|
|
41
|
+
* fresh binding to it is refused;
|
|
42
|
+
* 5. a row that recorded no binding (a session from before the column, or one
|
|
43
|
+
* that only ever ran unbound) can still establish one;
|
|
44
|
+
* 6. the HTTP route answers the refusal with the same code and message;
|
|
45
|
+
* 7. a fork inherits the binding, because its source thread started under it;
|
|
46
|
+
* 8. the pre-admission send guard refuses the same switch before a run exists.
|
|
47
|
+
*
|
|
48
|
+
* WHY THESE READ THE SERVICE, NOT A COPY OF ITS LOGIC. `setSessionModel` is the
|
|
49
|
+
* funnel every send passes through (`chat.send`, `chat.edit-send`, the
|
|
50
|
+
* active-model route and the MCP reconfigure tool), so a green run over it is a
|
|
51
|
+
* reading of the guard every entry point shares. The registry is a shared port
|
|
52
|
+
* installed by the settings module in production; these legs install a fake
|
|
53
|
+
* reading and mutate its ids to model a deleted or renamed provider.
|
|
54
|
+
*/
|
|
55
|
+
|
|
56
|
+
const BOUND_BOB = 'bound-bob-model';
|
|
57
|
+
const BOUND_BOB_SECOND = 'bound-bob-second-model';
|
|
58
|
+
const BOUND_ALICE = 'bound-alice-model';
|
|
59
|
+
const UNBOUND = 'plain-unbound-model';
|
|
60
|
+
|
|
61
|
+
type BindingWorkspace = {
|
|
62
|
+
baseUrl: string;
|
|
63
|
+
workspacePath: string;
|
|
64
|
+
/** The project provider ids the fake registry currently defines. */
|
|
65
|
+
providerIds: Set<string>;
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const createRegistry = (): { providerIds: Set<string>; read: () => CodexProviderRegistryReading } => {
|
|
69
|
+
const providerIds = new Set(['bob', 'alice']);
|
|
70
|
+
const read = (): CodexProviderRegistryReading => ({
|
|
71
|
+
providers: [...providerIds].map((id) => ({ id })),
|
|
72
|
+
reservedIds: ['openai', 'ollama', 'lmstudio'],
|
|
73
|
+
});
|
|
74
|
+
return { providerIds, read };
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
async function withBindingWorkspace(run: (context: BindingWorkspace) => Promise<void>): Promise<void> {
|
|
78
|
+
const previousDatabasePath = process.env.DATABASE_PATH;
|
|
79
|
+
const directory = await mkdtemp(path.join(os.tmpdir(), 'codex-session-provider-binding-'));
|
|
80
|
+
const workspacePath = path.join(directory, 'workspace');
|
|
81
|
+
await mkdir(workspacePath, { recursive: true });
|
|
82
|
+
const { providerIds, read } = createRegistry();
|
|
83
|
+
|
|
84
|
+
closeConnection();
|
|
85
|
+
process.env.DATABASE_PATH = path.join(directory, 'auth.db');
|
|
86
|
+
await writeFile(process.env.DATABASE_PATH, '');
|
|
87
|
+
await initializeDatabase();
|
|
88
|
+
// Installed before the first write: the service reads this port lazily, so
|
|
89
|
+
// every leg below is judged against the registry this test states.
|
|
90
|
+
installCodexProviderRegistryPort({ read });
|
|
91
|
+
|
|
92
|
+
const app = express().use(express.json()).use('/api/providers', providerRouter);
|
|
93
|
+
app.use((error: unknown, _req: Request, res: Response, _next: NextFunction) => {
|
|
94
|
+
if (error instanceof AppError) {
|
|
95
|
+
res.status(error.statusCode).json({
|
|
96
|
+
success: false,
|
|
97
|
+
error: { code: error.code, message: error.message },
|
|
98
|
+
});
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
res.status(500).json({ success: false, error: { code: 'INTERNAL_ERROR' } });
|
|
102
|
+
});
|
|
103
|
+
const server = app.listen(0, '127.0.0.1');
|
|
104
|
+
await once(server, 'listening');
|
|
105
|
+
|
|
106
|
+
try {
|
|
107
|
+
const address = server.address() as AddressInfo;
|
|
108
|
+
await run({ baseUrl: `http://127.0.0.1:${address.port}`, workspacePath, providerIds });
|
|
109
|
+
} finally {
|
|
110
|
+
await new Promise<void>((resolve, reject) => {
|
|
111
|
+
server.close((error) => error ? reject(error) : resolve());
|
|
112
|
+
});
|
|
113
|
+
closeConnection();
|
|
114
|
+
if (previousDatabasePath === undefined) {
|
|
115
|
+
delete process.env.DATABASE_PATH;
|
|
116
|
+
} else {
|
|
117
|
+
process.env.DATABASE_PATH = previousDatabasePath;
|
|
118
|
+
}
|
|
119
|
+
await rm(directory, { recursive: true, force: true });
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** Seeds one custom Codex model entry; `providerId` is its `codexProviderId`. */
|
|
124
|
+
function seedModel(modelId: string, providerId?: string): void {
|
|
125
|
+
providerModelsDb.createCustomProviderModel('codex', {
|
|
126
|
+
id: modelId,
|
|
127
|
+
model: modelId,
|
|
128
|
+
config: providerId === undefined ? { env: [] } : { env: [], codexProviderId: providerId },
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/** Seeds the app row a send would write to. */
|
|
133
|
+
function seedSession(sessionId: string, workspacePath: string, provider = 'codex'): void {
|
|
134
|
+
sessionsDb.createAppSession(sessionId, provider, workspacePath, 'Binding session');
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function sessionColumns(): string[] {
|
|
138
|
+
return (getConnection().prepare('PRAGMA table_info(sessions)').all() as Array<{ name: string }>)
|
|
139
|
+
.map((column) => column.name);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* The refusal a provider switch must produce: 409, the recorded id, what the
|
|
144
|
+
* requested selection would run under, and the way out.
|
|
145
|
+
*/
|
|
146
|
+
function providerSwitchRefusal(expectedTarget: RegExp): (error: unknown) => boolean {
|
|
147
|
+
return (error: unknown): boolean => {
|
|
148
|
+
assert.ok(error instanceof AppError, `expected an AppError, got ${String(error)}`);
|
|
149
|
+
assert.equal(error.statusCode, 409);
|
|
150
|
+
assert.equal(error.code, 'SESSION_PROVIDER_MISMATCH');
|
|
151
|
+
assert.match(error.message, /"bob"/);
|
|
152
|
+
assert.match(error.message, expectedTarget);
|
|
153
|
+
assert.match(error.message, /fork this session/i);
|
|
154
|
+
assert.match(error.message, /start a new one/i);
|
|
155
|
+
return true;
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
test('the first send records the upstream provider its model is bound to, and readers carry it', async () => {
|
|
160
|
+
await withBindingWorkspace(async ({ workspacePath }) => {
|
|
161
|
+
assert.ok(
|
|
162
|
+
sessionColumns().includes('model_provider_id'),
|
|
163
|
+
'the sessions table must carry the column this feature records into',
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
seedModel(BOUND_BOB, 'bob');
|
|
167
|
+
seedSession('first-send', workspacePath);
|
|
168
|
+
|
|
169
|
+
const stored = providerModelsService.setSessionModel('codex', 'first-send', BOUND_BOB);
|
|
170
|
+
|
|
171
|
+
assert.equal(sessionsDb.getSessionById('first-send')?.model_provider_id, 'bob');
|
|
172
|
+
assert.equal(stored?.modelProviderId, 'bob');
|
|
173
|
+
assert.equal(stored?.modelProviderProblem, null);
|
|
174
|
+
|
|
175
|
+
const resolved = await providerModelsService.resolveSessionModel('codex', { sessionId: 'first-send' });
|
|
176
|
+
assert.equal(resolved.model, BOUND_BOB);
|
|
177
|
+
assert.equal(resolved.modelProviderId, 'bob');
|
|
178
|
+
assert.equal(resolved.modelProviderProblem, null);
|
|
179
|
+
assert.equal(resolved.source, 'session');
|
|
180
|
+
|
|
181
|
+
// Write-once: a later write cannot move the conversation behind the guard.
|
|
182
|
+
assert.equal(sessionsDb.setSessionModelProviderId('first-send', 'alice'), false);
|
|
183
|
+
assert.equal(sessionsDb.getSessionById('first-send')?.model_provider_id, 'bob');
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
test('a model that declares no provider leaves the session unbound', async () => {
|
|
188
|
+
await withBindingWorkspace(async ({ workspacePath }) => {
|
|
189
|
+
seedModel(UNBOUND);
|
|
190
|
+
seedSession('unbound-session', workspacePath);
|
|
191
|
+
|
|
192
|
+
const stored = providerModelsService.setSessionModel('codex', 'unbound-session', UNBOUND);
|
|
193
|
+
|
|
194
|
+
assert.equal(sessionsDb.getSessionById('unbound-session')?.model_provider_id, null);
|
|
195
|
+
assert.equal(stored?.modelProviderId, null);
|
|
196
|
+
assert.equal(stored?.modelProviderProblem, null);
|
|
197
|
+
|
|
198
|
+
const resolved = await providerModelsService.resolveSessionModel('codex', { sessionId: 'unbound-session' });
|
|
199
|
+
assert.equal(resolved.model, UNBOUND);
|
|
200
|
+
assert.equal(resolved.modelProviderId, null);
|
|
201
|
+
assert.equal(resolved.modelProviderProblem, null);
|
|
202
|
+
});
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
test('moving a bound session to another provider is refused with the fork action; the same provider is not', async () => {
|
|
206
|
+
await withBindingWorkspace(async ({ workspacePath }) => {
|
|
207
|
+
seedModel(BOUND_BOB, 'bob');
|
|
208
|
+
seedModel(BOUND_BOB_SECOND, 'bob');
|
|
209
|
+
seedModel(BOUND_ALICE, 'alice');
|
|
210
|
+
seedModel(UNBOUND);
|
|
211
|
+
seedSession('bound-session', workspacePath);
|
|
212
|
+
providerModelsService.setSessionModel('codex', 'bound-session', BOUND_BOB);
|
|
213
|
+
|
|
214
|
+
// Another provider's model — including a model that would inherit the
|
|
215
|
+
// process-global provider (NULL) — is a provider switch, not a model switch.
|
|
216
|
+
assert.throws(
|
|
217
|
+
() => providerModelsService.setSessionModel('codex', 'bound-session', BOUND_ALICE),
|
|
218
|
+
providerSwitchRefusal(/"alice"/),
|
|
219
|
+
);
|
|
220
|
+
assert.throws(
|
|
221
|
+
() => providerModelsService.setSessionModel('codex', 'bound-session', UNBOUND),
|
|
222
|
+
providerSwitchRefusal(/inherits the provider default/),
|
|
223
|
+
);
|
|
224
|
+
|
|
225
|
+
// Nothing was written by either refusal.
|
|
226
|
+
const unchanged = sessionsDb.getSessionById('bound-session');
|
|
227
|
+
assert.equal(unchanged?.model, BOUND_BOB);
|
|
228
|
+
assert.equal(unchanged?.model_provider_id, 'bob');
|
|
229
|
+
|
|
230
|
+
// Another model on the SAME provider is an ordinary switch.
|
|
231
|
+
providerModelsService.setSessionModel('codex', 'bound-session', BOUND_BOB_SECOND);
|
|
232
|
+
const switched = sessionsDb.getSessionById('bound-session');
|
|
233
|
+
assert.equal(switched?.model, BOUND_BOB_SECOND);
|
|
234
|
+
assert.equal(switched?.model_provider_id, 'bob');
|
|
235
|
+
});
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
test('a deleted or renamed provider is marked, and the session keeps the model it ran with', async () => {
|
|
239
|
+
await withBindingWorkspace(async ({ workspacePath, providerIds }) => {
|
|
240
|
+
seedModel(BOUND_BOB, 'bob');
|
|
241
|
+
seedSession('orphaned-session', workspacePath);
|
|
242
|
+
providerModelsService.setSessionModel('codex', 'orphaned-session', BOUND_BOB);
|
|
243
|
+
|
|
244
|
+
const before = await providerModelsService.resolveSessionModel('codex', { sessionId: 'orphaned-session' });
|
|
245
|
+
assert.equal(before.modelProviderProblem, null, 'a provider that still exists is not marked');
|
|
246
|
+
|
|
247
|
+
// Renaming the provider is a full-file replacement of the registry: the old
|
|
248
|
+
// id disappears, exactly as a delete would.
|
|
249
|
+
providerIds.delete('bob');
|
|
250
|
+
providerIds.add('robert');
|
|
251
|
+
|
|
252
|
+
const after = await providerModelsService.resolveSessionModel('codex', { sessionId: 'orphaned-session' });
|
|
253
|
+
// The model is the recorded one, NOT the catalog default: a provider that
|
|
254
|
+
// disappeared must not make the session look like it never picked a model.
|
|
255
|
+
assert.equal(after.model, BOUND_BOB);
|
|
256
|
+
assert.equal(after.modelProviderId, 'bob');
|
|
257
|
+
assert.ok(after.modelProviderProblem);
|
|
258
|
+
assert.match(after.modelProviderProblem, /"bob"/);
|
|
259
|
+
assert.match(after.modelProviderProblem, /no longer defined/);
|
|
260
|
+
assert.match(after.modelProviderProblem, /fork this session/i);
|
|
261
|
+
|
|
262
|
+
// The row itself is untouched by the read.
|
|
263
|
+
const row = sessionsDb.getSessionById('orphaned-session');
|
|
264
|
+
assert.equal(row?.model, BOUND_BOB);
|
|
265
|
+
assert.equal(row?.model_provider_id, 'bob');
|
|
266
|
+
|
|
267
|
+
// A fresh binding to the retired provider is refused, and writes nothing.
|
|
268
|
+
seedSession('fresh-session', workspacePath);
|
|
269
|
+
assert.throws(
|
|
270
|
+
() => providerModelsService.setSessionModel('codex', 'fresh-session', BOUND_BOB),
|
|
271
|
+
(error: unknown) => error instanceof AppError
|
|
272
|
+
&& error.statusCode === 409
|
|
273
|
+
&& error.code === 'SESSION_PROVIDER_UNKNOWN'
|
|
274
|
+
&& /"bob"/.test(error.message)
|
|
275
|
+
&& /no longer exists/.test(error.message),
|
|
276
|
+
);
|
|
277
|
+
const fresh = sessionsDb.getSessionById('fresh-session');
|
|
278
|
+
assert.equal(fresh?.model_provider_id, null);
|
|
279
|
+
});
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
test('a row that recorded no binding can still establish one', async () => {
|
|
283
|
+
await withBindingWorkspace(async ({ workspacePath }) => {
|
|
284
|
+
seedModel(UNBOUND);
|
|
285
|
+
seedModel(BOUND_BOB, 'bob');
|
|
286
|
+
seedSession('legacy-session', workspacePath);
|
|
287
|
+
|
|
288
|
+
// A session from before the column: it has a model, but no provider id.
|
|
289
|
+
sessionsDb.setSessionModel('legacy-session', UNBOUND);
|
|
290
|
+
assert.equal(sessionsDb.getSessionById('legacy-session')?.model_provider_id, null);
|
|
291
|
+
|
|
292
|
+
const stored = providerModelsService.setSessionModel('codex', 'legacy-session', BOUND_BOB);
|
|
293
|
+
|
|
294
|
+
assert.equal(stored?.modelProviderId, 'bob');
|
|
295
|
+
assert.equal(sessionsDb.getSessionById('legacy-session')?.model_provider_id, 'bob');
|
|
296
|
+
});
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
test('the active-model route answers the refusal with the same readable code', async () => {
|
|
300
|
+
await withBindingWorkspace(async ({ baseUrl, workspacePath }) => {
|
|
301
|
+
seedModel(BOUND_BOB, 'bob');
|
|
302
|
+
seedModel(BOUND_ALICE, 'alice');
|
|
303
|
+
seedSession('route-session', workspacePath);
|
|
304
|
+
providerModelsService.setSessionModel('codex', 'route-session', BOUND_BOB);
|
|
305
|
+
|
|
306
|
+
const refused = await fetch(`${baseUrl}/api/providers/codex/sessions/route-session/active-model`, {
|
|
307
|
+
method: 'POST',
|
|
308
|
+
headers: { 'content-type': 'application/json' },
|
|
309
|
+
body: JSON.stringify({ model: BOUND_ALICE }),
|
|
310
|
+
});
|
|
311
|
+
const refusedBody = await refused.json() as { error?: { code?: string; message?: string } };
|
|
312
|
+
|
|
313
|
+
assert.equal(refused.status, 409);
|
|
314
|
+
assert.equal(refusedBody.error?.code, 'SESSION_PROVIDER_MISMATCH');
|
|
315
|
+
assert.match(String(refusedBody.error?.message), /fork this session/i);
|
|
316
|
+
assert.equal(sessionsDb.getSessionById('route-session')?.model, BOUND_BOB);
|
|
317
|
+
|
|
318
|
+
const reading = await fetch(`${baseUrl}/api/providers/codex/sessions/route-session/active-model`);
|
|
319
|
+
const readingBody = await reading.json() as {
|
|
320
|
+
data: { model: string; modelProviderId: string | null; modelProviderProblem: string | null };
|
|
321
|
+
};
|
|
322
|
+
assert.equal(readingBody.data.model, BOUND_BOB);
|
|
323
|
+
assert.equal(readingBody.data.modelProviderId, 'bob');
|
|
324
|
+
assert.equal(readingBody.data.modelProviderProblem, null);
|
|
325
|
+
});
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
test('a fork inherits the upstream provider binding of the conversation it branched from', async () => {
|
|
329
|
+
await withBindingWorkspace(async ({ workspacePath }) => {
|
|
330
|
+
const sourcePath = path.join(workspacePath, 'native-source.jsonl');
|
|
331
|
+
const forkedPath = path.join(workspacePath, 'native-fork.jsonl');
|
|
332
|
+
await writeFile(sourcePath, '{}\n', 'utf8');
|
|
333
|
+
await writeFile(forkedPath, '{}\n', 'utf8');
|
|
334
|
+
|
|
335
|
+
seedSession('fork-source', workspacePath, 'claude');
|
|
336
|
+
sessionsDb.assignProviderSessionId('fork-source', 'native-source');
|
|
337
|
+
getConnection()
|
|
338
|
+
.prepare('UPDATE sessions SET jsonl_path = ? WHERE session_id = ?')
|
|
339
|
+
.run(sourcePath, 'fork-source');
|
|
340
|
+
sessionsDb.setSessionModelProviderId('fork-source', 'bob');
|
|
341
|
+
|
|
342
|
+
const claude = providerRegistry.resolveProvider('claude') as { fork?: IProviderFork };
|
|
343
|
+
const realFork = claude.fork;
|
|
344
|
+
Object.defineProperty(claude, 'fork', {
|
|
345
|
+
configurable: true,
|
|
346
|
+
writable: true,
|
|
347
|
+
value: {
|
|
348
|
+
forkSession: async () => ({ providerSessionId: 'native-fork', jsonlPath: forkedPath }),
|
|
349
|
+
} as IProviderFork,
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
try {
|
|
353
|
+
const result = await sessionsService.forkSessionById('fork-source');
|
|
354
|
+
// The branch resumes a thread the provider started under `bob`; dropping
|
|
355
|
+
// the binding would make that resume the mismatch this column prevents.
|
|
356
|
+
assert.equal(sessionsDb.getSessionById(result.sessionId)?.model_provider_id, 'bob');
|
|
357
|
+
} finally {
|
|
358
|
+
Object.defineProperty(claude, 'fork', { configurable: true, writable: true, value: realFork });
|
|
359
|
+
}
|
|
360
|
+
});
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
test('the pre-admission send guard refuses the switch and passes the first send', async () => {
|
|
364
|
+
await withBindingWorkspace(async ({ workspacePath }) => {
|
|
365
|
+
seedModel(BOUND_BOB, 'bob');
|
|
366
|
+
seedModel(BOUND_ALICE, 'alice');
|
|
367
|
+
seedSession('guard-session', workspacePath);
|
|
368
|
+
|
|
369
|
+
// A session with no recorded binding: the first send is exactly where one
|
|
370
|
+
// is allowed to be established, so the guard passes.
|
|
371
|
+
assert.doesNotThrow(() => {
|
|
372
|
+
providerModelsService.assertSessionProviderSwitch('codex', 'guard-session', BOUND_BOB);
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
providerModelsService.setSessionModel('codex', 'guard-session', BOUND_BOB);
|
|
376
|
+
|
|
377
|
+
assert.throws(
|
|
378
|
+
() => providerModelsService.assertSessionProviderSwitch('codex', 'guard-session', BOUND_ALICE),
|
|
379
|
+
providerSwitchRefusal(/"alice"/),
|
|
380
|
+
);
|
|
381
|
+
// Same provider, another model: nothing for the guard to refuse.
|
|
382
|
+
assert.doesNotThrow(() => {
|
|
383
|
+
providerModelsService.assertSessionProviderSwitch('codex', 'guard-session', BOUND_BOB);
|
|
384
|
+
});
|
|
385
|
+
});
|
|
386
|
+
});
|
|
@@ -78,7 +78,7 @@ const writeCodexTranscript = async (
|
|
|
78
78
|
return filePath;
|
|
79
79
|
};
|
|
80
80
|
|
|
81
|
-
test('Codex synchronizer preserves the title assigned when
|
|
81
|
+
test('Codex synchronizer preserves the title assigned when Cloudpit creates a session', { concurrency: false }, async () => {
|
|
82
82
|
const tempRoot = await mkdtemp(path.join(os.tmpdir(), 'codex-session-sync-app-'));
|
|
83
83
|
const workspacePath = path.join(tempRoot, 'workspace');
|
|
84
84
|
await mkdir(workspacePath, { recursive: true });
|
|
@@ -509,14 +509,14 @@ const seedOpenCodeSession = async (
|
|
|
509
509
|
}
|
|
510
510
|
};
|
|
511
511
|
|
|
512
|
-
test('OpenCode synchronizer preserves the title assigned when
|
|
512
|
+
test('OpenCode synchronizer preserves the title assigned when Cloudpit creates a session', { concurrency: false }, async () => {
|
|
513
513
|
const tempRoot = await mkdtemp(path.join(os.tmpdir(), 'opencode-session-sync-app-'));
|
|
514
514
|
const workspacePath = path.join(tempRoot, 'workspace');
|
|
515
515
|
await mkdir(workspacePath, { recursive: true });
|
|
516
516
|
const restoreHomeDir = patchHomeDir(tempRoot);
|
|
517
517
|
|
|
518
518
|
try {
|
|
519
|
-
// Both provider-owned values differ from the
|
|
519
|
+
// Both provider-owned values differ from the Cloudpit title so either one
|
|
520
520
|
// leaking through would change the assertion below.
|
|
521
521
|
await seedOpenCodeSession(tempRoot, workspacePath, {
|
|
522
522
|
sessionId: 'oc-app-1',
|