done-done-axi 0.2.0 → 0.3.0
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 +18 -22
- package/dist/agents.js +49 -8
- package/dist/cli.js +1106 -281
- package/dist/config.js +93 -21
- package/dist/design.js +8 -4
- package/dist/dom.js +83 -0
- package/dist/instance.js +106 -0
- package/dist/judge.js +60 -15
- package/dist/maintain.js +103 -81
- package/dist/matrix.js +79 -0
- package/dist/secrets.js +45 -0
- package/docs/media/config-dark.svg +1 -1
- package/docs/media/config-light.svg +1 -1
- package/docs/media/hero-dark.svg +137 -135
- package/docs/media/hero-light.svg +137 -135
- package/docs/media/make.mjs +9 -9
- package/docs/media/pass-dark.svg +4 -4
- package/docs/media/pass-light.svg +4 -4
- package/docs/media/reasons-dark.svg +19 -18
- package/docs/media/reasons-light.svg +19 -18
- package/docs/media/run-dark.svg +19 -18
- package/docs/media/run-light.svg +19 -18
- package/docs/reference.md +127 -28
- package/integrations/firstmate/brief-include.md +1 -1
- package/integrations/firstmate/merge-gate +6 -7
- package/package.json +1 -1
- package/skills/done-done/SKILL.md +3 -2
package/README.md
CHANGED
|
@@ -59,13 +59,13 @@ npx skills add umeranjum17/done-done-axi --skill done-done -a <agent> -y
|
|
|
59
59
|
|
|
60
60
|
`<agent>` is `claude-code`, `codex`, `opencode`, `pi` or `gemini-cli`. `-a` and `-y` skip the installer's four prompts and keep it from writing skill folders for agents you do not use.
|
|
61
61
|
|
|
62
|
-
The skill lands in this project (`.claude/skills/done-done/` for Claude Code, `.
|
|
62
|
+
The skill lands in this project (`.claude/skills/done-done/` for Claude Code, `.agents/skills/done-done/` for Codex, opencode, Pi and Gemini CLI) next to a `skills-lock.json`: commit both so teammates and CI get it too. Or add `-g` to install it once for all your projects (for example `~/.claude/skills/` for Claude Code) and leave the project untouched.
|
|
63
63
|
|
|
64
64
|
That is the entire setup - no npm install needed.
|
|
65
|
-
The skill tells your agent to use `done-done` if it is on PATH and otherwise run the
|
|
65
|
+
The skill tells your agent to use `done-done` if it is on PATH and otherwise run the npm package through `npx`, so the CLI comes along on demand:
|
|
66
66
|
|
|
67
67
|
```sh
|
|
68
|
-
npx -y
|
|
68
|
+
npx -y done-done-axi --help
|
|
69
69
|
```
|
|
70
70
|
|
|
71
71
|
The skill stays a short stub and sends the agent to `--help` and the bare command for current instructions, so an installed copy cannot go stale against a newer CLI.
|
|
@@ -86,21 +86,13 @@ The skill is the recommended path, but it is not the only one.
|
|
|
86
86
|
|
|
87
87
|
**1. Install**
|
|
88
88
|
|
|
89
|
-
```sh
|
|
90
|
-
npm install -g --prefix ~/.local --allow-git=all github:umeranjum17/done-done-axi
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
`--prefix ~/.local` installs into your home folder, so it needs no `sudo`. `--allow-git=all` is needed on npm 12, which blocks GitHub installs by default (verified accepted on npm 10). This installs two names for the same CLI: `done-done-axi` and the short `done-done` used below. If `command -v done-done` prints nothing, add `~/.local/bin` to your `PATH`: `export PATH="$HOME/.local/bin:$PATH"`.
|
|
94
|
-
|
|
95
|
-
To skip the install, run `npx -y --allow-git=all github:umeranjum17/done-done-axi` wherever this README says `done-done`.
|
|
96
|
-
|
|
97
|
-
Once done-done-axi is published to npm, the registry line takes over as the install:
|
|
98
|
-
|
|
99
89
|
```sh
|
|
100
90
|
npm install -g --prefix ~/.local done-done-axi
|
|
101
91
|
```
|
|
102
92
|
|
|
103
|
-
|
|
93
|
+
`--prefix ~/.local` installs into your home folder, so it needs no `sudo`. This installs two names for the same CLI: `done-done-axi` and the short `done-done` used below. If `command -v done-done` prints nothing, add `~/.local/bin` to your `PATH`: `export PATH="$HOME/.local/bin:$PATH"`.
|
|
94
|
+
|
|
95
|
+
To skip the install, run `npx -y done-done-axi` wherever this README says `done-done`.
|
|
104
96
|
|
|
105
97
|
**2. Set it up in your app's repo**
|
|
106
98
|
|
|
@@ -109,7 +101,7 @@ cd your-app
|
|
|
109
101
|
done-done init
|
|
110
102
|
```
|
|
111
103
|
|
|
112
|
-
`init` writes a minimal `.done-done.yaml` (only if you don't already have one) that names your app, and a commented global `~/.done-done/config.yaml` (only if there is none). done-done never picks the coding agent it starts: `init` asks you in a terminal, or takes `--agent <agent> --model <model>`. It never picks the judge either, the agent of its own that `verify` starts to look at each
|
|
104
|
+
`init` writes a minimal `.done-done.yaml` (only if you don't already have one) that names your app, and a commented global `~/.done-done/config.yaml` (only if there is none). done-done never picks the coding agent it starts: `init` asks you in a terminal, or takes `--agent <agent> --model <model>`. It never picks the judge either, the agent of its own that `verify` starts to look at each capture (never the one that did the work): `init` asks in a terminal, or takes `--judge <agent> --judge-model <model>`. The config keys are in the [reference](docs/reference.md#config). A plain `init` never overwrites an existing config. It also scaffolds a verify skill at `.agents/skills/verify-<app>` from the repo's own files, for your agent to finish with one live drive. Evidence lives outside the repo, in `~/.done-done/evidence/<app>`, so it outlives the checkout that captured it. Then describe how to capture proof — swap the command for whatever drives your real app and saves screenshots, recordings or logs:
|
|
113
105
|
|
|
114
106
|
```yaml
|
|
115
107
|
journeys:
|
|
@@ -151,7 +143,9 @@ done-done receipt --outcome "it starts" ~/.done-done/evidence/your-app/*/*.log
|
|
|
151
143
|
done-done receipt --verify "$(git rev-parse HEAD)"
|
|
152
144
|
```
|
|
153
145
|
|
|
154
|
-
No journey yet? `done-done verify --outcome "<what a user can now do>"` has your coding agent drive one feature of the verify skill the way a user does (`--feature <name>` picks it). A judge that is not the author's own agent and model then checks the
|
|
146
|
+
No journey yet? `done-done verify --outcome "<what a user can now do>"` has your coding agent drive one feature of the verify skill the way a user does (`--feature <name>` picks it). A judge that is not the author's own agent and model then checks the captures. When it says a capture is wrong or unclear, or a capture it cannot read (a video or other file) is not judged, the run waits at its gate (exit 4) until you answer with `done-done respond --action approve|skip --reason "<why>"` or `--action fix`. Until then, a new `verify` or `rerun` of the same claim in that checkout is refused, so nothing goes around the gate. Approve and skip are logged with who and why, and they end the run `overridden`, never `pass`, so `receipt --verify` still refuses it ([details](docs/reference.md#drive-by-agent)).
|
|
147
|
+
|
|
148
|
+
With `auto_fix: { verify: <n> }` (0, the default, reports only), a failed verify is fixed with your own agent: your judge first sorts each wrong screen (a product bug to fix, or a question that waits at the gate for you), then your agent fixes the product in a done-done worktree with your ignored files copied in, and verify runs again there, up to n times. A pass ends `changed`, with the fix on a `done-done/verify-<run-id>` branch and your branch untouched ([details](docs/reference.md#fix-the-product)).
|
|
155
149
|
|
|
156
150
|
Run `done-done` with no arguments to see the current config and the next step.
|
|
157
151
|
|
|
@@ -163,8 +157,9 @@ Run `done-done` with no arguments to see the current config and the next step.
|
|
|
163
157
|
- ♻️ **Not reused**: Proof made only of files already used for a different claim fails, and so does citing the same file twice. A claim is named by `--claim-id <id>`, which defaults to your branch, so rewording it keeps its proof. Several claims on one branch each need their own `--claim-id`.
|
|
164
158
|
- 🧾 **Honest claims**: Reads a pull request's `## Checks` block — from a file, a URL, or stdin — and makes sure every line is filled in.
|
|
165
159
|
- 📌 **Verifiable later**: `receipt` records a passing check against the exact commit and the sha256 of every file it cited; `receipt --verify` re-checks it from any checkout of that commit on the same machine (a merge gate or a fresh clone), or in CI once the evidence folder is copied there ([how](docs/reference.md#where-proof-lives)).
|
|
166
|
-
- 👀 **Shows the outcome** (on by default in `verify`; `check` and `receipt` with `--judge`): `verify` starts done-done's own judge, a fresh, tools-off agent on your own plan or key (Claude Code,
|
|
167
|
-
- 🎨 **Looks right**: `done-done design <screens...> [--mock <screen>=<mock>]` has the same judge look at each captured screen, alone or next to its approved mock, and fails it on stretched or squashed images, clipped text, overlap, elements off-screen, blank or black strips and broken layout, naming each defect and where it is ([how](docs/reference.md#design-check)).
|
|
160
|
+
- 👀 **Shows the outcome** (on by default in `verify`; `check` and `receipt` with `--judge`): `verify` starts done-done's own judge, a fresh, tools-off agent on your own plan or key (Claude Code, opencode, pi or Gemini CLI, or your own OpenAI API key), to say whether each capture is the right screen for the claim (and each text capture, such as a log, JSON or CLI output, the right output), and a wrong one fails. You choose the judge (`done-done init --judge <agent> --judge-model <model>`, or pick it from the list `verify` shows in a terminal); done-done never picks one, and it is never the agent and model that did the work. `--no-judge` or `judge.enabled: false` skips it when you choose to, and `check` and `receipt` judge only with `--judge`.
|
|
161
|
+
- 🎨 **Looks right**: `done-done design <screens...> [--mock <screen>=<mock>]` has the same judge look at each captured screen, alone or next to its approved mock, and fails it on stretched or squashed images, clipped or squashed text, overlap, elements off-screen, blank or black strips and broken layout, naming each defect and where it is ([how](docs/reference.md#design-check)).
|
|
162
|
+
- 🧩 **Page is clean** (with a `dom` block): each captured screen also needs its page-check record. A page that overflows the viewport, logs a console error, has a failed request or shows squashed text fails, unless an allow list names it ([how](docs/reference.md#dom-assertions)).
|
|
168
163
|
- 🔒 **Safe in CI**: `--trusted-ref origin/main` reads the rules from your main branch, so a pull request can't loosen them.
|
|
169
164
|
|
|
170
165
|
Every answer is an exit code: **0** pass, **1** fail (or overridden at the gate), **2** usage or config error, **3** can't tell either way, **4** a `verify`, `maintain` or `rerun` run is still running, or a `verify` run waits at its gate.
|
|
@@ -173,28 +168,29 @@ Every answer is an exit code: **0** pass, **1** fail (or overridden at the gate)
|
|
|
173
168
|
|
|
174
169
|
[Firstmate](https://github.com/kunchenguid/firstmate) hands tasks to agent workers and merges their work. done-done ships what those workers need to prove each task, and what the merge needs to check that proof, in [`integrations/firstmate/`](integrations/firstmate/).
|
|
175
170
|
|
|
176
|
-
Install the skill once per machine for your workers' agent (the same `-a` values), install the CLI from
|
|
171
|
+
Install the skill once per machine for your workers' agent (the same `-a` values), install the CLI from npm, and copy two files from it into your Firstmate home:
|
|
177
172
|
|
|
178
173
|
```sh
|
|
179
174
|
npx skills add umeranjum17/done-done-axi --skill done-done -g -a <agent> -y
|
|
180
|
-
npm install -g
|
|
175
|
+
npm install -g done-done-axi
|
|
181
176
|
cat "$(npm root -g)/done-done-axi/integrations/firstmate/brief-include.md" >> "$FM_HOME/config/brief-include.md"
|
|
182
177
|
cp "$(npm root -g)/done-done-axi/integrations/firstmate/merge-gate" "$FM_HOME/config/merge-gate"
|
|
183
178
|
```
|
|
184
179
|
|
|
185
180
|
- **`brief-include.md`**: Firstmate adds `config/brief-include.md` to every worker brief. It tells the worker to set up done-done, run a journey on its committed change, write a `## Checks` block, pass `check`, and record a `receipt`. The evidence and receipt stay in `~/.done-done/evidence/<app>` after teardown deletes the worktree.
|
|
186
|
-
- **`merge-gate`** (optional): `merge-gate <task-id> <worktree|-> <head> <pr-url|->`. In the worktree, it refuses a PR whose head is not `<head>`, then runs `done-done check --from <PR URL or $FM_HOME/data/<task-id>/checks.md> --trusted-ref <default branch>` (`origin/HEAD`, else `origin/main` or `origin/master`, else the local `main` or `master` in a repo with no remote), then `done-done receipt --verify <head>`. Both paths read done-done's rules from your default branch, never from the task's own branch, so merge `.done-done.yaml` there before you rely on it. The gate does not fetch: it reads the local `origin` refs, so run `git fetch origin` before you run it. It needs `done-done` on PATH (the `npm install` line above). A nonzero exit refuses the merge, and done-done's output says why. A worktree of `-` means the task recorded none: the gate
|
|
181
|
+
- **`merge-gate`** (optional): `merge-gate <task-id> <worktree|-> <head> <pr-url|->`. In the worktree, it refuses a PR whose head is not `<head>`, then runs `done-done check --from <PR URL or $FM_HOME/data/<task-id>/checks.md> --trusted-ref <default branch>` (`origin/HEAD`, else `origin/main` or `origin/master`, else the local `main` or `master` in a repo with no remote), then `done-done receipt --verify <head>`. Both paths read done-done's rules from your default branch, never from the task's own branch, so merge `.done-done.yaml` there before you rely on it. The gate does not fetch: it reads the local `origin` refs, so run `git fetch origin` before you run it. It needs `done-done` on PATH (the `npm install` line above). A nonzero exit refuses the merge, and done-done's output says why. A worktree of `-` means the task recorded none, so there is no repo to check: the gate refuses (exit 2) rather than check whatever folder it was called from. Its `check` runs with `--judge`, so the judge the user set must pass each capture. Upstream Firstmate does not call a merge gate yet, so this file has no effect until it does; you can run it by hand before a merge.
|
|
187
182
|
|
|
188
183
|
Another fleet works the same way: add the include text to whatever your workers read first, and run `merge-gate` before each merge, with `FM_HOME` set to the folder that holds `data/<task-id>/checks.md` for local-only tasks.
|
|
189
184
|
|
|
190
185
|
## Learn more
|
|
191
186
|
|
|
187
|
+
- [Vision](VISION.md): the three-tool family, the rules they share, and where done-done goes next.
|
|
192
188
|
- [Full reference](docs/reference.md): every `init`, `doctor`, `run`, `check` and `receipt` option, the Checks block rules, each file check, and both config files.
|
|
193
189
|
- [Report a problem or ask a question](https://github.com/umeranjum17/done-done-axi/issues)
|
|
194
190
|
|
|
195
191
|
## What's next
|
|
196
192
|
|
|
197
|
-
So far: `init`, `doctor`, `run`, `check`, `receipt`, `verify` (by journey or by your coding agent, with a gate you answer with `respond`), the
|
|
193
|
+
So far: `init`, `doctor`, `run`, `check`, `receipt`, `verify` (by journey or by your coding agent, with a gate you answer with `respond`), the capture judge (on by default in `verify`), the `design` visual check and the opt-in session hooks for Claude Code, Codex, opencode, Gemini CLI and pi. Coming next: help writing the proof steps for your app.
|
|
198
194
|
|
|
199
195
|
## Develop
|
|
200
196
|
|
package/dist/agents.js
CHANGED
|
@@ -162,6 +162,41 @@ const ADAPTERS = {
|
|
|
162
162
|
return extractJsonObject(text, c.schema.required);
|
|
163
163
|
},
|
|
164
164
|
},
|
|
165
|
+
// codex exec with no prompt argument reads it on stdin; --json streams JSONL events whose last agent_message holds the
|
|
166
|
+
// answer, and --ephemeral keeps no session. An edit step runs in codex's workspace-write sandbox, which writes only the
|
|
167
|
+
// repo, the run's folders (--add-dir) and $TMPDIR; it may use the network, since the drive launches the app and talks
|
|
168
|
+
// to it, and it keeps the user's config.toml. Without a model, codex runs its own default.
|
|
169
|
+
// A tool-less call is refused: probed live 13 Oct, codex with the user's config.toml kept its MCP server, which read a
|
|
170
|
+
// canary file outside the folder, and --disable does not turn off its multi-agent tools.
|
|
171
|
+
codex: {
|
|
172
|
+
editOnly: 'codex cannot run tool-less: with your config.toml it keeps its MCP tools, which read files outside the folder',
|
|
173
|
+
// Seen live 13 Oct: each sandboxed command runs in its own process namespace, so a server or browser bridge started in the
|
|
174
|
+
// background by one command was gone by the next one, and a drive that launched the app and then browsed it captured nothing.
|
|
175
|
+
editNote: 'Your shell sandbox ends every process a command started, background ones too, when that command ends. So start what a task needs (an app, a server, a browser bridge), use it, save what it produces and stop it within one shell command (a script in $TMPDIR is fine).',
|
|
176
|
+
argv: (c) => ['exec', '--json', '--ephemeral', ...(c.model ? ['-m', c.model] : []),
|
|
177
|
+
'--sandbox', 'workspace-write', '-c', 'sandbox_workspace_write.network_access=true', ...(c.dirs ?? []).flatMap((d) => ['--add-dir', d])],
|
|
178
|
+
answer: (out, c) => {
|
|
179
|
+
let text = '';
|
|
180
|
+
let failed = '';
|
|
181
|
+
let error = '';
|
|
182
|
+
for (const line of out.split('\n')) {
|
|
183
|
+
if (!line.startsWith('{'))
|
|
184
|
+
continue;
|
|
185
|
+
const e = JSON.parse(line);
|
|
186
|
+
if (e.type === 'item.completed' && e.item?.type === 'agent_message')
|
|
187
|
+
text = String(e.item.text ?? '');
|
|
188
|
+
if (e.type === 'turn.failed')
|
|
189
|
+
failed = String(e.error?.message ?? JSON.stringify(e));
|
|
190
|
+
if (e.type === 'error')
|
|
191
|
+
error = String(e.message ?? JSON.stringify(e));
|
|
192
|
+
}
|
|
193
|
+
// A failed turn has no answer; an error event (a retried stream) fails the call only when the agent said nothing.
|
|
194
|
+
const why = failed || (text ? '' : error);
|
|
195
|
+
if (why)
|
|
196
|
+
throw new ProviderError(why);
|
|
197
|
+
return extractJsonObject(text, c.schema.required);
|
|
198
|
+
},
|
|
199
|
+
},
|
|
165
200
|
// gemini -p text is appended to stdin, so -p carries only a fixed trailer and the prompt goes on stdin;
|
|
166
201
|
// -o json prints one object {response, stats, error?} (its headless.md). plan mode is read-only, so a
|
|
167
202
|
// headless call never blocks on an approval prompt; auto_edit (edits approved) plus the shell tool is only for edit steps,
|
|
@@ -193,7 +228,7 @@ export function selectAgent(cfg) {
|
|
|
193
228
|
return agentRun(cfg, cfg.agent);
|
|
194
229
|
}
|
|
195
230
|
// The agents a user can pick, and the error that tells a calling agent to ask the user rather than pick one itself.
|
|
196
|
-
export const agentChoices = () => [...AGENTS
|
|
231
|
+
export const agentChoices = () => [...AGENTS, 'acp:<target>'];
|
|
197
232
|
// The error a command gives when the agent or the judge is not set: the ask, the choices, and the command that saves the answer.
|
|
198
233
|
const askError = (kind, choices, rule, save) => new UsageError(`no ${kind} is set: ask the user which ${kind} to use (${choices}); ${rule}`, `Save the user's answer: ${save}`);
|
|
199
234
|
export const noAgent = () => askError('agent', agentChoices().join(', '), 'never pick one for them', `done-done init --agent <agent> --model <model>, e.g. done-done init --agent opencode --model ${CHEAP.opencode.model}`);
|
|
@@ -209,9 +244,8 @@ export function knownAgentOf(agent) {
|
|
|
209
244
|
return AGENTS.find((a) => target === a || (target.startsWith(a) && !/[A-Za-z0-9]/.test(target[a.length]))) ?? agent;
|
|
210
245
|
}
|
|
211
246
|
const adapterFor = (agent) => (agent.startsWith('acp:') ? ACP : Object.hasOwn(ADAPTERS, agent) ? ADAPTERS[agent] : undefined);
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
}
|
|
247
|
+
// Why an agent cannot run a tool-less call, '' when it can.
|
|
248
|
+
export const editOnlyReason = (agent) => adapterFor(agent)?.editOnly ?? '';
|
|
215
249
|
// A binary from config: a bare name is looked up on PATH, an absolute path is used when it is an executable file.
|
|
216
250
|
export const findBin = (bin) => (isAbsolute(bin) ? (isExecutable(bin) ? bin : '') : onPath(bin));
|
|
217
251
|
// The binary that starts an agent: agent_path_override.<agent>, else its name on PATH. An acp:<target> agent starts
|
|
@@ -234,7 +268,8 @@ export const JUDGES = AGENTS.filter((a) => a === 'claude' || ANSWERER_JUDGES.inc
|
|
|
234
268
|
// Like the agent, the judge is exactly the one the user set; with none, done-done asks the user (in a terminal) or
|
|
235
269
|
// stops with this error, so a calling agent asks its user rather than picking one.
|
|
236
270
|
export const noJudge = () => askError('judge', JUDGES.join(', '), 'never pick one for them, and never the agent that did the work', `done-done init --judge <agent> --judge-model <model>, e.g. done-done init --judge opencode --judge-model ${CHEAP.opencode.model}`);
|
|
237
|
-
|
|
271
|
+
// fix: the judge on a fix path (auto_fix) is never the fixer's own agent, at any model, and judge.allow_same does not apply.
|
|
272
|
+
export function selectJudge(cfg, found, fix = false) {
|
|
238
273
|
const route = cfg.judge.route;
|
|
239
274
|
if (!route)
|
|
240
275
|
throw noJudge();
|
|
@@ -250,6 +285,8 @@ export function selectJudge(cfg, found) {
|
|
|
250
285
|
const independence = keyed
|
|
251
286
|
? found.model && model === found.model ? 'session-only' : 'agent'
|
|
252
287
|
: agent !== knownAgentOf(found.agent) ? 'agent' : found.model && model !== found.model ? 'model' : 'session-only';
|
|
288
|
+
if (fix && independence !== 'agent')
|
|
289
|
+
throw new UsageError(`judge: the judge ${agent} (model ${model}) is the fixer's own agent (${found.agent}) at any model, and the judge is never the agent that did the work; judge.allow_same does not apply to auto_fix`, 'Ask the user for another judge agent and save it: done-done init --judge <agent> --judge-model <model>');
|
|
253
290
|
if (independence === 'session-only' && !cfg.judge.allow_same)
|
|
254
291
|
throw new UsageError(`judge: the judge ${agent} (model ${model}) is the author's own agent and model (${found.agent}), and the judge is never the agent that did the work`, `Ask the user for another judge agent or model and save it: done-done init --judge <agent> --judge-model <model>; or set judge.allow_same: true in ~/.done-done/config.yaml to accept a fresh session of the same one`);
|
|
255
292
|
return { route, agent, model, independence, author: { agent: found.agent, model: found.model } };
|
|
@@ -335,6 +372,8 @@ export async function askAgent(c) {
|
|
|
335
372
|
cwd = c.tools === 'none' ? dir : c.cwd;
|
|
336
373
|
if (!adapter)
|
|
337
374
|
throw new Error(`no adapter for ${c.agent} yet`);
|
|
375
|
+
if (adapter.editOnly && c.tools !== 'edit')
|
|
376
|
+
throw new Error(adapter.editOnly);
|
|
338
377
|
for (const i of c.images ?? [])
|
|
339
378
|
writeFileSync(join(dir, i.name), i.bytes, { mode: 0o600 });
|
|
340
379
|
for (const [name, text] of Object.entries(adapter.files?.(c) ?? {}))
|
|
@@ -345,9 +384,11 @@ export async function askAgent(c) {
|
|
|
345
384
|
let lastError = '';
|
|
346
385
|
for (;;) {
|
|
347
386
|
attempts++;
|
|
348
|
-
// An ask-again names what was wrong with the last answer.
|
|
349
|
-
|
|
350
|
-
const
|
|
387
|
+
// An ask-again names what was wrong with the last answer. The trailer ends on words, not the schema: a weak model
|
|
388
|
+
// (Go Muse, 13 Oct) copied a schema that closed the prompt back as its answer.
|
|
389
|
+
const schemaTrailer = `\n\nAnswer with only one JSON object that matches this JSON schema:\n${JSON.stringify(c.schema)}\nThat object holds your own values, never the schema itself, and you send no other text.\n`;
|
|
390
|
+
const prompt = c.tools === 'edit' && adapter.editNote ? `${c.prompt}\n\n${adapter.editNote}` : c.prompt;
|
|
391
|
+
const promptText = (attempts === 1 ? prompt : `${prompt}\n\nYour previous answer was not valid (${lastError}).`) + schemaTrailer;
|
|
351
392
|
if (adapter.file)
|
|
352
393
|
writeFileSync(join(dir, 'prompt.md'), promptText, { mode: 0o600 });
|
|
353
394
|
res = await start(bin, argv(dir), c, cwd, adapter.file ? '' : promptText, { ...process.env, ...c.env, ...adapter.env?.(c) });
|