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.
- package/dist/packages/agent-harness-core/src/event-filter.js +11 -0
- package/dist/packages/agent-harness-core/src/event-redact.js +7 -0
- package/dist/packages/agent-harness-core/src/event-tee.d.ts +20 -4
- package/dist/packages/agent-harness-core/src/event-tee.js +32 -5
- package/dist/packages/agent-harness-core/src/mcp-server.d.ts +14 -0
- package/dist/packages/agent-harness-core/src/mcp-server.js +86 -10
- package/dist/packages/agent-harness-core/src/predicate.d.ts +1 -1
- package/dist/packages/agent-harness-core/src/protocol.d.ts +24 -2
- package/dist/src/agent-harness/mock-model.d.ts +10 -0
- package/dist/src/agent-harness/mock-model.js +6 -2
- package/dist/src/chat/chat-keychain.d.ts +7 -12
- package/dist/src/chat/chat-keychain.js +19 -86
- package/dist/src/cli/keys.d.ts +8 -45
- package/dist/src/cli/keys.js +18 -324
- package/dist/src/council/clarifier.d.ts +4 -2
- package/dist/src/council/clarifier.js +158 -36
- package/dist/src/council/debate-planner.js +3 -2
- package/dist/src/council/debate.js +59 -5
- package/dist/src/council/index.d.ts +23 -0
- package/dist/src/council/index.js +37 -2
- package/dist/src/council/llm.d.ts +62 -0
- package/dist/src/council/llm.js +123 -23
- package/dist/src/council/panel-select.js +13 -3
- package/dist/src/council/planner.js +17 -0
- package/dist/src/council/preflight.d.ts +10 -0
- package/dist/src/council/preflight.js +36 -0
- package/dist/src/council/prompts.d.ts +9 -1
- package/dist/src/council/prompts.js +23 -6
- package/dist/src/council/types.d.ts +10 -0
- package/dist/src/ee/auth.d.ts +19 -0
- package/dist/src/ee/auth.js +39 -0
- package/dist/src/ee/client.js +28 -3
- package/dist/src/ee/ee-onboarding.js +6 -26
- package/dist/src/flow/compaction/compress.d.ts +2 -2
- package/dist/src/flow/compaction/compress.js +21 -8
- package/dist/src/flow/compaction/extract.d.ts +3 -3
- package/dist/src/flow/compaction/extract.js +6 -6
- package/dist/src/flow/compaction/index.d.ts +2 -1
- package/dist/src/flow/compaction/index.js +29 -3
- package/dist/src/flow/compaction/progress.d.ts +35 -0
- package/dist/src/flow/compaction/progress.js +35 -0
- package/dist/src/generated/version.d.ts +1 -1
- package/dist/src/generated/version.js +1 -1
- package/dist/src/gsd/flags.d.ts +11 -0
- package/dist/src/gsd/flags.js +19 -0
- package/dist/src/gsd/plan-council.js +104 -72
- package/dist/src/gsd/verdict-schema.d.ts +1 -1
- package/dist/src/headless/council-answers.js +4 -0
- package/dist/src/index.js +129 -260
- package/dist/src/lsp/builtins.js +3 -1
- package/dist/src/lsp/manager.d.ts +5 -1
- package/dist/src/lsp/manager.js +249 -3
- package/dist/src/lsp/npm-cache.d.ts +11 -1
- package/dist/src/lsp/npm-cache.js +17 -1
- package/dist/src/lsp/runtime.d.ts +6 -1
- package/dist/src/lsp/runtime.js +17 -1
- package/dist/src/lsp/types.d.ts +83 -1
- package/dist/src/lsp/types.js +10 -0
- package/dist/src/mcp/client-pool.js +43 -15
- package/dist/src/mcp/lsp-tools.d.ts +5 -1
- package/dist/src/mcp/lsp-tools.js +93 -2
- package/dist/src/mcp/mcp-keychain.d.ts +3 -5
- package/dist/src/mcp/mcp-keychain.js +9 -49
- package/dist/src/mcp/setup-guide-text.d.ts +1 -1
- package/dist/src/mcp/setup-guide-text.js +22 -2
- package/dist/src/mcp/tools-server.d.ts +10 -0
- package/dist/src/mcp/tools-server.js +10 -2
- package/dist/src/models/catalog.json +19 -19
- package/dist/src/orchestrator/ask-user.d.ts +61 -0
- package/dist/src/orchestrator/ask-user.js +65 -0
- package/dist/src/orchestrator/compaction.d.ts +2 -3
- package/dist/src/orchestrator/compaction.js +8 -8
- package/dist/src/orchestrator/council-manager.js +9 -8
- package/dist/src/orchestrator/council-request.d.ts +49 -0
- package/dist/src/orchestrator/council-request.js +62 -0
- package/dist/src/orchestrator/interactive-pause.d.ts +26 -0
- package/dist/src/orchestrator/interactive-pause.js +36 -0
- package/dist/src/orchestrator/message-processor.d.ts +4 -0
- package/dist/src/orchestrator/message-processor.js +26 -8
- package/dist/src/orchestrator/orchestrator.d.ts +25 -0
- package/dist/src/orchestrator/orchestrator.js +204 -50
- package/dist/src/orchestrator/preprocessor.js +2 -2
- package/dist/src/orchestrator/safety-askcard.d.ts +1 -1
- package/dist/src/orchestrator/safety-askcard.js +5 -2
- package/dist/src/orchestrator/safety-intercept.d.ts +5 -0
- package/dist/src/orchestrator/safety-intercept.js +7 -0
- package/dist/src/orchestrator/stall-watchdog.d.ts +8 -1
- package/dist/src/orchestrator/stall-watchdog.js +24 -3
- package/dist/src/orchestrator/stream-runner.d.ts +13 -3
- package/dist/src/orchestrator/stream-runner.js +54 -21
- package/dist/src/orchestrator/tool-engine.d.ts +19 -0
- package/dist/src/orchestrator/tool-engine.js +241 -25
- package/dist/src/orchestrator/turn-watchdog.d.ts +7 -0
- package/dist/src/orchestrator/turn-watchdog.js +38 -9
- package/dist/src/pil/agent-operating-contract.d.ts +1 -1
- package/dist/src/pil/agent-operating-contract.js +6 -4
- package/dist/src/pil/discovery.d.ts +1 -1
- package/dist/src/pil/discovery.js +2 -2
- package/dist/src/pil/layer1_5-complexity-size.d.ts +7 -0
- package/dist/src/pil/layer1_5-complexity-size.js +31 -5
- package/dist/src/pil/llm-classify.d.ts +78 -3
- package/dist/src/pil/llm-classify.js +351 -111
- package/dist/src/pil/native-capabilities-workbook.d.ts +1 -1
- package/dist/src/pil/native-capabilities-workbook.js +7 -0
- package/dist/src/pil/pipeline.js +2 -0
- package/dist/src/pil/repo-grounding-probe.d.ts +15 -0
- package/dist/src/pil/repo-grounding-probe.js +136 -0
- package/dist/src/pil/repo-structure-hints.d.ts +7 -0
- package/dist/src/pil/repo-structure-hints.js +45 -0
- package/dist/src/product-loop/artifact-io.js +4 -0
- package/dist/src/product-loop/criteria-seed.d.ts +51 -0
- package/dist/src/product-loop/criteria-seed.js +200 -0
- package/dist/src/product-loop/discovery-interview.d.ts +9 -0
- package/dist/src/product-loop/discovery-interview.js +37 -18
- package/dist/src/product-loop/discovery-recommender.js +2 -1
- package/dist/src/product-loop/discovery-schema.js +14 -1
- package/dist/src/product-loop/discovery-triage.d.ts +23 -0
- package/dist/src/product-loop/discovery-triage.js +109 -0
- package/dist/src/product-loop/gather.js +150 -2
- package/dist/src/product-loop/index.js +7 -0
- package/dist/src/product-loop/loop-driver.js +21 -8
- package/dist/src/product-loop/phase-plan.d.ts +16 -0
- package/dist/src/product-loop/phase-plan.js +42 -4
- package/dist/src/product-loop/phase-rituals.d.ts +3 -0
- package/dist/src/product-loop/phase-rituals.js +8 -3
- package/dist/src/product-loop/phase-runner.js +30 -11
- package/dist/src/product-loop/plan-adherence-review.d.ts +26 -0
- package/dist/src/product-loop/plan-adherence-review.js +144 -0
- package/dist/src/product-loop/sprint-runner.d.ts +62 -0
- package/dist/src/product-loop/sprint-runner.js +309 -8
- package/dist/src/product-loop/types.d.ts +25 -0
- package/dist/src/providers/anthropic.d.ts +9 -8
- package/dist/src/providers/anthropic.js +13 -47
- package/dist/src/providers/auth/grok-oauth.d.ts +1 -0
- package/dist/src/providers/auth/grok-oauth.js +30 -5
- package/dist/src/providers/auth/openai-oauth.d.ts +1 -0
- package/dist/src/providers/auth/openai-oauth.js +14 -0
- package/dist/src/providers/auth/token-store.d.ts +9 -9
- package/dist/src/providers/auth/token-store.js +8 -70
- package/dist/src/providers/auth/types.d.ts +8 -0
- package/dist/src/providers/env-store.d.ts +17 -0
- package/dist/src/providers/env-store.js +228 -0
- package/dist/src/providers/keychain.d.ts +21 -17
- package/dist/src/providers/keychain.js +124 -135
- package/dist/src/providers/runtime.d.ts +24 -9
- package/dist/src/providers/runtime.js +48 -37
- package/dist/src/providers/strategies/thinking-mode.js +9 -1
- package/dist/src/providers/strategies/xai.strategy.js +27 -0
- package/dist/src/providers/warm.d.ts +65 -0
- package/dist/src/providers/warm.js +145 -0
- package/dist/src/self-qa/agentic-loop.js +3 -2
- package/dist/src/storage/transcript.js +56 -2
- package/dist/src/tools/git-safety.d.ts +19 -0
- package/dist/src/tools/git-safety.js +168 -0
- package/dist/src/tools/native-tools.d.ts +1 -1
- package/dist/src/tools/native-tools.js +76 -1
- package/dist/src/tools/registry.d.ts +17 -0
- package/dist/src/tools/registry.js +116 -1
- package/dist/src/types/index.d.ts +29 -1
- package/dist/src/ui/app.js +67 -4
- package/dist/src/ui/components/agent-rail-activities.d.ts +26 -0
- package/dist/src/ui/components/agent-rail-activities.js +47 -0
- package/dist/src/ui/components/compact-progress-card.d.ts +24 -0
- package/dist/src/ui/components/compact-progress-card.js +42 -0
- package/dist/src/ui/components/council-phase-timeline.js +17 -2
- package/dist/src/ui/components/council-question-card.js +1 -0
- package/dist/src/ui/components/message-view.d.ts +15 -0
- package/dist/src/ui/components/message-view.js +50 -1
- package/dist/src/ui/components/tool-group.d.ts +15 -3
- package/dist/src/ui/components/tool-group.js +69 -11
- package/dist/src/ui/containers/modals-layer.d.ts +2 -1
- package/dist/src/ui/containers/modals-layer.js +2 -2
- package/dist/src/ui/council-harness-event.d.ts +57 -0
- package/dist/src/ui/council-harness-event.js +46 -0
- package/dist/src/ui/heartbeat-debug.d.ts +29 -0
- package/dist/src/ui/heartbeat-debug.js +45 -0
- package/dist/src/ui/modals/api-key-modal.js +1 -1
- package/dist/src/ui/modals/model-picker-modal.d.ts +8 -18
- package/dist/src/ui/modals/model-picker-modal.js +8 -10
- package/dist/src/ui/slash/ee.js +81 -0
- package/dist/src/ui/slash/menu-items.js +11 -2
- package/dist/src/ui/use-app-logic.js +354 -224
- package/dist/src/ui/utils/agent-activities.d.ts +39 -0
- package/dist/src/ui/utils/agent-activities.js +96 -0
- package/dist/src/ui/utils/group-tool-entries.d.ts +26 -0
- package/dist/src/ui/utils/group-tool-entries.js +111 -0
- package/dist/src/ui/utils/tool-summary.d.ts +21 -0
- package/dist/src/ui/utils/tool-summary.js +91 -0
- package/dist/src/utils/event-loop-monitor.d.ts +85 -0
- package/dist/src/utils/event-loop-monitor.js +107 -0
- package/dist/src/utils/llm-deadline.d.ts +14 -0
- package/dist/src/utils/llm-deadline.js +19 -0
- package/dist/src/utils/loop-profiler.d.ts +102 -0
- package/dist/src/utils/loop-profiler.js +202 -0
- package/dist/src/utils/settings.d.ts +27 -0
- package/dist/src/utils/settings.js +38 -2
- package/dist/src/utils/side-question.d.ts +1 -2
- package/dist/src/utils/side-question.js +2 -2
- package/dist/src/verify/entrypoint.js +51 -16
- package/dist/src/verify/orchestrator.d.ts +1 -1
- package/dist/src/verify/orchestrator.js +20 -3
- package/package.json +1 -2
- package/dist/src/cli/bw-vault.d.ts +0 -55
- package/dist/src/cli/bw-vault.js +0 -133
- package/dist/src/mcp/ee-tools.d.ts +0 -46
- 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
|
|
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
|
-
|
|
153
|
-
const runtime = resolveModelRuntime(
|
|
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
|
-
|
|
174
|
-
const runtime = resolveModelRuntime(
|
|
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:
|
|
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
|
|
512
|
-
|
|
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 = !
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
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(
|
|
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(
|
|
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";
|