@shawnstack/quickforge 1.7.4 → 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 (239) hide show
  1. package/dist/assets/AgentProfilesPage-XNjuqJs7.js +1 -0
  2. package/dist/assets/ChatPanelHost-CuF-PZpc.js +292 -0
  3. package/dist/assets/CloudAccountSettingsPage-GJSoi6sO.js +1 -0
  4. package/dist/assets/PluginsPage-BQgp0qN-.js +1 -0
  5. package/dist/assets/ScheduledTasksPage-DdekATzq.js +2 -0
  6. package/dist/assets/{SettingsWorkspacePage-B53_eDf2.js → SettingsWorkspacePage-Cb54Ic91.js} +389 -378
  7. package/dist/assets/ShareLinksSettingsPage-C75WJpaF.js +1 -0
  8. package/dist/assets/SharedConversationPage-CjMtSQTW.js +1 -0
  9. package/dist/assets/{TerminalDock-Dgg-_ZmU.js → TerminalDock-D4zJBJc4.js} +2 -2
  10. package/dist/assets/WorkspaceInspector-D9kdnLDd.js +36 -0
  11. package/dist/assets/{abnfDiagram-VRR7QNED-Dy7veffk.js → abnfDiagram-VRR7QNED-CMhVoYUh.js} +1 -1
  12. package/dist/assets/{arc-Bo3p2lGy.js → arc-w21O0TLn.js} +1 -1
  13. package/dist/assets/architecture-TIHT7OUA-DMwFMHxq.js +1 -0
  14. package/dist/assets/{architectureDiagram-ZJ3FMSHR-Cwx8qOvu.js → architectureDiagram-ZJ3FMSHR-C52qL6Hg.js} +1 -1
  15. package/dist/assets/{blockDiagram-677ZJIJ3-BiuaAOFa.js → blockDiagram-677ZJIJ3-CM3XbLol.js} +1 -1
  16. package/dist/assets/{c4Diagram-LMCZKHZV-BTcGfhK2.js → c4Diagram-LMCZKHZV-D7Bh1ui1.js} +1 -1
  17. package/dist/assets/channel-Duu1CuH9.js +1 -0
  18. package/dist/assets/chunk-2Q5K7J3B-B6rHWc4S.js +1 -0
  19. package/dist/assets/{chunk-32BRIVSS-B9PuF_RF.js → chunk-32BRIVSS-xgrtbT8e.js} +1 -1
  20. package/dist/assets/{chunk-52WLFC77-3zGItfIM.js → chunk-52WLFC77-CDbaHw80.js} +1 -1
  21. package/dist/assets/{chunk-5FCAYU7R-DQ66qlmt.js → chunk-5FCAYU7R-CcSvVbPc.js} +1 -1
  22. package/dist/assets/{chunk-5HE753X5-CLqTO-VF.js → chunk-5HE753X5-erFpzzgx.js} +1 -1
  23. package/dist/assets/{chunk-5JV3BV7I-Csxv1jr6.js → chunk-5JV3BV7I-rEhN9F6G.js} +1 -1
  24. package/dist/assets/{chunk-5TONJI2A-BCQttpBr.js → chunk-5TONJI2A-D8L-QTwD.js} +1 -1
  25. package/dist/assets/{chunk-5VM5RSS4-DEWJGSTb.js → chunk-5VM5RSS4-CjlfMBbM.js} +1 -1
  26. package/dist/assets/{chunk-7BUUIJ7U-DNorm9MM.js → chunk-7BUUIJ7U-CCcsI9XE.js} +1 -1
  27. package/dist/assets/{chunk-BIQX33UG-DgvY_BOT.js → chunk-BIQX33UG-FSqFbmb3.js} +1 -1
  28. package/dist/assets/{chunk-C7G6YPKG-DMvn9IPs.js → chunk-C7G6YPKG-ZMLUvfLy.js} +1 -1
  29. package/dist/assets/{chunk-CQNSW5MT-Dq2duUgr.js → chunk-CQNSW5MT-DZuBpk25.js} +1 -1
  30. package/dist/assets/{chunk-CYSBUYHQ-BovYyc4Y.js → chunk-CYSBUYHQ-CB26wsG0.js} +1 -1
  31. package/dist/assets/{chunk-EMLP6XTP-BMDOpuYN.js → chunk-EMLP6XTP-BbsM8oRw.js} +1 -1
  32. package/dist/assets/{chunk-EX3LRPZG-6eP2UPOf.js → chunk-EX3LRPZG-BcQhaqt7.js} +1 -1
  33. package/dist/assets/{chunk-FWX5IMBZ-D4q-wTSy.js → chunk-FWX5IMBZ-DrnrdrSu.js} +2 -2
  34. package/dist/assets/{chunk-HOUHSVGY-XjIcuzH7.js → chunk-HOUHSVGY-tdc-Z9eW.js} +1 -1
  35. package/dist/assets/{chunk-ICXQ74PX-YBXbanq9.js → chunk-ICXQ74PX-Cd7igVGt.js} +1 -1
  36. package/dist/assets/{chunk-JG7HCLWE-CcN3uNXF.js → chunk-JG7HCLWE-iXXGW8nf.js} +1 -1
  37. package/dist/assets/{chunk-JWPE2WC7-Cwg6AUAt.js → chunk-JWPE2WC7-BZ87mXEv.js} +1 -1
  38. package/dist/assets/{chunk-MOJQB5TN-NNspcGIU.js → chunk-MOJQB5TN-CLTjV5ov.js} +1 -1
  39. package/dist/assets/{chunk-MOZMSUNE-pMJ_jMos.js → chunk-MOZMSUNE-B3SCBrRH.js} +1 -1
  40. package/dist/assets/{chunk-OGEWGWER-CJy-3Ldv.js → chunk-OGEWGWER-TgvkXkFX.js} +1 -1
  41. package/dist/assets/{chunk-OSBZ3O6U-D7cLHHOA.js → chunk-OSBZ3O6U-B9E2E5v1.js} +1 -1
  42. package/dist/assets/{chunk-PUDLZKDR-D5YBi1Pi.js → chunk-PUDLZKDR-C5GMVYup.js} +1 -1
  43. package/dist/assets/{chunk-Q4XR5HBZ-DYmqQdIf.js → chunk-Q4XR5HBZ-fo5-HsqY.js} +1 -1
  44. package/dist/assets/{chunk-QBLGF6JB-Bfcx00X0.js → chunk-QBLGF6JB-Dwfg8sKc.js} +1 -1
  45. package/dist/assets/{chunk-R7FJI6CG-oXzadpZY.js → chunk-R7FJI6CG-DpBrv4ni.js} +1 -1
  46. package/dist/assets/{chunk-RYQCIY6F-Dt3-8yUh.js → chunk-RYQCIY6F-DzzmTl11.js} +1 -1
  47. package/dist/assets/{chunk-U6XO7XAA-B55eSvpx.js → chunk-U6XO7XAA-Dw6aviKa.js} +1 -1
  48. package/dist/assets/{chunk-V7JOEXUC-Btk4JwCt.js → chunk-V7JOEXUC-CEDazV6w.js} +1 -1
  49. package/dist/assets/chunk-VAUOI2AC-CWYjg8zE.js +1 -0
  50. package/dist/assets/{chunk-VR4S4FIN-DgaSiVOY.js → chunk-VR4S4FIN-C_-eEj-9.js} +1 -1
  51. package/dist/assets/{chunk-WYO6CB5R-rRDZ_Tta.js → chunk-WYO6CB5R-NF-MsJJR.js} +3 -3
  52. package/dist/assets/chunk-XXDRQBXY-DzTwx8ZH.js +1 -0
  53. package/dist/assets/{chunk-YOTPTUD7-BnFxj5o4.js → chunk-YOTPTUD7-DqzqtdII.js} +1 -1
  54. package/dist/assets/{chunk-ZGVPDNZ5-CrnlPlmJ.js → chunk-ZGVPDNZ5-CRrAYi4g.js} +1 -1
  55. package/dist/assets/{chunk-ZIRB5QZD-BYTiATIH.js → chunk-ZIRB5QZD-Dc1s5bLM.js} +1 -1
  56. package/dist/assets/classDiagram-OUVF2IWQ-Cc63fLWP.js +1 -0
  57. package/dist/assets/classDiagram-v2-EOCWNBFH-CoXU0qT6.js +1 -0
  58. package/dist/assets/{cose-bilkent-JH36ORCC-BORKJRwF.js → cose-bilkent-JH36ORCC-cL247pHg.js} +1 -1
  59. package/dist/assets/cynefin-VYW2F7L2-C8yCvH_o.js +1 -0
  60. package/dist/assets/{cynefinDiagram-TSTJHNR4-BXKeaSl8.js → cynefinDiagram-TSTJHNR4-D4sZ7LYI.js} +1 -1
  61. package/dist/assets/{dagre-CrA0XVwJ.js → dagre-DmHb3Ctt.js} +1 -1
  62. package/dist/assets/{dagre-VKFMJZFB-ouq_lA1a.js → dagre-VKFMJZFB-BlsnSh-Q.js} +1 -1
  63. package/dist/assets/{diagram-FQU43EPY-BxR5kUiQ.js → diagram-FQU43EPY-DW1yQiRO.js} +1 -1
  64. package/dist/assets/{diagram-G47NLZAW-DWonk1sL.js → diagram-G47NLZAW-BTUAsFjb.js} +1 -1
  65. package/dist/assets/{diagram-NH7WQ7WH-fmV_wV5e.js → diagram-NH7WQ7WH-BcaomqSd.js} +1 -1
  66. package/dist/assets/{diagram-OA4YK3LP-DvOSRRiV.js → diagram-OA4YK3LP-DDLX7Xwr.js} +1 -1
  67. package/dist/assets/{diagram-WEI45ONY-Bp-xb3uS.js → diagram-WEI45ONY-CIave0An.js} +1 -1
  68. package/dist/assets/{ebnfDiagram-CCIWWBDH-BTlnwneA.js → ebnfDiagram-CCIWWBDH-Cr1Y6lPu.js} +1 -1
  69. package/dist/assets/{erDiagram-Q63AITRT-C3fFWGCm.js → erDiagram-Q63AITRT-D3EgiVVH.js} +1 -1
  70. package/dist/assets/{esm-oa69b5u4.js → esm-Df4edDpj.js} +2 -2
  71. package/dist/assets/eventmodeling-45OFAUF4-DO1vRTM0.js +1 -0
  72. package/dist/assets/flowDiagram-23GEKE2U-BS7KXcqn.js +1 -0
  73. package/dist/assets/{ganttDiagram-NO4QXBWP-DzAiojNi.js → ganttDiagram-NO4QXBWP-B0oyDIqK.js} +1 -1
  74. package/dist/assets/gitGraph-TEB2WS4Q-m5F5ef09.js +1 -0
  75. package/dist/assets/{gitGraphDiagram-IHSO6WYX-Cv57oNEA.js → gitGraphDiagram-IHSO6WYX-uQtr6IqK.js} +1 -1
  76. package/dist/assets/icons-CXPP1fFT.js +1 -0
  77. package/dist/assets/index-_ZhK6bvp.js +65 -0
  78. package/dist/assets/index-iVV2bP_f.css +3 -0
  79. package/dist/assets/info-DKCQHKI2-CZxPlQJ0.js +1 -0
  80. package/dist/assets/{infoDiagram-FWYZ7A6U-B5Z_O3Ro.js → infoDiagram-FWYZ7A6U-B_GzlLIM.js} +1 -1
  81. package/dist/assets/{ishikawaDiagram-FXEZZL3T-CKTK_TKV.js → ishikawaDiagram-FXEZZL3T-CKQIsSdU.js} +1 -1
  82. package/dist/assets/{journeyDiagram-5HDEW3XC-Cm9k4ywW.js → journeyDiagram-5HDEW3XC-DrrvzZL1.js} +1 -1
  83. package/dist/assets/{kanban-definition-HUTT4EX6-DzNteLwx.js → kanban-definition-HUTT4EX6-CJQRTdXz.js} +1 -1
  84. package/dist/assets/{line-BG78c_WJ.js → line-B4DxX7tC.js} +1 -1
  85. package/dist/assets/{linear-1X20_EGu.js → linear-U-nKUhyW.js} +1 -1
  86. package/dist/assets/{map-CCI8WZkS.js → map-BneTM-ri.js} +1 -1
  87. package/dist/assets/{mcp-servers-dialog-GXXD9K0U.js → mcp-servers-dialog-BUSPvaqI.js} +2 -2
  88. package/dist/assets/{mermaid-parser.core-Bb8HI_OS.js → mermaid-parser.core-BlKWrEdZ.js} +2 -2
  89. package/dist/assets/{mermaid-renderer-PIaNiXAz.js → mermaid-renderer-jEbqFaTe.js} +2 -2
  90. package/dist/assets/{mermaid.core-DUHOIwL5.js → mermaid.core-DWpShXCs.js} +3 -3
  91. package/dist/assets/{mindmap-definition-LN4V7U3C-Tnbszo7b.js → mindmap-definition-LN4V7U3C-CEVwZD78.js} +1 -1
  92. package/dist/assets/model-identity-CgA6_Pbm.js +1 -0
  93. package/dist/assets/model-reference-o41fJ6jQ.js +1 -0
  94. package/dist/assets/{monaco-BVtCspYZ.js → monaco-D6OhXgjg.js} +1 -1
  95. package/dist/assets/{ordinal-v4prQcuq.js → ordinal-B2r5L1v4.js} +1 -1
  96. package/dist/assets/{otel-D1LswXpM.js → otel-BFnphzjt.js} +1 -1
  97. package/dist/assets/packet-7NZHBO7P-zIioIZVN.js +1 -0
  98. package/dist/assets/{pegDiagram-2B236MQR-CX824Kd0.js → pegDiagram-2B236MQR-CBLkBdnQ.js} +1 -1
  99. package/dist/assets/{pi-ai-DAJlO9ZB.js → pi-ai-Cr2ee4a0.js} +2 -2
  100. package/dist/assets/{pi-web-ui-B-RJSxjR.js → pi-web-ui-DV5jKjSg.js} +1 -1
  101. package/dist/assets/pie-RZYD4A2V-GGWijBvB.js +1 -0
  102. package/dist/assets/{pieDiagram-ENE6RG2P-BT04VWPQ.js → pieDiagram-ENE6RG2P-8tCnwtWS.js} +1 -1
  103. package/dist/assets/{quadrantDiagram-ABIIQ3AL-BvOZxkBr.js → quadrantDiagram-ABIIQ3AL-CWd28cHF.js} +1 -1
  104. package/dist/assets/radar-I7S5WNFK-yvwFH3RV.js +1 -0
  105. package/dist/assets/railroad-3IZDKUUU-BVadlswB.js +1 -0
  106. package/dist/assets/railroad-abnf-AHOZXSZD-B7R-p_2-.js +1 -0
  107. package/dist/assets/railroad-ebnf-EBAXGLYW-BnbJxPug.js +1 -0
  108. package/dist/assets/railroad-peg-LSFZ7HO6-Ci3DcEuQ.js +1 -0
  109. package/dist/assets/{railroadDiagram-RFXS5EU6-DnTU--BV.js → railroadDiagram-RFXS5EU6-CI4ouOBs.js} +1 -1
  110. package/dist/assets/{react-vendor-eWH0QhNU.js → react-vendor-1KC5PyRx.js} +1 -1
  111. package/dist/assets/{requirementDiagram-TGXJPOKE--MZX_yEv.js → requirementDiagram-TGXJPOKE-DtFz7sw4.js} +1 -1
  112. package/dist/assets/{sankeyDiagram-HTMAVEWB-SEZkHhYn.js → sankeyDiagram-HTMAVEWB--VeOwwk9.js} +1 -1
  113. package/dist/assets/{sequenceDiagram-DBY2YBRQ-BhC2OHiz.js → sequenceDiagram-DBY2YBRQ-B8AHk5Oe.js} +1 -1
  114. package/dist/assets/{sizeCapture-X5ZJPWSS-Ci5LeToV.js → sizeCapture-X5ZJPWSS-vV_ArM8D.js} +1 -1
  115. package/dist/assets/{skills-dialog-DDh1h8eH.js → skills-dialog-BThnIUBs.js} +3 -3
  116. package/dist/assets/{src-CovulcRC.js → src-PYsDme0X.js} +1 -1
  117. package/dist/assets/{stateDiagram-2N3HPSRC-DKK5tFT4.js → stateDiagram-2N3HPSRC-etXnJ8X8.js} +1 -1
  118. package/dist/assets/stateDiagram-v2-6OUMAXLB-Dms3pICj.js +1 -0
  119. package/dist/assets/{swimlanes-5IMT3BWC-DxxGCk67.js → swimlanes-5IMT3BWC-pabIKAmn.js} +2 -2
  120. package/dist/assets/swimlanesDiagram-G3AALYLV-B14aWjv6.js +8 -0
  121. package/dist/assets/{timeline-definition-FHXFAJF6-BPXB3Sue.js → timeline-definition-FHXFAJF6-C55MKm3L.js} +1 -1
  122. package/dist/assets/treeView-QDETBFTQ-Cru9B2zv.js +1 -0
  123. package/dist/assets/treemap-6X3UGDF4-DNOVHFaJ.js +1 -0
  124. package/dist/assets/{vennDiagram-L72KCM5P-CxcBCKye.js → vennDiagram-L72KCM5P-CgNf1Q9W.js} +1 -1
  125. package/dist/assets/wardley-OPB4EBWU-E3m1BOJQ.js +1 -0
  126. package/dist/assets/{wardleyDiagram-EHGQE667-DEL6Yes5.js → wardleyDiagram-EHGQE667-Drt5ytec.js} +1 -1
  127. package/dist/assets/{xychartDiagram-FW5EYKEG-N8cPbcjC.js → xychartDiagram-FW5EYKEG-hhoEo_Ar.js} +1 -1
  128. package/dist/index.html +9 -8
  129. package/package.json +3 -1
  130. package/plugins/{openai-documents → documents}/plugin.json +2 -2
  131. package/plugins/{openai-presentations → presentations}/plugin.json +2 -2
  132. package/plugins/{openai-spreadsheets → spreadsheets}/plugin.json +2 -2
  133. package/server/acp/server.mjs +36 -26
  134. package/server/agent-manager.mjs +130 -28
  135. package/server/agent-profile-files.mjs +8 -4
  136. package/server/agent-profile-schema.mjs +47 -23
  137. package/server/agent-profiles.mjs +1 -1
  138. package/server/ai-http-logger.mjs +98 -12
  139. package/server/cloud/chat-idempotency-store.mjs +96 -0
  140. package/server/cloud/client.mjs +102 -0
  141. package/server/cloud/config.mjs +53 -0
  142. package/server/cloud/credential-store.mjs +193 -0
  143. package/server/cloud/identity.mjs +355 -0
  144. package/server/cloud/index.mjs +7 -0
  145. package/server/cloud/models.mjs +80 -0
  146. package/server/cloud/runtime.mjs +129 -0
  147. package/server/cloud/service-config.mjs +77 -0
  148. package/server/index.mjs +28 -11
  149. package/server/model-catalog.mjs +262 -0
  150. package/server/plugins/registry.mjs +29 -5
  151. package/server/routes/agent-profiles.mjs +41 -7
  152. package/server/routes/agent.mjs +20 -11
  153. package/server/routes/backup.mjs +7 -1
  154. package/server/routes/cloud.mjs +227 -0
  155. package/server/routes/models.mjs +7 -1
  156. package/server/routes/scheduled-tasks.mjs +38 -8
  157. package/server/routes/shared-conversation.mjs +62 -58
  158. package/server/routes/shares.mjs +76 -1
  159. package/server/routes/storage.mjs +24 -1
  160. package/server/routes/workspace.mjs +6 -4
  161. package/server/share-store.mjs +168 -13
  162. package/server/skills.mjs +30 -3
  163. package/server/storage.mjs +7 -1
  164. package/server/subagents.mjs +3 -3
  165. package/server/tools/definitions.mjs +1 -1
  166. package/server/tools/index.mjs +2 -2
  167. package/server/utils/network.mjs +9 -0
  168. package/server/utils/response.mjs +8 -1
  169. package/skills/skill-creator/LICENSE.txt +202 -0
  170. package/skills/skill-creator/SKILL.md +485 -0
  171. package/skills/skill-creator/UPSTREAM.md +6 -0
  172. package/skills/skill-creator/agents/analyzer.md +274 -0
  173. package/skills/skill-creator/agents/comparator.md +202 -0
  174. package/skills/skill-creator/agents/grader.md +223 -0
  175. package/skills/skill-creator/assets/eval_review.html +146 -0
  176. package/skills/skill-creator/eval-viewer/generate_review.py +471 -0
  177. package/skills/skill-creator/eval-viewer/viewer.html +1325 -0
  178. package/skills/skill-creator/references/schemas.md +430 -0
  179. package/skills/skill-creator/scripts/__init__.py +0 -0
  180. package/skills/skill-creator/scripts/aggregate_benchmark.py +401 -0
  181. package/skills/skill-creator/scripts/generate_report.py +326 -0
  182. package/skills/skill-creator/scripts/improve_description.py +247 -0
  183. package/skills/skill-creator/scripts/package_skill.py +136 -0
  184. package/skills/skill-creator/scripts/quick_validate.py +103 -0
  185. package/skills/skill-creator/scripts/run_eval.py +310 -0
  186. package/skills/skill-creator/scripts/run_loop.py +328 -0
  187. package/skills/skill-creator/scripts/utils.py +47 -0
  188. package/dist/assets/AgentProfilesPage-B84w-r9S.js +0 -1
  189. package/dist/assets/ChatPanelHost-CyO8fEoj.js +0 -288
  190. package/dist/assets/PluginsPage-D2862wul.js +0 -1
  191. package/dist/assets/ScheduledTasksPage-GqbiJDPX.js +0 -2
  192. package/dist/assets/SharedConversationPage-Cw2QFnbC.js +0 -1
  193. package/dist/assets/WorkspaceInspector-3uLiu6Bv.js +0 -13
  194. package/dist/assets/architecture-TIHT7OUA-7FQo5GbP.js +0 -1
  195. package/dist/assets/channel-DToA8d-4.js +0 -1
  196. package/dist/assets/chunk-2Q5K7J3B-D_Wxpk-T.js +0 -1
  197. package/dist/assets/chunk-VAUOI2AC-CIZseNLi.js +0 -1
  198. package/dist/assets/chunk-XXDRQBXY-Do-XYxat.js +0 -1
  199. package/dist/assets/classDiagram-OUVF2IWQ-Bu24Sy-k.js +0 -1
  200. package/dist/assets/classDiagram-v2-EOCWNBFH-DKhh5HSW.js +0 -1
  201. package/dist/assets/cynefin-VYW2F7L2-oyiAgYC4.js +0 -1
  202. package/dist/assets/eventmodeling-45OFAUF4-YB_0MefX.js +0 -1
  203. package/dist/assets/flowDiagram-23GEKE2U-Bc73xdBd.js +0 -1
  204. package/dist/assets/gitGraph-TEB2WS4Q-Bvp27L0n.js +0 -1
  205. package/dist/assets/icons-cTkqD_Gi.js +0 -1
  206. package/dist/assets/index-B48prgLL.js +0 -65
  207. package/dist/assets/index-DQhwHHer.css +0 -3
  208. package/dist/assets/info-DKCQHKI2-BsLOivgq.js +0 -1
  209. package/dist/assets/packet-7NZHBO7P-BKQkXRtk.js +0 -1
  210. package/dist/assets/pie-RZYD4A2V-BTiTN5Ge.js +0 -1
  211. package/dist/assets/radar-I7S5WNFK-BU-oVjKM.js +0 -1
  212. package/dist/assets/railroad-3IZDKUUU-7H2hxNbL.js +0 -1
  213. package/dist/assets/railroad-abnf-AHOZXSZD-Mx6wf6XQ.js +0 -1
  214. package/dist/assets/railroad-ebnf-EBAXGLYW-BgXSKhYy.js +0 -1
  215. package/dist/assets/railroad-peg-LSFZ7HO6-DTEl2rAC.js +0 -1
  216. package/dist/assets/stateDiagram-v2-6OUMAXLB-D8x3mkni.js +0 -1
  217. package/dist/assets/swimlanesDiagram-G3AALYLV-C4nOBNp8.js +0 -8
  218. package/dist/assets/treeView-QDETBFTQ-DOE6NSdf.js +0 -1
  219. package/dist/assets/treemap-6X3UGDF4-Ca7QD_b4.js +0 -1
  220. package/dist/assets/wardley-OPB4EBWU-D9O4RKRm.js +0 -1
  221. /package/dist/assets/{array-DqLCdDFv.js → array-B9UHiPd-.js} +0 -0
  222. /package/dist/assets/{chunk-KEIR6QF5-s7Qq-R3i.js → chunk-KEIR6QF5-jrz1pRUS.js} +0 -0
  223. /package/dist/assets/{chunk-Y2CYZVJY-BgISNSiw.js → chunk-Y2CYZVJY-D6N-CrJv.js} +0 -0
  224. /package/dist/assets/{cytoscape.esm-iekjSZ2n.js → cytoscape.esm-DyfqweF2.js} +0 -0
  225. /package/dist/assets/{defaultLocale-CrJzLgRD.js → defaultLocale-5eAKkKJC.js} +0 -0
  226. /package/dist/assets/{dist-zzfqk5hx.js → dist-CLg13NtV.js} +0 -0
  227. /package/dist/assets/{graphlib-CIz_CfxW.js → graphlib-DJkmx0Cq.js} +0 -0
  228. /package/dist/assets/{init-C0r9Gk5G.js → init-DlZdxViB.js} +0 -0
  229. /package/dist/assets/{path-DIKpVbHL.js → path-6uRLdFF7.js} +0 -0
  230. /package/dist/assets/{plugin-api-C8hMT5W6.js → plugin-api-Uu2xAB4d.js} +0 -0
  231. /package/dist/assets/{rough.esm-BMOtDaaS.js → rough.esm-Bud4g4fh.js} +0 -0
  232. /package/dist/assets/{web-RgX7H-OH.js → web-DoV8MSMn.js} +0 -0
  233. /package/dist/assets/{xterm-BFzZ1FH4.js → xterm-Bm_IbozX.js} +0 -0
  234. /package/plugins/{openai-documents → documents}/commands/document.md +0 -0
  235. /package/plugins/{openai-documents → documents}/skills/documents/SKILL.md +0 -0
  236. /package/plugins/{openai-presentations → presentations}/commands/presentation.md +0 -0
  237. /package/plugins/{openai-presentations → presentations}/skills/presentations/SKILL.md +0 -0
  238. /package/plugins/{openai-spreadsheets → spreadsheets}/commands/spreadsheet.md +0 -0
  239. /package/plugins/{openai-spreadsheets → spreadsheets}/skills/spreadsheets/SKILL.md +0 -0
package/server/index.mjs CHANGED
@@ -17,6 +17,7 @@ import { handleToolApi, handleGetTools } from './routes/tools.mjs'
17
17
  import { handleInstructionsApi } from './routes/instructions.mjs'
18
18
  import { handleMemoryApi } from './routes/memory.mjs'
19
19
  import { handleSkillsApi } from './routes/skills.mjs'
20
+ import { ensureDefaultGlobalSkills } from './skills.mjs'
20
21
  import { handleAgentApi } from './routes/agent.mjs'
21
22
  import { handleAgentProfilesApi } from './routes/agent-profiles.mjs'
22
23
  import { handleScheduledTasksApi, startScheduledTaskRunner, stopScheduledTaskRunner } from './routes/scheduled-tasks.mjs'
@@ -33,6 +34,7 @@ import { handleWorkspaceApi, handleGitApi } from './routes/workspace.mjs'
33
34
  import { handleTerminalApi, handleTerminalUpgrade } from './routes/terminal.mjs'
34
35
  import { handleChannelsApi } from './routes/channels.mjs'
35
36
  import { handleModelsApi } from './routes/models.mjs'
37
+ import { handleCloudApi } from './routes/cloud.mjs'
36
38
  import { serveStatic } from './routes/static.mjs'
37
39
  import { logger, flushLogger } from './utils/logger.mjs'
38
40
  import { getPackageInfo, checkForUpdates, checkDesktopRelease } from './utils/package-update.mjs'
@@ -289,12 +291,12 @@ async function handleApi(req, res, url, requestContext = {}) {
289
291
 
290
292
  // Conversation share routes (management + public LAN access)
291
293
  if (pathname === '/api/shares' || pathname.startsWith('/api/shares/')) {
292
- await handleSharesApi(req, res, url, { port })
294
+ await handleSharesApi(req, res, url, { ...requestContext, port })
293
295
  return
294
296
  }
295
297
 
296
298
  if (pathname.startsWith('/api/shared/')) {
297
- await handleSharedConversationApi(req, res, url)
299
+ await handleSharedConversationApi(req, res, url, requestContext)
298
300
  return
299
301
  }
300
302
 
@@ -336,13 +338,23 @@ async function handleApi(req, res, url, requestContext = {}) {
336
338
 
337
339
  // Agent profiles
338
340
  if (pathname === '/api/agent-profiles' || pathname.startsWith('/api/agent-profiles/')) {
339
- await handleAgentProfilesApi(req, res, url)
341
+ await handleAgentProfilesApi(req, res, url, requestContext)
342
+ return
343
+ }
344
+
345
+ // QuickForge Cloud account and managed models (local or authenticated Tailscale requests).
346
+ if (pathname === '/api/cloud' || pathname.startsWith('/api/cloud/')) {
347
+ await handleCloudApi(req, res, url, {
348
+ isLocalRequest: requestContext.isLocalRequest === true,
349
+ remoteAddress: requestContext.remoteAddress,
350
+ remoteAuthorized: requestContext.remoteAuthorized === true,
351
+ })
340
352
  return
341
353
  }
342
354
 
343
355
  // Custom model management (connection test)
344
- if (pathname === '/api/models/test-connection') {
345
- await handleModelsApi(req, res, url)
356
+ if (pathname === '/api/models/catalog' || pathname === '/api/models/test-connection') {
357
+ await handleModelsApi(req, res, url, requestContext)
346
358
  return
347
359
  }
348
360
 
@@ -387,7 +399,7 @@ async function handleApi(req, res, url, requestContext = {}) {
387
399
  }
388
400
 
389
401
  if (pathname.startsWith('/api/git/')) {
390
- await handleGitApi(req, res, url)
402
+ await handleGitApi(req, res, url, requestContext)
391
403
  return
392
404
  }
393
405
 
@@ -411,19 +423,19 @@ async function handleApi(req, res, url, requestContext = {}) {
411
423
 
412
424
  // Agent routes
413
425
  if (parts[0] === 'api' && parts[1] === 'agents') {
414
- await handleAgentApi(req, res, url)
426
+ await handleAgentApi(req, res, url, requestContext)
415
427
  return
416
428
  }
417
429
 
418
430
  // Scheduled task routes
419
431
  if (pathname === '/api/scheduled-tasks' || pathname.startsWith('/api/scheduled-tasks/')) {
420
- await handleScheduledTasksApi(req, res, url)
432
+ await handleScheduledTasksApi(req, res, url, requestContext)
421
433
  return
422
434
  }
423
435
 
424
436
  // Backup / import-export routes
425
437
  if (pathname === '/api/backup/export' || pathname === '/api/backup/import' || pathname === '/api/backup/inspect' || pathname === '/api/backup/inspect-file') {
426
- await handleBackupApi(req, res, url)
438
+ await handleBackupApi(req, res, url, requestContext)
427
439
  return
428
440
  }
429
441
 
@@ -461,7 +473,7 @@ async function handleApi(req, res, url, requestContext = {}) {
461
473
 
462
474
  // Storage routes (catch-all)
463
475
  if (parts[0] === 'api' && parts[1] === 'storage') {
464
- await handleStorageApi(req, res, url)
476
+ await handleStorageApi(req, res, url, requestContext)
465
477
  return
466
478
  }
467
479
 
@@ -619,7 +631,11 @@ const server = createServer(async (req, res) => {
619
631
  }
620
632
 
621
633
  if (url.pathname.startsWith('/api/')) {
622
- await handleApi(req, res, url, { isLocalRequest: !isRemoteRequest })
634
+ await handleApi(req, res, url, {
635
+ isLocalRequest: !isRemoteRequest,
636
+ remoteAddress,
637
+ remoteAuthorized,
638
+ })
623
639
  return
624
640
  }
625
641
 
@@ -681,6 +697,7 @@ server.on('upgrade', (req, socket, head) => {
681
697
  })
682
698
 
683
699
  await ensureStorage()
700
+ await ensureDefaultGlobalSkills()
684
701
  await initializeNetworkProxy()
685
702
  installAiHttpLogger()
686
703
  initializeChannels({
@@ -0,0 +1,262 @@
1
+ import { getCloudRuntime } from './cloud/runtime.mjs'
2
+ import { isManagedCloudModel } from './cloud/models.mjs'
3
+ import { readStore } from './storage.mjs'
4
+ import { isTailscaleAddress } from './utils/network.mjs'
5
+
6
+ export const MODEL_REFERENCE_VERSION = 1
7
+
8
+ function requestError(message, statusCode = 400, code) {
9
+ const error = new Error(message)
10
+ error.statusCode = statusCode
11
+ if (code) {
12
+ error.code = code
13
+ error.errorCode = code
14
+ }
15
+ return error
16
+ }
17
+
18
+ function normalizedBaseUrl(value) {
19
+ return String(value || '').trim().replace(/\/$/, '')
20
+ }
21
+
22
+ function isUsableModel(model) {
23
+ return Boolean(model && typeof model === 'object' && model.id && model.provider && model.api && model.baseUrl)
24
+ }
25
+
26
+ function sameModel(left, right) {
27
+ return Boolean(left && right
28
+ && left.id === right.id
29
+ && left.provider === right.provider
30
+ && left.api === right.api
31
+ && normalizedBaseUrl(left.baseUrl) === normalizedBaseUrl(right.baseUrl))
32
+ }
33
+
34
+ function publicModel(model, modelRef) {
35
+ const result = {
36
+ ...model,
37
+ quickforgeModelRef: modelRef,
38
+ }
39
+ delete result.apiKey
40
+ delete result.key
41
+ delete result.headers
42
+ return result
43
+ }
44
+
45
+ async function configuredProviders() {
46
+ const store = await readStore('custom-providers').catch(() => ({}))
47
+ return Array.isArray(store) ? store : Object.values(store || {})
48
+ }
49
+
50
+ function configuredEntries(providers) {
51
+ return providers.flatMap((provider) => {
52
+ const providerId = String(provider?.id || '').trim()
53
+ return (Array.isArray(provider?.models) ? provider.models : [])
54
+ .filter(isUsableModel)
55
+ .map((model) => ({ provider, providerId, model }))
56
+ })
57
+ }
58
+
59
+ export function cloudAllowedForContext(context = {}) {
60
+ if (context.allowCloud === false) return false
61
+ if (context.allowCloud === true || context.source === 'acp' || context.source === 'scheduled') return true
62
+ if (context.isLocalRequest !== false) return true
63
+ return context.remoteAuthorized === true && isTailscaleAddress(context.remoteAddress)
64
+ }
65
+
66
+ export function modelReferenceFromSnapshot(model, providers = []) {
67
+ if (!model || typeof model !== 'object') return null
68
+ if (isManagedCloudModel(model)) {
69
+ const catalogId = String(model.quickforgeCatalogId || model.id || '').trim()
70
+ return catalogId ? { version: MODEL_REFERENCE_VERSION, source: 'cloud', catalogId } : null
71
+ }
72
+
73
+ const matched = configuredEntries(providers).find((entry) => sameModel(entry.model, model))
74
+ if (matched?.providerId) {
75
+ return {
76
+ version: MODEL_REFERENCE_VERSION,
77
+ source: 'custom',
78
+ providerId: matched.providerId,
79
+ modelId: matched.model.id,
80
+ }
81
+ }
82
+
83
+ if (model.provider && model.id) {
84
+ return {
85
+ version: MODEL_REFERENCE_VERSION,
86
+ source: 'legacy-custom',
87
+ provider: String(model.provider),
88
+ modelId: String(model.id),
89
+ ...(model.api ? { api: String(model.api) } : {}),
90
+ ...(model.baseUrl ? { baseUrl: String(model.baseUrl) } : {}),
91
+ }
92
+ }
93
+ return null
94
+ }
95
+
96
+ export function normalizeModelReference(value) {
97
+ if (!value || typeof value !== 'object' || Array.isArray(value)) return null
98
+ const source = String(value.source || '').trim()
99
+ if (source === 'cloud') {
100
+ const catalogId = String(value.catalogId || value.quickforgeCatalogId || value.modelId || '').trim()
101
+ return catalogId ? { version: MODEL_REFERENCE_VERSION, source: 'cloud', catalogId } : null
102
+ }
103
+ if (source === 'custom') {
104
+ const providerId = String(value.providerId || '').trim()
105
+ const modelId = String(value.modelId || value.id || '').trim()
106
+ return providerId && modelId
107
+ ? { version: MODEL_REFERENCE_VERSION, source: 'custom', providerId, modelId }
108
+ : null
109
+ }
110
+ if (source === 'legacy-custom') {
111
+ const provider = String(value.provider || '').trim()
112
+ const modelId = String(value.modelId || value.id || '').trim()
113
+ return provider && modelId
114
+ ? {
115
+ version: MODEL_REFERENCE_VERSION,
116
+ source: 'legacy-custom',
117
+ provider,
118
+ modelId,
119
+ ...(value.api ? { api: String(value.api) } : {}),
120
+ ...(value.baseUrl ? { baseUrl: String(value.baseUrl) } : {}),
121
+ }
122
+ : null
123
+ }
124
+ return null
125
+ }
126
+
127
+ async function listCloudModels(context, { refresh = false } = {}) {
128
+ if (!cloudAllowedForContext(context)) return []
129
+ try {
130
+ const runtime = await getCloudRuntime()
131
+ if (!runtime?.enabled) return []
132
+ const models = await runtime.models.list(undefined, { refresh })
133
+ return models.filter((model) => model?.quickforgeCatalogId)
134
+ } catch {
135
+ return []
136
+ }
137
+ }
138
+
139
+ export async function listModelCatalog({
140
+ context = {},
141
+ includeHidden = false,
142
+ currentModel = null,
143
+ refreshCloud = false,
144
+ } = {}) {
145
+ const providers = await configuredProviders()
146
+ const custom = configuredEntries(providers)
147
+ .filter(({ model }) => includeHidden || model.quickforgeHidden !== true || sameModel(model, currentModel))
148
+ .map(({ providerId, model }) => publicModel(model, {
149
+ version: MODEL_REFERENCE_VERSION,
150
+ source: 'custom',
151
+ providerId,
152
+ modelId: model.id,
153
+ }))
154
+ if (currentModel) {
155
+ const currentIndex = custom.findIndex((model) => sameModel(model, currentModel))
156
+ if (currentIndex > 0 && custom[currentIndex].quickforgeHidden === true) {
157
+ custom.unshift(custom.splice(currentIndex, 1)[0])
158
+ }
159
+ }
160
+ const cloud = (await listCloudModels(context, { refresh: refreshCloud }))
161
+ .map((model) => publicModel(model, {
162
+ version: MODEL_REFERENCE_VERSION,
163
+ source: 'cloud',
164
+ catalogId: model.quickforgeCatalogId,
165
+ }))
166
+
167
+ if (currentModel && ![...custom, ...cloud].some((model) => sameModel(model, currentModel))) {
168
+ const ref = modelReferenceFromSnapshot(currentModel, providers)
169
+ if (ref) return [publicModel(currentModel, ref), ...custom, ...cloud]
170
+ }
171
+ return [...custom, ...cloud]
172
+ }
173
+
174
+ async function resolveCloud(ref, context) {
175
+ if (!cloudAllowedForContext(context)) {
176
+ throw requestError('QuickForge Cloud is not available from this client.', 403, 'cloud_access_denied')
177
+ }
178
+ const runtime = await getCloudRuntime()
179
+ if (!runtime?.enabled) throw requestError('QuickForge Cloud is not configured.', 503, 'cloud_not_configured')
180
+ return (await runtime.models.resolve({
181
+ provider: 'quickforge-cloud',
182
+ quickforgeModelSource: 'cloud',
183
+ quickforgeCatalogId: ref.catalogId,
184
+ })).publicModel
185
+ }
186
+
187
+ function findCustomByReference(ref, entries) {
188
+ if (ref.source === 'custom') {
189
+ return entries.find(({ providerId, model }) => providerId === ref.providerId && model.id === ref.modelId)?.model
190
+ }
191
+ if (ref.source === 'legacy-custom') {
192
+ return entries.find(({ model }) => model.provider === ref.provider
193
+ && model.id === ref.modelId
194
+ && (!ref.api || model.api === ref.api)
195
+ && (!ref.baseUrl || normalizedBaseUrl(model.baseUrl) === normalizedBaseUrl(ref.baseUrl)))?.model
196
+ }
197
+ return null
198
+ }
199
+
200
+ export async function resolveModelBinding(input, {
201
+ context = {},
202
+ currentModel = null,
203
+ allowCurrentHidden = false,
204
+ forExecution = false,
205
+ legacySnapshot = null,
206
+ } = {}) {
207
+ const providers = await configuredProviders()
208
+ const entries = configuredEntries(providers)
209
+ const explicitRef = normalizeModelReference(input?.modelRef || input?.quickforgeModelRef || input)
210
+ const snapshot = input?.model && typeof input.model === 'object'
211
+ ? input.model
212
+ : (!explicitRef && input && typeof input === 'object' && input.id ? input : legacySnapshot)
213
+ const ref = explicitRef || modelReferenceFromSnapshot(snapshot, providers)
214
+ if (!ref) throw requestError('A valid model reference is required.', 400, 'invalid_model_reference')
215
+
216
+ if (ref.source === 'cloud') {
217
+ const model = await resolveCloud(ref, context)
218
+ return { model, modelRef: ref }
219
+ }
220
+
221
+ const configured = findCustomByReference(ref, entries)
222
+ if (configured) {
223
+ const hidden = configured.quickforgeHidden === true
224
+ if (hidden && !forExecution && !(allowCurrentHidden && sameModel(configured, currentModel))) {
225
+ throw requestError('Selected model is not available for new selection.', 400, 'model_not_selectable')
226
+ }
227
+ const canonicalRef = modelReferenceFromSnapshot(configured, providers)
228
+ return { model: configured, modelRef: canonicalRef }
229
+ }
230
+
231
+ throw requestError('Selected model is not configured in QuickForge.', 400, 'model_not_configured')
232
+ }
233
+
234
+ export async function resolveImplicitModelPreference(value, context = {}) {
235
+ let stored = value
236
+ if (typeof stored === 'string') {
237
+ try { stored = JSON.parse(stored) } catch { stored = null }
238
+ }
239
+ const ref = normalizeModelReference(stored?.modelRef || stored?.quickforgeModelRef)
240
+ const snapshot = stored?.modelSnapshot || stored?.model || (stored?.id ? stored : null)
241
+ const catalog = await listModelCatalog({ context })
242
+ if (ref) {
243
+ const matched = catalog.find((model) => {
244
+ const candidate = normalizeModelReference(model.quickforgeModelRef)
245
+ if (!candidate || candidate.source !== ref.source) return false
246
+ if (ref.source === 'cloud') return candidate.catalogId === ref.catalogId
247
+ if (ref.source === 'custom') return candidate.providerId === ref.providerId && candidate.modelId === ref.modelId
248
+ return false
249
+ })
250
+ if (matched) return matched
251
+ }
252
+ if (snapshot) {
253
+ const matched = catalog.find((model) => sameModel(model, snapshot))
254
+ if (matched) return matched
255
+ }
256
+ return catalog[0] || null
257
+ }
258
+
259
+ export async function modelBindingFromModel(model) {
260
+ const providers = await configuredProviders()
261
+ return { model, modelRef: modelReferenceFromSnapshot(model, providers) }
262
+ }
@@ -16,6 +16,12 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url))
16
16
  const bundledPluginDir = path.resolve(__dirname, '..', '..', 'plugins')
17
17
  const globalPluginDir = path.join(dataDir, 'plugins')
18
18
  const legacyGlobalPluginDir = path.join(os.homedir(), '.agents', 'plugins')
19
+ const PLUGIN_STORE_SCHEMA_VERSION = 2
20
+ const LEGACY_BUNDLED_PLUGIN_NAMES = {
21
+ 'openai-documents': 'documents',
22
+ 'openai-presentations': 'presentations',
23
+ 'openai-spreadsheets': 'spreadsheets',
24
+ }
19
25
  export const DEFAULT_PLUGIN_TOOL_TIMEOUT_MS = 2 * 60 * 1000
20
26
  let cachedCatalog = null
21
27
  let cachedCatalogKey = null
@@ -108,13 +114,31 @@ async function listPluginDirs(root) {
108
114
  function normalizePluginStore(store) {
109
115
  const source = isPlainObject(store) ? store : {}
110
116
  return {
111
- enabled: isPlainObject(source.enabled) ? source.enabled : {},
112
- config: isPlainObject(source.config) ? source.config : {},
117
+ schemaVersion: Number.isInteger(source.schemaVersion) ? source.schemaVersion : 1,
118
+ enabled: isPlainObject(source.enabled) ? { ...source.enabled } : {},
119
+ config: isPlainObject(source.config) ? { ...source.config } : {},
120
+ }
121
+ }
122
+
123
+ function migratePluginStore(store) {
124
+ const next = normalizePluginStore(store)
125
+ if (next.schemaVersion >= PLUGIN_STORE_SCHEMA_VERSION) return { store: next, changed: false }
126
+
127
+ for (const [legacyName, name] of Object.entries(LEGACY_BUNDLED_PLUGIN_NAMES)) {
128
+ if (!(name in next.enabled) && legacyName in next.enabled) next.enabled[name] = next.enabled[legacyName]
129
+ if (!(name in next.config) && legacyName in next.config) next.config[name] = next.config[legacyName]
130
+ delete next.enabled[legacyName]
131
+ delete next.config[legacyName]
113
132
  }
133
+ next.schemaVersion = PLUGIN_STORE_SCHEMA_VERSION
134
+ return { store: next, changed: true }
114
135
  }
115
136
 
116
137
  async function readPluginStore() {
117
- return normalizePluginStore(await readStore('plugins'))
138
+ const current = await readStore('plugins')
139
+ const migration = migratePluginStore(current)
140
+ if (!migration.changed) return migration.store
141
+ return atomicUpdate('plugins', (store) => migratePluginStore(store).store)
118
142
  }
119
143
 
120
144
  function contributionSummary(contribution) {
@@ -292,7 +316,7 @@ export async function getPluginStatus(projectContext = null) {
292
316
 
293
317
  export async function setPluginEnabled(name, enabled) {
294
318
  await atomicUpdate('plugins', (store) => {
295
- const next = normalizePluginStore(store)
319
+ const next = migratePluginStore(store).store
296
320
  next.enabled[name] = enabled === true
297
321
  return next
298
322
  })
@@ -301,7 +325,7 @@ export async function setPluginEnabled(name, enabled) {
301
325
 
302
326
  export async function setPluginConfig(name, config) {
303
327
  await atomicUpdate('plugins', (store) => {
304
- const next = normalizePluginStore(store)
328
+ const next = migratePluginStore(store).store
305
329
  next.config[name] = isPlainObject(config) ? config : {}
306
330
  return next
307
331
  })
@@ -3,6 +3,36 @@ import { DEFAULT_AI_MAX_RETRIES } from '../ai-provider-options.mjs'
3
3
  import { sendJson, readJsonBody, decodeSegment } from '../utils/response.mjs'
4
4
  import { readStore } from '../storage.mjs'
5
5
  import { logger } from '../utils/logger.mjs'
6
+ import { resolveModelBinding } from '../model-catalog.mjs'
7
+
8
+ function profileModelReference(model) {
9
+ if (!model || model.mode !== 'fixed') return null
10
+ if (model.source === 'cloud') return { version: 1, source: 'cloud', catalogId: model.catalogId || model.modelId }
11
+ if (model.source === 'custom') return { version: 1, source: 'custom', providerId: model.providerId, modelId: model.modelId }
12
+ return {
13
+ version: 1,
14
+ source: 'legacy-custom',
15
+ provider: model.provider,
16
+ modelId: model.modelId,
17
+ ...(model.api ? { api: model.api } : {}),
18
+ ...(model.baseUrl ? { baseUrl: model.baseUrl } : {}),
19
+ }
20
+ }
21
+
22
+ async function canonicalizeProfileModel(body, current, context) {
23
+ if (!body || !Object.prototype.hasOwnProperty.call(body, 'model')) return body
24
+ const model = body.model
25
+ if (!model || model.mode !== 'fixed') return body
26
+ if (current?.model && JSON.stringify(current.model) === JSON.stringify(model)) return body
27
+ const binding = await resolveModelBinding({ modelRef: profileModelReference(model) }, { context })
28
+ const ref = binding.modelRef
29
+ return {
30
+ ...body,
31
+ model: ref.source === 'cloud'
32
+ ? { mode: 'fixed', source: 'cloud', catalogId: ref.catalogId }
33
+ : { mode: 'fixed', source: 'custom', providerId: ref.providerId, modelId: ref.modelId },
34
+ }
35
+ }
6
36
  import {
7
37
  agentProfileSnapshot,
8
38
  createCustomAgentProfile,
@@ -65,10 +95,14 @@ function normalizeGeneratedAgentProfile(value) {
65
95
  return { name, label, description, systemPrompt }
66
96
  }
67
97
 
68
- async function generateAgentProfileWithAi(instruction, model, thinkingLevel = 'off') {
98
+ async function generateAgentProfileWithAi(instruction, modelInput, thinkingLevel = 'off', context = {}) {
69
99
  const text = String(instruction || '').trim()
70
100
  if (!text) throw requestError('Please describe the agent you want to create')
71
- if (!model) throw requestError('Please configure a default model first')
101
+ if (!modelInput) throw requestError('Please configure a default model first')
102
+ const { model } = await resolveModelBinding(
103
+ modelInput?.modelRef || modelInput?.model ? modelInput : { model: modelInput },
104
+ { context, legacySnapshot: modelInput?.model || modelInput },
105
+ )
72
106
 
73
107
  const systemPrompt = `You are a QuickForge Agent Profile generator.
74
108
  Generate only the basic definition fields for a custom Agent Profile from the user's request.
@@ -120,7 +154,7 @@ Rules:
120
154
  }
121
155
  }
122
156
 
123
- export async function handleAgentProfilesApi(req, res, url) {
157
+ export async function handleAgentProfilesApi(req, res, url, context = {}) {
124
158
  const parts = url.pathname.split('/').filter(Boolean)
125
159
 
126
160
  if (req.method === 'GET' && url.pathname === '/api/agent-profiles') {
@@ -136,13 +170,13 @@ export async function handleAgentProfilesApi(req, res, url) {
136
170
 
137
171
  if (req.method === 'POST' && url.pathname === '/api/agent-profiles/ai-fill') {
138
172
  const body = await readJsonBody(req)
139
- sendJson(res, 200, { agent: await generateAgentProfileWithAi(body?.instruction, body?.model, body?.thinkingLevel) })
173
+ sendJson(res, 200, { agent: await generateAgentProfileWithAi(body?.instruction, body?.modelRef ? body : body?.model, body?.thinkingLevel, context) })
140
174
  return
141
175
  }
142
176
 
143
177
  if (req.method === 'POST' && url.pathname === '/api/agent-profiles') {
144
178
  const body = await readJsonBody(req)
145
- sendJson(res, 200, { agent: await createCustomAgentProfile(body || {}) })
179
+ sendJson(res, 200, { agent: await createCustomAgentProfile(await canonicalizeProfileModel(body || {}, null, context)) })
146
180
  return
147
181
  }
148
182
 
@@ -162,11 +196,11 @@ export async function handleAgentProfilesApi(req, res, url) {
162
196
  if (current?.builtin) {
163
197
  const keys = Object.keys(body || {})
164
198
  if (keys.length !== 1 || keys[0] !== 'model') throw requestError('Built-in agents only allow model updates', 403)
165
- sendJson(res, 200, { agent: agentProfileSnapshot(await updateBuiltinAgentModelOverride(id, body.model)) })
199
+ sendJson(res, 200, { agent: agentProfileSnapshot(await updateBuiltinAgentModelOverride(id, (await canonicalizeProfileModel(body, current, context)).model)) })
166
200
  return
167
201
  }
168
202
  if (current?.readonly) throw requestError('Read-only agents cannot be modified from the API', 403)
169
- sendJson(res, 200, { agent: await updateCustomAgentProfile(id, body || {}) })
203
+ sendJson(res, 200, { agent: await updateCustomAgentProfile(id, await canonicalizeProfileModel(body || {}, current, context)) })
170
204
  return
171
205
  }
172
206
 
@@ -1,5 +1,6 @@
1
1
  import { sendJson, readJsonBody, decodeSegment } from '../utils/response.mjs'
2
2
  import { logger } from '../utils/logger.mjs'
3
+ import { resolveModelBinding } from '../model-catalog.mjs'
3
4
  import {
4
5
  createAgent,
5
6
  runPrompt,
@@ -31,7 +32,7 @@ import {
31
32
  agentEvents,
32
33
  } from '../agent-manager.mjs'
33
34
 
34
- export async function handleAgentApi(req, res, url) {
35
+ export async function handleAgentApi(req, res, url, context = {}) {
35
36
  const pathname = url.pathname
36
37
  const parts = pathname.split('/').filter(Boolean)
37
38
 
@@ -85,7 +86,7 @@ export async function handleAgentApi(req, res, url) {
85
86
  error.statusCode = 400
86
87
  throw error
87
88
  }
88
- const result = await runPrompt(sessionId, message, body?.selectedCapabilities, body?.command)
89
+ const result = await runPrompt(sessionId, message, body?.selectedCapabilities, body?.command, null, context)
89
90
  sendJson(res, 200, result)
90
91
  return
91
92
  }
@@ -160,7 +161,14 @@ export async function handleAgentApi(req, res, url) {
160
161
  // POST /api/agents/:sessionId — create/ensure agent
161
162
  if (req.method === 'POST' && parts.length === 3) {
162
163
  const body = await readJsonBody(req)
163
- const session = await createAgent(sessionId, body)
164
+ let config
165
+ if (body?.modelRef || body?.model) {
166
+ const binding = await resolveModelBinding(body, { context, legacySnapshot: body?.model })
167
+ config = { ...body, model: binding.model, modelRef: binding.modelRef, modelAccessContext: context, resolvePersistedModel: true }
168
+ } else {
169
+ config = { ...body, modelAccessContext: context, resolvePersistedModel: true }
170
+ }
171
+ const session = await createAgent(sessionId, config)
164
172
  sendJson(res, 200, {
165
173
  sessionId: session.sessionId,
166
174
  status: session.status,
@@ -209,13 +217,14 @@ export async function handleAgentApi(req, res, url) {
209
217
  // POST /api/agents/:sessionId/model — update session model
210
218
  if (req.method === 'POST' && subPath === 'model') {
211
219
  const body = await readJsonBody(req)
212
- const model = body?.model
213
- if (!model) {
214
- const error = new Error('Missing model in request body')
215
- error.statusCode = 400
216
- throw error
217
- }
218
- const result = updateSessionModel(sessionId, model)
220
+ const currentModel = getSessionState(sessionId)?.model
221
+ const binding = await resolveModelBinding(body, {
222
+ context,
223
+ currentModel,
224
+ allowCurrentHidden: true,
225
+ legacySnapshot: body?.model,
226
+ })
227
+ const result = updateSessionModel(sessionId, binding.model, binding.modelRef)
219
228
  sendJson(res, 200, result)
220
229
  return
221
230
  }
@@ -244,7 +253,7 @@ export async function handleAgentApi(req, res, url) {
244
253
 
245
254
  // POST /api/agents/:sessionId/continue — continue generation from last message (retry)
246
255
  if (req.method === 'POST' && subPath === 'continue') {
247
- const result = await continueSession(sessionId)
256
+ const result = await continueSession(sessionId, context)
248
257
  sendJson(res, 200, result)
249
258
  return
250
259
  }
@@ -580,7 +580,13 @@ async function restoreValidatedBackup(backup, mode = 'replace') {
580
580
  return summary
581
581
  }
582
582
 
583
- export async function handleBackupApi(req, res, url) {
583
+ export async function handleBackupApi(req, res, url, context = {}) {
584
+ if (context.isLocalRequest === false) {
585
+ const error = new Error('Backup import and export are available only on this computer.')
586
+ error.statusCode = 403
587
+ error.errorCode = 'backup_local_only'
588
+ throw error
589
+ }
584
590
  if (req.method === 'GET' && url.pathname === '/api/backup/export') {
585
591
  await ensureStorage()
586
592
  sendJson(res, 200, await buildBackup(url.searchParams.get('scope'), {