@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,35 @@
|
|
|
1
|
+
import { type By, type Case, type ChangeRow, type JournalRow, type Manifest, type NoteRow, type RevisionRow } from './types.ts';
|
|
2
|
+
export declare function whoIsThis(): By;
|
|
3
|
+
export interface Commit {
|
|
4
|
+
rev: number;
|
|
5
|
+
rows: ChangeRow[];
|
|
6
|
+
revision: RevisionRow;
|
|
7
|
+
manifest: Manifest;
|
|
8
|
+
/** cases whose stored source hash moved with no change worth a revision */
|
|
9
|
+
refreshed: Case[];
|
|
10
|
+
}
|
|
11
|
+
export declare class DatasetStore {
|
|
12
|
+
#private;
|
|
13
|
+
readonly root: string;
|
|
14
|
+
readonly dir: string;
|
|
15
|
+
manifest: Manifest;
|
|
16
|
+
private constructor();
|
|
17
|
+
/** `write` claims the directory, creating it if it does not exist yet. */
|
|
18
|
+
static open(root: string, options?: {
|
|
19
|
+
write?: boolean;
|
|
20
|
+
}): DatasetStore;
|
|
21
|
+
exists(): boolean;
|
|
22
|
+
ids(): string[];
|
|
23
|
+
get(id: string): Case | undefined;
|
|
24
|
+
all(): Case[];
|
|
25
|
+
journal(id: string): JournalRow[];
|
|
26
|
+
/** The case as it stood at a revision, read back from its journal. */
|
|
27
|
+
at(id: string, rev: number): Case | undefined;
|
|
28
|
+
revisions(): RevisionRow[];
|
|
29
|
+
/** Cases written by an apply that never reached its manifest. */
|
|
30
|
+
unfinished(): string[];
|
|
31
|
+
note(id: string, row: NoteRow): void;
|
|
32
|
+
commit(commit: Commit): void;
|
|
33
|
+
release(): void;
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=store.d.ts.map
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import { claimLock, ownLock } from '@zenera/neo';
|
|
2
|
+
import { appendFileSync, existsSync, mkdirSync, readdirSync, readFileSync, renameSync, unlinkSync, writeFileSync, } from 'node:fs';
|
|
3
|
+
import { hostname } from 'node:os';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
import { CliError, EXIT, META_PROMPT_ENV, META_SESSION_ENV } from "../../host.js";
|
|
6
|
+
import { DATASET_DIR, DATASET_VERSION, } from "./types.js";
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
// The store
|
|
9
|
+
//
|
|
10
|
+
// Plain files, so git can diff them and a person can read them: a case per
|
|
11
|
+
// file, a journal per case, a line per revision, and a manifest written last —
|
|
12
|
+
// the manifest's revision is the commit marker, so an apply killed half-way
|
|
13
|
+
// shows up as cases newer than it.
|
|
14
|
+
//
|
|
15
|
+
// Writes take the directory lock; reads never do. A note is one appended line
|
|
16
|
+
// and takes no lock either, so an `analyze` running beside an apply can still
|
|
17
|
+
// say what it found.
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
const MANIFEST = 'manifest.json';
|
|
20
|
+
const REVISIONS = 'revisions.jsonl';
|
|
21
|
+
const CASES = 'cases';
|
|
22
|
+
const LOG = 'log';
|
|
23
|
+
const LOCK = '.lock';
|
|
24
|
+
export function whoIsThis() {
|
|
25
|
+
const session = process.env[META_SESSION_ENV]?.trim();
|
|
26
|
+
const prompt = process.env[META_PROMPT_ENV]?.trim();
|
|
27
|
+
return {
|
|
28
|
+
...(session ? { session } : {}),
|
|
29
|
+
...(prompt ? { prompt } : {}),
|
|
30
|
+
host: hostname(),
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
function atomic(path, text) {
|
|
34
|
+
const tmp = `${path}.tmp`;
|
|
35
|
+
writeFileSync(tmp, text);
|
|
36
|
+
renameSync(tmp, path);
|
|
37
|
+
}
|
|
38
|
+
function lines(path) {
|
|
39
|
+
let text;
|
|
40
|
+
try {
|
|
41
|
+
text = readFileSync(path, 'utf8');
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
return [];
|
|
45
|
+
}
|
|
46
|
+
return text
|
|
47
|
+
.split('\n')
|
|
48
|
+
.filter((line) => line.trim())
|
|
49
|
+
.flatMap((line) => {
|
|
50
|
+
try {
|
|
51
|
+
return [JSON.parse(line)];
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
return [];
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
const empty = () => ({ version: DATASET_VERSION, revision: 0, sources: {}, ignored: [] });
|
|
59
|
+
export class DatasetStore {
|
|
60
|
+
root;
|
|
61
|
+
dir;
|
|
62
|
+
manifest;
|
|
63
|
+
#locked = false;
|
|
64
|
+
constructor(root) {
|
|
65
|
+
this.root = root;
|
|
66
|
+
this.dir = join(root, DATASET_DIR);
|
|
67
|
+
this.manifest = empty();
|
|
68
|
+
try {
|
|
69
|
+
const read = JSON.parse(readFileSync(join(this.dir, MANIFEST), 'utf8'));
|
|
70
|
+
if (read.version !== DATASET_VERSION) {
|
|
71
|
+
throw new CliError(`${this.dir} is dataset version ${read.version}; this zen reads ${DATASET_VERSION}`, EXIT.invalid, 'upgrade zen');
|
|
72
|
+
}
|
|
73
|
+
this.manifest = { ...empty(), ...read };
|
|
74
|
+
}
|
|
75
|
+
catch (err) {
|
|
76
|
+
if (err instanceof CliError) {
|
|
77
|
+
throw err;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
/** `write` claims the directory, creating it if it does not exist yet. */
|
|
82
|
+
static open(root, options = {}) {
|
|
83
|
+
const store = new DatasetStore(root);
|
|
84
|
+
if (options.write) {
|
|
85
|
+
mkdirSync(join(store.dir, CASES), { recursive: true });
|
|
86
|
+
mkdirSync(join(store.dir, LOG), { recursive: true });
|
|
87
|
+
claimLock(join(store.dir, LOCK), ownLock(), (held) => new CliError(`the dataset is being written (pid ${held.pid}, since ${held.startedAt})`, EXIT.failed, `wait for it, or remove ${join(store.dir, LOCK)} if that process crashed`));
|
|
88
|
+
store.#locked = true;
|
|
89
|
+
}
|
|
90
|
+
return store;
|
|
91
|
+
}
|
|
92
|
+
exists() {
|
|
93
|
+
return existsSync(join(this.dir, MANIFEST));
|
|
94
|
+
}
|
|
95
|
+
ids() {
|
|
96
|
+
try {
|
|
97
|
+
return readdirSync(join(this.dir, CASES))
|
|
98
|
+
.filter((f) => f.endsWith('.json'))
|
|
99
|
+
.map((f) => f.slice(0, -'.json'.length))
|
|
100
|
+
.sort();
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
return [];
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
get(id) {
|
|
107
|
+
try {
|
|
108
|
+
return JSON.parse(readFileSync(join(this.dir, CASES, `${id}.json`), 'utf8'));
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
return undefined;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
all() {
|
|
115
|
+
return this.ids().flatMap((id) => this.get(id) ?? []);
|
|
116
|
+
}
|
|
117
|
+
journal(id) {
|
|
118
|
+
return lines(join(this.dir, LOG, `${id}.jsonl`));
|
|
119
|
+
}
|
|
120
|
+
/** The case as it stood at a revision, read back from its journal. */
|
|
121
|
+
at(id, rev) {
|
|
122
|
+
let found;
|
|
123
|
+
for (const row of this.journal(id)) {
|
|
124
|
+
if (row.type === 'change' && row.rev <= rev) {
|
|
125
|
+
found = row.case;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return found;
|
|
129
|
+
}
|
|
130
|
+
revisions() {
|
|
131
|
+
return lines(join(this.dir, REVISIONS));
|
|
132
|
+
}
|
|
133
|
+
/** Cases written by an apply that never reached its manifest. */
|
|
134
|
+
unfinished() {
|
|
135
|
+
return this.all()
|
|
136
|
+
.filter((c) => c.rev > this.manifest.revision)
|
|
137
|
+
.map((c) => c.id);
|
|
138
|
+
}
|
|
139
|
+
note(id, row) {
|
|
140
|
+
mkdirSync(join(this.dir, LOG), { recursive: true });
|
|
141
|
+
appendFileSync(join(this.dir, LOG, `${id}.jsonl`), `${JSON.stringify(row)}\n`);
|
|
142
|
+
}
|
|
143
|
+
commit(commit) {
|
|
144
|
+
if (!this.#locked) {
|
|
145
|
+
throw new Error('commit without the dataset lock');
|
|
146
|
+
}
|
|
147
|
+
for (const row of commit.rows) {
|
|
148
|
+
atomic(join(this.dir, CASES, `${row.case.id}.json`), `${JSON.stringify(row.case, null, 4)}\n`);
|
|
149
|
+
appendFileSync(join(this.dir, LOG, `${row.case.id}.jsonl`), `${JSON.stringify(row)}\n`);
|
|
150
|
+
}
|
|
151
|
+
for (const c of commit.refreshed) {
|
|
152
|
+
atomic(join(this.dir, CASES, `${c.id}.json`), `${JSON.stringify(c, null, 4)}\n`);
|
|
153
|
+
}
|
|
154
|
+
if (commit.rows.length > 0) {
|
|
155
|
+
appendFileSync(join(this.dir, REVISIONS), `${JSON.stringify(commit.revision)}\n`);
|
|
156
|
+
}
|
|
157
|
+
this.manifest = {
|
|
158
|
+
...commit.manifest,
|
|
159
|
+
revision: commit.rows.length > 0 ? commit.rev : commit.manifest.revision,
|
|
160
|
+
updatedAt: new Date().toISOString(),
|
|
161
|
+
};
|
|
162
|
+
atomic(join(this.dir, MANIFEST), `${JSON.stringify(this.manifest, null, 4)}\n`);
|
|
163
|
+
}
|
|
164
|
+
release() {
|
|
165
|
+
if (!this.#locked) {
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
this.#locked = false;
|
|
169
|
+
try {
|
|
170
|
+
unlinkSync(join(this.dir, LOCK));
|
|
171
|
+
}
|
|
172
|
+
catch {
|
|
173
|
+
// Already gone, or taken over as stale.
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
//# sourceMappingURL=store.js.map
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
export declare const DATASET_DIR = "dataset";
|
|
2
|
+
export declare const DATASET_VERSION = 1;
|
|
3
|
+
export declare const COMPLEXITIES: readonly ["simple", "medium", "complex"];
|
|
4
|
+
export type Complexity = (typeof COMPLEXITIES)[number];
|
|
5
|
+
export declare const STATUSES: readonly ["active", "retired"];
|
|
6
|
+
export type Status = (typeof STATUSES)[number];
|
|
7
|
+
export declare const NOTE_KINDS: readonly ["graded", "analyze", "difficult", "rubric-suspect", "observation"];
|
|
8
|
+
export type NoteKind = (typeof NOTE_KINDS)[number];
|
|
9
|
+
export declare const VERDICTS: readonly ["right", "wrong", "void"];
|
|
10
|
+
export type Verdict = (typeof VERDICTS)[number];
|
|
11
|
+
/** A case id names a directory under a batch dir, so it is one path segment. */
|
|
12
|
+
export declare const CASE_ID: RegExp;
|
|
13
|
+
export declare const MEDIA_KINDS: readonly ["image", "audio", "video", "file"];
|
|
14
|
+
export type MediaKind = (typeof MEDIA_KINDS)[number];
|
|
15
|
+
export type Part = string | {
|
|
16
|
+
text: string;
|
|
17
|
+
} | ({
|
|
18
|
+
[K in MediaKind]?: string;
|
|
19
|
+
} & {
|
|
20
|
+
mimeType?: string;
|
|
21
|
+
});
|
|
22
|
+
export type Input = string | Part[];
|
|
23
|
+
export interface RubricItem {
|
|
24
|
+
id: string;
|
|
25
|
+
text: string;
|
|
26
|
+
}
|
|
27
|
+
/** Where in a source file a case came from. Neither key means the whole file. */
|
|
28
|
+
export interface Anchor {
|
|
29
|
+
heading?: string[];
|
|
30
|
+
pointer?: string;
|
|
31
|
+
}
|
|
32
|
+
export interface Source {
|
|
33
|
+
/** project-relative */
|
|
34
|
+
file: string;
|
|
35
|
+
anchor?: Anchor;
|
|
36
|
+
/** of the section the anchor names, when the case was last applied */
|
|
37
|
+
sha256?: string;
|
|
38
|
+
}
|
|
39
|
+
export interface Case {
|
|
40
|
+
id: string;
|
|
41
|
+
rev: number;
|
|
42
|
+
status: Status;
|
|
43
|
+
class?: string;
|
|
44
|
+
complexity?: Complexity;
|
|
45
|
+
tags?: string[];
|
|
46
|
+
input: Input;
|
|
47
|
+
rubric: RubricItem[];
|
|
48
|
+
expected?: string;
|
|
49
|
+
notes?: string;
|
|
50
|
+
source?: Source;
|
|
51
|
+
/** fields a re-extraction must not overwrite */
|
|
52
|
+
overrides?: string[];
|
|
53
|
+
}
|
|
54
|
+
/** A change to these is a different question: earlier results no longer count. */
|
|
55
|
+
export declare const RESTART_FIELDS: readonly ["input", "rubric", "expected"];
|
|
56
|
+
/** Fields a proposal or `update --set` may write. */
|
|
57
|
+
export declare const CONTENT_FIELDS: readonly ["class", "complexity", "tags", "input", "rubric", "expected", "notes", "source"];
|
|
58
|
+
export type ContentField = (typeof CONTENT_FIELDS)[number];
|
|
59
|
+
/** Who did it: read from the environment `zen meta` gives its agent. */
|
|
60
|
+
export interface By {
|
|
61
|
+
session?: string;
|
|
62
|
+
prompt?: string;
|
|
63
|
+
host: string;
|
|
64
|
+
}
|
|
65
|
+
export interface FieldChange {
|
|
66
|
+
path: string;
|
|
67
|
+
from?: unknown;
|
|
68
|
+
to?: unknown;
|
|
69
|
+
}
|
|
70
|
+
export type ChangeOp = 'add' | 'update' | 'retire' | 'restore';
|
|
71
|
+
export interface ChangeRow {
|
|
72
|
+
type: 'change';
|
|
73
|
+
rev: number;
|
|
74
|
+
at: string;
|
|
75
|
+
op: ChangeOp;
|
|
76
|
+
changes: FieldChange[];
|
|
77
|
+
why: string;
|
|
78
|
+
by: By;
|
|
79
|
+
/** the case as it stood after this change, so any revision can be read back */
|
|
80
|
+
case: Case;
|
|
81
|
+
}
|
|
82
|
+
export interface NoteRow {
|
|
83
|
+
type: 'note';
|
|
84
|
+
at: string;
|
|
85
|
+
kind: NoteKind;
|
|
86
|
+
/** the case revision the note is about */
|
|
87
|
+
caseRev: number;
|
|
88
|
+
run?: string;
|
|
89
|
+
verdict?: Verdict;
|
|
90
|
+
rubric?: Record<string, 'pass' | 'fail'>;
|
|
91
|
+
text: string;
|
|
92
|
+
by: By;
|
|
93
|
+
}
|
|
94
|
+
export type JournalRow = ChangeRow | NoteRow;
|
|
95
|
+
export interface Ignored {
|
|
96
|
+
file: string;
|
|
97
|
+
anchor: Anchor;
|
|
98
|
+
why: string;
|
|
99
|
+
rev: number;
|
|
100
|
+
}
|
|
101
|
+
export interface Manifest {
|
|
102
|
+
version: number;
|
|
103
|
+
revision: number;
|
|
104
|
+
updatedAt?: string;
|
|
105
|
+
/** whole-file hash of every source a case or an ignore names */
|
|
106
|
+
sources: Record<string, {
|
|
107
|
+
sha256: string;
|
|
108
|
+
}>;
|
|
109
|
+
ignored: Ignored[];
|
|
110
|
+
}
|
|
111
|
+
export interface RevisionRow {
|
|
112
|
+
rev: number;
|
|
113
|
+
at: string;
|
|
114
|
+
why: string;
|
|
115
|
+
by: By;
|
|
116
|
+
added: string[];
|
|
117
|
+
updated: string[];
|
|
118
|
+
retired: string[];
|
|
119
|
+
restored: string[];
|
|
120
|
+
/** cases whose input, rubric or expected changed */
|
|
121
|
+
restarted: string[];
|
|
122
|
+
}
|
|
123
|
+
export declare const anchorText: (anchor: Anchor | undefined) => string;
|
|
124
|
+
export declare const sameAnchor: (a: Anchor | undefined, b: Anchor | undefined) => boolean;
|
|
125
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// A dataset, as stored
|
|
3
|
+
//
|
|
4
|
+
// One file per case holds what the case is now; one append-only journal per
|
|
5
|
+
// case holds how it got there and what was learned about it. Nothing is ever
|
|
6
|
+
// deleted: a removed case is `retired`, so an id is never reused and an old run
|
|
7
|
+
// that names it still resolves.
|
|
8
|
+
// ---------------------------------------------------------------------------
|
|
9
|
+
export const DATASET_DIR = 'dataset';
|
|
10
|
+
export const DATASET_VERSION = 1;
|
|
11
|
+
export const COMPLEXITIES = ['simple', 'medium', 'complex'];
|
|
12
|
+
export const STATUSES = ['active', 'retired'];
|
|
13
|
+
export const NOTE_KINDS = [
|
|
14
|
+
'graded',
|
|
15
|
+
'analyze',
|
|
16
|
+
'difficult',
|
|
17
|
+
'rubric-suspect',
|
|
18
|
+
'observation',
|
|
19
|
+
];
|
|
20
|
+
export const VERDICTS = ['right', 'wrong', 'void'];
|
|
21
|
+
/** A case id names a directory under a batch dir, so it is one path segment. */
|
|
22
|
+
export const CASE_ID = /^[A-Za-z0-9_.-]+$/;
|
|
23
|
+
export const MEDIA_KINDS = ['image', 'audio', 'video', 'file'];
|
|
24
|
+
/** A change to these is a different question: earlier results no longer count. */
|
|
25
|
+
export const RESTART_FIELDS = ['input', 'rubric', 'expected'];
|
|
26
|
+
/** Fields a proposal or `update --set` may write. */
|
|
27
|
+
export const CONTENT_FIELDS = [
|
|
28
|
+
'class',
|
|
29
|
+
'complexity',
|
|
30
|
+
'tags',
|
|
31
|
+
'input',
|
|
32
|
+
'rubric',
|
|
33
|
+
'expected',
|
|
34
|
+
'notes',
|
|
35
|
+
'source',
|
|
36
|
+
];
|
|
37
|
+
export const anchorText = (anchor) => anchor?.heading ? anchor.heading.join(' > ') : (anchor?.pointer ?? '(whole file)');
|
|
38
|
+
export const sameAnchor = (a, b) => anchorText(a) === anchorText(b);
|
|
39
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/** A step that failed, with the log that says why. */
|
|
2
|
+
export declare class StepError extends Error {
|
|
3
|
+
readonly log?: string | undefined;
|
|
4
|
+
constructor(message: string, log?: string | undefined);
|
|
5
|
+
}
|
|
6
|
+
export declare const sizeOf: (file: string) => number;
|
|
7
|
+
/**
|
|
8
|
+
* What broke, from what a step appended to its log since `from`: the provider's own line before
|
|
9
|
+
* zen's generic `error ...`, and the meta agent's fuller log when the step names one.
|
|
10
|
+
*/
|
|
11
|
+
export declare function errorIn(log: string, from?: number): {
|
|
12
|
+
message?: string;
|
|
13
|
+
log: string;
|
|
14
|
+
};
|
|
15
|
+
//# sourceMappingURL=failure.d.ts.map
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { existsSync, readFileSync, statSync } from 'node:fs';
|
|
2
|
+
/** A step that failed, with the log that says why. */
|
|
3
|
+
export class StepError extends Error {
|
|
4
|
+
log;
|
|
5
|
+
constructor(message, log) {
|
|
6
|
+
super(message);
|
|
7
|
+
this.log = log;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
export const sizeOf = (file) => (existsSync(file) ? statSync(file).size : 0);
|
|
11
|
+
/**
|
|
12
|
+
* What broke, from what a step appended to its log since `from`: the provider's own line before
|
|
13
|
+
* zen's generic `error ...`, and the meta agent's fuller log when the step names one.
|
|
14
|
+
*/
|
|
15
|
+
export function errorIn(log, from = 0) {
|
|
16
|
+
const text = existsSync(log) ? readFileSync(log).subarray(from).toString('utf8') : '';
|
|
17
|
+
const lines = text
|
|
18
|
+
.split('\n')
|
|
19
|
+
.map((l) => l.trim())
|
|
20
|
+
.filter(Boolean);
|
|
21
|
+
const provider = lines.findLast((l) => /\bHTTP \d{3}\b|^"\d{3} [^"]+" \(\d{3}\)|not found|quota|rate limit|unavailable|timed out/i.test(l));
|
|
22
|
+
const exited = lines.findLast((l) => /^error\s/.test(l))?.replace(/^error\s+/, '');
|
|
23
|
+
const meta = lines.findLast((l) => /^log\s+\//.test(l))?.replace(/^log\s+/, '');
|
|
24
|
+
return { message: provider ?? exited, log: meta ?? log };
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=failure.js.map
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { type Verdict } from './dataset/types.ts';
|
|
2
|
+
export type Phase = 'nomem' | 'mem';
|
|
3
|
+
export interface Improvement {
|
|
4
|
+
id: string;
|
|
5
|
+
kind?: string;
|
|
6
|
+
file?: string;
|
|
7
|
+
change: string;
|
|
8
|
+
why?: string;
|
|
9
|
+
expect?: string;
|
|
10
|
+
}
|
|
11
|
+
export interface Feedback {
|
|
12
|
+
/** `<case>@<phase>-<attempt>`, `<case>#<round>@...` after a retry; unique across the tuning */
|
|
13
|
+
id: string;
|
|
14
|
+
case: string;
|
|
15
|
+
caseRev: number;
|
|
16
|
+
/** absent in the first round */
|
|
17
|
+
round?: number;
|
|
18
|
+
phase: Phase;
|
|
19
|
+
attempt: number;
|
|
20
|
+
verdict: Verdict;
|
|
21
|
+
rubric: Record<string, 'pass' | 'fail'>;
|
|
22
|
+
/** right, and nothing left worth changing */
|
|
23
|
+
done: boolean;
|
|
24
|
+
summary: string;
|
|
25
|
+
improvements: Improvement[];
|
|
26
|
+
/** what outside the project made the run void: the sandbox, the network, a provider */
|
|
27
|
+
infra?: string;
|
|
28
|
+
}
|
|
29
|
+
export declare const feedbackId: (caseId: string, phase: Phase, attempt: number, round?: number) => string;
|
|
30
|
+
/**
|
|
31
|
+
* The feedback, or what is wrong with the file - said so a model can fix it.
|
|
32
|
+
* `rubric` is the case's rubric ids: given, every one must be graded.
|
|
33
|
+
*/
|
|
34
|
+
export declare function readFeedback(file: string, known: {
|
|
35
|
+
case: string;
|
|
36
|
+
caseRev: number;
|
|
37
|
+
round?: number;
|
|
38
|
+
phase: Phase;
|
|
39
|
+
attempt: number;
|
|
40
|
+
}, rubricIds?: readonly string[]): {
|
|
41
|
+
feedback: Feedback;
|
|
42
|
+
} | {
|
|
43
|
+
problem: string;
|
|
44
|
+
};
|
|
45
|
+
//# sourceMappingURL=feedback.d.ts.map
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { VERDICTS } from "./dataset/types.js";
|
|
3
|
+
// Round 1 keeps the bare form, so the ids applies already took still match.
|
|
4
|
+
export const feedbackId = (caseId, phase, attempt, round = 1) => `${caseId}${round > 1 ? `#${round}` : ''}@${phase}-${attempt}`;
|
|
5
|
+
const isObject = (v) => typeof v === 'object' && v !== null && !Array.isArray(v);
|
|
6
|
+
/**
|
|
7
|
+
* The feedback, or what is wrong with the file - said so a model can fix it.
|
|
8
|
+
* `rubric` is the case's rubric ids: given, every one must be graded.
|
|
9
|
+
*/
|
|
10
|
+
export function readFeedback(file, known, rubricIds = []) {
|
|
11
|
+
let raw;
|
|
12
|
+
try {
|
|
13
|
+
raw = JSON.parse(readFileSync(file, 'utf8'));
|
|
14
|
+
}
|
|
15
|
+
catch (err) {
|
|
16
|
+
return {
|
|
17
|
+
problem: err.code === 'ENOENT' ? 'missing' : 'not JSON',
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
if (!isObject(raw)) {
|
|
21
|
+
return { problem: 'not a JSON object' };
|
|
22
|
+
}
|
|
23
|
+
if (!VERDICTS.includes(raw.verdict)) {
|
|
24
|
+
return { problem: `"verdict" must be one of ${VERDICTS.join(', ')}` };
|
|
25
|
+
}
|
|
26
|
+
if (typeof raw.done !== 'boolean') {
|
|
27
|
+
return { problem: '"done" must be true or false' };
|
|
28
|
+
}
|
|
29
|
+
if (!Array.isArray(raw.improvements)) {
|
|
30
|
+
return { problem: '"improvements" must be a list (empty when there is nothing to change)' };
|
|
31
|
+
}
|
|
32
|
+
const improvements = [];
|
|
33
|
+
for (const [i, item] of raw.improvements.entries()) {
|
|
34
|
+
if (!isObject(item) || typeof item.change !== 'string' || !item.change.trim()) {
|
|
35
|
+
return { problem: `improvements[${i}] needs a "change"` };
|
|
36
|
+
}
|
|
37
|
+
const text = (key) => typeof item[key] === 'string' ? item[key] : undefined;
|
|
38
|
+
improvements.push({
|
|
39
|
+
id: text('id') ?? `i${i + 1}`,
|
|
40
|
+
change: item.change,
|
|
41
|
+
...(text('kind') ? { kind: text('kind') } : {}),
|
|
42
|
+
...(text('file') ? { file: text('file') } : {}),
|
|
43
|
+
...(text('why') ? { why: text('why') } : {}),
|
|
44
|
+
...(text('expect') ? { expect: text('expect') } : {}),
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
const rubric = {};
|
|
48
|
+
if (isObject(raw.rubric)) {
|
|
49
|
+
for (const [id, result] of Object.entries(raw.rubric)) {
|
|
50
|
+
if (result === 'pass' || result === 'fail') {
|
|
51
|
+
rubric[id] = result;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
const verdict = raw.verdict;
|
|
56
|
+
const infra = typeof raw.infra === 'string' ? raw.infra.trim() : '';
|
|
57
|
+
if (verdict === 'void' && !infra) {
|
|
58
|
+
return {
|
|
59
|
+
problem: 'void without "infra" - say what outside the project failed (the sandbox, the ' +
|
|
60
|
+
'network, a provider, a service the case needs), with node ids',
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
const ungraded = rubricIds.filter((id) => !(id in rubric));
|
|
64
|
+
if (verdict !== 'void' && ungraded.length > 0) {
|
|
65
|
+
return {
|
|
66
|
+
problem: `missing grades for rubric lines ${ungraded.join(', ')} - grade every line of ` +
|
|
67
|
+
`\`zen meta dataset show ${known.case}@${known.caseRev}\` "pass" or "fail" under "rubric"`,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
const failed = Object.values(rubric).includes('fail');
|
|
71
|
+
return {
|
|
72
|
+
feedback: {
|
|
73
|
+
id: feedbackId(known.case, known.phase, known.attempt, known.round),
|
|
74
|
+
...known,
|
|
75
|
+
verdict,
|
|
76
|
+
rubric,
|
|
77
|
+
// `done` on a wrong answer or a failed rubric line is a contradiction; those win.
|
|
78
|
+
done: raw.done && verdict === 'right' && !failed,
|
|
79
|
+
summary: typeof raw.summary === 'string' ? raw.summary : '',
|
|
80
|
+
improvements,
|
|
81
|
+
...(verdict === 'void' ? { infra } : {}),
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=feedback.js.map
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { Feedback } from './feedback.ts';
|
|
2
|
+
import { type Seat, type Tuning } from './tuning.ts';
|
|
3
|
+
export interface AppliedFile {
|
|
4
|
+
ids: string[];
|
|
5
|
+
from: number;
|
|
6
|
+
to: number;
|
|
7
|
+
at: string;
|
|
8
|
+
startedAt?: string;
|
|
9
|
+
skipped?: boolean;
|
|
10
|
+
}
|
|
11
|
+
export declare class Improvements {
|
|
12
|
+
#private;
|
|
13
|
+
readonly pending: Feedback[];
|
|
14
|
+
/** the batch an apply is working on */
|
|
15
|
+
inApply: Feedback[];
|
|
16
|
+
/** cases parked right now */
|
|
17
|
+
parked: number;
|
|
18
|
+
/** `zen run`s in flight */
|
|
19
|
+
runs: number;
|
|
20
|
+
applying: boolean;
|
|
21
|
+
/** the apply under way; `name` once it has a folder, before that it waits for runs */
|
|
22
|
+
current: {
|
|
23
|
+
since: number;
|
|
24
|
+
name?: string;
|
|
25
|
+
dir?: string;
|
|
26
|
+
requests?: number;
|
|
27
|
+
} | undefined;
|
|
28
|
+
/** applies started in this process */
|
|
29
|
+
started: number;
|
|
30
|
+
/** applies finished in this process - an analysis spanning one may see edits */
|
|
31
|
+
count: number;
|
|
32
|
+
constructor(t: Tuning);
|
|
33
|
+
/** Re-reads which requests the applies took - a recovery may have removed one. */
|
|
34
|
+
reload(): void;
|
|
35
|
+
get dir(): string;
|
|
36
|
+
isApplied(id: string): boolean;
|
|
37
|
+
/** Parks until an apply has taken this feedback, the worker free meanwhile. Taken already - after a restart - returns at once. */
|
|
38
|
+
apply(feedback: Feedback, s: Seat): Promise<void>;
|
|
39
|
+
/** Before a run starts: wait out an apply, or stop. */
|
|
40
|
+
notApplying(): Promise<void>;
|
|
41
|
+
/** The applier: runs until the tuning is finished. */
|
|
42
|
+
loop(): Promise<void>;
|
|
43
|
+
/** Requests every apply so far took, for the report. */
|
|
44
|
+
applies(): {
|
|
45
|
+
name: string;
|
|
46
|
+
dir: string;
|
|
47
|
+
applied?: AppliedFile;
|
|
48
|
+
inputs?: Feedback[];
|
|
49
|
+
}[];
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=improvements.d.ts.map
|