@mjasnikovs/pi-task 0.40.50 → 0.41.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 +7 -4
- package/dist/config/config.d.ts +61 -18
- package/dist/config/config.js +50 -6
- package/dist/config/register.js +18 -5
- package/dist/shared/content-tokens.d.ts +16 -0
- package/dist/shared/content-tokens.js +137 -0
- package/dist/task/accept-debt.d.ts +15 -1
- package/dist/task/accept-debt.js +4 -0
- package/dist/task/artifact-closure.js +7 -1
- package/dist/task/auto-io.d.ts +59 -7
- package/dist/task/auto-io.js +140 -47
- package/dist/task/auto-orchestrator.d.ts +22 -19
- package/dist/task/auto-orchestrator.js +126 -112
- package/dist/task/child-runner.d.ts +18 -0
- package/dist/task/child-runner.js +7 -8
- package/dist/task/command-run.d.ts +4 -0
- package/dist/task/command-run.js +4 -2
- package/dist/task/constraint-policy.d.ts +52 -0
- package/dist/task/constraint-policy.js +66 -0
- package/dist/task/context-silence.d.ts +5 -4
- package/dist/task/context-silence.js +13 -5
- package/dist/task/coverage-loop.d.ts +7 -4
- package/dist/task/coverage-loop.js +16 -138
- package/dist/task/debug-log.d.ts +25 -2
- package/dist/task/debug-log.js +64 -10
- package/dist/task/decompose-fidelity.d.ts +17 -4
- package/dist/task/decompose-fidelity.js +95 -55
- package/dist/task/decompose-granularity.d.ts +7 -7
- package/dist/task/decompose-granularity.js +8 -8
- package/dist/task/env-notes.d.ts +65 -19
- package/dist/task/env-notes.js +152 -50
- package/dist/task/external-context.js +25 -5
- package/dist/task/fix-context.d.ts +39 -0
- package/dist/task/fix-context.js +58 -0
- package/dist/task/gate-deps.d.ts +49 -0
- package/dist/task/gate-deps.js +199 -38
- package/dist/task/gate-evidence.d.ts +51 -0
- package/dist/task/gate-evidence.js +90 -0
- package/dist/task/gate-resolution.d.ts +119 -0
- package/dist/task/gate-resolution.js +120 -0
- package/dist/task/git-state-guard.js +3 -26
- package/dist/task/handoff.d.ts +35 -0
- package/dist/task/handoff.js +100 -0
- package/dist/task/health-baseline.d.ts +87 -0
- package/dist/task/health-baseline.js +158 -0
- package/dist/task/ledger.d.ts +10 -0
- package/dist/task/ledger.js +22 -11
- package/dist/task/lint-fix.d.ts +11 -0
- package/dist/task/lint-fix.js +14 -8
- package/dist/task/loop-detector.d.ts +52 -3
- package/dist/task/loop-detector.js +81 -5
- package/dist/task/mentions.d.ts +17 -0
- package/dist/task/mentions.js +86 -0
- package/dist/task/orchestrator.d.ts +53 -9
- package/dist/task/orchestrator.js +104 -28
- package/dist/task/orientation.d.ts +135 -29
- package/dist/task/orientation.js +259 -67
- package/dist/task/owned-freeze-reassign.d.ts +7 -3
- package/dist/task/owned-freeze-reassign.js +13 -5
- package/dist/task/parsers.d.ts +6 -1
- package/dist/task/parsers.js +21 -4
- package/dist/task/phases.d.ts +1 -1
- package/dist/task/phases.js +125 -153
- package/dist/task/plan-orchestrator.js +5 -6
- package/dist/task/plan-rounds.d.ts +1 -0
- package/dist/task/plan-rounds.js +8 -1
- package/dist/task/prohibition-probe.d.ts +6 -1
- package/dist/task/prohibition-probe.js +26 -6
- package/dist/task/prompts.js +13 -2
- package/dist/task/qa-transcript.d.ts +17 -0
- package/dist/task/qa-transcript.js +28 -0
- package/dist/task/question-source.d.ts +16 -2
- package/dist/task/question-source.js +21 -8
- package/dist/task/repo-health-check.d.ts +27 -0
- package/dist/task/repo-health-check.js +39 -5
- package/dist/task/requirements.d.ts +67 -13
- package/dist/task/requirements.js +129 -67
- package/dist/task/research-worker.d.ts +43 -2
- package/dist/task/research-worker.js +103 -8
- package/dist/task/run-bracket.d.ts +9 -4
- package/dist/task/run-bracket.js +9 -0
- package/dist/task/run-context.d.ts +152 -0
- package/dist/task/run-context.js +277 -0
- package/dist/task/spec-doc.d.ts +78 -0
- package/dist/task/spec-doc.js +205 -0
- package/dist/task/spec-model.d.ts +70 -0
- package/dist/task/spec-model.js +139 -0
- package/dist/task/stall-detector.d.ts +1 -1
- package/dist/task/stall-detector.js +9 -5
- package/dist/task/state-dir.d.ts +29 -0
- package/dist/task/state-dir.js +80 -0
- package/dist/task/suppression-probe.d.ts +67 -0
- package/dist/task/suppression-probe.js +78 -0
- package/dist/task/task-gates.d.ts +39 -57
- package/dist/task/task-gates.js +115 -195
- package/dist/task/task-io.d.ts +10 -0
- package/dist/task/task-io.js +69 -18
- package/dist/task/task-parsers.js +6 -6
- package/dist/task/task-types.d.ts +7 -0
- package/dist/task/timings.d.ts +11 -0
- package/dist/task/timings.js +21 -0
- package/dist/task/tree-hash.d.ts +8 -0
- package/dist/task/tree-hash.js +55 -0
- package/dist/task/verify-reconcile.d.ts +4 -3
- package/dist/task/verify-reconcile.js +7 -8
- package/dist/task/verify-resolution.d.ts +28 -5
- package/dist/task/verify-resolution.js +70 -13
- package/dist/task/verify-work.d.ts +98 -35
- package/dist/task/verify-work.js +227 -60
- package/dist/task/yolo.d.ts +0 -13
- package/dist/task/yolo.js +9 -21
- package/dist/workers/pi-worker-core.d.ts +20 -0
- package/dist/workers/pi-worker-core.js +16 -11
- package/dist/workers/pi-worker-docs.d.ts +6 -5
- package/dist/workers/pi-worker-docs.js +8 -7
- package/dist/workers/pi-worker-fetch.d.ts +3 -2
- package/dist/workers/pi-worker-fetch.js +5 -4
- package/dist/workers/pi-worker-search.js +5 -4
- package/dist/workers/research-cache.d.ts +23 -1
- package/dist/workers/research-cache.js +63 -3
- package/dist/workers/shared.d.ts +18 -0
- package/dist/workers/shared.js +38 -22
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -233,7 +233,7 @@ Run `/task-config` to toggle pi-task's behavior in an editor dialog. Settings pe
|
|
|
233
233
|
| **yolo mode** | off | **Unattended runs.** Wherever pi-task would stop and ask, it takes the option already marked RECOMMENDED, stamps the artifact `(YOLO)` so an audit can tell a machine decided, and shows no prompt at all — clarify/grill answers, the verify-FAIL picker (auto-**Accept**, recorded as a yolo debt), and the final-gate picker (autofix while the budget lasts, then leave the run FAILED). A question with no recommendation is **skipped**, never invented. For throwaway/test projects nobody is watching; a real run should decide these itself. |
|
|
234
234
|
| **profile** | default | How much the helper sessions think, in one word, for every step at once. Local models differ sharply here: some break without reasoning, some waste minutes with it, and some cannot do it at all. **default** uses the per-step table pi-task has measured, **on** and **off** force one answer everywhere and ignore that table, and **custom** is whatever the step rows say — changing any of them switches this to custom. A step on **inherit** passes no flag at all, so it uses whatever thinking level pi itself is set to, which is what every step did before this setting existed. |
|
|
235
235
|
| **steps: …** | models `inherit`; levels per the shipped table | One row per group of steps, carrying BOTH dials: the model those children run on and the level they think at, shown as `level · model`. Enter walks a two-step picker — model first, then level — and **the level step offers only what that model declares, opening on the one that will actually run**. That is the whole point of the merge: pi silently CLAMPS a level a model cannot do (a level you set can be erased, and an `off` can be clamped back up to `medium`), so instead of discovering that later you watch the cursor land on the level you are really getting. Models are offered from `pi.modelRegistry.getAvailable()` and stored as the canonical `provider/id` that pi's own `--model` takes. **inherit** on the model half emits no flag, so an all-inherit table is byte-identical to a build without this feature; that is the shipped default, because which models exist is a property of your machine and nothing here can be measured for you. A stored model this machine cannot resolve is never erased (you may have set it on another machine): the flag is dropped, the step runs on pi's default, and a startup hint names the step. Two need care — a provider registered by a host **extension** needs that extension enabled under **ext: …** or those children exit 1; and **implementation** is not free, because it is *your* session moved for the turn and moved back, and a model switch re-bills the whole prompt as a cache miss, twice per task. |
|
|
236
|
-
| **debug logs** | events | How much of a run is written to
|
|
236
|
+
| **debug logs** | events | How much of a run is written to its log directory, `${XDG_STATE_HOME:-~/.local/state}/pi-task/<repo-hash>/<run-id>/*-debug.log` — outside the repository, so the trail is never committed, never rewound by a gate, and never re-read when one is. **`events`** keeps decisions and guard actions — which phase ran, why a worker was retried, what the git-state guard restored, what a write-capable child changed on disk, why a gate returned FAIL — a few lines per task. **`full`** adds every line the child model emitted and every tool result; that's ~85% of the bytes (a real 247 KB `verify-debug.log` is 1315 lines, 521 of them tool dumps) and is what you want while actively debugging. **`off`** writes nothing. Control characters are stripped and long lines are capped, so the files stay greppable. Nothing in pi-task ever reads these files back, so the setting cannot change how a run behaves — only whether you can explain it afterwards, and a log not written can't be recovered later. A completed `/task-auto` run keeps the 20 most recent runs' logs for the repository and deletes the rest. |
|
|
237
237
|
| **watch: …** | all on | One toggle per tool in the live session, deciding whether **command timeout** applies to it. The list is discovered from `pi.getAllTools()` when the menu opens — built-ins first, then each extension's tools with the owning entry-point path in the description — so nothing is typed by hand and an uninstalled tool just stops being listed. Turn one **off** only for a tool that already owns a longer bounded, cancellable contract of its own (the guard exists because pi's `bash` has an optional timeout with *no* default — that reasoning doesn't transfer to a tool that has one). Two things to know before you do: a genuine hang in an unwatched tool is caught by nothing, since **stuck reply retry** is paused for the whole time any tool runs; and an unwatched tool is still killed as collateral if a *watched* sibling in the same turn overruns, because pi runs sibling tool calls concurrently and the abort ends the whole turn. Stored as exemptions, so the default and every tool pi-task has never seen stay guarded. |
|
|
238
238
|
| **ext: …** | all off | One toggle per installed host `pi` extension, loading it into every child session by explicit path. Children otherwise run with extensions off, so a provider registered by an extension (e.g. `pi-lmstudio`) doesn't exist in them and they can't resolve the default model. Children also inherit the extension's tools and hooks, so only enable ones you trust. The list is strictly additive (discovery stays off), and an entry whose file is gone is skipped at spawn time, never fatal. |
|
|
239
239
|
|
|
@@ -246,6 +246,7 @@ Run `/task-config` to toggle pi-task's behavior in an editor dialog. Settings pe
|
|
|
246
246
|
| `CARGO_HOME` | `pi-worker-docs` | Where crate source checkouts are read from (defaults to `~/.cargo`). |
|
|
247
247
|
| `CABAL_DIR` | `pi-worker-docs` | Where cabal's downloaded package tarballs are read from (also checks `~/.cabal/packages` and `${XDG_CACHE_HOME:-~/.cache}/cabal/packages`). |
|
|
248
248
|
| `XDG_DATA_HOME` | remote push | Where the VAPID keypair is stored (defaults to `~/.local/share`). |
|
|
249
|
+
| `XDG_STATE_HOME` | task trail | Where a run's `*-debug.log` files are kept, under `pi-task/<repo-hash>/<run-id>/` (defaults to `~/.local/state`). A relative value is ignored, per the XDG spec. |
|
|
249
250
|
| `PI_REMOTE_PUSH_SUBJECT` | remote push | VAPID JWT `sub` contact. Defaults to the project URL; set your own `mailto:you@domain.com` or `https://…`. |
|
|
250
251
|
| `PI_REMOTE_PUSH_DEBUG` | remote push | When set (e.g. `1`), logs push delivery and push-service HTTP status. Off by default. |
|
|
251
252
|
| `PI_REMOTE_PUSH_LOG` | remote push | Path for the debug log (defaults to `/tmp/pi-task-push.log`). |
|
|
@@ -255,15 +256,17 @@ Run `/task-config` to toggle pi-task's behavior in an editor dialog. Settings pe
|
|
|
255
256
|
|
|
256
257
|
Tasks are persisted to `<cwd>/.pi-tasks/TASK_NNNN.md`. A run also keeps small
|
|
257
258
|
line-oriented ledgers beside them — contracts, launch contract, environment
|
|
258
|
-
notes, accepted debt, repair queue, requirements
|
|
259
|
-
|
|
259
|
+
notes, accepted debt, repair queue, requirements. The `*-debug.log` trail is not
|
|
260
|
+
among them: it lives outside the tree, under
|
|
261
|
+
`${XDG_STATE_HOME:-~/.local/state}/pi-task/<repo-hash>/<run-id>/`.
|
|
262
|
+
Add `.pi-tasks/` to your `.gitignore` if you don't want
|
|
260
263
|
them checked in.
|
|
261
264
|
|
|
262
265
|
## Development
|
|
263
266
|
|
|
264
267
|
```sh
|
|
265
268
|
bun install
|
|
266
|
-
bun run test #
|
|
269
|
+
bun run test # 4770 tests pass across 252 files (3 skips)
|
|
267
270
|
bun run lint # prettier + eslint + tsc --noEmit
|
|
268
271
|
bun run build # tsc → dist/
|
|
269
272
|
```
|
package/dist/config/config.d.ts
CHANGED
|
@@ -7,13 +7,27 @@ export interface PiTaskConfig {
|
|
|
7
7
|
enforceGuidelines: boolean;
|
|
8
8
|
verifyWork: boolean;
|
|
9
9
|
/**
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
10
|
+
* What shape the research phase runs its four workers in (task/phases.ts).
|
|
11
|
+
*
|
|
12
|
+
* `graph` (DEFAULT) runs each worker as soon as the sections it declares it
|
|
13
|
+
* comes `after` are finished: FILES first, then APIS, with CONTEXT and TOOLING
|
|
14
|
+
* alongside. The FILES→APIS handoff survives, which the old all-at-once
|
|
15
|
+
* parallel mode lost.
|
|
16
|
+
*
|
|
17
|
+
* `serial` runs them one at a time, for a single local GPU where concurrent
|
|
18
|
+
* streams share one device and slow each other down.
|
|
19
|
+
*/
|
|
20
|
+
researchConcurrency: ResearchConcurrency;
|
|
21
|
+
/**
|
|
22
|
+
* Extra paths the project tour must never pre-read, as `.gitignore`-style
|
|
23
|
+
* patterns (`docs/generated/**`, `fixtures/`).
|
|
24
|
+
*
|
|
25
|
+
* The shipped exclusions — vendored dependencies, build output, the agent's
|
|
26
|
+
* own directories — are `VENDORED_DIRS` in task/orientation.ts, and the
|
|
27
|
+
* repo's `.gitignore` is read on top of them. This is the hand-edited third
|
|
28
|
+
* source, for a tracked directory that is bulk to this project alone.
|
|
15
29
|
*/
|
|
16
|
-
|
|
30
|
+
orientationExclude: string[];
|
|
17
31
|
/**
|
|
18
32
|
* Cache docs/search/fetch worker RESULTS for the duration of one /task-auto
|
|
19
33
|
* run, so sibling tasks re-asking the same (package/url, query) reuse the
|
|
@@ -44,6 +58,15 @@ export interface PiTaskConfig {
|
|
|
44
58
|
* /task-config, which enumerates the currently installed extensions.
|
|
45
59
|
*/
|
|
46
60
|
extensionWhitelist: string[];
|
|
61
|
+
/**
|
|
62
|
+
* Extra suppression spellings the verify gate's widening probe counts, as
|
|
63
|
+
* regular-expression SOURCES (see task/suppression-probe.ts). The shipped
|
|
64
|
+
* registry covers the common checkers across ecosystems; this is for a
|
|
65
|
+
* project's own — a house `// SAFETY-OVERRIDE` marker, a generated-code
|
|
66
|
+
* pragma. An entry that does not compile is dropped, never fatal.
|
|
67
|
+
* DEFAULT empty: the shipped rows are the policy until a project says more.
|
|
68
|
+
*/
|
|
69
|
+
suppressionPatterns: string[];
|
|
47
70
|
/**
|
|
48
71
|
* Wall-clock ceiling (ms) on a SINGLE tool execution before the command
|
|
49
72
|
* watchdog steps in. pi's bash tool declares its `timeout` parameter as
|
|
@@ -117,14 +140,13 @@ export interface PiTaskConfig {
|
|
|
117
140
|
*/
|
|
118
141
|
yoloMode: boolean;
|
|
119
142
|
/**
|
|
120
|
-
* How much the run writes to its
|
|
121
|
-
* (task/debug-log.ts)
|
|
122
|
-
* `
|
|
123
|
-
*
|
|
124
|
-
*
|
|
125
|
-
*
|
|
126
|
-
*
|
|
127
|
-
* config path away from the developer's own file.
|
|
143
|
+
* How much the run writes to its `*-debug.log` forensic trail
|
|
144
|
+
* (task/debug-log.ts), kept in the run's state dir (task/state-dir.ts).
|
|
145
|
+
* Nothing in `src/` reads these files back, so this knob is
|
|
146
|
+
* behaviour-neutral by construction. It trades disk against the ability to
|
|
147
|
+
* explain a run after it has finished. The TESTS do read the trail back,
|
|
148
|
+
* which is why the test preload pins the config path — and the state home —
|
|
149
|
+
* away from the developer's own.
|
|
128
150
|
*
|
|
129
151
|
* `full` is every line the child model emitted plus every tool result;
|
|
130
152
|
* `events` keeps only decisions and guard actions; `off` writes nothing.
|
|
@@ -171,8 +193,26 @@ export interface PiTaskConfig {
|
|
|
171
193
|
*/
|
|
172
194
|
groupModels: Record<ChildGroup, string>;
|
|
173
195
|
}
|
|
174
|
-
/** How verbose the
|
|
196
|
+
/** How verbose the `*-debug.log` trail is. See {@link PiTaskConfig.debugLogs}. */
|
|
175
197
|
export type DebugLogLevel = 'off' | 'events' | 'full';
|
|
198
|
+
/** How the research workers are scheduled. See {@link PiTaskConfig.researchConcurrency}. */
|
|
199
|
+
export type ResearchConcurrency = 'graph' | 'serial';
|
|
200
|
+
/** The concurrency choices offered by /task-config, in cycle order. */
|
|
201
|
+
export declare const RESEARCH_CONCURRENCY_OPTIONS: ReadonlyArray<{
|
|
202
|
+
label: string;
|
|
203
|
+
value: ResearchConcurrency;
|
|
204
|
+
}>;
|
|
205
|
+
/** Pin a hand-edited or stale value to one of the offered choices. */
|
|
206
|
+
export declare function sanitizeResearchConcurrency(value: unknown): ResearchConcurrency;
|
|
207
|
+
/**
|
|
208
|
+
* The boolean this setting shipped as, and how a stored one is read.
|
|
209
|
+
*
|
|
210
|
+
* `true` meant "all four at once", which is the graph without its one edge, so it
|
|
211
|
+
* maps to `graph`; `false` meant one at a time. Kept as a documented alias rather
|
|
212
|
+
* than dropped, because dropping it silently re-enables concurrency on the local
|
|
213
|
+
* GPU of every user who turned it off.
|
|
214
|
+
*/
|
|
215
|
+
export declare const DEPRECATED_PARALLEL_KEY = "parallelResearchWorkers";
|
|
176
216
|
/**
|
|
177
217
|
* The debug-log choices offered by /task-config, in cycle order (quietest →
|
|
178
218
|
* loudest, so the cycle reads as a volume dial). Unlike the timeout options the
|
|
@@ -200,6 +240,8 @@ export declare const COMMAND_TIMEOUT_OPTIONS: ReadonlyArray<{
|
|
|
200
240
|
* one of the offered choices so the watchdog never arms on a nonsense value.
|
|
201
241
|
*/
|
|
202
242
|
export declare function sanitizeRequestTimeoutMs(value: unknown): number;
|
|
243
|
+
/** Keep only non-empty string patterns from a hand-edited exclusion list. */
|
|
244
|
+
export declare function sanitizeIgnorePatterns(value: unknown): string[];
|
|
203
245
|
/** Keep only exact, unique Pi tool names from an advanced config override. */
|
|
204
246
|
export declare function sanitizeCommandTimeoutExemptTools(value: unknown): string[];
|
|
205
247
|
/**
|
|
@@ -222,10 +264,11 @@ export declare function sanitizeStreamInactivityMs(value: unknown): number;
|
|
|
222
264
|
*/
|
|
223
265
|
export declare const DEFAULT_CONFIG: PiTaskConfig;
|
|
224
266
|
/**
|
|
225
|
-
* A hand-edited config can hold anything; keep only string entries so a
|
|
226
|
-
* object/number can't reach the child argv as `-e [object Object]
|
|
267
|
+
* A hand-edited config can hold anything; keep only non-blank string entries so a
|
|
268
|
+
* stray object/number can't reach the child argv as `-e [object Object]`, or
|
|
269
|
+
* `new RegExp`.
|
|
227
270
|
*/
|
|
228
|
-
export declare function
|
|
271
|
+
export declare function sanitizeStringList(value: unknown): string[];
|
|
229
272
|
/**
|
|
230
273
|
* How each setting's STORED value becomes a safe in-memory value — one loader per
|
|
231
274
|
* key, keyed on the config's own type.
|
package/dist/config/config.js
CHANGED
|
@@ -6,6 +6,27 @@ import { isSearchProvider } from '../workers/search-types.js';
|
|
|
6
6
|
import { DEFAULT_REASONING_TABLE, sanitizeReasoningLevels, sanitizeReasoningMode } from './reasoning.js';
|
|
7
7
|
import { DEFAULT_GROUP_MODELS, sanitizeGroupModels } from './group-models.js';
|
|
8
8
|
import { DEFAULT_STREAM_INACTIVITY_MS } from '../shared/stream-watchdog.js';
|
|
9
|
+
/** The concurrency choices offered by /task-config, in cycle order. */
|
|
10
|
+
export const RESEARCH_CONCURRENCY_OPTIONS = [
|
|
11
|
+
{ label: 'by dependency', value: 'graph' },
|
|
12
|
+
{ label: 'one at a time', value: 'serial' }
|
|
13
|
+
];
|
|
14
|
+
/** Pin a hand-edited or stale value to one of the offered choices. */
|
|
15
|
+
export function sanitizeResearchConcurrency(value) {
|
|
16
|
+
return RESEARCH_CONCURRENCY_OPTIONS.some(o => o.value === value) ?
|
|
17
|
+
value
|
|
18
|
+
: DEFAULT_RESEARCH_CONCURRENCY;
|
|
19
|
+
}
|
|
20
|
+
const DEFAULT_RESEARCH_CONCURRENCY = 'graph';
|
|
21
|
+
/**
|
|
22
|
+
* The boolean this setting shipped as, and how a stored one is read.
|
|
23
|
+
*
|
|
24
|
+
* `true` meant "all four at once", which is the graph without its one edge, so it
|
|
25
|
+
* maps to `graph`; `false` meant one at a time. Kept as a documented alias rather
|
|
26
|
+
* than dropped, because dropping it silently re-enables concurrency on the local
|
|
27
|
+
* GPU of every user who turned it off.
|
|
28
|
+
*/
|
|
29
|
+
export const DEPRECATED_PARALLEL_KEY = 'parallelResearchWorkers';
|
|
9
30
|
/**
|
|
10
31
|
* The debug-log choices offered by /task-config, in cycle order (quietest →
|
|
11
32
|
* loudest, so the cycle reads as a volume dial). Unlike the timeout options the
|
|
@@ -46,6 +67,15 @@ export function sanitizeRequestTimeoutMs(value) {
|
|
|
46
67
|
value
|
|
47
68
|
: DEFAULT_REQUEST_TIMEOUT_MS;
|
|
48
69
|
}
|
|
70
|
+
/** Keep only non-empty string patterns from a hand-edited exclusion list. */
|
|
71
|
+
export function sanitizeIgnorePatterns(value) {
|
|
72
|
+
if (!Array.isArray(value))
|
|
73
|
+
return [];
|
|
74
|
+
return value
|
|
75
|
+
.filter((p) => typeof p === 'string')
|
|
76
|
+
.map(p => p.trim())
|
|
77
|
+
.filter(p => p.length > 0);
|
|
78
|
+
}
|
|
49
79
|
/** Keep only exact, unique Pi tool names from an advanced config override. */
|
|
50
80
|
export function sanitizeCommandTimeoutExemptTools(value) {
|
|
51
81
|
if (!Array.isArray(value))
|
|
@@ -94,10 +124,14 @@ export const DEFAULT_CONFIG = {
|
|
|
94
124
|
orientation: true,
|
|
95
125
|
enforceGuidelines: true,
|
|
96
126
|
verifyWork: true,
|
|
97
|
-
|
|
127
|
+
// GRAPH: three of the four workers have no dependency on each other, and the
|
|
128
|
+
// one that does keeps its input. See ResearchConcurrency.
|
|
129
|
+
researchConcurrency: DEFAULT_RESEARCH_CONCURRENCY,
|
|
130
|
+
orientationExclude: [],
|
|
98
131
|
researchCache: true,
|
|
99
132
|
searchProvider: 'exa',
|
|
100
133
|
extensionWhitelist: [],
|
|
134
|
+
suppressionPatterns: [],
|
|
101
135
|
requestTimeoutMs: DEFAULT_REQUEST_TIMEOUT_MS,
|
|
102
136
|
commandTimeoutExemptTools: [],
|
|
103
137
|
streamInactivityMs: DEFAULT_STREAM_INACTIVITY_MS,
|
|
@@ -115,10 +149,11 @@ export const DEFAULT_CONFIG = {
|
|
|
115
149
|
groupModels: { ...DEFAULT_GROUP_MODELS }
|
|
116
150
|
};
|
|
117
151
|
/**
|
|
118
|
-
* A hand-edited config can hold anything; keep only string entries so a
|
|
119
|
-
* object/number can't reach the child argv as `-e [object Object]
|
|
152
|
+
* A hand-edited config can hold anything; keep only non-blank string entries so a
|
|
153
|
+
* stray object/number can't reach the child argv as `-e [object Object]`, or
|
|
154
|
+
* `new RegExp`.
|
|
120
155
|
*/
|
|
121
|
-
export function
|
|
156
|
+
export function sanitizeStringList(value) {
|
|
122
157
|
if (!Array.isArray(value))
|
|
123
158
|
return [];
|
|
124
159
|
return value.filter((p) => typeof p === 'string' && p.trim().length > 0);
|
|
@@ -146,13 +181,15 @@ export const CONFIG_LOADERS = {
|
|
|
146
181
|
orientation: asBoolean('orientation'),
|
|
147
182
|
enforceGuidelines: asBoolean('enforceGuidelines'),
|
|
148
183
|
verifyWork: asBoolean('verifyWork'),
|
|
149
|
-
|
|
184
|
+
researchConcurrency: sanitizeResearchConcurrency,
|
|
185
|
+
orientationExclude: sanitizeIgnorePatterns,
|
|
150
186
|
researchCache: asBoolean('researchCache'),
|
|
151
187
|
yoloMode: asBoolean('yoloMode'),
|
|
152
188
|
// A hand-edited or stale enum value must not leak an unknown provider into
|
|
153
189
|
// the dispatch switch — fall back to the default.
|
|
154
190
|
searchProvider: raw => (isSearchProvider(raw) ? raw : DEFAULT_CONFIG.searchProvider),
|
|
155
|
-
extensionWhitelist:
|
|
191
|
+
extensionWhitelist: sanitizeStringList,
|
|
192
|
+
suppressionPatterns: sanitizeStringList,
|
|
156
193
|
requestTimeoutMs: sanitizeRequestTimeoutMs,
|
|
157
194
|
commandTimeoutExemptTools: sanitizeCommandTimeoutExemptTools,
|
|
158
195
|
streamInactivityMs: sanitizeStreamInactivityMs,
|
|
@@ -184,6 +221,13 @@ export function loadConfig(raw) {
|
|
|
184
221
|
;
|
|
185
222
|
out[key] = CONFIG_LOADERS[key](stored[key]);
|
|
186
223
|
}
|
|
224
|
+
// The deprecated boolean answers only for a config saved before the setting
|
|
225
|
+
// became an enum — a file carrying both was written by this version, so the
|
|
226
|
+
// enum is the user's live choice and the boolean is the leftover.
|
|
227
|
+
if (stored.researchConcurrency === undefined
|
|
228
|
+
&& typeof stored[DEPRECATED_PARALLEL_KEY] === 'boolean') {
|
|
229
|
+
out.researchConcurrency = stored[DEPRECATED_PARALLEL_KEY] ? 'graph' : 'serial';
|
|
230
|
+
}
|
|
187
231
|
return out;
|
|
188
232
|
}
|
|
189
233
|
/**
|
package/dist/config/register.js
CHANGED
|
@@ -6,7 +6,7 @@ import { PairPicker } from './option-picker.js';
|
|
|
6
6
|
import { registerBridgeCommand, isRemoteOrigin, publishNote } from '../remote/bridge.js';
|
|
7
7
|
import { readPkgVersion } from '../shared/pkg-version.js';
|
|
8
8
|
import { SEARCH_PROVIDERS, SEARCH_PROVIDER_LABELS, providerForLabel } from '../workers/search-types.js';
|
|
9
|
-
import { COMMAND_TIMEOUT_OPTIONS, DEBUG_LOG_OPTIONS, getConfig, sanitizeDebugLogs, saveConfig, STREAM_INACTIVITY_OPTIONS } from './config.js';
|
|
9
|
+
import { COMMAND_TIMEOUT_OPTIONS, DEBUG_LOG_OPTIONS, getConfig, RESEARCH_CONCURRENCY_OPTIONS, sanitizeDebugLogs, saveConfig, STREAM_INACTIVITY_OPTIONS } from './config.js';
|
|
10
10
|
import { listInstalledExtensions } from './extension-list.js';
|
|
11
11
|
import { listGuardableTools } from './tool-list.js';
|
|
12
12
|
import { CHILD_GROUPS, REASONING_MODES, sanitizeReasoningMode, STEP_GROUP_HELP, REASONING_SETTINGS, effectiveReasoning, resolveReasoning } from './reasoning.js';
|
|
@@ -174,9 +174,22 @@ export const ITEMS = [
|
|
|
174
174
|
booleanItem('research', 'orientation', 'project tour', 'Show the research workers the shape of the project first — package manifest, '
|
|
175
175
|
+ 'types, schema — so they spend their steps on the question instead of on finding '
|
|
176
176
|
+ 'their way around'),
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
177
|
+
{
|
|
178
|
+
id: 'researchConcurrency',
|
|
179
|
+
section: 'research',
|
|
180
|
+
label: 'research workers',
|
|
181
|
+
description: 'How the 4 research workers are run. "by dependency" starts each one as soon as '
|
|
182
|
+
+ 'the work it builds on is done, so three of them overlap; "one at a time" runs '
|
|
183
|
+
+ 'them in order, which is faster on a single local GPU, where parallel requests '
|
|
184
|
+
+ 'share one device',
|
|
185
|
+
values: RESEARCH_CONCURRENCY_OPTIONS.map(o => o.label),
|
|
186
|
+
format: cfg => RESEARCH_CONCURRENCY_OPTIONS.find(o => o.value === cfg.researchConcurrency).label,
|
|
187
|
+
apply: (cfg, chosen) => {
|
|
188
|
+
const opt = RESEARCH_CONCURRENCY_OPTIONS.find(o => o.label === chosen);
|
|
189
|
+
if (opt)
|
|
190
|
+
cfg.researchConcurrency = opt.value;
|
|
191
|
+
}
|
|
192
|
+
},
|
|
180
193
|
booleanItem('research', 'researchCache', 'research cache', 'Remember docs and web pages for the length of one run, so later tasks reuse what '
|
|
181
194
|
+ 'the first one already fetched instead of downloading it again. Only external '
|
|
182
195
|
+ 'sources, only successful fetches, and it is dropped when the run ends'),
|
|
@@ -259,7 +272,7 @@ export const ITEMS = [
|
|
|
259
272
|
id: 'debugLogs',
|
|
260
273
|
section: 'logging',
|
|
261
274
|
label: 'debug logs',
|
|
262
|
-
description: 'How much of a run gets written to
|
|
275
|
+
description: 'How much of a run gets written to the run log dir. "events" keeps the '
|
|
263
276
|
+ 'decisions and the guard actions — what a checking step changed, why something '
|
|
264
277
|
+ 'failed — a few lines per task. "full" adds everything the model said and every '
|
|
265
278
|
+ 'command it ran, which is most of the size and only useful while you are digging '
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* content-tokens — the distinctive words of a phrase, and the one tokenizer that
|
|
3
|
+
* decides them.
|
|
4
|
+
*
|
|
5
|
+
* Two consumers ask the same question of a string and must get the same answer:
|
|
6
|
+
* the coverage guard, which grounds requirement/title ownership in shared
|
|
7
|
+
* distinctive nouns, and the research cache, whose key is the sorted token set of
|
|
8
|
+
* a query so two phrasings of one question share one digest. A second tokenizer
|
|
9
|
+
* would be a second answer.
|
|
10
|
+
*/
|
|
11
|
+
export declare const CONTENT_STOPWORDS: Set<string>;
|
|
12
|
+
/** Distinctive content tokens of a phrase: lowercased alphanumeric words ≥3 chars,
|
|
13
|
+
* minus the ubiquitous stopwords. `--json` → `json`, `dead-letter` → `dead`,`letter`.
|
|
14
|
+
* A single trailing `s` is stripped (len ≥4) so `scan`/`scans`, `file`/`files`,
|
|
15
|
+
* `serialize`/`serializes` match — plain plural/3rd-person, no full stemmer. */
|
|
16
|
+
export declare function contentTokens(s: string): Set<string>;
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* content-tokens — the distinctive words of a phrase, and the one tokenizer that
|
|
3
|
+
* decides them.
|
|
4
|
+
*
|
|
5
|
+
* Two consumers ask the same question of a string and must get the same answer:
|
|
6
|
+
* the coverage guard, which grounds requirement/title ownership in shared
|
|
7
|
+
* distinctive nouns, and the research cache, whose key is the sorted token set of
|
|
8
|
+
* a query so two phrasings of one question share one digest. A second tokenizer
|
|
9
|
+
* would be a second answer.
|
|
10
|
+
*/
|
|
11
|
+
// Ubiquitous words that carry no coverage signal: they appear across most task
|
|
12
|
+
// titles and requirement quotes, so overlap on them would falsely connect a
|
|
13
|
+
// requirement to any plan. Stopped so grounding keys on the DISTINCTIVE nouns
|
|
14
|
+
// (json, dead-letter, serialize, symlink…) that actually name a deliverable.
|
|
15
|
+
//
|
|
16
|
+
// Confirmed: titles built only from these words own NOTHING, while titles naming
|
|
17
|
+
// `JSON output` and `dead-letter queue` own the matching requirements.
|
|
18
|
+
//
|
|
19
|
+
// English function words + generic task verbs + generic project nouns — all
|
|
20
|
+
// domain-agnostic.
|
|
21
|
+
export const CONTENT_STOPWORDS = new Set([
|
|
22
|
+
// function words
|
|
23
|
+
'the',
|
|
24
|
+
'a',
|
|
25
|
+
'an',
|
|
26
|
+
'and',
|
|
27
|
+
'or',
|
|
28
|
+
'of',
|
|
29
|
+
'to',
|
|
30
|
+
'in',
|
|
31
|
+
'on',
|
|
32
|
+
'for',
|
|
33
|
+
'with',
|
|
34
|
+
'by',
|
|
35
|
+
'at',
|
|
36
|
+
'as',
|
|
37
|
+
'is',
|
|
38
|
+
'are',
|
|
39
|
+
'be',
|
|
40
|
+
'it',
|
|
41
|
+
'its',
|
|
42
|
+
'that',
|
|
43
|
+
'this',
|
|
44
|
+
'from',
|
|
45
|
+
'into',
|
|
46
|
+
'out',
|
|
47
|
+
'up',
|
|
48
|
+
'per',
|
|
49
|
+
'via',
|
|
50
|
+
'not',
|
|
51
|
+
'no',
|
|
52
|
+
'but',
|
|
53
|
+
'if',
|
|
54
|
+
'then',
|
|
55
|
+
'than',
|
|
56
|
+
'so',
|
|
57
|
+
'such',
|
|
58
|
+
'each',
|
|
59
|
+
'any',
|
|
60
|
+
'all',
|
|
61
|
+
'every',
|
|
62
|
+
'when',
|
|
63
|
+
'where',
|
|
64
|
+
'must',
|
|
65
|
+
'should',
|
|
66
|
+
'shall',
|
|
67
|
+
'may',
|
|
68
|
+
'can',
|
|
69
|
+
'will',
|
|
70
|
+
'end',
|
|
71
|
+
'new',
|
|
72
|
+
// generic task verbs
|
|
73
|
+
'add',
|
|
74
|
+
'implement',
|
|
75
|
+
'create',
|
|
76
|
+
'build',
|
|
77
|
+
'scaffold',
|
|
78
|
+
'setup',
|
|
79
|
+
'set',
|
|
80
|
+
'support',
|
|
81
|
+
'handle',
|
|
82
|
+
'apply',
|
|
83
|
+
'use',
|
|
84
|
+
'used',
|
|
85
|
+
'using',
|
|
86
|
+
'make',
|
|
87
|
+
'makes',
|
|
88
|
+
'made',
|
|
89
|
+
'enable',
|
|
90
|
+
'provide',
|
|
91
|
+
'ensure',
|
|
92
|
+
'allow',
|
|
93
|
+
'run',
|
|
94
|
+
'runs',
|
|
95
|
+
'get',
|
|
96
|
+
'gets',
|
|
97
|
+
'define',
|
|
98
|
+
'configure',
|
|
99
|
+
'init',
|
|
100
|
+
'update',
|
|
101
|
+
'manage',
|
|
102
|
+
// generic project nouns
|
|
103
|
+
'cli',
|
|
104
|
+
'tool',
|
|
105
|
+
'app',
|
|
106
|
+
'application',
|
|
107
|
+
'project',
|
|
108
|
+
'feature',
|
|
109
|
+
'task',
|
|
110
|
+
'tasks',
|
|
111
|
+
'user',
|
|
112
|
+
'users',
|
|
113
|
+
'mode',
|
|
114
|
+
'flag',
|
|
115
|
+
'flags',
|
|
116
|
+
'option',
|
|
117
|
+
'options',
|
|
118
|
+
'system',
|
|
119
|
+
'code',
|
|
120
|
+
'thing',
|
|
121
|
+
'things',
|
|
122
|
+
'work'
|
|
123
|
+
]);
|
|
124
|
+
/** Distinctive content tokens of a phrase: lowercased alphanumeric words ≥3 chars,
|
|
125
|
+
* minus the ubiquitous stopwords. `--json` → `json`, `dead-letter` → `dead`,`letter`.
|
|
126
|
+
* A single trailing `s` is stripped (len ≥4) so `scan`/`scans`, `file`/`files`,
|
|
127
|
+
* `serialize`/`serializes` match — plain plural/3rd-person, no full stemmer. */
|
|
128
|
+
export function contentTokens(s) {
|
|
129
|
+
const out = new Set();
|
|
130
|
+
for (const raw of s.toLowerCase().split(/[^a-z0-9]+/)) {
|
|
131
|
+
if (raw.length < 3 || CONTENT_STOPWORDS.has(raw))
|
|
132
|
+
continue;
|
|
133
|
+
const w = raw.length >= 4 && raw.endsWith('s') && !raw.endsWith('ss') ? raw.slice(0, -1) : raw;
|
|
134
|
+
out.add(w);
|
|
135
|
+
}
|
|
136
|
+
return out;
|
|
137
|
+
}
|
|
@@ -47,8 +47,22 @@ import { type CommandRunner } from './command-run.js';
|
|
|
47
47
|
* defect is recorded here and a scoped repair task is queued into the plan
|
|
48
48
|
* (root-cause-repair.ts). Recording without queuing would let the same root
|
|
49
49
|
* cause be re-recorded by each task it fails, with nothing ever fixing it.
|
|
50
|
+
* - 'spec-contradiction' — the gate's decision table proved the failing criterion
|
|
51
|
+
* can only be met by editing a path the SAME spec freezes, on any fail class
|
|
52
|
+
* (gate-resolution.ts). Supersedes 'frozen-blocked', which said the same thing
|
|
53
|
+
* about repo-health alone; that origin stays registered because debts already
|
|
54
|
+
* on disk carry it.
|
|
55
|
+
* - 'dismissed' — a human was shown the verify-FAIL picker and walked away. The
|
|
56
|
+
* defect is no less real for not being answered, and this is the class that
|
|
57
|
+
* used to leave no ledger entry at all.
|
|
58
|
+
* - 'inherited-health' — the repo entered the task ALREADY failing its own static
|
|
59
|
+
* check, so the FAIL is not this task's regression (see the differential health
|
|
60
|
+
* delta). Recorded rather than blamed: the task proceeds, the pre-existing
|
|
61
|
+
* breakage is still surfaced at run end.
|
|
62
|
+
* - 'abandoned' — the loop gave up on the task past its attempt budget without a
|
|
63
|
+
* verified artifact. Nothing else records what was left unfinished.
|
|
50
64
|
*/
|
|
51
|
-
export type DebtOrigin = 'accepted' | 'enforce-revert' | 'enforce-kept' | 'frozen-blocked' | 'cross-task-deletion' | 'yolo-accepted' | 'final-gate' | 'root-cause';
|
|
65
|
+
export type DebtOrigin = 'accepted' | 'enforce-revert' | 'enforce-kept' | 'frozen-blocked' | 'spec-contradiction' | 'dismissed' | 'inherited-health' | 'abandoned' | 'cross-task-deletion' | 'yolo-accepted' | 'final-gate' | 'root-cause';
|
|
52
66
|
/** One recorded defect: the task, why its VERIFY failed, and how it was recorded. */
|
|
53
67
|
export interface AcceptDebt {
|
|
54
68
|
taskId: string;
|
package/dist/task/accept-debt.js
CHANGED
|
@@ -66,6 +66,10 @@ const DEBT_LABELS = {
|
|
|
66
66
|
'enforce-revert': 'enforce re-verify FAILED then the edits were reverted (defect indicts the ORIGINAL work, still shipped)',
|
|
67
67
|
'enforce-kept': 'enforce re-verify FAILED on a check the enforce diff cannot reach — the guideline edits were KEPT (reverting them could not fix it) and the defect indicts the ORIGINAL work, still shipped',
|
|
68
68
|
'frozen-blocked': 'repo health blocked by a spec-frozen path (cross-task contradiction — no task may perform the fixing edit)',
|
|
69
|
+
'spec-contradiction': "the failing criterion can only be met by editing a path this task's own spec freezes (no re-run under the same freeze converges)",
|
|
70
|
+
dismissed: 'the verify-FAIL picker was DISMISSED — a human saw the defect and answered nothing',
|
|
71
|
+
'inherited-health': "the repo was ALREADY failing its own static check before this task ran (pre-existing, not this task's regression)",
|
|
72
|
+
abandoned: 'the task was abandoned past its attempt budget with no verified artifact',
|
|
69
73
|
'cross-task-deletion': "a sibling task's committed deliverable was DELETED by this task's work and the deletion was accepted (still missing from the tree)",
|
|
70
74
|
'yolo-accepted': 'auto-ACCEPTED by YOLO mode despite verify-FAIL (unattended — no human weighed this)',
|
|
71
75
|
'final-gate': 'final-gate check DEMOTED to UNOBSERVED (identical failure across two tree-changing fix attempts — unfalsifiable in that environment, never proven passing)',
|
|
@@ -51,6 +51,7 @@
|
|
|
51
51
|
import { existsSync, readdirSync, readFileSync } from 'node:fs';
|
|
52
52
|
import * as path from 'node:path';
|
|
53
53
|
import { shippedSources, stripCommentLines, SOURCE_HTML_RE, SOURCE_JS_RE } from './shipped-source.js';
|
|
54
|
+
import { blocksOf, parseSpecDoc } from './spec-doc.js';
|
|
54
55
|
export function emptyProducers() {
|
|
55
56
|
return {
|
|
56
57
|
files: new Set(),
|
|
@@ -940,10 +941,15 @@ const PROSE_CONSUME_RE = /\b(?:serves?|serving|served|fallback|reads?|loads?|ren
|
|
|
940
941
|
* `index.html`, `data/seed.json` and `assets/logo.svg` all extract.
|
|
941
942
|
* Code-construct refs are not subject to this list. */
|
|
942
943
|
const PROSE_ASSET_EXT_RE = /\.(?:html?|css|m?js|cjs|json|svg|png|jpe?g|gif|webp|ico|woff2?|ttf|otf|wasm|webmanifest|xml|csv|sql|ya?ml|toml|pdf|mp[34]|db|sqlite)$/i;
|
|
944
|
+
/** The block kinds that can carry consuming PROSE. A fence is code — its
|
|
945
|
+
* `serve`/`read` calls are the code channel's to resolve, not prose's — and a
|
|
946
|
+
* blockquote is quoting something the spec is not itself saying. */
|
|
947
|
+
const PROSE_KINDS = new Set(['para', 'list-item', 'table-row']);
|
|
943
948
|
/** Backticked, asset-extension, path-shaped tokens on consuming-verb lines. */
|
|
944
949
|
export function extractSpecProseRefs(spec) {
|
|
945
950
|
const out = [];
|
|
946
|
-
|
|
951
|
+
const prose = blocksOf(parseSpecDoc(spec)).filter(b => PROSE_KINDS.has(b.kind));
|
|
952
|
+
for (const line of prose.flatMap(b => b.text.split('\n'))) {
|
|
947
953
|
if (!PROSE_CONSUME_RE.test(line))
|
|
948
954
|
continue;
|
|
949
955
|
const tick = /`([^`\n]+)`/g;
|
package/dist/task/auto-io.d.ts
CHANGED
|
@@ -1,10 +1,27 @@
|
|
|
1
1
|
import type { TaskState } from './task-types.js';
|
|
2
2
|
import type { AutoResumeCandidate } from './resume-gap.js';
|
|
3
|
+
import { type RunEndKind } from './run-end.js';
|
|
3
4
|
export interface TaskEntry {
|
|
5
|
+
/** Position in the list — shifts when a repair step is spliced in. */
|
|
4
6
|
index: number;
|
|
7
|
+
/**
|
|
8
|
+
* The entry's stable identity, allocated at plan time and never reused.
|
|
9
|
+
* `index` moves and `title` is prose a later pass may rewrite; this is what
|
|
10
|
+
* the owned-requirements ledger joins on. Absent on an AUTO file written
|
|
11
|
+
* before the key existed, where the title join is still the only one there is.
|
|
12
|
+
*/
|
|
13
|
+
key?: string;
|
|
5
14
|
title: string;
|
|
6
15
|
done: boolean;
|
|
7
16
|
producedId?: string;
|
|
17
|
+
/** How many implementation attempts this entry has had. */
|
|
18
|
+
attempts?: number;
|
|
19
|
+
/**
|
|
20
|
+
* How the last attempt ENDED, recorded only when it ended abnormally. A
|
|
21
|
+
* resume otherwise cannot tell an entry the user cancelled from one that
|
|
22
|
+
* faulted, because both leave the same unchecked, stamped line.
|
|
23
|
+
*/
|
|
24
|
+
lastEnd?: RunEndKind;
|
|
8
25
|
}
|
|
9
26
|
export declare function allocateAutoId(cwd: string): Promise<string>;
|
|
10
27
|
/**
|
|
@@ -23,20 +40,40 @@ export declare function allocateAutoId(cwd: string): Promise<string>;
|
|
|
23
40
|
*/
|
|
24
41
|
export declare function parseDecomposeList(raw: string): string[];
|
|
25
42
|
/** Parsed DECOMPOSE_COVERAGE_PROMPT verdict. */
|
|
26
|
-
export
|
|
27
|
-
kind: 'complete'
|
|
43
|
+
export type CoverageVerdict = {
|
|
44
|
+
kind: 'complete';
|
|
45
|
+
missing: string[];
|
|
46
|
+
} | {
|
|
47
|
+
kind: 'incomplete';
|
|
28
48
|
missing: string[];
|
|
29
49
|
}
|
|
50
|
+
/** The judge DID rule INCOMPLETE but named nothing to reprompt with. */
|
|
51
|
+
| {
|
|
52
|
+
kind: 'unparseable';
|
|
53
|
+
};
|
|
54
|
+
/** What an `unparseable` verdict contributes to the plan's missing-area list, so
|
|
55
|
+
* the judge's own INCOMPLETE cannot be shipped as COMPLETE. */
|
|
56
|
+
export declare const UNNAMED_COVERAGE_GAP = "the coverage judge ruled INCOMPLETE without naming the uncovered area";
|
|
30
57
|
/**
|
|
31
58
|
* Parse the coverage-triage child's verdict. Returns null when no COVERAGE tag is
|
|
32
|
-
* present (the model wrote prose), and the caller reads a null verdict as
|
|
33
|
-
* empty missing-list, so a malformed judgment can never block planning.
|
|
59
|
+
* present at all (the model wrote prose), and the caller reads a null verdict as
|
|
60
|
+
* an empty missing-list, so a malformed judgment can never block planning.
|
|
34
61
|
*
|
|
35
|
-
* `COVERAGE: INCOMPLETE` with no MISSING lines
|
|
36
|
-
*
|
|
37
|
-
*
|
|
62
|
+
* `COVERAGE: INCOMPLETE` with no MISSING lines is NOT null, and that distinction
|
|
63
|
+
* is the whole point: prose is no verdict, while this is a verdict of INCOMPLETE
|
|
64
|
+
* that happens to name nothing. Collapsing the two shipped a plan the judge had
|
|
65
|
+
* just ruled incomplete, logged as COMPLETE.
|
|
38
66
|
*/
|
|
39
67
|
export declare function parseCoverageVerdict(raw: string): CoverageVerdict | null;
|
|
68
|
+
/** The plan key for the `index`th entry of a freshly planned list. */
|
|
69
|
+
export declare function planKeyAt(index: number): string;
|
|
70
|
+
/**
|
|
71
|
+
* The lowest key number no entry in `entries` holds. Keys are allocated 1:1 with
|
|
72
|
+
* plan entries, so an unkeyed legacy list has implicitly spent its first N
|
|
73
|
+
* numbers — counting it in keeps a spliced repair step from minting a key an
|
|
74
|
+
* eventual migration would hand to an existing entry.
|
|
75
|
+
*/
|
|
76
|
+
export declare function nextPlanKey(entries: readonly TaskEntry[]): string;
|
|
40
77
|
/** Parse the "## tasks" checkbox list. */
|
|
41
78
|
export declare function parseTaskList(body: string): TaskEntry[];
|
|
42
79
|
/** Build the initial AUTO-file body. `coverage` is the requirement-level
|
|
@@ -51,8 +88,23 @@ export declare function checkOffTask(cwd: string, id: string, index: number, pro
|
|
|
51
88
|
* the inner task is allocated. This links the AUTO entry to its in-progress
|
|
52
89
|
* inner task so /task-auto-resume can continue it from its saved phase instead
|
|
53
90
|
* of starting a brand-new task — matching how /task-resume behaves.
|
|
91
|
+
*
|
|
92
|
+
* The first stamp is also where the attempt counter is minted: an entry under way
|
|
93
|
+
* has had one attempt.
|
|
54
94
|
*/
|
|
55
95
|
export declare function stampTaskInProgress(cwd: string, id: string, index: number, producedId: string, title: string): Promise<void>;
|
|
96
|
+
/**
|
|
97
|
+
* Count one more attempt on the `index`th entry and return the new total.
|
|
98
|
+
*
|
|
99
|
+
* Called for a fresh start AND for a resume, because both spend a run on the
|
|
100
|
+
* entry: a task that crashes in refine is re-entered from scratch every time, and
|
|
101
|
+
* a counter that only saw fresh starts would read 1 forever while the loop re-ran
|
|
102
|
+
* it without end. The previous ending is cleared here — it describes the attempt
|
|
103
|
+
* that is now over.
|
|
104
|
+
*/
|
|
105
|
+
export declare function beginTaskAttempt(cwd: string, id: string, index: number): Promise<number>;
|
|
106
|
+
/** Record how the `index`th entry's attempt ended. */
|
|
107
|
+
export declare function recordTaskEnd(cwd: string, id: string, index: number, lastEnd: RunEndKind): Promise<void>;
|
|
56
108
|
/**
|
|
57
109
|
* Insert a NEW unchecked entry directly after the `afterIndex`th checkbox — the
|
|
58
110
|
* mid-run plan mutation the root-cause repair channel needs: a repair task must
|