muonroi-cli 1.8.5 → 1.9.0

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 (206) hide show
  1. package/dist/packages/agent-harness-core/src/event-filter.js +11 -0
  2. package/dist/packages/agent-harness-core/src/event-redact.js +7 -0
  3. package/dist/packages/agent-harness-core/src/event-tee.d.ts +20 -4
  4. package/dist/packages/agent-harness-core/src/event-tee.js +32 -5
  5. package/dist/packages/agent-harness-core/src/mcp-server.d.ts +14 -0
  6. package/dist/packages/agent-harness-core/src/mcp-server.js +86 -10
  7. package/dist/packages/agent-harness-core/src/predicate.d.ts +1 -1
  8. package/dist/packages/agent-harness-core/src/protocol.d.ts +24 -2
  9. package/dist/src/agent-harness/mock-model.d.ts +10 -0
  10. package/dist/src/agent-harness/mock-model.js +6 -2
  11. package/dist/src/chat/chat-keychain.d.ts +7 -12
  12. package/dist/src/chat/chat-keychain.js +19 -86
  13. package/dist/src/cli/keys.d.ts +8 -45
  14. package/dist/src/cli/keys.js +18 -324
  15. package/dist/src/council/clarifier.d.ts +4 -2
  16. package/dist/src/council/clarifier.js +158 -36
  17. package/dist/src/council/debate-planner.js +3 -2
  18. package/dist/src/council/debate.js +59 -5
  19. package/dist/src/council/index.d.ts +23 -0
  20. package/dist/src/council/index.js +37 -2
  21. package/dist/src/council/llm.d.ts +62 -0
  22. package/dist/src/council/llm.js +123 -23
  23. package/dist/src/council/panel-select.js +13 -3
  24. package/dist/src/council/planner.js +17 -0
  25. package/dist/src/council/preflight.d.ts +10 -0
  26. package/dist/src/council/preflight.js +36 -0
  27. package/dist/src/council/prompts.d.ts +9 -1
  28. package/dist/src/council/prompts.js +23 -6
  29. package/dist/src/council/types.d.ts +10 -0
  30. package/dist/src/ee/auth.d.ts +19 -0
  31. package/dist/src/ee/auth.js +39 -0
  32. package/dist/src/ee/client.js +28 -3
  33. package/dist/src/ee/ee-onboarding.js +6 -26
  34. package/dist/src/flow/compaction/compress.d.ts +2 -2
  35. package/dist/src/flow/compaction/compress.js +21 -8
  36. package/dist/src/flow/compaction/extract.d.ts +3 -3
  37. package/dist/src/flow/compaction/extract.js +6 -6
  38. package/dist/src/flow/compaction/index.d.ts +2 -1
  39. package/dist/src/flow/compaction/index.js +29 -3
  40. package/dist/src/flow/compaction/progress.d.ts +35 -0
  41. package/dist/src/flow/compaction/progress.js +35 -0
  42. package/dist/src/generated/version.d.ts +1 -1
  43. package/dist/src/generated/version.js +1 -1
  44. package/dist/src/gsd/flags.d.ts +11 -0
  45. package/dist/src/gsd/flags.js +19 -0
  46. package/dist/src/gsd/plan-council.js +104 -72
  47. package/dist/src/gsd/verdict-schema.d.ts +1 -1
  48. package/dist/src/headless/council-answers.js +4 -0
  49. package/dist/src/index.js +129 -260
  50. package/dist/src/lsp/builtins.js +3 -1
  51. package/dist/src/lsp/manager.d.ts +5 -1
  52. package/dist/src/lsp/manager.js +249 -3
  53. package/dist/src/lsp/npm-cache.d.ts +11 -1
  54. package/dist/src/lsp/npm-cache.js +17 -1
  55. package/dist/src/lsp/runtime.d.ts +6 -1
  56. package/dist/src/lsp/runtime.js +17 -1
  57. package/dist/src/lsp/types.d.ts +83 -1
  58. package/dist/src/lsp/types.js +10 -0
  59. package/dist/src/mcp/client-pool.js +43 -15
  60. package/dist/src/mcp/lsp-tools.d.ts +5 -1
  61. package/dist/src/mcp/lsp-tools.js +93 -2
  62. package/dist/src/mcp/mcp-keychain.d.ts +3 -5
  63. package/dist/src/mcp/mcp-keychain.js +9 -49
  64. package/dist/src/mcp/setup-guide-text.d.ts +1 -1
  65. package/dist/src/mcp/setup-guide-text.js +22 -2
  66. package/dist/src/mcp/tools-server.d.ts +10 -0
  67. package/dist/src/mcp/tools-server.js +10 -2
  68. package/dist/src/models/catalog.json +19 -19
  69. package/dist/src/orchestrator/ask-user.d.ts +61 -0
  70. package/dist/src/orchestrator/ask-user.js +65 -0
  71. package/dist/src/orchestrator/compaction.d.ts +2 -3
  72. package/dist/src/orchestrator/compaction.js +8 -8
  73. package/dist/src/orchestrator/council-manager.js +9 -8
  74. package/dist/src/orchestrator/council-request.d.ts +49 -0
  75. package/dist/src/orchestrator/council-request.js +62 -0
  76. package/dist/src/orchestrator/interactive-pause.d.ts +26 -0
  77. package/dist/src/orchestrator/interactive-pause.js +36 -0
  78. package/dist/src/orchestrator/message-processor.d.ts +4 -0
  79. package/dist/src/orchestrator/message-processor.js +26 -8
  80. package/dist/src/orchestrator/orchestrator.d.ts +25 -0
  81. package/dist/src/orchestrator/orchestrator.js +204 -50
  82. package/dist/src/orchestrator/preprocessor.js +2 -2
  83. package/dist/src/orchestrator/safety-askcard.d.ts +1 -1
  84. package/dist/src/orchestrator/safety-askcard.js +5 -2
  85. package/dist/src/orchestrator/safety-intercept.d.ts +5 -0
  86. package/dist/src/orchestrator/safety-intercept.js +7 -0
  87. package/dist/src/orchestrator/stall-watchdog.d.ts +8 -1
  88. package/dist/src/orchestrator/stall-watchdog.js +24 -3
  89. package/dist/src/orchestrator/stream-runner.d.ts +13 -3
  90. package/dist/src/orchestrator/stream-runner.js +54 -21
  91. package/dist/src/orchestrator/tool-engine.d.ts +19 -0
  92. package/dist/src/orchestrator/tool-engine.js +241 -25
  93. package/dist/src/orchestrator/turn-watchdog.d.ts +7 -0
  94. package/dist/src/orchestrator/turn-watchdog.js +38 -9
  95. package/dist/src/pil/agent-operating-contract.d.ts +1 -1
  96. package/dist/src/pil/agent-operating-contract.js +6 -4
  97. package/dist/src/pil/discovery.d.ts +1 -1
  98. package/dist/src/pil/discovery.js +2 -2
  99. package/dist/src/pil/layer1_5-complexity-size.d.ts +7 -0
  100. package/dist/src/pil/layer1_5-complexity-size.js +31 -5
  101. package/dist/src/pil/llm-classify.d.ts +78 -3
  102. package/dist/src/pil/llm-classify.js +351 -111
  103. package/dist/src/pil/native-capabilities-workbook.d.ts +1 -1
  104. package/dist/src/pil/native-capabilities-workbook.js +7 -0
  105. package/dist/src/pil/pipeline.js +2 -0
  106. package/dist/src/pil/repo-grounding-probe.d.ts +15 -0
  107. package/dist/src/pil/repo-grounding-probe.js +136 -0
  108. package/dist/src/pil/repo-structure-hints.d.ts +7 -0
  109. package/dist/src/pil/repo-structure-hints.js +45 -0
  110. package/dist/src/product-loop/artifact-io.js +4 -0
  111. package/dist/src/product-loop/criteria-seed.d.ts +51 -0
  112. package/dist/src/product-loop/criteria-seed.js +200 -0
  113. package/dist/src/product-loop/discovery-interview.d.ts +9 -0
  114. package/dist/src/product-loop/discovery-interview.js +37 -18
  115. package/dist/src/product-loop/discovery-recommender.js +2 -1
  116. package/dist/src/product-loop/discovery-schema.js +14 -1
  117. package/dist/src/product-loop/discovery-triage.d.ts +23 -0
  118. package/dist/src/product-loop/discovery-triage.js +109 -0
  119. package/dist/src/product-loop/gather.js +150 -2
  120. package/dist/src/product-loop/index.js +7 -0
  121. package/dist/src/product-loop/loop-driver.js +21 -8
  122. package/dist/src/product-loop/phase-plan.d.ts +16 -0
  123. package/dist/src/product-loop/phase-plan.js +42 -4
  124. package/dist/src/product-loop/phase-rituals.d.ts +3 -0
  125. package/dist/src/product-loop/phase-rituals.js +8 -3
  126. package/dist/src/product-loop/phase-runner.js +30 -11
  127. package/dist/src/product-loop/plan-adherence-review.d.ts +26 -0
  128. package/dist/src/product-loop/plan-adherence-review.js +144 -0
  129. package/dist/src/product-loop/sprint-runner.d.ts +62 -0
  130. package/dist/src/product-loop/sprint-runner.js +309 -8
  131. package/dist/src/product-loop/types.d.ts +25 -0
  132. package/dist/src/providers/anthropic.d.ts +9 -8
  133. package/dist/src/providers/anthropic.js +13 -47
  134. package/dist/src/providers/auth/grok-oauth.d.ts +1 -0
  135. package/dist/src/providers/auth/grok-oauth.js +30 -5
  136. package/dist/src/providers/auth/openai-oauth.d.ts +1 -0
  137. package/dist/src/providers/auth/openai-oauth.js +14 -0
  138. package/dist/src/providers/auth/token-store.d.ts +9 -9
  139. package/dist/src/providers/auth/token-store.js +8 -70
  140. package/dist/src/providers/auth/types.d.ts +8 -0
  141. package/dist/src/providers/env-store.d.ts +17 -0
  142. package/dist/src/providers/env-store.js +228 -0
  143. package/dist/src/providers/keychain.d.ts +21 -17
  144. package/dist/src/providers/keychain.js +124 -135
  145. package/dist/src/providers/runtime.d.ts +24 -9
  146. package/dist/src/providers/runtime.js +48 -37
  147. package/dist/src/providers/strategies/thinking-mode.js +9 -1
  148. package/dist/src/providers/strategies/xai.strategy.js +27 -0
  149. package/dist/src/providers/warm.d.ts +65 -0
  150. package/dist/src/providers/warm.js +145 -0
  151. package/dist/src/self-qa/agentic-loop.js +3 -2
  152. package/dist/src/storage/transcript.js +56 -2
  153. package/dist/src/tools/git-safety.d.ts +19 -0
  154. package/dist/src/tools/git-safety.js +168 -0
  155. package/dist/src/tools/native-tools.d.ts +1 -1
  156. package/dist/src/tools/native-tools.js +76 -1
  157. package/dist/src/tools/registry.d.ts +17 -0
  158. package/dist/src/tools/registry.js +116 -1
  159. package/dist/src/types/index.d.ts +29 -1
  160. package/dist/src/ui/app.js +67 -4
  161. package/dist/src/ui/components/agent-rail-activities.d.ts +26 -0
  162. package/dist/src/ui/components/agent-rail-activities.js +47 -0
  163. package/dist/src/ui/components/compact-progress-card.d.ts +24 -0
  164. package/dist/src/ui/components/compact-progress-card.js +42 -0
  165. package/dist/src/ui/components/council-phase-timeline.js +17 -2
  166. package/dist/src/ui/components/council-question-card.js +1 -0
  167. package/dist/src/ui/components/message-view.d.ts +15 -0
  168. package/dist/src/ui/components/message-view.js +50 -1
  169. package/dist/src/ui/components/tool-group.d.ts +15 -3
  170. package/dist/src/ui/components/tool-group.js +69 -11
  171. package/dist/src/ui/containers/modals-layer.d.ts +2 -1
  172. package/dist/src/ui/containers/modals-layer.js +2 -2
  173. package/dist/src/ui/council-harness-event.d.ts +57 -0
  174. package/dist/src/ui/council-harness-event.js +46 -0
  175. package/dist/src/ui/heartbeat-debug.d.ts +29 -0
  176. package/dist/src/ui/heartbeat-debug.js +45 -0
  177. package/dist/src/ui/modals/api-key-modal.js +1 -1
  178. package/dist/src/ui/modals/model-picker-modal.d.ts +8 -18
  179. package/dist/src/ui/modals/model-picker-modal.js +8 -10
  180. package/dist/src/ui/slash/ee.js +81 -0
  181. package/dist/src/ui/slash/menu-items.js +11 -2
  182. package/dist/src/ui/use-app-logic.js +354 -224
  183. package/dist/src/ui/utils/agent-activities.d.ts +39 -0
  184. package/dist/src/ui/utils/agent-activities.js +96 -0
  185. package/dist/src/ui/utils/group-tool-entries.d.ts +26 -0
  186. package/dist/src/ui/utils/group-tool-entries.js +111 -0
  187. package/dist/src/ui/utils/tool-summary.d.ts +21 -0
  188. package/dist/src/ui/utils/tool-summary.js +91 -0
  189. package/dist/src/utils/event-loop-monitor.d.ts +85 -0
  190. package/dist/src/utils/event-loop-monitor.js +107 -0
  191. package/dist/src/utils/llm-deadline.d.ts +14 -0
  192. package/dist/src/utils/llm-deadline.js +19 -0
  193. package/dist/src/utils/loop-profiler.d.ts +102 -0
  194. package/dist/src/utils/loop-profiler.js +202 -0
  195. package/dist/src/utils/settings.d.ts +27 -0
  196. package/dist/src/utils/settings.js +38 -2
  197. package/dist/src/utils/side-question.d.ts +1 -2
  198. package/dist/src/utils/side-question.js +2 -2
  199. package/dist/src/verify/entrypoint.js +51 -16
  200. package/dist/src/verify/orchestrator.d.ts +1 -1
  201. package/dist/src/verify/orchestrator.js +20 -3
  202. package/package.json +1 -2
  203. package/dist/src/cli/bw-vault.d.ts +0 -55
  204. package/dist/src/cli/bw-vault.js +0 -133
  205. package/dist/src/mcp/ee-tools.d.ts +0 -46
  206. package/dist/src/mcp/ee-tools.js +0 -194
@@ -19,7 +19,7 @@ import { getRoutedModelByTier } from "../router/peak-hour.js";
19
19
  import { appendSystemMessage } from "../storage/index.js";
20
20
  import { createBuiltinTools } from "../tools/registry.js";
21
21
  import { isProviderDisabled } from "../utils/settings.js";
22
- import { COUNCIL_COLOR_BG, COUNCIL_COLOR_RESET, COUNCIL_ROLE_COLORS, } from "./agent-options.js";
22
+ import { COUNCIL_COLOR_BG, COUNCIL_COLOR_RESET, COUNCIL_ROLE_COLORS } from "./agent-options.js";
23
23
  import { extractUserContent, getCompactionSummaryText, isCompactionSummaryMessage } from "./compaction.js";
24
24
  /**
25
25
  * CouncilManager — extracted council subsystem.
@@ -149,8 +149,8 @@ export class CouncilManager {
149
149
  async generate(modelId, system, prompt, maxTokens = 2048) {
150
150
  const providerId = detectProviderForModel(modelId);
151
151
  const key = await loadKeyForProvider(providerId);
152
- const provider = createCouncilProvider(providerId, key);
153
- const runtime = resolveModelRuntime(provider, modelId);
152
+ ensureCouncilProvider(providerId, key);
153
+ const runtime = resolveModelRuntime(modelId);
154
154
  const { text } = await generateText({
155
155
  model: runtime.model,
156
156
  system,
@@ -170,8 +170,8 @@ export class CouncilManager {
170
170
  async research(modelId, topic, conversationContext, signal) {
171
171
  const providerId = detectProviderForModel(modelId);
172
172
  const key = await loadKeyForProvider(providerId);
173
- const provider = createCouncilProvider(providerId, key);
174
- const runtime = resolveModelRuntime(provider, modelId);
173
+ ensureCouncilProvider(providerId, key);
174
+ const runtime = resolveModelRuntime(modelId);
175
175
  // Build tool set with bash, grep, read_file for codebase research
176
176
  const researchTools = createBuiltinTools(this.deps.getBash(), this.deps.getMode(), {
177
177
  // research phase intentionally has no runTask/runDelegation handlers
@@ -504,11 +504,12 @@ export class CouncilManager {
504
504
  static COLOR_RESET = COUNCIL_COLOR_RESET;
505
505
  }
506
506
  /**
507
- * Internal: build a provider factory using the shared runtime factory.
507
+ * Internal: register a provider factory using the shared runtime factory, so a
508
+ * council sub-call on a provider the session never built can still resolve.
508
509
  * Mirrors `createProvider` in orchestrator.ts to keep council sub-calls
509
510
  * decoupled from Agent state.
510
511
  */
511
- function createCouncilProvider(providerId, apiKey, baseURL) {
512
- return createProviderFactory(providerId, { apiKey, baseURL }).factory;
512
+ function ensureCouncilProvider(providerId, apiKey, baseURL) {
513
+ createProviderFactory(providerId, { apiKey, baseURL });
513
514
  }
514
515
  //# sourceMappingURL=council-manager.js.map
@@ -0,0 +1,49 @@
1
+ /**
2
+ * src/orchestrator/council-request.ts
3
+ *
4
+ * Process-global one-shot channel for an AGENT-initiated council convening.
5
+ *
6
+ * The `convene_council` tool (src/tools/registry.ts) sets a pending request when
7
+ * the model decides THIS request warrants a multi-model debate; the tool-engine
8
+ * consumes it from the OUTER restart loop after a stream drain (NOT solely via
9
+ * dynamicStopWhen — a phase-1 SAMR step ends on stepCountIs(1) and never
10
+ * evaluates the stop hook), runs `runCouncilV2({ convenePath: true })`, splices
11
+ * the synthesis into the convene tool's tool_result, and restarts streamText so
12
+ * the model reads the conclusion as the tool's result and continues the turn.
13
+ *
14
+ * Modelled on compact-request.ts. Two differences that matter for correctness:
15
+ * 1. It carries the `toolCallId` of the convene_council call so the tool-engine
16
+ * can (a) confirm the pending request actually belongs to THIS drain's
17
+ * messages before running council — guarding against a nested sub-session
18
+ * whose convene call would otherwise be consumed by the parent loop — and
19
+ * (b) locate the exact tool-result to replace with the synthesis.
20
+ * 2. `peekCouncilConveneToolCallId()` is a NON-consuming read for that guard;
21
+ * the flag is only cleared by `consumeCouncilConvene()` (on run) or by the
22
+ * turn-teardown `finally` (discard) so a request can never leak into the
23
+ * next user turn when the convene call shared a step with a terminal
24
+ * `respond_*` tool.
25
+ *
26
+ * Single-active-turn assumption (inherited from compact-request.ts): the TUI
27
+ * streams one turn at a time, so a process-global slot cannot cross-talk between
28
+ * concurrent user turns.
29
+ */
30
+ export interface CouncilConveneRequestState {
31
+ /** Model-supplied justification for convening (the specific tradeoff/decision at stake). */
32
+ reason: string | null;
33
+ /** Tool-call id of the convene_council call, so the tool-engine can match + replace its result. */
34
+ toolCallId: string | null;
35
+ }
36
+ /** Queue a council convening to run before the model's next step. */
37
+ export declare function requestCouncilConvene(reason?: string | null, toolCallId?: string | null): void;
38
+ /** True when a council convening is queued (non-consuming peek). */
39
+ export declare function hasPendingCouncilConvene(): boolean;
40
+ /**
41
+ * Non-consuming read of the pending request's toolCallId. Used by the tool-engine
42
+ * to check whether the pending convene belongs to THIS drain's response.messages
43
+ * before committing to a (possibly expensive, possibly wrong-frame) debate.
44
+ */
45
+ export declare function peekCouncilConveneToolCallId(): string | null;
46
+ /** Consume the pending request exactly once (returns null when none queued). */
47
+ export declare function consumeCouncilConvene(): CouncilConveneRequestState | null;
48
+ /** Test hook — clear any queued request. */
49
+ export declare function __resetCouncilConveneForTests(): void;
@@ -0,0 +1,62 @@
1
+ /**
2
+ * src/orchestrator/council-request.ts
3
+ *
4
+ * Process-global one-shot channel for an AGENT-initiated council convening.
5
+ *
6
+ * The `convene_council` tool (src/tools/registry.ts) sets a pending request when
7
+ * the model decides THIS request warrants a multi-model debate; the tool-engine
8
+ * consumes it from the OUTER restart loop after a stream drain (NOT solely via
9
+ * dynamicStopWhen — a phase-1 SAMR step ends on stepCountIs(1) and never
10
+ * evaluates the stop hook), runs `runCouncilV2({ convenePath: true })`, splices
11
+ * the synthesis into the convene tool's tool_result, and restarts streamText so
12
+ * the model reads the conclusion as the tool's result and continues the turn.
13
+ *
14
+ * Modelled on compact-request.ts. Two differences that matter for correctness:
15
+ * 1. It carries the `toolCallId` of the convene_council call so the tool-engine
16
+ * can (a) confirm the pending request actually belongs to THIS drain's
17
+ * messages before running council — guarding against a nested sub-session
18
+ * whose convene call would otherwise be consumed by the parent loop — and
19
+ * (b) locate the exact tool-result to replace with the synthesis.
20
+ * 2. `peekCouncilConveneToolCallId()` is a NON-consuming read for that guard;
21
+ * the flag is only cleared by `consumeCouncilConvene()` (on run) or by the
22
+ * turn-teardown `finally` (discard) so a request can never leak into the
23
+ * next user turn when the convene call shared a step with a terminal
24
+ * `respond_*` tool.
25
+ *
26
+ * Single-active-turn assumption (inherited from compact-request.ts): the TUI
27
+ * streams one turn at a time, so a process-global slot cannot cross-talk between
28
+ * concurrent user turns.
29
+ */
30
+ let pending = null;
31
+ /** Queue a council convening to run before the model's next step. */
32
+ export function requestCouncilConvene(reason, toolCallId) {
33
+ const trimmedReason = typeof reason === "string" ? reason.trim() : "";
34
+ const trimmedId = typeof toolCallId === "string" ? toolCallId.trim() : "";
35
+ pending = {
36
+ reason: trimmedReason.length > 0 ? trimmedReason : null,
37
+ toolCallId: trimmedId.length > 0 ? trimmedId : null,
38
+ };
39
+ }
40
+ /** True when a council convening is queued (non-consuming peek). */
41
+ export function hasPendingCouncilConvene() {
42
+ return pending !== null;
43
+ }
44
+ /**
45
+ * Non-consuming read of the pending request's toolCallId. Used by the tool-engine
46
+ * to check whether the pending convene belongs to THIS drain's response.messages
47
+ * before committing to a (possibly expensive, possibly wrong-frame) debate.
48
+ */
49
+ export function peekCouncilConveneToolCallId() {
50
+ return pending?.toolCallId ?? null;
51
+ }
52
+ /** Consume the pending request exactly once (returns null when none queued). */
53
+ export function consumeCouncilConvene() {
54
+ const p = pending;
55
+ pending = null;
56
+ return p;
57
+ }
58
+ /** Test hook — clear any queued request. */
59
+ export function __resetCouncilConveneForTests() {
60
+ pending = null;
61
+ }
62
+ //# sourceMappingURL=council-request.js.map
@@ -0,0 +1,26 @@
1
+ /**
2
+ * src/orchestrator/interactive-pause.ts
3
+ *
4
+ * Process-global "an interactive card is blocking the turn" gate.
5
+ *
6
+ * Some tools (`ask_user`) block inside their AI-SDK `execute()` waiting for a
7
+ * human to answer a card. During that wait NO stream chunks flow, so both the
8
+ * per-attempt stall watchdog (getProviderStallTimeoutMs, default 2 min) and the
9
+ * turn-idle watchdog (MUONROI_TURN_IDLE_MS, default 2 min) would fire and abort
10
+ * a turn that is not stalled at all — the human is just thinking.
11
+ *
12
+ * Rather than thread a keepalive timer through every call site, the blocking
13
+ * handler brackets its wait with {@link beginInteractivePause} /
14
+ * {@link endInteractivePause}, and both watchdogs consult
15
+ * {@link isInteractivePaused} before firing — re-arming instead of aborting
16
+ * while a card is open. Counter (not boolean) so nested/concurrent cards are
17
+ * safe.
18
+ */
19
+ /** Enter an interactive pause — call before awaiting a blocking card. */
20
+ export declare function beginInteractivePause(): void;
21
+ /** Leave an interactive pause — call in a `finally` after the card resolves. */
22
+ export declare function endInteractivePause(): void;
23
+ /** True while at least one interactive card is blocking the turn. */
24
+ export declare function isInteractivePaused(): boolean;
25
+ /** Test-only: force the counter back to zero. */
26
+ export declare function __resetInteractivePauseForTests(): void;
@@ -0,0 +1,36 @@
1
+ /**
2
+ * src/orchestrator/interactive-pause.ts
3
+ *
4
+ * Process-global "an interactive card is blocking the turn" gate.
5
+ *
6
+ * Some tools (`ask_user`) block inside their AI-SDK `execute()` waiting for a
7
+ * human to answer a card. During that wait NO stream chunks flow, so both the
8
+ * per-attempt stall watchdog (getProviderStallTimeoutMs, default 2 min) and the
9
+ * turn-idle watchdog (MUONROI_TURN_IDLE_MS, default 2 min) would fire and abort
10
+ * a turn that is not stalled at all — the human is just thinking.
11
+ *
12
+ * Rather than thread a keepalive timer through every call site, the blocking
13
+ * handler brackets its wait with {@link beginInteractivePause} /
14
+ * {@link endInteractivePause}, and both watchdogs consult
15
+ * {@link isInteractivePaused} before firing — re-arming instead of aborting
16
+ * while a card is open. Counter (not boolean) so nested/concurrent cards are
17
+ * safe.
18
+ */
19
+ let pauseDepth = 0;
20
+ /** Enter an interactive pause — call before awaiting a blocking card. */
21
+ export function beginInteractivePause() {
22
+ pauseDepth += 1;
23
+ }
24
+ /** Leave an interactive pause — call in a `finally` after the card resolves. */
25
+ export function endInteractivePause() {
26
+ pauseDepth = Math.max(0, pauseDepth - 1);
27
+ }
28
+ /** True while at least one interactive card is blocking the turn. */
29
+ export function isInteractivePaused() {
30
+ return pauseDepth > 0;
31
+ }
32
+ /** Test-only: force the counter back to zero. */
33
+ export function __resetInteractivePauseForTests() {
34
+ pauseDepth = 0;
35
+ }
36
+ //# sourceMappingURL=interactive-pause.js.map
@@ -7,6 +7,7 @@ import type { SessionInfo, StreamChunk, SubagentStatus } from "../types/index.js
7
7
  import { type PermissionMode } from "../utils/permission-mode.js";
8
8
  import type { AbortContext } from "./abort.js";
9
9
  import type { LegacyProvider, ProcessMessageObserver } from "./agent-options.js";
10
+ import type { AskUserAskInfo } from "./ask-user.js";
10
11
  import type { CouncilManager } from "./council-manager.js";
11
12
  import type { CrossTurnDedup } from "./cross-turn-dedup.js";
12
13
  import type { PendingCallsLog } from "./pending-calls.js";
@@ -115,10 +116,13 @@ export interface MessageProcessorDeps extends TurnRunnerDepsBase {
115
116
  askToolLoopContinue?: ToolLoopCapAsk;
116
117
  /** Safety override handler — invoked when a tool call is blocked by the safety filter. */
117
118
  askSafetyOverride?: (info: SafetyOverrideAskInfo) => Promise<SafetyOverrideVerdict>;
119
+ /** ask_user handler — invoked when the model calls the `ask_user` tool; resolves the human's answer. */
120
+ askUser?: (info: AskUserAskInfo) => Promise<string>;
118
121
  runCouncilV2(userMessage: string, opts: {
119
122
  skipClarification: boolean;
120
123
  observer?: ProcessMessageObserver;
121
124
  userModelMessage: ModelMessage;
125
+ convenePath?: boolean;
122
126
  }): AsyncGenerator<StreamChunk, void, unknown>;
123
127
  processMessage(userMessage: string, observer?: ProcessMessageObserver, images?: Array<{
124
128
  path: string;
@@ -681,13 +681,31 @@ export class MessageProcessor {
681
681
  const turnProviderId = detectProviderForModel(turnModelId);
682
682
  let turnProvider;
683
683
  if (turnProviderId !== deps.providerId) {
684
+ const disabled = isProviderDisabled(turnProviderId);
684
685
  // Even if the key is reachable, skip disabled providers
685
- const turnKey = !isProviderDisabled(turnProviderId)
686
- ? await loadKeyForProvider(turnProviderId).catch(() => null)
687
- : null;
688
- if (turnKey) {
689
- const { createProviderFactory } = await import("../providers/runtime.js");
690
- turnProvider = createProviderFactory(turnProviderId, { apiKey: turnKey }).factory;
686
+ const turnKey = !disabled ? await loadKeyForProvider(turnProviderId).catch(() => null) : null;
687
+ // An OAuth-capable provider may be authenticated by subscription tokens
688
+ // (no env API key). Detect that so the turn can still run over OAuth.
689
+ let hasOAuth = false;
690
+ if (!disabled) {
691
+ try {
692
+ const { getOAuthProviderConfig } = await import("../providers/auth/registry.js");
693
+ const cfg = await getOAuthProviderConfig(turnProviderId);
694
+ const tokens = cfg ? await cfg.loadTokensWithRefresh().catch(() => null) : null;
695
+ hasOAuth = !!tokens?.accessToken;
696
+ }
697
+ catch {
698
+ hasOAuth = false;
699
+ }
700
+ }
701
+ if (turnKey || hasOAuth) {
702
+ // OAuth XOR API key: the ASYNC factory injects OAuth (codex baseURL +
703
+ // Bearer headers) when tokens exist for this provider, otherwise it
704
+ // uses the env key. Using the sync factory here was the cross-provider
705
+ // shadowing bug (it shipped a stale sk-proj key to api.openai.com).
706
+ const { createProviderFactoryAsync } = await import("../providers/runtime.js");
707
+ const built = await createProviderFactoryAsync(turnProviderId, turnKey ? { apiKey: turnKey } : {});
708
+ turnProvider = built.factory;
691
709
  }
692
710
  else {
693
711
  // Router's provider unreachable or disabled — fall back to a non-disabled provider
@@ -905,7 +923,7 @@ export class MessageProcessor {
905
923
  const system = applyModelConstraints(applyPilSuffix(`${systemParts.staticPrefix}${playwrightGuidance}${systemParts.dynamicSuffix}`, pilCtx, _hasResponseTools), turnModelId);
906
924
  // Tool-turn system: same template as system but with toolTurn-prefix
907
925
  const toolTurnSystem = applyModelConstraints(applyPilSuffix(`${toolTurnParts.staticPrefix}${playwrightGuidance}${toolTurnParts.dynamicSuffix}`, pilCtx, _hasResponseTools), turnModelId);
908
- const runtime = resolveModelRuntime(provider, turnModelId);
926
+ const runtime = resolveModelRuntime(turnModelId);
909
927
  const modelInfo = runtime.modelInfo;
910
928
  // SAMR: Step-Aware Model Routing — downgrade to fast model for tool-execution
911
929
  // steps after the initial reasoning step. The premium model decides WHAT to do;
@@ -938,7 +956,7 @@ export class MessageProcessor {
938
956
  const stepRouterDecision = decideStepRouting(turnModelId, deps.providerId, stepRouterCfg);
939
957
  const stepRouterPhase = stepRouterDecision.phase2ModelId ? "phase1" : "done";
940
958
  const phase2Runtime = stepRouterDecision.phase2ModelId
941
- ? resolveModelRuntime(provider, stepRouterDecision.phase2ModelId)
959
+ ? resolveModelRuntime(stepRouterDecision.phase2ModelId)
942
960
  : null;
943
961
  if (stepRouterDecision.phase2ModelId && _debugOn) {
944
962
  _debugSteps.push({
@@ -5,6 +5,7 @@ import type { AgentMode, ChatEntry, SessionInfo, SessionSnapshot, StreamChunk, S
5
5
  import { type SandboxMode, type SandboxSettings } from "../utils/settings.js";
6
6
  import { type SideQuestionResult } from "../utils/side-question.js";
7
7
  import { type AgentOptions, type LegacyProvider, type ProcessMessageObserver } from "./agent-options.js";
8
+ import type { AskUserAskInfo } from "./ask-user.js";
8
9
  import type { SafetyOverrideAskInfo, SafetyOverrideVerdict } from "./safety-askcard.js";
9
10
  import type { ToolLoopCapAsk } from "./tool-loop-cap.js";
10
11
  /** Deterministic fallback title: truncated first user message, or "" when the
@@ -121,6 +122,22 @@ export declare class Agent {
121
122
  text: string;
122
123
  }[]) | null): void;
123
124
  getActiveRunId(): string | null;
125
+ /**
126
+ * Point the agent at `model`, re-resolving the provider when the new model
127
+ * belongs to a different one.
128
+ *
129
+ * EVERY path that changes `this.modelId` must go through here. Changing the
130
+ * id alone leaves `providerId` / `provider` / `baseURL` / `apiKey` describing
131
+ * the PREVIOUS provider, so the next turn POSTs the new model to the old
132
+ * provider's endpoint — which rejects it as unknown (session 0c6728ba1a25:
133
+ * `gpt-5.4` sent to api.x.ai → 404 "The model gpt-5.4 does not exist", a
134
+ * wiring bug that reads like a bad model name). `setMode` used to do exactly
135
+ * that; it now calls this too.
136
+ *
137
+ * Session persistence is NOT done here — callers own that, because setMode
138
+ * writes mode+model together.
139
+ */
140
+ private _applyModelId;
124
141
  setModel(model: string): void;
125
142
  getMode(): AgentMode;
126
143
  getSandboxMode(): SandboxMode;
@@ -174,6 +191,8 @@ export declare class Agent {
174
191
  extendHardCeilingForAutoCompaction(): void;
175
192
  private _safetyOverrideHandler;
176
193
  setSafetyOverrideHandler(fn: ((block: SafetyOverrideAskInfo) => Promise<SafetyOverrideVerdict>) | null): void;
194
+ private _askUserHandler;
195
+ setAskUserHandler(fn: ((info: AskUserAskInfo) => Promise<string>) | null): void;
177
196
  respondToToolApproval(approvalId: string, approved: boolean): void;
178
197
  clearHistory(): Promise<void>;
179
198
  startNewSession(): SessionSnapshot | null;
@@ -237,6 +256,12 @@ export declare class Agent {
237
256
  skipClarification?: boolean;
238
257
  observer?: ProcessMessageObserver;
239
258
  userModelMessage?: ModelMessage;
259
+ /**
260
+ * convene_council path — forwarded into runCouncil so the post-debate
261
+ * decision surface is suppressed and the synthesis is returned to the
262
+ * calling agent (no CLI-hardcoded post-council branch).
263
+ */
264
+ convenePath?: boolean;
240
265
  }): AsyncGenerator<StreamChunk, void, unknown>;
241
266
  runProductLoopV1(payload: {
242
267
  subcommand: "start" | "status" | "resume" | "abort" | "ship" | "review";