@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,84 @@
1
+ import type { EventLine } from '../host.ts';
2
+ import type { StepFailure, Tuning } from './tuning.ts';
3
+ import { type Stage, type Tokens } from './usage.ts';
4
+ export type ModelSpend = Tokens;
5
+ /** One events file, folded as it grows. */
6
+ export interface Activity {
7
+ model?: string;
8
+ calls: number;
9
+ input: number;
10
+ output: number;
11
+ byModel: Record<string, ModelSpend>;
12
+ /** the latest tool call, with what was said just before it, or the latest thing said */
13
+ last?: string;
14
+ /** said since the last tool call */
15
+ said?: string;
16
+ /** what the agent is doing now: `says: …`, `thinks: …` or `runs: …` */
17
+ now?: string;
18
+ /** the last few `now` lines, oldest first */
19
+ recent: string[];
20
+ /** the latest lines, oldest first */
21
+ lines: EventLine[];
22
+ }
23
+ export interface NowRow {
24
+ /** stable across snapshots: `w1`, `apply`, `merge` */
25
+ key: string;
26
+ who: string;
27
+ step: string;
28
+ what: string;
29
+ sinceMs: number;
30
+ stage?: Stage;
31
+ act?: Activity;
32
+ /** what it is doing now, in one line */
33
+ now?: string;
34
+ /** the last few such lines, oldest first */
35
+ recent?: string[];
36
+ }
37
+ export interface TokenRow extends ModelSpend {
38
+ stage: Stage;
39
+ model: string;
40
+ }
41
+ export interface LogRow {
42
+ at: string;
43
+ action: string;
44
+ case: string;
45
+ result: string;
46
+ took?: number;
47
+ tokens?: number;
48
+ }
49
+ export interface Snapshot {
50
+ pid: number;
51
+ project: string;
52
+ state: 'running' | 'stopping' | 'stopped' | 'finished';
53
+ at: string;
54
+ elapsedMs: number;
55
+ system: number;
56
+ workers: {
57
+ busy: number;
58
+ all: number;
59
+ };
60
+ total: number;
61
+ counts: Record<string, number>;
62
+ parked: number;
63
+ applyAt: number;
64
+ now: NowRow[];
65
+ tokens: TokenRow[];
66
+ log: LogRow[];
67
+ /** the latest step failures, newest first */
68
+ errors: StepFailure[];
69
+ }
70
+ /** What the applier runs once the agent is done. */
71
+ export declare const CHECK_NOW = "runs: zen check --no-models --no-sandbox";
72
+ /** `now` lines a step keeps. */
73
+ export declare const KEEP_NOW = 6;
74
+ /** Reads only what each events file gained since the last look; a file cleared for a new step starts over. */
75
+ export declare class Tails {
76
+ #private;
77
+ read(file: string): Activity;
78
+ /** Forgets the files no step writes any more. */
79
+ keep(files: ReadonlySet<string>): void;
80
+ }
81
+ export declare function snapshot(t: Tuning): Snapshot;
82
+ /** Takes a snapshot, keeps it in live.json without the raw lines, and hands it to every watcher. */
83
+ export declare function publish(t: Tuning): Snapshot | undefined;
84
+ //# sourceMappingURL=live.d.ts.map
@@ -0,0 +1,272 @@
1
+ import { closeSync, fstatSync, mkdirSync, openSync, readSync, renameSync, writeFileSync, } from 'node:fs';
2
+ import { basename, join } from 'node:path';
3
+ import { modelName, STAGES } from "./usage.js";
4
+ const KEEP_LINES = 200;
5
+ const KEEP_LOG = 50;
6
+ const KEEP_ERRORS = 5;
7
+ const PHASE_WORD = { nomem: 'no memory', mem: 'with memory' };
8
+ /** Not log rows: a start shows in `now` and ends in an event of its own, an error has its own section. */
9
+ const UNLOGGED = new Set(['run', 'analyze', 'apply', 'memory merge', 'idle', 'error']);
10
+ /** Shell tools: their subject is the command itself. */
11
+ const SHELL = new Set(['bash', 'run_command', 'run_command_background']);
12
+ /** What the applier runs once the agent is done. */
13
+ export const CHECK_NOW = 'runs: zen check --no-models --no-sandbox';
14
+ /** `now` lines a step keeps. */
15
+ export const KEEP_NOW = 6;
16
+ const blank = () => ({
17
+ calls: 0,
18
+ input: 0,
19
+ output: 0,
20
+ byModel: {},
21
+ recent: [],
22
+ lines: [],
23
+ });
24
+ function doing(a, line) {
25
+ a.now = line;
26
+ a.recent.push(line);
27
+ if (a.recent.length > KEEP_NOW) {
28
+ a.recent.shift();
29
+ }
30
+ }
31
+ function fold(a, text) {
32
+ let e;
33
+ try {
34
+ e = JSON.parse(text);
35
+ }
36
+ catch {
37
+ return;
38
+ }
39
+ a.lines.push(e);
40
+ if (a.lines.length > KEEP_LINES) {
41
+ a.lines.shift();
42
+ }
43
+ if (e.type === 'llm') {
44
+ const model = modelName(e.model);
45
+ a.model = model;
46
+ a.calls++;
47
+ a.input += e.in;
48
+ a.output += e.out;
49
+ const m = (a.byModel[model] ??= { calls: 0, input: 0, cached: 0, output: 0, reasoning: 0 });
50
+ m.calls++;
51
+ m.input += e.in;
52
+ m.cached += e.cached;
53
+ m.output += e.out;
54
+ m.reasoning += e.reasoning;
55
+ }
56
+ else if (e.type === 'tool' && e.phase === 'start') {
57
+ const tool = `${e.name} ${e.subject}`.trim();
58
+ a.last = a.said ? `${a.said} · ${tool}` : tool;
59
+ a.said = undefined;
60
+ doing(a, `runs: ${SHELL.has(e.name) && e.subject ? e.subject : tool}`);
61
+ }
62
+ else if (e.type === 'say' || e.type === 'think') {
63
+ // A call's thought comes before what it said, so the words win.
64
+ a.said = e.text;
65
+ a.last = e.text;
66
+ doing(a, `${e.type === 'say' ? 'says' : 'thinks'}: ${e.text}`);
67
+ }
68
+ }
69
+ /** Reads only what each events file gained since the last look; a file cleared for a new step starts over. */
70
+ export class Tails {
71
+ #files = new Map();
72
+ read(file) {
73
+ let fd;
74
+ try {
75
+ fd = openSync(file, 'r');
76
+ }
77
+ catch {
78
+ this.#files.delete(file);
79
+ return blank();
80
+ }
81
+ try {
82
+ const st = fstatSync(fd);
83
+ let f = this.#files.get(file);
84
+ if (!f || f.ino !== st.ino || st.size < f.offset) {
85
+ f = { ino: st.ino, offset: 0, rest: '', act: blank() };
86
+ this.#files.set(file, f);
87
+ }
88
+ if (st.size > f.offset) {
89
+ const buf = Buffer.alloc(st.size - f.offset);
90
+ readSync(fd, buf, 0, buf.length, f.offset);
91
+ f.offset = st.size;
92
+ const parts = (f.rest + buf.toString('utf8')).split('\n');
93
+ f.rest = parts.pop() ?? '';
94
+ for (const line of parts) {
95
+ fold(f.act, line);
96
+ }
97
+ }
98
+ return f.act;
99
+ }
100
+ finally {
101
+ closeSync(fd);
102
+ }
103
+ }
104
+ /** Forgets the files no step writes any more. */
105
+ keep(files) {
106
+ for (const file of this.#files.keys()) {
107
+ if (!files.has(file)) {
108
+ this.#files.delete(file);
109
+ }
110
+ }
111
+ }
112
+ }
113
+ const tails = new WeakMap();
114
+ function stateOf(t) {
115
+ if (t.finished) {
116
+ return t.stopping ? 'stopped' : 'finished';
117
+ }
118
+ return t.stopping ? 'stopping' : 'running';
119
+ }
120
+ function logRow(e) {
121
+ const where = e.phase ? `${PHASE_WORD[e.phase]}${e.attempt ? ` try ${e.attempt}` : ''}` : '';
122
+ return {
123
+ at: e.at,
124
+ action: e.what,
125
+ case: [e.case, where].filter(Boolean).join(' · '),
126
+ result: e.result ?? e.detail ?? '',
127
+ ...(e.took !== undefined ? { took: e.took } : {}),
128
+ ...(e.tokens ? { tokens: e.tokens } : {}),
129
+ };
130
+ }
131
+ export function snapshot(t) {
132
+ let tail = tails.get(t);
133
+ if (!tail) {
134
+ tail = new Tails();
135
+ tails.set(t, tail);
136
+ }
137
+ const now = [];
138
+ const files = new Set();
139
+ const watch = (file) => {
140
+ files.add(file);
141
+ return tail.read(file);
142
+ };
143
+ const clock = Date.now();
144
+ for (const w of t.workers) {
145
+ if (!w.case || (w.step !== 'run' && w.step !== 'analyze')) {
146
+ continue;
147
+ }
148
+ const dir = t.workDir({ ...w, case: w.case });
149
+ const act = watch(join(dir, w.step === 'run' ? 'events.jsonl' : 'analyze.events.jsonl'));
150
+ now.push({
151
+ key: `w${w.slot}`,
152
+ who: `worker ${w.slot}`,
153
+ step: w.step,
154
+ what: `${w.case.id} · ${w.recheck ? 'recheck' : PHASE_WORD[w.phase ?? 'nomem']} try ${w.attempt ?? 1}`,
155
+ sinceMs: clock - w.since,
156
+ stage: w.step,
157
+ act,
158
+ ...(act.now ? { now: act.now, recent: [...act.recent] } : {}),
159
+ });
160
+ }
161
+ const q = t.improvements;
162
+ if (q.current?.dir) {
163
+ const checking = q.current.step === 'check';
164
+ const act = watch(join(q.current.dir, 'apply.events.jsonl'));
165
+ const line = checking ? CHECK_NOW : act.now;
166
+ const recent = checking ? [...act.recent, CHECK_NOW].slice(-KEEP_NOW) : [...act.recent];
167
+ now.push({
168
+ key: 'apply',
169
+ who: 'applier',
170
+ step: checking ? 'check' : 'apply',
171
+ what: `apply ${basename(q.current.dir)} · ${q.current.requests} request(s)`,
172
+ sinceMs: clock - q.current.since,
173
+ stage: 'apply',
174
+ act,
175
+ ...(line ? { now: line, recent } : {}),
176
+ });
177
+ }
178
+ else if (q.current) {
179
+ now.push({
180
+ key: 'apply',
181
+ who: 'applier',
182
+ step: 'waiting',
183
+ what: `${q.pending.length} request(s), ${q.runs} run(s) to finish first`,
184
+ sinceMs: clock - q.current.since,
185
+ });
186
+ }
187
+ const merge = t.memories.merging;
188
+ if (merge) {
189
+ now.push({
190
+ key: 'merge',
191
+ who: 'memory',
192
+ step: 'merge',
193
+ what: merge.name,
194
+ sinceMs: clock - merge.since,
195
+ });
196
+ }
197
+ tail.keep(files);
198
+ const sums = new Map();
199
+ const add = (stage, model, s) => {
200
+ const key = `${stage}\u0000${model}`;
201
+ const row = sums.get(key) ?? {
202
+ stage,
203
+ model,
204
+ calls: 0,
205
+ input: 0,
206
+ cached: 0,
207
+ output: 0,
208
+ reasoning: 0,
209
+ };
210
+ row.calls += s.calls;
211
+ row.input += s.input;
212
+ row.cached += s.cached;
213
+ row.output += s.output;
214
+ row.reasoning += s.reasoning;
215
+ sums.set(key, row);
216
+ };
217
+ for (const stage of STAGES) {
218
+ for (const [model, s] of Object.entries(t.progress?.spent[stage] ?? {})) {
219
+ add(stage, model, s);
220
+ }
221
+ }
222
+ for (const row of now) {
223
+ for (const [model, s] of Object.entries(row.act?.byModel ?? {})) {
224
+ add(row.stage, model, s);
225
+ }
226
+ }
227
+ const tokens = [...sums.values()]
228
+ .filter((r) => r.calls > 0)
229
+ .sort((a, b) => STAGES.indexOf(a.stage) - STAGES.indexOf(b.stage) ||
230
+ b.input + b.output - (a.input + a.output));
231
+ return {
232
+ pid: process.pid,
233
+ project: basename(t.root),
234
+ state: stateOf(t),
235
+ at: new Date(clock).toISOString(),
236
+ elapsedMs: clock - t.startedAt,
237
+ system: t.system.version(),
238
+ workers: { busy: t.busy(), all: t.workers.length },
239
+ total: t.progress?.total ?? 0,
240
+ counts: t.progress?.counts ?? {},
241
+ parked: q.pending.length,
242
+ applyAt: t.config.applyAt,
243
+ now,
244
+ tokens,
245
+ log: t.recent
246
+ .filter((e) => !UNLOGGED.has(e.what))
247
+ .slice(0, KEEP_LOG)
248
+ .map(logRow),
249
+ errors: t.errors.slice(0, KEEP_ERRORS),
250
+ };
251
+ }
252
+ /** Takes a snapshot, keeps it in live.json without the raw lines, and hands it to every watcher. */
253
+ export function publish(t) {
254
+ let snap;
255
+ try {
256
+ snap = snapshot(t);
257
+ const file = join(t.dir, 'live.json');
258
+ mkdirSync(t.dir, { recursive: true });
259
+ writeFileSync(`${file}.tmp`, `${JSON.stringify(snap, (k, v) => (k === 'lines' ? undefined : v), 2)}\n`);
260
+ renameSync(`${file}.tmp`, file);
261
+ }
262
+ catch {
263
+ // A live view that failed to draw must not stop the tuning.
264
+ return undefined;
265
+ }
266
+ t.live = snap;
267
+ for (const watcher of t.watchers) {
268
+ watcher(snap);
269
+ }
270
+ return snap;
271
+ }
272
+ //# sourceMappingURL=live.js.map
@@ -0,0 +1,3 @@
1
+ import { type Tuning } from './tuning.ts';
2
+ export declare function runLoop(t: Tuning): Promise<void>;
3
+ //# sourceMappingURL=loop.d.ts.map
@@ -0,0 +1,163 @@
1
+ import { claimLock, ownLock } from '@zenera/neo';
2
+ import { mkdirSync, rmSync, unlinkSync } from 'node:fs';
3
+ import { join } from 'node:path';
4
+ import { CliError, EXIT } from "../host.js";
5
+ import { StepError } from "./failure.js";
6
+ import { recover } from "./journal.js";
7
+ import { publish } from "./live.js";
8
+ import { nextCase, resultOf } from "./sampler.js";
9
+ import { markFailed, SandboxDown, train, VoidRun } from "./train.js";
10
+ import { Stopped } from "./tuning.js";
11
+ // ---------------------------------------------------------------------------
12
+ // The loop: N workers, cases started as they come free, one applier
13
+ //
14
+ // Whenever a worker is free - and no woken case is waiting for one - the next
15
+ // recheck, or else the next case, starts on it and is trained to the end. A
16
+ // parked case gives its worker back, so a lane never sits idle waiting for an
17
+ // apply. A stop never interrupts a step: it is noticed between steps, and the
18
+ // case resumes next time.
19
+ // ---------------------------------------------------------------------------
20
+ const REPORT_EVERY_MS = 15_000;
21
+ const LIVE_EVERY_MS = 1000;
22
+ export async function runLoop(t) {
23
+ mkdirSync(t.dir, { recursive: true });
24
+ const lock = join(t.dir, 'loop.lock');
25
+ claimLock(lock, ownLock(), (held) => new CliError(`a tuning is already running here (pid ${held.pid}, since ${held.startedAt})`, EXIT.failed, `stop it with: zen meta finetune stop - or remove ${lock} if it crashed`));
26
+ rmSync(t.stopFile, { force: true });
27
+ const timer = setInterval(() => t.report(), REPORT_EVERY_MS);
28
+ const live = setInterval(() => {
29
+ publish(t);
30
+ t.report(true);
31
+ }, LIVE_EVERY_MS);
32
+ try {
33
+ await recover(t);
34
+ t.rechecks.resume();
35
+ const start = t.system.record('start');
36
+ await t.improvements.baseline();
37
+ t.event({ what: 'started', detail: `system v${start.v}, ${t.workers.length} workers` });
38
+ publish(t);
39
+ const applier = t.improvements.loop();
40
+ await dispatch(t);
41
+ t.finished = true;
42
+ await applier;
43
+ await t.memories.finish();
44
+ t.event({ what: t.stopping ? 'stopped' : 'finished' });
45
+ }
46
+ finally {
47
+ clearInterval(timer);
48
+ clearInterval(live);
49
+ t.finished = true;
50
+ publish(t);
51
+ t.report();
52
+ try {
53
+ unlinkSync(lock);
54
+ }
55
+ catch {
56
+ // Taken over as stale; nothing to give back.
57
+ }
58
+ }
59
+ if (t.fatal) {
60
+ throw t.fatal;
61
+ }
62
+ }
63
+ async function dispatch(t) {
64
+ const running = new Set();
65
+ const start = (seat, work) => {
66
+ const task = work(seat).finally(() => running.delete(task));
67
+ running.add(task);
68
+ };
69
+ while (!t.stopping) {
70
+ if (t.improvements.applying || t.resuming > 0 || !t.freeWorker()) {
71
+ await t.tick();
72
+ continue;
73
+ }
74
+ // A recheck tests the prose of the day, so it goes before a new case.
75
+ const taken = t.rechecks.take();
76
+ if (taken) {
77
+ const seat = { case: taken.case };
78
+ t.claimed.add(taken.case.id);
79
+ await t.seat(seat, false);
80
+ start(seat, (s) => recheckCase(t, s, taken.recheck));
81
+ continue;
82
+ }
83
+ const c = nextCase(t);
84
+ if (!c) {
85
+ t.drained = true;
86
+ // An apply still to come queues rechecks; only when none can is the work done.
87
+ const more = running.size > 0 ||
88
+ t.improvements.applying ||
89
+ t.improvements.pending.length > 0 ||
90
+ t.rechecks.waiting() > 0;
91
+ if (!more) {
92
+ break;
93
+ }
94
+ await t.tick();
95
+ continue;
96
+ }
97
+ const seat = { case: c };
98
+ t.claimed.add(c.id);
99
+ t.tried.add(c.id);
100
+ const before = resultOf(t, c);
101
+ if (before?.state === 'failed') {
102
+ // Taken again: until it ends anew it is in progress, not failed.
103
+ rmSync(join(t.caseDir(c), 'result.json'), { force: true });
104
+ t.event({ what: 'resumed', case: c.id, rev: c.rev, detail: before.reason });
105
+ }
106
+ await t.seat(seat, false);
107
+ start(seat, (s) => trainCase(t, s));
108
+ }
109
+ await Promise.all(running);
110
+ }
111
+ async function recheckCase(t, s, r) {
112
+ try {
113
+ await t.rechecks.run(s, r, s.case);
114
+ }
115
+ catch (err) {
116
+ if (!(err instanceof Stopped)) {
117
+ t.error(`${s.case.id} · recheck after ${r.apply}`, err.message, err instanceof StepError ? err.log : undefined);
118
+ if (err instanceof SandboxDown) {
119
+ // Left running: the next start queues it again.
120
+ t.sandboxDown(err.message);
121
+ }
122
+ else {
123
+ t.rechecks.void(r, err.message);
124
+ t.failed(`recheck: ${err.message}`);
125
+ }
126
+ }
127
+ }
128
+ finally {
129
+ t.unseat(s);
130
+ t.claimed.delete(s.case.id);
131
+ t.report();
132
+ }
133
+ }
134
+ async function trainCase(t, s) {
135
+ const c = s.case;
136
+ try {
137
+ await train(t, s, c);
138
+ }
139
+ catch (err) {
140
+ if (!(err instanceof Stopped)) {
141
+ const why = err instanceof SandboxDown
142
+ ? `sandbox: ${err.message}`
143
+ : err instanceof VoidRun
144
+ ? `void: ${err.message}`
145
+ : `error: ${err.message}`;
146
+ markFailed(t, c, s.phase ?? 'nomem', why);
147
+ const attempt = s.worker?.attempt;
148
+ t.error(`${c.id} · ${s.phase === 'mem' ? 'with memory' : 'no memory'}${attempt ? ` try ${attempt}` : ''}`, err.message, err instanceof StepError ? err.log : undefined);
149
+ if (err instanceof SandboxDown) {
150
+ t.sandboxDown(err.message);
151
+ }
152
+ else {
153
+ t.failed(why);
154
+ }
155
+ }
156
+ }
157
+ finally {
158
+ t.unseat(s);
159
+ t.claimed.delete(c.id);
160
+ t.report();
161
+ }
162
+ }
163
+ //# sourceMappingURL=loop.js.map
@@ -0,0 +1,47 @@
1
+ import type { Case } from './dataset/types.ts';
2
+ import type { Run } from './train.ts';
3
+ import { type Tuning } from './tuning.ts';
4
+ export interface Submission {
5
+ /** `<case>@r<rev>` */
6
+ key: string;
7
+ case: string;
8
+ rev: number;
9
+ attempt: number;
10
+ run: string;
11
+ dir: string;
12
+ at: string;
13
+ /** the run committed nothing: there is no graph to merge */
14
+ empty: boolean;
15
+ }
16
+ export interface MergeRow {
17
+ n: number;
18
+ name: string;
19
+ at: string;
20
+ dir: string;
21
+ log: string;
22
+ /** every submission in this graph */
23
+ includes: string[];
24
+ /** the ones this merge brought in */
25
+ added: string[];
26
+ ok: boolean;
27
+ nodes?: number;
28
+ edges?: number;
29
+ }
30
+ export declare class Memories {
31
+ #private;
32
+ merging: {
33
+ name: string;
34
+ since: number;
35
+ } | undefined;
36
+ constructor(t: Tuning);
37
+ get dir(): string;
38
+ submissions(): Submission[];
39
+ merges(): MergeRow[];
40
+ lastMerge(): MergeRow | undefined;
41
+ unmerged(): Submission[];
42
+ /** Keeps a copy of a passing with-memory run's memory. Once per case revision. */
43
+ submit(c: Case, attempt: number, run: Run): void;
44
+ /** Merges whatever is left, and waits for every merge queued. */
45
+ finish(): Promise<void>;
46
+ }
47
+ //# sourceMappingURL=memories.d.ts.map