@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
@@ -3,6 +3,7 @@ import { init_logger } from "../../utils/logger.js";
3
3
  import { parseTurnOutcome } from "../../../packages/gateway-contract/src/turn-outcome.js";
4
4
  import { resolveEffectiveAgentProfileForSession } from "../../config/agent-profile.js";
5
5
  import { init_providers, resolveModel } from "../../providers/index.js";
6
+ import { runWithEmbeddedExecutionSession } from "./execution-context.js";
6
7
  import { resolveAgentTurnTimeoutMs } from "../orchestration/run-agent-turn-with-timeout.js";
7
8
  import { resolvePromptCachePolicy } from "../../providers/prompt-cache-plan.js";
8
9
  import { evaluateContextBudget } from "../memory/context-budget.js";
@@ -27,7 +28,7 @@ async function runEmbeddedTurnForSession(params) {
27
28
  parentSignal: params.abortSignal
28
29
  });
29
30
  const finish = async (result) => {
30
- if (result.stopReason === "connection_required") return result;
31
+ if (result.stopReason === "connection_required" || result.stopReason === "clarification_required") return result;
31
32
  try {
32
33
  const rows = await sessionStore.loadTranscriptRows(sessionKey);
33
34
  const hasTurn = rows.some((source) => {
@@ -180,7 +181,7 @@ async function runEmbeddedTurnForSession(params) {
180
181
  total: resolvedCandidates.length
181
182
  }, "Agent model fallback started");
182
183
  try {
183
- const turnResult = await runXopcEmbeddedTurn({
184
+ const turnResult = await runWithEmbeddedExecutionSession(sessionKey, () => runXopcEmbeddedTurn({
184
185
  sessionKey,
185
186
  runId,
186
187
  userMessage: userMessageForTurn,
@@ -199,7 +200,7 @@ async function runEmbeddedTurnForSession(params) {
199
200
  onEvent: params.onEvent,
200
201
  onAgentEvent: (event) => agentManager.emitRuntimeEvent(sessionKey, event),
201
202
  resumeLastUserMessage
202
- });
203
+ }), runId);
203
204
  if (turnResult.ok) {
204
205
  if (isFallbackAttempt) log.info({
205
206
  sessionKey,
@@ -1,5 +1,6 @@
1
1
  import { createLogger } from "../../utils/logger/index.js";
2
2
  import { init_logger } from "../../utils/logger.js";
3
+ import { getClarificationResumeInput, isClarificationSuspended } from "../../storage/sqlite/clarification-wait-repository.js";
3
4
  import { getConnectionResumeInput, isConnectionSuspended } from "../../storage/sqlite/connection-wait-repository.js";
4
5
  import { EmbeddedRunConflictError, abortEmbeddedRun, acquireEmbeddedRunLease, queueEmbeddedSteer } from "./runs.js";
5
6
  import { lastAssistantPlainText, subscribeEmbeddedSessionEvents } from "./subscribe-session.js";
@@ -235,6 +236,7 @@ async function runXopcEmbeddedTurn(params) {
235
236
  });
236
237
  let policyStopped = false;
237
238
  let connectionStopped = false;
239
+ let clarificationStopped = false;
238
240
  params.turnPolicy?.reset();
239
241
  session.agent.beforeToolCall = async (context, signal) => {
240
242
  if (connectionStopped) return {
@@ -242,6 +244,11 @@ async function runXopcEmbeddedTurn(params) {
242
244
  reason: "Waiting for the user to connect an app.",
243
245
  terminate: true
244
246
  };
247
+ if (clarificationStopped) return {
248
+ block: true,
249
+ reason: "Waiting for the user to answer.",
250
+ terminate: true
251
+ };
245
252
  const decision = await params.turnPolicy?.beforeToolCall(context, signal);
246
253
  if (decision?.block) {
247
254
  policyStopped ||= decision.terminate === true;
@@ -259,6 +266,7 @@ async function runXopcEmbeddedTurn(params) {
259
266
  };
260
267
  session.agent.afterToolCall = async (context) => {
261
268
  connectionStopped ||= isConnectionSuspended(sessionKey, runId);
269
+ clarificationStopped ||= isClarificationSuspended(sessionKey, runId);
262
270
  const scoped = context.toolCall.name === "read_file" ? await instructions.forTool(context.toolCall.name, context.args) : "";
263
271
  const checked = await verification.afterTool(context);
264
272
  if (scoped) checked.result.content.unshift({
@@ -273,7 +281,7 @@ async function runXopcEmbeddedTurn(params) {
273
281
  };
274
282
  session.agent.shouldStopAfterTurn = (context) => {
275
283
  policyStopped ||= params.turnPolicy?.shouldStopAfterTurn(context) ?? false;
276
- return policyStopped || connectionStopped;
284
+ return policyStopped || connectionStopped || clarificationStopped;
277
285
  };
278
286
  unsubscribe = subscribeEmbeddedSessionEvents(session, (event) => {
279
287
  if (event.type === "message_end" && event.message.role === "assistant") recordPromptCacheTouch(sessionKey, resolvedModel, event.message.usage);
@@ -290,11 +298,17 @@ async function runXopcEmbeddedTurn(params) {
290
298
  try {
291
299
  await runAgentTurnWithTimeout(session.agent, async () => {
292
300
  const connectionResume = getConnectionResumeInput(sessionKey, runId);
301
+ const clarificationResume = getClarificationResumeInput(sessionKey, runId);
293
302
  if (connectionResume) await session.sendCustomMessage({
294
303
  customType: "connection_resume",
295
304
  content: connectionResume.content,
296
305
  display: false
297
306
  }, { triggerTurn: true });
307
+ else if (clarificationResume) await session.sendCustomMessage({
308
+ customType: "clarification_resume",
309
+ content: clarificationResume.content,
310
+ display: false
311
+ }, { triggerTurn: true });
298
312
  else if (params.resumeLastUserMessage) await session.agent.continue();
299
313
  else {
300
314
  const text = userMessageToPromptText(userMessage);
@@ -302,7 +316,8 @@ async function runXopcEmbeddedTurn(params) {
302
316
  await session.prompt(text, images.length > 0 ? { images } : void 0);
303
317
  }
304
318
  await session.agent.waitForIdle();
305
- if (connectionStopped) return;
319
+ clarificationStopped ||= isClarificationSuspended(sessionKey, runId);
320
+ if (connectionStopped || clarificationStopped) return;
306
321
  await maybeRetryTurnAfterTransientLlmFailure(session.agent, {
307
322
  sessionKey,
308
323
  log,
@@ -330,6 +345,10 @@ async function runXopcEmbeddedTurn(params) {
330
345
  runId,
331
346
  sessionKey
332
347
  });
348
+ if (clarificationStopped) runner.piSm.appendCustomEntry("clarification_required", {
349
+ runId,
350
+ sessionKey
351
+ });
333
352
  runner.piSm.appendCustomEntry("coding_verification", {
334
353
  runId,
335
354
  workspace: workspaceDir,
@@ -356,7 +375,7 @@ async function runXopcEmbeddedTurn(params) {
356
375
  });
357
376
  return {
358
377
  ok: true,
359
- ...connectionStopped ? { stopReason: "connection_required" } : {},
378
+ ...connectionStopped ? { stopReason: "connection_required" } : clarificationStopped ? { stopReason: "clarification_required" } : {},
360
379
  lastAssistantText: lastAssistantPlainText(session)
361
380
  };
362
381
  } finally {
@@ -89,7 +89,7 @@ export type RunXopcEmbeddedTurnParams = {
89
89
  };
90
90
  export type RunXopcEmbeddedTurnResult = {
91
91
  ok: boolean;
92
- stopReason?: 'connection_required';
92
+ stopReason?: 'connection_required' | 'clarification_required';
93
93
  /** False for harness failures that must not trigger another model attempt. */
94
94
  retryable?: boolean;
95
95
  errorMessage?: string;
@@ -1,3 +1,4 @@
1
+ import { getClarificationResumeInput } from "../../storage/sqlite/clarification-wait-repository.js";
1
2
  import { getConnectionResumeInput } from "../../storage/sqlite/connection-wait-repository.js";
2
3
  import { resolveImageHandlingStrategy } from "../image/vision-detection.js";
3
4
  import { hydrateUserTurnForLlm } from "../inbound/attachment-pipeline.js";
@@ -87,6 +88,8 @@ async function tryRunSlashCommand(deps, ctx, content, options) {
87
88
  async function runDirectAgentTurn(deps, input) {
88
89
  const turnId = input.runId ?? crypto.randomUUID();
89
90
  const isConnectionResume = Boolean(getConnectionResumeInput(input.sessionKey, turnId));
91
+ const isClarificationResume = Boolean(getClarificationResumeInput(input.sessionKey, turnId));
92
+ const isResume = isConnectionResume || isClarificationResume;
90
93
  const userPlain = extractAgentUserPlainText(input.userMessage);
91
94
  const userContext = await deps.agentManager.prepareUserTurnContext(input.userMessage, input.sessionKey, turnId);
92
95
  const sourceContexts = input.sourceContexts ?? [];
@@ -111,11 +114,11 @@ async function runDirectAgentTurn(deps, input) {
111
114
  getConfig: () => deps.config,
112
115
  abortSignal: input.abortSignal,
113
116
  deadlineAtMs: input.deadlineAtMs,
114
- beforeTurn: isConnectionResume ? void 0 : () => deps.agentManager.beginBackgroundReviewUserTurn(input.sessionKey),
117
+ beforeTurn: isResume ? void 0 : () => deps.agentManager.beginBackgroundReviewUserTurn(input.sessionKey),
115
118
  onEvent: input.onEvent
116
119
  });
117
- if (!isConnectionResume) await deps.agentManager.afterAgentTurn(input.sessionKey, userPlain, turnId);
118
- if (!isConnectionResume && result.stopReason !== "connection_required") deps.agentManager.scheduleBackgroundReviewAfterUserTurn(input.sessionKey);
120
+ if (!isResume) await deps.agentManager.afterAgentTurn(input.sessionKey, userPlain, turnId);
121
+ if (!isResume && result.stopReason !== "connection_required" && result.stopReason !== "clarification_required") deps.agentManager.scheduleBackgroundReviewAfterUserTurn(input.sessionKey);
119
122
  return result;
120
123
  }
121
124
  //#endregion
@@ -1,3 +1,4 @@
1
+ import { getClarificationResumeInput } from "../../storage/sqlite/clarification-wait-repository.js";
1
2
  import { getConnectionResumeInput } from "../../storage/sqlite/connection-wait-repository.js";
2
3
  import { readAgentMessageContent } from "../memory/agent-message-access.js";
3
4
  import { abortEmbeddedRun } from "../embedded/runs.js";
@@ -126,6 +127,8 @@ function pushAssistantReceipt(queue, text, runId, metadata) {
126
127
  async function* runProcessDirectStreaming(deps, input) {
127
128
  const sessionKey = input.sessionKey ?? "agent:main:main";
128
129
  const isConnectionResume = Boolean(input.runId && getConnectionResumeInput(sessionKey, input.runId));
130
+ const isClarificationResume = Boolean(input.runId && getClarificationResumeInput(sessionKey, input.runId));
131
+ const isInternalResume = isConnectionResume || isClarificationResume;
129
132
  const { channel, chatId } = await deps.resolveSessionEndpoint(sessionKey);
130
133
  const context = deps.initDirectStreamingSession(sessionKey, channel, chatId, input.origin);
131
134
  const queue = new AsyncQueue({
@@ -276,7 +279,7 @@ async function* runProcessDirectStreaming(deps, input) {
276
279
  if (sourceContext && !sourceContexts.some((context) => context.kind === sourceContext.kind && context.sourceId === sourceContext.sourceId)) sourceContexts.push(sourceContext);
277
280
  }
278
281
  }
279
- if (channel === "webchat" && !isConnectionResume) {
282
+ if (channel === "webchat" && !isInternalResume) {
280
283
  pushVisible({
281
284
  type: "user_message",
282
285
  timestamp: userMessage.timestamp ?? Date.now(),
@@ -287,7 +290,7 @@ async function* runProcessDirectStreaming(deps, input) {
287
290
  if (textForAgent.trim()) deps.enqueueProvisionalSessionTitle?.(sessionKey, textForAgent);
288
291
  }
289
292
  const pendingUserMessage = sourceContexts.length > 0 ? injectSourceContextsIntoUserMessage(userMessage, sourceContexts) : userMessage;
290
- if (!isConnectionResume) setPendingTranscriptUserMessage(sessionKey, pendingUserMessage);
293
+ if (!isInternalResume) setPendingTranscriptUserMessage(sessionKey, pendingUserMessage);
291
294
  try {
292
295
  const result = await deps.agentManager.withSkillCapabilities(sessionKey, skillTurn.activatedCapabilityNames, () => runDirectAgentTurn({
293
296
  sessionStore: deps.sessionStore,
@@ -314,7 +317,7 @@ async function* runProcessDirectStreaming(deps, input) {
314
317
  content: formatStreamError(result.errorMessage)
315
318
  });
316
319
  } finally {
317
- if (!isConnectionResume) clearPendingTranscriptUserMessage(sessionKey, pendingUserMessage);
320
+ if (!isInternalResume) clearPendingTranscriptUserMessage(sessionKey, pendingUserMessage);
318
321
  }
319
322
  } catch (err) {
320
323
  if (err instanceof AsyncQueueOverflowError) {
@@ -121,12 +121,14 @@ export declare class AgentService {
121
121
  refreshUserProfileContext(): void;
122
122
  getMemoryManager(): import("./memory/manager.js").MemoryManager;
123
123
  getModelForSession(sessionKey: string): string;
124
+ getThinkingLevelForSession(sessionKey: string): ThinkingLevel;
124
125
  /** Run a full tool-capable turn against a caller-owned, non-persistent transcript. */
125
126
  runEphemeralTurn(params: {
126
127
  executionSessionKey: string;
127
128
  parentSessionKey: string;
128
129
  runId: string;
129
130
  content: string;
131
+ attachments?: InboundAttachmentInput[];
130
132
  modelRef: string;
131
133
  thinkingLevel?: ThinkingLevel;
132
134
  transcriptRuntime: EmbeddedTranscriptRuntime;
@@ -19,6 +19,7 @@ import "../user-model/index.js";
19
19
  import { resolveAgentTurnTimeoutMs } from "./orchestration/run-agent-turn-with-timeout.js";
20
20
  import { onSessionTranscriptUpdate } from "../session/transcript-events.js";
21
21
  import { persistInboundAttachments } from "../channels/attachments/inbound-persist.js";
22
+ import { buildTranscriptUserMessage, hydrateUserTurnForLlm } from "./inbound/attachment-pipeline.js";
22
23
  import { runXopcEmbeddedTurn } from "./embedded/run-turn.js";
23
24
  import { resolveResponseLanguageForSession } from "./prompt/response-language.js";
24
25
  import { normalizeSkillInstallTarget } from "./skills/install-target.js";
@@ -577,19 +578,31 @@ var AgentService = class {
577
578
  getModelForSession(sessionKey) {
578
579
  return this.modelManager.getModelForSession(sessionKey);
579
580
  }
581
+ getThinkingLevelForSession(sessionKey) {
582
+ return this.agentManager.getOrCreateAgent(sessionKey).state.thinkingLevel ?? "medium";
583
+ }
580
584
  /** Run a full tool-capable turn against a caller-owned, non-persistent transcript. */
581
585
  async runEphemeralTurn(params) {
582
586
  const agent = this.agentManager.getOrCreateAgent(params.parentSessionKey);
583
587
  const workspaceDir = this.agentManager.getResolvedWorkspaceForSession(params.parentSessionKey);
584
588
  const model = resolveModel(params.modelRef);
589
+ const prepared = await this.prepareInboundAttachments(params.executionSessionKey, params.attachments);
590
+ const userMessage = await buildTranscriptUserMessage({
591
+ text: params.content,
592
+ prepared,
593
+ sessionKey: params.parentSessionKey,
594
+ modelRef: params.modelRef,
595
+ config: this.effectiveAppConfig(),
596
+ agentManager: this.agentManager
597
+ });
598
+ const llmTurn = await hydrateUserTurnForLlm({
599
+ message: userMessage,
600
+ modelRef: params.modelRef
601
+ });
585
602
  return runWithEmbeddedExecutionSession(params.executionSessionKey, () => runXopcEmbeddedTurn({
586
603
  sessionKey: params.executionSessionKey,
587
604
  runId: params.runId,
588
- userMessage: {
589
- role: "user",
590
- content: params.content,
591
- timestamp: Date.now()
592
- },
605
+ userMessage,
593
606
  model,
594
607
  modelRef: params.modelRef,
595
608
  tools: agent.state.tools,
@@ -600,8 +613,9 @@ var AgentService = class {
600
613
  timeoutMs: resolveAgentTurnTimeoutMs(this.effectiveAppConfig(), params.parentSessionKey),
601
614
  turnPolicy: this.agentManager.createAgentTurnPolicy(params.parentSessionKey),
602
615
  abortSignal: params.abortSignal,
603
- onEvent: params.onEvent
604
- }));
616
+ onEvent: params.onEvent,
617
+ images: llmTurn.images
618
+ }), params.runId);
605
619
  }
606
620
  async switchModelForSession(sessionKey, modelId) {
607
621
  if (!await this.modelManager.switchModelForSession(sessionKey, modelId)) return false;
@@ -3,11 +3,25 @@ export type ClarifyRequestPayload = {
3
3
  kind?: 'approval';
4
4
  question: string;
5
5
  choices?: string[];
6
- default?: string;
6
+ suggestedAnswer?: string;
7
+ /** Stable hash of the exact operation guarded by an approval. */
8
+ approvalKey?: string;
7
9
  };
8
- export type GatewayClarifyRequestFn = (sessionKey: string, request: ClarifyRequestPayload) => Promise<string>;
10
+ export type ClarifyRequestResult = {
11
+ status: 'answered';
12
+ answer: string;
13
+ } | {
14
+ status: 'waiting';
15
+ waitId: string;
16
+ expiresAt?: number;
17
+ };
18
+ export type GatewayClarifyRequestFn = (context: {
19
+ sessionKey: string;
20
+ runId: string;
21
+ toolCallId: string;
22
+ }, request: ClarifyRequestPayload) => Promise<ClarifyRequestResult>;
9
23
  export interface ClarifyToolDeps {
10
24
  /** Resolve a per-turn callback; returns null when clarification is unavailable. */
11
- resolveAskUser: () => ((request: ClarifyRequestPayload) => Promise<string>) | null;
25
+ resolveAskUser: (toolCallId: string) => ((request: ClarifyRequestPayload) => Promise<ClarifyRequestResult>) | null;
12
26
  }
13
27
  export declare function createClarifyTool(deps: ClarifyToolDeps): AgentTool;
@@ -7,7 +7,7 @@ const ClarifySchema = Type.Object({
7
7
  minItems: 2,
8
8
  maxItems: 10
9
9
  })),
10
- default: Type.Optional(Type.String({ description: "Default answer if the user does not respond within timeout." }))
10
+ suggestedAnswer: Type.Optional(Type.String({ description: "Optional recommendation shown to the user. It is never selected automatically." }))
11
11
  });
12
12
  function waitForAbort(signal) {
13
13
  return new Promise((_, reject) => {
@@ -23,40 +23,42 @@ function createClarifyTool(deps) {
23
23
  return {
24
24
  name: "clarify",
25
25
  label: "❓ Clarify",
26
- description: "Ask the user a clarifying question and wait for their response.\n\nUse this when you need more information to proceed correctly, rather than guessing.\n\nWHEN TO USE:\n- Ambiguous instructions with multiple valid interpretations\n- Missing critical information (which file, which approach, etc.)\n- Confirming destructive or irreversible actions\n\nWHEN NOT TO USE:\n- When you can reasonably infer the answer from context\n- For trivial decisions that do not affect the task\n- When the user explicitly said \"just do it\" or \"your choice\"\n\nTIPS:\n- Provide choices when there are clear options (faster for the user)\n- Keep questions short and specific\n- Include a default when one option is clearly more likely",
26
+ description: "Ask the user a clarifying question and pause the objective until they respond.\n\nUse this when you need more information to proceed correctly, rather than guessing.\n\nWHEN TO USE:\n- Ambiguous instructions with multiple valid interpretations\n- Missing critical information (which file, which approach, etc.)\n- Confirming destructive or irreversible actions\n\nWHEN NOT TO USE:\n- When you can reasonably infer the answer from context\n- For trivial decisions that do not affect the task\n- When the user explicitly said \"just do it\" or \"your choice\"\n\nTIPS:\n- Provide choices when there are clear options (faster for the user)\n- Keep questions short and specific\n- Include a suggested answer when one option is clearly more likely",
27
27
  parameters: ClarifySchema,
28
- async execute(_toolCallId, params, signal) {
28
+ async execute(toolCallId, params, signal) {
29
29
  const p = params;
30
- const askUser = deps.resolveAskUser();
31
- if (!askUser) {
32
- if (p.default !== void 0 && p.default !== "") return {
33
- content: [{
34
- type: "text",
35
- text: `Interactive clarification is not available in this environment. Using default: ${p.default}`
36
- }],
37
- details: { answer: p.default }
38
- };
39
- return {
40
- content: [{
41
- type: "text",
42
- text: "Clarification is not available in this environment (no active webchat run). Provide a default or proceed with the best assumption."
43
- }],
44
- details: { answer: "" }
45
- };
46
- }
30
+ const askUser = deps.resolveAskUser(toolCallId);
31
+ if (!askUser) return {
32
+ content: [{
33
+ type: "text",
34
+ text: "Clarification is not available in this environment. Proceed with the best safe assumption or explain what is missing."
35
+ }],
36
+ details: { answer: "" }
37
+ };
47
38
  const payload = {
48
39
  question: p.question,
49
40
  choices: p.choices,
50
- default: p.default
41
+ suggestedAnswer: p.suggestedAnswer
51
42
  };
52
43
  try {
53
- const answer = await Promise.race([askUser(payload), waitForAbort(signal)]);
44
+ const result = await Promise.race([askUser(payload), waitForAbort(signal)]);
45
+ if (result.status === "waiting") return {
46
+ content: [{
47
+ type: "text",
48
+ text: "Waiting for the user to answer this clarification. End the current run now."
49
+ }],
50
+ details: {
51
+ answer: "",
52
+ waitId: result.waitId,
53
+ status: result.status
54
+ }
55
+ };
54
56
  return {
55
57
  content: [{
56
58
  type: "text",
57
- text: `User answered: ${answer}`
59
+ text: `User answered: ${result.answer}`
58
60
  }],
59
- details: { answer }
61
+ details: { answer: result.answer }
60
62
  };
61
63
  } catch (error) {
62
64
  const message = error instanceof Error ? error.message : String(error);
@@ -65,14 +67,7 @@ function createClarifyTool(deps) {
65
67
  type: "text",
66
68
  text: "Clarification cancelled (run aborted)."
67
69
  }],
68
- details: { answer: p.default ?? "" }
69
- };
70
- if (p.default !== void 0 && p.default !== "" && message.toLowerCase().includes("timeout")) return {
71
- content: [{
72
- type: "text",
73
- text: `User did not respond in time. Using default: ${p.default}`
74
- }],
75
- details: { answer: p.default }
70
+ details: { answer: "" }
76
71
  };
77
72
  return {
78
73
  content: [{
@@ -5,7 +5,7 @@ import * as readline from "node:readline/promises";
5
5
  * stdin/stdout prompts for `clarify` when running the CLI agent (no gateway).
6
6
  */
7
7
  function createCliReadlineClarifyRequestFn() {
8
- return async (_sessionKey, req) => {
8
+ return async (_context, req) => {
9
9
  const rl = readline.createInterface({
10
10
  input: stdin,
11
11
  output: stdout
@@ -15,19 +15,34 @@ function createCliReadlineClarifyRequestFn() {
15
15
  if (choices && choices.length >= 2) {
16
16
  stdout.write(`\n${req.question}\n`);
17
17
  choices.forEach((c, i) => stdout.write(` ${i + 1}) ${c}\n`));
18
- if (req.default) stdout.write(` (default: ${req.default})\n`);
18
+ if (req.suggestedAnswer) stdout.write(` (recommended: ${req.suggestedAnswer})\n`);
19
19
  const raw = (await rl.question("Choice (number or exact text): ")).trim();
20
20
  const n = parseInt(raw, 10);
21
- if (!Number.isNaN(n) && n >= 1 && n <= choices.length) return choices[n - 1];
22
- if (raw) return raw;
23
- if (req.default) return req.default;
21
+ if (!Number.isNaN(n) && n >= 1 && n <= choices.length) return {
22
+ status: "answered",
23
+ answer: choices[n - 1]
24
+ };
25
+ if (raw) return {
26
+ status: "answered",
27
+ answer: raw
28
+ };
29
+ if (req.suggestedAnswer) return {
30
+ status: "answered",
31
+ answer: req.suggestedAnswer
32
+ };
24
33
  throw new Error("No answer provided");
25
34
  }
26
35
  stdout.write(`\n${req.question}\n`);
27
- if (req.default) stdout.write(`(Press Enter for default: ${req.default})\n`);
36
+ if (req.suggestedAnswer) stdout.write(`(Press Enter for recommendation: ${req.suggestedAnswer})\n`);
28
37
  const raw = (await rl.question("> ")).trim();
29
- if (raw) return raw;
30
- if (req.default) return req.default;
38
+ if (raw) return {
39
+ status: "answered",
40
+ answer: raw
41
+ };
42
+ if (req.suggestedAnswer) return {
43
+ status: "answered",
44
+ answer: req.suggestedAnswer
45
+ };
31
46
  throw new Error("No answer provided");
32
47
  } finally {
33
48
  rl.close();
@@ -7,7 +7,7 @@ import { getSessionTaskPlan, setSessionTaskPlan } from "../../storage/sqlite/ses
7
7
  import "../../storage/sqlite/index.js";
8
8
  import { init_agent_scope, resolveDefaultAgentId } from "../agent-scope.js";
9
9
  import { resolveEffectiveAgentConfigForSession } from "../../config/agent-profile.js";
10
- import { getEmbeddedExecutionSession } from "../embedded/execution-context.js";
10
+ import { getEmbeddedExecutionRunId, getEmbeddedExecutionSession } from "../embedded/execution-context.js";
11
11
  import { wrapToolsWithProtection } from "./executor.js";
12
12
  import { EXTERNAL_TOOL_NAMES } from "../external-tools/gateway-tools.js";
13
13
  import { buildRuntimeEnvironment } from "../../runtime-tools/environment.js";
@@ -215,15 +215,25 @@ var AgentToolsFactory = class AgentToolsFactory {
215
215
  createSessionStatusTool(),
216
216
  createMemoryMaintenanceTool({ getConfig: () => this.deps.getConfig?.() }),
217
217
  createToolManualTool(),
218
- createClarifyTool({ resolveAskUser: () => {
218
+ createClarifyTool({ resolveAskUser: (toolCallId) => {
219
219
  const req = this.deps.gatewayClarify?.requestClarification;
220
220
  if (!req) return null;
221
221
  const executionSession = getEmbeddedExecutionSession();
222
- if (executionSession) return (r) => req(executionSession, r);
222
+ const runId = getEmbeddedExecutionRunId();
223
+ if (executionSession && runId) return (request) => req({
224
+ sessionKey: executionSession,
225
+ runId,
226
+ toolCallId
227
+ }, request);
223
228
  const ctx = this.deps.getCurrentContext();
224
229
  if (!ctx?.sessionKey) return null;
225
230
  if (!CLARIFY_SUPPORTED_CHANNELS.has(ctx.channel)) return null;
226
- return (r) => req(ctx.sessionKey, r);
231
+ if (!runId) return null;
232
+ return (request) => req({
233
+ sessionKey: ctx.sessionKey,
234
+ runId,
235
+ toolCallId
236
+ }, request);
227
237
  } }),
228
238
  createTodoTool({
229
239
  getSessionKey: () => this.deps.getCurrentContext()?.sessionKey,
@@ -117,9 +117,9 @@ export declare const AutomationActionSchema: z.ZodDiscriminatedUnion<[z.ZodObjec
117
117
  type: z.ZodLiteral<"add_wait">;
118
118
  wait: z.ZodObject<{
119
119
  kind: z.ZodEnum<{
120
+ approval: "approval";
120
121
  dependency: "dependency";
121
122
  user_input: "user_input";
122
- approval: "approval";
123
123
  external_event: "external_event";
124
124
  scheduled_time: "scheduled_time";
125
125
  retry_backoff: "retry_backoff";
@@ -294,9 +294,9 @@ export declare const AutomationSchema: z.ZodObject<{
294
294
  type: z.ZodLiteral<"add_wait">;
295
295
  wait: z.ZodObject<{
296
296
  kind: z.ZodEnum<{
297
+ approval: "approval";
297
298
  dependency: "dependency";
298
299
  user_input: "user_input";
299
- approval: "approval";
300
300
  external_event: "external_event";
301
301
  scheduled_time: "scheduled_time";
302
302
  retry_backoff: "retry_backoff";
@@ -456,9 +456,9 @@ export declare const CreateAutomationSchema: z.ZodObject<{
456
456
  type: z.ZodLiteral<"add_wait">;
457
457
  wait: z.ZodObject<{
458
458
  kind: z.ZodEnum<{
459
+ approval: "approval";
459
460
  dependency: "dependency";
460
461
  user_input: "user_input";
461
- approval: "approval";
462
462
  external_event: "external_event";
463
463
  scheduled_time: "scheduled_time";
464
464
  retry_backoff: "retry_backoff";
@@ -645,9 +645,9 @@ export declare const UpdateAutomationSchema: z.ZodObject<{
645
645
  type: z.ZodLiteral<"add_wait">;
646
646
  wait: z.ZodObject<{
647
647
  kind: z.ZodEnum<{
648
+ approval: "approval";
648
649
  dependency: "dependency";
649
650
  user_input: "user_input";
650
- approval: "approval";
651
651
  external_event: "external_event";
652
652
  scheduled_time: "scheduled_time";
653
653
  retry_backoff: "retry_backoff";
@@ -79,7 +79,7 @@ export declare function buildAutomationDraftResponse(draft: GeneratedAutomationD
79
79
  } | {
80
80
  type: "add_wait";
81
81
  wait: {
82
- kind: "dependency" | "user_input" | "approval" | "external_event" | "scheduled_time" | "retry_backoff" | "paused";
82
+ kind: "approval" | "dependency" | "user_input" | "external_event" | "scheduled_time" | "retry_backoff" | "paused";
83
83
  reason: string;
84
84
  condition: Record<string, unknown>;
85
85
  resumeAt?: number;
@@ -52,7 +52,7 @@ async function checkDatabaseSchema(ctx) {
52
52
  id: "database-schema",
53
53
  label: "Database schema",
54
54
  status: "pass",
55
- message: `SQLite schema v${status.dbVersion} (current release v153).`,
55
+ message: `SQLite schema v${status.dbVersion} (current release v154).`,
56
56
  hints: []
57
57
  };
58
58
  }
@@ -95,9 +95,13 @@ var ChatStreamMapper = class {
95
95
  })];
96
96
  case "clarify_request": return [this.make("clarify_request", {
97
97
  requestId: String(event.requestId ?? ""),
98
+ kind: event.kind === "approval" ? "approval" : "input",
98
99
  question: String(event.question ?? ""),
99
100
  choices: Array.isArray(event.choices) ? event.choices.filter((x) => typeof x === "string") : void 0,
100
- default: typeof event.default === "string" ? event.default : void 0,
101
+ suggestedAnswer: typeof event.suggestedAnswer === "string" ? event.suggestedAnswer : void 0,
102
+ version: typeof event.version === "number" ? event.version : 1,
103
+ createdAt: typeof event.createdAt === "number" ? event.createdAt : Date.now(),
104
+ expiresAt: typeof event.expiresAt === "number" ? event.expiresAt : void 0,
101
105
  petFeedback: createPetFeedback("clarify")
102
106
  })];
103
107
  case "turn_outcome": {
@@ -200,9 +200,13 @@ export type TtsAudioEvent = ChatStreamEnvelope<'tts_audio', {
200
200
  }>;
201
201
  export type ClarifyRequestEvent = ChatStreamEnvelope<'clarify_request', {
202
202
  requestId: string;
203
+ kind: 'input' | 'approval';
203
204
  question: string;
204
205
  choices?: string[];
205
- default?: string;
206
+ suggestedAnswer?: string;
207
+ version: number;
208
+ createdAt: number;
209
+ expiresAt?: number;
206
210
  petFeedback: PetFeedback;
207
211
  }>;
208
212
  export type RunEndEvent = ChatStreamEnvelope<'run_end', {
@@ -1,5 +1,7 @@
1
- import type { ClarifyBridge } from './clarify-bridge.js';
2
- export declare function registerClarifyBridge(bridge: ClarifyBridge | null): void;
3
- export declare function submitClarifyResponseFromChannel(requestId: string, answer: string): boolean;
4
- export declare function submitClarifyChoiceFromChannel(requestId: string, choiceIndex: number): boolean;
5
- export declare function tryConsumeTelegramClarifyFreeText(sessionKey: string, text: string): boolean;
1
+ export type ClarificationChannelRuntime = {
2
+ answerChoice(requestId: string, choiceIndex: number, idempotencyKey: string): boolean;
3
+ answerText(sessionKey: string, text: string, idempotencyKey: string): boolean;
4
+ };
5
+ export declare function registerClarificationChannelRuntime(next: ClarificationChannelRuntime | null): void;
6
+ export declare function answerClarificationChoiceFromChannel(requestId: string, choiceIndex: number, idempotencyKey: string): boolean;
7
+ export declare function answerClarificationTextFromChannel(sessionKey: string, text: string, idempotencyKey: string): boolean;
@@ -1,16 +1,13 @@
1
1
  //#region src/gateway/clarify-runtime.ts
2
- let bridgeRef = null;
3
- function registerClarifyBridge(bridge) {
4
- bridgeRef = bridge;
2
+ let runtime = null;
3
+ function registerClarificationChannelRuntime(next) {
4
+ runtime = next;
5
5
  }
6
- function submitClarifyResponseFromChannel(requestId, answer) {
7
- return bridgeRef?.handleResponse(requestId, answer) ?? false;
6
+ function answerClarificationChoiceFromChannel(requestId, choiceIndex, idempotencyKey) {
7
+ return runtime?.answerChoice(requestId, choiceIndex, idempotencyKey) ?? false;
8
8
  }
9
- function submitClarifyChoiceFromChannel(requestId, choiceIndex) {
10
- return bridgeRef?.handleChoiceCallback(requestId, choiceIndex) ?? false;
11
- }
12
- function tryConsumeTelegramClarifyFreeText(sessionKey, text) {
13
- return bridgeRef?.tryConsumeFreeTextReply(sessionKey, text) ?? false;
9
+ function answerClarificationTextFromChannel(sessionKey, text, idempotencyKey) {
10
+ return runtime?.answerText(sessionKey, text, idempotencyKey) ?? false;
14
11
  }
15
12
  //#endregion
16
- export { registerClarifyBridge, submitClarifyChoiceFromChannel, submitClarifyResponseFromChannel, tryConsumeTelegramClarifyFreeText };
13
+ export { answerClarificationChoiceFromChannel, answerClarificationTextFromChannel, registerClarificationChannelRuntime };
@@ -0,0 +1,17 @@
1
+ import type { ClarifyRequestPayload } from '../agent/tools/clarify-tool.js';
2
+ export type ClarificationStreamEvent = {
3
+ type: string;
4
+ } & Record<string, unknown>;
5
+ /** In-memory waiter exclusively for caller-owned ephemeral runs such as side chat. */
6
+ export declare class EphemeralClarificationWaiter {
7
+ private readonly pending;
8
+ start(input: {
9
+ runId: string;
10
+ publish: (event: ClarificationStreamEvent) => void;
11
+ request: ClarifyRequestPayload;
12
+ beforeResponse?: () => boolean;
13
+ }): Promise<string>;
14
+ answer(requestId: string, answer: string): boolean;
15
+ cancelForRun(runId: string): void;
16
+ dispose(): void;
17
+ }