blun-king-cli 9.1.565 → 9.1.567

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/LIESMICH.txt CHANGED
@@ -1,7 +1,7 @@
1
- BLUN Code 9.1.563
1
+ BLUN Code 9.1.566
2
2
 
3
3
  Installation:
4
- npm install -g blun-king-cli@9.1.563
4
+ npm install -g blun-king-cli@9.1.566
5
5
 
6
6
  Start:
7
7
  blun
package/README.md CHANGED
@@ -11,7 +11,7 @@ web tools, MCP servers, and an optional Telegram channel.
11
11
  ## Install
12
12
 
13
13
  ```sh
14
- npm install -g blun-king-cli@9.1.563
14
+ npm install -g blun-king-cli@9.1.566
15
15
  ```
16
16
 
17
17
  Start the local console with:
@@ -9,7 +9,7 @@
9
9
  "name": "agent-spine",
10
10
  "source": ".",
11
11
  "description": "Map existing rules, soul, and memory without rewriting source files.",
12
- "version": "0.54.0",
12
+ "version": "0.59.1",
13
13
  "author": {
14
14
  "name": "Mayk Biletti"
15
15
  },
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-spine",
3
- "version": "0.54.0",
3
+ "version": "0.59.1",
4
4
  "description": "A non-destructive identity and memory spine for Claude Code agents.",
5
5
  "author": {
6
6
  "name": "Mayk Biletti",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-spine",
3
- "version": "0.54.0",
3
+ "version": "0.59.1",
4
4
  "description": "A non-destructive identity and memory spine for Codex agents.",
5
5
  "author": {
6
6
  "name": "Mayk Biletti",
@@ -92,6 +92,18 @@ claude --plugin-dir .
92
92
 
93
93
  Claude Code discovers the bundled skill, hooks, and MCP server. Review and trust executable components when the host asks.
94
94
 
95
+ Version `0.59.1` keeps every lifecycle hook available when a bounded filesystem scan encounters an inaccessible directory. Scanner-tagged failures and raw `EPERM` or `EACCES` directory-enumeration errors return success from `PreToolUse`, `PostToolUse`, `Stop`, and `SubagentStop`, while recording the event, phase, code, and path locally. Security decisions remain fail closed. Starting at the exact Claude, Codex, or BLUN profile root also suppresses project and self-starter recursion through that profile; a real nested project is still scanned normally.
96
+
97
+ Version `0.59.0` adds bounded, outcome-driven exploration to durable plan execution. An owner-confirmed step can freeze two to four attempts; after an objectively measured non-blocking failure, AgentSpine tries exactly one remaining sufficient strategy from the same minimum-risk class. The host receives an immutable attempt number, strategy, budget and previous-outcome digest. Missing or reused evidence, a blocking defect, budget exhaustion and any attempt to enter a higher-risk class stop fail closed. Exploration remains context only and cannot grant tools, permissions or policy exceptions.
98
+
99
+ Version `0.58.0` transfers objectively successful execution strategies to new scope-matching plan steps. A transfer needs two independent passing goals with distinct source digests, the same capability and evaluator contract, an unexpired owner-bound transfer key, and no matching regression. It may break cost ties only inside the lowest-risk sufficient strategy class. A single blocking defect withdraws the strategy from future tasks, while foreign groups, stale evidence and fabricated proof lineages receive the ordinary unproven choice. Transfer records contain only stable IDs, timestamps and digests and never grant tools or authority.
100
+
101
+ Version `0.57.0` adds bounded execution-strategy selection and objective post-action reflection to durable plan steps. An owner-confirmed plan precommits required capability classes, two to eight candidate strategies, risk and cost, plus an evaluator, metric, threshold and minimum case count. AgentSpine selects the lowest-risk sufficient strategy deterministically and will not complete the step from a model or runner self-report alone. The exact strategy and a SHA-256-bound objective outcome must pass; a blocking defect overrides a favorable score. These capability labels remain context only and cannot grant host tools or authority.
102
+
103
+ Version `0.56.0` adds deterministic shared-resource conflict resolution to durable team plans. A step may declare bounded resource IDs in its immutable definition; while that step is leased, only conflicting work in the exact project and group waits. The highest current owner-confirmed goal priority wins even if mutable queue state is altered, while unrelated resources and foreign groups continue in parallel. Resource waits are visible only in the affected agent's scoped gateway context, and completion or lease-expiry recovery releases the resource without granting any tool or authority.
104
+
105
+ Version `0.55.0` adds provider-neutral team execution to durable goal plans. Each dependent step can be bound to a different already-authenticated agent in the same tenant and exact project group, so a plan can move from Codex to Claude and back while preserving one immutable dependency graph, checkpoint chain and objective success criteria. Queue creation, claim, host/profile routing and completion all verify the same step assignee. Foreign-group members, identity drift and assignment tampering fail closed; a departed assignee pauses the exact step before host execution. Historical single-agent plans remain compatible, and plan context never grants tools, delegation or policy exceptions.
106
+
95
107
  Version `0.54.0` adds durable, evidence-classified knowledge-gap handling to hierarchical goal plans. A host can pause the exact current step with one bounded question and an objective reason; reconciliation does not ask again, and the step resumes only after an exact locally confirmed answer. Owner input and objective observations remain distinct, objective answers require a SHA-256 source digest, and every answer is context-only. Conflicts, state tampering, secrets and authority-shaped answers fail closed.
96
108
 
97
109
  Version `0.53.0` adds durable hierarchical goal plans to the optional gateway worker. An owner can precommit a bounded dependency graph of objective steps; the worker leases only the current ready step, advances after its explicit success result, resumes the exact step after restart, and reconstructs one missing wake after a torn write. Cycles, definition drift and stale-step completion fail closed, while the plan remains context-only and cannot grant tools, rights or delegation.
@@ -257,7 +269,7 @@ Read the full [preservation contract](docs/preservation-contract.md), including
257
269
  | `agentspine channel-events …` | Inspect the exact-scope durable ingress queue and leases |
258
270
  | `agentspine persona-sync …` | Synchronize an explicitly approved external authenticated roster |
259
271
  | `agentspine personas …` | Inspect active and historical persona identities and provenance |
260
- | `agentspine goal-assign …` | Assign one authenticated focused goal or dependency-bound plan to an active agent |
272
+ | `agentspine goal-assign …` | Assign one focused goal or an authenticated multi-agent dependency plan |
261
273
  | `agentspine goal-clarify …` | Resolve one exact plan-bound knowledge gap with locally confirmed context |
262
274
  | `agentspine gateway-control …` | Enable, stop, or kill-switch the local worker under explicit owner control |
263
275
  | `agentspine gateway-status …` | Inspect goals, queue, delivery receipts, and independent health gates |
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-spine",
3
- "version": "0.54.0",
3
+ "version": "0.59.1",
4
4
  "description": "A non-destructive identity and memory spine for BLUN King agents.",
5
5
  "license": "Apache-2.0",
6
6
  "author": {
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "schema": "agentspine.hook-bundle/v1",
3
- "version": "0.54.0",
3
+ "version": "0.59.1",
4
4
  "contract": "agentspine.preflight/v2"
5
5
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-spine",
3
- "version": "0.54.0",
3
+ "version": "0.59.1",
4
4
  "description": "A non-destructive identity and memory spine for AI agents.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -253,8 +253,8 @@ function sessionId(input) {
253
253
  async function startSelfstarter(input, event, root, scope, sourceDiagnostics) {
254
254
  if (!SELFSTART_EVENTS.has(event)) return null;
255
255
  const requested = selfstarterInput(input);
256
- if (["skipped-unmarked-home", "skipped-home-root"].includes(sourceDiagnostics?.projectTreeScan)) {
257
- if (requested) throw new Error("self-starter cannot use a user home as its workspace root");
256
+ if (selfstarterRootSkipped(sourceDiagnostics)) {
257
+ if (requested) throw new Error("self-starter cannot use a user home or host profile as its workspace root");
258
258
  return null;
259
259
  }
260
260
  const session = sessionId(input);
@@ -269,6 +269,11 @@ async function startSelfstarter(input, event, root, scope, sourceDiagnostics) {
269
269
  });
270
270
  }
271
271
 
272
+ function selfstarterRootSkipped(sourceDiagnostics) {
273
+ return ["skipped-unmarked-home", "skipped-home-root", "skipped-profile-root"]
274
+ .includes(sourceDiagnostics?.projectTreeScan);
275
+ }
276
+
272
277
  async function startChannelEvent(input, event, root, scope, catalog) {
273
278
  const requested = channelEventInput(input);
274
279
  if (!requested) return null;
@@ -476,20 +481,14 @@ function isScanFailOpenTool(name = "") {
476
481
  return /(^|__)(apply_patch|edit|write|bash|exec_command)(_|$)/i.test(name);
477
482
  }
478
483
 
479
- const RAW_SCAN_ERROR_CODES = new Set(["EPERM", "EACCES"]);
480
- const RAW_SCAN_SYSCALLS = new Set(["opendir", "readdir", "scandir", "lstat", "stat", "realpath"]);
481
-
482
484
  function filesystemScanError(error) {
483
- if (!error) return false;
484
- if (error.code === "AGENTSPINE_SCAN_INCOMPLETE" || error.agentSpineScan === true) return true;
485
- return RAW_SCAN_ERROR_CODES.has(error.code)
486
- && RAW_SCAN_SYSCALLS.has(error.syscall)
487
- && typeof error.path === "string"
488
- && error.path.length > 0;
485
+ return Boolean(error && (error.code === "AGENTSPINE_SCAN_INCOMPLETE" || error.agentSpineScan === true));
489
486
  }
490
487
 
491
- export function hookScanFailureFailsOpen(_event, _toolName, error) {
492
- return filesystemScanError(error);
488
+ export function hookScanFailureFailsOpen(eventOrError, _toolName, suppliedError) {
489
+ const error = suppliedError ?? eventOrError;
490
+ return filesystemScanError(error) || (["EPERM", "EACCES"].includes(error?.code)
491
+ && ["opendir", "readdir", "scandir"].includes(error?.syscall));
493
492
  }
494
493
 
495
494
  async function auditSkippedScans(input, phase, skipped = []) {
@@ -503,12 +502,19 @@ async function auditSkippedScans(input, phase, skipped = []) {
503
502
  }
504
503
 
505
504
  async function allowScanFailure(input, phase, error) {
505
+ const event = input.hook_event_name || input.event_name || "unknown";
506
506
  await recordHookScanAudit({
507
- event: input.hook_event_name || input.event_name || "unknown",
508
- toolName: input.tool_name || null, phase, error,
507
+ event, toolName: input.tool_name || null, phase, error,
509
508
  path: error?.path || input.cwd || process.cwd(), now: input.timestamp || new Date()
510
509
  });
511
- return { blocked: false, scanFailedOpen: true, phase, error: error.message };
510
+ return { blocked: false, scanFailedOpen: true, event, phase, error: error.message };
511
+ }
512
+
513
+ async function finishScanFailure(input, payload, phase, error) {
514
+ const allowed = await allowScanFailure(input, phase, error);
515
+ if (payload) return allowed;
516
+ process.stdout.write("{}\n");
517
+ return undefined;
512
518
  }
513
519
 
514
520
  function stringValues(value, output = []) {
@@ -553,7 +559,7 @@ function blockPrompt(reason) {
553
559
  })}\n`);
554
560
  }
555
561
 
556
- async function runHookWithInput(input, payload) {
562
+ async function runHookCore(input, payload) {
557
563
  if (!input || typeof input !== "object" || Array.isArray(input)) throw new Error("hook input must be one JSON object");
558
564
  const event = input.hook_event_name || input.event_name || "";
559
565
  if (!KNOWN_EVENTS.has(event)) throw new Error(`unsupported hook event: ${event || "missing"}`);
@@ -583,11 +589,8 @@ async function runHookWithInput(input, payload) {
583
589
  try {
584
590
  resolvedSources = await resolveHostSourceCatalog({ host: instructionHost, cwd, input });
585
591
  } catch (error) {
586
- if (hookScanFailureFailsOpen(event, input.tool_name, error)) {
587
- const allowed = await allowScanFailure(input, "source-resolution", error);
588
- if (payload) return allowed;
589
- process.stdout.write("{}\n");
590
- return;
592
+ if (hookScanFailureFailsOpen(error)) {
593
+ return finishScanFailure(input, payload, "source-resolution", error);
591
594
  }
592
595
  const reason = `AgentSpine source resolution failed closed: ${error.message}`;
593
596
  if (event === "PreToolUse" && isMutationTool(input.tool_name)) {
@@ -661,7 +664,7 @@ async function runHookWithInput(input, payload) {
661
664
  }
662
665
  }
663
666
 
664
- if (event === "PreToolUse") {
667
+ if (event === "PreToolUse" && !selfstarterRootSkipped(resolvedSources.diagnostics)) {
665
668
  try {
666
669
  scope ||= await runtimeScope(input, root, resolvedSources.userStateRoot, catalog);
667
670
  const exact = await selfstarterScope(input, scope, root, "effect");
@@ -672,11 +675,8 @@ async function runHookWithInput(input, payload) {
672
675
  });
673
676
  }
674
677
  } catch (error) {
675
- if (isScanFailOpenTool(input.tool_name) && filesystemScanError(error)) {
676
- const allowed = await allowScanFailure(input, "self-starter", error);
677
- if (payload) return allowed;
678
- process.stdout.write("{}\n");
679
- return;
678
+ if (isScanFailOpenTool(input.tool_name) && hookScanFailureFailsOpen(error)) {
679
+ return finishScanFailure(input, payload, "self-starter", error);
680
680
  }
681
681
  const reason = `AgentSpine self-starter denied this effect: ${error.message}`;
682
682
  if (payload) return { blocked: true, reason, selfstarter: { allowed: false, reason: error.message } };
@@ -691,7 +691,7 @@ async function runHookWithInput(input, payload) {
691
691
  attentionEvent = await captureAttentionLifecycle(input, event, root, scope, catalog);
692
692
  }
693
693
 
694
- if (event === "PostToolUse") {
694
+ if (event === "PostToolUse" && !selfstarterRootSkipped(resolvedSources.diagnostics)) {
695
695
  scope ||= await runtimeScope(input, root, resolvedSources.userStateRoot, catalog);
696
696
  const exact = await selfstarterScope(input, scope, root, "effect");
697
697
  if (exact) {
@@ -704,7 +704,8 @@ async function runHookWithInput(input, payload) {
704
704
 
705
705
  if (["Stop", "SubagentStop"].includes(event)) {
706
706
  scope ||= await runtimeScope(input, root, resolvedSources.userStateRoot, catalog);
707
- const exact = await selfstarterScope(input, scope, root, "resume");
707
+ const exact = selfstarterRootSkipped(resolvedSources.diagnostics)
708
+ ? null : await selfstarterScope(input, scope, root, "resume");
708
709
  if (exact && !scope.currentTaskId) scope.currentTaskId = exact.taskId;
709
710
  try {
710
711
  learningDelivery = await recordLearningDeliveries({
@@ -826,15 +827,12 @@ async function runHookWithInput(input, payload) {
826
827
  process.stdout.write(`${JSON.stringify(hookOutput(event, context))}\n`);
827
828
  return;
828
829
  } catch (error) {
830
+ if (hookScanFailureFailsOpen(error)) {
831
+ return finishScanFailure(input, payload, "context-lifecycle", error);
832
+ }
829
833
  if (event === "UserPromptSubmit") {
830
834
  await recordPreflightFailure({ receiptId: preflight?.receipt?.id || null, input, host,
831
835
  error, now: input.timestamp || new Date(), env: process.env }).catch(() => {});
832
- if (hookScanFailureFailsOpen(event, input.tool_name, error)) {
833
- const allowed = await allowScanFailure(input, "preflight", error);
834
- if (payload) return { ...allowed, catalogPath };
835
- process.stdout.write("{}\n");
836
- return;
837
- }
838
836
  const reason = `AgentSpine pre-answer preflight blocked this turn: ${error.message}`;
839
837
  if (payload) return { blocked: true, failedClosed: true, reason, error: error.message, catalogPath };
840
838
  blockPrompt(reason);
@@ -859,16 +857,14 @@ async function runHookWithInput(input, payload) {
859
857
  }
860
858
 
861
859
  export async function runHook(payload = null, options = {}) {
862
- const input = payload || await readStdin(options);
863
- if (input === SILENT_OVERSIZE_POST_TOOL_USE) return;
860
+ let input = payload;
864
861
  try {
865
- return await runHookWithInput(input, payload);
862
+ input ||= await readStdin(options);
863
+ if (input === SILENT_OVERSIZE_POST_TOOL_USE) return;
864
+ return await runHookCore(input, payload);
866
865
  } catch (error) {
867
- const event = input?.hook_event_name || input?.event_name || "";
868
- if (!hookScanFailureFailsOpen(event, input?.tool_name, error)) throw error;
869
- const allowed = await allowScanFailure(input, "hook-runtime", error);
870
- if (payload) return allowed;
871
- process.stdout.write("{}\n");
866
+ if (!hookScanFailureFailsOpen(error) || input === SILENT_OVERSIZE_POST_TOOL_USE) throw error;
867
+ return finishScanFailure(input, payload, "lifecycle", error);
872
868
  }
873
869
  }
874
870
 
@@ -45,9 +45,10 @@ export {
45
45
  personaRuntimeFindings, syncPersonaRosterFromEnvironment
46
46
  } from "./lib/persona-runtime.js";
47
47
  export {
48
- GATEWAY_EVENT_SCHEMA, GATEWAY_POLICY_SCHEMA, GATEWAY_RUNTIME_SCHEMA, GOAL_PLAN_SCHEMA, KNOWLEDGE_GAP_SCHEMA,
48
+ EXECUTION_ATTEMPT_SCHEMA, GATEWAY_EVENT_SCHEMA, GATEWAY_POLICY_SCHEMA, GATEWAY_RUNTIME_SCHEMA,
49
+ GOAL_PLAN_SCHEMA, KNOWLEDGE_GAP_SCHEMA,
49
50
  assignGoal, claimGatewayWork, completeGatewayRun, deliverPrepared,
50
- enqueueGatewayWake, gatewayContext, gatewayRuntimeFindings,
51
+ enqueueGatewayWake, executionAttemptForStep, gatewayContext, gatewayRuntimeFindings,
51
52
  inspectGatewayRuntime, loadGatewayRuntime, reconcileGateway, resolveGoalKnowledgeGap, setGatewayControl
52
53
  } from "./lib/gateway-runtime.js";
53
54
  export { createTelegramAdapter } from "./lib/telegram-adapter.js";