blun-king-cli 9.1.597 → 9.1.600

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 (106) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/README.md +17 -1
  3. package/agent-spine-plugin/CHANGELOG.md +1 -1
  4. package/agent-spine-plugin/docs/host-integration.md +1 -15
  5. package/agent-spine-plugin/docs/preflight-recall.md +1 -1
  6. package/agent-spine-plugin/scripts/check-install.js +1 -6
  7. package/agent-spine-plugin/scripts/hermetic-process.js +8 -0
  8. package/agent-spine-plugin/scripts/release-check.js +2 -3
  9. package/agent-spine-plugin/scripts/run-tests-hermetic.js +8 -11
  10. package/agent-spine-plugin/src/hook.js +40 -40
  11. package/agent-spine-plugin/src/lib/briefing.js +6 -3
  12. package/agent-spine-plugin/src/lib/hook-context.js +63 -64
  13. package/agent-spine-plugin/src/lib/hook-output.js +115 -94
  14. package/agent-spine-plugin/src/lib/hook-timeline.js +20 -11
  15. package/agent-spine-plugin/src/lib/host-instruction-budget.js +8 -14
  16. package/agent-spine-plugin/src/lib/mcp-timeline-tools.js +0 -7
  17. package/agent-spine-plugin/src/lib/pre-answer-timeline-recall.js +104 -0
  18. package/agent-spine-plugin/src/lib/session-timeline-codex.js +0 -4
  19. package/agent-spine-plugin/src/lib/session-timeline-enrollment-source.js +0 -3
  20. package/agent-spine-plugin/src/lib/session-timeline-enrollment-storage.js +0 -4
  21. package/agent-spine-plugin/src/lib/session-timeline-enrollment-transport.js +0 -2
  22. package/agent-spine-plugin/src/lib/session-timeline-enrollment.js +0 -22
  23. package/agent-spine-plugin/src/lib/session-timeline-host-origin.js +0 -4
  24. package/agent-spine-plugin/src/lib/session-timeline-prior.js +42 -50
  25. package/agent-spine-plugin/src/lib/session-timeline-provider.js +0 -3
  26. package/agent-spine-plugin/src/lib/session-timeline-query.js +0 -1
  27. package/agent-spine-plugin/src/lib/session-timeline-sid-acl.js +0 -4
  28. package/agent-spine-plugin/src/lib/session-timeline-source.js +0 -3
  29. package/agent-spine-plugin/src/lib/session-timeline-transport.js +0 -8
  30. package/agent-spine-plugin/src/lib/session-timeline-windows-acl.js +0 -3
  31. package/agent-spine-plugin/src/lib/session-timeline.js +370 -408
  32. package/agent-spine-plugin/src/lib/source-roots.js +26 -16
  33. package/agent-spine-plugin/src/lib/timeline-user-feedback.js +0 -1
  34. package/bin/core-bootstrap.js +18 -10
  35. package/bin/launcher-mode.js +5 -0
  36. package/bin/launcher-runtime.js +27 -0
  37. package/bin/verify-bundled-agent-sources.cjs +28 -2
  38. package/blun.mjs +798 -143
  39. package/bundled-agent-sources.json +192 -155
  40. package/package.json +1 -1
  41. package/standard-skills/translate-native/README.md +1334 -1
  42. package/standard-skills/translate-native/SKILL.md +1 -1
  43. package/standard-skills/translate-native/VERSION +1 -1
  44. package/standard-skills/translate-native/docs/PREMORTEM.md +901 -0
  45. package/standard-skills/translate-native/docs/WEBSITE_LOCALIZATION.md +208 -41
  46. package/standard-skills/translate-native/docs/WEBSITE_LOCALIZATION_API.md +1319 -77
  47. package/standard-skills/translate-native/docs/WEBSITE_LOCALIZATION_EVIDENCE_HTTP.md +66 -11
  48. package/standard-skills/translate-native/docs/WEBSITE_LOCALIZATION_HTTP_PROVIDER.md +19 -1
  49. package/standard-skills/translate-native/docs/WEBSITE_LOCALIZATION_RECEIPT_VERIFIER_HTTP.md +24 -6
  50. package/standard-skills/translate-native/integrations/commercial_localization_profile.py +23 -0
  51. package/standard-skills/translate-native/integrations/website_localization.py +234 -10
  52. package/standard-skills/translate-native/integrations/website_localization_benchmark.py +39 -3
  53. package/standard-skills/translate-native/integrations/website_localization_cms.py +512 -10
  54. package/standard-skills/translate-native/integrations/website_localization_cms_client.py +116 -0
  55. package/standard-skills/translate-native/integrations/website_localization_cms_receiver.py +101 -11
  56. package/standard-skills/translate-native/integrations/website_localization_cms_receiver_runtime.py +12 -1
  57. package/standard-skills/translate-native/integrations/website_localization_cms_receiver_store.py +186 -24
  58. package/standard-skills/translate-native/integrations/website_localization_cms_source_client.py +64 -1
  59. package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery.py +528 -5
  60. package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery_auth.py +918 -0
  61. package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery_auth_runtime.py +609 -0
  62. package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery_client.py +885 -0
  63. package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery_http.py +1244 -0
  64. package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery_runtime.py +220 -0
  65. package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery_sidecar_adapter.py +331 -0
  66. package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery_submission_capabilities_http.py +444 -0
  67. package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery_submission_client.py +673 -0
  68. package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery_submission_dispatch.py +1317 -0
  69. package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery_submission_dispatch_client.py +826 -0
  70. package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery_submission_dispatch_http.py +1152 -0
  71. package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery_submission_dispatch_openapi.py +896 -0
  72. package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery_submission_dispatch_runtime.py +621 -0
  73. package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery_submission_http.py +1180 -0
  74. package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery_submission_runtime.py +1479 -0
  75. package/standard-skills/translate-native/integrations/website_localization_cms_source_http.py +133 -17
  76. package/standard-skills/translate-native/integrations/website_localization_cms_source_runtime.py +319 -4
  77. package/standard-skills/translate-native/integrations/website_localization_cms_source_service.py +72 -2
  78. package/standard-skills/translate-native/integrations/website_localization_cms_terminal_notification_http.py +13 -0
  79. package/standard-skills/translate-native/integrations/website_localization_cms_terminal_notification_receiver.py +56 -13
  80. package/standard-skills/translate-native/integrations/website_localization_cms_terminal_notification_receiver_openapi.py +335 -0
  81. package/standard-skills/translate-native/integrations/website_localization_cms_terminal_notification_receiver_runtime.py +66 -1
  82. package/standard-skills/translate-native/integrations/website_localization_cms_terminal_processing_monitor.py +73 -7
  83. package/standard-skills/translate-native/integrations/website_localization_cms_terminal_receiver_client.py +93 -9
  84. package/standard-skills/translate-native/integrations/website_localization_evidence_http.py +221 -11
  85. package/standard-skills/translate-native/integrations/website_localization_health.py +6 -0
  86. package/standard-skills/translate-native/integrations/website_localization_http_provider.py +1 -1
  87. package/standard-skills/translate-native/integrations/website_localization_quality_profiles.py +350 -0
  88. package/standard-skills/translate-native/integrations/website_localization_queue.py +126 -56
  89. package/standard-skills/translate-native/integrations/website_localization_receipt_verifier_http.py +166 -13
  90. package/standard-skills/translate-native/integrations/website_localization_release.py +423 -17
  91. package/standard-skills/translate-native/integrations/website_localization_release_coordinator.py +168 -13
  92. package/standard-skills/translate-native/integrations/website_localization_runner.py +10 -0
  93. package/standard-skills/translate-native/integrations/website_localization_worker.py +213 -9
  94. package/standard-skills/translate-native/provenance.json +2 -2
  95. package/standard-skills/translate-native/references/commercial-localization.md +96 -17
  96. package/standard-skills/translate-native/scripts/blun_language_guard.py +3 -1
  97. package/standard-skills/translate-native/scripts/check_commercial_review.py +69 -9
  98. package/standard-skills/translate-native/scripts/commercial_localization_profile.py +829 -31
  99. package/standard-tools/language-guard/VERSION +1 -1
  100. package/standard-tools/language-guard/blun_language_guard.py +3 -1
  101. package/standard-tools/language-guard/check_commercial_review.py +69 -9
  102. package/standard-tools/language-guard/commercial_localization_profile.py +829 -31
  103. package/standard-tools/language-guard/provenance.json +2 -2
  104. package/standard-tools/manifest.json +1 -1
  105. package/worker-host.mjs +215 -82
  106. package/agent-spine-plugin/scripts/check-install-king.js +0 -37
@@ -8,7 +8,7 @@ import { isFileLockContention, replaceFileWithRetry } from "./filesystem-retry.j
8
8
  import { purgeIndexedMemoryCache, resolveIndexedMemory } from "./indexed-memory.js";
9
9
  import { hookMemoryQuery, loadLessonRecallSelection, rememberLessonRecallSelection } from "./lesson-recall-session.js";
10
10
  import { catalogScanPolicy } from "./catalog.js";
11
- import { isKingHost, KING_SOURCE_MAX_BYTES } from "./host-instruction-budget.js";
11
+ import { isKingHost, KING_SOURCE_BYTES } from "./host-instruction-budget.js";
12
12
  import {
13
13
  SOURCE_SCAN_INCOMPLETE, boundedMarkdownTree, existingDirectory, existingRegular, sourceScanError
14
14
  } from "./source-tree-scan.js";
@@ -25,6 +25,20 @@ const SOURCE_RESOLUTION_MS = 2000;
25
25
  const SAFE_NAME = /^[A-Za-z0-9._-]{1,128}$/;
26
26
 
27
27
  function digest(value) { return createHash("sha256").update(value).digest("hex"); }
28
+ async function partitionSources(sources, env) {
29
+ if (!isKingHost(env)) return { sources, unverified: [] };
30
+ const loaded = [], unverified = []; let used = 0;
31
+ for (const source of sources) {
32
+ if (!source.id.startsWith("codex:")) { loaded.push(source); continue; }
33
+ const bytes = (await lstat(source.path)).size, limit = source.maxBytes || KING_SOURCE_BYTES;
34
+ const total = bytes <= limit && used + bytes > MAX_TOTAL_SOURCE_BYTES;
35
+ if (bytes <= limit && !total) { loaded.push(source); used += bytes; continue; }
36
+ const ceiling = total ? MAX_TOTAL_SOURCE_BYTES : limit;
37
+ const message = `King rule ${source.id} (${bytes} bytes) exceeds AgentSpine's ${ceiling}-byte ${total ? "total " : ""}reader budget; AgentSpine did not load or verify it. Native rules apply. Continue; no retry.`;
38
+ unverified.push({ id: source.id, path: source.path, bytes, message });
39
+ }
40
+ return { sources: loaded, unverified };
41
+ }
28
42
  function registryPath(env = process.env) { return join(stateRoot(env), "source-roots.json"); }
29
43
  function emptyRegistry() { return { schema: SOURCE_REGISTRY_SCHEMA, revision: 0, bindings: [], history: [] }; }
30
44
 
@@ -354,7 +368,7 @@ export async function resolveHostSourceCatalog({ host, cwd = process.cwd(), inpu
354
368
  const { registry } = await readRegistry(env);
355
369
  let hostHome;
356
370
  let projectRoot;
357
- let sources;
371
+ let sources, unverified;
358
372
  let skipped = [];
359
373
  let hostDetails = {};
360
374
  let rootResolution = "explicit-root";
@@ -362,7 +376,7 @@ export async function resolveHostSourceCatalog({ host, cwd = process.cwd(), inpu
362
376
  const codexHome = isKingHost(env) ? env.BLUN_HOME : env.CODEX_HOME || env.BLUN_HOME || join(homedir(), ".codex");
363
377
  hostHome = await existingDirectory(resolve(codexHome)) || resolve(codexHome);
364
378
  const config = await codexConfig(hostHome);
365
- if (isKingHost(env)) config.maxBytes = KING_SOURCE_MAX_BYTES;
379
+ if (isKingHost(env)) config.maxBytes = KING_SOURCE_BYTES;
366
380
  if (env.AGENTSPINE_ROOT) projectRoot = await canonicalPath(env.AGENTSPINE_ROOT);
367
381
  else ({ root: projectRoot, resolution: rootResolution } = await findRoot(canonicalCwd, config.rootMarkers));
368
382
  sources = await codexSources({ cwd: canonicalCwd, projectRoot, codexHome: hostHome, config });
@@ -378,7 +392,6 @@ export async function resolveHostSourceCatalog({ host, cwd = process.cwd(), inpu
378
392
  hostDetails = { memoryRoot: result.memoryRoot, memoryProvenance: result.memoryProvenance,
379
393
  memoryDiagnostics: result.memoryDiagnostics };
380
394
  } else {
381
- // Generic hosts get project sources, never another provider's profile.
382
395
  hostHome = await canonicalPath(homedir());
383
396
  if (env.AGENTSPINE_ROOT) projectRoot = await canonicalPath(env.AGENTSPINE_ROOT);
384
397
  else ({ root: projectRoot, resolution: rootResolution } = await findRoot(canonicalCwd, [".git"]));
@@ -394,6 +407,7 @@ export async function resolveHostSourceCatalog({ host, cwd = process.cwd(), inpu
394
407
  }
395
408
  sources = [...new Map(sources.map((item) => [item.path, item])).values()];
396
409
  if (sources.length > MAX_SOURCES) throw new Error(`host-native required source set exceeds ${MAX_SOURCES} files`);
410
+ ({ sources, unverified } = await partitionSources(sources, env));
397
411
  const nativeNames = new Set(host === "codex"
398
412
  ? ["AGENTS.override.md", "AGENTS.md", ...(hostDetails.fallbackNames || [])]
399
413
  : host === "claude" ? ["CLAUDE.md", "CLAUDE.local.md"] : ["AGENTS.md", "SOUL.md", "MEMORY.md"]);
@@ -410,10 +424,6 @@ export async function resolveHostSourceCatalog({ host, cwd = process.cwd(), inpu
410
424
  }
411
425
  sources = [...new Map(sources.map((item) => [item.path, item])).values()];
412
426
  if (sources.length > MAX_SOURCES) throw new Error(`host-native source set exceeds ${MAX_SOURCES} files`);
413
- const projectScanIncomplete = skipped.some((item) => item.code === SOURCE_SCAN_INCOMPLETE);
414
- if (Date.now() > deadline && !projectScanIncomplete) {
415
- throw new Error(`host-native source resolution exceeded ${SOURCE_RESOLUTION_MS} ms`);
416
- }
417
427
  let documents;
418
428
  try {
419
429
  documents = await indexExplicitDocuments(sources);
@@ -421,25 +431,25 @@ export async function resolveHostSourceCatalog({ host, cwd = process.cwd(), inpu
421
431
  if (typeof error?.code === "string" && (error.path || error.syscall)) throw sourceScanError(error);
422
432
  throw error;
423
433
  }
424
- if (Date.now() > deadline && !projectScanIncomplete) {
425
- throw new Error(`host-native source resolution exceeded ${SOURCE_RESOLUTION_MS} ms`);
426
- }
427
434
  const totalBytes = documents.reduce((sum, document) => sum + document.bytes, 0);
428
435
  if (totalBytes > MAX_TOTAL_SOURCE_BYTES) throw new Error("host-native source set exceeds 8 MiB");
429
436
  const activeUserState = activeBinding(registry, host, hostHome, projectRoot, "state-user");
430
437
  const orderedSkipped = skipped.sort((a, b) => a.path.localeCompare(b.path) || a.operation.localeCompare(b.operation));
431
438
  const warnings = orderedSkipped.filter((item) => item.code === SOURCE_SCAN_INCOMPLETE);
439
+ const unverifiedWarning = unverified.length > 1
440
+ ? `${unverified.length} King rules exceed AgentSpine reader budgets; none were loaded or verified. Native rules apply. Continue; no retry.`
441
+ : unverified[0]?.message;
432
442
  const diagnostics = {
433
- schema: SOURCE_REGISTRY_SCHEMA, host, status: documents.length ? "loaded" : "empty", projectRoot,
443
+ schema: SOURCE_REGISTRY_SCHEMA, host, status: unverified.length ? "incomplete" : documents.length ? "loaded" : "empty", projectRoot,
434
444
  hostHomeDigest: digest(hostHome).slice(0, 16), checked: ["host-profile", "project-chain", ...(host === "claude" ? ["project-memory"] : [])],
435
445
  scopes: Object.fromEntries(["user", "project", "project-memory"].map((scope) => [scope, documents.filter((item) => item.sourceScope === scope).length])),
436
- reason: documents.length ? null : "No regular, non-symlink host-native Markdown source exists in the checked scope.",
446
+ reason: unverifiedWarning || (documents.length ? null : "No regular, non-symlink host-native Markdown source exists in the checked scope."),
437
447
  personalContinuityLoaded: documents.some((item) => item.sourceScope === "user") || Boolean(activeUserState),
438
448
  broadHomeScan: false, projectTreeScan: skippedFallbackHomeTree ? "skipped-unmarked-home"
439
449
  : skippedHomeTree ? "skipped-home-root" : skippedProfileTree ? "skipped-profile-root"
440
450
  : warnings.length ? "bounded-truncated" : "bounded",
441
- incomplete: warnings.length > 0,
442
- warning: warnings[0]?.message || null,
451
+ incomplete: Boolean(unverified.length || warnings.length),
452
+ warning: unverifiedWarning || warnings[0]?.message || null,
443
453
  warnings,
444
454
  skipped: orderedSkipped,
445
455
  rootResolution, registryRevision: registry.revision,
@@ -460,6 +470,6 @@ export async function resolveHostSourceCatalog({ host, cwd = process.cwd(), inpu
460
470
  summary: { total: documents.length, protected: documents.filter((item) => item.protected).length, conflicts: 0,
461
471
  byLayer: Object.fromEntries([...new Set(documents.map((item) => item.layer))].sort().map((layer) => [layer, documents.filter((item) => item.layer === layer).length])) }
462
472
  };
463
- return { host, hostHome, projectRoot, cwd: canonicalCwd, catalog, diagnostics,
473
+ return { host, hostHome, projectRoot, cwd: canonicalCwd, catalog, diagnostics, unverified,
464
474
  userStateRoot: activeUserState?.sourceRoot || null, memoryRoot: hostDetails.memoryRoot || null };
465
475
  }
@@ -69,7 +69,6 @@ function validateInterpretation(input, value) {
69
69
  return true;
70
70
  }
71
71
 
72
- // Provenance only.
73
72
  export function validateUserFeedback(input, value) {
74
73
  if (validateInterpretation(input, value)) return;
75
74
  if (input.evidenceKind !== KIND && value?.schema !== USER_FEEDBACK_SCHEMA) return;
@@ -7,12 +7,16 @@ const { pathToFileURL } = require('node:url');
7
7
  const { acquireSharedRuntimeLease } = require('./update-lease');
8
8
  const { installWindowsNodeCrashDump } = require('./windows-node-crash-dump.cjs');
9
9
  const { recordRuntimeExit } = require('./runtime-exit-ledger.cjs');
10
+ const { isStandaloneHelpRequest } = require('./launcher-mode');
10
11
 
11
12
  const CORE_LOADED_MESSAGE = 'blun-core-bootstrap-loaded';
12
13
 
13
14
  async function runCoreBootstrap() {
15
+ const informational = isStandaloneHelpRequest(process.argv.slice(2));
14
16
  let diagnostics;
15
- try { diagnostics = installWindowsNodeCrashDump({ homeDir: process.env.BLUN_HOME }); } catch {}
17
+ if (!informational) {
18
+ try { diagnostics = installWindowsNodeCrashDump({ homeDir: process.env.BLUN_HOME }); } catch {}
19
+ }
16
20
  const packageRoot = path.resolve(__dirname, '..');
17
21
  const mainPath = path.join(packageRoot, 'blun.mjs');
18
22
  let leaseResult;
@@ -25,18 +29,22 @@ async function runCoreBootstrap() {
25
29
  await import(pathToFileURL(mainPath).href);
26
30
  leaseResult.lease.assertOwned();
27
31
  } catch (error) {
28
- try {
29
- if (diagnostics?.enabled) diagnostics.write(error, 'bootstrap-failure');
30
- else recordRuntimeExit({ homeDir: process.env.BLUN_HOME, source: 'bootstrap',
31
- kind: 'bootstrap-failure', phase: 'startup', error });
32
- } catch {}
32
+ if (!informational) {
33
+ try {
34
+ if (diagnostics?.enabled) diagnostics.write(error, 'bootstrap-failure');
35
+ else recordRuntimeExit({ homeDir: process.env.BLUN_HOME, source: 'bootstrap',
36
+ kind: 'bootstrap-failure', phase: 'startup', error });
37
+ } catch {}
38
+ }
33
39
  try { diagnostics?.dispose(); } catch {}
34
40
  try { await leaseResult?.lease?.release(); }
35
41
  catch (leaseError) {
36
- try {
37
- recordRuntimeExit({ homeDir: process.env.BLUN_HOME, source: 'bootstrap',
38
- kind: 'lease-release-failure', phase: 'cleanup', error: leaseError });
39
- } catch {}
42
+ if (!informational) {
43
+ try {
44
+ recordRuntimeExit({ homeDir: process.env.BLUN_HOME, source: 'bootstrap',
45
+ kind: 'lease-release-failure', phase: 'cleanup', error: leaseError });
46
+ } catch {}
47
+ }
40
48
  }
41
49
  throw error;
42
50
  }
@@ -75,6 +75,10 @@ function isNpmPackageUpdateRequest(args) {
75
75
  return command === 'update' || command === 'upgrade';
76
76
  }
77
77
 
78
+ function isStandaloneHelpRequest(args) {
79
+ return Array.isArray(args) && args.length === 1 && (args[0] === '--help' || args[0] === '-h');
80
+ }
81
+
78
82
  function launcherHelpText() {
79
83
  return LAUNCHER_HELP_TEXT;
80
84
  }
@@ -83,6 +87,7 @@ module.exports = {
83
87
  LAUNCHER_MODES,
84
88
  createLauncherEnvironment,
85
89
  isNpmPackageUpdateRequest,
90
+ isStandaloneHelpRequest,
86
91
  launcherHelpText,
87
92
  launcherModeFromArgv,
88
93
  };
@@ -12,6 +12,7 @@ const {
12
12
  LAUNCHER_MODES,
13
13
  createLauncherEnvironment,
14
14
  isNpmPackageUpdateRequest,
15
+ isStandaloneHelpRequest,
15
16
  launcherHelpText,
16
17
  launcherModeFromArgv,
17
18
  } = require('./launcher-mode');
@@ -28,6 +29,7 @@ const {
28
29
  } = require('./launcher-restart-policy.cjs');
29
30
  const { recordRuntimeExit } = require('./runtime-exit-ledger.cjs');
30
31
  const { prepareManagedNodeRuntime } = require('./node-runtime');
32
+ const { unsupportedNodeMessage } = require('./node-version');
31
33
  const { repairConfiguredNativeModules } = require('./native-module-repair');
32
34
  const { acquireSharedRuntimeLease, tryAcquireUpdateLease } = require('./update-lease');
33
35
  const { runExplicitUpdate, runUpdateNotice } = require('./update-notice');
@@ -160,6 +162,15 @@ function spawnProtectedCore(args, env, cwd) {
160
162
  return { child, completed, loaded };
161
163
  }
162
164
 
165
+ async function runInformationalCore(args, env, cwd, releaseNotice) {
166
+ const core = spawnProtectedCore(args, env, cwd);
167
+ await core.loaded;
168
+ await releaseNotice();
169
+ const result = await core.completed;
170
+ if (result.error) throw result.error;
171
+ return Number.isInteger(result.code) ? result.code : exitCodeForChild(result, false);
172
+ }
173
+
163
174
  async function superviseProtectedCore(args, env, cwd, releaseNotice) {
164
175
  let noticeReleased = false;
165
176
  for (;;) {
@@ -347,6 +358,22 @@ async function runLauncher(options = {}) {
347
358
  return;
348
359
  }
349
360
 
361
+ if (isStandaloneHelpRequest(ARGS)) {
362
+ const compatibilityError = unsupportedNodeMessage(process.versions.node, process.platform);
363
+ if (compatibilityError !== null) {
364
+ process.stderr.write(`${compatibilityError}\n`);
365
+ process.exitCode = 1;
366
+ return;
367
+ }
368
+ process.stdout.write(launcherHelpText());
369
+ const env = createLauncherEnvironment(process.env, mode, readPackageVersion());
370
+ const informationalEnv = { ...process.env };
371
+ delete informationalEnv.BLUN_HOME;
372
+ env.BLUN_HOME = resolveLauncherPrivatePaths({ env: informationalEnv }).blunHome;
373
+ process.exitCode = await runInformationalCore(ARGS, env, callerCwd, releaseNotice);
374
+ return;
375
+ }
376
+
350
377
  let privatePaths;
351
378
  const getPrivatePaths = () => {
352
379
  privatePaths ||= resolveLauncherPrivatePaths();
@@ -5,10 +5,20 @@ const { createHash } = require('node:crypto');
5
5
  const RECEIPT = 'bundled-agent-sources.json';
6
6
  const SCHEMA = 'blun.bundled-agent-sources/v1';
7
7
  const PREFIXES = ['agent-spine-plugin/', 'standard-skills/translate-native/', 'standard-tools/language-guard/'];
8
+ const GUARD_RESPONSE_PROFILE = Object.freeze({
9
+ schema: 'blun.language-guard-response-profile/v1',
10
+ file: 'translate-native/scripts/blun_language_guard.py',
11
+ beforeSha256: '837361690535ded4251ca23fbab313f5c3a7005472c259aa8fc51ebf7ecb9cb9',
12
+ afterSha256: '1e21b570259de0145ace2abf55fb82bf8896c90aaae93839c97fff36a01731f2',
13
+ });
14
+ const GUARD_RESPONSE_PAYLOADS = Object.freeze([
15
+ 'standard-skills/translate-native/scripts/blun_language_guard.py',
16
+ 'standard-tools/language-guard/blun_language_guard.py',
17
+ ]);
8
18
  const REQUIRED = [
9
19
  'agent-spine-plugin/blun.plugin.json', 'agent-spine-plugin/src/hook.js',
10
20
  'agent-spine-plugin/src/mcp.js', 'agent-spine-plugin/skills/agent-spine/SKILL.md',
11
- 'standard-skills/translate-native/SKILL.md', 'standard-tools/language-guard/blun_language_guard.py',
21
+ 'standard-skills/translate-native/SKILL.md', ...GUARD_RESPONSE_PAYLOADS,
12
22
  'standard-tools/language-guard/check_diacritics.py', 'standard-tools/language-guard/language_quality.py',
13
23
  'standard-tools/language-guard/guard_service_client.py', 'standard-tools/language-guard/translation_guard.py',
14
24
  ];
@@ -46,6 +56,21 @@ function verifyBundledAgentSources(root, pins) {
46
56
  for (const file of expectedNames) {
47
57
  if (!/^[a-f0-9]{64}$/.test(receipt.files[file]) || actual[file] !== receipt.files[file]) throw new Error('Bundled source digest mismatch: ' + file);
48
58
  }
59
+ // Bind the semantic repair independently of the self-reported file inventory.
60
+ const patch = receipt.localPatches?.guardResponseProfile;
61
+ if (!patch || typeof patch !== 'object' || Array.isArray(patch)
62
+ || Object.keys(patch).length !== Object.keys(GUARD_RESPONSE_PROFILE).length
63
+ || Object.entries(GUARD_RESPONSE_PROFILE).some(([key, value]) => patch[key] !== value)) {
64
+ throw new Error('Invalid language guard response profile repair receipt');
65
+ }
66
+ if (receipt.sourceDigests?.translateNative?.[GUARD_RESPONSE_PROFILE.file] !== GUARD_RESPONSE_PROFILE.beforeSha256) {
67
+ throw new Error('Invalid language guard response profile source digest');
68
+ }
69
+ for (const file of GUARD_RESPONSE_PAYLOADS) {
70
+ if (actual[file] !== GUARD_RESPONSE_PROFILE.afterSha256) {
71
+ throw new Error('Invalid language guard response profile payload: ' + file);
72
+ }
73
+ }
49
74
  return receipt;
50
75
  }
51
76
 
@@ -54,4 +79,5 @@ if (require.main === module) {
54
79
  console.log(JSON.stringify({ verified: true, sources: receipt.sources, files: Object.keys(receipt.files).length }));
55
80
  } catch (error) { console.error(error.message); process.exitCode = 1; }
56
81
  }
57
- module.exports = { verifyBundledAgentSources, payloadInventory, sha256, RECEIPT, SCHEMA, PREFIXES, REQUIRED };
82
+ module.exports = { verifyBundledAgentSources, payloadInventory, sha256, RECEIPT, SCHEMA, PREFIXES, REQUIRED,
83
+ GUARD_RESPONSE_PROFILE, GUARD_RESPONSE_PAYLOADS };