@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/commands/meta.js
CHANGED
|
@@ -1,16 +1,60 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { readProjectConfig } from '@zenera/neo';
|
|
2
|
+
import { spawn } from 'node:child_process';
|
|
3
|
+
import { randomUUID } from 'node:crypto';
|
|
4
|
+
import { existsSync } from 'node:fs';
|
|
5
|
+
import { join, resolve } from 'node:path';
|
|
6
|
+
import { setTimeout as sleep } from 'node:timers/promises';
|
|
2
7
|
import { parse } from "../args.js";
|
|
3
8
|
import { loadProjectEnv } from "../env.js";
|
|
9
|
+
import { copilotEventLog } from "../eventlog.js";
|
|
4
10
|
import { ensureHome } from "../home.js";
|
|
5
11
|
import { assertOwner, KeyStore, PROVIDERS } from "../keys.js";
|
|
6
|
-
import { probeModel } from "../liveness.js";
|
|
7
|
-
import { answerBox, chooseModel, defaultRef, launch, listPrompts, loadPrompt, locate, masked, misspelledProvider, MODEL_ENV, openLog, ORDER, PROMPT_DIR, providersWarning, readMeta, RECOMMENDED, SOURCE_LABELS,
|
|
12
|
+
import { probeChat, probeModel } from "../liveness.js";
|
|
13
|
+
import { answerBox, chooseModel, DATASET_DETAILS, DATASET_SUMMARY, DATASET_USAGE, datasetProjectFlag, DEFAULT_RESUMES, defaultRef, editorFiles, FINETUNE_DETAILS, FINETUNE_SUMMARY, FINETUNE_USAGE, finetuneProjectFlag, lastSession, launch, listPrompts, loadPrompt, locate, masked, misspelledProvider, MODEL_ENV, openLog, ORDER, PROMPT_DIR, providersWarning, readMeta, RECOMMENDED, recordSession, RESUME_PROMPT, resumeDelayMs, runDataset, runFinetune, sameModel, sessionModel, sessionTotals, SOURCE_LABELS, startVertexRelay, tailSpans, Tally, terminalSink, transient, wire, writeMeta, } from "../meta/index.js";
|
|
14
|
+
import { splitRef } from "../modelref.js";
|
|
15
|
+
import { duration } from "../narrate.js";
|
|
8
16
|
import * as Projects from "../projects.js";
|
|
9
17
|
import { project as resolveProject } from "../resolve.js";
|
|
10
|
-
import { editorFiles } from "../scaffold.js";
|
|
11
18
|
import { bold, choose, CliError, credentialError, cyan, dim, EXIT, green, isInteractive, json, note, progress, readStdin, table, usageError, warn, write, writeAll, } from "../term.js";
|
|
19
|
+
import { appendUsage, LEDGER_ENV, ledgerPath, META_PROMPT_ENV, META_SESSION_ENV, } from "../usage.js";
|
|
12
20
|
const USAGE = 'zen meta run [project] [prompt] [options]';
|
|
13
|
-
const VERBS = new Set(['model', 'run', 'prompts']);
|
|
21
|
+
const VERBS = new Set(['model', 'run', 'prompts', 'resume', 'dataset', 'finetune']);
|
|
22
|
+
const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
23
|
+
/** A copilot session id, or the 7+ hex prefix of one it also accepts. */
|
|
24
|
+
const SESSION_ID = /^[0-9a-f]{7,}(?:-[0-9a-f]+)*$/i;
|
|
25
|
+
/** Verbs that own their flags and their help page. */
|
|
26
|
+
const SUBCOMMANDS = {
|
|
27
|
+
dataset: {
|
|
28
|
+
run: runDataset,
|
|
29
|
+
projectFlag: datasetProjectFlag,
|
|
30
|
+
page: { usage: DATASET_USAGE, summary: DATASET_SUMMARY, details: DATASET_DETAILS },
|
|
31
|
+
},
|
|
32
|
+
finetune: {
|
|
33
|
+
run: runFinetune,
|
|
34
|
+
projectFlag: finetuneProjectFlag,
|
|
35
|
+
page: { usage: FINETUNE_USAGE, summary: FINETUNE_SUMMARY, details: FINETUNE_DETAILS },
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
/** `zen meta dataset ...` or `zen meta acme dataset ...`: which one, and where its own args start. */
|
|
39
|
+
function subcommand(args) {
|
|
40
|
+
for (const name of Object.keys(SUBCOMMANDS)) {
|
|
41
|
+
if (args[0] === name) {
|
|
42
|
+
return { name, at: 0 };
|
|
43
|
+
}
|
|
44
|
+
if (args[1] === name && !args[0]?.startsWith('-')) {
|
|
45
|
+
return { name, at: 1 };
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
50
|
+
function page(p) {
|
|
51
|
+
write(bold(p.usage));
|
|
52
|
+
write(`\n ${p.summary}`);
|
|
53
|
+
write('');
|
|
54
|
+
for (const line of p.details) {
|
|
55
|
+
write(line ? ` ${line}` : '');
|
|
56
|
+
}
|
|
57
|
+
}
|
|
14
58
|
export const meta = {
|
|
15
59
|
summary: 'Run the meta agent over this project, on your own keys.',
|
|
16
60
|
usage: USAGE,
|
|
@@ -21,9 +65,13 @@ export const meta = {
|
|
|
21
65
|
' zen meta run [project] /<name> [words] run a stored prompt',
|
|
22
66
|
' zen meta run [project] pick one of its stored prompts',
|
|
23
67
|
' zen meta prompts [project] list the stored prompts',
|
|
68
|
+
' zen meta resume [project] [session] carry on a run that stopped',
|
|
24
69
|
' zen meta model [ref] show or set the model it uses',
|
|
70
|
+
' zen meta dataset <verb> the cases it tunes against',
|
|
71
|
+
' zen meta finetune <verb> tune the project on those cases',
|
|
25
72
|
'',
|
|
26
73
|
'The project may come before the verb instead: `zen meta acme run`.',
|
|
74
|
+
'`zen meta dataset --help` and `zen meta finetune --help` have their own pages.',
|
|
27
75
|
'',
|
|
28
76
|
'Arguments:',
|
|
29
77
|
' [project] Name of a project. Default: the one you are in.',
|
|
@@ -41,8 +89,14 @@ export const meta = {
|
|
|
41
89
|
' --ask Have it ask before each tool. Default: it does not.',
|
|
42
90
|
' --add-dir <dir> Another directory it may touch. Repeatable.',
|
|
43
91
|
' --resume <id> Continue a copilot session. --continue takes the last.',
|
|
92
|
+
' --session-id <uuid> Start a new session under this id. Default: a random one.',
|
|
93
|
+
` --retries <n> Resume after a rate limit or outage. Default ${DEFAULT_RESUMES}, 0 = off.`,
|
|
44
94
|
' --share <file> Write the transcript to a markdown file.',
|
|
95
|
+
' --events <file> Append what the agent does to this file as it does it:',
|
|
96
|
+
' one JSON line per model call, tool call and message.',
|
|
45
97
|
' --dry-run Print what would run, secrets masked, and stop.',
|
|
98
|
+
' --no-refresh Leave the editor files as they are (parallel runs).',
|
|
99
|
+
' --json Print one object: answer, answerFile, sessionId, exitCode, tokens.',
|
|
46
100
|
'',
|
|
47
101
|
'It always uses your own keys — `zen key add` — and never a coding-agent',
|
|
48
102
|
'subscription. The answer goes to stdout and the progress to stderr, so',
|
|
@@ -53,6 +107,13 @@ export const meta = {
|
|
|
53
107
|
'watching to answer. Narrow it with --allow-tool, or restore the asking',
|
|
54
108
|
'with --ask.',
|
|
55
109
|
'',
|
|
110
|
+
'`zen meta resume` picks up the last session the project ran - or the one',
|
|
111
|
+
'named - where it stopped. A run that ends any way but success prints it.',
|
|
112
|
+
'',
|
|
113
|
+
'Its tokens are counted as it runs and summed when it ends. In a project with',
|
|
114
|
+
'a finetune/, every call - and every zen run and inspect ask it makes - is',
|
|
115
|
+
'appended to finetune/usage/ledger.jsonl, and finetune/USAGE.md is rewritten.',
|
|
116
|
+
'',
|
|
56
117
|
'Nothing it needs is put on a command line: every credential reaches it',
|
|
57
118
|
'through the environment, where other processes cannot read it.',
|
|
58
119
|
'',
|
|
@@ -75,12 +136,29 @@ export const meta = {
|
|
|
75
136
|
' zen meta prompts',
|
|
76
137
|
' zen meta run /project-review',
|
|
77
138
|
' zen meta run acme /spec-sync-project agents/triage.md',
|
|
139
|
+
' zen meta run acme /analyze --json',
|
|
140
|
+
' zen meta resume acme',
|
|
78
141
|
' git diff | zen meta run "what broke?" --allow-tool read',
|
|
79
142
|
' zen meta model vertex/gemini-3.8-flash',
|
|
80
143
|
' zen meta model --pick',
|
|
81
144
|
' zen meta run --dry-run "hello"',
|
|
82
145
|
],
|
|
146
|
+
help: (ctx) => {
|
|
147
|
+
const sub = subcommand(ctx.args);
|
|
148
|
+
page(sub ? SUBCOMMANDS[sub.name].page : { ...meta, details: meta.details ?? [] });
|
|
149
|
+
},
|
|
83
150
|
run: async (ctx) => {
|
|
151
|
+
// These own their flags, so they are routed before the meta agent's are parsed.
|
|
152
|
+
const sub = subcommand(ctx.args);
|
|
153
|
+
if (sub) {
|
|
154
|
+
const { run, projectFlag } = SUBCOMMANDS[sub.name];
|
|
155
|
+
const args = ctx.args.slice(sub.at + 1);
|
|
156
|
+
const found = await resolveProject({
|
|
157
|
+
cwd: ctx.cwd,
|
|
158
|
+
project: projectFlag(args) ?? (sub.at === 1 ? ctx.args[0] : undefined),
|
|
159
|
+
});
|
|
160
|
+
return await run({ args, json: ctx.json, cwd: ctx.cwd }, found);
|
|
161
|
+
}
|
|
84
162
|
const { values, positionals } = parse(ctx.args, {
|
|
85
163
|
project: { type: 'string' },
|
|
86
164
|
provider: { type: 'string' },
|
|
@@ -89,8 +167,11 @@ export const meta = {
|
|
|
89
167
|
agent: { type: 'string' },
|
|
90
168
|
effort: { type: 'string' },
|
|
91
169
|
share: { type: 'string' },
|
|
170
|
+
events: { type: 'string' },
|
|
92
171
|
resume: { type: 'string' },
|
|
93
172
|
continue: { type: 'boolean' },
|
|
173
|
+
'session-id': { type: 'string' },
|
|
174
|
+
retries: { type: 'string' },
|
|
94
175
|
'allow-all': { type: 'boolean' },
|
|
95
176
|
'allow-tool': { type: 'string' },
|
|
96
177
|
ask: { type: 'boolean' },
|
|
@@ -100,6 +181,7 @@ export const meta = {
|
|
|
100
181
|
pick: { type: 'boolean' },
|
|
101
182
|
clear: { type: 'boolean' },
|
|
102
183
|
force: { type: 'boolean' },
|
|
184
|
+
'no-refresh': { type: 'boolean' },
|
|
103
185
|
}, USAGE);
|
|
104
186
|
// `zen meta acme run` is typed at least as often as `zen meta run acme`,
|
|
105
187
|
// and read literally it asks the agent the one-word question "run" —
|
|
@@ -126,6 +208,9 @@ export const meta = {
|
|
|
126
208
|
if (verb === 'prompts') {
|
|
127
209
|
return await prompts(ctx, values, args);
|
|
128
210
|
}
|
|
211
|
+
if (verb === 'resume') {
|
|
212
|
+
return await resumeRun(ctx, values, args);
|
|
213
|
+
}
|
|
129
214
|
throw usageError(first === undefined ? 'nothing to run' : 'a prompt goes through `run`', `try: zen meta run ${positionals.map(requote).join(' ') || '"what does this project do?"'}`);
|
|
130
215
|
},
|
|
131
216
|
};
|
|
@@ -191,7 +276,9 @@ async function stored(ctx, values, args) {
|
|
|
191
276
|
rest = args;
|
|
192
277
|
}
|
|
193
278
|
const { project } = await where(ctx, values.project, head);
|
|
194
|
-
refresh
|
|
279
|
+
if (!values['no-refresh']) {
|
|
280
|
+
refresh(project);
|
|
281
|
+
}
|
|
195
282
|
if (name) {
|
|
196
283
|
return await runPrompt(ctx, values, project, name, rest);
|
|
197
284
|
}
|
|
@@ -208,7 +295,7 @@ async function runPrompt(ctx, values, project, name, extra) {
|
|
|
208
295
|
note(dim(found.description));
|
|
209
296
|
}
|
|
210
297
|
const prompt = extra.length > 0 ? `${found.body}\n\n${extra.join(' ')}` : found.body;
|
|
211
|
-
await go(ctx, values, project, prompt);
|
|
298
|
+
await go(ctx, values, project, prompt, name.replace(/^\//, ''));
|
|
212
299
|
}
|
|
213
300
|
async function pickPrompt(ctx, project) {
|
|
214
301
|
const names = await listPrompts(project.dir);
|
|
@@ -243,9 +330,40 @@ async function prompts(ctx, values, args) {
|
|
|
243
330
|
note(dim(`run one: zen meta run /${found[0].name}`));
|
|
244
331
|
}
|
|
245
332
|
// ---------------------------------------------------------------------------
|
|
333
|
+
// zen meta resume [project] [session id] [words...]
|
|
334
|
+
//
|
|
335
|
+
// A long run stops for reasons nobody chose - a refused request, a closed
|
|
336
|
+
// laptop, Ctrl-C. Copilot keeps the session; this hands it back, by default the
|
|
337
|
+
// last one the project ran, with the words (or a plain "continue") as the turn.
|
|
338
|
+
// ---------------------------------------------------------------------------
|
|
339
|
+
async function resumeRun(ctx, values, args) {
|
|
340
|
+
const { project, rest } = await where(ctx, values.project, args);
|
|
341
|
+
const named = rest[0] !== undefined && SESSION_ID.test(rest[0]) ? rest[0] : undefined;
|
|
342
|
+
const words = named ? rest.slice(1) : rest;
|
|
343
|
+
const session = named ?? values.resume ?? lastSession(project.dir);
|
|
344
|
+
if (!session) {
|
|
345
|
+
throw usageError(`no meta session recorded in ${project.name}`, `name one: zen meta resume ${project.name} <session id>`);
|
|
346
|
+
}
|
|
347
|
+
note(dim(`resuming session ${session}`));
|
|
348
|
+
if (!values['no-refresh']) {
|
|
349
|
+
refresh(project);
|
|
350
|
+
}
|
|
351
|
+
const message = (values.prompt ?? words.join(' ')).trim() || RESUME_PROMPT;
|
|
352
|
+
await go(ctx, { ...values, resume: session, continue: false }, project, message);
|
|
353
|
+
}
|
|
354
|
+
// ---------------------------------------------------------------------------
|
|
246
355
|
// The run itself
|
|
247
356
|
// ---------------------------------------------------------------------------
|
|
248
|
-
async function go(ctx, values, project, prompt) {
|
|
357
|
+
async function go(ctx, values, project, prompt, promptName) {
|
|
358
|
+
const fresh = values['session-id'];
|
|
359
|
+
if (fresh !== undefined) {
|
|
360
|
+
if (values.resume || values.continue) {
|
|
361
|
+
throw usageError('--session-id starts a new session', 'drop --resume / --continue');
|
|
362
|
+
}
|
|
363
|
+
if (!UUID.test(fresh)) {
|
|
364
|
+
throw usageError(`--session-id must be a UUID, not ${fresh}`, 'try: uuidgen');
|
|
365
|
+
}
|
|
366
|
+
}
|
|
249
367
|
const shell = process.env[MODEL_ENV];
|
|
250
368
|
loadProjectEnv(project.dir);
|
|
251
369
|
ensureHome();
|
|
@@ -280,7 +398,19 @@ async function go(ctx, values, project, prompt) {
|
|
|
280
398
|
}
|
|
281
399
|
const wiring = wire(store, entry, id);
|
|
282
400
|
const binary = locate();
|
|
283
|
-
|
|
401
|
+
// Copilot cannot carry a session to another model: the resume fails on the first call.
|
|
402
|
+
const ran = values.resume ? sessionModel(values.resume) : undefined;
|
|
403
|
+
if (values.resume && ran && !sameModel(ran, wiring.model)) {
|
|
404
|
+
if (prompt === RESUME_PROMPT) {
|
|
405
|
+
throw usageError(`session ${values.resume} ran on ${ran}, and the meta model is now ${wiring.model} - copilot cannot carry a session to another model`, `switch back for it: zen meta model ${owner}/${ran.replace(/^.*\//, '')} - or ask again, which starts a new session`);
|
|
406
|
+
}
|
|
407
|
+
warn(`session ${values.resume} ran on ${ran}; the meta model is now ${wiring.model}, so this starts a new session`);
|
|
408
|
+
values = { ...values, resume: undefined };
|
|
409
|
+
}
|
|
410
|
+
// Named up front, so a run killed before copilot reports its id can still be resumed.
|
|
411
|
+
let session = values.resume ?? (values.continue ? undefined : (fresh ?? randomUUID()));
|
|
412
|
+
const args = argv(values, project.dir, prompt, wiring.secret, session);
|
|
413
|
+
const retries = count(values.retries, '--retries') ?? DEFAULT_RESUMES;
|
|
284
414
|
if (values['dry-run']) {
|
|
285
415
|
const lines = masked(wiring.env, wiring.secret);
|
|
286
416
|
if (ctx.json) {
|
|
@@ -310,33 +440,112 @@ async function go(ctx, values, project, prompt) {
|
|
|
310
440
|
note(dim('set another: zen meta model <provider>/<id>'));
|
|
311
441
|
}
|
|
312
442
|
note(cyan(`${owner} · ${wiring.model} · ${project.name}`));
|
|
313
|
-
const log = openLog(project.dir);
|
|
443
|
+
const log = openLog(project.dir, new Date(), session);
|
|
314
444
|
note(`log ${log.path}`);
|
|
315
445
|
const elided = args.map((a, i) => (args[i - 1] === '-p' ? '<prompt>' : a));
|
|
316
446
|
log.line(`zen meta ${new Date().toISOString()}`);
|
|
317
447
|
log.line(`project ${project.name} ${project.dir}`);
|
|
318
448
|
log.line(`model ${chosen.ref} from ${SOURCE_LABELS[chosen.from]}`);
|
|
319
449
|
log.line(`command ${[binary.command, ...binary.args, ...elided].join(' ')}`);
|
|
450
|
+
if (session) {
|
|
451
|
+
recordSession(project.dir, session);
|
|
452
|
+
log.line(`session ${session}`);
|
|
453
|
+
}
|
|
454
|
+
const relay = wiring.relay ? await startVertexRelay(wiring.relay) : undefined;
|
|
455
|
+
if (relay) {
|
|
456
|
+
wiring.env.COPILOT_PROVIDER_BASE_URL = relay.url;
|
|
457
|
+
log.line(`relay ${relay.url} -> vertex rawPredict`);
|
|
458
|
+
}
|
|
320
459
|
log.line('');
|
|
321
460
|
log.line('--- prompt ---');
|
|
322
461
|
log.line(prompt);
|
|
323
462
|
log.line('');
|
|
324
463
|
log.line('--- run ---');
|
|
464
|
+
const resumeHint = () => session ? `resume it: zen meta resume ${project.name} ${session}` : undefined;
|
|
465
|
+
// Every zen command the agent runs inherits these, so a batch it starts or
|
|
466
|
+
// an ask it makes lands in the same ledger, tagged with this session.
|
|
467
|
+
const spans = log.path.replace(/\.log$/, '.otel.jsonl');
|
|
468
|
+
const ledger = ledgerPath(project.dir);
|
|
469
|
+
const env = {
|
|
470
|
+
...wiring.env,
|
|
471
|
+
COPILOT_OTEL_FILE_EXPORTER_PATH: spans,
|
|
472
|
+
...(ledger ? { [LEDGER_ENV]: ledger } : {}),
|
|
473
|
+
...(session ? { [META_SESSION_ENV]: session } : {}),
|
|
474
|
+
...(promptName ? { [META_PROMPT_ENV]: promptName } : {}),
|
|
475
|
+
};
|
|
476
|
+
const tally = new Tally();
|
|
477
|
+
const record = values.events ? copilotEventLog(resolve(ctx.cwd, values.events)) : undefined;
|
|
478
|
+
const tail = tailSpans(spans, (call) => {
|
|
479
|
+
tally.add(call);
|
|
480
|
+
record?.call(call);
|
|
481
|
+
appendUsage(project.dir, {
|
|
482
|
+
kind: 'meta.call',
|
|
483
|
+
ts: new Date(Date.parse(call.startedAt) + call.durationMs).toISOString(),
|
|
484
|
+
...call,
|
|
485
|
+
session: call.session ?? session,
|
|
486
|
+
});
|
|
487
|
+
});
|
|
488
|
+
const reporter = usageReporter(project.dir);
|
|
489
|
+
const tokens = () => {
|
|
490
|
+
tail.stop();
|
|
491
|
+
return tally.toString();
|
|
492
|
+
};
|
|
325
493
|
// A run that dies still has to leave a readable file behind.
|
|
326
494
|
try {
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
495
|
+
let resumes = 0;
|
|
496
|
+
const run = async (argList) => {
|
|
497
|
+
const launchedAt = new Date();
|
|
498
|
+
const outcome = await launch({
|
|
499
|
+
binary,
|
|
500
|
+
args: argList,
|
|
501
|
+
env,
|
|
502
|
+
cwd: project.dir,
|
|
503
|
+
log,
|
|
504
|
+
sink: ctx.json ? terminalSink : undefined,
|
|
505
|
+
onEvent: record?.event,
|
|
506
|
+
status: () => (tally.calls > 0 ? tally.toString() : ''),
|
|
507
|
+
});
|
|
508
|
+
if (outcome.sessionId && outcome.sessionId !== session) {
|
|
509
|
+
session = outcome.sessionId;
|
|
510
|
+
recordSession(project.dir, session);
|
|
511
|
+
}
|
|
512
|
+
const models = session ? sessionTotals(session) : undefined;
|
|
513
|
+
if (session && models) {
|
|
514
|
+
appendUsage(project.dir, {
|
|
515
|
+
kind: 'meta.session',
|
|
516
|
+
ts: new Date().toISOString(),
|
|
517
|
+
session,
|
|
518
|
+
models,
|
|
519
|
+
exitCode: outcome.exitCode,
|
|
520
|
+
resumes,
|
|
521
|
+
startedAt: launchedAt.toISOString(),
|
|
522
|
+
durationMs: Date.now() - launchedAt.getTime(),
|
|
523
|
+
});
|
|
524
|
+
}
|
|
525
|
+
return outcome;
|
|
526
|
+
};
|
|
527
|
+
let outcome = await run(args);
|
|
528
|
+
for (let reason = transient(outcome); reason && resumes < retries;) {
|
|
529
|
+
const wait = resumeDelayMs(resumes);
|
|
530
|
+
resumes += 1;
|
|
531
|
+
const said = `${reason}: resuming in ${duration(wait)} (${resumes}/${retries})`;
|
|
532
|
+
warn(said);
|
|
533
|
+
log.line('');
|
|
534
|
+
log.line(`--- resume ${resumes}: ${said} ---`);
|
|
535
|
+
await pause(wait, resumeHint());
|
|
536
|
+
const again = { ...values, resume: session, continue: false };
|
|
537
|
+
outcome = await run(argv(again, project.dir, RESUME_PROMPT, wiring.secret));
|
|
538
|
+
reason = transient(outcome);
|
|
539
|
+
}
|
|
334
540
|
log.line('');
|
|
335
541
|
log.line('--- answer ---');
|
|
336
542
|
log.line(outcome.answer);
|
|
337
543
|
if (outcome.answer) {
|
|
338
544
|
log.saveAnswer(outcome.answer);
|
|
339
545
|
}
|
|
546
|
+
const spent = tokens();
|
|
547
|
+
log.line('');
|
|
548
|
+
log.line(`tokens ${spent}`);
|
|
340
549
|
if (ctx.json) {
|
|
341
550
|
json({
|
|
342
551
|
project: project.dir,
|
|
@@ -344,7 +553,9 @@ async function go(ctx, values, project, prompt) {
|
|
|
344
553
|
model: chosen.ref,
|
|
345
554
|
sessionId: outcome.sessionId,
|
|
346
555
|
exitCode: outcome.exitCode,
|
|
556
|
+
resumes,
|
|
347
557
|
usage: outcome.usage,
|
|
558
|
+
tokens: { calls: tally.calls, ...tally.usage },
|
|
348
559
|
answer: outcome.answer,
|
|
349
560
|
answerFile: outcome.answer ? log.answerPath : undefined,
|
|
350
561
|
});
|
|
@@ -355,16 +566,82 @@ async function go(ctx, values, project, prompt) {
|
|
|
355
566
|
// have read enough to want it in an editor.
|
|
356
567
|
note(cyan(log.answerPath));
|
|
357
568
|
}
|
|
569
|
+
if (!ctx.json && tally.calls > 0) {
|
|
570
|
+
note(dim(`tokens ${spent}`));
|
|
571
|
+
}
|
|
358
572
|
if (outcome.exitCode !== 0) {
|
|
359
|
-
throw new CliError(`the meta agent exited ${outcome.exitCode}`, EXIT.failed,
|
|
573
|
+
throw new CliError(`the meta agent exited ${outcome.exitCode}`, EXIT.failed, resumeHint() ??
|
|
574
|
+
(binary.from === 'npx' ? 'install it: npm i -g @github/copilot' : undefined));
|
|
360
575
|
}
|
|
361
576
|
}
|
|
577
|
+
catch (err) {
|
|
578
|
+
const hint = resumeHint();
|
|
579
|
+
if (hint && !(err instanceof CliError)) {
|
|
580
|
+
note(dim(hint));
|
|
581
|
+
}
|
|
582
|
+
throw err;
|
|
583
|
+
}
|
|
362
584
|
finally {
|
|
585
|
+
tail.stop();
|
|
586
|
+
reporter.stop();
|
|
587
|
+
await relay?.close();
|
|
363
588
|
log.close();
|
|
364
589
|
}
|
|
365
590
|
}
|
|
591
|
+
/** How often a running meta agent rewrites the fine-tuning's usage report. */
|
|
592
|
+
const REPORT_EVERY_MS = 5 * 60_000;
|
|
593
|
+
/**
|
|
594
|
+
* Rewrites `finetune/USAGE.md` on a timer and once more at the end, when the
|
|
595
|
+
* project is being tuned. Detached and silent: the report is a by-product, and
|
|
596
|
+
* neither its time nor its failure belongs to the run.
|
|
597
|
+
*/
|
|
598
|
+
function usageReporter(dir) {
|
|
599
|
+
const script = join(dir, '.github/skills/zen-finetune/scripts/usage.mjs');
|
|
600
|
+
if (!existsSync(join(dir, 'finetune')) || !existsSync(script)) {
|
|
601
|
+
return { stop: () => undefined };
|
|
602
|
+
}
|
|
603
|
+
let running = false;
|
|
604
|
+
const refresh = () => {
|
|
605
|
+
if (running) {
|
|
606
|
+
return;
|
|
607
|
+
}
|
|
608
|
+
running = true;
|
|
609
|
+
const child = spawn(process.execPath, [script, '-q'], {
|
|
610
|
+
cwd: dir,
|
|
611
|
+
stdio: 'ignore',
|
|
612
|
+
detached: true,
|
|
613
|
+
});
|
|
614
|
+
child.on('exit', () => (running = false));
|
|
615
|
+
child.on('error', () => (running = false));
|
|
616
|
+
child.unref();
|
|
617
|
+
};
|
|
618
|
+
const timer = setInterval(refresh, REPORT_EVERY_MS);
|
|
619
|
+
timer.unref();
|
|
620
|
+
return {
|
|
621
|
+
stop: () => {
|
|
622
|
+
clearInterval(timer);
|
|
623
|
+
running = false;
|
|
624
|
+
refresh();
|
|
625
|
+
},
|
|
626
|
+
};
|
|
627
|
+
}
|
|
628
|
+
/** The wait before a resume, cut short by Ctrl-C - which still says how to resume. */
|
|
629
|
+
async function pause(ms, hint) {
|
|
630
|
+
const stop = new AbortController();
|
|
631
|
+
const onInt = () => stop.abort();
|
|
632
|
+
process.once('SIGINT', onInt);
|
|
633
|
+
try {
|
|
634
|
+
await sleep(ms, undefined, { signal: stop.signal });
|
|
635
|
+
}
|
|
636
|
+
catch {
|
|
637
|
+
throw new CliError('interrupted', EXIT.failed, hint);
|
|
638
|
+
}
|
|
639
|
+
finally {
|
|
640
|
+
process.off('SIGINT', onInt);
|
|
641
|
+
}
|
|
642
|
+
}
|
|
366
643
|
/** Copilot's own flags, assembled once. Nothing secret goes on this line. */
|
|
367
|
-
function argv(values, dir, prompt, secret) {
|
|
644
|
+
function argv(values, dir, prompt, secret, fresh) {
|
|
368
645
|
const args = ['-C', dir, '-p', prompt, '--output-format', 'json', '--no-color'];
|
|
369
646
|
// A prompt written for an editor expects to read, write and run things, and
|
|
370
647
|
// there is nobody at a `-p` run to answer the question. Naming tools is the
|
|
@@ -390,9 +667,12 @@ function argv(values, dir, prompt, secret) {
|
|
|
390
667
|
if (values.resume) {
|
|
391
668
|
args.push('--resume', values.resume);
|
|
392
669
|
}
|
|
393
|
-
if (values.continue) {
|
|
670
|
+
else if (values.continue) {
|
|
394
671
|
args.push('--continue');
|
|
395
672
|
}
|
|
673
|
+
else if (fresh) {
|
|
674
|
+
args.push('--session-id', fresh);
|
|
675
|
+
}
|
|
396
676
|
if (secret.length > 0) {
|
|
397
677
|
args.push('--secret-env-vars', secret.join(','));
|
|
398
678
|
}
|
|
@@ -417,6 +697,16 @@ function projectModel(dir) {
|
|
|
417
697
|
return undefined;
|
|
418
698
|
}
|
|
419
699
|
}
|
|
700
|
+
function count(text, flag) {
|
|
701
|
+
if (text === undefined) {
|
|
702
|
+
return undefined;
|
|
703
|
+
}
|
|
704
|
+
const value = Number(text);
|
|
705
|
+
if (!Number.isInteger(value) || value < 0) {
|
|
706
|
+
throw usageError(`${flag} takes a whole number, 0 or more`, `got "${text}"`);
|
|
707
|
+
}
|
|
708
|
+
return value;
|
|
709
|
+
}
|
|
420
710
|
function asProvider(name) {
|
|
421
711
|
const owner = assertOwner(name);
|
|
422
712
|
if (owner === 'exa') {
|
|
@@ -534,7 +824,7 @@ async function vet(ctx, ref) {
|
|
|
534
824
|
// The colon form, so a refusal suggests `zen models ls <provider>`.
|
|
535
825
|
ref: `${owner}:${id}`,
|
|
536
826
|
kind: 'model',
|
|
537
|
-
model:
|
|
827
|
+
model: probeChat(owner, id),
|
|
538
828
|
});
|
|
539
829
|
bar?.done();
|
|
540
830
|
if (probe.check.state !== 'live') {
|