mixdog 0.9.2 → 0.9.3

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 (154) hide show
  1. package/package.json +2 -1
  2. package/scripts/anthropic-maxtokens-test.mjs +119 -0
  3. package/scripts/build-tui.mjs +13 -1
  4. package/scripts/explore-bench.mjs +124 -0
  5. package/scripts/hook-bus-test.mjs +191 -0
  6. package/scripts/path-suffix-test.mjs +57 -0
  7. package/scripts/recall-bench.mjs +207 -0
  8. package/scripts/tool-smoke.mjs +7 -4
  9. package/src/agents/debugger/AGENT.md +2 -2
  10. package/src/agents/heavy-worker/AGENT.md +20 -11
  11. package/src/agents/reviewer/AGENT.md +2 -2
  12. package/src/agents/worker/AGENT.md +17 -11
  13. package/src/mixdog-session-runtime.mjs +424 -1812
  14. package/src/repl.mjs +5 -5
  15. package/src/rules/agent/30-explorer.md +8 -11
  16. package/src/rules/lead/lead-tool.md +9 -5
  17. package/src/rules/shared/01-tool.md +11 -5
  18. package/src/runtime/agent/orchestrator/context/collect.mjs +51 -0
  19. package/src/runtime/agent/orchestrator/mcp/client.mjs +6 -2
  20. package/src/runtime/agent/orchestrator/providers/anthropic-effort.mjs +1 -1
  21. package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens.mjs +93 -0
  22. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +22 -68
  23. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +46 -7
  24. package/src/runtime/agent/orchestrator/providers/api-usage.mjs +1 -13
  25. package/src/runtime/agent/orchestrator/providers/gemini.mjs +1 -1
  26. package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +1 -1
  27. package/src/runtime/agent/orchestrator/providers/lib/usage-primitives.mjs +32 -0
  28. package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +54 -20
  29. package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +19 -12
  30. package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +7 -5
  31. package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +33 -23
  32. package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +31 -14
  33. package/src/runtime/agent/orchestrator/providers/opencode-go-usage.mjs +38 -12
  34. package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +7 -8
  35. package/src/runtime/agent/orchestrator/session/loop/compact-debug.mjs +28 -0
  36. package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +262 -0
  37. package/src/runtime/agent/orchestrator/session/loop/context-overflow.mjs +38 -0
  38. package/src/runtime/agent/orchestrator/session/loop/env.mjs +14 -0
  39. package/src/runtime/agent/orchestrator/session/loop/hidden-agents.mjs +21 -0
  40. package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +49 -0
  41. package/src/runtime/agent/orchestrator/session/loop/steering.mjs +63 -0
  42. package/src/runtime/agent/orchestrator/session/loop/stored-tool-args.mjs +100 -0
  43. package/src/runtime/agent/orchestrator/session/loop/tool-classify.mjs +52 -0
  44. package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +218 -0
  45. package/src/runtime/agent/orchestrator/session/loop/transcript-repair.mjs +101 -0
  46. package/src/runtime/agent/orchestrator/session/loop/usage.mjs +35 -0
  47. package/src/runtime/agent/orchestrator/session/loop.mjs +169 -918
  48. package/src/runtime/agent/orchestrator/session/manager/context-meta.mjs +227 -0
  49. package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +235 -0
  50. package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +137 -0
  51. package/src/runtime/agent/orchestrator/session/manager/rules-cache.mjs +155 -0
  52. package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +303 -0
  53. package/src/runtime/agent/orchestrator/session/manager.mjs +65 -1032
  54. package/src/runtime/agent/orchestrator/stall-policy.mjs +3 -3
  55. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +2 -2
  56. package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.mjs +241 -0
  57. package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.test.mjs +162 -0
  58. package/src/runtime/agent/orchestrator/tools/builtin/fuzzy-match.mjs +1 -1
  59. package/src/runtime/agent/orchestrator/tools/builtin/path-diagnostics.mjs +42 -2
  60. package/src/runtime/agent/orchestrator/tools/builtin/read-formatting.mjs +1 -1
  61. package/src/runtime/agent/orchestrator/tools/builtin/read-single-tool.mjs +11 -4
  62. package/src/runtime/agent/orchestrator/tools/builtin/search-tool.mjs +5 -0
  63. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +50 -39
  64. package/src/runtime/agent/orchestrator/tools/builtin.mjs +11 -0
  65. package/src/runtime/agent/orchestrator/tools/code-graph/build.mjs +303 -0
  66. package/src/runtime/agent/orchestrator/tools/code-graph/constants.mjs +43 -0
  67. package/src/runtime/agent/orchestrator/tools/code-graph/disk-cache.mjs +382 -0
  68. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +497 -0
  69. package/src/runtime/agent/orchestrator/tools/code-graph/graph-binary.mjs +295 -0
  70. package/src/runtime/agent/orchestrator/tools/code-graph/graph-model.mjs +158 -0
  71. package/src/runtime/agent/orchestrator/tools/code-graph/lang-predicates.mjs +128 -0
  72. package/src/runtime/agent/orchestrator/tools/code-graph/memory-cache.mjs +66 -0
  73. package/src/runtime/agent/orchestrator/tools/code-graph/project-root.mjs +44 -0
  74. package/src/runtime/agent/orchestrator/tools/code-graph/search.mjs +1192 -0
  75. package/src/runtime/agent/orchestrator/tools/code-graph/source-access.mjs +81 -0
  76. package/src/runtime/agent/orchestrator/tools/code-graph/span.mjs +19 -0
  77. package/src/runtime/agent/orchestrator/tools/code-graph/symbol-index.mjs +280 -0
  78. package/src/runtime/agent/orchestrator/tools/code-graph/text-mask.mjs +347 -0
  79. package/src/runtime/agent/orchestrator/tools/code-graph.mjs +36 -4277
  80. package/src/runtime/agent/orchestrator/tools/patch.mjs +3 -3
  81. package/src/runtime/agent/orchestrator/tools/progress-message.mjs +1 -2
  82. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +1 -2
  83. package/src/runtime/agent/orchestrator/tools/shell-snapshot.mjs +2 -4
  84. package/src/runtime/channels/index.mjs +14 -233
  85. package/src/runtime/channels/lib/boot-profile.mjs +23 -0
  86. package/src/runtime/channels/lib/crash-log.mjs +106 -0
  87. package/src/runtime/channels/lib/index-drop-trace.mjs +72 -0
  88. package/src/runtime/channels/lib/output-forwarder.mjs +8 -0
  89. package/src/runtime/channels/lib/telegram-format.mjs +19 -22
  90. package/src/runtime/channels/lib/whisper-language.mjs +42 -0
  91. package/src/runtime/memory/index.mjs +314 -359
  92. package/src/runtime/memory/lib/core-memory-store.mjs +351 -1
  93. package/src/runtime/memory/lib/cycle-signatures.mjs +34 -0
  94. package/src/runtime/memory/lib/http-wire.mjs +57 -0
  95. package/src/runtime/memory/lib/memory-cycle2.mjs +56 -3
  96. package/src/runtime/memory/lib/memory-recall-scope-filter.mjs +24 -0
  97. package/src/runtime/memory/lib/memory-retrievers.mjs +8 -0
  98. package/src/runtime/memory/lib/memory.mjs +20 -0
  99. package/src/runtime/memory/lib/promotion-fingerprint.mjs +50 -0
  100. package/src/runtime/memory/lib/recall-format.mjs +183 -0
  101. package/src/runtime/memory/tool-defs.mjs +4 -4
  102. package/src/runtime/shared/abort-controller.mjs +1 -1
  103. package/src/runtime/shared/background-tasks.mjs +2 -3
  104. package/src/runtime/shared/buffered-appender.mjs +149 -0
  105. package/src/runtime/shared/task-notification-envelope.mjs +98 -0
  106. package/src/runtime/shared/task-notification-envelope.test.mjs +107 -0
  107. package/src/runtime/shared/tool-execution-contract.mjs +2 -2
  108. package/src/runtime/shared/transcript-writer.mjs +29 -2
  109. package/src/session-runtime/config-helpers.mjs +209 -0
  110. package/src/session-runtime/effort.mjs +128 -0
  111. package/src/session-runtime/fs-utils.mjs +10 -0
  112. package/src/session-runtime/model-capabilities.mjs +130 -0
  113. package/src/session-runtime/output-styles.mjs +124 -0
  114. package/src/session-runtime/plugin-mcp.mjs +114 -0
  115. package/src/session-runtime/session-text.mjs +100 -0
  116. package/src/session-runtime/statusline-route.mjs +35 -0
  117. package/src/session-runtime/tool-catalog.mjs +720 -0
  118. package/src/session-runtime/workflow.mjs +358 -0
  119. package/src/standalone/agent-tool.mjs +49 -10
  120. package/src/standalone/channel-worker.mjs +3 -2
  121. package/src/standalone/explore-tool.mjs +10 -3
  122. package/src/standalone/hook-bus.mjs +165 -8
  123. package/src/standalone/opencode-go-login.mjs +121 -0
  124. package/src/standalone/provider-admin.mjs +14 -3
  125. package/src/tui/App.jsx +884 -143
  126. package/src/tui/components/PromptInput.jsx +272 -15
  127. package/src/tui/components/ToolExecution.jsx +20 -10
  128. package/src/tui/components/tool-output-format.mjs +2 -2
  129. package/src/tui/dist/index.mjs +1771 -1947
  130. package/src/tui/engine/agent-envelope.mjs +296 -0
  131. package/src/tui/engine/boot-profile.mjs +21 -0
  132. package/src/tui/engine/labels.mjs +67 -0
  133. package/src/tui/engine/notice-text.mjs +112 -0
  134. package/src/tui/engine/queue-helpers.mjs +161 -0
  135. package/src/tui/engine/session-stats.mjs +46 -0
  136. package/src/tui/engine/tool-call-fields.mjs +23 -0
  137. package/src/tui/engine/tool-result-text.mjs +126 -0
  138. package/src/tui/engine.mjs +311 -851
  139. package/src/tui/input-editing.mjs +58 -8
  140. package/src/tui/input-editing.selection.test.mjs +75 -0
  141. package/src/tui/keyboard-protocol.mjs +2 -2
  142. package/src/tui/lib/voice-recorder.mjs +35 -19
  143. package/src/tui/markdown/format-token.mjs +7 -8
  144. package/src/tui/markdown/format-token.test.mjs +3 -3
  145. package/src/tui/paste-attachments.mjs +38 -0
  146. package/src/tui/paste-fix.test.mjs +119 -0
  147. package/src/tui/themes/base.mjs +2 -2
  148. package/src/tui/themes/kanagawa.mjs +4 -4
  149. package/src/tui/themes/teal.mjs +4 -5
  150. package/src/tui/themes/utils.mjs +1 -1
  151. package/src/ui/statusline.mjs +49 -0
  152. package/src/workflows/default/WORKFLOW.md +16 -9
  153. package/src/workflows/sequential/WORKFLOW.md +16 -11
  154. package/src/workflows/solo/WORKFLOW.md +5 -1
@@ -0,0 +1,207 @@
1
+ #!/usr/bin/env node
2
+ // recall-bench.mjs — case-based recall quality bench against the LIVE memory
3
+ // DB. READ-ONLY: every case goes through handleToolCall('search_memories')
4
+ // which only performs SELECTs (handleSearch path). No mutation actions
5
+ // ('memory' tool / cycle1 / cycle2 / prune / purge / etc.) are ever invoked
6
+ // here — do not add any.
7
+ //
8
+ // node scripts/recall-bench.mjs [--cases scripts/recall-bench-cases.json] [--json]
9
+ //
10
+ // Loads src/runtime/memory/index.mjs in-process (same module the daemon
11
+ // uses), calls init() once, runs each bench case through handleToolCall,
12
+ // prints params/result-count/top-3/latency/PASS-WARN per case, then a
13
+ // summary table, then calls stop().
14
+ import { existsSync, readFileSync } from 'node:fs';
15
+ import { resolve, dirname } from 'node:path';
16
+ import { fileURLToPath, pathToFileURL } from 'node:url';
17
+
18
+ const __dir = dirname(fileURLToPath(import.meta.url));
19
+ const ROOT = resolve(__dir, '..');
20
+ const DEFAULT_CASES_PATH = resolve(__dir, 'recall-bench-cases.json');
21
+
22
+ function argValue(name, fallback = null) {
23
+ const idx = process.argv.indexOf(`--${name}`);
24
+ if (idx >= 0 && idx + 1 < process.argv.length) return process.argv[idx + 1];
25
+ const pref = `--${name}=`;
26
+ const hit = process.argv.find((a) => a.startsWith(pref));
27
+ return hit ? hit.slice(pref.length) : fallback;
28
+ }
29
+ function hasFlag(name) { return process.argv.includes(`--${name}`); }
30
+
31
+ const WARN_LATENCY_MS = 3000;
32
+
33
+ const DEFAULT_CASES = [
34
+ { id: 'kw-ko', label: 'keyword query (ko)', args: { query: '메모리 재현' }, expect: 'results' },
35
+ { id: 'kw-en', label: 'keyword query (en)', args: { query: 'memory recall pipeline' }, expect: 'results' },
36
+ { id: 'short-1tok', label: 'short 1-token query', args: { query: 'recall' }, expect: 'results' },
37
+ { id: 'short-2tok', label: 'short 2-token query', args: { query: 'cycle1 drain' }, expect: 'results' },
38
+ { id: 'browse-last', label: 'query-less recent browse (period=last)', args: { period: 'last', limit: 10 }, expect: 'browse' },
39
+ { id: 'period-24h', label: 'period window 24h', args: { period: '24h', limit: 10 }, expect: 'browse' },
40
+ { id: 'period-7d', label: 'period window 7d', args: { period: '7d', limit: 10 }, expect: 'browse' },
41
+ { id: 'category-filter', label: 'category filter (decision)', args: { period: '30d', category: 'decision', limit: 10 }, expect: 'browse' },
42
+ { id: 'id-lookup', label: 'id lookup', args: { id: 1 }, expect: 'idlookup' },
43
+ { id: 'scope-project', label: 'project-scoped query', args: { query: 'recall', cwd: ROOT, limit: 10 }, expect: 'results' },
44
+ { id: 'scope-all', label: 'all-scope query', args: { query: 'recall', projectScope: 'all', limit: 10 }, expect: 'results' },
45
+ { id: 'raw-on', label: 'includeRaw on', args: { query: 'recall', includeRaw: true, limit: 10 }, expect: 'results' },
46
+ { id: 'raw-off', label: 'includeRaw off', args: { query: 'recall', includeRaw: false, limit: 10 }, expect: 'results' },
47
+ ];
48
+
49
+ function loadCases(path) {
50
+ if (path && existsSync(resolve(path))) {
51
+ try {
52
+ const parsed = JSON.parse(readFileSync(resolve(path), 'utf8'));
53
+ if (Array.isArray(parsed) && parsed.length) return parsed;
54
+ } catch (e) {
55
+ process.stderr.write(`[recall-bench] failed to parse cases file ${path}: ${e.message}; using built-in cases\n`);
56
+ }
57
+ }
58
+ return DEFAULT_CASES;
59
+ }
60
+
61
+ function textOfResult(result) {
62
+ if (result && typeof result === 'object' && Array.isArray(result.content)) {
63
+ return result.content.map((p) => (p?.type === 'text' ? p.text || '' : JSON.stringify(p))).join('\n');
64
+ }
65
+ if (result && typeof result === 'object' && typeof result.text === 'string') return result.text;
66
+ if (typeof result === 'string') return result;
67
+ return JSON.stringify(result ?? '');
68
+ }
69
+
70
+ function countEntryLines(text) {
71
+ const t = String(text || '').trim();
72
+ if (!t || t === '(no results)' || t === '(no valid ids)') return 0;
73
+ return t.split('\n').filter((line) => line.trim() && !line.startsWith('[recall truncated') && !line.startsWith('note:')).length;
74
+ }
75
+
76
+ function topN(text, n = 3, maxLen = 140) {
77
+ const t = String(text || '').trim();
78
+ if (!t || t === '(no results)') return [];
79
+ return t.split('\n')
80
+ .filter((line) => line.trim() && !line.startsWith('[recall truncated') && !line.startsWith('note:'))
81
+ .slice(0, n)
82
+ .map((line) => (line.length > maxLen ? `${line.slice(0, maxLen - 1)}…` : line));
83
+ }
84
+
85
+ function evaluateCase(kase, { count, ms, isError }) {
86
+ const warnings = [];
87
+ if (isError) warnings.push('error result');
88
+ if (ms > WARN_LATENCY_MS) warnings.push(`latency ${ms}ms > ${WARN_LATENCY_MS}ms`);
89
+ if ((kase.expect === 'browse' || kase.expect === 'idlookup') && count === 0) {
90
+ warnings.push('0 results for a browse/id-lookup case (expected data present)');
91
+ }
92
+ const status = warnings.length ? 'WARN' : 'PASS';
93
+ return { status, warnings };
94
+ }
95
+
96
+ async function runCase(memoryModule, kase) {
97
+ const started = Date.now();
98
+ let result;
99
+ let isError = false;
100
+ let errMsg = null;
101
+ try {
102
+ result = await memoryModule.handleToolCall('search_memories', kase.args || {});
103
+ isError = Boolean(result?.isError);
104
+ } catch (e) {
105
+ isError = true;
106
+ errMsg = e?.message || String(e);
107
+ result = { text: `(error: ${errMsg})` };
108
+ }
109
+ const ms = Date.now() - started;
110
+ const text = textOfResult(result);
111
+ const count = countEntryLines(text);
112
+ const evalResult = evaluateCase(kase, { count, ms, isError });
113
+ return {
114
+ id: kase.id,
115
+ label: kase.label,
116
+ args: kase.args,
117
+ count,
118
+ ms,
119
+ isError,
120
+ errMsg,
121
+ top3: topN(text, 3),
122
+ status: evalResult.status,
123
+ warnings: evalResult.warnings,
124
+ };
125
+ }
126
+
127
+ function printCase(row) {
128
+ process.stdout.write(`\n[${row.status}] ${row.id} — ${row.label}\n`);
129
+ process.stdout.write(` params: ${JSON.stringify(row.args)}\n`);
130
+ process.stdout.write(` results: ${row.count} latency: ${row.ms}ms${row.isError ? ` ERROR: ${row.errMsg}` : ''}\n`);
131
+ if (row.top3.length) {
132
+ for (const line of row.top3) process.stdout.write(` - ${line}\n`);
133
+ } else {
134
+ process.stdout.write(' (no results)\n');
135
+ }
136
+ for (const w of row.warnings) process.stdout.write(` WARN: ${w}\n`);
137
+ }
138
+
139
+ function printSummary(rows) {
140
+ process.stdout.write('\n=== recall-bench summary ===\n');
141
+ const widths = { id: 18, results: 8, ms: 8, status: 6 };
142
+ process.stdout.write(
143
+ `${'case'.padEnd(widths.id)}${'results'.padEnd(widths.results)}${'ms'.padEnd(widths.ms)}${'status'.padEnd(widths.status)}notes\n`,
144
+ );
145
+ for (const r of rows) {
146
+ process.stdout.write(
147
+ `${String(r.id).padEnd(widths.id)}${String(r.count).padEnd(widths.results)}${String(r.ms).padEnd(widths.ms)}${String(r.status).padEnd(widths.status)}${r.warnings.join('; ')}\n`,
148
+ );
149
+ }
150
+ const warnCount = rows.filter((r) => r.status === 'WARN').length;
151
+ const totalMs = rows.reduce((s, r) => s + r.ms, 0);
152
+ process.stdout.write(`\ncases=${rows.length} pass=${rows.length - warnCount} warn=${warnCount} total_latency=${totalMs}ms\n`);
153
+ }
154
+
155
+ async function main() {
156
+ const casesPath = argValue('cases', DEFAULT_CASES_PATH);
157
+ const jsonMode = hasFlag('json');
158
+ const cases = loadCases(casesPath);
159
+
160
+ let memoryModule;
161
+ try {
162
+ memoryModule = await import(pathToFileURL(resolve(ROOT, 'src/runtime/memory/index.mjs')).href);
163
+ } catch (e) {
164
+ process.stderr.write(`[recall-bench] failed to load memory module: ${e?.stack || e?.message || e}\n`);
165
+ process.exitCode = 1;
166
+ return;
167
+ }
168
+
169
+ try {
170
+ await memoryModule.init();
171
+ } catch (e) {
172
+ process.stdout.write(`recall-bench: DB unreachable / init failed — reporting clearly, no fabricated results.\n`);
173
+ process.stdout.write(`error: ${e?.stack || e?.message || e}\n`);
174
+ process.exitCode = 1;
175
+ return;
176
+ }
177
+
178
+ const rows = [];
179
+ try {
180
+ for (const kase of cases) {
181
+ const row = await runCase(memoryModule, kase);
182
+ rows.push(row);
183
+ if (!jsonMode) printCase(row);
184
+ }
185
+ } finally {
186
+ try { await memoryModule.stop?.(); } catch {}
187
+ }
188
+
189
+ const allZero = rows.length > 0 && rows.every((r) => r.count === 0 && !r.isError);
190
+ if (allZero) {
191
+ process.stdout.write('\nNOTE: every case returned 0 results — DB is likely empty (or unreachable pool). Treat WARNs below as expected-empty, not a recall bug, until data is present.\n');
192
+ }
193
+
194
+ if (jsonMode) {
195
+ process.stdout.write(JSON.stringify({ cases: rows }, null, 2) + '\n');
196
+ } else {
197
+ printSummary(rows);
198
+ }
199
+
200
+ const hardErrors = rows.filter((r) => r.isError);
201
+ if (hardErrors.length) process.exitCode = 1;
202
+ }
203
+
204
+ main().catch((e) => {
205
+ process.stderr.write(`${e?.stack || e?.message || e}\n`);
206
+ process.exitCode = 1;
207
+ });
@@ -747,7 +747,7 @@ if (agentProps.mode || agentProps.wait) throw new Error('agent schema should not
747
747
  agentRules: '# Tool Use',
748
748
  skillManifest: '',
749
749
  });
750
- if (!heavyPrompt.stableSystemContext.includes('## heavy-worker') || !heavyPrompt.stableSystemContext.includes('Handoff fragments only')) {
750
+ if (!heavyPrompt.stableSystemContext.includes('## heavy-worker')) {
751
751
  throw new Error(`heavy-worker AGENT.md must be included in scoped role instructions: ${heavyPrompt.stableSystemContext}`);
752
752
  }
753
753
  const workerPrompt = composeSystemPrompt({
@@ -756,7 +756,7 @@ if (agentProps.mode || agentProps.wait) throw new Error('agent schema should not
756
756
  agentRules: '# Tool Use',
757
757
  skillManifest: '',
758
758
  });
759
- if (!workerPrompt.stableSystemContext.includes('## worker') || !workerPrompt.stableSystemContext.includes('Handoff fragments only')) {
759
+ if (!workerPrompt.stableSystemContext.includes('## worker')) {
760
760
  throw new Error(`worker AGENT.md must be included in scoped role instructions: ${workerPrompt.stableSystemContext}`);
761
761
  }
762
762
  }
@@ -1091,7 +1091,7 @@ setInternalToolsProvider({
1091
1091
  if (!/Read-only retrieval role/i.test(visible) || /# environment/i.test(visible) || /git operations deferred to Lead/i.test(visible)) {
1092
1092
  throw new Error(`explorer hidden retrieval context should stay slim: ${visible.slice(0, 1200)}`);
1093
1093
  }
1094
- if (!/# Role: explorer/i.test(systemVisible) || /# Role: explorer/i.test(userReminderVisible) || !/Locator only/i.test(systemVisible)) {
1094
+ if (!/# Role: explorer/i.test(systemVisible) || /# Role: explorer/i.test(userReminderVisible) || !/one-shot locator/i.test(systemVisible)) {
1095
1095
  throw new Error(`explorer role md must ride BP2 system, not BP3 user reminder: system=${systemVisible.slice(0, 600)} user=${userReminderVisible.slice(0, 600)}`);
1096
1096
  }
1097
1097
  const visibleBytes = Buffer.byteLength(visible, 'utf8');
@@ -1434,9 +1434,12 @@ for (const requiredGrammarLine of [
1434
1434
  }
1435
1435
  const readPathSchema = BUILTIN_TOOLS.find((tool) => tool.name === 'read')?.inputSchema?.properties?.path || {};
1436
1436
  const readPathDescription = readPathSchema.description || '';
1437
- if (!/File path/i.test(readPathDescription) || !/\{path,offset,limit\}\[\]/i.test(readPathDescription) || !/Pass arrays directly/i.test(readPathDescription) || !/legacy recovery only/i.test(readPathDescription) || !/Dirs use list/i.test(readPathDescription)) {
1437
+ if (!/File path/i.test(readPathDescription) || !/\{path,offset,limit\}\[\]/i.test(readPathDescription) || !/Pass arrays directly/i.test(readPathDescription) || !/legacy recovery only/i.test(readPathDescription)) {
1438
1438
  throw new Error('read schema must keep directory-vs-file guidance');
1439
1439
  }
1440
+ if (!/Dirs use list/i.test((BUILTIN_TOOLS.find((tool) => tool.name === 'read')?.description) || '')) {
1441
+ throw new Error('read description must keep directory-vs-file guidance');
1442
+ }
1440
1443
  const readTool = BUILTIN_TOOLS.find((tool) => tool.name === 'read');
1441
1444
  const readDescription = readTool?.description || '';
1442
1445
  const readProps = readTool?.inputSchema?.properties || {};
@@ -7,5 +7,5 @@ permission: read
7
7
  Root-cause analysis agent.
8
8
 
9
9
  Smallest confirmed cause chain before fixes. Return likely cause, evidence
10
- (`file:line`), smallest next check/fix. Fragments only, no narration. Mark
11
- confirmed facts vs inferences; avoid broad speculation.
10
+ (`file:line`), smallest next check/fix. Mark confirmed facts vs inferences;
11
+ avoid broad speculation.
@@ -1,11 +1,20 @@
1
- ---
2
- permission: read-write
3
- ---
4
-
5
- # Heavy Worker
6
- Broad implementation agent.
7
-
8
- Bounded slices; smallest coherent change, not rewrite. Stop: unclear scope,
9
- growing blast radius, or Lead-only verification. Minimal checks + how-to-verify.
10
- Handoff fragments only, no report bloat: changed `file:line`, verification, risks.
11
-
1
+ ---
2
+ permission: read-write
3
+ ---
4
+
5
+ # Heavy Worker
6
+ Broad implementation agent.
7
+
8
+ Bounded slices; smallest coherent change, not rewrite. Stop: unclear scope,
9
+ growing blast radius, or Lead-only verification.
10
+
11
+ EDIT-FIRST DISCIPLINE. Survey the slice with ONE batched read/grep round,
12
+ then start patching the first bounded piece — broad scope means edit
13
+ incrementally, not read exhaustively. NEVER "one more confirming read": if
14
+ an anchor is plausible, the next call is `apply_patch`. 3+ consecutive
15
+ read-only calls without an edit = stalling — patch the piece you understand
16
+ or stop and report blocked. Self-check comes AFTER edits; deep verification
17
+ is Lead's.
18
+
19
+ Minimal checks + how-to-verify.
20
+
@@ -7,5 +7,5 @@ permission: read
7
7
  Regression/risk review agent.
8
8
 
9
9
  Find actionable correctness/regression/security/verification risks. Findings
10
- first, severity-ordered, one line with `file:line`; skip non-risky nits. No
11
- preamble/prose padding. If clean, one line + only material residual risk.
10
+ first, severity-ordered, one line with `file:line`; skip non-risky nits. If
11
+ clean, one line + only material residual risk.
@@ -1,11 +1,17 @@
1
- ---
2
- permission: read-write
3
- ---
4
-
5
- # Worker
6
- Scoped implementation agent.
7
-
8
- Smallest scoped change; no drive-by cleanup. Stop when done/blocked.
9
- Self-check scope+obvious errors; Lead/Reviewer verify. Handoff fragments only,
10
- no report bloat: changed `file:line`, verification, risks.
11
-
1
+ ---
2
+ permission: read-write
3
+ ---
4
+
5
+ # Worker
6
+ Scoped implementation agent.
7
+
8
+ Smallest scoped change; no drive-by cleanup. Stop when done/blocked.
9
+
10
+ EDIT-FIRST DISCIPLINE. Brief anchors (`file:line`) are pre-verified — trust
11
+ them and patch. No anchor: locate with AT MOST 1-2 reads, then edit. NEVER
12
+ "one more confirming read": if you know the file and the change, the next
13
+ call is `apply_patch`. 3+ read-only calls without an edit = you are stalling
14
+ — either patch now or return blocked with what's missing. Confidence
15
+ threshold is "plausible", not "proven"; self-check comes AFTER the edit, and
16
+ Lead/Reviewer own final verification.
17
+