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.
- package/AGENTS.md +53 -0
- package/CHANGELOG.md +45 -0
- package/index.ts +172 -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/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 +74 -1
- package/packages/jev-judge/dist/delegation-harness.d.ts.map +1 -1
- package/packages/jev-judge/dist/delegation-harness.js +183 -18
- package/packages/jev-judge/dist/delegation-harness.js.map +1 -1
- 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/package.json +1 -1
- package/packages/jev-judge/src/continuation-status.ts +41 -0
- package/packages/jev-judge/src/delegation-harness.ts +254 -45
- package/packages/jev-judge/src/delegation-scope.ts +264 -0
- 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 +22 -1
- package/packages/two-phase-review/dist/classify.d.ts.map +1 -1
- package/packages/two-phase-review/dist/classify.js +37 -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 +8 -3
- package/packages/two-phase-review/dist/rules.d.ts.map +1 -1
- package/packages/two-phase-review/dist/rules.js +36 -5
- package/packages/two-phase-review/dist/rules.js.map +1 -1
- package/packages/two-phase-review/dist/runtime.d.ts +9 -0
- package/packages/two-phase-review/dist/runtime.d.ts.map +1 -1
- package/packages/two-phase-review/dist/runtime.js +68 -17
- 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 +38 -1
- package/packages/two-phase-review/src/index.ts +16 -0
- package/packages/two-phase-review/src/rules.ts +48 -4
- package/packages/two-phase-review/src/runtime.ts +88 -17
- 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
|
@@ -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
|
-
//
|
|
1482
|
-
|
|
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
|
-
|
|
1641
|
-
//
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
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
|
@@ -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"}
|