muonroi-cli 1.8.5 → 1.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (206) hide show
  1. package/dist/packages/agent-harness-core/src/event-filter.js +11 -0
  2. package/dist/packages/agent-harness-core/src/event-redact.js +7 -0
  3. package/dist/packages/agent-harness-core/src/event-tee.d.ts +20 -4
  4. package/dist/packages/agent-harness-core/src/event-tee.js +32 -5
  5. package/dist/packages/agent-harness-core/src/mcp-server.d.ts +14 -0
  6. package/dist/packages/agent-harness-core/src/mcp-server.js +86 -10
  7. package/dist/packages/agent-harness-core/src/predicate.d.ts +1 -1
  8. package/dist/packages/agent-harness-core/src/protocol.d.ts +24 -2
  9. package/dist/src/agent-harness/mock-model.d.ts +10 -0
  10. package/dist/src/agent-harness/mock-model.js +6 -2
  11. package/dist/src/chat/chat-keychain.d.ts +7 -12
  12. package/dist/src/chat/chat-keychain.js +19 -86
  13. package/dist/src/cli/keys.d.ts +8 -45
  14. package/dist/src/cli/keys.js +18 -324
  15. package/dist/src/council/clarifier.d.ts +4 -2
  16. package/dist/src/council/clarifier.js +158 -36
  17. package/dist/src/council/debate-planner.js +3 -2
  18. package/dist/src/council/debate.js +59 -5
  19. package/dist/src/council/index.d.ts +23 -0
  20. package/dist/src/council/index.js +37 -2
  21. package/dist/src/council/llm.d.ts +62 -0
  22. package/dist/src/council/llm.js +123 -23
  23. package/dist/src/council/panel-select.js +13 -3
  24. package/dist/src/council/planner.js +17 -0
  25. package/dist/src/council/preflight.d.ts +10 -0
  26. package/dist/src/council/preflight.js +36 -0
  27. package/dist/src/council/prompts.d.ts +9 -1
  28. package/dist/src/council/prompts.js +23 -6
  29. package/dist/src/council/types.d.ts +10 -0
  30. package/dist/src/ee/auth.d.ts +19 -0
  31. package/dist/src/ee/auth.js +39 -0
  32. package/dist/src/ee/client.js +28 -3
  33. package/dist/src/ee/ee-onboarding.js +6 -26
  34. package/dist/src/flow/compaction/compress.d.ts +2 -2
  35. package/dist/src/flow/compaction/compress.js +21 -8
  36. package/dist/src/flow/compaction/extract.d.ts +3 -3
  37. package/dist/src/flow/compaction/extract.js +6 -6
  38. package/dist/src/flow/compaction/index.d.ts +2 -1
  39. package/dist/src/flow/compaction/index.js +29 -3
  40. package/dist/src/flow/compaction/progress.d.ts +35 -0
  41. package/dist/src/flow/compaction/progress.js +35 -0
  42. package/dist/src/generated/version.d.ts +1 -1
  43. package/dist/src/generated/version.js +1 -1
  44. package/dist/src/gsd/flags.d.ts +11 -0
  45. package/dist/src/gsd/flags.js +19 -0
  46. package/dist/src/gsd/plan-council.js +104 -72
  47. package/dist/src/gsd/verdict-schema.d.ts +1 -1
  48. package/dist/src/headless/council-answers.js +4 -0
  49. package/dist/src/index.js +129 -260
  50. package/dist/src/lsp/builtins.js +3 -1
  51. package/dist/src/lsp/manager.d.ts +5 -1
  52. package/dist/src/lsp/manager.js +249 -3
  53. package/dist/src/lsp/npm-cache.d.ts +11 -1
  54. package/dist/src/lsp/npm-cache.js +17 -1
  55. package/dist/src/lsp/runtime.d.ts +6 -1
  56. package/dist/src/lsp/runtime.js +17 -1
  57. package/dist/src/lsp/types.d.ts +83 -1
  58. package/dist/src/lsp/types.js +10 -0
  59. package/dist/src/mcp/client-pool.js +43 -15
  60. package/dist/src/mcp/lsp-tools.d.ts +5 -1
  61. package/dist/src/mcp/lsp-tools.js +93 -2
  62. package/dist/src/mcp/mcp-keychain.d.ts +3 -5
  63. package/dist/src/mcp/mcp-keychain.js +9 -49
  64. package/dist/src/mcp/setup-guide-text.d.ts +1 -1
  65. package/dist/src/mcp/setup-guide-text.js +22 -2
  66. package/dist/src/mcp/tools-server.d.ts +10 -0
  67. package/dist/src/mcp/tools-server.js +10 -2
  68. package/dist/src/models/catalog.json +19 -19
  69. package/dist/src/orchestrator/ask-user.d.ts +61 -0
  70. package/dist/src/orchestrator/ask-user.js +65 -0
  71. package/dist/src/orchestrator/compaction.d.ts +2 -3
  72. package/dist/src/orchestrator/compaction.js +8 -8
  73. package/dist/src/orchestrator/council-manager.js +9 -8
  74. package/dist/src/orchestrator/council-request.d.ts +49 -0
  75. package/dist/src/orchestrator/council-request.js +62 -0
  76. package/dist/src/orchestrator/interactive-pause.d.ts +26 -0
  77. package/dist/src/orchestrator/interactive-pause.js +36 -0
  78. package/dist/src/orchestrator/message-processor.d.ts +4 -0
  79. package/dist/src/orchestrator/message-processor.js +26 -8
  80. package/dist/src/orchestrator/orchestrator.d.ts +25 -0
  81. package/dist/src/orchestrator/orchestrator.js +204 -50
  82. package/dist/src/orchestrator/preprocessor.js +2 -2
  83. package/dist/src/orchestrator/safety-askcard.d.ts +1 -1
  84. package/dist/src/orchestrator/safety-askcard.js +5 -2
  85. package/dist/src/orchestrator/safety-intercept.d.ts +5 -0
  86. package/dist/src/orchestrator/safety-intercept.js +7 -0
  87. package/dist/src/orchestrator/stall-watchdog.d.ts +8 -1
  88. package/dist/src/orchestrator/stall-watchdog.js +24 -3
  89. package/dist/src/orchestrator/stream-runner.d.ts +13 -3
  90. package/dist/src/orchestrator/stream-runner.js +54 -21
  91. package/dist/src/orchestrator/tool-engine.d.ts +19 -0
  92. package/dist/src/orchestrator/tool-engine.js +241 -25
  93. package/dist/src/orchestrator/turn-watchdog.d.ts +7 -0
  94. package/dist/src/orchestrator/turn-watchdog.js +38 -9
  95. package/dist/src/pil/agent-operating-contract.d.ts +1 -1
  96. package/dist/src/pil/agent-operating-contract.js +6 -4
  97. package/dist/src/pil/discovery.d.ts +1 -1
  98. package/dist/src/pil/discovery.js +2 -2
  99. package/dist/src/pil/layer1_5-complexity-size.d.ts +7 -0
  100. package/dist/src/pil/layer1_5-complexity-size.js +31 -5
  101. package/dist/src/pil/llm-classify.d.ts +78 -3
  102. package/dist/src/pil/llm-classify.js +351 -111
  103. package/dist/src/pil/native-capabilities-workbook.d.ts +1 -1
  104. package/dist/src/pil/native-capabilities-workbook.js +7 -0
  105. package/dist/src/pil/pipeline.js +2 -0
  106. package/dist/src/pil/repo-grounding-probe.d.ts +15 -0
  107. package/dist/src/pil/repo-grounding-probe.js +136 -0
  108. package/dist/src/pil/repo-structure-hints.d.ts +7 -0
  109. package/dist/src/pil/repo-structure-hints.js +45 -0
  110. package/dist/src/product-loop/artifact-io.js +4 -0
  111. package/dist/src/product-loop/criteria-seed.d.ts +51 -0
  112. package/dist/src/product-loop/criteria-seed.js +200 -0
  113. package/dist/src/product-loop/discovery-interview.d.ts +9 -0
  114. package/dist/src/product-loop/discovery-interview.js +37 -18
  115. package/dist/src/product-loop/discovery-recommender.js +2 -1
  116. package/dist/src/product-loop/discovery-schema.js +14 -1
  117. package/dist/src/product-loop/discovery-triage.d.ts +23 -0
  118. package/dist/src/product-loop/discovery-triage.js +109 -0
  119. package/dist/src/product-loop/gather.js +150 -2
  120. package/dist/src/product-loop/index.js +7 -0
  121. package/dist/src/product-loop/loop-driver.js +21 -8
  122. package/dist/src/product-loop/phase-plan.d.ts +16 -0
  123. package/dist/src/product-loop/phase-plan.js +42 -4
  124. package/dist/src/product-loop/phase-rituals.d.ts +3 -0
  125. package/dist/src/product-loop/phase-rituals.js +8 -3
  126. package/dist/src/product-loop/phase-runner.js +30 -11
  127. package/dist/src/product-loop/plan-adherence-review.d.ts +26 -0
  128. package/dist/src/product-loop/plan-adherence-review.js +144 -0
  129. package/dist/src/product-loop/sprint-runner.d.ts +62 -0
  130. package/dist/src/product-loop/sprint-runner.js +309 -8
  131. package/dist/src/product-loop/types.d.ts +25 -0
  132. package/dist/src/providers/anthropic.d.ts +9 -8
  133. package/dist/src/providers/anthropic.js +13 -47
  134. package/dist/src/providers/auth/grok-oauth.d.ts +1 -0
  135. package/dist/src/providers/auth/grok-oauth.js +30 -5
  136. package/dist/src/providers/auth/openai-oauth.d.ts +1 -0
  137. package/dist/src/providers/auth/openai-oauth.js +14 -0
  138. package/dist/src/providers/auth/token-store.d.ts +9 -9
  139. package/dist/src/providers/auth/token-store.js +8 -70
  140. package/dist/src/providers/auth/types.d.ts +8 -0
  141. package/dist/src/providers/env-store.d.ts +17 -0
  142. package/dist/src/providers/env-store.js +228 -0
  143. package/dist/src/providers/keychain.d.ts +21 -17
  144. package/dist/src/providers/keychain.js +124 -135
  145. package/dist/src/providers/runtime.d.ts +24 -9
  146. package/dist/src/providers/runtime.js +48 -37
  147. package/dist/src/providers/strategies/thinking-mode.js +9 -1
  148. package/dist/src/providers/strategies/xai.strategy.js +27 -0
  149. package/dist/src/providers/warm.d.ts +65 -0
  150. package/dist/src/providers/warm.js +145 -0
  151. package/dist/src/self-qa/agentic-loop.js +3 -2
  152. package/dist/src/storage/transcript.js +56 -2
  153. package/dist/src/tools/git-safety.d.ts +19 -0
  154. package/dist/src/tools/git-safety.js +168 -0
  155. package/dist/src/tools/native-tools.d.ts +1 -1
  156. package/dist/src/tools/native-tools.js +76 -1
  157. package/dist/src/tools/registry.d.ts +17 -0
  158. package/dist/src/tools/registry.js +116 -1
  159. package/dist/src/types/index.d.ts +29 -1
  160. package/dist/src/ui/app.js +67 -4
  161. package/dist/src/ui/components/agent-rail-activities.d.ts +26 -0
  162. package/dist/src/ui/components/agent-rail-activities.js +47 -0
  163. package/dist/src/ui/components/compact-progress-card.d.ts +24 -0
  164. package/dist/src/ui/components/compact-progress-card.js +42 -0
  165. package/dist/src/ui/components/council-phase-timeline.js +17 -2
  166. package/dist/src/ui/components/council-question-card.js +1 -0
  167. package/dist/src/ui/components/message-view.d.ts +15 -0
  168. package/dist/src/ui/components/message-view.js +50 -1
  169. package/dist/src/ui/components/tool-group.d.ts +15 -3
  170. package/dist/src/ui/components/tool-group.js +69 -11
  171. package/dist/src/ui/containers/modals-layer.d.ts +2 -1
  172. package/dist/src/ui/containers/modals-layer.js +2 -2
  173. package/dist/src/ui/council-harness-event.d.ts +57 -0
  174. package/dist/src/ui/council-harness-event.js +46 -0
  175. package/dist/src/ui/heartbeat-debug.d.ts +29 -0
  176. package/dist/src/ui/heartbeat-debug.js +45 -0
  177. package/dist/src/ui/modals/api-key-modal.js +1 -1
  178. package/dist/src/ui/modals/model-picker-modal.d.ts +8 -18
  179. package/dist/src/ui/modals/model-picker-modal.js +8 -10
  180. package/dist/src/ui/slash/ee.js +81 -0
  181. package/dist/src/ui/slash/menu-items.js +11 -2
  182. package/dist/src/ui/use-app-logic.js +354 -224
  183. package/dist/src/ui/utils/agent-activities.d.ts +39 -0
  184. package/dist/src/ui/utils/agent-activities.js +96 -0
  185. package/dist/src/ui/utils/group-tool-entries.d.ts +26 -0
  186. package/dist/src/ui/utils/group-tool-entries.js +111 -0
  187. package/dist/src/ui/utils/tool-summary.d.ts +21 -0
  188. package/dist/src/ui/utils/tool-summary.js +91 -0
  189. package/dist/src/utils/event-loop-monitor.d.ts +85 -0
  190. package/dist/src/utils/event-loop-monitor.js +107 -0
  191. package/dist/src/utils/llm-deadline.d.ts +14 -0
  192. package/dist/src/utils/llm-deadline.js +19 -0
  193. package/dist/src/utils/loop-profiler.d.ts +102 -0
  194. package/dist/src/utils/loop-profiler.js +202 -0
  195. package/dist/src/utils/settings.d.ts +27 -0
  196. package/dist/src/utils/settings.js +38 -2
  197. package/dist/src/utils/side-question.d.ts +1 -2
  198. package/dist/src/utils/side-question.js +2 -2
  199. package/dist/src/verify/entrypoint.js +51 -16
  200. package/dist/src/verify/orchestrator.d.ts +1 -1
  201. package/dist/src/verify/orchestrator.js +20 -3
  202. package/package.json +1 -2
  203. package/dist/src/cli/bw-vault.d.ts +0 -55
  204. package/dist/src/cli/bw-vault.js +0 -133
  205. package/dist/src/mcp/ee-tools.d.ts +0 -46
  206. package/dist/src/mcp/ee-tools.js +0 -194
@@ -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
- return fail("lsp_error", e instanceof Error ? e.message : String(e));
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 keychain loader with env-var fallback.
5
- * Parallel module to src/providers/keychain.ts but keyed by McpKeyId
6
- * (MCP servers like Tavily) instead of ProviderId (LLM providers).
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 keychain loader with env-var fallback.
5
- * Parallel module to src/providers/keychain.ts but keyed by McpKeyId
6
- * (MCP servers like Tavily) instead of ProviderId (LLM providers).
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
- const kt = await loadKeytar();
32
- if (!kt?.setPassword)
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 kt = await loadKeytar();
70
- if (!kt?.deletePassword)
71
- return false;
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 (B 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 \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\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";
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 (B in /providers), keys export/import (encrypted bundle), or skip for later.
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.14",
3
- "updated_at": "2026-07-09",
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.4-mini",
50
- "name": "GPT-5.4 mini (OpenAI ChatGPT OAuth)",
49
+ "id": "gpt-5.5",
50
+ "name": "GPT-5.5 (OpenAI ChatGPT OAuth)",
51
51
  "provider": "openai",
52
- "tier": "fast",
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": "low",
61
- "description": "OpenAI GPT-5.4 mini 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.4-mini-oauth"],
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": ["research", "implement", "verify"],
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": "premium",
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 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.",
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": ["leader", "implement", "verify"],
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.3-codex",
90
- "name": "GPT-5.3 Codex (OpenAI ChatGPT OAuth)",
89
+ "id": "gpt-5.4-mini",
90
+ "name": "GPT-5.4 mini (OpenAI ChatGPT OAuth)",
91
91
  "provider": "openai",
92
- "tier": "balanced",
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": "medium",
101
- "description": "OpenAI GPT-5.3 Codex (coding-specialized) 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.3-codex-oauth", "codex"],
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(provider: LegacyProvider, modelId: string, messages: ModelMessage[], signal?: AbortSignal): Promise<CompactionProposal | null>;
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(provider: LegacyProvider, modelId: string, preparation: PreparedCompaction, customInstructions?: string, signal?: AbortSignal): Promise<CompactionSummaryResult>;
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(provider, modelId, messages, signal) {
14
+ export async function proposeCompaction(modelId, messages, signal) {
15
15
  try {
16
- const runtime = resolveModelRuntime(provider, modelId);
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(provider, modelId, messages, reserveTokens, customInstructions, previousSummary, promptOverride, signal) {
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(provider, modelId);
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(provider, modelId, preparation, customInstructions, signal) {
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(provider, modelId, messagesToSummarize, settings.reserveTokens, customInstructions, previousSummary, undefined, signal)
566
+ ? summarizeConversation(modelId, messagesToSummarize, settings.reserveTokens, customInstructions, previousSummary, undefined, signal)
567
567
  : Promise.resolve({ summary: previousSummary?.trim() || "", usage: emptyUsage() }),
568
- summarizeConversation(provider, modelId, turnPrefixMessages, settings.reserveTokens, undefined, undefined, TURN_PREFIX_SUMMARIZATION_PROMPT, signal),
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(provider, modelId, messagesToSummarize, settings.reserveTokens, customInstructions, previousSummary, undefined, signal);
576
+ return summarizeConversation(modelId, messagesToSummarize, settings.reserveTokens, customInstructions, previousSummary, undefined, signal);
577
577
  }
578
578
  //# sourceMappingURL=compaction.js.map