@mjasnikovs/pi-task 0.38.32 → 0.39.1
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 +2 -2
- package/dist/config/group-args.d.ts +24 -9
- package/dist/config/group-args.js +38 -28
- package/dist/config/option-picker.d.ts +39 -11
- package/dist/config/option-picker.js +52 -12
- package/dist/config/reasoning.d.ts +10 -7
- package/dist/config/reasoning.js +19 -32
- package/dist/config/register.d.ts +66 -41
- package/dist/config/register.js +201 -162
- package/dist/shared/child-process.d.ts +34 -32
- package/dist/shared/child-process.js +44 -58
- package/dist/shared/command-watchdog.d.ts +12 -4
- package/dist/shared/command-watchdog.js +6 -7
- package/dist/shared/connection-error.d.ts +7 -0
- package/dist/shared/connection-error.js +65 -0
- package/dist/shared/model-endpoint.d.ts +12 -24
- package/dist/shared/model-endpoint.js +32 -82
- package/dist/shared/model-resolve.d.ts +105 -0
- package/dist/shared/model-resolve.js +97 -0
- package/dist/shared/reasoning-capability.d.ts +20 -0
- package/dist/shared/reasoning-capability.js +32 -1
- package/dist/shared/stall-probe.d.ts +51 -0
- package/dist/shared/stall-probe.js +79 -0
- package/dist/task/child-runner.d.ts +76 -278
- package/dist/task/child-runner.js +186 -722
- package/dist/task/context-usage.js +2 -7
- package/dist/task/failure-classifier.js +53 -81
- package/dist/task/gate-child.js +1 -1
- package/dist/task/impl-widget.d.ts +2 -0
- package/dist/task/impl-widget.js +4 -0
- package/dist/task/implementation-hold.d.ts +11 -0
- package/dist/task/implementation-hold.js +20 -0
- package/dist/task/implementation-scope.d.ts +24 -0
- package/dist/task/implementation-scope.js +34 -0
- package/dist/task/loop-detector.d.ts +13 -5
- package/dist/task/loop-detector.js +11 -5
- package/dist/task/model-hold-stash.js +4 -14
- package/dist/task/orchestrator.d.ts +1 -8
- package/dist/task/orchestrator.js +11 -34
- package/dist/task/phases.js +2 -2
- package/dist/task/stall-detector.d.ts +1 -1
- package/dist/task/stall-detector.js +1 -1
- package/dist/workers/model-warning.d.ts +4 -16
- package/dist/workers/model-warning.js +14 -70
- package/dist/workers/pi-worker-core.d.ts +65 -20
- package/dist/workers/pi-worker-core.js +109 -50
- package/dist/workers/reasoning-warning.js +2 -24
- package/dist/workers/worker-failure.d.ts +2 -0
- package/dist/workers/worker-failure.js +2 -1
- package/dist/workers/worker-kill.d.ts +30 -11
- package/dist/workers/worker-kill.js +68 -20
- package/dist/workers/worker-profiles.d.ts +20 -0
- package/dist/workers/worker-profiles.js +22 -9
- package/package.json +1 -1
|
@@ -1,204 +1,66 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* The phase children's adapter over the one attempt loop.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
4
|
+
* `runWorker` (workers/pi-worker-core.ts) owns every guard a model child runs
|
|
5
|
+
* under and every restart it may be granted; the `phase` row of WORKER_PROFILES
|
|
6
|
+
* says which. What is left here is what a PHASE child is that a research worker
|
|
7
|
+
* is not: it is named, its name picks its group, its failure is THROWN as a
|
|
8
|
+
* typed error the pipeline switches on, and its loop kills leave a trail in the
|
|
9
|
+
* task file. Nothing here re-decides how a child may die.
|
|
10
|
+
*/
|
|
11
|
+
import { type RunWorkerInput, type RunWorkerResult } from '../workers/pi-worker-core.js';
|
|
12
|
+
import { type WorkerFailure } from '../workers/worker-failure.js';
|
|
13
|
+
import type { SpawnFn, ContextSnapshot, LoopHit } from '../shared/child-process.js';
|
|
11
14
|
import type { DebugLine } from './debug-log.js';
|
|
12
|
-
import type { RunWorkerInput, RunWorkerResult } from '../workers/pi-worker-core.js';
|
|
13
15
|
import type { docsRaw, docsFocused } from '../workers/docs-core.js';
|
|
14
16
|
import type { fetchRaw, fetchFocused } from '../workers/fetch-core.js';
|
|
15
17
|
import type { npmVersionLookup } from '../workers/npm-version.js';
|
|
16
18
|
import type { SearchCoreInput, SearchCoreResult } from '../workers/search-core.js';
|
|
19
|
+
export declare const USER_CANCELLED = "__user_cancelled__";
|
|
17
20
|
/**
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
* WHY OFF, AND NOT A NUMBER. A wall clock on a model child measures the
|
|
21
|
-
* MODEL'S SPEED, not its health. The same planning child that answers well in
|
|
22
|
-
* seconds on one backend takes many minutes on another, or on the same backend
|
|
23
|
-
* with thinking turned on — so any cap generous enough to be safe is too loose
|
|
24
|
-
* to catch anything, and any cap tight enough to catch a runaway kills healthy
|
|
25
|
-
* work. Assume a model that emits one token per second and the number has no
|
|
26
|
-
* defensible value at all.
|
|
27
|
-
*
|
|
28
|
-
* The runaway it was there to catch — a child forward-paging through its whole
|
|
29
|
-
* context window, past the loop detector, never going to return — is caught by
|
|
30
|
-
* StallDetector (stall-detector.ts) instead. That bounds NON-PROGRESS and
|
|
31
|
-
* CONTEXT CHURN, both properties of the pathology itself, so neither has to be
|
|
32
|
-
* re-tuned for a slower model or a bigger repo.
|
|
33
|
-
*
|
|
34
|
-
* The value and the plumbing stay for a caller that genuinely wants a hard stop
|
|
35
|
-
* (tests inject a short one), but nothing sets it in production. Pass
|
|
36
|
-
* `timeoutMs` explicitly to arm it.
|
|
37
|
-
*/
|
|
38
|
-
export declare const PHASE_CHILD_TIMEOUT_MS = 0;
|
|
39
|
-
/**
|
|
40
|
-
* Restart hint after a phase child burns its whole wall-clock budget. It
|
|
41
|
-
* diagnoses over-exploration, which is what the cap actually catches — the same
|
|
42
|
-
* job WORKER_TIMEOUT_HINT does for research workers.
|
|
43
|
-
*/
|
|
44
|
-
export declare const PHASE_TIMEOUT_HINT: string;
|
|
45
|
-
/** Thrown when a phase child spends its whole restart budget hitting the cap. */
|
|
46
|
-
export declare class PhaseTimeoutError extends Error {
|
|
47
|
-
readonly childName: string;
|
|
48
|
-
readonly budgetMs: number;
|
|
49
|
-
readonly attempts: number;
|
|
50
|
-
constructor(childName: string, budgetMs: number, attempts: number);
|
|
51
|
-
}
|
|
52
|
-
/**
|
|
53
|
-
* The terminal error for a guard kill, or null when the child was not killed.
|
|
21
|
+
* Why a phase child did not answer.
|
|
54
22
|
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
23
|
+
* The worker roster's kills, plus the two outcomes `worker-failure.ts`
|
|
24
|
+
* deliberately leaves to the consumer: a reported model error, and an empty
|
|
25
|
+
* answer. For a phase child both ARE failures, so they join the union here.
|
|
26
|
+
* `loop` carries how many strikes it took, which is what the notice reports.
|
|
27
|
+
*/
|
|
28
|
+
export type ChildFailure = Exclude<WorkerFailure, {
|
|
29
|
+
kind: 'loop';
|
|
30
|
+
}> | {
|
|
31
|
+
kind: 'loop';
|
|
32
|
+
hit: LoopHit;
|
|
33
|
+
strikes: number;
|
|
34
|
+
} | {
|
|
35
|
+
kind: 'model-error';
|
|
36
|
+
cause: string;
|
|
37
|
+
} | {
|
|
38
|
+
kind: 'empty-answer';
|
|
39
|
+
};
|
|
62
40
|
/**
|
|
63
|
-
*
|
|
41
|
+
* ONE error class for every way a phase child fails, carrying the cause as data.
|
|
64
42
|
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
43
|
+
* Six classes plus a string sentinel used to say the same nine things, and
|
|
44
|
+
* `classifyFailure` rebuilt the ladder by `instanceof` and then fell through
|
|
45
|
+
* to sniffing the message — the tell that the vocabulary was leaking. A catch
|
|
46
|
+
* site asks `isFatalChildCause`; the notice switches on `failure.kind`.
|
|
69
47
|
*/
|
|
70
|
-
export declare class
|
|
71
|
-
readonly
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
* A phase child spent every attempt on a command that never returned. Its own
|
|
76
|
-
* class because the fix is in the SPEC, not the model's exploration: a VERIFY
|
|
77
|
-
* block naming an unbounded `dev` command re-hangs every attempt.
|
|
78
|
-
*/
|
|
79
|
-
export declare class CommandTimeoutError extends Error {
|
|
80
|
-
readonly childName: string;
|
|
81
|
-
readonly kill: CommandKill;
|
|
82
|
-
constructor(childName: string, kill: CommandKill);
|
|
48
|
+
export declare class ChildFailureError extends Error {
|
|
49
|
+
readonly phase: string;
|
|
50
|
+
readonly failure: ChildFailure;
|
|
51
|
+
readonly stderr: string;
|
|
52
|
+
constructor(phase: string, failure: ChildFailure, stderr?: string);
|
|
83
53
|
}
|
|
84
54
|
/**
|
|
85
55
|
* Causes a best-effort `catch` must NOT absorb.
|
|
86
56
|
*
|
|
87
57
|
* A phase child that merely answered badly should degrade — that is what those
|
|
88
|
-
* catches are for.
|
|
89
|
-
* and swallowing them ships a half-built spec while
|
|
90
|
-
* against the same dead
|
|
91
|
-
*
|
|
92
|
-
* unreachable.
|
|
58
|
+
* catches are for. A dead backend and a user cancel are different in kind: the
|
|
59
|
+
* run is over either way, and swallowing them ships a half-built spec while
|
|
60
|
+
* every later phase dies against the same dead server, or turns an ESC into
|
|
61
|
+
* silent progress. Which kills are fatal is the roster's column, not a list here.
|
|
93
62
|
*/
|
|
94
63
|
export declare function isFatalChildCause(e: unknown): boolean;
|
|
95
|
-
/**
|
|
96
|
-
* Retry budget is three attempts at 500ms/1s/2s — three requests over 3.5s, which
|
|
97
|
-
* is not a storm even against a throttle. pi's own ladder is three at 2s/4s/8s.
|
|
98
|
-
*/
|
|
99
|
-
export declare function isConnectionError(cause: string): boolean;
|
|
100
|
-
/** Exponential backoff before a connection-error retry: 500ms, 1s, 2s, …, so a
|
|
101
|
-
* brief saturation window can drain before we re-issue the request. */
|
|
102
|
-
export declare function connectionRetryBackoffMs(attempt: number): number;
|
|
103
|
-
export interface PhaseRunResult {
|
|
104
|
-
text: string;
|
|
105
|
-
exitCode: number;
|
|
106
|
-
stderr: string;
|
|
107
|
-
loopHit?: LoopHit;
|
|
108
|
-
/** Set when the assistant text contains an unexecuted, leaked tool call. */
|
|
109
|
-
leakedToolCall?: string;
|
|
110
|
-
/** Set when the child's final turn failed with stopReason "error" (model/provider failure). */
|
|
111
|
-
modelError?: string;
|
|
112
|
-
/**
|
|
113
|
-
* Set when the per-command watchdog killed the child: one tool call outran
|
|
114
|
-
* `requestTimeoutMs`. RESTARTABLE (worker-kill.ts) — a hung command is a
|
|
115
|
-
* mistake the next attempt can be told not to repeat.
|
|
116
|
-
*/
|
|
117
|
-
commandKill?: CommandKill;
|
|
118
|
-
/**
|
|
119
|
-
* Set when the dead-backend probe killed the child: no output for the stall
|
|
120
|
-
* window AND the model endpoint unreachable. NOT restartable (worker-kill.ts):
|
|
121
|
-
* re-spawning against a backend that is down buys nothing.
|
|
122
|
-
*/
|
|
123
|
-
stalled?: boolean;
|
|
124
|
-
}
|
|
125
|
-
export declare function childArgs(tools: string, extensions?: readonly string[],
|
|
126
|
-
/**
|
|
127
|
-
* This child's group fragment: `--model` then `--thinking`, either half
|
|
128
|
-
* possibly absent. Resolved by the CALLER, never here — both are properties
|
|
129
|
-
* of the child's ROLE, and this function is handed tools and extensions, not
|
|
130
|
-
* a name. Omitted ⇒ byte-identical argv to the version before group profiles.
|
|
131
|
-
*
|
|
132
|
-
* ONE field rather than a `model` beside a `thinking`, because nothing may
|
|
133
|
-
* compose the two halves by hand: `groupChildArgs` is the only producer, so a
|
|
134
|
-
* doubled `--thinking` is unreachable rather than merely unlikely.
|
|
135
|
-
*/
|
|
136
|
-
groupArgs?: readonly string[]): string[];
|
|
137
|
-
export declare const USER_CANCELLED = "__user_cancelled__";
|
|
138
|
-
/**
|
|
139
|
-
* Run a child pi process with JSON event-stream output, loop detection, and
|
|
140
|
-
* context-usage tracking. This is the typed convenience wrapper used by
|
|
141
|
-
* phase-level code.
|
|
142
|
-
*/
|
|
143
|
-
/**
|
|
144
|
-
* One child-pi invocation, as a value.
|
|
145
|
-
*
|
|
146
|
-
* WHY A RECORD RATHER THAN POSITIONALS. With this many optional parameters of
|
|
147
|
-
* the same type, a caller reaching a late one must write bare `undefined`s to get
|
|
148
|
-
* there, and one that miscounts silently lands the wrong value in the wrong slot.
|
|
149
|
-
* The failure that shape produces here is a child spawned with the RAW signal
|
|
150
|
-
* instead of the wall-clocked one, escaping a guard its siblings run under, with
|
|
151
|
-
* nothing to catch it. Named fields make adjacent optionals of the same type
|
|
152
|
-
* impossible to swap without a type error.
|
|
153
|
-
*/
|
|
154
|
-
export interface ChildRun {
|
|
155
|
-
cwd: string;
|
|
156
|
-
/** `''` means `--no-tools`. See childArgs. */
|
|
157
|
-
tools: string;
|
|
158
|
-
prompt: string;
|
|
159
|
-
signal: AbortSignal;
|
|
160
|
-
onLine?: (line: string) => void;
|
|
161
|
-
onContextUsage?: (snapshot: ContextSnapshot) => void;
|
|
162
|
-
onToolCall?: (call: ToolCall) => LoopHit | null;
|
|
163
|
-
spawn?: SpawnFn;
|
|
164
|
-
/** Internal `-e` extension paths for in-run guards (see childArgs). */
|
|
165
|
-
extensions?: readonly string[];
|
|
166
|
-
/**
|
|
167
|
-
* Every finished tool call's result text. The StallDetector's churn rule
|
|
168
|
-
* needs the size of what actually entered the child's context, which the
|
|
169
|
-
* CALL alone does not carry (task/stall-detector.ts).
|
|
170
|
-
*/
|
|
171
|
-
onToolResult?: (text: string, isError: boolean) => void;
|
|
172
|
-
/**
|
|
173
|
-
* The child's context window in tokens. Nothing in pi's `--mode json` stream
|
|
174
|
-
* reports one — a real capture carries token counts and a model id, but no key
|
|
175
|
-
* naming a window — so the parent hands its own down. Children carry no `-m`
|
|
176
|
-
* (CHILD_BASE_ARGS) and resolve the same default model, which is what makes
|
|
177
|
-
* the parent's window the honest value. 0 or omitted = unknown.
|
|
178
|
-
*/
|
|
179
|
-
contextWindow?: number;
|
|
180
|
-
/**
|
|
181
|
-
* The resolved argv fragment for this child's group — `--model` then
|
|
182
|
-
* `--thinking` — or `[]`/omitted to inherit both defaults as before.
|
|
183
|
-
*/
|
|
184
|
-
groupArgs?: readonly string[];
|
|
185
|
-
/**
|
|
186
|
-
* This attempt's per-command ceiling, already halved for prior hangs by the
|
|
187
|
-
* caller's strike loop. Omitted -> the `phase` row's full configured ceiling,
|
|
188
|
-
* which is the right value for a single-attempt caller.
|
|
189
|
-
*/
|
|
190
|
-
commandCeilingMs?: number;
|
|
191
|
-
}
|
|
192
|
-
/**
|
|
193
|
-
* The `phase` row of WORKER_PROFILES, resolved with this machine's config.
|
|
194
|
-
*
|
|
195
|
-
* Read here rather than at module load so a /task-config change reaches the next
|
|
196
|
-
* child, the same contract childBaseArgs already keeps. Both spawn paths in this
|
|
197
|
-
* file go through it, so the degraded final attempt cannot drift from the ordinary
|
|
198
|
-
* one — the mislabel class runDegradedFinalAttempt's own comment warns about.
|
|
199
|
-
*/
|
|
200
|
-
export declare function phasePolicy(): WorkerGuardPolicy;
|
|
201
|
-
export declare function runChild({ cwd, tools, prompt, signal, onLine, onContextUsage, onToolCall, spawn: spawnFn, extensions, onToolResult, contextWindow, groupArgs, commandCeilingMs }: ChildRun): Promise<PhaseRunResult>;
|
|
202
64
|
export interface PhaseDeps {
|
|
203
65
|
cwd: string;
|
|
204
66
|
taskId: string;
|
|
@@ -206,14 +68,13 @@ export interface PhaseDeps {
|
|
|
206
68
|
onChildOutput?: (line: string) => void;
|
|
207
69
|
onContextUsage?: (snapshot: ContextSnapshot) => void;
|
|
208
70
|
/**
|
|
209
|
-
* The parent session's context window in tokens, handed down to every child
|
|
71
|
+
* The parent session's context window in tokens, handed down to every child
|
|
72
|
+
* whose group resolved no window of its own.
|
|
210
73
|
*
|
|
211
74
|
* pi's `--mode json` stream reports token counts but no window, so without
|
|
212
75
|
* this the gauge shows a bare number and — worse — the StallDetector's CONTEXT
|
|
213
76
|
* CHURN rule can never fire: it opens with `if (this.contextWindow <= 0)
|
|
214
|
-
* return false`.
|
|
215
|
-
* run the parent's own default model, which is what makes its window the
|
|
216
|
-
* honest value. Absent = unknown, and both consumers degrade.
|
|
77
|
+
* return false`. Absent = unknown, and both consumers degrade.
|
|
217
78
|
*/
|
|
218
79
|
contextWindow?: number;
|
|
219
80
|
/**
|
|
@@ -235,10 +96,9 @@ export interface PhaseDeps {
|
|
|
235
96
|
*/
|
|
236
97
|
childExtensions?: readonly string[];
|
|
237
98
|
/**
|
|
238
|
-
* Wall-clock budget for ONE spawn of this child, in ms
|
|
239
|
-
*
|
|
240
|
-
*
|
|
241
|
-
* (workers/pi-worker-core.ts). Tests inject a short budget.
|
|
99
|
+
* Wall-clock budget for ONE spawn of this child, in ms — the `phase`
|
|
100
|
+
* profile's `worker-timeout` row. Nothing sets it in production; the row
|
|
101
|
+
* says why. Tests inject a short budget.
|
|
242
102
|
*/
|
|
243
103
|
timeoutMs?: number;
|
|
244
104
|
/**
|
|
@@ -256,9 +116,9 @@ export interface PhaseDeps {
|
|
|
256
116
|
sleepFor?: (ms: number) => Promise<void>;
|
|
257
117
|
/**
|
|
258
118
|
* Run ONE named Child pi and return its assistant text — the seam every phase
|
|
259
|
-
* child goes through. Absent (production) → the real
|
|
260
|
-
*
|
|
261
|
-
*
|
|
119
|
+
* child goes through. Absent (production) → the real loop runs, with every
|
|
120
|
+
* guard the `phase` profile names. Present → the substitute answers directly
|
|
121
|
+
* and NONE of those guards run.
|
|
262
122
|
*
|
|
263
123
|
* The child's NAME is the first parameter because the name is what a caller
|
|
264
124
|
* branches on and what a test wants to assert. Discarded before it reaches
|
|
@@ -266,8 +126,8 @@ export interface PhaseDeps {
|
|
|
266
126
|
* by matching prompt PROSE against prompts.ts — which makes prompt copy
|
|
267
127
|
* load-bearing test infrastructure in a codebase that rewords prompts.
|
|
268
128
|
*
|
|
269
|
-
* `spawn` stays: the
|
|
270
|
-
*
|
|
129
|
+
* `spawn` stays: the loop's OWN tests drive a real process to exercise the
|
|
130
|
+
* rungs. This seam is for callers to whom the child is a premise.
|
|
271
131
|
*/
|
|
272
132
|
runChild?: (name: string, tools: string, prompt: string) => Promise<string>;
|
|
273
133
|
/**
|
|
@@ -316,29 +176,27 @@ export interface PhaseDeps {
|
|
|
316
176
|
*/
|
|
317
177
|
export type PhaseSeams = Omit<PhaseDeps, 'cwd' | 'taskId' | 'signal' | 'onChildOutput' | 'onContextUsage' | 'contextWindow' | 'recordSubStep'>;
|
|
318
178
|
/**
|
|
319
|
-
*
|
|
320
|
-
*
|
|
321
|
-
* If the child leaks a tool call as plain text (wrong dialect — never executed),
|
|
322
|
-
* re-prompt with a correction hint up to MAX_LEAK_RETRIES times; if it keeps
|
|
323
|
-
* leaking, throw LeakedToolCallError rather than returning the unexecuted call.
|
|
324
|
-
* Empty completions and connection-class model errors share that same budget —
|
|
325
|
-
* see triageChildResult, which decides every one of those cases.
|
|
326
|
-
*
|
|
327
|
-
* THREE RUNAWAY GUARDS ride the same budget, because this is the runner every
|
|
328
|
-
* /task-auto planning child goes through (clarify, decompose, coverage,
|
|
329
|
-
* contract-extract), and an unguarded planning child can burn a whole run:
|
|
330
|
-
* • a LoopDetector, so an identical repeated tool call is killed and
|
|
331
|
-
* re-prompted instead of being allowed to fill the context window;
|
|
332
|
-
* • a StallDetector, the backstop for the varied-args thrash the loop
|
|
333
|
-
* detector's short window cannot see — a child that keeps calling tools with
|
|
334
|
-
* different arguments, learns nothing, and is never going to return. It bounds
|
|
335
|
-
* consecutive no-new-ground calls and total context churn, NOT elapsed time;
|
|
336
|
-
* • PHASE_CHILD_TIMEOUT_MS, a hard wall clock, OFF by default: a healthy
|
|
337
|
-
* reasoning-on planning child and a runaway one occupy the same range of
|
|
338
|
-
* elapsed times, so no threshold separates them. See its comment.
|
|
339
|
-
* All three are checked BEFORE the triage ladder: we killed the child, so its
|
|
340
|
-
* exit status describes our SIGTERM and says nothing about its verdict.
|
|
179
|
+
* The two things a phase child can disagree about. Everything else — the
|
|
180
|
+
* guards, the budgets, the loop trail — is the one loop's and the `phase` row's.
|
|
341
181
|
*/
|
|
182
|
+
export interface PhaseChildOptions {
|
|
183
|
+
/**
|
|
184
|
+
* The wrapper's own word in the debug log for "we are going round again".
|
|
185
|
+
* The debug trail of a real run is read by a human who knows which phases
|
|
186
|
+
* restart and which retry.
|
|
187
|
+
*/
|
|
188
|
+
verb?: 'retry' | 'restart';
|
|
189
|
+
/**
|
|
190
|
+
* When the strike budget is exhausted by loops, do NOT fail the phase. Run
|
|
191
|
+
* ONE final attempt with NO tools and a terminal hint ordering the model to
|
|
192
|
+
* emit its output from what it already has. Only safe for phases whose
|
|
193
|
+
* deliverable is a pure text rewrite that never strictly required a read
|
|
194
|
+
* (refine) — a hard-fail there kills the whole /task-auto run for a model
|
|
195
|
+
* that simply over-explored. Research/location phases must NOT enable this:
|
|
196
|
+
* their output depends on real reads, so a no-tools fallback would fabricate.
|
|
197
|
+
*/
|
|
198
|
+
degradeOnExhaustion?: boolean;
|
|
199
|
+
}
|
|
342
200
|
/**
|
|
343
201
|
* The group fragment for a named child, or `[]` when the name is unmapped.
|
|
344
202
|
*
|
|
@@ -350,42 +208,13 @@ export type PhaseSeams = Omit<PhaseDeps, 'cwd' | 'taskId' | 'signal' | 'onChildO
|
|
|
350
208
|
*/
|
|
351
209
|
export declare function groupArgsForChild(name: string): string[];
|
|
352
210
|
export declare function runPhaseChild(deps: PhaseDeps, name: string, tools: string, prompt: string, opts?: PhaseChildOptions): Promise<string>;
|
|
353
|
-
export declare function formatLoopHint(hit: LoopHit): string;
|
|
354
211
|
/**
|
|
355
212
|
* Terminal hint for the degrade attempt: the model has thrashed through the whole
|
|
356
213
|
* strike budget re-reading files without converging, so we strip its tools and
|
|
357
|
-
* order it to emit the deliverable NOW from what it already has.
|
|
358
|
-
* read-only analysis phases (refine) whose output is a text rewrite that never
|
|
359
|
-
* strictly required a successful read — far better to ship a best-effort spec
|
|
360
|
-
* than to hard-fail the whole /task-auto run. See countRevisits / LoopExhausted.
|
|
214
|
+
* order it to emit the deliverable NOW from what it already has.
|
|
361
215
|
*/
|
|
362
216
|
export declare function formatDegradeHint(hit: LoopHit): string;
|
|
363
217
|
export declare function prependHint(hint: string | null, prompt: string): string;
|
|
364
|
-
/**
|
|
365
|
-
* The two things a phase child can disagree about. Everything else — the loop
|
|
366
|
-
* and stall detectors, the wall clock, the loop trail, the triage ladder and its
|
|
367
|
-
* budget — is the one loop's. These two are the only differences observable from
|
|
368
|
-
* outside it.
|
|
369
|
-
*/
|
|
370
|
-
export interface PhaseChildOptions {
|
|
371
|
-
/**
|
|
372
|
-
* The wrapper's own word in the debug log for "we are going round again".
|
|
373
|
-
* An option rather than one word because it is the single externally visible
|
|
374
|
-
* difference between the two loops this collapsed, and the debug trail of a
|
|
375
|
-
* real run is read by a human who knows which phases restart and which retry.
|
|
376
|
-
*/
|
|
377
|
-
verb?: 'retry' | 'restart';
|
|
378
|
-
/**
|
|
379
|
-
* When the strike budget is exhausted by loops, do NOT fail the phase. Run
|
|
380
|
-
* ONE final attempt with NO tools and a terminal hint ordering the model to
|
|
381
|
-
* emit its output from what it already has. Only safe for phases whose
|
|
382
|
-
* deliverable is a pure text rewrite that never strictly required a read
|
|
383
|
-
* (refine) — a hard-fail there kills the whole /task-auto run for a model
|
|
384
|
-
* that simply over-explored. Research/location phases must NOT enable this:
|
|
385
|
-
* their output depends on real reads, so a no-tools fallback would fabricate.
|
|
386
|
-
*/
|
|
387
|
-
degradeOnExhaustion?: boolean;
|
|
388
|
-
}
|
|
389
218
|
/**
|
|
390
219
|
* Run a child up to twice; the second attempt gets `emphasized=true` to escalate
|
|
391
220
|
* the prompt. On success, return the validator's value; on two failures, throw
|
|
@@ -398,34 +227,3 @@ export declare function runWithEmphasisRetry<T>(deps: PhaseDeps, name: string, t
|
|
|
398
227
|
ok: false;
|
|
399
228
|
problem: string;
|
|
400
229
|
}, onFail: (problem: string) => Error): Promise<T>;
|
|
401
|
-
export declare class LoopExhaustedError extends Error {
|
|
402
|
-
readonly phase: string;
|
|
403
|
-
readonly history: LoopHit[];
|
|
404
|
-
constructor(phase: string, history: LoopHit[]);
|
|
405
|
-
}
|
|
406
|
-
/**
|
|
407
|
-
* Thrown when a phase child's final turn failed with stopReason "error" — the
|
|
408
|
-
* model/provider died (local model disconnect, fetch failed, socket hang up,
|
|
409
|
-
* provider 5xx) after pi exhausted its own internal retries. pi reports this as
|
|
410
|
-
* an agent_end with empty assistant text, which would otherwise surface as the
|
|
411
|
-
* misleading "produced no output"; this names the real cause instead.
|
|
412
|
-
*
|
|
413
|
-
* Fail-fast: not retried at the pi-task layer. pi already retried the retryable
|
|
414
|
-
* cases; re-spawning a fresh child against the same dead endpoint only burns
|
|
415
|
-
* time and buries the real error. Restart the model/provider, then resume.
|
|
416
|
-
*/
|
|
417
|
-
export declare class ModelError extends Error {
|
|
418
|
-
readonly phase: string;
|
|
419
|
-
readonly cause: string;
|
|
420
|
-
constructor(phase: string, cause: string);
|
|
421
|
-
}
|
|
422
|
-
/**
|
|
423
|
-
* Thrown when a phase child repeatedly wrote a tool call as plain text (a markup
|
|
424
|
-
* dialect pi's harness didn't parse) instead of invoking it. The call never ran,
|
|
425
|
-
* so the phase output is untrustworthy — fail loudly rather than check it off.
|
|
426
|
-
*/
|
|
427
|
-
export declare class LeakedToolCallError extends Error {
|
|
428
|
-
readonly phase: string;
|
|
429
|
-
readonly marker: string;
|
|
430
|
-
constructor(phase: string, marker: string);
|
|
431
|
-
}
|