@zenera/cli 1.1.23 → 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 +88 -2
- package/dist/ask.d.ts +51 -0
- package/dist/ask.js +166 -0
- package/dist/banner.js +2 -2
- package/dist/batch.d.ts +83 -0
- package/dist/batch.js +413 -0
- package/dist/catalog.d.ts +11 -1
- package/dist/catalog.js +80 -7
- package/dist/command.d.ts +6 -0
- package/dist/commands/inspect.d.ts +57 -0
- package/dist/commands/inspect.js +539 -91
- package/dist/commands/list.js +128 -19
- package/dist/commands/memory.js +461 -32
- package/dist/commands/meta.js +311 -21
- package/dist/commands/models.js +110 -14
- package/dist/commands/open.js +4 -4
- package/dist/commands/run.js +175 -36
- package/dist/commands/sandbox.js +148 -15
- package/dist/engine.d.ts +24 -0
- package/dist/engine.js +61 -8
- package/dist/eventlog.d.ts +59 -0
- package/dist/eventlog.js +146 -0
- package/dist/liveness.d.ts +7 -0
- package/dist/liveness.js +20 -3
- package/dist/main.js +5 -2
- 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 +42 -0
- package/dist/podman.js +198 -5
- package/dist/progress.d.ts +33 -0
- package/dist/progress.js +402 -0
- package/dist/projects.d.ts +0 -5
- package/dist/projects.js +2 -15
- package/dist/request.d.ts +45 -0
- package/dist/request.js +300 -0
- package/dist/sandbox.d.ts +2 -0
- package/dist/sandbox.js +20 -3
- package/dist/scaffold.d.ts +15 -6
- package/dist/scaffold.js +5 -60
- package/dist/session.d.ts +26 -1
- package/dist/session.js +46 -32
- package/dist/term.d.ts +5 -0
- package/dist/term.js +28 -3
- package/dist/trace.d.ts +84 -0
- package/dist/trace.js +737 -0
- 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/dist/validate.js +25 -3
- package/package.json +3 -2
- package/templates/editor/.github/copilot-instructions.md +41 -107
- 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 +744 -0
- 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 +18 -4
- package/templates/editor/.github/skills/zen-cli/references/check.md +7 -5
- 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 +300 -21
- package/templates/editor/.github/skills/zen-cli/references/memory.md +121 -28
- package/templates/editor/.github/skills/zen-cli/references/meta.md +61 -0
- package/templates/editor/.github/skills/zen-cli/references/models.md +6 -2
- package/templates/editor/.github/skills/zen-cli/references/projects.md +12 -5
- package/templates/editor/.github/skills/zen-cli/references/rag.md +24 -6
- package/templates/editor/.github/skills/zen-cli/references/run.md +201 -4
- 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 +1791 -0
- 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 +431 -0
- package/templates/editor/.github/skills/zen-inspect-ask/SKILL.md +270 -0
- package/templates/editor/.github/skills/zen-memory/SKILL.md +78 -29
- package/templates/editor/.github/skills/zen-memory-warmup/SKILL.md +363 -531
- 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 +25 -2
- package/templates/editor/.github/skills/zen-sandbox-capacity/SKILL.md +334 -0
- package/templates/editor/.github/skills/zen-sandbox-capacity/references/loadtest.md +181 -0
- package/templates/editor/.github/skills/zen-sandbox-capacity/scripts/preflight_sandbox.sh +457 -0
- package/templates/editor/.github/skills/zen-topology/SKILL.md +283 -0
- package/templates/editor/.vscode/agents.schema.json +1 -1
- package/templates/editor/.vscode/settings.json +2 -0
- package/templates/project/agents/files-instructions.md +35 -13
- package/templates/project/agents/fork-instructions.md +27 -0
- package/templates/project/agents/memory-instructions.md +106 -34
- package/templates/project/agents/tools-instructions.md +7 -4
- package/templates/project/gitignore +5 -0
- package/dist/meta.d.ts +0 -221
- package/dist/meta.js +0 -684
package/dist/batch.js
ADDED
|
@@ -0,0 +1,413 @@
|
|
|
1
|
+
import { addUsage, lockHolder, MANIFEST_FILE as MEMORY_MANIFEST, memoryDir, MemoryStore, mergeModelUsage, readProjectConfig, SANDBOX_GROUP, selectTools, usageByModel, zeroUsage, } from '@zenera/neo';
|
|
2
|
+
import { existsSync } from 'node:fs';
|
|
3
|
+
import { cp, mkdir, rm, writeFile } from 'node:fs/promises';
|
|
4
|
+
import { basename, join, resolve } from 'node:path';
|
|
5
|
+
import * as Engine from "./engine.js";
|
|
6
|
+
import { stamp } from "./ids.js";
|
|
7
|
+
import { duration } from "./narrate.js";
|
|
8
|
+
import { BatchProgress } from "./progress.js";
|
|
9
|
+
import { assertLocks } from "./podman.js";
|
|
10
|
+
import { project as resolveProject, target } from "./resolve.js";
|
|
11
|
+
import { display } from "./session.js";
|
|
12
|
+
import { CliError, count, cyan, dim, EXIT, invalidError, json, jsonText, note, usageError, write, } from "./term.js";
|
|
13
|
+
import { appendUsage } from "./usage.js";
|
|
14
|
+
import { availableTools } from "./validate.js";
|
|
15
|
+
/** Past this, the limit is the provider's rate limit and the failures are 429s. */
|
|
16
|
+
const MAX_CONCURRENCY = 32;
|
|
17
|
+
export async function runBatch(opts) {
|
|
18
|
+
const project = await resolveProject({ cwd: opts.cwd, project: opts.project });
|
|
19
|
+
const items = opts.request.items;
|
|
20
|
+
// Every item at once holds its own containers, so a short engine fails
|
|
21
|
+
// here once instead of once per item.
|
|
22
|
+
const boxes = containersPerItem(project.dir);
|
|
23
|
+
if (boxes > 0) {
|
|
24
|
+
await assertLocks(boxes * Math.min(opts.concurrency, items.length));
|
|
25
|
+
}
|
|
26
|
+
const dir = opts.dir ? resolve(opts.cwd, opts.dir) : join(project.dir, 'batches', stamp());
|
|
27
|
+
// Two batches in one directory would interleave their item folders and the
|
|
28
|
+
// second would overwrite the first's index while the first still points at
|
|
29
|
+
// it. Cheaper to refuse than to explain.
|
|
30
|
+
if (existsSync(join(dir, 'batch.json'))) {
|
|
31
|
+
throw invalidError(`${dir} already holds a batch`, 'name an empty directory with --batch-dir, or leave it out for a fresh one');
|
|
32
|
+
}
|
|
33
|
+
await mkdir(dir, { recursive: true });
|
|
34
|
+
const source = memorySource(project.dir, opts.memory);
|
|
35
|
+
const mode = source ? (opts.memoryReadOnly ? 'read-only' : 'copied') : 'none';
|
|
36
|
+
// A project declares its memory directory; it is the first run that makes
|
|
37
|
+
// one. So a batch may legitimately be pointed at a graph that is not there
|
|
38
|
+
// yet - which is the whole of warming a cold project.
|
|
39
|
+
const seeded = source !== undefined && existsSync(source);
|
|
40
|
+
if (opts.memoryReadOnly && !source) {
|
|
41
|
+
throw usageError('--memory-read-only, but this project has no memory', 'give one with --memory, or drop the flag');
|
|
42
|
+
}
|
|
43
|
+
if (source && seeded) {
|
|
44
|
+
// A graph is three files that only mean anything together. Reading one
|
|
45
|
+
// mid-commit is a wrong answer and copying one is a wrong archive, so
|
|
46
|
+
// both modes wait rather than race.
|
|
47
|
+
const held = lockHolder(source);
|
|
48
|
+
if (held) {
|
|
49
|
+
throw invalidError(`that memory is in use (pid ${held.pid}, since ${held.startedAt})`, 'wait for that run to finish, or point --memory at another directory');
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
// Serially, and before anything runs: sixteen recursive copies of the same
|
|
53
|
+
// tree at once is the one moment a batch is disk-bound, and it would be
|
|
54
|
+
// spent racing itself.
|
|
55
|
+
const prepared = await prepareMemories(items, { dir, mode, source, seeded });
|
|
56
|
+
// Opened once, here, so a path that is not a memory is one error rather
|
|
57
|
+
// than one per item — and so the read-only refusal lands before any cost.
|
|
58
|
+
if (mode === 'read-only' && source) {
|
|
59
|
+
await MemoryStore.open(source, { readOnly: true });
|
|
60
|
+
}
|
|
61
|
+
// Ctrl-C asks every run to stop rather than killing the process: each one
|
|
62
|
+
// still lands its turn, and each finished item keeps the answer it already
|
|
63
|
+
// wrote.
|
|
64
|
+
const stopping = new AbortController();
|
|
65
|
+
const onInterrupt = () => stopping.abort();
|
|
66
|
+
process.once('SIGINT', onInterrupt);
|
|
67
|
+
if (!opts.json) {
|
|
68
|
+
note(`${cyan(project.name)} ${dim(`${count(items.length, 'item')}, ${opts.concurrency} at a time`)}`);
|
|
69
|
+
note(dim(`memory: ${source ? `${mode}${seeded ? '' : ' (new)'}, ${display(source, opts.cwd)}` : 'none'}`));
|
|
70
|
+
note(dim(`batch: ${display(dir, opts.cwd)}`));
|
|
71
|
+
note(dim(`watch: ${display(join(dir, 'README.md'), opts.cwd)}`));
|
|
72
|
+
note('');
|
|
73
|
+
}
|
|
74
|
+
// The dashboard, from here until the last item lands.
|
|
75
|
+
const progress = new BatchProgress({
|
|
76
|
+
dir,
|
|
77
|
+
project: project.name,
|
|
78
|
+
projectDir: project.dir,
|
|
79
|
+
input: opts.input,
|
|
80
|
+
concurrency: opts.concurrency,
|
|
81
|
+
memory: source ? `${mode}${seeded ? '' : ' (new)'} — \`${source}\`` : 'none',
|
|
82
|
+
items,
|
|
83
|
+
});
|
|
84
|
+
progress.begin();
|
|
85
|
+
const startedAt = new Date();
|
|
86
|
+
let done = 0;
|
|
87
|
+
// What each item spent, per model. Collected here rather than read back
|
|
88
|
+
// out of the dashboard, which is deliberately not load-bearing.
|
|
89
|
+
const spent = [];
|
|
90
|
+
const results = await pool(items, opts.concurrency, async (item) => {
|
|
91
|
+
const result = await runItem(item, {
|
|
92
|
+
opts,
|
|
93
|
+
projectDir: project.dir,
|
|
94
|
+
dir,
|
|
95
|
+
memory: prepared.get(item.id),
|
|
96
|
+
memoryReadOnly: mode === 'read-only',
|
|
97
|
+
signal: stopping.signal,
|
|
98
|
+
progress,
|
|
99
|
+
spent,
|
|
100
|
+
});
|
|
101
|
+
done += 1;
|
|
102
|
+
if (!opts.json) {
|
|
103
|
+
note(`${dim(`[${done}/${items.length}]`)} ` +
|
|
104
|
+
`${result.ok ? cyan('ok') : 'failed'} ${item.id}` +
|
|
105
|
+
`${result.error ? dim(` — ${result.error.message}`) : ''}`);
|
|
106
|
+
}
|
|
107
|
+
return result;
|
|
108
|
+
});
|
|
109
|
+
process.off('SIGINT', onInterrupt);
|
|
110
|
+
await progress.close();
|
|
111
|
+
// An item that was given an empty graph and found nothing worth keeping
|
|
112
|
+
// leaves a directory that is not a memory, and `merge` rightly refuses
|
|
113
|
+
// one. Nothing was learned there, so there is nothing to leave behind.
|
|
114
|
+
const written = mode === 'copied' ? await keepWritten(prepared) : 0;
|
|
115
|
+
const finishedAt = new Date();
|
|
116
|
+
const failed = results.filter((r) => !r.ok).length;
|
|
117
|
+
const models = mergeModelUsage(...spent);
|
|
118
|
+
const body = {
|
|
119
|
+
batch: {
|
|
120
|
+
dir,
|
|
121
|
+
readme: join(dir, 'README.md'),
|
|
122
|
+
input: opts.input,
|
|
123
|
+
project: project.dir,
|
|
124
|
+
items: items.length,
|
|
125
|
+
ok: results.length - failed,
|
|
126
|
+
failed,
|
|
127
|
+
concurrency: opts.concurrency,
|
|
128
|
+
memory: { source: source ?? null, mode },
|
|
129
|
+
usage: models.reduce((sum, m) => addUsage(sum, m.usage), zeroUsage()),
|
|
130
|
+
models,
|
|
131
|
+
startedAt: startedAt.toISOString(),
|
|
132
|
+
finishedAt: finishedAt.toISOString(),
|
|
133
|
+
durationMs: finishedAt.getTime() - startedAt.getTime(),
|
|
134
|
+
},
|
|
135
|
+
batch_results: results,
|
|
136
|
+
};
|
|
137
|
+
const text = jsonText(body);
|
|
138
|
+
await writeFile(join(dir, 'batch.json'), text, 'utf8');
|
|
139
|
+
appendUsage(project.dir, {
|
|
140
|
+
kind: 'batch',
|
|
141
|
+
ts: finishedAt.toISOString(),
|
|
142
|
+
batchDir: dir,
|
|
143
|
+
input: opts.input,
|
|
144
|
+
items: items.length,
|
|
145
|
+
ok: results.length - failed,
|
|
146
|
+
failed,
|
|
147
|
+
concurrency: opts.concurrency,
|
|
148
|
+
memory: mode,
|
|
149
|
+
startedAt: startedAt.toISOString(),
|
|
150
|
+
durationMs: finishedAt.getTime() - startedAt.getTime(),
|
|
151
|
+
models,
|
|
152
|
+
});
|
|
153
|
+
if (opts.out) {
|
|
154
|
+
await writeFile(resolve(opts.cwd, opts.out), text, 'utf8');
|
|
155
|
+
}
|
|
156
|
+
if (opts.json) {
|
|
157
|
+
json(body);
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
note('');
|
|
161
|
+
note(`${count(results.length - failed, 'run')} ok, ${failed} failed ` +
|
|
162
|
+
dim(duration(finishedAt.getTime() - startedAt.getTime())));
|
|
163
|
+
if (mode === 'copied' && written > 0) {
|
|
164
|
+
note(dim(`fold what they learned back in: zen memory merge ${dir}/*/memory`));
|
|
165
|
+
}
|
|
166
|
+
// The batch directory is the answer, and it is the whole answer: every
|
|
167
|
+
// path in the index is under it.
|
|
168
|
+
write(dir);
|
|
169
|
+
}
|
|
170
|
+
// After everything is written. A batch that ends in a non-zero exit still
|
|
171
|
+
// has to leave behind the answers it did get.
|
|
172
|
+
if (failed > 0) {
|
|
173
|
+
throw new CliError(`${count(failed, 'item')} failed`, EXIT.failed, `what each one said: ${join(dir, 'batch.json')}`);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
// ---------------------------------------------------------------------------
|
|
177
|
+
// One item
|
|
178
|
+
// ---------------------------------------------------------------------------
|
|
179
|
+
/**
|
|
180
|
+
* The most containers one item can start: one shared by the agents on the
|
|
181
|
+
* project's sandbox block, one more for each agent with a block of its own.
|
|
182
|
+
* A project that does not load is each item's to report, so it counts none.
|
|
183
|
+
*/
|
|
184
|
+
export function containersPerItem(dir) {
|
|
185
|
+
let read;
|
|
186
|
+
try {
|
|
187
|
+
read = readProjectConfig(dir);
|
|
188
|
+
}
|
|
189
|
+
catch {
|
|
190
|
+
return 0;
|
|
191
|
+
}
|
|
192
|
+
const tools = availableTools(read.root, read.config);
|
|
193
|
+
const shells = read.config.agents.filter((a) => {
|
|
194
|
+
try {
|
|
195
|
+
return selectTools(tools, [...(a.tools ?? [])], { where: a.name }).some((t) => t.group === SANDBOX_GROUP);
|
|
196
|
+
}
|
|
197
|
+
catch {
|
|
198
|
+
return false;
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
const own = shells.filter((a) => a.sandbox).length;
|
|
202
|
+
return own + (shells.length > own ? 1 : 0);
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* A failure is data. One item that asks something the model cannot answer must
|
|
206
|
+
* not cost the other ninety-nine their answers, so everything is caught and
|
|
207
|
+
* written down, and the exit code is decided once at the end.
|
|
208
|
+
*/
|
|
209
|
+
async function runItem(item, ctx) {
|
|
210
|
+
const at = join(ctx.dir, item.id);
|
|
211
|
+
const output = join(at, 'output.json');
|
|
212
|
+
const watch = ctx.progress.watch(item.id);
|
|
213
|
+
try {
|
|
214
|
+
const where = await target({
|
|
215
|
+
cwd: ctx.opts.cwd,
|
|
216
|
+
project: ctx.projectDir,
|
|
217
|
+
// Every item is its own conversation. There is no session to
|
|
218
|
+
// resume and nobody to ask which one.
|
|
219
|
+
fresh: true,
|
|
220
|
+
workspace: item.workspace ?? join(at, 'workspace'),
|
|
221
|
+
yes: true,
|
|
222
|
+
});
|
|
223
|
+
const engine = await Engine.open({
|
|
224
|
+
project: where.project,
|
|
225
|
+
session: where.session,
|
|
226
|
+
readOnly: ctx.opts.readOnly,
|
|
227
|
+
model: ctx.opts.model,
|
|
228
|
+
image: ctx.opts.image,
|
|
229
|
+
memoryDir: ctx.memory,
|
|
230
|
+
memoryReadOnly: ctx.memoryReadOnly,
|
|
231
|
+
keys: ctx.opts.keys,
|
|
232
|
+
yes: true,
|
|
233
|
+
// The project's own problems are the batch's, not each item's.
|
|
234
|
+
quiet: true,
|
|
235
|
+
// A fresh session nobody resumes: a stopped container per item
|
|
236
|
+
// is how a few fine-tuning rounds used up every engine lock.
|
|
237
|
+
disposable: true,
|
|
238
|
+
});
|
|
239
|
+
try {
|
|
240
|
+
// Nothing is narrated to the terminal — a hundred interleaved
|
|
241
|
+
// streams there is not progress, it is noise. The events go to the
|
|
242
|
+
// dashboard instead, where each item has a line of its own.
|
|
243
|
+
const outcome = await Engine.run(engine, item.input, watch, ctx.signal);
|
|
244
|
+
// Written the moment it is known, not at the end — a batch killed
|
|
245
|
+
// halfway still has every answer it managed to get.
|
|
246
|
+
await writeFile(output, jsonText(Engine.envelope(engine, outcome)), 'utf8');
|
|
247
|
+
const models = usageByModel(outcome.result.state.trajectory);
|
|
248
|
+
ctx.spent.push(models);
|
|
249
|
+
appendUsage(ctx.projectDir, {
|
|
250
|
+
kind: 'run',
|
|
251
|
+
ts: new Date().toISOString(),
|
|
252
|
+
runDir: outcome.run.dir,
|
|
253
|
+
session: engine.session.id,
|
|
254
|
+
batchDir: ctx.dir,
|
|
255
|
+
item: item.id,
|
|
256
|
+
ok: true,
|
|
257
|
+
stopReason: outcome.result.stopReason,
|
|
258
|
+
durationMs: outcome.durationMs,
|
|
259
|
+
models,
|
|
260
|
+
});
|
|
261
|
+
ctx.progress.finish(item.id, { ok: true, usage: outcome.result.usage, models });
|
|
262
|
+
return { index: item.index, id: item.id, ok: true, input: index(item.input), output };
|
|
263
|
+
}
|
|
264
|
+
finally {
|
|
265
|
+
await engine.close();
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
catch (err) {
|
|
269
|
+
const error = {
|
|
270
|
+
message: err instanceof Error ? err.message : String(err),
|
|
271
|
+
...(err instanceof CliError && err.hint ? { hint: err.hint } : {}),
|
|
272
|
+
};
|
|
273
|
+
await writeFile(output, jsonText({ ok: false, id: item.id, error }), 'utf8');
|
|
274
|
+
appendUsage(ctx.projectDir, {
|
|
275
|
+
kind: 'run',
|
|
276
|
+
ts: new Date().toISOString(),
|
|
277
|
+
batchDir: ctx.dir,
|
|
278
|
+
item: item.id,
|
|
279
|
+
ok: false,
|
|
280
|
+
error: error.message,
|
|
281
|
+
models: [],
|
|
282
|
+
});
|
|
283
|
+
ctx.progress.finish(item.id, { ok: false, error: error.message });
|
|
284
|
+
return {
|
|
285
|
+
index: item.index,
|
|
286
|
+
id: item.id,
|
|
287
|
+
ok: false,
|
|
288
|
+
input: index(item.input),
|
|
289
|
+
output,
|
|
290
|
+
error,
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
// ---------------------------------------------------------------------------
|
|
295
|
+
// Parts
|
|
296
|
+
// ---------------------------------------------------------------------------
|
|
297
|
+
/**
|
|
298
|
+
* A bounded map that keeps its order. Workers pull from one cursor rather than
|
|
299
|
+
* taking a slice each, so a batch of one slow item and fifty fast ones finishes
|
|
300
|
+
* in the time of the slow one instead of the time of the slowest slice.
|
|
301
|
+
*
|
|
302
|
+
* `worker` is expected not to throw; `runItem` is the only caller and catches.
|
|
303
|
+
*/
|
|
304
|
+
export async function pool(items, limit, worker) {
|
|
305
|
+
const out = new Array(items.length);
|
|
306
|
+
let next = 0;
|
|
307
|
+
const runner = async () => {
|
|
308
|
+
for (;;) {
|
|
309
|
+
const i = next;
|
|
310
|
+
next += 1;
|
|
311
|
+
if (i >= items.length) {
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
out[i] = await worker(items[i], i);
|
|
315
|
+
}
|
|
316
|
+
};
|
|
317
|
+
await Promise.all(Array.from({ length: Math.min(limit, items.length) }, runner));
|
|
318
|
+
return out;
|
|
319
|
+
}
|
|
320
|
+
/** `--concurrency`, or the default. */
|
|
321
|
+
export function concurrency(value) {
|
|
322
|
+
if (value === undefined) {
|
|
323
|
+
return 16;
|
|
324
|
+
}
|
|
325
|
+
const n = Number(value);
|
|
326
|
+
if (!Number.isInteger(n) || n < 1) {
|
|
327
|
+
throw usageError(`--concurrency ${value}: expected a whole number, 1 or more`);
|
|
328
|
+
}
|
|
329
|
+
if (n > MAX_CONCURRENCY) {
|
|
330
|
+
throw usageError(`--concurrency ${n} is more than this runs at once`, `${MAX_CONCURRENCY} is the most; past that the limit is the provider's, not ours`);
|
|
331
|
+
}
|
|
332
|
+
return n;
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
* Each item's memory, made before any of them runs.
|
|
336
|
+
*
|
|
337
|
+
* In the copying mode every item gets its own graph to write into, so that
|
|
338
|
+
* sixteen runs at once are sixteen private histories rather than one contended
|
|
339
|
+
* lock. A source that is not there yet is not an error: a project declares its
|
|
340
|
+
* memory directory and the first run makes it, so warming a cold project means
|
|
341
|
+
* copying from nothing, and nothing is what each item starts with.
|
|
342
|
+
*/
|
|
343
|
+
export async function prepareMemories(items, at) {
|
|
344
|
+
const prepared = new Map();
|
|
345
|
+
for (const item of items) {
|
|
346
|
+
await mkdir(join(at.dir, item.id), { recursive: true });
|
|
347
|
+
if (at.mode === 'copied' && at.source) {
|
|
348
|
+
const mine = join(at.dir, item.id, 'memory');
|
|
349
|
+
if (at.seeded) {
|
|
350
|
+
await cp(at.source, mine, { recursive: true, filter: notLock });
|
|
351
|
+
}
|
|
352
|
+
prepared.set(item.id, mine);
|
|
353
|
+
}
|
|
354
|
+
else if (at.mode === 'read-only' && at.source) {
|
|
355
|
+
prepared.set(item.id, at.source);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
return prepared;
|
|
359
|
+
}
|
|
360
|
+
/**
|
|
361
|
+
* Drops the memories nothing was committed to, and counts the ones left.
|
|
362
|
+
*
|
|
363
|
+
* A memory is its manifest and the two files beside it; a directory without
|
|
364
|
+
* one is not a memory and `merge` refuses it by name. Leaving those behind
|
|
365
|
+
* would mean the `merge` line this command prints is advice that fails, so an
|
|
366
|
+
* item that learned nothing leaves nothing.
|
|
367
|
+
*/
|
|
368
|
+
export async function keepWritten(prepared) {
|
|
369
|
+
let kept = 0;
|
|
370
|
+
for (const at of prepared.values()) {
|
|
371
|
+
if (existsSync(join(at, MEMORY_MANIFEST))) {
|
|
372
|
+
kept += 1;
|
|
373
|
+
}
|
|
374
|
+
else {
|
|
375
|
+
await rm(at, { recursive: true, force: true });
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
return kept;
|
|
379
|
+
}
|
|
380
|
+
/** Where the batch reads memory from: `--memory`, else what the project says. */
|
|
381
|
+
function memorySource(projectDir, override) {
|
|
382
|
+
try {
|
|
383
|
+
return memoryDir(projectDir, readProjectConfig(projectDir).config, override);
|
|
384
|
+
}
|
|
385
|
+
catch {
|
|
386
|
+
// An unloadable project is about to fail properly, with its own error.
|
|
387
|
+
return override;
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
/** The lock belongs to the run that took it, never to a copy of its memory. */
|
|
391
|
+
const notLock = (src) => basename(src) !== '.lock';
|
|
392
|
+
/**
|
|
393
|
+
* The question, for the index — text whole, media named rather than repeated.
|
|
394
|
+
* Every byte is already in the item's own run input file, and a combined file
|
|
395
|
+
* carrying forty inlined screenshots is not an index of anything.
|
|
396
|
+
*/
|
|
397
|
+
function index(input) {
|
|
398
|
+
if (typeof input === 'string') {
|
|
399
|
+
return input;
|
|
400
|
+
}
|
|
401
|
+
const parts = Array.isArray(input) ? input : [input];
|
|
402
|
+
return parts.map((part) => summarise(part));
|
|
403
|
+
}
|
|
404
|
+
function summarise(part) {
|
|
405
|
+
if (part.type === 'text') {
|
|
406
|
+
return part;
|
|
407
|
+
}
|
|
408
|
+
if (part.url.startsWith('data:')) {
|
|
409
|
+
return { type: part.type, mimeType: part.mimeType, bytes: part.url.length, inlined: true };
|
|
410
|
+
}
|
|
411
|
+
return part;
|
|
412
|
+
}
|
|
413
|
+
//# sourceMappingURL=batch.js.map
|
package/dist/catalog.d.ts
CHANGED
|
@@ -36,6 +36,8 @@ export interface CatalogEntry {
|
|
|
36
36
|
};
|
|
37
37
|
/** ISO date the model was published, when known */
|
|
38
38
|
created?: string;
|
|
39
|
+
/** vertex only: who makes it - `google`, or the partner Vertex resells */
|
|
40
|
+
publisher?: string;
|
|
39
41
|
source: 'live' | 'curated';
|
|
40
42
|
}
|
|
41
43
|
/** A provider's listing, and an honest account of where it came from. */
|
|
@@ -72,6 +74,7 @@ export declare const PREFERRED: Record<Provider, {
|
|
|
72
74
|
chat: readonly string[];
|
|
73
75
|
embedding: readonly string[];
|
|
74
76
|
}>;
|
|
77
|
+
export declare function fromVertexPartners(): Promise<CatalogEntry[]>;
|
|
75
78
|
/**
|
|
76
79
|
* Asks one provider what it serves. Throws whatever the SDK throws.
|
|
77
80
|
*
|
|
@@ -79,8 +82,9 @@ export declare const PREFERRED: Record<Provider, {
|
|
|
79
82
|
* to break when a vendor reshapes a payload, and they are untestable if the
|
|
80
83
|
* only way to reach them is a credential and a network. It mirrors
|
|
81
84
|
* `ProviderSpec.client`, which exists in the library for the same reason.
|
|
85
|
+
* `partners` is the same seam for Vertex's second listing.
|
|
82
86
|
*/
|
|
83
|
-
export declare function fetchCatalog(provider: Provider, client?: unknown): Promise<CatalogEntry[]>;
|
|
87
|
+
export declare function fetchCatalog(provider: Provider, client?: unknown, partners?: () => Promise<CatalogEntry[]>): Promise<CatalogEntry[]>;
|
|
84
88
|
/** The key one provider's listing is filed under, version and all. */
|
|
85
89
|
export declare const catalogKey: (provider: Provider) => string;
|
|
86
90
|
export interface CatalogOptions {
|
|
@@ -111,4 +115,10 @@ export interface Filters {
|
|
|
111
115
|
export declare function matches(row: CatalogEntry, query: string, filters?: Filters): boolean;
|
|
112
116
|
/** The providers a search covers when none was named. */
|
|
113
117
|
export declare const catalogProviders: () => readonly Provider[];
|
|
118
|
+
/** Resold on Vertex rather than made by Google. */
|
|
119
|
+
export declare const isPartner: (e: CatalogEntry) => boolean;
|
|
120
|
+
/** Provider, then Google's own models, then each partner's together. */
|
|
121
|
+
export declare function byGroup(a: CatalogEntry, b: CatalogEntry): number;
|
|
122
|
+
/** The heading a row is listed under, when its provider resells other makers' models. */
|
|
123
|
+
export declare function groupOf(e: CatalogEntry): string | undefined;
|
|
114
124
|
//# sourceMappingURL=catalog.d.ts.map
|
package/dist/catalog.js
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
// `source` for exactly that reason: a curated guess must never be mistaken for
|
|
17
17
|
// the vendor's own word.
|
|
18
18
|
// ---------------------------------------------------------------------------
|
|
19
|
-
import { ModelRegistry } from '@zenera/neo';
|
|
19
|
+
import { ModelRegistry, vertexEndpoint } from '@zenera/neo';
|
|
20
20
|
import { Cache, cacheKey } from "./cache.js";
|
|
21
21
|
import { PROVIDERS } from "./keys.js";
|
|
22
22
|
import { classify } from "./liveness.js";
|
|
@@ -24,7 +24,7 @@ import { classify } from "./liveness.js";
|
|
|
24
24
|
export const CATALOG_TTL_MS = 24 * 60 * 60 * 1000;
|
|
25
25
|
export const CATALOG_KIND = 'catalog';
|
|
26
26
|
/** Bumped when the stored shape changes, which makes every old entry a miss. */
|
|
27
|
-
const CATALOG_VERSION =
|
|
27
|
+
const CATALOG_VERSION = 2;
|
|
28
28
|
// ---------------------------------------------------------------------------
|
|
29
29
|
// The fallback table
|
|
30
30
|
// ---------------------------------------------------------------------------
|
|
@@ -53,9 +53,17 @@ export const CURATED = {
|
|
|
53
53
|
{ id: 'gemini-embedding-2', roles: ['embedding'] },
|
|
54
54
|
],
|
|
55
55
|
vertex: [
|
|
56
|
-
{
|
|
57
|
-
|
|
58
|
-
|
|
56
|
+
{
|
|
57
|
+
id: 'gemini-3.5-flash-lite',
|
|
58
|
+
roles: ['chat'],
|
|
59
|
+
contextLength: 1_048_576,
|
|
60
|
+
publisher: 'google',
|
|
61
|
+
},
|
|
62
|
+
{ id: 'gemini-3.8-flash', roles: ['chat'], contextLength: 1_048_576, publisher: 'google' },
|
|
63
|
+
{ id: 'gemini-embedding-2', roles: ['embedding'], publisher: 'google' },
|
|
64
|
+
// Open-weight models are served but not listed by Model Garden, so these are the only record.
|
|
65
|
+
{ id: 'qwen/qwen3-235b-a22b-instruct-2507-maas', roles: ['chat'], publisher: 'qwen' },
|
|
66
|
+
{ id: 'openai/gpt-oss-120b-maas', roles: ['chat'], publisher: 'openai' },
|
|
59
67
|
],
|
|
60
68
|
openrouter: [
|
|
61
69
|
{ id: 'openai/gpt-5.6-luna', roles: ['chat'], contextLength: 1_050_000 },
|
|
@@ -227,6 +235,7 @@ async function fromGenAI(client, provider) {
|
|
|
227
235
|
description: model.description,
|
|
228
236
|
contextLength: model.inputTokenLimit,
|
|
229
237
|
maxOutputTokens: model.outputTokenLimit,
|
|
238
|
+
publisher: provider === 'vertex' ? 'google' : undefined,
|
|
230
239
|
}));
|
|
231
240
|
}
|
|
232
241
|
return rows;
|
|
@@ -296,6 +305,38 @@ async function fromRouter(client, provider) {
|
|
|
296
305
|
}
|
|
297
306
|
return rows;
|
|
298
307
|
}
|
|
308
|
+
/**
|
|
309
|
+
* Partner models on Vertex. The GenAI listing holds Google's alone, so each
|
|
310
|
+
* partner's Model Garden listing is asked as well - only for publishers whose
|
|
311
|
+
* models the runtime can call: Claude at `rawPredict`, the rest at the
|
|
312
|
+
* OpenAI-compatible endpoint as `publisher/model`.
|
|
313
|
+
*/
|
|
314
|
+
const VERTEX_PARTNERS = ['anthropic', 'xai'];
|
|
315
|
+
export async function fromVertexPartners() {
|
|
316
|
+
const at = vertexEndpoint();
|
|
317
|
+
const token = await at.token();
|
|
318
|
+
const lists = await Promise.all(VERTEX_PARTNERS.map(async (publisher) => {
|
|
319
|
+
const res = await fetch(`https://aiplatform.googleapis.com/v1beta1/publishers/${publisher}/models?pageSize=200`, {
|
|
320
|
+
headers: {
|
|
321
|
+
Authorization: `Bearer ${token}`,
|
|
322
|
+
'x-goog-user-project': at.project,
|
|
323
|
+
},
|
|
324
|
+
});
|
|
325
|
+
if (!res.ok) {
|
|
326
|
+
return [];
|
|
327
|
+
}
|
|
328
|
+
const body = (await res.json());
|
|
329
|
+
return (body.publisherModels ?? [])
|
|
330
|
+
.map((m) => bareId(m.name ?? ''))
|
|
331
|
+
.filter((id) => id && !/embed|ocr/.test(id))
|
|
332
|
+
.map((id) => entry('vertex', {
|
|
333
|
+
id: publisher === 'anthropic' ? id : `${publisher}/${id}`,
|
|
334
|
+
roles: ['chat'],
|
|
335
|
+
publisher,
|
|
336
|
+
}));
|
|
337
|
+
}));
|
|
338
|
+
return lists.flat();
|
|
339
|
+
}
|
|
299
340
|
/**
|
|
300
341
|
* Asks one provider what it serves. Throws whatever the SDK throws.
|
|
301
342
|
*
|
|
@@ -303,16 +344,27 @@ async function fromRouter(client, provider) {
|
|
|
303
344
|
* to break when a vendor reshapes a payload, and they are untestable if the
|
|
304
345
|
* only way to reach them is a credential and a network. It mirrors
|
|
305
346
|
* `ProviderSpec.client`, which exists in the library for the same reason.
|
|
347
|
+
* `partners` is the same seam for Vertex's second listing.
|
|
306
348
|
*/
|
|
307
|
-
export async function fetchCatalog(provider, client = new ModelRegistry().client(provider)) {
|
|
349
|
+
export async function fetchCatalog(provider, client = new ModelRegistry().client(provider), partners = fromVertexPartners) {
|
|
308
350
|
switch (provider) {
|
|
309
351
|
case 'openai':
|
|
310
352
|
return enrich(provider, await fromOpenAI(client, provider));
|
|
311
353
|
case 'anthropic':
|
|
312
354
|
return enrich(provider, await fromAnthropic(client, provider));
|
|
313
355
|
case 'google':
|
|
314
|
-
case 'vertex':
|
|
315
356
|
return enrich(provider, await fromGenAI(client, provider));
|
|
357
|
+
case 'vertex': {
|
|
358
|
+
// A partner listing that fails must not cost the Gemini one.
|
|
359
|
+
const [google, others] = await Promise.all([
|
|
360
|
+
fromGenAI(client, provider),
|
|
361
|
+
partners().catch(() => []),
|
|
362
|
+
]);
|
|
363
|
+
const live = [...google, ...others];
|
|
364
|
+
const seen = new Set(live.map((r) => r.id));
|
|
365
|
+
const unlisted = curated(provider).filter((r) => r.publisher !== 'google' && !seen.has(r.id));
|
|
366
|
+
return enrich(provider, [...live, ...unlisted]);
|
|
367
|
+
}
|
|
316
368
|
case 'openrouter':
|
|
317
369
|
return enrich(provider, await fromRouter(client, provider));
|
|
318
370
|
}
|
|
@@ -459,4 +511,25 @@ export function matches(row, query, filters = {}) {
|
|
|
459
511
|
}
|
|
460
512
|
/** The providers a search covers when none was named. */
|
|
461
513
|
export const catalogProviders = () => PROVIDERS;
|
|
514
|
+
// ---------------------------------------------------------------------------
|
|
515
|
+
// Grouping
|
|
516
|
+
// ---------------------------------------------------------------------------
|
|
517
|
+
/** Resold on Vertex rather than made by Google. */
|
|
518
|
+
export const isPartner = (e) => e.publisher !== undefined && e.publisher !== 'google';
|
|
519
|
+
/** Provider, then Google's own models, then each partner's together. */
|
|
520
|
+
export function byGroup(a, b) {
|
|
521
|
+
return (a.provider.localeCompare(b.provider) ||
|
|
522
|
+
Number(isPartner(a)) - Number(isPartner(b)) ||
|
|
523
|
+
(a.publisher ?? '').localeCompare(b.publisher ?? '') ||
|
|
524
|
+
a.id.localeCompare(b.id));
|
|
525
|
+
}
|
|
526
|
+
/** The heading a row is listed under, when its provider resells other makers' models. */
|
|
527
|
+
export function groupOf(e) {
|
|
528
|
+
if (!e.publisher) {
|
|
529
|
+
return undefined;
|
|
530
|
+
}
|
|
531
|
+
return isPartner(e)
|
|
532
|
+
? `${e.provider} · partner · ${e.publisher}`
|
|
533
|
+
: `${e.provider} · Google (Gemini, Gemma, Imagen, Veo)`;
|
|
534
|
+
}
|
|
462
535
|
//# sourceMappingURL=catalog.js.map
|
package/dist/command.d.ts
CHANGED
|
@@ -12,6 +12,12 @@ export interface Command {
|
|
|
12
12
|
readonly details?: readonly string[];
|
|
13
13
|
/** printed instead of the `zen` banner, so the sub-brand shows through */
|
|
14
14
|
readonly banner?: BannerText;
|
|
15
|
+
/**
|
|
16
|
+
* True when this invocation's stdout is the answer rather than a report on
|
|
17
|
+
* it. The banner is then held back rather than dropped: if the run turns
|
|
18
|
+
* out to need a prompt, someone is there to see it, and it appears.
|
|
19
|
+
*/
|
|
20
|
+
readonly quiet?: (args: readonly string[]) => boolean;
|
|
15
21
|
/**
|
|
16
22
|
* Answers `--help` itself, for a command whose arguments select a page the
|
|
17
23
|
* frame cannot see. Without it the frame prints this command's own page.
|
|
@@ -1,3 +1,60 @@
|
|
|
1
1
|
import type { Command } from '../command.ts';
|
|
2
|
+
import { type RunPaths, type SessionPaths } from '../session.ts';
|
|
3
|
+
import { type NodeDetail, type Trace, type TraceEntry } from '../trace.ts';
|
|
4
|
+
interface Flags {
|
|
5
|
+
project?: string;
|
|
6
|
+
session?: string;
|
|
7
|
+
run?: string;
|
|
8
|
+
dir?: string;
|
|
9
|
+
memory?: string;
|
|
10
|
+
model?: string;
|
|
11
|
+
part?: string[];
|
|
12
|
+
'question-file'?: string;
|
|
13
|
+
full?: boolean;
|
|
14
|
+
open?: boolean;
|
|
15
|
+
rebuild?: boolean;
|
|
16
|
+
'no-timing'?: boolean;
|
|
17
|
+
'no-style'?: boolean;
|
|
18
|
+
}
|
|
2
19
|
export declare const inspect: Command;
|
|
20
|
+
/**
|
|
21
|
+
* Which parts print in full: everything `--part` names, or everything but
|
|
22
|
+
* `request`. A prefix matches, so `--part call` catches `call run_command (…)`.
|
|
23
|
+
*/
|
|
24
|
+
export declare function wholeParts(found: readonly NodeDetail[], values: {
|
|
25
|
+
part?: string[];
|
|
26
|
+
full?: boolean;
|
|
27
|
+
}): (name: string) => boolean;
|
|
28
|
+
/**
|
|
29
|
+
* One recorded call, put back to a model with a question on the end.
|
|
30
|
+
*
|
|
31
|
+
* `node` shows what the model was given; this asks the model what it made of
|
|
32
|
+
* it. Everything it sees is what it saw at the time, so an answer naming the
|
|
33
|
+
* skill that steered it is checkable against the same node.
|
|
34
|
+
*/
|
|
35
|
+
export declare function replayableCalls(trace: Trace): {
|
|
36
|
+
entry: TraceEntry;
|
|
37
|
+
node: import("@zenera/neo").LlmCallNode;
|
|
38
|
+
}[];
|
|
39
|
+
export declare function renderAskExchange(question: string, answer: string, columns?: number): string[];
|
|
40
|
+
export declare function repeatQuestions(initial: string, next: () => Promise<string>, answer: (question: string) => Promise<void>): Promise<void>;
|
|
41
|
+
/** The question: the words on the line, or the whole of `--question-file` (`-` is stdin). */
|
|
42
|
+
export declare function readQuestion(words: readonly string[], file: string | undefined, cwd: string): Promise<string>;
|
|
43
|
+
/** One node, framed so a payload cannot be mistaken for the next node. */
|
|
44
|
+
export declare function renderNode(node: NodeDetail, whole?: (name: string) => boolean): string;
|
|
45
|
+
export interface Located {
|
|
46
|
+
project: string;
|
|
47
|
+
session: SessionPaths;
|
|
48
|
+
run: RunPaths;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Which run, by whichever handle the caller has.
|
|
52
|
+
*
|
|
53
|
+
* A directory is the handle a *program* holds: `zen run --json` reports one,
|
|
54
|
+
* and asking it to be taken apart into a project, a session and a run before
|
|
55
|
+
* it can be used again would be work for nothing. A person holds an id, or
|
|
56
|
+
* nothing at all and gets asked.
|
|
57
|
+
*/
|
|
58
|
+
export declare function locate(cwd: string, values: Pick<Flags, 'project' | 'session' | 'run' | 'dir'>, positional: string | undefined, asking: boolean): Promise<Located>;
|
|
59
|
+
export {};
|
|
3
60
|
//# sourceMappingURL=inspect.d.ts.map
|