micro-models-agent 0.46.1 → 0.46.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 (218) hide show
  1. package/README.md +312 -312
  2. package/dist/main.js +724 -530
  3. package/dist/modules/browser/bridge-server.mjs +202 -202
  4. package/dist/skills/builtin/git.md +36 -36
  5. package/dist/skills/builtin/typescript.md +35 -35
  6. package/package.json +48 -48
  7. package/dist/cli/commands.js +0 -323
  8. package/dist/cli/completer.js +0 -167
  9. package/dist/cli/index.js +0 -2
  10. package/dist/cli/main.js +0 -165
  11. package/dist/cli/plugin-commands.js +0 -36
  12. package/dist/cli/repl-commands.js +0 -661
  13. package/dist/cli/repl.js +0 -616
  14. package/dist/cli/run-result.js +0 -22
  15. package/dist/cli/security-commands.js +0 -164
  16. package/dist/cli/setup.js +0 -231
  17. package/dist/config/config.js +0 -249
  18. package/dist/config/defaults.js +0 -124
  19. package/dist/config/experts.js +0 -15
  20. package/dist/config/index.js +0 -3
  21. package/dist/config/security.js +0 -193
  22. package/dist/config/types.js +0 -1
  23. package/dist/core/agent-moe.js +0 -102
  24. package/dist/core/agent.js +0 -886
  25. package/dist/core/bootstrap.js +0 -404
  26. package/dist/core/index.js +0 -2
  27. package/dist/core/prompt-builder.js +0 -76
  28. package/dist/core/session-logger.js +0 -197
  29. package/dist/core/types.js +0 -1
  30. package/dist/core/version.js +0 -24
  31. package/dist/core/workspace.js +0 -76
  32. package/dist/i18n/en.json +0 -598
  33. package/dist/i18n/index.js +0 -46
  34. package/dist/i18n/ru.json +0 -598
  35. package/dist/index.js +0 -22
  36. package/dist/llm/image-utils.js +0 -143
  37. package/dist/llm/index.js +0 -4
  38. package/dist/llm/model-loader.js +0 -78
  39. package/dist/llm/openai-compat.js +0 -359
  40. package/dist/llm/orchestrator.js +0 -198
  41. package/dist/llm/provider.js +0 -10
  42. package/dist/llm/response.js +0 -39
  43. package/dist/llm/token-counter.js +0 -39
  44. package/dist/llm/types.js +0 -1
  45. package/dist/logger/app-logger.js +0 -143
  46. package/dist/logger/file-log.js +0 -151
  47. package/dist/logger/index.js +0 -1
  48. package/dist/migration/backup.js +0 -45
  49. package/dist/migration/detect.js +0 -50
  50. package/dist/migration/index.js +0 -2
  51. package/dist/modules/artifacts/store.js +0 -61
  52. package/dist/modules/browser/actions.js +0 -76
  53. package/dist/modules/browser/bridge-client.js +0 -199
  54. package/dist/modules/browser/bridge-path.js +0 -10
  55. package/dist/modules/browser/cookie-store.js +0 -24
  56. package/dist/modules/browser/driver.js +0 -136
  57. package/dist/modules/browser/index.js +0 -7
  58. package/dist/modules/browser/module.js +0 -29
  59. package/dist/modules/browser/session.js +0 -338
  60. package/dist/modules/browser/snapshot.js +0 -148
  61. package/dist/modules/browser/types.js +0 -12
  62. package/dist/modules/certification/cli.js +0 -174
  63. package/dist/modules/certification/fact-checker.js +0 -82
  64. package/dist/modules/certification/loader.js +0 -105
  65. package/dist/modules/certification/manifest.js +0 -50
  66. package/dist/modules/certification/runner.js +0 -159
  67. package/dist/modules/certification/scenarios.js +0 -124
  68. package/dist/modules/certification/types.js +0 -1
  69. package/dist/modules/context/chunk-query.js +0 -100
  70. package/dist/modules/context/fact-extractor.js +0 -162
  71. package/dist/modules/context/history.js +0 -15
  72. package/dist/modules/context/index.js +0 -1
  73. package/dist/modules/context/manager.js +0 -423
  74. package/dist/modules/execution/audit-runners.js +0 -152
  75. package/dist/modules/execution/auditor.js +0 -218
  76. package/dist/modules/execution/execution-plugin.js +0 -272
  77. package/dist/modules/execution/index.js +0 -8
  78. package/dist/modules/execution/module.js +0 -436
  79. package/dist/modules/execution/moe-executor.js +0 -291
  80. package/dist/modules/execution/plan-coverage.js +0 -68
  81. package/dist/modules/execution/plan-persister.js +0 -46
  82. package/dist/modules/execution/plan-store.js +0 -157
  83. package/dist/modules/execution/plan-tool.js +0 -508
  84. package/dist/modules/execution/plan-validator.js +0 -153
  85. package/dist/modules/execution/planner.js +0 -90
  86. package/dist/modules/execution/stuck-detector.js +0 -510
  87. package/dist/modules/execution/tracker.js +0 -67
  88. package/dist/modules/execution/types.js +0 -1
  89. package/dist/modules/execution/verifier.js +0 -222
  90. package/dist/modules/execution/windows-commands.js +0 -41
  91. package/dist/modules/hallucination/confidence.js +0 -66
  92. package/dist/modules/hallucination/consistency.js +0 -26
  93. package/dist/modules/hallucination/detector.js +0 -43
  94. package/dist/modules/hallucination/factual.js +0 -129
  95. package/dist/modules/hallucination/index.js +0 -5
  96. package/dist/modules/hallucination/js-identifiers.js +0 -262
  97. package/dist/modules/hallucination/llm-judge.js +0 -101
  98. package/dist/modules/index.js +0 -5
  99. package/dist/modules/indexer/cache.js +0 -40
  100. package/dist/modules/indexer/index.js +0 -3
  101. package/dist/modules/indexer/module.js +0 -245
  102. package/dist/modules/indexer/project-profile.js +0 -183
  103. package/dist/modules/indexer/walker.js +0 -101
  104. package/dist/modules/lsp/check-tool.js +0 -58
  105. package/dist/modules/lsp/client.js +0 -278
  106. package/dist/modules/lsp/command.js +0 -60
  107. package/dist/modules/lsp/config.js +0 -135
  108. package/dist/modules/lsp/index.js +0 -3
  109. package/dist/modules/lsp/module.js +0 -232
  110. package/dist/modules/lsp/probe.js +0 -76
  111. package/dist/modules/lsp/project-root.js +0 -32
  112. package/dist/modules/lsp/startup-check.js +0 -141
  113. package/dist/modules/lsp/types.js +0 -1
  114. package/dist/modules/mcp/client.js +0 -399
  115. package/dist/modules/mcp/index.js +0 -3
  116. package/dist/modules/mcp/module.js +0 -142
  117. package/dist/modules/mcp/registry.js +0 -15
  118. package/dist/modules/memory/index.js +0 -1
  119. package/dist/modules/memory/module.js +0 -96
  120. package/dist/modules/memory/search.js +0 -42
  121. package/dist/modules/memory/store.js +0 -69
  122. package/dist/modules/pipelines/engine.js +0 -60
  123. package/dist/modules/pipelines/index.js +0 -3
  124. package/dist/modules/pipelines/parser.js +0 -56
  125. package/dist/modules/pipelines/template.js +0 -14
  126. package/dist/modules/plugins/builtin/lint-on-write.js +0 -231
  127. package/dist/modules/plugins/builtin/notify.js +0 -9
  128. package/dist/modules/plugins/index.js +0 -1
  129. package/dist/modules/plugins/loader.js +0 -70
  130. package/dist/modules/plugins/manager.js +0 -217
  131. package/dist/modules/plugins/types.js +0 -1
  132. package/dist/modules/processes/detect.js +0 -34
  133. package/dist/modules/processes/index.js +0 -2
  134. package/dist/modules/processes/registry.js +0 -327
  135. package/dist/modules/processes/runner.js +0 -23
  136. package/dist/modules/registry.js +0 -47
  137. package/dist/modules/security/audit-log.js +0 -136
  138. package/dist/modules/security/audit-notifier.js +0 -292
  139. package/dist/modules/security/command-validator.js +0 -205
  140. package/dist/modules/security/content-scanner.js +0 -53
  141. package/dist/modules/security/data-sanitizer.js +0 -89
  142. package/dist/modules/security/encryption.js +0 -242
  143. package/dist/modules/security/index.js +0 -14
  144. package/dist/modules/security/network-validator.js +0 -71
  145. package/dist/modules/security/path-validator.js +0 -207
  146. package/dist/modules/security/rate-limiter.js +0 -119
  147. package/dist/modules/security/security-policies.js +0 -531
  148. package/dist/modules/security/session-encryption.js +0 -210
  149. package/dist/modules/security/session-isolation.js +0 -95
  150. package/dist/modules/session/index.js +0 -3
  151. package/dist/modules/session/manager.js +0 -172
  152. package/dist/modules/session/module.js +0 -24
  153. package/dist/modules/session/store.js +0 -222
  154. package/dist/modules/session/types.js +0 -1
  155. package/dist/modules/skills/index.js +0 -2
  156. package/dist/modules/skills/loader.js +0 -72
  157. package/dist/modules/skills/matcher.js +0 -27
  158. package/dist/modules/skills/module.js +0 -129
  159. package/dist/modules/types.js +0 -1
  160. package/dist/modules/updater/checker.js +0 -96
  161. package/dist/modules/updater/index.js +0 -2
  162. package/dist/modules/updater/module.js +0 -116
  163. package/dist/modules/user-profile/compressor.js +0 -16
  164. package/dist/modules/user-profile/index.js +0 -1
  165. package/dist/modules/user-profile/profile.js +0 -68
  166. package/dist/tools/approve.js +0 -32
  167. package/dist/tools/attach-image.js +0 -89
  168. package/dist/tools/bash.js +0 -496
  169. package/dist/tools/browser.js +0 -114
  170. package/dist/tools/chunk-query.js +0 -99
  171. package/dist/tools/create-dir.js +0 -55
  172. package/dist/tools/delete-file.js +0 -62
  173. package/dist/tools/download-file.js +0 -116
  174. package/dist/tools/edit-file.js +0 -79
  175. package/dist/tools/enable-tools.js +0 -58
  176. package/dist/tools/executor.js +0 -144
  177. package/dist/tools/file-info.js +0 -46
  178. package/dist/tools/filter-tools.js +0 -17
  179. package/dist/tools/glob-tool.js +0 -26
  180. package/dist/tools/grep-tool.js +0 -84
  181. package/dist/tools/hidden-tools-block.js +0 -37
  182. package/dist/tools/index.js +0 -78
  183. package/dist/tools/list-dir.js +0 -48
  184. package/dist/tools/load-skill.js +0 -42
  185. package/dist/tools/mcp-call.js +0 -68
  186. package/dist/tools/move-file.js +0 -85
  187. package/dist/tools/path-utils.js +0 -51
  188. package/dist/tools/pipeline-run.js +0 -144
  189. package/dist/tools/preview.js +0 -2
  190. package/dist/tools/process-kill.js +0 -29
  191. package/dist/tools/process-list.js +0 -36
  192. package/dist/tools/process-log.js +0 -45
  193. package/dist/tools/question.js +0 -140
  194. package/dist/tools/read-file.js +0 -91
  195. package/dist/tools/recall.js +0 -117
  196. package/dist/tools/registry.js +0 -47
  197. package/dist/tools/remember.js +0 -67
  198. package/dist/tools/scope-check.js +0 -30
  199. package/dist/tools/search-history.js +0 -84
  200. package/dist/tools/subagent.js +0 -196
  201. package/dist/tools/types.js +0 -1
  202. package/dist/tools/user-input.js +0 -123
  203. package/dist/tools/web-browse.js +0 -86
  204. package/dist/tools/web-fetch.js +0 -98
  205. package/dist/tools/web-search.js +0 -78
  206. package/dist/tools/write-file.js +0 -81
  207. package/dist/ui/box.js +0 -77
  208. package/dist/ui/colors.js +0 -4
  209. package/dist/ui/diff.js +0 -178
  210. package/dist/ui/index.js +0 -6
  211. package/dist/ui/line-editor.js +0 -703
  212. package/dist/ui/line-math.js +0 -69
  213. package/dist/ui/md-formatter.js +0 -212
  214. package/dist/ui/output.js +0 -13
  215. package/dist/ui/plan-view.js +0 -103
  216. package/dist/ui/renderer.js +0 -209
  217. package/dist/ui/spinner.js +0 -70
  218. package/dist/ui/table.js +0 -144
@@ -1,436 +0,0 @@
1
- import { t } from "../../i18n/index";
2
- import { PlanTracker } from "./tracker";
3
- import { StepVerifier } from "./verifier";
4
- import { StuckDetector } from "./stuck-detector";
5
- import { Auditor, findExistingFile } from "./auditor";
6
- import { PlanStore } from "./plan-store";
7
- import { getMessageText } from "../../llm/provider";
8
- import { extractFileLikeTokens, stripUrls } from "../hallucination/js-identifiers";
9
- import { existsSync, readFileSync } from "fs";
10
- import { resolve } from "path";
11
- import { createPlanToolDefinitions } from "./plan-tool";
12
- import { createExecutionPlugin, STUCK_RECOVERY_COOLDOWN, } from "./execution-plugin";
13
- export class ExecutionModule {
14
- name = "execution";
15
- tracker = null;
16
- verifier;
17
- stuckDetector;
18
- auditor;
19
- store;
20
- baseDir;
21
- // Skip the first audit after restoring a plan from disk — gives the user
22
- // one chance to change the subject before the audit gate locks in.
23
- _auditSkipsRemaining = 0;
24
- /**
25
- * Deferred <system-summary> messages. Injected by onBeforeTool/onAfterTool
26
- * but flushed in onBeforeThink, so they never land between an assistant
27
- * tool_call message and its tool results (which breaks message pairing and
28
- * buries the warnings the model should react to).
29
- */
30
- pendingMessages = [];
31
- // Per-command failure counters for forbidden Windows commands (grep/sed/ls/
32
- // find/… invoked through the bash tool). A hard stop after the second
33
- // failure is evidence-based (actual command failures), not task
34
- // classification (rule #10).
35
- forbiddenBashFailures = new Map();
36
- /**
37
- * The execution-plugin's mutable state. Owned HERE and passed BY REFERENCE
38
- * to the plugin factory — the plugin writes deps.state.* directly, so the
39
- * assignments are visible on the module without any write-back.
40
- */
41
- state = {
42
- lastRecoveryIteration: -STUCK_RECOVERY_COOLDOWN,
43
- consecutivePlanWarnings: 0,
44
- lastStepId: -1,
45
- stuckNotified: false,
46
- depsGateHints: new Map(),
47
- };
48
- /**
49
- * Read/write accessor for the tracker, shared with the plan-tool and
50
- * execution-plugin factories. The `plan` tool's abort action clears it
51
- * (current = null) — the setter keeps this.tracker in sync. Object-literal
52
- * getters bind `this` to the literal, so the instance is captured in a
53
- * closure instead.
54
- */
55
- trackerRef = (() => {
56
- const self = this;
57
- return {
58
- get current() {
59
- return self.tracker;
60
- },
61
- set current(v) {
62
- self.tracker = v;
63
- },
64
- };
65
- })();
66
- constructor(baseDir, stuckThreshold = 6) {
67
- this.baseDir = baseDir;
68
- this.verifier = new StepVerifier(baseDir);
69
- this.stuckDetector = new StuckDetector(stuckThreshold);
70
- this.auditor = new Auditor(baseDir);
71
- this.store = new PlanStore(baseDir);
72
- }
73
- setPlan(plan) {
74
- this.tracker = new PlanTracker(plan);
75
- this.store.saveActive(plan);
76
- this._auditSkipsRemaining = 0; // plan explicitly created — arm the audit gate
77
- }
78
- restorePlan() {
79
- const plan = this.store.loadActive();
80
- if (!plan)
81
- return false;
82
- const restored = new PlanTracker(plan);
83
- // A plan that was already complete when saved must not come back as the
84
- // active plan — archive it so it cannot re-anchor the agent (restored
85
- // completed plans caused the "continue step 4" loop on restart).
86
- if (restored.isComplete()) {
87
- this.store.archivePlan(plan);
88
- this.tracker = null;
89
- return false;
90
- }
91
- this.tracker = restored;
92
- // Advance to the first non-done step
93
- this.tracker.syncCurrentStep();
94
- this._auditSkipsRemaining = 1; // restored plan — one free pass
95
- return true;
96
- }
97
- getTracker() {
98
- return this.tracker;
99
- }
100
- getStore() {
101
- return this.store;
102
- }
103
- getStuckDetector() {
104
- return this.stuckDetector;
105
- }
106
- /**
107
- * Final audit before the agent may declare the task done. Returns null when
108
- * no plan is active; otherwise checks plan completion + artifact existence.
109
- */
110
- async runFinalAudit() {
111
- if (!this.tracker)
112
- return null;
113
- if (this._auditSkipsRemaining > 0) {
114
- this._auditSkipsRemaining--;
115
- return null;
116
- }
117
- const plan = this.tracker.getPlan();
118
- const audit = await this.auditor.audit(plan);
119
- const pendingSteps = plan.steps.flatMap((s) => s.status !== "done" && s.status !== "skipped" ? [`${s.id}. ${s.description}`] : []);
120
- const done = plan.steps.filter((s) => s.status === "done").length;
121
- const passed = audit.passed && pendingSteps.length === 0;
122
- return {
123
- passed,
124
- done,
125
- total: plan.steps.length,
126
- pendingSteps,
127
- missingFiles: audit.missingFiles,
128
- summary: audit.summary,
129
- };
130
- }
131
- getSystemPromptBlock() {
132
- if (!this.tracker || this.tracker.isComplete())
133
- return null;
134
- return {
135
- content: this.tracker.toPromptBlock(),
136
- priority: "critical",
137
- essential: true,
138
- estimatedTokens: 200,
139
- };
140
- }
141
- /**
142
- * Short "current plan" line for the compaction summary. Null when no plan
143
- * is active. For 9B models the explicit instruction "continue it, do NOT
144
- * create a new plan" matters — after compaction the plan block is visible
145
- * but the model re-creates the plan anyway (observed: ses_msvuao0h).
146
- *
147
- * When the plan is complete, the summary says the plan is done and the
148
- * model may create a new plan — NOT "continue step 4, do NOT create a
149
- * new plan" which anchored the ses_msxjbnm4 loop.
150
- */
151
- getPlanSummary() {
152
- if (!this.tracker)
153
- return null;
154
- const plan = this.tracker.getPlan();
155
- const done = plan.steps.filter((s) => s.status === "done").length;
156
- const total = plan.steps.length;
157
- if (this.tracker.isComplete()) {
158
- return `[${plan.id}] "${plan.title}" ${done}/${total} done — plan complete. You may create a new plan for new work (plan create), or reply with your final answer.`;
159
- }
160
- const current = this.tracker.getCurrentStepIndex() + 1;
161
- return `[${plan.id}] "${plan.title}" ${done}/${total} done, current: step ${current} — continue it (plan show / re-plan), do NOT create a new plan`;
162
- }
163
- getToolDefinitions() {
164
- return createPlanToolDefinitions({
165
- store: this.store,
166
- verifier: this.verifier,
167
- trackerRef: this.trackerRef,
168
- preserveActive: () => this.preserveActive(),
169
- setPlan: (p) => this.setPlan(p),
170
- missingStepDeliverables: (s) => this.missingStepDeliverables(s),
171
- stillExistingDeliverables: (s) => this.stillExistingDeliverables(s),
172
- parseKinds: (a, st) => this.parseKinds(a, st),
173
- getTaskText: (c) => this.getTaskText(c),
174
- baseDir: this.baseDir,
175
- });
176
- }
177
- getPlugin() {
178
- return createExecutionPlugin({
179
- stuckDetector: this.stuckDetector,
180
- trackerRef: this.trackerRef,
181
- pendingMessages: this.pendingMessages,
182
- forbiddenBashFailures: this.forbiddenBashFailures,
183
- state: this.state,
184
- checkPlanAlignment: (call) => this.checkPlanAlignment(call),
185
- advancePlanIfStepComplete: (cm, sl) => this.advancePlanIfStepComplete(cm, sl),
186
- });
187
- }
188
- preserveActive() {
189
- if (!this.tracker)
190
- return;
191
- const plan = this.tracker.getPlan();
192
- if (plan.steps.every((s) => s.status === "done" || s.status === "skipped")) {
193
- this.store.archivePlan(plan);
194
- }
195
- else {
196
- this.store.saveDraft(plan);
197
- }
198
- }
199
- checkPlanAlignment(call) {
200
- if (!this.tracker)
201
- return null;
202
- const step = this.tracker.getCurrentStep();
203
- if (!step)
204
- return null;
205
- const allowedAlways = [
206
- "plan",
207
- "todo",
208
- "verify",
209
- "list_dir",
210
- "read_file",
211
- "glob",
212
- "grep",
213
- "file_info",
214
- "load_skill",
215
- ];
216
- if (allowedAlways.includes(call.name))
217
- return null;
218
- const stepPaths = extractFileLikeTokens(stripUrls(step.description)).map((p) => p.toLowerCase());
219
- if (stepPaths.length === 0)
220
- return null;
221
- const argStr = JSON.stringify(call.arguments);
222
- const callPaths = extractFileLikeTokens(stripUrls(argStr)).map((p) => p.toLowerCase());
223
- if (callPaths.length === 0)
224
- return null;
225
- // Files that already belong to COMPLETED (done/skipped) steps are
226
- // legitimate rewrite targets — the agent iterates on them while fixing
227
- // LSP/type errors. Blocking those would stop normal bug-fixing.
228
- const plan = this.tracker.getPlan();
229
- const finishedPaths = new Set();
230
- for (const s of plan.steps) {
231
- if (s.status === "done" || s.status === "skipped") {
232
- extractFileLikeTokens(stripUrls(s.description))
233
- .map((p) => p.toLowerCase())
234
- .forEach((p) => finishedPaths.add(p));
235
- }
236
- }
237
- const offPath = callPaths.some((p) => {
238
- if (finishedPaths.has(p) || [...finishedPaths].some((s) => s.includes(p))) {
239
- return false;
240
- }
241
- return !stepPaths.some((s) => p.includes(s) || s.includes(p));
242
- });
243
- if (!offPath)
244
- return null;
245
- return t("exec.plan_warning", {
246
- step: String(step.id),
247
- description: step.description,
248
- tool: call.name,
249
- });
250
- }
251
- advancePlanIfStepComplete(contextManager, sessionLog) {
252
- const step = this.tracker?.getCurrentStep();
253
- if (!step)
254
- return;
255
- const stepText = step.description.toLowerCase();
256
- // Shared file-token filter (allow-list of real extensions, extension-only
257
- // segments rejected). A step like "Удалить .js, оставить .tsx/.ts" must
258
- // NOT produce the phantom token "tsx/.ts" — that never exists, making
259
- // allGone trivially true and auto-advancing a delete step while the real
260
- // files to delete still exist (the raw regex path did exactly that).
261
- const stepPaths = extractFileLikeTokens(stripUrls(step.description)) || [];
262
- // Step gate: verify dependencies when mentioned — check BEFORE file paths
263
- const isDepsStep = stepText.includes("install") ||
264
- stepText.includes("зависим") ||
265
- stepText.includes("init") ||
266
- stepText.includes("инициализац");
267
- if (isDepsStep) {
268
- const lockFiles = [
269
- "package-lock.json",
270
- "bun.lockb",
271
- "yarn.lock",
272
- "pnpm-lock.yaml",
273
- "node_modules",
274
- "go.sum",
275
- "Cargo.lock",
276
- "Pipfile.lock",
277
- "poetry.lock",
278
- "requirements.txt",
279
- ];
280
- const hasLockFile = lockFiles.some((f) => existsSync(resolve(this.baseDir, f)));
281
- if (!hasLockFile) {
282
- if (contextManager) {
283
- const hints = this.state.depsGateHints.get(step.id) || 0;
284
- this.state.depsGateHints.set(step.id, hints + 1);
285
- const force = hints >= 1;
286
- const content = force
287
- ? t("exec.step_gate_deps_force", { step: String(step.id) })
288
- : t("exec.step_gate_deps", {
289
- step: String(step.id),
290
- description: step.description,
291
- });
292
- contextManager.addMessage({
293
- role: "user",
294
- content: `<system-summary>${content}</system-summary>`,
295
- });
296
- }
297
- return;
298
- }
299
- }
300
- if (stepPaths.length === 0)
301
- return;
302
- const allExist = stepPaths.every((p) => existsSync(resolve(this.baseDir, p)));
303
- const allGone = stepPaths.every((p) => !existsSync(resolve(this.baseDir, p)));
304
- const satisfied = step.kind === "delete" ? allGone && !allExist : allExist;
305
- if (!satisfied)
306
- return;
307
- // Step gate: verify files have content (not empty)
308
- if (step.kind !== "delete") {
309
- const emptyFiles = [];
310
- for (const p of stepPaths) {
311
- try {
312
- const content = readFileSync(resolve(this.baseDir, p), "utf-8");
313
- if (content.trim().length < 10) {
314
- emptyFiles.push(p);
315
- }
316
- }
317
- catch {
318
- // If we can't read it, rely on existsSync result
319
- }
320
- }
321
- if (emptyFiles.length > 0 && contextManager) {
322
- contextManager.addMessage({
323
- role: "user",
324
- content: `<system-summary>${t("exec.step_gate_empty", { step: String(step.id), files: emptyFiles.join(", ") })}</system-summary>`,
325
- });
326
- return;
327
- }
328
- }
329
- // All checks passed — advance
330
- this.tracker?.updateStepStatus(step.id, "done");
331
- const hadNext = this.tracker?.advance() ?? false;
332
- if (this.tracker) {
333
- this.store.saveActive(this.tracker.getPlan());
334
- sessionLog?.plan("auto-advance", `Step ${step.id} auto-completed | ${this.tracker.getProgressString()} | current: step ${this.tracker.getCurrentStepIndex() + 1}`);
335
- }
336
- if (contextManager) {
337
- const nextStep = hadNext ? this.tracker?.getCurrentStep() : null;
338
- const nextMsg = nextStep
339
- ? t("exec.step_gate_ok", {
340
- step: String(step.id),
341
- nextStep: String(nextStep.id),
342
- nextDesc: nextStep.description,
343
- })
344
- : t("exec.step_gate_last", { step: String(step.id) });
345
- contextManager.addMessage({
346
- role: "user",
347
- content: `<system-summary>${nextMsg}</system-summary>`,
348
- });
349
- }
350
- // Auto-archive a plan that just became complete (all steps done/skipped).
351
- // Keeps a finished plan from staying active forever and anchoring the agent
352
- // to a completed task ("continue step 4, do NOT create a new plan" — the
353
- // ses_msxjbnm4 loop). The per-step deliverable gate already verified files.
354
- if (this.tracker?.isComplete()) {
355
- const completed = this.tracker.getPlan();
356
- this.store.archivePlan(completed);
357
- this.tracker = null;
358
- sessionLog?.plan("auto-archive", `Plan ${completed.id} complete — archived`);
359
- }
360
- }
361
- /**
362
- * File tokens named in a step description that do not resolve to a real
363
- * file. Uses the same shared token extractor (allow-list of real file
364
- * extensions, URLs stripped) and the auditor's subtree resolver, so
365
- * "bicycles.ts" finds "bicycle-shop/src/data/bicycles.ts" and a hostname in
366
- * the description is never reported as a missing deliverable. Steps with no
367
- * file tokens (pure commands, verification runs) cannot be verified.
368
- */
369
- missingStepDeliverables(step) {
370
- const tokens = extractFileLikeTokens(stripUrls(step.description));
371
- if (tokens.length === 0)
372
- return [];
373
- return tokens.filter((p) => !findExistingFile(this.baseDir, p));
374
- }
375
- /**
376
- * File tokens named in a step description that still resolve to a real
377
- * file. The inverse of missingStepDeliverables — used by the gate for
378
- * delete-kind steps ("done" means the files are gone).
379
- */
380
- stillExistingDeliverables(step) {
381
- const tokens = extractFileLikeTokens(stripUrls(step.description));
382
- if (tokens.length === 0)
383
- return [];
384
- return tokens.filter((p) => findExistingFile(this.baseDir, p));
385
- }
386
- /**
387
- * Validate the optional per-step `kinds` argument for plan create and
388
- * update-with-steps. Returns length-aligned kinds or an i18n error string.
389
- * Absent kinds → null (caller falls back to all-create semantics). Used by
390
- * BOTH branches so a rebuild via update can never silently drop kinds.
391
- */
392
- parseKinds(args, steps) {
393
- if (args.kinds === undefined)
394
- return { kinds: null, error: null };
395
- if (!Array.isArray(args.kinds)) {
396
- return { kinds: null, error: t("plan.kinds_not_array") };
397
- }
398
- const kindsRaw = args.kinds.map(String);
399
- if (kindsRaw.length !== steps.length) {
400
- return { kinds: null, error: t("plan.kinds_mismatch") };
401
- }
402
- const invalid = kindsRaw.filter((k) => k !== "create" && k !== "delete");
403
- if (invalid.length > 0) {
404
- return {
405
- kinds: null,
406
- error: t("plan.kinds_invalid", {
407
- kinds: invalid.join(", "),
408
- }),
409
- };
410
- }
411
- return { kinds: kindsRaw, error: null };
412
- }
413
- /**
414
- * Extract the original task statement from the first user message in the
415
- * conversation history. Used to validate plan coverage against requirements.
416
- */
417
- getTaskText(ctx) {
418
- const manager = ctx?.contextManager;
419
- if (!manager)
420
- return "";
421
- const history = manager.getActiveHistory();
422
- // The first user message may be an injected <system-summary> (compaction
423
- // block) — it is NOT the task. Coverage must compare the plan against the
424
- // model's real instruction (observed: a compaction summary's stale file
425
- // list produced a bogus "plan is missing files" warning).
426
- const firstUser = history.find((m) => {
427
- if (m.role !== "user")
428
- return false;
429
- const text = getMessageText(m.content);
430
- return !text.startsWith("<system-summary>") && !text.includes("[Compressed:");
431
- });
432
- if (!firstUser)
433
- return "";
434
- return getMessageText(firstUser.content).trim();
435
- }
436
- }