@zenera/cli 1.1.24 → 1.1.25

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 (167) hide show
  1. package/README.md +53 -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 +11 -1
  7. package/dist/catalog.js +80 -7
  8. package/dist/commands/inspect.d.ts +47 -1
  9. package/dist/commands/inspect.js +167 -18
  10. package/dist/commands/memory.js +128 -29
  11. package/dist/commands/meta.js +311 -21
  12. package/dist/commands/models.js +96 -12
  13. package/dist/commands/open.js +4 -4
  14. package/dist/commands/run.js +38 -4
  15. package/dist/commands/sandbox.js +76 -14
  16. package/dist/engine.d.ts +5 -0
  17. package/dist/engine.js +1 -0
  18. package/dist/eventlog.d.ts +59 -0
  19. package/dist/eventlog.js +146 -0
  20. package/dist/liveness.d.ts +7 -0
  21. package/dist/liveness.js +15 -1
  22. package/dist/meta/editor.d.ts +9 -0
  23. package/dist/meta/editor.js +60 -0
  24. package/dist/meta/finetune/children.d.ts +8 -0
  25. package/dist/meta/finetune/children.js +76 -0
  26. package/dist/meta/finetune/command.d.ts +30 -0
  27. package/dist/meta/finetune/command.js +338 -0
  28. package/dist/meta/finetune/dataset/anchors.d.ts +41 -0
  29. package/dist/meta/finetune/dataset/anchors.js +239 -0
  30. package/dist/meta/finetune/dataset/changes.d.ts +36 -0
  31. package/dist/meta/finetune/dataset/changes.js +319 -0
  32. package/dist/meta/finetune/dataset/command.d.ts +17 -0
  33. package/dist/meta/finetune/dataset/command.js +1011 -0
  34. package/dist/meta/finetune/dataset/drift.d.ts +33 -0
  35. package/dist/meta/finetune/dataset/drift.js +71 -0
  36. package/dist/meta/finetune/dataset/export.d.ts +26 -0
  37. package/dist/meta/finetune/dataset/export.js +51 -0
  38. package/dist/meta/finetune/dataset/index.d.ts +2 -0
  39. package/dist/meta/finetune/dataset/index.js +2 -0
  40. package/dist/meta/finetune/dataset/sample.d.ts +50 -0
  41. package/dist/meta/finetune/dataset/sample.js +197 -0
  42. package/dist/meta/finetune/dataset/schema.d.ts +33 -0
  43. package/dist/meta/finetune/dataset/schema.js +216 -0
  44. package/dist/meta/finetune/dataset/store.d.ts +35 -0
  45. package/dist/meta/finetune/dataset/store.js +177 -0
  46. package/dist/meta/finetune/dataset/types.d.ts +125 -0
  47. package/dist/meta/finetune/dataset/types.js +39 -0
  48. package/dist/meta/finetune/failure.d.ts +15 -0
  49. package/dist/meta/finetune/failure.js +26 -0
  50. package/dist/meta/finetune/feedback.d.ts +45 -0
  51. package/dist/meta/finetune/feedback.js +85 -0
  52. package/dist/meta/finetune/improvements.d.ts +51 -0
  53. package/dist/meta/finetune/improvements.js +244 -0
  54. package/dist/meta/finetune/index.d.ts +2 -0
  55. package/dist/meta/finetune/index.js +2 -0
  56. package/dist/meta/finetune/journal.d.ts +35 -0
  57. package/dist/meta/finetune/journal.js +135 -0
  58. package/dist/meta/finetune/live.d.ts +74 -0
  59. package/dist/meta/finetune/live.js +226 -0
  60. package/dist/meta/finetune/loop.d.ts +3 -0
  61. package/dist/meta/finetune/loop.js +114 -0
  62. package/dist/meta/finetune/memories.d.ts +47 -0
  63. package/dist/meta/finetune/memories.js +154 -0
  64. package/dist/meta/finetune/report.d.ts +33 -0
  65. package/dist/meta/finetune/report.js +1284 -0
  66. package/dist/meta/finetune/retry.d.ts +24 -0
  67. package/dist/meta/finetune/retry.js +96 -0
  68. package/dist/meta/finetune/sampler.d.ts +18 -0
  69. package/dist/meta/finetune/sampler.js +41 -0
  70. package/dist/meta/finetune/system.d.ts +27 -0
  71. package/dist/meta/finetune/system.js +132 -0
  72. package/dist/meta/finetune/train.d.ts +39 -0
  73. package/dist/meta/finetune/train.js +391 -0
  74. package/dist/meta/finetune/tui.d.ts +16 -0
  75. package/dist/meta/finetune/tui.js +291 -0
  76. package/dist/meta/finetune/tuning.d.ts +167 -0
  77. package/dist/meta/finetune/tuning.js +289 -0
  78. package/dist/meta/finetune/usage.d.ts +62 -0
  79. package/dist/meta/finetune/usage.js +225 -0
  80. package/dist/meta/host.d.ts +14 -0
  81. package/dist/meta/host.js +20 -0
  82. package/dist/meta/index.d.ts +11 -0
  83. package/dist/meta/index.js +11 -0
  84. package/dist/meta/launch.d.ts +81 -0
  85. package/dist/meta/launch.js +286 -0
  86. package/dist/meta/model.d.ts +44 -0
  87. package/dist/meta/model.js +119 -0
  88. package/dist/meta/prompts.d.ts +18 -0
  89. package/dist/meta/prompts.js +61 -0
  90. package/dist/meta/relay.d.ts +20 -0
  91. package/dist/meta/relay.js +123 -0
  92. package/dist/meta/tokens.d.ts +52 -0
  93. package/dist/meta/tokens.js +226 -0
  94. package/dist/meta/window.d.ts +61 -0
  95. package/dist/meta/window.js +198 -0
  96. package/dist/meta/wire.d.ts +42 -0
  97. package/dist/meta/wire.js +129 -0
  98. package/dist/modelref.d.ts +16 -0
  99. package/dist/modelref.js +41 -0
  100. package/dist/narrate.js +5 -2
  101. package/dist/podman.d.ts +26 -0
  102. package/dist/podman.js +83 -3
  103. package/dist/projects.d.ts +0 -5
  104. package/dist/projects.js +2 -15
  105. package/dist/sandbox.d.ts +2 -0
  106. package/dist/sandbox.js +10 -3
  107. package/dist/scaffold.d.ts +15 -6
  108. package/dist/scaffold.js +5 -60
  109. package/dist/session.js +4 -29
  110. package/dist/term.d.ts +2 -0
  111. package/dist/term.js +11 -2
  112. package/dist/trace.js +53 -2
  113. package/dist/tui/models.d.ts +33 -0
  114. package/dist/tui/models.js +389 -0
  115. package/dist/tui/wrap.d.ts +5 -0
  116. package/dist/tui/wrap.js +12 -0
  117. package/dist/usage.d.ts +78 -0
  118. package/dist/usage.js +51 -0
  119. package/package.json +3 -2
  120. package/templates/editor/.github/copilot-instructions.md +31 -102
  121. package/templates/editor/.github/prompts/analyze.prompt.md +31 -0
  122. package/templates/editor/.github/prompts/dataset.prompt.md +108 -0
  123. package/templates/editor/.github/prompts/finetune-apply.prompt.md +91 -0
  124. package/templates/editor/.github/prompts/finetune.prompt.md +691 -198
  125. package/templates/editor/.github/prompts/new-agent.prompt.md +3 -1
  126. package/templates/editor/.github/prompts/new-skill.prompt.md +2 -0
  127. package/templates/editor/.github/prompts/project-review.prompt.md +4 -3
  128. package/templates/editor/.github/skills/zen-analyze-run/SKILL.md +483 -0
  129. package/templates/editor/.github/skills/zen-cli/SKILL.md +9 -1
  130. package/templates/editor/.github/skills/zen-cli/references/dataset.md +34 -0
  131. package/templates/editor/.github/skills/zen-cli/references/finetune.md +77 -0
  132. package/templates/editor/.github/skills/zen-cli/references/inspect.md +47 -9
  133. package/templates/editor/.github/skills/zen-cli/references/memory.md +33 -10
  134. package/templates/editor/.github/skills/zen-cli/references/meta.md +61 -0
  135. package/templates/editor/.github/skills/zen-cli/references/models.md +5 -1
  136. package/templates/editor/.github/skills/zen-cli/references/rag.md +24 -6
  137. package/templates/editor/.github/skills/zen-cli/references/sandbox.md +10 -1
  138. package/templates/editor/.github/skills/zen-code-python/SKILL.md +123 -0
  139. package/templates/editor/.github/skills/zen-code-python/references/code_python.md +48 -0
  140. package/templates/editor/.github/skills/zen-dataset/SKILL.md +253 -0
  141. package/templates/editor/.github/skills/zen-finetune/SKILL.md +1449 -658
  142. package/templates/editor/.github/skills/zen-finetune/references/readme-template.md +268 -0
  143. package/templates/editor/.github/skills/zen-finetune/scripts/batch.mjs +390 -0
  144. package/templates/editor/.github/skills/zen-finetune/scripts/difficult.mjs +243 -0
  145. package/templates/editor/.github/skills/zen-finetune/scripts/memory.mjs +280 -0
  146. package/templates/editor/.github/skills/zen-finetune/scripts/next.mjs +398 -0
  147. package/templates/editor/.github/skills/zen-finetune/scripts/report.mjs +797 -0
  148. package/templates/editor/.github/skills/zen-finetune/scripts/select.mjs +354 -0
  149. package/templates/editor/.github/skills/zen-finetune/scripts/usage.mjs +722 -0
  150. package/templates/editor/.github/skills/zen-inspect/SKILL.md +60 -25
  151. package/templates/editor/.github/skills/zen-inspect-ask/SKILL.md +270 -0
  152. package/templates/editor/.github/skills/zen-memory/SKILL.md +18 -14
  153. package/templates/editor/.github/skills/zen-rag-docs/SKILL.md +20 -8
  154. package/templates/editor/.github/skills/zen-review/SKILL.md +5 -3
  155. package/templates/editor/.github/skills/zen-review/scripts/check-paths.sh +5 -4
  156. package/templates/editor/.github/skills/zen-sandbox/SKILL.md +7 -3
  157. package/templates/editor/.github/skills/zen-sandbox-capacity/SKILL.md +2 -2
  158. package/templates/editor/.github/skills/zen-topology/SKILL.md +283 -0
  159. package/templates/editor/.vscode/settings.json +1 -0
  160. package/templates/project/agents/files-instructions.md +29 -20
  161. package/templates/project/agents/memory-instructions.md +31 -32
  162. package/templates/project/agents/tools-instructions.md +7 -4
  163. package/templates/project/gitignore +2 -0
  164. package/dist/meta.d.ts +0 -221
  165. package/dist/meta.js +0 -684
  166. package/templates/editor/.github/skills/zen-finetune/scripts/collect.sh +0 -432
  167. package/templates/editor/.github/skills/zen-finetune/scripts/sample.sh +0 -199
@@ -0,0 +1,244 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { existsSync, mkdirSync, readdirSync, writeFileSync } from 'node:fs';
3
+ import { basename, join } from 'node:path';
4
+ import { errorIn, sizeOf } from "./failure.js";
5
+ import { writeApply } from "./report.js";
6
+ import { readJson, Stopped } from "./tuning.js";
7
+ import { envelopesIn, spent } from "./usage.js";
8
+ const pad = (n) => String(n).padStart(3, '0');
9
+ export class Improvements {
10
+ #t;
11
+ pending = [];
12
+ /** the batch an apply is working on */
13
+ inApply = [];
14
+ #applied = new Set();
15
+ /** cases parked right now */
16
+ parked = 0;
17
+ /** `zen run`s in flight */
18
+ runs = 0;
19
+ applying = false;
20
+ /** the apply under way; `name` once it has a folder, before that it waits for runs */
21
+ current;
22
+ /** applies started in this process */
23
+ started = 0;
24
+ /** applies finished in this process - an analysis spanning one may see edits */
25
+ count = 0;
26
+ constructor(t) {
27
+ this.#t = t;
28
+ this.reload();
29
+ }
30
+ /** Re-reads which requests the applies took - a recovery may have removed one. */
31
+ reload() {
32
+ this.#applied.clear();
33
+ for (const name of this.#names()) {
34
+ for (const id of readJson(join(this.dir, name, 'applied.json'))?.ids ??
35
+ []) {
36
+ this.#applied.add(id);
37
+ }
38
+ }
39
+ }
40
+ get dir() {
41
+ return join(this.#t.dir, 'applies');
42
+ }
43
+ #names() {
44
+ try {
45
+ return readdirSync(this.dir)
46
+ .filter((n) => /^\d{3}$/.test(n))
47
+ .sort();
48
+ }
49
+ catch {
50
+ return [];
51
+ }
52
+ }
53
+ isApplied(id) {
54
+ return this.#applied.has(id);
55
+ }
56
+ /** Parks until an apply has taken this feedback, the worker free meanwhile. Taken already - after a restart - returns at once. */
57
+ async apply(feedback, s) {
58
+ if (this.#applied.has(feedback.id)) {
59
+ return;
60
+ }
61
+ if (!this.pending.some((f) => f.id === feedback.id)) {
62
+ this.pending.push(feedback);
63
+ }
64
+ this.parked++;
65
+ this.#t.event({
66
+ what: 'parked',
67
+ worker: s.worker?.slot,
68
+ case: feedback.case,
69
+ phase: feedback.phase,
70
+ attempt: feedback.attempt,
71
+ detail: `${feedback.improvements.length} improvement(s), worker freed`,
72
+ });
73
+ this.#t.unseat(s);
74
+ try {
75
+ while (!this.#applied.has(feedback.id)) {
76
+ if (this.#t.stopping) {
77
+ throw new Stopped();
78
+ }
79
+ await this.#t.tick();
80
+ }
81
+ }
82
+ finally {
83
+ this.parked--;
84
+ }
85
+ await this.#t.seat(s, true);
86
+ }
87
+ /** Before a run starts: wait out an apply, or stop. */
88
+ async notApplying() {
89
+ while (this.applying) {
90
+ this.#t.checkStopping();
91
+ await this.#t.tick();
92
+ }
93
+ this.#t.checkStopping();
94
+ }
95
+ /** The applier: runs until the tuning is finished. */
96
+ async loop() {
97
+ while (!this.#t.finished) {
98
+ await this.#t.tick();
99
+ if (this.#t.stopping || this.pending.length === 0) {
100
+ continue;
101
+ }
102
+ if (this.pending.length < this.#t.config.applyAt &&
103
+ (this.#t.busy() > 0 || !this.#t.drained)) {
104
+ continue;
105
+ }
106
+ try {
107
+ await this.#applyNow();
108
+ }
109
+ catch (err) {
110
+ this.#t.fatal = err;
111
+ this.#t.stop(`apply failed: ${err.message}`);
112
+ }
113
+ }
114
+ }
115
+ async #applyNow() {
116
+ const t = this.#t;
117
+ this.applying = true;
118
+ this.current = { since: Date.now() };
119
+ this.started++;
120
+ t.event({ what: 'apply waiting', detail: `${this.runs} run(s) to finish` });
121
+ try {
122
+ while (this.runs > 0) {
123
+ await t.tick();
124
+ }
125
+ const batch = this.pending.splice(0);
126
+ this.inApply = batch;
127
+ const name = pad(Math.max(0, ...this.#names().map(Number)) + 1);
128
+ const dir = join(this.dir, name);
129
+ const from = t.system.record('external').v;
130
+ await t.journal.step({ kind: 'apply', dir, from }, () => this.#applyBatch(batch, name, dir, from));
131
+ }
132
+ finally {
133
+ this.applying = false;
134
+ this.current = undefined;
135
+ this.inApply = [];
136
+ }
137
+ }
138
+ async #applyBatch(batch, name, dir, from) {
139
+ const t = this.#t;
140
+ const startedAt = new Date().toISOString();
141
+ mkdirSync(dir, { recursive: true });
142
+ writeFileSync(join(dir, 'inputs.json'), `${JSON.stringify(batch.map((f) => ({
143
+ ...f,
144
+ file: join(t.attemptDir({ id: f.case, rev: f.caseRev }, f.phase, f.attempt), 'feedback.json'),
145
+ })), null, 2)}\n`);
146
+ const total = batch.reduce((n, f) => n + f.improvements.length, 0);
147
+ this.current = { since: Date.now(), name, dir, requests: batch.length };
148
+ let to = from;
149
+ if (total > 0) {
150
+ t.event({
151
+ what: 'apply',
152
+ detail: `${name}: ${batch.length} request(s), ${total} improvement(s)`,
153
+ });
154
+ writeApply(t, dir);
155
+ try {
156
+ await this.#edit(dir, from);
157
+ }
158
+ catch (err) {
159
+ writeFileSync(join(dir, 'failed.json'), `${JSON.stringify({ at: new Date().toISOString(), startedAt, error: err.message }, null, 2)}\n`);
160
+ writeApply(t, dir);
161
+ throw err;
162
+ }
163
+ to = t.system.record('apply', name).v;
164
+ if (to !== from) {
165
+ writeFileSync(join(dir, 'diff.patch'), t.system.diff(from, to));
166
+ }
167
+ }
168
+ const applied = {
169
+ ids: batch.map((f) => f.id),
170
+ from,
171
+ to,
172
+ at: new Date().toISOString(),
173
+ startedAt,
174
+ ...(total === 0 ? { skipped: true } : {}),
175
+ };
176
+ writeFileSync(join(dir, 'applied.json'), `${JSON.stringify(applied, null, 2)}\n`);
177
+ writeApply(t, dir);
178
+ for (const f of batch) {
179
+ this.#applied.add(f.id);
180
+ }
181
+ this.count++;
182
+ this.current = undefined;
183
+ t.event({
184
+ what: 'applied',
185
+ detail: `${name}: v${from} -> v${to}, see applies/${name}/APPLY.md`,
186
+ result: total === 0 ? 'nothing to change' : `v${from} → v${to}`,
187
+ took: Date.now() - Date.parse(startedAt),
188
+ tokens: spent(envelopesIn(dir, 'apply')),
189
+ });
190
+ }
191
+ /** The meta agent edits; `zen check` must pass, or the edit is undone and tried once more. */
192
+ async #edit(dir, from) {
193
+ const t = this.#t;
194
+ for (let attempt = 1;; attempt++) {
195
+ const session = randomUUID();
196
+ writeFileSync(join(dir, 'session'), `${session}\n`);
197
+ const applyLog = join(dir, 'apply.log');
198
+ const checkLog = join(dir, 'check.log');
199
+ const offset = { apply: sizeOf(applyLog), check: sizeOf(checkLog) };
200
+ const res = await t.zen([
201
+ 'meta',
202
+ 'run',
203
+ '--no-refresh',
204
+ '--json',
205
+ '--session-id',
206
+ session,
207
+ '--events',
208
+ join(dir, 'apply.events.jsonl'),
209
+ '/finetune-apply',
210
+ dir,
211
+ ], applyLog);
212
+ writeFileSync(join(dir, attempt === 1 ? 'apply.json' : `apply-${attempt}.json`), res.stdout);
213
+ const check = await t.zen(['check', '--no-models', '--no-sandbox'], checkLog);
214
+ if (res.code === 0 && check.code === 0) {
215
+ return;
216
+ }
217
+ const e = res.code !== 0 ? errorIn(applyLog, offset.apply) : errorIn(checkLog, offset.check);
218
+ t.error(`apply ${basename(dir)}${attempt > 1 ? ` try ${attempt}` : ''}`, `${res.code !== 0 ? 'the apply failed' : 'zen check failed after the apply'}${e.message ? `: ${e.message}` : ''}`, e.log);
219
+ t.system.restore(from);
220
+ if (attempt === 2) {
221
+ throw new Error(`apply ${dir} left the project broken twice - see check.log`);
222
+ }
223
+ t.event({
224
+ what: 'apply retry',
225
+ detail: res.code !== 0 ? 'the apply failed' : 'zen check failed',
226
+ });
227
+ }
228
+ }
229
+ /** Requests every apply so far took, for the report. */
230
+ applies() {
231
+ return this.#names().map((name) => {
232
+ const dir = join(this.dir, name);
233
+ return {
234
+ name,
235
+ dir,
236
+ applied: readJson(join(dir, 'applied.json')),
237
+ inputs: existsSync(join(dir, 'inputs.json'))
238
+ ? readJson(join(dir, 'inputs.json'))
239
+ : undefined,
240
+ };
241
+ });
242
+ }
243
+ }
244
+ //# sourceMappingURL=improvements.js.map
@@ -0,0 +1,2 @@
1
+ export { FINETUNE_DETAILS, FINETUNE_HELP, FINETUNE_SUMMARY, FINETUNE_USAGE, finetuneProjectFlag, runFinetune, } from './command.ts';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,2 @@
1
+ export { FINETUNE_DETAILS, FINETUNE_HELP, FINETUNE_SUMMARY, FINETUNE_USAGE, finetuneProjectFlag, runFinetune, } from "./command.js";
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,35 @@
1
+ import type { Tuning } from './tuning.ts';
2
+ export type Tx = {
3
+ kind: 'run';
4
+ dir: string;
5
+ } | {
6
+ kind: 'analyze';
7
+ dir: string;
8
+ } | {
9
+ kind: 'apply';
10
+ dir: string;
11
+ from: number;
12
+ } | {
13
+ kind: 'merge';
14
+ dir: string;
15
+ log: string;
16
+ };
17
+ export declare class Journal {
18
+ #private;
19
+ readonly file: string;
20
+ constructor(file: string);
21
+ begin(tx: Tx): string;
22
+ end(id: string, op: 'commit' | 'failed' | 'rollback'): void;
23
+ /** Runs `fn` as one transaction. */
24
+ step<T>(tx: Tx, fn: () => Promise<T>): Promise<T>;
25
+ /** Transactions begun and never closed, oldest first. */
26
+ open(): {
27
+ id: string;
28
+ tx: Tx;
29
+ }[];
30
+ /** Every transaction closed: the history can go. */
31
+ clear(): void;
32
+ }
33
+ /** Rolls back what a kill cut off. Only under the loop's lock: a live step looks the same. */
34
+ export declare function recover(t: Tuning): Promise<void>;
35
+ //# sourceMappingURL=journal.d.ts.map
@@ -0,0 +1,135 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { appendFileSync, mkdirSync, readFileSync, rmSync } from 'node:fs';
3
+ import { basename, dirname, isAbsolute, join, relative } from 'node:path';
4
+ import { reap } from "./children.js";
5
+ /** What a step leaves in its attempt folder; the logs stay, they only grow. */
6
+ const OUTPUTS = {
7
+ run: ['run.json', 'envelope.json', 'request.json', 'events.jsonl', 'memory', 'workspace'],
8
+ analyze: [
9
+ 'feedback.json',
10
+ 'analysis.md',
11
+ 'analyze.json',
12
+ 'analyze-2.json',
13
+ 'analyze.events.jsonl',
14
+ ],
15
+ };
16
+ export class Journal {
17
+ file;
18
+ #base;
19
+ constructor(file) {
20
+ this.file = file;
21
+ this.#base = dirname(file);
22
+ }
23
+ #append(row) {
24
+ mkdirSync(this.#base, { recursive: true });
25
+ appendFileSync(this.file, `${JSON.stringify(row)}\n`);
26
+ }
27
+ // Paths are kept relative, so a project moved between sessions still recovers.
28
+ #paths(tx, to) {
29
+ return { ...tx, dir: to(tx.dir), ...('log' in tx ? { log: to(tx.log) } : {}) };
30
+ }
31
+ begin(tx) {
32
+ const id = randomUUID().slice(0, 8);
33
+ this.#append({
34
+ id,
35
+ at: new Date().toISOString(),
36
+ op: 'begin',
37
+ tx: this.#paths(tx, (p) => relative(this.#base, p)),
38
+ });
39
+ return id;
40
+ }
41
+ end(id, op) {
42
+ this.#append({ id, at: new Date().toISOString(), op });
43
+ }
44
+ /** Runs `fn` as one transaction. */
45
+ async step(tx, fn) {
46
+ const id = this.begin(tx);
47
+ let result;
48
+ try {
49
+ result = await fn();
50
+ }
51
+ catch (err) {
52
+ this.end(id, 'failed');
53
+ throw err;
54
+ }
55
+ this.end(id, 'commit');
56
+ return result;
57
+ }
58
+ /** Transactions begun and never closed, oldest first. */
59
+ open() {
60
+ let text = '';
61
+ try {
62
+ text = readFileSync(this.file, 'utf8');
63
+ }
64
+ catch {
65
+ return [];
66
+ }
67
+ const begun = new Map();
68
+ for (const line of text.split('\n')) {
69
+ let row;
70
+ try {
71
+ row = JSON.parse(line);
72
+ }
73
+ catch {
74
+ // A line torn by the kill itself.
75
+ continue;
76
+ }
77
+ if (row.op === 'begin') {
78
+ begun.set(row.id, row.tx);
79
+ }
80
+ else {
81
+ begun.delete(row.id);
82
+ }
83
+ }
84
+ return [...begun].map(([id, tx]) => ({
85
+ id,
86
+ tx: this.#paths(tx, (p) => (isAbsolute(p) ? p : join(this.#base, p))),
87
+ }));
88
+ }
89
+ /** Every transaction closed: the history can go. */
90
+ clear() {
91
+ rmSync(this.file, { force: true });
92
+ }
93
+ }
94
+ /** Rolls back what a kill cut off. Only under the loop's lock: a live step looks the same. */
95
+ export async function recover(t) {
96
+ // Before anything is rolled back: a child still running would write into it again.
97
+ const reaped = await reap(t.dir);
98
+ if (reaped.length > 0) {
99
+ t.event({
100
+ what: 'stopped leftovers',
101
+ detail: `${reaped.length} process(es) the last loop left running`,
102
+ });
103
+ }
104
+ const open = t.journal.open().reverse();
105
+ for (const { id, tx } of open) {
106
+ rollback(t, tx);
107
+ t.journal.end(id, 'rollback');
108
+ t.event({ what: 'rolled back', detail: `${tx.kind} ${relative(t.dir, tx.dir)}` });
109
+ }
110
+ if (open.some(({ tx }) => tx.kind === 'apply')) {
111
+ t.improvements.reload();
112
+ }
113
+ t.journal.clear();
114
+ }
115
+ function rollback(t, tx) {
116
+ switch (tx.kind) {
117
+ case 'run':
118
+ case 'analyze':
119
+ for (const name of OUTPUTS[tx.kind]) {
120
+ rmSync(join(tx.dir, name), { recursive: true, force: true });
121
+ }
122
+ return;
123
+ case 'apply':
124
+ // Its edits never passed `zen check`: put back the version it started from.
125
+ t.system.restore(tx.from);
126
+ t.system.drop(basename(tx.dir));
127
+ rmSync(tx.dir, { recursive: true, force: true });
128
+ return;
129
+ case 'merge':
130
+ rmSync(tx.dir, { recursive: true, force: true });
131
+ rmSync(tx.log, { force: true });
132
+ return;
133
+ }
134
+ }
135
+ //# sourceMappingURL=journal.js.map
@@ -0,0 +1,74 @@
1
+ import type { EventLine } from '../host.ts';
2
+ import type { StepFailure, Tuning } from './tuning.ts';
3
+ import { type Stage } from './usage.ts';
4
+ export interface ModelSpend {
5
+ calls: number;
6
+ input: number;
7
+ output: number;
8
+ }
9
+ /** One events file, folded as it grows. */
10
+ export interface Activity {
11
+ model?: string;
12
+ calls: number;
13
+ input: number;
14
+ output: number;
15
+ byModel: Record<string, ModelSpend>;
16
+ /** the latest tool call or thing said */
17
+ last?: string;
18
+ /** the latest lines, oldest first */
19
+ lines: EventLine[];
20
+ }
21
+ export interface NowRow {
22
+ /** stable across snapshots: `w1`, `apply`, `merge` */
23
+ key: string;
24
+ who: string;
25
+ step: string;
26
+ what: string;
27
+ sinceMs: number;
28
+ stage?: Stage;
29
+ act?: Activity;
30
+ }
31
+ export interface TokenRow extends ModelSpend {
32
+ stage: Stage;
33
+ model: string;
34
+ }
35
+ export interface LogRow {
36
+ at: string;
37
+ action: string;
38
+ case: string;
39
+ result: string;
40
+ took?: number;
41
+ tokens?: number;
42
+ }
43
+ export interface Snapshot {
44
+ pid: number;
45
+ project: string;
46
+ state: 'running' | 'stopping' | 'stopped' | 'finished';
47
+ at: string;
48
+ elapsedMs: number;
49
+ system: number;
50
+ workers: {
51
+ busy: number;
52
+ all: number;
53
+ };
54
+ total: number;
55
+ counts: Record<string, number>;
56
+ parked: number;
57
+ applyAt: number;
58
+ now: NowRow[];
59
+ tokens: TokenRow[];
60
+ log: LogRow[];
61
+ /** the latest step failures, newest first */
62
+ errors: StepFailure[];
63
+ }
64
+ /** Reads only what each events file gained since the last look; a file cleared for a new step starts over. */
65
+ export declare class Tails {
66
+ #private;
67
+ read(file: string): Activity;
68
+ /** Forgets the files no step writes any more. */
69
+ keep(files: ReadonlySet<string>): void;
70
+ }
71
+ export declare function snapshot(t: Tuning): Snapshot;
72
+ /** Takes a snapshot, keeps it in live.json without the raw lines, and hands it to every watcher. */
73
+ export declare function publish(t: Tuning): Snapshot | undefined;
74
+ //# sourceMappingURL=live.d.ts.map