@shawnstack/quickforge 1.7.3 → 1.7.5
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/bin/quickforge.mjs +0 -21
- package/dist/assets/AgentProfilesPage-XNjuqJs7.js +1 -0
- package/dist/assets/ChatPanelHost-CuF-PZpc.js +292 -0
- package/dist/assets/CloudAccountSettingsPage-GJSoi6sO.js +1 -0
- package/dist/assets/PluginsPage-BQgp0qN-.js +1 -0
- package/dist/assets/ScheduledTasksPage-DdekATzq.js +2 -0
- package/dist/assets/SettingsWorkspacePage-Cb54Ic91.js +1639 -0
- package/dist/assets/ShareLinksSettingsPage-C75WJpaF.js +1 -0
- package/dist/assets/SharedConversationPage-CjMtSQTW.js +1 -0
- package/dist/assets/TerminalDock-D4zJBJc4.js +2 -0
- package/dist/assets/WorkspaceInspector-D9kdnLDd.js +36 -0
- package/dist/assets/{abnfDiagram-VRR7QNED-Dl_Fvlqg.js → abnfDiagram-VRR7QNED-CMhVoYUh.js} +1 -1
- package/dist/assets/{arc-Bhg4c0ht.js → arc-w21O0TLn.js} +1 -1
- package/dist/assets/architecture-TIHT7OUA-DMwFMHxq.js +1 -0
- package/dist/assets/{architectureDiagram-ZJ3FMSHR-BuojN27B.js → architectureDiagram-ZJ3FMSHR-C52qL6Hg.js} +1 -1
- package/dist/assets/{blockDiagram-677ZJIJ3-alDLbrR1.js → blockDiagram-677ZJIJ3-CM3XbLol.js} +1 -1
- package/dist/assets/{c4Diagram-LMCZKHZV-D1lKa4VT.js → c4Diagram-LMCZKHZV-D7Bh1ui1.js} +1 -1
- package/dist/assets/channel-Duu1CuH9.js +1 -0
- package/dist/assets/chunk-2Q5K7J3B-B6rHWc4S.js +1 -0
- package/dist/assets/{chunk-32BRIVSS-CwWLNjMZ.js → chunk-32BRIVSS-xgrtbT8e.js} +1 -1
- package/dist/assets/{chunk-52WLFC77-Be569m7d.js → chunk-52WLFC77-CDbaHw80.js} +1 -1
- package/dist/assets/{chunk-5FCAYU7R-BSvoiWV1.js → chunk-5FCAYU7R-CcSvVbPc.js} +1 -1
- package/dist/assets/{chunk-5HE753X5-CRgsy8WS.js → chunk-5HE753X5-erFpzzgx.js} +1 -1
- package/dist/assets/{chunk-5JV3BV7I-CdW477UR.js → chunk-5JV3BV7I-rEhN9F6G.js} +1 -1
- package/dist/assets/{chunk-5TONJI2A-CdtKzSmp.js → chunk-5TONJI2A-D8L-QTwD.js} +1 -1
- package/dist/assets/{chunk-5VM5RSS4-BKjjssLZ.js → chunk-5VM5RSS4-CjlfMBbM.js} +1 -1
- package/dist/assets/{chunk-7BUUIJ7U-ByAXiXdn.js → chunk-7BUUIJ7U-CCcsI9XE.js} +1 -1
- package/dist/assets/{chunk-BIQX33UG-DeG8SCT1.js → chunk-BIQX33UG-FSqFbmb3.js} +1 -1
- package/dist/assets/{chunk-C7G6YPKG-D_zHT68Q.js → chunk-C7G6YPKG-ZMLUvfLy.js} +1 -1
- package/dist/assets/{chunk-CQNSW5MT-CHpHoUSu.js → chunk-CQNSW5MT-DZuBpk25.js} +1 -1
- package/dist/assets/{chunk-CYSBUYHQ-CRP50Vov.js → chunk-CYSBUYHQ-CB26wsG0.js} +1 -1
- package/dist/assets/{chunk-EMLP6XTP-DX6uQQ2v.js → chunk-EMLP6XTP-BbsM8oRw.js} +1 -1
- package/dist/assets/{chunk-EX3LRPZG-DAJRNkHR.js → chunk-EX3LRPZG-BcQhaqt7.js} +1 -1
- package/dist/assets/{chunk-FWX5IMBZ-XbM5yomC.js → chunk-FWX5IMBZ-DrnrdrSu.js} +2 -2
- package/dist/assets/{chunk-HOUHSVGY-Ci0ljnb1.js → chunk-HOUHSVGY-tdc-Z9eW.js} +1 -1
- package/dist/assets/{chunk-ICXQ74PX-Dy2WJ71z.js → chunk-ICXQ74PX-Cd7igVGt.js} +1 -1
- package/dist/assets/{chunk-JG7HCLWE-2PrkzHkt.js → chunk-JG7HCLWE-iXXGW8nf.js} +1 -1
- package/dist/assets/{chunk-JWPE2WC7-D32uacSM.js → chunk-JWPE2WC7-BZ87mXEv.js} +1 -1
- package/dist/assets/{chunk-MOJQB5TN-DIAFLtiS.js → chunk-MOJQB5TN-CLTjV5ov.js} +1 -1
- package/dist/assets/{chunk-MOZMSUNE-BbscOaEz.js → chunk-MOZMSUNE-B3SCBrRH.js} +1 -1
- package/dist/assets/{chunk-OGEWGWER-ivBVxzKe.js → chunk-OGEWGWER-TgvkXkFX.js} +1 -1
- package/dist/assets/{chunk-OSBZ3O6U-frD0hkdx.js → chunk-OSBZ3O6U-B9E2E5v1.js} +1 -1
- package/dist/assets/{chunk-PUDLZKDR-B-37Rxae.js → chunk-PUDLZKDR-C5GMVYup.js} +1 -1
- package/dist/assets/{chunk-Q4XR5HBZ-Y90m2RTe.js → chunk-Q4XR5HBZ-fo5-HsqY.js} +1 -1
- package/dist/assets/{chunk-QBLGF6JB-DixBqslB.js → chunk-QBLGF6JB-Dwfg8sKc.js} +1 -1
- package/dist/assets/{chunk-R7FJI6CG-DXS1UXn_.js → chunk-R7FJI6CG-DpBrv4ni.js} +1 -1
- package/dist/assets/{chunk-RYQCIY6F-n4JxhNd0.js → chunk-RYQCIY6F-DzzmTl11.js} +1 -1
- package/dist/assets/{chunk-U6XO7XAA-yHlgxMmn.js → chunk-U6XO7XAA-Dw6aviKa.js} +1 -1
- package/dist/assets/{chunk-V7JOEXUC-Z6kuZfbl.js → chunk-V7JOEXUC-CEDazV6w.js} +1 -1
- package/dist/assets/chunk-VAUOI2AC-CWYjg8zE.js +1 -0
- package/dist/assets/{chunk-VR4S4FIN-Cs_SIQma.js → chunk-VR4S4FIN-C_-eEj-9.js} +1 -1
- package/dist/assets/{chunk-WYO6CB5R-CGExYbV-.js → chunk-WYO6CB5R-NF-MsJJR.js} +3 -3
- package/dist/assets/chunk-XXDRQBXY-DzTwx8ZH.js +1 -0
- package/dist/assets/{chunk-YOTPTUD7-DopPXWSl.js → chunk-YOTPTUD7-DqzqtdII.js} +1 -1
- package/dist/assets/{chunk-ZGVPDNZ5-CJHOeqMb.js → chunk-ZGVPDNZ5-CRrAYi4g.js} +1 -1
- package/dist/assets/{chunk-ZIRB5QZD-BBVc5mf6.js → chunk-ZIRB5QZD-Dc1s5bLM.js} +1 -1
- package/dist/assets/classDiagram-OUVF2IWQ-Cc63fLWP.js +1 -0
- package/dist/assets/classDiagram-v2-EOCWNBFH-CoXU0qT6.js +1 -0
- package/dist/assets/{cose-bilkent-JH36ORCC-DLjV-dTm.js → cose-bilkent-JH36ORCC-cL247pHg.js} +1 -1
- package/dist/assets/cynefin-VYW2F7L2-C8yCvH_o.js +1 -0
- package/dist/assets/{cynefinDiagram-TSTJHNR4-BfH3zVS-.js → cynefinDiagram-TSTJHNR4-D4sZ7LYI.js} +1 -1
- package/dist/assets/{dagre-CY3D84Cl.js → dagre-DmHb3Ctt.js} +1 -1
- package/dist/assets/{dagre-VKFMJZFB-BIhwZCIZ.js → dagre-VKFMJZFB-BlsnSh-Q.js} +1 -1
- package/dist/assets/{diagram-FQU43EPY-BHnzNM2w.js → diagram-FQU43EPY-DW1yQiRO.js} +1 -1
- package/dist/assets/{diagram-G47NLZAW-C7-ZlmFT.js → diagram-G47NLZAW-BTUAsFjb.js} +1 -1
- package/dist/assets/{diagram-NH7WQ7WH-C-P9Y7IR.js → diagram-NH7WQ7WH-BcaomqSd.js} +1 -1
- package/dist/assets/{diagram-OA4YK3LP-Ci5lZ_T5.js → diagram-OA4YK3LP-DDLX7Xwr.js} +1 -1
- package/dist/assets/{diagram-WEI45ONY-C1xuK2bs.js → diagram-WEI45ONY-CIave0An.js} +1 -1
- package/dist/assets/dist-DLKC_odd.js +1 -0
- package/dist/assets/{ebnfDiagram-CCIWWBDH-DKQ5NWMD.js → ebnfDiagram-CCIWWBDH-Cr1Y6lPu.js} +1 -1
- package/dist/assets/{erDiagram-Q63AITRT-HAWySadP.js → erDiagram-Q63AITRT-D3EgiVVH.js} +1 -1
- package/dist/assets/esm-Df4edDpj.js +2 -0
- package/dist/assets/eventmodeling-45OFAUF4-DO1vRTM0.js +1 -0
- package/dist/assets/flowDiagram-23GEKE2U-BS7KXcqn.js +1 -0
- package/dist/assets/{ganttDiagram-NO4QXBWP-C1hwIcOA.js → ganttDiagram-NO4QXBWP-B0oyDIqK.js} +1 -1
- package/dist/assets/gitGraph-TEB2WS4Q-m5F5ef09.js +1 -0
- package/dist/assets/{gitGraphDiagram-IHSO6WYX-DYUYxs1R.js → gitGraphDiagram-IHSO6WYX-uQtr6IqK.js} +1 -1
- package/dist/assets/icons-CXPP1fFT.js +1 -0
- package/dist/assets/index-_ZhK6bvp.js +65 -0
- package/dist/assets/index-iVV2bP_f.css +3 -0
- package/dist/assets/info-DKCQHKI2-CZxPlQJ0.js +1 -0
- package/dist/assets/{infoDiagram-FWYZ7A6U-CnIL6tv4.js → infoDiagram-FWYZ7A6U-B_GzlLIM.js} +1 -1
- package/dist/assets/{ishikawaDiagram-FXEZZL3T-B7WXNycQ.js → ishikawaDiagram-FXEZZL3T-CKQIsSdU.js} +1 -1
- package/dist/assets/{journeyDiagram-5HDEW3XC-D4HvioyY.js → journeyDiagram-5HDEW3XC-DrrvzZL1.js} +1 -1
- package/dist/assets/{kanban-definition-HUTT4EX6-CDs763eS.js → kanban-definition-HUTT4EX6-CJQRTdXz.js} +1 -1
- package/dist/assets/{line-ikKkQiFp.js → line-B4DxX7tC.js} +1 -1
- package/dist/assets/{linear-GqPyxQkN.js → linear-U-nKUhyW.js} +1 -1
- package/dist/assets/{map-CJwjCvwr.js → map-BneTM-ri.js} +1 -1
- package/dist/assets/{mcp-servers-dialog-BQbFywVL.js → mcp-servers-dialog-BUSPvaqI.js} +2 -2
- package/dist/assets/{mermaid-parser.core-CoE8cccr.js → mermaid-parser.core-BlKWrEdZ.js} +2 -2
- package/dist/assets/{mermaid-renderer-BmbSAmpG.js → mermaid-renderer-jEbqFaTe.js} +2 -2
- package/dist/assets/{mermaid.core-DlFSL90j.js → mermaid.core-DWpShXCs.js} +3 -3
- package/dist/assets/{mindmap-definition-LN4V7U3C-DLdq0K9C.js → mindmap-definition-LN4V7U3C-CEVwZD78.js} +1 -1
- package/dist/assets/model-identity-CgA6_Pbm.js +1 -0
- package/dist/assets/model-reference-o41fJ6jQ.js +1 -0
- package/dist/assets/{monaco-BTsVCDWS.js → monaco-D6OhXgjg.js} +1 -1
- package/dist/assets/{ordinal-CibZU3PM.js → ordinal-B2r5L1v4.js} +1 -1
- package/dist/assets/{otel-BNGM50C-.js → otel-BFnphzjt.js} +1 -1
- package/dist/assets/packet-7NZHBO7P-zIioIZVN.js +1 -0
- package/dist/assets/{pegDiagram-2B236MQR-Cm1kPRC2.js → pegDiagram-2B236MQR-CBLkBdnQ.js} +1 -1
- package/dist/assets/{pi-ai-Cr3rpCXT.js → pi-ai-Cr2ee4a0.js} +2 -2
- package/dist/assets/{pi-web-ui-DoRGtREc.js → pi-web-ui-DV5jKjSg.js} +1 -1
- package/dist/assets/pie-RZYD4A2V-GGWijBvB.js +1 -0
- package/dist/assets/{pieDiagram-ENE6RG2P-Df05wjjo.js → pieDiagram-ENE6RG2P-8tCnwtWS.js} +1 -1
- package/dist/assets/{quadrantDiagram-ABIIQ3AL-BjCwGxJC.js → quadrantDiagram-ABIIQ3AL-CWd28cHF.js} +1 -1
- package/dist/assets/radar-I7S5WNFK-yvwFH3RV.js +1 -0
- package/dist/assets/railroad-3IZDKUUU-BVadlswB.js +1 -0
- package/dist/assets/railroad-abnf-AHOZXSZD-B7R-p_2-.js +1 -0
- package/dist/assets/railroad-ebnf-EBAXGLYW-BnbJxPug.js +1 -0
- package/dist/assets/railroad-peg-LSFZ7HO6-Ci3DcEuQ.js +1 -0
- package/dist/assets/{railroadDiagram-RFXS5EU6-CTUvpKkv.js → railroadDiagram-RFXS5EU6-CI4ouOBs.js} +1 -1
- package/dist/assets/{react-vendor-Dr5xvL-e.js → react-vendor-1KC5PyRx.js} +1 -1
- package/dist/assets/{requirementDiagram-TGXJPOKE-BU1kvEku.js → requirementDiagram-TGXJPOKE-DtFz7sw4.js} +1 -1
- package/dist/assets/{sankeyDiagram-HTMAVEWB-BHmvxVqH.js → sankeyDiagram-HTMAVEWB--VeOwwk9.js} +1 -1
- package/dist/assets/{sequenceDiagram-DBY2YBRQ-CTpwICgg.js → sequenceDiagram-DBY2YBRQ-B8AHk5Oe.js} +1 -1
- package/dist/assets/{sizeCapture-X5ZJPWSS-BbLhGaa4.js → sizeCapture-X5ZJPWSS-vV_ArM8D.js} +1 -1
- package/dist/assets/{skills-dialog-REBeTFSH.js → skills-dialog-BThnIUBs.js} +3 -1
- package/dist/assets/{src-B4Tvg3gT.js → src-PYsDme0X.js} +1 -1
- package/dist/assets/{stateDiagram-2N3HPSRC-BxuDwIgT.js → stateDiagram-2N3HPSRC-etXnJ8X8.js} +1 -1
- package/dist/assets/stateDiagram-v2-6OUMAXLB-Dms3pICj.js +1 -0
- package/dist/assets/{swimlanes-5IMT3BWC-EkZsiylV.js → swimlanes-5IMT3BWC-pabIKAmn.js} +2 -2
- package/dist/assets/swimlanesDiagram-G3AALYLV-B14aWjv6.js +8 -0
- package/dist/assets/{timeline-definition-FHXFAJF6-C9ewOCTa.js → timeline-definition-FHXFAJF6-C55MKm3L.js} +1 -1
- package/dist/assets/treeView-QDETBFTQ-Cru9B2zv.js +1 -0
- package/dist/assets/treemap-6X3UGDF4-DNOVHFaJ.js +1 -0
- package/dist/assets/{vennDiagram-L72KCM5P-CR49Y4Lp.js → vennDiagram-L72KCM5P-CgNf1Q9W.js} +1 -1
- package/dist/assets/wardley-OPB4EBWU-E3m1BOJQ.js +1 -0
- package/dist/assets/{wardleyDiagram-EHGQE667-Cn0GgwWi.js → wardleyDiagram-EHGQE667-Drt5ytec.js} +1 -1
- package/dist/assets/web-DoV8MSMn.js +1 -0
- package/dist/assets/{xychartDiagram-FW5EYKEG-DlmPNAvd.js → xychartDiagram-FW5EYKEG-hhoEo_Ar.js} +1 -1
- package/dist/index.html +13 -11
- package/dist/mobile-error.html +24 -0
- package/package.json +6 -1
- package/plugins/{openai-documents → documents}/plugin.json +2 -2
- package/plugins/{openai-presentations → presentations}/plugin.json +2 -2
- package/plugins/{openai-spreadsheets → spreadsheets}/plugin.json +2 -2
- package/server/acp/server.mjs +50 -35
- package/server/agent-manager.mjs +343 -46
- package/server/agent-profile-files.mjs +8 -4
- package/server/agent-profile-schema.mjs +47 -23
- package/server/agent-profiles.mjs +1 -1
- package/server/ai-http-logger.mjs +156 -13
- package/server/ai-provider-options.mjs +3 -0
- package/server/channels/channel-logs.mjs +13 -0
- package/server/channels/event-relay.mjs +33 -0
- package/server/channels/process-channel.mjs +29 -0
- package/server/channels/providers/wechat.mjs +9 -3
- package/server/channels/registry.mjs +5 -1
- package/server/cloud/chat-idempotency-store.mjs +96 -0
- package/server/cloud/client.mjs +102 -0
- package/server/cloud/config.mjs +53 -0
- package/server/cloud/credential-store.mjs +193 -0
- package/server/cloud/identity.mjs +355 -0
- package/server/cloud/index.mjs +7 -0
- package/server/cloud/models.mjs +80 -0
- package/server/cloud/runtime.mjs +129 -0
- package/server/cloud/service-config.mjs +77 -0
- package/server/index.mjs +63 -33
- package/server/lan-access-store.mjs +93 -3
- package/server/model-catalog.mjs +262 -0
- package/server/network-proxy.mjs +68 -3
- package/server/plugins/registry.mjs +93 -9
- package/server/public-api.mjs +6 -2
- package/server/routes/agent-profiles.mjs +43 -9
- package/server/routes/agent.mjs +48 -11
- package/server/routes/backup.mjs +7 -1
- package/server/routes/channels.mjs +47 -0
- package/server/routes/cloud.mjs +227 -0
- package/server/routes/lan-access.mjs +246 -21
- package/server/routes/models.mjs +13 -4
- package/server/routes/project.mjs +19 -1
- package/server/routes/scheduled-tasks.mjs +40 -10
- package/server/routes/shared-conversation.mjs +62 -58
- package/server/routes/shares.mjs +76 -1
- package/server/routes/static.mjs +26 -1
- package/server/routes/storage.mjs +24 -1
- package/server/routes/system.mjs +11 -0
- package/server/routes/terminal.mjs +7 -2
- package/server/routes/workspace.mjs +66 -13
- package/server/session-utils.mjs +2 -2
- package/server/share-store.mjs +168 -13
- package/server/skills.mjs +30 -3
- package/server/storage.mjs +7 -1
- package/server/subagents.mjs +4 -4
- package/server/terminal/terminal-manager.mjs +2 -4
- package/server/tools/definitions.mjs +3 -3
- package/server/tools/index.mjs +7 -5
- package/server/utils/network.mjs +9 -0
- package/server/utils/package-update.mjs +70 -47
- package/server/utils/process-env.mjs +7 -0
- package/server/utils/response.mjs +10 -3
- package/skills/skill-creator/LICENSE.txt +202 -0
- package/skills/skill-creator/SKILL.md +485 -0
- package/skills/skill-creator/UPSTREAM.md +6 -0
- package/skills/skill-creator/agents/analyzer.md +274 -0
- package/skills/skill-creator/agents/comparator.md +202 -0
- package/skills/skill-creator/agents/grader.md +223 -0
- package/skills/skill-creator/assets/eval_review.html +146 -0
- package/skills/skill-creator/eval-viewer/generate_review.py +471 -0
- package/skills/skill-creator/eval-viewer/viewer.html +1325 -0
- package/skills/skill-creator/references/schemas.md +430 -0
- package/skills/skill-creator/scripts/__init__.py +0 -0
- package/skills/skill-creator/scripts/aggregate_benchmark.py +401 -0
- package/skills/skill-creator/scripts/generate_report.py +326 -0
- package/skills/skill-creator/scripts/improve_description.py +247 -0
- package/skills/skill-creator/scripts/package_skill.py +136 -0
- package/skills/skill-creator/scripts/quick_validate.py +103 -0
- package/skills/skill-creator/scripts/run_eval.py +310 -0
- package/skills/skill-creator/scripts/run_loop.py +328 -0
- package/skills/skill-creator/scripts/utils.py +47 -0
- package/dist/assets/AgentProfilesPage-D7pbg8nm.js +0 -1
- package/dist/assets/ChatPanelHost-wfGCaCzD.js +0 -288
- package/dist/assets/PluginsPage-NAGroBm3.js +0 -1
- package/dist/assets/ScheduledTasksPage-Bp0MNfQD.js +0 -2
- package/dist/assets/SettingsWorkspacePage-BtJZMIsZ.js +0 -1576
- package/dist/assets/SharedConversationPage-Ce3KoVZt.js +0 -1
- package/dist/assets/TerminalDock-4eUuXP7X.js +0 -2
- package/dist/assets/WorkspaceInspector-kM3BRPjY.js +0 -13
- package/dist/assets/architecture-TIHT7OUA-BgP-IMeo.js +0 -1
- package/dist/assets/channel-Rz20dKTK.js +0 -1
- package/dist/assets/chunk-2Q5K7J3B-Cb14ZF_k.js +0 -1
- package/dist/assets/chunk-VAUOI2AC-BOZvd2Sn.js +0 -1
- package/dist/assets/chunk-XXDRQBXY-CD_EGCtT.js +0 -1
- package/dist/assets/classDiagram-OUVF2IWQ-C_ozTKCt.js +0 -1
- package/dist/assets/classDiagram-v2-EOCWNBFH-FOfFbbZF.js +0 -1
- package/dist/assets/cynefin-VYW2F7L2-BzPhLtd5.js +0 -1
- package/dist/assets/eventmodeling-45OFAUF4-BcgGbVJ-.js +0 -1
- package/dist/assets/flowDiagram-23GEKE2U-dtkzcC8E.js +0 -1
- package/dist/assets/gitGraph-TEB2WS4Q-VByWboBU.js +0 -1
- package/dist/assets/icons-BP8YOS-Z.js +0 -1
- package/dist/assets/index-C6k5taeb.js +0 -66
- package/dist/assets/index-CzN8NSKC.css +0 -3
- package/dist/assets/info-DKCQHKI2-CZeDLZxx.js +0 -1
- package/dist/assets/packet-7NZHBO7P-decrrxyX.js +0 -1
- package/dist/assets/pie-RZYD4A2V-J1CasQZl.js +0 -1
- package/dist/assets/radar-I7S5WNFK-B-sZytUA.js +0 -1
- package/dist/assets/railroad-3IZDKUUU-BSaqNPm8.js +0 -1
- package/dist/assets/railroad-abnf-AHOZXSZD-lzZR7PxT.js +0 -1
- package/dist/assets/railroad-ebnf-EBAXGLYW-DQtz1Fh9.js +0 -1
- package/dist/assets/railroad-peg-LSFZ7HO6-CpMsuGY3.js +0 -1
- package/dist/assets/stateDiagram-v2-6OUMAXLB-BGLshxcY.js +0 -1
- package/dist/assets/swimlanesDiagram-G3AALYLV-CqdqxA-b.js +0 -8
- package/dist/assets/treeView-QDETBFTQ-Byb71heW.js +0 -1
- package/dist/assets/treemap-6X3UGDF4-CMV1SJsq.js +0 -1
- package/dist/assets/wardley-OPB4EBWU-DtCHtr2V.js +0 -1
- /package/dist/assets/{array-CYkMkqnU.js → array-B9UHiPd-.js} +0 -0
- /package/dist/assets/{chunk-KEIR6QF5-77hsebd7.js → chunk-KEIR6QF5-jrz1pRUS.js} +0 -0
- /package/dist/assets/{chunk-Y2CYZVJY-CQltPwYk.js → chunk-Y2CYZVJY-D6N-CrJv.js} +0 -0
- /package/dist/assets/{css-utils-rkE68RDy.js → css-utils-31qzCXsk.js} +0 -0
- /package/dist/assets/{cytoscape.esm-DwdZnGOm.js → cytoscape.esm-DyfqweF2.js} +0 -0
- /package/dist/assets/{defaultLocale-Beh6XjaL.js → defaultLocale-5eAKkKJC.js} +0 -0
- /package/dist/assets/{dist-DelZBZpK.js → dist-CLg13NtV.js} +0 -0
- /package/dist/assets/{graphlib-CAISHZh4.js → graphlib-DJkmx0Cq.js} +0 -0
- /package/dist/assets/{init-Rr1s_RiX.js → init-DlZdxViB.js} +0 -0
- /package/dist/assets/{lit-vendor-Dr3cpBGF.js → lit-vendor-DvaQX_UQ.js} +0 -0
- /package/dist/assets/{logger-B65Akg8A.js → logger-8REuLPGK.js} +0 -0
- /package/dist/assets/{path-BAQ3hXlG.js → path-6uRLdFF7.js} +0 -0
- /package/dist/assets/{plugin-api-BbUkYm3Y.js → plugin-api-Uu2xAB4d.js} +0 -0
- /package/dist/assets/{rough.esm-Djo4Abte.js → rough.esm-Bud4g4fh.js} +0 -0
- /package/dist/assets/{xterm-CGiQzDiR.js → xterm-Bm_IbozX.js} +0 -0
- /package/plugins/{openai-documents → documents}/commands/document.md +0 -0
- /package/plugins/{openai-documents → documents}/skills/documents/SKILL.md +0 -0
- /package/plugins/{openai-presentations → presentations}/commands/presentation.md +0 -0
- /package/plugins/{openai-presentations → presentations}/skills/presentations/SKILL.md +0 -0
- /package/plugins/{openai-spreadsheets → spreadsheets}/commands/spreadsheet.md +0 -0
- /package/plugins/{openai-spreadsheets → spreadsheets}/skills/spreadsheets/SKILL.md +0 -0
package/server/agent-manager.mjs
CHANGED
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
formatSubagentTask,
|
|
14
14
|
} from './subagents.mjs'
|
|
15
15
|
import { agentProfileSnapshot, getAgentProfile } from './agent-profiles.mjs'
|
|
16
|
+
import { modelBindingFromModel, resolveImplicitModelPreference, resolveModelBinding } from './model-catalog.mjs'
|
|
16
17
|
import { agentProfileFromMarkdown } from './agent-profile-files.mjs'
|
|
17
18
|
import {
|
|
18
19
|
applyCapabilityPolicy,
|
|
@@ -28,6 +29,7 @@ import {
|
|
|
28
29
|
import { projectContextFromId, defaultGlobalWorkspaceContext, readProjectConfig } from './project-config.mjs'
|
|
29
30
|
import { ensureStorage, readStore, atomicUpdate, atomicSessionMetadataUpdate, readSessionValue, writeSessionValue, deleteSessionValue, tempAgentsDir } from './storage.mjs'
|
|
30
31
|
import { logger } from './utils/logger.mjs'
|
|
32
|
+
import { publishChannelSessionChanged } from './channels/event-relay.mjs'
|
|
31
33
|
import { withSessionPersistenceLock } from './session-persistence-lock.mjs'
|
|
32
34
|
import { getGlobalMemoryRevision, isGlobalMemoryEnabled } from './global-memory.mjs'
|
|
33
35
|
import { buildSystemPrompt, generateAiTitle, generateTitle } from './session-utils.mjs'
|
|
@@ -198,7 +200,8 @@ function hasFullAccess(session) {
|
|
|
198
200
|
/** @typedef {{ agent: Agent, projectContext: object|null, projectId: string|null, accessMode: string, yoloMode: boolean, model: object, thinkingLevel: string, scope: string, title: string, createdAt: string, status: string, startedAt: string|null, finishedAt: string|null, listeners: Set<function>, idleTimer: NodeJS.Timeout|null, eventBus: EventEmitter }} AgentSession */
|
|
199
201
|
|
|
200
202
|
const IDLE_TIMEOUT_MS = 30 * 60 * 1000 // 30 minutes
|
|
201
|
-
const
|
|
203
|
+
const ABORT_IDLE_WAIT_TIMEOUT_MS = 3000
|
|
204
|
+
const SUBAGENT_DEFAULT_TIMEOUT_MS = 60 * 60 * 1000
|
|
202
205
|
const SUBAGENT_TRACE_THROTTLE_MS = 150
|
|
203
206
|
|
|
204
207
|
/**
|
|
@@ -429,6 +432,71 @@ function addToolTimingToEvent(session, event) {
|
|
|
429
432
|
return event
|
|
430
433
|
}
|
|
431
434
|
|
|
435
|
+
function isRecord(value) {
|
|
436
|
+
return Boolean(value && typeof value === 'object' && !Array.isArray(value))
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
function updateRuntimeToolExecution(session, event) {
|
|
440
|
+
if (!event?.toolCallId) return
|
|
441
|
+
if (event.type !== 'tool_execution_start' && event.type !== 'tool_execution_update' && event.type !== 'tool_execution_end') return
|
|
442
|
+
|
|
443
|
+
const existing = session.runtimeToolExecutions?.get(event.toolCallId)
|
|
444
|
+
const toolName = event.toolName ?? existing?.toolName
|
|
445
|
+
if (!toolName) return
|
|
446
|
+
const result = event.type === 'tool_execution_end' ? event.result : event.partialResult
|
|
447
|
+
const existingDetails = isRecord(existing?.details) ? existing.details : {}
|
|
448
|
+
const resultDetails = isRecord(result?.details) ? result.details : {}
|
|
449
|
+
const quickforgeTiming = event.quickforgeTiming || resultDetails.quickforgeTiming || existingDetails.quickforgeTiming
|
|
450
|
+
const details = {
|
|
451
|
+
...existingDetails,
|
|
452
|
+
...resultDetails,
|
|
453
|
+
...(quickforgeTiming ? { quickforgeTiming } : {}),
|
|
454
|
+
sessionId: resultDetails.sessionId ?? existingDetails.sessionId ?? session.sessionId,
|
|
455
|
+
toolCallId: resultDetails.toolCallId ?? existingDetails.toolCallId ?? event.toolCallId,
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
session.runtimeToolExecutions?.set(event.toolCallId, {
|
|
459
|
+
role: 'toolResult',
|
|
460
|
+
toolCallId: event.toolCallId,
|
|
461
|
+
toolName,
|
|
462
|
+
content: result?.content ?? existing?.content ?? [],
|
|
463
|
+
details,
|
|
464
|
+
isError: event.type === 'tool_execution_end' ? Boolean(event.isError) : false,
|
|
465
|
+
timestamp: existing?.timestamp ?? Date.now(),
|
|
466
|
+
pending: event.type !== 'tool_execution_end',
|
|
467
|
+
})
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
function messagesWithRuntimeToolExecutions(session) {
|
|
471
|
+
const messages = session.agent.state.messages || []
|
|
472
|
+
if (!session.runtimeToolExecutions?.size) return messages
|
|
473
|
+
|
|
474
|
+
const authoritativeToolCallIds = new Set(
|
|
475
|
+
messages
|
|
476
|
+
.filter((message) => message?.role === 'toolResult' && typeof message.toolCallId === 'string')
|
|
477
|
+
.map((message) => message.toolCallId),
|
|
478
|
+
)
|
|
479
|
+
const runtimeMessages = []
|
|
480
|
+
for (const [toolCallId, snapshot] of session.runtimeToolExecutions) {
|
|
481
|
+
if (authoritativeToolCallIds.has(toolCallId)) {
|
|
482
|
+
session.runtimeToolExecutions.delete(toolCallId)
|
|
483
|
+
} else {
|
|
484
|
+
const { pending: _pending, ...message } = snapshot
|
|
485
|
+
runtimeMessages.push(message)
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
return runtimeMessages.length > 0 ? [...messages, ...runtimeMessages] : messages
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
function runtimePendingToolCalls(session) {
|
|
492
|
+
const pending = new Set(session.agent.state.pendingToolCalls || [])
|
|
493
|
+
for (const [toolCallId, snapshot] of session.runtimeToolExecutions || []) {
|
|
494
|
+
if (snapshot.pending) pending.add(toolCallId)
|
|
495
|
+
else pending.delete(toolCallId)
|
|
496
|
+
}
|
|
497
|
+
return Array.from(pending)
|
|
498
|
+
}
|
|
499
|
+
|
|
432
500
|
export function markLatestAssistantProcessFinished(messages, finishedAt = Date.now()) {
|
|
433
501
|
if (!Array.isArray(messages)) return false
|
|
434
502
|
for (let index = messages.length - 1; index >= 0; index--) {
|
|
@@ -448,6 +516,37 @@ function updateSessionMessages(session, messages) {
|
|
|
448
516
|
session.agent.state.messages = messages
|
|
449
517
|
}
|
|
450
518
|
|
|
519
|
+
const CLIENT_MESSAGE_ID_FIELD = 'quickforgeClientMessageId'
|
|
520
|
+
|
|
521
|
+
function isManagedCloudModel(model) {
|
|
522
|
+
return model?.provider === 'quickforge-cloud' && model?.quickforgeModelSource === 'cloud'
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
function objectMetadata(message) {
|
|
526
|
+
const metadata = message?.metadata
|
|
527
|
+
return metadata && typeof metadata === 'object' && !Array.isArray(metadata) ? metadata : {}
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
function logicalMessageId(message) {
|
|
531
|
+
const value = objectMetadata(message)[CLIENT_MESSAGE_ID_FIELD]
|
|
532
|
+
return typeof value === 'string' && /^[A-Za-z0-9._:-]{1,128}$/.test(value) ? value : undefined
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
function messageWithLogicalId(message) {
|
|
536
|
+
if (!message || typeof message !== 'object' || logicalMessageId(message)) return message
|
|
537
|
+
return {
|
|
538
|
+
...message,
|
|
539
|
+
metadata: {
|
|
540
|
+
...objectMetadata(message),
|
|
541
|
+
[CLIENT_MESSAGE_ID_FIELD]: `qfcm_${randomUUID()}`,
|
|
542
|
+
},
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
function prepareCloudUserMessage(session, message) {
|
|
547
|
+
return isManagedCloudModel(session?.model) ? messageWithLogicalId(message) : message
|
|
548
|
+
}
|
|
549
|
+
|
|
451
550
|
function resetSessionCompaction(session) {
|
|
452
551
|
session.contextCompaction = null
|
|
453
552
|
session.lastAutoCompactAt = null
|
|
@@ -560,6 +659,9 @@ async function summarySession(session, initialUserMessage, summaryOptions) {
|
|
|
560
659
|
accessMode: session.accessMode,
|
|
561
660
|
yoloMode: session.yoloMode,
|
|
562
661
|
model: session.model,
|
|
662
|
+
modelRef: session.modelRef,
|
|
663
|
+
modelAccessContext: session.modelAccessContext,
|
|
664
|
+
resolvePersistedModel: true,
|
|
563
665
|
thinkingLevel: session.thinkingLevel,
|
|
564
666
|
messages: compactedMessages,
|
|
565
667
|
title: compactedTitle,
|
|
@@ -1014,7 +1116,7 @@ model:
|
|
|
1014
1116
|
modelId: ${frontmatterString(spec.model.modelId)}${spec.model.api ? `
|
|
1015
1117
|
api: ${frontmatterString(spec.model.api)}` : ''}${spec.model.baseUrl ? `
|
|
1016
1118
|
baseUrl: ${frontmatterString(spec.model.baseUrl)}` : ''}` : ''}
|
|
1017
|
-
max-runtime-ms: ${Math.max(1000, Math.min(Number(spec.maxRuntimeMs || SUBAGENT_DEFAULT_TIMEOUT_MS),
|
|
1119
|
+
max-runtime-ms: ${Math.max(1000, Math.min(Number(spec.maxRuntimeMs || SUBAGENT_DEFAULT_TIMEOUT_MS), 60 * 60 * 1000))}
|
|
1018
1120
|
max-tool-calls: ${Math.max(1, Math.min(Number(spec.maxToolCalls || 300), 300))}
|
|
1019
1121
|
---
|
|
1020
1122
|
${spec.systemPrompt}
|
|
@@ -1068,10 +1170,15 @@ async function runSubagent(parentSession, params, parentSignal, onUpdate) {
|
|
|
1068
1170
|
throw new Error('No active model is configured for the parent session.')
|
|
1069
1171
|
}
|
|
1070
1172
|
|
|
1071
|
-
const timeoutMs = Math.max(1000, Math.min(Number(definition.maxRuntimeMs || SUBAGENT_DEFAULT_TIMEOUT_MS),
|
|
1173
|
+
const timeoutMs = Math.max(1000, Math.min(Number(definition.maxRuntimeMs || SUBAGENT_DEFAULT_TIMEOUT_MS), 60 * 60 * 1000))
|
|
1072
1174
|
definition.capabilityPolicy ||= inferCapabilityPolicy(definition.allowedTools || [])
|
|
1073
1175
|
definition.allowedTools = applyCapabilityPolicy(definition.allowedTools || [], definition.capabilityPolicy)
|
|
1074
|
-
const { model: subagentModel, info: subagentModelInfo } = await resolveAgentProfileModel(
|
|
1176
|
+
const { model: subagentModel, info: subagentModelInfo } = await resolveAgentProfileModel(
|
|
1177
|
+
definition,
|
|
1178
|
+
parentSession.model,
|
|
1179
|
+
readStore,
|
|
1180
|
+
parentSession.modelAccessContext || {},
|
|
1181
|
+
)
|
|
1075
1182
|
const subagentThinkingLevel = resolveAgentProfileThinkingLevel(definition, parentSession.thinkingLevel, subagentModel)
|
|
1076
1183
|
const subagentSessionId = `${parentSession.sessionId}:subagent:${definition.name}:${randomUUID()}`
|
|
1077
1184
|
const startedAt = Date.now()
|
|
@@ -1319,6 +1426,12 @@ function applyActiveCapabilityPrompt(messages, capabilityPrompt) {
|
|
|
1319
1426
|
return messages
|
|
1320
1427
|
}
|
|
1321
1428
|
|
|
1429
|
+
function activeTurnContextPrompt(session) {
|
|
1430
|
+
return [session?.activeTransientContextPrompt, session?.activeCapabilityPrompt]
|
|
1431
|
+
.filter((prompt) => typeof prompt === 'string' && prompt.trim())
|
|
1432
|
+
.join('\n\n') || null
|
|
1433
|
+
}
|
|
1434
|
+
|
|
1322
1435
|
function compactSummaryIndex(messages) {
|
|
1323
1436
|
for (let index = messages.length - 1; index >= 0; index--) {
|
|
1324
1437
|
if (isCompactSummaryMessage(messages[index])) return index
|
|
@@ -1352,7 +1465,7 @@ async function transformSessionContext(session, messages, signal) {
|
|
|
1352
1465
|
session.lastTransformedContextMessages = transformedMessages
|
|
1353
1466
|
return applyActiveCapabilityPrompt(
|
|
1354
1467
|
applyActiveCommandPrompt(compactedContextMessages(transformedMessages), session?.activeCommandPrompt),
|
|
1355
|
-
session
|
|
1468
|
+
activeTurnContextPrompt(session),
|
|
1356
1469
|
)
|
|
1357
1470
|
}
|
|
1358
1471
|
|
|
@@ -1432,9 +1545,15 @@ export async function createAgent(sessionId, config = {}) {
|
|
|
1432
1545
|
const {
|
|
1433
1546
|
scope = 'global',
|
|
1434
1547
|
projectId = null,
|
|
1548
|
+
source = null,
|
|
1549
|
+
channelId = null,
|
|
1550
|
+
channelName = null,
|
|
1435
1551
|
accessMode: rawAccessMode,
|
|
1436
1552
|
yoloMode = false,
|
|
1437
1553
|
model = null,
|
|
1554
|
+
modelRef = null,
|
|
1555
|
+
modelAccessContext = null,
|
|
1556
|
+
resolvePersistedModel = false,
|
|
1438
1557
|
thinkingLevel = 'off',
|
|
1439
1558
|
messages = [],
|
|
1440
1559
|
systemPrompt = null,
|
|
@@ -1479,24 +1598,36 @@ export async function createAgent(sessionId, config = {}) {
|
|
|
1479
1598
|
|
|
1480
1599
|
// Resolve model
|
|
1481
1600
|
let resolvedModel = model
|
|
1601
|
+
let resolvedModelRef = modelRef
|
|
1482
1602
|
if (!resolvedModel) {
|
|
1483
|
-
// Try to load from storage
|
|
1603
|
+
// Try to load the active preference from storage. A stale/hidden implicit
|
|
1604
|
+
// preference may fall back, while an explicit session binding never does.
|
|
1484
1605
|
try {
|
|
1485
1606
|
const settings = await readStore('settings')
|
|
1486
1607
|
const raw = settings?.['active-model']
|
|
1487
|
-
|
|
1608
|
+
resolvedModel = await resolveImplicitModelPreference(raw, modelAccessContext || {})
|
|
1488
1609
|
} catch {
|
|
1489
1610
|
// ignore
|
|
1490
1611
|
}
|
|
1491
1612
|
}
|
|
1492
1613
|
if (agentProfile?.model?.mode === 'fixed') {
|
|
1493
|
-
|
|
1614
|
+
const profileBinding = await resolveAgentProfileModel(agentProfile, resolvedModel, readStore, modelAccessContext || {})
|
|
1615
|
+
resolvedModel = profileBinding.model
|
|
1616
|
+
resolvedModelRef = profileBinding.modelRef || null
|
|
1494
1617
|
resolvedAgentProfile = {
|
|
1495
1618
|
...agentProfile,
|
|
1496
1619
|
model: modelReferenceSnapshot(agentProfile.model),
|
|
1497
1620
|
}
|
|
1498
1621
|
}
|
|
1499
1622
|
if (resolvedAgentProfile && !resolvedModel) throw new Error('No active model is configured for the agent session.')
|
|
1623
|
+
const resolvedBinding = resolvedModel
|
|
1624
|
+
? (resolvedModelRef
|
|
1625
|
+
? { model: resolvedModel, modelRef: resolvedModelRef }
|
|
1626
|
+
: model
|
|
1627
|
+
? { model: resolvedModel, modelRef: null }
|
|
1628
|
+
: await modelBindingFromModel(resolvedModel))
|
|
1629
|
+
: { model: resolvedModel, modelRef: null }
|
|
1630
|
+
resolvedModel = resolvedBinding.model
|
|
1500
1631
|
const resolvedThinkingLevel = agentProfile
|
|
1501
1632
|
? resolveAgentProfileThinkingLevel(agentProfile, thinkingLevel, resolvedModel)
|
|
1502
1633
|
: thinkingLevel
|
|
@@ -1600,9 +1731,15 @@ export async function createAgent(sessionId, config = {}) {
|
|
|
1600
1731
|
agent,
|
|
1601
1732
|
projectContext,
|
|
1602
1733
|
projectId,
|
|
1734
|
+
source: typeof source === 'string' && source.trim() ? source.trim() : null,
|
|
1735
|
+
channelId: typeof channelId === 'string' && channelId.trim() ? channelId.trim() : null,
|
|
1736
|
+
channelName: typeof channelName === 'string' && channelName.trim() ? channelName.trim() : null,
|
|
1603
1737
|
accessMode,
|
|
1604
1738
|
yoloMode: resolvedYoloMode,
|
|
1605
1739
|
model: resolvedModel,
|
|
1740
|
+
modelRef: resolvedBinding.modelRef,
|
|
1741
|
+
modelAccessContext: modelAccessContext || null,
|
|
1742
|
+
resolvePersistedModel: resolvePersistedModel === true || Boolean(resolvedBinding.modelRef),
|
|
1606
1743
|
thinkingLevel: resolvedThinkingLevel,
|
|
1607
1744
|
scope,
|
|
1608
1745
|
title,
|
|
@@ -1617,6 +1754,8 @@ export async function createAgent(sessionId, config = {}) {
|
|
|
1617
1754
|
activeCommandName: null,
|
|
1618
1755
|
activeCommandPermissions: null,
|
|
1619
1756
|
activeCommandPrompt: null,
|
|
1757
|
+
activeCapabilityPrompt: null,
|
|
1758
|
+
activeTransientContextPrompt: null,
|
|
1620
1759
|
eventBus,
|
|
1621
1760
|
idleTimer: null,
|
|
1622
1761
|
persistTimer: null,
|
|
@@ -1625,6 +1764,7 @@ export async function createAgent(sessionId, config = {}) {
|
|
|
1625
1764
|
titleGenerationPromiseId: null,
|
|
1626
1765
|
sessionCreatedEmitted: messages.length > 0,
|
|
1627
1766
|
toolTimings: new Map(),
|
|
1767
|
+
runtimeToolExecutions: new Map(),
|
|
1628
1768
|
getApiKey,
|
|
1629
1769
|
contextCompaction,
|
|
1630
1770
|
agentProfile: resolvedAgentProfile ? agentProfileSnapshot(resolvedAgentProfile) : null,
|
|
@@ -1640,6 +1780,8 @@ export async function createAgent(sessionId, config = {}) {
|
|
|
1640
1780
|
/** Track active SSE connections. Only one SSE stream allowed per session to prevent
|
|
1641
1781
|
* connection-pool exhaustion when two browser tabs load the same session. */
|
|
1642
1782
|
sseConnected: false,
|
|
1783
|
+
abortPending: false,
|
|
1784
|
+
abortEndEmitted: false,
|
|
1643
1785
|
}
|
|
1644
1786
|
|
|
1645
1787
|
// Subscribe to agent lifecycle events and forward to eventBus
|
|
@@ -1649,6 +1791,7 @@ export async function createAgent(sessionId, config = {}) {
|
|
|
1649
1791
|
// complete session history. Replace with the authoritative full state
|
|
1650
1792
|
// before forwarding to clients.
|
|
1651
1793
|
const timedEvent = addToolTimingToEvent(session, event)
|
|
1794
|
+
updateRuntimeToolExecution(session, timedEvent)
|
|
1652
1795
|
const eventEndStatus = event.type === 'agent_end'
|
|
1653
1796
|
? session.agent.signal?.aborted
|
|
1654
1797
|
? 'aborted'
|
|
@@ -1668,11 +1811,14 @@ export async function createAgent(sessionId, config = {}) {
|
|
|
1668
1811
|
}
|
|
1669
1812
|
: timedEvent
|
|
1670
1813
|
|
|
1671
|
-
|
|
1672
|
-
|
|
1814
|
+
const shouldForwardEvent = !(timedEvent.type === 'agent_end' && session.abortEndEmitted)
|
|
1815
|
+
if (shouldForwardEvent) {
|
|
1816
|
+
emitSessionEvent(session, forwardEvent)
|
|
1817
|
+
}
|
|
1673
1818
|
|
|
1674
1819
|
// Track status
|
|
1675
1820
|
if (event.type === 'agent_start') {
|
|
1821
|
+
session.abortEndEmitted = false
|
|
1676
1822
|
session.status = 'running'
|
|
1677
1823
|
session.startedAt = session.startedAt ?? new Date().toISOString()
|
|
1678
1824
|
session.finishedAt = null
|
|
@@ -1687,6 +1833,8 @@ export async function createAgent(sessionId, config = {}) {
|
|
|
1687
1833
|
}
|
|
1688
1834
|
|
|
1689
1835
|
if (event.type === 'agent_end') {
|
|
1836
|
+
session.abortPending = false
|
|
1837
|
+
session.abortEndEmitted = false
|
|
1690
1838
|
session.status = eventEndStatus || (session.agent.state.errorMessage ? 'error' : 'idle')
|
|
1691
1839
|
session.finishedAt = new Date().toISOString()
|
|
1692
1840
|
session.toolTimings?.clear()
|
|
@@ -1699,14 +1847,15 @@ export async function createAgent(sessionId, config = {}) {
|
|
|
1699
1847
|
}
|
|
1700
1848
|
|
|
1701
1849
|
if (event.type === 'message_end') {
|
|
1702
|
-
const
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
//
|
|
1850
|
+
const isUserMessage = event.message?.role === 'user' || event.message?.role === 'user-with-attachments'
|
|
1851
|
+
const isInitialUserMessage = isUserMessage && session.agent.state.messages.length === 1
|
|
1852
|
+
const requiresDurableCloudMessage = isUserMessage && isManagedCloudModel(session.model) && Boolean(logicalMessageId(event.message))
|
|
1853
|
+
if (isInitialUserMessage || requiresDurableCloudMessage) {
|
|
1854
|
+
// Persist every managed Cloud user message before the provider stream starts,
|
|
1855
|
+
// so its logical ID survives a process restart and resolves to the same private key.
|
|
1707
1856
|
try {
|
|
1708
1857
|
const metadata = await flushSessionPersist(session)
|
|
1709
|
-
if (metadata) {
|
|
1858
|
+
if (metadata && isInitialUserMessage) {
|
|
1710
1859
|
if (!session.sessionCreatedEmitted) {
|
|
1711
1860
|
session.sessionCreatedEmitted = true
|
|
1712
1861
|
emitSessionEvent(session, { type: 'session_created', metadata })
|
|
@@ -1714,7 +1863,7 @@ export async function createAgent(sessionId, config = {}) {
|
|
|
1714
1863
|
scheduleSessionTitleGeneration(session, event.message)
|
|
1715
1864
|
}
|
|
1716
1865
|
} catch (err) {
|
|
1717
|
-
logger.error(`Failed to persist
|
|
1866
|
+
logger.error(`Failed to persist user message for session ${sessionId}:`, err, { sessionId })
|
|
1718
1867
|
}
|
|
1719
1868
|
} else {
|
|
1720
1869
|
// Debounced persist for crash recovery; coalesces the many message_end
|
|
@@ -1758,7 +1907,7 @@ function sessionLastModifiedFromMessages(messages, fallback) {
|
|
|
1758
1907
|
* Persist session data to storage.
|
|
1759
1908
|
*/
|
|
1760
1909
|
async function persistSessionUnlocked(session) {
|
|
1761
|
-
const { sessionId, agent, scope, projectId, title, titleSource, createdAt, lastModified: storedLastModified, status, startedAt, finishedAt, model, thinkingLevel, accessMode, yoloMode, contextCompaction } = session
|
|
1910
|
+
const { sessionId, agent, scope, projectId, source, channelId, channelName, title, titleSource, createdAt, lastModified: storedLastModified, status, startedAt, finishedAt, model, modelRef, thinkingLevel, accessMode, yoloMode, contextCompaction } = session
|
|
1762
1911
|
const messages = agent.state.messages
|
|
1763
1912
|
|
|
1764
1913
|
if (messages.length === 0) {
|
|
@@ -1781,6 +1930,7 @@ async function persistSessionUnlocked(session) {
|
|
|
1781
1930
|
title,
|
|
1782
1931
|
titleSource,
|
|
1783
1932
|
model,
|
|
1933
|
+
modelRef: modelRef || undefined,
|
|
1784
1934
|
thinkingLevel,
|
|
1785
1935
|
accessMode,
|
|
1786
1936
|
yoloMode,
|
|
@@ -1789,6 +1939,9 @@ async function persistSessionUnlocked(session) {
|
|
|
1789
1939
|
lastModified,
|
|
1790
1940
|
scope,
|
|
1791
1941
|
projectId: scope === 'project' ? projectId : undefined,
|
|
1942
|
+
source: source || undefined,
|
|
1943
|
+
channelId: channelId || undefined,
|
|
1944
|
+
channelName: channelName || undefined,
|
|
1792
1945
|
taskStatus: status,
|
|
1793
1946
|
taskStartedAt: startedAt,
|
|
1794
1947
|
taskFinishedAt: finishedAt,
|
|
@@ -1842,6 +1995,9 @@ async function persistSessionUnlocked(session) {
|
|
|
1842
1995
|
preview,
|
|
1843
1996
|
scope,
|
|
1844
1997
|
projectId: scope === 'project' ? projectId : undefined,
|
|
1998
|
+
source: source || undefined,
|
|
1999
|
+
channelId: channelId || undefined,
|
|
2000
|
+
channelName: channelName || undefined,
|
|
1845
2001
|
taskStatus: status,
|
|
1846
2002
|
taskStartedAt: startedAt,
|
|
1847
2003
|
taskFinishedAt: finishedAt,
|
|
@@ -1856,30 +2012,45 @@ async function persistSessionUnlocked(session) {
|
|
|
1856
2012
|
}
|
|
1857
2013
|
|
|
1858
2014
|
// Write to storage atomically (read-modify-write within queue)
|
|
2015
|
+
let persistedMetadata
|
|
1859
2016
|
try {
|
|
1860
2017
|
await writeSessionValue(sessionId, sessionData)
|
|
1861
|
-
let archivedAt
|
|
1862
2018
|
await atomicSessionMetadataUpdate(scope, projectId, (data) => {
|
|
1863
2019
|
const existingMetadata = data[sessionId]
|
|
1864
|
-
archivedAt = existingMetadata?.archivedAt
|
|
2020
|
+
const archivedAt = existingMetadata?.archivedAt
|
|
1865
2021
|
// Merge onto the existing record so storage-only fields the in-memory
|
|
1866
2022
|
// session does not own (pinnedAt, archivedAt, …) survive the rebuild.
|
|
1867
|
-
|
|
2023
|
+
persistedMetadata = {
|
|
1868
2024
|
...existingMetadata,
|
|
1869
2025
|
...metadata,
|
|
1870
2026
|
...(archivedAt ? { archivedAt } : {}),
|
|
1871
2027
|
}
|
|
2028
|
+
data[sessionId] = persistedMetadata
|
|
1872
2029
|
return data
|
|
1873
2030
|
})
|
|
1874
|
-
if (archivedAt) {
|
|
1875
|
-
await writeSessionValue(sessionId, { ...sessionData, archivedAt })
|
|
2031
|
+
if (persistedMetadata?.archivedAt) {
|
|
2032
|
+
await writeSessionValue(sessionId, { ...sessionData, archivedAt: persistedMetadata.archivedAt })
|
|
1876
2033
|
}
|
|
1877
2034
|
} catch (err) {
|
|
1878
2035
|
logger.error(`Failed to persist session ${sessionId}:`, err, { sessionId })
|
|
1879
2036
|
return null
|
|
1880
2037
|
}
|
|
1881
2038
|
|
|
1882
|
-
|
|
2039
|
+
if (source === 'acp' && channelId && persistedMetadata) {
|
|
2040
|
+
void publishChannelSessionChanged({
|
|
2041
|
+
channelId,
|
|
2042
|
+
channelName: channelName || undefined,
|
|
2043
|
+
sessionId,
|
|
2044
|
+
projectId: scope === 'project' ? projectId : null,
|
|
2045
|
+
workspace: scope === 'project'
|
|
2046
|
+
? { id: projectId, kind: 'project' }
|
|
2047
|
+
: { id: 'default', kind: 'default' },
|
|
2048
|
+
change: 'upsert',
|
|
2049
|
+
metadata: persistedMetadata,
|
|
2050
|
+
})
|
|
2051
|
+
}
|
|
2052
|
+
|
|
2053
|
+
return persistedMetadata || metadata
|
|
1883
2054
|
}
|
|
1884
2055
|
|
|
1885
2056
|
async function persistSession(session) {
|
|
@@ -1952,7 +2123,10 @@ export async function rollbackSessionMessages(sessionId, rollbackMessageIndex) {
|
|
|
1952
2123
|
throw Object.assign(new Error('Session not found'), { statusCode: 404 })
|
|
1953
2124
|
}
|
|
1954
2125
|
if (session.agent.state.isStreaming) {
|
|
1955
|
-
throw Object.assign(new Error('Generation is still running. Stop it or wait until it finishes before rolling back.'), {
|
|
2126
|
+
throw Object.assign(new Error('Generation is still running. Stop it or wait until it finishes before rolling back.'), {
|
|
2127
|
+
statusCode: 409,
|
|
2128
|
+
errorCode: 'GENERATION_STILL_RUNNING_BEFORE_ROLLBACK',
|
|
2129
|
+
})
|
|
1956
2130
|
}
|
|
1957
2131
|
|
|
1958
2132
|
const messages = Array.isArray(session.agent.state.messages) ? session.agent.state.messages : []
|
|
@@ -2016,21 +2190,42 @@ function scheduleSessionTitleGeneration(session, userMessage) {
|
|
|
2016
2190
|
session.titleGenerationPromiseId = generationId
|
|
2017
2191
|
}
|
|
2018
2192
|
|
|
2193
|
+
async function refreshSessionModelBinding(session) {
|
|
2194
|
+
if (!session?.model || (!session.modelRef && session.resolvePersistedModel !== true)) return session
|
|
2195
|
+
const binding = await resolveModelBinding(
|
|
2196
|
+
session.modelRef ? { modelRef: session.modelRef } : { model: session.model },
|
|
2197
|
+
{
|
|
2198
|
+
context: session.modelAccessContext || {},
|
|
2199
|
+
currentModel: session.model,
|
|
2200
|
+
allowCurrentHidden: true,
|
|
2201
|
+
forExecution: true,
|
|
2202
|
+
legacySnapshot: session.model,
|
|
2203
|
+
},
|
|
2204
|
+
)
|
|
2205
|
+
session.model = binding.model
|
|
2206
|
+
session.modelRef = binding.modelRef
|
|
2207
|
+
session.resolvePersistedModel = true
|
|
2208
|
+
session.agent.state.model = binding.model
|
|
2209
|
+
return session
|
|
2210
|
+
}
|
|
2211
|
+
|
|
2019
2212
|
/**
|
|
2020
2213
|
* Send a user message to the agent and start the agent loop.
|
|
2021
2214
|
* Returns immediately; events are streamed via the event bus.
|
|
2022
2215
|
*/
|
|
2023
|
-
export async function runPrompt(sessionId, message, selectedCapabilities = [], promptCommand = null) {
|
|
2024
|
-
let session =
|
|
2025
|
-
if (!session) {
|
|
2026
|
-
session = await restoreAgent(sessionId)
|
|
2027
|
-
}
|
|
2216
|
+
export async function runPrompt(sessionId, message, selectedCapabilities = [], promptCommand = null, transientContextPrompt = null, modelAccessContext = null) {
|
|
2217
|
+
let session = await syncSessionFromStorage(sessionId)
|
|
2028
2218
|
if (!session) {
|
|
2029
2219
|
throw Object.assign(new Error('Session not found'), { statusCode: 404 })
|
|
2030
2220
|
}
|
|
2221
|
+
if (modelAccessContext) session.modelAccessContext = modelAccessContext
|
|
2222
|
+
await refreshSessionModelBinding(session)
|
|
2031
2223
|
|
|
2032
|
-
if (session.agent.state.isStreaming) {
|
|
2033
|
-
throw Object.assign(new Error('Generation is still running. Stop it or wait until it finishes.'), {
|
|
2224
|
+
if (session.agent.state.isStreaming || session.abortPending) {
|
|
2225
|
+
throw Object.assign(new Error('Generation is still running. Stop it or wait until it finishes.'), {
|
|
2226
|
+
statusCode: 409,
|
|
2227
|
+
errorCode: 'GENERATION_ALREADY_RUNNING',
|
|
2228
|
+
})
|
|
2034
2229
|
}
|
|
2035
2230
|
|
|
2036
2231
|
await refreshMemoryState(session)
|
|
@@ -2041,7 +2236,7 @@ export async function runPrompt(sessionId, message, selectedCapabilities = [], p
|
|
|
2041
2236
|
? { role: 'user', content: message, timestamp: new Date().toISOString() }
|
|
2042
2237
|
: message
|
|
2043
2238
|
const commandState = await resolveCommandState(session, initialUserMessage, promptCommand)
|
|
2044
|
-
const
|
|
2239
|
+
const resolvedUserMessage = commandState.userMessage ?? initialUserMessage
|
|
2045
2240
|
|
|
2046
2241
|
if (commandState.textResponse) {
|
|
2047
2242
|
session.agent.state.messages = [
|
|
@@ -2068,6 +2263,8 @@ export async function runPrompt(sessionId, message, selectedCapabilities = [], p
|
|
|
2068
2263
|
return compactSession(session, initialUserMessage, commandState.compact)
|
|
2069
2264
|
}
|
|
2070
2265
|
|
|
2266
|
+
const userMessage = prepareCloudUserMessage(session, resolvedUserMessage)
|
|
2267
|
+
|
|
2071
2268
|
// Set a meaningful fallback immediately. The AI title request starts only
|
|
2072
2269
|
// after the first user message has been persisted by the message_end handler.
|
|
2073
2270
|
if (session.titleSource === 'default' && session.title === 'New chat') {
|
|
@@ -2082,6 +2279,9 @@ export async function runPrompt(sessionId, message, selectedCapabilities = [], p
|
|
|
2082
2279
|
session.activeCommandPermissions = commandState.permissions ?? null
|
|
2083
2280
|
session.activeCommandPrompt = commandState.commandPrompt ?? null
|
|
2084
2281
|
session.activeCapabilityPrompt = selectedCapabilityPrompt(selectedCapabilities)
|
|
2282
|
+
session.activeTransientContextPrompt = typeof transientContextPrompt === 'string' && transientContextPrompt.trim()
|
|
2283
|
+
? transientContextPrompt
|
|
2284
|
+
: null
|
|
2085
2285
|
|
|
2086
2286
|
// Fire and forget — events come through eventBus
|
|
2087
2287
|
session.agent.prompt(userMessage).catch((err) => {
|
|
@@ -2096,6 +2296,7 @@ export async function runPrompt(sessionId, message, selectedCapabilities = [], p
|
|
|
2096
2296
|
session.activeCommandPermissions = null
|
|
2097
2297
|
session.activeCommandPrompt = null
|
|
2098
2298
|
session.activeCapabilityPrompt = null
|
|
2299
|
+
session.activeTransientContextPrompt = null
|
|
2099
2300
|
})
|
|
2100
2301
|
|
|
2101
2302
|
return { sessionId, status: session.status }
|
|
@@ -2109,14 +2310,19 @@ export async function runPrompt(sessionId, message, selectedCapabilities = [], p
|
|
|
2109
2310
|
* Trims messages to keep up to and including the last user message,
|
|
2110
2311
|
* removing the assistant response that follows it.
|
|
2111
2312
|
*/
|
|
2112
|
-
export async function continueSession(sessionId) {
|
|
2313
|
+
export async function continueSession(sessionId, modelAccessContext = null) {
|
|
2113
2314
|
const session = agentSessions.get(sessionId)
|
|
2114
2315
|
if (!session) {
|
|
2115
2316
|
throw Object.assign(new Error('Session not found'), { statusCode: 404 })
|
|
2116
2317
|
}
|
|
2117
2318
|
if (session.agent.state.isStreaming) {
|
|
2118
|
-
throw Object.assign(new Error('Generation is still running. Stop it or wait until it finishes.'), {
|
|
2319
|
+
throw Object.assign(new Error('Generation is still running. Stop it or wait until it finishes.'), {
|
|
2320
|
+
statusCode: 409,
|
|
2321
|
+
errorCode: 'GENERATION_ALREADY_RUNNING',
|
|
2322
|
+
})
|
|
2119
2323
|
}
|
|
2324
|
+
if (modelAccessContext) session.modelAccessContext = modelAccessContext
|
|
2325
|
+
await refreshSessionModelBinding(session)
|
|
2120
2326
|
|
|
2121
2327
|
const messages = Array.isArray(session.agent.state.messages) ? session.agent.state.messages : []
|
|
2122
2328
|
|
|
@@ -2134,10 +2340,13 @@ export async function continueSession(sessionId) {
|
|
|
2134
2340
|
|
|
2135
2341
|
const lastUserMessage = messages[lastUserIndex]
|
|
2136
2342
|
const commandState = await resolveCommandState(session, lastUserMessage)
|
|
2137
|
-
const continuedUserMessage = commandState.userMessage ?? lastUserMessage
|
|
2343
|
+
const continuedUserMessage = prepareCloudUserMessage(session, commandState.userMessage ?? lastUserMessage)
|
|
2138
2344
|
const trimmedMessages = messages.slice(0, lastUserIndex).concat(continuedUserMessage)
|
|
2139
2345
|
updateSessionMessages(session, trimmedMessages)
|
|
2140
2346
|
resetSessionCompaction(session)
|
|
2347
|
+
if (isManagedCloudModel(session.model) && logicalMessageId(continuedUserMessage)) {
|
|
2348
|
+
await flushSessionPersist(session)
|
|
2349
|
+
}
|
|
2141
2350
|
|
|
2142
2351
|
resetIdleTimer(session)
|
|
2143
2352
|
session.activeCommandName = commandState.commandName ?? null
|
|
@@ -2181,7 +2390,19 @@ export async function abortRun(sessionId) {
|
|
|
2181
2390
|
}
|
|
2182
2391
|
|
|
2183
2392
|
session.agent.abort()
|
|
2184
|
-
|
|
2393
|
+
let idleWaitTimer
|
|
2394
|
+
const becameIdle = await Promise.race([
|
|
2395
|
+
session.agent.waitForIdle().then(() => true),
|
|
2396
|
+
new Promise((resolve) => {
|
|
2397
|
+
idleWaitTimer = setTimeout(() => resolve(false), ABORT_IDLE_WAIT_TIMEOUT_MS)
|
|
2398
|
+
idleWaitTimer.unref?.()
|
|
2399
|
+
}),
|
|
2400
|
+
])
|
|
2401
|
+
clearTimeout(idleWaitTimer)
|
|
2402
|
+
if (!becameIdle) {
|
|
2403
|
+
session.abortPending = true
|
|
2404
|
+
logger.warn(`Agent ${sessionId} did not become idle within ${ABORT_IDLE_WAIT_TIMEOUT_MS}ms after abort`, { sessionId })
|
|
2405
|
+
}
|
|
2185
2406
|
|
|
2186
2407
|
if (session.status === 'running') {
|
|
2187
2408
|
session.status = 'aborted'
|
|
@@ -2189,6 +2410,7 @@ export async function abortRun(sessionId) {
|
|
|
2189
2410
|
persistSession(session).catch((err) =>
|
|
2190
2411
|
logger.error(`Failed to persist aborted session ${sessionId}:`, err, { sessionId }),
|
|
2191
2412
|
)
|
|
2413
|
+
session.abortEndEmitted = true
|
|
2192
2414
|
const event = {
|
|
2193
2415
|
type: 'agent_end',
|
|
2194
2416
|
status: 'aborted',
|
|
@@ -2209,9 +2431,9 @@ export function steerAgent(sessionId, message) {
|
|
|
2209
2431
|
throw Object.assign(new Error('Session not found'), { statusCode: 404 })
|
|
2210
2432
|
}
|
|
2211
2433
|
|
|
2212
|
-
const agentMessage = typeof message === 'string'
|
|
2434
|
+
const agentMessage = prepareCloudUserMessage(session, typeof message === 'string'
|
|
2213
2435
|
? { role: 'user', content: message, timestamp: Date.now() }
|
|
2214
|
-
: message
|
|
2436
|
+
: message)
|
|
2215
2437
|
|
|
2216
2438
|
session.agent.steer(agentMessage)
|
|
2217
2439
|
return { sessionId, steered: true }
|
|
@@ -2226,9 +2448,9 @@ export function followUpAgent(sessionId, message) {
|
|
|
2226
2448
|
throw Object.assign(new Error('Session not found'), { statusCode: 404 })
|
|
2227
2449
|
}
|
|
2228
2450
|
|
|
2229
|
-
const agentMessage = typeof message === 'string'
|
|
2451
|
+
const agentMessage = prepareCloudUserMessage(session, typeof message === 'string'
|
|
2230
2452
|
? { role: 'user', content: message, timestamp: Date.now() }
|
|
2231
|
-
: message
|
|
2453
|
+
: message)
|
|
2232
2454
|
|
|
2233
2455
|
session.agent.followUp(agentMessage)
|
|
2234
2456
|
return { sessionId, followUp: true }
|
|
@@ -2243,6 +2465,62 @@ function getSessionContextUsage(session) {
|
|
|
2243
2465
|
}
|
|
2244
2466
|
}
|
|
2245
2467
|
|
|
2468
|
+
function storedMessagesExtendLocalHistory(localMessages, storedMessages) {
|
|
2469
|
+
if (!Array.isArray(localMessages) || !Array.isArray(storedMessages) || storedMessages.length <= localMessages.length) return false
|
|
2470
|
+
return localMessages.every((message, index) => {
|
|
2471
|
+
const local = { ...message }
|
|
2472
|
+
const stored = { ...storedMessages[index] }
|
|
2473
|
+
delete local.processFinishedAt
|
|
2474
|
+
delete stored.processFinishedAt
|
|
2475
|
+
return JSON.stringify(local) === JSON.stringify(stored)
|
|
2476
|
+
})
|
|
2477
|
+
}
|
|
2478
|
+
|
|
2479
|
+
export async function syncSessionFromStorage(sessionId) {
|
|
2480
|
+
let session = agentSessions.get(sessionId)
|
|
2481
|
+
if (!session) return restoreAgent(sessionId)
|
|
2482
|
+
if (session.agent.state.isStreaming) return session
|
|
2483
|
+
|
|
2484
|
+
try {
|
|
2485
|
+
const stored = await readSessionValue(sessionId)
|
|
2486
|
+
if (!stored || !Array.isArray(stored.messages)) return session
|
|
2487
|
+
|
|
2488
|
+
const localMessages = Array.isArray(session.agent.state.messages) ? session.agent.state.messages : []
|
|
2489
|
+
const storedStateVersion = Number.isFinite(stored.stateVersion) ? stored.stateVersion : 0
|
|
2490
|
+
const localStateVersion = Number.isFinite(session.stateVersion) ? session.stateVersion : 0
|
|
2491
|
+
const storedLastModified = Date.parse(stored.lastModified || '')
|
|
2492
|
+
const localLastModified = Date.parse(session.lastModified || '')
|
|
2493
|
+
const hasNewerVersion = storedStateVersion > localStateVersion
|
|
2494
|
+
const storedExtendsLocalHistory = storedMessagesExtendLocalHistory(localMessages, stored.messages)
|
|
2495
|
+
const hasMoreMessages = storedExtendsLocalHistory
|
|
2496
|
+
const hasNewerActivityAtSameVersion = storedStateVersion === localStateVersion
|
|
2497
|
+
&& stored.messages.length === localMessages.length
|
|
2498
|
+
&& Number.isFinite(storedLastModified)
|
|
2499
|
+
&& (!Number.isFinite(localLastModified) || storedLastModified > localLastModified)
|
|
2500
|
+
|
|
2501
|
+
if (!hasNewerVersion && !hasMoreMessages && !hasNewerActivityAtSameVersion) return session
|
|
2502
|
+
|
|
2503
|
+
session.agent.state.messages = stored.messages
|
|
2504
|
+
session.title = stored.title || session.title
|
|
2505
|
+
session.titleSource = stored.titleSource || session.titleSource
|
|
2506
|
+
session.source = stored.source || session.source
|
|
2507
|
+
session.channelId = stored.channelId || session.channelId
|
|
2508
|
+
session.channelName = stored.channelName || session.channelName
|
|
2509
|
+
session.lastModified = stored.lastModified || session.lastModified
|
|
2510
|
+
session.status = stored.taskStatus || 'idle'
|
|
2511
|
+
session.startedAt = stored.taskStartedAt || null
|
|
2512
|
+
session.finishedAt = stored.taskFinishedAt || null
|
|
2513
|
+
session.contextCompaction = stored.contextCompaction || null
|
|
2514
|
+
session.stateVersion = Math.max(localStateVersion, storedStateVersion)
|
|
2515
|
+
session.runtimeToolExecutions?.clear()
|
|
2516
|
+
resetIdleTimer(session)
|
|
2517
|
+
return session
|
|
2518
|
+
} catch (error) {
|
|
2519
|
+
logger.warn(`Failed to sync session ${sessionId} from storage:`, error?.message || error, { sessionId })
|
|
2520
|
+
return session
|
|
2521
|
+
}
|
|
2522
|
+
}
|
|
2523
|
+
|
|
2246
2524
|
/**
|
|
2247
2525
|
* Get the current state of a session (for page refresh recovery).
|
|
2248
2526
|
*/
|
|
@@ -2250,14 +2528,19 @@ export function getSessionState(sessionId) {
|
|
|
2250
2528
|
const session = agentSessions.get(sessionId)
|
|
2251
2529
|
if (!session) return null
|
|
2252
2530
|
|
|
2531
|
+
const messages = messagesWithRuntimeToolExecutions(session)
|
|
2253
2532
|
return {
|
|
2254
2533
|
sessionId: session.sessionId,
|
|
2255
2534
|
scope: session.scope,
|
|
2256
2535
|
projectId: session.projectId,
|
|
2536
|
+
source: session.source || undefined,
|
|
2537
|
+
channelId: session.channelId || undefined,
|
|
2538
|
+
channelName: session.channelName || undefined,
|
|
2257
2539
|
accessMode: session.accessMode,
|
|
2258
2540
|
yoloMode: session.yoloMode,
|
|
2259
2541
|
systemPrompt: session.agent.state.systemPrompt,
|
|
2260
2542
|
model: session.model,
|
|
2543
|
+
modelRef: session.modelRef || undefined,
|
|
2261
2544
|
thinkingLevel: session.thinkingLevel,
|
|
2262
2545
|
title: session.title,
|
|
2263
2546
|
titleSource: session.titleSource,
|
|
@@ -2268,12 +2551,13 @@ export function getSessionState(sessionId) {
|
|
|
2268
2551
|
startedAt: session.startedAt,
|
|
2269
2552
|
finishedAt: session.finishedAt,
|
|
2270
2553
|
tools: session.agent.state.tools,
|
|
2271
|
-
messages
|
|
2554
|
+
messages,
|
|
2555
|
+
pendingToolCalls: runtimePendingToolCalls(session),
|
|
2272
2556
|
contextCompaction: session.contextCompaction,
|
|
2273
2557
|
contextUsage: getSessionContextUsage(session),
|
|
2274
2558
|
pendingToolApproval: getPendingApprovalForSession(session.sessionId),
|
|
2275
2559
|
pendingAutoCompactApproval: getPendingAutoCompactApprovalForSession(session.sessionId),
|
|
2276
|
-
isStreaming: session.agent.state.isStreaming,
|
|
2560
|
+
isStreaming: session.abortPending ? false : session.agent.state.isStreaming,
|
|
2277
2561
|
errorMessage: session.agent.state.errorMessage,
|
|
2278
2562
|
}
|
|
2279
2563
|
}
|
|
@@ -2291,6 +2575,9 @@ export function getSessionStatus(sessionId) {
|
|
|
2291
2575
|
sessionId: session.sessionId,
|
|
2292
2576
|
scope: session.scope,
|
|
2293
2577
|
projectId: session.projectId,
|
|
2578
|
+
source: session.source || undefined,
|
|
2579
|
+
channelId: session.channelId || undefined,
|
|
2580
|
+
channelName: session.channelName || undefined,
|
|
2294
2581
|
title: session.title,
|
|
2295
2582
|
createdAt: session.createdAt,
|
|
2296
2583
|
lastModified: session.lastModified,
|
|
@@ -2298,7 +2585,7 @@ export function getSessionStatus(sessionId) {
|
|
|
2298
2585
|
status: session.status,
|
|
2299
2586
|
startedAt: session.startedAt,
|
|
2300
2587
|
finishedAt: session.finishedAt,
|
|
2301
|
-
isStreaming: session.agent.state.isStreaming,
|
|
2588
|
+
isStreaming: session.abortPending ? false : session.agent.state.isStreaming,
|
|
2302
2589
|
errorMessage: session.agent.state.errorMessage,
|
|
2303
2590
|
messageCount: messages.length,
|
|
2304
2591
|
lastMessageTimestamp: lastMessage?.timestamp ?? null,
|
|
@@ -2402,9 +2689,14 @@ export async function restoreAgent(sessionId) {
|
|
|
2402
2689
|
return await createAgent(sessionId, {
|
|
2403
2690
|
scope: sessionData.scope || 'global',
|
|
2404
2691
|
projectId: sessionData.projectId || null,
|
|
2692
|
+
source: sessionData.source || null,
|
|
2693
|
+
channelId: sessionData.channelId || null,
|
|
2694
|
+
channelName: sessionData.channelName || null,
|
|
2405
2695
|
accessMode: normalizeAccessMode(sessionData.accessMode, sessionData.yoloMode),
|
|
2406
2696
|
yoloMode: sessionData.yoloMode || false,
|
|
2407
2697
|
model: sessionData.model,
|
|
2698
|
+
modelRef: sessionData.modelRef || null,
|
|
2699
|
+
resolvePersistedModel: true,
|
|
2408
2700
|
thinkingLevel: sessionData.thinkingLevel || 'off',
|
|
2409
2701
|
messages: sessionData.messages || [],
|
|
2410
2702
|
title: sessionData.title || 'New chat',
|
|
@@ -2486,6 +2778,9 @@ export function listSessions() {
|
|
|
2486
2778
|
scope: session.scope,
|
|
2487
2779
|
status: session.status,
|
|
2488
2780
|
title: session.title,
|
|
2781
|
+
source: session.source || undefined,
|
|
2782
|
+
channelId: session.channelId || undefined,
|
|
2783
|
+
channelName: session.channelName || undefined,
|
|
2489
2784
|
idleRetention: session.idleRetention || undefined,
|
|
2490
2785
|
})
|
|
2491
2786
|
}
|
|
@@ -2557,7 +2852,7 @@ export async function updateSessionYoloMode(sessionId, yoloMode) {
|
|
|
2557
2852
|
* Does NOT force persistence — normal lifecycle events (message_end, agent_end) will persist
|
|
2558
2853
|
* the updated model.
|
|
2559
2854
|
*/
|
|
2560
|
-
export function updateSessionModel(sessionId, model) {
|
|
2855
|
+
export function updateSessionModel(sessionId, model, modelRef = null) {
|
|
2561
2856
|
const session = agentSessions.get(sessionId)
|
|
2562
2857
|
if (!session) {
|
|
2563
2858
|
throw Object.assign(new Error('Session not found'), { statusCode: 404 })
|
|
@@ -2567,9 +2862,11 @@ export function updateSessionModel(sessionId, model) {
|
|
|
2567
2862
|
}
|
|
2568
2863
|
|
|
2569
2864
|
session.model = model
|
|
2865
|
+
session.modelRef = modelRef
|
|
2866
|
+
session.resolvePersistedModel = true
|
|
2570
2867
|
session.agent.state.model = model
|
|
2571
2868
|
|
|
2572
|
-
return { sessionId, model }
|
|
2869
|
+
return { sessionId, model, modelRef: modelRef || undefined }
|
|
2573
2870
|
}
|
|
2574
2871
|
|
|
2575
2872
|
/**
|