@evoclock/pi-agentic-driver 0.10.0 → 0.10.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.
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@evoclock/pi-agentic-driver",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.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-only",
|
|
@@ -379,6 +379,75 @@ function safeJsonParse(text) {
|
|
|
379
379
|
}
|
|
380
380
|
}
|
|
381
381
|
|
|
382
|
+
const CAPABILITY_REGISTRY_PATH = ".vogelkop/capabilities.json";
|
|
383
|
+
|
|
384
|
+
function capabilityRegistryError(registryPath, reason) {
|
|
385
|
+
return Object.assign(new Error(`capability registry is invalid at ${registryPath}: ${reason}`), {
|
|
386
|
+
code: "capability-registry-invalid",
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
// The workspace registry is deliberately loaded at tool-call time. A missing
|
|
391
|
+
// registry is an empty declaration; an unreadable or malformed present file is
|
|
392
|
+
// not silently downgraded to empty. Capability-less boards are independent of
|
|
393
|
+
// the registry, so they retain the writer's existing behavior even while an
|
|
394
|
+
// unused registry file is malformed.
|
|
395
|
+
function loadWorkspaceCapabilityNames(boardPath) {
|
|
396
|
+
const registryPath = join(dirname(boardPath), CAPABILITY_REGISTRY_PATH);
|
|
397
|
+
let text;
|
|
398
|
+
try {
|
|
399
|
+
text = readFileSync(registryPath, "utf8");
|
|
400
|
+
} catch (error) {
|
|
401
|
+
if (error?.code === "ENOENT") return [];
|
|
402
|
+
throw capabilityRegistryError(registryPath, String(error?.message || error).slice(0, 256));
|
|
403
|
+
}
|
|
404
|
+
let entries;
|
|
405
|
+
try {
|
|
406
|
+
entries = JSON.parse(text);
|
|
407
|
+
} catch (error) {
|
|
408
|
+
throw capabilityRegistryError(registryPath, `invalid JSON: ${String(error?.message || error).slice(0, 256)}`);
|
|
409
|
+
}
|
|
410
|
+
if (!Array.isArray(entries)) {
|
|
411
|
+
throw capabilityRegistryError(registryPath, "expected a JSON array");
|
|
412
|
+
}
|
|
413
|
+
const names = [];
|
|
414
|
+
for (const [index, entry] of entries.entries()) {
|
|
415
|
+
if (entry === null || typeof entry !== "object" || Array.isArray(entry)
|
|
416
|
+
|| typeof entry.name !== "string" || entry.name.trim() === ""
|
|
417
|
+
|| (entry.description !== undefined && typeof entry.description !== "string")) {
|
|
418
|
+
throw capabilityRegistryError(registryPath, `entry ${index} must contain a non-empty string name and an optional string description`);
|
|
419
|
+
}
|
|
420
|
+
names.push(entry.name);
|
|
421
|
+
}
|
|
422
|
+
return names;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
function boardHasCapabilities(boardPath) {
|
|
426
|
+
if (!existsSync(boardPath)) return false;
|
|
427
|
+
try {
|
|
428
|
+
const parsed = parseBoard(readFileSync(boardPath, "utf8"));
|
|
429
|
+
return parsed.cards.some((card) => Array.isArray(card.capabilities) && card.capabilities.length > 0);
|
|
430
|
+
} catch {
|
|
431
|
+
// Let the writer report the board error; do not use an unreadable board to
|
|
432
|
+
// justify ignoring a malformed capability registry.
|
|
433
|
+
return true;
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
function workspaceRegistriesFor(boardPath, requestedCapabilities) {
|
|
438
|
+
try {
|
|
439
|
+
return { capabilities: loadWorkspaceCapabilityNames(boardPath) };
|
|
440
|
+
} catch (error) {
|
|
441
|
+
const capabilityRequested = requestedCapabilities !== undefined
|
|
442
|
+
&& requestedCapabilities !== null
|
|
443
|
+
&& (!Array.isArray(requestedCapabilities) || requestedCapabilities.length > 0);
|
|
444
|
+
if (error?.code === "capability-registry-invalid" && !capabilityRequested && !boardHasCapabilities(boardPath)) {
|
|
445
|
+
return { capabilities: [] };
|
|
446
|
+
}
|
|
447
|
+
throw error;
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
|
|
382
451
|
// ---------------------------------------------------------------------------
|
|
383
452
|
// Closed validator (§1, §6 gate 2). Validate before persist; decline on
|
|
384
453
|
// violation; duplicate cardId is a validation error; decorative use of the
|
|
@@ -2898,11 +2967,12 @@ export function registerKanbanBoardTools(pi, { boardPath = null, resolveBoardPat
|
|
|
2898
2967
|
// reason), never raw throws — the model sees the governance reason.
|
|
2899
2968
|
let result;
|
|
2900
2969
|
try {
|
|
2970
|
+
const registries = workspaceRegistriesFor(activeBoardPath, input?.capabilities);
|
|
2901
2971
|
result = writeCard({
|
|
2902
2972
|
boardPath: activeBoardPath,
|
|
2903
2973
|
input: writerInput,
|
|
2904
2974
|
authority: input?.authority,
|
|
2905
|
-
registries
|
|
2975
|
+
registries,
|
|
2906
2976
|
surface: "tasks",
|
|
2907
2977
|
// No requireExistingBoard: the write tool bootstraps a fresh
|
|
2908
2978
|
// board when none exists. Recreation is safe — fresh content only,
|
|
@@ -3028,9 +3098,10 @@ export function registerKanbanBoardTools(pi, { boardPath = null, resolveBoardPat
|
|
|
3028
3098
|
}
|
|
3029
3099
|
let result;
|
|
3030
3100
|
try {
|
|
3101
|
+
const registries = workspaceRegistriesFor(activeBoardPath, operation === "update" ? input?.capabilities : undefined);
|
|
3031
3102
|
result = operation === "update"
|
|
3032
|
-
? updateCard({ boardPath: activeBoardPath, cardId: input.cardId, changes, authority: input?.authority, registries
|
|
3033
|
-
: deleteCard({ boardPath: activeBoardPath, cardId: input.cardId, authority: input?.authority, registries
|
|
3103
|
+
? updateCard({ boardPath: activeBoardPath, cardId: input.cardId, changes, authority: input?.authority, registries, surface: "tasks" })
|
|
3104
|
+
: deleteCard({ boardPath: activeBoardPath, cardId: input.cardId, authority: input?.authority, registries, surface: "tasks" });
|
|
3034
3105
|
} catch (error) {
|
|
3035
3106
|
const code = typeof error?.code === "string" ? error.code : "writer-error";
|
|
3036
3107
|
const value = { ok: false, persisted: false, code, reason: String(error?.message || error).slice(0, 512), errors: [String(error?.message || error).slice(0, 512)] };
|