mixdog 1.4.0 → 1.4.1

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 (236) hide show
  1. package/package.json +1 -1
  2. package/scripts/check-voice-runtime-inputs.mjs +25 -0
  3. package/scripts/gate-local.mjs +12 -3
  4. package/scripts/latest-guard.mjs +21 -9
  5. package/scripts/lib/isolated-test-env.mjs +10 -5
  6. package/scripts/lib/run-node-tests.mjs +37 -37
  7. package/scripts/lib/scoped-temp.d.mts +16 -0
  8. package/scripts/lib/scoped-temp.mjs +123 -0
  9. package/scripts/lib/stale-temp.mjs +44 -0
  10. package/scripts/post-release-check.mjs +1 -6
  11. package/scripts/pptx-kit-index.mjs +17 -4
  12. package/scripts/suite-health.mjs +10 -1
  13. package/scripts/test-data-dir.mjs +10 -7
  14. package/src/app.mjs +4 -0
  15. package/src/defaults/shipped-history.json +5 -0
  16. package/src/defaults/skills/browser-use/SKILL.md +6 -4
  17. package/src/defaults/skills/computer-use/SKILL.md +4 -2
  18. package/src/lib/rules-builder.cjs +29 -12
  19. package/src/output-styles/common.md +2 -2
  20. package/src/output-styles/simple.md +1 -1
  21. package/src/rules/agent/AGENT.md +5 -4
  22. package/src/rules/lead/LEAD.md +10 -5
  23. package/src/rules/shared/10-tool-workflow.md +3 -1
  24. package/src/rules/shared/45-destructive.md +0 -2
  25. package/src/rules/shared/50-execution.md +2 -0
  26. package/src/rules/shared/70-delivery.md +7 -9
  27. package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +29 -6
  28. package/src/runtime/agent/orchestrator/cache-break-trace.mjs +2 -0
  29. package/src/runtime/agent/orchestrator/context/deferred-tools.mjs +1 -1
  30. package/src/runtime/agent/orchestrator/context/skill-catalog.mjs +3 -1
  31. package/src/runtime/agent/orchestrator/mcp/oauth-login.mjs +5 -2
  32. package/src/runtime/agent/orchestrator/providers/account-pool.mjs +11 -0
  33. package/src/runtime/agent/orchestrator/providers/anthropic-api-recovery.mjs +1 -1
  34. package/src/runtime/agent/orchestrator/providers/anthropic-api-request.mjs +7 -3
  35. package/src/runtime/agent/orchestrator/providers/anthropic-api-transport.mjs +8 -2
  36. package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens-recovery.mjs +6 -2
  37. package/src/runtime/agent/orchestrator/providers/anthropic-messages.mjs +2 -2
  38. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-request/initial-status.mjs +3 -1
  39. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +14 -3
  40. package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +6 -0
  41. package/src/runtime/agent/orchestrator/providers/anthropic-turn-result.mjs +2 -2
  42. package/src/runtime/agent/orchestrator/providers/command-code-oauth-login.mjs +1 -10
  43. package/src/runtime/agent/orchestrator/providers/cursor.mjs +3 -0
  44. package/src/runtime/agent/orchestrator/providers/devin-wire-transport.mjs +1 -1
  45. package/src/runtime/agent/orchestrator/providers/devin.mjs +3 -0
  46. package/src/runtime/agent/orchestrator/providers/grok-oauth-login.mjs +210 -20
  47. package/src/runtime/agent/orchestrator/providers/lib/anthropic-prefix-trace.mjs +76 -11
  48. package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +45 -6
  49. package/src/runtime/agent/orchestrator/providers/lib/first-output-window.mjs +58 -0
  50. package/src/runtime/agent/orchestrator/providers/lib/listen-on.mjs +14 -0
  51. package/src/runtime/agent/orchestrator/providers/mimo-oauth-login.mjs +1 -10
  52. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-send.mjs +1 -0
  53. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/chunk-events.mjs +9 -2
  54. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-state.mjs +7 -0
  55. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream.mjs +14 -3
  56. package/src/runtime/agent/orchestrator/providers/openai-compat-responses-events.mjs +4 -2
  57. package/src/runtime/agent/orchestrator/providers/openai-compat-responses.mjs +1 -0
  58. package/src/runtime/agent/orchestrator/providers/openai-compat-stream-common.mjs +5 -3
  59. package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +16 -3
  60. package/src/runtime/agent/orchestrator/providers/openai-compat-xai-send.mjs +1 -0
  61. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events/output-items.mjs +5 -2
  62. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events.mjs +13 -3
  63. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state.mjs +13 -2
  64. package/src/runtime/agent/orchestrator/providers/openai-http-sse-watchdogs.mjs +81 -42
  65. package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +8 -0
  66. package/src/runtime/agent/orchestrator/providers/openai-oauth-login.mjs +136 -5
  67. package/src/runtime/agent/orchestrator/providers/openai-ws-stream/event-switch.mjs +20 -12
  68. package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +7 -1
  69. package/src/runtime/agent/orchestrator/providers/openai-ws-watchdogs.mjs +48 -22
  70. package/src/runtime/agent/orchestrator/providers/registry.mjs +12 -0
  71. package/src/runtime/agent/orchestrator/runtime-core/ask-user-tool-defs.mjs +41 -13
  72. package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +7 -6
  73. package/src/runtime/agent/orchestrator/runtime-core/session-text.mjs +7 -3
  74. package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +3 -1
  75. package/src/runtime/agent/orchestrator/runtime-core/workflow.mjs +5 -2
  76. package/src/runtime/agent/orchestrator/session/cache/scoped-cache.mjs +1 -3
  77. package/src/runtime/agent/orchestrator/session/cache-warm.mjs +8 -2
  78. package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +4 -1
  79. package/src/runtime/agent/orchestrator/session/compact/execution-tail.mjs +1 -8
  80. package/src/runtime/agent/orchestrator/session/compact/messages.mjs +23 -0
  81. package/src/runtime/agent/orchestrator/session/compact/observation-supersession.mjs +103 -23
  82. package/src/runtime/agent/orchestrator/session/compact/runner.mjs +160 -36
  83. package/src/runtime/agent/orchestrator/session/context-utils.mjs +13 -1
  84. package/src/runtime/agent/orchestrator/session/conversation-import/claude.mjs +163 -22
  85. package/src/runtime/agent/orchestrator/session/conversation-import/codex.mjs +132 -17
  86. package/src/runtime/agent/orchestrator/session/conversation-import/convert.mjs +38 -7
  87. package/src/runtime/agent/orchestrator/session/conversation-import/imported-session.mjs +3 -0
  88. package/src/runtime/agent/orchestrator/session/conversation-import/turns.mjs +90 -22
  89. package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +33 -3
  90. package/src/runtime/agent/orchestrator/session/loop/fresh-context.mjs +7 -2
  91. package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +1 -1
  92. package/src/runtime/agent/orchestrator/session/loop/provider-tool-exclusions.mjs +13 -5
  93. package/src/runtime/agent/orchestrator/session/loop/route-fallback.mjs +14 -0
  94. package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +13 -1
  95. package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +2 -2
  96. package/src/runtime/agent/orchestrator/session/loop/tool-exposure-migration.mjs +39 -21
  97. package/src/runtime/agent/orchestrator/session/manager/ask-turn-loop-options.mjs +3 -3
  98. package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +3 -1
  99. package/src/runtime/agent/orchestrator/session/manager/owner-route-fallbacks.mjs +2 -2
  100. package/src/runtime/agent/orchestrator/session/manager/route-fallback-chain.mjs +2 -1
  101. package/src/runtime/agent/orchestrator/session/manager/rules-cache.mjs +27 -9
  102. package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +61 -6
  103. package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +19 -8
  104. package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +12 -5
  105. package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +3 -3
  106. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-context.mjs +4 -0
  107. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint.mjs +3 -0
  108. package/src/runtime/agent/orchestrator/session/manager/turn-interruption.mjs +9 -0
  109. package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +5 -1
  110. package/src/runtime/agent/orchestrator/session/prompt-cache-touch-store.mjs +30 -7
  111. package/src/runtime/agent/orchestrator/session/restart-prefix-parity.fixture.mjs +154 -0
  112. package/src/runtime/agent/orchestrator/session/store/session-delete.mjs +7 -0
  113. package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +4 -0
  114. package/src/runtime/agent/orchestrator/session/store-summary-fields.mjs +15 -0
  115. package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +37 -8
  116. package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +9 -1
  117. package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +61 -2
  118. package/src/runtime/agent/orchestrator/stall-policy.mjs +23 -0
  119. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/invocation-plan.mjs +2 -2
  120. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/spawn-env.mjs +4 -2
  121. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +6 -1
  122. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +46 -20
  123. package/src/runtime/agent/orchestrator/tools/builtin/cache-layers.mjs +7 -1
  124. package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +31 -5
  125. package/src/runtime/agent/orchestrator/tools/builtin/git-worktree-tool.mjs +22 -8
  126. package/src/runtime/agent/orchestrator/tools/builtin/lib/scoped-search-flight.mjs +9 -5
  127. package/src/runtime/agent/orchestrator/tools/builtin/read-single-stream.mjs +7 -1
  128. package/src/runtime/agent/orchestrator/tools/builtin/read-streaming.mjs +6 -3
  129. package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-dispatch.mjs +1 -1
  130. package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-publication.mjs +23 -12
  131. package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-snapshot.mjs +6 -1
  132. package/src/runtime/agent/orchestrator/tools/builtin/read-tool.mjs +1 -8
  133. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch/federation.mjs +3 -1
  134. package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +1 -1
  135. package/src/runtime/agent/orchestrator/tools/graph-manifest.json +13 -13
  136. package/src/runtime/agent/orchestrator/tools/lib/isolated-test-env.mjs +4 -3
  137. package/src/runtime/agent/orchestrator/tools/lib/shell-warm-standby.mjs +26 -4
  138. package/src/runtime/channels/lib/webhook/signature.mjs +4 -1
  139. package/src/runtime/channels/lib/whisper-port.mjs +10 -1
  140. package/src/runtime/computer-bridge/action-schema.mjs +2 -2
  141. package/src/runtime/computer-bridge/core-actions.mjs +2 -2
  142. package/src/runtime/computer-bridge/error-recovery.mjs +5 -4
  143. package/src/runtime/computer-bridge/linux-action-capabilities.mjs +24 -5
  144. package/src/runtime/computer-bridge/linux-readiness.d.mts +7 -2
  145. package/src/runtime/local-provider/server-process.mjs +2 -1
  146. package/src/runtime/media/adapters/gemini-video.mjs +4 -1
  147. package/src/runtime/media/adapters/xai-media.mjs +2 -1
  148. package/src/runtime/memory/lib/embedding-worker.mjs +6 -3
  149. package/src/runtime/memory/lib/memory-action-handlers/cycle-backfill-action.mjs +1 -7
  150. package/src/runtime/memory/lib/memory-chunk-quality.mjs +1 -6
  151. package/src/runtime/office/authoring/pdf-author-action.mjs +1 -6
  152. package/src/runtime/office/design/design-art-direction.mjs +29 -27
  153. package/src/runtime/office/pdf/pdf-writer.mjs +6 -1
  154. package/src/runtime/office/portable/portable-snapshot-docx.mjs +7 -1
  155. package/src/runtime/office/portable/portable-snapshot-shared.mjs +6 -1
  156. package/src/runtime/office/quality/assurance-rendered.mjs +9 -2
  157. package/src/runtime/office/quality/assurance-structure-docx.mjs +3 -1
  158. package/src/runtime/office/quality/design-review.mjs +1 -5
  159. package/src/runtime/office/quality/document-brief.mjs +1 -5
  160. package/src/runtime/office/quality/forced-break-pages.mjs +9 -6
  161. package/src/runtime/office/quality/quality-pipeline.mjs +1 -1
  162. package/src/runtime/shared/bot-owned-work.mjs +56 -0
  163. package/src/runtime/shared/defaults-separation.mjs +1 -4
  164. package/src/runtime/shared/fetch-safe-listen.d.mts +16 -0
  165. package/src/runtime/shared/fetch-safe-listen.mjs +50 -0
  166. package/src/runtime/shared/handoff-artifacts.d.mts +2 -1
  167. package/src/runtime/shared/handoff-artifacts.mjs +18 -8
  168. package/src/runtime/shared/idle-auto-clear-gate.mjs +29 -7
  169. package/src/runtime/shared/llm/cost.mjs +5 -1
  170. package/src/runtime/shared/loopback-listener.mjs +25 -2
  171. package/src/runtime/shared/model-route-chain.d.mts +7 -2
  172. package/src/runtime/shared/model-route-chain.mjs +22 -4
  173. package/src/runtime/shared/open-url.mjs +10 -0
  174. package/src/runtime/shared/session-activity-signal.mjs +14 -0
  175. package/src/runtime/shared/session-scratch.mjs +37 -0
  176. package/src/runtime/shared/tool-card-model/agent-surface.mjs +1 -3
  177. package/src/runtime/shared/tool-card-model/card-status.mjs +15 -2
  178. package/src/runtime/shared/tool-result-summary.mjs +1 -1
  179. package/src/runtime/shared/tool-surface.mjs +3 -0
  180. package/src/runtime/shared/tool-work-units.mjs +2 -0
  181. package/src/runtime/shared/turn-snapshot-store.mjs +13 -6
  182. package/src/runtime/shared/worktrees/contract.mjs +4 -1
  183. package/src/runtime/shared/worktrees/git.mjs +15 -0
  184. package/src/runtime/shared/worktrees/manager.mjs +87 -38
  185. package/src/runtime/shared/worktrees/test-support.mjs +6 -10
  186. package/src/runtime/visualize-bridge/client.mjs +2 -0
  187. package/src/session-runtime/agent-home-delivery.mjs +41 -0
  188. package/src/session-runtime/agent-task-sessions.mjs +152 -63
  189. package/src/session-runtime/agent-tool-routing.mjs +8 -41
  190. package/src/session-runtime/boot/tools.mjs +5 -1
  191. package/src/session-runtime/bot-status.mjs +12 -6
  192. package/src/session-runtime/bot-target.mjs +38 -0
  193. package/src/session-runtime/channel-config-api.mjs +6 -1
  194. package/src/session-runtime/conversation-import/codex-titles.mjs +88 -0
  195. package/src/session-runtime/conversation-import/discovery.mjs +271 -25
  196. package/src/session-runtime/conversation-import/service.mjs +26 -5
  197. package/src/session-runtime/handoff-attachments.mjs +82 -31
  198. package/src/session-runtime/internal-tool-executor/ask-user-tool.mjs +12 -13
  199. package/src/session-runtime/internal-tool-executor/bridge-tools.mjs +1 -0
  200. package/src/session-runtime/internal-tool-executor/reaction-tool.mjs +4 -1
  201. package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +8 -2
  202. package/src/session-runtime/lifecycle/cold-heir.mjs +11 -6
  203. package/src/session-runtime/lifecycle/session-catalog.mjs +9 -1
  204. package/src/session-runtime/model-route/set-route.mjs +11 -2
  205. package/src/session-runtime/provider-catalog-cache.mjs +82 -36
  206. package/src/session-runtime/provider-models/catalog-load.mjs +23 -6
  207. package/src/session-runtime/provider-models.mjs +4 -2
  208. package/src/session-runtime/resource-recall-api.mjs +6 -1
  209. package/src/session-runtime/runtime-feature-gates.mjs +5 -1
  210. package/src/session-runtime/services/agent-tool/execute.mjs +14 -9
  211. package/src/session-runtime/services/agent-tool/job-views/agent-list.mjs +20 -4
  212. package/src/session-runtime/services/agent-tool/model-select.mjs +48 -7
  213. package/src/session-runtime/services/agent-tool/render.mjs +71 -17
  214. package/src/session-runtime/services/agent-tool/spawn-preset.mjs +6 -2
  215. package/src/session-runtime/services/agent-tool/tool-def.mjs +9 -9
  216. package/src/session-runtime/services/provider-admin.mjs +2 -1
  217. package/src/session-runtime/tool-policy/policy-stage.mjs +1 -0
  218. package/src/session-runtime/workflow-agents-api/agent-editor.mjs +5 -1
  219. package/src/session-runtime/workflow-agents-api/agent-route.mjs +15 -4
  220. package/src/session-runtime/workflow-agents-api/onboarding.mjs +3 -3
  221. package/src/session-runtime/workspace/transitions.mjs +25 -0
  222. package/src/session-runtime/workspace/worktree-tool.mjs +26 -9
  223. package/src/standalone/daemon-session-control.mjs +18 -2
  224. package/src/standalone/session-service/agent-tree/agent-turns.mjs +16 -12
  225. package/src/standalone/session-service/session-calls/session-action.mjs +10 -3
  226. package/src/standalone/session-transport/client-registry/client-grace.mjs +12 -9
  227. package/src/tui/app/app-view/floating-panel.jsx +7 -4
  228. package/src/tui/app/model-picker/route-selection/fast-parameters.mjs +1 -3
  229. package/src/tui/app/model-picker/route-selection.mjs +5 -6
  230. package/src/tui/app/provider-setup/oauth-actions.mjs +38 -20
  231. package/src/tui/app/provider-setup/provider-items.mjs +8 -2
  232. package/src/tui/app/provider-setup-picker.mjs +9 -5
  233. package/src/tui/components/QuestionPanel.jsx +13 -4
  234. package/src/tui/dist/index.mjs +75 -28
  235. package/src/tui/session/session-flow/queue/take-entries.mjs +11 -0
  236. package/src/ui/statusline-agents.mjs +1 -3
@@ -5,8 +5,12 @@ import { randomUUID } from 'node:crypto';
5
5
  import { createAgentChildFactory } from './agent-child.mjs';
6
6
  import { partialHandoffTextFromSession } from '../../../runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs';
7
7
  import { attachmentsForIntake } from '../../../runtime/attachments/store.mjs';
8
- import { artifactLines } from '../../../runtime/shared/handoff-artifacts.mjs';
9
- import { collectTurnArtifacts, handoffContent, sessionRoots } from '../../../session-runtime/handoff-attachments.mjs';
8
+ import { producedFileLines } from '../../../runtime/shared/handoff-artifacts.mjs';
9
+ import {
10
+ collectUnreportedWrites,
11
+ handoffContent,
12
+ sessionRoots,
13
+ } from '../../../session-runtime/handoff-attachments.mjs';
10
14
 
11
15
  /** An agent turn's prompt: text, or text with forwarded attachment parts
12
16
  * (handoff-attachments.mjs), never any other shape. */
@@ -25,18 +29,19 @@ export function agentTurnTranscriptMeta(content) {
25
29
  return { sender: 'lead', ...(attachments.length ? { attachments } : {}) };
26
30
  }
27
31
 
28
- /** The artifact lines of the turn that began at `messageStart`; [] on any
29
- * failure to read it (the handoff still goes out). */
30
- async function turnArtifactLines(runtime, messageStart, descriptor) {
32
+ /** The lines for files the turn that began at `messageStart` created or
33
+ * modified and `report` does not already name; [] on any failure to read it
34
+ * (the handoff still goes out). */
35
+ async function turnArtifactLines(runtime, messageStart, descriptor, report) {
31
36
  try {
32
37
  const { messages } = await runtime.readModelMessages(messageStart);
33
38
  const session = runtime.session || descriptor;
34
- const { files, omitted } = collectTurnArtifacts(messages, {
39
+ const { files, omitted } = collectUnreportedWrites(messages, {
35
40
  cwd: descriptor.cwd,
36
41
  roots: sessionRoots(session, descriptor.cwd),
42
+ report,
37
43
  });
38
- // Text only: the caller forwards these by path (an id in text is never trusted).
39
- return artifactLines(files, omitted, { ids: false });
44
+ return producedFileLines(files, omitted);
40
45
  } catch {
41
46
  return [];
42
47
  }
@@ -131,10 +136,9 @@ export function createAgentTurns({
131
136
  }
132
137
  let result = detail?.result || { content: '' };
133
138
  // The files the turn produced reach the owner with its handoff.
134
- const files = await turnArtifactLines(entry.runtime, messageStart, descriptor);
135
- if (files.length) {
136
- result = { ...result, content: [String(result.content || ''), files.join('\n')].join('\n\n') };
137
- }
139
+ const report = String(result.content || '');
140
+ const files = await turnArtifactLines(entry.runtime, messageStart, descriptor, report);
141
+ if (files.length) result = { ...result, content: [report, files.join('\n')].join('\n\n') };
138
142
  descriptor.status = 'idle';
139
143
  descriptor.stage = 'idle';
140
144
  descriptor.lastHandoff = typeof result?.content === 'string' ? result.content : '';
@@ -4,6 +4,7 @@
4
4
  import { SESSION_CONFIGURE_ACTION_SET, SESSION_READ_ACTION_SET } from '../../session-protocol.mjs';
5
5
  import { annotateSpan, markPhase } from '../../../runtime/shared/slow-span.mjs';
6
6
  import { sanitizeForWire } from '../../session-wire-values.mjs';
7
+ import { botWorkBySession } from '../../../runtime/shared/bot-owned-work.mjs';
7
8
 
8
9
  /** Reads a session without a live runtime answers from disk (readStoredReview). */
9
10
  const STORED_REVIEW_ACTIONS = new Set([
@@ -114,20 +115,26 @@ export function createSessionActionCalls(ctx) {
114
115
  /** Rows with `awaitingApproval` on every session whose live runtime shows an
115
116
  * approval card: a list row reads "needs you" from it whether or not any
116
117
  * pane receives that session's lane. A room member's row also carries the
117
- * handed-off questions it holds (`handoffQuestions`), which its room shows. */
118
+ * handed-off questions it holds (`handoffQuestions`), which its room shows.
119
+ * A session holding work a bot sent (bot-owned-work.mjs) carries those bots
120
+ * (`handedBy`): the work there is theirs, and a turn nobody sent (the
121
+ * user's own) is nobody's but the user's. */
118
122
  function withApprovalWaits(rows) {
119
123
  if (!Array.isArray(rows)) return rows;
120
124
  const waiting = awaitingApprovalSessionKinds();
121
125
  const questions = roomHandoffQuestions();
122
- if (!waiting.size && !questions.size) return rows;
126
+ const sent = botWorkBySession();
127
+ if (!waiting.size && !questions.size && !sent.size) return rows;
123
128
  return rows.map((row) => {
124
129
  const kind = waiting.get(row?.id);
125
130
  const asked = questions.get(row?.id);
126
- if (!kind && !asked) return row;
131
+ const bots = sent.get(row?.id);
132
+ if (!kind && !asked && !bots) return row;
127
133
  return {
128
134
  ...row,
129
135
  ...(kind ? { awaitingApproval: true, ...(kind === 'question' ? { awaitingQuestion: true } : {}) } : {}),
130
136
  ...(asked ? { handoffQuestions: asked } : {}),
137
+ ...(bots ? { handedBy: bots } : {}),
131
138
  };
132
139
  });
133
140
  }
@@ -70,15 +70,18 @@ export function createClientGrace({
70
70
  // tick for as long as the daemon took to go down. A lifecycle client that
71
71
  // attaches again clears the latch, because that daemon is staying up.
72
72
  if (graceTimer || signalled) return;
73
- graceTimer = setTimeout(() => {
74
- graceTimer = null;
75
- if (isClosed() || lifecycleClientCount() > 0) return;
76
- signalled = true;
77
- log(`no clients remain (${reason}) — signalling shutdown`);
78
- try {
79
- onClientsEmpty();
80
- } catch {}
81
- }, lastLifecycleWasRemote ? remoteClientGraceMs() : clientGraceMs);
73
+ graceTimer = setTimeout(
74
+ () => {
75
+ graceTimer = null;
76
+ if (isClosed() || lifecycleClientCount() > 0) return;
77
+ signalled = true;
78
+ log(`no clients remain (${reason}) — signalling shutdown`);
79
+ try {
80
+ onClientsEmpty();
81
+ } catch {}
82
+ },
83
+ lastLifecycleWasRemote ? remoteClientGraceMs() : clientGraceMs
84
+ );
82
85
  graceTimer.unref?.();
83
86
  }
84
87
 
@@ -38,9 +38,7 @@ function renderToolApproval({ toolApproval, store, frameColumns, expandedOptionP
38
38
  onSubmit={(answers) =>
39
39
  store.resolveToolApproval?.(toolApproval.id, { approved: true, reason: 'answered by user', answers })
40
40
  }
41
- onDismiss={() =>
42
- store.resolveToolApproval?.(toolApproval.id, { approved: false, reason: 'dismissed by user' })
43
- }
41
+ onDismiss={() => store.resolveToolApproval?.(toolApproval.id, { approved: false, reason: 'dismissed by user' })}
44
42
  />
45
43
  );
46
44
  }
@@ -249,7 +247,12 @@ export function renderFloatingPanel(ctx) {
249
247
  }
250
248
  if (btwPanel) {
251
249
  return (
252
- <BtwPanel panel={btwPanel} columns={frameColumns} panelRows={floatingPanelRows} themeEpoch={ctx.state?.themeEpoch || 0} />
250
+ <BtwPanel
251
+ panel={btwPanel}
252
+ columns={frameColumns}
253
+ panelRows={floatingPanelRows}
254
+ themeEpoch={ctx.state?.themeEpoch || 0}
255
+ />
253
256
  );
254
257
  }
255
258
  return null;
@@ -89,9 +89,7 @@ export function createFastParameterSelection({ state, currentRoute, isCurrentRou
89
89
  * on/off) to its next option; false when the model has none. Fast is
90
90
  * dropped when the new parameters rule it out. */
91
91
  const cycleThinking = (model) => {
92
- const definition = (model.modelParameterOptions || []).find((parameter) =>
93
- TOGGLE_PARAMETER_IDS.has(parameter.id)
94
- );
92
+ const definition = (model.modelParameterOptions || []).find((parameter) => TOGGLE_PARAMETER_IDS.has(parameter.id));
95
93
  if (!definition?.options?.length) return false;
96
94
  const wanted = definition.id;
97
95
  const parameters = modelParametersFor(model);
@@ -27,9 +27,10 @@ export function createRouteSelection({ providerModels, state, currentRoute }) {
27
27
  const fastCapable = fast.fastAvailableFor(selected, coercedEffort);
28
28
  const contextSelection = context.contextSelectionFor(selected);
29
29
  const { serviceTier, ...otherParameters } = fast.modelParametersFor(selected);
30
- const modelParameters = serviceTier && fastCapable && fast.getSelectedFast(selected)
31
- ? { ...otherParameters, serviceTier }
32
- : otherParameters;
30
+ const modelParameters =
31
+ serviceTier && fastCapable && fast.getSelectedFast(selected)
32
+ ? { ...otherParameters, serviceTier }
33
+ : otherParameters;
33
34
  return {
34
35
  effort: coercedEffort,
35
36
  routeInput: {
@@ -38,9 +39,7 @@ export function createRouteSelection({ providerModels, state, currentRoute }) {
38
39
  ...(coercedEffort ? { effort: coercedEffort } : {}),
39
40
  ...(contextSelection ? { contextPercent: contextSelection.percent } : {}),
40
41
  ...(selected.fastCapable ? { fast: fastCapable && fast.getSelectedFast(selected) } : {}),
41
- ...((selected.modelParameterOptions || []).length
42
- ? { modelParameters }
43
- : {}),
42
+ ...((selected.modelParameterOptions || []).length ? { modelParameters } : {}),
44
43
  },
45
44
  };
46
45
  };
@@ -3,6 +3,7 @@
3
3
  // progress and result panels, the interactive (code prompt) login through
4
4
  // store.beginOAuthProviderLogin, and the legacy store.loginOAuthProvider
5
5
  // path. `flow` is the per-open picker state built by provider-setup-picker.mjs.
6
+ import { canOpenBrowser } from '../../../runtime/shared/open-url.mjs';
6
7
  import { providerDetailText } from '../app-format.mjs';
7
8
  import { forgetProviderAuth, providerStatusFooter } from './provider-items.mjs';
8
9
 
@@ -27,7 +28,7 @@ function showOAuthProgress(flow, providerItem, message, onBack) {
27
28
  value: 'waiting',
28
29
  label: 'Waiting for login',
29
30
  meta: 'Running',
30
- description: 'finish the browser/OAuth prompt',
31
+ description: 'finish the sign-in shown above',
31
32
  _action: 'waiting',
32
33
  },
33
34
  {
@@ -71,6 +72,20 @@ function showOAuthResult(flow, providerItem, ok, message = '') {
71
72
  });
72
73
  }
73
74
 
75
+ function oauthCodeHint(manualUrl, loginUrl, providerName) {
76
+ if (manualUrl) return 'If the browser callback does not finish, open the URL below manually and paste code#state.';
77
+ if (loginUrl) {
78
+ return `Open the URL below to sign in, then paste the authorization code or full redirect URL for ${providerName}.`;
79
+ }
80
+ return `Paste the authorization code or full redirect URL for ${providerName}.`;
81
+ }
82
+
83
+ function pollingLoginMessage(userCode, loginUrl, opened) {
84
+ if (userCode) return `Enter code ${userCode} at:\n${loginUrl}${opened}`;
85
+ if (loginUrl) return `Open this URL to sign in:\n${loginUrl}${opened}`;
86
+ return 'Finish signing in for this provider.';
87
+ }
88
+
74
89
  /** Login through store.beginOAuthProviderLogin: a code-completing login hands
75
90
  * the surface to the OAuth code prompt; the callback (or the prompt) finishes. */
76
91
  function beginInteractiveOAuthLogin(flow, providerItem, login) {
@@ -91,26 +106,26 @@ function beginInteractiveOAuthLogin(flow, providerItem, login) {
91
106
  message || (ok ? `${providerName} login complete.` : `${providerName} login failed.`)
92
107
  );
93
108
  };
109
+ // Headless machines never try a browser; the URL is shown in the panel either way.
110
+ const openBrowser = canOpenBrowser();
94
111
  void flow.store
95
- .beginOAuthProviderLogin(providerItem._providerId)
112
+ .beginOAuthProviderLogin(providerItem._providerId, { openBrowser })
96
113
  .then((started) => {
114
+ const loginUrl = started?.manualUrl || started?.url || '';
97
115
  if (typeof started?.completeCode === 'function') {
98
116
  // Post-await handover to the OAuth code prompt: only if this flow
99
117
  // still owns the surface (Esc during beginOAuthProviderLogin).
100
118
  if (!flow.ownsSurface()) return;
101
119
  flow.releaseSurface();
102
- const manualUrl = started?.manualUrl || '';
103
120
  flow.setProviderPrompt({
104
121
  kind: 'oauth-code',
105
122
  providerId: providerItem._providerId,
106
123
  providerName,
107
124
  label: `${providerName} OAuth code`,
108
- hint: manualUrl
109
- ? 'If the browser callback does not finish, open the URL below manually and paste code#state.'
110
- : `Paste the authorization code or full redirect URL for ${providerName}.`,
125
+ hint: oauthCodeHint(started?.manualUrl, loginUrl, providerName),
111
126
  // Shown inside the live panel only — never written to the
112
127
  // transcript, so it cannot linger in scrollback after the flow.
113
- detail: manualUrl,
128
+ detail: loginUrl,
114
129
  login: started,
115
130
  afterSave: flow.returnTo,
116
131
  successReturn: () => {
@@ -124,16 +139,16 @@ function beginInteractiveOAuthLogin(flow, providerItem, login) {
124
139
  },
125
140
  });
126
141
  flow.store.pushNotice(
127
- `browser opened for ${providerName}; paste code/redirect here if callback does not finish`,
128
- 'info'
129
- );
130
- } else if (started?.userCode) {
131
- flow.store.pushNotice(
132
- `${providerName}: enter code ${started.userCode} in the browser to finish signing in`,
142
+ openBrowser
143
+ ? `browser opened for ${providerName}; paste code/redirect here if callback does not finish`
144
+ : `open the URL shown to sign in to ${providerName}, then paste the code/redirect here`,
133
145
  'info'
134
146
  );
135
- } else {
136
- flow.store.pushNotice(`browser opened for ${providerName}; finish signing in there`, 'info');
147
+ } else if (!login.backedOut && flow.ownsSurface()) {
148
+ // Device-code and URL-only polling logins stay in the progress panel,
149
+ // with the code and URL, until the result panel replaces it or Back.
150
+ const opened = openBrowser ? ' (browser opened)' : '';
151
+ showOAuthProgress(flow, providerItem, pollingLoginMessage(started?.userCode, loginUrl, opened), login.onBack);
137
152
  }
138
153
  started.waitForCallback
139
154
  ?.then((result) => {
@@ -171,11 +186,14 @@ function runLegacyOAuthLogin(flow, providerItem, login) {
171
186
  function startOAuthLogin(flow, providerItem) {
172
187
  // Back on the progress panel returns to the actions; a login that settles
173
188
  // afterwards becomes a notice instead of a result panel.
174
- const login = { backedOut: false };
175
- showOAuthProgress(flow, providerItem, 'Opening login flow. Complete it in the browser if prompted.', () => {
176
- login.backedOut = true;
177
- openOAuthProviderActions(flow, providerItem);
178
- });
189
+ const login = {
190
+ backedOut: false,
191
+ onBack: () => {
192
+ login.backedOut = true;
193
+ openOAuthProviderActions(flow, providerItem);
194
+ },
195
+ };
196
+ showOAuthProgress(flow, providerItem, 'Starting login flow…', login.onBack);
179
197
  if (typeof flow.store.beginOAuthProviderLogin === 'function') {
180
198
  beginInteractiveOAuthLogin(flow, providerItem, login);
181
199
  return;
@@ -80,8 +80,14 @@ export function buildProviderItems(setup) {
80
80
  if (setup.managedByClient === true) {
81
81
  for (const p of setup.local || []) {
82
82
  providerItems.push({
83
- value: `local:${p.id}`, label: p.name, meta: providerStatusLabel(p), description: '',
84
- _type: 'local', _providerId: p.id, _providerName: p.name, _provider: p,
83
+ value: `local:${p.id}`,
84
+ label: p.name,
85
+ meta: providerStatusLabel(p),
86
+ description: '',
87
+ _type: 'local',
88
+ _providerId: p.id,
89
+ _providerName: p.name,
90
+ _provider: p,
85
91
  });
86
92
  }
87
93
  }
@@ -129,13 +129,17 @@ export function createProviderSetupPicker({
129
129
 
130
130
  flow.paint({
131
131
  title: options.title || 'Providers',
132
- description: setup.managedByClient === true
133
- ? 'Manage provider sign-ins and accounts in the local desktop window. Refresh to update availability.'
134
- : options.description || 'Choose a provider. Enter opens provider actions.',
132
+ description:
133
+ setup.managedByClient === true
134
+ ? 'Manage provider sign-ins and accounts in the local desktop window. Refresh to update availability.'
135
+ : options.description || 'Choose a provider. Enter opens provider actions.',
135
136
  footer: providerFooter,
136
137
  footerGapRows: 1,
137
- help: options.confirmBar ? undefined : setup.managedByClient === true
138
- ? '↑/↓ Select · Enter Refresh · Esc Back' : '↑/↓ Select · Enter Open · Esc Back',
138
+ help: options.confirmBar
139
+ ? undefined
140
+ : setup.managedByClient === true
141
+ ? '↑/↓ Select · Enter Refresh · Esc Back'
142
+ : '↑/↓ Select · Enter Open · Esc Back',
139
143
  indexMode: 'always',
140
144
  labelWidth: 18,
141
145
  metaWidth: 12,
@@ -69,9 +69,12 @@ export function QuestionPanel({ questions, columns = 80, rows, onSubmit, onDismi
69
69
  if (text) commit({ ...s, buf: appendCapped(s.buf, text) });
70
70
  };
71
71
 
72
- usePaste((text) => {
73
- if (ref.current.typing) typeInto(text);
74
- }, { isActive: isRawModeSupported });
72
+ usePaste(
73
+ (text) => {
74
+ if (ref.current.typing) typeInto(text);
75
+ },
76
+ { isActive: isRawModeSupported }
77
+ );
75
78
 
76
79
  useInput(
77
80
  (input, key) => {
@@ -232,7 +235,13 @@ export function QuestionPanel({ questions, columns = 80, rows, onSubmit, onDismi
232
235
  }
233
236
  if (plan.below > 0) out.push(moreRow('below', `↓ ${plan.below} more`));
234
237
 
235
- const help = questionHelp({ multi, typing: state.typing, last: state.qi >= last, count: n, questionCount: questions.length });
238
+ const help = questionHelp({
239
+ multi,
240
+ typing: state.typing,
241
+ last: state.qi >= last,
242
+ count: n,
243
+ questionCount: questions.length,
244
+ });
236
245
 
237
246
  return (
238
247
  <Box flexDirection="column" flexShrink={0} width="100%">
@@ -41076,7 +41076,7 @@ var summarizeAskUserResult = ({ text, translate }) => {
41076
41076
  };
41077
41077
  var TOOL_RESULT_SUMMARIZERS = new Map(
41078
41078
  [
41079
- [["ask_user"], summarizeAskUserResult],
41079
+ [["ask_user", "handoff_user"], summarizeAskUserResult],
41080
41080
  [["read", "view_image", "read_mcp_resource"], summarizeReadResult],
41081
41081
  [["apply_patch"], summarizePatchResult],
41082
41082
  [["grep"], summarizeGrepResult],
@@ -41240,6 +41240,7 @@ var TOOL_CATEGORY = /* @__PURE__ */ new Map([
41240
41240
  ["goal", "Setup"],
41241
41241
  ["request_user_input", "Setup"],
41242
41242
  ["ask_user", "Setup"],
41243
+ ["handoff_user", "Setup"],
41243
41244
  ["update_plan", "Setup"],
41244
41245
  ["skill", "Skill"],
41245
41246
  ["skill_execute", "Skill"],
@@ -41815,6 +41816,7 @@ var TOOL_UNITS = /* @__PURE__ */ new Map([
41815
41816
  ["code_graph", codeGraphUnit],
41816
41817
  ["request_user_input", () => unitDescriptor("Setup", { active: "Asking", done: "Asked", noun: "user" })],
41817
41818
  ["ask_user", () => unitDescriptor("Setup", { active: "Asking", done: "Asked", noun: "user" })],
41819
+ ["handoff_user", () => unitDescriptor("Setup", { active: "Asking", done: "Asked", noun: "user" })],
41818
41820
  ["update_plan", () => unitDescriptor("Setup", { active: "Updating", done: "Updated", noun: "plan" })],
41819
41821
  ["list_mcp_resources", () => unitDescriptor("Setup", { active: "Listing", done: "Listed", noun: "MCP resource" })],
41820
41822
  [
@@ -41912,6 +41914,7 @@ function displayToolName(name, args = {}) {
41912
41914
  case "request_user_input":
41913
41915
  return "Ask User";
41914
41916
  case "ask_user":
41917
+ case "handoff_user":
41915
41918
  return "Question";
41916
41919
  case "update_plan":
41917
41920
  return "Plan";
@@ -41994,6 +41997,8 @@ function summarizeToolArgs(name, args, { max = DEFAULT_SUMMARY_MAX } = {}) {
41994
41997
  return summarizeGithub(a, max);
41995
41998
  case "goal":
41996
41999
  return compactParts([String(a.action || ""), truncateToolText(a.objective, max)]);
42000
+ case "handoff_user":
42001
+ return "";
41997
42002
  case "ask_user":
41998
42003
  return truncateToolText(
41999
42004
  (Array.isArray(a.questions) ? a.questions : []).map((q) => String(q?.header || q?.question || "").trim()).filter(Boolean).join(", "),
@@ -44282,7 +44287,15 @@ function resolveDisplayedResult(base, { normalizedName, parsedArgs }, translate)
44282
44287
  }
44283
44288
 
44284
44289
  // src/runtime/shared/tool-card-model/card-status.mjs
44285
- var ENVELOPE_STATUS_TOOLS = /* @__PURE__ */ new Set(["agent", "bridge", "task", "web_search", "search_query", "image_query", "web_search_call"]);
44290
+ var ENVELOPE_STATUS_TOOLS = /* @__PURE__ */ new Set([
44291
+ "agent",
44292
+ "bridge",
44293
+ "task",
44294
+ "web_search",
44295
+ "search_query",
44296
+ "image_query",
44297
+ "web_search_call"
44298
+ ]);
44286
44299
  function shellFragments(base, display, isShellSurface) {
44287
44300
  if (!isShellSurface) return { shellStatus: "", shellElapsed: "" };
44288
44301
  const { pending, failedCount, exitFailedCount, isError, elapsed } = base;
@@ -54506,9 +54519,12 @@ function QuestionPanel({ questions, columns = 80, rows: rows2, onSubmit, onDismi
54506
54519
  const text = cleanTyped(input);
54507
54520
  if (text) commit({ ...s, buf: appendCapped(s.buf, text) });
54508
54521
  };
54509
- use_paste_default((text) => {
54510
- if (ref.current.typing) typeInto(text);
54511
- }, { isActive: isRawModeSupported });
54522
+ use_paste_default(
54523
+ (text) => {
54524
+ if (ref.current.typing) typeInto(text);
54525
+ },
54526
+ { isActive: isRawModeSupported }
54527
+ );
54512
54528
  use_input_default(
54513
54529
  (input, key) => {
54514
54530
  const s = ref.current;
@@ -54643,7 +54659,13 @@ function QuestionPanel({ questions, columns = 80, rows: rows2, onSubmit, onDismi
54643
54659
  else optionRow(item.index);
54644
54660
  }
54645
54661
  if (plan.below > 0) out.push(moreRow("below", `\u2193 ${plan.below} more`));
54646
- const help = questionHelp({ multi, typing: state.typing, last: state.qi >= last, count: n, questionCount: questions.length });
54662
+ const help = questionHelp({
54663
+ multi,
54664
+ typing: state.typing,
54665
+ last: state.qi >= last,
54666
+ count: n,
54667
+ questionCount: questions.length
54668
+ });
54647
54669
  return /* @__PURE__ */ jsx11(Box_default, { flexDirection: "column", flexShrink: 0, width: "100%", children: /* @__PURE__ */ jsxs9(Box_default, { flexDirection: "column", borderStyle: "round", borderColor: theme.promptBorder, paddingX: 1, width: "100%", children: [
54648
54670
  /* @__PURE__ */ jsxs9(Box_default, { flexDirection: "row", justifyContent: "space-between", children: [
54649
54671
  /* @__PURE__ */ jsx11(Box_default, { flexShrink: 1, children: /* @__PURE__ */ jsxs9(Text, { wrap: "truncate", children: [
@@ -56467,7 +56489,15 @@ function renderFloatingPanel(ctx) {
56467
56489
  );
56468
56490
  }
56469
56491
  if (btwPanel) {
56470
- return /* @__PURE__ */ jsx21(BtwPanel, { panel: btwPanel, columns: frameColumns, panelRows: floatingPanelRows, themeEpoch: ctx.state?.themeEpoch || 0 });
56492
+ return /* @__PURE__ */ jsx21(
56493
+ BtwPanel,
56494
+ {
56495
+ panel: btwPanel,
56496
+ columns: frameColumns,
56497
+ panelRows: floatingPanelRows,
56498
+ themeEpoch: ctx.state?.themeEpoch || 0
56499
+ }
56500
+ );
56471
56501
  }
56472
56502
  return null;
56473
56503
  }
@@ -60258,6 +60288,11 @@ var hiddenSpawnOpts = Object.freeze({
60258
60288
  });
60259
60289
 
60260
60290
  // src/runtime/shared/open-url.mjs
60291
+ function canOpenBrowser(env4 = process.env, platform2 = process.platform, wsl = isWSL()) {
60292
+ if (env4.SSH_TTY || env4.SSH_CONNECTION) return false;
60293
+ if (platform2 !== "linux" || wsl) return true;
60294
+ return Boolean(env4.DISPLAY || env4.WAYLAND_DISPLAY);
60295
+ }
60261
60296
  function openInBrowser(url) {
60262
60297
  const u = String(url);
60263
60298
  let candidates;
@@ -60472,7 +60507,7 @@ function showOAuthProgress(flow, providerItem, message, onBack) {
60472
60507
  value: "waiting",
60473
60508
  label: "Waiting for login",
60474
60509
  meta: "Running",
60475
- description: "finish the browser/OAuth prompt",
60510
+ description: "finish the sign-in shown above",
60476
60511
  _action: "waiting"
60477
60512
  },
60478
60513
  {
@@ -60514,6 +60549,20 @@ function showOAuthResult(flow, providerItem, ok, message = "") {
60514
60549
  onCancel: leave
60515
60550
  });
60516
60551
  }
60552
+ function oauthCodeHint(manualUrl, loginUrl, providerName) {
60553
+ if (manualUrl) return "If the browser callback does not finish, open the URL below manually and paste code#state.";
60554
+ if (loginUrl) {
60555
+ return `Open the URL below to sign in, then paste the authorization code or full redirect URL for ${providerName}.`;
60556
+ }
60557
+ return `Paste the authorization code or full redirect URL for ${providerName}.`;
60558
+ }
60559
+ function pollingLoginMessage(userCode, loginUrl, opened) {
60560
+ if (userCode) return `Enter code ${userCode} at:
60561
+ ${loginUrl}${opened}`;
60562
+ if (loginUrl) return `Open this URL to sign in:
60563
+ ${loginUrl}${opened}`;
60564
+ return "Finish signing in for this provider.";
60565
+ }
60517
60566
  function beginInteractiveOAuthLogin(flow, providerItem, login) {
60518
60567
  let handled = false;
60519
60568
  const providerName = providerItem._providerName || providerItem._providerId || "OAuth";
@@ -60532,20 +60581,21 @@ function beginInteractiveOAuthLogin(flow, providerItem, login) {
60532
60581
  message || (ok ? `${providerName} login complete.` : `${providerName} login failed.`)
60533
60582
  );
60534
60583
  };
60535
- void flow.store.beginOAuthProviderLogin(providerItem._providerId).then((started) => {
60584
+ const openBrowser = canOpenBrowser();
60585
+ void flow.store.beginOAuthProviderLogin(providerItem._providerId, { openBrowser }).then((started) => {
60586
+ const loginUrl = started?.manualUrl || started?.url || "";
60536
60587
  if (typeof started?.completeCode === "function") {
60537
60588
  if (!flow.ownsSurface()) return;
60538
60589
  flow.releaseSurface();
60539
- const manualUrl = started?.manualUrl || "";
60540
60590
  flow.setProviderPrompt({
60541
60591
  kind: "oauth-code",
60542
60592
  providerId: providerItem._providerId,
60543
60593
  providerName,
60544
60594
  label: `${providerName} OAuth code`,
60545
- hint: manualUrl ? "If the browser callback does not finish, open the URL below manually and paste code#state." : `Paste the authorization code or full redirect URL for ${providerName}.`,
60595
+ hint: oauthCodeHint(started?.manualUrl, loginUrl, providerName),
60546
60596
  // Shown inside the live panel only — never written to the
60547
60597
  // transcript, so it cannot linger in scrollback after the flow.
60548
- detail: manualUrl,
60598
+ detail: loginUrl,
60549
60599
  login: started,
60550
60600
  afterSave: flow.returnTo,
60551
60601
  successReturn: () => {
@@ -60559,16 +60609,12 @@ function beginInteractiveOAuthLogin(flow, providerItem, login) {
60559
60609
  }
60560
60610
  });
60561
60611
  flow.store.pushNotice(
60562
- `browser opened for ${providerName}; paste code/redirect here if callback does not finish`,
60612
+ openBrowser ? `browser opened for ${providerName}; paste code/redirect here if callback does not finish` : `open the URL shown to sign in to ${providerName}, then paste the code/redirect here`,
60563
60613
  "info"
60564
60614
  );
60565
- } else if (started?.userCode) {
60566
- flow.store.pushNotice(
60567
- `${providerName}: enter code ${started.userCode} in the browser to finish signing in`,
60568
- "info"
60569
- );
60570
- } else {
60571
- flow.store.pushNotice(`browser opened for ${providerName}; finish signing in there`, "info");
60615
+ } else if (!login.backedOut && flow.ownsSurface()) {
60616
+ const opened = openBrowser ? " (browser opened)" : "";
60617
+ showOAuthProgress(flow, providerItem, pollingLoginMessage(started?.userCode, loginUrl, opened), login.onBack);
60572
60618
  }
60573
60619
  started.waitForCallback?.then((result) => {
60574
60620
  if (result && !flow.oauthSubmitRef.current) finish(true, `${providerName} login complete`);
@@ -60595,11 +60641,14 @@ function runLegacyOAuthLogin(flow, providerItem, login) {
60595
60641
  });
60596
60642
  }
60597
60643
  function startOAuthLogin(flow, providerItem) {
60598
- const login = { backedOut: false };
60599
- showOAuthProgress(flow, providerItem, "Opening login flow. Complete it in the browser if prompted.", () => {
60600
- login.backedOut = true;
60601
- openOAuthProviderActions(flow, providerItem);
60602
- });
60644
+ const login = {
60645
+ backedOut: false,
60646
+ onBack: () => {
60647
+ login.backedOut = true;
60648
+ openOAuthProviderActions(flow, providerItem);
60649
+ }
60650
+ };
60651
+ showOAuthProgress(flow, providerItem, "Starting login flow\u2026", login.onBack);
60603
60652
  if (typeof flow.store.beginOAuthProviderLogin === "function") {
60604
60653
  beginInteractiveOAuthLogin(flow, providerItem, login);
60605
60654
  return;
@@ -61103,9 +61152,7 @@ function createFastParameterSelection({ state, currentRoute, isCurrentRoute, isS
61103
61152
  return true;
61104
61153
  };
61105
61154
  const cycleThinking = (model) => {
61106
- const definition = (model.modelParameterOptions || []).find(
61107
- (parameter) => TOGGLE_PARAMETER_IDS.has(parameter.id)
61108
- );
61155
+ const definition = (model.modelParameterOptions || []).find((parameter) => TOGGLE_PARAMETER_IDS.has(parameter.id));
61109
61156
  if (!definition?.options?.length) return false;
61110
61157
  const wanted = definition.id;
61111
61158
  const parameters = modelParametersFor(model);
@@ -87,6 +87,17 @@ export function createTakeEntriesOps({ pending, pendingNotificationKeys, removeQ
87
87
  // prompt the user already took back.
88
88
  const mirrored = queued.filter((entry) => entry.steeringPersistId);
89
89
  if (mirrored.length > 0) void dropTuiSteeringPersist(leadSessionId(), mirrored);
90
+ // The entries left the queue for good, so nothing later (a drain, the
91
+ // close of the session) can settle them: whoever submitted one is told it
92
+ // was cancelled, once, here. A submitter that waits on its settlement (a
93
+ // bot's send, which owns the work until then) is released.
94
+ for (const entry of queued) {
95
+ try {
96
+ entry.onSettled?.({ status: 'cancelled', reason: 'reclaimed', result: null, session: null });
97
+ } catch {
98
+ // A throwing callback must not leave the remaining entries unsettled.
99
+ }
100
+ }
90
101
  const queuedText = queued
91
102
  .map((item) => item.text)
92
103
  .filter((text) => String(text || '').trim())
@@ -13,9 +13,7 @@ import { positiveInt } from '../runtime/shared/numbers.mjs';
13
13
 
14
14
  // One table owns both halves of a maintenance agent: which agents are hidden
15
15
  // from the worker list by default, and the short label they render as.
16
- const MAINTENANCE_AGENT_LABELS = new Map([
17
- ['cycle1-agent', 'cycle1'],
18
- ]);
16
+ const MAINTENANCE_AGENT_LABELS = new Map([['cycle1-agent', 'cycle1']]);
19
17
  const DEFAULT_HIDDEN_STATUSLINE_AGENTS = Object.freeze([...MAINTENANCE_AGENT_LABELS.keys()]);
20
18
  const TERMINAL_AGENT_STATUS = /idle|done|complete|success|closed|error|fail|cancel|killed|timeout/i;
21
19
  const ACTIVE_AGENT_STATUS = /^(?:connecting|requesting|streaming|tool[-_\s]?running|running|queued|pending|starting)$/i;