@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.
Files changed (267) hide show
  1. package/README.md +1 -1
  2. package/bin/quickforge.mjs +0 -21
  3. package/dist/assets/AgentProfilesPage-XNjuqJs7.js +1 -0
  4. package/dist/assets/ChatPanelHost-CuF-PZpc.js +292 -0
  5. package/dist/assets/CloudAccountSettingsPage-GJSoi6sO.js +1 -0
  6. package/dist/assets/PluginsPage-BQgp0qN-.js +1 -0
  7. package/dist/assets/ScheduledTasksPage-DdekATzq.js +2 -0
  8. package/dist/assets/SettingsWorkspacePage-Cb54Ic91.js +1639 -0
  9. package/dist/assets/ShareLinksSettingsPage-C75WJpaF.js +1 -0
  10. package/dist/assets/SharedConversationPage-CjMtSQTW.js +1 -0
  11. package/dist/assets/TerminalDock-D4zJBJc4.js +2 -0
  12. package/dist/assets/WorkspaceInspector-D9kdnLDd.js +36 -0
  13. package/dist/assets/{abnfDiagram-VRR7QNED-Dl_Fvlqg.js → abnfDiagram-VRR7QNED-CMhVoYUh.js} +1 -1
  14. package/dist/assets/{arc-Bhg4c0ht.js → arc-w21O0TLn.js} +1 -1
  15. package/dist/assets/architecture-TIHT7OUA-DMwFMHxq.js +1 -0
  16. package/dist/assets/{architectureDiagram-ZJ3FMSHR-BuojN27B.js → architectureDiagram-ZJ3FMSHR-C52qL6Hg.js} +1 -1
  17. package/dist/assets/{blockDiagram-677ZJIJ3-alDLbrR1.js → blockDiagram-677ZJIJ3-CM3XbLol.js} +1 -1
  18. package/dist/assets/{c4Diagram-LMCZKHZV-D1lKa4VT.js → c4Diagram-LMCZKHZV-D7Bh1ui1.js} +1 -1
  19. package/dist/assets/channel-Duu1CuH9.js +1 -0
  20. package/dist/assets/chunk-2Q5K7J3B-B6rHWc4S.js +1 -0
  21. package/dist/assets/{chunk-32BRIVSS-CwWLNjMZ.js → chunk-32BRIVSS-xgrtbT8e.js} +1 -1
  22. package/dist/assets/{chunk-52WLFC77-Be569m7d.js → chunk-52WLFC77-CDbaHw80.js} +1 -1
  23. package/dist/assets/{chunk-5FCAYU7R-BSvoiWV1.js → chunk-5FCAYU7R-CcSvVbPc.js} +1 -1
  24. package/dist/assets/{chunk-5HE753X5-CRgsy8WS.js → chunk-5HE753X5-erFpzzgx.js} +1 -1
  25. package/dist/assets/{chunk-5JV3BV7I-CdW477UR.js → chunk-5JV3BV7I-rEhN9F6G.js} +1 -1
  26. package/dist/assets/{chunk-5TONJI2A-CdtKzSmp.js → chunk-5TONJI2A-D8L-QTwD.js} +1 -1
  27. package/dist/assets/{chunk-5VM5RSS4-BKjjssLZ.js → chunk-5VM5RSS4-CjlfMBbM.js} +1 -1
  28. package/dist/assets/{chunk-7BUUIJ7U-ByAXiXdn.js → chunk-7BUUIJ7U-CCcsI9XE.js} +1 -1
  29. package/dist/assets/{chunk-BIQX33UG-DeG8SCT1.js → chunk-BIQX33UG-FSqFbmb3.js} +1 -1
  30. package/dist/assets/{chunk-C7G6YPKG-D_zHT68Q.js → chunk-C7G6YPKG-ZMLUvfLy.js} +1 -1
  31. package/dist/assets/{chunk-CQNSW5MT-CHpHoUSu.js → chunk-CQNSW5MT-DZuBpk25.js} +1 -1
  32. package/dist/assets/{chunk-CYSBUYHQ-CRP50Vov.js → chunk-CYSBUYHQ-CB26wsG0.js} +1 -1
  33. package/dist/assets/{chunk-EMLP6XTP-DX6uQQ2v.js → chunk-EMLP6XTP-BbsM8oRw.js} +1 -1
  34. package/dist/assets/{chunk-EX3LRPZG-DAJRNkHR.js → chunk-EX3LRPZG-BcQhaqt7.js} +1 -1
  35. package/dist/assets/{chunk-FWX5IMBZ-XbM5yomC.js → chunk-FWX5IMBZ-DrnrdrSu.js} +2 -2
  36. package/dist/assets/{chunk-HOUHSVGY-Ci0ljnb1.js → chunk-HOUHSVGY-tdc-Z9eW.js} +1 -1
  37. package/dist/assets/{chunk-ICXQ74PX-Dy2WJ71z.js → chunk-ICXQ74PX-Cd7igVGt.js} +1 -1
  38. package/dist/assets/{chunk-JG7HCLWE-2PrkzHkt.js → chunk-JG7HCLWE-iXXGW8nf.js} +1 -1
  39. package/dist/assets/{chunk-JWPE2WC7-D32uacSM.js → chunk-JWPE2WC7-BZ87mXEv.js} +1 -1
  40. package/dist/assets/{chunk-MOJQB5TN-DIAFLtiS.js → chunk-MOJQB5TN-CLTjV5ov.js} +1 -1
  41. package/dist/assets/{chunk-MOZMSUNE-BbscOaEz.js → chunk-MOZMSUNE-B3SCBrRH.js} +1 -1
  42. package/dist/assets/{chunk-OGEWGWER-ivBVxzKe.js → chunk-OGEWGWER-TgvkXkFX.js} +1 -1
  43. package/dist/assets/{chunk-OSBZ3O6U-frD0hkdx.js → chunk-OSBZ3O6U-B9E2E5v1.js} +1 -1
  44. package/dist/assets/{chunk-PUDLZKDR-B-37Rxae.js → chunk-PUDLZKDR-C5GMVYup.js} +1 -1
  45. package/dist/assets/{chunk-Q4XR5HBZ-Y90m2RTe.js → chunk-Q4XR5HBZ-fo5-HsqY.js} +1 -1
  46. package/dist/assets/{chunk-QBLGF6JB-DixBqslB.js → chunk-QBLGF6JB-Dwfg8sKc.js} +1 -1
  47. package/dist/assets/{chunk-R7FJI6CG-DXS1UXn_.js → chunk-R7FJI6CG-DpBrv4ni.js} +1 -1
  48. package/dist/assets/{chunk-RYQCIY6F-n4JxhNd0.js → chunk-RYQCIY6F-DzzmTl11.js} +1 -1
  49. package/dist/assets/{chunk-U6XO7XAA-yHlgxMmn.js → chunk-U6XO7XAA-Dw6aviKa.js} +1 -1
  50. package/dist/assets/{chunk-V7JOEXUC-Z6kuZfbl.js → chunk-V7JOEXUC-CEDazV6w.js} +1 -1
  51. package/dist/assets/chunk-VAUOI2AC-CWYjg8zE.js +1 -0
  52. package/dist/assets/{chunk-VR4S4FIN-Cs_SIQma.js → chunk-VR4S4FIN-C_-eEj-9.js} +1 -1
  53. package/dist/assets/{chunk-WYO6CB5R-CGExYbV-.js → chunk-WYO6CB5R-NF-MsJJR.js} +3 -3
  54. package/dist/assets/chunk-XXDRQBXY-DzTwx8ZH.js +1 -0
  55. package/dist/assets/{chunk-YOTPTUD7-DopPXWSl.js → chunk-YOTPTUD7-DqzqtdII.js} +1 -1
  56. package/dist/assets/{chunk-ZGVPDNZ5-CJHOeqMb.js → chunk-ZGVPDNZ5-CRrAYi4g.js} +1 -1
  57. package/dist/assets/{chunk-ZIRB5QZD-BBVc5mf6.js → chunk-ZIRB5QZD-Dc1s5bLM.js} +1 -1
  58. package/dist/assets/classDiagram-OUVF2IWQ-Cc63fLWP.js +1 -0
  59. package/dist/assets/classDiagram-v2-EOCWNBFH-CoXU0qT6.js +1 -0
  60. package/dist/assets/{cose-bilkent-JH36ORCC-DLjV-dTm.js → cose-bilkent-JH36ORCC-cL247pHg.js} +1 -1
  61. package/dist/assets/cynefin-VYW2F7L2-C8yCvH_o.js +1 -0
  62. package/dist/assets/{cynefinDiagram-TSTJHNR4-BfH3zVS-.js → cynefinDiagram-TSTJHNR4-D4sZ7LYI.js} +1 -1
  63. package/dist/assets/{dagre-CY3D84Cl.js → dagre-DmHb3Ctt.js} +1 -1
  64. package/dist/assets/{dagre-VKFMJZFB-BIhwZCIZ.js → dagre-VKFMJZFB-BlsnSh-Q.js} +1 -1
  65. package/dist/assets/{diagram-FQU43EPY-BHnzNM2w.js → diagram-FQU43EPY-DW1yQiRO.js} +1 -1
  66. package/dist/assets/{diagram-G47NLZAW-C7-ZlmFT.js → diagram-G47NLZAW-BTUAsFjb.js} +1 -1
  67. package/dist/assets/{diagram-NH7WQ7WH-C-P9Y7IR.js → diagram-NH7WQ7WH-BcaomqSd.js} +1 -1
  68. package/dist/assets/{diagram-OA4YK3LP-Ci5lZ_T5.js → diagram-OA4YK3LP-DDLX7Xwr.js} +1 -1
  69. package/dist/assets/{diagram-WEI45ONY-C1xuK2bs.js → diagram-WEI45ONY-CIave0An.js} +1 -1
  70. package/dist/assets/dist-DLKC_odd.js +1 -0
  71. package/dist/assets/{ebnfDiagram-CCIWWBDH-DKQ5NWMD.js → ebnfDiagram-CCIWWBDH-Cr1Y6lPu.js} +1 -1
  72. package/dist/assets/{erDiagram-Q63AITRT-HAWySadP.js → erDiagram-Q63AITRT-D3EgiVVH.js} +1 -1
  73. package/dist/assets/esm-Df4edDpj.js +2 -0
  74. package/dist/assets/eventmodeling-45OFAUF4-DO1vRTM0.js +1 -0
  75. package/dist/assets/flowDiagram-23GEKE2U-BS7KXcqn.js +1 -0
  76. package/dist/assets/{ganttDiagram-NO4QXBWP-C1hwIcOA.js → ganttDiagram-NO4QXBWP-B0oyDIqK.js} +1 -1
  77. package/dist/assets/gitGraph-TEB2WS4Q-m5F5ef09.js +1 -0
  78. package/dist/assets/{gitGraphDiagram-IHSO6WYX-DYUYxs1R.js → gitGraphDiagram-IHSO6WYX-uQtr6IqK.js} +1 -1
  79. package/dist/assets/icons-CXPP1fFT.js +1 -0
  80. package/dist/assets/index-_ZhK6bvp.js +65 -0
  81. package/dist/assets/index-iVV2bP_f.css +3 -0
  82. package/dist/assets/info-DKCQHKI2-CZxPlQJ0.js +1 -0
  83. package/dist/assets/{infoDiagram-FWYZ7A6U-CnIL6tv4.js → infoDiagram-FWYZ7A6U-B_GzlLIM.js} +1 -1
  84. package/dist/assets/{ishikawaDiagram-FXEZZL3T-B7WXNycQ.js → ishikawaDiagram-FXEZZL3T-CKQIsSdU.js} +1 -1
  85. package/dist/assets/{journeyDiagram-5HDEW3XC-D4HvioyY.js → journeyDiagram-5HDEW3XC-DrrvzZL1.js} +1 -1
  86. package/dist/assets/{kanban-definition-HUTT4EX6-CDs763eS.js → kanban-definition-HUTT4EX6-CJQRTdXz.js} +1 -1
  87. package/dist/assets/{line-ikKkQiFp.js → line-B4DxX7tC.js} +1 -1
  88. package/dist/assets/{linear-GqPyxQkN.js → linear-U-nKUhyW.js} +1 -1
  89. package/dist/assets/{map-CJwjCvwr.js → map-BneTM-ri.js} +1 -1
  90. package/dist/assets/{mcp-servers-dialog-BQbFywVL.js → mcp-servers-dialog-BUSPvaqI.js} +2 -2
  91. package/dist/assets/{mermaid-parser.core-CoE8cccr.js → mermaid-parser.core-BlKWrEdZ.js} +2 -2
  92. package/dist/assets/{mermaid-renderer-BmbSAmpG.js → mermaid-renderer-jEbqFaTe.js} +2 -2
  93. package/dist/assets/{mermaid.core-DlFSL90j.js → mermaid.core-DWpShXCs.js} +3 -3
  94. package/dist/assets/{mindmap-definition-LN4V7U3C-DLdq0K9C.js → mindmap-definition-LN4V7U3C-CEVwZD78.js} +1 -1
  95. package/dist/assets/model-identity-CgA6_Pbm.js +1 -0
  96. package/dist/assets/model-reference-o41fJ6jQ.js +1 -0
  97. package/dist/assets/{monaco-BTsVCDWS.js → monaco-D6OhXgjg.js} +1 -1
  98. package/dist/assets/{ordinal-CibZU3PM.js → ordinal-B2r5L1v4.js} +1 -1
  99. package/dist/assets/{otel-BNGM50C-.js → otel-BFnphzjt.js} +1 -1
  100. package/dist/assets/packet-7NZHBO7P-zIioIZVN.js +1 -0
  101. package/dist/assets/{pegDiagram-2B236MQR-Cm1kPRC2.js → pegDiagram-2B236MQR-CBLkBdnQ.js} +1 -1
  102. package/dist/assets/{pi-ai-Cr3rpCXT.js → pi-ai-Cr2ee4a0.js} +2 -2
  103. package/dist/assets/{pi-web-ui-DoRGtREc.js → pi-web-ui-DV5jKjSg.js} +1 -1
  104. package/dist/assets/pie-RZYD4A2V-GGWijBvB.js +1 -0
  105. package/dist/assets/{pieDiagram-ENE6RG2P-Df05wjjo.js → pieDiagram-ENE6RG2P-8tCnwtWS.js} +1 -1
  106. package/dist/assets/{quadrantDiagram-ABIIQ3AL-BjCwGxJC.js → quadrantDiagram-ABIIQ3AL-CWd28cHF.js} +1 -1
  107. package/dist/assets/radar-I7S5WNFK-yvwFH3RV.js +1 -0
  108. package/dist/assets/railroad-3IZDKUUU-BVadlswB.js +1 -0
  109. package/dist/assets/railroad-abnf-AHOZXSZD-B7R-p_2-.js +1 -0
  110. package/dist/assets/railroad-ebnf-EBAXGLYW-BnbJxPug.js +1 -0
  111. package/dist/assets/railroad-peg-LSFZ7HO6-Ci3DcEuQ.js +1 -0
  112. package/dist/assets/{railroadDiagram-RFXS5EU6-CTUvpKkv.js → railroadDiagram-RFXS5EU6-CI4ouOBs.js} +1 -1
  113. package/dist/assets/{react-vendor-Dr5xvL-e.js → react-vendor-1KC5PyRx.js} +1 -1
  114. package/dist/assets/{requirementDiagram-TGXJPOKE-BU1kvEku.js → requirementDiagram-TGXJPOKE-DtFz7sw4.js} +1 -1
  115. package/dist/assets/{sankeyDiagram-HTMAVEWB-BHmvxVqH.js → sankeyDiagram-HTMAVEWB--VeOwwk9.js} +1 -1
  116. package/dist/assets/{sequenceDiagram-DBY2YBRQ-CTpwICgg.js → sequenceDiagram-DBY2YBRQ-B8AHk5Oe.js} +1 -1
  117. package/dist/assets/{sizeCapture-X5ZJPWSS-BbLhGaa4.js → sizeCapture-X5ZJPWSS-vV_ArM8D.js} +1 -1
  118. package/dist/assets/{skills-dialog-REBeTFSH.js → skills-dialog-BThnIUBs.js} +3 -1
  119. package/dist/assets/{src-B4Tvg3gT.js → src-PYsDme0X.js} +1 -1
  120. package/dist/assets/{stateDiagram-2N3HPSRC-BxuDwIgT.js → stateDiagram-2N3HPSRC-etXnJ8X8.js} +1 -1
  121. package/dist/assets/stateDiagram-v2-6OUMAXLB-Dms3pICj.js +1 -0
  122. package/dist/assets/{swimlanes-5IMT3BWC-EkZsiylV.js → swimlanes-5IMT3BWC-pabIKAmn.js} +2 -2
  123. package/dist/assets/swimlanesDiagram-G3AALYLV-B14aWjv6.js +8 -0
  124. package/dist/assets/{timeline-definition-FHXFAJF6-C9ewOCTa.js → timeline-definition-FHXFAJF6-C55MKm3L.js} +1 -1
  125. package/dist/assets/treeView-QDETBFTQ-Cru9B2zv.js +1 -0
  126. package/dist/assets/treemap-6X3UGDF4-DNOVHFaJ.js +1 -0
  127. package/dist/assets/{vennDiagram-L72KCM5P-CR49Y4Lp.js → vennDiagram-L72KCM5P-CgNf1Q9W.js} +1 -1
  128. package/dist/assets/wardley-OPB4EBWU-E3m1BOJQ.js +1 -0
  129. package/dist/assets/{wardleyDiagram-EHGQE667-Cn0GgwWi.js → wardleyDiagram-EHGQE667-Drt5ytec.js} +1 -1
  130. package/dist/assets/web-DoV8MSMn.js +1 -0
  131. package/dist/assets/{xychartDiagram-FW5EYKEG-DlmPNAvd.js → xychartDiagram-FW5EYKEG-hhoEo_Ar.js} +1 -1
  132. package/dist/index.html +13 -11
  133. package/dist/mobile-error.html +24 -0
  134. package/package.json +6 -1
  135. package/plugins/{openai-documents → documents}/plugin.json +2 -2
  136. package/plugins/{openai-presentations → presentations}/plugin.json +2 -2
  137. package/plugins/{openai-spreadsheets → spreadsheets}/plugin.json +2 -2
  138. package/server/acp/server.mjs +50 -35
  139. package/server/agent-manager.mjs +343 -46
  140. package/server/agent-profile-files.mjs +8 -4
  141. package/server/agent-profile-schema.mjs +47 -23
  142. package/server/agent-profiles.mjs +1 -1
  143. package/server/ai-http-logger.mjs +156 -13
  144. package/server/ai-provider-options.mjs +3 -0
  145. package/server/channels/channel-logs.mjs +13 -0
  146. package/server/channels/event-relay.mjs +33 -0
  147. package/server/channels/process-channel.mjs +29 -0
  148. package/server/channels/providers/wechat.mjs +9 -3
  149. package/server/channels/registry.mjs +5 -1
  150. package/server/cloud/chat-idempotency-store.mjs +96 -0
  151. package/server/cloud/client.mjs +102 -0
  152. package/server/cloud/config.mjs +53 -0
  153. package/server/cloud/credential-store.mjs +193 -0
  154. package/server/cloud/identity.mjs +355 -0
  155. package/server/cloud/index.mjs +7 -0
  156. package/server/cloud/models.mjs +80 -0
  157. package/server/cloud/runtime.mjs +129 -0
  158. package/server/cloud/service-config.mjs +77 -0
  159. package/server/index.mjs +63 -33
  160. package/server/lan-access-store.mjs +93 -3
  161. package/server/model-catalog.mjs +262 -0
  162. package/server/network-proxy.mjs +68 -3
  163. package/server/plugins/registry.mjs +93 -9
  164. package/server/public-api.mjs +6 -2
  165. package/server/routes/agent-profiles.mjs +43 -9
  166. package/server/routes/agent.mjs +48 -11
  167. package/server/routes/backup.mjs +7 -1
  168. package/server/routes/channels.mjs +47 -0
  169. package/server/routes/cloud.mjs +227 -0
  170. package/server/routes/lan-access.mjs +246 -21
  171. package/server/routes/models.mjs +13 -4
  172. package/server/routes/project.mjs +19 -1
  173. package/server/routes/scheduled-tasks.mjs +40 -10
  174. package/server/routes/shared-conversation.mjs +62 -58
  175. package/server/routes/shares.mjs +76 -1
  176. package/server/routes/static.mjs +26 -1
  177. package/server/routes/storage.mjs +24 -1
  178. package/server/routes/system.mjs +11 -0
  179. package/server/routes/terminal.mjs +7 -2
  180. package/server/routes/workspace.mjs +66 -13
  181. package/server/session-utils.mjs +2 -2
  182. package/server/share-store.mjs +168 -13
  183. package/server/skills.mjs +30 -3
  184. package/server/storage.mjs +7 -1
  185. package/server/subagents.mjs +4 -4
  186. package/server/terminal/terminal-manager.mjs +2 -4
  187. package/server/tools/definitions.mjs +3 -3
  188. package/server/tools/index.mjs +7 -5
  189. package/server/utils/network.mjs +9 -0
  190. package/server/utils/package-update.mjs +70 -47
  191. package/server/utils/process-env.mjs +7 -0
  192. package/server/utils/response.mjs +10 -3
  193. package/skills/skill-creator/LICENSE.txt +202 -0
  194. package/skills/skill-creator/SKILL.md +485 -0
  195. package/skills/skill-creator/UPSTREAM.md +6 -0
  196. package/skills/skill-creator/agents/analyzer.md +274 -0
  197. package/skills/skill-creator/agents/comparator.md +202 -0
  198. package/skills/skill-creator/agents/grader.md +223 -0
  199. package/skills/skill-creator/assets/eval_review.html +146 -0
  200. package/skills/skill-creator/eval-viewer/generate_review.py +471 -0
  201. package/skills/skill-creator/eval-viewer/viewer.html +1325 -0
  202. package/skills/skill-creator/references/schemas.md +430 -0
  203. package/skills/skill-creator/scripts/__init__.py +0 -0
  204. package/skills/skill-creator/scripts/aggregate_benchmark.py +401 -0
  205. package/skills/skill-creator/scripts/generate_report.py +326 -0
  206. package/skills/skill-creator/scripts/improve_description.py +247 -0
  207. package/skills/skill-creator/scripts/package_skill.py +136 -0
  208. package/skills/skill-creator/scripts/quick_validate.py +103 -0
  209. package/skills/skill-creator/scripts/run_eval.py +310 -0
  210. package/skills/skill-creator/scripts/run_loop.py +328 -0
  211. package/skills/skill-creator/scripts/utils.py +47 -0
  212. package/dist/assets/AgentProfilesPage-D7pbg8nm.js +0 -1
  213. package/dist/assets/ChatPanelHost-wfGCaCzD.js +0 -288
  214. package/dist/assets/PluginsPage-NAGroBm3.js +0 -1
  215. package/dist/assets/ScheduledTasksPage-Bp0MNfQD.js +0 -2
  216. package/dist/assets/SettingsWorkspacePage-BtJZMIsZ.js +0 -1576
  217. package/dist/assets/SharedConversationPage-Ce3KoVZt.js +0 -1
  218. package/dist/assets/TerminalDock-4eUuXP7X.js +0 -2
  219. package/dist/assets/WorkspaceInspector-kM3BRPjY.js +0 -13
  220. package/dist/assets/architecture-TIHT7OUA-BgP-IMeo.js +0 -1
  221. package/dist/assets/channel-Rz20dKTK.js +0 -1
  222. package/dist/assets/chunk-2Q5K7J3B-Cb14ZF_k.js +0 -1
  223. package/dist/assets/chunk-VAUOI2AC-BOZvd2Sn.js +0 -1
  224. package/dist/assets/chunk-XXDRQBXY-CD_EGCtT.js +0 -1
  225. package/dist/assets/classDiagram-OUVF2IWQ-C_ozTKCt.js +0 -1
  226. package/dist/assets/classDiagram-v2-EOCWNBFH-FOfFbbZF.js +0 -1
  227. package/dist/assets/cynefin-VYW2F7L2-BzPhLtd5.js +0 -1
  228. package/dist/assets/eventmodeling-45OFAUF4-BcgGbVJ-.js +0 -1
  229. package/dist/assets/flowDiagram-23GEKE2U-dtkzcC8E.js +0 -1
  230. package/dist/assets/gitGraph-TEB2WS4Q-VByWboBU.js +0 -1
  231. package/dist/assets/icons-BP8YOS-Z.js +0 -1
  232. package/dist/assets/index-C6k5taeb.js +0 -66
  233. package/dist/assets/index-CzN8NSKC.css +0 -3
  234. package/dist/assets/info-DKCQHKI2-CZeDLZxx.js +0 -1
  235. package/dist/assets/packet-7NZHBO7P-decrrxyX.js +0 -1
  236. package/dist/assets/pie-RZYD4A2V-J1CasQZl.js +0 -1
  237. package/dist/assets/radar-I7S5WNFK-B-sZytUA.js +0 -1
  238. package/dist/assets/railroad-3IZDKUUU-BSaqNPm8.js +0 -1
  239. package/dist/assets/railroad-abnf-AHOZXSZD-lzZR7PxT.js +0 -1
  240. package/dist/assets/railroad-ebnf-EBAXGLYW-DQtz1Fh9.js +0 -1
  241. package/dist/assets/railroad-peg-LSFZ7HO6-CpMsuGY3.js +0 -1
  242. package/dist/assets/stateDiagram-v2-6OUMAXLB-BGLshxcY.js +0 -1
  243. package/dist/assets/swimlanesDiagram-G3AALYLV-CqdqxA-b.js +0 -8
  244. package/dist/assets/treeView-QDETBFTQ-Byb71heW.js +0 -1
  245. package/dist/assets/treemap-6X3UGDF4-CMV1SJsq.js +0 -1
  246. package/dist/assets/wardley-OPB4EBWU-DtCHtr2V.js +0 -1
  247. /package/dist/assets/{array-CYkMkqnU.js → array-B9UHiPd-.js} +0 -0
  248. /package/dist/assets/{chunk-KEIR6QF5-77hsebd7.js → chunk-KEIR6QF5-jrz1pRUS.js} +0 -0
  249. /package/dist/assets/{chunk-Y2CYZVJY-CQltPwYk.js → chunk-Y2CYZVJY-D6N-CrJv.js} +0 -0
  250. /package/dist/assets/{css-utils-rkE68RDy.js → css-utils-31qzCXsk.js} +0 -0
  251. /package/dist/assets/{cytoscape.esm-DwdZnGOm.js → cytoscape.esm-DyfqweF2.js} +0 -0
  252. /package/dist/assets/{defaultLocale-Beh6XjaL.js → defaultLocale-5eAKkKJC.js} +0 -0
  253. /package/dist/assets/{dist-DelZBZpK.js → dist-CLg13NtV.js} +0 -0
  254. /package/dist/assets/{graphlib-CAISHZh4.js → graphlib-DJkmx0Cq.js} +0 -0
  255. /package/dist/assets/{init-Rr1s_RiX.js → init-DlZdxViB.js} +0 -0
  256. /package/dist/assets/{lit-vendor-Dr3cpBGF.js → lit-vendor-DvaQX_UQ.js} +0 -0
  257. /package/dist/assets/{logger-B65Akg8A.js → logger-8REuLPGK.js} +0 -0
  258. /package/dist/assets/{path-BAQ3hXlG.js → path-6uRLdFF7.js} +0 -0
  259. /package/dist/assets/{plugin-api-BbUkYm3Y.js → plugin-api-Uu2xAB4d.js} +0 -0
  260. /package/dist/assets/{rough.esm-Djo4Abte.js → rough.esm-Bud4g4fh.js} +0 -0
  261. /package/dist/assets/{xterm-CGiQzDiR.js → xterm-Bm_IbozX.js} +0 -0
  262. /package/plugins/{openai-documents → documents}/commands/document.md +0 -0
  263. /package/plugins/{openai-documents → documents}/skills/documents/SKILL.md +0 -0
  264. /package/plugins/{openai-presentations → presentations}/commands/presentation.md +0 -0
  265. /package/plugins/{openai-presentations → presentations}/skills/presentations/SKILL.md +0 -0
  266. /package/plugins/{openai-spreadsheets → spreadsheets}/commands/spreadsheet.md +0 -0
  267. /package/plugins/{openai-spreadsheets → spreadsheets}/skills/spreadsheets/SKILL.md +0 -0
@@ -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 SUBAGENT_DEFAULT_TIMEOUT_MS = 30 * 60 * 1000
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), 30 * 60 * 1000))}
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), 30 * 60 * 1000))
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(definition, parentSession.model, readStore)
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?.activeCapabilityPrompt,
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
- if (raw) resolvedModel = typeof raw === 'string' ? JSON.parse(raw) : raw
1608
+ resolvedModel = await resolveImplicitModelPreference(raw, modelAccessContext || {})
1488
1609
  } catch {
1489
1610
  // ignore
1490
1611
  }
1491
1612
  }
1492
1613
  if (agentProfile?.model?.mode === 'fixed') {
1493
- resolvedModel = (await resolveAgentProfileModel(agentProfile, resolvedModel, readStore)).model
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
- // Forward all events to the session event bus and the global bus.
1672
- emitSessionEvent(session, forwardEvent)
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 isInitialUserMessage = (event.message?.role === 'user' || event.message?.role === 'user-with-attachments')
1703
- && session.agent.state.messages.length === 1
1704
- if (isInitialUserMessage) {
1705
- // Persist the first user message before announcing the session or
1706
- // starting the asynchronous AI title request.
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 initial user message for session ${sessionId}:`, err, { sessionId })
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
- data[sessionId] = {
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
- return metadata
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.'), { statusCode: 409 })
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 = agentSessions.get(sessionId)
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.'), { statusCode: 409 })
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 userMessage = commandState.userMessage ?? initialUserMessage
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.'), { statusCode: 409 })
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
- await session.agent.waitForIdle()
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: session.agent.state.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
  /**