pi-harness-runtime 1.1.450 → 1.1.452

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.
Files changed (93) hide show
  1. package/AGENTS.md +53 -0
  2. package/CHANGELOG.md +45 -0
  3. package/index.ts +172 -21
  4. package/package.json +1 -1
  5. package/packages/agent-policy/package.json +1 -1
  6. package/packages/auth/package.json +1 -1
  7. package/packages/autonomous-runtime/package.json +1 -1
  8. package/packages/checkpoint/package.json +1 -1
  9. package/packages/clipboard/package.json +1 -1
  10. package/packages/codex-watcher/package.json +1 -1
  11. package/packages/config-capture/package.json +1 -1
  12. package/packages/contracts/package.json +1 -1
  13. package/packages/cookie-sanitizer/package.json +1 -1
  14. package/packages/evaluation-engine/package.json +1 -1
  15. package/packages/event-bus/package.json +1 -1
  16. package/packages/event-store/package.json +1 -1
  17. package/packages/experience-replay/package.json +1 -1
  18. package/packages/file-copy-helper/package.json +1 -1
  19. package/packages/intent-analyzer/package.json +1 -1
  20. package/packages/jev-judge/dist/continuation-status.d.ts +31 -0
  21. package/packages/jev-judge/dist/continuation-status.d.ts.map +1 -0
  22. package/packages/jev-judge/dist/continuation-status.js +40 -0
  23. package/packages/jev-judge/dist/continuation-status.js.map +1 -0
  24. package/packages/jev-judge/dist/delegation-harness.d.ts +74 -1
  25. package/packages/jev-judge/dist/delegation-harness.d.ts.map +1 -1
  26. package/packages/jev-judge/dist/delegation-harness.js +183 -18
  27. package/packages/jev-judge/dist/delegation-harness.js.map +1 -1
  28. package/packages/jev-judge/dist/delegation-scope.d.ts +88 -0
  29. package/packages/jev-judge/dist/delegation-scope.d.ts.map +1 -0
  30. package/packages/jev-judge/dist/delegation-scope.js +218 -0
  31. package/packages/jev-judge/dist/delegation-scope.js.map +1 -0
  32. package/packages/jev-judge/package.json +1 -1
  33. package/packages/jev-judge/src/continuation-status.ts +41 -0
  34. package/packages/jev-judge/src/delegation-harness.ts +254 -45
  35. package/packages/jev-judge/src/delegation-scope.ts +264 -0
  36. package/packages/knowledge-retrieval/package.json +1 -1
  37. package/packages/learning-engine/package.json +1 -1
  38. package/packages/logging/package.json +1 -1
  39. package/packages/memory-engine/package.json +1 -1
  40. package/packages/notification/package.json +1 -1
  41. package/packages/paperclip-integration/package.json +1 -1
  42. package/packages/performance-optimizer/package.json +1 -1
  43. package/packages/privilege-broker/package.json +1 -1
  44. package/packages/prompt-compiler/package.json +1 -1
  45. package/packages/providers/package.json +1 -1
  46. package/packages/qdrant-skills/package.json +1 -1
  47. package/packages/quota-manager/package.json +1 -1
  48. package/packages/relay/package.json +1 -1
  49. package/packages/skill-mcp-client/package.json +1 -1
  50. package/packages/skills/package.json +1 -1
  51. package/packages/todo-bd-sync/package.json +1 -1
  52. package/packages/token-estimation/package.json +1 -1
  53. package/packages/tooling-preference/package.json +1 -1
  54. package/packages/trajectory/package.json +1 -1
  55. package/packages/tui/package.json +1 -1
  56. package/packages/two-phase-review/dist/classify.d.ts +22 -1
  57. package/packages/two-phase-review/dist/classify.d.ts.map +1 -1
  58. package/packages/two-phase-review/dist/classify.js +37 -1
  59. package/packages/two-phase-review/dist/classify.js.map +1 -1
  60. package/packages/two-phase-review/dist/index.d.ts +4 -2
  61. package/packages/two-phase-review/dist/index.d.ts.map +1 -1
  62. package/packages/two-phase-review/dist/index.js +3 -2
  63. package/packages/two-phase-review/dist/index.js.map +1 -1
  64. package/packages/two-phase-review/dist/rules.d.ts +8 -3
  65. package/packages/two-phase-review/dist/rules.d.ts.map +1 -1
  66. package/packages/two-phase-review/dist/rules.js +36 -5
  67. package/packages/two-phase-review/dist/rules.js.map +1 -1
  68. package/packages/two-phase-review/dist/runtime.d.ts +9 -0
  69. package/packages/two-phase-review/dist/runtime.d.ts.map +1 -1
  70. package/packages/two-phase-review/dist/runtime.js +68 -17
  71. package/packages/two-phase-review/dist/runtime.js.map +1 -1
  72. package/packages/two-phase-review/dist/state.d.ts +15 -0
  73. package/packages/two-phase-review/dist/state.d.ts.map +1 -1
  74. package/packages/two-phase-review/dist/state.js +37 -2
  75. package/packages/two-phase-review/dist/state.js.map +1 -1
  76. package/packages/two-phase-review/dist/types.d.ts +2 -2
  77. package/packages/two-phase-review/dist/types.d.ts.map +1 -1
  78. package/packages/two-phase-review/dist/validation.d.ts +50 -0
  79. package/packages/two-phase-review/dist/validation.d.ts.map +1 -0
  80. package/packages/two-phase-review/dist/validation.js +54 -0
  81. package/packages/two-phase-review/dist/validation.js.map +1 -0
  82. package/packages/two-phase-review/package.json +1 -1
  83. package/packages/two-phase-review/src/classify.ts +38 -1
  84. package/packages/two-phase-review/src/index.ts +16 -0
  85. package/packages/two-phase-review/src/rules.ts +48 -4
  86. package/packages/two-phase-review/src/runtime.ts +88 -17
  87. package/packages/two-phase-review/src/state.ts +38 -2
  88. package/packages/two-phase-review/src/types.ts +2 -1
  89. package/packages/two-phase-review/src/validation.ts +86 -0
  90. package/packages/types/package.json +1 -1
  91. package/packages/webmcp-tester/package.json +1 -1
  92. package/packages/wiki/package.json +1 -1
  93. package/packages/write-review/package.json +1 -1
package/AGENTS.md CHANGED
@@ -273,6 +273,18 @@ ls packages/notification/dist/
273
273
  bun add -d typescript
274
274
  ```
275
275
 
276
+ **The canonical build**: the compiled output that is committed comes from
277
+ `bun scripts/build-packages.ts`, which uses the TypeScript of the root
278
+ (`node_modules/.bin/tsc`). A package-level `bun run build` uses the
279
+ TypeScript that the package itself installed, and the two write different
280
+ source maps for the same source. A package-level build therefore leaves
281
+ the tree with a `dist/` that the next canonical build changes again.
282
+
283
+ ```bash
284
+ # The build whose output is committed
285
+ bun scripts/build-packages.ts
286
+ ```
287
+
276
288
  ## Icon Usage
277
289
 
278
290
  **This is a Node.js CLI daemon — do NOT add React as a dependency for icons.**
@@ -343,6 +355,47 @@ todo create "Replace Film icon with themed icon"
343
355
 
344
356
  ---
345
357
 
358
+ ## Phase Model: Implementation, Review, Validation (CRITICAL)
359
+
360
+ A task with several steps runs in three phases, and the harness enforces
361
+ the order. The todo prefixes are the contract:
362
+
363
+ | Prefix | Meaning |
364
+ |--------|---------|
365
+ | `[IMPLEMENT]` | implementation work of the current request |
366
+ | `[REVIEW]` | the review of the task diff (one todo per chunk) |
367
+ | `[REVIEW-FIX]` | a confirmed finding that has to be fixed |
368
+ | `[TEST]` | the validation of the task (the tiers are declared in its metadata) |
369
+
370
+ **Phase 1 - implementation.** Every open `[IMPLEMENT]` todo (and every
371
+ unclassified todo) is implementation work. The phase ends when the list
372
+ shows finished implementation work.
373
+
374
+ **Phase 2 - review.** The review starts **on its own**: as soon as the
375
+ implementation is done and the task changed a file, the harness derives the
376
+ scope from the task diff, splits it into chunks and asks for one `[REVIEW]`
377
+ todo per chunk. You never have to queue a review item to get one. A task
378
+ that changed no file completes without a review - there is nothing to
379
+ review.
380
+
381
+ **Phase 3 - validation.** A `[TEST]` todo declares the validation plan of
382
+ the task in `metadata.validationPlan` (see the section above) and the
383
+ recorded runs in the validation ledger decide whether the task is done:
384
+
385
+ - The task is **not** complete while the required final tier is pending.
386
+ - The task is **not** complete when the final tier failed, timed out or
387
+ errored.
388
+ - A plan that requires a final tier but declares none is an error to
389
+ report, not a plan to complete.
390
+ - A validation state that cannot be read is never a validation that
391
+ passed.
392
+
393
+ So the order of a real task is: implement, review the diff, fix the
394
+ findings, run the tiers, record them, and only then report the task as
395
+ done.
396
+
397
+ ---
398
+
346
399
  ### Habit: Detect New Tasks from User Prompts (CRITICAL)
347
400
 
348
401
  **When a new user prompt arrives with tasks that differ from the current in-progress task — add ALL new tasks to the todo list immediately.**
package/CHANGELOG.md CHANGED
@@ -2,6 +2,51 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4
4
 
5
+ ### [1.1.452](https://github.com/ManotLuijiu/pi-harness-runtime/compare/v1.1.448...v1.1.452) (2026-10-27)
6
+
7
+
8
+ ### Features
9
+
10
+ * **delegation:** the scope of a request is measured before it is judged ([684b6a5](https://github.com/ManotLuijiu/pi-harness-runtime/commit/684b6a55184f63d3be24c48dcecfb0b13410dc15))
11
+ * **jev:** the pending question carries the ask and the answer ([a792133](https://github.com/ManotLuijiu/pi-harness-runtime/commit/a792133e517fb8c12d3363e98942f3af30270f27))
12
+ * **merge:** a manual run can leave the merge to the pull request event ([b3d1950](https://github.com/ManotLuijiu/pi-harness-runtime/commit/b3d1950b5941c78ccd56951acdafd1d4afbe1bff))
13
+ * **review:** the review starts on its own when the implementation is done ([04f51f5](https://github.com/ManotLuijiu/pi-harness-runtime/commit/04f51f599cd3222b6c74dd8fadb1373bd72d9bd7))
14
+ * **test:** the task waits for the final tier of the validation plan ([fa147db](https://github.com/ManotLuijiu/pi-harness-runtime/commit/fa147dbf7d50d56e6ea48c6d8fbb4fcb190caf8a))
15
+
16
+
17
+ ### Bug Fixes
18
+
19
+ * **hooks:** a tag is checked through the commit behind it ([c823e8e](https://github.com/ManotLuijiu/pi-harness-runtime/commit/c823e8e1f6973c62b5449a97c101759a4d5cbba5))
20
+ * **jev:** the alternatives of a question are parsed without a quadratic pattern ([63016a3](https://github.com/ManotLuijiu/pi-harness-runtime/commit/63016a3f0488acb801990dd46770c00d382b5858))
21
+ * **keys:** the key directory is audited as a tree ([081cc75](https://github.com/ManotLuijiu/pi-harness-runtime/commit/081cc75ac8ceff36b0d619c3f76371dc08459d3c))
22
+ * **merge:** the boolean input is read as a boolean ([971b716](https://github.com/ManotLuijiu/pi-harness-runtime/commit/971b7169a43db382dbdeb6bb9bab02ab56e0351c))
23
+ * **merge:** the manual job needs the right of the merge ([7ba685e](https://github.com/ManotLuijiu/pi-harness-runtime/commit/7ba685ec43d815f90aa35b742579e87e76c69209))
24
+ * **merge:** the merge of the pull request is the GraphQL mutation ([972bab4](https://github.com/ManotLuijiu/pi-harness-runtime/commit/972bab4b0f7faf1b0047f3bb767ef6db3de4b2fa))
25
+ * **merge:** the workflow merges the pull request the gates cleared ([d7639ec](https://github.com/ManotLuijiu/pi-harness-runtime/commit/d7639ec9b83723510acb4a5c4e23d5d6167c3af6))
26
+ * **release:** a manual release keeps the version of its manifest ([122e661](https://github.com/ManotLuijiu/pi-harness-runtime/commit/122e661ea48cb8aff14c71dbd0221a4cf0ba4c8e))
27
+ * **release:** the release scans the tree of the commit it publishes ([7d47a0b](https://github.com/ManotLuijiu/pi-harness-runtime/commit/7d47a0b5406480a1e1d3a52623a76aabd1e2d0ba))
28
+
29
+ ### [1.1.451](https://github.com/ManotLuijiu/pi-harness-runtime/compare/v1.1.448...v1.1.451) (2026-10-27)
30
+
31
+
32
+ ### Features
33
+
34
+ * **jev:** the pending question carries the ask and the answer ([a792133](https://github.com/ManotLuijiu/pi-harness-runtime/commit/a792133e517fb8c12d3363e98942f3af30270f27))
35
+ * **merge:** a manual run can leave the merge to the pull request event ([b3d1950](https://github.com/ManotLuijiu/pi-harness-runtime/commit/b3d1950b5941c78ccd56951acdafd1d4afbe1bff))
36
+
37
+
38
+ ### Bug Fixes
39
+
40
+ * **hooks:** a tag is checked through the commit behind it ([c823e8e](https://github.com/ManotLuijiu/pi-harness-runtime/commit/c823e8e1f6973c62b5449a97c101759a4d5cbba5))
41
+ * **jev:** the alternatives of a question are parsed without a quadratic pattern ([63016a3](https://github.com/ManotLuijiu/pi-harness-runtime/commit/63016a3f0488acb801990dd46770c00d382b5858))
42
+ * **keys:** the key directory is audited as a tree ([081cc75](https://github.com/ManotLuijiu/pi-harness-runtime/commit/081cc75ac8ceff36b0d619c3f76371dc08459d3c))
43
+ * **merge:** the boolean input is read as a boolean ([971b716](https://github.com/ManotLuijiu/pi-harness-runtime/commit/971b7169a43db382dbdeb6bb9bab02ab56e0351c))
44
+ * **merge:** the manual job needs the right of the merge ([7ba685e](https://github.com/ManotLuijiu/pi-harness-runtime/commit/7ba685ec43d815f90aa35b742579e87e76c69209))
45
+ * **merge:** the merge of the pull request is the GraphQL mutation ([972bab4](https://github.com/ManotLuijiu/pi-harness-runtime/commit/972bab4b0f7faf1b0047f3bb767ef6db3de4b2fa))
46
+ * **merge:** the workflow merges the pull request the gates cleared ([d7639ec](https://github.com/ManotLuijiu/pi-harness-runtime/commit/d7639ec9b83723510acb4a5c4e23d5d6167c3af6))
47
+ * **release:** a manual release keeps the version of its manifest ([122e661](https://github.com/ManotLuijiu/pi-harness-runtime/commit/122e661ea48cb8aff14c71dbd0221a4cf0ba4c8e))
48
+ * **release:** the release scans the tree of the commit it publishes ([7d47a0b](https://github.com/ManotLuijiu/pi-harness-runtime/commit/7d47a0b5406480a1e1d3a52623a76aabd1e2d0ba))
49
+
5
50
  ### [1.1.450](https://github.com/ManotLuijiu/pi-harness-runtime/compare/v1.1.448...v1.1.450) (2026-10-27)
6
51
 
7
52
 
package/index.ts CHANGED
@@ -253,6 +253,9 @@ function keyFileFixesOrFallback(feature: string, labels: string[]): string[] {
253
253
  const sharedState = {
254
254
  waitingForUserSince: null as Date | null,
255
255
  todoContinuationController: null as unknown,
256
+ // The ledger of the validation runs. The two-phase workflow reads it to
257
+ // know whether the final tier of a test item passed.
258
+ validationLedger: null as unknown,
256
259
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
257
260
  updateAutoContinueStatus: null as any,
258
261
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -439,6 +442,43 @@ async function loadTwoPhaseModule(): Promise<TwoPhaseModule | null> {
439
442
  }
440
443
  }
441
444
 
445
+ /**
446
+ * Measure the scope of a request in the workspace.
447
+ *
448
+ * The measurement is the only source of the numbers the delegation
449
+ * assessment uses: the request names the files, or the session changed
450
+ * them. When nothing can be measured the result is null, and the caller
451
+ * says that instead of inventing a scope.
452
+ */
453
+ async function measureDelegationScope(
454
+ userMessage: string,
455
+ ): Promise<import("./packages/jev-judge/dist/delegation-harness.js").ScopeFacts | null> {
456
+ try {
457
+ const scope = await import("./packages/jev-judge/dist/delegation-scope.js");
458
+ const harness = await import("./packages/jev-judge/dist/delegation-harness.js");
459
+ const measurement = scope.measureScope({
460
+ workspacePath: _piSessionCwd || process.cwd(),
461
+ hints: scope.scopeHintsFromText(userMessage),
462
+ });
463
+ if (measurement.files.length === 0) return null;
464
+ return harness.scopeFactsFromMeasurement(measurement);
465
+ } catch (error) {
466
+ logToFile("[Delegation]", "Scope measurement failed:", oneLineError(error));
467
+ return null;
468
+ }
469
+ }
470
+
471
+ /**
472
+ * The decision of the delegation assessment, as the harness returns it.
473
+ */
474
+ type DelegationDecisionLike = {
475
+ strategy: string;
476
+ reason: string;
477
+ workGroups?: { id: string; files: string[] }[];
478
+ dispatched?: boolean;
479
+ dispatchTool?: string;
480
+ };
481
+
442
482
  /**
443
483
  * Register the two-phase workflow in the session.
444
484
  *
@@ -473,10 +513,55 @@ async function initTwoPhaseReview(pi: ExtensionAPI): Promise<void> {
473
513
  }
474
514
  };
475
515
 
516
+ /**
517
+ * Read the validation state of the test items of the session.
518
+ *
519
+ * The ledger is the only mechanical source for "did the final tier
520
+ * pass?". The workflow never runs a test itself, it only reads what the
521
+ * ledger recorded.
522
+ */
523
+ const readValidation = async (todos: TwoPhaseTodoLike[]) => {
524
+ const ledger = sharedState.validationLedger as
525
+ | import("./packages/jev-judge/dist/validation-ledger.js").ValidationLedger
526
+ | null
527
+ | undefined;
528
+ if (!ledger) {
529
+ return {
530
+ available: false,
531
+ pendingFinalTier: 0,
532
+ failedFinalTier: 0,
533
+ incompletePlan: 0,
534
+ reason: "the validation ledger is not initialized",
535
+ };
536
+ }
537
+ let pendingFinalTier = 0;
538
+ let failedFinalTier = 0;
539
+ let incompletePlan = 0;
540
+ for (const todo of todos) {
541
+ if (todo.id === undefined) continue;
542
+ if (mod.classifyTodo(todo) !== "test") continue;
543
+ const state = ledger.stateFor(todo as never);
544
+ if (state.planIncomplete) {
545
+ incompletePlan += 1;
546
+ continue;
547
+ }
548
+ if (!state.finalTierRequired) continue;
549
+ const status = state.full.status;
550
+ if (status === "passed") continue;
551
+ if (status === "failed" || status === "timeout" || status === "error") {
552
+ failedFinalTier += 1;
553
+ } else {
554
+ pendingFinalTier += 1;
555
+ }
556
+ }
557
+ return { available: true, pendingFinalTier, failedFinalTier, incompletePlan };
558
+ };
559
+
476
560
  try {
477
561
  await mod.initTwoPhaseReview({
478
562
  pi,
479
563
  readTodos,
564
+ readValidation,
480
565
  workspaceRoot: () => {
481
566
  try {
482
567
  return realpathSync(process.cwd());
@@ -748,6 +833,12 @@ async function initAutoContinue(pi: ExtensionAPI): Promise<void> {
748
833
  });
749
834
  logStartup("[pi-harness] Delegation harness initialized");
750
835
 
836
+ // The lifecycle of a continuation the harness started itself: the
837
+ // status stays "RUN" until the turn settles.
838
+ const { ContinuationGate } = await import(
839
+ "./packages/jev-judge/dist/continuation-status.js"
840
+ );
841
+
751
842
  // The Jev key is missing: the fix commands must survive every later
752
843
  // message with the same label - `HarnessStartupStatus.update` replaces
753
844
  // a message by label, so a later "verifying API..." line would drop
@@ -774,6 +865,44 @@ async function initAutoContinue(pi: ExtensionAPI): Promise<void> {
774
865
  let lastUserMessage: Date | null = null;
775
866
  let autoContinueTimer: NodeJS.Timeout | null = null;
776
867
  let _inferenceInProgress = false; // Track for overlap prevention
868
+ // A continuation was dispatched and its turn has not settled yet.
869
+ // The gate owns that state: the status stays "RUN" until the turn
870
+ // settles, and no timer may clear it in the meantime.
871
+ const _continuation = new ContinuationGate();
872
+ // The measured scope that was already handed to the agent. The
873
+ // plan is sent once per scope, not once per message.
874
+ let _lastDelegationPlanFingerprint: string | null = null;
875
+
876
+ /**
877
+ * Hand the measured delegation plan to the agent.
878
+ *
879
+ * The harness measured the scope; the agent is the one that sends
880
+ * the groups to the sub-agent tool. The message says so, and the
881
+ * plan is not repeated for a scope the agent already knows.
882
+ */
883
+ async function handDelegationPlanToAgent(
884
+ decision: DelegationDecisionLike | null,
885
+ fingerprint: string,
886
+ ): Promise<void> {
887
+ if (!decision) return;
888
+ if (_lastDelegationPlanFingerprint === fingerprint) return;
889
+ let message: string | null = null;
890
+ try {
891
+ const harness = await import("./packages/jev-judge/dist/delegation-harness.js");
892
+ message = harness.buildDelegationSteer(decision);
893
+ } catch (error) {
894
+ logToFile("[Delegation]", "The delegation plan could not be built:", oneLineError(error));
895
+ return;
896
+ }
897
+ if (!message) return;
898
+ _lastDelegationPlanFingerprint = fingerprint;
899
+ try {
900
+ await pi.sendUserMessage(message, { deliverAs: "steer" });
901
+ logToFile("[Delegation]", "The measured delegation plan was handed to the agent");
902
+ } catch (error) {
903
+ logToFile("[Delegation]", "The delegation steer failed:", oneLineError(error));
904
+ }
905
+ }
777
906
  let _sessionId = Date.now().toString(36); // Track session for cancellation
778
907
  let _lastNotifiedQuestion = ""; // Track last question to prevent repeat notifications
779
908
  let _completedNotifiedFor: string | null = null; // Track completion notification deduplication
@@ -1478,8 +1607,11 @@ async function initAutoContinue(pi: ExtensionAPI): Promise<void> {
1478
1607
  sharedState.waitingForUserSince = null;
1479
1608
  _lastNotifiedQuestion = "";
1480
1609
 
1481
- // Clear status after short delay
1482
- setTimeout(() => updateAutoContinueStatus("IDLE"), 2000);
1610
+ // The status stays "RUN" until the turn that the steer
1611
+ // started has settled. A timer would clear it while the
1612
+ // agent is still working, and the status would claim
1613
+ // that nothing runs any more.
1614
+ _continuation.begin();
1483
1615
  }
1484
1616
  _inferenceInProgress = false;
1485
1617
  } catch {
@@ -1523,6 +1655,9 @@ async function initAutoContinue(pi: ExtensionAPI): Promise<void> {
1523
1655
  );
1524
1656
  },
1525
1657
  });
1658
+ // The two-phase workflow reads the same ledger: the phase gate
1659
+ // and the completion report must never disagree about a tier.
1660
+ sharedState.validationLedger = validationLedger;
1526
1661
  // SAFETY: ctx is ExtensionContext with sessionManager, isIdle, hasPendingMessages
1527
1662
  // dispatch uses pi.sendUserMessage for actual message delivery
1528
1663
  todoContinuationController = new TodoContinuationController(
@@ -1546,6 +1681,7 @@ async function initAutoContinue(pi: ExtensionAPI): Promise<void> {
1546
1681
  // keep a timer alive nor block a later continuation.
1547
1682
  todoContinuationController?.dispose?.();
1548
1683
  todoContinuationController = null;
1684
+ sharedState.validationLedger = null;
1549
1685
  });
1550
1686
 
1551
1687
  // --- task-intake: Automatic todo capture from user requests -----------
@@ -1637,25 +1773,33 @@ async function initAutoContinue(pi: ExtensionAPI): Promise<void> {
1637
1773
  ];
1638
1774
  const isLargeRefactor = largeRefactorPatterns.some((p) => p.test(lowerMsg));
1639
1775
  if (isLargeRefactor) {
1640
- logToFile("[Delegation]", "Large refactor request detected, triggering assessment");
1641
- // Trigger delegation assessment asynchronously
1642
- sharedState.delegationHarness.assess({
1643
- estimatedFiles: 10,
1644
- observedFiles: 10,
1645
- estimatedCallSites: 50,
1646
- observedCallSites: 50,
1647
- independentComponents: 2,
1648
- tightlyCoupled: false,
1649
- isMechanical: false,
1650
- isComplex: true,
1651
- fileTypes: {},
1652
- fingerprint: `user-msg-${Date.now()}`,
1653
- }, "user-request").then((decision: { strategy: string; reason: string } | null) => {
1654
- if (decision) {
1655
- logToFile("[Delegation]", `Assessment result: ${decision.strategy} - ${decision.reason}`);
1656
- }
1657
- }).catch((e: unknown) => logToFile("[Delegation]", "Assessment error:", String(e)));
1658
- }
1776
+ // The scope is measured in the workspace: the request
1777
+ // names the files, or the session changed them. A
1778
+ // number that nobody counted is not a scope, so the
1779
+ // assessment waits for the measurement.
1780
+ logToFile("[Delegation]", "Large refactor request detected, measuring the scope");
1781
+ measureDelegationScope(userMessage)
1782
+ .then((facts) => {
1783
+ if (!facts) {
1784
+ logToFile("[Delegation]", "No measurable scope in the request - no delegation assessment");
1785
+ return null;
1786
+ }
1787
+ return sharedState.delegationHarness
1788
+ .assess(facts, `user-request-${facts.fingerprint}`)
1789
+ .then(async (decision: DelegationDecisionLike | null) => {
1790
+ if (decision) {
1791
+ logToFile(
1792
+ "[Delegation]",
1793
+ `Assessment: ${decision.strategy} - ${decision.reason} (${decision.workGroups?.length ?? 0} measured group(s), dispatched=${decision.dispatched === true})`,
1794
+ );
1795
+ }
1796
+ await handDelegationPlanToAgent(decision, facts.fingerprint);
1797
+ return decision;
1798
+ })
1799
+ .catch((e: unknown) => logToFile("[Delegation]", "Assessment error:", String(e)));
1800
+ })
1801
+ .catch((e: unknown) => logToFile("[Delegation]", "Scope measurement failed:", String(e)));
1802
+ }
1659
1803
  }
1660
1804
 
1661
1805
  // Temporary pause for user input processing (auto-clears on next decision)
@@ -1721,6 +1865,13 @@ async function initAutoContinue(pi: ExtensionAPI): Promise<void> {
1721
1865
  // consuming the retry budget.
1722
1866
  todoContinuationController?.releaseContinuationGuard?.("agent_settled");
1723
1867
 
1868
+ // The turn that a continuation started has settled: the status
1869
+ // returns to idle, unless the handler below dispatches the next
1870
+ // continuation.
1871
+ if (_continuation.settle()) {
1872
+ updateAutoContinueStatus("IDLE");
1873
+ }
1874
+
1724
1875
  // Don't continue if user is waiting for response
1725
1876
  if (waitingForUserSince) {
1726
1877
  todoContinuationController?.clearQueue();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-harness-runtime",
3
- "version": "1.1.450",
3
+ "version": "1.1.452",
4
4
  "description": "[BETA] Codex-style /usage status + autonomous coding harness for pi. Not production ready — expect breaking changes.",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/agent-policy",
3
- "version": "1.1.450",
3
+ "version": "1.1.452",
4
4
  "description": "Agent policy runtime for pi-harness - enforces mandatory rule delivery and mutation gating",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/auth",
3
- "version": "1.1.450",
3
+ "version": "1.1.452",
4
4
  "description": "pi-harness-runtime — auth",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/autonomous-runtime",
3
- "version": "1.1.450",
3
+ "version": "1.1.452",
4
4
  "description": "Autonomous Operations Runtime — task inbox, lease management, and worker lifecycle (RFC-0101)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/checkpoint",
3
- "version": "1.1.450",
3
+ "version": "1.1.452",
4
4
  "description": "Enhanced checkpoint system with incremental snapshots, diff-based storage, and recovery strategies",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/clipboard",
3
- "version": "1.1.450",
3
+ "version": "1.1.452",
4
4
  "type": "module",
5
5
  "main": "src/index.ts",
6
6
  "exports": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/codex-watcher",
3
- "version": "1.1.450",
3
+ "version": "1.1.452",
4
4
  "description": "Watches Codex CLI sessions and emits visible messages for PING-PONG monitoring",
5
5
  "type": "module",
6
6
  "main": "./dist/src/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moocoding/config-capture",
3
- "version": "1.1.450",
3
+ "version": "1.1.452",
4
4
  "description": "Auto-capture API-fetched configuration values to AGENTS.md",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/contracts",
3
- "version": "1.1.450",
3
+ "version": "1.1.452",
4
4
  "type": "module",
5
5
  "description": "RPC schema definitions for pi-harness runtime",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/cookie-sanitizer",
3
- "version": "1.1.450",
3
+ "version": "1.1.452",
4
4
  "description": "pi-harness-runtime — cookie sanitizer (provider-quota cookie file normaliser)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/evaluation-engine",
3
- "version": "1.1.450",
3
+ "version": "1.1.452",
4
4
  "description": "Evaluation engine for pi-harness runtime (RFC-0057)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/event-bus",
3
- "version": "1.1.450",
3
+ "version": "1.1.452",
4
4
  "type": "module",
5
5
  "main": "src/index.ts",
6
6
  "exports": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/event-store",
3
- "version": "1.1.450",
3
+ "version": "1.1.452",
4
4
  "type": "module",
5
5
  "main": "src/index.ts",
6
6
  "exports": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/experience-replay",
3
- "version": "1.1.450",
3
+ "version": "1.1.452",
4
4
  "description": "Experience replay for debugging and reproducibility (RFC-0059)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moocoding/file-copy-helper",
3
- "version": "1.1.450",
3
+ "version": "1.1.452",
4
4
  "description": "Injects cp/sudo cp rule when user asks to mimic/copy files",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/intent-analyzer",
3
- "version": "1.1.450",
3
+ "version": "1.1.452",
4
4
  "type": "module",
5
5
  "main": "src/index.ts",
6
6
  "exports": {
@@ -0,0 +1,31 @@
1
+ /**
2
+ * The lifecycle of a continuation that the harness dispatched.
3
+ *
4
+ * A continuation is a turn that the harness started itself. While that
5
+ * turn runs, the status says so: a timer that clears the status while the
6
+ * agent is still working would claim that nothing runs any more.
7
+ *
8
+ * The gate is the only thing that decides whether a continuation is still
9
+ * pending, so it can be read and tested without a session.
10
+ */
11
+ export declare class ContinuationGate {
12
+ private inFlight;
13
+ /**
14
+ * A continuation was dispatched.
15
+ *
16
+ * The status stays "RUN" from here until the turn settles.
17
+ */
18
+ begin(): void;
19
+ /**
20
+ * The turn settled.
21
+ *
22
+ * @returns True when a continuation was still pending, so the caller
23
+ * has to clear the status. False when there was nothing to clear.
24
+ */
25
+ settle(): boolean;
26
+ /**
27
+ * Whether a continuation is still running.
28
+ */
29
+ get pending(): boolean;
30
+ }
31
+ //# sourceMappingURL=continuation-status.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"continuation-status.d.ts","sourceRoot":"","sources":["../src/continuation-status.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,qBAAa,gBAAgB;IAC5B,OAAO,CAAC,QAAQ,CAAS;IAEzB;;;;OAIG;IACH,KAAK,IAAI,IAAI,CAEZ;IAED;;;;;OAKG;IACH,MAAM,IAAI,OAAO,CAIhB;IAED;;OAEG;IACH,IAAI,OAAO,IAAI,OAAO,CAErB;CACD"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * The lifecycle of a continuation that the harness dispatched.
3
+ *
4
+ * A continuation is a turn that the harness started itself. While that
5
+ * turn runs, the status says so: a timer that clears the status while the
6
+ * agent is still working would claim that nothing runs any more.
7
+ *
8
+ * The gate is the only thing that decides whether a continuation is still
9
+ * pending, so it can be read and tested without a session.
10
+ */
11
+ export class ContinuationGate {
12
+ inFlight = false;
13
+ /**
14
+ * A continuation was dispatched.
15
+ *
16
+ * The status stays "RUN" from here until the turn settles.
17
+ */
18
+ begin() {
19
+ this.inFlight = true;
20
+ }
21
+ /**
22
+ * The turn settled.
23
+ *
24
+ * @returns True when a continuation was still pending, so the caller
25
+ * has to clear the status. False when there was nothing to clear.
26
+ */
27
+ settle() {
28
+ if (!this.inFlight)
29
+ return false;
30
+ this.inFlight = false;
31
+ return true;
32
+ }
33
+ /**
34
+ * Whether a continuation is still running.
35
+ */
36
+ get pending() {
37
+ return this.inFlight;
38
+ }
39
+ }
40
+ //# sourceMappingURL=continuation-status.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"continuation-status.js","sourceRoot":"","sources":["../src/continuation-status.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,MAAM,OAAO,gBAAgB;IACpB,QAAQ,GAAG,KAAK,CAAC;IAEzB;;;;OAIG;IACH,KAAK;QACJ,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;IACtB,CAAC;IAED;;;;;OAKG;IACH,MAAM;QACL,IAAI,CAAC,IAAI,CAAC,QAAQ;YAAE,OAAO,KAAK,CAAC;QACjC,IAAI,CAAC,QAAQ,GAAG,KAAK,CAAC;QACtB,OAAO,IAAI,CAAC;IACb,CAAC;IAED;;OAEG;IACH,IAAI,OAAO;QACV,OAAO,IAAI,CAAC,QAAQ,CAAC;IACtB,CAAC;CACD"}