@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.
- package/README.md +65 -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 +31 -1
- package/dist/catalog.js +140 -7
- package/dist/commands/index.js +2 -0
- package/dist/commands/inspect.d.ts +52 -2
- package/dist/commands/inspect.js +220 -31
- package/dist/commands/memory.js +156 -32
- package/dist/commands/meta.js +333 -24
- package/dist/commands/models.js +97 -12
- package/dist/commands/open.js +4 -4
- package/dist/commands/run.js +38 -4
- package/dist/commands/sandbox.js +148 -16
- package/dist/commands/view.d.ts +8 -0
- package/dist/commands/view.js +114 -0
- package/dist/engine.d.ts +8 -1
- package/dist/engine.js +7 -0
- package/dist/eventlog.d.ts +64 -0
- package/dist/eventlog.js +170 -0
- package/dist/liveness.d.ts +7 -0
- package/dist/liveness.js +35 -3
- package/dist/main.js +0 -0
- package/dist/meta/editor.d.ts +9 -0
- package/dist/meta/editor.js +43 -0
- package/dist/meta/finetune/analyze-brief.d.ts +26 -0
- package/dist/meta/finetune/analyze-brief.js +131 -0
- package/dist/meta/finetune/apply-brief.d.ts +23 -0
- package/dist/meta/finetune/apply-brief.js +247 -0
- package/dist/meta/finetune/brief.d.ts +45 -0
- package/dist/meta/finetune/brief.js +158 -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 +516 -0
- package/dist/meta/finetune/cost.d.ts +46 -0
- package/dist/meta/finetune/cost.js +105 -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 +127 -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 +57 -0
- package/dist/meta/finetune/feedback.js +104 -0
- package/dist/meta/finetune/guard.d.ts +29 -0
- package/dist/meta/finetune/guard.js +104 -0
- package/dist/meta/finetune/improvements.d.ts +98 -0
- package/dist/meta/finetune/improvements.js +501 -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 +84 -0
- package/dist/meta/finetune/live.js +272 -0
- package/dist/meta/finetune/loop.d.ts +3 -0
- package/dist/meta/finetune/loop.js +163 -0
- package/dist/meta/finetune/memories.d.ts +47 -0
- package/dist/meta/finetune/memories.js +154 -0
- package/dist/meta/finetune/recheck.d.ts +58 -0
- package/dist/meta/finetune/recheck.js +311 -0
- package/dist/meta/finetune/report.d.ts +40 -0
- package/dist/meta/finetune/report.js +1397 -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 +47 -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 +43 -0
- package/dist/meta/finetune/train.js +425 -0
- package/dist/meta/finetune/tui.d.ts +16 -0
- package/dist/meta/finetune/tui.js +316 -0
- package/dist/meta/finetune/tuning.d.ts +193 -0
- package/dist/meta/finetune/tuning.js +308 -0
- package/dist/meta/finetune/usage.d.ts +66 -0
- package/dist/meta/finetune/usage.js +260 -0
- package/dist/meta/finetune/view.d.ts +9 -0
- package/dist/meta/finetune/view.js +27 -0
- package/dist/meta/host.d.ts +15 -0
- package/dist/meta/host.js +21 -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/meta.d.ts +71 -1
- package/dist/meta.js +303 -6
- 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/resources.d.ts +215 -0
- package/dist/resources.js +856 -0
- package/dist/sandbox.d.ts +2 -0
- package/dist/sandbox.js +10 -3
- package/dist/scaffold.d.ts +15 -7
- package/dist/scaffold.js +24 -67
- package/dist/session.js +4 -29
- package/dist/term.d.ts +2 -0
- package/dist/term.js +11 -2
- package/dist/trace.d.ts +5 -0
- package/dist/trace.js +107 -7
- package/dist/tui/app.js +19 -5
- package/dist/tui/links.d.ts +27 -0
- package/dist/tui/links.js +142 -0
- package/dist/tui/models.d.ts +35 -0
- package/dist/tui/models.js +447 -0
- package/dist/tui/sandbox.d.ts +10 -0
- package/dist/tui/sandbox.js +131 -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 +4 -2
- package/templates/editor/.github/copilot-instructions.md +60 -145
- package/templates/editor/.github/prompts/analyze.prompt.md +33 -0
- package/templates/editor/.github/prompts/dataset.prompt.md +108 -0
- package/templates/editor/.github/prompts/finetune-apply.prompt.md +270 -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 +5 -4
- package/templates/editor/.github/skills/zen-analyze-run/SKILL.md +588 -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 +137 -0
- package/templates/editor/.github/skills/zen-cli/references/inspect.md +63 -11
- 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/projects.md +4 -6
- package/templates/editor/.github/skills/zen-cli/references/rag.md +24 -6
- package/templates/editor/.github/skills/zen-cli/references/sandbox.md +35 -14
- 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 +1492 -660
- 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 +74 -26
- package/templates/editor/.github/skills/zen-inspect-ask/SKILL.md +271 -0
- package/templates/editor/.github/skills/zen-instructions/SKILL.md +4 -12
- package/templates/editor/.github/skills/zen-memory/SKILL.md +82 -53
- package/templates/editor/.github/skills/zen-rag-docs/SKILL.md +20 -8
- package/templates/editor/.github/skills/zen-review/SKILL.md +8 -31
- package/templates/editor/.github/skills/zen-review/scripts/check-paths.sh +5 -5
- package/templates/editor/.github/skills/zen-review/scripts/review.sh +3 -12
- package/templates/editor/.github/skills/zen-sandbox/SKILL.md +9 -4
- package/templates/editor/.github/skills/zen-sandbox-capacity/SKILL.md +2 -2
- package/templates/editor/.github/skills/zen-spec-sync/SKILL.md +2 -4
- 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/fork-instructions.md +78 -28
- package/templates/project/agents/memory-instructions.md +222 -494
- package/templates/project/agents/tools-instructions.md +14 -4
- package/templates/project/gitignore +2 -0
- package/templates/editor/.github/skills/zen-finetune/scripts/collect.sh +0 -432
- package/templates/editor/.github/skills/zen-finetune/scripts/sample.sh +0 -199
- package/templates/editor/.github/skills/zen-memory/scripts/check-instructions.sh +0 -116
|
@@ -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,127 @@
|
|
|
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, Grade>;
|
|
91
|
+
text: string;
|
|
92
|
+
by: By;
|
|
93
|
+
}
|
|
94
|
+
/** `invalid`: the rubric line itself is broken - it says how one implementation did it, not what is right. */
|
|
95
|
+
export type Grade = 'pass' | 'fail' | 'invalid';
|
|
96
|
+
export type JournalRow = ChangeRow | NoteRow;
|
|
97
|
+
export interface Ignored {
|
|
98
|
+
file: string;
|
|
99
|
+
anchor: Anchor;
|
|
100
|
+
why: string;
|
|
101
|
+
rev: number;
|
|
102
|
+
}
|
|
103
|
+
export interface Manifest {
|
|
104
|
+
version: number;
|
|
105
|
+
revision: number;
|
|
106
|
+
updatedAt?: string;
|
|
107
|
+
/** whole-file hash of every source a case or an ignore names */
|
|
108
|
+
sources: Record<string, {
|
|
109
|
+
sha256: string;
|
|
110
|
+
}>;
|
|
111
|
+
ignored: Ignored[];
|
|
112
|
+
}
|
|
113
|
+
export interface RevisionRow {
|
|
114
|
+
rev: number;
|
|
115
|
+
at: string;
|
|
116
|
+
why: string;
|
|
117
|
+
by: By;
|
|
118
|
+
added: string[];
|
|
119
|
+
updated: string[];
|
|
120
|
+
retired: string[];
|
|
121
|
+
restored: string[];
|
|
122
|
+
/** cases whose input, rubric or expected changed */
|
|
123
|
+
restarted: string[];
|
|
124
|
+
}
|
|
125
|
+
export declare const anchorText: (anchor: Anchor | undefined) => string;
|
|
126
|
+
export declare const sameAnchor: (a: Anchor | undefined, b: Anchor | undefined) => boolean;
|
|
127
|
+
//# 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,57 @@
|
|
|
1
|
+
import { type Grade, 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, Grade>;
|
|
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
|
+
/** a recheck's: the apply whose version the case failed on, after passing before it */
|
|
29
|
+
recheck?: string;
|
|
30
|
+
/** the try's folder, when it is not the case's own attempt folder */
|
|
31
|
+
dir?: string;
|
|
32
|
+
}
|
|
33
|
+
/** A summary's first paragraph - the verdict, for a table cell or a one-line note. */
|
|
34
|
+
export declare const lead: (summary: string) => string;
|
|
35
|
+
/** A `specification` improvement is for the dataset, never for an apply: prose cannot fix a rubric. */
|
|
36
|
+
export declare const forDataset: (i: Improvement) => boolean;
|
|
37
|
+
/** The improvements an apply can act on. */
|
|
38
|
+
export declare const forProse: (f: {
|
|
39
|
+
improvements: Improvement[];
|
|
40
|
+
}) => Improvement[];
|
|
41
|
+
export declare const feedbackId: (caseId: string, phase: Phase, attempt: number, round?: number) => string;
|
|
42
|
+
/**
|
|
43
|
+
* The feedback, or what is wrong with the file - said so a model can fix it.
|
|
44
|
+
* `rubric` is the case's rubric ids: given, every one must be graded.
|
|
45
|
+
*/
|
|
46
|
+
export declare function readFeedback(file: string, known: {
|
|
47
|
+
case: string;
|
|
48
|
+
caseRev: number;
|
|
49
|
+
round?: number;
|
|
50
|
+
phase: Phase;
|
|
51
|
+
attempt: number;
|
|
52
|
+
}, rubricIds?: readonly string[]): {
|
|
53
|
+
feedback: Feedback;
|
|
54
|
+
} | {
|
|
55
|
+
problem: string;
|
|
56
|
+
};
|
|
57
|
+
//# sourceMappingURL=feedback.d.ts.map
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { VERDICTS } from "./dataset/types.js";
|
|
3
|
+
/** A summary's first paragraph - the verdict, for a table cell or a one-line note. */
|
|
4
|
+
export const lead = (summary) => summary.trim().split(/\n\s*\n/)[0] ?? '';
|
|
5
|
+
/** A `specification` improvement is for the dataset, never for an apply: prose cannot fix a rubric. */
|
|
6
|
+
export const forDataset = (i) => i.kind === 'specification';
|
|
7
|
+
/** The improvements an apply can act on. */
|
|
8
|
+
export const forProse = (f) => f.improvements.filter((i) => !forDataset(i));
|
|
9
|
+
// Round 1 keeps the bare form, so the ids applies already took still match.
|
|
10
|
+
export const feedbackId = (caseId, phase, attempt, round = 1) => `${caseId}${round > 1 ? `#${round}` : ''}@${phase}-${attempt}`;
|
|
11
|
+
const isObject = (v) => typeof v === 'object' && v !== null && !Array.isArray(v);
|
|
12
|
+
/**
|
|
13
|
+
* The feedback, or what is wrong with the file - said so a model can fix it.
|
|
14
|
+
* `rubric` is the case's rubric ids: given, every one must be graded.
|
|
15
|
+
*/
|
|
16
|
+
export function readFeedback(file, known, rubricIds = []) {
|
|
17
|
+
let raw;
|
|
18
|
+
try {
|
|
19
|
+
raw = JSON.parse(readFileSync(file, 'utf8'));
|
|
20
|
+
}
|
|
21
|
+
catch (err) {
|
|
22
|
+
return {
|
|
23
|
+
problem: err.code === 'ENOENT' ? 'missing' : 'not JSON',
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
if (!isObject(raw)) {
|
|
27
|
+
return { problem: 'not a JSON object' };
|
|
28
|
+
}
|
|
29
|
+
if (!VERDICTS.includes(raw.verdict)) {
|
|
30
|
+
return { problem: `"verdict" must be one of ${VERDICTS.join(', ')}` };
|
|
31
|
+
}
|
|
32
|
+
if (typeof raw.done !== 'boolean') {
|
|
33
|
+
return { problem: '"done" must be true or false' };
|
|
34
|
+
}
|
|
35
|
+
if (!Array.isArray(raw.improvements)) {
|
|
36
|
+
return { problem: '"improvements" must be a list (empty when there is nothing to change)' };
|
|
37
|
+
}
|
|
38
|
+
const improvements = [];
|
|
39
|
+
for (const [i, item] of raw.improvements.entries()) {
|
|
40
|
+
if (!isObject(item) || typeof item.change !== 'string' || !item.change.trim()) {
|
|
41
|
+
return { problem: `improvements[${i}] needs a "change"` };
|
|
42
|
+
}
|
|
43
|
+
const text = (key) => typeof item[key] === 'string' ? item[key] : undefined;
|
|
44
|
+
improvements.push({
|
|
45
|
+
id: text('id') ?? `i${i + 1}`,
|
|
46
|
+
change: item.change,
|
|
47
|
+
...(text('kind') ? { kind: text('kind') } : {}),
|
|
48
|
+
...(text('file') ? { file: text('file') } : {}),
|
|
49
|
+
...(text('why') ? { why: text('why') } : {}),
|
|
50
|
+
...(text('expect') ? { expect: text('expect') } : {}),
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
const rubric = {};
|
|
54
|
+
if (isObject(raw.rubric)) {
|
|
55
|
+
for (const [id, result] of Object.entries(raw.rubric)) {
|
|
56
|
+
if (result === 'pass' || result === 'fail' || result === 'invalid') {
|
|
57
|
+
rubric[id] = result;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
const verdict = raw.verdict;
|
|
62
|
+
const infra = typeof raw.infra === 'string' ? raw.infra.trim() : '';
|
|
63
|
+
if (verdict === 'void' && !infra) {
|
|
64
|
+
return {
|
|
65
|
+
problem: 'void without "infra" - say what outside the project failed (the sandbox, the ' +
|
|
66
|
+
'network, a provider, a service the case needs), with node ids',
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
const ungraded = rubricIds.filter((id) => !(id in rubric));
|
|
70
|
+
if (verdict !== 'void' && ungraded.length > 0) {
|
|
71
|
+
return {
|
|
72
|
+
problem: `missing grades for rubric lines ${ungraded.join(', ')} - grade every line of ` +
|
|
73
|
+
`\`zen meta dataset show ${known.case}@${known.caseRev}\` "pass", "fail" or "invalid" under "rubric"`,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
const unexplained = Object.entries(rubric)
|
|
77
|
+
.filter(([, g]) => g === 'invalid')
|
|
78
|
+
.map(([id]) => id)
|
|
79
|
+
.filter((id) => !improvements.some((i) => forDataset(i) && `${i.change} ${i.why ?? ''}`.split(/[^\w-]+/).includes(id)));
|
|
80
|
+
if (unexplained.length > 0) {
|
|
81
|
+
return {
|
|
82
|
+
problem: `rubric lines ${unexplained.join(', ')} are graded "invalid" with no ` +
|
|
83
|
+
'"specification" improvement naming them - add one per line saying what is wrong ' +
|
|
84
|
+
'with it and how it should read',
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
const failed = Object.values(rubric).includes('fail');
|
|
88
|
+
// A right run whose only findings are about the dataset has nothing left for the prose.
|
|
89
|
+
const onlyDataset = improvements.length > 0 && forProse({ improvements }).length === 0;
|
|
90
|
+
return {
|
|
91
|
+
feedback: {
|
|
92
|
+
id: feedbackId(known.case, known.phase, known.attempt, known.round),
|
|
93
|
+
...known,
|
|
94
|
+
verdict,
|
|
95
|
+
rubric,
|
|
96
|
+
// `done` on a wrong answer or a failed rubric line is a contradiction; those win.
|
|
97
|
+
done: (raw.done || onlyDataset) && verdict === 'right' && !failed,
|
|
98
|
+
summary: typeof raw.summary === 'string' ? raw.summary : '',
|
|
99
|
+
improvements,
|
|
100
|
+
...(verdict === 'void' ? { infra } : {}),
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=feedback.js.map
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { Case } from './dataset/types.ts';
|
|
2
|
+
import { type Tuning } from './tuning.ts';
|
|
3
|
+
export interface GuardTry {
|
|
4
|
+
dir: string;
|
|
5
|
+
verdict: string;
|
|
6
|
+
passed: boolean;
|
|
7
|
+
summary?: string;
|
|
8
|
+
}
|
|
9
|
+
export interface GuardResult {
|
|
10
|
+
case: string;
|
|
11
|
+
tries: GuardTry[];
|
|
12
|
+
broken: boolean;
|
|
13
|
+
}
|
|
14
|
+
/** A guard case as the live view and the status page show it, updated as it goes. */
|
|
15
|
+
export interface GuardSeat {
|
|
16
|
+
case: string;
|
|
17
|
+
try: number;
|
|
18
|
+
step: 'run' | 'analyze' | 'passed' | 'broken';
|
|
19
|
+
/** the try's directory */
|
|
20
|
+
dir: string;
|
|
21
|
+
since: number;
|
|
22
|
+
}
|
|
23
|
+
/** A case is broken only after failing this many tries in a row. */
|
|
24
|
+
export declare const GUARD_TRIES = 2;
|
|
25
|
+
/** Completed cases outside `exclude`, in an order fixed by the seed and the apply. */
|
|
26
|
+
export declare function guardCases(t: Tuning, apply: string, exclude: Set<string>): Case[];
|
|
27
|
+
/** Runs the guard for one attempt of an apply; `patch` is that attempt's diff. */
|
|
28
|
+
export declare function guard(t: Tuning, dir: string, attempt: number, exclude: Set<string>, patch: string): Promise<GuardResult[]>;
|
|
29
|
+
//# sourceMappingURL=guard.d.ts.map
|