@evoclock/pi-agentic-driver 0.9.1 → 0.9.2

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 (38) hide show
  1. package/.agentic-driver/router.defaults.json +64 -0
  2. package/LICENSE +11 -8
  3. package/README.md +92 -32
  4. package/extensions/attended-authority-guard.ts +1 -1
  5. package/extensions/code-phage.js +1 -1
  6. package/extensions/herdr-communication.ts +1 -1
  7. package/extensions/herdr-dispatch.ts +1 -1
  8. package/extensions/herdr-lifecycle.ts +1 -1
  9. package/extensions/linux-microvm.ts +1 -1
  10. package/extensions/pulse.ts +33 -2
  11. package/extensions/task-board.ts +1 -1
  12. package/lib/adapters/diff-scope.mjs +1 -1
  13. package/lib/adapters/evidence.mjs +1 -1
  14. package/lib/adapters/narrative.mjs +1 -1
  15. package/lib/adapters/review-feedback.mjs +1 -1
  16. package/lib/adapters/visualization.mjs +1 -1
  17. package/lib/code-phage-core.mjs +1 -1
  18. package/lib/python_ast_metrics.py +1 -1
  19. package/lib/typescript_ast_metrics.mjs +1 -1
  20. package/package.json +12 -2
  21. package/scripts/aidr_writing_review.js +1 -1
  22. package/scripts/enforcement/attended_authority_guard.js +1 -1
  23. package/scripts/enforcement/guest_containment_taxonomy.v1.json +1 -1
  24. package/scripts/enforcement/herdr_async_dispatch_pi.js +1 -1
  25. package/scripts/enforcement/herdr_communication_pi.js +13 -8
  26. package/scripts/enforcement/herdr_lifecycle_pi.js +3 -3
  27. package/scripts/enforcement/linux_microvm_cutover_pi.js +1 -1
  28. package/scripts/enforcement/native_tui_context.js +1 -1
  29. package/scripts/enforcement/pulse_core_pi.js +38 -14
  30. package/scripts/enforcement/pulse_scheduler_pi.js +188 -25
  31. package/scripts/enforcement/router_engine_pi.js +483 -0
  32. package/scripts/enforcement/router_ranking_pi.js +146 -0
  33. package/scripts/enforcement/router_replacement_pi.js +98 -0
  34. package/scripts/enforcement/router_runtime_pi.js +299 -0
  35. package/scripts/enforcement/router_schemas_pi.js +604 -0
  36. package/scripts/enforcement/router_store_pi.js +438 -0
  37. package/scripts/enforcement/task_board_core_pi.js +255 -44
  38. package/skills/pulse/SKILL.md +1 -1
@@ -0,0 +1,64 @@
1
+ {
2
+ "schema": "agentic-driver.router-config.v1",
3
+ "revision": 1,
4
+ "endpoints": {
5
+ "local-default": {
6
+ "url": "http://127.0.0.1:8080",
7
+ "kind": "llama-server"
8
+ }
9
+ },
10
+ "seats": [
11
+ {
12
+ "schema": "agentic-driver.seat.v1",
13
+ "seatId": "local-default",
14
+ "kind": "local",
15
+ "provider": "llama-server",
16
+ "accountId": null,
17
+ "endpointRef": "local-default",
18
+ "model": "replace-with-installed-model",
19
+ "capabilities": ["implement", "review"],
20
+ "containmentTier": "none",
21
+ "maxConcurrency": 1,
22
+ "costClass": "free",
23
+ "quotaCollector": null,
24
+ "clusterMembership": null,
25
+ "enabled": false,
26
+ "deprecated": null
27
+ }
28
+ ],
29
+ "models": {
30
+ "replace-with-installed-model": {
31
+ "aliases": [],
32
+ "contextWindow": 32768,
33
+ "capabilities": ["implement", "review"]
34
+ }
35
+ },
36
+ "collectors": {},
37
+ "eligibility": {
38
+ "rules": [],
39
+ "reserve": {
40
+ "floorPercent": 40,
41
+ "scope": "account-window",
42
+ "coldStartFraction": 0.25,
43
+ "estimateSamples": 20,
44
+ "estimateMinSamples": 5,
45
+ "estimateOutlierSigma": 3,
46
+ "ownerInteractiveOverride": false
47
+ }
48
+ },
49
+ "preferences": {
50
+ "order": [{ "seatId": "local-default" }],
51
+ "costPolicy": "free-first"
52
+ },
53
+ "ranking": {
54
+ "enabled": false,
55
+ "janusUrl": "http://127.0.0.1:7431",
56
+ "maxCandidates": 8,
57
+ "fallback": "preference-order"
58
+ },
59
+ "localHealth": {
60
+ "probeSeconds": 60,
61
+ "warmStateTracking": true
62
+ },
63
+ "secrets": {}
64
+ }
package/LICENSE CHANGED
@@ -723,14 +723,17 @@ on the Software.
723
723
  COMMERCIAL LICENCE OPTION
724
724
  ================================================================================
725
725
 
726
- A commercial licence that waives the obligations of the GNU Affero
727
- General Public License (including the network-use source-disclosure
728
- obligation of Section 13 and the additional terms of Section 7(b)
729
- above) is available for users who cannot or do not wish to comply
730
- with the terms of the GNU Affero General Public License.
731
-
732
- The commercial licence is intended for for-profit entities and any
733
- use in a paid product or service. Contact the author for details.
726
+ Commercial use, modification, and distribution are permitted under
727
+ the GNU Affero General Public License when all AGPL obligations and
728
+ the additional terms of Section 7(b) above are followed. This includes
729
+ the network-use source-disclosure obligation of Section 13 and the
730
+ required preservation of attribution.
731
+
732
+ A separate commercial licence is available for organisations that
733
+ require proprietary modifications, do not wish to disclose covered
734
+ source as required by the AGPL, need alternative attribution terms, or
735
+ otherwise cannot or do not wish to comply with the AGPL. Contact the
736
+ author for details.
734
737
 
735
738
  When published, the commercial licence template will be available at
736
739
  COMMERCIAL.md in this repository.
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # pi-agentic-driver v0.9.0
1
+ # pi-agentic-driver
2
2
 
3
3
  <p align="center">
4
4
  <img src="assets/Yamagane-origami.png" alt="pi-agentic-driver, Yamagane origami mark" width="140"/>
@@ -7,7 +7,6 @@
7
7
  <p align="center">
8
8
  <a href="LICENSE"><img src="https://img.shields.io/badge/license-AGPL%20v3-blue?style=flat" alt="License: AGPL v3"/></a>
9
9
  <a href="https://www.npmjs.com/package/@evoclock/pi-agentic-driver"><img src="https://img.shields.io/npm/v/@evoclock/pi-agentic-driver?style=flat" alt="npm version"/></a>
10
- <img src="https://img.shields.io/badge/version-0.9.0-blue?style=flat" alt="Version 0.9.0"/>
11
10
  <img src="https://img.shields.io/badge/status-active%20development%20%26%20testing-orange?style=flat" alt="Status"/>
12
11
  <img src="https://img.shields.io/badge/JavaScript-F7DF1E?style=flat&logo=javascript&logoColor=black" alt="JavaScript"/>
13
12
  <img src="https://img.shields.io/badge/TypeScript-3178C6?style=flat&logo=typescript&logoColor=white" alt="TypeScript"/>
@@ -53,6 +52,7 @@ proofs for agentic workflows.
53
52
  | `agentic_kanban_board_write` | Adds cards to the board through the trusted writer, which records who authorized the work. | shipped |
54
53
  | `agentic_kanban_board_update` | Moves, closes, flags, edits, or removes cards on the board, always recording who authorized the change. | shipped |
55
54
  | `agentic_kanban_board_dispatch` | Claims an eligible board card for automated contained work and creates its assignment envelope. | shipped |
55
+ | `agentic_kanban_pulse` | Checks for ready work and available capacity, then starts assignments under the board policy. | shipped |
56
56
 
57
57
  **Status: active development and testing.** Each extension ships only after
58
58
  it passes fixture-based acceptance, native tests, live-session checks, and
@@ -66,7 +66,7 @@ components are listed here for transparency and are not packaged.
66
66
  <details>
67
67
  <summary><strong>code-phage, advisory code review</strong> <em>(released, 0.1.1)</em></summary>
68
68
 
69
- `code_phage` reviews a proposed change against a stated goal before the agent
69
+ The `code_phage` tool reviews a proposed change against a stated goal before the agent
70
70
  writes or commits code. Give it a goal, candidate files, accepted
71
71
  requirements, and test paths. It will:
72
72
 
@@ -109,8 +109,10 @@ only, not a runtime dependency).
109
109
  <details>
110
110
  <summary><strong>herdr-communication, controlled role communication</strong> <em>(released, 0.2.1)</em></summary>
111
111
 
112
- `agentic_herdr_communication` exchanges controlled, marked reports with
113
- configured Pi worker roles running under [Herdr](https://herdr.dev/) 0.8.2.
112
+ The `agentic_herdr_communication` tool exchanges controlled, marked reports with
113
+ configured Pi worker roles running under [Herdr](https://herdr.dev/) (validated
114
+ against Herdr 0.9.1; the trust seam accepts the Homebrew-managed herdr binary
115
+ across versions rather than pinning one).
114
116
 
115
117
  - **List and observe.** Worker roles are filtered to trusted repositories: a
116
118
  checked-in registry plus canonical-path validation. Unlisted or
@@ -133,7 +135,7 @@ configured Pi worker roles running under [Herdr](https://herdr.dev/) 0.8.2.
133
135
  <details>
134
136
  <summary><strong>herdr-lifecycle, role-labelled worker dispatch</strong> <em>(released, 0.2.1)</em></summary>
135
137
 
136
- `agentic_herdr_spawn_worker` turns one natural-language request into Herdr's
138
+ The `agentic_herdr_spawn_worker` tool turns one natural-language request into Herdr's
137
139
  native lifecycle. Choose a placement (`right`, `below`, or `tab`), a safe role
138
140
  label, a model from the active Pi model roster, and a trusted repository. The
139
141
  extension:
@@ -165,7 +167,7 @@ for the wider task and model-routing workflow.
165
167
  <details>
166
168
  <summary><strong>herdr-dispatch, continuous worker journeys</strong> <em>(released, 0.5.0)</em></summary>
167
169
 
168
- `agentic_worker_dispatch` runs controlled worker journeys and observes worker
170
+ The `agentic_worker_dispatch` tool runs controlled worker journeys and observes worker
169
171
  liveness. Two actions:
170
172
 
171
173
  - **pulse** reports whether a worker role is alive, its current state, and
@@ -192,7 +194,7 @@ silently, and return results as untrusted evidence.
192
194
 
193
195
 
194
196
  <details>
195
- <summary><strong>task board, planned and automated work you can see</strong> <em>(released, 0.9.0)</em></summary>
197
+ <summary><strong>task board and Pulse, planned and automated work you can see</strong> <em>(released, 0.9.1)</em></summary>
196
198
 
197
199
  Keep planned work in canonical `TASKS.md`. You can read it directly or use
198
200
  the generated `board.md` view in Obsidian. Agents use the same canonical
@@ -216,10 +218,10 @@ For automated work, you set an explicit policy for the board. It defines:
216
218
 
217
219
  Without a valid policy, automated dispatch is refused.
218
220
 
219
- `agentic_kanban_board_dispatch` claims the highest-priority eligible card and
220
- creates a single-attempt assignment envelope. The envelope binds the card,
221
- repository, starting revision, assigned branch, allowed paths, stopping
222
- point, expiry, and interaction profile.
221
+ The `agentic_kanban_board_dispatch` tool claims the highest-priority eligible card. It
222
+ creates a single-attempt assignment envelope that binds the card, repository,
223
+ starting revision, assigned branch, allowed paths, stopping point, expiry, and
224
+ interaction profile.
223
225
 
224
226
  Blocked, altered, expired, reused, or unauthorized assignments fail closed.
225
227
  The journey validates the authenticated envelope before each consequential
@@ -232,10 +234,12 @@ Claims and envelopes are kept in authenticated state beside `TASKS.md`. The
232
234
  derived `board.md` view shows active claims but never becomes an authority
233
235
  source.
234
236
 
235
- `agentic_kanban_pulse` checks the board for ready work and available capacity.
236
- A check only reports which work is ready to run. It does not claim cards. Interactive host
237
- work needs explicit policy and one native confirmation for the batch. Automated
238
- work requires containment and never falls back to the host.
237
+ The `agentic_kanban_pulse` tool checks the board for ready work and available capacity.
238
+ A check only reports which work is ready to run. It does not claim cards.
239
+
240
+ Interactive host work needs explicit policy and one native confirmation for
241
+ the batch. Automated work requires containment and never falls back to the
242
+ host.
239
243
 
240
244
  Coming next:
241
245
 
@@ -264,7 +268,7 @@ writer's voice.*
264
268
  <details>
265
269
  <summary><strong>AI;DR, writing review</strong> <em>(released, 0.4.2)</em></summary>
266
270
 
267
- `agentic_aidr` reviews the last assistant response, supplied prose, or a
271
+ The `agentic_aidr` tool reviews the last assistant response, supplied prose, or a
268
272
  Markdown file. It checks four principles:
269
273
 
270
274
  - **Clarity.** Each sentence carries one useful idea.
@@ -304,8 +308,8 @@ QEMU/KVM virtual machine on a Linux host. The job cannot reach the host, the
304
308
  network, or anything else outside the machine. The system deletes the machine
305
309
  after the job ends.
306
310
 
307
- Isolation is off at the start of every session. You turn it on with
308
- `/agentic-isolation-enable` in the Pi TUI, and confirm. The model cannot run
311
+ Isolation is off at the start of every session. Use the `/agentic-isolation-enable`
312
+ command in the Pi TUI, and confirm. The model cannot run
309
313
  that command. Every cutover run asks for its own confirmation too. Nothing is
310
314
  saved to settings, and headless sessions stay blocked.
311
315
 
@@ -532,10 +536,9 @@ acceptance with all prohibited effects absent.
532
536
 
533
537
  ### Portable repository contract
534
538
 
535
- `templates/AGENTS.md` is a starting contract you can copy into any repository
536
- that uses Pi agents. It asks for controlled tasks and realistic tests, and
537
- treats agent reports as untrusted evidence. It never overwrites an existing
538
- `AGENTS.md`; add your project rules below it.
539
+ The `templates/AGENTS.md` file is a starting contract you can copy into any
540
+ repository that uses Pi agents. It asks for controlled tasks and realistic tests, and
541
+ treats agent reports as untrusted evidence. It never overwrites an existing `AGENTS.md`. Add your project rules below it.
539
542
 
540
543
  ## Install
541
544
 
@@ -545,24 +548,24 @@ From npm:
545
548
  pi install npm:@evoclock/pi-agentic-driver
546
549
  ```
547
550
 
548
- Or from Git at a pinned tag:
551
+ Or from Git at the latest release tag (see the repo's tags for the current one; the npm badge above shows the live version):
549
552
 
550
553
  ```sh
551
- pi install git:github.com/evoclock/pi-agentic-driver@v0.9.0
554
+ pi install git:github.com/evoclock/pi-agentic-driver@<latest-tag>
552
555
  ```
553
556
 
554
- Released extensions load standalone; neither requires the other.
557
+ Released extensions load independently.
555
558
 
556
559
  ### Choose which extensions load
557
560
 
558
- A normal install loads all six extensions by default. You can load only the
561
+ A normal install loads all packaged extensions by default. You can load only the
559
562
  extensions you want with the object form in your Pi settings:
560
563
 
561
564
  ```json
562
565
  {
563
566
  "packages": [
564
567
  {
565
- "source": "npm:@evoclock/pi-agentic-driver@0.9.0",
568
+ "source": "npm:@evoclock/pi-agentic-driver",
566
569
  "extensions": [
567
570
  "extensions/aidr.ts",
568
571
  "extensions/code-phage.js"
@@ -572,12 +575,69 @@ extensions you want with the object form in your Pi settings:
572
575
  }
573
576
  ```
574
577
 
575
- The package also includes `herdr-communication.ts`, `herdr-lifecycle.ts`,
576
- `herdr-dispatch.ts`, and `linux-microvm.ts`. Use `pi config` to enable or
578
+ The package includes the `herdr-communication.ts`, `herdr-lifecycle.ts`, and
579
+ `herdr-dispatch.ts` extensions. It also includes `linux-microvm.ts`. Use `pi config` to enable or
577
580
  disable individual resources from an installed package. You are not required
578
581
  to use every extension.
579
582
 
583
+ ## Router configuration
584
+
585
+ Board Pulse selects a route seat through the capacity/policy router. The
586
+ router reads a closed configuration file and a local-first SQLite file for
587
+ operational state (reservations, observations, route-decision audit).
588
+
589
+ - **Repository defaults** ship at `.agentic-driver/router.defaults.json`. The
590
+ template contains no accounts and one disabled placeholder seat, so it loads
591
+ out of the box and schedules nothing until you enable a real seat.
592
+ - **User profile (precedence over defaults):**
593
+ `~/.config/agentic-driver/router/profile.json`, or the path in the
594
+ `AGENTIC_DRIVER_ROUTER_PROFILE` environment variable. A profile section
595
+ replaces the matching defaults section wholesale; the merged config must
596
+ validate or the router refuses to produce decisions (fails closed).
597
+ - **Operational state:** `~/.local/share/agentic-driver/router/state.db`, or
598
+ `AGENTIC_DRIVER_ROUTER_DB`. This store is operational state only. The
599
+ authenticated claims file and the board writer remain the only dispatch
600
+ authority, and SQLite is repaired from them, never the reverse.
601
+ - **Runtime requirement:** the operational store uses the built-in
602
+ `node:sqlite` module. Node.js 22.5 or newer is required.
603
+
604
+ ### Tunable configuration values
605
+
606
+ Every numeric policy in the router is a configuration key — never a
607
+ constant in code. All keys are **required**. The repository defaults below
608
+ are conservative starting values. Your profile overrides them. A missing key
609
+ is a configuration validation failure: the router fails closed rather than
610
+ guess. Changing a value participates in the configuration digest, so cached
611
+ routing decisions computed under the old values no longer apply.
612
+
613
+ | Section | Key | Default | What it controls |
614
+ |---|---|---|---|
615
+ | `eligibility.reserve` | `floorPercent` | 40 | Share of every quota window kept in reserve (per account, per window). Headroom for an unexpected burst and your next interactive session, without idling the account. |
616
+ | `eligibility.reserve` | `scope` | `account-window` | How widely the reserve floor applies (per account per window at launch). |
617
+ | `eligibility.reserve` | `coldStartFraction` | 0.25 | Consumption assumed for a seat with too little history: deliberately generous, so a mis-estimate fails eligibility instead of draining the account. |
618
+ | `eligibility.reserve` | `estimateSamples` | 20 | How many recent dispatches the consumption estimate averages: enough to smooth outliers, quick enough to track a model change. |
619
+ | `eligibility.reserve` | `estimateMinSamples` | 5 | Below this many observations the average is treated as noise and the cold-start value is used instead. |
620
+ | `eligibility.reserve` | `estimateOutlierSigma` | 3 | Outlier cut for the estimate: one unusually slow dispatch cannot inflate the average, real tail latency is kept. |
621
+ | `eligibility.reserve` | `ownerInteractiveOverride` | false | Whether your own interactive dispatches bypass the reserve rule (they do not, at launch). |
622
+ | `ranking` | `janusUrl` | `http://127.0.0.1:8787` | The local janus evaluation service endpoint used for semantic ranking. |
623
+ | `ranking` | `maxCandidates` | 8 | At most eight eligible seats are ranked per request; preference order trims first. |
624
+ | `ranking` | `fallback` | `preference-order` | What happens when ranking is unavailable: dispatch falls back to your preference order and never blocks. |
625
+ | `gate` | `threshold` | 0.9 | The dispatch gate's confidence threshold. A task is only dispatched when the evaluation clears it. Lower it only with evidence from your own audit log. |
626
+ | `gate` | `timeoutMs` | 8000 | How long the gate waits for an evaluation. On timeout there is no verdict and dispatch is blocked (fail closed). |
627
+ | `gate` | `requestBudgetTokens` | 30000 | Maximum size of a gate evaluation request. Normal evaluations use under a thousand tokens. |
628
+ | seat record | `maxConcurrency` | 4 | How many workers one seat may run at once. The default suits a typical single machine or hosted seat. Tune it per seat: a cluster takes more, a constrained subscription endpoint may take less. |
629
+ | automation policy | `maxConcurrent` | 4 | How many workers may run at once across all seats. The default is a conservative start. Tune it to the capacity you actually have, and raise it when you add seats or hardware. |
630
+
631
+ Seats are named for infrastructure, not models: `dgx-spark-cluster`,
632
+ `mac-studio`, `merge-gateway-worker`. The model is a mutable field on the
633
+ seat, so changing models never renumbers your configuration. Each seat also
634
+ carries `scopeStatements`: plain-language descriptions of what the seat is
635
+ for. The dispatch gate reads them when judging whether a task fits.
636
+
580
637
  ## License
581
638
 
582
- AGPL-3.0-or-later with author-attribution additional terms (Section 7(b));
583
- see [LICENSE](LICENSE). A commercial licence is available on request.
639
+ AGPL-3.0-only with author-attribution additional terms (Section 7(b));
640
+ see [LICENSE](LICENSE). Commercial use, forks, and substantial modifications
641
+ are permitted when all AGPL and attribution obligations are followed. A
642
+ separate commercial licence is available for organisations requiring
643
+ proprietary modifications or other terms incompatible with the AGPL.
@@ -1,5 +1,5 @@
1
1
  // SPDX-FileCopyrightText: 2026 Julen Gamboa <j.a.r.gamboa@gmail.com>
2
- // SPDX-License-Identifier: AGPL-3.0-or-later
2
+ // SPDX-License-Identifier: AGPL-3.0-only
3
3
 
4
4
  export default async function attendedAuthorityGuard(pi) {
5
5
  const module = await import(new URL("../scripts/enforcement/attended_authority_guard.js", import.meta.url).href);
@@ -1,5 +1,5 @@
1
1
  // SPDX-FileCopyrightText: 2026 Julen Gamboa <j.a.r.gamboa@gmail.com>
2
- // SPDX-License-Identifier: AGPL-3.0-or-later
2
+ // SPDX-License-Identifier: AGPL-3.0-only
3
3
 
4
4
  import {
5
5
  analyzeCodePhage,
@@ -1,5 +1,5 @@
1
1
  // SPDX-FileCopyrightText: 2026 Julen Gamboa <j.a.r.gamboa@gmail.com>
2
- // SPDX-License-Identifier: AGPL-3.0-or-later
2
+ // SPDX-License-Identifier: AGPL-3.0-only
3
3
 
4
4
  import { statSync } from "node:fs";
5
5
  import { fileURLToPath } from "node:url";
@@ -1,5 +1,5 @@
1
1
  // SPDX-FileCopyrightText: 2026 Julen Gamboa <j.a.r.gamboa@gmail.com>
2
- // SPDX-License-Identifier: AGPL-3.0-or-later
2
+ // SPDX-License-Identifier: AGPL-3.0-only
3
3
 
4
4
  export default async function herdrDispatchPi(pi) {
5
5
  const dispatch = await import(new URL("../scripts/enforcement/herdr_async_dispatch_pi.js", import.meta.url).href);
@@ -1,5 +1,5 @@
1
1
  // SPDX-FileCopyrightText: 2026 Julen Gamboa <j.a.r.gamboa@gmail.com>
2
- // SPDX-License-Identifier: AGPL-3.0-or-later
2
+ // SPDX-License-Identifier: AGPL-3.0-only
3
3
 
4
4
  import { registerHerdrLifecycleInterface } from "../scripts/enforcement/herdr_lifecycle_pi.js";
5
5
 
@@ -1,5 +1,5 @@
1
1
  // SPDX-FileCopyrightText: 2026 Julen Gamboa <j.a.r.gamboa@gmail.com>
2
- // SPDX-License-Identifier: AGPL-3.0-or-later
2
+ // SPDX-License-Identifier: AGPL-3.0-only
3
3
 
4
4
  import { registerLinuxMicroVMCutoverInterface, registerIsolationSwitchCommands, createIsolationSwitch } from "../scripts/enforcement/linux_microvm_cutover_pi.js";
5
5
 
@@ -1,5 +1,5 @@
1
1
  // SPDX-FileCopyrightText: 2026 Julen Gamboa <j.a.r.gamboa@gmail.com>
2
- // SPDX-License-Identifier: AGPL-3.0-or-later
2
+ // SPDX-License-Identifier: AGPL-3.0-only
3
3
 
4
4
  // Board Pulse provider extension. Registers the deterministic
5
5
  // agentic_kanban_pulse tool and owns the optional fixed-interval timer. The
@@ -16,6 +16,7 @@
16
16
 
17
17
  import { executeHerdrSpawnWorker } from "../scripts/enforcement/herdr_lifecycle_pi.js";
18
18
  import { resolveBoardPath, registerPulseTools, createPulseTimer, pulseTick, pulseWorkerSpawnSeam } from "../scripts/enforcement/pulse_scheduler_pi.js";
19
+ import { prepareRouterRuntimeWithHealth, closeCachedRouterStores } from "../scripts/enforcement/router_runtime_pi.js";
19
20
 
20
21
  export default function pulsePi(pi) {
21
22
 
@@ -26,9 +27,31 @@ export default function pulsePi(pi) {
26
27
  // worker is ever started. No other creation path exists.
27
28
  const spawnWorker = pulseWorkerSpawnSeam({ executeHerdrSpawnWorker });
28
29
 
30
+ // The runtime is rebuilt per tool call and per automated tick (W3): a
31
+ // snapshot frozen at session setup would leave every subscription seat
32
+ // stale after the first observation expiry and freeze the prior-reservation
33
+ // term. The localHealth probe adapter (W2) is attached here, producing
34
+ // endpoint-keyed fresh health observations for local seats. `check`
35
+ // assembles the runtime read-only (W4): no store writes, no store creation.
36
+ const routerRuntimeFor = async (ctx, boardPath, { readOnly = false } = {}) => {
37
+ const runtime = await prepareRouterRuntimeWithHealth({
38
+ repoRoot: ctx?.cwd || process.cwd(), boardPath,
39
+ defaultsPath: ctx?.routerDefaultsPath ?? null, profilePath: ctx?.routerProfilePath ?? null,
40
+ dbPath: ctx?.routerDbPath ?? null, cacheRoot: ctx?.routerCacheRoot ?? null,
41
+ consumptionReceipts: ctx?.consumptionReceipts ?? [], readOnly,
42
+ fetchFn: ctx?.routerHealthFetch ?? null,
43
+ });
44
+ const installed = new Set(runtime.routerConfig.seats.map((seat) => seat.model).filter((model) => {
45
+ try { return ctx?.modelRegistry?.get?.(model) != null; } catch { return false; }
46
+ }));
47
+ runtime.routerSnapshot.modelInstalled = installed;
48
+ return { ...runtime, activeSessions: ctx?.activeSessions ?? [], providerLimit: ctx?.providerLimit ?? null };
49
+ };
50
+
29
51
  const result = registerPulseTools(pi, {
30
52
  resolveBoardPath: (ctx) => resolveBoardPath(typeof ctx === "string" ? ctx : ctx?.cwd || process.cwd()),
31
53
  spawnWorker,
54
+ routerRuntimeFor,
32
55
  });
33
56
 
34
57
  // Optional timer (§8): created only after session_start, only when the
@@ -43,6 +66,9 @@ export default function pulsePi(pi) {
43
66
  timer.clear();
44
67
  timer = null;
45
68
  }
69
+ // Release the cached per-dbPath store handles with the session so the
70
+ // runtime never accumulates DatabaseSync handles (W5).
71
+ closeCachedRouterStores();
46
72
  };
47
73
  const setupTimer = async (_event, ctx) => {
48
74
  clearTimer();
@@ -54,10 +80,15 @@ export default function pulsePi(pi) {
54
80
  const check = checkAutomationPolicy(policy, { boardPath });
55
81
  const pulse = check.ok ? check.policy.pulse : null;
56
82
  if (!pulse || pulse.enabled !== true || pulse.mode !== "automated") return;
83
+ // W3: the tick asks for a FRESH runtime every tick instead of reusing a
84
+ // setup-time snapshot. The factory re-reads collector caches, health
85
+ // probes, and the prior-reservation term under the read-time freshness
86
+ // rules; a factory failure fails that tick closed.
57
87
  timer = createPulseTimer({
58
88
  intervalSeconds: pulse.intervalSeconds,
59
89
  fillOnStart: pulse.fillOnStart === true,
60
- tick: () => pulseTick({ boardPath, spawnWorker, context: ctx }),
90
+ tick: () => pulseTick({ boardPath, spawnWorker, context: ctx,
91
+ routerRuntimeFor: () => routerRuntimeFor(ctx, boardPath, { readOnly: false }) }),
61
92
  });
62
93
  timer.start();
63
94
  } catch {
@@ -1,5 +1,5 @@
1
1
  // SPDX-FileCopyrightText: 2026 Julen Gamboa <j.a.r.gamboa@gmail.com>
2
- // SPDX-License-Identifier: AGPL-3.0-or-later
2
+ // SPDX-License-Identifier: AGPL-3.0-only
3
3
 
4
4
  // BOARD-1 provider extension. Both tools register at startup; the board is
5
5
  // resolved per tool call from the calling session's working directory. A
@@ -1,5 +1,5 @@
1
1
  // SPDX-FileCopyrightText: 2026 Julen Gamboa <j.a.r.gamboa@gmail.com>
2
- // SPDX-License-Identifier: AGPL-3.0-or-later
2
+ // SPDX-License-Identifier: AGPL-3.0-only
3
3
 
4
4
  // diff-scope adapter — changed-range extraction for code-phage.
5
5
  //
@@ -1,5 +1,5 @@
1
1
  // SPDX-FileCopyrightText: 2026 Julen Gamboa <j.a.r.gamboa@gmail.com>
2
- // SPDX-License-Identifier: AGPL-3.0-or-later
2
+ // SPDX-License-Identifier: AGPL-3.0-only
3
3
 
4
4
  // evidence adapter — content-bound source anchors for code-phage.
5
5
  //
@@ -1,5 +1,5 @@
1
1
  // SPDX-FileCopyrightText: 2026 Julen Gamboa <j.a.r.gamboa@gmail.com>
2
- // SPDX-License-Identifier: AGPL-3.0-or-later
2
+ // SPDX-License-Identifier: AGPL-3.0-only
3
3
 
4
4
  // narrative adapter — structured, concern-grouped review narrative.
5
5
  //
@@ -1,5 +1,5 @@
1
1
  // SPDX-FileCopyrightText: 2026 Julen Gamboa <j.a.r.gamboa@gmail.com>
2
- // SPDX-License-Identifier: AGPL-3.0-or-later
2
+ // SPDX-License-Identifier: AGPL-3.0-only
3
3
 
4
4
  // review-feedback adapter — two-intent review items for code-phage.
5
5
  //
@@ -1,5 +1,5 @@
1
1
  // SPDX-FileCopyrightText: 2026 Julen Gamboa <j.a.r.gamboa@gmail.com>
2
- // SPDX-License-Identifier: AGPL-3.0-or-later
2
+ // SPDX-License-Identifier: AGPL-3.0-only
3
3
 
4
4
  // visualization adapter — optional bounded visual payloads for code-phage.
5
5
  //
@@ -1,5 +1,5 @@
1
1
  // SPDX-FileCopyrightText: 2026 Julen Gamboa <j.a.r.gamboa@gmail.com>
2
- // SPDX-License-Identifier: AGPL-3.0-or-later
2
+ // SPDX-License-Identifier: AGPL-3.0-only
3
3
 
4
4
  import { createHash } from "node:crypto";
5
5
  import { lstatSync, readFileSync, realpathSync, statSync } from "node:fs";
@@ -1,5 +1,5 @@
1
1
  # SPDX-FileCopyrightText: 2026 Julen Gamboa <j.a.r.gamboa@gmail.com>
2
- # SPDX-License-Identifier: AGPL-3.0-or-later
2
+ # SPDX-License-Identifier: AGPL-3.0-only
3
3
 
4
4
  #!/usr/bin/env python3
5
5
  """AST-backed complexity metrics for code-phage.
@@ -1,5 +1,5 @@
1
1
  // SPDX-FileCopyrightText: 2026 Julen Gamboa <j.a.r.gamboa@gmail.com>
2
- // SPDX-License-Identifier: AGPL-3.0-or-later
2
+ // SPDX-License-Identifier: AGPL-3.0-only
3
3
 
4
4
  import ts from "typescript";
5
5
 
package/package.json CHANGED
@@ -1,9 +1,12 @@
1
1
  {
2
2
  "name": "@evoclock/pi-agentic-driver",
3
- "version": "0.9.1",
3
+ "version": "0.9.2",
4
4
  "description": "Guardrail extensions for Agentic Driver: advisory review, bounded Herdr communication, and guarded worker lifecycle.",
5
5
  "type": "module",
6
- "license": "AGPL-3.0-or-later",
6
+ "license": "AGPL-3.0-only",
7
+ "engines": {
8
+ "node": ">=22.5.0"
9
+ },
7
10
  "keywords": [
8
11
  "pi-package",
9
12
  "pi-extension",
@@ -70,6 +73,13 @@
70
73
  "scripts/enforcement/task_board_core_pi.js",
71
74
  "scripts/enforcement/pulse_core_pi.js",
72
75
  "scripts/enforcement/pulse_scheduler_pi.js",
76
+ "scripts/enforcement/router_engine_pi.js",
77
+ "scripts/enforcement/router_schemas_pi.js",
78
+ "scripts/enforcement/router_ranking_pi.js",
79
+ "scripts/enforcement/router_store_pi.js",
80
+ "scripts/enforcement/router_replacement_pi.js",
81
+ "scripts/enforcement/router_runtime_pi.js",
82
+ ".agentic-driver/router.defaults.json",
73
83
  "skills/pulse/SKILL.md"
74
84
  ],
75
85
  "pi": {
@@ -1,5 +1,5 @@
1
1
  // SPDX-FileCopyrightText: 2026 Julen Gamboa <j.a.r.gamboa@gmail.com>
2
- // SPDX-License-Identifier: AGPL-3.0-or-later
2
+ // SPDX-License-Identifier: AGPL-3.0-only
3
3
 
4
4
  import { createHash, randomUUID } from "node:crypto";
5
5
  import fs, { realpathSync } from "node:fs";
@@ -1,5 +1,5 @@
1
1
  // SPDX-FileCopyrightText: 2026 Julen Gamboa <j.a.r.gamboa@gmail.com>
2
- // SPDX-License-Identifier: AGPL-3.0-or-later
2
+ // SPDX-License-Identifier: AGPL-3.0-only
3
3
 
4
4
  // Attended-authority destructive-command guard for Pi.
5
5
  //
@@ -10,7 +10,7 @@
10
10
  {"id":"GC-FSW-003","class":"GC-FSW","severity":"ELEVATED","action":"deny","match":"file-growth-anomaly: unexpected growth in cache/package directories (~/.npm, ~/.cache/pip, /var/cache/*) beyond the size-delta threshold","evidence":"METR evaluation logs: bulk cache staging of payloads","since":"v1","disposition":"detector:fs-sweep size-delta sampling (gc_cache_growth_sample; aggregate ELEVATED, GC_CACHE_GROWTH_KIB per sweep)"},
11
11
  {"id":"GC-SHR-001","class":"GC-SHR","severity":"HIGH","action":"deny","match":"shared-cache-write: writes to paths shared with other agents or the coordinator; with host-share absent any such attempt is a deny","evidence":"receipt proves host-share=absent; writes would indicate boundary violation","since":"v1","disposition":"detector:fs-path; share paths auto-arm via gc_share_mounted (/proc/mounts); shared-cache roots classify as SHR when unarmed"},
12
12
  {"id":"GC-SHR-002","class":"GC-SHR","severity":"HIGH","action":"deny","match":"cross-agent-channel: creation of or writes to inter-agent coordination paths (maildirs, task-queue files, agent state files)","evidence":"HF incident multi-agent escalation; METR coordination/sabotage threat model","since":"v1","disposition":"detector:fs-path+shim-argv heuristics (Maildir, task-queue, agent-channel, coordination paths)"},
13
- {"id":"GC-NET-001","class":"GC-NET","severity":"HIGH","action":"deny","match":"socket-open: any entry in /proc/net/tcp, /proc/net/tcp6, /proc/net/udp at any sweep","evidence":"fixture boots with no network device; any socket is exfiltration-capable","since":"v1","disposition":"detector:net-proc-sweep"},
13
+ {"id":"GC-NET-001","class":"GC-NET","severity":"HIGH","action":"deny","match":"socket-open: any entry in /proc/net/tcp, /proc/net/tcp6, /proc/net/udp not present in the pre-arming socket baseline, at any sweep","evidence":"fixture boots with no network device; any socket is exfiltration-capable","since":"v1","disposition":"detector:net-proc-sweep-baselined"},
14
14
  {"id":"GC-NET-002","class":"GC-NET","severity":"HIGH","action":"deny","match":"network-tool-invocation: shim-detects wget, curl, nc, ssh, telnet, ping regardless of arguments","evidence":"METR self-replication exfiltration patterns; HF incident remote control","since":"v1","disposition":"detector:shim-argv"},
15
15
  {"id":"GC-CRED-001","class":"GC-CRED","severity":"CRITICAL","action":"deny","match":"credential-path-read: opens of ~/.ssh/, ~/.aws/, ~/.netrc, ~/.git-credentials, token files","evidence":"credential theft is step one in every reported agent-escape chain","since":"v1","disposition":"detector:shim-argv"},
16
16
  {"id":"GC-CRED-002","class":"GC-CRED","severity":"CRITICAL","action":"deny","match":"env-secret-probing: attempts to dump or scan the environment for secret-shaped keys","evidence":"METR evaluation logs; HF incident postmortem credential harvest attempts","since":"v1","disposition":"detector:shim-argv"},
@@ -1,5 +1,5 @@
1
1
  // SPDX-FileCopyrightText: 2026 Julen Gamboa <j.a.r.gamboa@gmail.com>
2
- // SPDX-License-Identifier: AGPL-3.0-or-later
2
+ // SPDX-License-Identifier: AGPL-3.0-only
3
3
 
4
4
  // Async continuous worker dispatch and pulse. One bounded step on top of the
5
5
  // shipped herdr-communication interface: a worker progresses through an