blun-king-cli 9.1.510 → 9.1.511

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 (34) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/LIESMICH.txt +12 -1
  3. package/README.md +12 -1
  4. package/agent-spine-plugin/.claude-plugin/marketplace.json +1 -1
  5. package/agent-spine-plugin/.claude-plugin/plugin.json +1 -1
  6. package/agent-spine-plugin/.codex-plugin/plugin.json +2 -1
  7. package/agent-spine-plugin/CHANGELOG.md +70 -8
  8. package/agent-spine-plugin/README.md +1 -1
  9. package/agent-spine-plugin/blun.plugin.json +33 -33
  10. package/agent-spine-plugin/docs/acceptance.md +2 -2
  11. package/agent-spine-plugin/docs/gateway-runtime.md +8 -1
  12. package/agent-spine-plugin/docs/host-integration.md +34 -34
  13. package/agent-spine-plugin/docs/preflight-recall.md +69 -0
  14. package/agent-spine-plugin/docs/relationships.md +6 -0
  15. package/agent-spine-plugin/hooks/codex.json +47 -0
  16. package/agent-spine-plugin/hooks/hooks.json +11 -0
  17. package/agent-spine-plugin/hooks/version.json +2 -2
  18. package/agent-spine-plugin/package.json +4 -4
  19. package/agent-spine-plugin/scripts/check-hosts.js +53 -51
  20. package/agent-spine-plugin/scripts/check-install.js +53 -35
  21. package/agent-spine-plugin/scripts/release-check.js +11 -10
  22. package/agent-spine-plugin/skills/agent-spine/SKILL.md +1 -1
  23. package/agent-spine-plugin/src/cli.js +46 -1
  24. package/agent-spine-plugin/src/hook.js +168 -90
  25. package/agent-spine-plugin/src/index.js +6 -0
  26. package/agent-spine-plugin/src/lib/acceptance.js +40 -0
  27. package/agent-spine-plugin/src/lib/audit.js +9 -2
  28. package/agent-spine-plugin/src/lib/graph.js +22 -4
  29. package/agent-spine-plugin/src/lib/persona-runtime.js +103 -31
  30. package/agent-spine-plugin/src/lib/preflight.js +678 -0
  31. package/agent-spine-plugin/src/lib/source-roots.js +32 -32
  32. package/agent-spine-plugin/src/version.js +1 -1
  33. package/agent-spine-plugin/src/worker.js +20 -3
  34. package/package.json +1 -1
@@ -9,12 +9,12 @@ import { purgeIndexedMemoryCache, resolveIndexedMemory } from "./indexed-memory.
9
9
 
10
10
  export const SOURCE_REGISTRY_SCHEMA = "agentspine.source-roots/v1";
11
11
  const MAX_REGISTRY_BYTES = 1024 * 1024;
12
- const MAX_SOURCES = 256;
13
- const MAX_RULE_FILES = 128;
14
- const MAX_PROJECT_FILES = 240;
15
- const MAX_TOTAL_SOURCE_BYTES = 8 * 1024 * 1024;
16
- const MAX_DIRECTORY_ENTRIES = 4096;
17
- const MAX_PROJECT_DIRECTORY_ENTRIES = 8192;
12
+ const MAX_SOURCES = 256;
13
+ const MAX_RULE_FILES = 128;
14
+ const MAX_PROJECT_FILES = 240;
15
+ const MAX_TOTAL_SOURCE_BYTES = 8 * 1024 * 1024;
16
+ const MAX_DIRECTORY_ENTRIES = 4096;
17
+ const MAX_PROJECT_DIRECTORY_ENTRIES = 8192;
18
18
  const SOURCE_RESOLUTION_MS = 2000;
19
19
  const SAFE_NAME = /^[A-Za-z0-9._-]{1,128}$/;
20
20
  const SKIP_EXTRA_DIRS = new Set([".git", ".hg", ".svn", ".claude", ".codex", "node_modules", "vendor", "dist", "build", "coverage"]);
@@ -160,7 +160,7 @@ async function findRoot(cwd, markers) {
160
160
  }
161
161
  }
162
162
 
163
- async function containsProjectMarker(directory) {
163
+ async function containsProjectMarker(directory) {
164
164
  try {
165
165
  await lstat(join(directory, ".git"));
166
166
  return true;
@@ -168,37 +168,37 @@ async function containsProjectMarker(directory) {
168
168
  if (error.code === "ENOENT") return false;
169
169
  throw error;
170
170
  }
171
- }
172
-
173
- async function containsEmbeddedHostProfile(directory) {
174
- return Boolean(
175
- await existingDirectory(join(directory, ".runtime-private"))
176
- && await existingRegular(join(directory, "config.toml"))
177
- );
178
- }
179
-
180
- async function boundedMarkdownTree(directory, prefix, host, scope, precedenceStart, deadline, {
181
- projectBoundary = false,
182
- maxFiles = MAX_RULE_FILES,
183
- maxDirectoryEntries = MAX_DIRECTORY_ENTRIES
184
- } = {}) {
171
+ }
172
+
173
+ async function containsEmbeddedHostProfile(directory) {
174
+ return Boolean(
175
+ await existingDirectory(join(directory, ".runtime-private"))
176
+ && await existingRegular(join(directory, "config.toml"))
177
+ );
178
+ }
179
+
180
+ async function boundedMarkdownTree(directory, prefix, host, scope, precedenceStart, deadline, {
181
+ projectBoundary = false,
182
+ maxFiles = MAX_RULE_FILES,
183
+ maxDirectoryEntries = MAX_DIRECTORY_ENTRIES
184
+ } = {}) {
185
185
  const root = await existingDirectory(directory);
186
186
  if (!root) return [];
187
187
  const output = [];
188
188
  let visitedEntries = 0;
189
189
  async function walk(current) {
190
190
  if (Date.now() > deadline) throw new Error(`host-native source resolution exceeded ${SOURCE_RESOLUTION_MS} ms`);
191
- if (projectBoundary && current !== root
192
- && (await containsProjectMarker(current) || await containsEmbeddedHostProfile(current))) return;
191
+ if (projectBoundary && current !== root
192
+ && (await containsProjectMarker(current) || await containsEmbeddedHostProfile(current))) return;
193
193
  const entries = [];
194
194
  for await (const entry of await opendir(current)) {
195
195
  visitedEntries += 1;
196
- if (visitedEntries > maxDirectoryEntries) throw new Error(`host-native source tree exceeds ${maxDirectoryEntries} entries`);
196
+ if (visitedEntries > maxDirectoryEntries) throw new Error(`host-native source tree exceeds ${maxDirectoryEntries} entries`);
197
197
  entries.push(entry);
198
198
  }
199
199
  entries.sort((a, b) => a.name.localeCompare(b.name));
200
200
  for (const entry of entries) {
201
- if (output.length >= maxFiles) throw new Error(`host-native rule tree exceeds ${maxFiles} files`);
201
+ if (output.length >= maxFiles) throw new Error(`host-native rule tree exceeds ${maxFiles} files`);
202
202
  if (entry.isSymbolicLink()) continue;
203
203
  const path = join(current, entry.name);
204
204
  if (entry.isDirectory() && !entry.name.startsWith(".") && !SKIP_EXTRA_DIRS.has(entry.name)) await walk(path);
@@ -388,11 +388,11 @@ export async function resolveHostSourceCatalog({ host, cwd = process.cwd(), inpu
388
388
  const { registry } = await readRegistry(env);
389
389
  let hostHome;
390
390
  let projectRoot;
391
- let sources;
392
- let hostDetails = {};
393
- if (host === "codex") {
394
- const codexHome = env.CODEX_HOME || env.BLUN_HOME || join(homedir(), ".codex");
395
- hostHome = await existingDirectory(resolve(codexHome)) || resolve(codexHome);
391
+ let sources;
392
+ let hostDetails = {};
393
+ if (host === "codex") {
394
+ const codexHome = env.CODEX_HOME || env.BLUN_HOME || join(homedir(), ".codex");
395
+ hostHome = await existingDirectory(resolve(codexHome)) || resolve(codexHome);
396
396
  const config = await codexConfig(hostHome);
397
397
  projectRoot = env.AGENTSPINE_ROOT ? await canonicalPath(env.AGENTSPINE_ROOT) : await findRoot(canonicalCwd, config.rootMarkers);
398
398
  sources = await codexSources({ cwd: canonicalCwd, projectRoot, codexHome: hostHome, config });
@@ -407,8 +407,8 @@ export async function resolveHostSourceCatalog({ host, cwd = process.cwd(), inpu
407
407
  memoryDiagnostics: result.memoryDiagnostics };
408
408
  }
409
409
  if (projectRoot !== homedir() && projectRoot !== dirname(hostHome)) {
410
- sources.push(...await boundedMarkdownTree(projectRoot, "agentspine:project", host, "project", 3000, deadline,
411
- { projectBoundary: true, maxFiles: MAX_PROJECT_FILES, maxDirectoryEntries: MAX_PROJECT_DIRECTORY_ENTRIES }));
410
+ sources.push(...await boundedMarkdownTree(projectRoot, "agentspine:project", host, "project", 3000, deadline,
411
+ { projectBoundary: true, maxFiles: MAX_PROJECT_FILES, maxDirectoryEntries: MAX_PROJECT_DIRECTORY_ENTRIES }));
412
412
  }
413
413
  const nativeNames = new Set(host === "codex"
414
414
  ? ["AGENTS.override.md", "AGENTS.md", ...(hostDetails.fallbackNames || [])]
@@ -1 +1 @@
1
- export const VERSION = "0.8.0";
1
+ export const VERSION = "0.10.1";
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  import { spawn } from "node:child_process";
3
3
  import { watch } from "node:fs";
4
- import { realpath } from "node:fs/promises";
5
- import { isAbsolute, resolve } from "node:path";
4
+ import { lstat, realpath } from "node:fs/promises";
5
+ import { isAbsolute, join, resolve } from "node:path";
6
6
  import { claimGatewayWork, completeGatewayRun, deliverPrepared, failGatewayRun, loadGatewayRuntime, reconcileGateway, updateGatewayHealth } from "./lib/gateway-runtime.js";
7
7
  import { createTelegramAdapter } from "./lib/telegram-adapter.js";
8
8
  import { acknowledgeChannelDelivery, loadChannelRuntime } from "./lib/channel-runtime.js";
@@ -12,11 +12,25 @@ import { isMainModule } from "./lib/runtime.js";
12
12
  const MAX_FRAME = 64 * 1024;
13
13
  const WAKE_FILES = new Set(["attention.json", "channel-runtime.json", "gateway-policy.json", "persona-policy.json", "persona-runtime.json"]);
14
14
 
15
- export async function waitForGatewayWake(root, delayMs, { watchFactory = watch, realpathFactory = realpath } = {}) {
15
+ async function wakeSnapshot(directory) {
16
+ return Promise.all([...WAKE_FILES].sort().map(async (name) => {
17
+ try {
18
+ const metadata = await lstat(join(directory, name), { bigint: true });
19
+ return `${name}:${metadata.dev}:${metadata.ino}:${metadata.size}:${metadata.mtimeNs}`;
20
+ } catch (error) {
21
+ if (error.code === "ENOENT") return `${name}:missing`;
22
+ throw error;
23
+ }
24
+ })).then((entries) => entries.join("\n"));
25
+ }
26
+
27
+ export async function waitForGatewayWake(root, delayMs, { watchFactory = watch, realpathFactory = realpath, onReady = null } = {}) {
16
28
  const delay = Math.max(250, Math.min(60000, Number(delayMs) || 60000));
17
29
  const { directory } = await loadGatewayRuntime(root);
18
30
  let watchPath;
19
31
  try { watchPath = await realpathFactory(directory); } catch { return "watch-unavailable"; }
32
+ let before;
33
+ try { before = await wakeSnapshot(watchPath); } catch { return "watch-unavailable"; }
20
34
  return new Promise((resolvePromise) => {
21
35
  let settled = false; let watcher;
22
36
  const finish = (reason) => {
@@ -32,6 +46,9 @@ export async function waitForGatewayWake(root, delayMs, { watchFactory = watch,
32
46
  if (name === null || WAKE_FILES.has(name)) finish("event");
33
47
  });
34
48
  watcher.on?.("error", () => finish("watch-error"));
49
+ Promise.resolve().then(() => onReady?.(watchPath)).then(() => wakeSnapshot(watchPath)).then((after) => {
50
+ if (after !== before) finish("event");
51
+ }).catch(() => finish("watch-error"));
35
52
  } catch { finish("watch-unavailable"); }
36
53
  });
37
54
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blun-king-cli",
3
- "version": "9.1.510",
3
+ "version": "9.1.511",
4
4
  "description": "BLUN CLI - your own AI agent with a Telegram channel. Get it done. With BLUN.",
5
5
  "license": "MIT",
6
6
  "bin": {