@xopcai/xopc 0.0.252 → 0.0.253

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 (168) hide show
  1. package/dist/browser-ext/manifest.json +1 -1
  2. package/dist/extensions/telegram/src/inbound-processor.js +2 -2
  3. package/dist/extensions/telegram/src/plugin.js +3 -2
  4. package/dist/extensions/telegram/xopc.extension.json +1 -1
  5. package/dist/gateway/static/root/assets/{activity-store-w3eq3YpP.js → activity-store-CeS1bgXP.js} +1 -1
  6. package/dist/gateway/static/root/assets/{agent-avatar-dicebear-k039rbUz.js → agent-avatar-dicebear-BdF3HhCW.js} +1 -1
  7. package/dist/gateway/static/root/assets/agent-defaults-settings-panel-Bs34Kp0K.js +1 -0
  8. package/dist/gateway/static/root/assets/{agents-Bmth_bSQ.js → agents-DARlgjTm.js} +1 -1
  9. package/dist/gateway/static/root/assets/{api-2joIfbrH.js → api-zMPeC0yU.js} +1 -1
  10. package/dist/gateway/static/root/assets/{app-management-settings-panel-ByJ4kiD0.js → app-management-settings-panel-BaAFln8W.js} +1 -1
  11. package/dist/gateway/static/root/assets/{appearance-settings-BK2Jga-B.js → appearance-settings-CXvsoBlw.js} +1 -1
  12. package/dist/gateway/static/root/assets/{apps-page-B3ky7cWU.js → apps-page-BhgvfBJT.js} +1 -1
  13. package/dist/gateway/static/root/assets/{archive-plugin-DMDKtYpi.js → archive-plugin-C0o_mVQI.js} +1 -1
  14. package/dist/gateway/static/root/assets/{attachment-load-B764Ql3Z.js → attachment-load-B5Ry3tGL.js} +1 -1
  15. package/dist/gateway/static/root/assets/{automations-page-D-sO8CZe.js → automations-page-DY5QAEQd.js} +1 -1
  16. package/dist/gateway/static/root/assets/automations-workspace-BzX4lILP.js +6 -0
  17. package/dist/gateway/static/root/assets/{binary-plugins-fxtzYRpv.js → binary-plugins-BydpEgBA.js} +1 -1
  18. package/dist/gateway/static/root/assets/{block-editor-Bcjtvckh.js → block-editor-D0CpUOMo.js} +3 -3
  19. package/dist/gateway/static/root/assets/{browser-automation-inputs-BSmnusjD.js → browser-automation-inputs-DzbTsGI6.js} +1 -1
  20. package/dist/gateway/static/root/assets/{browser-automations-page-DC_OQIBk.js → browser-automations-page-D7fZYjni.js} +1 -1
  21. package/dist/gateway/static/root/assets/{browser-settings-page-C1FojyG6.js → browser-settings-page-BBdMqxl0.js} +1 -1
  22. package/dist/gateway/static/root/assets/capabilities-settings-panel-D_WfftDW.js +7 -0
  23. package/dist/gateway/static/root/assets/channels-settings-bdRLT9KA.js +1 -0
  24. package/dist/gateway/static/root/assets/{channels-status-swr-BuWjIG_a.js → channels-status-swr-BGGRTkn6.js} +1 -1
  25. package/dist/gateway/static/root/assets/{chat-terminal-dock-5SevEo_I.js → chat-terminal-dock-_CP4nvlg.js} +1 -1
  26. package/dist/gateway/static/root/assets/connectors-page-CPDjU5wU.js +2 -0
  27. package/dist/gateway/static/root/assets/{cron-expression-DtsfalBM.js → cron-expression-g2U8Rllf.js} +1 -1
  28. package/dist/gateway/static/root/assets/{date-picker-tDEQV5TP.js → date-picker-SmVlhtj_.js} +1 -1
  29. package/dist/gateway/static/root/assets/{dependency-picker-HhNwg0Y2.js → dependency-picker-D-0gcbbM.js} +1 -1
  30. package/dist/gateway/static/root/assets/{desktop-pet-acU4Li-C.js → desktop-pet-Cl3JRIu6.js} +1 -1
  31. package/dist/gateway/static/root/assets/{desktop-pet-settings-R84AR0Pe.js → desktop-pet-settings-D7kvWQ9H.js} +1 -1
  32. package/dist/gateway/static/root/assets/{extension-debug-page-CrLlRkSN.js → extension-debug-page-DtP2tJHY.js} +1 -1
  33. package/dist/gateway/static/root/assets/{extension-page-BH5lmZXE.js → extension-page-CphZDdoT.js} +1 -1
  34. package/dist/gateway/static/root/assets/{extension-settings-page-BscjNAd1.js → extension-settings-page-CsxVLV4-.js} +1 -1
  35. package/dist/gateway/static/root/assets/{gateway-config-swr-9HidFZUd.js → gateway-config-swr-BW3Bx-aV.js} +1 -1
  36. package/dist/gateway/static/root/assets/gateway-settings-DxBZ05dX.js +1 -0
  37. package/dist/gateway/static/root/assets/{gateway-startup-retry-DRCPXONr.js → gateway-startup-retry-DAq74TZe.js} +1 -1
  38. package/dist/gateway/static/root/assets/{heartbeat-settings-K5Zhl2rx.js → heartbeat-settings-DWcKsIoZ.js} +1 -1
  39. package/dist/gateway/static/root/assets/home-page-BdvLZKQZ.js +1 -0
  40. package/dist/gateway/static/root/assets/{image-generation-api-CR6Kle8H.js → image-generation-api-MPuaXDRs.js} +1 -1
  41. package/dist/gateway/static/root/assets/index-C7RDs6S-.js +74 -0
  42. package/dist/gateway/static/root/assets/index-Cu8xqZQ9.css +2 -0
  43. package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-B1QR49-7.js → keyboard-shortcuts-settings-CfhaTqgE.js} +1 -1
  44. package/dist/gateway/static/root/assets/{local-app-workbench-page-wicoV-ti.js → local-app-workbench-page-CR6asX7L.js} +1 -1
  45. package/dist/gateway/static/root/assets/{local-apps-page-C74It-T8.js → local-apps-page-g01W3kEK.js} +1 -1
  46. package/dist/gateway/static/root/assets/{locale-store-BfZGZKSs.js → locale-store-BkeaPZpn.js} +1 -1
  47. package/dist/gateway/static/root/assets/{logs-page-xX-7S49A.js → logs-page-ZP4QTWoC.js} +1 -1
  48. package/dist/gateway/static/root/assets/{management-settings-1kNnYL97.js → management-settings-DeGxJF_Z.js} +1 -1
  49. package/dist/gateway/static/root/assets/markdown-view-Ca7BRBLL.js +201 -0
  50. package/dist/gateway/static/root/assets/{media-plugins-B5c0AoS1.js → media-plugins-FmOhmzlD.js} +1 -1
  51. package/dist/gateway/static/root/assets/messages-C72stReU.js +3 -0
  52. package/dist/gateway/static/root/assets/{mobile-pairing-wizard-CsIZqK51.js → mobile-pairing-wizard-BXNbn05L.js} +1 -1
  53. package/dist/gateway/static/root/assets/note-detail-panel-EeVeF4II.js +5 -0
  54. package/dist/gateway/static/root/assets/notes-page-D7bMff96.js +8 -0
  55. package/dist/gateway/static/root/assets/{page-tabs-CqJN6Ljz.js → page-tabs-pyukZ1PJ.js} +1 -1
  56. package/dist/gateway/static/root/assets/{preview-open-alternatives-DqRIGxf4.js → preview-open-alternatives-slFHoMUe.js} +1 -1
  57. package/dist/gateway/static/root/assets/{product-open-page-DE2nPE_K.js → product-open-page-CXa3FNNk.js} +1 -1
  58. package/dist/gateway/static/root/assets/project-detail-page-BDF88VdN.js +1 -0
  59. package/dist/gateway/static/root/assets/projects-page-DeTD6pu1.js +1 -0
  60. package/dist/gateway/static/root/assets/{registry-api-Bpy8XX-5.js → registry-api-CviQoUBf.js} +1 -1
  61. package/dist/gateway/static/root/assets/remote-access-hub-CxKrdQ6a.js +1 -0
  62. package/dist/gateway/static/root/assets/{runtime-tools-settings-panel-Bb4b74-P.js → runtime-tools-settings-panel-O9D9o7-O.js} +1 -1
  63. package/dist/gateway/static/root/assets/{schema-form-DoPU5j3J.js → schema-form-8FesicnL.js} +1 -1
  64. package/dist/gateway/static/root/assets/sessions-page-DUFJN-2t.js +1 -0
  65. package/dist/gateway/static/root/assets/settings-advanced-gate-CcSYDjaH.js +1 -0
  66. package/dist/gateway/static/root/assets/{settings-form-section-Ci-9WajJ.js → settings-form-section-CBic9TFH.js} +1 -1
  67. package/dist/gateway/static/root/assets/{settings-page-CL_2F95D.js → settings-page-Xz66_Mjg.js} +1 -1
  68. package/dist/gateway/static/root/assets/setup-status-panel-C6DuwrP5.js +1 -0
  69. package/dist/gateway/static/root/assets/{share-preview-page-XUA3oKQX.js → share-preview-page--JpjwB58.js} +1 -1
  70. package/dist/gateway/static/root/assets/shares-settings-CovcdHSC.js +1 -0
  71. package/dist/gateway/static/root/assets/skill-reload-api-CrHagTsE.js +1 -0
  72. package/dist/gateway/static/root/assets/{skills-page-sXXAZDc9.js → skills-page-fVKExKv5.js} +1 -1
  73. package/dist/gateway/static/root/assets/{skills-page.utils-DD79Bl1I.js → skills-page.utils-Yv9u3uJp.js} +1 -1
  74. package/dist/gateway/static/root/assets/{system-settings-panel-BEH64qBV.js → system-settings-panel-CCWjVdc2.js} +1 -1
  75. package/dist/gateway/static/root/assets/task-detail-page-I8hueJnG.js +1 -0
  76. package/dist/gateway/static/root/assets/{theme-store-CISSxU1c.js → theme-store-BtkaVpMm.js} +1 -1
  77. package/dist/gateway/static/root/assets/{tunnel-api-DrKDhnJm.js → tunnel-api-C__W9BaN.js} +1 -1
  78. package/dist/gateway/static/root/assets/{use-autosave-CPYePL-Q.js → use-autosave-Dow9RRcq.js} +1 -1
  79. package/dist/gateway/static/root/assets/user-model-page-CU6nxmoM.js +2 -0
  80. package/dist/gateway/static/root/assets/{voice-api-key-field-DKqzrCXx.js → voice-api-key-field-CvF3lEyx.js} +1 -1
  81. package/dist/gateway/static/root/assets/{voice-config-api-e9xWjwBX.js → voice-config-api-BEUVAyM6.js} +1 -1
  82. package/dist/gateway/static/root/assets/{work-discovery-overlay-kTQug6bq.js → work-discovery-overlay-B9PDfReq.js} +1 -1
  83. package/dist/gateway/static/root/assets/{workflow-run-setup-panel-m9SKj_GT.js → workflow-run-setup-panel-kWGFkhjz.js} +2 -2
  84. package/dist/gateway/static/root/assets/workflows-page-B4Is5E42.js +5 -0
  85. package/dist/gateway/static/root/index.html +10 -10
  86. package/dist/package.js +1 -1
  87. package/dist/packages/realtime-protocol/src/voice.js +6 -1
  88. package/dist/src/agent/agent-manager.js +20 -3
  89. package/dist/src/agent/embedded/execution-context.d.ts +2 -1
  90. package/dist/src/agent/embedded/execution-context.js +10 -4
  91. package/dist/src/agent/embedded/index.d.ts +1 -1
  92. package/dist/src/agent/embedded/index.js +2 -2
  93. package/dist/src/agent/embedded/run-for-session.js +4 -3
  94. package/dist/src/agent/embedded/run-turn.js +22 -3
  95. package/dist/src/agent/embedded/types.d.ts +1 -1
  96. package/dist/src/agent/service/direct-turn-helpers.js +6 -3
  97. package/dist/src/agent/service/process-direct-streaming.js +6 -3
  98. package/dist/src/agent/service.d.ts +2 -0
  99. package/dist/src/agent/service.js +21 -7
  100. package/dist/src/agent/tools/clarify-tool.d.ts +17 -3
  101. package/dist/src/agent/tools/clarify-tool.js +27 -32
  102. package/dist/src/agent/tools/cli-clarify.js +23 -8
  103. package/dist/src/agent/tools/factory.js +14 -4
  104. package/dist/src/automations/domain/validation.d.ts +4 -4
  105. package/dist/src/automations/draft/automation-draft-validator.d.ts +1 -1
  106. package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
  107. package/dist/src/gateway/chat-stream/mapper.js +5 -1
  108. package/dist/src/gateway/chat-stream/protocol.d.ts +5 -1
  109. package/dist/src/gateway/clarify-runtime.d.ts +7 -5
  110. package/dist/src/gateway/clarify-runtime.js +8 -11
  111. package/dist/src/gateway/ephemeral-clarification-waiter.d.ts +17 -0
  112. package/dist/src/gateway/ephemeral-clarification-waiter.js +46 -0
  113. package/dist/src/gateway/hono/routes/agent-stream.js +44 -18
  114. package/dist/src/gateway/hono/routes/session-input-handler.d.ts +10 -2
  115. package/dist/src/gateway/hono/routes/side-chats.js +24 -4
  116. package/dist/src/gateway/security/gateway-scopes.js +1 -1
  117. package/dist/src/gateway/service/agent-runner.d.ts +25 -36
  118. package/dist/src/gateway/service/agent-runner.js +103 -32
  119. package/dist/src/gateway/service/run-gateway-agent.js +5 -2
  120. package/dist/src/gateway/service/session-input-coordinator.js +4 -1
  121. package/dist/src/gateway/service/sessions-api.js +1 -1
  122. package/dist/src/gateway/service.d.ts +4 -2
  123. package/dist/src/gateway/service.js +26 -10
  124. package/dist/src/gateway/side-chat/manager.d.ts +2 -1
  125. package/dist/src/gateway/side-chat/manager.js +5 -4
  126. package/dist/src/gateway/side-chat/run-service.d.ts +5 -1
  127. package/dist/src/gateway/side-chat/run-service.js +24 -23
  128. package/dist/src/gateway/side-chat/types.d.ts +3 -1
  129. package/dist/src/storage/sqlite/clarification-wait-repository.d.ts +41 -0
  130. package/dist/src/storage/sqlite/clarification-wait-repository.js +418 -0
  131. package/dist/src/storage/sqlite/index.d.ts +2 -1
  132. package/dist/src/storage/sqlite/index.js +2 -1
  133. package/dist/src/storage/sqlite/migrations/154_session_clarification_waits.sql +60 -0
  134. package/dist/src/storage/sqlite/migrations/runner.d.ts +1 -1
  135. package/dist/src/storage/sqlite/migrations/runner.js +3 -3
  136. package/dist/src/storage/sqlite/migrations/runner.ts +1 -1
  137. package/dist/src/storage/sqlite/schema.js +1 -1
  138. package/dist/src/storage/sqlite/session-input-repository.d.ts +11 -6
  139. package/dist/src/voice/realtime/agentEngine.js +7 -2
  140. package/dist/src/voice/realtime/runtime.types.d.ts +5 -0
  141. package/package.json +3 -3
  142. package/skills/creative/hatch-pet/SKILL.md +1 -1
  143. package/dist/gateway/static/root/assets/agent-defaults-settings-panel-D4J4oq8p.js +0 -1
  144. package/dist/gateway/static/root/assets/automations-workspace-Ds5-_-9z.js +0 -6
  145. package/dist/gateway/static/root/assets/capabilities-settings-panel-Bs59UCut.js +0 -7
  146. package/dist/gateway/static/root/assets/channels-settings-CM9vNmoo.js +0 -1
  147. package/dist/gateway/static/root/assets/connectors-page-BPZ44D-V.js +0 -2
  148. package/dist/gateway/static/root/assets/gateway-settings-BxLcW6ij.js +0 -1
  149. package/dist/gateway/static/root/assets/home-page-CZAg9RBv.js +0 -1
  150. package/dist/gateway/static/root/assets/index-D4Ykja-U.js +0 -72
  151. package/dist/gateway/static/root/assets/index-zIJ3AXgR.css +0 -2
  152. package/dist/gateway/static/root/assets/markdown-view-DjiwT28p.js +0 -201
  153. package/dist/gateway/static/root/assets/messages-DaIwVWar.js +0 -3
  154. package/dist/gateway/static/root/assets/note-detail-panel-CfWxopl4.js +0 -5
  155. package/dist/gateway/static/root/assets/notes-page-CYN-LCHJ.js +0 -8
  156. package/dist/gateway/static/root/assets/project-detail-page-B5G3LzDq.js +0 -1
  157. package/dist/gateway/static/root/assets/projects-page-CuVK2mKu.js +0 -1
  158. package/dist/gateway/static/root/assets/remote-access-hub-Dnd73xWt.js +0 -1
  159. package/dist/gateway/static/root/assets/sessions-page-XL4LyKZe.js +0 -1
  160. package/dist/gateway/static/root/assets/settings-advanced-gate-0ForzDSS.js +0 -1
  161. package/dist/gateway/static/root/assets/setup-status-panel-DVws6XZc.js +0 -1
  162. package/dist/gateway/static/root/assets/shares-settings-BwDvcacF.js +0 -1
  163. package/dist/gateway/static/root/assets/skill-reload-api-BJjrATqE.js +0 -1
  164. package/dist/gateway/static/root/assets/task-detail-page-0jxMi75O.js +0 -1
  165. package/dist/gateway/static/root/assets/user-model-page-B6zLDfDl.js +0 -2
  166. package/dist/gateway/static/root/assets/workflows-page-Bcd1XqP7.js +0 -5
  167. package/dist/src/gateway/clarify-bridge.d.ts +0 -46
  168. package/dist/src/gateway/clarify-bridge.js +0 -111
@@ -0,0 +1,46 @@
1
+ //#region src/gateway/ephemeral-clarification-waiter.ts
2
+ /** In-memory waiter exclusively for caller-owned ephemeral runs such as side chat. */
3
+ var EphemeralClarificationWaiter = class {
4
+ pending = /* @__PURE__ */ new Map();
5
+ start(input) {
6
+ const requestId = crypto.randomUUID();
7
+ return new Promise((resolve, reject) => {
8
+ this.pending.set(requestId, {
9
+ runId: input.runId,
10
+ resolve,
11
+ reject,
12
+ beforeResponse: input.beforeResponse
13
+ });
14
+ input.publish({
15
+ type: "clarify_request",
16
+ requestId,
17
+ kind: input.request.kind ?? "input",
18
+ question: input.request.question,
19
+ choices: input.request.choices,
20
+ suggestedAnswer: input.request.suggestedAnswer,
21
+ version: 1,
22
+ createdAt: Date.now()
23
+ });
24
+ });
25
+ }
26
+ answer(requestId, answer) {
27
+ const pending = this.pending.get(requestId);
28
+ if (!pending || pending.beforeResponse?.() === false) return false;
29
+ this.pending.delete(requestId);
30
+ pending.resolve(answer.trim());
31
+ return true;
32
+ }
33
+ cancelForRun(runId) {
34
+ for (const [requestId, pending] of this.pending) {
35
+ if (pending.runId !== runId) continue;
36
+ this.pending.delete(requestId);
37
+ pending.reject(/* @__PURE__ */ new Error("Ephemeral clarification cancelled"));
38
+ }
39
+ }
40
+ dispose() {
41
+ for (const pending of this.pending.values()) pending.reject(/* @__PURE__ */ new Error("Gateway shutting down"));
42
+ this.pending.clear();
43
+ }
44
+ };
45
+ //#endregion
46
+ export { EphemeralClarificationWaiter };
@@ -122,38 +122,64 @@ function registerAgentStreamRoutes(authenticated, deps) {
122
122
  payload: result.state
123
123
  }, 409);
124
124
  });
125
- authenticated.post("/api/clarify/:requestId", chatRateLimitMiddleware, async (c) => {
126
- const requestId = c.req.param("requestId")?.trim() ?? "";
127
- if (!requestId) return c.json({
128
- ok: false,
129
- error: {
130
- code: "BAD_REQUEST",
131
- message: "Missing requestId"
132
- }
133
- }, 400);
134
- const body = await c.req.json().catch(() => null);
135
- const skip = body && typeof body === "object" && body.skip === true;
136
- const rawAnswer = body && typeof body === "object" && typeof body.answer === "string" ? body.answer : "";
137
- const answer = typeof rawAnswer === "string" ? rawAnswer.trim() : "";
138
- if (!skip && !answer) return c.json({
125
+ authenticated.get("/api/sessions/:sessionKey/clarification", (c) => {
126
+ const sessionKey = (c.req.param("sessionKey") ?? "").trim();
127
+ if (!sessionKey) return c.json({
139
128
  ok: false,
140
129
  error: {
141
130
  code: "BAD_REQUEST",
142
- message: "Missing answer field"
131
+ message: "Missing sessionKey"
143
132
  }
144
133
  }, 400);
145
- if (!service.submitClarifyResponse(requestId, skip ? "" : answer)) return c.json({
134
+ const snapshot = service.getClarificationState(sessionKey);
135
+ if (!snapshot) return c.json({
146
136
  ok: false,
147
137
  error: {
148
138
  code: "NOT_FOUND",
149
- message: "No pending clarification with this ID"
139
+ message: "Session not found"
150
140
  }
151
141
  }, 404);
152
142
  return c.json({
153
143
  ok: true,
154
- payload: { received: true }
144
+ payload: snapshot
155
145
  });
156
146
  });
147
+ authenticated.post("/api/clarifications/:id/responses", chatRateLimitMiddleware, async (c) => {
148
+ const id = c.req.param("id")?.trim() ?? "";
149
+ const body = await c.req.json().catch(() => null);
150
+ const record = body && typeof body === "object" ? body : {};
151
+ const action = record.action;
152
+ const expectedVersion = record.expectedVersion;
153
+ const idempotencyKey = typeof record.idempotencyKey === "string" ? record.idempotencyKey.trim() : "";
154
+ if (!id || !idempotencyKey || typeof expectedVersion !== "number" || !Number.isInteger(expectedVersion) || action !== "answer" && action !== "agent_decide" && action !== "cancel") return c.json({
155
+ ok: false,
156
+ error: {
157
+ code: "BAD_REQUEST",
158
+ message: "Invalid clarification response"
159
+ }
160
+ }, 400);
161
+ const result = service.resolveClarificationResponse({
162
+ id,
163
+ expectedVersion,
164
+ idempotencyKey,
165
+ action,
166
+ answer: typeof record.answer === "string" ? record.answer : void 0
167
+ });
168
+ if (result.ok === true) return c.json({
169
+ ok: true,
170
+ payload: result
171
+ }, result.queued ? 202 : 200);
172
+ const status = result.code === "NOT_FOUND" ? 404 : result.code === "EXPIRED" ? 410 : result.code === "INVALID" ? 400 : 409;
173
+ const message = result.code === "NOT_FOUND" ? "This clarification is no longer active" : result.code === "EXPIRED" ? "This approval has expired" : result.code === "INVALID" ? "The clarification response is invalid" : "The clarification changed; reload and try again";
174
+ return c.json({
175
+ ok: false,
176
+ error: {
177
+ code: result.code,
178
+ message
179
+ },
180
+ payload: result.clarification
181
+ }, status);
182
+ });
157
183
  authenticated.post("/api/send", chatRateLimitMiddleware, async (c) => {
158
184
  const body = await c.req.json().catch(() => null);
159
185
  const channel = typeof body?.channel === "string" ? body.channel : "";
@@ -45,11 +45,15 @@ export declare function submitSessionInput(c: Context, deps: AuthenticatedRouteD
45
45
  status: import("../../../storage/sqlite/session-input-repository.js").SessionInputStatus;
46
46
  content: string;
47
47
  taskRunId?: string;
48
- kind: "message" | "connection_resume";
48
+ kind: "message" | "connection_resume" | "clarification_resume";
49
49
  payload?: {
50
50
  waitId: string;
51
51
  objectiveRevision: number;
52
52
  resolution: "continued" | "skipped";
53
+ } | {
54
+ waitId: string;
55
+ objectiveRevision: number;
56
+ resolution: "answered" | "agent_decide" | "cancelled";
53
57
  };
54
58
  attachments?: import("hono/dist/types/utils/types.js").JSONValue[];
55
59
  contextRefs?: {
@@ -141,11 +145,15 @@ export declare function replaceLatestSessionTurn(c: Context, deps: Authenticated
141
145
  status: import("../../../storage/sqlite/session-input-repository.js").SessionInputStatus;
142
146
  content: string;
143
147
  taskRunId?: string;
144
- kind: "message" | "connection_resume";
148
+ kind: "message" | "connection_resume" | "clarification_resume";
145
149
  payload?: {
146
150
  waitId: string;
147
151
  objectiveRevision: number;
148
152
  resolution: "continued" | "skipped";
153
+ } | {
154
+ waitId: string;
155
+ objectiveRevision: number;
156
+ resolution: "answered" | "agent_decide" | "cancelled";
149
157
  };
150
158
  attachments?: import("hono/dist/types/utils/types.js").JSONValue[];
151
159
  contextRefs?: {
@@ -1,9 +1,13 @@
1
- import { validateWebchatContent } from "../../chat-limits.js";
1
+ import { chooseModelThinking } from "../../../../packages/gateway-contract/src/model-selection.js";
2
+ import { init_providers, resolveModel } from "../../../providers/index.js";
3
+ import { getModelThinking } from "../../../providers/model-thinking.js";
4
+ import { validateWebchatAttachments, validateWebchatContent } from "../../chat-limits.js";
2
5
  import { validateSideChatSelections } from "../../side-chat/context-snapshot.js";
3
6
  import { SideChatError, parseThinkingLevel } from "../../side-chat/manager.js";
4
7
  import "../../side-chat/index.js";
5
8
  import { createGatewayRouteLogger, logRouteError } from "../lib/route-logger.js";
6
9
  //#region src/gateway/hono/routes/side-chats.ts
10
+ init_providers();
7
11
  const CLIENT_HEADER = "x-xopc-client-instance-id";
8
12
  const log = createGatewayRouteLogger("SideChats");
9
13
  function registerSideChatRoutes(authenticated, deps) {
@@ -64,9 +68,18 @@ function registerSideChatRoutes(authenticated, deps) {
64
68
  const body = await c.req.json().catch(() => ({}));
65
69
  if (body.modelRef !== void 0 && typeof body.modelRef !== "string") throw new SideChatError("modelRef must be a string", "INVALID_REQUEST");
66
70
  const modelRef = typeof body.modelRef === "string" ? body.modelRef : void 0;
71
+ const current = service.sideChats.get(c.req.param("sideChatId"), readClientInstanceId(c, body.clientInstanceId));
72
+ const nextModelRef = modelRef ?? current.config.modelRef;
73
+ let thinking;
74
+ try {
75
+ thinking = getModelThinking(resolveModel(nextModelRef));
76
+ } catch {
77
+ throw new SideChatError(`Model unavailable: ${nextModelRef}`, "INVALID_REQUEST");
78
+ }
79
+ const requestedThinking = parseThinkingLevel(body.thinkingLevel);
67
80
  const sideChat = service.sideChats.updateConfig(c.req.param("sideChatId"), readClientInstanceId(c, body.clientInstanceId), {
68
- modelRef,
69
- thinkingLevel: parseThinkingLevel(body.thinkingLevel)
81
+ modelRef: nextModelRef,
82
+ thinkingLevel: chooseModelThinking(thinking, requestedThinking ?? current.config.thinkingLevel)
70
83
  });
71
84
  return c.json({
72
85
  ok: true,
@@ -101,9 +114,16 @@ function registerSideChatRoutes(authenticated, deps) {
101
114
  try {
102
115
  const body = await c.req.json().catch(() => ({}));
103
116
  const content = typeof body.content === "string" ? body.content : "";
117
+ const attachments = Array.isArray(body.attachments) ? body.attachments : void 0;
104
118
  const contentError = validateWebchatContent(content);
105
119
  if (contentError) throw new SideChatError(contentError, "INVALID_REQUEST");
106
- const result = service.sideChatRuns.submit(c.req.param("sideChatId"), readClientInstanceId(c, body.clientInstanceId), content);
120
+ const attachmentError = validateWebchatAttachments(attachments);
121
+ if (attachmentError) throw new SideChatError(attachmentError, "INVALID_REQUEST");
122
+ if (!content.trim() && !attachments?.length) throw new SideChatError("content or attachments are required", "INVALID_REQUEST");
123
+ const result = service.sideChatRuns.submit(c.req.param("sideChatId"), readClientInstanceId(c, body.clientInstanceId), {
124
+ content,
125
+ attachments
126
+ });
107
127
  return c.json({
108
128
  ok: true,
109
129
  payload: result
@@ -57,7 +57,7 @@ function requiredGatewayScope(method, path) {
57
57
  if (path.startsWith("/api/home") || path.startsWith("/api/inbox")) return methodScope(method, "tasks.read", "tasks.write");
58
58
  if (path.startsWith("/api/automations") || path.startsWith("/api/automation-runs")) return methodScope(method, "automations.read", "automations.write");
59
59
  if (path.startsWith("/api/workflows")) return methodScope(method, "automations.read", "automations.write");
60
- if (path.startsWith("/api/voice") || path.startsWith("/api/media") || path.startsWith("/api/clarify")) return "sessions.write";
60
+ if (path.startsWith("/api/voice") || path.startsWith("/api/media") || path.startsWith("/api/clarifications")) return "sessions.write";
61
61
  if (path.startsWith("/api/commands") || path.startsWith("/api/skills")) return methodScope(method, "agents.read", "gateway.admin");
62
62
  if (path.startsWith("/api/workspace") || path.startsWith("/api/files") || path.startsWith("/api/projects") || path.startsWith("/api/notes") || path.startsWith("/api/shares")) return methodScope(method, "workspace.read", "workspace.write");
63
63
  return "gateway.admin";
@@ -1,31 +1,12 @@
1
1
  import type { TurnOrigin } from '@xopcai/endpoint-tools-protocol';
2
- /**
3
- * GatewayAgentRunner — webchat agent invocation and the surrounding control
4
- * surface (abort, steer, clarify-bridge plumbing, scheduled Task continuations).
5
- *
6
- * Was 200 lines of `GatewayService` covering seven concerns that all hung off
7
- * the same handful of fields (`activeWebchatRunBySession`, `runAbortControllers`,
8
- * `clarifyBridge`, `runRelay`):
9
- *
10
- * - `runAgent(message, channel, chatId, ...)` — wraps {@link runGatewayAgent}
11
- * - `abortAgentRun(runId)` — POST /api/agent/abort + cleanup
12
- * - `submitClarifyResponse(requestId, answer)` — UI answers a `clarify` call
13
- * - `runScheduledWebchatTurn(sk, userTurn)` — background webchat user turn
14
- * - `drainScheduledWebchatContinuation(sk, msg)` — background Task continuation
15
- * - `clarifyForSession({ sessionKey, request })` — clarify-bridge dispatch
16
- * used by `gatewayClarify.requestClarification` in AgentService
17
- *
18
- * Owns the two state maps (`activeWebchatRunBySession`, `runAbortControllers`)
19
- * directly so peer coordinators (sessions-api, marketplace, config) cannot
20
- * accidentally mutate them.
21
- */
2
+ import type { ClarificationResponseAction } from '@xopcai/gateway-contract';
22
3
  import type { Config } from '../../config/schema.js';
23
4
  import type { MessageBus } from '../../infra/bus/index.js';
24
5
  import type { AgentService } from '../../agent/service.js';
25
6
  import type { ChannelManager } from '../../channels/manager.js';
26
7
  import type { SessionIndex } from '../../session/index.js';
27
- import type { ClarifyStreamEvent } from '../clarify-bridge.js';
28
- import { ClarifyBridge, type ClarifyBridgeRequest } from '../clarify-bridge.js';
8
+ import { type ClarificationStreamEvent } from '../ephemeral-clarification-waiter.js';
9
+ import type { ClarifyRequestPayload, ClarifyRequestResult } from '../../agent/tools/clarify-tool.js';
29
10
  import type { UserTurnAttachment, UserTurnInput } from '../user-turn-input.js';
30
11
  import type { AgentSourceContext, TurnContextRef } from '../../agent/source-context/types.js';
31
12
  import { SessionInputCoordinator, type ReplaceLatestTurnInput, type SubmitSessionInput } from './session-input-coordinator.js';
@@ -49,11 +30,10 @@ export declare class GatewayAgentRunner {
49
30
  private readonly runAbortControllers;
50
31
  private readonly runCompletions;
51
32
  private readonly resolveRunCompletions;
52
- private readonly clarifyBridge;
33
+ private readonly ephemeralClarifications;
53
34
  /** Maps webchat session key → active `runId` for `clarify` tool routing. */
54
35
  private readonly activeWebchatRunBySession;
55
36
  private readonly externalStreamBySession;
56
- private readonly externalClarificationTimeouts;
57
37
  private readonly externalClarificationResponses;
58
38
  readonly inputs: SessionInputCoordinator;
59
39
  private readonly unsubscribeConnectionWait;
@@ -65,11 +45,8 @@ export declare class GatewayAgentRunner {
65
45
  sessionKey: string;
66
46
  runId: string;
67
47
  }>;
68
- getClarifyBridge(): ClarifyBridge;
69
- /** Called from `GatewayService.stop()` so the bridge gets cleaned up. */
70
- disposeClarifyBridge(): void;
71
- registerExternalWebchatRun(sessionKey: string, runId: string, publish: (event: ClarifyStreamEvent) => void, options?: {
72
- clarificationTimeoutMs?: number | null;
48
+ disposeClarifications(): void;
49
+ registerExternalWebchatRun(sessionKey: string, runId: string, publish: (event: ClarificationStreamEvent) => void, options?: {
73
50
  beforeClarificationResponse?: () => boolean;
74
51
  }): void;
75
52
  unregisterExternalWebchatRun(sessionKey: string, runId: string): void;
@@ -126,16 +103,26 @@ export declare class GatewayAgentRunner {
126
103
  aborted: boolean;
127
104
  idle: boolean;
128
105
  }>;
129
- /** Deliver a user's answer to a pending `clarify` tool call. */
130
- submitClarifyResponse(requestId: string, answer: string): boolean;
106
+ answerEphemeralClarification(requestId: string, answer: string): boolean;
107
+ getClarificationState(sessionKey: string): import("@xopcai/gateway-contract").ClarificationWaitSnapshot;
108
+ resolveClarificationResponse(input: {
109
+ id: string;
110
+ expectedVersion: number;
111
+ idempotencyKey: string;
112
+ action: ClarificationResponseAction;
113
+ answer?: string;
114
+ }): import("../../storage/sqlite/clarification-wait-repository.js").ResolveClarificationResult;
115
+ getClarificationById(id: string): import("@xopcai/gateway-contract").ClarificationWait;
116
+ answerClarificationChoice(requestId: string, choiceIndex: number, idempotencyKey: string): boolean;
117
+ answerClarificationText(sessionKey: string, answer: string, idempotencyKey: string): boolean;
131
118
  /** Same execution path as scheduled continuation, but lets callers observe failures. */
132
119
  runScheduledWebchatTurn(sessionKey: string, userTurn: UserTurnInput): Promise<void>;
133
120
  runScheduledWebchatContinuation(sessionKey: string, message: string): Promise<void>;
134
121
  /** Background drain for extension-initiated webchat turns (`scheduleWebchatContinuation`). */
135
122
  drainScheduledWebchatContinuation(sessionKey: string, message: string): Promise<void>;
136
123
  /**
137
- * Resolve clarify-bridge config for `sessionKey`: who delivers the question
138
- * (webchat stream, Telegram message, or both), then start the bridge request.
124
+ * Persist normal clarification waits and use the lifecycle-bound waiter only
125
+ * for caller-owned ephemeral sessions.
139
126
  * Rejects when neither path is available (e.g. CLI without webchat or TG).
140
127
  *
141
128
  * `publishStreamFor(runId)` is the bridge into AgentService's
@@ -144,8 +131,10 @@ export declare class GatewayAgentRunner {
144
131
  */
145
132
  requestClarification(opts: {
146
133
  sessionKey: string;
147
- request: ClarifyBridgeRequest;
148
- publishStreamFor: (runId: string) => (event: ClarifyStreamEvent) => void;
149
- }): Promise<string>;
134
+ runId: string;
135
+ toolCallId: string;
136
+ request: ClarifyRequestPayload;
137
+ publishStreamFor: (runId: string) => (event: ClarificationStreamEvent) => void;
138
+ }): Promise<ClarifyRequestResult>;
150
139
  private deliverTelegramClarify;
151
140
  }
@@ -1,15 +1,20 @@
1
1
  import { createLogger } from "../../utils/logger/index.js";
2
2
  import { init_logger } from "../../utils/logger.js";
3
3
  import { listActiveSessionInputRuns } from "../../storage/sqlite/session-input-repository.js";
4
+ import { createClarificationWait, getClarification, getClarificationSnapshot, resolveClarification, supersedeActiveClarification } from "../../storage/sqlite/clarification-wait-repository.js";
4
5
  import { onConnectionWaitChanged } from "../../storage/sqlite/connection-wait-repository.js";
5
6
  import "../../storage/sqlite/index.js";
6
- import { ClarifyBridge } from "../clarify-bridge.js";
7
+ import { EphemeralClarificationWaiter } from "../ephemeral-clarification-waiter.js";
7
8
  import { runGatewayAgent } from "./run-gateway-agent.js";
8
9
  import { fitSourceContextsToBudget } from "../../agent/source-context/budget.js";
9
10
  import { SessionInputCoordinator } from "./session-input-coordinator.js";
10
11
  import crypto from "node:crypto";
11
12
  //#region src/gateway/service/agent-runner.ts
12
13
  init_logger();
14
+ /**
15
+ * Owns gateway agent runs, durable clarification coordination, and the
16
+ * lifecycle-bound clarification waiter used by ephemeral side chats.
17
+ */
13
18
  const log = createLogger("Gateway:AgentRunner");
14
19
  const MAX_TURN_CONTEXTS = 5;
15
20
  var GatewayAgentRunner = class {
@@ -18,11 +23,10 @@ var GatewayAgentRunner = class {
18
23
  runAbortControllers = /* @__PURE__ */ new Map();
19
24
  runCompletions = /* @__PURE__ */ new Map();
20
25
  resolveRunCompletions = /* @__PURE__ */ new Map();
21
- clarifyBridge = new ClarifyBridge();
26
+ ephemeralClarifications = new EphemeralClarificationWaiter();
22
27
  /** Maps webchat session key → active `runId` for `clarify` tool routing. */
23
28
  activeWebchatRunBySession = /* @__PURE__ */ new Map();
24
29
  externalStreamBySession = /* @__PURE__ */ new Map();
25
- externalClarificationTimeouts = /* @__PURE__ */ new Map();
26
30
  externalClarificationResponses = /* @__PURE__ */ new Map();
27
31
  inputs;
28
32
  unsubscribeConnectionWait;
@@ -104,30 +108,24 @@ var GatewayAgentRunner = class {
104
108
  runId
105
109
  }));
106
110
  }
107
- getClarifyBridge() {
108
- return this.clarifyBridge;
109
- }
110
- /** Called from `GatewayService.stop()` so the bridge gets cleaned up. */
111
- disposeClarifyBridge() {
112
- this.clarifyBridge.dispose();
111
+ disposeClarifications() {
112
+ this.ephemeralClarifications.dispose();
113
113
  this.unsubscribeConnectionWait();
114
114
  }
115
115
  registerExternalWebchatRun(sessionKey, runId, publish, options) {
116
116
  this.activeWebchatRunBySession.set(sessionKey, runId);
117
117
  this.externalStreamBySession.set(sessionKey, publish);
118
- if (options?.clarificationTimeoutMs !== void 0) this.externalClarificationTimeouts.set(sessionKey, options.clarificationTimeoutMs);
119
118
  if (options?.beforeClarificationResponse) this.externalClarificationResponses.set(sessionKey, options.beforeClarificationResponse);
120
119
  }
121
120
  unregisterExternalWebchatRun(sessionKey, runId) {
122
121
  if (this.activeWebchatRunBySession.get(sessionKey) === runId) {
123
122
  this.activeWebchatRunBySession.delete(sessionKey);
124
123
  this.externalStreamBySession.delete(sessionKey);
125
- this.externalClarificationTimeouts.delete(sessionKey);
126
124
  this.externalClarificationResponses.delete(sessionKey);
127
125
  }
128
126
  }
129
127
  cancelClarificationForRun(runId) {
130
- this.clarifyBridge.cancelForRun(runId);
128
+ this.ephemeralClarifications.cancelForRun(runId);
131
129
  }
132
130
  async *runAgent(message, channel, chatId, origin, attachments, thinking, runOptions) {
133
131
  const trackedRunId = runOptions?.runId;
@@ -185,7 +183,7 @@ var GatewayAgentRunner = class {
185
183
  }
186
184
  /** Abort an in-flight webchat agent run. */
187
185
  async abortAgentRun(runId) {
188
- this.clarifyBridge.cancelForRun(runId);
186
+ this.ephemeralClarifications.cancelForRun(runId);
189
187
  const keysToMark = [];
190
188
  for (const [sk, id] of this.activeWebchatRunBySession) if (id === runId) keysToMark.push(sk);
191
189
  const c = this.runAbortControllers.get(runId);
@@ -194,10 +192,13 @@ var GatewayAgentRunner = class {
194
192
  idle: true
195
193
  };
196
194
  const completion = this.runCompletions.get(runId);
197
- for (const sk of keysToMark) this.opts.sessionIndex.appendTranscriptContextEntry(sk, {
198
- text: "Webchat agent run aborted",
199
- data: { runId }
200
- }).catch(() => {});
195
+ for (const sk of keysToMark) {
196
+ if ((getClarificationSnapshot(sk)?.clarification)?.originRunId === runId) supersedeActiveClarification(sk);
197
+ this.opts.sessionIndex.appendTranscriptContextEntry(sk, {
198
+ text: "Webchat agent run aborted",
199
+ data: { runId }
200
+ }).catch(() => {});
201
+ }
201
202
  c.abort();
202
203
  const { abortEmbeddedRun } = await import("../../agent/embedded/runs.js");
203
204
  await Promise.all(keysToMark.map((sessionKey) => abortEmbeddedRun(sessionKey).catch(() => false)));
@@ -207,9 +208,45 @@ var GatewayAgentRunner = class {
207
208
  idle: true
208
209
  };
209
210
  }
210
- /** Deliver a user's answer to a pending `clarify` tool call. */
211
- submitClarifyResponse(requestId, answer) {
212
- return this.clarifyBridge.handleResponse(requestId, answer);
211
+ answerEphemeralClarification(requestId, answer) {
212
+ return this.ephemeralClarifications.answer(requestId, answer);
213
+ }
214
+ getClarificationState(sessionKey) {
215
+ return getClarificationSnapshot(sessionKey);
216
+ }
217
+ resolveClarificationResponse(input) {
218
+ const result = resolveClarification(input);
219
+ if (result.ok) {
220
+ this.opts.emit("clarification.updated", result.clarification);
221
+ if (result.queued) this.inputs.drain(result.clarification.sessionKey);
222
+ }
223
+ return result;
224
+ }
225
+ getClarificationById(id) {
226
+ return getClarification(id);
227
+ }
228
+ answerClarificationChoice(requestId, choiceIndex, idempotencyKey) {
229
+ const wait = getClarification(requestId);
230
+ const answer = wait?.status === "open" ? wait.choices?.[choiceIndex] : void 0;
231
+ if (!answer) return false;
232
+ return this.resolveClarificationResponse({
233
+ id: wait.id,
234
+ expectedVersion: wait.version,
235
+ idempotencyKey,
236
+ action: "answer",
237
+ answer
238
+ }).ok;
239
+ }
240
+ answerClarificationText(sessionKey, answer, idempotencyKey) {
241
+ const wait = getClarificationSnapshot(sessionKey)?.clarification;
242
+ if (!wait || wait.status !== "open" || !answer.trim()) return false;
243
+ return this.resolveClarificationResponse({
244
+ id: wait.id,
245
+ expectedVersion: wait.version,
246
+ idempotencyKey,
247
+ action: "answer",
248
+ answer
249
+ }).ok;
213
250
  }
214
251
  /** Same execution path as scheduled continuation, but lets callers observe failures. */
215
252
  async runScheduledWebchatTurn(sessionKey, userTurn) {
@@ -243,8 +280,8 @@ var GatewayAgentRunner = class {
243
280
  }
244
281
  }
245
282
  /**
246
- * Resolve clarify-bridge config for `sessionKey`: who delivers the question
247
- * (webchat stream, Telegram message, or both), then start the bridge request.
283
+ * Persist normal clarification waits and use the lifecycle-bound waiter only
284
+ * for caller-owned ephemeral sessions.
248
285
  * Rejects when neither path is available (e.g. CLI without webchat or TG).
249
286
  *
250
287
  * `publishStreamFor(runId)` is the bridge into AgentService's
@@ -253,33 +290,67 @@ var GatewayAgentRunner = class {
253
290
  */
254
291
  async requestClarification(opts) {
255
292
  const { sessionKey, request, publishStreamFor } = opts;
256
- const runId = this.activeWebchatRunBySession.get(sessionKey);
293
+ const runId = this.activeWebchatRunBySession.get(sessionKey) ?? opts.runId;
257
294
  const publishStream = this.externalStreamBySession.get(sessionKey) ?? (runId ? publishStreamFor(runId) : void 0);
258
295
  const deliver = ((await this.opts.sessionIndex.getSessionMetadata(sessionKey).catch(() => null))?.routing)?.source === "telegram" ? async (ctx) => {
259
296
  await this.deliverTelegramClarify(ctx);
260
297
  } : void 0;
261
- if (!runId && !deliver) return Promise.reject(/* @__PURE__ */ new Error("Clarify is not available for this session (use webchat, Telegram, or CLI)"));
262
- return this.clarifyBridge.startRequest({
263
- timeoutMs: this.externalClarificationTimeouts.get(sessionKey),
298
+ const persistedSession = getClarificationSnapshot(sessionKey);
299
+ if (!persistedSession && this.externalStreamBySession.has(sessionKey)) return this.ephemeralClarifications.start({
264
300
  beforeResponse: this.externalClarificationResponses.get(sessionKey),
265
- sessionKey,
266
301
  runId,
267
- publishStream,
268
- request,
269
- deliver
302
+ publish: publishStream,
303
+ request
304
+ }).then((answer) => ({
305
+ status: "answered",
306
+ answer
307
+ }));
308
+ if (!persistedSession) return Promise.reject(/* @__PURE__ */ new Error("Clarify requires a persisted session."));
309
+ const wait = createClarificationWait({
310
+ sessionKey,
311
+ runId: opts.runId,
312
+ toolCallId: opts.toolCallId,
313
+ kind: request.kind === "approval" ? "approval" : "input",
314
+ question: request.question,
315
+ choices: request.choices,
316
+ suggestedAnswer: request.suggestedAnswer,
317
+ approvalKey: request.approvalKey
318
+ });
319
+ const event = {
320
+ type: "clarify_request",
321
+ requestId: wait.id,
322
+ kind: wait.kind,
323
+ question: wait.question,
324
+ choices: wait.choices,
325
+ suggestedAnswer: wait.suggestedAnswer,
326
+ expiresAt: wait.expiresAt,
327
+ createdAt: wait.createdAt,
328
+ version: wait.version
329
+ };
330
+ if (this.activeWebchatRunBySession.has(sessionKey)) publishStream?.(event);
331
+ if (deliver) await deliver({
332
+ sessionKey,
333
+ requestId: wait.id,
334
+ request
270
335
  });
336
+ return {
337
+ status: "waiting",
338
+ waitId: wait.id,
339
+ expiresAt: wait.expiresAt
340
+ };
271
341
  }
272
342
  async deliverTelegramClarify(ctx) {
273
343
  const routing = (await this.opts.sessionIndex.getSessionMetadata(ctx.sessionKey).catch(() => null))?.routing;
274
344
  if (!routing || routing.source !== "telegram") return;
275
345
  let body = ctx.request.question;
276
- if (ctx.request.default) body += `\n\nDefault if unsure: ${ctx.request.default}`;
346
+ if (ctx.request.suggestedAnswer) body += `\n\nSuggested answer (never selected automatically): ${ctx.request.suggestedAnswer}`;
347
+ body += ctx.request.kind === "approval" ? "\n\nFor safety, this approval expires after 10 minutes." : "\n\nThis question remains available until you answer or cancel the task.";
277
348
  const choices = ctx.request.choices;
278
349
  const buttonRows = choices && choices.length >= 2 ? choices.map((c, i) => [{
279
350
  text: c.length > 64 ? `${c.slice(0, 61)}…` : c,
280
351
  callback_data: `clarify:${ctx.requestId}:${i}`
281
352
  }]) : void 0;
282
- if (!buttonRows) body += "\n\nReply with your answer in this chat.";
353
+ body += buttonRows ? "\n\nReply with your answer, or tap an option below." : "\n\nReply with your answer.";
283
354
  await this.opts.getChannelManager().send({
284
355
  channel: "telegram",
285
356
  chat_id: routing.peerId,
@@ -1,6 +1,7 @@
1
1
  import { inboundCorrelationMetadataFromAsyncLogContext, updateAsyncLogContext } from "../../utils/logger/context.js";
2
2
  import { createLogger } from "../../utils/logger/index.js";
3
3
  import { init_logger } from "../../utils/logger.js";
4
+ import { isClarificationSuspended } from "../../storage/sqlite/clarification-wait-repository.js";
4
5
  import { updateInteractionStateFromMessage } from "../../storage/sqlite/interaction-state-repository.js";
5
6
  import { getConnectionResumeInput, isConnectionSuspended } from "../../storage/sqlite/connection-wait-repository.js";
6
7
  import "../../storage/sqlite/index.js";
@@ -144,9 +145,11 @@ async function* runGatewayAgent(deps, message, channel, chatId, origin, attachme
144
145
  for await (const event of eventStream) yield* mapper.map(event);
145
146
  })();
146
147
  for await (const event of coalesceThinkingDeltas(mappedEvents)) yield* emitAndYield([event]);
147
- const suspended = isConnectionSuspended(sessionKey, runId);
148
+ const connectionSuspended = isConnectionSuspended(sessionKey, runId);
149
+ const clarificationSuspended = isClarificationSuspended(sessionKey, runId);
150
+ const suspended = connectionSuspended || clarificationSuspended;
148
151
  const endStatus = mergedSignal.aborted ? "cancelled" : suspended ? "suspended" : "success";
149
- const endSummary = mergedSignal.aborted ? "Interrupted" : suspended ? "Waiting for connection" : "Message processed successfully";
152
+ const endSummary = mergedSignal.aborted ? "Interrupted" : connectionSuspended ? "Waiting for connection" : clarificationSuspended ? "Waiting for user input" : "Message processed successfully";
150
153
  taskRunStatus = mergedSignal.aborted ? "cancelled" : "succeeded";
151
154
  terminalStatus = endStatus;
152
155
  taskRunSummary = endSummary;
@@ -1,6 +1,7 @@
1
1
  import { createLogger } from "../../utils/logger/index.js";
2
2
  import { init_logger } from "../../utils/logger.js";
3
3
  import { SessionInstanceChangedError, cancelQueuedSessionInput, claimNextSessionInput, findSessionInput, finishSessionInputRun, getSessionInputById, getSessionInputState, insertSessionInput, mutateQueuedSessionInput, recoverSessionInputState, setSessionInputStatus } from "../../storage/sqlite/session-input-repository.js";
4
+ import { consumeClarificationResume, supersedeActiveClarification } from "../../storage/sqlite/clarification-wait-repository.js";
4
5
  import { replaceLatestSessionTurnAndQueueInput, validateLatestSessionTurnTarget } from "../../storage/sqlite/session-turn-replace-repository.js";
5
6
  import { cancelConnectionObjective, consumeConnectionResume, invalidateConnectionResumeIntent, publishConnectionWait } from "../../storage/sqlite/connection-wait-repository.js";
6
7
  import "../../storage/sqlite/index.js";
@@ -113,6 +114,7 @@ var SessionInputCoordinator = class {
113
114
  if (result.ok === false) return result;
114
115
  if (!result.idempotent) {
115
116
  cancelConnectionObjective(sessionKey);
117
+ supersedeActiveClarification(sessionKey);
116
118
  const replacement = {
117
119
  role: "user",
118
120
  content,
@@ -187,6 +189,7 @@ var SessionInputCoordinator = class {
187
189
  targetRunId: canSteer ? runtime.activeRunId : void 0
188
190
  });
189
191
  invalidateConnectionResumeIntent(sessionKey);
192
+ supersedeActiveClarification(sessionKey);
190
193
  if (canSteer) {
191
194
  if (!await this.deps.steer(sessionKey, content)) {
192
195
  setSessionInputStatus(row.id, "queued", {
@@ -221,7 +224,7 @@ var SessionInputCoordinator = class {
221
224
  const runId = crypto.randomUUID();
222
225
  const input = claimNextSessionInput(sessionKey, runId);
223
226
  if (!input) return;
224
- if (this.deps.beforeExecute && !await this.deps.beforeExecute(input) || !consumeConnectionResume(input)) {
227
+ if (this.deps.beforeExecute && !await this.deps.beforeExecute(input) || !consumeConnectionResume(input) || !consumeClarificationResume(input)) {
225
228
  finishSessionInputRun(sessionKey, runId, "cancelled");
226
229
  this.publish(sessionKey);
227
230
  continue;
@@ -4,8 +4,8 @@ import { retireSessionMcpRuntimeForSessionKey } from "../../agent/mcp/bundle-mcp
4
4
  import "../../agent/mcp/bundle-mcp-tools.js";
5
5
  import { buildSessionTimeline } from "../../session/transcript-outline.js";
6
6
  import { transcriptRowsToClientHistory } from "../../session/client-history.js";
7
- import { withModelConfigLock } from "../../session/model-config-lock.js";
8
7
  import { collectMediaUrisFromValues, deleteMediaUris } from "../../media/session-references.js";
8
+ import { withModelConfigLock } from "../../session/model-config-lock.js";
9
9
  import { getDistinctSessionChatIds } from "./session-chat-ids.js";
10
10
  import { performSessionReset } from "../session-reset-service.js";
11
11
  import { randomUUID } from "node:crypto";