ultra-builder-pro-cli 0.23.0 → 0.24.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.
Files changed (95) hide show
  1. package/.ultra-template/docs/research/README.md +4 -4
  2. package/.ultra-template/tasks/tasks.json +4 -2
  3. package/CHANGELOG.md +72 -0
  4. package/README.md +55 -39
  5. package/adapters/_shared/codex-assets.cjs +121 -65
  6. package/adapters/_shared/interaction-contract.cjs +7 -1
  7. package/adapters/_shared/provenance.cjs +7 -2
  8. package/adapters/_shared/runtime-assets.cjs +3 -2
  9. package/adapters/_shared/runtime-native.cjs +132 -0
  10. package/adapters/claude.js +60 -30
  11. package/adapters/codex.js +178 -81
  12. package/adapters/grok.js +372 -0
  13. package/adapters/kimi.js +7 -3
  14. package/adapters/opencode.js +210 -32
  15. package/agents/code-reviewer.md +4 -2
  16. package/agents/debugger.md +4 -1
  17. package/agents/review-code.md +7 -3
  18. package/agents/review-comments.md +7 -4
  19. package/agents/review-coordinator.md +11 -7
  20. package/agents/review-design.md +8 -4
  21. package/agents/review-errors.md +8 -4
  22. package/agents/review-spec.md +8 -5
  23. package/agents/review-tests.md +7 -4
  24. package/agents/tdd-runner.md +5 -2
  25. package/bin/install.js +9 -4
  26. package/bin/orchestrator.js +4 -13
  27. package/docs/AGENT-CONTEXT.md +143 -229
  28. package/docs/ARCHITECTURE.md +205 -494
  29. package/docs/ARTIFACT-AUTHORITY.md +35 -28
  30. package/docs/LEGACY-CLI-CROSSWALK.md +1 -1
  31. package/docs/PLUGIN-ISOLATION-CONTRACT.md +12 -9
  32. package/docs/ROADMAP.md +85 -110
  33. package/docs/RUNTIME-COMPAT-MATRIX.md +139 -175
  34. package/docs/SKILL-AUTHORING.md +3 -3
  35. package/docs/STATE-DB-ACCESS-POLICY.md +13 -8
  36. package/docs/WORKFLOW-LIFECYCLE.md +189 -448
  37. package/hooks/README.md +11 -8
  38. package/hooks/active_task_context.py +16 -16
  39. package/hooks/adapters/grok.py +134 -0
  40. package/hooks/{context_spine.py → context_envelope.py} +26 -22
  41. package/hooks/health_check.py +29 -34
  42. package/hooks/pre_stop_check.py +22 -12
  43. package/hooks/subagent_tracker.py +1 -1
  44. package/hooks/workflow_checkpoint.py +14 -13
  45. package/hooks/workflow_context.py +11 -12
  46. package/hooks/workflow_resume.py +14 -13
  47. package/mcp-server/{breadcrumb.cjs → hook-context.cjs} +7 -9
  48. package/mcp-server/lib/baseline-workflow.cjs +108 -82
  49. package/mcp-server/lib/canonical-json.cjs +19 -0
  50. package/mcp-server/lib/change-authority.cjs +26 -0
  51. package/mcp-server/lib/change-workflow.cjs +630 -45
  52. package/mcp-server/lib/context-envelope.cjs +508 -0
  53. package/mcp-server/lib/decision-records.cjs +337 -0
  54. package/mcp-server/lib/doctor.cjs +91 -33
  55. package/mcp-server/lib/init-project.cjs +78 -39
  56. package/mcp-server/lib/managed-file-write.cjs +116 -0
  57. package/mcp-server/lib/plan-checkpoint.cjs +177 -0
  58. package/mcp-server/lib/project-context.cjs +178 -0
  59. package/mcp-server/lib/stage-checkpoints.cjs +309 -0
  60. package/mcp-server/lib/state-db.cjs +155 -24
  61. package/mcp-server/lib/state-ops.cjs +21 -3
  62. package/mcp-server/lib/task-ledger.cjs +522 -13
  63. package/mcp-server/lib/ultra-facade.cjs +757 -442
  64. package/mcp-server/lib/worker-packet.cjs +358 -0
  65. package/mcp-server/server.cjs +200 -1103
  66. package/orchestrator/daemon.cjs +27 -9
  67. package/orchestrator/parallel-orchestrator.cjs +111 -76
  68. package/orchestrator/session-runner.cjs +135 -34
  69. package/package.json +10 -3
  70. package/skills/ultra-change/SKILL.md +10 -8
  71. package/skills/ultra-deliver/SKILL.md +17 -19
  72. package/skills/ultra-dev/SKILL.md +20 -11
  73. package/skills/ultra-doctor/SKILL.md +4 -4
  74. package/skills/ultra-init/SKILL.md +6 -5
  75. package/skills/ultra-init/references/brownfield-adoption.md +20 -14
  76. package/skills/ultra-plan/SKILL.md +13 -9
  77. package/skills/ultra-research/SKILL.md +18 -9
  78. package/skills/ultra-review/SKILL.md +14 -9
  79. package/skills/ultra-review/references/unified-schema.md +2 -0
  80. package/skills/ultra-review/scripts/review_wait.py +8 -0
  81. package/skills/ultra-status/SKILL.md +2 -2
  82. package/skills/ultra-test/SKILL.md +10 -6
  83. package/skills/ultra-think/SKILL.md +8 -7
  84. package/skills/ultra-think/references/interaction-boundary.md +56 -0
  85. package/spec/cli-protocol.md +80 -199
  86. package/spec/mcp-tools.yaml +43 -2806
  87. package/spec/schemas/state-db.sql +107 -4
  88. package/spec/schemas/{task-ledger.v1.schema.json → task-ledger.v2.schema.json} +70 -6
  89. package/ultra-tools/commands/status.cjs +369 -243
  90. package/mcp-server/lib/context-spine.cjs +0 -1208
  91. package/mcp-server/lib/decision-dialogue.cjs +0 -1128
  92. package/mcp-server/lib/project-breadcrumb.cjs +0 -173
  93. package/mcp-server/lib/spec-learning.cjs +0 -284
  94. package/mcp-server/lib/workflow-state.cjs +0 -3129
  95. package/skills/ultra-think/references/decision-dialogue.md +0 -155
@@ -30,44 +30,6 @@ const {
30
30
 
31
31
  const PLUGIN_NAME = 'ultra-builder-pro';
32
32
  const MANAGED_MARKER = 'Managed by Ultra Builder Pro Codex adapter.';
33
- const CODEX_NATIVE_MCP_REPLACEMENTS = Object.freeze({
34
- 'review.run': {
35
- surface: 'native_custom_agents',
36
- replacement: '$ultra-builder-pro:ultra-review with the installed review-* agents',
37
- },
38
- 'review.verdict': {
39
- surface: 'native_custom_agents',
40
- replacement: 'review-coordinator synthesis using the Ultra unified review artifact',
41
- },
42
- 'impact.radius': {
43
- surface: 'codex_code_discovery',
44
- replacement: 'indexed code graph tools when current, otherwise targeted repository reads',
45
- },
46
- 'impact.changes': {
47
- surface: 'codex_code_discovery',
48
- replacement: 'git diff plus indexed code graph tools when current',
49
- },
50
- 'impact.dependents': {
51
- surface: 'codex_code_discovery',
52
- replacement: 'indexed caller/dependency tracing when current, otherwise targeted repository search',
53
- },
54
- 'skill.resolve': {
55
- surface: 'plugin_skill_discovery',
56
- replacement: 'Codex plugin skill discovery with explicit $ultra-builder-pro:<skill> invocation',
57
- },
58
- 'skill.manifest': {
59
- surface: 'plugin_skill_discovery',
60
- replacement: 'the installed plugin skill SKILL.md and agents/openai.yaml contract',
61
- },
62
- 'ask.question': {
63
- surface: 'request_user_input',
64
- replacement: 'use request_user_input when available, otherwise ask one concise direct question',
65
- },
66
- 'ask.menu': {
67
- surface: 'request_user_input',
68
- replacement: 'use request_user_input when available, otherwise present one concise direct choice',
69
- },
70
- });
71
33
  const COMMAND_NAMES = Object.freeze(CORE_PUBLIC_SKILLS.filter((name) => name !== 'ultra-review'));
72
34
  const SKILL_REFERENCE_NAMES = Object.freeze([
73
35
  ...COMMAND_NAMES,
@@ -77,6 +39,11 @@ const SKILL_REFERENCE_NAMES = Object.freeze([
77
39
  ]);
78
40
  const CODEX_PRIMARY_SKILLS = new Set([...COMMAND_NAMES, 'ultra-review']);
79
41
  const TEXT_EXTENSIONS = new Set(['.md', '.json', '.py', '.sh', '.txt', '.yaml', '.yml']);
42
+ const NATIVE_PACKAGES = Object.freeze([
43
+ 'better-sqlite3',
44
+ 'bindings',
45
+ 'file-uri-to-path',
46
+ ]);
80
47
 
81
48
  function titleCase(name) {
82
49
  const special = { 'cc-collab': 'Claude Code Collaboration' };
@@ -273,7 +240,7 @@ function buildHooksManifest(pluginRoot) {
273
240
  };
274
241
  }
275
242
 
276
- function copyHooks(repoRoot, pluginRoot) {
243
+ function copyHooks(repoRoot, pluginRoot, publishedRoot = pluginRoot) {
277
244
  const sourceRoot = path.join(repoRoot, 'hooks');
278
245
  const targetRoot = path.join(pluginRoot, 'hooks');
279
246
  ensureDir(targetRoot);
@@ -287,7 +254,7 @@ function copyHooks(repoRoot, pluginRoot) {
287
254
  );
288
255
  writeAtomic(
289
256
  path.join(targetRoot, 'hooks.json'),
290
- JSON.stringify(buildHooksManifest(pluginRoot), null, 2) + '\n',
257
+ JSON.stringify(buildHooksManifest(publishedRoot), null, 2) + '\n',
291
258
  );
292
259
  }
293
260
 
@@ -322,6 +289,8 @@ function buildMcpRuntime(repoRoot, pluginRoot, { runtime = 'codex' } = {}) {
322
289
  buildRoot,
323
290
  '--no-cache',
324
291
  '--quiet',
292
+ '-e',
293
+ 'better-sqlite3',
325
294
  ], {
326
295
  cwd: repoRoot,
327
296
  encoding: 'utf8',
@@ -347,6 +316,8 @@ function buildMcpRuntime(repoRoot, pluginRoot, { runtime = 'codex' } = {}) {
347
316
  cliBuildRoot,
348
317
  '--no-cache',
349
318
  '--quiet',
319
+ '-e',
320
+ 'better-sqlite3',
350
321
  ], {
351
322
  cwd: repoRoot,
352
323
  encoding: 'utf8',
@@ -386,6 +357,8 @@ function buildMcpRuntime(repoRoot, pluginRoot, { runtime = 'codex' } = {}) {
386
357
  workerBuildRoot,
387
358
  '--no-cache',
388
359
  '--quiet',
360
+ '-e',
361
+ 'better-sqlite3',
389
362
  ], {
390
363
  cwd: repoRoot,
391
364
  encoding: 'utf8',
@@ -416,6 +389,52 @@ function buildMcpRuntime(repoRoot, pluginRoot, { runtime = 'codex' } = {}) {
416
389
  fs.copyFileSync(sourceFile, path.join(runtimeRoot, supportFile));
417
390
  }
418
391
 
392
+ const nodeModulesRoot = path.join(runtimeRoot, 'node_modules');
393
+ for (const packageName of NATIVE_PACKAGES) {
394
+ let packageFile;
395
+ try {
396
+ packageFile = require.resolve(`${packageName}/package.json`, { paths: [repoRoot] });
397
+ } catch (error) {
398
+ throw new Error(`Ultra native runtime dependency ${packageName} is unavailable: ${error.message}`);
399
+ }
400
+ copyTree(
401
+ path.dirname(packageFile),
402
+ path.join(nodeModulesRoot, packageName),
403
+ );
404
+ }
405
+ const nativeRelative = path.join(
406
+ 'node_modules',
407
+ 'better-sqlite3',
408
+ 'build',
409
+ 'Release',
410
+ 'better_sqlite3.node',
411
+ );
412
+ const nativeFile = path.join(runtimeRoot, nativeRelative);
413
+ if (!fs.existsSync(nativeFile) || !fs.statSync(nativeFile).isFile()) {
414
+ throw new Error(`Ultra native SQLite driver is missing after staging: ${nativeFile}`);
415
+ }
416
+ const betterSqlitePackage = JSON.parse(fs.readFileSync(
417
+ path.join(nodeModulesRoot, 'better-sqlite3', 'package.json'),
418
+ 'utf8',
419
+ ));
420
+ writeAtomic(path.join(runtimeRoot, 'native-runtime.json'), JSON.stringify({
421
+ schema_version: 1,
422
+ platform: process.platform,
423
+ arch: process.arch,
424
+ modules: process.versions.modules,
425
+ node_version: process.versions.node,
426
+ package: {
427
+ name: 'better-sqlite3',
428
+ version: betterSqlitePackage.version,
429
+ },
430
+ native_module: nativeRelative.split(path.sep).join('/'),
431
+ native_sha256: crypto.createHash('sha256').update(fs.readFileSync(nativeFile)).digest('hex'),
432
+ }, null, 2) + '\n');
433
+ fs.copyFileSync(
434
+ path.join(repoRoot, 'adapters', '_shared', 'runtime-native.cjs'),
435
+ path.join(runtimeRoot, 'runtime-native.cjs'),
436
+ );
437
+
419
438
  const runtimeBootstrap = runtime === 'kimi' ? `
420
439
  const fs = require('node:fs');
421
440
  const pluginRoot = path.resolve(__dirname, '..');
@@ -443,10 +462,12 @@ if (!process.env.UBP_ROOT_DIR) {
443
462
  const launcher = `'use strict';
444
463
 
445
464
  const path = require('node:path');
465
+ const { inspectRuntimeNative } = require('./runtime-native.cjs');
446
466
 
447
467
  process.env.UBP_RUNTIME_ROOT = path.resolve(__dirname, '..');
448
468
  ${runtimeBootstrap}
449
469
 
470
+ inspectRuntimeNative(__dirname);
450
471
  const { main } = require('./index.cjs');
451
472
 
452
473
  main().catch((error) => {
@@ -456,8 +477,8 @@ main().catch((error) => {
456
477
  `;
457
478
  writeAtomic(path.join(runtimeRoot, 'launch.cjs'), launcher);
458
479
  fs.copyFileSync(
459
- path.join(repoRoot, 'mcp-server', 'breadcrumb.cjs'),
460
- path.join(runtimeRoot, 'breadcrumb.cjs'),
480
+ path.join(repoRoot, 'mcp-server', 'hook-context.cjs'),
481
+ path.join(runtimeRoot, 'hook-context.cjs'),
461
482
  );
462
483
  fs.copyFileSync(
463
484
  path.join(repoRoot, 'mcp-server', 'hook-event.cjs'),
@@ -470,11 +491,7 @@ main().catch((error) => {
470
491
 
471
492
  const sourceToolsFile = path.join(repoRoot, 'spec', 'mcp-tools.yaml');
472
493
  const upstreamManifest = yaml.load(fs.readFileSync(sourceToolsFile, 'utf8'));
473
- const {
474
- LEGACY_TOOLS,
475
- PUBLIC_TOOLS,
476
- REGISTERED_TOOLS,
477
- } = require(path.join(repoRoot, 'mcp-server', 'server.cjs'));
494
+ const { REGISTERED_TOOLS } = require(path.join(repoRoot, 'mcp-server', 'server.cjs'));
478
495
  const registered = new Set(REGISTERED_TOOLS);
479
496
  const liveFamilies = new Set(
480
497
  upstreamManifest.tools.filter((tool) => registered.has(tool.name)).map((tool) => tool.family),
@@ -495,15 +512,6 @@ main().catch((error) => {
495
512
  path.join(specRoot, 'interaction-contract.json'),
496
513
  JSON.stringify(interactionContract(runtime), null, 2) + '\n',
497
514
  );
498
- if (runtime === 'codex') {
499
- fs.copyFileSync(sourceToolsFile, path.join(specRoot, 'upstream-mcp-tools.yaml'));
500
- writeAtomic(path.join(specRoot, 'codex-capability-map.json'), JSON.stringify({
501
- runtime: 'codex',
502
- live_mcp_tools: PUBLIC_TOOLS,
503
- compatibility_mcp_tools: LEGACY_TOOLS,
504
- codex_native_replacements: CODEX_NATIVE_MCP_REPLACEMENTS,
505
- }, null, 2) + '\n');
506
- }
507
515
  const sourceSchema = path.join(repoRoot, 'spec', 'schemas', 'state-db.sql');
508
516
  const targetSchema = path.join(specRoot, 'schemas', 'state-db.sql');
509
517
  ensureDir(path.dirname(targetSchema));
@@ -521,9 +529,49 @@ main().catch((error) => {
521
529
  launcher: path.join(runtimeRoot, 'launch.cjs'),
522
530
  bundle: path.join(runtimeRoot, 'index.cjs'),
523
531
  ultraTools: path.join(runtimeRoot, 'ultra-tools.cjs'),
532
+ nativeManifest: path.join(runtimeRoot, 'native-runtime.json'),
524
533
  };
525
534
  }
526
535
 
536
+ function mcpCommand(launcher) {
537
+ if (process.platform === 'win32') {
538
+ return { command: 'node.exe', args: [launcher] };
539
+ }
540
+ return { command: '/usr/bin/env', args: ['node', launcher] };
541
+ }
542
+
543
+ function inspectInstalledRuntime(runtimeRoot) {
544
+ const report = {
545
+ status: 'healthy',
546
+ checks: {},
547
+ issues: [],
548
+ };
549
+ try {
550
+ const detail = require(path.join(runtimeRoot, 'runtime-native.cjs'))
551
+ .inspectRuntimeNative(runtimeRoot);
552
+ report.checks.native_runtime = { status: 'pass', ...detail };
553
+ } catch (error) {
554
+ report.status = 'degraded';
555
+ report.checks.native_runtime = { status: 'fail' };
556
+ report.issues.push({
557
+ code: error.code || 'RUNTIME_NATIVE_MISSING',
558
+ message: error.message,
559
+ ...(error.details || {}),
560
+ });
561
+ }
562
+ return report;
563
+ }
564
+
565
+ function applyNativeDoctor(report, runtimeRoot) {
566
+ const native = inspectInstalledRuntime(runtimeRoot);
567
+ report.checks = { ...(report.checks || {}), ...native.checks };
568
+ report.issues = [...(report.issues || []), ...native.issues];
569
+ if (report.status !== 'missing') {
570
+ report.status = report.issues.length === 0 ? 'healthy' : 'degraded';
571
+ }
572
+ return report;
573
+ }
574
+
527
575
  function pluginContentHash(pluginRoot, baseVersion) {
528
576
  const hash = crypto.createHash('sha256');
529
577
  hash.update(baseVersion);
@@ -535,13 +583,14 @@ function pluginContentHash(pluginRoot, baseVersion) {
535
583
  return hash.digest('hex').slice(0, 12);
536
584
  }
537
585
 
538
- function buildPlugin({ repoRoot, pluginRoot }) {
586
+ function buildPlugin({ repoRoot, pluginRoot, publishedRoot = pluginRoot }) {
539
587
  if (fs.existsSync(pluginRoot)) {
540
588
  const marker = path.join(pluginRoot, '.ubp-managed');
541
- if (!fs.existsSync(marker)) {
589
+ const entries = fs.readdirSync(pluginRoot);
590
+ if (entries.length > 0 && !fs.existsSync(marker)) {
542
591
  throw new Error(`refusing to replace unmanaged plugin directory: ${pluginRoot}`);
543
592
  }
544
- removeTree(pluginRoot);
593
+ if (entries.length > 0) removeTree(pluginRoot);
545
594
  }
546
595
  ensureDir(pluginRoot);
547
596
 
@@ -555,14 +604,18 @@ function buildPlugin({ repoRoot, pluginRoot }) {
555
604
  installedSkills.push(name);
556
605
  }
557
606
 
558
- copyHooks(repoRoot, pluginRoot);
607
+ copyHooks(repoRoot, pluginRoot, publishedRoot);
559
608
  const mcpRuntime = buildMcpRuntime(repoRoot, pluginRoot);
609
+ const command = mcpCommand(path.join(
610
+ publishedRoot,
611
+ path.relative(pluginRoot, mcpRuntime.launcher),
612
+ ));
560
613
  writeAtomic(path.join(pluginRoot, '.mcp.json'), JSON.stringify({
561
614
  mcpServers: {
562
615
  [PLUGIN_NAME]: {
563
616
  type: 'stdio',
564
- command: process.execPath,
565
- args: [mcpRuntime.launcher],
617
+ command: command.command,
618
+ args: command.args,
566
619
  },
567
620
  },
568
621
  }, null, 2) + '\n');
@@ -576,7 +629,7 @@ function buildPlugin({ repoRoot, pluginRoot }) {
576
629
  const manifest = {
577
630
  name: PLUGIN_NAME,
578
631
  version,
579
- description: 'Codex-native Ultra Builder Pro workflows, agents, hooks, and MCP task state.',
632
+ description: 'Codex-native Ultra Builder Pro workflows with a persistent safety kernel.',
580
633
  author: { name: typeof pkg.author === 'string' ? pkg.author : 'Ultra Builder Pro contributors' },
581
634
  homepage: pkg.homepage,
582
635
  repository: 'https://github.com/rocky2431/ultra-builder-pro-cli',
@@ -587,7 +640,7 @@ function buildPlugin({ repoRoot, pluginRoot }) {
587
640
  interface: {
588
641
  displayName: 'Ultra Builder Pro',
589
642
  shortDescription: 'Codex-native engineering workflows and verification gates.',
590
- longDescription: 'Complete Codex adaptation of Ultra Builder Pro skills, command workflows, custom agents, lifecycle hooks, and project-local MCP state.',
643
+ longDescription: 'Complete Codex adaptation of Ultra Builder Pro skills, command workflows, custom agents, lifecycle hooks, and a project-local persistence and safety kernel.',
591
644
  developerName: typeof pkg.author === 'string' ? pkg.author : 'Ultra Builder Pro contributors',
592
645
  category: 'Developer Tools',
593
646
  capabilities: ['Interactive', 'Write'],
@@ -602,7 +655,7 @@ function buildPlugin({ repoRoot, pluginRoot }) {
602
655
  };
603
656
  writeAtomic(path.join(pluginRoot, '.codex-plugin', 'plugin.json'), JSON.stringify(manifest, null, 2) + '\n');
604
657
  writeAtomic(path.join(pluginRoot, '.ubp-managed'), JSON.stringify({ source: 'ubp', adapter: 'codex', version }, null, 2) + '\n');
605
- return { root: pluginRoot, version, skills: installedSkills.sort() };
658
+ return { root: publishedRoot, version, skills: installedSkills.sort() };
606
659
  }
607
660
 
608
661
  function tomlMultiline(value) {
@@ -663,6 +716,9 @@ module.exports = {
663
716
  buildCommandMap,
664
717
  buildHooksManifest,
665
718
  buildMcpRuntime,
719
+ inspectInstalledRuntime,
720
+ applyNativeDoctor,
721
+ mcpCommand,
666
722
  buildPlugin,
667
723
  buildAgentToml,
668
724
  installAgents,
@@ -23,10 +23,15 @@ const SURFACES = Object.freeze({
23
23
  fallback: 'direct_user_interaction',
24
24
  availability: 'interactive_non_auto_mode',
25
25
  }),
26
+ grok: Object.freeze({
27
+ primary: 'AskUserQuestion',
28
+ fallback: 'direct_user_interaction',
29
+ availability: 'interactive_session',
30
+ }),
26
31
  });
27
32
 
28
33
  const GENERIC_QUESTION_GUIDANCE =
29
- /Use the host-native structured question surface declared by the installed interaction\s+contract(?: when it exists)?\./g;
34
+ /Use the host-native (?:structured )?question surface declared by the installed interaction\s+contract(?: when it exists)?(?:;?\s+prefer its structured form when the host exposes one)?\./g;
30
35
 
31
36
  function interactionContract(runtime) {
32
37
  const question = SURFACES[runtime];
@@ -106,6 +111,7 @@ function interactionPrompt(runtime) {
106
111
  codex: 'Codex',
107
112
  opencode: 'OpenCode',
108
113
  kimi: 'Kimi Code',
114
+ grok: 'Grok Build',
109
115
  }[runtime];
110
116
  const primary = runtime === 'opencode' ? `\`${surface.primary}\`` : surface.primary;
111
117
  return `Use ${host} ${primary} when ${surface.availability.replaceAll('_', ' ')} permits it.`;
@@ -187,7 +187,8 @@ function assetRefsForTree(rootName, root, { exclude = [] } = {}) {
187
187
  function writeProvenance({
188
188
  file, adapter, packageInfo, repository, sourceCommit: commit = null,
189
189
  sourceDirty: dirty = null, sourceWorktreeDigest: worktreeDigest = null,
190
- roots: inputRoots, assets: inputAssets, contracts: inputContracts = {},
190
+ roots: inputRoots, assetRoots: inputAssetRoots = inputRoots,
191
+ assets: inputAssets, contracts: inputContracts = {},
191
192
  }) {
192
193
  if (!file || !adapter || !packageInfo?.name || !packageInfo?.version || !repository) {
193
194
  throw new Error('provenance file, adapter, package, version, and repository are required');
@@ -199,9 +200,13 @@ function writeProvenance({
199
200
  throw new Error('provenance source worktree digest must be a SHA-256 digest or null');
200
201
  }
201
202
  const roots = normalizeRoots(inputRoots);
203
+ const assetRoots = normalizeRoots(inputAssetRoots);
204
+ for (const root of Object.keys(roots)) {
205
+ if (!assetRoots[root]) throw new Error(`missing provenance asset root: ${root}`);
206
+ }
202
207
  const refs = normalizeAssetRefs(inputAssets, roots);
203
208
  const assets = refs.map((ref) => {
204
- const { target } = resolveRef(roots, ref);
209
+ const { target } = resolveRef(assetRoots, ref);
205
210
  const stat = fs.statSync(target);
206
211
  if (!stat.isFile()) throw new Error(`provenance asset is not a file: ${target}`);
207
212
  return { ...ref, size_bytes: stat.size, sha256: hashFile(target) };
@@ -56,13 +56,14 @@ const INTERNAL_AGENT_SKILLS = Object.freeze([
56
56
  'testing-rules',
57
57
  ]);
58
58
 
59
- const SUPPORTED_RUNTIMES = Object.freeze(['claude', 'opencode', 'codex', 'kimi']);
59
+ const SUPPORTED_RUNTIMES = Object.freeze(['claude', 'opencode', 'codex', 'kimi', 'grok']);
60
60
 
61
61
  const COLLAB_SKILLS_BY_RUNTIME = Object.freeze({
62
62
  claude: Object.freeze(['codex-collab', 'ultra-verify']),
63
63
  codex: Object.freeze(['cc-collab', 'ultra-verify']),
64
64
  opencode: Object.freeze(['cc-collab', 'codex-collab', 'ultra-verify']),
65
65
  kimi: Object.freeze(['cc-collab', 'codex-collab', 'ultra-verify']),
66
+ grok: Object.freeze(['cc-collab', 'codex-collab', 'ultra-verify']),
66
67
  });
67
68
 
68
69
  const MCP_DEPENDENT_SKILLS = Object.freeze([
@@ -80,7 +81,7 @@ const MCP_DEPENDENT_SKILLS = Object.freeze([
80
81
 
81
82
  const WORKFLOW_HOOK_FILES = Object.freeze([
82
83
  'active_task_context.py',
83
- 'context_spine.py',
84
+ 'context_envelope.py',
84
85
  'health_check.py',
85
86
  'pre_stop_check.py',
86
87
  'runtime_paths.py',
@@ -0,0 +1,132 @@
1
+ 'use strict';
2
+
3
+ const crypto = require('node:crypto');
4
+ const fs = require('node:fs');
5
+ const path = require('node:path');
6
+
7
+ const MANIFEST_FILE = 'native-runtime.json';
8
+
9
+ class RuntimeNativeError extends Error {
10
+ constructor(code, message, details = {}) {
11
+ super(message);
12
+ this.name = 'RuntimeNativeError';
13
+ this.code = code;
14
+ this.details = details;
15
+ }
16
+ }
17
+
18
+ function sha256(file) {
19
+ return crypto.createHash('sha256').update(fs.readFileSync(file)).digest('hex');
20
+ }
21
+
22
+ function readManifest(runtimeRoot) {
23
+ const file = path.join(path.resolve(runtimeRoot), MANIFEST_FILE);
24
+ if (!fs.existsSync(file)) {
25
+ throw new RuntimeNativeError(
26
+ 'RUNTIME_NATIVE_MISSING',
27
+ `Ultra native runtime manifest is missing: ${file}`,
28
+ { path: file },
29
+ );
30
+ }
31
+ let manifest;
32
+ try {
33
+ manifest = JSON.parse(fs.readFileSync(file, 'utf8'));
34
+ } catch (cause) {
35
+ throw new RuntimeNativeError(
36
+ 'RUNTIME_NATIVE_MISSING',
37
+ `Ultra native runtime manifest is invalid: ${file}`,
38
+ { path: file, cause: cause.message },
39
+ );
40
+ }
41
+ if (manifest?.schema_version !== 1
42
+ || typeof manifest.native_module !== 'string'
43
+ || !/^[0-9a-f]{64}$/i.test(manifest.native_sha256 || '')) {
44
+ throw new RuntimeNativeError(
45
+ 'RUNTIME_NATIVE_MISSING',
46
+ `Ultra native runtime manifest is incomplete: ${file}`,
47
+ { path: file },
48
+ );
49
+ }
50
+ return { file, manifest };
51
+ }
52
+
53
+ function inspectRuntimeNative(runtimeRoot, { load = true } = {}) {
54
+ const root = path.resolve(runtimeRoot);
55
+ const { file, manifest } = readManifest(root);
56
+ const expected = {
57
+ platform: process.platform,
58
+ arch: process.arch,
59
+ modules: process.versions.modules,
60
+ };
61
+ const actual = {
62
+ platform: manifest.platform,
63
+ arch: manifest.arch,
64
+ modules: String(manifest.modules || ''),
65
+ };
66
+ if (actual.platform !== expected.platform
67
+ || actual.arch !== expected.arch
68
+ || actual.modules !== expected.modules) {
69
+ throw new RuntimeNativeError(
70
+ 'RUNTIME_ABI_MISMATCH',
71
+ `Ultra native runtime requires ${actual.platform}/${actual.arch} ABI ${
72
+ actual.modules
73
+ }, but the active Node is ${expected.platform}/${expected.arch} ABI ${expected.modules}`,
74
+ { expected: actual, actual: expected, manifest: file },
75
+ );
76
+ }
77
+ const nativeFile = path.resolve(root, manifest.native_module);
78
+ if (nativeFile !== root && !nativeFile.startsWith(`${root}${path.sep}`)) {
79
+ throw new RuntimeNativeError(
80
+ 'RUNTIME_NATIVE_MISSING',
81
+ 'Ultra native module path escapes the runtime root',
82
+ { path: manifest.native_module },
83
+ );
84
+ }
85
+ if (!fs.existsSync(nativeFile) || !fs.statSync(nativeFile).isFile()) {
86
+ throw new RuntimeNativeError(
87
+ 'RUNTIME_NATIVE_MISSING',
88
+ `Ultra native SQLite driver is missing: ${nativeFile}`,
89
+ { path: nativeFile },
90
+ );
91
+ }
92
+ const digest = sha256(nativeFile);
93
+ if (digest !== manifest.native_sha256) {
94
+ throw new RuntimeNativeError(
95
+ 'RUNTIME_NATIVE_MISSING',
96
+ `Ultra native SQLite driver digest does not match its manifest: ${nativeFile}`,
97
+ { path: nativeFile, expected: manifest.native_sha256, actual: digest },
98
+ );
99
+ }
100
+ if (load) {
101
+ let Database;
102
+ try {
103
+ Database = require(require.resolve('better-sqlite3', {
104
+ paths: [path.join(root, 'node_modules')],
105
+ }));
106
+ const db = new Database(':memory:');
107
+ db.prepare('SELECT 1 AS ok').get();
108
+ db.close();
109
+ } catch (cause) {
110
+ throw new RuntimeNativeError(
111
+ 'RUNTIME_ABI_MISMATCH',
112
+ `Ultra native SQLite driver cannot load in the active Node runtime: ${cause.message}`,
113
+ { path: nativeFile, cause: cause.code || cause.message },
114
+ );
115
+ }
116
+ }
117
+ return {
118
+ status: 'healthy',
119
+ manifest: file,
120
+ platform: actual.platform,
121
+ arch: actual.arch,
122
+ modules: actual.modules,
123
+ native_module: nativeFile,
124
+ native_sha256: digest,
125
+ };
126
+ }
127
+
128
+ module.exports = {
129
+ MANIFEST_FILE,
130
+ RuntimeNativeError,
131
+ inspectRuntimeNative,
132
+ };