@mjasnikovs/pi-task 0.38.7 → 0.38.8
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.
|
@@ -25,7 +25,7 @@ import { requestCancel, resetCancel, isCancelRequested, cancelCheckpoint } from
|
|
|
25
25
|
import { armCancelListener, disarmCancelListener } from './cancel-input.js';
|
|
26
26
|
import { beginRun, endRun } from './mid-run-input.js';
|
|
27
27
|
import { reportDroppedInput } from './dropped-input.js';
|
|
28
|
-
import { refineExistingFilesBlock } from './phases.js';
|
|
28
|
+
import { refineExistingFilesBlock, SINGLE_READ_EXTENSION_PATH } from './phases.js';
|
|
29
29
|
import { SessionUI, registerBridgeCommand, publishLifecycleNotice } from '../remote/bridge.js';
|
|
30
30
|
import { pushNotify } from '../remote/push.js';
|
|
31
31
|
import { startAutoLoader } from './widget.js';
|
|
@@ -1079,6 +1079,13 @@ function defaultDeps(ctx, cwd, signal, title) {
|
|
|
1079
1079
|
cwd,
|
|
1080
1080
|
taskId: '',
|
|
1081
1081
|
signal,
|
|
1082
|
+
// IN-RUN thrash guard for the planning children (mx5-n 2026-08-14: a
|
|
1083
|
+
// decompose child re-read DESIGN/marketplace.html until it filled a
|
|
1084
|
+
// 120k window, and ran 16m23s without returning). Every planning child
|
|
1085
|
+
// gets its source doc INLINED in its prompt, so a second read of a file
|
|
1086
|
+
// it has already opened can only be thrash — which makes the read-once
|
|
1087
|
+
// block safe here in a way it is not for a phase that must explore.
|
|
1088
|
+
childExtensions: [SINGLE_READ_EXTENSION_PATH],
|
|
1082
1089
|
onChildOutput: (line) => {
|
|
1083
1090
|
lastLine = line;
|
|
1084
1091
|
},
|
|
@@ -58,14 +58,16 @@ export interface PhaseRunResult {
|
|
|
58
58
|
/** Set when the child's final turn failed with stopReason "error" (model/provider failure). */
|
|
59
59
|
modelError?: string;
|
|
60
60
|
}
|
|
61
|
-
export declare function childArgs(tools: string): string[];
|
|
61
|
+
export declare function childArgs(tools: string, extensions?: readonly string[]): string[];
|
|
62
62
|
export declare const USER_CANCELLED = "__user_cancelled__";
|
|
63
63
|
/**
|
|
64
64
|
* Run a child pi process with JSON event-stream output, loop detection, and
|
|
65
65
|
* context-usage tracking. This is the typed convenience wrapper used by
|
|
66
66
|
* phase-level code.
|
|
67
67
|
*/
|
|
68
|
-
export declare function runChild(cwd: string, tools: string, prompt: string, signal: AbortSignal, onLine?: (line: string) => void, onContextUsage?: (snapshot: ContextSnapshot) => void, onToolCall?: (call: ToolCall) => LoopHit | null, spawnFn?: SpawnFn
|
|
68
|
+
export declare function runChild(cwd: string, tools: string, prompt: string, signal: AbortSignal, onLine?: (line: string) => void, onContextUsage?: (snapshot: ContextSnapshot) => void, onToolCall?: (call: ToolCall) => LoopHit | null, spawnFn?: SpawnFn,
|
|
69
|
+
/** Internal `-e` extension paths for in-run guards (see childArgs). */
|
|
70
|
+
extensions?: readonly string[]): Promise<PhaseRunResult>;
|
|
69
71
|
interface PhaseDeps {
|
|
70
72
|
cwd: string;
|
|
71
73
|
taskId: string;
|
|
@@ -79,6 +81,17 @@ interface PhaseDeps {
|
|
|
79
81
|
*/
|
|
80
82
|
recordSubStep?: (label: string, ms: number) => void;
|
|
81
83
|
spawn?: SpawnFn;
|
|
84
|
+
/**
|
|
85
|
+
* Internal `-e` extension paths loaded into this child for IN-RUN guards.
|
|
86
|
+
*
|
|
87
|
+
* The point of a guard that runs inside the child is that it does not have
|
|
88
|
+
* to kill it: pi turns a `tool_call` handler's `{block, reason}` into an
|
|
89
|
+
* error tool result, so the model reads the reason as its own tool output
|
|
90
|
+
* and continues with its context intact. The host's only alternative is to
|
|
91
|
+
* kill and re-spawn from nothing, which just re-runs a model that
|
|
92
|
+
* deterministically re-thrashes (workers/single-read-guard.ts).
|
|
93
|
+
*/
|
|
94
|
+
childExtensions?: readonly string[];
|
|
82
95
|
/**
|
|
83
96
|
* Wall-clock budget for ONE spawn of this child, in ms. Defaults to
|
|
84
97
|
* PHASE_CHILD_TIMEOUT_MS; `0` disables the cap. Mirrors runWorker's
|
|
@@ -127,7 +127,7 @@ export function connectionRetryBackoffMs(attempt) {
|
|
|
127
127
|
}
|
|
128
128
|
const defaultSleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
|
|
129
129
|
// ─── Spawn helpers ───────────────────────────────────────────────────────────
|
|
130
|
-
export function childArgs(tools) {
|
|
130
|
+
export function childArgs(tools, extensions = []) {
|
|
131
131
|
// `--mode json` puts the child into the structured event stream the
|
|
132
132
|
// unified runner parses in `mode: 'json-events'`. Without it the child
|
|
133
133
|
// emits plain text, every line fails JSON.parse, finalText stays empty,
|
|
@@ -142,8 +142,13 @@ export function childArgs(tools) {
|
|
|
142
142
|
// The prompt is NOT an argv element: it goes to the child over stdin (see
|
|
143
143
|
// runChild below / getPiInvocation), so a large inlined-design prompt can't
|
|
144
144
|
// overflow the OS command-line limit (Windows `spawn ENAMETOOLONG`).
|
|
145
|
+
//
|
|
146
|
+
// `extensions` are internal `-e` loads for in-run guards (the caller supplies
|
|
147
|
+
// the path). A no-tools child cannot make a tool call, so it never carries
|
|
148
|
+
// one — the guards all hang off pi's `tool_call` hook.
|
|
145
149
|
const toolFlags = tools === '' ? ['--no-tools'] : ['--tools', tools];
|
|
146
|
-
|
|
150
|
+
const internal = tools === '' ? [] : extensions;
|
|
151
|
+
return [...childBaseArgs(internal), '--mode', 'json', ...toolFlags];
|
|
147
152
|
}
|
|
148
153
|
// Sentinel error thrown when the user dismisses a grill-me dialog.
|
|
149
154
|
// Defined here (not in failure-classifier.ts) to avoid circular dependency.
|
|
@@ -154,8 +159,10 @@ export const USER_CANCELLED = '__user_cancelled__';
|
|
|
154
159
|
* context-usage tracking. This is the typed convenience wrapper used by
|
|
155
160
|
* phase-level code.
|
|
156
161
|
*/
|
|
157
|
-
export async function runChild(cwd, tools, prompt, signal, onLine, onContextUsage, onToolCall, spawnFn
|
|
158
|
-
|
|
162
|
+
export async function runChild(cwd, tools, prompt, signal, onLine, onContextUsage, onToolCall, spawnFn,
|
|
163
|
+
/** Internal `-e` extension paths for in-run guards (see childArgs). */
|
|
164
|
+
extensions) {
|
|
165
|
+
const invocation = getPiInvocation(childArgs(tools, extensions), prompt);
|
|
159
166
|
let loopHit;
|
|
160
167
|
const result = await runChildUnified(spawnFn ?? spawn, invocation, cwd, signal, {
|
|
161
168
|
mode: 'json-events',
|
|
@@ -289,7 +296,7 @@ export async function runPhaseChild(deps, name, tools, prompt) {
|
|
|
289
296
|
const clock = phaseTimeout(deps.signal, budgetMs);
|
|
290
297
|
let r;
|
|
291
298
|
try {
|
|
292
|
-
r = await runChild(deps.cwd, tools, prependHint(hint, prompt), clock.signal, deps.onChildOutput, deps.onContextUsage, call => detector.record(call), deps.spawn);
|
|
299
|
+
r = await runChild(deps.cwd, tools, prependHint(hint, prompt), clock.signal, deps.onChildOutput, deps.onContextUsage, call => detector.record(call), deps.spawn, deps.childExtensions);
|
|
293
300
|
}
|
|
294
301
|
finally {
|
|
295
302
|
clock.cleanup();
|
package/dist/task/phases.d.ts
CHANGED
|
@@ -111,6 +111,15 @@ export declare function searchConfigured(getEnv?: (k: string) => string | undefi
|
|
|
111
111
|
/** Extra prompt block for the APIS worker when search is available — trigger-framed
|
|
112
112
|
* (the validated shape for getting a local model to actually reach for search). */
|
|
113
113
|
export declare const RESEARCH_SEARCH_HINT: string;
|
|
114
|
+
/**
|
|
115
|
+
* In-process guards loaded into the TOOLING worker only: block a re-read of any
|
|
116
|
+
* file already read, and block any byte-identical grep/find/ls repeat, feeding
|
|
117
|
+
* the model "you already have this, answer now" instead of letting it re-run.
|
|
118
|
+
* TOOLING reads each file once and never needs an identical search twice in any
|
|
119
|
+
* healthy recorded run, so neither rule has a legitimate false positive here.
|
|
120
|
+
* See single-read-guard.ts.
|
|
121
|
+
*/
|
|
122
|
+
export declare const SINGLE_READ_EXTENSION_PATH: string;
|
|
114
123
|
/**
|
|
115
124
|
* The TOOLING worker only needs to know which verification commands the task
|
|
116
125
|
* cares about — never the per-file edit list. Big refined prompts embed a long
|
package/dist/task/phases.js
CHANGED
|
@@ -332,7 +332,7 @@ export const RESEARCH_SEARCH_HINT = '\n\nLIVE WEB — use pi-worker-search for e
|
|
|
332
332
|
* healthy recorded run, so neither rule has a legitimate false positive here.
|
|
333
333
|
* See single-read-guard.ts.
|
|
334
334
|
*/
|
|
335
|
-
const SINGLE_READ_EXTENSION_PATH = fileURLToPath(new URL('../workers/single-read-extension.js', import.meta.url));
|
|
335
|
+
export const SINGLE_READ_EXTENSION_PATH = fileURLToPath(new URL('../workers/single-read-extension.js', import.meta.url));
|
|
336
336
|
/**
|
|
337
337
|
* Task-file heading under which a research worker's validated output is cached.
|
|
338
338
|
* A resumed research phase reads these to skip workers that already succeeded,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mjasnikovs/pi-task",
|
|
3
|
-
"version": "0.38.
|
|
3
|
+
"version": "0.38.8",
|
|
4
4
|
"description": "Deterministic task planning and spec-orchestration for local models — crash-safe /task pipelines with verify/enforce gates, a real-time remote web view, and web/docs/fetch/worker subagent tools.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|