@mjasnikovs/pi-task 0.13.33 → 0.13.34

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.
@@ -15,7 +15,7 @@ import { isDuplicateQuestion, MAX_DUP_STRIKES, DUP_REPROMPT_HINT } from './quest
15
15
  import { allocateAutoId, buildAutoBody, parseDecomposeList, parseTaskList, checkOffTask, stampTaskInProgress, findResumableAuto } from './auto-io.js';
16
16
  import { writeTaskFile, readTaskFile, updateTaskFrontMatter, taskFilePath, tasksDir } from './task-io.js';
17
17
  import { gitCommitAll } from './auto-commit.js';
18
- import { runGuidelineEnforcement, classifyEnforceChildFailure, ENFORCE_TIMEOUT_MS } from './enforce-guidelines.js';
18
+ import { runGuidelineEnforcement, classifyEnforceChildFailure, ENFORCE_TIMEOUT_MS, ENFORCE_LOOP } from './enforce-guidelines.js';
19
19
  import { runWorker } from '../workers/pi-worker-core.js';
20
20
  import { runPhaseChild, prependHint, USER_CANCELLED } from './child-runner.js';
21
21
  import { SessionUI, registerBridgeCommand } from '../remote/bridge.js';
@@ -359,6 +359,7 @@ function defaultDeps(ctx, cwd, signal, title) {
359
359
  signal: sig,
360
360
  tools,
361
361
  timeoutMs: ENFORCE_TIMEOUT_MS,
362
+ loop: ENFORCE_LOOP,
362
363
  onLine: line => {
363
364
  lastLine = line;
364
365
  logEnforce(line);
@@ -7,6 +7,21 @@ declare const ENFORCE_TOOLS = "read,grep,find,ls,edit,write";
7
7
  * fix pass reads several files and may rewrite a few; give it room but never
8
8
  * let a wedged child hang the whole run. */
9
9
  declare const ENFORCE_TIMEOUT_MS = 600000;
10
+ /**
11
+ * Loop-guard tuning for the enforcement child. Unlike a read-only research
12
+ * worker, this pass's JOB is to rework files in place, so it legitimately reads,
13
+ * edits, and re-greps the SAME file many times — the default path-revisit
14
+ * heuristic (5 ops on one path) mislabels that as a runaway. It is disabled here
15
+ * (pathThreshold = Infinity); only the exact-match guard remains (a byte-for-byte
16
+ * identical call repeated, raised to 12 over a 40-call window), with the 10-min
17
+ * wall-clock timeout as the real runaway backstop. Validated against a real
18
+ * loop-kill on mx5 TASK_0002, where a single-file fix pass on queries.ts tripped
19
+ * the default detector at 5 revisits. */
20
+ export declare const ENFORCE_LOOP: {
21
+ readonly window: 40;
22
+ readonly threshold: 12;
23
+ readonly pathThreshold: number;
24
+ };
10
25
  export interface GuidelineDoc {
11
26
  /** Filenames found, in discovery order (e.g. ['AGENTS.md', 'CLAUDE.md']). */
12
27
  files: string[];
@@ -28,6 +28,21 @@ const ENFORCE_TOOLS = 'read,grep,find,ls,edit,write';
28
28
  * fix pass reads several files and may rewrite a few; give it room but never
29
29
  * let a wedged child hang the whole run. */
30
30
  const ENFORCE_TIMEOUT_MS = 600_000;
31
+ /**
32
+ * Loop-guard tuning for the enforcement child. Unlike a read-only research
33
+ * worker, this pass's JOB is to rework files in place, so it legitimately reads,
34
+ * edits, and re-greps the SAME file many times — the default path-revisit
35
+ * heuristic (5 ops on one path) mislabels that as a runaway. It is disabled here
36
+ * (pathThreshold = Infinity); only the exact-match guard remains (a byte-for-byte
37
+ * identical call repeated, raised to 12 over a 40-call window), with the 10-min
38
+ * wall-clock timeout as the real runaway backstop. Validated against a real
39
+ * loop-kill on mx5 TASK_0002, where a single-file fix pass on queries.ts tripped
40
+ * the default detector at 5 revisits. */
41
+ export const ENFORCE_LOOP = {
42
+ window: 40,
43
+ threshold: 12,
44
+ pathThreshold: Number.POSITIVE_INFINITY
45
+ };
31
46
  /**
32
47
  * Read the guideline files that live directly in `cwd` (AGENTS.md, CLAUDE.md).
33
48
  * Returns null when none exist or all are empty — the caller treats that as a
@@ -13,6 +13,17 @@ export interface RunWorkerInput {
13
13
  onLine?: (line: string) => void;
14
14
  /** Per-worker wall-clock timeout in ms. Defaults to RESEARCH_WORKER_TIMEOUT_MS. */
15
15
  timeoutMs?: number;
16
+ /**
17
+ * Per-worker loop-detector tuning. Defaults to the read-only research/impl
18
+ * guard (LOOP_WINDOW / LOOP_THRESHOLD, path threshold = exact threshold). An
19
+ * edit/fix pass legitimately revisits one file, so it can raise (or disable
20
+ * via Infinity) `pathThreshold` to keep only the exact-match guard.
21
+ */
22
+ loop?: {
23
+ window?: number;
24
+ threshold?: number;
25
+ pathThreshold?: number;
26
+ };
16
27
  }
17
28
  export interface RunWorkerResult {
18
29
  text: string;
@@ -71,7 +71,9 @@ export async function runWorker(input) {
71
71
  const invocation = getPiInvocation([...baseArgs, prompt]);
72
72
  const tStart = Date.now();
73
73
  let tFirstByte = null;
74
- const loopDetector = new LoopDetector(LOOP_WINDOW, LOOP_THRESHOLD);
74
+ const loopWindow = input.loop?.window ?? LOOP_WINDOW;
75
+ const loopThreshold = input.loop?.threshold ?? LOOP_THRESHOLD;
76
+ const loopDetector = new LoopDetector(loopWindow, loopThreshold, input.loop?.pathThreshold ?? loopThreshold);
75
77
  // Capture the hit the detector reports (it also returns it to the unified
76
78
  // runner, which kills the child on a hit). Without capturing it here the
77
79
  // SIGTERM that kill produces would surface as a bare non-zero exit the
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mjasnikovs/pi-task",
3
- "version": "0.13.33",
3
+ "version": "0.13.34",
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",