@evoclock/pi-agentic-driver 0.8.0 → 0.8.1

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.
@@ -1,15 +1,14 @@
1
1
  // SPDX-FileCopyrightText: 2026 Julen Gamboa <j.a.r.gamboa@gmail.com>
2
2
  // SPDX-License-Identifier: AGPL-3.0-or-later
3
3
 
4
- // BOARD-1 provider extension (§5 reversibility): the board surface registers
5
- // only when a real board file is observed in the workspace. No board file,
6
- // no behavior change and no new tool.
4
+ // BOARD-1 provider extension. Both tools register at startup; the board is
5
+ // resolved per tool call from the calling session's working directory. A
6
+ // workspace with no board file gets a structured board-unavailable result —
7
+ // nothing is created and nothing else changes.
7
8
 
8
9
  import { existsSync } from "node:fs";
9
10
  import { join } from "node:path";
10
11
 
11
- // Documented board locations (design §2): workspace-level Obsidian Kanban
12
- // Markdown, or vogelkop's simpler TASKS.md shape.
13
12
  const BOARD_FILENAMES = ["board.md", "TASKS.md"];
14
13
 
15
14
  export function resolveBoardPath(cwd) {
@@ -23,9 +22,5 @@ export function resolveBoardPath(cwd) {
23
22
 
24
23
  export default async function taskBoardPi(pi) {
25
24
  const module = await import(new URL("../scripts/enforcement/task_board_core_pi.js", import.meta.url).href);
26
- const boardPath = resolveBoardPath(pi?.ctx?.cwd);
27
- if (boardPath === null) {
28
- return { registered: [], observation: { present: false, boardPath: null } };
29
- }
30
- return module.registerKanbanBoardTools(pi, { boardPath });
25
+ return module.registerKanbanBoardTools(pi, { resolveBoardPath });
31
26
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@evoclock/pi-agentic-driver",
3
- "version": "0.8.0",
3
+ "version": "0.8.1",
4
4
  "description": "Guardrail extensions for Agentic Driver: advisory review, bounded Herdr communication, and guarded worker lifecycle.",
5
5
  "type": "module",
6
6
  "license": "AGPL-3.0-or-later",
@@ -988,11 +988,12 @@ export function observeBoardProvider({ boardPath }) {
988
988
  return { present, boardPath: present ? boardPath : null };
989
989
  }
990
990
 
991
- export function registerKanbanBoardTools(pi, { boardPath } = {}) {
992
- const observation = observeBoardProvider({ boardPath });
993
- if (!observation.present) return { registered: [], observation };
994
- // Additive, fail-closed: registered only behind the observation,
995
- // so removing the board file removes the surface (§5).
991
+ export function registerKanbanBoardTools(pi, { resolveBoardPath, boardPath } = {}) {
992
+ // Tools register unconditionally; the board is resolved per call from the
993
+ // calling session's working directory (Pi extensions have no ctx at
994
+ // registration time). A workspace with no board file gets a structured
995
+ // board-unavailable result per call, so the surface stays reversible (§5):
996
+ // no board, nothing happens.
996
997
  const registered = [];
997
998
  if (typeof pi?.registerTool === "function") {
998
999
  pi.registerTool({
@@ -1000,25 +1001,25 @@ export function registerKanbanBoardTools(pi, { boardPath } = {}) {
1000
1001
  label: "Kanban Board",
1001
1002
  description: "Read-only view of the validated task board: lanes, flags, priorities, dependencies, and dispatchability. The board is additive and grants no authority; agents read it and act within card states.",
1002
1003
  parameters: { type: "object", additionalProperties: false, properties: {} },
1003
- async execute() {
1004
- // F7(b): every tool call re-observes the board. If the board file was
1005
- // removed after registration, return an observed board-unavailable
1006
- // result instead of silently serving a stale board. Registration
1007
- // stays; the surface is gated per call.
1008
- if (!existsSync(observation.boardPath)) {
1004
+ async execute(_toolCallId, _params, _signal, _onUpdate, ctx) {
1005
+ // The board is resolved per call from the calling session's working
1006
+ // directory. No board in this workspace: structured board-unavailable,
1007
+ // nothing happens.
1008
+ const resolvedBoardPath = (typeof resolveBoardPath === "function" ? resolveBoardPath(ctx?.cwd) : null) ?? boardPath ?? null;
1009
+ if (resolvedBoardPath === null || !existsSync(resolvedBoardPath)) {
1009
1010
  const value = {
1010
1011
  ok: false,
1011
1012
  nonAuthorizing: true,
1012
1013
  persisted: false,
1013
1014
  boardUnavailable: true,
1014
1015
  cards: [],
1015
- errors: ["board file is no longer present (board-unavailable)"],
1016
+ errors: ["no board.md or TASKS.md in this workspace (board-unavailable)"],
1016
1017
  };
1017
1018
  return { content: [{ type: "text", text: JSON.stringify(value, null, 2) }], details: value };
1018
1019
  }
1019
1020
  let value;
1020
1021
  try {
1021
- const markdown = readFileSync(observation.boardPath, "utf8");
1022
+ const markdown = readFileSync(resolvedBoardPath, "utf8");
1022
1023
  const validated = validateBoard(markdown);
1023
1024
  value = validated.ok
1024
1025
  ? { ok: true, nonAuthorizing: true, persisted: false, cards: validated.cards, errors: [] }
@@ -1075,18 +1076,18 @@ export function registerKanbanBoardTools(pi, { boardPath } = {}) {
1075
1076
  },
1076
1077
  required: ["title", "specification", "definitionOfDone", "stoppingPoint", "scopePaths", "authority"],
1077
1078
  },
1078
- async execute(_toolContext, input) {
1079
- // F7(b): re-observe the board on every call. If the board file was
1080
- // removed after registration, return board-unavailable instead of
1081
- // writing to a stale path.
1082
- if (!existsSync(observation.boardPath)) {
1079
+ async execute(_toolContext, input, _signal, _onUpdate, ctx) {
1080
+ // Resolve the board per call from the calling session's working
1081
+ // directory. No board here: structured board-unavailable, no write.
1082
+ const resolvedBoardPath = (typeof resolveBoardPath === "function" ? resolveBoardPath(ctx?.cwd) : null) ?? boardPath ?? null;
1083
+ if (resolvedBoardPath === null || !existsSync(resolvedBoardPath)) {
1083
1084
  const value = {
1084
1085
  ok: false,
1085
1086
  persisted: false,
1086
1087
  boardUnavailable: true,
1087
1088
  code: "board-unavailable",
1088
- reason: "board file is no longer present (board-unavailable)",
1089
- errors: ["board file is no longer present (board-unavailable)"],
1089
+ reason: "no board.md or TASKS.md in this workspace (board-unavailable)",
1090
+ errors: ["no board.md or TASKS.md in this workspace (board-unavailable)"],
1090
1091
  };
1091
1092
  return { content: [{ type: "text", text: JSON.stringify(value, null, 2) }], details: value };
1092
1093
  }
@@ -1132,7 +1133,7 @@ export function registerKanbanBoardTools(pi, { boardPath } = {}) {
1132
1133
  let result;
1133
1134
  try {
1134
1135
  result = writeCard({
1135
- boardPath: observation.boardPath,
1136
+ boardPath: resolvedBoardPath,
1136
1137
  input: writerInput,
1137
1138
  authority: input?.authority,
1138
1139
  registries: {},
@@ -1179,5 +1180,5 @@ export function registerKanbanBoardTools(pi, { boardPath } = {}) {
1179
1180
  });
1180
1181
  registered.push("agentic_kanban_board_write");
1181
1182
  }
1182
- return { registered, observation };
1183
+ return { registered };
1183
1184
  }