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
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* deps are injected so unit tests never spawn a real language server.
|
|
9
9
|
*/
|
|
10
10
|
import { z } from "zod";
|
|
11
|
-
import { LSP_TOOL_OPERATIONS } from "../lsp/types.js";
|
|
11
|
+
import { LSP_TOOL_OPERATIONS, } from "../lsp/types.js";
|
|
12
12
|
function ok(data) {
|
|
13
13
|
return { content: [{ type: "text", text: JSON.stringify(data) }] };
|
|
14
14
|
}
|
|
@@ -18,6 +18,17 @@ function fail(error, message) {
|
|
|
18
18
|
isError: true,
|
|
19
19
|
};
|
|
20
20
|
}
|
|
21
|
+
/** Map a thrown error to the LspError tagged-union shape. */
|
|
22
|
+
function mapLspError(e) {
|
|
23
|
+
if (e && typeof e === "object" && "kind" in e && "message" in e) {
|
|
24
|
+
const lspErr = e;
|
|
25
|
+
return { error: lspErr.kind, message: lspErr.message };
|
|
26
|
+
}
|
|
27
|
+
return {
|
|
28
|
+
error: "lsp_error",
|
|
29
|
+
message: e instanceof Error ? e.message : String(e),
|
|
30
|
+
};
|
|
31
|
+
}
|
|
21
32
|
async function defaultQuery(cwd, input) {
|
|
22
33
|
const { queryLsp } = await import("../lsp/runtime.js");
|
|
23
34
|
return queryLsp(cwd, input);
|
|
@@ -26,9 +37,29 @@ async function defaultEnabled(cwd) {
|
|
|
26
37
|
const { isLspToolEnabled } = await import("../lsp/runtime.js");
|
|
27
38
|
return isLspToolEnabled(cwd);
|
|
28
39
|
}
|
|
40
|
+
async function defaultWaitForDiagnostics(cwd, filePath, timeout = 1500) {
|
|
41
|
+
const { getOrCreateManager } = await import("../lsp/runtime.js");
|
|
42
|
+
return getOrCreateManager(cwd).waitForDiagnostics(filePath, timeout);
|
|
43
|
+
}
|
|
44
|
+
async function defaultImpactOfChange(cwd, filePath, query) {
|
|
45
|
+
const { getOrCreateManager } = await import("../lsp/runtime.js");
|
|
46
|
+
return getOrCreateManager(cwd).impactOfChange(filePath, query);
|
|
47
|
+
}
|
|
48
|
+
async function defaultMutationPreview(cwd, filePath, change) {
|
|
49
|
+
const { getOrCreateManager } = await import("../lsp/runtime.js");
|
|
50
|
+
return getOrCreateManager(cwd).lspMutationPreview(filePath, change);
|
|
51
|
+
}
|
|
52
|
+
async function defaultBeforeGrep(cwd, filePath, query) {
|
|
53
|
+
const { getOrCreateManager } = await import("../lsp/runtime.js");
|
|
54
|
+
return getOrCreateManager(cwd).lspBeforeGrep(filePath, query);
|
|
55
|
+
}
|
|
29
56
|
export function registerLspTools(server, deps = {}) {
|
|
30
57
|
const query = deps.query ?? defaultQuery;
|
|
31
58
|
const enabled = deps.enabled ?? defaultEnabled;
|
|
59
|
+
const waitForDiagnostics = deps.waitForDiagnostics ?? defaultWaitForDiagnostics;
|
|
60
|
+
const impactOfChange = deps.impactOfChange ?? defaultImpactOfChange;
|
|
61
|
+
const mutationPreview = deps.lspMutationPreview ?? defaultMutationPreview;
|
|
62
|
+
const lspBeforeGrep = deps.lspBeforeGrep ?? defaultBeforeGrep;
|
|
32
63
|
server.registerTool("lsp_query", {
|
|
33
64
|
description: "Semantic code intelligence via language servers. operation is one of: goToDefinition, findReferences, hover, documentSymbol, workspaceSymbol, goToImplementation, prepareCallHierarchy, incomingCalls, outgoingCalls. " +
|
|
34
65
|
"filePath: absolute, or relative to the workspace root (cwd). line/character: 1-based (line 1 = first line, like an editor or file:line reference) — required for position-based ops; omit for documentSymbol; use query (not position) for workspaceSymbol. " +
|
|
@@ -48,7 +79,8 @@ export function registerLspTools(server, deps = {}) {
|
|
|
48
79
|
isEnabled = await enabled(cwd);
|
|
49
80
|
}
|
|
50
81
|
catch (e) {
|
|
51
|
-
|
|
82
|
+
const m = mapLspError(e);
|
|
83
|
+
return fail(m.error, m.message);
|
|
52
84
|
}
|
|
53
85
|
if (!isEnabled) {
|
|
54
86
|
return fail("lsp_disabled", "LSP tool is disabled in settings (lsp.enabled / lsp.tool)");
|
|
@@ -57,9 +89,68 @@ export function registerLspTools(server, deps = {}) {
|
|
|
57
89
|
const resp = await query(cwd, args);
|
|
58
90
|
return ok(resp);
|
|
59
91
|
}
|
|
92
|
+
catch (e) {
|
|
93
|
+
const m = mapLspError(e);
|
|
94
|
+
return fail(m.error, m.message);
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
server.registerTool("lsp_waitForDiagnostics", {
|
|
98
|
+
description: "Wait for LSP diagnostics for a file. Returns { diagnostics, lspStatus, clean, metadata }. " +
|
|
99
|
+
"lspStatus: 'ok'|'partial'|'unavailable'. clean: true when zero error-level diagnostics. " +
|
|
100
|
+
"timeout defaults to 1500ms, max 5000ms. The lsp-before-grep policy allows grep fallback when lspStatus !== 'ok'.",
|
|
101
|
+
inputSchema: {
|
|
102
|
+
operation: z.literal("waitForDiagnostics"),
|
|
103
|
+
filePath: z.string().min(1).max(1000),
|
|
104
|
+
timeout: z.number().int().min(0).optional(),
|
|
105
|
+
},
|
|
106
|
+
}, async (args) => {
|
|
107
|
+
const cwd = process.cwd();
|
|
108
|
+
let isEnabled;
|
|
109
|
+
try {
|
|
110
|
+
isEnabled = await enabled(cwd);
|
|
111
|
+
}
|
|
112
|
+
catch (e) {
|
|
113
|
+
return fail("lsp_error", e instanceof Error ? e.message : String(e));
|
|
114
|
+
}
|
|
115
|
+
if (!isEnabled) {
|
|
116
|
+
return fail("lsp_disabled", "LSP tool is disabled in settings (lsp.enabled / lsp.tool)");
|
|
117
|
+
}
|
|
118
|
+
return ok(await waitForDiagnostics(cwd, args.filePath, args.timeout));
|
|
119
|
+
});
|
|
120
|
+
server.registerTool("lsp_impactOfChange", {
|
|
121
|
+
description: "Composite LSP analysis: returns { references, diagnostics, referencesComplete, safeToRename, clean, " +
|
|
122
|
+
"suggestedGuard, degraded, lspStatus, metadata }. Fans in diagnostics + references + rename safety over the " +
|
|
123
|
+
"frozen union (symbol file + reference files). Grep fallback allowed when lspStatus !== 'ok'.",
|
|
124
|
+
inputSchema: {
|
|
125
|
+
operation: z.literal("impactOfChange"),
|
|
126
|
+
filePath: z.string().min(1).max(1000),
|
|
127
|
+
query: z.string().max(1000).optional(),
|
|
128
|
+
},
|
|
129
|
+
}, async (args) => {
|
|
130
|
+
const cwd = process.cwd();
|
|
131
|
+
let isEnabled;
|
|
132
|
+
try {
|
|
133
|
+
isEnabled = await enabled(cwd);
|
|
134
|
+
}
|
|
60
135
|
catch (e) {
|
|
61
136
|
return fail("lsp_error", e instanceof Error ? e.message : String(e));
|
|
62
137
|
}
|
|
138
|
+
if (!isEnabled) {
|
|
139
|
+
return fail("lsp_disabled", "LSP tool is disabled in settings (lsp.enabled / lsp.tool)");
|
|
140
|
+
}
|
|
141
|
+
return ok(await impactOfChange(cwd, args.filePath, args.query));
|
|
142
|
+
});
|
|
143
|
+
server.registerTool("lsp_mutationPreview", {
|
|
144
|
+
description: "Preview an LSP code mutation (stub). Returns { preview: [] }. " +
|
|
145
|
+
"No side-effects in slice 1. Registered in MUTATION_TOOLS set for routing through the mutation gate.",
|
|
146
|
+
inputSchema: {
|
|
147
|
+
operation: z.literal("mutationPreview"),
|
|
148
|
+
filePath: z.string().min(1).max(1000),
|
|
149
|
+
change: z.string().min(1).max(10000),
|
|
150
|
+
},
|
|
151
|
+
}, async (args) => {
|
|
152
|
+
const cwd = process.cwd();
|
|
153
|
+
return ok(await mutationPreview(cwd, args.filePath, args.change));
|
|
63
154
|
});
|
|
64
155
|
}
|
|
65
156
|
//# sourceMappingURL=lsp-tools.js.map
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* src/mcp/mcp-keychain.ts
|
|
3
3
|
*
|
|
4
|
-
* Per-MCP
|
|
5
|
-
*
|
|
6
|
-
* (
|
|
7
|
-
*
|
|
8
|
-
* Priority: OS keychain (keytar) > environment variable > null.
|
|
4
|
+
* Per-MCP key store, keyed by McpKeyId (MCP servers like Tavily). Backed by the
|
|
5
|
+
* env-store (`.env` file + process.env + Windows registry mirror) — the OS
|
|
6
|
+
* keychain (keytar) has been removed. Reads come straight from process.env.
|
|
9
7
|
*/
|
|
10
8
|
export type McpKeyId = "tavily";
|
|
11
9
|
export declare function setMcpKey(id: McpKeyId, key: string): Promise<boolean>;
|
|
@@ -1,63 +1,24 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* src/mcp/mcp-keychain.ts
|
|
3
3
|
*
|
|
4
|
-
* Per-MCP
|
|
5
|
-
*
|
|
6
|
-
* (
|
|
7
|
-
*
|
|
8
|
-
* Priority: OS keychain (keytar) > environment variable > null.
|
|
4
|
+
* Per-MCP key store, keyed by McpKeyId (MCP servers like Tavily). Backed by the
|
|
5
|
+
* env-store (`.env` file + process.env + Windows registry mirror) — the OS
|
|
6
|
+
* keychain (keytar) has been removed. Reads come straight from process.env.
|
|
9
7
|
*/
|
|
8
|
+
import { clearEnvVar, persistEnvVar } from "../providers/env-store.js";
|
|
10
9
|
import { redactor } from "../utils/redactor.js";
|
|
11
|
-
const KEYCHAIN_SERVICE = "muonroi-cli";
|
|
12
|
-
const ACCOUNT_BY_MCP = {
|
|
13
|
-
tavily: "mcp-tavily",
|
|
14
|
-
};
|
|
15
10
|
const ENV_BY_MCP = {
|
|
16
11
|
tavily: "TAVILY_API_KEY",
|
|
17
12
|
};
|
|
18
13
|
const MIN_KEY_LEN = 16;
|
|
19
|
-
async function loadKeytar() {
|
|
20
|
-
try {
|
|
21
|
-
return (await import("keytar"));
|
|
22
|
-
}
|
|
23
|
-
catch {
|
|
24
|
-
return null;
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
14
|
export async function setMcpKey(id, key) {
|
|
28
15
|
if (!key || key.length < MIN_KEY_LEN) {
|
|
29
16
|
throw new Error(`Key for MCP '${id}' is too short (< ${MIN_KEY_LEN} chars).`);
|
|
30
17
|
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
return false;
|
|
34
|
-
redactor.enrollSecret(key);
|
|
35
|
-
try {
|
|
36
|
-
await kt.setPassword(KEYCHAIN_SERVICE, ACCOUNT_BY_MCP[id], key);
|
|
37
|
-
return true;
|
|
38
|
-
}
|
|
39
|
-
catch (err) {
|
|
40
|
-
// Runtime backend failure (e.g. Linux without libsecret or no active secret service).
|
|
41
|
-
if (process.env.DEBUG || process.env.MUONROI_DEBUG_KEYCHAIN) {
|
|
42
|
-
console.error(`[mcp-keychain] setPassword backend error for ${id}:`, err?.message || err);
|
|
43
|
-
}
|
|
44
|
-
return false;
|
|
45
|
-
}
|
|
18
|
+
persistEnvVar(ENV_BY_MCP[id], key);
|
|
19
|
+
return true;
|
|
46
20
|
}
|
|
47
21
|
export async function getMcpKey(id) {
|
|
48
|
-
const kt = await loadKeytar();
|
|
49
|
-
if (kt) {
|
|
50
|
-
try {
|
|
51
|
-
const k = await kt.getPassword(KEYCHAIN_SERVICE, ACCOUNT_BY_MCP[id]);
|
|
52
|
-
if (k && k.length >= MIN_KEY_LEN) {
|
|
53
|
-
redactor.enrollSecret(k);
|
|
54
|
-
return k;
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
catch {
|
|
58
|
-
/* keytar backend failure → fall through to env */
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
22
|
const envKey = process.env[ENV_BY_MCP[id]];
|
|
62
23
|
if (envKey && envKey.length >= MIN_KEY_LEN) {
|
|
63
24
|
redactor.enrollSecret(envKey);
|
|
@@ -66,9 +27,8 @@ export async function getMcpKey(id) {
|
|
|
66
27
|
return null;
|
|
67
28
|
}
|
|
68
29
|
export async function deleteMcpKey(id) {
|
|
69
|
-
const
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
return kt.deletePassword(KEYCHAIN_SERVICE, ACCOUNT_BY_MCP[id]);
|
|
30
|
+
const had = !!process.env[ENV_BY_MCP[id]];
|
|
31
|
+
clearEnvVar(ENV_BY_MCP[id]);
|
|
32
|
+
return had;
|
|
73
33
|
}
|
|
74
34
|
//# sourceMappingURL=mcp-keychain.js.map
|
|
@@ -6,4 +6,4 @@
|
|
|
6
6
|
* and the muonroi-tools MCP server (src/mcp/tools-server.ts, for external agents).
|
|
7
7
|
* Keeping it here avoids duplicating ~70 lines across the two.
|
|
8
8
|
*/
|
|
9
|
-
export declare const SETUP_GUIDE_TEXT = "# muonroi-cli Setup Guide\n\n## Install (Recommended \u2014 NPM/Bun global install)\nNPM (requires Node >= 20):\n npm install -g muonroi-cli\n\nBun (requires Bun >= 1.3):\n bun add -g muonroi-cli\n\n## Install (Alternative \u2014 Prebuilt standalone binary, requires repo access)\nLinux / macOS:\n curl -fsSL https://raw.githubusercontent.com/muonroi/muonroi-cli/master/install.sh | bash\n\nWindows PowerShell:\n irm https://raw.githubusercontent.com/muonroi/muonroi-cli/master/install.ps1 | iex\n\nThe installers fetch a pre-compiled single binary from GitHub Releases.\n\n## First run\n- Wizard appears automatically.\n- Lists supported providers (DeepSeek + SiliconFlow ready; others via BYOK).\n- Four credential options: paste key, Bitwarden sync (
|
|
9
|
+
export declare const SETUP_GUIDE_TEXT = "# muonroi-cli Setup Guide\n\n## Install (Recommended \u2014 NPM/Bun global install)\nNPM (requires Node >= 20):\n npm install -g muonroi-cli\n\nBun (requires Bun >= 1.3):\n bun add -g muonroi-cli\n\n## Install (Alternative \u2014 Prebuilt standalone binary, requires repo access)\nLinux / macOS:\n curl -fsSL https://raw.githubusercontent.com/muonroi/muonroi-cli/master/install.sh | bash\n\nWindows PowerShell:\n irm https://raw.githubusercontent.com/muonroi/muonroi-cli/master/install.ps1 | iex\n\nThe installers fetch a pre-compiled single binary from GitHub Releases.\n\n## First run\n- Wizard appears automatically.\n- Lists supported providers (DeepSeek + SiliconFlow ready; others via BYOK).\n- Four credential options: paste key, Bitwarden sync (bw in /providers), keys export/import (encrypted bundle), or skip for later.\n- Keys land in OS keychain (keytar). Settings written to ~/.muonroi-cli/user-settings.json.\n- Role routing (leader/implement/verify/research) is configured for you.\n\nAfter setup: run `muonroi-cli doctor` to validate.\n\n## Essential commands\n- Interactive TUI: `muonroi-cli` (or `node dist/index.js` after build)\n- Headless one-shot: `muonroi-cli --prompt \"your task\" --max-tool-rounds 8`\n- Health + MCP nudge: `muonroi-cli doctor`\n- Update: `muonroi-cli update` (or set \"autoUpdate\": true in user-settings)\n- Keys move between machines: `muonroi-cli keys export file.json` then import on target\n- Native tools MCP (for external agents): `muonroi-cli tools-mcp` (stdio)\n- Harness driver MCP: `muonroi-cli mcp-driver`\n\n## MCP integration (for Claude Desktop, Cursor, other agents)\nAdd to your MCP client config:\n\n{\n \"mcpServers\": {\n \"muonroi-tools\": {\n \"command\": \"bun\",\n \"args\": [\"run\", \"/absolute/path/to/muonroi-cli/src/index.ts\", \"tools-mcp\"]\n }\n }\n}\n\n(Use absolute path. After `bun run build`: \"node\", \"dist/index.js\", \"tools-mcp\")\n\nThe CLI's OWN inner agent exposes these as NATIVE in-process tools (no MCP self-spawn):\n- setup_guide (this document)\n- ee_query / ee_health / ee_feedback / ee_write \u2014 Experience Engine semantic recall + compaction checkpoints + feedback for learning\n- usage_forensics <id-prefix> \u2014 per-session cost/token forensics (peak input, cache hits, anomalies)\n- lsp_query \u2014 goToDefinition, findReferences, hover, symbols, call hierarchy etc.\n- selfverify_* \u2014 Tier-1 heuristic + Tier-2 agentic self-QA harness runs (start/poll/result/cancel/list)\n\n## Experience Engine over MCP (other agents)\n\nThe ee_* tools above are native to this CLI's agent. To give ANOTHER agent\n(Claude Code, Codex, Gemini CLI\u2026) the same brain, install the engine and register\nits own MCP server \u2014 no muonroi-cli needed:\n\n npm i -g @muonroi/experience-engine\n claude mcp add experience-engine -- exp-mcp\n\nOr in any mcpServers config:\n\n{\n \"mcpServers\": {\n \"experience-engine\": { \"command\": \"exp-mcp\" }\n }\n}\n\nThe \"muonroi-tools\" server above deliberately does NOT serve ee_* \u2014 same brain,\none implementation, reachable without installing a CLI you have no use for.\n\nFor BB/.NET template recipes and package docs, also connect an external \"muonroi-docs\" MCP server if available (provides docs_search + setup_guide for the templates).\n\n## Development\ngit clone https://github.com/muonroi/muonroi-cli.git\ncd muonroi-cli && bun install\n\nbun run dev # run from source (TUI)\nbun run typecheck # tsc --noEmit\nbun run test # vitest (unit + headless)\nbunx vitest -c vitest.harness.config.ts run tests/harness/ # TUI E2E (named-pipes on Win, fd3/4 on POSIX)\nbun run build # or build:binary for standalone exe\n\nSee AGENTS.md (quick ref + rules), CLAUDE.md (harness verification), README.md.\n\n## Verify\nmuonroi-cli doctor\n# Checks runtimes, catalog load, keychain, MCP servers enabled, council research MCP nudge, EE reachability, recent error rate.\n# Any \"warn\" entries tell you exactly what to enable (e.g. tavily for web research in council).\n\nFor BB-aware scaffolding (/ideal on a muonroi-building-block target): ensure dotnet SDK + the three Muonroi.*.Template packages are installed via NuGet; doctor surfaces missing feed/template cases.\n";
|
|
@@ -27,7 +27,7 @@ The installers fetch a pre-compiled single binary from GitHub Releases.
|
|
|
27
27
|
## First run
|
|
28
28
|
- Wizard appears automatically.
|
|
29
29
|
- Lists supported providers (DeepSeek + SiliconFlow ready; others via BYOK).
|
|
30
|
-
- Four credential options: paste key, Bitwarden sync (
|
|
30
|
+
- Four credential options: paste key, Bitwarden sync (bw in /providers), keys export/import (encrypted bundle), or skip for later.
|
|
31
31
|
- Keys land in OS keychain (keytar). Settings written to ~/.muonroi-cli/user-settings.json.
|
|
32
32
|
- Role routing (leader/implement/verify/research) is configured for you.
|
|
33
33
|
|
|
@@ -58,11 +58,31 @@ Add to your MCP client config:
|
|
|
58
58
|
|
|
59
59
|
The CLI's OWN inner agent exposes these as NATIVE in-process tools (no MCP self-spawn):
|
|
60
60
|
- setup_guide (this document)
|
|
61
|
-
- ee_query / ee_health / ee_feedback — Experience Engine semantic recall + compaction checkpoints + feedback for learning
|
|
61
|
+
- ee_query / ee_health / ee_feedback / ee_write — Experience Engine semantic recall + compaction checkpoints + feedback for learning
|
|
62
62
|
- usage_forensics <id-prefix> — per-session cost/token forensics (peak input, cache hits, anomalies)
|
|
63
63
|
- lsp_query — goToDefinition, findReferences, hover, symbols, call hierarchy etc.
|
|
64
64
|
- selfverify_* — Tier-1 heuristic + Tier-2 agentic self-QA harness runs (start/poll/result/cancel/list)
|
|
65
65
|
|
|
66
|
+
## Experience Engine over MCP (other agents)
|
|
67
|
+
|
|
68
|
+
The ee_* tools above are native to this CLI's agent. To give ANOTHER agent
|
|
69
|
+
(Claude Code, Codex, Gemini CLI…) the same brain, install the engine and register
|
|
70
|
+
its own MCP server — no muonroi-cli needed:
|
|
71
|
+
|
|
72
|
+
npm i -g @muonroi/experience-engine
|
|
73
|
+
claude mcp add experience-engine -- exp-mcp
|
|
74
|
+
|
|
75
|
+
Or in any mcpServers config:
|
|
76
|
+
|
|
77
|
+
{
|
|
78
|
+
"mcpServers": {
|
|
79
|
+
"experience-engine": { "command": "exp-mcp" }
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
The "muonroi-tools" server above deliberately does NOT serve ee_* — same brain,
|
|
84
|
+
one implementation, reachable without installing a CLI you have no use for.
|
|
85
|
+
|
|
66
86
|
For BB/.NET template recipes and package docs, also connect an external "muonroi-docs" MCP server if available (provides docs_search + setup_guide for the templates).
|
|
67
87
|
|
|
68
88
|
## Development
|
|
@@ -12,6 +12,16 @@
|
|
|
12
12
|
* (src/tools/native-tools.ts) — it no longer self-spawns this server. The two
|
|
13
13
|
* surfaces share their cores (self-verify-runner.ts, setup-guide-text.ts, the
|
|
14
14
|
* ee/forensics/lsp modules) so behaviour is identical.
|
|
15
|
+
*
|
|
16
|
+
* The ee_* tools are deliberately NOT here. The brain is the product and
|
|
17
|
+
* muonroi-cli is one consumer of it, so serving the brain to foreign agents is
|
|
18
|
+
* experience-engine's job, not ours: `npm i -g @muonroi/experience-engine` then
|
|
19
|
+
* `claude mcp add experience-engine -- exp-mcp`. Shipping them from here forced
|
|
20
|
+
* anyone who wanted the brain over MCP to install a whole CLI they had no use
|
|
21
|
+
* for, and left two implementations of the same four tools to drift apart. The
|
|
22
|
+
* CLI's own agent is unaffected — it calls the brain natively via
|
|
23
|
+
* src/ee/search.ts (no MCP hop), which is where ee_query/ee_write/ee_feedback
|
|
24
|
+
* live for us.
|
|
15
25
|
*/
|
|
16
26
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
17
27
|
import { JobManager, type Runner } from "./self-verify-jobs.js";
|
|
@@ -12,11 +12,20 @@
|
|
|
12
12
|
* (src/tools/native-tools.ts) — it no longer self-spawns this server. The two
|
|
13
13
|
* surfaces share their cores (self-verify-runner.ts, setup-guide-text.ts, the
|
|
14
14
|
* ee/forensics/lsp modules) so behaviour is identical.
|
|
15
|
+
*
|
|
16
|
+
* The ee_* tools are deliberately NOT here. The brain is the product and
|
|
17
|
+
* muonroi-cli is one consumer of it, so serving the brain to foreign agents is
|
|
18
|
+
* experience-engine's job, not ours: `npm i -g @muonroi/experience-engine` then
|
|
19
|
+
* `claude mcp add experience-engine -- exp-mcp`. Shipping them from here forced
|
|
20
|
+
* anyone who wanted the brain over MCP to install a whole CLI they had no use
|
|
21
|
+
* for, and left two implementations of the same four tools to drift apart. The
|
|
22
|
+
* CLI's own agent is unaffected — it calls the brain natively via
|
|
23
|
+
* src/ee/search.ts (no MCP hop), which is where ee_query/ee_write/ee_feedback
|
|
24
|
+
* live for us.
|
|
15
25
|
*/
|
|
16
26
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
17
27
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
18
28
|
import { z } from "zod";
|
|
19
|
-
import { registerEETools } from "./ee-tools.js";
|
|
20
29
|
import { registerForensicsTools } from "./forensics-tools.js";
|
|
21
30
|
import { registerLspTools } from "./lsp-tools.js";
|
|
22
31
|
import { JobManager } from "./self-verify-jobs.js";
|
|
@@ -129,7 +138,6 @@ export function createToolsServer(runner = defaultRunner) {
|
|
|
129
138
|
const server = new McpServer({ name: "muonroi-tools", version: "0.1.0" });
|
|
130
139
|
const jm = new JobManager(runner);
|
|
131
140
|
registerSelfVerifyTools(server, jm);
|
|
132
|
-
registerEETools(server);
|
|
133
141
|
registerForensicsTools(server);
|
|
134
142
|
registerLspTools(server);
|
|
135
143
|
registerSetupGuideTool(server);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "2.
|
|
3
|
-
"updated_at": "2026-07-
|
|
2
|
+
"version": "2.15",
|
|
3
|
+
"updated_at": "2026-07-15",
|
|
4
4
|
"description": "Local fallback catalog: deepseek + zai + opencode-go + xai. routing.council multi-provider debate; routing.vision_proxy Z.ai/xai backend for text-only models (deepseek etc.). provider_policies.peak_hour from vendor docs. Pricing APPROXIMATE.",
|
|
5
5
|
"models": [
|
|
6
6
|
{
|
|
@@ -46,10 +46,10 @@
|
|
|
46
46
|
"web_research_kind": null
|
|
47
47
|
},
|
|
48
48
|
{
|
|
49
|
-
"id": "gpt-5.
|
|
50
|
-
"name": "GPT-5.
|
|
49
|
+
"id": "gpt-5.5",
|
|
50
|
+
"name": "GPT-5.5 (OpenAI ChatGPT OAuth)",
|
|
51
51
|
"provider": "openai",
|
|
52
|
-
"tier": "
|
|
52
|
+
"tier": "premium",
|
|
53
53
|
"context_window": 256000,
|
|
54
54
|
"max_output_tokens": 32000,
|
|
55
55
|
"input_price_per_million": 0,
|
|
@@ -57,11 +57,11 @@
|
|
|
57
57
|
"reasoning": true,
|
|
58
58
|
"thinking_type": "enabled",
|
|
59
59
|
"supports_effort": true,
|
|
60
|
-
"default_reasoning_effort": "
|
|
61
|
-
"description": "OpenAI GPT-5.
|
|
62
|
-
"aliases": ["gpt-5.
|
|
60
|
+
"default_reasoning_effort": "high",
|
|
61
|
+
"description": "OpenAI GPT-5.5 — frontier model for complex coding, research, and real-world work. Via ChatGPT subscription OAuth (chatgpt.com/backend-api/codex, Responses API). Subscription-billed — per-token price N/A (0 placeholder). Added to local fallback catalog so OAuth-granted OpenAI models resolve when the CP catalog endpoint is unreachable.",
|
|
62
|
+
"aliases": ["gpt-5.5-oauth"],
|
|
63
63
|
"supports_vision": false,
|
|
64
|
-
"roles": ["
|
|
64
|
+
"roles": ["leader", "implement", "verify", "research"],
|
|
65
65
|
"native_web_research": true,
|
|
66
66
|
"web_research_kind": "web-tool"
|
|
67
67
|
},
|
|
@@ -69,7 +69,7 @@
|
|
|
69
69
|
"id": "gpt-5.4",
|
|
70
70
|
"name": "GPT-5.4 (OpenAI ChatGPT OAuth)",
|
|
71
71
|
"provider": "openai",
|
|
72
|
-
"tier": "
|
|
72
|
+
"tier": "balanced",
|
|
73
73
|
"context_window": 256000,
|
|
74
74
|
"max_output_tokens": 32000,
|
|
75
75
|
"input_price_per_million": 0,
|
|
@@ -78,18 +78,18 @@
|
|
|
78
78
|
"thinking_type": "enabled",
|
|
79
79
|
"supports_effort": true,
|
|
80
80
|
"default_reasoning_effort": "medium",
|
|
81
|
-
"description": "OpenAI GPT-5.4
|
|
81
|
+
"description": "OpenAI GPT-5.4 — strong model for everyday coding. Via ChatGPT subscription OAuth (chatgpt.com/backend-api/codex, Responses API). Subscription-billed — per-token price N/A (0 placeholder). Added to local fallback catalog so OAuth-granted OpenAI models resolve when the CP catalog endpoint is unreachable.",
|
|
82
82
|
"aliases": ["gpt-5.4-oauth"],
|
|
83
83
|
"supports_vision": false,
|
|
84
|
-
"roles": ["
|
|
84
|
+
"roles": ["implement", "verify", "research"],
|
|
85
85
|
"native_web_research": true,
|
|
86
86
|
"web_research_kind": "web-tool"
|
|
87
87
|
},
|
|
88
88
|
{
|
|
89
|
-
"id": "gpt-5.
|
|
90
|
-
"name": "GPT-5.
|
|
89
|
+
"id": "gpt-5.4-mini",
|
|
90
|
+
"name": "GPT-5.4 mini (OpenAI ChatGPT OAuth)",
|
|
91
91
|
"provider": "openai",
|
|
92
|
-
"tier": "
|
|
92
|
+
"tier": "fast",
|
|
93
93
|
"context_window": 256000,
|
|
94
94
|
"max_output_tokens": 32000,
|
|
95
95
|
"input_price_per_million": 0,
|
|
@@ -97,11 +97,11 @@
|
|
|
97
97
|
"reasoning": true,
|
|
98
98
|
"thinking_type": "enabled",
|
|
99
99
|
"supports_effort": true,
|
|
100
|
-
"default_reasoning_effort": "
|
|
101
|
-
"description": "OpenAI GPT-5.
|
|
102
|
-
"aliases": ["gpt-5.
|
|
100
|
+
"default_reasoning_effort": "low",
|
|
101
|
+
"description": "OpenAI GPT-5.4 mini — small, fast, and cost-efficient model for simpler coding tasks. Via ChatGPT subscription OAuth (chatgpt.com/backend-api/codex, Responses API). Subscription-billed — per-token price N/A (0 placeholder). Added to local fallback catalog so OAuth-granted OpenAI models resolve when the CP catalog endpoint is unreachable.",
|
|
102
|
+
"aliases": ["gpt-5.4-mini-oauth"],
|
|
103
103
|
"supports_vision": false,
|
|
104
|
-
"roles": ["implement", "verify"],
|
|
104
|
+
"roles": ["research", "implement", "verify"],
|
|
105
105
|
"native_web_research": true,
|
|
106
106
|
"web_research_kind": "web-tool"
|
|
107
107
|
},
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* src/orchestrator/ask-user.ts
|
|
3
|
+
*
|
|
4
|
+
* Types + pure helpers for the model-callable `ask_user` tool.
|
|
5
|
+
*
|
|
6
|
+
* The agent calls `ask_user` from its OWN intent — typically after a
|
|
7
|
+
* `convene_council` conclusion, when it is in an implementation discussion and
|
|
8
|
+
* wants the human's go/no-go before building. Per the hard no-hardcode
|
|
9
|
+
* directive, the CLI does NOT synthesise the option set or decide the branch:
|
|
10
|
+
* the question, options, and default ALL come from the agent's tool input. The
|
|
11
|
+
* CLI only renders the card and returns the human's answer AS the tool result
|
|
12
|
+
* string; the agent decides what to do next.
|
|
13
|
+
*
|
|
14
|
+
* This mirrors the blocking safety-override askcard plumbing
|
|
15
|
+
* (setSafetyOverrideHandler → resolver ref-map → answer/cancel drain) but the
|
|
16
|
+
* card content is agent-supplied, never CLI-built.
|
|
17
|
+
*/
|
|
18
|
+
import type { CouncilQuestionData } from "../types/index.js";
|
|
19
|
+
/** One agent-supplied choice. `value` defaults to `label` when omitted. */
|
|
20
|
+
export interface AskUserOption {
|
|
21
|
+
label: string;
|
|
22
|
+
description?: string;
|
|
23
|
+
value?: string;
|
|
24
|
+
}
|
|
25
|
+
/** The agent's request payload (validated from the tool input). */
|
|
26
|
+
export interface AskUserAskInfo {
|
|
27
|
+
/** The question to put to the human. Required. */
|
|
28
|
+
question: string;
|
|
29
|
+
/** Optional short context/detail shown under the question. */
|
|
30
|
+
context?: string;
|
|
31
|
+
/**
|
|
32
|
+
* Agent-supplied choices. When empty/omitted the card is a free-text prompt.
|
|
33
|
+
* The CLI NEVER injects its own options.
|
|
34
|
+
*/
|
|
35
|
+
options?: AskUserOption[];
|
|
36
|
+
/**
|
|
37
|
+
* Agent-supplied default selection index into `options`. Clamped into range;
|
|
38
|
+
* defaults to a neutral 0 (the agent's FIRST option) — index 0 is NOT a CLI
|
|
39
|
+
* recommendation, just first-listed.
|
|
40
|
+
*/
|
|
41
|
+
defaultIndex?: number;
|
|
42
|
+
}
|
|
43
|
+
/** Sentinel returned to the agent when the human dismisses the card (Esc). */
|
|
44
|
+
export declare const ASK_USER_DISMISSED = "(the user dismissed the question without answering)";
|
|
45
|
+
/**
|
|
46
|
+
* Resolve the agent-facing answer string for a chosen option index (or free
|
|
47
|
+
* text). Returns the option's `value` (falling back to its `label`), the raw
|
|
48
|
+
* free-text, or the dismissed sentinel. This is the value the AI-SDK
|
|
49
|
+
* `execute()` returns — the agent reads it and decides the follow-up.
|
|
50
|
+
*/
|
|
51
|
+
export declare function resolveAskUserAnswer(info: AskUserAskInfo, choice: {
|
|
52
|
+
index?: number;
|
|
53
|
+
text?: string;
|
|
54
|
+
cancelled?: boolean;
|
|
55
|
+
}): string;
|
|
56
|
+
/**
|
|
57
|
+
* Build the CouncilQuestionData card from the agent's request. Options come
|
|
58
|
+
* ONLY from `info.options`; with none, the card is a single free-text field.
|
|
59
|
+
* `defaultIndex` is clamped into range (neutral 0 otherwise).
|
|
60
|
+
*/
|
|
61
|
+
export declare function buildAskUserQuestion(info: AskUserAskInfo, questionId: string): CouncilQuestionData;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* src/orchestrator/ask-user.ts
|
|
3
|
+
*
|
|
4
|
+
* Types + pure helpers for the model-callable `ask_user` tool.
|
|
5
|
+
*
|
|
6
|
+
* The agent calls `ask_user` from its OWN intent — typically after a
|
|
7
|
+
* `convene_council` conclusion, when it is in an implementation discussion and
|
|
8
|
+
* wants the human's go/no-go before building. Per the hard no-hardcode
|
|
9
|
+
* directive, the CLI does NOT synthesise the option set or decide the branch:
|
|
10
|
+
* the question, options, and default ALL come from the agent's tool input. The
|
|
11
|
+
* CLI only renders the card and returns the human's answer AS the tool result
|
|
12
|
+
* string; the agent decides what to do next.
|
|
13
|
+
*
|
|
14
|
+
* This mirrors the blocking safety-override askcard plumbing
|
|
15
|
+
* (setSafetyOverrideHandler → resolver ref-map → answer/cancel drain) but the
|
|
16
|
+
* card content is agent-supplied, never CLI-built.
|
|
17
|
+
*/
|
|
18
|
+
/** Sentinel returned to the agent when the human dismisses the card (Esc). */
|
|
19
|
+
export const ASK_USER_DISMISSED = "(the user dismissed the question without answering)";
|
|
20
|
+
/**
|
|
21
|
+
* Resolve the agent-facing answer string for a chosen option index (or free
|
|
22
|
+
* text). Returns the option's `value` (falling back to its `label`), the raw
|
|
23
|
+
* free-text, or the dismissed sentinel. This is the value the AI-SDK
|
|
24
|
+
* `execute()` returns — the agent reads it and decides the follow-up.
|
|
25
|
+
*/
|
|
26
|
+
export function resolveAskUserAnswer(info, choice) {
|
|
27
|
+
if (choice.cancelled)
|
|
28
|
+
return ASK_USER_DISMISSED;
|
|
29
|
+
if (typeof choice.text === "string" && choice.text.length > 0)
|
|
30
|
+
return choice.text;
|
|
31
|
+
const opts = info.options ?? [];
|
|
32
|
+
if (typeof choice.index === "number" && choice.index >= 0 && choice.index < opts.length) {
|
|
33
|
+
const opt = opts[choice.index];
|
|
34
|
+
return opt.value ?? opt.label;
|
|
35
|
+
}
|
|
36
|
+
return ASK_USER_DISMISSED;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Build the CouncilQuestionData card from the agent's request. Options come
|
|
40
|
+
* ONLY from `info.options`; with none, the card is a single free-text field.
|
|
41
|
+
* `defaultIndex` is clamped into range (neutral 0 otherwise).
|
|
42
|
+
*/
|
|
43
|
+
export function buildAskUserQuestion(info, questionId) {
|
|
44
|
+
const rawOptions = info.options ?? [];
|
|
45
|
+
const options = rawOptions.map((o) => ({
|
|
46
|
+
label: o.label,
|
|
47
|
+
value: o.value ?? o.label,
|
|
48
|
+
kind: "choice",
|
|
49
|
+
description: o.description,
|
|
50
|
+
}));
|
|
51
|
+
const hasOptions = options.length > 0;
|
|
52
|
+
const clampedDefault = hasOptions ? Math.min(Math.max(0, info.defaultIndex ?? 0), options.length - 1) : 0;
|
|
53
|
+
return {
|
|
54
|
+
questionId,
|
|
55
|
+
question: info.question,
|
|
56
|
+
context: info.context,
|
|
57
|
+
isRequired: true,
|
|
58
|
+
phase: "ask-user",
|
|
59
|
+
// No options → a free-text prompt (single freetext field); otherwise the
|
|
60
|
+
// agent-supplied choices, verbatim.
|
|
61
|
+
options: hasOptions ? options : [{ label: "Your answer", value: "", kind: "freetext" }],
|
|
62
|
+
defaultIndex: clampedDefault,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=ask-user.js.map
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { type ModelMessage } from "ai";
|
|
2
|
-
import type { ProviderFactory as LegacyProvider } from "../providers/runtime.js";
|
|
3
2
|
/**
|
|
4
3
|
* A single action the compaction proposer model wants to take on one message.
|
|
5
4
|
*/
|
|
@@ -21,7 +20,7 @@ export interface CompactionProposal {
|
|
|
21
20
|
* The model sees the current messages and decides whether compaction is needed at all.
|
|
22
21
|
* Returns null if the proposer call fails (caller should fall back gracefully).
|
|
23
22
|
*/
|
|
24
|
-
export declare function proposeCompaction(
|
|
23
|
+
export declare function proposeCompaction(modelId: string, messages: ModelMessage[], signal?: AbortSignal): Promise<CompactionProposal | null>;
|
|
25
24
|
export interface CompactionSettings {
|
|
26
25
|
reserveTokens: number;
|
|
27
26
|
keepRecentTokens: number;
|
|
@@ -78,4 +77,4 @@ export interface CompactionSummaryResult {
|
|
|
78
77
|
summary: string;
|
|
79
78
|
usage: CompactionUsage;
|
|
80
79
|
}
|
|
81
|
-
export declare function generateCompactionSummary(
|
|
80
|
+
export declare function generateCompactionSummary(modelId: string, preparation: PreparedCompaction, customInstructions?: string, signal?: AbortSignal): Promise<CompactionSummaryResult>;
|
|
@@ -11,9 +11,9 @@ import { countTokens } from "./token-counter.js";
|
|
|
11
11
|
* The model sees the current messages and decides whether compaction is needed at all.
|
|
12
12
|
* Returns null if the proposer call fails (caller should fall back gracefully).
|
|
13
13
|
*/
|
|
14
|
-
export async function proposeCompaction(
|
|
14
|
+
export async function proposeCompaction(modelId, messages, signal) {
|
|
15
15
|
try {
|
|
16
|
-
const runtime = resolveModelRuntime(
|
|
16
|
+
const runtime = resolveModelRuntime(modelId);
|
|
17
17
|
const compactCaps = getProviderCapabilities(requireRuntimeProvider(runtime));
|
|
18
18
|
const serialized = serializeConversation(messages);
|
|
19
19
|
const result = await generateText({
|
|
@@ -517,7 +517,7 @@ function readUsage(usage) {
|
|
|
517
517
|
: 0;
|
|
518
518
|
return { promptTokens: prompt, completionTokens: completion };
|
|
519
519
|
}
|
|
520
|
-
async function summarizeConversation(
|
|
520
|
+
async function summarizeConversation(modelId, messages, reserveTokens, customInstructions, previousSummary, promptOverride, signal) {
|
|
521
521
|
const serialized = serializeConversation(messages);
|
|
522
522
|
const promptParts = [serialized];
|
|
523
523
|
// Improvement from session df2dbb878984 analysis: detect meta-analysis (self-evaluation of agent/PIL)
|
|
@@ -535,7 +535,7 @@ async function summarizeConversation(provider, modelId, messages, reserveTokens,
|
|
|
535
535
|
if (customInstructions?.trim()) {
|
|
536
536
|
promptParts.push(`Additional focus: ${customInstructions.trim()}`);
|
|
537
537
|
}
|
|
538
|
-
const runtime = resolveModelRuntime(
|
|
538
|
+
const runtime = resolveModelRuntime(modelId);
|
|
539
539
|
const compactCaps = getProviderCapabilities(requireRuntimeProvider(runtime));
|
|
540
540
|
const result = await generateText({
|
|
541
541
|
model: runtime.model,
|
|
@@ -558,14 +558,14 @@ function addUsage(a, b) {
|
|
|
558
558
|
completionTokens: a.completionTokens + b.completionTokens,
|
|
559
559
|
};
|
|
560
560
|
}
|
|
561
|
-
export async function generateCompactionSummary(
|
|
561
|
+
export async function generateCompactionSummary(modelId, preparation, customInstructions, signal) {
|
|
562
562
|
const { messagesToSummarize, turnPrefixMessages, isSplitTurn, previousSummary, settings } = preparation;
|
|
563
563
|
if (isSplitTurn && turnPrefixMessages.length > 0) {
|
|
564
564
|
const [historyResult, prefixResult] = await Promise.all([
|
|
565
565
|
messagesToSummarize.length > 0
|
|
566
|
-
? summarizeConversation(
|
|
566
|
+
? summarizeConversation(modelId, messagesToSummarize, settings.reserveTokens, customInstructions, previousSummary, undefined, signal)
|
|
567
567
|
: Promise.resolve({ summary: previousSummary?.trim() || "", usage: emptyUsage() }),
|
|
568
|
-
summarizeConversation(
|
|
568
|
+
summarizeConversation(modelId, turnPrefixMessages, settings.reserveTokens, undefined, undefined, TURN_PREFIX_SUMMARIZATION_PROMPT, signal),
|
|
569
569
|
]);
|
|
570
570
|
const usage = addUsage(historyResult.usage, prefixResult.usage);
|
|
571
571
|
if (historyResult.summary && prefixResult.summary) {
|
|
@@ -573,6 +573,6 @@ export async function generateCompactionSummary(provider, modelId, preparation,
|
|
|
573
573
|
}
|
|
574
574
|
return { summary: (historyResult.summary || prefixResult.summary).trim(), usage };
|
|
575
575
|
}
|
|
576
|
-
return summarizeConversation(
|
|
576
|
+
return summarizeConversation(modelId, messagesToSummarize, settings.reserveTokens, customInstructions, previousSummary, undefined, signal);
|
|
577
577
|
}
|
|
578
578
|
//# sourceMappingURL=compaction.js.map
|