@mjasnikovs/pi-task 0.13.37 → 0.13.38
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.
|
@@ -17,7 +17,7 @@ import { writeTaskFile, readTaskFile, updateTaskFrontMatter, taskFilePath, tasks
|
|
|
17
17
|
import { gitCommitAll } from './auto-commit.js';
|
|
18
18
|
import { runGuidelineEnforcement, classifyEnforceChildFailure } from './enforce-guidelines.js';
|
|
19
19
|
import { runWorker } from '../workers/pi-worker-core.js';
|
|
20
|
-
import { runPhaseChild, prependHint, USER_CANCELLED } from './child-runner.js';
|
|
20
|
+
import { runPhaseChild, prependHint, formatLoopHint, USER_CANCELLED } from './child-runner.js';
|
|
21
21
|
import { SessionUI, registerBridgeCommand } from '../remote/bridge.js';
|
|
22
22
|
import { pushNotify } from '../remote/push.js';
|
|
23
23
|
import { getConfig } from '../config/config.js';
|
|
@@ -362,7 +362,15 @@ function defaultDeps(ctx, cwd, signal, title) {
|
|
|
362
362
|
signal: sig,
|
|
363
363
|
tools,
|
|
364
364
|
timeoutMs: 0, // no wall-clock timeout — run to completion
|
|
365
|
-
|
|
365
|
+
// Exact-match loop guard only: pathThreshold Infinity
|
|
366
|
+
// disables the path-revisit heuristic, so revisiting one
|
|
367
|
+
// file (which IS this pass's job) never trips — only a
|
|
368
|
+
// literally-identical call repeated past threshold does
|
|
369
|
+
// (e.g. the same grep fired 900× in enforce-debug.log).
|
|
370
|
+
// A hit nudges via the normal restart-with-hint; a loop
|
|
371
|
+
// that survives the nudges is WARNED, not blocked (see
|
|
372
|
+
// r.loopHit handling below and classifyEnforceChildFailure).
|
|
373
|
+
loop: { pathThreshold: Number.POSITIVE_INFINITY },
|
|
366
374
|
onLine: line => {
|
|
367
375
|
lastLine = line;
|
|
368
376
|
logEnforce(line);
|
|
@@ -371,6 +379,13 @@ function defaultDeps(ctx, cwd, signal, title) {
|
|
|
371
379
|
contextUsage = resolveContextUsage(snapshot, contextUsage, parentContextWindow);
|
|
372
380
|
}
|
|
373
381
|
});
|
|
382
|
+
// A loop that survived the restart-with-hint nudges is a
|
|
383
|
+
// warning, not a failure: log it and tell the user, but let
|
|
384
|
+
// the verdict gate (below) be the only thing that can block.
|
|
385
|
+
if (r.loopHit) {
|
|
386
|
+
logEnforce(`=== enforce LOOP WARNING — ${formatLoopHint(r.loopHit)} ===`);
|
|
387
|
+
enforceCtx.ui.notify(`${taskTitle}: enforce worker looped past the nudges — continuing (not blocked).`, 'warning');
|
|
388
|
+
}
|
|
374
389
|
const failure = classifyEnforceChildFailure(r);
|
|
375
390
|
logEnforce(failure ?
|
|
376
391
|
`=== enforce end: FAIL — ${failure} ===`
|
|
@@ -56,10 +56,17 @@ export interface EnforceChildResult {
|
|
|
56
56
|
* when it finished cleanly enough to parse a verdict from its text.
|
|
57
57
|
*
|
|
58
58
|
* The order is load-bearing. A loop-kill AND a wall-clock timeout BOTH also set
|
|
59
|
-
* `aborted` — killProc flips it on every kill path — so the
|
|
60
|
-
* (timeout, loop, leaked tool call) must be
|
|
61
|
-
* `aborted → user-cancel`
|
|
62
|
-
* inline code did) mislabels a loop-killed enforcement
|
|
59
|
+
* `aborted` (and a non-zero exit) — killProc flips it on every kill path — so the
|
|
60
|
+
* specific causes (timeout, loop, leaked tool call) must be handled BEFORE the
|
|
61
|
+
* generic `aborted → user-cancel` and `exitCode` mappings. Checking `aborted`
|
|
62
|
+
* first (as the original inline code did) mislabels a loop-killed enforcement
|
|
63
|
+
* child as a user cancel.
|
|
64
|
+
*
|
|
65
|
+
* A loop is NOT fatal: enforce attaches the detector in nudge-then-warn mode, so a
|
|
66
|
+
* loop that survived its restart-with-hint nudges returns null here (the caller
|
|
67
|
+
* logs/notifies it as a warning) and the verdict gate alone decides the outcome.
|
|
68
|
+
* It still has to be matched before `aborted`/`exitCode` so the kill's side
|
|
69
|
+
* effects don't get re-classified as a user cancel or a crash.
|
|
63
70
|
*/
|
|
64
71
|
export declare function classifyEnforceChildFailure(r: EnforceChildResult): string | null;
|
|
65
72
|
/**
|
|
@@ -101,16 +101,23 @@ export function parseEnforceVerdict(text) {
|
|
|
101
101
|
* when it finished cleanly enough to parse a verdict from its text.
|
|
102
102
|
*
|
|
103
103
|
* The order is load-bearing. A loop-kill AND a wall-clock timeout BOTH also set
|
|
104
|
-
* `aborted` — killProc flips it on every kill path — so the
|
|
105
|
-
* (timeout, loop, leaked tool call) must be
|
|
106
|
-
* `aborted → user-cancel`
|
|
107
|
-
* inline code did) mislabels a loop-killed enforcement
|
|
104
|
+
* `aborted` (and a non-zero exit) — killProc flips it on every kill path — so the
|
|
105
|
+
* specific causes (timeout, loop, leaked tool call) must be handled BEFORE the
|
|
106
|
+
* generic `aborted → user-cancel` and `exitCode` mappings. Checking `aborted`
|
|
107
|
+
* first (as the original inline code did) mislabels a loop-killed enforcement
|
|
108
|
+
* child as a user cancel.
|
|
109
|
+
*
|
|
110
|
+
* A loop is NOT fatal: enforce attaches the detector in nudge-then-warn mode, so a
|
|
111
|
+
* loop that survived its restart-with-hint nudges returns null here (the caller
|
|
112
|
+
* logs/notifies it as a warning) and the verdict gate alone decides the outcome.
|
|
113
|
+
* It still has to be matched before `aborted`/`exitCode` so the kill's side
|
|
114
|
+
* effects don't get re-classified as a user cancel or a crash.
|
|
108
115
|
*/
|
|
109
116
|
export function classifyEnforceChildFailure(r) {
|
|
110
117
|
if (r.timedOut)
|
|
111
118
|
return 'enforcement child timed out';
|
|
112
119
|
if (r.loopHit)
|
|
113
|
-
return
|
|
120
|
+
return null; // looped past the nudges → warning, handled by caller
|
|
114
121
|
if (r.leakedToolCall)
|
|
115
122
|
return 'enforcement child leaked a tool call';
|
|
116
123
|
if (r.aborted)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mjasnikovs/pi-task",
|
|
3
|
-
"version": "0.13.
|
|
3
|
+
"version": "0.13.38",
|
|
4
4
|
"description": "Deterministic spec-orchestration for local models, with a bundled real-time remote web view and web/docs/fetch/worker subagent tools.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|