@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.
- package/README.md +53 -0
- package/dist/ask.js +28 -5
- package/dist/banner.js +2 -2
- package/dist/batch.d.ts +6 -0
- package/dist/batch.js +76 -1
- package/dist/catalog.d.ts +11 -1
- package/dist/catalog.js +80 -7
- package/dist/commands/inspect.d.ts +47 -1
- package/dist/commands/inspect.js +167 -18
- package/dist/commands/memory.js +128 -29
- package/dist/commands/meta.js +311 -21
- package/dist/commands/models.js +96 -12
- package/dist/commands/open.js +4 -4
- package/dist/commands/run.js +38 -4
- package/dist/commands/sandbox.js +76 -14
- package/dist/engine.d.ts +5 -0
- package/dist/engine.js +1 -0
- package/dist/eventlog.d.ts +59 -0
- package/dist/eventlog.js +146 -0
- package/dist/liveness.d.ts +7 -0
- package/dist/liveness.js +15 -1
- package/dist/meta/editor.d.ts +9 -0
- package/dist/meta/editor.js +60 -0
- package/dist/meta/finetune/children.d.ts +8 -0
- package/dist/meta/finetune/children.js +76 -0
- package/dist/meta/finetune/command.d.ts +30 -0
- package/dist/meta/finetune/command.js +338 -0
- package/dist/meta/finetune/dataset/anchors.d.ts +41 -0
- package/dist/meta/finetune/dataset/anchors.js +239 -0
- package/dist/meta/finetune/dataset/changes.d.ts +36 -0
- package/dist/meta/finetune/dataset/changes.js +319 -0
- package/dist/meta/finetune/dataset/command.d.ts +17 -0
- package/dist/meta/finetune/dataset/command.js +1011 -0
- package/dist/meta/finetune/dataset/drift.d.ts +33 -0
- package/dist/meta/finetune/dataset/drift.js +71 -0
- package/dist/meta/finetune/dataset/export.d.ts +26 -0
- package/dist/meta/finetune/dataset/export.js +51 -0
- package/dist/meta/finetune/dataset/index.d.ts +2 -0
- package/dist/meta/finetune/dataset/index.js +2 -0
- package/dist/meta/finetune/dataset/sample.d.ts +50 -0
- package/dist/meta/finetune/dataset/sample.js +197 -0
- package/dist/meta/finetune/dataset/schema.d.ts +33 -0
- package/dist/meta/finetune/dataset/schema.js +216 -0
- package/dist/meta/finetune/dataset/store.d.ts +35 -0
- package/dist/meta/finetune/dataset/store.js +177 -0
- package/dist/meta/finetune/dataset/types.d.ts +125 -0
- package/dist/meta/finetune/dataset/types.js +39 -0
- package/dist/meta/finetune/failure.d.ts +15 -0
- package/dist/meta/finetune/failure.js +26 -0
- package/dist/meta/finetune/feedback.d.ts +45 -0
- package/dist/meta/finetune/feedback.js +85 -0
- package/dist/meta/finetune/improvements.d.ts +51 -0
- package/dist/meta/finetune/improvements.js +244 -0
- package/dist/meta/finetune/index.d.ts +2 -0
- package/dist/meta/finetune/index.js +2 -0
- package/dist/meta/finetune/journal.d.ts +35 -0
- package/dist/meta/finetune/journal.js +135 -0
- package/dist/meta/finetune/live.d.ts +74 -0
- package/dist/meta/finetune/live.js +226 -0
- package/dist/meta/finetune/loop.d.ts +3 -0
- package/dist/meta/finetune/loop.js +114 -0
- package/dist/meta/finetune/memories.d.ts +47 -0
- package/dist/meta/finetune/memories.js +154 -0
- package/dist/meta/finetune/report.d.ts +33 -0
- package/dist/meta/finetune/report.js +1284 -0
- package/dist/meta/finetune/retry.d.ts +24 -0
- package/dist/meta/finetune/retry.js +96 -0
- package/dist/meta/finetune/sampler.d.ts +18 -0
- package/dist/meta/finetune/sampler.js +41 -0
- package/dist/meta/finetune/system.d.ts +27 -0
- package/dist/meta/finetune/system.js +132 -0
- package/dist/meta/finetune/train.d.ts +39 -0
- package/dist/meta/finetune/train.js +391 -0
- package/dist/meta/finetune/tui.d.ts +16 -0
- package/dist/meta/finetune/tui.js +291 -0
- package/dist/meta/finetune/tuning.d.ts +167 -0
- package/dist/meta/finetune/tuning.js +289 -0
- package/dist/meta/finetune/usage.d.ts +62 -0
- package/dist/meta/finetune/usage.js +225 -0
- package/dist/meta/host.d.ts +14 -0
- package/dist/meta/host.js +20 -0
- package/dist/meta/index.d.ts +11 -0
- package/dist/meta/index.js +11 -0
- package/dist/meta/launch.d.ts +81 -0
- package/dist/meta/launch.js +286 -0
- package/dist/meta/model.d.ts +44 -0
- package/dist/meta/model.js +119 -0
- package/dist/meta/prompts.d.ts +18 -0
- package/dist/meta/prompts.js +61 -0
- package/dist/meta/relay.d.ts +20 -0
- package/dist/meta/relay.js +123 -0
- package/dist/meta/tokens.d.ts +52 -0
- package/dist/meta/tokens.js +226 -0
- package/dist/meta/window.d.ts +61 -0
- package/dist/meta/window.js +198 -0
- package/dist/meta/wire.d.ts +42 -0
- package/dist/meta/wire.js +129 -0
- package/dist/modelref.d.ts +16 -0
- package/dist/modelref.js +41 -0
- package/dist/narrate.js +5 -2
- package/dist/podman.d.ts +26 -0
- package/dist/podman.js +83 -3
- package/dist/projects.d.ts +0 -5
- package/dist/projects.js +2 -15
- package/dist/sandbox.d.ts +2 -0
- package/dist/sandbox.js +10 -3
- package/dist/scaffold.d.ts +15 -6
- package/dist/scaffold.js +5 -60
- package/dist/session.js +4 -29
- package/dist/term.d.ts +2 -0
- package/dist/term.js +11 -2
- package/dist/trace.js +53 -2
- package/dist/tui/models.d.ts +33 -0
- package/dist/tui/models.js +389 -0
- package/dist/tui/wrap.d.ts +5 -0
- package/dist/tui/wrap.js +12 -0
- package/dist/usage.d.ts +78 -0
- package/dist/usage.js +51 -0
- package/package.json +3 -2
- package/templates/editor/.github/copilot-instructions.md +31 -102
- package/templates/editor/.github/prompts/analyze.prompt.md +31 -0
- package/templates/editor/.github/prompts/dataset.prompt.md +108 -0
- package/templates/editor/.github/prompts/finetune-apply.prompt.md +91 -0
- package/templates/editor/.github/prompts/finetune.prompt.md +691 -198
- package/templates/editor/.github/prompts/new-agent.prompt.md +3 -1
- package/templates/editor/.github/prompts/new-skill.prompt.md +2 -0
- package/templates/editor/.github/prompts/project-review.prompt.md +4 -3
- package/templates/editor/.github/skills/zen-analyze-run/SKILL.md +483 -0
- package/templates/editor/.github/skills/zen-cli/SKILL.md +9 -1
- package/templates/editor/.github/skills/zen-cli/references/dataset.md +34 -0
- package/templates/editor/.github/skills/zen-cli/references/finetune.md +77 -0
- package/templates/editor/.github/skills/zen-cli/references/inspect.md +47 -9
- package/templates/editor/.github/skills/zen-cli/references/memory.md +33 -10
- package/templates/editor/.github/skills/zen-cli/references/meta.md +61 -0
- package/templates/editor/.github/skills/zen-cli/references/models.md +5 -1
- package/templates/editor/.github/skills/zen-cli/references/rag.md +24 -6
- package/templates/editor/.github/skills/zen-cli/references/sandbox.md +10 -1
- package/templates/editor/.github/skills/zen-code-python/SKILL.md +123 -0
- package/templates/editor/.github/skills/zen-code-python/references/code_python.md +48 -0
- package/templates/editor/.github/skills/zen-dataset/SKILL.md +253 -0
- package/templates/editor/.github/skills/zen-finetune/SKILL.md +1449 -658
- package/templates/editor/.github/skills/zen-finetune/references/readme-template.md +268 -0
- package/templates/editor/.github/skills/zen-finetune/scripts/batch.mjs +390 -0
- package/templates/editor/.github/skills/zen-finetune/scripts/difficult.mjs +243 -0
- package/templates/editor/.github/skills/zen-finetune/scripts/memory.mjs +280 -0
- package/templates/editor/.github/skills/zen-finetune/scripts/next.mjs +398 -0
- package/templates/editor/.github/skills/zen-finetune/scripts/report.mjs +797 -0
- package/templates/editor/.github/skills/zen-finetune/scripts/select.mjs +354 -0
- package/templates/editor/.github/skills/zen-finetune/scripts/usage.mjs +722 -0
- package/templates/editor/.github/skills/zen-inspect/SKILL.md +60 -25
- package/templates/editor/.github/skills/zen-inspect-ask/SKILL.md +270 -0
- package/templates/editor/.github/skills/zen-memory/SKILL.md +18 -14
- package/templates/editor/.github/skills/zen-rag-docs/SKILL.md +20 -8
- package/templates/editor/.github/skills/zen-review/SKILL.md +5 -3
- package/templates/editor/.github/skills/zen-review/scripts/check-paths.sh +5 -4
- package/templates/editor/.github/skills/zen-sandbox/SKILL.md +7 -3
- package/templates/editor/.github/skills/zen-sandbox-capacity/SKILL.md +2 -2
- package/templates/editor/.github/skills/zen-topology/SKILL.md +283 -0
- package/templates/editor/.vscode/settings.json +1 -0
- package/templates/project/agents/files-instructions.md +29 -20
- package/templates/project/agents/memory-instructions.md +31 -32
- package/templates/project/agents/tools-instructions.md +7 -4
- package/templates/project/gitignore +2 -0
- package/dist/meta.d.ts +0 -221
- package/dist/meta.js +0 -684
- package/templates/editor/.github/skills/zen-finetune/scripts/collect.sh +0 -432
- package/templates/editor/.github/skills/zen-finetune/scripts/sample.sh +0 -199
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { type ChildProcess } from 'node:child_process';
|
|
2
|
+
import type { Case } from './dataset/types.ts';
|
|
3
|
+
import type { Phase } from './feedback.ts';
|
|
4
|
+
import { Improvements } from './improvements.ts';
|
|
5
|
+
import { Journal } from './journal.ts';
|
|
6
|
+
import type { Snapshot } from './live.ts';
|
|
7
|
+
import { Memories } from './memories.ts';
|
|
8
|
+
import { SystemVersions } from './system.ts';
|
|
9
|
+
import type { ByStage } from './usage.ts';
|
|
10
|
+
export declare const FINETUNE_DIR = "finetune";
|
|
11
|
+
/** Where a tuning was kept before it moved to {@link FINETUNE_DIR}. */
|
|
12
|
+
export declare const LEGACY_FINETUNE_DIR = ".finetune";
|
|
13
|
+
export interface Config {
|
|
14
|
+
workers: number;
|
|
15
|
+
/** apply once this many requests are waiting */
|
|
16
|
+
applyAt: number;
|
|
17
|
+
tries: number;
|
|
18
|
+
memTries: number;
|
|
19
|
+
/** merge the submitted memories after this many more arrive */
|
|
20
|
+
mergeEvery: number;
|
|
21
|
+
seed: number;
|
|
22
|
+
classes?: string[];
|
|
23
|
+
ids?: string[];
|
|
24
|
+
rubric?: boolean;
|
|
25
|
+
limit?: number;
|
|
26
|
+
}
|
|
27
|
+
export declare const DEFAULTS: Config;
|
|
28
|
+
export type Step = 'idle' | 'run' | 'analyze' | 'parked';
|
|
29
|
+
export interface Worker {
|
|
30
|
+
slot: number;
|
|
31
|
+
case?: Case;
|
|
32
|
+
phase?: Phase;
|
|
33
|
+
attempt?: number;
|
|
34
|
+
step: Step;
|
|
35
|
+
since: number;
|
|
36
|
+
}
|
|
37
|
+
/** A case in flight. It holds a worker only while it runs or is analyzed; parked, it holds none. */
|
|
38
|
+
export interface Seat {
|
|
39
|
+
case: Case;
|
|
40
|
+
worker?: Worker;
|
|
41
|
+
phase?: Phase;
|
|
42
|
+
}
|
|
43
|
+
/** Runs `zen <args>` in the project, stderr into `log`; stdout is returned. */
|
|
44
|
+
export type Zen = (args: string[], log: string) => Promise<{
|
|
45
|
+
code: number;
|
|
46
|
+
stdout: string;
|
|
47
|
+
}>;
|
|
48
|
+
export declare class Stopped extends Error {
|
|
49
|
+
constructor();
|
|
50
|
+
}
|
|
51
|
+
export interface Event {
|
|
52
|
+
at: string;
|
|
53
|
+
what: string;
|
|
54
|
+
worker?: number;
|
|
55
|
+
case?: string;
|
|
56
|
+
rev?: number;
|
|
57
|
+
phase?: Phase;
|
|
58
|
+
attempt?: number;
|
|
59
|
+
detail?: string;
|
|
60
|
+
/** a step that ended: what it came to, how long it took, what it spent */
|
|
61
|
+
result?: string;
|
|
62
|
+
took?: number;
|
|
63
|
+
tokens?: number;
|
|
64
|
+
}
|
|
65
|
+
/** Failed cases in a row, with nothing going through between, that stop the loop. */
|
|
66
|
+
export declare const FAILURES_IN_A_ROW = 3;
|
|
67
|
+
export interface StepFailure {
|
|
68
|
+
at: string;
|
|
69
|
+
/** `merge-20 · no memory try 1`, `apply 004`, `memory m03` */
|
|
70
|
+
where: string;
|
|
71
|
+
message: string;
|
|
72
|
+
log?: string;
|
|
73
|
+
}
|
|
74
|
+
export declare class Tuning {
|
|
75
|
+
readonly root: string;
|
|
76
|
+
readonly dir: string;
|
|
77
|
+
readonly config: Config;
|
|
78
|
+
readonly zen: Zen;
|
|
79
|
+
readonly tickMs: number;
|
|
80
|
+
readonly workers: Worker[];
|
|
81
|
+
readonly claimed: Set<string>;
|
|
82
|
+
readonly startedAt: number;
|
|
83
|
+
readonly recent: Event[];
|
|
84
|
+
readonly system: SystemVersions;
|
|
85
|
+
readonly journal: Journal;
|
|
86
|
+
readonly improvements: Improvements;
|
|
87
|
+
readonly memories: Memories;
|
|
88
|
+
stopping: boolean;
|
|
89
|
+
finished: boolean;
|
|
90
|
+
fatal: Error | undefined;
|
|
91
|
+
/** woken cases waiting for a worker: they go before any new case */
|
|
92
|
+
resuming: number;
|
|
93
|
+
/** every case of the selection has been started */
|
|
94
|
+
drained: boolean;
|
|
95
|
+
/** cases started by this process: a failed one is not taken twice in one start */
|
|
96
|
+
readonly tried: Set<string>;
|
|
97
|
+
/** cases failed since the last step that went through */
|
|
98
|
+
failures: number;
|
|
99
|
+
/** what the last status page counted */
|
|
100
|
+
progress: {
|
|
101
|
+
counts: Record<string, number>;
|
|
102
|
+
total: number;
|
|
103
|
+
spent: ByStage;
|
|
104
|
+
} | undefined;
|
|
105
|
+
/** called with every live snapshot */
|
|
106
|
+
readonly watchers: Set<(s: Snapshot) => void>;
|
|
107
|
+
/** the latest step failures, newest first */
|
|
108
|
+
readonly errors: StepFailure[];
|
|
109
|
+
constructor(root: string, config: Config, zen: Zen, tickMs?: number);
|
|
110
|
+
get stopFile(): string;
|
|
111
|
+
tick(): Promise<void>;
|
|
112
|
+
stop(why: string): void;
|
|
113
|
+
/** Between steps: a stop takes effect here, never in the middle of one. */
|
|
114
|
+
checkStopping(): void;
|
|
115
|
+
caseDir(c: {
|
|
116
|
+
id: string;
|
|
117
|
+
rev: number;
|
|
118
|
+
}): string;
|
|
119
|
+
/** Where `retry` puts a case's earlier rounds: `r<rev>.<round>`. */
|
|
120
|
+
roundsDir(c: {
|
|
121
|
+
id: string;
|
|
122
|
+
}): string;
|
|
123
|
+
/** 1 for a case never retried at this revision. */
|
|
124
|
+
round(c: {
|
|
125
|
+
id: string;
|
|
126
|
+
rev: number;
|
|
127
|
+
}): number;
|
|
128
|
+
/** A step went through: the sandbox and the providers are up. */
|
|
129
|
+
succeeded(): void;
|
|
130
|
+
/** A case failed for a reason outside the prose. Enough in a row means nothing will go through. */
|
|
131
|
+
failed(why: string): void;
|
|
132
|
+
/** No run goes through without the container engine: stop at the first sign, not the third. */
|
|
133
|
+
sandboxDown(why: string): void;
|
|
134
|
+
attemptDir(c: {
|
|
135
|
+
id: string;
|
|
136
|
+
rev: number;
|
|
137
|
+
}, phase: Phase, attempt: number): string;
|
|
138
|
+
/** What the loop knows of a try, which a feedback file is never trusted for. */
|
|
139
|
+
known(c: {
|
|
140
|
+
id: string;
|
|
141
|
+
rev: number;
|
|
142
|
+
}, phase: Phase, attempt: number): {
|
|
143
|
+
phase: Phase;
|
|
144
|
+
attempt: number;
|
|
145
|
+
round?: number | undefined;
|
|
146
|
+
case: string;
|
|
147
|
+
caseRev: number;
|
|
148
|
+
};
|
|
149
|
+
freeWorker(): Worker | undefined;
|
|
150
|
+
busy(): number;
|
|
151
|
+
/** Puts a case on a free worker, waiting for one; a woken case goes before a new one. */
|
|
152
|
+
seat(s: Seat, woken: boolean): Promise<void>;
|
|
153
|
+
/** Gives the case's worker back, for another case to use. */
|
|
154
|
+
unseat(s: Seat): void;
|
|
155
|
+
/** A case moved to another step on its worker. */
|
|
156
|
+
at(s: Seat, phase: Phase, attempt: number, step: Step): void;
|
|
157
|
+
/** A step ended: the worker is between steps until the next `at`, and the event says how it went. */
|
|
158
|
+
ended(s: Seat, e: Omit<Event, 'at' | 'worker' | 'case' | 'rev'>): void;
|
|
159
|
+
/** A step failed: kept for the live view, and in events.jsonl with its log. */
|
|
160
|
+
error(where: string, message: string, log?: string): void;
|
|
161
|
+
event(e: Omit<Event, 'at'>): void;
|
|
162
|
+
report(): void;
|
|
163
|
+
}
|
|
164
|
+
/** The real `zen`: this same binary, in its own process group so Ctrl-C reaches only us. */
|
|
165
|
+
export declare function spawnZen(root: string, children: Set<ChildProcess>): Zen;
|
|
166
|
+
export declare function readJson<T>(file: string): T | undefined;
|
|
167
|
+
//# sourceMappingURL=tuning.d.ts.map
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { appendFileSync, closeSync, existsSync, mkdirSync, openSync, readdirSync, readFileSync, } from 'node:fs';
|
|
3
|
+
import { dirname, join } from 'node:path';
|
|
4
|
+
import { setTimeout as sleep } from 'node:timers/promises';
|
|
5
|
+
import { CliError, EXIT } from "../host.js";
|
|
6
|
+
import { forget, remember } from "./children.js";
|
|
7
|
+
import { Improvements } from "./improvements.js";
|
|
8
|
+
import { Journal } from "./journal.js";
|
|
9
|
+
import { Memories } from "./memories.js";
|
|
10
|
+
import { writeStatus } from "./report.js";
|
|
11
|
+
import { SystemVersions } from "./system.js";
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
// One tuning, in progress
|
|
14
|
+
//
|
|
15
|
+
// Everything the workers and the applier share: where things go, the settings,
|
|
16
|
+
// how `zen` is run, and what each worker is doing right now. Each case's own
|
|
17
|
+
// progress is not held here - it is on disk, one folder per attempt.
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
export const FINETUNE_DIR = 'finetune';
|
|
20
|
+
/** Where a tuning was kept before it moved to {@link FINETUNE_DIR}. */
|
|
21
|
+
export const LEGACY_FINETUNE_DIR = '.finetune';
|
|
22
|
+
export const DEFAULTS = {
|
|
23
|
+
workers: 4,
|
|
24
|
+
applyAt: 4,
|
|
25
|
+
tries: 4,
|
|
26
|
+
memTries: 3,
|
|
27
|
+
mergeEvery: 3,
|
|
28
|
+
seed: 1,
|
|
29
|
+
};
|
|
30
|
+
export class Stopped extends Error {
|
|
31
|
+
constructor() {
|
|
32
|
+
super('stopped');
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
const pad = (n) => String(n).padStart(2, '0');
|
|
36
|
+
/** Failed cases in a row, with nothing going through between, that stop the loop. */
|
|
37
|
+
export const FAILURES_IN_A_ROW = 3;
|
|
38
|
+
export class Tuning {
|
|
39
|
+
root;
|
|
40
|
+
dir;
|
|
41
|
+
config;
|
|
42
|
+
zen;
|
|
43
|
+
tickMs;
|
|
44
|
+
workers;
|
|
45
|
+
claimed = new Set();
|
|
46
|
+
startedAt = Date.now();
|
|
47
|
+
recent = [];
|
|
48
|
+
system;
|
|
49
|
+
journal;
|
|
50
|
+
improvements;
|
|
51
|
+
memories;
|
|
52
|
+
stopping = false;
|
|
53
|
+
finished = false;
|
|
54
|
+
fatal;
|
|
55
|
+
/** woken cases waiting for a worker: they go before any new case */
|
|
56
|
+
resuming = 0;
|
|
57
|
+
/** every case of the selection has been started */
|
|
58
|
+
drained = false;
|
|
59
|
+
/** cases started by this process: a failed one is not taken twice in one start */
|
|
60
|
+
tried = new Set();
|
|
61
|
+
/** cases failed since the last step that went through */
|
|
62
|
+
failures = 0;
|
|
63
|
+
/** what the last status page counted */
|
|
64
|
+
progress;
|
|
65
|
+
/** called with every live snapshot */
|
|
66
|
+
watchers = new Set();
|
|
67
|
+
/** the latest step failures, newest first */
|
|
68
|
+
errors = [];
|
|
69
|
+
constructor(root, config, zen, tickMs = 1000) {
|
|
70
|
+
this.root = root;
|
|
71
|
+
this.dir = join(root, FINETUNE_DIR);
|
|
72
|
+
this.config = config;
|
|
73
|
+
this.zen = zen;
|
|
74
|
+
this.tickMs = tickMs;
|
|
75
|
+
this.workers = Array.from({ length: config.workers }, (_, slot) => ({
|
|
76
|
+
slot: slot + 1,
|
|
77
|
+
step: 'idle',
|
|
78
|
+
since: Date.now(),
|
|
79
|
+
}));
|
|
80
|
+
this.system = new SystemVersions(root, join(this.dir, 'systems'));
|
|
81
|
+
this.journal = new Journal(join(this.dir, 'journal.jsonl'));
|
|
82
|
+
this.improvements = new Improvements(this);
|
|
83
|
+
this.memories = new Memories(this);
|
|
84
|
+
}
|
|
85
|
+
get stopFile() {
|
|
86
|
+
return join(this.dir, 'stop');
|
|
87
|
+
}
|
|
88
|
+
async tick() {
|
|
89
|
+
await sleep(this.tickMs);
|
|
90
|
+
if (!this.stopping && existsSync(this.stopFile)) {
|
|
91
|
+
this.stop('stop requested');
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
stop(why) {
|
|
95
|
+
if (!this.stopping) {
|
|
96
|
+
this.stopping = true;
|
|
97
|
+
this.event({ what: 'stopping', detail: why });
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
/** Between steps: a stop takes effect here, never in the middle of one. */
|
|
101
|
+
checkStopping() {
|
|
102
|
+
if (this.stopping) {
|
|
103
|
+
throw new Stopped();
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
caseDir(c) {
|
|
107
|
+
return join(this.dir, 'cases', c.id, `r${c.rev}`);
|
|
108
|
+
}
|
|
109
|
+
/** Where `retry` puts a case's earlier rounds: `r<rev>.<round>`. */
|
|
110
|
+
roundsDir(c) {
|
|
111
|
+
return join(this.dir, 'cases', c.id, 'rounds');
|
|
112
|
+
}
|
|
113
|
+
/** 1 for a case never retried at this revision. */
|
|
114
|
+
round(c) {
|
|
115
|
+
try {
|
|
116
|
+
return (readdirSync(this.roundsDir(c)).filter((n) => n.startsWith(`r${c.rev}.`)).length + 1);
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
return 1;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
/** A step went through: the sandbox and the providers are up. */
|
|
123
|
+
succeeded() {
|
|
124
|
+
this.failures = 0;
|
|
125
|
+
}
|
|
126
|
+
/** A case failed for a reason outside the prose. Enough in a row means nothing will go through. */
|
|
127
|
+
failed(why) {
|
|
128
|
+
this.failures++;
|
|
129
|
+
if (this.failures >= FAILURES_IN_A_ROW && !this.stopping) {
|
|
130
|
+
this.fatal = new CliError(`${this.failures} cases failed in a row - the last: ${why}`, EXIT.failed, 'fix what broke (the sandbox, a provider, a key), then start again: failed cases carry on where they stopped');
|
|
131
|
+
this.stop(`${this.failures} cases failed in a row`);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
/** No run goes through without the container engine: stop at the first sign, not the third. */
|
|
135
|
+
sandboxDown(why) {
|
|
136
|
+
this.fatal ??= new CliError(`the sandbox failed: ${why}`, EXIT.sandbox, 'see what the engine says: zen sandbox status - fix it, then start again: failed cases carry on where they stopped');
|
|
137
|
+
this.stop('the sandbox failed');
|
|
138
|
+
}
|
|
139
|
+
attemptDir(c, phase, attempt) {
|
|
140
|
+
return join(this.caseDir(c), `${pad(attempt)}-${phase}`);
|
|
141
|
+
}
|
|
142
|
+
/** What the loop knows of a try, which a feedback file is never trusted for. */
|
|
143
|
+
known(c, phase, attempt) {
|
|
144
|
+
const round = this.round(c);
|
|
145
|
+
return { case: c.id, caseRev: c.rev, ...(round > 1 ? { round } : {}), phase, attempt };
|
|
146
|
+
}
|
|
147
|
+
freeWorker() {
|
|
148
|
+
return this.workers.find((w) => !w.case);
|
|
149
|
+
}
|
|
150
|
+
busy() {
|
|
151
|
+
return this.workers.filter((w) => w.case).length;
|
|
152
|
+
}
|
|
153
|
+
/** Puts a case on a free worker, waiting for one; a woken case goes before a new one. */
|
|
154
|
+
async seat(s, woken) {
|
|
155
|
+
if (woken) {
|
|
156
|
+
this.resuming++;
|
|
157
|
+
}
|
|
158
|
+
try {
|
|
159
|
+
for (;;) {
|
|
160
|
+
const w = this.freeWorker();
|
|
161
|
+
if (w && (woken || this.resuming === 0)) {
|
|
162
|
+
Object.assign(w, {
|
|
163
|
+
case: s.case,
|
|
164
|
+
phase: undefined,
|
|
165
|
+
attempt: undefined,
|
|
166
|
+
step: 'idle',
|
|
167
|
+
since: Date.now(),
|
|
168
|
+
});
|
|
169
|
+
s.worker = w;
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
this.checkStopping();
|
|
173
|
+
await this.tick();
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
finally {
|
|
177
|
+
if (woken) {
|
|
178
|
+
this.resuming--;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
/** Gives the case's worker back, for another case to use. */
|
|
183
|
+
unseat(s) {
|
|
184
|
+
if (!s.worker) {
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
Object.assign(s.worker, {
|
|
188
|
+
case: undefined,
|
|
189
|
+
phase: undefined,
|
|
190
|
+
attempt: undefined,
|
|
191
|
+
step: 'idle',
|
|
192
|
+
since: Date.now(),
|
|
193
|
+
});
|
|
194
|
+
s.worker = undefined;
|
|
195
|
+
}
|
|
196
|
+
/** A case moved to another step on its worker. */
|
|
197
|
+
at(s, phase, attempt, step) {
|
|
198
|
+
const w = s.worker;
|
|
199
|
+
s.phase = phase;
|
|
200
|
+
Object.assign(w, { case: s.case, phase, attempt, step, since: Date.now() });
|
|
201
|
+
this.event({
|
|
202
|
+
what: step,
|
|
203
|
+
worker: w.slot,
|
|
204
|
+
case: s.case.id,
|
|
205
|
+
rev: s.case.rev,
|
|
206
|
+
phase,
|
|
207
|
+
attempt,
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
/** A step ended: the worker is between steps until the next `at`, and the event says how it went. */
|
|
211
|
+
ended(s, e) {
|
|
212
|
+
if (s.worker) {
|
|
213
|
+
Object.assign(s.worker, { step: 'idle', since: Date.now() });
|
|
214
|
+
}
|
|
215
|
+
this.event({ worker: s.worker?.slot, case: s.case.id, rev: s.case.rev, ...e });
|
|
216
|
+
}
|
|
217
|
+
/** A step failed: kept for the live view, and in events.jsonl with its log. */
|
|
218
|
+
error(where, message, log) {
|
|
219
|
+
this.errors.unshift({
|
|
220
|
+
at: new Date().toISOString(),
|
|
221
|
+
where,
|
|
222
|
+
message,
|
|
223
|
+
...(log ? { log } : {}),
|
|
224
|
+
});
|
|
225
|
+
this.errors.length = Math.min(this.errors.length, 20);
|
|
226
|
+
this.event({ what: 'error', detail: `${where}: ${message}${log ? ` - see ${log}` : ''}` });
|
|
227
|
+
}
|
|
228
|
+
event(e) {
|
|
229
|
+
const row = { at: new Date().toISOString(), ...e };
|
|
230
|
+
mkdirSync(this.dir, { recursive: true });
|
|
231
|
+
appendFileSync(join(this.dir, 'events.jsonl'), `${JSON.stringify(row)}\n`);
|
|
232
|
+
this.recent.unshift(row);
|
|
233
|
+
this.recent.length = Math.min(this.recent.length, 50);
|
|
234
|
+
this.report();
|
|
235
|
+
}
|
|
236
|
+
report() {
|
|
237
|
+
try {
|
|
238
|
+
writeStatus(this);
|
|
239
|
+
}
|
|
240
|
+
catch {
|
|
241
|
+
// A status page that failed to render must not stop the tuning.
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
/** The real `zen`: this same binary, in its own process group so Ctrl-C reaches only us. */
|
|
246
|
+
export function spawnZen(root, children) {
|
|
247
|
+
return (args, log) => new Promise((resolve) => {
|
|
248
|
+
mkdirSync(dirname(log), { recursive: true });
|
|
249
|
+
const fd = openSync(log, 'a');
|
|
250
|
+
const child = spawn(process.execPath, [process.argv[1], ...args], {
|
|
251
|
+
cwd: root,
|
|
252
|
+
stdio: ['ignore', 'pipe', fd],
|
|
253
|
+
detached: true,
|
|
254
|
+
});
|
|
255
|
+
children.add(child);
|
|
256
|
+
const dir = join(root, FINETUNE_DIR);
|
|
257
|
+
if (child.pid !== undefined) {
|
|
258
|
+
remember(dir, child.pid, process.argv[1]);
|
|
259
|
+
}
|
|
260
|
+
let stdout = '';
|
|
261
|
+
child.stdout?.on('data', (chunk) => {
|
|
262
|
+
stdout += chunk.toString('utf8');
|
|
263
|
+
});
|
|
264
|
+
let finished = false;
|
|
265
|
+
const done = (code) => {
|
|
266
|
+
if (finished) {
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
finished = true;
|
|
270
|
+
children.delete(child);
|
|
271
|
+
if (child.pid !== undefined) {
|
|
272
|
+
forget(dir, child.pid);
|
|
273
|
+
}
|
|
274
|
+
closeSync(fd);
|
|
275
|
+
resolve({ code, stdout });
|
|
276
|
+
};
|
|
277
|
+
child.on('error', () => done(1));
|
|
278
|
+
child.on('close', (code) => done(code ?? 1));
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
export function readJson(file) {
|
|
282
|
+
try {
|
|
283
|
+
return JSON.parse(readFileSync(file, 'utf8'));
|
|
284
|
+
}
|
|
285
|
+
catch {
|
|
286
|
+
return undefined;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
//# sourceMappingURL=tuning.js.map
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
export interface Tokens {
|
|
2
|
+
calls: number;
|
|
3
|
+
input: number;
|
|
4
|
+
cached: number;
|
|
5
|
+
output: number;
|
|
6
|
+
reasoning: number;
|
|
7
|
+
}
|
|
8
|
+
export type ByModel = Record<string, Tokens>;
|
|
9
|
+
export type Stage = 'run' | 'analyze' | 'apply';
|
|
10
|
+
export declare const STAGES: Stage[];
|
|
11
|
+
export type ByStage = Partial<Record<Stage, ByModel>>;
|
|
12
|
+
export declare function mergeModels(...all: (ByModel | undefined)[]): ByModel;
|
|
13
|
+
export declare function mergeStages(...all: (ByStage | undefined)[]): ByStage;
|
|
14
|
+
export declare function total(by: ByModel | undefined): Tokens;
|
|
15
|
+
/** Input plus output, the input counting cached tokens. */
|
|
16
|
+
export declare function spent(by: ByModel | undefined): number;
|
|
17
|
+
/** `vertex/gemini-3.8-flash` and `gemini-3.8-flash` are one model in a table. */
|
|
18
|
+
export declare const modelName: (m: string | undefined) => string;
|
|
19
|
+
export interface ModelMetrics extends Tokens {
|
|
20
|
+
/** the largest input of a single call */
|
|
21
|
+
peak: number;
|
|
22
|
+
/** time spent waiting on this model */
|
|
23
|
+
ms: number;
|
|
24
|
+
}
|
|
25
|
+
export interface RunMetrics {
|
|
26
|
+
models: Record<string, ModelMetrics>;
|
|
27
|
+
/** summed over every lane, so parallel branches can exceed the wall clock */
|
|
28
|
+
llmMs: number;
|
|
29
|
+
toolMs: number;
|
|
30
|
+
toolCalls: number;
|
|
31
|
+
toolErrors: number;
|
|
32
|
+
memory: {
|
|
33
|
+
recall: number;
|
|
34
|
+
load: number;
|
|
35
|
+
grep: number;
|
|
36
|
+
commit: number;
|
|
37
|
+
forget: number;
|
|
38
|
+
};
|
|
39
|
+
forks: number;
|
|
40
|
+
branches: number;
|
|
41
|
+
compactions: number;
|
|
42
|
+
handoffs: number;
|
|
43
|
+
/** the first error a sandbox tool got from the container engine, not from a command */
|
|
44
|
+
sandbox?: string;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* One walk of a run's `state.json`, branches included. Nested records repeat a
|
|
48
|
+
* node, so nodes are counted by id. A node is stamped when the work behind it
|
|
49
|
+
* finished, so the gap to the node before it in the same lane is that work -
|
|
50
|
+
* the same timing as the Stats tab of report.html.
|
|
51
|
+
*/
|
|
52
|
+
export declare function runMetrics(runDir: string): RunMetrics | undefined;
|
|
53
|
+
/** The token part of a run's metrics, per model. */
|
|
54
|
+
export declare function tokensOf(m: RunMetrics): ByModel;
|
|
55
|
+
export declare function runTokens(runDir: string): ByModel | undefined;
|
|
56
|
+
/** One `zen meta run --json` envelope: `{ model, tokens: { calls, inputTokens, … } }`. */
|
|
57
|
+
export declare function envelopeTokens(file: string): ByModel | undefined;
|
|
58
|
+
/** Every envelope in `dir` named `<stem>.json` or `<stem>-<n>.json` - retries keep their own. */
|
|
59
|
+
export declare function envelopesIn(dir: string, stem: string): ByModel | undefined;
|
|
60
|
+
/** 1234 → 1.2k, 2184341 → 2.2M. */
|
|
61
|
+
export declare function short(n: number): string;
|
|
62
|
+
//# sourceMappingURL=usage.d.ts.map
|