@cloudcli-ai/cloudcli 1.37.2 → 1.37.3
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 +269 -0
- package/dist/assets/{abnfDiagram-N423BO3Z-C4UAoHJb.js → abnfDiagram-N423BO3Z-CJntskwA.js} +1 -1
- package/dist/assets/{arc-B8jF-ja9.js → arc-CIfH4uMN.js} +1 -1
- package/dist/assets/{architectureDiagram-T3A2C74G-C_Wur_CU.js → architectureDiagram-T3A2C74G-_IpEkukg.js} +1 -1
- package/dist/assets/{blockDiagram-VBNYF7ZC-Cq4P1i8t.js → blockDiagram-VBNYF7ZC-BttxrpgZ.js} +1 -1
- package/dist/assets/{c4Diagram-5PPSVZJV-C1wG4eUj.js → c4Diagram-5PPSVZJV-g51dSRr1.js} +1 -1
- package/dist/assets/channel-C5xU9jMw.js +1 -0
- package/dist/assets/{chunk-2GRJ4B5K-B8fnBG6E.js → chunk-2GRJ4B5K-D_-LbQUa.js} +1 -1
- package/dist/assets/{chunk-2Q5K7J3B-WdfPooPJ.js → chunk-2Q5K7J3B-BtlHzwaO.js} +1 -1
- package/dist/assets/{chunk-5RXB4S5H-DhZJuG2E.js → chunk-5RXB4S5H-UzkpvpAT.js} +1 -1
- package/dist/assets/{chunk-5VM5RSS4-B0rjslal.js → chunk-5VM5RSS4-BKwx5Gag.js} +1 -1
- package/dist/assets/{chunk-6Q2QTUOP-CG2Oj4jL.js → chunk-6Q2QTUOP-D_KEBH5f.js} +1 -1
- package/dist/assets/{chunk-GF5L2VYU-rrl2lRxY.js → chunk-GF5L2VYU-rK2DGyto.js} +1 -1
- package/dist/assets/{chunk-JWPE2WC7-CjVbrVMA.js → chunk-JWPE2WC7-CeBl_N9i.js} +1 -1
- package/dist/assets/{chunk-KBJHAD2P-CRzKgU8S.js → chunk-KBJHAD2P-BiQLWVIr.js} +1 -1
- package/dist/assets/{chunk-RYQCIY6F-DBz4GTZF.js → chunk-RYQCIY6F-C1knVGV5.js} +1 -1
- package/dist/assets/{chunk-XXDRQBXY-wHxalsXk.js → chunk-XXDRQBXY-CtKbJkvF.js} +1 -1
- package/dist/assets/classDiagram-JCYQIIEL-CRrL3vp7.js +1 -0
- package/dist/assets/classDiagram-v2-OCEON4UE-CRrL3vp7.js +1 -0
- package/dist/assets/{cose-bilkent-JH36ORCC-s1hygY8e.js → cose-bilkent-JH36ORCC-DjX8y40Z.js} +1 -1
- package/dist/assets/{cynefin-VYW2F7L2-C4LGV15i.js → cynefin-VYW2F7L2-DM6YeRba.js} +1 -1
- package/dist/assets/{cynefinDiagram-MW4NZA55-C5uhu7B3.js → cynefinDiagram-MW4NZA55-wZJ-vy5B.js} +1 -1
- package/dist/assets/{dagre-VZM6K2ZE-B-OBTewm.js → dagre-VZM6K2ZE-BzP0ejkH.js} +1 -1
- package/dist/assets/{diagram-7IWD3JNH-BjGKQxih.js → diagram-7IWD3JNH-BTL_j23J.js} +1 -1
- package/dist/assets/{diagram-B4RE2ZJO-BMKz1X2L.js → diagram-B4RE2ZJO-Cf1Norgu.js} +1 -1
- package/dist/assets/{diagram-LBJQPF4R-DNh-GhKC.js → diagram-LBJQPF4R-DPiUEKr-.js} +1 -1
- package/dist/assets/{diagram-Q27KOJAE-BaTG9jPT.js → diagram-Q27KOJAE-CTcTpiz5.js} +1 -1
- package/dist/assets/{diagram-UB23O5K3-BUwo2OvJ.js → diagram-UB23O5K3-DPMSdYm0.js} +1 -1
- package/dist/assets/{ebnfDiagram-BXEA7PRR-DAZgfzX2.js → ebnfDiagram-BXEA7PRR-7yvD6uiF.js} +1 -1
- package/dist/assets/{erDiagram-JOGREHBK-1w1CcGEb.js → erDiagram-JOGREHBK-CfgeDoV6.js} +1 -1
- package/dist/assets/{flowDiagram-UKHOOZJN-D4EII8Jj.js → flowDiagram-UKHOOZJN-B27ng5zu.js} +1 -1
- package/dist/assets/{ganttDiagram-PKOTCBZU-ChvJZAgB.js → ganttDiagram-PKOTCBZU-CcC6b_7t.js} +1 -1
- package/dist/assets/{gitGraphDiagram-DS77QQ5N-DtOn41S8.js → gitGraphDiagram-DS77QQ5N-BX5kPGrO.js} +1 -1
- package/dist/assets/index-BTDHQ6Id.css +32 -0
- package/dist/assets/index-sFfz8iBR.js +4931 -0
- package/dist/assets/{infoDiagram-6WML65LV-DN_PVEJZ.js → infoDiagram-6WML65LV-CUP-SlN4.js} +1 -1
- package/dist/assets/{ishikawaDiagram-WSZJBQD7-DbkF1kXb.js → ishikawaDiagram-WSZJBQD7-BTTDhOpX.js} +1 -1
- package/dist/assets/{journeyDiagram-NVQOT4AX-pJwmhddE.js → journeyDiagram-NVQOT4AX-ARXcc61m.js} +1 -1
- package/dist/assets/{kanban-definition-27J2QSJJ-PPAPgNp5.js → kanban-definition-27J2QSJJ-BieMpqMA.js} +1 -1
- package/dist/assets/{linear-iPR-J9f7.js → linear-61vxjRnP.js} +1 -1
- package/dist/assets/{mermaid.core-BEUL86Z6.js → mermaid.core-nhnwkfQx.js} +5 -5
- package/dist/assets/{mindmap-definition-FAOFIHXS-BvZoBib7.js → mindmap-definition-FAOFIHXS-Cm6x5vT1.js} +1 -1
- package/dist/assets/{pegDiagram-VL7TDLO6-DhYreaUd.js → pegDiagram-VL7TDLO6-CcYBq_9H.js} +1 -1
- package/dist/assets/{pieDiagram-7S7Q4E2Y-CrOHlyD0.js → pieDiagram-7S7Q4E2Y-CfvIjfzo.js} +1 -1
- package/dist/assets/{quadrantDiagram-CIZ2JOQS-D4g5yH3t.js → quadrantDiagram-CIZ2JOQS-SgYoWPNb.js} +1 -1
- package/dist/assets/{railroadDiagram-AXF67PYL-Dj39lHlf.js → railroadDiagram-AXF67PYL-D2Mu5E_f.js} +1 -1
- package/dist/assets/{requirementDiagram-LRYGKXZP-1AS4Zgm_.js → requirementDiagram-LRYGKXZP-Azwe_k4Q.js} +1 -1
- package/dist/assets/{sankeyDiagram-W5VNT64P-CKlMn19P.js → sankeyDiagram-W5VNT64P-DN1Ju3PA.js} +1 -1
- package/dist/assets/{sequenceDiagram-SI44F4Z6-B_j3qyvo.js → sequenceDiagram-SI44F4Z6-Bj4qC34S.js} +1 -1
- package/dist/assets/server.browser-CtX3Hdl5.js +27 -0
- package/dist/assets/{sizeCapture-X5ZJPWSS-Bp2Op16v.js → sizeCapture-X5ZJPWSS-Co4PdklG.js} +1 -1
- package/dist/assets/{stateDiagram-OKZ733FA-C0cN6DoY.js → stateDiagram-OKZ733FA-nOvL9dRC.js} +1 -1
- package/dist/assets/stateDiagram-v2-UEYNNEHI-DNv8ppw7.js +1 -0
- package/dist/assets/{swimlanes-SLNWSIFB-BEvXOV9b.js → swimlanes-SLNWSIFB-CCK3wMid.js} +2 -2
- package/dist/assets/swimlanesDiagram-ULZ7WXOC-DnfbNQEp.js +8 -0
- package/dist/assets/{timeline-definition-Z64GVDOM-zdJQinvZ.js → timeline-definition-Z64GVDOM-BC29RNFm.js} +1 -1
- package/dist/assets/{vendor-codemirror-OwyKSvPE.js → vendor-codemirror-CYrSt7-U.js} +18 -18
- package/dist/assets/{vennDiagram-T6HMQDX7-DuF1uZMY.js → vennDiagram-T6HMQDX7-pZlZCtEo.js} +1 -1
- package/dist/assets/{wardleyDiagram-T6FBY63Y-B9AlLQup.js → wardleyDiagram-T6FBY63Y-De8k2-zM.js} +1 -1
- package/dist/assets/{xychartDiagram-ELKLHX3M-DTJX48Qg.js → xychartDiagram-ELKLHX3M-Bk8ykH0w.js} +1 -1
- package/dist/index.html +3 -3
- package/dist-server/server/index.js +7 -3
- package/dist-server/server/index.js.map +1 -1
- package/dist-server/server/modules/agent/agent.routes.js +2 -2
- package/dist-server/server/modules/agent/agent.routes.js.map +1 -1
- package/dist-server/server/modules/agent/tests/agent.routes.test.js +30 -1
- package/dist-server/server/modules/agent/tests/agent.routes.test.js.map +1 -1
- package/dist-server/server/modules/browser-use/browser-use.routes.js +39 -33
- package/dist-server/server/modules/browser-use/browser-use.routes.js.map +1 -1
- package/dist-server/server/modules/database/connection.js +1 -1
- package/dist-server/server/modules/database/connection.js.map +1 -1
- package/dist-server/server/modules/database/index.js +5 -0
- package/dist-server/server/modules/database/index.js.map +1 -1
- package/dist-server/server/modules/database/migrations.js +32 -1
- package/dist-server/server/modules/database/migrations.js.map +1 -1
- package/dist-server/server/modules/database/repositories/scheduled-messages.db.js +71 -0
- package/dist-server/server/modules/database/repositories/scheduled-messages.db.js.map +1 -0
- package/dist-server/server/modules/database/repositories/session-drafts.db.js +105 -0
- package/dist-server/server/modules/database/repositories/session-drafts.db.js.map +1 -0
- package/dist-server/server/modules/database/repositories/sessions.db.js +137 -5
- package/dist-server/server/modules/database/repositories/sessions.db.js.map +1 -1
- package/dist-server/server/modules/database/repositories/user-preferences.db.js +65 -0
- package/dist-server/server/modules/database/repositories/user-preferences.db.js.map +1 -0
- package/dist-server/server/modules/database/schema.js +92 -0
- package/dist-server/server/modules/database/schema.js.map +1 -1
- package/dist-server/server/modules/database/tests/sessions.db.integration.test.js +39 -0
- package/dist-server/server/modules/database/tests/sessions.db.integration.test.js.map +1 -1
- package/dist-server/server/modules/database/tests/user-preferences.db.integration.test.js +105 -0
- package/dist-server/server/modules/database/tests/user-preferences.db.integration.test.js.map +1 -0
- package/dist-server/server/modules/projects/services/project-delete.service.js +12 -1
- package/dist-server/server/modules/projects/services/project-delete.service.js.map +1 -1
- package/dist-server/server/modules/projects/services/project-star.service.js +2 -2
- package/dist-server/server/modules/projects/services/project-star.service.js.map +1 -1
- package/dist-server/server/modules/providers/index.js +3 -0
- package/dist-server/server/modules/providers/index.js.map +1 -1
- package/dist-server/server/modules/providers/list/claude/claude-auth.provider.js +20 -1
- package/dist-server/server/modules/providers/list/claude/claude-auth.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/claude/claude-fork.provider.js +46 -0
- package/dist-server/server/modules/providers/list/claude/claude-fork.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/claude/claude-models.provider.js +35 -6
- package/dist-server/server/modules/providers/list/claude/claude-models.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/claude/claude-runtime.provider.js +168 -38
- package/dist-server/server/modules/providers/list/claude/claude-runtime.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/claude/claude-sessions.provider.js +388 -50
- package/dist-server/server/modules/providers/list/claude/claude-sessions.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/claude/claude-skills.provider.js +4 -1
- package/dist-server/server/modules/providers/list/claude/claude-skills.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/claude/claude.provider.js +2 -0
- package/dist-server/server/modules/providers/list/claude/claude.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/codex/codex-app-server.client.js +204 -0
- package/dist-server/server/modules/providers/list/codex/codex-app-server.client.js.map +1 -0
- package/dist-server/server/modules/providers/list/codex/codex-fork.provider.js +32 -0
- package/dist-server/server/modules/providers/list/codex/codex-fork.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/codex/codex-models.provider.js +16 -0
- package/dist-server/server/modules/providers/list/codex/codex-models.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/codex/codex-runtime.provider.js +60 -49
- package/dist-server/server/modules/providers/list/codex/codex-runtime.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/codex/codex-session-synchronizer.provider.js +8 -0
- package/dist-server/server/modules/providers/list/codex/codex-session-synchronizer.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/codex/codex-sessions.provider.js +1565 -432
- package/dist-server/server/modules/providers/list/codex/codex-sessions.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/codex/codex.provider.js +2 -0
- package/dist-server/server/modules/providers/list/codex/codex.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/cursor/cursor-runtime.provider.js +8 -6
- package/dist-server/server/modules/providers/list/cursor/cursor-runtime.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/cursor/cursor-session-synchronizer.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/opencode/opencode-models.provider.js +258 -6
- package/dist-server/server/modules/providers/list/opencode/opencode-models.provider.js.map +1 -1
- package/dist-server/server/modules/providers/provider.routes.js +24 -0
- package/dist-server/server/modules/providers/provider.routes.js.map +1 -1
- package/dist-server/server/modules/providers/services/provider-capabilities.service.js +14 -0
- package/dist-server/server/modules/providers/services/provider-capabilities.service.js.map +1 -1
- package/dist-server/server/modules/providers/services/provider-token-usage.service.js +139 -35
- package/dist-server/server/modules/providers/services/provider-token-usage.service.js.map +1 -1
- package/dist-server/server/modules/providers/services/session-history-cache.service.js +87 -0
- package/dist-server/server/modules/providers/services/session-history-cache.service.js.map +1 -0
- package/dist-server/server/modules/providers/services/session-synchronizer.service.js +105 -32
- package/dist-server/server/modules/providers/services/session-synchronizer.service.js.map +1 -1
- package/dist-server/server/modules/providers/services/sessions-watcher.service.js +3 -60
- package/dist-server/server/modules/providers/services/sessions-watcher.service.js.map +1 -1
- package/dist-server/server/modules/providers/services/sessions.service.js +179 -9
- package/dist-server/server/modules/providers/services/sessions.service.js.map +1 -1
- package/dist-server/server/modules/providers/tests/claude-auth.test.js +52 -0
- package/dist-server/server/modules/providers/tests/claude-auth.test.js.map +1 -1
- package/dist-server/server/modules/providers/tests/claude-models.test.js +39 -0
- package/dist-server/server/modules/providers/tests/claude-models.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/claude-sessions.test.js +465 -39
- package/dist-server/server/modules/providers/tests/claude-sessions.test.js.map +1 -1
- package/dist-server/server/modules/providers/tests/claude-subagent-echo.test.js +16 -0
- package/dist-server/server/modules/providers/tests/claude-subagent-echo.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/claude-token-budget.test.js +85 -0
- package/dist-server/server/modules/providers/tests/claude-token-budget.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/codex-message-editing.test.js +338 -0
- package/dist-server/server/modules/providers/tests/codex-message-editing.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/codex-runtime.test.js +51 -0
- package/dist-server/server/modules/providers/tests/codex-runtime.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/codex-session-fork.test.js +54 -0
- package/dist-server/server/modules/providers/tests/codex-session-fork.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/codex-sessions.test.js +304 -15
- package/dist-server/server/modules/providers/tests/codex-sessions.test.js.map +1 -1
- package/dist-server/server/modules/providers/tests/opencode-models.test.js +117 -4
- package/dist-server/server/modules/providers/tests/opencode-models.test.js.map +1 -1
- package/dist-server/server/modules/providers/tests/provider-token-usage.service.test.js +164 -1
- package/dist-server/server/modules/providers/tests/provider-token-usage.service.test.js.map +1 -1
- package/dist-server/server/modules/providers/tests/session-fork.test.js +113 -0
- package/dist-server/server/modules/providers/tests/session-fork.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/session-history-cache.service.test.js +120 -0
- package/dist-server/server/modules/providers/tests/session-history-cache.service.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/session-orphan-prune.test.js +90 -0
- package/dist-server/server/modules/providers/tests/session-orphan-prune.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/session-synchronizer-coalescing.test.js +122 -0
- package/dist-server/server/modules/providers/tests/session-synchronizer-coalescing.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/sessions.service.test.js +45 -1
- package/dist-server/server/modules/providers/tests/sessions.service.test.js.map +1 -1
- package/dist-server/server/modules/providers/tests/skills.test.js +15 -3
- package/dist-server/server/modules/providers/tests/skills.test.js.map +1 -1
- package/dist-server/server/modules/scheduled-messages/index.js +5 -0
- package/dist-server/server/modules/scheduled-messages/index.js.map +1 -0
- package/dist-server/server/modules/scheduled-messages/scheduled-messages.routes.js +47 -0
- package/dist-server/server/modules/scheduled-messages/scheduled-messages.routes.js.map +1 -0
- package/dist-server/server/modules/scheduled-messages/services/scheduled-message-dispatcher.service.js +163 -0
- package/dist-server/server/modules/scheduled-messages/services/scheduled-message-dispatcher.service.js.map +1 -0
- package/dist-server/server/modules/scheduled-messages/services/scheduled-messages.service.js +95 -0
- package/dist-server/server/modules/scheduled-messages/services/scheduled-messages.service.js.map +1 -0
- package/dist-server/server/modules/scheduled-messages/tests/scheduled-messages.test.js +255 -0
- package/dist-server/server/modules/scheduled-messages/tests/scheduled-messages.test.js.map +1 -0
- package/dist-server/server/modules/taskmaster/taskmaster.routes.js +23 -23
- package/dist-server/server/modules/taskmaster/taskmaster.routes.js.map +1 -1
- package/dist-server/server/modules/taskmaster/tests/taskmaster.routes.test.js +48 -0
- package/dist-server/server/modules/taskmaster/tests/taskmaster.routes.test.js.map +1 -1
- package/dist-server/server/modules/user/tests/user.service.test.js +70 -0
- package/dist-server/server/modules/user/tests/user.service.test.js.map +1 -1
- package/dist-server/server/modules/user/user.module.js +10 -1
- package/dist-server/server/modules/user/user.module.js.map +1 -1
- package/dist-server/server/modules/user/user.routes.js +44 -0
- package/dist-server/server/modules/user/user.routes.js.map +1 -1
- package/dist-server/server/modules/user/user.service.js +75 -0
- package/dist-server/server/modules/user/user.service.js.map +1 -1
- package/dist-server/server/modules/voice/tests/voice.service.test.js +3 -2
- package/dist-server/server/modules/voice/tests/voice.service.test.js.map +1 -1
- package/dist-server/server/modules/websocket/index.js +7 -0
- package/dist-server/server/modules/websocket/index.js.map +1 -1
- package/dist-server/server/modules/websocket/services/chat-run-registry.service.js +10 -44
- package/dist-server/server/modules/websocket/services/chat-run-registry.service.js.map +1 -1
- package/dist-server/server/modules/websocket/services/chat-session-writer.service.js +35 -5
- package/dist-server/server/modules/websocket/services/chat-session-writer.service.js.map +1 -1
- package/dist-server/server/modules/websocket/services/chat-websocket.service.js +166 -9
- package/dist-server/server/modules/websocket/services/chat-websocket.service.js.map +1 -1
- package/dist-server/server/modules/websocket/services/session-upsert-broadcast.service.js +94 -0
- package/dist-server/server/modules/websocket/services/session-upsert-broadcast.service.js.map +1 -0
- package/dist-server/server/modules/websocket/services/shell-websocket.service.js +9 -3
- package/dist-server/server/modules/websocket/services/shell-websocket.service.js.map +1 -1
- package/dist-server/server/modules/websocket/tests/chat-edit-send.test.js +263 -0
- package/dist-server/server/modules/websocket/tests/chat-edit-send.test.js.map +1 -0
- package/dist-server/server/modules/websocket/tests/chat-run-registry.test.js +29 -3
- package/dist-server/server/modules/websocket/tests/chat-run-registry.test.js.map +1 -1
- package/dist-server/server/modules/websocket/tests/session-upsert-broadcast.test.js +106 -0
- package/dist-server/server/modules/websocket/tests/session-upsert-broadcast.test.js.map +1 -0
- package/dist-server/server/modules/websocket/tests/shell-websocket.service.test.js +80 -0
- package/dist-server/server/modules/websocket/tests/shell-websocket.service.test.js.map +1 -1
- package/dist-server/server/shared/claude-cli-path.js +22 -5
- package/dist-server/server/shared/claude-cli-path.js.map +1 -1
- package/dist-server/server/shared/index.js +3 -0
- package/dist-server/server/shared/index.js.map +1 -0
- package/dist-server/server/shared/message-unification.js +376 -0
- package/dist-server/server/shared/message-unification.js.map +1 -0
- package/dist-server/server/shared/tests/claude-cli-path.test.js +45 -1
- package/dist-server/server/shared/tests/claude-cli-path.test.js.map +1 -1
- package/dist-server/server/shared/tests/message-unification.test.js +174 -0
- package/dist-server/server/shared/tests/message-unification.test.js.map +1 -0
- package/dist-server/server/shared/utils.js +34 -0
- package/dist-server/server/shared/utils.js.map +1 -1
- package/package.json +19 -16
- package/server/index.ts +11 -4
- package/server/modules/agent/agent.routes.ts +2 -2
- package/server/modules/agent/tests/agent.routes.test.ts +33 -1
- package/server/modules/browser-use/browser-use.routes.ts +96 -33
- package/server/modules/database/connection.ts +2 -1
- package/server/modules/database/index.ts +10 -0
- package/server/modules/database/migrations.ts +37 -0
- package/server/modules/database/repositories/scheduled-messages.db.ts +127 -0
- package/server/modules/database/repositories/session-drafts.db.ts +171 -0
- package/server/modules/database/repositories/sessions.db.ts +196 -3
- package/server/modules/database/repositories/user-preferences.db.ts +80 -0
- package/server/modules/database/schema.ts +96 -0
- package/server/modules/database/tests/sessions.db.integration.test.ts +52 -0
- package/server/modules/database/tests/user-preferences.db.integration.test.ts +135 -0
- package/server/modules/projects/services/project-delete.service.ts +13 -1
- package/server/modules/projects/services/project-star.service.ts +2 -2
- package/server/modules/providers/index.ts +4 -0
- package/server/modules/providers/list/claude/claude-auth.provider.ts +21 -1
- package/server/modules/providers/list/claude/claude-fork.provider.ts +57 -0
- package/server/modules/providers/list/claude/claude-models.provider.ts +38 -6
- package/server/modules/providers/list/claude/claude-runtime.provider.js +188 -39
- package/server/modules/providers/list/claude/claude-sessions.provider.ts +478 -54
- package/server/modules/providers/list/claude/claude-skills.provider.ts +4 -1
- package/server/modules/providers/list/claude/claude.provider.ts +3 -0
- package/server/modules/providers/list/codex/codex-app-server.client.ts +243 -0
- package/server/modules/providers/list/codex/codex-fork.provider.ts +40 -0
- package/server/modules/providers/list/codex/codex-models.provider.ts +16 -0
- package/server/modules/providers/list/codex/{codex-runtime.provider.js → codex-runtime.provider.ts} +93 -66
- package/server/modules/providers/list/codex/codex-session-synchronizer.provider.ts +9 -0
- package/server/modules/providers/list/codex/codex-sessions.provider.ts +1806 -477
- package/server/modules/providers/list/codex/codex.provider.ts +3 -0
- package/server/modules/providers/list/cursor/cursor-runtime.provider.js +8 -6
- package/server/modules/providers/list/cursor/cursor-session-synchronizer.provider.ts +0 -1
- package/server/modules/providers/list/opencode/opencode-models.provider.ts +279 -6
- package/server/modules/providers/provider.routes.ts +31 -0
- package/server/modules/providers/services/provider-capabilities.service.ts +23 -0
- package/server/modules/providers/services/provider-token-usage.service.ts +166 -39
- package/server/modules/providers/services/session-history-cache.service.ts +139 -0
- package/server/modules/providers/services/session-synchronizer.service.ts +125 -38
- package/server/modules/providers/services/sessions-watcher.service.ts +3 -64
- package/server/modules/providers/services/sessions.service.ts +201 -9
- package/server/modules/providers/tests/claude-auth.test.ts +58 -0
- package/server/modules/providers/tests/claude-models.test.ts +87 -0
- package/server/modules/providers/tests/claude-sessions.test.ts +552 -40
- package/server/modules/providers/tests/claude-subagent-echo.test.ts +25 -0
- package/server/modules/providers/tests/claude-token-budget.test.ts +104 -0
- package/server/modules/providers/tests/codex-message-editing.test.ts +419 -0
- package/server/modules/providers/tests/codex-runtime.test.ts +58 -0
- package/server/modules/providers/tests/codex-session-fork.test.ts +59 -0
- package/server/modules/providers/tests/codex-sessions.test.ts +337 -15
- package/server/modules/providers/tests/opencode-models.test.ts +166 -8
- package/server/modules/providers/tests/provider-token-usage.service.test.ts +188 -1
- package/server/modules/providers/tests/session-fork.test.ts +173 -0
- package/server/modules/providers/tests/session-history-cache.service.test.ts +143 -0
- package/server/modules/providers/tests/session-orphan-prune.test.ts +107 -0
- package/server/modules/providers/tests/session-synchronizer-coalescing.test.ts +136 -0
- package/server/modules/providers/tests/sessions.service.test.ts +67 -1
- package/server/modules/providers/tests/skills.test.ts +21 -5
- package/server/modules/scheduled-messages/index.ts +8 -0
- package/server/modules/scheduled-messages/scheduled-messages.routes.ts +68 -0
- package/server/modules/scheduled-messages/services/scheduled-message-dispatcher.service.ts +203 -0
- package/server/modules/scheduled-messages/services/scheduled-messages.service.ts +121 -0
- package/server/modules/scheduled-messages/tests/scheduled-messages.test.ts +310 -0
- package/server/modules/taskmaster/taskmaster.routes.ts +23 -33
- package/server/modules/taskmaster/tests/taskmaster.routes.test.ts +57 -0
- package/server/modules/user/tests/user.service.test.ts +90 -0
- package/server/modules/user/user.module.ts +10 -1
- package/server/modules/user/user.routes.ts +44 -0
- package/server/modules/user/user.service.ts +105 -0
- package/server/modules/voice/tests/voice.service.test.ts +3 -2
- package/server/modules/websocket/index.ts +8 -0
- package/server/modules/websocket/services/chat-run-registry.service.ts +17 -50
- package/server/modules/websocket/services/chat-session-writer.service.ts +36 -6
- package/server/modules/websocket/services/chat-websocket.service.ts +244 -15
- package/server/modules/websocket/services/session-upsert-broadcast.service.ts +108 -0
- package/server/modules/websocket/services/shell-websocket.service.ts +10 -3
- package/server/modules/websocket/tests/chat-edit-send.test.ts +309 -0
- package/server/modules/websocket/tests/chat-run-registry.test.ts +34 -3
- package/server/modules/websocket/tests/session-upsert-broadcast.test.ts +138 -0
- package/server/modules/websocket/tests/shell-websocket.service.test.ts +108 -0
- package/server/shared/claude-cli-path.ts +24 -6
- package/server/shared/index.ts +3 -0
- package/server/shared/interfaces.ts +62 -0
- package/server/shared/message-unification.ts +449 -0
- package/server/shared/tests/claude-cli-path.test.ts +58 -1
- package/server/shared/tests/message-unification.test.ts +218 -0
- package/server/shared/types.ts +123 -2
- package/server/shared/utils.ts +40 -0
- package/dist/assets/channel-_falYbU1.js +0 -1
- package/dist/assets/classDiagram-JCYQIIEL-Dn-3CfpR.js +0 -1
- package/dist/assets/classDiagram-v2-OCEON4UE-Dn-3CfpR.js +0 -1
- package/dist/assets/index-CzPLBvXT.css +0 -32
- package/dist/assets/index-DFkBBuSA.js +0 -1549
- package/dist/assets/stateDiagram-v2-UEYNNEHI-BvBxUUrZ.js +0 -1
- package/dist/assets/swimlanesDiagram-ULZ7WXOC-CsE-x4YK.js +0 -8
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { stat } from 'node:fs/promises';
|
|
3
|
+
import { createRequire } from 'node:module';
|
|
4
|
+
import readline from 'node:readline';
|
|
5
|
+
|
|
6
|
+
import { AppError } from '@/shared/utils.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Minimal JSON-RPC client for `codex app-server`.
|
|
10
|
+
*
|
|
11
|
+
* Codex ships two entry points and they expose different things. The
|
|
12
|
+
* `@openai/codex-sdk` this app runs conversations through is a wrapper around
|
|
13
|
+
* `codex exec`, and its whole surface is `startThread` and `resumeThread` —
|
|
14
|
+
* there is no way to branch a thread or to resume one partway. The same
|
|
15
|
+
* binary's `app-server` subcommand speaks JSON-RPC and does have that
|
|
16
|
+
* primitive, `thread/fork`, which is what the Codex IDE clients build their
|
|
17
|
+
* own "fork" and "edit an earlier message" on top of.
|
|
18
|
+
*
|
|
19
|
+
* So this is a second transport to the same CLI, opened only for the
|
|
20
|
+
* operations the SDK cannot express. Everything else still goes through the
|
|
21
|
+
* SDK.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
/** How long a single request may take before the child is killed. */
|
|
25
|
+
const REQUEST_TIMEOUT_MS = 30_000;
|
|
26
|
+
|
|
27
|
+
type JsonRpcResponse = {
|
|
28
|
+
id?: number;
|
|
29
|
+
result?: unknown;
|
|
30
|
+
error?: { code?: number; message?: string };
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* One fork of a Codex thread.
|
|
35
|
+
*
|
|
36
|
+
* `path` is returned by the server rather than reconstructed: the rollout
|
|
37
|
+
* lands in today's date directory, not next to the file it was copied from,
|
|
38
|
+
* so deriving it from the source path would be wrong roughly every day.
|
|
39
|
+
*/
|
|
40
|
+
export type CodexThreadFork = {
|
|
41
|
+
threadId: string;
|
|
42
|
+
path: string;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Resolves the `codex` launcher shipped in node_modules.
|
|
47
|
+
*
|
|
48
|
+
* Deliberately not the `codex` on PATH: a machine can have a second, older
|
|
49
|
+
* install, and the protocol this speaks is only guaranteed against the
|
|
50
|
+
* version this package depends on.
|
|
51
|
+
*/
|
|
52
|
+
function resolveCodexLauncher(): string {
|
|
53
|
+
const require_ = createRequire(import.meta.url);
|
|
54
|
+
try {
|
|
55
|
+
return require_.resolve('@openai/codex/bin/codex.js');
|
|
56
|
+
} catch {
|
|
57
|
+
throw new AppError('The Codex CLI package is not installed, so Codex conversations cannot be branched.', {
|
|
58
|
+
code: 'CODEX_APP_SERVER_UNAVAILABLE',
|
|
59
|
+
statusCode: 501,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Runs one exchange against a freshly spawned `codex app-server`.
|
|
66
|
+
*
|
|
67
|
+
* A process per operation rather than a pooled long-lived one: the handshake
|
|
68
|
+
* costs a fraction of a second, forking happens at most once per user action,
|
|
69
|
+
* and a shared child would need lifecycle handling — restarts, back-pressure,
|
|
70
|
+
* a crash taking every pending fork with it — for no measurable gain next to
|
|
71
|
+
* the model turn that follows.
|
|
72
|
+
*/
|
|
73
|
+
async function withAppServer<T>(
|
|
74
|
+
run: (call: (method: string, params: unknown) => Promise<unknown>) => Promise<T>,
|
|
75
|
+
): Promise<T> {
|
|
76
|
+
const launcher = resolveCodexLauncher();
|
|
77
|
+
const child = spawn(process.execPath, [launcher, 'app-server'], {
|
|
78
|
+
env: process.env,
|
|
79
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
// The server logs sandbox and skill warnings to stderr on every start. They
|
|
83
|
+
// are not failures and drowning the app log in them helps nobody, so stderr
|
|
84
|
+
// is only kept around to explain a spawn that dies.
|
|
85
|
+
let stderr = '';
|
|
86
|
+
child.stderr?.on('data', (chunk) => {
|
|
87
|
+
stderr = (stderr + String(chunk)).slice(-2000);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
let nextRequestId = 1;
|
|
91
|
+
const pending = new Map<number, (response: JsonRpcResponse) => void>();
|
|
92
|
+
let exitReason: string | null = null;
|
|
93
|
+
|
|
94
|
+
const reader = readline.createInterface({ input: child.stdout });
|
|
95
|
+
reader.on('line', (line) => {
|
|
96
|
+
if (!line.trim()) {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
let message: JsonRpcResponse;
|
|
100
|
+
try {
|
|
101
|
+
message = JSON.parse(line) as JsonRpcResponse;
|
|
102
|
+
} catch {
|
|
103
|
+
// Server-to-client notifications and any non-JSON banner are not
|
|
104
|
+
// replies to anything this client asked for.
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
if (typeof message.id !== 'number') {
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
pending.get(message.id)?.(message);
|
|
111
|
+
pending.delete(message.id);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
const failPending = (reason: string) => {
|
|
115
|
+
exitReason = reason;
|
|
116
|
+
for (const resolve of pending.values()) {
|
|
117
|
+
resolve({ error: { message: reason } });
|
|
118
|
+
}
|
|
119
|
+
pending.clear();
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
child.on('error', (error) => failPending(error.message));
|
|
123
|
+
child.on('exit', (code, signal) => {
|
|
124
|
+
failPending(`codex app-server exited (code ${code ?? 'null'}, signal ${signal ?? 'null'})`);
|
|
125
|
+
});
|
|
126
|
+
// A child that dies mid-request leaves its pipes broken, and the next write
|
|
127
|
+
// raises EPIPE on the stream rather than at the call site. Without a
|
|
128
|
+
// listener that is an unhandled 'error' event, which takes the whole server
|
|
129
|
+
// down over one failed fork.
|
|
130
|
+
child.stdin?.on('error', (error) => failPending(error.message));
|
|
131
|
+
child.stdout?.on('error', (error) => failPending(error.message));
|
|
132
|
+
child.stderr?.on('error', () => {});
|
|
133
|
+
|
|
134
|
+
const call = (method: string, params: unknown): Promise<unknown> =>
|
|
135
|
+
new Promise((resolve, reject) => {
|
|
136
|
+
if (exitReason) {
|
|
137
|
+
reject(new AppError(`Codex app-server is not running: ${exitReason}`, {
|
|
138
|
+
code: 'CODEX_APP_SERVER_UNAVAILABLE',
|
|
139
|
+
statusCode: 502,
|
|
140
|
+
}));
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const id = nextRequestId++;
|
|
145
|
+
const timer = setTimeout(() => {
|
|
146
|
+
pending.delete(id);
|
|
147
|
+
reject(new AppError(`Codex app-server did not answer "${method}" within ${REQUEST_TIMEOUT_MS}ms.`, {
|
|
148
|
+
code: 'CODEX_APP_SERVER_TIMEOUT',
|
|
149
|
+
statusCode: 504,
|
|
150
|
+
}));
|
|
151
|
+
}, REQUEST_TIMEOUT_MS);
|
|
152
|
+
|
|
153
|
+
pending.set(id, (response) => {
|
|
154
|
+
clearTimeout(timer);
|
|
155
|
+
if (response.error) {
|
|
156
|
+
reject(new AppError(response.error.message || `Codex app-server rejected "${method}".`, {
|
|
157
|
+
code: 'CODEX_APP_SERVER_ERROR',
|
|
158
|
+
statusCode: 502,
|
|
159
|
+
details: { method, rpcCode: response.error.code },
|
|
160
|
+
}));
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
resolve(response.result);
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
child.stdin?.write(`${JSON.stringify({ jsonrpc: '2.0', id, method, params })}\n`);
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
try {
|
|
170
|
+
// `capabilities` is deliberately empty. `thread/fork` with `lastTurnId` is
|
|
171
|
+
// in the stable protocol; only `beforeTurnId` and the turn-listing methods
|
|
172
|
+
// are gated behind `experimentalApi`, and neither is needed here.
|
|
173
|
+
await call('initialize', {
|
|
174
|
+
clientInfo: { name: 'cloudcli', title: 'CloudCLI', version: '1' },
|
|
175
|
+
capabilities: {},
|
|
176
|
+
});
|
|
177
|
+
child.stdin?.write(`${JSON.stringify({ jsonrpc: '2.0', method: 'initialized', params: {} })}\n`);
|
|
178
|
+
|
|
179
|
+
return await run(call);
|
|
180
|
+
} catch (error) {
|
|
181
|
+
if (error instanceof AppError && exitReason) {
|
|
182
|
+
throw new AppError(`${error.message}${stderr ? ` — ${stderr.trim().split('\n').slice(-1)[0]}` : ''}`, {
|
|
183
|
+
code: error.code,
|
|
184
|
+
statusCode: error.statusCode,
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
throw error;
|
|
188
|
+
} finally {
|
|
189
|
+
reader.close();
|
|
190
|
+
child.kill();
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export const codexAppServer = {
|
|
195
|
+
/**
|
|
196
|
+
* Copies a thread into a new one that ends at `lastTurnId`, or copies the
|
|
197
|
+
* whole thread when it is omitted.
|
|
198
|
+
*
|
|
199
|
+
* `lastTurnId` is inclusive of the turn it names, which is the same
|
|
200
|
+
* convention the app's edit anchor uses ("the last row to keep").
|
|
201
|
+
*
|
|
202
|
+
* `cwd` decides the working directory recorded in the copy's `session_meta`,
|
|
203
|
+
* and that field is what the session indexer keys a session's project off —
|
|
204
|
+
* omitting it would file every fork under whatever directory this server
|
|
205
|
+
* happens to be running from.
|
|
206
|
+
*/
|
|
207
|
+
async forkThread(input: {
|
|
208
|
+
threadId: string;
|
|
209
|
+
lastTurnId?: string;
|
|
210
|
+
cwd: string;
|
|
211
|
+
}): Promise<CodexThreadFork> {
|
|
212
|
+
return withAppServer(async (call) => {
|
|
213
|
+
const result = await call('thread/fork', {
|
|
214
|
+
threadId: input.threadId,
|
|
215
|
+
...(input.lastTurnId ? { lastTurnId: input.lastTurnId } : {}),
|
|
216
|
+
...(input.cwd ? { cwd: input.cwd } : {}),
|
|
217
|
+
}) as { thread?: { id?: unknown; path?: unknown } } | undefined;
|
|
218
|
+
|
|
219
|
+
const threadId = typeof result?.thread?.id === 'string' ? result.thread.id : '';
|
|
220
|
+
const path = typeof result?.thread?.path === 'string' ? result.thread.path : '';
|
|
221
|
+
if (!threadId || !path) {
|
|
222
|
+
throw new AppError('Codex reported a fork without a thread id or transcript path.', {
|
|
223
|
+
code: 'FORK_FAILED',
|
|
224
|
+
statusCode: 502,
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Confirmed rather than trusted: both callers are about to point a
|
|
229
|
+
// database row at this file, and a row naming a transcript that is not
|
|
230
|
+
// there is a session that can never be opened.
|
|
231
|
+
try {
|
|
232
|
+
await stat(path);
|
|
233
|
+
} catch {
|
|
234
|
+
throw new AppError('Codex reported a fork but wrote no transcript for it.', {
|
|
235
|
+
code: 'FORK_FAILED',
|
|
236
|
+
statusCode: 502,
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
return { threadId, path };
|
|
241
|
+
});
|
|
242
|
+
},
|
|
243
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { codexAppServer } from '@/modules/providers/list/codex/codex-app-server.client.js';
|
|
2
|
+
import type { IProviderFork } from '@/shared/interfaces.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Branches a Codex conversation into an independent thread.
|
|
6
|
+
*
|
|
7
|
+
* `thread/fork` writes a real rollout with its own thread id and a
|
|
8
|
+
* `forked_from_id` back-reference, which is what makes the copy resumable
|
|
9
|
+
* rather than an inert duplicate of the file.
|
|
10
|
+
*
|
|
11
|
+
* One difference from the Claude fork worth knowing about: Codex's unit is a
|
|
12
|
+
* turn, not a row. `upToAnchorId` names the turn a user message belongs to and
|
|
13
|
+
* the cut is inclusive of it, so forking from a message keeps that message
|
|
14
|
+
* *and the answer it got*. Claude's `upToMessageId` can stop at the prompt
|
|
15
|
+
* itself. There is no way to express the finer cut here — a turn is written as
|
|
16
|
+
* one thing — and the coarser one is the more useful of the two anyway.
|
|
17
|
+
*/
|
|
18
|
+
export class CodexForkProvider implements IProviderFork {
|
|
19
|
+
async forkSession(input: {
|
|
20
|
+
providerSessionId: string;
|
|
21
|
+
jsonlPath: string;
|
|
22
|
+
projectPath: string;
|
|
23
|
+
upToAnchorId?: string;
|
|
24
|
+
title?: string;
|
|
25
|
+
}): Promise<{ providerSessionId: string; jsonlPath: string }> {
|
|
26
|
+
// `title` is deliberately not forwarded. The sidebar name lives in this
|
|
27
|
+
// app's own session row, and naming the thread inside Codex would mean a
|
|
28
|
+
// second call that could fail after the fork already succeeded.
|
|
29
|
+
const fork = await codexAppServer.forkThread({
|
|
30
|
+
threadId: input.providerSessionId,
|
|
31
|
+
lastTurnId: input.upToAnchorId,
|
|
32
|
+
cwd: input.projectPath,
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
// The path is the one the server reported and already confirmed on disk,
|
|
36
|
+
// not one derived from the source: a fork lands in today's date directory
|
|
37
|
+
// rather than beside the transcript it was copied from.
|
|
38
|
+
return { providerSessionId: fork.threadId, jsonlPath: fork.path };
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -18,6 +18,22 @@ import {
|
|
|
18
18
|
/** Curated Codex catalog shipped as immutable CloudCLI defaults. */
|
|
19
19
|
export const CODEX_PREDEFINED_MODELS: ProviderModelsDefinition = {
|
|
20
20
|
OPTIONS: [
|
|
21
|
+
{
|
|
22
|
+
value: 'gpt-6-astra',
|
|
23
|
+
label: 'GPT-6 Astra',
|
|
24
|
+
description: 'Our most capable model for complex, demanding work.',
|
|
25
|
+
effort: {
|
|
26
|
+
default: 'low',
|
|
27
|
+
values: [
|
|
28
|
+
{ value: 'low' },
|
|
29
|
+
{ value: 'medium' },
|
|
30
|
+
{ value: 'high' },
|
|
31
|
+
{ value: 'xhigh' },
|
|
32
|
+
{ value: 'max' },
|
|
33
|
+
{ value: 'ultra' },
|
|
34
|
+
],
|
|
35
|
+
},
|
|
36
|
+
},
|
|
21
37
|
{
|
|
22
38
|
value: 'gpt-5.6-sol',
|
|
23
39
|
label: 'GPT-5.6 Sol',
|
package/server/modules/providers/list/codex/{codex-runtime.provider.js → codex-runtime.provider.ts}
RENAMED
|
@@ -8,29 +8,44 @@
|
|
|
8
8
|
* ## Usage
|
|
9
9
|
*
|
|
10
10
|
* - codexRuntime.run(command, options, writer, context) - Execute a streamed prompt
|
|
11
|
-
* -
|
|
12
|
-
* - isCodexSessionActive(sessionId) - Check if a session is running
|
|
13
|
-
* - getActiveCodexSessions() - List all active sessions
|
|
11
|
+
* - codexRuntime.abort(sessionId) - Cancel an active session
|
|
14
12
|
*/
|
|
15
13
|
|
|
16
14
|
import { Codex } from '@openai/codex-sdk';
|
|
15
|
+
import type { ModelReasoningEffort, Thread, ThreadOptions } from '@openai/codex-sdk';
|
|
17
16
|
|
|
18
17
|
import {
|
|
19
18
|
appendFilesInputTag,
|
|
20
19
|
buildCodexInputItems,
|
|
21
|
-
normalizeImageDescriptors
|
|
22
|
-
|
|
20
|
+
normalizeImageDescriptors,
|
|
21
|
+
createCompleteMessage,
|
|
22
|
+
createNormalizedMessage,
|
|
23
|
+
} from '@/shared/index.js';
|
|
23
24
|
import { notifyRunFailed, notifyRunStopped } from '@/modules/notifications/index.js';
|
|
24
|
-
import {
|
|
25
|
+
import type { AnyRecord, ProviderRuntimeContext, ProviderRuntimeWriter } from '@/shared/index.js';
|
|
26
|
+
|
|
27
|
+
type ActiveCodexSession = {
|
|
28
|
+
thread: Thread;
|
|
29
|
+
codex: Codex;
|
|
30
|
+
status: 'running' | 'aborted' | 'completed';
|
|
31
|
+
abortController: AbortController;
|
|
32
|
+
startedAt: string;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const activeCodexSessions = new Map<string, ActiveCodexSession>();
|
|
25
36
|
|
|
26
|
-
|
|
37
|
+
/**
|
|
38
|
+
* Item types whose in-flight updates are worth showing. These are the ones a
|
|
39
|
+
* user waits on — a shell command's output, an MCP call, and the running plan.
|
|
40
|
+
*/
|
|
41
|
+
const PROGRESSIVE_CODEX_ITEM_TYPES = new Set(['command_execution', 'mcp_tool_call', 'todo_list']);
|
|
27
42
|
|
|
28
|
-
function readUsageNumber(value) {
|
|
43
|
+
function readUsageNumber(value: unknown) {
|
|
29
44
|
const parsed = Number(value);
|
|
30
45
|
return Number.isFinite(parsed) ? parsed : 0;
|
|
31
46
|
}
|
|
32
47
|
|
|
33
|
-
function extractCodexTokenBudget(event) {
|
|
48
|
+
function extractCodexTokenBudget(event: AnyRecord) {
|
|
34
49
|
const info = event?.info || event?.payload?.info || event?.usage?.info;
|
|
35
50
|
const usage = info?.total_token_usage || event?.usage?.total_token_usage || event?.usage;
|
|
36
51
|
if (!usage || typeof usage !== 'object') {
|
|
@@ -58,7 +73,7 @@ function extractCodexTokenBudget(event) {
|
|
|
58
73
|
* @param {object} event - SDK event
|
|
59
74
|
* @returns {object} - Transformed event for WebSocket
|
|
60
75
|
*/
|
|
61
|
-
function transformCodexEvent(event) {
|
|
76
|
+
function transformCodexEvent(event: AnyRecord): AnyRecord {
|
|
62
77
|
// Map SDK event types to a consistent format
|
|
63
78
|
switch (event.type) {
|
|
64
79
|
case 'item.started':
|
|
@@ -69,12 +84,16 @@ function transformCodexEvent(event) {
|
|
|
69
84
|
return { type: event.type, item: null };
|
|
70
85
|
}
|
|
71
86
|
|
|
72
|
-
//
|
|
87
|
+
// `itemId` is the SDK's stable per-item id. Carrying it through means an
|
|
88
|
+
// in-progress row and its later completion normalize to the same message
|
|
89
|
+
// id, so the client updates one transcript entry instead of appending a
|
|
90
|
+
// new one for every progress tick.
|
|
73
91
|
switch (item.type) {
|
|
74
92
|
case 'agent_message':
|
|
75
93
|
return {
|
|
76
94
|
type: 'item',
|
|
77
95
|
itemType: 'agent_message',
|
|
96
|
+
itemId: item.id,
|
|
78
97
|
message: {
|
|
79
98
|
role: 'assistant',
|
|
80
99
|
content: item.text
|
|
@@ -85,6 +104,7 @@ function transformCodexEvent(event) {
|
|
|
85
104
|
return {
|
|
86
105
|
type: 'item',
|
|
87
106
|
itemType: 'reasoning',
|
|
107
|
+
itemId: item.id,
|
|
88
108
|
message: {
|
|
89
109
|
role: 'assistant',
|
|
90
110
|
content: item.text,
|
|
@@ -96,6 +116,7 @@ function transformCodexEvent(event) {
|
|
|
96
116
|
return {
|
|
97
117
|
type: 'item',
|
|
98
118
|
itemType: 'command_execution',
|
|
119
|
+
itemId: item.id,
|
|
99
120
|
command: item.command,
|
|
100
121
|
output: item.aggregated_output,
|
|
101
122
|
exitCode: item.exit_code,
|
|
@@ -106,6 +127,7 @@ function transformCodexEvent(event) {
|
|
|
106
127
|
return {
|
|
107
128
|
type: 'item',
|
|
108
129
|
itemType: 'file_change',
|
|
130
|
+
itemId: item.id,
|
|
109
131
|
changes: item.changes,
|
|
110
132
|
status: item.status
|
|
111
133
|
};
|
|
@@ -114,6 +136,7 @@ function transformCodexEvent(event) {
|
|
|
114
136
|
return {
|
|
115
137
|
type: 'item',
|
|
116
138
|
itemType: 'mcp_tool_call',
|
|
139
|
+
itemId: item.id,
|
|
117
140
|
server: item.server,
|
|
118
141
|
tool: item.tool,
|
|
119
142
|
arguments: item.arguments,
|
|
@@ -126,6 +149,7 @@ function transformCodexEvent(event) {
|
|
|
126
149
|
return {
|
|
127
150
|
type: 'item',
|
|
128
151
|
itemType: 'web_search',
|
|
152
|
+
itemId: item.id,
|
|
129
153
|
query: item.query
|
|
130
154
|
};
|
|
131
155
|
|
|
@@ -133,6 +157,7 @@ function transformCodexEvent(event) {
|
|
|
133
157
|
return {
|
|
134
158
|
type: 'item',
|
|
135
159
|
itemType: 'todo_list',
|
|
160
|
+
itemId: item.id,
|
|
136
161
|
items: item.items
|
|
137
162
|
};
|
|
138
163
|
|
|
@@ -140,6 +165,7 @@ function transformCodexEvent(event) {
|
|
|
140
165
|
return {
|
|
141
166
|
type: 'item',
|
|
142
167
|
itemType: 'error',
|
|
168
|
+
itemId: item.id,
|
|
143
169
|
message: {
|
|
144
170
|
role: 'error',
|
|
145
171
|
content: item.message
|
|
@@ -150,6 +176,7 @@ function transformCodexEvent(event) {
|
|
|
150
176
|
return {
|
|
151
177
|
type: 'item',
|
|
152
178
|
itemType: item.type,
|
|
179
|
+
itemId: item.id,
|
|
153
180
|
item: item
|
|
154
181
|
};
|
|
155
182
|
}
|
|
@@ -196,7 +223,7 @@ function transformCodexEvent(event) {
|
|
|
196
223
|
* @param {string} permissionMode - 'default', 'acceptEdits', or 'bypassPermissions'
|
|
197
224
|
* @returns {object} - { sandboxMode, approvalPolicy }
|
|
198
225
|
*/
|
|
199
|
-
function mapPermissionModeToCodexOptions(permissionMode) {
|
|
226
|
+
function mapPermissionModeToCodexOptions(permissionMode: string): Pick<ThreadOptions, 'sandboxMode' | 'approvalPolicy'> {
|
|
200
227
|
switch (permissionMode) {
|
|
201
228
|
case 'acceptEdits':
|
|
202
229
|
return {
|
|
@@ -212,7 +239,9 @@ function mapPermissionModeToCodexOptions(permissionMode) {
|
|
|
212
239
|
default:
|
|
213
240
|
return {
|
|
214
241
|
sandboxMode: 'workspace-write',
|
|
215
|
-
|
|
242
|
+
// Current Codex CLI versions reject the retired `untrusted` policy.
|
|
243
|
+
// Keep sandboxing enabled; exec cannot grant interactive approval requests.
|
|
244
|
+
approvalPolicy: 'on-request'
|
|
216
245
|
};
|
|
217
246
|
}
|
|
218
247
|
}
|
|
@@ -223,7 +252,12 @@ function mapPermissionModeToCodexOptions(permissionMode) {
|
|
|
223
252
|
* @param {object} options - Options including cwd, sessionId, model, permissionMode
|
|
224
253
|
* @param {WebSocket|object} ws - WebSocket connection or response writer
|
|
225
254
|
*/
|
|
226
|
-
|
|
255
|
+
async function queryCodex(
|
|
256
|
+
command: string,
|
|
257
|
+
options: AnyRecord = {},
|
|
258
|
+
ws: ProviderRuntimeWriter,
|
|
259
|
+
context: ProviderRuntimeContext,
|
|
260
|
+
) {
|
|
227
261
|
const {
|
|
228
262
|
sessionId,
|
|
229
263
|
sessionSummary,
|
|
@@ -248,16 +282,22 @@ export async function queryCodex(command, options = {}, ws, context) {
|
|
|
248
282
|
const selectedModel = catalog.OPTIONS.find((option) => option.value === resolvedModel) || null;
|
|
249
283
|
const allowedEfforts = selectedModel?.effort?.values?.map((value) => value.value) || [];
|
|
250
284
|
const resolvedEffort = typeof effort === 'string' && effort !== 'default' && allowedEfforts.includes(effort)
|
|
251
|
-
? effort
|
|
285
|
+
? effort as ModelReasoningEffort
|
|
252
286
|
: undefined;
|
|
253
287
|
|
|
254
|
-
let codex;
|
|
255
|
-
let thread;
|
|
288
|
+
let codex: Codex;
|
|
289
|
+
let thread: Thread;
|
|
256
290
|
// Provider-native thread id (starts as the resume id, or is captured from
|
|
257
291
|
// the stream for brand-new sessions).
|
|
258
292
|
let capturedSessionId = providerSessionId;
|
|
259
293
|
let sessionCreatedSent = false;
|
|
260
|
-
let terminalFailure = null;
|
|
294
|
+
let terminalFailure: { message: string } | null = null;
|
|
295
|
+
// Codex surfaces API failures as streamed error items/turn.failed events, and
|
|
296
|
+
// then the SDK also throws "Codex Exec exited with code N: <stderr>" once the
|
|
297
|
+
// process dies. Showing both means the rendered error is followed by a raw
|
|
298
|
+
// stderr dump of unrelated CLI log lines, so the thrown wrapper is dropped
|
|
299
|
+
// when the stream already reported the failure.
|
|
300
|
+
let errorSurfaced = false;
|
|
261
301
|
const abortController = new AbortController();
|
|
262
302
|
// Session-map key: the app session id when the caller supplied one, else
|
|
263
303
|
// the provider-native thread id once captured (legacy/direct API callers).
|
|
@@ -266,7 +306,7 @@ export async function queryCodex(command, options = {}, ws, context) {
|
|
|
266
306
|
try {
|
|
267
307
|
codex = new Codex();
|
|
268
308
|
|
|
269
|
-
const threadOptions = {
|
|
309
|
+
const threadOptions: ThreadOptions = {
|
|
270
310
|
workingDirectory,
|
|
271
311
|
skipGitRepoCheck: true,
|
|
272
312
|
sandboxMode,
|
|
@@ -281,7 +321,7 @@ export async function queryCodex(command, options = {}, ws, context) {
|
|
|
281
321
|
thread = codex.startThread(threadOptions);
|
|
282
322
|
}
|
|
283
323
|
|
|
284
|
-
const registerSession = (id) => {
|
|
324
|
+
const registerSession = (id: string | null) => {
|
|
285
325
|
if (!id) {
|
|
286
326
|
return;
|
|
287
327
|
}
|
|
@@ -311,7 +351,7 @@ export async function queryCodex(command, options = {}, ws, context) {
|
|
|
311
351
|
for await (const event of streamedTurn.events) {
|
|
312
352
|
// Capture thread/session id lazily from the stream (Codex emits this asynchronously).
|
|
313
353
|
if (event.type === 'thread.started') {
|
|
314
|
-
const discoveredSessionId = event.thread_id ||
|
|
354
|
+
const discoveredSessionId = event.thread_id || null;
|
|
315
355
|
if (discoveredSessionId && !capturedSessionId) {
|
|
316
356
|
capturedSessionId = discoveredSessionId;
|
|
317
357
|
registerSession(sessionKey());
|
|
@@ -332,17 +372,29 @@ export async function queryCodex(command, options = {}, ws, context) {
|
|
|
332
372
|
break;
|
|
333
373
|
}
|
|
334
374
|
if (sessionKey()) {
|
|
335
|
-
const session = activeCodexSessions.get(sessionKey());
|
|
375
|
+
const session = activeCodexSessions.get(sessionKey() || '');
|
|
336
376
|
if (session?.status === 'aborted') {
|
|
337
377
|
break;
|
|
338
378
|
}
|
|
339
379
|
}
|
|
340
380
|
|
|
341
|
-
|
|
381
|
+
// Progress events used to be dropped, so a long shell command or a
|
|
382
|
+
// growing plan showed nothing until it finished. They are forwarded now;
|
|
383
|
+
// every item carries a stable id, so the client replaces the row it
|
|
384
|
+
// already has rather than stacking a new one per tick. Text items are
|
|
385
|
+
// still skipped mid-flight because assistant prose arrives through the
|
|
386
|
+
// separate streaming path.
|
|
387
|
+
if (
|
|
388
|
+
(event.type === 'item.started' || event.type === 'item.updated')
|
|
389
|
+
&& !PROGRESSIVE_CODEX_ITEM_TYPES.has(event.item?.type)
|
|
390
|
+
) {
|
|
342
391
|
continue;
|
|
343
392
|
}
|
|
344
393
|
|
|
345
394
|
const transformed = transformCodexEvent(event);
|
|
395
|
+
if (transformed.type === 'error' || transformed.itemType === 'error') {
|
|
396
|
+
errorSurfaced = true;
|
|
397
|
+
}
|
|
346
398
|
|
|
347
399
|
// Normalize the transformed event into NormalizedMessage(s) via adapter
|
|
348
400
|
const normalizedMsgs = context.normalizeMessage(transformed, capturedSessionId || sessionId || null);
|
|
@@ -352,6 +404,7 @@ export async function queryCodex(command, options = {}, ws, context) {
|
|
|
352
404
|
|
|
353
405
|
if (event.type === 'turn.failed' && !terminalFailure) {
|
|
354
406
|
terminalFailure = event.error || new Error('Turn failed');
|
|
407
|
+
errorSurfaced = true;
|
|
355
408
|
// Notifications are app-facing, so they carry the app session id.
|
|
356
409
|
notifyRunFailed({
|
|
357
410
|
userId: ws?.userId || null,
|
|
@@ -373,7 +426,7 @@ export async function queryCodex(command, options = {}, ws, context) {
|
|
|
373
426
|
|
|
374
427
|
// Send the terminal completion event — skipped for aborted runs, whose
|
|
375
428
|
// terminal `complete` (aborted: true) was already sent by abort-session.
|
|
376
|
-
const runSession =
|
|
429
|
+
const runSession = activeCodexSessions.get(sessionKey() || '');
|
|
377
430
|
const runAborted = runSession?.status === 'aborted' || abortController.signal.aborted;
|
|
378
431
|
if (!runAborted) {
|
|
379
432
|
sendMessage(ws, createCompleteMessage({
|
|
@@ -394,22 +447,25 @@ export async function queryCodex(command, options = {}, ws, context) {
|
|
|
394
447
|
}
|
|
395
448
|
|
|
396
449
|
} catch (error) {
|
|
397
|
-
const session =
|
|
450
|
+
const session = activeCodexSessions.get(sessionKey() || '');
|
|
451
|
+
const runError = error instanceof Error ? error : new Error(String(error));
|
|
398
452
|
const wasAborted =
|
|
399
453
|
session?.status === 'aborted' ||
|
|
400
|
-
|
|
401
|
-
|
|
454
|
+
runError.name === 'AbortError' ||
|
|
455
|
+
runError.message.toLowerCase().includes('aborted');
|
|
402
456
|
|
|
403
457
|
if (!wasAborted) {
|
|
404
458
|
console.error('[Codex] Error:', error);
|
|
405
459
|
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
460
|
+
if (!errorSurfaced) {
|
|
461
|
+
// Check if Codex SDK is available for a clearer error message
|
|
462
|
+
const installed = await context.isProviderInstalled();
|
|
463
|
+
const errorContent = !installed
|
|
464
|
+
? 'Codex CLI is not configured. Please set up authentication first.'
|
|
465
|
+
: runError.message;
|
|
411
466
|
|
|
412
|
-
|
|
467
|
+
sendMessage(ws, createNormalizedMessage({ kind: 'error', content: errorContent, sessionId: capturedSessionId || sessionId || null, provider: 'codex' }));
|
|
468
|
+
}
|
|
413
469
|
sendMessage(ws, createCompleteMessage({
|
|
414
470
|
provider: 'codex',
|
|
415
471
|
sessionId: capturedSessionId || sessionId || null,
|
|
@@ -429,7 +485,7 @@ export async function queryCodex(command, options = {}, ws, context) {
|
|
|
429
485
|
} finally {
|
|
430
486
|
// Update session status
|
|
431
487
|
if (sessionKey()) {
|
|
432
|
-
const session = activeCodexSessions.get(sessionKey());
|
|
488
|
+
const session = activeCodexSessions.get(sessionKey() || '');
|
|
433
489
|
if (session) {
|
|
434
490
|
session.status = session.status === 'aborted' ? 'aborted' : 'completed';
|
|
435
491
|
}
|
|
@@ -442,7 +498,7 @@ export async function queryCodex(command, options = {}, ws, context) {
|
|
|
442
498
|
* @param {string} sessionId - Session ID to abort
|
|
443
499
|
* @returns {boolean} - Whether abort was successful
|
|
444
500
|
*/
|
|
445
|
-
|
|
501
|
+
function abortCodexSession(sessionId: string) {
|
|
446
502
|
const session = activeCodexSessions.get(sessionId);
|
|
447
503
|
|
|
448
504
|
if (!session) {
|
|
@@ -459,36 +515,7 @@ export function abortCodexSession(sessionId) {
|
|
|
459
515
|
return true;
|
|
460
516
|
}
|
|
461
517
|
|
|
462
|
-
/**
|
|
463
|
-
* Check if a session is active
|
|
464
|
-
* @param {string} sessionId - Session ID to check
|
|
465
|
-
* @returns {boolean} - Whether session is active
|
|
466
|
-
*/
|
|
467
|
-
export function isCodexSessionActive(sessionId) {
|
|
468
|
-
const session = activeCodexSessions.get(sessionId);
|
|
469
|
-
return session?.status === 'running';
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
/**
|
|
473
|
-
* Get all active sessions
|
|
474
|
-
* @returns {Array} - Array of active session info
|
|
475
|
-
*/
|
|
476
|
-
export function getActiveCodexSessions() {
|
|
477
|
-
const sessions = [];
|
|
478
|
-
|
|
479
|
-
for (const [id, session] of activeCodexSessions.entries()) {
|
|
480
|
-
if (session.status === 'running') {
|
|
481
|
-
sessions.push({
|
|
482
|
-
id,
|
|
483
|
-
status: session.status,
|
|
484
|
-
startedAt: session.startedAt
|
|
485
|
-
});
|
|
486
|
-
}
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
return sessions;
|
|
490
|
-
}
|
|
491
|
-
|
|
518
|
+
/** Used by the providers module's CodexProvider to run and abort SDK turns. */
|
|
492
519
|
export const codexRuntime = {
|
|
493
520
|
run: queryCodex,
|
|
494
521
|
abort: abortCodexSession,
|
|
@@ -499,7 +526,7 @@ export const codexRuntime = {
|
|
|
499
526
|
* @param {WebSocket|object} ws - WebSocket or response writer
|
|
500
527
|
* @param {object} data - Data to send
|
|
501
528
|
*/
|
|
502
|
-
function sendMessage(ws, data) {
|
|
529
|
+
function sendMessage(ws: ProviderRuntimeWriter, data: unknown) {
|
|
503
530
|
try {
|
|
504
531
|
if (ws.isSSEStreamWriter || ws.isWebSocketWriter) {
|
|
505
532
|
// Writer handles stringification (SSEStreamWriter or WebSocketWriter)
|