@xopcai/xopc 0.0.199 → 0.0.201

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 (152) hide show
  1. package/dist/browser-ext/manifest.json +1 -1
  2. package/dist/extensions/telegram/xopc.extension.json +1 -1
  3. package/dist/gateway/static/root/assets/{activity-store-DYVN1hRp.js → activity-store-DOhnAQOQ.js} +1 -1
  4. package/dist/gateway/static/root/assets/{agent-avatar-dicebear-Bt70bZjv.js → agent-avatar-dicebear-D8Cw0Mve.js} +1 -1
  5. package/dist/gateway/static/root/assets/agents-ANavi648.js +68 -0
  6. package/dist/gateway/static/root/assets/{api-B8mzbAle.js → api-E9Bk-Idp.js} +1 -1
  7. package/dist/gateway/static/root/assets/{app-management-settings-panel-B4x7JfL8.js → app-management-settings-panel-BubrReXg.js} +1 -1
  8. package/dist/gateway/static/root/assets/{appearance-settings-Dwdagf-Q.js → appearance-settings-BY5Az2p4.js} +1 -1
  9. package/dist/gateway/static/root/assets/{apps-page-DkTiesxe.js → apps-page-CFh9MBxc.js} +1 -1
  10. package/dist/gateway/static/root/assets/{archive-plugin-Cv0VXKLm.js → archive-plugin-BY8843mB.js} +1 -1
  11. package/dist/gateway/static/root/assets/{attachment-load-CgidKrXn.js → attachment-load-DG45KScf.js} +1 -1
  12. package/dist/gateway/static/root/assets/automations-page-751CeNI3.js +6 -0
  13. package/dist/gateway/static/root/assets/{binary-plugins-DN3CQgIq.js → binary-plugins-DGIF2ZAv.js} +1 -1
  14. package/dist/gateway/static/root/assets/{block-editor-BwkSl1F_.js → block-editor-C1Qktr1g.js} +5 -5
  15. package/dist/gateway/static/root/assets/browser-settings-page-CGAWs-TT.js +8 -0
  16. package/dist/gateway/static/root/assets/{browser-workflow-inputs-_ndaecsf.js → browser-workflow-inputs-D_jnndyz.js} +1 -1
  17. package/dist/gateway/static/root/assets/{browser-workflows-page-C_I31wLm.js → browser-workflows-page-DyIRvPaq.js} +2 -2
  18. package/dist/gateway/static/root/assets/capabilities-settings-panel-CghUK68J.js +7 -0
  19. package/dist/gateway/static/root/assets/{capability-presets-api-Dy1jZJKs.js → capability-presets-api--tz0mGLY.js} +1 -1
  20. package/dist/gateway/static/root/assets/capability-presets-settings-panel-XsTfkvUn.js +3 -0
  21. package/dist/gateway/static/root/assets/{channel-recipient-api-D-9qiuio.js → channel-recipient-api-CkOKF9nc.js} +1 -1
  22. package/dist/gateway/static/root/assets/{channels-settings-DV5BhAeu.js → channels-settings-DkoAZZMe.js} +1 -1
  23. package/dist/gateway/static/root/assets/channels-status-swr-DNDvMWpl.js +1 -0
  24. package/dist/gateway/static/root/assets/{connectors-api-5FEBYguL.js → connectors-api-FBOe9SWC.js} +1 -1
  25. package/dist/gateway/static/root/assets/connectors-page-e4vekkAu.js +2 -0
  26. package/dist/gateway/static/root/assets/{cron-expression-SEFx5rnr.js → cron-expression-Q4QaYjpI.js} +1 -1
  27. package/dist/gateway/static/root/assets/{date-picker-D4zCUQJL.js → date-picker-Dtn7e6TW.js} +1 -1
  28. package/dist/gateway/static/root/assets/{dependency-picker-DpQAxS2k.js → dependency-picker-Br6nPaM6.js} +1 -1
  29. package/dist/gateway/static/root/assets/{desktop-pet-B_5G3lQl.js → desktop-pet-DzTM6aH8.js} +1 -1
  30. package/dist/gateway/static/root/assets/{desktop-pet-settings-ClEyoUJe.js → desktop-pet-settings-FDmaty3P.js} +1 -1
  31. package/dist/gateway/static/root/assets/{directory-picker-path-field-AsFWmOjG.js → directory-picker-path-field-X2e8xn8j.js} +1 -1
  32. package/dist/gateway/static/root/assets/{extension-debug-page-C22Y2o5l.js → extension-debug-page-Dx4MBfga.js} +1 -1
  33. package/dist/gateway/static/root/assets/{extension-page-C61Ikawk.js → extension-page-Bf_FEDhM.js} +1 -1
  34. package/dist/gateway/static/root/assets/{extension-settings-page-WlWWcDFr.js → extension-settings-page-CrOgZxkf.js} +1 -1
  35. package/dist/gateway/static/root/assets/{gateway-config-api-ByIOMQdc.js → gateway-config-api-CZ4r-7qT.js} +1 -1
  36. package/dist/gateway/static/root/assets/{gateway-config-swr-jB0hAhWO.js → gateway-config-swr-Cv2ywpdb.js} +1 -1
  37. package/dist/gateway/static/root/assets/{gateway-settings-CzzS9A17.js → gateway-settings-DpWmVi0w.js} +1 -1
  38. package/dist/gateway/static/root/assets/{gateway-startup-retry-C-fRlHCL.js → gateway-startup-retry-undf-k5g.js} +1 -1
  39. package/dist/gateway/static/root/assets/heartbeat-settings-DSQZhSvT.js +1 -0
  40. package/dist/gateway/static/root/assets/home-page-L0uHiu0T.js +3 -0
  41. package/dist/gateway/static/root/assets/{index-CR233S0Q.css → index-Bp8I_eX4.css} +1 -1
  42. package/dist/gateway/static/root/assets/index-DRrjYYzZ.js +152 -0
  43. package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-BD_TFz0_.js → keyboard-shortcuts-settings-Bc3Quw42.js} +1 -1
  44. package/dist/gateway/static/root/assets/local-app-workbench-page-Copi3sdP.js +2 -0
  45. package/dist/gateway/static/root/assets/{local-apps-page-BwbUeLfm.js → local-apps-page-CP9X_vtz.js} +1 -1
  46. package/dist/gateway/static/root/assets/{locale-store-xULEcFVs.js → locale-store-CnamlR07.js} +1 -1
  47. package/dist/gateway/static/root/assets/logs-page-DMKq6e29.js +2 -0
  48. package/dist/gateway/static/root/assets/{management-settings-CW2VpqtT.js → management-settings-CJmcaVje.js} +1 -1
  49. package/dist/gateway/static/root/assets/{markdown-split-B1M9KO16.js → markdown-split-AcuFp0TH.js} +1 -1
  50. package/dist/gateway/static/root/assets/{media-plugins-B_QlNdqZ.js → media-plugins-BvFgsiJL.js} +1 -1
  51. package/dist/gateway/static/root/assets/messages-D7kOD_o_.js +3 -0
  52. package/dist/gateway/static/root/assets/{notes-page-BTbD4GmH.js → notes-page-IW7IQ9nJ.js} +1 -1
  53. package/dist/gateway/static/root/assets/notes-workbench-Zb8U0anO.js +4 -0
  54. package/dist/gateway/static/root/assets/{preview-open-alternatives-CeqtArlZ.js → preview-open-alternatives-C-b34dgQ.js} +1 -1
  55. package/dist/gateway/static/root/assets/{product-open-page-DnalBZZs.js → product-open-page-CWEzLykF.js} +1 -1
  56. package/dist/gateway/static/root/assets/project-detail-page-BuIbLnan.js +1 -0
  57. package/dist/gateway/static/root/assets/projects-page-BwUfRqNV.js +1 -0
  58. package/dist/gateway/static/root/assets/remote-access-hub-BTaXxcEi.js +1 -0
  59. package/dist/gateway/static/root/assets/{runtime-tools-settings-panel-cGkBEfg4.js → runtime-tools-settings-panel-DAohEZrX.js} +2 -2
  60. package/dist/gateway/static/root/assets/{schema-form-CmsrLlxu.js → schema-form-C_OBW8A5.js} +1 -1
  61. package/dist/gateway/static/root/assets/sessions-page-BnHoTCD3.js +1 -0
  62. package/dist/gateway/static/root/assets/settings-advanced-gate-BTB_fxjt.js +1 -0
  63. package/dist/gateway/static/root/assets/{settings-form-section-DUWCbaMJ.js → settings-form-section-C7w2WS-A.js} +1 -1
  64. package/dist/gateway/static/root/assets/{settings-loading-skeleton-Bvy5rdMY.js → settings-loading-skeleton-zwibve4m.js} +1 -1
  65. package/dist/gateway/static/root/assets/{settings-page-cZSAwsRR.js → settings-page-B9W03dSW.js} +1 -1
  66. package/dist/gateway/static/root/assets/setup-status-panel-C8Hec2Af.js +1 -0
  67. package/dist/gateway/static/root/assets/{share-preview-page-BzA2NCAH.js → share-preview-page-6e3ks2be.js} +1 -1
  68. package/dist/gateway/static/root/assets/shares-settings-Bwc5HfLI.js +1 -0
  69. package/dist/gateway/static/root/assets/skill-reload-api-D7kunhqy.js +1 -0
  70. package/dist/gateway/static/root/assets/{skills-page-Bvde7_i5.js → skills-page-WgkaNzpc.js} +1 -1
  71. package/dist/gateway/static/root/assets/{skills-page.utils-vXSqHR7g.js → skills-page.utils-D9QT5cxj.js} +1 -1
  72. package/dist/gateway/static/root/assets/{system-settings-panel-9cQGOGCK.js → system-settings-panel-c1HVtv1Y.js} +1 -1
  73. package/dist/gateway/static/root/assets/task-detail-page-CjP-6WVU.js +1 -0
  74. package/dist/gateway/static/root/assets/{theme-store-CHQRiuCF.js → theme-store-BDyUTObE.js} +1 -1
  75. package/dist/gateway/static/root/assets/{use-autosave-7XfKULPI.js → use-autosave-D6UkH_Ga.js} +1 -1
  76. package/dist/gateway/static/root/assets/{user-context-page-CZ9m9aC_.js → user-context-page-D8ZhPDNF.js} +1 -1
  77. package/dist/gateway/static/root/assets/{voice-api-key-field-DXr21v-3.js → voice-api-key-field-BOxkKbGh.js} +1 -1
  78. package/dist/gateway/static/root/assets/{work-discovery-overlay-CeG7g_DZ.js → work-discovery-overlay-Dd19yK9t.js} +1 -1
  79. package/dist/gateway/static/root/assets/work-discovery-page-DUkdJl1e.js +1 -0
  80. package/dist/gateway/static/root/assets/{workflow-run-setup-panel-DSQQZP9T.js → workflow-run-setup-panel-ZA3bP65I.js} +2 -2
  81. package/dist/gateway/static/root/assets/workflows-page-C2b5W5bw.js +5 -0
  82. package/dist/gateway/static/root/assets/{working-directory-picker-modal-D-IgcHPp.js → working-directory-picker-modal-BcyiDXU3.js} +1 -1
  83. package/dist/gateway/static/root/index.html +9 -9
  84. package/dist/package.js +1 -1
  85. package/dist/packages/gateway-contract/src/tasks.js +51 -9
  86. package/dist/src/agent/context/execution-scope.js +3 -1
  87. package/dist/src/agent/tasks/task-judge-service.d.ts +1 -1
  88. package/dist/src/agent/tasks/task-judge-service.js +3 -2
  89. package/dist/src/agent/tools/xopc-use-tool.js +13 -3
  90. package/dist/src/auth/oauth/types.d.ts +2 -0
  91. package/dist/src/auth/oauth/xopc-cloud.js +24 -2
  92. package/dist/src/automations/domain/validation.d.ts +0 -12
  93. package/dist/src/automations/draft/automation-draft-validator.d.ts +0 -3
  94. package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
  95. package/dist/src/gateway/hono/app.js +1 -1
  96. package/dist/src/gateway/hono/oauth-async.js +2 -0
  97. package/dist/src/gateway/hono/routes/agent-stream.js +2 -74
  98. package/dist/src/gateway/hono/routes/session-input-handler.d.ts +62 -0
  99. package/dist/src/gateway/hono/routes/session-input-handler.js +82 -0
  100. package/dist/src/gateway/hono/routes/tasks.js +161 -2
  101. package/dist/src/gateway/service/task-conversation-query-service.d.ts +58 -0
  102. package/dist/src/gateway/service/task-conversation-query-service.js +84 -0
  103. package/dist/src/gateway/service.d.ts +10 -0
  104. package/dist/src/gateway/service.js +75 -42
  105. package/dist/src/projects/project-agent.js +1 -1
  106. package/dist/src/storage/sqlite/migrations/120_task_conversation.sql +112 -0
  107. package/dist/src/storage/sqlite/migrations/runner.d.ts +1 -1
  108. package/dist/src/storage/sqlite/migrations/runner.js +3 -3
  109. package/dist/src/storage/sqlite/migrations/runner.ts +1 -1
  110. package/dist/src/storage/sqlite/schema.js +1 -1
  111. package/dist/src/tasks/execution-context.js +8 -4
  112. package/dist/src/tasks/index.d.ts +2 -0
  113. package/dist/src/tasks/index.js +3 -1
  114. package/dist/src/tasks/task-application-service.js +10 -17
  115. package/dist/src/tasks/task-context-assembler.js +9 -4
  116. package/dist/src/tasks/task-context-repository.d.ts +0 -6
  117. package/dist/src/tasks/task-context-repository.js +0 -4
  118. package/dist/src/tasks/task-conversation-repository.d.ts +71 -0
  119. package/dist/src/tasks/task-conversation-repository.js +194 -0
  120. package/dist/src/tasks/task-handoff-service.d.ts +31 -0
  121. package/dist/src/tasks/task-handoff-service.js +143 -0
  122. package/dist/src/tasks/task-read-model-projector.js +1 -5
  123. package/dist/src/tasks/task-repository.d.ts +0 -2
  124. package/dist/src/tasks/task-repository.js +2 -9
  125. package/dist/src/tasks/task-run-coordinator.js +3 -1
  126. package/dist/src/workflows/service/workflow-run-service.js +4 -2
  127. package/package.json +2 -2
  128. package/dist/gateway/static/root/assets/agents-D19uNmJf.js +0 -68
  129. package/dist/gateway/static/root/assets/automations-page-BR6qMOVr.js +0 -6
  130. package/dist/gateway/static/root/assets/browser-settings-page-CDFQWAxI.js +0 -8
  131. package/dist/gateway/static/root/assets/capabilities-settings-panel-D41mssOq.js +0 -7
  132. package/dist/gateway/static/root/assets/capability-presets-settings-panel-BXgCyuu2.js +0 -3
  133. package/dist/gateway/static/root/assets/channels-status-swr-B4Rbh3bM.js +0 -1
  134. package/dist/gateway/static/root/assets/connectors-page-C_qkEN0T.js +0 -2
  135. package/dist/gateway/static/root/assets/heartbeat-settings-B4A_CXUL.js +0 -1
  136. package/dist/gateway/static/root/assets/home-page-FVGYYPWo.js +0 -3
  137. package/dist/gateway/static/root/assets/index-CGbKTCST.js +0 -152
  138. package/dist/gateway/static/root/assets/local-app-workbench-page-BNpkigNb.js +0 -2
  139. package/dist/gateway/static/root/assets/logs-page-DXP9OQQt.js +0 -2
  140. package/dist/gateway/static/root/assets/messages-3qRsLZuX.js +0 -3
  141. package/dist/gateway/static/root/assets/notes-workbench-JRiq7QDW.js +0 -4
  142. package/dist/gateway/static/root/assets/project-detail-page-CjWA13aE.js +0 -1
  143. package/dist/gateway/static/root/assets/projects-page-jsCkScbG.js +0 -1
  144. package/dist/gateway/static/root/assets/remote-access-hub-CGPJTEkM.js +0 -1
  145. package/dist/gateway/static/root/assets/sessions-page-BBHzziMd.js +0 -1
  146. package/dist/gateway/static/root/assets/settings-advanced-gate-2cGg8-9h.js +0 -1
  147. package/dist/gateway/static/root/assets/setup-status-panel-Cf06b66-.js +0 -1
  148. package/dist/gateway/static/root/assets/shares-settings-xlNKydYY.js +0 -1
  149. package/dist/gateway/static/root/assets/skill-reload-api-C5Xu93c_.js +0 -1
  150. package/dist/gateway/static/root/assets/task-detail-page-Bkno4tTe.js +0 -1
  151. package/dist/gateway/static/root/assets/work-discovery-page-CQWZD03N.js +0 -1
  152. package/dist/gateway/static/root/assets/workflows-page-Cs-MXFJ1.js +0 -5
@@ -0,0 +1,82 @@
1
+ import { endpointTurnClaimSchema } from "../../../../packages/endpoint-tools-protocol/src/index.js";
2
+ import { validateWebchatAttachments, validateWebchatContent } from "../../chat-limits.js";
3
+ //#region src/gateway/hono/routes/session-input-handler.ts
4
+ async function submitSessionInput(c, deps, sessionKey) {
5
+ const body = await c.req.json().catch(() => null);
6
+ if (!body || !sessionKey) return c.json({
7
+ ok: false,
8
+ error: {
9
+ code: "BAD_REQUEST",
10
+ message: "Invalid request"
11
+ }
12
+ }, 400);
13
+ const attachments = Array.isArray(body.attachments) ? body.attachments : void 0;
14
+ const content = typeof body.content === "string" ? body.content : "";
15
+ const contentError = validateWebchatContent(content);
16
+ if (contentError) return c.json({
17
+ ok: false,
18
+ error: {
19
+ code: "BAD_REQUEST",
20
+ message: contentError
21
+ }
22
+ }, 400);
23
+ const attachmentError = validateWebchatAttachments(attachments);
24
+ if (attachmentError) return c.json({
25
+ ok: false,
26
+ error: {
27
+ code: "BAD_REQUEST",
28
+ message: attachmentError
29
+ }
30
+ }, 400);
31
+ const delivery = body.delivery === "next" || body.delivery === "steer" ? body.delivery : null;
32
+ if (!delivery) return c.json({
33
+ ok: false,
34
+ error: {
35
+ code: "BAD_REQUEST",
36
+ message: "Missing delivery"
37
+ }
38
+ }, 400);
39
+ const origin = endpointTurnClaimSchema.safeParse(body.origin);
40
+ if (!origin.success) return c.json({
41
+ ok: false,
42
+ error: {
43
+ code: "BAD_REQUEST",
44
+ message: "Invalid endpoint origin"
45
+ }
46
+ }, 400);
47
+ if (!deps.service.endpointTools.registry.verifyTurnClaim(origin.data.endpointId, origin.data.token)) return c.json({
48
+ ok: false,
49
+ error: {
50
+ code: "INVALID_ENDPOINT",
51
+ message: "Endpoint connection is not active"
52
+ }
53
+ }, 401);
54
+ const result = await deps.service.submitSessionInput({
55
+ sessionKey,
56
+ clientMessageId: typeof body.clientMessageId === "string" ? body.clientMessageId : "",
57
+ delivery,
58
+ content,
59
+ attachments,
60
+ thinking: typeof body.thinking === "string" ? body.thinking : void 0,
61
+ origin: {
62
+ type: "endpoint",
63
+ endpointId: origin.data.endpointId
64
+ }
65
+ });
66
+ if (result.ok === false) return c.json({
67
+ ok: false,
68
+ error: {
69
+ code: result.code,
70
+ message: "Input was not accepted"
71
+ }
72
+ }, result.code === "QUEUE_FULL" ? 409 : 400);
73
+ return c.json({
74
+ ok: true,
75
+ payload: {
76
+ ...result,
77
+ sessionKey
78
+ }
79
+ }, 202);
80
+ }
81
+ //#endregion
82
+ export { submitSessionInput };
@@ -1,10 +1,12 @@
1
1
  import { runSqliteWriteTransaction } from "../../../storage/sqlite/transaction.js";
2
2
  import { TaskRepository } from "../../../tasks/task-repository.js";
3
+ import { TaskConversationRepository } from "../../../tasks/task-conversation-repository.js";
3
4
  import { TaskRunRepository } from "../../../tasks/task-run-repository.js";
4
5
  import { TaskReadModelProjector } from "../../../tasks/task-read-model-projector.js";
5
6
  import { TaskPhaseSchema } from "../../../../packages/gateway-contract/src/task-lifecycle.js";
6
- import { TaskBoardPositionRequestSchema, TaskCommandRequestSchema, TaskContextInputSchema, TaskCreateRequestSchema, TaskDependencyUpdateRequestSchema, TaskPatchRequestSchema } from "../../../../packages/gateway-contract/src/tasks.js";
7
+ import { TaskBoardPositionRequestSchema, TaskCommandRequestSchema, TaskContextInputSchema, TaskCreateRequestSchema, TaskDependencyUpdateRequestSchema, TaskHandoffRequestSchema, TaskPatchRequestSchema } from "../../../../packages/gateway-contract/src/tasks.js";
7
8
  import { ProjectMonitoringUpdateSchema } from "../../../../packages/gateway-contract/src/project-monitoring.js";
9
+ import { resolveProjectAgentId } from "../../../projects/project-agent.js";
8
10
  import { TaskContextRepository } from "../../../tasks/task-context-repository.js";
9
11
  import { enqueueTaskChangedEvent } from "../../../tasks/task-change-events.js";
10
12
  import { TaskDependencyError, TaskDependencyService } from "../../../tasks/task-dependency-service.js";
@@ -12,7 +14,10 @@ import { TaskApplicationService } from "../../../tasks/task-application-service.
12
14
  import { ProjectMonitoringService } from "../../../tasks/project-monitoring-service.js";
13
15
  import { ProjectOperatingViewService } from "../../../tasks/project-operating-view-service.js";
14
16
  import { TaskValueMetricsService } from "../../../tasks/task-value-metrics-service.js";
17
+ import { TaskHandoffService } from "../../../tasks/task-handoff-service.js";
15
18
  import { TaskSignalService } from "../../../tasks/task-signal-service.js";
19
+ import { submitSessionInput } from "./session-input-handler.js";
20
+ import { TaskConversationQueryService } from "../../service/task-conversation-query-service.js";
16
21
  //#region src/gateway/hono/routes/tasks.ts
17
22
  function registerTaskRoutes(authenticated, deps) {
18
23
  const taskRateLimit = deps.taskRateLimitMiddleware ?? deps.strictRateLimitMiddleware;
@@ -23,9 +28,17 @@ function registerTaskRoutes(authenticated, deps) {
23
28
  const tasks = new TaskRepository();
24
29
  const runs = new TaskRunRepository();
25
30
  const context = new TaskContextRepository();
31
+ const conversations = new TaskConversationRepository();
32
+ const conversationQuery = new TaskConversationQueryService(deps.service.sessions);
26
33
  const projector = new TaskReadModelProjector();
27
34
  const signals = new TaskSignalService(() => deps.service.dispatchTaskRuns());
28
35
  const dependencies = new TaskDependencyService();
36
+ const handoffs = new TaskHandoffService({
37
+ getConfig: () => deps.service.currentConfig,
38
+ sessionIndex: deps.service.sessionIndexInstance,
39
+ getActiveRunId: (sessionKey) => deps.service.getActiveWebchatRunId(sessionKey),
40
+ abortRun: (runId) => deps.service.abortAgentRun(runId)
41
+ });
29
42
  authenticated.get("/api/tasks", (c) => {
30
43
  const phase = TaskPhaseSchema.safeParse(c.req.query("phase"));
31
44
  const rawLimit = Number(c.req.query("limit") ?? 50);
@@ -53,7 +66,25 @@ function registerTaskRoutes(authenticated, deps) {
53
66
  error: "Invalid task request"
54
67
  }, 400);
55
68
  try {
56
- const created = application.create(parsed.data);
69
+ const requestedAgentId = parsed.data.delegateAgentId ?? (parsed.data.activation.mode === "start" && parsed.data.activation.executor?.kind === "agent" ? parsed.data.activation.executor.agentId : void 0);
70
+ const agentId = resolveProjectAgentId({
71
+ config: deps.service.currentConfig,
72
+ projects: deps.service.projects,
73
+ explicitAgentId: requestedAgentId,
74
+ projectId: parsed.data.projectId
75
+ });
76
+ const input = {
77
+ ...parsed.data,
78
+ delegateAgentId: agentId,
79
+ activation: parsed.data.activation.mode === "start" ? {
80
+ ...parsed.data.activation,
81
+ executor: parsed.data.activation.executor?.kind === "agent" || !parsed.data.activation.executor ? {
82
+ kind: "agent",
83
+ agentId
84
+ } : parsed.data.activation.executor
85
+ } : parsed.data.activation
86
+ };
87
+ const created = application.create(input);
57
88
  if (created.ok === false) return c.json({
58
89
  ok: false,
59
90
  code: created.reason,
@@ -95,12 +126,140 @@ function registerTaskRoutes(authenticated, deps) {
95
126
  runs: runs.listByTask(task.id),
96
127
  receipts: runs.listReceipts(task.id),
97
128
  context: context.list(task.id),
129
+ conversation: conversations.requireState(task.id),
130
+ sessions: conversations.listSessions(task.id),
98
131
  authorityGrants: context.listActiveGrants(task.id),
99
132
  dependencies: dependencies.listDependencies(task.id),
100
133
  dependents: dependencies.listDependents(task.id),
101
134
  allowedCommands: model.allowedCommands
102
135
  });
103
136
  });
137
+ authenticated.post("/api/tasks/:id/conversation", taskRateLimit, async (c) => {
138
+ try {
139
+ const result = await deps.service.ensureTaskConversation(c.req.param("id"));
140
+ if (result.created) {
141
+ const task = tasks.require(c.req.param("id"));
142
+ runSqliteWriteTransaction((db) => enqueueTaskChangedEvent(db, {
143
+ taskId: task.id,
144
+ projectId: task.projectId,
145
+ version: task.version,
146
+ changedFields: ["conversation"],
147
+ actor: { kind: "user" }
148
+ }));
149
+ deps.service.dispatchTaskEvents();
150
+ }
151
+ return c.json({
152
+ ok: true,
153
+ ...result
154
+ });
155
+ } catch (error) {
156
+ const message = error instanceof Error ? error.message : String(error);
157
+ return c.json({
158
+ ok: false,
159
+ error: message
160
+ }, message.startsWith("Task not found") ? 404 : 409);
161
+ }
162
+ });
163
+ authenticated.post("/api/tasks/:id/handoff", taskRateLimit, async (c) => {
164
+ const parsed = TaskHandoffRequestSchema.safeParse(await c.req.json().catch(() => ({})));
165
+ if (!parsed.success) return c.json({
166
+ ok: false,
167
+ error: "Invalid task handoff request"
168
+ }, 400);
169
+ try {
170
+ const result = await handoffs.handoff({
171
+ taskId: c.req.param("id"),
172
+ ...parsed.data
173
+ });
174
+ runSqliteWriteTransaction((db) => enqueueTaskChangedEvent(db, {
175
+ taskId: result.task.id,
176
+ projectId: result.task.projectId,
177
+ version: result.task.version,
178
+ changedFields: ["delegateAgentId", "conversation"],
179
+ actor: { kind: "user" }
180
+ }));
181
+ deps.service.dispatchTaskEvents();
182
+ return c.json({
183
+ ok: true,
184
+ task: result.task,
185
+ conversation: result.conversation,
186
+ ...result.fromAgentId ? { fromAgentId: result.fromAgentId } : {},
187
+ toAgentId: result.toAgentId,
188
+ activeSessionKey: result.activeSessionKey,
189
+ assignmentEpoch: result.assignmentEpoch
190
+ });
191
+ } catch (error) {
192
+ const message = error instanceof Error ? error.message : String(error);
193
+ return c.json({
194
+ ok: false,
195
+ error: message
196
+ }, message.startsWith("Agent not found") || message.startsWith("Task not found") ? 404 : 409);
197
+ }
198
+ });
199
+ authenticated.post("/api/tasks/:id/inputs", deps.chatRateLimitMiddleware, async (c) => {
200
+ const active = conversations.getActiveSession(c.req.param("id"));
201
+ if (!active?.sessionKey) return c.json({
202
+ ok: false,
203
+ error: {
204
+ code: "NOT_FOUND",
205
+ message: "Task has no active conversation"
206
+ }
207
+ }, 404);
208
+ const expectedSessionKey = c.req.header("X-Xopc-Expected-Session-Key")?.trim();
209
+ if (expectedSessionKey && expectedSessionKey !== active.sessionKey) return c.json({
210
+ ok: false,
211
+ error: {
212
+ code: "CONFLICT",
213
+ message: "Task executor changed; refresh the conversation"
214
+ }
215
+ }, 409);
216
+ return submitSessionInput(c, deps, active.sessionKey);
217
+ });
218
+ authenticated.patch("/api/tasks/:id/conversation/config", taskRateLimit, async (c) => {
219
+ const body = await c.req.json().catch(() => null);
220
+ const model = typeof body?.model === "string" ? body.model.trim() : "";
221
+ if (!model) return c.json({
222
+ ok: false,
223
+ error: "Missing model"
224
+ }, 400);
225
+ const active = conversations.getActiveSession(c.req.param("id"));
226
+ if (!active?.sessionKey) return c.json({
227
+ ok: false,
228
+ error: "Task has no active conversation"
229
+ }, 404);
230
+ const result = await deps.service.sessions.patchAgentConfig(active.sessionKey, { model });
231
+ if (!result.ok) return c.json({
232
+ ok: false,
233
+ error: result.error
234
+ }, 400);
235
+ return c.json({
236
+ ok: true,
237
+ activeSessionKey: active.sessionKey
238
+ });
239
+ });
240
+ authenticated.get("/api/tasks/:id/conversation/history", async (c) => {
241
+ const parsedLimit = Number.parseInt(c.req.query("limit") ?? "50", 10);
242
+ const parsedOffset = Number.parseInt(c.req.query("offset") ?? "0", 10);
243
+ const rawBefore = c.req.query("before")?.trim();
244
+ const before = rawBefore === void 0 ? void 0 : Number.parseInt(rawBefore, 10);
245
+ if (rawBefore !== void 0 && (!Number.isInteger(before) || before < 0)) return c.json({ error: "Invalid conversation history cursor" }, 400);
246
+ const result = await conversationQuery.getMessagePage(c.req.param("id"), {
247
+ limit: Number.isFinite(parsedLimit) ? Math.min(200, Math.max(1, parsedLimit)) : 50,
248
+ offset: Number.isFinite(parsedOffset) ? Math.max(0, parsedOffset) : 0,
249
+ ...before === void 0 ? {} : { before }
250
+ });
251
+ return result ? c.json(result) : c.json({ error: "Task conversation not found" }, 404);
252
+ });
253
+ authenticated.get("/api/tasks/:id/conversation/timeline", async (c) => {
254
+ const items = await conversationQuery.getTimeline(c.req.param("id"));
255
+ return items ? c.json({
256
+ ok: true,
257
+ items
258
+ }) : c.json({
259
+ ok: false,
260
+ error: "Task conversation not found"
261
+ }, 404);
262
+ });
104
263
  authenticated.patch("/api/tasks/:id", taskRateLimit, async (c) => {
105
264
  const parsed = TaskPatchRequestSchema.safeParse(await c.req.json().catch(() => ({})));
106
265
  if (!parsed.success) return c.json({
@@ -0,0 +1,58 @@
1
+ import type { SessionTimelineItem } from '../../session/transcript-outline.js';
2
+ import { TaskConversationRepository } from '../../tasks/task-conversation-repository.js';
3
+ import type { GatewaySessionsApi } from './sessions-api.js';
4
+ export declare class TaskConversationQueryService {
5
+ #private;
6
+ private readonly sessions;
7
+ private readonly conversations;
8
+ constructor(sessions: Pick<GatewaySessionsApi, 'getMessagePage' | 'getTimeline'>, conversations?: Pick<TaskConversationRepository, 'listSessions'>);
9
+ getMessagePage(taskId: string, options: {
10
+ limit: number;
11
+ offset: number;
12
+ before?: number;
13
+ }): Promise<{
14
+ session: {
15
+ messages: import("../../session/types.js").Message[];
16
+ transcriptSummary?: import("../../session/types.js").SessionTranscriptSummary;
17
+ transcriptRows?: import("../../session/session-context-for-llm.js").TranscriptStoredRow[];
18
+ key: string;
19
+ name?: string;
20
+ status: import("../../session/types.js").SessionStatus;
21
+ tags: string[];
22
+ createdAt: string;
23
+ updatedAt: string;
24
+ lastAccessedAt: string;
25
+ messageCount: number;
26
+ estimatedTokens: number;
27
+ compactedCount: number;
28
+ lastFlushedAt?: string;
29
+ flushCount?: number;
30
+ sourceChannel: string;
31
+ sourceChatId: string;
32
+ customData?: Record<string, unknown>;
33
+ projectId?: string;
34
+ routing?: import("../../session/types.js").SessionRoutingMeta;
35
+ stats?: import("../../session/types.js").SessionStats;
36
+ sessionType: import("../../session/types.js").SessionType;
37
+ hiddenFromSessionList?: boolean;
38
+ parentSessionKey?: string;
39
+ workflowRunId?: string;
40
+ workflowDefinitionId?: string;
41
+ workflowAgentId?: string;
42
+ workflowAgentLabel?: string;
43
+ sessionId?: string;
44
+ cwd?: string;
45
+ sessionStartedAt?: string;
46
+ lastInteractionAt?: string;
47
+ };
48
+ pagination: {
49
+ nextBeforeCursor?: string;
50
+ before?: string;
51
+ total: number;
52
+ limit: number;
53
+ offset: number;
54
+ hasMore: boolean;
55
+ };
56
+ }>;
57
+ getTimeline(taskId: string): Promise<SessionTimelineItem[] | null>;
58
+ }
@@ -0,0 +1,84 @@
1
+ import { TaskConversationRepository } from "../../tasks/task-conversation-repository.js";
2
+ //#region src/gateway/service/task-conversation-query-service.ts
3
+ var TaskConversationQueryService = class {
4
+ constructor(sessions, conversations = new TaskConversationRepository()) {
5
+ this.sessions = sessions;
6
+ this.conversations = conversations;
7
+ }
8
+ async getMessagePage(taskId, options) {
9
+ const links = this.#executionSessions(taskId);
10
+ if (links.length === 0) return null;
11
+ const probes = await Promise.all(links.map((link) => this.sessions.getMessagePage(link.sessionKey, { limit: 1 })));
12
+ if (probes.some((page) => !page)) return null;
13
+ const pages = probes;
14
+ const total = pages.reduce((sum, page) => sum + page.pagination.total, 0);
15
+ const end = Math.min(total, Math.max(0, options.before ?? total - options.offset));
16
+ const start = Math.max(0, end - options.limit);
17
+ const messages = [];
18
+ let cursor = 0;
19
+ for (let index = 0; index < links.length; index += 1) {
20
+ const sessionTotal = pages[index].pagination.total;
21
+ const localStart = Math.max(0, start - cursor);
22
+ const localEnd = Math.min(sessionTotal, end - cursor);
23
+ if (localStart < localEnd) {
24
+ const page = await this.sessions.getMessagePage(links[index].sessionKey, {
25
+ offset: sessionTotal - localEnd,
26
+ limit: localEnd - localStart
27
+ });
28
+ if (!page) return null;
29
+ messages.push(...page.session.messages);
30
+ }
31
+ cursor += sessionTotal;
32
+ }
33
+ const activeIndex = links.findIndex((link) => link.status === "active");
34
+ return {
35
+ session: {
36
+ ...pages[activeIndex >= 0 ? activeIndex : pages.length - 1].session,
37
+ messages
38
+ },
39
+ pagination: {
40
+ total,
41
+ limit: options.limit,
42
+ offset: options.offset,
43
+ hasMore: start > 0,
44
+ ...options.before === void 0 ? {} : { before: String(end) },
45
+ ...start > 0 ? { nextBeforeCursor: String(start) } : {}
46
+ }
47
+ };
48
+ }
49
+ async getTimeline(taskId) {
50
+ const links = this.#executionSessions(taskId);
51
+ if (links.length === 0) return null;
52
+ const output = [];
53
+ let displayOffset = 0;
54
+ let turnOffset = 0;
55
+ for (const [index, link] of links.entries()) {
56
+ const [items, page] = await Promise.all([this.sessions.getTimeline(link.sessionKey), this.sessions.getMessagePage(link.sessionKey, { limit: 1 })]);
57
+ if (!items || !page) return null;
58
+ if (index > 0) output.push({
59
+ id: `assignment:${link.id}`,
60
+ kind: "branch",
61
+ title: link.agentId ? `Execution continued by ${link.agentId}` : "Execution continued",
62
+ timestamp: link.startedAt,
63
+ depth: 0,
64
+ turn: turnOffset,
65
+ displayIndex: displayOffset
66
+ });
67
+ output.push(...items.map((item) => ({
68
+ ...item,
69
+ id: `${link.id}:${item.id}`,
70
+ turn: item.turn + turnOffset,
71
+ ...item.displayIndex === void 0 ? {} : { displayIndex: item.displayIndex + displayOffset }
72
+ })));
73
+ displayOffset += page.pagination.total;
74
+ const highestTurn = items.reduce((max, item) => Math.max(max, item.turn), -1);
75
+ turnOffset += highestTurn + 1;
76
+ }
77
+ return output;
78
+ }
79
+ #executionSessions(taskId) {
80
+ return this.conversations.listSessions(taskId).filter((link) => link.role === "execution").sort((a, b) => a.assignmentEpoch - b.assignmentEpoch || a.startedAt - b.startedAt);
81
+ }
82
+ };
83
+ //#endregion
84
+ export { TaskConversationQueryService };
@@ -12,6 +12,7 @@ import { type ResolvedGatewayAuth } from './auth.js';
12
12
  import { ModelCatalogSyncService } from '../providers/model-catalog-sync-service.js';
13
13
  import { ProjectService } from '../projects/index.js';
14
14
  import { LocalAppService } from '../local-apps/index.js';
15
+ import { TaskConversationRepository } from '../tasks/task-conversation-repository.js';
15
16
  import { type BrowserRecipeService } from '../browser/recipes/index.js';
16
17
  import { listInsights, ProactiveEventService, ProactiveInboxService, ProactiveInboxWorker, ProactiveScenarioService, ProactiveTemporalWorker, ProactiveWorker } from '../proactive/index.js';
17
18
  import { GatewaySessionsApi } from './service/sessions-api.js';
@@ -120,6 +121,15 @@ export declare class GatewayService {
120
121
  private createTaskRunDispatcher;
121
122
  private createMobileNotificationService;
122
123
  dispatchTaskRuns(): void;
124
+ ensureTaskConversation(taskId: string, options?: {
125
+ runId?: string;
126
+ requestedAgentId?: string;
127
+ }): Promise<{
128
+ sessionKey: string;
129
+ agentId: string;
130
+ created: boolean;
131
+ conversation: ReturnType<TaskConversationRepository['requireState']>;
132
+ }>;
123
133
  dispatchTaskEvents(): void;
124
134
  runAgent(...args: Parameters<GatewayAgentRunner['runAgent']>): ReturnType<GatewayAgentRunner['runAgent']>;
125
135
  submitSessionInput(...args: Parameters<GatewayAgentRunner['submitSessionInput']>): Promise<{
@@ -19,19 +19,20 @@ import { loadConfig, saveConfig } from "../config/loader.js";
19
19
  import { prewarmModelRegistry } from "../providers/model-registry.js";
20
20
  import { init_providers, providers_exports } from "../providers/index.js";
21
21
  import { TaskRepository } from "../tasks/task-repository.js";
22
+ import { TaskConversationRepository } from "../tasks/task-conversation-repository.js";
22
23
  import { disposeAllSessionMcpRuntimes } from "../agent/mcp/bundle-mcp-runtime.js";
23
24
  import { resolveEffectiveGatewayPort } from "./host.js";
24
25
  import { AutomationService } from "../automations/service/automation-service.js";
25
26
  import { onAutomationProductEvent, publishAutomationProductEvent } from "../automations/product-events.js";
26
27
  import "../automations/index.js";
27
28
  import { ProjectService } from "../projects/project-service.js";
29
+ import { resolveProjectAgentId } from "../projects/project-agent.js";
28
30
  import "../projects/index.js";
29
31
  import { sanitizeTunnelConfig } from "../tunnel/tunnel-config.js";
30
32
  import { wireTunnelEventsToGateway } from "../tunnel/gateway-lifecycle.js";
31
33
  import { buckets } from "./rate-limit/buckets.js";
32
34
  import { isGatewayStrictSecurityEnabled } from "./rate-limit/env-flags.js";
33
35
  import "./rate-limit/index.js";
34
- import { TaskContextRepository } from "../tasks/task-context-repository.js";
35
36
  import { TaskApplicationService } from "../tasks/task-application-service.js";
36
37
  import { ProactiveScenarioService } from "../proactive/scenarios/service.js";
37
38
  import { TaskOutboxDispatcher } from "../tasks/task-outbox-dispatcher.js";
@@ -54,17 +55,17 @@ import { registerClarifyBridge } from "./clarify-runtime.js";
54
55
  import { ChannelManager } from "../channels/manager.js";
55
56
  import { getWorkspacePath } from "../config/workspace-path-helpers.js";
56
57
  import "../config/index.js";
57
- import { startConnectedKnowledgeCoordinator } from "../knowledge/coordinator.js";
58
- import "../knowledge/index.js";
59
- import { startConnectorLearningCoordinator } from "../connectors/learning-coordinator.js";
60
- import { ConnectedSourceChangePublisher } from "../connectors/source-change-publisher.js";
61
- import { startConnectorSupervisor } from "../connectors/supervisor.js";
62
58
  import { DiscussionService } from "../discussions/service.js";
63
59
  import { DiscussionOrganizer } from "../discussions/organizer.js";
64
60
  import { DiscussionLiveWorker } from "../discussions/live-worker.js";
65
61
  import { DiscussionOrganizerWorker } from "../discussions/organizer-worker.js";
66
62
  import { DiscussionSealer } from "../discussions/sealer.js";
67
63
  import "../discussions/index.js";
64
+ import { startConnectedKnowledgeCoordinator } from "../knowledge/coordinator.js";
65
+ import "../knowledge/index.js";
66
+ import { startConnectorLearningCoordinator } from "../connectors/learning-coordinator.js";
67
+ import { ConnectedSourceChangePublisher } from "../connectors/source-change-publisher.js";
68
+ import { startConnectorSupervisor } from "../connectors/supervisor.js";
68
69
  import { EndpointToolRuntime } from "../endpoint-tools/runtime.js";
69
70
  import "../endpoint-tools/index.js";
70
71
  import { assertGatewayAuthConfigured, extractToken, resolveGatewayAuth, validateToken } from "./auth.js";
@@ -541,42 +542,10 @@ var GatewayService = class {
541
542
  if (!this.taskRunDispatcher) this.taskRunDispatcher = new TaskRunDispatcher({
542
543
  workerId: "gateway-agent",
543
544
  ensureSession: async (taskId, runId, requestedAgentId) => {
544
- const task = new TaskRepository().require(taskId);
545
- const agentId = requestedAgentId || task.delegateAgentId || getDefaultAgentId(this.config);
546
- const peerId = `task-${sanitizeSegment(taskId) || Date.now()}`;
547
- const sessionKey = buildSessionKey({
548
- agentId,
549
- source: "webchat",
550
- accountId: "default",
551
- peerKind: "direct",
552
- peerId
553
- });
554
- if (!await this.sessionIndex.getSessionMetadata(sessionKey)) await this.sessionIndex.saveMessages(sessionKey, [], { metadata: {
555
- sourceChannel: "webchat",
556
- sourceChatId: `default:direct:${peerId}`,
557
- sessionType: "chat",
558
- routing: {
559
- agentId,
560
- source: "webchat",
561
- accountId: "default",
562
- peerKind: "direct",
563
- peerId
564
- },
565
- projectId: task.projectId,
566
- customData: {
567
- taskId,
568
- origin: "task",
569
- triggerKind: "user",
570
- taskRunId: runId
571
- }
572
- } });
573
- if (task.projectId) this.projects.attachSession(sessionKey, task.projectId);
574
- new TaskContextRepository().linkSession({
575
- taskId,
576
- sessionKey,
577
- role: "execution"
578
- });
579
- return sessionKey;
545
+ return (await this.ensureTaskConversation(taskId, {
546
+ runId,
547
+ requestedAgentId
548
+ })).sessionKey;
580
549
  },
581
550
  runAgent: async (runId, sessionKey, message) => {
582
551
  const stream = this.agentRunner.runAgent(message, "webchat", sessionKey, {
@@ -598,6 +567,70 @@ var GatewayService = class {
598
567
  this.createTaskRunDispatcher().dispatch();
599
568
  this.createWorkflowRunService().dispatchTaskRuns();
600
569
  }
570
+ async ensureTaskConversation(taskId, options = {}) {
571
+ const task = new TaskRepository().require(taskId);
572
+ const agentId = resolveProjectAgentId({
573
+ config: this.config,
574
+ projects: this.projects,
575
+ explicitAgentId: options.requestedAgentId ?? task.delegateAgentId,
576
+ projectId: task.projectId
577
+ });
578
+ const conversations = new TaskConversationRepository();
579
+ const active = conversations.getActiveSession(task.id);
580
+ if (active) {
581
+ if (active.agentId && active.agentId !== agentId) throw new Error("Task executor differs from the active conversation");
582
+ const conversation = conversations.activateExecutionSession({
583
+ taskId: task.id,
584
+ sessionKey: active.sessionKey,
585
+ agentId,
586
+ runId: options.runId
587
+ });
588
+ return {
589
+ sessionKey: active.sessionKey,
590
+ agentId,
591
+ created: false,
592
+ conversation
593
+ };
594
+ }
595
+ const peerId = `task-${sanitizeSegment(task.id) || Date.now()}`;
596
+ const sessionKey = buildSessionKey({
597
+ agentId,
598
+ source: "webchat",
599
+ accountId: "default",
600
+ peerKind: "direct",
601
+ peerId
602
+ });
603
+ if (!await this.sessionIndex.getSessionMetadata(sessionKey)) await this.sessionIndex.saveMessages(sessionKey, [], { metadata: {
604
+ sourceChannel: "webchat",
605
+ sourceChatId: `default:direct:${peerId}`,
606
+ sessionType: "chat",
607
+ routing: {
608
+ agentId,
609
+ source: "webchat",
610
+ accountId: "default",
611
+ peerKind: "direct",
612
+ peerId
613
+ },
614
+ projectId: task.projectId,
615
+ customData: {
616
+ origin: "task",
617
+ triggerKind: "user",
618
+ ...options.runId ? { taskRunId: options.runId } : {}
619
+ }
620
+ } });
621
+ if (task.projectId) this.projects.attachSession(sessionKey, task.projectId);
622
+ return {
623
+ sessionKey,
624
+ agentId,
625
+ created: true,
626
+ conversation: conversations.activateExecutionSession({
627
+ taskId: task.id,
628
+ sessionKey,
629
+ agentId,
630
+ runId: options.runId
631
+ })
632
+ };
633
+ }
601
634
  dispatchTaskEvents() {
602
635
  new TaskOutboxDispatcher(publishAutomationProductEvent).drain();
603
636
  }
@@ -8,7 +8,7 @@ function normalizeAgentId(value) {
8
8
  function resolveProjectAgentId(input) {
9
9
  const fallback = getDefaultAgentId(input.config);
10
10
  const explicitAgentId = normalizeAgentId(input.explicitAgentId);
11
- if (explicitAgentId) return agentExists(explicitAgentId, input.config) ? explicitAgentId : fallback;
11
+ if (explicitAgentId && agentExists(explicitAgentId, input.config)) return explicitAgentId;
12
12
  const projectId = input.projectId?.trim();
13
13
  const projectAgentId = projectId ? normalizeAgentId(input.projects.get(projectId)?.defaultAgentId) : void 0;
14
14
  if (projectAgentId && agentExists(projectAgentId, input.config)) return projectAgentId;