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
@@ -1,133 +0,0 @@
1
- import { spawnSync } from "node:child_process";
2
- const defaultRunner = (cmd, args, input) => spawnSync(cmd, args, { encoding: "utf8", input });
3
- function ensureUnlocked(session, run) {
4
- const status = run("bw", ["status", "--session", session]);
5
- if (status.status !== 0) {
6
- return { ok: false, error: `bw status failed: ${status.stderr || status.stdout}` };
7
- }
8
- let parsed;
9
- try {
10
- parsed = JSON.parse(status.stdout);
11
- }
12
- catch {
13
- parsed = {};
14
- }
15
- if (parsed.status !== "unlocked") {
16
- return { ok: false, error: `Bitwarden vault is not unlocked (status: ${parsed.status ?? "unknown"})` };
17
- }
18
- return { ok: true };
19
- }
20
- function findExistingItem(name, session, run) {
21
- const list = run("bw", ["list", "items", "--search", name, "--session", session]);
22
- if (list.status !== 0)
23
- return null;
24
- let items;
25
- try {
26
- items = JSON.parse(list.stdout);
27
- }
28
- catch {
29
- return null;
30
- }
31
- return items.find((it) => it.name === name) ?? null;
32
- }
33
- /**
34
- * Write `notes` into a vault item named `name`. Creates a Secure Note if
35
- * absent; updates the notes field if present. Calls `bw sync` after a write
36
- * so subsequent `bw get` calls see the new value.
37
- */
38
- export async function writeBwSecureNote(name, notes, options = {}) {
39
- const run = options.runner ?? defaultRunner;
40
- const which = run("bw", ["--version"]);
41
- if (which.status !== 0) {
42
- return { ok: false, error: "Bitwarden CLI ('bw') not found in PATH. Install: https://bitwarden.com/help/cli/" };
43
- }
44
- const session = process.env.BW_SESSION;
45
- if (!session) {
46
- return { ok: false, error: "BW_SESSION not set. Run: export BW_SESSION=$(bw unlock --raw)" };
47
- }
48
- const unlock = ensureUnlocked(session, run);
49
- if (!unlock.ok)
50
- return { ok: false, error: unlock.error };
51
- const existing = findExistingItem(name, session, run);
52
- if (existing) {
53
- const updated = { ...existing, notes };
54
- const encoded = run("bw", ["encode"], JSON.stringify(updated));
55
- if (encoded.status !== 0) {
56
- return { ok: false, error: `bw encode failed: ${encoded.stderr || encoded.stdout}` };
57
- }
58
- const edit = run("bw", ["edit", "item", existing.id, encoded.stdout.trim(), "--session", session]);
59
- if (edit.status !== 0) {
60
- return { ok: false, error: `bw edit failed: ${edit.stderr || edit.stdout}` };
61
- }
62
- run("bw", ["sync", "--session", session]);
63
- return { ok: true, action: "updated" };
64
- }
65
- // Create a new Secure Note. Type 2 = secure note; secureNote.type 0 = generic.
66
- const item = {
67
- type: 2,
68
- name,
69
- notes,
70
- secureNote: { type: 0 },
71
- };
72
- const encoded = run("bw", ["encode"], JSON.stringify(item));
73
- if (encoded.status !== 0) {
74
- return { ok: false, error: `bw encode failed: ${encoded.stderr || encoded.stdout}` };
75
- }
76
- const create = run("bw", ["create", "item", encoded.stdout.trim(), "--session", session]);
77
- if (create.status !== 0) {
78
- return { ok: false, error: `bw create failed: ${create.stderr || create.stdout}` };
79
- }
80
- run("bw", ["sync", "--session", session]);
81
- return { ok: true, action: "created" };
82
- }
83
- /**
84
- * Unlock the BW vault with a master password and return the session token
85
- * in-memory only. Caller passes the returned session to subsequent calls;
86
- * we never write to BW_SESSION env or persist it to disk.
87
- */
88
- export async function unlockWithPassword(password, options = {}) {
89
- const run = options.runner ?? defaultRunner;
90
- const which = run("bw", ["--version"]);
91
- if (which.status !== 0) {
92
- return { ok: false, error: "Bitwarden CLI ('bw') not found in PATH. Install: https://bitwarden.com/help/cli/" };
93
- }
94
- // `bw unlock --raw` reads the password from stdin and prints the session
95
- // token to stdout. Exit code != 0 on wrong password.
96
- const res = run("bw", ["unlock", "--raw", "--passwordenv", "BW_PASSWORD_PIPE"]);
97
- // Fallback to stdin if --passwordenv isn't supported by this bw version.
98
- if (res.status !== 0) {
99
- const r2 = spawnSync("bw", ["unlock", "--raw"], { encoding: "utf8", input: `${password}\n` });
100
- if (r2.status !== 0) {
101
- const msg = (r2.stderr || r2.stdout || "").trim();
102
- return { ok: false, error: `bw unlock failed: ${msg || "wrong master password?"}` };
103
- }
104
- return { ok: true, session: r2.stdout.trim() };
105
- }
106
- return { ok: true, session: res.stdout.trim() };
107
- }
108
- /**
109
- * List Secure Notes whose name starts with `prefix`. Used by the in-TUI
110
- * sync flow to discover which providers have keys stored in the vault.
111
- */
112
- export async function listSecureNotesByPrefix(session, prefix, options = {}) {
113
- const run = options.runner ?? defaultRunner;
114
- const unlock = ensureUnlocked(session, run);
115
- if (!unlock.ok)
116
- return { ok: false, error: unlock.error };
117
- const list = run("bw", ["list", "items", "--search", prefix, "--session", session]);
118
- if (list.status !== 0) {
119
- return { ok: false, error: `bw list failed: ${list.stderr || list.stdout}` };
120
- }
121
- let items;
122
- try {
123
- items = JSON.parse(list.stdout);
124
- }
125
- catch {
126
- return { ok: false, error: "bw list returned non-JSON output" };
127
- }
128
- const matched = items
129
- .filter((it) => it.type === 2 && it.name.startsWith(prefix) && typeof it.notes === "string")
130
- .map((it) => ({ name: it.name, notes: it.notes }));
131
- return { ok: true, items: matched };
132
- }
133
- //# sourceMappingURL=bw-vault.js.map
@@ -1,46 +0,0 @@
1
- /**
2
- * src/mcp/ee-tools.ts
3
- *
4
- * EE (Experience Engine) MCP tools: ee_query (semantic recall) + ee_health +
5
- * ee_feedback (rate a recalled entry). Read-mostly; ee_feedback POSTs a verdict.
6
- *
7
- * Anti-mù: ee_query supports explicit "recent task checkpoint" / "Progress DONE" queries
8
- * so the agent (or sub-agent) can deliberately confirm finished subtasks after compactions.
9
- * collections: prefer "experience-behavioral" for compaction checkpoints (see layer3).
10
- *
11
- * Feedback gate: ee_query stamps every returned `[id col]` into a session-scoped
12
- * pending ledger (recall-ledger.ts). Subsequent ee_query calls surface (soft) or
13
- * refuse on (hard) accumulated unrated debt, so useful recalls actually get a
14
- * verdict via ee_feedback — the signal the brain needs to keep the good entries
15
- * and prune the rest. Mode via EXPERIENCE_RECALL_FEEDBACK_GATE = off|soft|hard
16
- * (default soft); hard-mode threshold via EXPERIENCE_RECALL_FEEDBACK_THRESHOLD.
17
- *
18
- * Dependencies are injected (deps) so unit tests never touch the network/singleton.
19
- */
20
- import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
21
- import { type RecallLedger } from "../ee/recall-ledger.js";
22
- import { type FeedbackResult, type FeedbackVerdict, type NoiseReason } from "../ee/search.js";
23
- import type { EERecallResponse } from "../ee/types.js";
24
- export interface EEToolDeps {
25
- recall?: (query: string, opts: {
26
- project?: string;
27
- }) => Promise<EERecallResponse | null>;
28
- health?: () => Promise<{
29
- ok: boolean;
30
- status: number;
31
- }>;
32
- feedback?: (pointId: string, collection: string, verdict: FeedbackVerdict, reason?: NoiseReason) => Promise<FeedbackResult>;
33
- write?: (lesson: string, opts: {
34
- collection?: string;
35
- title?: string;
36
- projectSlug?: string;
37
- confidence?: number;
38
- }) => Promise<{
39
- ok: boolean;
40
- id?: string;
41
- error?: string;
42
- }>;
43
- /** Session pending-recall ledger. Defaults to the process singleton. */
44
- ledger?: RecallLedger;
45
- }
46
- export declare function registerEETools(server: McpServer, deps?: EEToolDeps): void;
@@ -1,194 +0,0 @@
1
- /**
2
- * src/mcp/ee-tools.ts
3
- *
4
- * EE (Experience Engine) MCP tools: ee_query (semantic recall) + ee_health +
5
- * ee_feedback (rate a recalled entry). Read-mostly; ee_feedback POSTs a verdict.
6
- *
7
- * Anti-mù: ee_query supports explicit "recent task checkpoint" / "Progress DONE" queries
8
- * so the agent (or sub-agent) can deliberately confirm finished subtasks after compactions.
9
- * collections: prefer "experience-behavioral" for compaction checkpoints (see layer3).
10
- *
11
- * Feedback gate: ee_query stamps every returned `[id col]` into a session-scoped
12
- * pending ledger (recall-ledger.ts). Subsequent ee_query calls surface (soft) or
13
- * refuse on (hard) accumulated unrated debt, so useful recalls actually get a
14
- * verdict via ee_feedback — the signal the brain needs to keep the good entries
15
- * and prune the rest. Mode via EXPERIENCE_RECALL_FEEDBACK_GATE = off|soft|hard
16
- * (default soft); hard-mode threshold via EXPERIENCE_RECALL_FEEDBACK_THRESHOLD.
17
- *
18
- * Dependencies are injected (deps) so unit tests never touch the network/singleton.
19
- */
20
- import { z } from "zod";
21
- import { sessionRecallLedger } from "../ee/recall-ledger.js";
22
- import { formatRecallForAgent, healthEE, recallEE, } from "../ee/search.js";
23
- function ok(data) {
24
- return { content: [{ type: "text", text: JSON.stringify(data) }] };
25
- }
26
- function okText(text) {
27
- return { content: [{ type: "text", text }] };
28
- }
29
- function fail(error, message) {
30
- return {
31
- content: [{ type: "text", text: JSON.stringify({ error, message }) }],
32
- isError: true,
33
- };
34
- }
35
- /** Resolve the feedback-gate policy from env at call time (so tests can vary it). */
36
- function resolveGate() {
37
- const raw = String(process.env.EXPERIENCE_RECALL_FEEDBACK_GATE ?? "soft")
38
- .trim()
39
- .toLowerCase();
40
- const mode = raw === "off" || raw === "hard" ? raw : "soft";
41
- const parsed = Number.parseInt(process.env.EXPERIENCE_RECALL_FEEDBACK_THRESHOLD ?? "", 10);
42
- const threshold = Number.isFinite(parsed) && parsed > 0 ? parsed : 3;
43
- return { mode, threshold };
44
- }
45
- /** One readable block naming the still-unrated recalls + how to clear them. */
46
- function formatPendingBlock(pending, hard, max = 8) {
47
- const shown = pending.slice(0, max);
48
- const lines = shown.map((p) => ` - [${p.id} ${p.collection ?? "?"}] (from recall: "${p.query}")`);
49
- const more = pending.length > max ? `\n …and ${pending.length - max} more` : "";
50
- const head = hard
51
- ? `⚠️ FEEDBACK GATE — ${pending.length} earlier recall(s) are still unrated. Rate them with ` +
52
- `ee_feedback(id, collection, verdict) before pulling new recalls:`
53
- : `⚠️ ${pending.length} earlier recall(s) still unrated — call ` +
54
- `ee_feedback(id, collection, verdict=followed|ignored|noise) so the brain keeps what helped and prunes the rest:`;
55
- return `${head}\n${lines.join("\n")}${more}`;
56
- }
57
- export function registerEETools(server, deps = {}) {
58
- // Default to the shared EE recall/health/feedback helpers (src/ee/search.ts) so
59
- // the MCP tools and the in-CLI builtin tools resolve auth/baseUrl the same way.
60
- // Tests inject `deps` to avoid the network and a fresh `ledger` to stay isolated.
61
- const recall = deps.recall ?? ((q, o) => recallEE(q, o));
62
- const health = deps.health ?? (() => healthEE());
63
- const ledger = deps.ledger ?? sessionRecallLedger;
64
- server.registerTool("ee_query", {
65
- description: "Active recall over the Experience Engine brain — prior decisions, gotchas, learned warnings/recipes, and " +
66
- "task checkpoints for this codebase — via the recallMode pipeline (same path as exp-recall.js). " +
67
- "CALL THIS PROACTIVELY, before acting: when starting work in an unfamiliar area, when unsure how something " +
68
- "is done in this stack, before a risky or hard-to-reverse step, or to recall finished work after a " +
69
- "compaction (e.g. query='recent compaction checkpoint Progress DONE for <subtask>'). A deliberate query " +
70
- "here is cheaper than re-deriving or repeating a past mistake. Returns a formatted index whose entries " +
71
- "carry `[id col]` handles — after you act on a recall, rate each entry you used or judged with the " +
72
- "ee_feedback tool (followed/ignored/noise) so the brain keeps what helped and prunes the rest; unrated " +
73
- "recalls are surfaced back to you on the next ee_query. Optional project scopes the recall. Returns a " +
74
- "compact ranked index (cosine-ranked, strongest first), capped at maxChars (default 6000, range " +
75
- "500-20000) and truncated from the tail; raise maxChars to see more. Returns ee_unavailable if EE is " +
76
- "down (then proceed without it).",
77
- inputSchema: {
78
- query: z.string().min(1).max(1000),
79
- project: z.string().max(200).optional(),
80
- maxChars: z.number().int().min(500).max(20_000).optional(),
81
- },
82
- }, async ({ query, project, maxChars }) => {
83
- const gate = resolveGate();
84
- const pendingBefore = gate.mode === "off" ? [] : ledger.pending();
85
- // Hard gate: refuse a NEW recall while unrated debt is at/over threshold —
86
- // do not even spend the brain call. Below threshold, fall through to soft.
87
- if (gate.mode === "hard" && pendingBefore.length >= gate.threshold) {
88
- return fail("feedback_required", formatPendingBlock(pendingBefore, true));
89
- }
90
- try {
91
- const resp = await recall(query, { project });
92
- if (resp === null) {
93
- return fail("ee_unavailable", "EE recall returned no response (server down, timeout, or circuit open)");
94
- }
95
- // Stamp the new entries as pending debt (unless the gate is off).
96
- if (gate.mode !== "off")
97
- ledger.record(resp.entries, query);
98
- // Return the compact `[id col]` index (capped) rather than JSON.stringify
99
- // of the whole response — the wide-net recall text (~30k) otherwise blows
100
- // the MCP per-result token cap and spills to a file. See formatRecallForAgent.
101
- const index = formatRecallForAgent(resp, { query, maxChars });
102
- if (gate.mode !== "off" && pendingBefore.length > 0) {
103
- return okText(`${formatPendingBlock(pendingBefore, false)}\n\n${index}`);
104
- }
105
- return okText(index);
106
- }
107
- catch (e) {
108
- return fail("ee_unavailable", e instanceof Error ? e.message : String(e));
109
- }
110
- });
111
- server.registerTool("ee_feedback", {
112
- description: "Rate an Experience Engine recall entry so the brain keeps what helped and prunes the rest. Call after " +
113
- "acting on an ee_query result — once per `[id col]` you used or judged. verdict: 'followed' (you changed " +
114
- "your approach because of it), 'ignored' (topical but did not apply this time), 'noise' (wrong by category — " +
115
- "REQUIRES reason: wrong_repo | wrong_language | wrong_task | stale_rule). id may be a short prefix; the " +
116
- "server resolves it. Clears the entry from this session's pending-feedback gate.",
117
- inputSchema: {
118
- id: z.string().min(1).max(200),
119
- collection: z.string().min(1).max(200),
120
- verdict: z.enum(["followed", "ignored", "noise"]),
121
- reason: z.enum(["wrong_repo", "wrong_language", "wrong_task", "stale_rule"]).optional(),
122
- },
123
- }, async ({ id, collection, verdict, reason }) => {
124
- if (verdict === "noise" && !reason) {
125
- return fail("reason_required", "verdict 'noise' requires reason: wrong_repo | wrong_language | wrong_task | stale_rule");
126
- }
127
- const feedback = deps.feedback ?? ((pid, col, v, r) => import("../ee/search.js").then((m) => m.feedbackEE(pid, col, v, r)));
128
- try {
129
- const result = await feedback(id, collection, verdict, reason);
130
- if (!result.ok) {
131
- return fail("feedback_failed", result.error ?? "feedback POST failed");
132
- }
133
- // Clear by the server-resolved full id AND the (possibly short) id the
134
- // agent passed, so a prefix-based call still settles the ledger debt.
135
- const clearedId = result.resolvedId ?? id;
136
- ledger.clear(clearedId);
137
- ledger.clear(id);
138
- return ok({
139
- ok: true,
140
- id: clearedId,
141
- verdict: result.verdict,
142
- ...(result.reason ? { reason: result.reason } : {}),
143
- ...(result.queued ? { queued: true } : {}),
144
- pendingRemaining: ledger.pendingCount(),
145
- });
146
- }
147
- catch (e) {
148
- return fail("feedback_failed", e instanceof Error ? e.message : String(e));
149
- }
150
- });
151
- server.registerTool("ee_health", { description: "Check Experience Engine server reachability.", inputSchema: {} }, async () => {
152
- try {
153
- return ok(await health());
154
- }
155
- catch (e) {
156
- return fail("ee_unavailable", e instanceof Error ? e.message : String(e));
157
- }
158
- });
159
- server.registerTool("ee_write", {
160
- description: "Save a NEW lesson to the Experience Engine brain so you and future sessions recall it. Call the MOMENT you " +
161
- "hit a mistake / error / dead-end and find the working fix: record the pitfall AND the fix in one concise, " +
162
- "generalizable lesson (1-3 sentences — what to do or avoid next time, NOT a play-by-play of this turn). The " +
163
- "lesson is embedded immediately (via /api/import-memory: dense + sparse) and becomes recallable via ee_query " +
164
- "in this and future sessions. Use ee_query first if unsure a lesson already exists. collection defaults to " +
165
- "experience-behavioral (use experience-principles only for a broad, project-independent principle); project " +
166
- "scopes the lesson.",
167
- inputSchema: {
168
- lesson: z.string().min(12).max(4000),
169
- title: z.string().max(200).optional(),
170
- collection: z.enum(["experience-behavioral", "experience-principles"]).optional(),
171
- project: z.string().max(200).optional(),
172
- },
173
- }, async ({ lesson, title, collection, project }) => {
174
- const trimmed = lesson.trim();
175
- const text = trimmed.length > 1500 ? `${trimmed.slice(0, 1497)}...` : trimmed;
176
- const targetCollection = collection ?? "experience-behavioral";
177
- const write = deps.write ?? ((l, o) => import("../ee/search.js").then((m) => m.writeExperienceEE(l, o)));
178
- try {
179
- const result = await write(text, {
180
- collection: targetCollection,
181
- title,
182
- projectSlug: project,
183
- confidence: 0.65,
184
- });
185
- if (!result.ok)
186
- return fail("write_failed", result.error ?? "import-memory POST failed");
187
- return ok({ ok: true, id: result.id, collection: targetCollection, recallable: "now via ee_query" });
188
- }
189
- catch (e) {
190
- return fail("write_failed", e instanceof Error ? e.message : String(e));
191
- }
192
- });
193
- }
194
- //# sourceMappingURL=ee-tools.js.map