@aiwg/cli 2026.10.5 → 2026.10.7

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 (263) hide show
  1. package/agentic/code/addons/composition-engine/lib/runtime.mjs +1401 -0
  2. package/agentic/code/addons/composition-engine/lib/validator.mjs +513 -0
  3. package/agentic/code/addons/composition-engine/schemas/flow-graph.schema.json +372 -0
  4. package/agentic/code/providers/model-capabilities.v1.json +74 -50
  5. package/bin/aiwg.mjs +7 -3
  6. package/dist/src/a2a/agent-card.js +11 -0
  7. package/dist/src/api/index.d.ts +1 -0
  8. package/dist/src/api/index.js +1 -0
  9. package/dist/src/artifacts/browser-export.js +1 -0
  10. package/dist/src/artifacts/citation-parser.js +8 -1
  11. package/dist/src/artifacts/cli.js +57 -6
  12. package/dist/src/artifacts/fortemi-core-query-adapter.js +2 -0
  13. package/dist/src/artifacts/fortemi-core-sync.js +26 -8
  14. package/dist/src/artifacts/fortemi-shard-contract/2.2.0/contract.json +103 -0
  15. package/dist/src/artifacts/fortemi-shard-contract/2.2.0/full-v1-embedding-config.schema.json +212 -0
  16. package/dist/src/artifacts/fortemi-shard-contract/2.2.0/full-v1-embedding-set.schema.json +240 -0
  17. package/dist/src/artifacts/fortemi-shard-contract/2.2.0/full-v1-manifest.schema.json +451 -0
  18. package/dist/src/artifacts/fortemi-shard-export.js +284 -0
  19. package/dist/src/artifacts/graph-query.js +18 -8
  20. package/dist/src/artifacts/index-adapters.js +339 -0
  21. package/dist/src/artifacts/index-builder.js +143 -14
  22. package/dist/src/artifacts/index-files.js +15 -4
  23. package/dist/src/artifacts/index-preparation.js +2 -0
  24. package/dist/src/artifacts/index-reader.js +44 -0
  25. package/dist/src/artifacts/index-refresh.js +114 -55
  26. package/dist/src/artifacts/index-status.js +20 -5
  27. package/dist/src/artifacts/memory-maintenance.js +503 -0
  28. package/dist/src/artifacts/prebuilt-relations.js +3 -1
  29. package/dist/src/artifacts/query-engine.js +263 -108
  30. package/dist/src/artifacts/relations-builder.js +2 -2
  31. package/dist/src/artifacts/source-graph.js +2 -2
  32. package/dist/src/artifacts/types.js +9 -1
  33. package/dist/src/auth/client.js +5 -1
  34. package/dist/src/auth/credential-store.js +5 -1
  35. package/dist/src/auth/sensitive-input.js +151 -0
  36. package/dist/src/channel/manager.mjs +3 -0
  37. package/dist/src/cli/handlers/agentcard.js +47 -25
  38. package/dist/src/cli/handlers/auth.js +22 -2
  39. package/dist/src/cli/handlers/decision.js +22 -3
  40. package/dist/src/cli/handlers/effect.js +4 -84
  41. package/dist/src/cli/handlers/guided-setup.js +470 -0
  42. package/dist/src/cli/handlers/help.js +2 -2
  43. package/dist/src/cli/handlers/housekeeper.js +1110 -0
  44. package/dist/src/cli/handlers/index.js +4 -2
  45. package/dist/src/cli/handlers/init.js +41 -6
  46. package/dist/src/cli/handlers/install.js +6 -0
  47. package/dist/src/cli/handlers/local-executor.js +5 -3
  48. package/dist/src/cli/handlers/marketplace.js +15 -4
  49. package/dist/src/cli/handlers/mc.js +509 -53
  50. package/dist/src/cli/handlers/models.js +32 -2
  51. package/dist/src/cli/handlers/ralph-launcher.js +76 -4
  52. package/dist/src/cli/handlers/rules.js +1 -1
  53. package/dist/src/cli/handlers/script-runner.js +73 -2
  54. package/dist/src/cli/handlers/serve.js +5 -3
  55. package/dist/src/cli/handlers/sessions.js +1 -0
  56. package/dist/src/cli/handlers/setup.js +432 -1
  57. package/dist/src/cli/handlers/skill-lint.js +1 -1
  58. package/dist/src/cli/handlers/subcommands.js +118 -8
  59. package/dist/src/cli/handlers/use.js +335 -107
  60. package/dist/src/cli/handlers/workspace.js +3 -1
  61. package/dist/src/cli/prompt-utils.js +81 -0
  62. package/dist/src/cli/router.js +16 -1
  63. package/dist/src/config/aiwg-config.js +42 -0
  64. package/dist/src/config/cli.js +7 -3
  65. package/dist/src/config/decision-config.js +14 -1
  66. package/dist/src/config/gitignore.js +4 -0
  67. package/dist/src/config/native-sandbox-warnings.js +25 -0
  68. package/dist/src/config/workspace.js +2 -1
  69. package/dist/src/contributors/validation.js +6 -5
  70. package/dist/src/decision/adapters/jev.js +20 -31
  71. package/dist/src/decision/admission.js +7 -3
  72. package/dist/src/decision/ask.js +6 -1
  73. package/dist/src/decision/benchmark.js +482 -0
  74. package/dist/src/decision/ledger.js +22 -3
  75. package/dist/src/decision/pricing.js +147 -0
  76. package/dist/src/decision/qualification/gate-evidence.js +23 -18
  77. package/dist/src/decision/qualification/load-staged.js +576 -0
  78. package/dist/src/decision/qualification/load.js +109 -33
  79. package/dist/src/decision/qualification/runner.js +12 -5
  80. package/dist/src/decision/review/service.js +6 -4
  81. package/dist/src/decision/review/validate.js +23 -2
  82. package/dist/src/decision/savings.js +8 -3
  83. package/dist/src/discovery/index.js +492 -0
  84. package/dist/src/discovery/interview.js +238 -0
  85. package/dist/src/discovery/plan-apply.js +567 -0
  86. package/dist/src/discovery/plan-store.js +171 -0
  87. package/dist/src/discovery/plan-types.js +2 -0
  88. package/dist/src/discovery/plan-validation.js +139 -0
  89. package/dist/src/discovery/types.js +2 -0
  90. package/dist/src/effects/host.js +91 -0
  91. package/dist/src/extensions/commands/definitions.js +49 -9
  92. package/dist/src/extensions/manifest.js +71 -44
  93. package/dist/src/extensions/project-local-discovery.js +1 -1
  94. package/dist/src/extensions/project-local-gitignore.js +2 -0
  95. package/dist/src/extensions/project-quickref.js +18 -17
  96. package/dist/src/extensions/validation.js +32 -29
  97. package/dist/src/housekeeper/daemon-config.js +148 -0
  98. package/dist/src/housekeeper/ipc.js +84 -0
  99. package/dist/src/housekeeper/lifecycle.js +148 -0
  100. package/dist/src/housekeeper/platform.js +35 -0
  101. package/dist/src/housekeeper/status.js +70 -0
  102. package/dist/src/housekeeper/units.js +114 -0
  103. package/dist/src/issues/cli.js +16 -5
  104. package/dist/src/issues/live.js +44 -12
  105. package/dist/src/jobs/flow.js +22 -21
  106. package/dist/src/lint/rule-activation.js +20 -5
  107. package/dist/src/lint/runner.js +1 -1
  108. package/dist/src/marketplace/exchange.js +18 -6
  109. package/dist/src/marketplace/provenance.js +3 -2
  110. package/dist/src/mcp/adapters/codex-runtime.js +46 -76
  111. package/dist/src/mcp/cli.mjs +22 -96
  112. package/dist/src/mcp/config-file.mjs +6 -2
  113. package/dist/src/mcp/config-layers.mjs +23 -7
  114. package/dist/src/mcp/grok-build-config.mjs +2 -1
  115. package/dist/src/mcp/muse-native-config.mjs +2 -1
  116. package/dist/src/mcp/profiles.js +18 -31
  117. package/dist/src/mcp/profiles.mjs +18 -32
  118. package/dist/src/mcp/registry.js +7 -27
  119. package/dist/src/mcp/registry.mjs +7 -28
  120. package/dist/src/mcp/server.mjs +34 -1
  121. package/dist/src/mcp/tools/agentic-sandbox.mjs +8 -7
  122. package/dist/src/mcp/tools/command-run.mjs +3 -2
  123. package/dist/src/mcp/tools/decision.mjs +49 -48
  124. package/dist/src/mcp/tools/discovery.mjs +46 -21
  125. package/dist/src/mcp/tools/orchestration.mjs +22 -22
  126. package/dist/src/mcp/tools/subsystems.mjs +9 -8
  127. package/dist/src/mcp/tools/worker-invoke.mjs +30 -6
  128. package/dist/src/mcp/tools/writer-profiles.mjs +1 -1
  129. package/dist/src/memory/audit.js +719 -0
  130. package/dist/src/memory/canonical-context.js +392 -13
  131. package/dist/src/memory/canonical-maintenance.js +95 -0
  132. package/dist/src/memory/cli-result.js +22 -0
  133. package/dist/src/memory/cli.js +176 -0
  134. package/dist/src/memory/context-pack.js +57 -6
  135. package/dist/src/memory/cost.js +512 -0
  136. package/dist/src/memory/firewall-classification.d.mts +7 -0
  137. package/dist/src/memory/firewall-classification.mjs +32 -0
  138. package/dist/src/memory/index.js +8 -0
  139. package/dist/src/memory/line-maintenance.js +68 -0
  140. package/dist/src/memory/maintenance-cli.js +286 -0
  141. package/dist/src/memory/maintenance-contract.js +136 -0
  142. package/dist/src/memory/maintenance-index.js +18 -0
  143. package/dist/src/memory/maintenance-owner.js +2 -0
  144. package/dist/src/memory/maintenance-record.js +138 -0
  145. package/dist/src/memory/maintenance.js +254 -0
  146. package/dist/src/memory/project-registry.js +2 -1
  147. package/dist/src/memory/provider-memory-adapter.js +154 -0
  148. package/dist/src/memory/recommend-cli.js +83 -0
  149. package/dist/src/memory/recommend.js +269 -0
  150. package/dist/src/memory/scheduled.js +81 -0
  151. package/dist/src/memory/taxonomy.js +44 -0
  152. package/dist/src/mission-protocol/codecs.js +19 -9
  153. package/dist/src/mission-protocol/worker-status.js +91 -1
  154. package/dist/src/models/config-loader.js +11 -1
  155. package/dist/src/models/model-capabilities.v1.json +74 -50
  156. package/dist/src/models/provider-policy.js +125 -62
  157. package/dist/src/models/resolver.js +45 -8
  158. package/dist/src/models/worker-installation.d.mts +8 -1
  159. package/dist/src/models/worker-installation.mjs +231 -38
  160. package/dist/src/models/wrapper-route.js +1 -1
  161. package/dist/src/output-modes/registry.js +8 -7
  162. package/dist/src/packages/adapters/git.js +99 -23
  163. package/dist/src/packages/adapters/gitea.js +8 -27
  164. package/dist/src/packages/adapters/github.js +7 -25
  165. package/dist/src/packages/registry.js +4 -4
  166. package/dist/src/plugin/metadata-validator.js +1 -1
  167. package/dist/src/process/cli-command.d.mts +5 -0
  168. package/dist/src/process/cli-command.mjs +42 -0
  169. package/dist/src/providers/deploy-version.d.mts +1 -0
  170. package/dist/src/providers/deploy-version.mjs +17 -0
  171. package/dist/src/providers/provider-definitions.js +19 -16
  172. package/dist/src/providers/skill-ownership.d.mts +11 -2
  173. package/dist/src/providers/skill-ownership.mjs +35 -17
  174. package/dist/src/rules/activation.d.mts +11 -2
  175. package/dist/src/rules/activation.mjs +82 -6
  176. package/dist/src/rules/inventory.js +1 -1
  177. package/dist/src/rules/rule-frontmatter.schema.json +2 -0
  178. package/dist/src/schema/zod-validation.d.mts +6 -0
  179. package/dist/src/schema/zod-validation.mjs +50 -0
  180. package/dist/src/serve/executor-registry.js +297 -20
  181. package/dist/src/serve/executor-token.d.mts +1 -0
  182. package/dist/src/serve/executor-token.mjs +8 -0
  183. package/dist/src/serve/shared-host-scheduler.js +1102 -31
  184. package/dist/src/serve/worker-invocation.js +124 -15
  185. package/dist/src/sessions/adapters/claude.js +19 -18
  186. package/dist/src/sessions/adapters/codex.js +10 -9
  187. package/dist/src/sessions/adapters/copilot.js +14 -13
  188. package/dist/src/sessions/adapters/cursor.js +19 -18
  189. package/dist/src/sessions/adapters/deepseek-harness.js +9 -8
  190. package/dist/src/sessions/adapters/factory.js +11 -10
  191. package/dist/src/sessions/adapters/generic.js +32 -23
  192. package/dist/src/sessions/adapters/grokbot.js +35 -35
  193. package/dist/src/sessions/adapters/hermes.js +20 -19
  194. package/dist/src/sessions/adapters/muse.js +50 -49
  195. package/dist/src/sessions/adapters/omp.js +5 -4
  196. package/dist/src/sessions/adapters/openclaw.js +30 -29
  197. package/dist/src/sessions/adapters/opencode.js +26 -26
  198. package/dist/src/sessions/adapters/openhuman.js +24 -23
  199. package/dist/src/sessions/adapters/pi.js +5 -4
  200. package/dist/src/sessions/adapters/windsurf.js +8 -8
  201. package/dist/src/sessions/analytics.js +10 -9
  202. package/dist/src/sessions/candidates.js +7 -6
  203. package/dist/src/sessions/contracts.js +63 -53
  204. package/dist/src/sessions/output-registration.js +11 -10
  205. package/dist/src/sessions/workspace-discovery.js +3 -4
  206. package/dist/src/smiths/context-pipeline/aiwg-md.js +18 -1
  207. package/dist/src/smiths/context-pipeline/claude-hook.js +46 -10
  208. package/dist/src/smiths/context-pipeline/context-file-safety.js +330 -9
  209. package/dist/src/smiths/context-pipeline/finalization.js +3 -2
  210. package/dist/src/smiths/context-pipeline/generator.js +7 -19
  211. package/dist/src/smiths/context-pipeline/legacy-inject.js +3 -10
  212. package/dist/src/smiths/context-pipeline/managed-hook.js +5 -5
  213. package/dist/src/smiths/context-pipeline/parallelism-section.js +72 -5
  214. package/dist/src/smiths/context-pipeline/workspace-context.js +110 -22
  215. package/dist/src/tracker/project-inputs.js +2 -0
  216. package/dist/src/update/checker.d.mts +4 -0
  217. package/dist/src/update/checker.mjs +14 -0
  218. package/dist/src/update/notifier.mjs +2 -4
  219. package/dist/src/writing/exemplar-selection.js +6 -6
  220. package/dist/src/writing/voice-evaluation.js +24 -23
  221. package/dist/src/writing/writer-migration.js +14 -13
  222. package/dist/src/writing/writer-profile.js +19 -18
  223. package/dist/src/writing/writing-brief.js +18 -17
  224. package/dist/src/writing/writing-receipt.js +47 -46
  225. package/package.json +5 -2
  226. package/schemas/decision/DecisionReview.schema.json +78 -1
  227. package/schemas/mission-v1.schema.json +3 -0
  228. package/tools/agents/deploy-agents.mjs +67 -39
  229. package/tools/agents/providers/antigravity.mjs +6 -1
  230. package/tools/agents/providers/base.mjs +24 -14
  231. package/tools/agents/providers/claude.mjs +6 -3
  232. package/tools/agents/providers/codex.mjs +54 -57
  233. package/tools/agents/providers/context-write.mjs +28 -0
  234. package/tools/agents/providers/copilot.mjs +13 -4
  235. package/tools/agents/providers/cursor.mjs +1 -1
  236. package/tools/agents/providers/deepseek-harness.mjs +1 -1
  237. package/tools/agents/providers/factory.mjs +7 -1
  238. package/tools/agents/providers/grok-build.mjs +18 -2
  239. package/tools/agents/providers/grokbot.mjs +1 -1
  240. package/tools/agents/providers/hermes.mjs +12 -15
  241. package/tools/agents/providers/muse-hooks.mjs +21 -14
  242. package/tools/agents/providers/muse.mjs +7 -2
  243. package/tools/agents/providers/omp.mjs +31 -5
  244. package/tools/agents/providers/opencode.mjs +14 -5
  245. package/tools/agents/providers/openhuman.mjs +1 -1
  246. package/tools/agents/providers/pi.mjs +1 -1
  247. package/tools/agents/providers/provider-prepare-coordinator.mjs +29 -9
  248. package/tools/agents/providers/rule-delivery.mjs +289 -80
  249. package/tools/agents/providers/warp.mjs +4 -1
  250. package/tools/agents/providers/windsurf.mjs +13 -3
  251. package/tools/cli/workspace-status.mjs +5 -0
  252. package/tools/commands/deploy-prompts-codex.mjs +11 -8
  253. package/tools/ralph-external/lib/codex-adapter.mjs +6 -2
  254. package/tools/ralph-external/lib/consecutive-failure-circuit-breaker.mjs +26 -0
  255. package/tools/ralph-external/lib/launcher-recovery-evidence.mjs +7 -1
  256. package/tools/ralph-external/lib/muse-adapter.mjs +12 -2
  257. package/tools/ralph-external/lib/recovery-probe.mjs +14 -4
  258. package/tools/ralph-external/lib/worker-health.mjs +1 -1
  259. package/tools/ralph-external/lib/worker-isolation.mjs +65 -9
  260. package/tools/ralph-external/process-monitor.mjs +49 -8
  261. package/tools/ralph-external/session-launcher.mjs +405 -35
  262. package/tools/skills/deploy-skills-codex.mjs +11 -7
  263. package/vscode-extension/schemas/aiwg.config.v1.json +11 -1
@@ -87,9 +87,14 @@ export async function collectGraphIndexFiles(cwd, graph, options = {}) {
87
87
  throw new Error(`Unknown graph: ${graph}`);
88
88
  }
89
89
  const artifactRoot = resolveProjectAiwgDir(cwd);
90
- const excludedDirs = [path.join(cwd, DEFAULT_PROJECT_AIWG_DIR), artifactRoot]
91
- .flatMap(root => INDEX_SCRATCH_DIRS.map(name => path.resolve(root, name)));
92
- const excludedPaths = new Set(excludedDirs);
90
+ const artifactRoots = [path.join(cwd, DEFAULT_PROJECT_AIWG_DIR), artifactRoot];
91
+ // Aggregate owner state contains history, tombstones and transaction payloads.
92
+ // It is not an ordinary artifact: only each owner's public projection is a
93
+ // retrieval surface, never the backing store or maintenance receipts.
94
+ const privateDirs = ['context/compound-memory', 'memory/transactions', 'memory/maintenance'];
95
+ const privateFiles = ['memory/line-memory.meta.json', 'memory/line-memory.config.json', 'memory/line-memory.lock'];
96
+ const excludedDirs = artifactRoots.flatMap(root => [...INDEX_SCRATCH_DIRS, ...privateDirs].map(name => path.resolve(root, name)));
97
+ const excludedPaths = new Set([...excludedDirs, ...artifactRoots.flatMap(root => privateFiles.map(name => path.resolve(root, name)))]);
93
98
  // A directory's ancestors are shared by thousands of sibling artifacts.
94
99
  // Cache only within this scan so the next build still notices new symlinks.
95
100
  const safePaths = new Map();
@@ -109,7 +114,7 @@ export async function collectGraphIndexFiles(cwd, graph, options = {}) {
109
114
  }
110
115
  const inputPath = file;
111
116
  file = path.resolve(file);
112
- if (excludedDirs.some(dir => process.platform === 'win32'
117
+ if (excludedPaths.has(file) || excludedDirs.some(dir => process.platform === 'win32'
113
118
  ? pathContains(dir, file) : file === dir || file.startsWith(dir + path.sep)))
114
119
  return false;
115
120
  if (knownStat && !knownStat.isSymbolicLink() && safePaths.get(path.dirname(file)) === true) {
@@ -190,6 +195,10 @@ export async function collectGraphIndexFiles(cwd, graph, options = {}) {
190
195
  }
191
196
  }
192
197
  let sources = [...files];
198
+ const candidates = [...sources].sort();
199
+ if (options.selection?.files && JSON.stringify(candidates) === JSON.stringify(options.selection.candidates)) {
200
+ return [...options.selection.files];
201
+ }
193
202
  if (!options.includeIgnored) {
194
203
  // Many projects ignore the whole artifact root (`.aiwg/`). Honoring that
195
204
  // would empty the index, so ignore rules apply only beneath a scan root
@@ -225,6 +234,8 @@ export async function collectGraphIndexFiles(cwd, graph, options = {}) {
225
234
  if (!selected.has(file))
226
235
  options.fileStats.delete(file);
227
236
  }
237
+ if (options.selection)
238
+ Object.assign(options.selection, { candidates, files: [...sources] });
228
239
  return sources;
229
240
  }
230
241
  //# sourceMappingURL=index-files.js.map
@@ -2,6 +2,7 @@
2
2
  import os from 'node:os';
3
3
  import { fork } from 'node:child_process';
4
4
  import { signalChild } from '../process/signal-child.mjs';
5
+ import { helperEnvironment, DEPLOYMENT_HELPER_ENV_ALLOWLIST } from '../process/cli-command.mjs';
5
6
  const MIB = 1024 ** 2;
6
7
  const WORKER_HEAP_MB = 256;
7
8
  // Leave room for V8's young generation, executable code, IPC and native allocations.
@@ -68,6 +69,7 @@ export async function runIndexPreparation(jobs, serial, moduleUrl, requested, on
68
69
  // Node during allocation before its isolate-limit error is delivered.
69
70
  // A child heap fatal/SIGABRT is instead an ordinary, recoverable exit.
70
71
  const worker = fork(new URL('./index-worker.mjs', import.meta.url), [moduleUrl], {
72
+ env: helperEnvironment(process.env, DEPLOYMENT_HELPER_ENV_ALLOWLIST),
71
73
  execArgv: [`--max-old-space-size=${WORKER_HEAP_MB}`, '--max-semi-space-size=16'],
72
74
  stdio: ['ignore', 'ignore', 'pipe', 'ipc'], serialization: 'advanced',
73
75
  });
@@ -11,6 +11,7 @@
11
11
  import fs from 'fs';
12
12
  import path from 'path';
13
13
  import { getGraphIndexDir, getProjectIndexRoot } from './types.js';
14
+ import { loadManifest, writeManifest } from './checksum-manifest.js';
14
15
  /**
15
16
  * Load a JSON file from the index directory
16
17
  *
@@ -109,4 +110,47 @@ export function loadGraphIndexFile(cwd, filename, graph) {
109
110
  return null;
110
111
  }
111
112
  }
113
+ /** Publish a prepared snapshot with the existing atomic file writes under the owner lock. */
114
+ export function publishIndexFiles(staging, directory) {
115
+ const files = fs.readdirSync(staging).filter(file => fs.statSync(path.join(staging, file)).isFile())
116
+ .sort((a, b) => Number(a === 'metadata.json') - Number(b === 'metadata.json'));
117
+ const backups = path.join(staging, '.previous');
118
+ fs.mkdirSync(backups);
119
+ for (const file of files) {
120
+ const target = path.join(directory, file);
121
+ if (fs.existsSync(target))
122
+ fs.copyFileSync(target, path.join(backups, file));
123
+ }
124
+ const published = [];
125
+ try {
126
+ for (const file of files) {
127
+ fs.renameSync(path.join(staging, file), path.join(directory, file));
128
+ published.push(file);
129
+ }
130
+ // Receipts bind to final inode signatures, after the atomic renames.
131
+ if (files.includes('checksum-manifest.json')) {
132
+ const manifest = loadManifest(directory);
133
+ const signature = (file) => {
134
+ const stat = fs.statSync(path.join(directory, file), { bigint: true });
135
+ return [stat.dev, stat.ino, stat.size, stat.mtimeNs, stat.ctimeNs].join(':');
136
+ };
137
+ if (manifest.receipt) {
138
+ manifest.receipt.metadataSignature = signature('metadata.json');
139
+ if (manifest.receipt.relations)
140
+ manifest.receipt.relations.signature = signature('relations.json');
141
+ writeManifest(directory, manifest);
142
+ }
143
+ }
144
+ }
145
+ catch (error) {
146
+ for (const file of published.reverse()) {
147
+ const backup = path.join(backups, file);
148
+ if (fs.existsSync(backup))
149
+ fs.renameSync(backup, path.join(directory, file));
150
+ else
151
+ fs.rmSync(path.join(directory, file), { force: true });
152
+ }
153
+ throw error;
154
+ }
155
+ }
112
156
  //# sourceMappingURL=index-reader.js.map
@@ -1,4 +1,4 @@
1
- import { createHash, randomUUID } from 'node:crypto';
1
+ import { randomUUID } from 'node:crypto';
2
2
  import * as fs from 'node:fs';
3
3
  import { homedir } from 'node:os';
4
4
  import path from 'node:path';
@@ -7,13 +7,14 @@ import { findPackageRoot } from '../cli/find-package-root.js';
7
7
  import { projectAiwgPath, projectControlPath } from '../config/project-artifacts.js';
8
8
  import { getUserStateDir } from '../config/user-state.js';
9
9
  import { resolveIndexWorkers } from './index-preparation.js';
10
- import { collectGraphIndexFiles } from './index-files.js';
10
+ import { resolveFrameworkIndexRoot } from './framework-index-paths.js';
11
11
  import { collectIndexStatus } from './index-status.js';
12
+ import { graphIndexDigest, indexAdapterRegistry, selectIndexAdapters, indexFreshnessSnapshot, indexAdapterDigest, recordIndexVerification } from './index-adapters.js';
12
13
  import { getFortemiCoreSyncStatus, syncFortemiCoreIndex } from './fortemi-core-sync.js';
13
14
  import { acquireDirectoryLock, DirectoryLockContentionError } from './prebuilt-build-lock.js';
14
15
  import { resolveImportExclusions } from '../sessions/import-exclusions.js';
15
16
  import { resolveImportLimits } from '../sessions/import-limits.js';
16
- import { BUILTIN_GRAPH_CONFIGS, GRAPH_CONFIGS, INDEX_EXTRACTOR_VERSION, INDEX_VERSION, getGraphIndexDir, graphBuildOrder, loadGlobalGraphConfigs, loadUserGraphConfigs, resolveGraphBackendType, resolveGraphScanDir, } from './types.js';
17
+ import { GRAPH_CONFIGS, getGraphIndexDir, graphBuildOrder, loadWorkspaceGraphs, resolveGraphScanDir, } from './types.js';
17
18
  function readJson(file, tolerateMalformed = true) {
18
19
  try {
19
20
  return JSON.parse(fs.readFileSync(file, 'utf8'));
@@ -29,14 +30,6 @@ function writeJson(file, value) {
29
30
  fs.writeFileSync(temporary, `${JSON.stringify(value, null, 2)}\n`, { mode: 0o600 });
30
31
  fs.renameSync(temporary, file);
31
32
  }
32
- /** The registry is process-global; reload it per workspace to avoid cross-project leakage. */
33
- function loadWorkspaceGraphs(workspace) {
34
- for (const name of Object.keys(GRAPH_CONFIGS))
35
- delete GRAPH_CONFIGS[name];
36
- Object.assign(GRAPH_CONFIGS, structuredClone(BUILTIN_GRAPH_CONFIGS));
37
- loadUserGraphConfigs(workspace);
38
- loadGlobalGraphConfigs();
39
- }
40
33
  function workspacePaths(options) {
41
34
  const cwd = options.cwd ?? process.cwd();
42
35
  const found = new Map();
@@ -124,33 +117,38 @@ function planWorkspaceRefresh(options, workspaces) {
124
117
  loadWorkspaceGraphs(workspace);
125
118
  const graphs = collectIndexStatus(workspace).graphs
126
119
  .sort((a, b) => graphBuildOrder(a.name, GRAPH_CONFIGS[a.name]) - graphBuildOrder(b.name, GRAPH_CONFIGS[b.name]) || a.name.localeCompare(b.name));
127
- if (!options.only || options.only === 'graphs') {
120
+ const adapters = indexAdapterRegistry(workspace);
121
+ const selected = selectIndexAdapters(adapters, options.only);
122
+ const includes = (name) => selected.some(adapter => adapter.name === name);
123
+ if (!options.only || options.only === 'all' || options.only === 'graphs' || selected.some(adapter => adapter.group === 'graphs')) {
128
124
  for (const graph of graphs) {
129
- if (graph.built || graph.defaultBuild || graph.origin === 'registered') {
125
+ if (selected.some(adapter => adapter.group === 'graphs' && adapter.graph === graph.name)
126
+ && (graph.built || graph.defaultBuild || graph.origin === 'registered'
127
+ || (options.only && options.only !== 'all' && options.only !== 'graphs'))) {
130
128
  jobs.push({ kind: 'graph', workspace, graph: graph.name, status: 'planned', wallMs: 0 });
131
129
  }
132
130
  }
133
131
  }
134
- if (!options.only || options.only === 'caches') {
132
+ if (!options.only || options.only === 'all' || options.only === 'caches' || options.only?.startsWith('caches:')) {
135
133
  for (const graph of graphs) {
136
134
  // Refresh only local, opted-in caches; never create a new cache from a packaged prebuilt.
137
- if (fs.existsSync(getFortemiCoreSyncStatus(workspace, graph.name).location)) {
135
+ if (includes(`caches:${graph.name}`) && fs.existsSync(getFortemiCoreSyncStatus(workspace, graph.name).location)) {
138
136
  jobs.push({ kind: 'cache', workspace, graph: graph.name, status: 'planned', wallMs: 0 });
139
137
  }
140
138
  }
141
139
  }
142
- if ((!options.only || options.only === 'sessions') && fs.existsSync(path.join(workspace, '.aiwg/sessions/catalog.sqlite'))) {
140
+ if (includes('sessions') && fs.existsSync(path.join(workspace, '.aiwg/sessions/catalog.sqlite'))) {
143
141
  jobs.push({ kind: 'sessions', workspace, status: 'planned', wallMs: 0, noManifest: 0 });
144
142
  }
145
143
  for (const kind of ['runtime', 'issues']) {
146
- if (options.only && options.only !== kind)
144
+ if (!includes(kind))
147
145
  continue;
148
146
  if (kind === 'issues' && !fs.existsSync(projectAiwgPath(workspace, 'issues')))
149
147
  continue;
150
148
  jobs.push({ kind, workspace, status: 'planned', wallMs: 0 });
151
149
  }
152
150
  }
153
- if (!options.only || options.only === 'models') {
151
+ if (!options.only || options.only === 'all' || options.only === 'models') {
154
152
  jobs.push({ kind: 'models', scope: 'host', status: 'planned', wallMs: 0 });
155
153
  }
156
154
  return jobs;
@@ -177,23 +175,6 @@ export async function refreshResourceGate(workspace, live) {
177
175
  const result = preflight.evaluateDispatchGate(live ?? preflight.readLiveHostResources({ paths: [workspace] }), thresholds);
178
176
  return { ok: result.ok, reason: result.failures.map((failure) => failure.reason).join('; ') || undefined };
179
177
  }
180
- async function graphFingerprint(workspace, graph) {
181
- const files = await collectGraphIndexFiles(workspace, graph);
182
- // Source import resolution also depends on the compiler/package configuration.
183
- if (graph === 'source') {
184
- for (const file of ['tsconfig.json', 'package.json']) {
185
- if (fs.existsSync(path.join(workspace, file)))
186
- files.push(path.join(workspace, file));
187
- }
188
- }
189
- const stats = [...new Set(files)].sort().map(file => {
190
- const stat = fs.statSync(file);
191
- return [file, stat.mtimeMs, stat.ctimeMs, stat.size];
192
- });
193
- return createHash('sha256').update(JSON.stringify([
194
- INDEX_VERSION, INDEX_EXTRACTOR_VERSION, GRAPH_CONFIGS[graph], resolveGraphBackendType(graph), stats,
195
- ])).digest('hex');
196
- }
197
178
  async function refreshWorkspaceRuntime(workspace, full, runtime) {
198
179
  const directory = projectAiwgPath(workspace, 'smiths', 'toolsmith');
199
180
  const output = path.join(directory, 'runtime.json');
@@ -237,34 +218,54 @@ async function refreshWorkspaceRuntime(workspace, full, runtime) {
237
218
  async function executeJob(job, options, aliases, runtime) {
238
219
  const workspace = job.workspace ?? process.env.HOME ?? homedir();
239
220
  if (job.kind === 'graph') {
221
+ const graphStart = Date.now();
240
222
  const graph = job.graph;
241
223
  const config = GRAPH_CONFIGS[graph];
242
- if (!config.scanDirs.some(dir => fs.existsSync(resolveGraphScanDir(workspace, dir)))) {
224
+ const sourceWorkspace = graph === 'framework' ? resolveFrameworkIndexRoot(workspace) : workspace;
225
+ if (!config.scanDirs.some(dir => fs.existsSync(resolveGraphScanDir(sourceWorkspace, dir)))) {
243
226
  job.status = 'skipped';
244
227
  job.reason = 'scan directories missing';
245
228
  return;
246
229
  }
247
230
  const directory = getGraphIndexDir(workspace, graph);
248
231
  const markerPath = path.join(directory, 'refresh-state.json');
232
+ const fingerprint = await graphIndexDigest(sourceWorkspace, graph);
249
233
  const marker = readJson(markerPath);
250
234
  const metadata = readJson(path.join(directory, 'metadata.json'));
251
- const fingerprint = await graphFingerprint(workspace, graph);
252
- const outputs = ['metadata.json', 'dependencies.json', 'tags.json', 'stats.json'];
253
- if (resolveGraphBackendType(graph) === 'sqlite')
254
- outputs.push('graph.db');
255
- if (!options.full && marker?.fingerprint === fingerprint && metadata?.builtAt === marker.builtAt
256
- && outputs.every(file => fs.existsSync(path.join(directory, file)))) {
257
- job.status = 'skipped';
258
- job.reason = 'unchanged';
259
- return;
235
+ const adapter = indexAdapterRegistry(workspace).find(item => item.name === graph);
236
+ if (!options.full && marker?.fingerprint === fingerprint && marker.builtAt === metadata?.builtAt
237
+ && indexFreshnessSnapshot(adapter, workspace).pendingWork === 0) {
238
+ const release = await acquireDirectoryLock(`${directory}.build.lock`, {
239
+ timeoutMs: options.lockWaitMs ?? 0, reclaimDeadOwner: true,
240
+ });
241
+ try {
242
+ const current = await graphIndexDigest(sourceWorkspace, graph);
243
+ if (current === fingerprint && indexFreshnessSnapshot(adapter, workspace).pendingWork === 0) {
244
+ recordIndexVerification(adapter, workspace, { before: fingerprint, after: current, wallMs: Date.now() - graphStart });
245
+ job.status = 'skipped';
246
+ job.reason = 'unchanged';
247
+ return;
248
+ }
249
+ }
250
+ finally {
251
+ await release();
252
+ }
260
253
  }
254
+ const beforeOutput = outputStamp(path.join(directory, 'metadata.json'));
261
255
  const { buildIndex } = await import('./index-builder.js');
262
- await buildIndex(workspace, { graph, force: Boolean(options.full), explicit: false, quiet: true, workers: options.workers,
256
+ await buildIndex(sourceWorkspace, { graph, force: Boolean(options.full), explicit: false, quiet: true, workers: options.workers, lockWaitMs: options.lockWaitMs ?? 0,
263
257
  onDiagnostic: message => (job.diagnostics ??= []).push(message) });
264
258
  const built = readJson(path.join(directory, 'metadata.json'));
265
259
  if (!built)
266
260
  throw new Error(`graph did not produce metadata: ${graph}`);
267
- writeJson(markerPath, { fingerprint, builtAt: built.builtAt });
261
+ const verification = readJson(path.join(directory, 'freshness.json'));
262
+ if (!options.full && beforeOutput && beforeOutput === outputStamp(path.join(directory, 'metadata.json'))) {
263
+ job.status = 'skipped';
264
+ job.reason = 'unchanged';
265
+ }
266
+ if (!verification?.pendingWork && verification?.lastVerifiedDigest === fingerprint) {
267
+ writeJson(markerPath, { fingerprint, builtAt: built.builtAt });
268
+ }
268
269
  }
269
270
  else if (job.kind === 'cache') {
270
271
  const status = getFortemiCoreSyncStatus(workspace, job.graph);
@@ -333,7 +334,8 @@ async function executeJob(job, options, aliases, runtime) {
333
334
  }
334
335
  }
335
336
  }
336
- job.status = 'ok';
337
+ if (job.status !== 'skipped')
338
+ job.status = 'ok';
337
339
  }
338
340
  function outputStamp(file) {
339
341
  try {
@@ -385,6 +387,17 @@ export async function refreshIndexes(options = {}, dependencies = {}) {
385
387
  .reduce((total, receipt) => total + (receipt.totals.excluded ?? 0), 0), 0),
386
388
  wallMs: Date.now() - start,
387
389
  };
390
+ report.indices = [];
391
+ for (const workspace of workspaces.keys()) {
392
+ loadWorkspaceGraphs(workspace);
393
+ for (const adapter of selectIndexAdapters(indexAdapterRegistry(workspace), options.only)) {
394
+ report.indices.push({ ...indexFreshnessSnapshot(adapter, workspace), workspace });
395
+ }
396
+ }
397
+ if (!jobs.length && options.only && report.indices.length && report.indices.every(index => index.status === 'unsupported')) {
398
+ report.status = 'deferred';
399
+ report.reason = 'unsupported';
400
+ }
388
401
  return report;
389
402
  };
390
403
  if (options.dryRun)
@@ -435,6 +448,8 @@ export async function refreshIndexes(options = {}, dependencies = {}) {
435
448
  continue;
436
449
  }
437
450
  const jobStart = Date.now();
451
+ let adapter;
452
+ let beforeDigest;
438
453
  try {
439
454
  const gateWorkspace = job.workspace ?? workspaces.keys().next().value ?? options.cwd ?? process.cwd();
440
455
  const gate = await (dependencies.gate ?? refreshResourceGate)(gateWorkspace);
@@ -447,6 +462,15 @@ export async function refreshIndexes(options = {}, dependencies = {}) {
447
462
  }
448
463
  if (job.workspace)
449
464
  loadWorkspaceGraphs(job.workspace);
465
+ const workspace = job.workspace ?? options.cwd ?? process.cwd();
466
+ adapter = indexAdapterRegistry(workspace).find(item => item.name === (job.kind === 'graph' ? job.graph
467
+ : job.kind === 'cache' ? `caches:${job.graph}` : job.kind));
468
+ if (adapter?.supported && job.kind !== 'graph') {
469
+ try {
470
+ beforeDigest = await indexAdapterDigest(adapter, workspace);
471
+ }
472
+ catch { /* Owner reports unavailable dependencies. */ }
473
+ }
450
474
  if (job.kind === 'cache' && jobs.some(other => other.kind === 'graph'
451
475
  && other.workspace === job.workspace && other.graph === job.graph && other.status === 'error')) {
452
476
  job.status = 'skipped';
@@ -471,13 +495,52 @@ export async function refreshIndexes(options = {}, dependencies = {}) {
471
495
  await executeJob(job, options, aliases, discoverRuntime);
472
496
  }
473
497
  catch (error) {
474
- job.status = 'error';
475
- job.error = error instanceof Error ? error.message : String(error);
498
+ if (error instanceof DirectoryLockContentionError) {
499
+ job.status = 'deferred';
500
+ job.reason = 'lock-held';
501
+ report.status = 'deferred';
502
+ }
503
+ else {
504
+ job.status = 'error';
505
+ job.error = error instanceof Error ? error.message : String(error);
506
+ }
476
507
  }
477
508
  finally {
478
509
  job.wallMs = Date.now() - jobStart;
510
+ if (adapter) {
511
+ const workspace = job.workspace ?? options.cwd ?? process.cwd();
512
+ if (adapter.supported && job.kind !== 'graph' && job.status !== 'deferred') {
513
+ try {
514
+ let after;
515
+ let probeError;
516
+ try {
517
+ after = await indexAdapterDigest(adapter, workspace);
518
+ }
519
+ catch (error) {
520
+ probeError = String(error);
521
+ }
522
+ if (job.kind === 'runtime' && job.status === 'ok')
523
+ beforeDigest = after;
524
+ const partial = job.reason && job.reason !== 'unchanged';
525
+ job.freshness = recordIndexVerification(adapter, workspace, { before: beforeDigest, after,
526
+ error: job.error ?? probeError, pendingWork: partial ? 1 : 0, wallMs: job.wallMs });
527
+ }
528
+ catch (error) {
529
+ job.freshness = { ...indexFreshnessSnapshot(adapter, workspace), status: 'degraded',
530
+ pendingWork: 1, lastError: String(error) };
531
+ }
532
+ }
533
+ else
534
+ job.freshness = indexFreshnessSnapshot(adapter, workspace);
535
+ if (job.freshness.pendingWork && !job.error && job.status !== 'deferred' && job.kind === 'graph'
536
+ && job.reason !== 'scan directories missing') {
537
+ job.status = 'deferred';
538
+ job.reason = 'inputs changed during build';
539
+ report.status = 'deferred';
540
+ }
541
+ }
479
542
  }
480
- if (job.status !== 'error' && job.reason !== 'graph failed'
543
+ if (job.status !== 'error' && job.status !== 'deferred' && job.reason !== 'graph failed'
481
544
  && job.reason !== 'no authorized discovery manifest')
482
545
  checkpoint.completed.push(key);
483
546
  writeJson(checkpointPath, checkpoint);
@@ -534,10 +597,6 @@ export async function runIndexRefresh(args, cwd = process.cwd(), signal) {
534
597
  else if (flag === '--exclude')
535
598
  (options.exclude ??= []).push(value);
536
599
  else if (flag === '--only') {
537
- if (value !== 'graphs' && value !== 'sessions' && value !== 'caches'
538
- && value !== 'runtime' && value !== 'models' && value !== 'issues') {
539
- throw new Error('--only must be graphs, sessions, caches, runtime, models, or issues');
540
- }
541
600
  options.only = value;
542
601
  }
543
602
  else {
@@ -18,8 +18,9 @@
18
18
  */
19
19
  import * as fs from 'node:fs';
20
20
  import * as path from 'node:path';
21
- import { GRAPH_CONFIGS, BUILTIN_GRAPH_CONFIGS, getGraphIndexDir, getProjectIndexRoot, loadGlobalGraphConfigs, loadUserGraphConfigs, resolveGraphBackendType, } from './types.js';
21
+ import { GRAPH_CONFIGS, BUILTIN_GRAPH_CONFIGS, getGraphIndexDir, getProjectIndexRoot, loadWorkspaceGraphs, resolveGraphBackendType, } from './types.js';
22
22
  import { getFortemiCoreSyncStatus, } from './fortemi-core-sync.js';
23
+ import { indexAdapterRegistry, indexFreshnessSnapshot, selectIndexAdapters } from './index-adapters.js';
23
24
  function readBuiltMeta(indexDir) {
24
25
  try {
25
26
  const metaPath = path.join(indexDir, 'metadata.json');
@@ -42,12 +43,11 @@ function readBuiltMeta(indexDir) {
42
43
  *
43
44
  * @param nowMs - injectable clock for deterministic tests (defaults to Date.now()).
44
45
  */
45
- export function collectIndexStatus(cwd, nowMs) {
46
+ export function collectIndexStatus(cwd, nowMs, only) {
46
47
  // Populate the registry from module + operator config, collecting warnings
47
48
  // instead of letting malformed defs vanish (#1624).
48
49
  const warnings = [];
49
- loadUserGraphConfigs(cwd, warnings);
50
- loadGlobalGraphConfigs(warnings);
50
+ loadWorkspaceGraphs(cwd, warnings);
51
51
  const now = nowMs ?? Date.now();
52
52
  const graphs = [];
53
53
  for (const [name, config] of Object.entries(GRAPH_CONFIGS)) {
@@ -99,6 +99,10 @@ export function collectIndexStatus(cwd, nowMs) {
99
99
  const built = graphs.filter((g) => g.built).length;
100
100
  return {
101
101
  graphs,
102
+ indices: selectIndexAdapters(indexAdapterRegistry(cwd), only).map(adapter => ({
103
+ ...indexFreshnessSnapshot(adapter, cwd, now), identity: adapter.identity, backend: adapter.backend,
104
+ operation: adapter.operation, roots: adapter.roots(cwd), exclusions: adapter.exclusions, outputPaths: adapter.outputPaths,
105
+ })),
102
106
  orphanIndexDirs,
103
107
  warnings,
104
108
  fortemiCore: getFortemiCoreSyncStatus(cwd),
@@ -111,9 +115,17 @@ export function collectIndexStatus(cwd, nowMs) {
111
115
  },
112
116
  };
113
117
  }
118
+ /** Probe inputs through their owners; the legacy synchronous reader stays available. */
119
+ export async function probeIndexStatus(cwd, only, nowMs = Date.now()) {
120
+ const report = collectIndexStatus(cwd, nowMs, only);
121
+ const adapters = new Map(indexAdapterRegistry(cwd).map(adapter => [adapter.name, adapter]));
122
+ report.indices = await Promise.all(report.indices.map(async (index) => ({ ...index,
123
+ ...await adapters.get(index.name).probe(cwd, nowMs) })));
124
+ return report;
125
+ }
114
126
  /** Render `aiwg index status` (human table or JSON). */
115
127
  export async function showIndexStatus(cwd, opts = {}) {
116
- const report = collectIndexStatus(cwd);
128
+ const report = await probeIndexStatus(cwd, opts.only);
117
129
  if (opts.json) {
118
130
  console.log(JSON.stringify(report, null, 2));
119
131
  return;
@@ -182,6 +194,9 @@ export async function showIndexStatus(cwd, opts = {}) {
182
194
  console.log(`⚠ ${missing} registered durable index(es) are not built. Run \`aiwg index build --all\`.`);
183
195
  }
184
196
  console.log('');
197
+ for (const index of report.indices) {
198
+ console.log(` ${index.name}: ${index.status} (target ${index.policy.targetStaleAfterMs}ms, pending ${index.pendingWork})${index.lastError ? `: ${index.lastError}` : ''}`);
199
+ }
185
200
  console.log(`${report.summary.total} graph(s): ${report.summary.built} built, ${missing} missing, ` +
186
201
  `${report.summary.orphans} orphan(s), ${report.summary.warnings} warning(s).`);
187
202
  }