@zenera/cli 1.1.24 → 1.1.26

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 (199) hide show
  1. package/README.md +65 -0
  2. package/dist/ask.js +28 -5
  3. package/dist/banner.js +2 -2
  4. package/dist/batch.d.ts +6 -0
  5. package/dist/batch.js +76 -1
  6. package/dist/catalog.d.ts +31 -1
  7. package/dist/catalog.js +140 -7
  8. package/dist/commands/index.js +2 -0
  9. package/dist/commands/inspect.d.ts +52 -2
  10. package/dist/commands/inspect.js +220 -31
  11. package/dist/commands/memory.js +156 -32
  12. package/dist/commands/meta.js +333 -24
  13. package/dist/commands/models.js +97 -12
  14. package/dist/commands/open.js +4 -4
  15. package/dist/commands/run.js +38 -4
  16. package/dist/commands/sandbox.js +148 -16
  17. package/dist/commands/view.d.ts +8 -0
  18. package/dist/commands/view.js +114 -0
  19. package/dist/engine.d.ts +8 -1
  20. package/dist/engine.js +7 -0
  21. package/dist/eventlog.d.ts +64 -0
  22. package/dist/eventlog.js +170 -0
  23. package/dist/liveness.d.ts +7 -0
  24. package/dist/liveness.js +35 -3
  25. package/dist/main.js +0 -0
  26. package/dist/meta/editor.d.ts +9 -0
  27. package/dist/meta/editor.js +43 -0
  28. package/dist/meta/finetune/analyze-brief.d.ts +26 -0
  29. package/dist/meta/finetune/analyze-brief.js +131 -0
  30. package/dist/meta/finetune/apply-brief.d.ts +23 -0
  31. package/dist/meta/finetune/apply-brief.js +247 -0
  32. package/dist/meta/finetune/brief.d.ts +45 -0
  33. package/dist/meta/finetune/brief.js +158 -0
  34. package/dist/meta/finetune/children.d.ts +8 -0
  35. package/dist/meta/finetune/children.js +76 -0
  36. package/dist/meta/finetune/command.d.ts +30 -0
  37. package/dist/meta/finetune/command.js +516 -0
  38. package/dist/meta/finetune/cost.d.ts +46 -0
  39. package/dist/meta/finetune/cost.js +105 -0
  40. package/dist/meta/finetune/dataset/anchors.d.ts +41 -0
  41. package/dist/meta/finetune/dataset/anchors.js +239 -0
  42. package/dist/meta/finetune/dataset/changes.d.ts +36 -0
  43. package/dist/meta/finetune/dataset/changes.js +319 -0
  44. package/dist/meta/finetune/dataset/command.d.ts +17 -0
  45. package/dist/meta/finetune/dataset/command.js +1011 -0
  46. package/dist/meta/finetune/dataset/drift.d.ts +33 -0
  47. package/dist/meta/finetune/dataset/drift.js +71 -0
  48. package/dist/meta/finetune/dataset/export.d.ts +26 -0
  49. package/dist/meta/finetune/dataset/export.js +51 -0
  50. package/dist/meta/finetune/dataset/index.d.ts +2 -0
  51. package/dist/meta/finetune/dataset/index.js +2 -0
  52. package/dist/meta/finetune/dataset/sample.d.ts +50 -0
  53. package/dist/meta/finetune/dataset/sample.js +197 -0
  54. package/dist/meta/finetune/dataset/schema.d.ts +33 -0
  55. package/dist/meta/finetune/dataset/schema.js +216 -0
  56. package/dist/meta/finetune/dataset/store.d.ts +35 -0
  57. package/dist/meta/finetune/dataset/store.js +177 -0
  58. package/dist/meta/finetune/dataset/types.d.ts +127 -0
  59. package/dist/meta/finetune/dataset/types.js +39 -0
  60. package/dist/meta/finetune/failure.d.ts +15 -0
  61. package/dist/meta/finetune/failure.js +26 -0
  62. package/dist/meta/finetune/feedback.d.ts +57 -0
  63. package/dist/meta/finetune/feedback.js +104 -0
  64. package/dist/meta/finetune/guard.d.ts +29 -0
  65. package/dist/meta/finetune/guard.js +104 -0
  66. package/dist/meta/finetune/improvements.d.ts +98 -0
  67. package/dist/meta/finetune/improvements.js +501 -0
  68. package/dist/meta/finetune/index.d.ts +2 -0
  69. package/dist/meta/finetune/index.js +2 -0
  70. package/dist/meta/finetune/journal.d.ts +35 -0
  71. package/dist/meta/finetune/journal.js +135 -0
  72. package/dist/meta/finetune/live.d.ts +84 -0
  73. package/dist/meta/finetune/live.js +272 -0
  74. package/dist/meta/finetune/loop.d.ts +3 -0
  75. package/dist/meta/finetune/loop.js +163 -0
  76. package/dist/meta/finetune/memories.d.ts +47 -0
  77. package/dist/meta/finetune/memories.js +154 -0
  78. package/dist/meta/finetune/recheck.d.ts +58 -0
  79. package/dist/meta/finetune/recheck.js +311 -0
  80. package/dist/meta/finetune/report.d.ts +40 -0
  81. package/dist/meta/finetune/report.js +1397 -0
  82. package/dist/meta/finetune/retry.d.ts +24 -0
  83. package/dist/meta/finetune/retry.js +96 -0
  84. package/dist/meta/finetune/sampler.d.ts +18 -0
  85. package/dist/meta/finetune/sampler.js +47 -0
  86. package/dist/meta/finetune/system.d.ts +27 -0
  87. package/dist/meta/finetune/system.js +132 -0
  88. package/dist/meta/finetune/train.d.ts +43 -0
  89. package/dist/meta/finetune/train.js +425 -0
  90. package/dist/meta/finetune/tui.d.ts +16 -0
  91. package/dist/meta/finetune/tui.js +316 -0
  92. package/dist/meta/finetune/tuning.d.ts +193 -0
  93. package/dist/meta/finetune/tuning.js +308 -0
  94. package/dist/meta/finetune/usage.d.ts +66 -0
  95. package/dist/meta/finetune/usage.js +260 -0
  96. package/dist/meta/finetune/view.d.ts +9 -0
  97. package/dist/meta/finetune/view.js +27 -0
  98. package/dist/meta/host.d.ts +15 -0
  99. package/dist/meta/host.js +21 -0
  100. package/dist/meta/index.d.ts +11 -0
  101. package/dist/meta/index.js +11 -0
  102. package/dist/meta/launch.d.ts +81 -0
  103. package/dist/meta/launch.js +286 -0
  104. package/dist/meta/model.d.ts +44 -0
  105. package/dist/meta/model.js +119 -0
  106. package/dist/meta/prompts.d.ts +18 -0
  107. package/dist/meta/prompts.js +61 -0
  108. package/dist/meta/relay.d.ts +20 -0
  109. package/dist/meta/relay.js +123 -0
  110. package/dist/meta/tokens.d.ts +52 -0
  111. package/dist/meta/tokens.js +226 -0
  112. package/dist/meta/window.d.ts +61 -0
  113. package/dist/meta/window.js +198 -0
  114. package/dist/meta/wire.d.ts +42 -0
  115. package/dist/meta/wire.js +129 -0
  116. package/dist/meta.d.ts +71 -1
  117. package/dist/meta.js +303 -6
  118. package/dist/modelref.d.ts +16 -0
  119. package/dist/modelref.js +41 -0
  120. package/dist/narrate.js +5 -2
  121. package/dist/podman.d.ts +26 -0
  122. package/dist/podman.js +83 -3
  123. package/dist/projects.d.ts +0 -5
  124. package/dist/projects.js +2 -15
  125. package/dist/resources.d.ts +215 -0
  126. package/dist/resources.js +856 -0
  127. package/dist/sandbox.d.ts +2 -0
  128. package/dist/sandbox.js +10 -3
  129. package/dist/scaffold.d.ts +15 -7
  130. package/dist/scaffold.js +24 -67
  131. package/dist/session.js +4 -29
  132. package/dist/term.d.ts +2 -0
  133. package/dist/term.js +11 -2
  134. package/dist/trace.d.ts +5 -0
  135. package/dist/trace.js +107 -7
  136. package/dist/tui/app.js +19 -5
  137. package/dist/tui/links.d.ts +27 -0
  138. package/dist/tui/links.js +142 -0
  139. package/dist/tui/models.d.ts +35 -0
  140. package/dist/tui/models.js +447 -0
  141. package/dist/tui/sandbox.d.ts +10 -0
  142. package/dist/tui/sandbox.js +131 -0
  143. package/dist/tui/wrap.d.ts +5 -0
  144. package/dist/tui/wrap.js +12 -0
  145. package/dist/usage.d.ts +78 -0
  146. package/dist/usage.js +51 -0
  147. package/package.json +4 -2
  148. package/templates/editor/.github/copilot-instructions.md +60 -145
  149. package/templates/editor/.github/prompts/analyze.prompt.md +33 -0
  150. package/templates/editor/.github/prompts/dataset.prompt.md +108 -0
  151. package/templates/editor/.github/prompts/finetune-apply.prompt.md +270 -0
  152. package/templates/editor/.github/prompts/finetune.prompt.md +691 -198
  153. package/templates/editor/.github/prompts/new-agent.prompt.md +3 -1
  154. package/templates/editor/.github/prompts/new-skill.prompt.md +2 -0
  155. package/templates/editor/.github/prompts/project-review.prompt.md +5 -4
  156. package/templates/editor/.github/skills/zen-analyze-run/SKILL.md +588 -0
  157. package/templates/editor/.github/skills/zen-cli/SKILL.md +9 -1
  158. package/templates/editor/.github/skills/zen-cli/references/dataset.md +34 -0
  159. package/templates/editor/.github/skills/zen-cli/references/finetune.md +137 -0
  160. package/templates/editor/.github/skills/zen-cli/references/inspect.md +63 -11
  161. package/templates/editor/.github/skills/zen-cli/references/memory.md +33 -10
  162. package/templates/editor/.github/skills/zen-cli/references/meta.md +61 -0
  163. package/templates/editor/.github/skills/zen-cli/references/models.md +5 -1
  164. package/templates/editor/.github/skills/zen-cli/references/projects.md +4 -6
  165. package/templates/editor/.github/skills/zen-cli/references/rag.md +24 -6
  166. package/templates/editor/.github/skills/zen-cli/references/sandbox.md +35 -14
  167. package/templates/editor/.github/skills/zen-code-python/SKILL.md +123 -0
  168. package/templates/editor/.github/skills/zen-code-python/references/code_python.md +48 -0
  169. package/templates/editor/.github/skills/zen-dataset/SKILL.md +253 -0
  170. package/templates/editor/.github/skills/zen-finetune/SKILL.md +1492 -660
  171. package/templates/editor/.github/skills/zen-finetune/references/readme-template.md +268 -0
  172. package/templates/editor/.github/skills/zen-finetune/scripts/batch.mjs +390 -0
  173. package/templates/editor/.github/skills/zen-finetune/scripts/difficult.mjs +243 -0
  174. package/templates/editor/.github/skills/zen-finetune/scripts/memory.mjs +280 -0
  175. package/templates/editor/.github/skills/zen-finetune/scripts/next.mjs +398 -0
  176. package/templates/editor/.github/skills/zen-finetune/scripts/report.mjs +797 -0
  177. package/templates/editor/.github/skills/zen-finetune/scripts/select.mjs +354 -0
  178. package/templates/editor/.github/skills/zen-finetune/scripts/usage.mjs +722 -0
  179. package/templates/editor/.github/skills/zen-inspect/SKILL.md +74 -26
  180. package/templates/editor/.github/skills/zen-inspect-ask/SKILL.md +271 -0
  181. package/templates/editor/.github/skills/zen-instructions/SKILL.md +4 -12
  182. package/templates/editor/.github/skills/zen-memory/SKILL.md +82 -53
  183. package/templates/editor/.github/skills/zen-rag-docs/SKILL.md +20 -8
  184. package/templates/editor/.github/skills/zen-review/SKILL.md +8 -31
  185. package/templates/editor/.github/skills/zen-review/scripts/check-paths.sh +5 -5
  186. package/templates/editor/.github/skills/zen-review/scripts/review.sh +3 -12
  187. package/templates/editor/.github/skills/zen-sandbox/SKILL.md +9 -4
  188. package/templates/editor/.github/skills/zen-sandbox-capacity/SKILL.md +2 -2
  189. package/templates/editor/.github/skills/zen-spec-sync/SKILL.md +2 -4
  190. package/templates/editor/.github/skills/zen-topology/SKILL.md +283 -0
  191. package/templates/editor/.vscode/settings.json +1 -0
  192. package/templates/project/agents/files-instructions.md +29 -20
  193. package/templates/project/agents/fork-instructions.md +78 -28
  194. package/templates/project/agents/memory-instructions.md +222 -494
  195. package/templates/project/agents/tools-instructions.md +14 -4
  196. package/templates/project/gitignore +2 -0
  197. package/templates/editor/.github/skills/zen-finetune/scripts/collect.sh +0 -432
  198. package/templates/editor/.github/skills/zen-finetune/scripts/sample.sh +0 -199
  199. package/templates/editor/.github/skills/zen-memory/scripts/check-instructions.sh +0 -116
@@ -0,0 +1,425 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { copyFileSync, cpSync, existsSync, mkdirSync, readFileSync, renameSync, rmSync, statSync, writeFileSync, } from 'node:fs';
3
+ import { hostname } from 'node:os';
4
+ import { basename, dirname, join } from 'node:path';
5
+ import { EXIT } from "../host.js";
6
+ import { sessionKept } from "../tokens.js";
7
+ import { writeAnalyzeBrief } from "./analyze-brief.js";
8
+ import { batchInput } from "./dataset/export.js";
9
+ import { DatasetStore } from "./dataset/store.js";
10
+ import { errorIn, sizeOf, StepError } from "./failure.js";
11
+ import { feedbackId, forDataset, forProse, lead, readFeedback, } from "./feedback.js";
12
+ import { attemptsOf, caseTokens, rubricScore, writeFeedback } from "./report.js";
13
+ import { readJson } from "./tuning.js";
14
+ import { envelopesIn, runMetrics, spent, tokensOf, } from "./usage.js";
15
+ // ---------------------------------------------------------------------------
16
+ // One case, start to finish
17
+ //
18
+ // `train` is the whole method: tries without memory until the case passes,
19
+ // then tries with the memory that passing run wrote. Every step leaves its
20
+ // result in the attempt's folder and is skipped when that result is already
21
+ // there, so calling `train` again after a stop carries on where it was.
22
+ // ---------------------------------------------------------------------------
23
+ export async function train(t, s, c) {
24
+ let passed;
25
+ for (let attempt = 1; attempt <= t.config.tries; attempt++) {
26
+ const { run, feedback } = await tryOnce(t, s, c, 'nomem', attempt);
27
+ if (feedback.done) {
28
+ passed = run;
29
+ break;
30
+ }
31
+ const rubricOnly = datasetOnly(feedback);
32
+ if (rubricOnly) {
33
+ return markDifficult(t, c, 'nomem', rubricOnly);
34
+ }
35
+ if (attempt < t.config.tries) {
36
+ const { structural } = await t.improvements.apply(feedback, s);
37
+ if (structural) {
38
+ return markDifficult(t, c, 'nomem', `structural: ${structural}`);
39
+ }
40
+ }
41
+ }
42
+ if (!passed) {
43
+ return markDifficult(t, c, 'nomem', 'wrong');
44
+ }
45
+ let cheaper = false;
46
+ for (let attempt = 1; attempt <= t.config.memTries; attempt++) {
47
+ const { run, feedback } = await tryOnce(t, s, c, 'mem', attempt, passed.memory);
48
+ if (feedback.done) {
49
+ cheaper = true;
50
+ t.memories.submit(c, attempt, run);
51
+ break;
52
+ }
53
+ const rubricOnly = datasetOnly(feedback);
54
+ if (rubricOnly) {
55
+ return markDifficult(t, c, 'mem', rubricOnly);
56
+ }
57
+ if (attempt < t.config.memTries) {
58
+ const { structural } = await t.improvements.apply(feedback, s);
59
+ if (structural) {
60
+ return markDifficult(t, c, 'mem', `structural: ${structural}`);
61
+ }
62
+ }
63
+ }
64
+ if (!cheaper) {
65
+ return markDifficult(t, c, 'mem', 'memory');
66
+ }
67
+ markCompleted(t, c);
68
+ }
69
+ /** Not done, and every finding is about the case itself: another try on the same prose learns nothing. */
70
+ function datasetOnly(f) {
71
+ const dataset = f.improvements.filter(forDataset);
72
+ if (dataset.length === 0 || forProse(f).length > 0) {
73
+ return undefined;
74
+ }
75
+ return `specification: ${dataset.map((i) => lead(i.change)).join('; ')}`;
76
+ }
77
+ /** A run its analysis finds void is set aside and run again: it says nothing about the prose. */
78
+ async function tryOnce(t, s, c, phase, attempt, memory) {
79
+ for (let voids = 1;; voids++) {
80
+ const run = await zenRun(t, s, c, phase, attempt, memory);
81
+ const feedback = await analyze(t, s, c, phase, attempt, run);
82
+ if (feedback.verdict !== 'void') {
83
+ return { run, feedback };
84
+ }
85
+ const dir = t.attemptDir(c, phase, attempt);
86
+ let n = 1;
87
+ while (existsSync(`${dir}.void-${n}`)) {
88
+ n++;
89
+ }
90
+ renameSync(dir, `${dir}.void-${n}`);
91
+ t.event({
92
+ what: 'set aside',
93
+ worker: s.worker?.slot,
94
+ case: c.id,
95
+ phase,
96
+ attempt,
97
+ detail: `void, kept in ${basename(dir)}.void-${n} - running the try again`,
98
+ });
99
+ if (voids >= VOIDS) {
100
+ throw new VoidRun(`${VOIDS} analyses in a row found the run void - the last: ${feedback.infra}`, join(`${dir}.void-${n}`, 'analysis.md'));
101
+ }
102
+ t.checkStopping();
103
+ }
104
+ }
105
+ export class VoidRun extends StepError {
106
+ }
107
+ /** The container engine failed: every run after this one would fail the same way. */
108
+ export class SandboxDown extends StepError {
109
+ }
110
+ /** Killed for memory, or nothing to show for it: the run says nothing about the prose. */
111
+ const VOIDS = 3;
112
+ async function zenRun(t, s, c, phase, attempt, memory) {
113
+ const dir = t.attemptDir(c, phase, attempt);
114
+ const file = join(dir, 'run.json');
115
+ for (let tries = 1; !existsSync(file); tries++) {
116
+ if (tries > VOIDS) {
117
+ throw new VoidRun(`${VOIDS} runs in a row were void`, join(dir, 'run.log'));
118
+ }
119
+ await t.improvements.notApplying();
120
+ t.at(s, phase, attempt, 'run');
121
+ t.improvements.runs++;
122
+ const began = Date.now();
123
+ // The skills the apply after the last try made for this case: this run should load them.
124
+ const expect = attempt > 1
125
+ ? t.improvements.skillsFor(feedbackId(c.id, phase, attempt - 1, t.round(c)))
126
+ : [];
127
+ try {
128
+ const why = await t.journal.step({ kind: 'run', dir }, () => runOnce(t, c, dir, memory, expect));
129
+ const ran = why ? undefined : readJson(file);
130
+ t.ended(s, {
131
+ what: why ? 'void' : 'ran',
132
+ phase,
133
+ attempt,
134
+ result: why ?? (ran?.failed ? `failed: ${ran.failed}` : `${ran?.turns ?? '?'} turns`),
135
+ took: Date.now() - began,
136
+ tokens: spent(ran?.tokens),
137
+ });
138
+ }
139
+ finally {
140
+ t.improvements.runs--;
141
+ }
142
+ }
143
+ return readJson(file);
144
+ }
145
+ /** One `zen run`. Writes run.json, or says why the run was void. */
146
+ export async function runOnce(t, c, dir, memory, expect = []) {
147
+ mkdirSync(dir, { recursive: true });
148
+ const mem = join(dir, 'memory');
149
+ rmSync(mem, { recursive: true, force: true });
150
+ if (memory && existsSync(memory)) {
151
+ cpSync(memory, mem, { recursive: true, filter: (src) => basename(src) !== '.lock' });
152
+ }
153
+ // Else `zen run` roots the agent's files at its cwd: the project, shared by every run.
154
+ const workspace = join(dir, 'workspace');
155
+ rmSync(workspace, { recursive: true, force: true });
156
+ mkdirSync(workspace, { recursive: true });
157
+ const request = join(dir, 'request.json');
158
+ const envelope = join(dir, 'envelope.json');
159
+ const events = join(dir, 'events.jsonl');
160
+ rmSync(envelope, { force: true });
161
+ rmSync(events, { force: true });
162
+ writeFileSync(request, `${JSON.stringify({ input: batchInput([c], t.root, request).batch[0].input }, null, 2)}\n`);
163
+ const log = join(dir, 'run.log');
164
+ const from = existsSync(log) ? statSync(log).size : 0;
165
+ const said = () => existsSync(log) ? readFileSync(log).subarray(from).toString('utf8') : '';
166
+ const res = await t.zen([
167
+ 'run',
168
+ '--plain',
169
+ '--new',
170
+ '--yes',
171
+ '--json',
172
+ // A try is never continued: its containers would only hold podman's locks.
173
+ '--disposable',
174
+ '--input',
175
+ request,
176
+ '--memory',
177
+ mem,
178
+ '--workspace',
179
+ workspace,
180
+ '--out',
181
+ envelope,
182
+ '--events',
183
+ events,
184
+ ], log);
185
+ if (res.code === EXIT.sandbox) {
186
+ throw new SandboxDown(/error\s+(.+)/.exec(said())?.[1]?.trim() ?? 'the sandbox is not ready', log);
187
+ }
188
+ const out = readJson(envelope);
189
+ let runDir = out?.run?.dir;
190
+ let failed;
191
+ if (!runDir) {
192
+ // A failed run throws before the envelope; its directory is in the hint.
193
+ const text = said();
194
+ const report = /report: (\S+report\.html)/.exec(text)?.[1];
195
+ if (!report) {
196
+ return 'no run directory';
197
+ }
198
+ runDir = dirname(report);
199
+ failed = /error\s+(.+)/.exec(text)?.[1]?.trim() ?? 'the run failed';
200
+ }
201
+ const graph = join(runDir, 'graph.mmd');
202
+ if (existsSync(graph) && /exit code 137/.test(readFileSync(graph, 'utf8'))) {
203
+ return 'a command was killed for memory (exit 137)';
204
+ }
205
+ const meta = readJson(join(runDir, 'meta.json'));
206
+ const metrics = runMetrics(runDir);
207
+ if (metrics?.sandbox) {
208
+ throw new SandboxDown(metrics.sandbox, join(runDir, 'graph.mmd'));
209
+ }
210
+ const missed = expect.filter((name) => !metrics?.skills.includes(name));
211
+ const run = {
212
+ dir: runDir,
213
+ memory: mem,
214
+ workspace,
215
+ system: t.system.version(),
216
+ ...(existsSync(join(runDir, 'report.html')) ? { report: join(runDir, 'report.html') } : {}),
217
+ ...(existsSync(graph) ? { graph } : {}),
218
+ ...(failed || meta?.error ? { failed: failed ?? meta?.error } : {}),
219
+ ...(meta?.turns !== undefined ? { turns: meta.turns } : {}),
220
+ ...(meta?.usage ? { usage: meta.usage } : {}),
221
+ ...(metrics ? { tokens: tokensOf(metrics), metrics } : {}),
222
+ ...(meta?.durationMs !== undefined ? { durationMs: meta.durationMs } : {}),
223
+ ...(missed.length > 0 ? { missedSkills: missed } : {}),
224
+ };
225
+ writeFileSync(join(dir, 'run.json'), `${JSON.stringify(run, null, 2)}\n`);
226
+ return undefined;
227
+ }
228
+ // ---------------------------------------------------------------------------
229
+ // zen meta run /analyze
230
+ // ---------------------------------------------------------------------------
231
+ /** One analyze session per try, kept in its folder: the brief carries the earlier tries. */
232
+ async function analyze(t, s, c, phase, attempt, run) {
233
+ const dir = t.attemptDir(c, phase, attempt);
234
+ const file = join(dir, 'feedback.json');
235
+ const known = t.known(c, phase, attempt);
236
+ const kept = readFeedback(file, known);
237
+ let feedback;
238
+ if ('feedback' in kept) {
239
+ feedback = kept.feedback;
240
+ }
241
+ else {
242
+ t.checkStopping();
243
+ t.at(s, phase, attempt, 'analyze');
244
+ const began = Date.now();
245
+ feedback = await t.journal.step({ kind: 'analyze', dir }, () => analyzeOnce(t, s, c, phase, attempt, run, file));
246
+ const graded = Object.values(feedback.rubric);
247
+ t.ended(s, {
248
+ what: 'analyzed',
249
+ phase,
250
+ attempt,
251
+ result: feedback.verdict === 'void'
252
+ ? `void: ${feedback.infra}`
253
+ : `${feedback.verdict}${graded.length ? ` ${rubricScore(feedback)}` : ''}${feedback.done ? ', done' : ''}`,
254
+ took: Date.now() - began,
255
+ tokens: spent(envelopesIn(dir, 'analyze')),
256
+ });
257
+ // Only a try that went all the way through says the sandbox and the providers are up:
258
+ // a run can pass on one provider while the analysis fails on another.
259
+ if (feedback.verdict !== 'void') {
260
+ t.succeeded();
261
+ }
262
+ }
263
+ writeFeedback(t, c);
264
+ return feedback;
265
+ }
266
+ async function analyzeOnce(t, s, c, phase, attempt, run, file) {
267
+ const dir = dirname(file);
268
+ const known = t.known(c, phase, attempt);
269
+ let session = randomUUID();
270
+ writeFileSync(join(dir, 'session'), `${session}\n`);
271
+ const all = attemptsOf(t, c);
272
+ const at = all.findIndex((a) => a.phase === phase && a.attempt === attempt);
273
+ const brief = await writeAnalyzeBrief(t, dir, {
274
+ run,
275
+ case: c,
276
+ phase,
277
+ attempt,
278
+ earlier: at >= 0 ? all.slice(0, at) : all,
279
+ ...(run.missedSkills?.length ? { missed: run.missedSkills } : {}),
280
+ });
281
+ const hash = t.system.hash();
282
+ const applies = t.improvements.started;
283
+ const events = join(dir, 'analyze.events.jsonl');
284
+ rmSync(events, { force: true });
285
+ const continuing = () => [
286
+ 'meta',
287
+ 'run',
288
+ '--no-refresh',
289
+ '--json',
290
+ '--events',
291
+ events,
292
+ ...(sessionKept(session) ? ['--resume', session] : ['--session-id', session]),
293
+ ];
294
+ const log = join(dir, 'analyze.log');
295
+ const from = sizeOf(log);
296
+ const res = await t.zen([
297
+ ...continuing(),
298
+ '--prompt-file',
299
+ brief,
300
+ '/analyze',
301
+ run.dir,
302
+ `case=${c.id}`,
303
+ `rev=${c.rev}`,
304
+ `phase=${phase}`,
305
+ `attempt=${attempt}`,
306
+ `feedback=${file}`,
307
+ ...(run.missedSkills?.length ? [`missed-skills=${run.missedSkills.join(',')}`] : []),
308
+ ], log);
309
+ keepAnalysis(dir, res.stdout);
310
+ // `zen meta run` starts a new session when the meta model changed since this one; follow it.
311
+ const started = sessionIn(res.stdout);
312
+ if (started && started !== session) {
313
+ session = started;
314
+ writeFileSync(join(dir, 'session'), `${session}\n`);
315
+ }
316
+ const rubricIds = c.rubric.map((r) => r.id);
317
+ let read = readFeedback(file, known, rubricIds);
318
+ // An agent that exited on an error would only meet it again; one that wrote a bad file can fix it.
319
+ if (!('feedback' in read) && res.code === 0) {
320
+ const again = await t.zen([
321
+ ...continuing(),
322
+ `The feedback file ${file} is ${read.problem}. Write it again exactly as section 7 of the zen-analyze-run skill describes, then stop.`,
323
+ ], log);
324
+ keepAnalysis(dir, again.stdout, false);
325
+ read = readFeedback(file, known, rubricIds);
326
+ }
327
+ // Any apply that overlapped this analysis owns the change; undoing it would gut the apply.
328
+ const overlapped = t.improvements.applying || t.improvements.started !== applies;
329
+ if (t.system.hash() !== hash && !overlapped) {
330
+ t.system.restore(t.system.version());
331
+ t.event({
332
+ what: 'analyze edited the system - undone',
333
+ worker: s.worker?.slot,
334
+ case: c.id,
335
+ phase,
336
+ attempt,
337
+ });
338
+ }
339
+ if (!('feedback' in read)) {
340
+ const e = errorIn(log, from);
341
+ throw new StepError(`analysis wrote no usable feedback (${read.problem})${e.message ? `: ${e.message}` : ''}`, e.log);
342
+ }
343
+ note(t, c, read.feedback, run, session);
344
+ return read.feedback;
345
+ }
346
+ function sessionIn(stdout) {
347
+ try {
348
+ const id = JSON.parse(stdout).sessionId;
349
+ return typeof id === 'string' && id ? id : undefined;
350
+ }
351
+ catch {
352
+ return undefined;
353
+ }
354
+ }
355
+ /** The meta agent's answer is the human report: kept as analysis.md beside the feedback. A retry keeps its own envelope, so its tokens are counted too. */
356
+ export function keepAnalysis(dir, stdout, replace = true) {
357
+ let envelope;
358
+ try {
359
+ envelope = JSON.parse(stdout);
360
+ }
361
+ catch {
362
+ return;
363
+ }
364
+ writeFileSync(join(dir, replace ? 'analyze.json' : 'analyze-2.json'), stdout);
365
+ if (envelope.answerFile &&
366
+ existsSync(envelope.answerFile) &&
367
+ (replace || !existsSync(join(dir, 'analysis.md')))) {
368
+ copyFileSync(envelope.answerFile, join(dir, 'analysis.md'));
369
+ }
370
+ }
371
+ function note(t, c, f, run, session) {
372
+ const ids = new Set(c.rubric.map((r) => r.id));
373
+ const rubric = Object.fromEntries(Object.entries(f.rubric).filter(([id]) => ids.has(id)));
374
+ DatasetStore.open(t.root).note(c.id, {
375
+ type: 'note',
376
+ at: new Date().toISOString(),
377
+ kind: 'graded',
378
+ caseRev: c.rev,
379
+ run: run.dir,
380
+ verdict: f.verdict,
381
+ ...(Object.keys(rubric).length > 0 ? { rubric } : {}),
382
+ text: f.verdict === 'void' ? `void: ${f.infra}` : lead(f.summary) || f.verdict,
383
+ by: { session, prompt: 'analyze', host: hostname() },
384
+ });
385
+ }
386
+ // ---------------------------------------------------------------------------
387
+ // The end of a case
388
+ // ---------------------------------------------------------------------------
389
+ function finish(t, c, result) {
390
+ const full = {
391
+ ...result,
392
+ caseRev: c.rev,
393
+ at: new Date().toISOString(),
394
+ tokens: caseTokens(t, c),
395
+ };
396
+ mkdirSync(t.caseDir(c), { recursive: true });
397
+ writeFileSync(join(t.caseDir(c), 'result.json'), `${JSON.stringify(full, null, 2)}\n`);
398
+ t.event({
399
+ what: result.state,
400
+ case: c.id,
401
+ rev: c.rev,
402
+ phase: result.phase,
403
+ detail: result.reason,
404
+ });
405
+ writeFeedback(t, c);
406
+ }
407
+ export function markCompleted(t, c) {
408
+ finish(t, c, { state: 'completed', phase: 'mem' });
409
+ }
410
+ export function markDifficult(t, c, phase, reason) {
411
+ finish(t, c, { state: 'difficult', phase, reason });
412
+ DatasetStore.open(t.root).note(c.id, {
413
+ type: 'note',
414
+ at: new Date().toISOString(),
415
+ kind: 'difficult',
416
+ caseRev: c.rev,
417
+ text: `${phase === 'nomem' ? 'without memory' : 'with memory'}: ${reason}`,
418
+ by: { prompt: 'finetune', host: hostname() },
419
+ });
420
+ }
421
+ /** Something outside the prose broke. The case keeps its tries and runs again at the next start. */
422
+ export function markFailed(t, c, phase, reason) {
423
+ finish(t, c, { state: 'failed', phase, reason });
424
+ }
425
+ //# sourceMappingURL=train.js.map
@@ -0,0 +1,16 @@
1
+ import type { Theme } from '../host.ts';
2
+ import type { Snapshot } from './live.ts';
3
+ import type { Tuning } from './tuning.ts';
4
+ export interface Part {
5
+ text: string;
6
+ color?: string;
7
+ bold?: boolean;
8
+ }
9
+ export type Line = Part[];
10
+ /** The overview: under way, tokens, log - as many log rows as the height leaves. */
11
+ export declare function mainLines(s: Snapshot, selected: number, width: number, height: number, theme: Theme, url?: string): Line[];
12
+ /** One step's agent, line by line as it works. */
13
+ export declare function followLines(s: Snapshot, key: string, width: number, height: number, theme: Theme, url?: string): Line[];
14
+ /** Draws the tuning until the returned function is called. */
15
+ export declare function watch(t: Tuning, interrupt: () => void, url?: string): Promise<() => void>;
16
+ //# sourceMappingURL=tui.d.ts.map