@xopcai/xopc 0.0.233 → 0.0.235

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 (204) hide show
  1. package/dist/browser-ext/manifest.json +1 -1
  2. package/dist/extensions/feishu/src/outbound/media-load.js +1 -1
  3. package/dist/extensions/telegram/xopc.extension.json +1 -1
  4. package/dist/gateway/static/root/assets/{activity-store-DQ5L3PTi.js → activity-store-BzswYHcj.js} +1 -1
  5. package/dist/gateway/static/root/assets/{agent-avatar-dicebear-Bps8fvBp.js → agent-avatar-dicebear-dvh9aEm6.js} +1 -1
  6. package/dist/gateway/static/root/assets/agent-defaults-settings-panel-B9tkmkUj.js +1 -0
  7. package/dist/gateway/static/root/assets/agents-DTXYo-7z.js +1 -0
  8. package/dist/gateway/static/root/assets/{api-DE6tZKG0.js → api-7zOtMbaE.js} +1 -1
  9. package/dist/gateway/static/root/assets/{app-management-settings-panel-BryzDVod.js → app-management-settings-panel-DXFTk7x6.js} +1 -1
  10. package/dist/gateway/static/root/assets/{appearance-settings-BoweLBDm.js → appearance-settings-D-FD5SOq.js} +1 -1
  11. package/dist/gateway/static/root/assets/apps-page-BT102W58.js +1 -0
  12. package/dist/gateway/static/root/assets/{archive-plugin-DyujzZNu.js → archive-plugin-DawToDU8.js} +1 -1
  13. package/dist/gateway/static/root/assets/{attachment-load-DmXGUWve.js → attachment-load-DmBAKvsi.js} +1 -1
  14. package/dist/gateway/static/root/assets/{automations-page-DrTVSK7o.js → automations-page-BGkF6XNH.js} +1 -1
  15. package/dist/gateway/static/root/assets/automations-workspace-BRb45-_R.js +6 -0
  16. package/dist/gateway/static/root/assets/{binary-plugins-mFyeC74W.js → binary-plugins-B-6Ski7l.js} +1 -1
  17. package/dist/gateway/static/root/assets/{block-editor-D5yMFrgH.js → block-editor-BgeKQN1F.js} +3 -3
  18. package/dist/gateway/static/root/assets/browser-settings-page-BkRWxC7D.js +8 -0
  19. package/dist/gateway/static/root/assets/{browser-workflow-inputs-B6rLBtua.js → browser-workflow-inputs-4ooVX-Tx.js} +1 -1
  20. package/dist/gateway/static/root/assets/{browser-workflows-page-C74sg50f.js → browser-workflows-page-D92g81lS.js} +2 -2
  21. package/dist/gateway/static/root/assets/capabilities-settings-panel-DfT-vWTA.js +7 -0
  22. package/dist/gateway/static/root/assets/channels-settings-C9oGpLR0.js +1 -0
  23. package/dist/gateway/static/root/assets/{channels-status-swr-DowVNjNw.js → channels-status-swr-BDJvMnA_.js} +1 -1
  24. package/dist/gateway/static/root/assets/{chat-terminal-dock-BO6_BgjE.js → chat-terminal-dock-C_9LUs7G.js} +2 -2
  25. package/dist/gateway/static/root/assets/connectors-page-SJKV6SHT.js +2 -0
  26. package/dist/gateway/static/root/assets/{cron-expression-CYMXfk4M.js → cron-expression-BYmr5ZuN.js} +1 -1
  27. package/dist/gateway/static/root/assets/{date-picker-DIM_OkQP.js → date-picker-FKL_b_S0.js} +1 -1
  28. package/dist/gateway/static/root/assets/{dependency-picker-Cp8I6rsL.js → dependency-picker-BP68vqBI.js} +1 -1
  29. package/dist/gateway/static/root/assets/{desktop-pet-wzSzZ6S8.js → desktop-pet-DOXjRTqK.js} +1 -1
  30. package/dist/gateway/static/root/assets/{desktop-pet-settings-NjPiz3M_.js → desktop-pet-settings-CuG2qYoY.js} +1 -1
  31. package/dist/gateway/static/root/assets/{extension-debug-page-CoMl13ET.js → extension-debug-page-BeJ1-5ID.js} +1 -1
  32. package/dist/gateway/static/root/assets/{extension-page-CXc1Lc8x.js → extension-page-CncDndLS.js} +1 -1
  33. package/dist/gateway/static/root/assets/{extension-settings-page-CfEWpgjl.js → extension-settings-page-BxxL0HqU.js} +1 -1
  34. package/dist/gateway/static/root/assets/{files-Dp8_uGTO.js → files-DQjhZjr2.js} +1 -1
  35. package/dist/gateway/static/root/assets/{gateway-config-api-yH4KAEkK.js → gateway-config-api-Dc6A6HyJ.js} +1 -1
  36. package/dist/gateway/static/root/assets/{gateway-config-swr-YyZJKvFf.js → gateway-config-swr-R702lNJR.js} +1 -1
  37. package/dist/gateway/static/root/assets/{gateway-settings-BmjJIiKh.js → gateway-settings-C2XcazGD.js} +1 -1
  38. package/dist/gateway/static/root/assets/{gateway-startup-retry-Dnrl91pU.js → gateway-startup-retry-2rVSUvyw.js} +1 -1
  39. package/dist/gateway/static/root/assets/heartbeat-settings-BjUEqkkC.js +1 -0
  40. package/dist/gateway/static/root/assets/home-page-DTSjwfAQ.js +1 -0
  41. package/dist/gateway/static/root/assets/{image-generation-api-BNzAAw4Y.js → image-generation-api-B28USRCK.js} +1 -1
  42. package/dist/gateway/static/root/assets/index-DFfvY8IN.js +70 -0
  43. package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-CfNV1ysF.js → keyboard-shortcuts-settings-Cwinks_5.js} +1 -1
  44. package/dist/gateway/static/root/assets/local-app-workbench-page-0wntj9mK.js +2 -0
  45. package/dist/gateway/static/root/assets/{local-apps-page-MPd4Fl_l.js → local-apps-page-WeKm5vZ0.js} +1 -1
  46. package/dist/gateway/static/root/assets/{locale-store-D_dq_0LV.js → locale-store-8k4mvYGL.js} +1 -1
  47. package/dist/gateway/static/root/assets/logs-page-DFgJr7Zx.js +2 -0
  48. package/dist/gateway/static/root/assets/{management-settings-DeIjN4e0.js → management-settings-8bEvKO0R.js} +1 -1
  49. package/dist/gateway/static/root/assets/{markdown-split-BUAVv6Gu.js → markdown-split-B6wQhwLV.js} +1 -1
  50. package/dist/gateway/static/root/assets/{markdown-view-CPkBOY3h.js → markdown-view-D02mJdoU.js} +1 -1
  51. package/dist/gateway/static/root/assets/{media-plugins-DFsVnOY-.js → media-plugins-BSEPuO76.js} +1 -1
  52. package/dist/gateway/static/root/assets/{messages-D4qOHT4D.js → messages-CoovNoMn.js} +2 -2
  53. package/dist/gateway/static/root/assets/{mobile-device-api-D9JevyCE.js → mobile-device-api-CvjVB_TB.js} +1 -1
  54. package/dist/gateway/static/root/assets/models-hub-cache-CYr6LzvY.js +1 -0
  55. package/dist/gateway/static/root/assets/{notes-api-BWSfTP1w.js → notes-api-p2Pncp2B.js} +1 -1
  56. package/dist/gateway/static/root/assets/{notes-page-CV8UJj7g.js → notes-page-CPgbeO1u.js} +1 -1
  57. package/dist/gateway/static/root/assets/notes-workbench-DIXCtwhs.js +4 -0
  58. package/dist/gateway/static/root/assets/{oauth-api-DnanEBsf.js → oauth-api-DpLONwfK.js} +1 -1
  59. package/dist/gateway/static/root/assets/{product-open-page-zNg1KOy2.js → product-open-page-DNHWn_26.js} +1 -1
  60. package/dist/gateway/static/root/assets/project-detail-page-6jgRJg4w.js +1 -0
  61. package/dist/gateway/static/root/assets/projects-page-Bd4nXs4y.js +1 -0
  62. package/dist/gateway/static/root/assets/{registry-api-BeVzfRvs.js → registry-api-B7b0LJgI.js} +1 -1
  63. package/dist/gateway/static/root/assets/remote-access-hub-C_5-u9U-.js +1 -0
  64. package/dist/gateway/static/root/assets/{runtime-tools-settings-panel-BxoioSvT.js → runtime-tools-settings-panel-D3TCJGy3.js} +1 -1
  65. package/dist/gateway/static/root/assets/{schema-form-vzbmscrE.js → schema-form-DUAlwNoO.js} +1 -1
  66. package/dist/gateway/static/root/assets/sessions-page-BqYAR86S.js +1 -0
  67. package/dist/gateway/static/root/assets/settings-advanced-gate-a146I0uZ.js +1 -0
  68. package/dist/gateway/static/root/assets/{settings-form-section-x9k7MWD3.js → settings-form-section-D7nQ3Lfu.js} +1 -1
  69. package/dist/gateway/static/root/assets/{settings-loading-skeleton-BLFOD9h9.js → settings-loading-skeleton-CwGM9zKH.js} +1 -1
  70. package/dist/gateway/static/root/assets/{settings-page-CwgTooar.js → settings-page-CMAkMc3I.js} +1 -1
  71. package/dist/gateway/static/root/assets/setup-status-panel-rhaERxAL.js +1 -0
  72. package/dist/gateway/static/root/assets/{share-preview-page-COTrPAd-.js → share-preview-page-BzkXvuH6.js} +1 -1
  73. package/dist/gateway/static/root/assets/shares-settings-BvsM3Im5.js +1 -0
  74. package/dist/gateway/static/root/assets/skill-reload-api-u7E0gqFo.js +1 -0
  75. package/dist/gateway/static/root/assets/{skills-page-D9wM6_Jn.js → skills-page-DJRr5HFw.js} +1 -1
  76. package/dist/gateway/static/root/assets/{skills-page.utils-PtlQLM5a.js → skills-page.utils-DcrBe39q.js} +1 -1
  77. package/dist/gateway/static/root/assets/{system-settings-panel-BR74mNEK.js → system-settings-panel-BUsykmjx.js} +1 -1
  78. package/dist/gateway/static/root/assets/task-detail-page-C9QgWfYG.js +1 -0
  79. package/dist/gateway/static/root/assets/{theme-store-Bud54q9e.js → theme-store-CpcBI73Q.js} +1 -1
  80. package/dist/gateway/static/root/assets/{use-autosave-DH47MH7s.js → use-autosave-DDEypreQ.js} +1 -1
  81. package/dist/gateway/static/root/assets/user-context-page-Ch0cfQfI.js +1 -0
  82. package/dist/gateway/static/root/assets/{voice-api-key-field-B59GaqRa.js → voice-api-key-field-BLa_vdXE.js} +1 -1
  83. package/dist/gateway/static/root/assets/{voice-config-api-C0ahrZ0e.js → voice-config-api-ynE-vY6I.js} +1 -1
  84. package/dist/gateway/static/root/assets/{work-discovery-overlay-DTn4RCcx.js → work-discovery-overlay-DS3w1Fyq.js} +1 -1
  85. package/dist/gateway/static/root/assets/{workflow-run-setup-panel-yDFoEX06.js → workflow-run-setup-panel-jQl_wmYC.js} +2 -2
  86. package/dist/gateway/static/root/assets/workflows-page-DyTwkdzs.js +5 -0
  87. package/dist/gateway/static/root/index.html +13 -13
  88. package/dist/package.js +1 -1
  89. package/dist/src/agent/agent-manager.js +4 -4
  90. package/dist/src/agent/background-review/run-background-review.js +1 -1
  91. package/dist/src/agent/embedded/model-runtime.js +1 -1
  92. package/dist/src/agent/embedded/run-for-session.js +57 -8
  93. package/dist/src/agent/embedded/types.d.ts +5 -0
  94. package/dist/src/agent/image/tool-model-config.js +1 -1
  95. package/dist/src/agent/ipc/inbox.js +1 -1
  96. package/dist/src/agent/mcp/oauth/mcp-oauth-store.js +1 -1
  97. package/dist/src/agent/prompt/sections/tooling.js +1 -1
  98. package/dist/src/agent/service.js +2 -2
  99. package/dist/src/agent/session/session-inspector.js +1 -1
  100. package/dist/src/agent/skills/hub-lock.js +1 -1
  101. package/dist/src/agent/tasks/task-judge-service.js +3 -3
  102. package/dist/src/agent/tools/create-share-tool.js +1 -1
  103. package/dist/src/agent/tools/exec-command.d.ts +2 -0
  104. package/dist/src/agent/tools/exec-command.js +23 -7
  105. package/dist/src/agent/tools/factory.js +1 -1
  106. package/dist/src/agent/tools/index.js +1 -1
  107. package/dist/src/agent/tools/publish-artifacts.d.ts +6 -0
  108. package/dist/src/agent/tools/publish-artifacts.js +55 -42
  109. package/dist/src/agent/tools/send-media.js +1 -1
  110. package/dist/src/agent/tools/workflow-tool.js +1 -1
  111. package/dist/src/agent/tools/write.js +1 -1
  112. package/dist/src/agent/tools/xopc-use-tool.js +4 -4
  113. package/dist/src/ai-assist/text-assist.js +1 -1
  114. package/dist/src/auth/credentials.js +2 -2
  115. package/dist/src/auth/profiles/store.js +1 -1
  116. package/dist/src/auth/sync-provider-auth.js +2 -2
  117. package/dist/src/browser/providers/browser-ext-install.js +1 -1
  118. package/dist/src/cli/commands/channels.js +1 -1
  119. package/dist/src/cli/commands/connectors.js +1 -1
  120. package/dist/src/cli/commands/doctor/checks/provider-auth.js +1 -1
  121. package/dist/src/cli/commands/extension-marketplace.js +1 -1
  122. package/dist/src/cli/commands/gateway/lifecycle.js +1 -1
  123. package/dist/src/cli/commands/models.js +2 -2
  124. package/dist/src/cli/commands/onboard.js +1 -1
  125. package/dist/src/config/index.js +4 -4
  126. package/dist/src/config/loader.js +1 -1
  127. package/dist/src/config/models-json.js +1 -1
  128. package/dist/src/extensions/lockfile.js +1 -1
  129. package/dist/src/extensions/sdk/index.js +1 -1
  130. package/dist/src/files/file-service.d.ts +10 -2
  131. package/dist/src/files/file-service.js +33 -12
  132. package/dist/src/gateway/agent-builtin-tools.d.ts +1 -1
  133. package/dist/src/gateway/agents-admin.js +1 -1
  134. package/dist/src/gateway/chat-stream/mapper.js +5 -0
  135. package/dist/src/gateway/file-path-classifier.d.ts +30 -0
  136. package/dist/src/gateway/file-path-classifier.js +105 -0
  137. package/dist/src/gateway/file-reference-errors.d.ts +6 -0
  138. package/dist/src/gateway/file-reference-errors.js +21 -0
  139. package/dist/src/gateway/file-reference-registry.d.ts +34 -0
  140. package/dist/src/gateway/file-reference-registry.js +45 -0
  141. package/dist/src/gateway/file-space-service.d.ts +4 -0
  142. package/dist/src/gateway/file-space-service.js +19 -0
  143. package/dist/src/gateway/hono/app.js +3 -1
  144. package/dist/src/gateway/hono/lib/config-payload.js +1 -1
  145. package/dist/src/gateway/hono/routes/connectors.js +1 -1
  146. package/dist/src/gateway/hono/routes/file-references.d.ts +4 -0
  147. package/dist/src/gateway/hono/routes/file-references.js +98 -0
  148. package/dist/src/gateway/hono/routes/files.js +39 -17
  149. package/dist/src/gateway/hono/routes/html-preview.d.ts +4 -0
  150. package/dist/src/gateway/hono/routes/html-preview.js +45 -0
  151. package/dist/src/gateway/hono/routes/models.js +1 -1
  152. package/dist/src/gateway/hono/routes/shares.js +18 -10
  153. package/dist/src/gateway/hono/routes/tasks.js +3 -3
  154. package/dist/src/gateway/security/csp.d.ts +1 -1
  155. package/dist/src/gateway/security/csp.js +2 -2
  156. package/dist/src/gateway/service/config-coordinator.js +1 -1
  157. package/dist/src/gateway/service/run-gateway-agent.js +0 -35
  158. package/dist/src/gateway/service.js +8 -8
  159. package/dist/src/infra/update-restart.js +1 -1
  160. package/dist/src/providers/auth-runtime/resolve-auth.js +1 -1
  161. package/dist/src/providers/index.js +2 -2
  162. package/dist/src/providers/model-registry.js +1 -1
  163. package/dist/src/providers/register-runtime-providers.js +1 -1
  164. package/dist/src/session/store.js +4 -2
  165. package/dist/src/session/turn-outcome-projector.d.ts +9 -0
  166. package/dist/src/session/turn-outcome-projector.js +209 -0
  167. package/dist/src/share/hosted-session-share.js +1 -1
  168. package/dist/src/storage/sqlite/config-repository.d.ts +5 -0
  169. package/dist/src/storage/sqlite/config-repository.js +8 -1
  170. package/dist/src/tasks/home-query-service.js +2 -2
  171. package/dist/src/tasks/task-signal-service.js +1 -1
  172. package/dist/src/tui/backends/embedded-backend.js +1 -1
  173. package/dist/src/tui/tui-startup-resources.js +1 -1
  174. package/dist/src/tui/tui.js +1 -1
  175. package/dist/src/workflows/service/workflow-run-service.js +2 -2
  176. package/package.json +4 -4
  177. package/dist/gateway/static/root/assets/agent-defaults-settings-panel-JrK80PvP.js +0 -1
  178. package/dist/gateway/static/root/assets/agents-CJYEnz94.js +0 -1
  179. package/dist/gateway/static/root/assets/apps-page-Bt8_sWtU.js +0 -1
  180. package/dist/gateway/static/root/assets/automations-workspace-86UTesz3.js +0 -6
  181. package/dist/gateway/static/root/assets/browser-settings-page-DfMU7yXv.js +0 -8
  182. package/dist/gateway/static/root/assets/capabilities-settings-panel-lsHRuBlX.js +0 -7
  183. package/dist/gateway/static/root/assets/channels-settings-CxR_clkO.js +0 -1
  184. package/dist/gateway/static/root/assets/connectors-page-CpNLyKB4.js +0 -2
  185. package/dist/gateway/static/root/assets/heartbeat-settings-DGq9q4b6.js +0 -1
  186. package/dist/gateway/static/root/assets/home-page-Dj4B1wb3.js +0 -1
  187. package/dist/gateway/static/root/assets/index-BGf-TiZj.js +0 -70
  188. package/dist/gateway/static/root/assets/local-app-workbench-page-IJg75WaN.js +0 -2
  189. package/dist/gateway/static/root/assets/logs-page-NsfoazgK.js +0 -2
  190. package/dist/gateway/static/root/assets/models-hub-cache-C5y2gzMU.js +0 -1
  191. package/dist/gateway/static/root/assets/notes-workbench-DrjMVgEI.js +0 -4
  192. package/dist/gateway/static/root/assets/project-detail-page-tfcGYL-O.js +0 -1
  193. package/dist/gateway/static/root/assets/projects-page-BkjnXLdJ.js +0 -1
  194. package/dist/gateway/static/root/assets/remote-access-hub-BXn-3n2d.js +0 -1
  195. package/dist/gateway/static/root/assets/sessions-page-MLNjrOkw.js +0 -1
  196. package/dist/gateway/static/root/assets/settings-advanced-gate-ByNLBY2q.js +0 -1
  197. package/dist/gateway/static/root/assets/setup-status-panel-bnueBZg8.js +0 -1
  198. package/dist/gateway/static/root/assets/shares-settings-D-v_psqL.js +0 -1
  199. package/dist/gateway/static/root/assets/skill-reload-api-DJICr4wh.js +0 -1
  200. package/dist/gateway/static/root/assets/task-detail-page-B-rmeE8t.js +0 -1
  201. package/dist/gateway/static/root/assets/user-context-page-B6eyxxOT.js +0 -1
  202. package/dist/gateway/static/root/assets/workflows-page-BOwSbfWm.js +0 -5
  203. package/dist/src/gateway/chat-stream/turn-outcome-collector.d.ts +0 -16
  204. package/dist/src/gateway/chat-stream/turn-outcome-collector.js +0 -146
@@ -0,0 +1,45 @@
1
+ import { randomUUID } from "node:crypto";
2
+ //#region src/gateway/file-reference-registry.ts
3
+ const DEFAULT_TTL_MS = 1800 * 1e3;
4
+ var FileReferenceRegistry = class {
5
+ refs = /* @__PURE__ */ new Map();
6
+ register(input) {
7
+ this.expire();
8
+ const id = randomUUID();
9
+ const ref = {
10
+ id,
11
+ absolutePath: input.absolutePath,
12
+ sessionKey: input.sessionKey?.trim() || void 0,
13
+ scope: input.scope,
14
+ locationKind: input.locationKind,
15
+ capabilities: [...new Set(input.capabilities)],
16
+ expiresAt: Date.now() + (input.ttlMs ?? DEFAULT_TTL_MS)
17
+ };
18
+ this.refs.set(id, ref);
19
+ return ref;
20
+ }
21
+ resolve(id) {
22
+ this.expire();
23
+ const ref = this.refs.get(id);
24
+ if (!ref) return null;
25
+ if (ref.expiresAt <= Date.now()) {
26
+ this.refs.delete(id);
27
+ return null;
28
+ }
29
+ return ref;
30
+ }
31
+ /**
32
+ * Remove a specific reference (used after a one-shot action like
33
+ * import-to-workspace completes so the source card can't be re-triggered).
34
+ * Returns true if the reference existed and was removed.
35
+ */
36
+ expireById(id) {
37
+ return this.refs.delete(id);
38
+ }
39
+ expire(now = Date.now()) {
40
+ for (const [id, ref] of this.refs) if (ref.expiresAt <= now) this.refs.delete(id);
41
+ }
42
+ };
43
+ const fileReferenceRegistry = new FileReferenceRegistry();
44
+ //#endregion
45
+ export { FileReferenceRegistry, fileReferenceRegistry };
@@ -0,0 +1,4 @@
1
+ import { FileSpaceService } from '../files/file-service.js';
2
+ import type { GatewayService } from './service.js';
3
+ /** Share the file registry, including session-specific spaces, across gateway routes. */
4
+ export declare function getGatewayFileSpaceService(service: GatewayService): FileSpaceService;
@@ -0,0 +1,19 @@
1
+ import { listSessionWorkspaceOverrides } from "../storage/sqlite/config-repository.js";
2
+ import { effectiveWorkspacePathForSession } from "../session/session-workspace.js";
3
+ import { getProjectForSession } from "../projects/workspace.js";
4
+ import { FileSpaceService } from "../files/file-service.js";
5
+ //#region src/gateway/file-space-service.ts
6
+ const services = /* @__PURE__ */ new WeakMap();
7
+ /** Share the file registry, including session-specific spaces, across gateway routes. */
8
+ function getGatewayFileSpaceService(service) {
9
+ const existing = services.get(service);
10
+ if (existing) return existing;
11
+ const created = new FileSpaceService(() => service.currentConfig, service.projects, (sessionKey) => service.sessions.getEffectiveWorkspacePath(sessionKey), () => listSessionWorkspaceOverrides().map(({ sessionKey, workingDirectoryOverride }) => ({
12
+ sessionKey,
13
+ root: effectiveWorkspacePathForSession(service.currentConfig, sessionKey, { workingDirectoryOverride }, getProjectForSession(sessionKey))
14
+ })));
15
+ services.set(service, created);
16
+ return created;
17
+ }
18
+ //#endregion
19
+ export { getGatewayFileSpaceService };
@@ -21,6 +21,7 @@ import { resetLazyRouteBundlesForTests } from "./routes/lazy-fallback.js";
21
21
  import { registerAuthenticatedRoutes } from "./routes/index.js";
22
22
  import { prewarmStaticUiCache } from "./lib/static-ui.js";
23
23
  import { registerPublicGatewayRoutes } from "./routes/public-gateway.js";
24
+ import { registerPublicHtmlPreviewRoute } from "./routes/html-preview.js";
24
25
  import { registerSiteShareMiddleware } from "../../share/site-share-router.js";
25
26
  import { Hono } from "hono";
26
27
  import { cors } from "hono/cors";
@@ -109,7 +110,7 @@ function createHonoApp(config) {
109
110
  const gatewayConsoleCsp = buildGatewayConsoleCspHeader();
110
111
  app.use(createMiddleware(async (c, next) => {
111
112
  await next();
112
- if (isExtensionGatewayUiAssetPath(c.req.path)) return;
113
+ if (isExtensionGatewayUiAssetPath(c.req.path) || c.req.path === "/api/preview/html" && (c.req.method === "GET" || c.req.method === "HEAD")) return;
113
114
  c.header("X-Frame-Options", "DENY");
114
115
  c.header("X-Content-Type-Options", "nosniff");
115
116
  c.header("Referrer-Policy", "strict-origin-when-cross-origin");
@@ -194,6 +195,7 @@ function createHonoApp(config) {
194
195
  })(c, next);
195
196
  });
196
197
  registerPublicGatewayRoutes(app, service);
198
+ registerPublicHtmlPreviewRoute(app);
197
199
  registerDeviceAuthPublicRoutes(app);
198
200
  registerPublicExtensionAssetRoutes(app, service);
199
201
  registerPublicLocalAppPreviewRoutes(app, service);
@@ -1,9 +1,9 @@
1
1
  import { ContextCompactionPolicySchema, UserContextDreamingSchema, UserContextPrivacySchema, init_config } from "../../../user-context/config.js";
2
2
  import { init_agent_scope, listAgentEntries, normalizeAgentId, resolveDefaultAgentId } from "../../../agent/agent-scope.js";
3
3
  import { resolveModelsJsonPath } from "../../../config/paths.js";
4
- import { buildChannelCatalogForConfig, buildChannelCatalogFromSnapshot, getChannelSetupStatus, init_channel_catalog_service } from "../../../channels/catalog/channel-catalog-service.js";
5
4
  import { init_models_json, loadModelsJson } from "../../../config/models-json.js";
6
5
  import { CredentialResolver, init_credentials } from "../../../auth/credentials.js";
6
+ import { buildChannelCatalogForConfig, buildChannelCatalogFromSnapshot, getChannelSetupStatus, init_channel_catalog_service } from "../../../channels/catalog/channel-catalog-service.js";
7
7
  import { getProviderRegistry, init_plugin_registry } from "../../../providers/plugin-registry.js";
8
8
  import { getAllProviders, init_providers, isProviderConfigured } from "../../../providers/index.js";
9
9
  import { normalizeConfiguredMcpServers } from "../../../config/mcp-config-normalize.js";
@@ -15,7 +15,6 @@ import { listUnderstandingSourceGrants, revokeUnderstandingSourceGrant } from ".
15
15
  import { recordConnectorHealthUsage } from "../../../connectors/usage.js";
16
16
  import { getConnectorInstance, getInstalledConnectorDefinition, listConnectorInstances } from "../../../connectors/instances.js";
17
17
  import { installConnector, installConnectorDefinition, uninstallConnector, updateConnectorConfig } from "../../../connectors/install.js";
18
- import { ConfigPersistenceError, persistConfigMutation } from "../../../config/config-mutation.js";
19
18
  import { getMcpOAuthManager } from "../../../agent/mcp/oauth/mcp-oauth-manager.js";
20
19
  import { startConnectorAuthorization } from "../../../connectors/auth-provider-registry.js";
21
20
  import { composioLogoResponse } from "../../../connectors/composio-logo.js";
@@ -25,6 +24,7 @@ import { ingestLocalFolderSource } from "../../../connectors/connected-source-in
25
24
  import { previewConnectorDefinition, testConnectorInstance } from "../../../connectors/health.js";
26
25
  import { setConnectorEnabled } from "../../../connectors/lifecycle.js";
27
26
  import { projectComposioConnectionStatus } from "../../../connectors/runtime-status.js";
27
+ import { ConfigPersistenceError, persistConfigMutation } from "../../../config/config-mutation.js";
28
28
  import { applySourceRevocationChoices, sourceRevocationImpact } from "./understanding-sources.js";
29
29
  //#region src/gateway/hono/routes/connectors.ts
30
30
  init_agent_scope();
@@ -0,0 +1,4 @@
1
+ import type { Hono } from 'hono';
2
+ import type { AuthenticatedRouteDeps } from './deps.js';
3
+ /** Metadata and explicit desktop actions for links outside a managed workspace. */
4
+ export declare function registerFileReferenceRoutes(app: Hono, deps: AuthenticatedRouteDeps): void;
@@ -0,0 +1,98 @@
1
+ import { FileServiceError } from "../../../files/file-service.js";
2
+ import { buildFilePathClassifierContext, classifyFileLocation, displayNameForPath, fileRefSessionKeysMatch, resolveFileReferenceCandidate } from "../../file-path-classifier.js";
3
+ import { classifyFileReferenceFsError } from "../../file-reference-errors.js";
4
+ import { FileReferenceRegistry } from "../../file-reference-registry.js";
5
+ import { getGatewayFileSpaceService } from "../../file-space-service.js";
6
+ import { realpath, stat } from "node:fs/promises";
7
+ //#region src/gateway/hono/routes/file-references.ts
8
+ /** Metadata and explicit desktop actions for links outside a managed workspace. */
9
+ function registerFileReferenceRoutes(app, deps) {
10
+ const files = getGatewayFileSpaceService(deps.service);
11
+ const refs = new FileReferenceRegistry();
12
+ app.post("/api/files/resolve-reference", async (c) => {
13
+ const body = await c.req.json();
14
+ if (!body.spaceId || !body.path?.trim()) return c.json({ error: { message: "spaceId and path are required" } }, 400);
15
+ try {
16
+ const space = await files.get(body.spaceId);
17
+ const sessionKey = body.sessionKey?.trim();
18
+ const agentId = space.bindings.find((binding) => binding.kind === "agent")?.id;
19
+ const ctx = {
20
+ ...buildFilePathClassifierContext(deps.service.currentConfig, sessionKey || (agentId ? `agent:${agentId}:main` : void 0)),
21
+ workspaceRoot: space.root
22
+ };
23
+ const inputPath = body.path.trim();
24
+ const base = {
25
+ inputPath,
26
+ displayName: displayNameForPath(inputPath)
27
+ };
28
+ const { candidate, invalid } = await resolveFileReferenceCandidate(inputPath, space.root, ctx);
29
+ if (!candidate || invalid) return c.json({ reference: {
30
+ ...base,
31
+ scope: "invalid",
32
+ exists: false,
33
+ capabilities: []
34
+ } });
35
+ let absolutePath;
36
+ let info;
37
+ try {
38
+ absolutePath = await realpath(candidate);
39
+ info = await stat(absolutePath);
40
+ } catch (error) {
41
+ const failure = classifyFileReferenceFsError(error);
42
+ if (failure.code !== "FILE_NOT_FOUND") return c.json({ error: { message: failure.message } }, failure.status === 403 ? 403 : 500);
43
+ return c.json({ reference: {
44
+ ...base,
45
+ scope: "missing",
46
+ absolutePath: candidate,
47
+ exists: false,
48
+ capabilities: ["copyPath"]
49
+ } });
50
+ }
51
+ const classified = classifyFileLocation(absolutePath, ctx);
52
+ const capabilities = info.isFile() || info.isDirectory() ? [
53
+ "openExternal",
54
+ "revealInFolder",
55
+ "copyPath"
56
+ ] : ["copyPath"];
57
+ const ref = refs.register({
58
+ absolutePath,
59
+ sessionKey,
60
+ ...classified,
61
+ capabilities
62
+ });
63
+ return c.json({ reference: {
64
+ ...base,
65
+ ...classified,
66
+ fileRefId: ref.id,
67
+ absolutePath,
68
+ exists: true,
69
+ isDirectory: info.isDirectory(),
70
+ capabilities,
71
+ mtimeMs: info.mtimeMs
72
+ } });
73
+ } catch (error) {
74
+ if (error instanceof FileServiceError) return c.json({ error: { message: error.message } }, error.status);
75
+ throw error;
76
+ }
77
+ });
78
+ app.post("/api/files/references/:id/action", async (c) => {
79
+ const ref = refs.resolve(c.req.param("id"));
80
+ if (!ref) return c.json({ error: { message: "File reference expired" } }, 404);
81
+ const body = await c.req.json();
82
+ if (!fileRefSessionKeysMatch(ref.sessionKey, body.sessionKey)) return c.json({ error: { message: "File reference forbidden" } }, 403);
83
+ if (body.action !== "openExternal" && body.action !== "revealInFolder" || !ref.capabilities.includes(body.action)) return c.json({ error: { message: "Action not allowed" } }, 403);
84
+ try {
85
+ if (await realpath(ref.absolutePath) !== ref.absolutePath) return c.json({ error: { message: "File reference changed" } }, 409);
86
+ const info = await stat(ref.absolutePath);
87
+ if (!info.isFile() && !info.isDirectory()) return c.json({ error: { message: "Not a file or directory" } }, 400);
88
+ return c.json({
89
+ absolutePath: ref.absolutePath,
90
+ isDirectory: info.isDirectory()
91
+ });
92
+ } catch {
93
+ return c.json({ error: { message: "File not found" } }, 404);
94
+ }
95
+ });
96
+ }
97
+ //#endregion
98
+ export { registerFileReferenceRoutes };
@@ -1,8 +1,10 @@
1
- import { FileServiceError, FileSpaceService, fileResourceFromPath, resolveFilePath } from "../../../files/file-service.js";
1
+ import { FileServiceError, fileResourceFromPath, resolveFilePath } from "../../../files/file-service.js";
2
+ import { getGatewayFileSpaceService } from "../../file-space-service.js";
3
+ import { fuzzySearchWorkspaceFiles, fuzzySubsequenceScore } from "../../workspace-file-search.js";
4
+ import { registerFileReferenceRoutes } from "./file-references.js";
2
5
  import { basename, join } from "node:path";
3
6
  import { readFile, writeFile } from "node:fs/promises";
4
7
  //#region src/gateway/hono/routes/files.ts
5
- const services = /* @__PURE__ */ new WeakMap();
6
8
  const mutationLocks = /* @__PURE__ */ new Map();
7
9
  async function withMutationLock(key, action) {
8
10
  const previous = mutationLocks.get(key) ?? Promise.resolve();
@@ -20,13 +22,6 @@ async function withMutationLock(key, action) {
20
22
  if (mutationLocks.get(key) === tail) mutationLocks.delete(key);
21
23
  }
22
24
  }
23
- function getFiles(deps) {
24
- const existing = services.get(deps.service);
25
- if (existing) return existing;
26
- const created = new FileSpaceService(() => deps.service.currentConfig, deps.service.projects, (sessionKey) => deps.service.agentService.getResolvedWorkspaceForSession(sessionKey));
27
- services.set(deps.service, created);
28
- return created;
29
- }
30
25
  function publicSpace(space) {
31
26
  const { root: _root, ...value } = space;
32
27
  return value;
@@ -39,22 +34,28 @@ function contentDisposition(disposition, fileName) {
39
34
  const sanitized = fileName.replace(/[\u0000-\u001f\u007f]/g, "_");
40
35
  return `${disposition}; filename="${sanitized.replace(/[^\x20-\x7e]/g, "_").replace(/["\\]/g, "_")}"; filename*=UTF-8''${encodeURIComponent(sanitized).replace(/['()*]/g, (character) => `%${character.charCodeAt(0).toString(16).toUpperCase()}`)}`;
41
36
  }
42
- async function collectFiles(files, spaceId, query, max) {
37
+ async function collectFiles(files, spaceId, max) {
43
38
  const output = [];
44
39
  const directories = [""];
40
+ const visited = /* @__PURE__ */ new Set();
41
+ const space = await files.get(spaceId);
45
42
  while (directories.length && output.length < max) {
46
43
  const directory = directories.shift();
44
+ const canonical = await resolveFilePath(space.root, directory).catch(() => null);
45
+ if (!canonical || visited.has(canonical)) continue;
46
+ visited.add(canonical);
47
47
  const children = await files.children(spaceId, directory);
48
48
  for (const child of children) {
49
49
  if (child.kind === "directory") directories.push(child.relativePath);
50
- else if (!query || child.name.toLocaleLowerCase().includes(query)) output.push(child);
50
+ else output.push(child);
51
51
  if (output.length >= max) break;
52
52
  }
53
53
  }
54
54
  return output;
55
55
  }
56
56
  function registerFilesRoutes(authenticated, deps) {
57
- const files = getFiles(deps);
57
+ const files = getGatewayFileSpaceService(deps.service);
58
+ registerFileReferenceRoutes(authenticated, deps);
58
59
  authenticated.get("/api/files/spaces", async (c) => {
59
60
  try {
60
61
  return c.json({ spaces: (await files.list()).map(publicSpace) });
@@ -62,6 +63,13 @@ function registerFilesRoutes(authenticated, deps) {
62
63
  return errorResponse(c, error);
63
64
  }
64
65
  });
66
+ authenticated.get("/api/files/default-space", async (c) => {
67
+ try {
68
+ return c.json({ space: publicSpace(await files.defaultSpace()) });
69
+ } catch (error) {
70
+ return errorResponse(c, error);
71
+ }
72
+ });
65
73
  authenticated.get("/api/files/contexts/:kind/:id", async (c) => {
66
74
  try {
67
75
  const kind = c.req.param("kind");
@@ -82,7 +90,7 @@ function registerFilesRoutes(authenticated, deps) {
82
90
  try {
83
91
  const limit = Math.min(100, Math.max(1, Number(c.req.query("limit")) || 50));
84
92
  const spaces = await files.list();
85
- const items = (await Promise.all(spaces.map((space) => collectFiles(files, space.id, "", 5e3)))).flat().sort((a, b) => b.modifiedAt - a.modifiedAt).slice(0, limit);
93
+ const items = (await Promise.all(spaces.map((space) => collectFiles(files, space.id, 5e3)))).flat().sort((a, b) => b.modifiedAt - a.modifiedAt).slice(0, limit);
86
94
  return c.json({ items });
87
95
  } catch (error) {
88
96
  return errorResponse(c, error);
@@ -91,11 +99,23 @@ function registerFilesRoutes(authenticated, deps) {
91
99
  authenticated.get("/api/files/search", async (c) => {
92
100
  try {
93
101
  const query = (c.req.query("q") ?? "").trim().toLocaleLowerCase();
94
- if (!query) return c.json({ items: [] });
95
102
  const limit = Math.min(100, Math.max(1, Number(c.req.query("limit")) || 50));
96
103
  const requestedSpaceId = c.req.query("spaceId");
97
104
  const spaces = requestedSpaceId ? [await files.get(requestedSpaceId)] : await files.list();
98
- const items = (await Promise.all(spaces.map((space) => collectFiles(files, space.id, query, 5e3)))).flat().sort((a, b) => b.modifiedAt - a.modifiedAt).slice(0, limit);
105
+ const items = (await Promise.all(spaces.map(async (space) => {
106
+ const candidates = await fuzzySearchWorkspaceFiles(space.root, query, limit);
107
+ return Promise.all(candidates.map(async (candidate) => {
108
+ try {
109
+ return await fileResourceFromPath(space, await resolveFilePath(space.root, candidate.path));
110
+ } catch (error) {
111
+ if (error instanceof FileServiceError) return null;
112
+ throw error;
113
+ }
114
+ }));
115
+ }))).flat().filter((file) => file !== null).map((file) => ({
116
+ file,
117
+ score: Math.max(fuzzySubsequenceScore(query, file.relativePath) ?? -Infinity, fuzzySubsequenceScore(query, file.name) ?? -Infinity)
118
+ })).sort((a, b) => b.score - a.score || a.file.relativePath.localeCompare(b.file.relativePath)).slice(0, limit).map((row) => row.file);
99
119
  return c.json({ items });
100
120
  } catch (error) {
101
121
  return errorResponse(c, error);
@@ -144,12 +164,14 @@ function registerFilesRoutes(authenticated, deps) {
144
164
  const body = await c.req.json();
145
165
  if (typeof body.content !== "string" || !body.revision) throw new FileServiceError(400, "content and revision are required");
146
166
  const id = c.req.param("id");
147
- const resource = await withMutationLock(id, async () => {
167
+ const initial = await files.resource(id);
168
+ const resource = await withMutationLock(initial.absolutePath, async () => {
148
169
  const current = await files.resource(id);
170
+ if (current.absolutePath !== initial.absolutePath) throw new FileServiceError(409, "File has changed");
149
171
  if (!current.resource.capabilities.includes("edit")) throw new FileServiceError(403, "File is not editable");
150
172
  if (body.revision !== current.resource.revision) throw new FileServiceError(409, "File has changed");
151
173
  await writeFile(current.absolutePath, body.content, "utf8");
152
- return fileResourceFromPath(current.space, current.absolutePath);
174
+ return (await files.resource(id)).resource;
153
175
  });
154
176
  return c.json({ resource });
155
177
  } catch (error) {
@@ -0,0 +1,4 @@
1
+ import type { Hono } from 'hono';
2
+ export declare const HTML_PREVIEW_PATH = "/api/preview/html";
3
+ /** Public static shell only: file contents arrive from the authenticated UI, never a URL. */
4
+ export declare function registerPublicHtmlPreviewRoute(app: Hono): void;
@@ -0,0 +1,45 @@
1
+ //#region src/gateway/hono/routes/html-preview.ts
2
+ const HTML_PREVIEW_PATH = "/api/preview/html";
3
+ const HTML_PREVIEW_SANDBOX = "allow-scripts allow-popups allow-popups-to-escape-sandbox allow-downloads allow-forms allow-modals";
4
+ const HTML_PREVIEW_CSP = [
5
+ "default-src 'none'",
6
+ `sandbox ${HTML_PREVIEW_SANDBOX}`,
7
+ "script-src 'unsafe-inline' https: blob:",
8
+ "style-src 'unsafe-inline' https:",
9
+ "img-src https: data: blob:",
10
+ "media-src https: data: blob:",
11
+ "font-src https: data:",
12
+ "connect-src https:",
13
+ "frame-src 'self' about:",
14
+ "frame-ancestors 'self'",
15
+ "base-uri 'none'",
16
+ "object-src 'none'",
17
+ "form-action https:"
18
+ ].join("; ");
19
+ const HTML_PREVIEW_SHELL = `<!doctype html>
20
+ <html><head><meta charset="utf-8"><title>HTML preview</title>
21
+ <style>html,body{height:100%;margin:0;overflow:hidden}iframe{display:block;width:100%;height:100%;border:0}</style>
22
+ </head><body><iframe title="HTML preview" sandbox="${HTML_PREVIEW_SANDBOX}"></iframe>
23
+ <script>
24
+ const preview = document.querySelector('iframe');
25
+ window.addEventListener('message', (event) => {
26
+ if (event.source !== window.parent || window.parent === window) return;
27
+ const data = event.data;
28
+ if (!data || data.type !== 'xopc-html-preview' || typeof data.html !== 'string') return;
29
+ // Repeated load notifications must not restart scripts or discard preview state.
30
+ if (preview.srcdoc !== data.html) preview.srcdoc = data.html;
31
+ });
32
+ <\/script></body></html>`;
33
+ /** Public static shell only: file contents arrive from the authenticated UI, never a URL. */
34
+ function registerPublicHtmlPreviewRoute(app) {
35
+ app.get(HTML_PREVIEW_PATH, (c) => c.html(HTML_PREVIEW_SHELL, 200, {
36
+ "Content-Security-Policy": HTML_PREVIEW_CSP,
37
+ "X-Frame-Options": "SAMEORIGIN",
38
+ "X-Content-Type-Options": "nosniff",
39
+ "Referrer-Policy": "no-referrer",
40
+ "Permissions-Policy": "camera=(), microphone=(), geolocation=()",
41
+ "Cache-Control": "no-store"
42
+ }));
43
+ }
44
+ //#endregion
45
+ export { HTML_PREVIEW_PATH, registerPublicHtmlPreviewRoute };
@@ -1,5 +1,5 @@
1
- import { resolveModelsJsonPath } from "../../../config/paths.js";
2
1
  import { init_resolve_config_value, testApiKeyResolution } from "../../../config/resolve-config-value.js";
2
+ import { resolveModelsJsonPath } from "../../../config/paths.js";
3
3
  import { init_models_json, loadModelsJson, saveModelsJson, validateModelsConfig } from "../../../config/models-json.js";
4
4
  import { getModelCatalogStore, init_model_catalog_store } from "../../../providers/model-catalog-store.js";
5
5
  import { getModelRegistry } from "../../../providers/model-registry.js";
@@ -1,5 +1,6 @@
1
1
  import { loadCompactionSourceSnapshot } from "../../../storage/sqlite/transcript-repository.js";
2
2
  import "../../../storage/sqlite/index.js";
3
+ import { FileServiceError } from "../../../files/file-service.js";
3
4
  import { resolveGatewayEffectiveHost } from "../../../config/gateway-bind.js";
4
5
  import { SHARE_CONFIG_DEFAULTS } from "../../../share/share-types.js";
5
6
  import { resolveSiteShareConfig } from "../../../share/site-share-config.js";
@@ -11,6 +12,7 @@ import { resolveReverseProxyPublicUrl } from "../../public-url.js";
11
12
  import { audienceDefaults, cleanupStagedSite, decideShareKind, forgetStagedSite, makeDescription, makeTitle, probeShareTarget, rememberStagedSite, stageSingleHtmlAsSite } from "../../../share/share-auto.js";
12
13
  import { deleteThumbnail, placeholderSvg, readThumbnail, scheduleThumbnail, thumbnailContentType, thumbnailExists } from "../../../share/share-thumbnail.js";
13
14
  import { extractToken } from "../../auth.js";
15
+ import { getGatewayFileSpaceService } from "../../file-space-service.js";
14
16
  import { getClientIpFromHeaders } from "../../security/loopback.js";
15
17
  import { renderFolderLandingPage, renderShareExpiredPage, renderShareLandingPage, renderSnapshotShareLandingPage } from "../../../share/share-landing.js";
16
18
  import { SessionShareService, SessionShareSnapshotConflictError } from "../../../share/session-share-service.js";
@@ -902,10 +904,11 @@ function registerShareRoutes(authenticated, deps) {
902
904
  error: { message: "Invalid JSON" }
903
905
  }, 400);
904
906
  }
905
- const path = typeof body.path === "string" ? body.path.trim() : "";
906
- if (!path) return c.json({
907
+ const fileId = typeof body.fileId === "string" ? body.fileId.trim() : "";
908
+ let path = typeof body.path === "string" ? body.path.trim() : "";
909
+ if (!fileId && !path) return c.json({
907
910
  ok: false,
908
- error: { message: "Missing path" }
911
+ error: { message: "Missing path or fileId" }
909
912
  }, 400);
910
913
  const sessionKey = typeof body.sessionKey === "string" ? body.sessionKey.trim() : void 0;
911
914
  const agentId = typeof body.agentId === "string" ? body.agentId.trim() : void 0;
@@ -921,20 +924,25 @@ function registerShareRoutes(authenticated, deps) {
921
924
  const ttlOverride = typeof body.ttlMs === "number" ? body.ttlMs : void 0;
922
925
  const maxViewsOverride = body.maxViews === null ? null : typeof body.maxViews === "number" ? body.maxViews : void 0;
923
926
  const wantThumbnail = body.thumbnail !== false;
924
- const workspaceRoot = await resolveWorkspaceRootForShare(service, sessionKey, agentId);
925
- if (!workspaceRoot) return c.json({
926
- ok: false,
927
- error: { message: "Workspace not configured" }
928
- }, 400);
927
+ let workspaceRoot;
929
928
  let probe;
930
929
  try {
930
+ if (fileId) {
931
+ const target = await getGatewayFileSpaceService(service).resource(fileId);
932
+ workspaceRoot = target.space.root;
933
+ path = target.resource.relativePath || ".";
934
+ } else workspaceRoot = await resolveWorkspaceRootForShare(service, sessionKey, agentId);
935
+ if (!workspaceRoot) return c.json({
936
+ ok: false,
937
+ error: { message: "Workspace not configured" }
938
+ }, 400);
931
939
  probe = await probeShareTarget(workspaceRoot, path);
932
940
  } catch (err) {
933
941
  const message = err instanceof Error ? err.message : String(err);
934
942
  return c.json({
935
943
  ok: false,
936
944
  error: { message }
937
- }, 400);
945
+ }, err instanceof FileServiceError ? err.status : 400);
938
946
  }
939
947
  let decision;
940
948
  try {
@@ -1438,7 +1446,7 @@ async function createFileShareResponse(args) {
1438
1446
  }
1439
1447
  function relPathFromAbs(workspaceRoot, abs) {
1440
1448
  const root = workspaceRoot.replace(/[\\/]+$/, "");
1441
- if (abs === root) return "";
1449
+ if (abs === root) return ".";
1442
1450
  if (abs.startsWith(`${root}/`)) return abs.slice(root.length + 1);
1443
1451
  if (abs.startsWith(`${root}\\`)) return abs.slice(root.length + 1).replace(/\\/g, "/");
1444
1452
  return abs.split(/[\\/]/).pop() ?? abs;
@@ -1,12 +1,12 @@
1
1
  import { runSqliteWriteTransaction } from "../../../storage/sqlite/transaction.js";
2
+ import { TaskPhaseSchema } from "../../../../packages/gateway-contract/src/task-lifecycle.js";
3
+ import { TaskBoardPositionRequestSchema, TaskCommandRequestSchema, TaskContextInputSchema, TaskCreateRequestSchema, TaskDependencyUpdateRequestSchema, TaskHandoffRequestSchema, TaskPatchRequestSchema } from "../../../../packages/gateway-contract/src/tasks.js";
4
+ import { ProjectMonitoringUpdateSchema } from "../../../../packages/gateway-contract/src/project-monitoring.js";
2
5
  import { TaskRepository } from "../../../tasks/task-repository.js";
3
6
  import { TaskConversationRepository } from "../../../tasks/task-conversation-repository.js";
4
7
  import { enqueueTaskChangedEvent } from "../../../tasks/task-change-events.js";
5
8
  import { TaskRunRepository } from "../../../tasks/task-run-repository.js";
6
9
  import { TaskReadModelProjector } from "../../../tasks/task-read-model-projector.js";
7
- import { TaskPhaseSchema } from "../../../../packages/gateway-contract/src/task-lifecycle.js";
8
- import { TaskBoardPositionRequestSchema, TaskCommandRequestSchema, TaskContextInputSchema, TaskCreateRequestSchema, TaskDependencyUpdateRequestSchema, TaskHandoffRequestSchema, TaskPatchRequestSchema } from "../../../../packages/gateway-contract/src/tasks.js";
9
- import { ProjectMonitoringUpdateSchema } from "../../../../packages/gateway-contract/src/project-monitoring.js";
10
10
  import { resolveProjectAgentId } from "../../../projects/project-agent.js";
11
11
  import { TaskContextRepository } from "../../../tasks/task-context-repository.js";
12
12
  import { TaskDependencyError, TaskDependencyService } from "../../../tasks/task-dependency-service.js";
@@ -11,7 +11,7 @@ export declare function computeInlineScriptHashes(html: string): string[];
11
11
  * - `style-src 'self' 'unsafe-inline'` (Tailwind + runtime style injection)
12
12
  * - `media-src 'self' blob:` (chat voice previews / recorded clips use blob URLs)
13
13
  * - `frame-ancestors 'none'` (prevent clickjacking)
14
- * - `frame-src 'none'` (the console never embeds frames)
14
+ * - `frame-src 'self'` (sandboxed previews and extension UI)
15
15
  * - `form-action 'self'` (prevent form submissions to untrusted origins)
16
16
  * - `base-uri 'none'` (prevent base tag hijacking)
17
17
  * - `object-src 'none'` (prevent plugin execution)
@@ -29,7 +29,7 @@ function hasSrcAttribute(openTag) {
29
29
  * - `style-src 'self' 'unsafe-inline'` (Tailwind + runtime style injection)
30
30
  * - `media-src 'self' blob:` (chat voice previews / recorded clips use blob URLs)
31
31
  * - `frame-ancestors 'none'` (prevent clickjacking)
32
- * - `frame-src 'none'` (the console never embeds frames)
32
+ * - `frame-src 'self'` (sandboxed previews and extension UI)
33
33
  * - `form-action 'self'` (prevent form submissions to untrusted origins)
34
34
  * - `base-uri 'none'` (prevent base tag hijacking)
35
35
  * - `object-src 'none'` (prevent plugin execution)
@@ -41,7 +41,7 @@ function buildGatewayConsoleCspHeader(options) {
41
41
  "base-uri 'none'",
42
42
  "object-src 'none'",
43
43
  "frame-ancestors 'none'",
44
- "frame-src 'none'",
44
+ "frame-src 'self'",
45
45
  "form-action 'self'",
46
46
  hashes?.length ? `script-src 'self' ${hashes.map((hash) => `'${hash}'`).join(" ")}` : "script-src 'self'",
47
47
  "style-src 'self' 'unsafe-inline'",
@@ -1,7 +1,7 @@
1
1
  import { createLogger } from "../../utils/logger/index.js";
2
2
  import { init_logger } from "../../utils/logger.js";
3
- import { loadConfig, saveConfig } from "../../config/loader.js";
4
3
  import { getModelRegistry } from "../../providers/model-registry.js";
4
+ import { loadConfig, saveConfig } from "../../config/loader.js";
5
5
  import { init_providers } from "../../providers/index.js";
6
6
  import { disposeAllSessionMcpRuntimes } from "../../agent/mcp/bundle-mcp-runtime.js";
7
7
  import { reloadImageGenerationProviders } from "../../agent/image/generation/provider-registry.js";
@@ -13,7 +13,6 @@ import { ChatStreamMapper } from "../chat-stream/mapper.js";
13
13
  import { resolveWebchatSessionKey } from "../resolve-webchat-session-key.js";
14
14
  import { recordExplicitRelationshipFollowUp } from "../../user-context/relationship-continuity.js";
15
15
  import { coalesceThinkingDeltas } from "../chat-stream/thinking-delta-coalescer.js";
16
- import { TurnOutcomeCollector } from "../chat-stream/turn-outcome-collector.js";
17
16
  import crypto from "crypto";
18
17
  //#region src/gateway/service/run-gateway-agent.ts
19
18
  init_logger();
@@ -39,7 +38,6 @@ async function* runGatewayAgent(deps, message, channel, chatId, origin, attachme
39
38
  let webchatSessionKey;
40
39
  let webchatSessionId;
41
40
  let webchatMetadata;
42
- let outcomeEnvironment = "workspace";
43
41
  if (channel === "webchat") {
44
42
  const resolved = resolveWebchatSessionKey({ sessionKey: chatId });
45
43
  if (resolved.ok === false) throw new Error(resolved.error);
@@ -68,8 +66,6 @@ async function* runGatewayAgent(deps, message, channel, chatId, origin, attachme
68
66
  channel,
69
67
  metadata: webchatMetadata
70
68
  });
71
- if (/remote/i.test(executionContext.strategy ?? "")) outcomeEnvironment = "remote_runtime";
72
- else if (/worktree/i.test(executionContext.strategy ?? "")) outcomeEnvironment = "worktree";
73
69
  taskRun = TaskRunCoordinator.start({
74
70
  runId,
75
71
  context: executionContext,
@@ -81,11 +77,8 @@ async function* runGatewayAgent(deps, message, channel, chatId, origin, attachme
81
77
  sessionKey: streamSessionKey,
82
78
  channel
83
79
  });
84
- const outcomeCollector = new TurnOutcomeCollector(runId, outcomeEnvironment);
85
- let outcomeFinalized = false;
86
80
  let registeredActiveWebchatRun = false;
87
81
  const captureTaskEvent = (event) => {
88
- outcomeCollector.capture(event);
89
82
  if (event.type === "task_plan_updated") {
90
83
  taskRun?.capturePlan(event.payload.items);
91
84
  return;
@@ -110,30 +103,6 @@ async function* runGatewayAgent(deps, message, channel, chatId, origin, attachme
110
103
  yield event;
111
104
  }
112
105
  };
113
- const finalizeOutcome = async (status, summary) => {
114
- if (outcomeFinalized) return [];
115
- outcomeFinalized = true;
116
- const outcome = outcomeCollector.finalize(status, summary);
117
- if (webchatSessionKey) try {
118
- await sessionIndex.appendTranscriptCustomEntry(webchatSessionKey, {
119
- customType: "turn_outcome",
120
- data: outcome
121
- });
122
- } catch (err) {
123
- log.warn({
124
- err,
125
- runId,
126
- sessionKey: webchatSessionKey
127
- }, `Turn outcome persistence failed: ${err instanceof Error ? err.message : String(err)}`);
128
- }
129
- return [{
130
- type: "turn_outcome",
131
- runId,
132
- sessionKey: streamSessionKey,
133
- timestamp: Date.now(),
134
- payload: outcome
135
- }];
136
- };
137
106
  try {
138
107
  yield* emitAndYield(mapper.start());
139
108
  if (channel === "webchat" && webchatSessionKey) {
@@ -173,7 +142,6 @@ async function* runGatewayAgent(deps, message, channel, chatId, origin, attachme
173
142
  taskRunStatus = mergedSignal.aborted ? "cancelled" : "succeeded";
174
143
  terminalStatus = endStatus;
175
144
  taskRunSummary = endSummary;
176
- yield* emitAndYield(await finalizeOutcome(endStatus, endSummary));
177
145
  yield* emitAndYield(mapper.end(endStatus, endSummary));
178
146
  completeRealtimeTopic(`run:${runId}`);
179
147
  runTopicCompleted = true;
@@ -197,7 +165,6 @@ async function* runGatewayAgent(deps, message, channel, chatId, origin, attachme
197
165
  taskRunSummary = em;
198
166
  const errorContent = formatAgentRunErrorForClient(streamError);
199
167
  yield* emitAndYield(mapper.error(errorContent));
200
- yield* emitAndYield(await finalizeOutcome("error", streamError));
201
168
  yield* emitAndYield(mapper.end("error", streamError));
202
169
  completeRealtimeTopic(`run:${runId}`);
203
170
  runTopicCompleted = true;
@@ -283,7 +250,6 @@ async function* runGatewayAgent(deps, message, channel, chatId, origin, attachme
283
250
  presentation: "answer"
284
251
  }
285
252
  }]);
286
- yield* emitAndYield(await finalizeOutcome("success", "Message processed"));
287
253
  yield* emitAndYield(mapper.end("success", "Message processed"));
288
254
  taskRunStatus = "succeeded";
289
255
  taskRunSummary = "Message processed";
@@ -306,7 +272,6 @@ async function* runGatewayAgent(deps, message, channel, chatId, origin, attachme
306
272
  }, `Agent run failed: ${em}`);
307
273
  if (channel === "webchat" && !runTopicCompleted) {
308
274
  yield* emitAndYield(mapper.error(formatAgentRunErrorForClient(em)));
309
- yield* emitAndYield(await finalizeOutcome("error", em));
310
275
  yield* emitAndYield(mapper.end("error", em));
311
276
  completeRealtimeTopic(`run:${runId}`);
312
277
  runTopicCompleted = true;