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 +2 -2
- package/README.md +1 -1
- package/agent-spine-plugin/.claude-plugin/marketplace.json +1 -1
- package/agent-spine-plugin/.claude-plugin/plugin.json +1 -1
- package/agent-spine-plugin/.codex-plugin/plugin.json +1 -1
- package/agent-spine-plugin/README.md +13 -1
- package/agent-spine-plugin/blun.plugin.json +1 -1
- package/agent-spine-plugin/hooks/version.json +1 -1
- package/agent-spine-plugin/package.json +1 -1
- package/agent-spine-plugin/src/hook.js +40 -44
- package/agent-spine-plugin/src/index.js +3 -2
- package/agent-spine-plugin/src/lib/gateway-runtime.js +531 -27
- package/agent-spine-plugin/src/lib/selfstarter.js +20 -1
- package/agent-spine-plugin/src/lib/source-roots.js +3 -2
- package/agent-spine-plugin/src/version.js +1 -1
- package/agent-spine-plugin/src/worker.js +12 -4
- package/bin/native-runtime-cache.cjs +76 -0
- package/blun.mjs +10 -0
- package/package.json +1 -1
- package/telegram-plugin/dist/bridge.mjs +4 -1
package/LIESMICH.txt
CHANGED
package/README.md
CHANGED
|
@@ -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
|
|
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 |
|
|
@@ -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 (
|
|
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
|
-
|
|
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(
|
|
492
|
-
|
|
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.
|
|
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
|
|
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(
|
|
587
|
-
|
|
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) &&
|
|
676
|
-
|
|
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 =
|
|
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
|
-
|
|
863
|
-
if (input === SILENT_OVERSIZE_POST_TOOL_USE) return;
|
|
860
|
+
let input = payload;
|
|
864
861
|
try {
|
|
865
|
-
|
|
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
|
-
|
|
868
|
-
|
|
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,
|
|
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";
|