pi-harness-runtime 1.1.451 → 1.1.453
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/AGENTS.md +85 -3
- package/CHANGELOG.md +52 -0
- package/index.ts +248 -21
- package/package.json +1 -1
- package/packages/agent-policy/package.json +1 -1
- package/packages/auth/package.json +1 -1
- package/packages/autonomous-runtime/package.json +1 -1
- package/packages/checkpoint/package.json +1 -1
- package/packages/clipboard/package.json +1 -1
- package/packages/codex-watcher/package.json +1 -1
- package/packages/config-capture/package.json +1 -1
- package/packages/contracts/package.json +1 -1
- package/packages/cookie-sanitizer/package.json +1 -1
- package/packages/evaluation-engine/package.json +1 -1
- package/packages/event-bus/package.json +1 -1
- package/packages/event-store/package.json +1 -1
- package/packages/experience-replay/package.json +1 -1
- package/packages/file-copy-helper/package.json +1 -1
- package/packages/intent-analyzer/package.json +1 -1
- package/packages/jev-judge/dist/batch-plan.d.ts +150 -0
- package/packages/jev-judge/dist/batch-plan.d.ts.map +1 -0
- package/packages/jev-judge/dist/batch-plan.js +192 -0
- package/packages/jev-judge/dist/batch-plan.js.map +1 -0
- package/packages/jev-judge/dist/continuation-status.d.ts +31 -0
- package/packages/jev-judge/dist/continuation-status.d.ts.map +1 -0
- package/packages/jev-judge/dist/continuation-status.js +40 -0
- package/packages/jev-judge/dist/continuation-status.js.map +1 -0
- package/packages/jev-judge/dist/delegation-harness.d.ts +94 -1
- package/packages/jev-judge/dist/delegation-harness.d.ts.map +1 -1
- package/packages/jev-judge/dist/delegation-harness.js +247 -20
- package/packages/jev-judge/dist/delegation-harness.js.map +1 -1
- package/packages/jev-judge/dist/delegation-observer.d.ts +98 -0
- package/packages/jev-judge/dist/delegation-observer.d.ts.map +1 -0
- package/packages/jev-judge/dist/delegation-observer.js +152 -0
- package/packages/jev-judge/dist/delegation-observer.js.map +1 -0
- package/packages/jev-judge/dist/delegation-scope.d.ts +88 -0
- package/packages/jev-judge/dist/delegation-scope.d.ts.map +1 -0
- package/packages/jev-judge/dist/delegation-scope.js +218 -0
- package/packages/jev-judge/dist/delegation-scope.js.map +1 -0
- package/packages/jev-judge/dist/index.d.ts +4 -0
- package/packages/jev-judge/dist/index.d.ts.map +1 -1
- package/packages/jev-judge/dist/index.js +4 -0
- package/packages/jev-judge/dist/index.js.map +1 -1
- package/packages/jev-judge/dist/pilot.d.ts +127 -0
- package/packages/jev-judge/dist/pilot.d.ts.map +1 -0
- package/packages/jev-judge/dist/pilot.js +178 -0
- package/packages/jev-judge/dist/pilot.js.map +1 -0
- package/packages/jev-judge/dist/validation-plan.d.ts +18 -0
- package/packages/jev-judge/dist/validation-plan.d.ts.map +1 -1
- package/packages/jev-judge/dist/validation-plan.js +93 -4
- package/packages/jev-judge/dist/validation-plan.js.map +1 -1
- package/packages/jev-judge/package.json +1 -1
- package/packages/jev-judge/src/batch-plan.ts +273 -0
- package/packages/jev-judge/src/continuation-status.ts +41 -0
- package/packages/jev-judge/src/delegation-harness.ts +337 -47
- package/packages/jev-judge/src/delegation-observer.ts +181 -0
- package/packages/jev-judge/src/delegation-scope.ts +264 -0
- package/packages/jev-judge/src/index.ts +36 -0
- package/packages/jev-judge/src/pilot.ts +257 -0
- package/packages/jev-judge/src/validation-plan.ts +110 -4
- package/packages/knowledge-retrieval/package.json +1 -1
- package/packages/learning-engine/package.json +1 -1
- package/packages/logging/package.json +1 -1
- package/packages/memory-engine/package.json +1 -1
- package/packages/notification/package.json +1 -1
- package/packages/paperclip-integration/package.json +1 -1
- package/packages/performance-optimizer/package.json +1 -1
- package/packages/privilege-broker/package.json +1 -1
- package/packages/prompt-compiler/package.json +1 -1
- package/packages/providers/package.json +1 -1
- package/packages/qdrant-skills/package.json +1 -1
- package/packages/quota-manager/package.json +1 -1
- package/packages/relay/package.json +1 -1
- package/packages/skill-mcp-client/package.json +1 -1
- package/packages/skills/package.json +1 -1
- package/packages/todo-bd-sync/package.json +1 -1
- package/packages/token-estimation/package.json +1 -1
- package/packages/tooling-preference/package.json +1 -1
- package/packages/trajectory/package.json +1 -1
- package/packages/tui/package.json +1 -1
- package/packages/two-phase-review/dist/classify.d.ts +26 -1
- package/packages/two-phase-review/dist/classify.d.ts.map +1 -1
- package/packages/two-phase-review/dist/classify.js +42 -1
- package/packages/two-phase-review/dist/classify.js.map +1 -1
- package/packages/two-phase-review/dist/index.d.ts +4 -2
- package/packages/two-phase-review/dist/index.d.ts.map +1 -1
- package/packages/two-phase-review/dist/index.js +3 -2
- package/packages/two-phase-review/dist/index.js.map +1 -1
- package/packages/two-phase-review/dist/rules.d.ts +38 -3
- package/packages/two-phase-review/dist/rules.d.ts.map +1 -1
- package/packages/two-phase-review/dist/rules.js +83 -10
- package/packages/two-phase-review/dist/rules.js.map +1 -1
- package/packages/two-phase-review/dist/runtime.d.ts +17 -0
- package/packages/two-phase-review/dist/runtime.d.ts.map +1 -1
- package/packages/two-phase-review/dist/runtime.js +122 -22
- package/packages/two-phase-review/dist/runtime.js.map +1 -1
- package/packages/two-phase-review/dist/state.d.ts +15 -0
- package/packages/two-phase-review/dist/state.d.ts.map +1 -1
- package/packages/two-phase-review/dist/state.js +37 -2
- package/packages/two-phase-review/dist/state.js.map +1 -1
- package/packages/two-phase-review/dist/types.d.ts +2 -2
- package/packages/two-phase-review/dist/types.d.ts.map +1 -1
- package/packages/two-phase-review/dist/validation.d.ts +50 -0
- package/packages/two-phase-review/dist/validation.d.ts.map +1 -0
- package/packages/two-phase-review/dist/validation.js +54 -0
- package/packages/two-phase-review/dist/validation.js.map +1 -0
- package/packages/two-phase-review/package.json +1 -1
- package/packages/two-phase-review/src/classify.ts +47 -1
- package/packages/two-phase-review/src/index.ts +16 -0
- package/packages/two-phase-review/src/rules.ts +117 -9
- package/packages/two-phase-review/src/runtime.ts +151 -21
- package/packages/two-phase-review/src/state.ts +38 -2
- package/packages/two-phase-review/src/types.ts +2 -1
- package/packages/two-phase-review/src/validation.ts +86 -0
- package/packages/types/package.json +1 -1
- package/packages/webmcp-tester/package.json +1 -1
- package/packages/wiki/package.json +1 -1
- package/packages/write-review/package.json +1 -1
package/AGENTS.md
CHANGED
|
@@ -46,8 +46,21 @@ A task with a cheap tier and an expensive tier MUST use the cheap tier while the
|
|
|
46
46
|
{
|
|
47
47
|
"metadata": {
|
|
48
48
|
"validationPlan": {
|
|
49
|
-
"fast": {
|
|
50
|
-
|
|
49
|
+
"fast": {
|
|
50
|
+
"command": "bun test tests/fast",
|
|
51
|
+
"expectedDurationMs": 18000,
|
|
52
|
+
"datasetSize": 100,
|
|
53
|
+
"owner": "the agent that runs the validation",
|
|
54
|
+
"passCriteria": ["every record is written once", "the counts of source and target match"],
|
|
55
|
+
"failureAction": "stop the job and report the failure of the pilot"
|
|
56
|
+
},
|
|
57
|
+
"full": {
|
|
58
|
+
"command": "bun test tests/full",
|
|
59
|
+
"expectedDurationMs": 2400000,
|
|
60
|
+
"datasetSize": 10000,
|
|
61
|
+
"owner": "the agent that runs the validation",
|
|
62
|
+
"scaleUpFrom": "fast"
|
|
63
|
+
},
|
|
51
64
|
"budgetMinutes": 90,
|
|
52
65
|
"finalTierRequired": true
|
|
53
66
|
}
|
|
@@ -57,6 +70,8 @@ A task with a cheap tier and an expensive tier MUST use the cheap tier while the
|
|
|
57
70
|
|
|
58
71
|
The harness never guesses a tier from a command string. Without a declared plan the current behaviour is unchanged.
|
|
59
72
|
|
|
73
|
+
A tier that declares `scaleUpFrom: "fast"` is the scale-up of a large job: it only counts when the fast tier passed. The fast tier of such a plan is the representative pilot and declares `"representative": true`.
|
|
74
|
+
|
|
60
75
|
2. **Iterate on the fast tier.** After every code edit, rerun the fast tier plus the focused unit/type checks the edit touches.
|
|
61
76
|
|
|
62
77
|
3. **Run the full tier once.** Run the full tier as the final acceptance check, when the implementation and the fast tier are stable.
|
|
@@ -71,7 +86,9 @@ A task with a cheap tier and an expensive tier MUST use the cheap tier while the
|
|
|
71
86
|
|
|
72
87
|
8. **The fast tier must be representative.** A small fixture keeps the shape of the data (optional fields, relations, uniqueness, boundary values, aggregates under test), not just the row count. Use deterministic data with cleanup so repeated runs do not accumulate rows.
|
|
73
88
|
|
|
74
|
-
9. **
|
|
89
|
+
9. **A large job starts with a measured pilot.** The pilot is the fast tier: it runs over a representative subset (100 records when that is safe) and it is measured, not assumed. The scale-up only starts when the pilot passed, the pilot ran on the declared number of records, and the run stayed inside the budget. A pilot that fails stops the job: report the finding instead of scaling up. The batches of a scale-up are bounded and explicit (`packages/jev-judge/src/batch-plan.ts`), a failed batch is retried instead of restarting the job, and the concurrency limit is always a declared number.
|
|
90
|
+
|
|
91
|
+
10. **Record every tier run in the ledger.** The harness never runs the tests, so the run is recorded by whoever knows the result:
|
|
75
92
|
|
|
76
93
|
```bash
|
|
77
94
|
bun scripts/validation-run.ts --item 12 --tier fast --status passed --duration 18s --dataset 100
|
|
@@ -83,6 +100,8 @@ A task with a cheap tier and an expensive tier MUST use the cheap tier while the
|
|
|
83
100
|
**What the harness does with the plan:**
|
|
84
101
|
|
|
85
102
|
- The continuation message carries the plan, the recorded tier state and the remaining budget, so the agent never re-runs a tier that already passed.
|
|
103
|
+
- A scale-up that ran before its pilot passed does not count: the final tier stays pending and the task stays open.
|
|
104
|
+
- The status line names the phase, the progress, the validation state and the children that really ran.
|
|
86
105
|
- When `budgetMinutes` is spent, the item pauses at a safe boundary: nothing is dispatched, the work stays open, and the reason is reported.
|
|
87
106
|
- When a todo with `finalTierRequired` is marked complete while that tier is pending or failed, the completion report is suppressed and the item is named instead. A todo may not be reported as done on the strength of an unreported run.
|
|
88
107
|
|
|
@@ -273,6 +292,18 @@ ls packages/notification/dist/
|
|
|
273
292
|
bun add -d typescript
|
|
274
293
|
```
|
|
275
294
|
|
|
295
|
+
**The canonical build**: the compiled output that is committed comes from
|
|
296
|
+
`bun scripts/build-packages.ts`, which uses the TypeScript of the root
|
|
297
|
+
(`node_modules/.bin/tsc`). A package-level `bun run build` uses the
|
|
298
|
+
TypeScript that the package itself installed, and the two write different
|
|
299
|
+
source maps for the same source. A package-level build therefore leaves
|
|
300
|
+
the tree with a `dist/` that the next canonical build changes again.
|
|
301
|
+
|
|
302
|
+
```bash
|
|
303
|
+
# The build whose output is committed
|
|
304
|
+
bun scripts/build-packages.ts
|
|
305
|
+
```
|
|
306
|
+
|
|
276
307
|
## Icon Usage
|
|
277
308
|
|
|
278
309
|
**This is a Node.js CLI daemon — do NOT add React as a dependency for icons.**
|
|
@@ -341,6 +372,57 @@ todo create "Replace Film icon with themed icon"
|
|
|
341
372
|
|
|
342
373
|
**IMPORTANT**: The `todo` tool and `bd` commands are DIFFERENT systems. Use `todo` for local task tracking.
|
|
343
374
|
|
|
375
|
+
**Who owns the todo list.** The **agent** creates the todos, the harness
|
|
376
|
+
only reads them. Two consequences:
|
|
377
|
+
|
|
378
|
+
- The kind of a todo is read from `metadata.kind` first and from the title
|
|
379
|
+
prefix second. An explicit kind is more reliable than a prefix, and the
|
|
380
|
+
agent can always set it.
|
|
381
|
+
- When the todo provider is not available, the harness says so: the status
|
|
382
|
+
line reads `[?] TODOS unavailable` and the agent is told that the phase
|
|
383
|
+
gate is not active. The harness never claims a gate it cannot enforce.
|
|
384
|
+
|
|
385
|
+
---
|
|
386
|
+
|
|
387
|
+
## Phase Model: Implementation, Review, Validation (CRITICAL)
|
|
388
|
+
|
|
389
|
+
A task with several steps runs in three phases, and the harness enforces
|
|
390
|
+
the order. The todo prefixes are the contract:
|
|
391
|
+
|
|
392
|
+
| Prefix | Meaning |
|
|
393
|
+
|--------|---------|
|
|
394
|
+
| `[IMPLEMENT]` | implementation work of the current request |
|
|
395
|
+
| `[REVIEW]` | the review of the task diff (one todo per chunk) |
|
|
396
|
+
| `[REVIEW-FIX]` | a confirmed finding that has to be fixed |
|
|
397
|
+
| `[TEST]` | the validation of the task (the tiers are declared in its metadata) |
|
|
398
|
+
|
|
399
|
+
**Phase 1 - implementation.** Every open `[IMPLEMENT]` todo (and every
|
|
400
|
+
unclassified todo) is implementation work. The phase ends when the list
|
|
401
|
+
shows finished implementation work.
|
|
402
|
+
|
|
403
|
+
**Phase 2 - review.** The review starts **on its own**: as soon as the
|
|
404
|
+
implementation is done and the task changed a file, the harness derives the
|
|
405
|
+
scope from the task diff, splits it into chunks and asks for one `[REVIEW]`
|
|
406
|
+
todo per chunk. You never have to queue a review item to get one. A task
|
|
407
|
+
that changed no file completes without a review - there is nothing to
|
|
408
|
+
review.
|
|
409
|
+
|
|
410
|
+
**Phase 3 - validation.** A `[TEST]` todo declares the validation plan of
|
|
411
|
+
the task in `metadata.validationPlan` (see the section above) and the
|
|
412
|
+
recorded runs in the validation ledger decide whether the task is done:
|
|
413
|
+
|
|
414
|
+
- The task is **not** complete while the required final tier is pending.
|
|
415
|
+
- The task is **not** complete when the final tier failed, timed out or
|
|
416
|
+
errored.
|
|
417
|
+
- A plan that requires a final tier but declares none is an error to
|
|
418
|
+
report, not a plan to complete.
|
|
419
|
+
- A validation state that cannot be read is never a validation that
|
|
420
|
+
passed.
|
|
421
|
+
|
|
422
|
+
So the order of a real task is: implement, review the diff, fix the
|
|
423
|
+
findings, run the tiers, record them, and only then report the task as
|
|
424
|
+
done.
|
|
425
|
+
|
|
344
426
|
---
|
|
345
427
|
|
|
346
428
|
### Habit: Detect New Tasks from User Prompts (CRITICAL)
|
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,58 @@
|
|
|
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.453](https://github.com/ManotLuijiu/pi-harness-runtime/compare/v1.1.448...v1.1.453) (2026-10-27)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
|
|
10
|
+
* **delegation:** a child is only a child when it really ran ([ac4e678](https://github.com/ManotLuijiu/pi-harness-runtime/commit/ac4e6782d7d4bc4941e458425c8a9f38a51fc9d4))
|
|
11
|
+
* **delegation:** the scope of a request is measured before it is judged ([684b6a5](https://github.com/ManotLuijiu/pi-harness-runtime/commit/684b6a55184f63d3be24c48dcecfb0b13410dc15))
|
|
12
|
+
* **jev:** the pending question carries the ask and the answer ([a792133](https://github.com/ManotLuijiu/pi-harness-runtime/commit/a792133e517fb8c12d3363e98942f3af30270f27))
|
|
13
|
+
* **merge:** a manual run can leave the merge to the pull request event ([b3d1950](https://github.com/ManotLuijiu/pi-harness-runtime/commit/b3d1950b5941c78ccd56951acdafd1d4afbe1bff))
|
|
14
|
+
* **phase:** the agent owns the todo list and the harness says so ([c489370](https://github.com/ManotLuijiu/pi-harness-runtime/commit/c489370bd7926aece8cb1a77aa20ca2d2672f1fb))
|
|
15
|
+
* **review:** the review starts on its own when the implementation is done ([04f51f5](https://github.com/ManotLuijiu/pi-harness-runtime/commit/04f51f599cd3222b6c74dd8fadb1373bd72d9bd7))
|
|
16
|
+
* **status:** the status line names the phase, the progress and the children ([0167bf3](https://github.com/ManotLuijiu/pi-harness-runtime/commit/0167bf396a0b987c1d44f3b869351976a951dd65))
|
|
17
|
+
* **test:** the task waits for the final tier of the validation plan ([fa147db](https://github.com/ManotLuijiu/pi-harness-runtime/commit/fa147dbf7d50d56e6ea48c6d8fbb4fcb190caf8a))
|
|
18
|
+
* **validation:** a large job starts with a measured pilot ([425964b](https://github.com/ManotLuijiu/pi-harness-runtime/commit/425964b04a3d4fa02adfabed47ff62c5ec2b9c28))
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
### Bug Fixes
|
|
22
|
+
|
|
23
|
+
* **hooks:** a tag is checked through the commit behind it ([c823e8e](https://github.com/ManotLuijiu/pi-harness-runtime/commit/c823e8e1f6973c62b5449a97c101759a4d5cbba5))
|
|
24
|
+
* **jev:** the alternatives of a question are parsed without a quadratic pattern ([63016a3](https://github.com/ManotLuijiu/pi-harness-runtime/commit/63016a3f0488acb801990dd46770c00d382b5858))
|
|
25
|
+
* **keys:** the key directory is audited as a tree ([081cc75](https://github.com/ManotLuijiu/pi-harness-runtime/commit/081cc75ac8ceff36b0d619c3f76371dc08459d3c))
|
|
26
|
+
* **merge:** the boolean input is read as a boolean ([971b716](https://github.com/ManotLuijiu/pi-harness-runtime/commit/971b7169a43db382dbdeb6bb9bab02ab56e0351c))
|
|
27
|
+
* **merge:** the manual job needs the right of the merge ([7ba685e](https://github.com/ManotLuijiu/pi-harness-runtime/commit/7ba685ec43d815f90aa35b742579e87e76c69209))
|
|
28
|
+
* **merge:** the merge of the pull request is the GraphQL mutation ([972bab4](https://github.com/ManotLuijiu/pi-harness-runtime/commit/972bab4b0f7faf1b0047f3bb767ef6db3de4b2fa))
|
|
29
|
+
* **merge:** the workflow merges the pull request the gates cleared ([d7639ec](https://github.com/ManotLuijiu/pi-harness-runtime/commit/d7639ec9b83723510acb4a5c4e23d5d6167c3af6))
|
|
30
|
+
* **release:** a manual release keeps the version of its manifest ([122e661](https://github.com/ManotLuijiu/pi-harness-runtime/commit/122e661ea48cb8aff14c71dbd0221a4cf0ba4c8e))
|
|
31
|
+
* **release:** the release scans the tree of the commit it publishes ([7d47a0b](https://github.com/ManotLuijiu/pi-harness-runtime/commit/7d47a0b5406480a1e1d3a52623a76aabd1e2d0ba))
|
|
32
|
+
|
|
33
|
+
### [1.1.452](https://github.com/ManotLuijiu/pi-harness-runtime/compare/v1.1.448...v1.1.452) (2026-10-27)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
### Features
|
|
37
|
+
|
|
38
|
+
* **delegation:** the scope of a request is measured before it is judged ([684b6a5](https://github.com/ManotLuijiu/pi-harness-runtime/commit/684b6a55184f63d3be24c48dcecfb0b13410dc15))
|
|
39
|
+
* **jev:** the pending question carries the ask and the answer ([a792133](https://github.com/ManotLuijiu/pi-harness-runtime/commit/a792133e517fb8c12d3363e98942f3af30270f27))
|
|
40
|
+
* **merge:** a manual run can leave the merge to the pull request event ([b3d1950](https://github.com/ManotLuijiu/pi-harness-runtime/commit/b3d1950b5941c78ccd56951acdafd1d4afbe1bff))
|
|
41
|
+
* **review:** the review starts on its own when the implementation is done ([04f51f5](https://github.com/ManotLuijiu/pi-harness-runtime/commit/04f51f599cd3222b6c74dd8fadb1373bd72d9bd7))
|
|
42
|
+
* **test:** the task waits for the final tier of the validation plan ([fa147db](https://github.com/ManotLuijiu/pi-harness-runtime/commit/fa147dbf7d50d56e6ea48c6d8fbb4fcb190caf8a))
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
### Bug Fixes
|
|
46
|
+
|
|
47
|
+
* **hooks:** a tag is checked through the commit behind it ([c823e8e](https://github.com/ManotLuijiu/pi-harness-runtime/commit/c823e8e1f6973c62b5449a97c101759a4d5cbba5))
|
|
48
|
+
* **jev:** the alternatives of a question are parsed without a quadratic pattern ([63016a3](https://github.com/ManotLuijiu/pi-harness-runtime/commit/63016a3f0488acb801990dd46770c00d382b5858))
|
|
49
|
+
* **keys:** the key directory is audited as a tree ([081cc75](https://github.com/ManotLuijiu/pi-harness-runtime/commit/081cc75ac8ceff36b0d619c3f76371dc08459d3c))
|
|
50
|
+
* **merge:** the boolean input is read as a boolean ([971b716](https://github.com/ManotLuijiu/pi-harness-runtime/commit/971b7169a43db382dbdeb6bb9bab02ab56e0351c))
|
|
51
|
+
* **merge:** the manual job needs the right of the merge ([7ba685e](https://github.com/ManotLuijiu/pi-harness-runtime/commit/7ba685ec43d815f90aa35b742579e87e76c69209))
|
|
52
|
+
* **merge:** the merge of the pull request is the GraphQL mutation ([972bab4](https://github.com/ManotLuijiu/pi-harness-runtime/commit/972bab4b0f7faf1b0047f3bb767ef6db3de4b2fa))
|
|
53
|
+
* **merge:** the workflow merges the pull request the gates cleared ([d7639ec](https://github.com/ManotLuijiu/pi-harness-runtime/commit/d7639ec9b83723510acb4a5c4e23d5d6167c3af6))
|
|
54
|
+
* **release:** a manual release keeps the version of its manifest ([122e661](https://github.com/ManotLuijiu/pi-harness-runtime/commit/122e661ea48cb8aff14c71dbd0221a4cf0ba4c8e))
|
|
55
|
+
* **release:** the release scans the tree of the commit it publishes ([7d47a0b](https://github.com/ManotLuijiu/pi-harness-runtime/commit/7d47a0b5406480a1e1d3a52623a76aabd1e2d0ba))
|
|
56
|
+
|
|
5
57
|
### [1.1.451](https://github.com/ManotLuijiu/pi-harness-runtime/compare/v1.1.448...v1.1.451) (2026-10-27)
|
|
6
58
|
|
|
7
59
|
|
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,79 @@ 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
|
+
|
|
560
|
+
/**
|
|
561
|
+
* The delegation state of the session.
|
|
562
|
+
*
|
|
563
|
+
* The numbers are the observed ones: the children that really ran and
|
|
564
|
+
* the groups the harness measured. A plan that was never dispatched
|
|
565
|
+
* shows zero dispatched children.
|
|
566
|
+
*/
|
|
567
|
+
const readDelegation = () => {
|
|
568
|
+
const harness = sharedState.delegationHarness as
|
|
569
|
+
| import("./packages/jev-judge/dist/delegation-harness.js").DelegationHarness
|
|
570
|
+
| null
|
|
571
|
+
| undefined;
|
|
572
|
+
if (!harness) return undefined;
|
|
573
|
+
try {
|
|
574
|
+
const receipt = harness.getReceipt();
|
|
575
|
+
const groups = receipt?.decision.workGroups?.length ?? 0;
|
|
576
|
+
const dispatched = harness.getChildren().length;
|
|
577
|
+
return { groups, dispatched };
|
|
578
|
+
} catch {
|
|
579
|
+
return undefined;
|
|
580
|
+
}
|
|
581
|
+
};
|
|
582
|
+
|
|
476
583
|
try {
|
|
477
584
|
await mod.initTwoPhaseReview({
|
|
478
585
|
pi,
|
|
479
586
|
readTodos,
|
|
587
|
+
readValidation,
|
|
588
|
+
readDelegation,
|
|
480
589
|
workspaceRoot: () => {
|
|
481
590
|
try {
|
|
482
591
|
return realpathSync(process.cwd());
|
|
@@ -748,6 +857,12 @@ async function initAutoContinue(pi: ExtensionAPI): Promise<void> {
|
|
|
748
857
|
});
|
|
749
858
|
logStartup("[pi-harness] Delegation harness initialized");
|
|
750
859
|
|
|
860
|
+
// The lifecycle of a continuation the harness started itself: the
|
|
861
|
+
// status stays "RUN" until the turn settles.
|
|
862
|
+
const { ContinuationGate } = await import(
|
|
863
|
+
"./packages/jev-judge/dist/continuation-status.js"
|
|
864
|
+
);
|
|
865
|
+
|
|
751
866
|
// The Jev key is missing: the fix commands must survive every later
|
|
752
867
|
// message with the same label - `HarnessStartupStatus.update` replaces
|
|
753
868
|
// a message by label, so a later "verifying API..." line would drop
|
|
@@ -774,6 +889,96 @@ async function initAutoContinue(pi: ExtensionAPI): Promise<void> {
|
|
|
774
889
|
let lastUserMessage: Date | null = null;
|
|
775
890
|
let autoContinueTimer: NodeJS.Timeout | null = null;
|
|
776
891
|
let _inferenceInProgress = false; // Track for overlap prevention
|
|
892
|
+
// A continuation was dispatched and its turn has not settled yet.
|
|
893
|
+
// The gate owns that state: the status stays "RUN" until the turn
|
|
894
|
+
// settles, and no timer may clear it in the meantime.
|
|
895
|
+
const _continuation = new ContinuationGate();
|
|
896
|
+
// The measured scope that was already handed to the agent. The
|
|
897
|
+
// plan is sent once per scope, not once per message.
|
|
898
|
+
let _lastDelegationPlanFingerprint: string | null = null;
|
|
899
|
+
|
|
900
|
+
/**
|
|
901
|
+
* Hand the measured delegation plan to the agent.
|
|
902
|
+
*
|
|
903
|
+
* The harness measured the scope; the agent is the one that sends
|
|
904
|
+
* the groups to the sub-agent tool. The message says so, and the
|
|
905
|
+
* plan is not repeated for a scope the agent already knows.
|
|
906
|
+
*/
|
|
907
|
+
async function handDelegationPlanToAgent(
|
|
908
|
+
decision: DelegationDecisionLike | null,
|
|
909
|
+
fingerprint: string,
|
|
910
|
+
): Promise<void> {
|
|
911
|
+
if (!decision) return;
|
|
912
|
+
if (_lastDelegationPlanFingerprint === fingerprint) return;
|
|
913
|
+
let message: string | null = null;
|
|
914
|
+
try {
|
|
915
|
+
const harness = await import("./packages/jev-judge/dist/delegation-harness.js");
|
|
916
|
+
message = harness.buildDelegationSteer(decision);
|
|
917
|
+
} catch (error) {
|
|
918
|
+
logToFile("[Delegation]", "The delegation plan could not be built:", oneLineError(error));
|
|
919
|
+
return;
|
|
920
|
+
}
|
|
921
|
+
if (!message) return;
|
|
922
|
+
_lastDelegationPlanFingerprint = fingerprint;
|
|
923
|
+
try {
|
|
924
|
+
await pi.sendUserMessage(message, { deliverAs: "steer" });
|
|
925
|
+
logToFile("[Delegation]", "The measured delegation plan was handed to the agent");
|
|
926
|
+
} catch (error) {
|
|
927
|
+
logToFile("[Delegation]", "The delegation steer failed:", oneLineError(error));
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
// The children of a delegation are observed, not assumed. The
|
|
932
|
+
// harness does not dispatch anything: the agent sends the measured
|
|
933
|
+
// groups to the sub-agent tool, and the tool events are the proof
|
|
934
|
+
// that a child really ran.
|
|
935
|
+
pi.on("tool_execution_start", (event) => {
|
|
936
|
+
try {
|
|
937
|
+
const toolEvent = event as {
|
|
938
|
+
toolCallId?: string;
|
|
939
|
+
toolName?: string;
|
|
940
|
+
args?: unknown;
|
|
941
|
+
};
|
|
942
|
+
const child = sharedState.delegationHarness?.observeToolStart?.({
|
|
943
|
+
toolCallId: String(toolEvent.toolCallId ?? ""),
|
|
944
|
+
toolName: String(toolEvent.toolName ?? ""),
|
|
945
|
+
args: toolEvent.args,
|
|
946
|
+
});
|
|
947
|
+
if (child) {
|
|
948
|
+
logToFile(
|
|
949
|
+
"[Delegation]",
|
|
950
|
+
`A child was dispatched: ${child.toolName} (${child.groupId ?? "no group"})`,
|
|
951
|
+
);
|
|
952
|
+
}
|
|
953
|
+
} catch (error) {
|
|
954
|
+
logToFile("[Delegation]", "The child could not be recorded:", oneLineError(error));
|
|
955
|
+
}
|
|
956
|
+
});
|
|
957
|
+
|
|
958
|
+
pi.on("tool_execution_end", (event) => {
|
|
959
|
+
try {
|
|
960
|
+
const toolEvent = event as {
|
|
961
|
+
toolCallId?: string;
|
|
962
|
+
toolName?: string;
|
|
963
|
+
result?: unknown;
|
|
964
|
+
isError?: boolean;
|
|
965
|
+
};
|
|
966
|
+
const child = sharedState.delegationHarness?.observeToolEnd?.({
|
|
967
|
+
toolCallId: String(toolEvent.toolCallId ?? ""),
|
|
968
|
+
toolName: String(toolEvent.toolName ?? ""),
|
|
969
|
+
result: toolEvent.result,
|
|
970
|
+
isError: toolEvent.isError === true,
|
|
971
|
+
});
|
|
972
|
+
if (child) {
|
|
973
|
+
logToFile(
|
|
974
|
+
"[Delegation]",
|
|
975
|
+
`The child ended: ${child.toolName} (${child.status})${child.model ? `, model ${child.model}` : ""}`,
|
|
976
|
+
);
|
|
977
|
+
}
|
|
978
|
+
} catch (error) {
|
|
979
|
+
logToFile("[Delegation]", "The child could not be closed:", oneLineError(error));
|
|
980
|
+
}
|
|
981
|
+
});
|
|
777
982
|
let _sessionId = Date.now().toString(36); // Track session for cancellation
|
|
778
983
|
let _lastNotifiedQuestion = ""; // Track last question to prevent repeat notifications
|
|
779
984
|
let _completedNotifiedFor: string | null = null; // Track completion notification deduplication
|
|
@@ -1478,8 +1683,11 @@ async function initAutoContinue(pi: ExtensionAPI): Promise<void> {
|
|
|
1478
1683
|
sharedState.waitingForUserSince = null;
|
|
1479
1684
|
_lastNotifiedQuestion = "";
|
|
1480
1685
|
|
|
1481
|
-
//
|
|
1482
|
-
|
|
1686
|
+
// The status stays "RUN" until the turn that the steer
|
|
1687
|
+
// started has settled. A timer would clear it while the
|
|
1688
|
+
// agent is still working, and the status would claim
|
|
1689
|
+
// that nothing runs any more.
|
|
1690
|
+
_continuation.begin();
|
|
1483
1691
|
}
|
|
1484
1692
|
_inferenceInProgress = false;
|
|
1485
1693
|
} catch {
|
|
@@ -1523,6 +1731,9 @@ async function initAutoContinue(pi: ExtensionAPI): Promise<void> {
|
|
|
1523
1731
|
);
|
|
1524
1732
|
},
|
|
1525
1733
|
});
|
|
1734
|
+
// The two-phase workflow reads the same ledger: the phase gate
|
|
1735
|
+
// and the completion report must never disagree about a tier.
|
|
1736
|
+
sharedState.validationLedger = validationLedger;
|
|
1526
1737
|
// SAFETY: ctx is ExtensionContext with sessionManager, isIdle, hasPendingMessages
|
|
1527
1738
|
// dispatch uses pi.sendUserMessage for actual message delivery
|
|
1528
1739
|
todoContinuationController = new TodoContinuationController(
|
|
@@ -1546,6 +1757,7 @@ async function initAutoContinue(pi: ExtensionAPI): Promise<void> {
|
|
|
1546
1757
|
// keep a timer alive nor block a later continuation.
|
|
1547
1758
|
todoContinuationController?.dispose?.();
|
|
1548
1759
|
todoContinuationController = null;
|
|
1760
|
+
sharedState.validationLedger = null;
|
|
1549
1761
|
});
|
|
1550
1762
|
|
|
1551
1763
|
// --- task-intake: Automatic todo capture from user requests -----------
|
|
@@ -1637,25 +1849,33 @@ async function initAutoContinue(pi: ExtensionAPI): Promise<void> {
|
|
|
1637
1849
|
];
|
|
1638
1850
|
const isLargeRefactor = largeRefactorPatterns.some((p) => p.test(lowerMsg));
|
|
1639
1851
|
if (isLargeRefactor) {
|
|
1640
|
-
|
|
1641
|
-
//
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1852
|
+
// The scope is measured in the workspace: the request
|
|
1853
|
+
// names the files, or the session changed them. A
|
|
1854
|
+
// number that nobody counted is not a scope, so the
|
|
1855
|
+
// assessment waits for the measurement.
|
|
1856
|
+
logToFile("[Delegation]", "Large refactor request detected, measuring the scope");
|
|
1857
|
+
measureDelegationScope(userMessage)
|
|
1858
|
+
.then((facts) => {
|
|
1859
|
+
if (!facts) {
|
|
1860
|
+
logToFile("[Delegation]", "No measurable scope in the request - no delegation assessment");
|
|
1861
|
+
return null;
|
|
1862
|
+
}
|
|
1863
|
+
return sharedState.delegationHarness
|
|
1864
|
+
.assess(facts, `user-request-${facts.fingerprint}`)
|
|
1865
|
+
.then(async (decision: DelegationDecisionLike | null) => {
|
|
1866
|
+
if (decision) {
|
|
1867
|
+
logToFile(
|
|
1868
|
+
"[Delegation]",
|
|
1869
|
+
`Assessment: ${decision.strategy} - ${decision.reason} (${decision.workGroups?.length ?? 0} measured group(s), dispatched=${decision.dispatched === true})`,
|
|
1870
|
+
);
|
|
1871
|
+
}
|
|
1872
|
+
await handDelegationPlanToAgent(decision, facts.fingerprint);
|
|
1873
|
+
return decision;
|
|
1874
|
+
})
|
|
1875
|
+
.catch((e: unknown) => logToFile("[Delegation]", "Assessment error:", String(e)));
|
|
1876
|
+
})
|
|
1877
|
+
.catch((e: unknown) => logToFile("[Delegation]", "Scope measurement failed:", String(e)));
|
|
1878
|
+
}
|
|
1659
1879
|
}
|
|
1660
1880
|
|
|
1661
1881
|
// Temporary pause for user input processing (auto-clears on next decision)
|
|
@@ -1721,6 +1941,13 @@ async function initAutoContinue(pi: ExtensionAPI): Promise<void> {
|
|
|
1721
1941
|
// consuming the retry budget.
|
|
1722
1942
|
todoContinuationController?.releaseContinuationGuard?.("agent_settled");
|
|
1723
1943
|
|
|
1944
|
+
// The turn that a continuation started has settled: the status
|
|
1945
|
+
// returns to idle, unless the handler below dispatches the next
|
|
1946
|
+
// continuation.
|
|
1947
|
+
if (_continuation.settle()) {
|
|
1948
|
+
updateAutoContinueStatus("IDLE");
|
|
1949
|
+
}
|
|
1950
|
+
|
|
1724
1951
|
// Don't continue if user is waiting for response
|
|
1725
1952
|
if (waitingForUserSince) {
|
|
1726
1953
|
todoContinuationController?.clearQueue();
|
package/package.json
CHANGED