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
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { LanguageModel, ToolSet } from "ai";
|
|
2
|
+
import { stepCountIs } from "ai";
|
|
1
3
|
import type { BashTool } from "../tools/bash.js";
|
|
2
4
|
import type { AgentMode, CouncilStatusPhase, StreamChunk } from "../types/index.js";
|
|
3
5
|
import type { CouncilLLM, CouncilStats } from "./types.js";
|
|
@@ -25,6 +27,56 @@ export declare function recordCouncilUsage(sessionId: string | undefined, modelI
|
|
|
25
27
|
declare global {
|
|
26
28
|
var __muonroiMockLlm: any;
|
|
27
29
|
}
|
|
30
|
+
/** Record `chars` of streamed output (text OR reasoning) against the live window. */
|
|
31
|
+
export declare function noteCouncilStreamDelta(chars: number): void;
|
|
32
|
+
/**
|
|
33
|
+
* Open a liveness window and return a reader for it.
|
|
34
|
+
*
|
|
35
|
+
* `streamedChars` counts only what streamed SINCE the window opened, and
|
|
36
|
+
* `lastDeltaAgeMs` is measured from the window's own start until the first
|
|
37
|
+
* delta lands — so a cold stall reads as an age growing from 0, never as a
|
|
38
|
+
* bogus epoch-sized number. Reasoning-delta counts toward both: a reasoning
|
|
39
|
+
* model emits reasoning tokens for minutes before any text, and that is
|
|
40
|
+
* exactly the window an operator must not mistake for a hang.
|
|
41
|
+
*/
|
|
42
|
+
export declare function councilStreamLivenessReader(): () => {
|
|
43
|
+
streamedChars: number;
|
|
44
|
+
lastDeltaAgeMs: number;
|
|
45
|
+
};
|
|
46
|
+
declare function collectStreamText(args: {
|
|
47
|
+
model: LanguageModel;
|
|
48
|
+
system: string;
|
|
49
|
+
prompt: string;
|
|
50
|
+
maxOutputTokens?: number;
|
|
51
|
+
temperature?: number;
|
|
52
|
+
providerOptions?: Record<string, unknown>;
|
|
53
|
+
abortSignal?: AbortSignal;
|
|
54
|
+
tools?: ToolSet;
|
|
55
|
+
stopWhen?: ReturnType<typeof stepCountIs>;
|
|
56
|
+
prepareStep?: (opts: {
|
|
57
|
+
stepNumber: number;
|
|
58
|
+
messages: readonly unknown[];
|
|
59
|
+
}) => unknown;
|
|
60
|
+
/** Called with the char length of every text/reasoning delta as it arrives. */
|
|
61
|
+
onDelta?: (chars: number) => void;
|
|
62
|
+
}): Promise<{
|
|
63
|
+
text: string;
|
|
64
|
+
usage?: unknown;
|
|
65
|
+
finishReason?: string;
|
|
66
|
+
reasoningText?: string;
|
|
67
|
+
toolCalls: Array<{
|
|
68
|
+
toolName: string;
|
|
69
|
+
input?: unknown;
|
|
70
|
+
result?: unknown;
|
|
71
|
+
}>;
|
|
72
|
+
}>;
|
|
73
|
+
/**
|
|
74
|
+
* Test-only handle on the private stream collector, so `onDelta` (the liveness
|
|
75
|
+
* signal that tells a slow reasoning call from a hung one) is directly
|
|
76
|
+
* assertable without standing up a full council run. Not for production use.
|
|
77
|
+
* @internal
|
|
78
|
+
*/
|
|
79
|
+
export declare const __testCollectStreamText: typeof collectStreamText;
|
|
28
80
|
export declare function createCouncilLLM(bash: BashTool, mode: AgentMode, sessionId: string | undefined, stats: CouncilStats): CouncilLLM;
|
|
29
81
|
interface TracedGenerateArgs {
|
|
30
82
|
phase: CouncilStatusPhase;
|
|
@@ -62,6 +114,16 @@ interface TracedAsyncArgs {
|
|
|
62
114
|
detail?: string;
|
|
63
115
|
role?: string;
|
|
64
116
|
tickIntervalMs?: number;
|
|
117
|
+
/**
|
|
118
|
+
* Optional push-based stream liveness reader (see {@link councilStreamLivenessReader}).
|
|
119
|
+
* When supplied, every tick/done carries the chars streamed inside this window
|
|
120
|
+
* and the age of the last delta — so a frozen `elapsedMs` (generator not being
|
|
121
|
+
* pumped) can still be told apart from a genuinely stuck call.
|
|
122
|
+
*/
|
|
123
|
+
liveness?: () => {
|
|
124
|
+
streamedChars: number;
|
|
125
|
+
lastDeltaAgeMs: number;
|
|
126
|
+
};
|
|
65
127
|
}
|
|
66
128
|
/**
|
|
67
129
|
* Generic version of {@link tracedGenerate} for arbitrary async work
|
package/dist/src/council/llm.js
CHANGED
|
@@ -7,7 +7,8 @@ import { buildMcpToolSet } from "../mcp/runtime.js";
|
|
|
7
7
|
import { getModelInfo } from "../models/registry.js";
|
|
8
8
|
import { getProviderCapabilities, resolveTemperature } from "../providers/capabilities.js";
|
|
9
9
|
import { loadKeyForProvider, ProviderKeyMissingError } from "../providers/keychain.js";
|
|
10
|
-
import { createProviderFactoryAsync, detectProviderForModel, resolveModelRuntime } from "../providers/runtime.js";
|
|
10
|
+
import { createProviderFactoryAsync, detectProviderForModel, resolveModelRuntime, shouldDropParam, } from "../providers/runtime.js";
|
|
11
|
+
import { wireDebug } from "../providers/wire-debug.js";
|
|
11
12
|
import { statusBarStore } from "../state/status-bar-store.js";
|
|
12
13
|
import { recordUsageEvent } from "../storage/index.js";
|
|
13
14
|
import { createBuiltinTools as createTools } from "../tools/registry.js";
|
|
@@ -20,7 +21,11 @@ import { withVisibleRetry } from "../utils/visible-retry.js";
|
|
|
20
21
|
import { buildResearchSystemPrompt } from "./prompts.js";
|
|
21
22
|
import { stripThinkBlocks } from "./strip-think.js";
|
|
22
23
|
/**
|
|
23
|
-
*
|
|
24
|
+
* Register a provider factory for a council sub-call, OAuth-aware.
|
|
25
|
+
*
|
|
26
|
+
* A council roster routes stances to providers the session itself never built,
|
|
27
|
+
* so their factory must exist in the registry before `resolveModelRuntime` can
|
|
28
|
+
* derive it from the model id.
|
|
24
29
|
*
|
|
25
30
|
* The council reachability gate (`isProviderReachable` in leader.ts) counts
|
|
26
31
|
* OAuth-authenticated providers as usable (via `getConfiguredProviders`), so a
|
|
@@ -33,7 +38,7 @@ import { stripThinkBlocks } from "./strip-think.js";
|
|
|
33
38
|
* refreshes the stored OAuth bearer token. Only the expected missing-key case
|
|
34
39
|
* is swallowed; unexpected errors propagate.
|
|
35
40
|
*/
|
|
36
|
-
async function
|
|
41
|
+
async function ensureCouncilFactory(providerId) {
|
|
37
42
|
let apiKey;
|
|
38
43
|
try {
|
|
39
44
|
apiKey = await loadKeyForProvider(providerId);
|
|
@@ -43,8 +48,7 @@ async function resolveCouncilFactory(providerId) {
|
|
|
43
48
|
throw err;
|
|
44
49
|
// OAuth-only provider — createProviderFactoryAsync injects the bearer token.
|
|
45
50
|
}
|
|
46
|
-
|
|
47
|
-
return factory;
|
|
51
|
+
await createProviderFactoryAsync(providerId, apiKey ? { apiKey } : {});
|
|
48
52
|
}
|
|
49
53
|
function getDebugLogPath() {
|
|
50
54
|
const p = process.env.MUONROI_COUNCIL_DEBUG_LOG;
|
|
@@ -297,13 +301,64 @@ const COUNCIL_LLM_TIMEOUT_MS = (() => {
|
|
|
297
301
|
* `generateText` result exposed. A debater on the codex/oauth endpoint hit the
|
|
298
302
|
* exact same non-stream 400 as the eval path — streaming fixes it uniformly.
|
|
299
303
|
*/
|
|
304
|
+
/**
|
|
305
|
+
* Spread for the `maxOutputTokens` param, omitted when the resolved runtime
|
|
306
|
+
* rejects it. The ChatGPT Codex OAuth endpoint (chatgpt.com/backend-api/codex/
|
|
307
|
+
* responses) 400s with `{"detail":"Unsupported parameter: max_output_tokens"}`
|
|
308
|
+
* on EVERY council sub-call (clarify / debate / research) — every council
|
|
309
|
+
* generate on a Codex-OAuth session was failing wholesale. Mirrors the
|
|
310
|
+
* orchestrator + classify paths, which already gate this param via
|
|
311
|
+
* `shouldDropParam`.
|
|
312
|
+
*/
|
|
313
|
+
function maxOutSpread(runtime, n) {
|
|
314
|
+
return shouldDropParam(runtime, "maxOutputTokens") ? {} : { maxOutputTokens: n };
|
|
315
|
+
}
|
|
316
|
+
/**
|
|
317
|
+
* Push-based stream liveness — module-level because the only readers
|
|
318
|
+
* (`tracedAsync` wrappers in debate.ts) sit OUTSIDE the `CouncilLLM` interface
|
|
319
|
+
* and cannot reach the in-flight `collectStreamText` any other way.
|
|
320
|
+
*
|
|
321
|
+
* Why it exists. `tracedAsync`'s 1s tick only materializes when the CONSUMER
|
|
322
|
+
* pulls `.next()`; a round that awaits its pairs via `Promise.all` stops
|
|
323
|
+
* pumping the tick generator, so `elapsedMs` FREEZES and a slow-but-alive run
|
|
324
|
+
* is indistinguishable from a hung one (observed: tick frozen at 33142ms for
|
|
325
|
+
* 8+ minutes while status was still "tick"). Token deltas are PUSHED here as
|
|
326
|
+
* they arrive, so liveness survives any back-pressure on the generator.
|
|
327
|
+
*/
|
|
328
|
+
let councilStreamedCharsTotal = 0;
|
|
329
|
+
let councilLastDeltaAt = 0;
|
|
330
|
+
/** Record `chars` of streamed output (text OR reasoning) against the live window. */
|
|
331
|
+
export function noteCouncilStreamDelta(chars) {
|
|
332
|
+
if (chars <= 0)
|
|
333
|
+
return;
|
|
334
|
+
councilStreamedCharsTotal += chars;
|
|
335
|
+
councilLastDeltaAt = Date.now();
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Open a liveness window and return a reader for it.
|
|
339
|
+
*
|
|
340
|
+
* `streamedChars` counts only what streamed SINCE the window opened, and
|
|
341
|
+
* `lastDeltaAgeMs` is measured from the window's own start until the first
|
|
342
|
+
* delta lands — so a cold stall reads as an age growing from 0, never as a
|
|
343
|
+
* bogus epoch-sized number. Reasoning-delta counts toward both: a reasoning
|
|
344
|
+
* model emits reasoning tokens for minutes before any text, and that is
|
|
345
|
+
* exactly the window an operator must not mistake for a hang.
|
|
346
|
+
*/
|
|
347
|
+
export function councilStreamLivenessReader() {
|
|
348
|
+
const baseChars = councilStreamedCharsTotal;
|
|
349
|
+
const windowStart = Date.now();
|
|
350
|
+
return () => ({
|
|
351
|
+
streamedChars: councilStreamedCharsTotal - baseChars,
|
|
352
|
+
lastDeltaAgeMs: Date.now() - (councilLastDeltaAt > windowStart ? councilLastDeltaAt : windowStart),
|
|
353
|
+
});
|
|
354
|
+
}
|
|
300
355
|
async function collectStreamText(args) {
|
|
301
356
|
const hasTools = !!args.tools && Object.keys(args.tools).length > 0;
|
|
302
357
|
const result = streamText({
|
|
303
358
|
model: args.model,
|
|
304
359
|
system: args.system,
|
|
305
360
|
prompt: args.prompt,
|
|
306
|
-
maxOutputTokens: args.maxOutputTokens,
|
|
361
|
+
...(args.maxOutputTokens === undefined ? {} : { maxOutputTokens: args.maxOutputTokens }),
|
|
307
362
|
maxRetries: 0,
|
|
308
363
|
...(args.temperature === undefined ? {} : { temperature: args.temperature }),
|
|
309
364
|
...(args.providerOptions ? { providerOptions: args.providerOptions } : {}),
|
|
@@ -318,12 +373,21 @@ async function collectStreamText(args) {
|
|
|
318
373
|
const byId = new Map();
|
|
319
374
|
for await (const part of result.fullStream) {
|
|
320
375
|
switch (part.type) {
|
|
321
|
-
case "text-delta":
|
|
322
|
-
|
|
376
|
+
case "text-delta": {
|
|
377
|
+
const d = part.text ?? "";
|
|
378
|
+
text += d;
|
|
379
|
+
args.onDelta?.(d.length);
|
|
323
380
|
break;
|
|
324
|
-
|
|
325
|
-
|
|
381
|
+
}
|
|
382
|
+
case "reasoning-delta": {
|
|
383
|
+
// Reasoning deltas count as liveness: a reasoning model streams these
|
|
384
|
+
// for minutes before its first text-delta, and that silent-on-text
|
|
385
|
+
// window is precisely what gets misread as a hang.
|
|
386
|
+
const d = part.text ?? "";
|
|
387
|
+
reasoningText += d;
|
|
388
|
+
args.onDelta?.(d.length);
|
|
326
389
|
break;
|
|
390
|
+
}
|
|
327
391
|
case "tool-call": {
|
|
328
392
|
const p = part;
|
|
329
393
|
const tc = { toolName: p.toolName, input: p.input };
|
|
@@ -352,6 +416,13 @@ async function collectStreamText(args) {
|
|
|
352
416
|
}
|
|
353
417
|
return { text, usage, finishReason, reasoningText: reasoningText || undefined, toolCalls };
|
|
354
418
|
}
|
|
419
|
+
/**
|
|
420
|
+
* Test-only handle on the private stream collector, so `onDelta` (the liveness
|
|
421
|
+
* signal that tells a slow reasoning call from a hung one) is directly
|
|
422
|
+
* assertable without standing up a full council run. Not for production use.
|
|
423
|
+
* @internal
|
|
424
|
+
*/
|
|
425
|
+
export const __testCollectStreamText = collectStreamText;
|
|
355
426
|
export function createCouncilLLM(bash, mode, sessionId, stats) {
|
|
356
427
|
return {
|
|
357
428
|
async generate(modelId, system, prompt, maxTokens = 4096, onUsage, signal) {
|
|
@@ -362,8 +433,8 @@ export function createCouncilLLM(bash, mode, sessionId, stats) {
|
|
|
362
433
|
return stripThinkBlocks(result.text);
|
|
363
434
|
}
|
|
364
435
|
const providerId = detectProviderForModel(modelId);
|
|
365
|
-
|
|
366
|
-
const runtime = resolveModelRuntime(
|
|
436
|
+
await ensureCouncilFactory(providerId);
|
|
437
|
+
const runtime = resolveModelRuntime(modelId);
|
|
367
438
|
const t0 = Date.now();
|
|
368
439
|
// Combine the user-abort signal (when threaded from runCouncil) with the
|
|
369
440
|
// per-call wall-clock deadline. Without the parent signal, an Esc/Ctrl-C
|
|
@@ -381,7 +452,7 @@ export function createCouncilLLM(bash, mode, sessionId, stats) {
|
|
|
381
452
|
model: runtime.model,
|
|
382
453
|
system,
|
|
383
454
|
prompt,
|
|
384
|
-
|
|
455
|
+
...maxOutSpread(runtime, maxTokens),
|
|
385
456
|
// Never hardcode temperature: some upstreams (Moonshot/Kimi via
|
|
386
457
|
// opencode-go) reject any value but their pinned one, which
|
|
387
458
|
// failed every clarify/spec call on a Kimi session. resolveTemperature
|
|
@@ -389,6 +460,7 @@ export function createCouncilLLM(bash, mode, sessionId, stats) {
|
|
|
389
460
|
temperature: resolveTemperature(providerId, runtime.modelInfo, 0.7),
|
|
390
461
|
providerOptions: runtime.providerOptions,
|
|
391
462
|
abortSignal: timedSignal,
|
|
463
|
+
onDelta: noteCouncilStreamDelta,
|
|
392
464
|
}), { label: "council.generate" }), COUNCIL_LLM_TIMEOUT_MS + 5_000, "council.generate", signal);
|
|
393
465
|
cleanupTimeout();
|
|
394
466
|
stats.calls++;
|
|
@@ -426,6 +498,12 @@ export function createCouncilLLM(bash, mode, sessionId, stats) {
|
|
|
426
498
|
}
|
|
427
499
|
catch (err) {
|
|
428
500
|
cleanupTimeout();
|
|
501
|
+
// Capture the provider-side detail (status code + response body +
|
|
502
|
+
// request param shape) that `err.message` alone drops — a generic
|
|
503
|
+
// "Bad Request" on the council path was previously undiagnosable
|
|
504
|
+
// because collectStreamText re-throws only the message. PII-safe,
|
|
505
|
+
// no-op unless MUONROI_DEBUG_LLM_WIRE=1. (No-Silent-Catch.)
|
|
506
|
+
wireDebug.logError(providerId, err);
|
|
429
507
|
writeDebugRecord({
|
|
430
508
|
ts: new Date().toISOString(),
|
|
431
509
|
kind: "generate",
|
|
@@ -452,8 +530,8 @@ export function createCouncilLLM(bash, mode, sessionId, stats) {
|
|
|
452
530
|
return { text: stripThinkBlocks(result.text), toolCalls: [] };
|
|
453
531
|
}
|
|
454
532
|
const providerId = detectProviderForModel(modelId);
|
|
455
|
-
|
|
456
|
-
const runtime = resolveModelRuntime(
|
|
533
|
+
await ensureCouncilFactory(providerId);
|
|
534
|
+
const runtime = resolveModelRuntime(modelId);
|
|
457
535
|
// Verification tools — re-introduced after the no-tools fix (session
|
|
458
536
|
// a7a5690d2049). The original failure was stepCountIs(4) + full toolset
|
|
459
537
|
// letting reasoning models exhaust steps on exploratory tool chains and
|
|
@@ -484,13 +562,16 @@ export function createCouncilLLM(bash, mode, sessionId, stats) {
|
|
|
484
562
|
}
|
|
485
563
|
}
|
|
486
564
|
}
|
|
487
|
-
catch {
|
|
488
|
-
|
|
565
|
+
catch (err) {
|
|
566
|
+
// MCP is optional here — debate continues with builtins only — but a
|
|
567
|
+
// silent swallow hid which server failed and why. (No-Silent-Catch.)
|
|
568
|
+
console.error(`[council/llm] debate MCP tool discovery failed, continuing with builtins only: ${err instanceof Error ? err.message : String(err)}`, { modelId, provider: providerId });
|
|
489
569
|
}
|
|
490
570
|
verificationTools = wrapToolsWithEeCheck(filtered, sessionId ?? "council-debate");
|
|
491
571
|
}
|
|
492
|
-
catch {
|
|
493
|
-
|
|
572
|
+
catch (err) {
|
|
573
|
+
// fail-open: the debate turn still runs, just without verification tools.
|
|
574
|
+
console.error(`[council/llm] debate verification toolset build failed, running without tools: ${err instanceof Error ? err.message : String(err)}`, { modelId, provider: providerId });
|
|
494
575
|
}
|
|
495
576
|
}
|
|
496
577
|
// sanitizeHistory is identity for every provider (kept as a hook for
|
|
@@ -515,11 +596,15 @@ export function createCouncilLLM(bash, mode, sessionId, stats) {
|
|
|
515
596
|
// text. E2E showed 2048 caused finishReason=length on 3KB debate
|
|
516
597
|
// prompts. 6144 leaves ~4000 tokens for text after typical reasoning
|
|
517
598
|
// overhead and avoids cuts mid-thought.
|
|
518
|
-
|
|
599
|
+
...maxOutSpread(runtime, 6144),
|
|
519
600
|
// See generate(): capability-aware temperature (omit / clamp).
|
|
520
601
|
temperature: resolveTemperature(providerId, runtime.modelInfo, 0.7),
|
|
521
602
|
providerOptions: runtime.providerOptions,
|
|
522
603
|
abortSignal: timedSignal,
|
|
604
|
+
// Push liveness for the round wrapper: a debate turn on a
|
|
605
|
+
// reasoning model runs near the 5-min ceiling, and the tick
|
|
606
|
+
// generator is not being pumped while Promise.all awaits.
|
|
607
|
+
onDelta: noteCouncilStreamDelta,
|
|
523
608
|
...(verificationTools && Object.keys(verificationTools).length > 0
|
|
524
609
|
? {
|
|
525
610
|
tools: verificationTools,
|
|
@@ -611,8 +696,8 @@ export function createCouncilLLM(bash, mode, sessionId, stats) {
|
|
|
611
696
|
return stripThinkBlocks(result.text);
|
|
612
697
|
}
|
|
613
698
|
const providerId = detectProviderForModel(modelId);
|
|
614
|
-
|
|
615
|
-
const runtime = resolveModelRuntime(
|
|
699
|
+
await ensureCouncilFactory(providerId);
|
|
700
|
+
const runtime = resolveModelRuntime(modelId);
|
|
616
701
|
const builtinTools = createTools(bash, mode);
|
|
617
702
|
// CQ-03: Lazy MCP bundle per research call — fail-open so builtins remain available
|
|
618
703
|
let mcpBundle = null;
|
|
@@ -668,7 +753,7 @@ export function createCouncilLLM(bash, mode, sessionId, stats) {
|
|
|
668
753
|
const stripped = researchCaps.sanitizeHistory(messages);
|
|
669
754
|
return stripped === messages ? {} : { messages: stripped };
|
|
670
755
|
},
|
|
671
|
-
|
|
756
|
+
...maxOutSpread(runtime, 4096),
|
|
672
757
|
// See generate(): capability-aware temperature (omit / clamp).
|
|
673
758
|
...(() => {
|
|
674
759
|
const t = resolveTemperature(providerId, runtime.modelInfo, 0.3);
|
|
@@ -894,6 +979,19 @@ export async function* tracedAsync(fn, args) {
|
|
|
894
979
|
: `status-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;
|
|
895
980
|
const start = Date.now();
|
|
896
981
|
const tickInterval = args.tickIntervalMs ?? 1000;
|
|
982
|
+
/** Never let a liveness read break the heartbeat it is meant to enrich. */
|
|
983
|
+
const livenessFields = () => {
|
|
984
|
+
if (!args.liveness)
|
|
985
|
+
return {};
|
|
986
|
+
try {
|
|
987
|
+
const l = args.liveness();
|
|
988
|
+
return { streamedChars: l.streamedChars, lastDeltaAgeMs: l.lastDeltaAgeMs };
|
|
989
|
+
}
|
|
990
|
+
catch (err) {
|
|
991
|
+
console.error(`[council/llm] tracedAsync liveness read failed: ${err instanceof Error ? err.message : String(err)}`, { phase: args.phase, label: args.label });
|
|
992
|
+
return {};
|
|
993
|
+
}
|
|
994
|
+
};
|
|
897
995
|
yield {
|
|
898
996
|
type: "council_status",
|
|
899
997
|
councilStatus: {
|
|
@@ -939,6 +1037,7 @@ export async function* tracedAsync(fn, args) {
|
|
|
939
1037
|
detail: args.detail,
|
|
940
1038
|
role: args.role,
|
|
941
1039
|
elapsedMs: Date.now() - start,
|
|
1040
|
+
...livenessFields(),
|
|
942
1041
|
},
|
|
943
1042
|
};
|
|
944
1043
|
}
|
|
@@ -970,6 +1069,7 @@ export async function* tracedAsync(fn, args) {
|
|
|
970
1069
|
detail: args.detail,
|
|
971
1070
|
role: args.role,
|
|
972
1071
|
elapsedMs: Date.now() - start,
|
|
1072
|
+
...livenessFields(),
|
|
973
1073
|
},
|
|
974
1074
|
};
|
|
975
1075
|
return result;
|
|
@@ -42,17 +42,27 @@ export async function* selectTaskAwarePanel(opts) {
|
|
|
42
42
|
maxTokens: 512,
|
|
43
43
|
});
|
|
44
44
|
}
|
|
45
|
-
catch {
|
|
45
|
+
catch (err) {
|
|
46
|
+
// Fail-open: caller keeps its default roster. Log so a provider outage on
|
|
47
|
+
// the leader model is diagnosable instead of silently degrading panel
|
|
48
|
+
// selection to the prompt-blind roster. (No-Silent-Catch.)
|
|
49
|
+
console.error(`[council/panel-select] leader panel selection call failed, keeping default roster: ${err instanceof Error ? err.message : String(err)}`, { leaderModelId, poolSize: pool.length });
|
|
46
50
|
return null;
|
|
47
51
|
}
|
|
48
52
|
const match = raw.match(/\{[\s\S]*\}/);
|
|
49
|
-
if (!match)
|
|
53
|
+
if (!match) {
|
|
54
|
+
console.error("[council/panel-select] leader returned no JSON object, keeping default roster", {
|
|
55
|
+
leaderModelId,
|
|
56
|
+
rawHead: raw.slice(0, 200),
|
|
57
|
+
});
|
|
50
58
|
return null;
|
|
59
|
+
}
|
|
51
60
|
let parsed;
|
|
52
61
|
try {
|
|
53
62
|
parsed = JSON.parse(match[0]);
|
|
54
63
|
}
|
|
55
|
-
catch {
|
|
64
|
+
catch (err) {
|
|
65
|
+
console.error(`[council/panel-select] leader selection JSON parse failed, keeping default roster: ${err instanceof Error ? err.message : String(err)}`, { leaderModelId, rawHead: match[0].slice(0, 200) });
|
|
56
66
|
return null;
|
|
57
67
|
}
|
|
58
68
|
const valid = new Set(pool.map((c) => c.model));
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { getCouncilLanguage } from "../utils/settings.js";
|
|
2
2
|
import { tracedGenerate } from "./llm.js";
|
|
3
3
|
import { phaseDone, phaseError, phaseStart } from "./phase-events.js";
|
|
4
|
+
import { emitPreflightHarnessEvent } from "./preflight.js";
|
|
4
5
|
import { buildSynthesisPrompt } from "./prompts.js";
|
|
5
6
|
export async function* runPlanning(debateState, spec, participants, leaderModelId, respondToPreflight, llm, debatePlan,
|
|
6
7
|
// CQ-18: PIL outputStyle from runCouncil
|
|
@@ -145,6 +146,15 @@ planEmphasis) {
|
|
|
145
146
|
yield { type: "content", content: `- [${step.priority}] ${step.description}\n` };
|
|
146
147
|
}
|
|
147
148
|
const preflightId = crypto.randomUUID();
|
|
149
|
+
emitPreflightHarnessEvent({
|
|
150
|
+
t: "event",
|
|
151
|
+
kind: "askcard-open",
|
|
152
|
+
questionId: preflightId,
|
|
153
|
+
question: `Approve action plan for: ${spec.problemStatement}`,
|
|
154
|
+
phase: "plan-confirm",
|
|
155
|
+
optionCount: 2,
|
|
156
|
+
defaultIndex: 0,
|
|
157
|
+
});
|
|
148
158
|
yield {
|
|
149
159
|
type: "council_preflight",
|
|
150
160
|
content: "Review the action plan above. Approve to proceed with execution, or reject.",
|
|
@@ -159,6 +169,13 @@ planEmphasis) {
|
|
|
159
169
|
},
|
|
160
170
|
};
|
|
161
171
|
const approved = await respondToPreflight(preflightId);
|
|
172
|
+
emitPreflightHarnessEvent({
|
|
173
|
+
t: "event",
|
|
174
|
+
kind: "askcard-answered",
|
|
175
|
+
questionId: preflightId,
|
|
176
|
+
answerKind: "choice",
|
|
177
|
+
answerText: approved ? "approve" : "reject",
|
|
178
|
+
});
|
|
162
179
|
yield phaseDone({
|
|
163
180
|
phaseId: "phase:action-plan",
|
|
164
181
|
kind: "action_plan",
|
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
import type { StreamChunk } from "../types/index.js";
|
|
2
2
|
import type { ClarifiedSpec, PreflightResponder } from "./types.js";
|
|
3
|
+
/**
|
|
4
|
+
* Emit a harness LiveEvent for a preflight/plan-confirm approval gate.
|
|
5
|
+
*
|
|
6
|
+
* These gates are surfaced as `council_preflight` StreamChunks (a UI render path),
|
|
7
|
+
* NOT as LiveEvents — so a monitor on MUONROI_HARNESS_EVENT_LOG is structurally
|
|
8
|
+
* blind to them, unlike the clarify gate which flows through askcard-open. Emitting
|
|
9
|
+
* askcard-open/answered here lets a wake-at-milestone monitor react to the
|
|
10
|
+
* human-approval pause. Observe-only: never throws, no-op when no agent runtime.
|
|
11
|
+
*/
|
|
12
|
+
export declare function emitPreflightHarnessEvent(event: Record<string, unknown>): void;
|
|
3
13
|
export interface RunPreflightOptions {
|
|
4
14
|
repoEmpty?: boolean;
|
|
5
15
|
researchOverridable?: boolean;
|
|
@@ -1,4 +1,24 @@
|
|
|
1
1
|
import { phaseDone, phaseStart } from "./phase-events.js";
|
|
2
|
+
/**
|
|
3
|
+
* Emit a harness LiveEvent for a preflight/plan-confirm approval gate.
|
|
4
|
+
*
|
|
5
|
+
* These gates are surfaced as `council_preflight` StreamChunks (a UI render path),
|
|
6
|
+
* NOT as LiveEvents — so a monitor on MUONROI_HARNESS_EVENT_LOG is structurally
|
|
7
|
+
* blind to them, unlike the clarify gate which flows through askcard-open. Emitting
|
|
8
|
+
* askcard-open/answered here lets a wake-at-milestone monitor react to the
|
|
9
|
+
* human-approval pause. Observe-only: never throws, no-op when no agent runtime.
|
|
10
|
+
*/
|
|
11
|
+
export function emitPreflightHarnessEvent(event) {
|
|
12
|
+
try {
|
|
13
|
+
const ar = globalThis.__muonroiAgentRuntime;
|
|
14
|
+
if (!ar || typeof ar.emitEvent !== "function")
|
|
15
|
+
return;
|
|
16
|
+
ar.emitEvent(event);
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
/* observe-only */
|
|
20
|
+
}
|
|
21
|
+
}
|
|
2
22
|
export async function* runPreflight(spec, participants, researchNeeded, respondToPreflight, options) {
|
|
3
23
|
const preflightId = crypto.randomUUID();
|
|
4
24
|
const startedAt = Date.now();
|
|
@@ -50,6 +70,15 @@ export async function* runPreflight(spec, participants, researchNeeded, respondT
|
|
|
50
70
|
});
|
|
51
71
|
return true;
|
|
52
72
|
}
|
|
73
|
+
emitPreflightHarnessEvent({
|
|
74
|
+
t: "event",
|
|
75
|
+
kind: "askcard-open",
|
|
76
|
+
questionId: preflightId,
|
|
77
|
+
question: `Approve discussion plan for: ${spec.problemStatement}`,
|
|
78
|
+
phase: "preflight",
|
|
79
|
+
optionCount: 2,
|
|
80
|
+
defaultIndex: 0,
|
|
81
|
+
});
|
|
53
82
|
yield {
|
|
54
83
|
type: "council_preflight",
|
|
55
84
|
content: "Review the discussion brief above. Approve to start debate, or reject to revise.",
|
|
@@ -66,6 +95,13 @@ export async function* runPreflight(spec, participants, researchNeeded, respondT
|
|
|
66
95
|
},
|
|
67
96
|
};
|
|
68
97
|
const approved = await respondToPreflight(preflightId);
|
|
98
|
+
emitPreflightHarnessEvent({
|
|
99
|
+
t: "event",
|
|
100
|
+
kind: "askcard-answered",
|
|
101
|
+
questionId: preflightId,
|
|
102
|
+
answerKind: "choice",
|
|
103
|
+
answerText: approved ? "approve" : "reject",
|
|
104
|
+
});
|
|
69
105
|
yield phaseDone({
|
|
70
106
|
phaseId: "phase:preflight",
|
|
71
107
|
kind: "preflight",
|
|
@@ -2,7 +2,15 @@ import type { ClarifiedSpec, DebatePlan, DebateStance, OutputShape } from "./typ
|
|
|
2
2
|
export declare function buildClarificationPrompt(topic: string, conversationContext: string, previousQA?: Array<{
|
|
3
3
|
question: string;
|
|
4
4
|
answer: string;
|
|
5
|
-
}
|
|
5
|
+
}>,
|
|
6
|
+
/**
|
|
7
|
+
* Interview language, already resolved by the caller (runClarification): a
|
|
8
|
+
* concrete language name ("vietnamese", "english", a pinned locale) or "auto".
|
|
9
|
+
* Feeds the shared `buildLanguageRule` so the cards follow the SAME language
|
|
10
|
+
* policy as the debate — the user's conversation language, not English-by-
|
|
11
|
+
* default. Defaults to "auto" so existing no-arg callers keep auto-detection.
|
|
12
|
+
*/
|
|
13
|
+
lang?: string): {
|
|
6
14
|
system: string;
|
|
7
15
|
prompt: string;
|
|
8
16
|
};
|
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
import { buildStackLockSection } from "./decisions-lock.js";
|
|
2
2
|
// ── Clarification prompts ────────────────────────────────────────────────────
|
|
3
|
-
export function buildClarificationPrompt(topic, conversationContext, previousQA
|
|
3
|
+
export function buildClarificationPrompt(topic, conversationContext, previousQA,
|
|
4
|
+
/**
|
|
5
|
+
* Interview language, already resolved by the caller (runClarification): a
|
|
6
|
+
* concrete language name ("vietnamese", "english", a pinned locale) or "auto".
|
|
7
|
+
* Feeds the shared `buildLanguageRule` so the cards follow the SAME language
|
|
8
|
+
* policy as the debate — the user's conversation language, not English-by-
|
|
9
|
+
* default. Defaults to "auto" so existing no-arg callers keep auto-detection.
|
|
10
|
+
*/
|
|
11
|
+
lang = "auto") {
|
|
4
12
|
const qaSection = previousQA?.length
|
|
5
13
|
? `\n\n## Already Clarified\n${previousQA.map((qa) => `Q: ${qa.question}\nA: ${qa.answer}`).join("\n\n")}`
|
|
6
14
|
: "";
|
|
@@ -12,6 +20,16 @@ export function buildClarificationPrompt(topic, conversationContext, previousQA)
|
|
|
12
20
|
`- SCOPE: what is in/out of scope for THIS change?\n` +
|
|
13
21
|
`- CONSTRAINTS: hard technical/time/business constraints not already implied by the context.\n` +
|
|
14
22
|
`- SUCCESS CRITERIA: how "done" is judged, when it isn't already obvious.\n\n` +
|
|
23
|
+
`## Ground your questions in the research (IMPORTANT)\n` +
|
|
24
|
+
`If a "## Scope Research" section is present, it is evidence gathered about THIS topic before you ` +
|
|
25
|
+
`were asked to clarify. Read it first, then use it to ask SHARPER, better-grounded questions — ` +
|
|
26
|
+
`research makes your questions more pointed, it does NOT reduce the need to ask. Treat the research's ` +
|
|
27
|
+
`own guesses/speculation as UNVERIFIED, never as the user's answer: if the research had to assume ` +
|
|
28
|
+
`what the user actually wants (their real goal, the concrete task, the system of record, the target ` +
|
|
29
|
+
`user), that assumption IS the gray area you must confirm with them — do not adopt it silently. ` +
|
|
30
|
+
`In particular, when the request is vague and the research could only speculate about the core goal, ` +
|
|
31
|
+
`asking the user to name that goal/pain concretely is the single highest-value question — ask it. ` +
|
|
32
|
+
`Only skip a question when the research (or the topic/context) settles it with real evidence, not a guess.\n\n` +
|
|
15
33
|
`## How many questions\n` +
|
|
16
34
|
`Ask the minimum that unblocks a focused discussion — typically 0-2. A well-scoped topic, or one ` +
|
|
17
35
|
`whose context already answers the gaps, needs ZERO questions: return []. Do NOT pad to a quota, ` +
|
|
@@ -36,11 +54,10 @@ export function buildClarificationPrompt(topic, conversationContext, previousQA)
|
|
|
36
54
|
`the project IS the one described in the "## Current Project" section of the context. DO NOT ask which project.\n` +
|
|
37
55
|
`- Only ask about project identity when the topic mentions multiple distinct projects or external products.\n` +
|
|
38
56
|
`- Use the project's package.json name and description as implicit context for follow-up questions.\n\n` +
|
|
39
|
-
|
|
40
|
-
`
|
|
41
|
-
`
|
|
42
|
-
`
|
|
43
|
-
`NOT default to English. Keep code identifiers, file paths, tech/product names, and JSON keys in English.\n\n` +
|
|
57
|
+
buildLanguageRule(lang) +
|
|
58
|
+
`The user reads and answers these on a card, so the "question", "why", and every option ` +
|
|
59
|
+
`"label"/"description" MUST be in that language — never default to English for card text, and ` +
|
|
60
|
+
`do NOT mirror the language of any "## Scope Research"/context block; follow the user's language.\n\n` +
|
|
44
61
|
`Output ONLY a JSON array (no markdown, no preamble):\n` +
|
|
45
62
|
`[{"question": "...", "why": "why this matters for a focused discussion", "options": [` +
|
|
46
63
|
`{"label": "option A", "description": "what picking this means and its trade-off — one line", "recommended": true}, ` +
|
|
@@ -313,6 +313,16 @@ export interface CouncilConfig {
|
|
|
313
313
|
* responder the clarifier + post-debate askcards use.
|
|
314
314
|
*/
|
|
315
315
|
respondToQuestion?: QuestionResponder;
|
|
316
|
+
/**
|
|
317
|
+
* convene_council path — when true, the mid-debate escalation askcard
|
|
318
|
+
* (runEscalationPrompt) is auto-accepted WITHOUT emitting a blocking
|
|
319
|
+
* council_question card. The convene tool runs the council autonomously
|
|
320
|
+
* mid-agent-turn: there is no interactive user answering the escalation, so a
|
|
321
|
+
* card would hang the tool call. Auto-accept = conclude with the best
|
|
322
|
+
* synthesis so far. No decision is hardcoded post-synthesis — the calling
|
|
323
|
+
* agent decides what to do with the returned conclusion.
|
|
324
|
+
*/
|
|
325
|
+
convenePath?: boolean;
|
|
316
326
|
/**
|
|
317
327
|
* C (mid-debate checkpoint) — directory to persist the per-round debate
|
|
318
328
|
* checkpoint (`debate-checkpoint.json`), normally the run dir
|
package/dist/src/ee/auth.d.ts
CHANGED
|
@@ -25,6 +25,25 @@ export declare function refreshAuthToken(opts?: {
|
|
|
25
25
|
export declare function writeExperienceConfig(patch: Partial<ExperienceConfig>, opts?: {
|
|
26
26
|
home?: string;
|
|
27
27
|
}): Promise<void>;
|
|
28
|
+
/** Where the EE config lives, for display in setup/status output. */
|
|
29
|
+
export declare function getExperienceConfigPath(opts?: {
|
|
30
|
+
home?: string;
|
|
31
|
+
}): string;
|
|
32
|
+
/**
|
|
33
|
+
* Best-effort reachability probe against an EE server's /health.
|
|
34
|
+
*
|
|
35
|
+
* Returns a reason on failure rather than a bare boolean: "saved but not
|
|
36
|
+
* reachable" and "saved, server says 401" are different problems for the user
|
|
37
|
+
* to act on, and collapsing them to `false` hides which one they have.
|
|
38
|
+
* Never throws — a probe is diagnostics, and must not fail a config write that
|
|
39
|
+
* already succeeded.
|
|
40
|
+
*/
|
|
41
|
+
export declare function probeEEHealth(baseUrl: string, token?: string, opts?: {
|
|
42
|
+
timeoutMs?: number;
|
|
43
|
+
}): Promise<{
|
|
44
|
+
ok: boolean;
|
|
45
|
+
detail: string;
|
|
46
|
+
}>;
|
|
28
47
|
export declare function getCachedAuthToken(): string | null;
|
|
29
48
|
export declare function getEmbeddingModelVersion(): string;
|
|
30
49
|
export declare function getCachedServerBaseUrl(): string | null;
|