@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
@@ -0,0 +1,719 @@
1
+ import fs from 'node:fs/promises';
2
+ import { constants } from 'node:fs';
3
+ import path from 'node:path';
4
+ import { homedir } from 'node:os';
5
+ import { performance } from 'node:perf_hooks';
6
+ import { z } from 'zod';
7
+ import { dateTime, number } from '../schema/zod-validation.mjs';
8
+ import { resolveProjectAiwgDir } from '../config/project-artifacts.js';
9
+ import { storageConfigPath, validateStorageConfig, resolveSubsystemRootRuntime } from '../storage/config.js';
10
+ import { projectMemoryHome, projectMemoryManifestPath } from './project-registry.js';
11
+ import { listProviderMemoryAdapters } from './provider-memory-adapter.js';
12
+ import { getProviderConfigPath } from '../mcp/registry.js';
13
+ import { getProviderDefinition } from '../providers/provider-definitions.js';
14
+ import { MemoryObservationSchema } from './taxonomy.js';
15
+ import { MemoryRecordSchema, loadMemoryRecord, memoryRecordId, parseMemoryMarkdown, indexMemoryAliases, detectMemoryIncludeCycles } from './maintenance-record.js';
16
+ import { sha256 } from './context-pack.js';
17
+ import { classifyContextMemory } from './firewall-classification.mjs';
18
+ const ExposureSchema = z.strictObject({
19
+ path: z.string(), provider: z.string().nullable(),
20
+ kind: z.enum(['eligible', 'include', 'reference', 'store', 'tool-description']),
21
+ chain: z.array(z.string()), detail: z.string(),
22
+ });
23
+ const ItemSchema = z.strictObject({
24
+ id: z.string(), sourcePath: z.string(), digest: z.string().nullable(), sourceDigest: z.string().nullable().optional(),
25
+ record: MemoryRecordSchema.nullable(), category: z.string(), bytes: number().nullable(),
26
+ observation: MemoryObservationSchema, exposures: z.array(ExposureSchema),
27
+ trust: z.string(), reviewStatus: z.string(), managed: z.boolean(),
28
+ });
29
+ export const MemoryAuditSchema = z.strictObject({
30
+ schemaVersion: z.literal('aiwg.memory-audit.v1'),
31
+ at: dateTime(), workspace: z.string(), scope: z.enum(['workspace', 'global', 'project', 'provider']),
32
+ providers: z.array(z.strictObject({
33
+ id: z.string(), support: z.string(), limits: z.array(z.string()), diagnostic: z.string().nullable(),
34
+ precedence: z.array(z.string()),
35
+ })),
36
+ items: z.array(ItemSchema),
37
+ findings: z.array(z.strictObject({ code: z.string(), paths: z.array(z.string()), detail: z.string() })),
38
+ coverage: z.strictObject({
39
+ mode: z.literal('static'), complete: z.boolean(), roots: z.array(z.string()),
40
+ limits: z.strictObject({ maxDepth: number(), maxFiles: number(), maxEntries: number(), maxMs: number(), maxFileBytes: number(), maxExposures: number() }),
41
+ limitsHit: z.array(z.string()), files: number(), entries: number(),
42
+ exclusions: z.array(z.string()),
43
+ }),
44
+ });
45
+ export const DEFAULT_AUDIT_LIMITS = Object.freeze({
46
+ maxDepth: 12, maxFiles: 5000, maxEntries: 20000, maxMs: 5000,
47
+ maxFileBytes: 2 * 1024 * 1024, maxExposures: 20000,
48
+ });
49
+ const TEXT = new Set(['.md', '.mdc', '.txt', '.json', '.jsonl', '.yaml', '.yml', '.toml', '.rst']);
50
+ const SKIP = new Set(['.git', 'node_modules', 'dist', 'coverage', '.venv', 'vendor']);
51
+ // Do not open credential files, even when linked from instruction text.
52
+ function sensitive(file) {
53
+ return /(?:^|[/\\])(?:\.env(?:\..*)?|auth(?:entication)?(?:\.[^/\\]+)?|credentials?(?:\.[^/\\]+)?|tokens?(?:\.[^/\\]+)?|secrets?(?:\.[^/\\]+)?|id_rsa|id_ed25519)$/i.test(file);
54
+ }
55
+ function within(root, file) {
56
+ const rel = path.relative(root, file);
57
+ return rel === '' || (!rel.startsWith(`..${path.sep}`) && rel !== '..' && !path.isAbsolute(rel));
58
+ }
59
+ /** Static, read-only inventory. Never constructs storage adapters or owner writers. */
60
+ export async function auditMemory(options = {}) {
61
+ const workspace = path.resolve(options.workspace ?? process.cwd());
62
+ const scope = options.scope ?? 'workspace';
63
+ const limits = { ...DEFAULT_AUDIT_LIMITS, ...options.limits };
64
+ for (const [key, value] of Object.entries(limits)) {
65
+ if (!Number.isSafeInteger(value) || value < 1)
66
+ throw new Error(`${key} must be a positive integer`);
67
+ }
68
+ const adapters = listProviderMemoryAdapters().filter(a => !options.provider || a.providerId === options.provider);
69
+ if (!adapters.length)
70
+ throw new Error(`Unknown memory provider '${options.provider}'`);
71
+ if (scope === 'provider' && !options.provider)
72
+ throw new Error('--scope provider requires --provider <id>');
73
+ const at = options.at ?? new Date().toISOString();
74
+ const clock = options.clock ?? (() => performance.now());
75
+ const started = clock();
76
+ const items = new Map();
77
+ const findings = [];
78
+ const findingKeys = new Set();
79
+ const limitsHit = new Set();
80
+ const roots = new Set();
81
+ const allowedRoots = new Set(scope === 'global' ? [] : [workspace]);
82
+ const includeGraph = Object.create(null);
83
+ const cache = new Map();
84
+ const physicalSources = new Map();
85
+ let files = 0;
86
+ let entries = 0;
87
+ let exposures = 0;
88
+ let incomplete = false;
89
+ const label = (file) => within(workspace, file) ? path.relative(workspace, file).split(path.sep).join('/') || '.' : file;
90
+ const evidence = (source, provider, status) => ({
91
+ status, evidence: { source, at, provider, session: null, version: null },
92
+ });
93
+ const finding = (code, paths, detail) => {
94
+ const value = { code, paths, detail };
95
+ const key = JSON.stringify(value);
96
+ if (!findingKeys.has(key)) {
97
+ findingKeys.add(key);
98
+ findings.push(value);
99
+ }
100
+ };
101
+ function hit(bound, file) {
102
+ limitsHit.add(bound);
103
+ incomplete = true;
104
+ finding('scan-truncated', [label(file)], `${bound} reached; coverage is incomplete`);
105
+ }
106
+ function bounded(file, depth) {
107
+ if (clock() - started >= limits.maxMs) {
108
+ hit('maxMs', file);
109
+ return true;
110
+ }
111
+ if (depth > limits.maxDepth) {
112
+ hit('maxDepth', file);
113
+ return true;
114
+ }
115
+ if (entries >= limits.maxEntries) {
116
+ hit('maxEntries', file);
117
+ return true;
118
+ }
119
+ if (exposures >= limits.maxExposures) {
120
+ hit('maxExposures', file);
121
+ return true;
122
+ }
123
+ return false;
124
+ }
125
+ function expose(item, exposure) {
126
+ if (exposures >= limits.maxExposures) {
127
+ hit('maxExposures', exposure.path);
128
+ return;
129
+ }
130
+ if (!item.exposures.some(e => JSON.stringify(e) === JSON.stringify(exposure))) {
131
+ item.exposures.push(exposure);
132
+ exposures++;
133
+ }
134
+ }
135
+ function unavailable(file, exposure, status, detail) {
136
+ const sourcePath = label(file);
137
+ const id = `unavailable:${status}:${sourcePath}`;
138
+ let item = items.get(id);
139
+ if (!item) {
140
+ item = { id, sourcePath, digest: null, record: null, category: 'coverage', bytes: null,
141
+ observation: evidence(detail, exposure.provider, status), exposures: [], trust: 'unknown', reviewStatus: 'unknown', managed: false };
142
+ items.set(id, item);
143
+ }
144
+ expose(item, exposure);
145
+ if (status === 'inaccessible' || status === 'opaque')
146
+ incomplete = true;
147
+ }
148
+ function errorStatus(error) {
149
+ return ['ENOENT', 'ENOTDIR'].includes(error.code ?? '') ? 'absent' : 'inaccessible';
150
+ }
151
+ const rootExposure = (file, provider, kind = 'eligible') => ({
152
+ path: label(file), provider, kind, chain: [label(file)], detail: 'Static discovery; live activation and enabled state unverified',
153
+ });
154
+ async function readBounded(file) {
155
+ if (files >= limits.maxFiles) {
156
+ hit('maxFiles', file);
157
+ return null;
158
+ }
159
+ if (clock() - started >= limits.maxMs) {
160
+ hit('maxMs', file);
161
+ return null;
162
+ }
163
+ const handle = await fs.open(file, constants.O_RDONLY | constants.O_NOFOLLOW);
164
+ try {
165
+ const stat = await handle.stat();
166
+ if (!stat.isFile() || (stat.mode & 0o444) === 0)
167
+ throw Object.assign(new Error('Unreadable regular file'), { code: 'EACCES' });
168
+ if (stat.size > limits.maxFileBytes) {
169
+ hit('maxFileBytes', file);
170
+ return null;
171
+ }
172
+ files++;
173
+ // One extra byte detects a growing file without allocating unbounded input.
174
+ const buffer = Buffer.alloc(Math.min(stat.size + 1, limits.maxFileBytes + 1));
175
+ let bytes = 0;
176
+ while (bytes < buffer.length) {
177
+ const result = await handle.read(buffer, bytes, buffer.length - bytes, null);
178
+ if (!result.bytesRead)
179
+ break;
180
+ bytes += result.bytesRead;
181
+ }
182
+ if (bytes > stat.size || bytes > limits.maxFileBytes) {
183
+ hit('maxFileBytes', file);
184
+ return null;
185
+ }
186
+ return buffer.subarray(0, bytes).toString('utf8');
187
+ }
188
+ finally {
189
+ await handle.close();
190
+ }
191
+ }
192
+ const metadataCache = new Map();
193
+ const metadataStates = new Map();
194
+ async function metadata(file) {
195
+ if (metadataCache.has(file))
196
+ return metadataCache.get(file);
197
+ metadataCache.set(file, null);
198
+ metadataStates.set(file, 'unavailable');
199
+ try {
200
+ const real = await fs.realpath(file);
201
+ if (sensitive(real) || ![...allowedRoots].some(root => within(root, real))) {
202
+ unavailable(file, rootExposure(file, null, 'store'), 'opaque', 'Metadata outside selected scope or credential target excluded');
203
+ return null;
204
+ }
205
+ const text = await readBounded(real);
206
+ if (text === null)
207
+ return null;
208
+ const value = JSON.parse(text);
209
+ const result = value && typeof value === 'object' && !Array.isArray(value) ? value : null;
210
+ metadataCache.set(file, result);
211
+ if (result)
212
+ metadataStates.set(file, 'ok');
213
+ return result;
214
+ }
215
+ catch (error) {
216
+ if (errorStatus(error) === 'absent')
217
+ metadataStates.set(file, 'absent');
218
+ else {
219
+ unavailable(file, rootExposure(file, null, 'store'), 'inaccessible', 'Metadata could not be read or parsed');
220
+ }
221
+ return null;
222
+ }
223
+ }
224
+ let baseline = {};
225
+ let artifactRoot = null;
226
+ const storeWorkspace = scope === 'global' ? homedir() : workspace;
227
+ if (scope !== 'provider') {
228
+ try {
229
+ artifactRoot = resolveProjectAiwgDir(storeWorkspace);
230
+ }
231
+ catch {
232
+ artifactRoot = null;
233
+ const pointer = path.join(storeWorkspace, '.aiwg-location');
234
+ unavailable(pointer, rootExposure(pointer, null, 'store'), 'inaccessible', 'Artifact root resolution failed; no fallback scan performed');
235
+ }
236
+ }
237
+ if (scope !== 'provider' && artifactRoot) {
238
+ allowedRoots.add(artifactRoot);
239
+ const raw = await metadata(path.join(artifactRoot, 'context-memory-firewall-baseline.json'));
240
+ if (raw?.schemaVersion === 'aiwg.context-memory-firewall-baseline/v1' && raw.files && typeof raw.files === 'object') {
241
+ baseline = raw.files;
242
+ }
243
+ }
244
+ async function visit(file, exposure, depth, category, directoryChain = []) {
245
+ if (bounded(file, depth))
246
+ return;
247
+ entries++;
248
+ if (sensitive(file)) {
249
+ unavailable(file, exposure, 'opaque', 'Credential path excluded without reading');
250
+ return;
251
+ }
252
+ let real;
253
+ let stat;
254
+ try {
255
+ real = await fs.realpath(file);
256
+ if (![...allowedRoots].some(root => within(root, real))) {
257
+ unavailable(file, exposure, 'opaque', 'Outside selected scope; external reference was not followed');
258
+ finding('external-reference', [label(file)], 'Select the owning scope to inventory this target');
259
+ return;
260
+ }
261
+ if (sensitive(real)) {
262
+ unavailable(file, exposure, 'opaque', 'Credential target excluded without reading');
263
+ return;
264
+ }
265
+ stat = await fs.stat(real);
266
+ if ((stat.mode & 0o444) === 0 || (stat.isDirectory() && (stat.mode & 0o111) === 0)) {
267
+ throw Object.assign(new Error('permission denied'), { code: 'EACCES' });
268
+ }
269
+ }
270
+ catch (error) {
271
+ const status = errorStatus(error);
272
+ unavailable(file, exposure, status, `Filesystem ${status}: ${error.code ?? 'unknown'}`);
273
+ if (exposure.kind === 'include' || exposure.kind === 'reference')
274
+ finding('stale-reference', [exposure.chain.at(-2) ?? exposure.path, label(file)], `Reference target is ${status}`);
275
+ return;
276
+ }
277
+ if (stat.isDirectory()) {
278
+ if (directoryChain.includes(real)) {
279
+ finding('include-cycle', [...directoryChain.map(label), label(real)], 'Directory symlink cycle');
280
+ return;
281
+ }
282
+ try {
283
+ const directory = await fs.opendir(real);
284
+ for await (const entry of directory) {
285
+ if (bounded(file, depth))
286
+ break;
287
+ if (SKIP.has(entry.name))
288
+ continue;
289
+ const child = path.join(file, entry.name);
290
+ if (entry.isDirectory() || entry.isSymbolicLink() || TEXT.has(path.extname(entry.name))) {
291
+ await visit(child, { ...exposure, path: label(child), chain: [...exposure.chain, label(child)] }, depth + 1, category, [...directoryChain, real]);
292
+ }
293
+ else {
294
+ entries++;
295
+ }
296
+ }
297
+ }
298
+ catch (error) {
299
+ unavailable(file, exposure, 'inaccessible', `Directory enumeration failed: ${error.code ?? 'unknown'}`);
300
+ }
301
+ return;
302
+ }
303
+ if (!stat.isFile()) {
304
+ unavailable(file, exposure, 'opaque', 'Non-regular file excluded');
305
+ return;
306
+ }
307
+ const cached = cache.get(real);
308
+ let item;
309
+ let content;
310
+ if (cached)
311
+ ({ item, content } = cached);
312
+ else {
313
+ if (files >= limits.maxFiles) {
314
+ hit('maxFiles', file);
315
+ return;
316
+ }
317
+ if (stat.size > limits.maxFileBytes) {
318
+ hit('maxFileBytes', file);
319
+ unavailable(file, exposure, 'on-disk', 'File exceeds scan byte bound; body and includes uninspected');
320
+ return;
321
+ }
322
+ try {
323
+ const text = await readBounded(real);
324
+ if (text === null) {
325
+ unavailable(file, exposure, 'on-disk', 'Scan bound reached; content uninspected');
326
+ return;
327
+ }
328
+ content = text;
329
+ }
330
+ catch (error) {
331
+ unavailable(file, exposure, 'inaccessible', `File read failed: ${error.code ?? 'unknown'}`);
332
+ return;
333
+ }
334
+ const sourcePath = label(real);
335
+ const digest = sha256(content);
336
+ const observation = evidence(`Filesystem read: ${sourcePath}; no live session telemetry`, exposure.provider, 'on-disk');
337
+ const indexed = category === 'skill' || /(?:^|\/)(memory|kb|wiki|index)(?:\/|$)/.test(sourcePath);
338
+ const owner = sourcePath.includes('compound-memory') ? 'compound-memory' : sourcePath.includes('line-memory') ? 'line-memory' : indexed ? 'semantic-memory' : exposure.provider ?? 'project-context';
339
+ let record = {
340
+ schemaVersion: 'aiwg.memory-record.v1', id: memoryRecordId(sourcePath, digest), sourcePath, digest,
341
+ version: 1, aliases: [], supersedes: [], mergedFrom: [], splitFrom: [],
342
+ scope: scope === 'global' ? 'global' : exposure.provider ? 'provider-native' : 'project',
343
+ placement: category === 'prompt' ? 'task-context' : indexed ? 'indexed' : 'resident', lifecycle: 'active',
344
+ authority: { owner, source: 'operator', protection: 'unprotected' }, observation,
345
+ };
346
+ if (path.extname(file) === '.json') {
347
+ try {
348
+ const parsed = JSON.parse(content);
349
+ if (parsed?.schemaVersion === 'aiwg.memory-record.v1')
350
+ record = { ...loadMemoryRecord(parsed), observation };
351
+ }
352
+ catch {
353
+ finding('unverified-metadata', [label(file)], 'JSON metadata is invalid; no owner authority inferred');
354
+ }
355
+ }
356
+ if (['.md', '.mdc'].includes(path.extname(file))) {
357
+ try {
358
+ const { data, body } = parseMemoryMarkdown(content);
359
+ if (data.memoryRecord !== undefined) {
360
+ const stamped = loadMemoryRecord(data.memoryRecord);
361
+ if (stamped.sourcePath !== sourcePath || stamped.digest !== sha256(body))
362
+ throw new Error('Stale owner metadata');
363
+ record = { ...stamped, observation };
364
+ }
365
+ if (data.pinned === true)
366
+ record = { ...record, authority: { ...record.authority, protection: 'pinned' } };
367
+ }
368
+ catch {
369
+ finding('conflicting-owner-metadata', [sourcePath], 'Invalid or stale Markdown owner metadata; source review required');
370
+ }
371
+ }
372
+ const manifestData = await metadata(path.join(path.dirname(real), '.aiwg-manifest.json'));
373
+ const managedEntries = manifestData?.managed;
374
+ const manifest = managedEntries?.[path.basename(real)] ?? null;
375
+ let managed = Boolean(manifest) || /aiwg[-:]managed/i.test(content.slice(0, 8192));
376
+ if (!managed && path.basename(real) === 'SKILL.md') {
377
+ const marker = path.join(path.dirname(real), '.aiwg-managed');
378
+ try {
379
+ managed = (await fs.lstat(marker)).isFile();
380
+ }
381
+ catch (error) {
382
+ if (errorStatus(error) !== 'absent')
383
+ unavailable(marker, exposure, 'inaccessible', 'Skill ownership marker could not be inspected');
384
+ }
385
+ }
386
+ const state = classifyContextMemory({ digest, managed, manifest, baselineEntry: baseline[label(real)] });
387
+ const prior = items.get(record.id);
388
+ if (prior?.record && JSON.stringify({ ...prior.record, observation: null }) !== JSON.stringify({ ...record, observation: null })) {
389
+ finding('conflicting-owner-metadata', [...prior.exposures.map(e => e.path), label(file)], 'Copies share immutable identity but disagree on owner metadata; no metadata winner is established');
390
+ }
391
+ const copies = physicalSources.get(record.id) ?? new Set();
392
+ copies.add(real);
393
+ physicalSources.set(record.id, copies);
394
+ if (copies.size > 1 && record.placement === 'resident')
395
+ finding('duplicate-resident-copy', [...copies].map(label), 'The same resident record identity has multiple physical copies');
396
+ item = prior ?? { id: record.id, sourcePath: record.sourcePath, digest: record.digest, sourceDigest: digest, record,
397
+ category, bytes: stat.size, observation, exposures: [], trust: state.trust, reviewStatus: state.reviewStatus, managed };
398
+ items.set(item.id, item);
399
+ cache.set(real, { item, content });
400
+ if (!managed && exposure.provider)
401
+ finding('unmanaged-artifact', [label(file)], 'No managed marker or deployment manifest entry; ownership is unverified');
402
+ for (const code of state.findings)
403
+ finding(code, [label(file)], state.reviewStatus);
404
+ // Trust findings do not silently change the owning record's lifecycle or authority.
405
+ }
406
+ expose(item, exposure);
407
+ const adapter = adapters.find(a => a.providerId === exposure.provider);
408
+ if (adapter?.context.maxContextBytes && stat.size > adapter.context.maxContextBytes)
409
+ finding('provider-truncation-risk', [label(file)], 'Durable bytes exceed the declared provider ceiling; injected bytes are unknown');
410
+ if (category === 'tool-description' || category === 'tool-config') {
411
+ try {
412
+ const parsed = JSON.parse(content);
413
+ const tools = Array.isArray(parsed.tools) ? parsed.tools : [];
414
+ for (const tool of tools) {
415
+ if (bounded(file, depth))
416
+ break;
417
+ entries++;
418
+ if (!tool || typeof tool.name !== 'string' || /[\r\n\0]/.test(tool.name))
419
+ continue;
420
+ const locator = `${label(file)}#tools/${encodeURIComponent(tool.name)}`;
421
+ const digest = sha256(JSON.stringify({ name: tool.name, description: tool.description ?? null, inputSchema: tool.inputSchema ?? null }));
422
+ const id = memoryRecordId(locator, digest);
423
+ let descriptor = items.get(id);
424
+ if (!descriptor) {
425
+ descriptor = { id, sourcePath: locator, digest, record: null, category: 'tool-description',
426
+ bytes: typeof tool.description === 'string' ? Buffer.byteLength(tool.description) : null,
427
+ observation: evidence(`Static tool declaration: ${label(file)}; runtime registration unverified`, exposure.provider, 'on-disk'),
428
+ exposures: [], trust: 'unknown', reviewStatus: 'unreviewed', managed: false };
429
+ items.set(id, descriptor);
430
+ }
431
+ expose(descriptor, { ...exposure, path: locator, kind: 'tool-description', chain: [...exposure.chain, locator] });
432
+ }
433
+ const servers = parsed.mcpServers ?? parsed.mcp ?? {};
434
+ for (const name of Object.keys(servers)) {
435
+ if (bounded(file, depth))
436
+ break;
437
+ entries++;
438
+ const locator = `${label(file)}#mcp/${encodeURIComponent(name)}`;
439
+ unavailable(locator, { ...exposure, path: locator, kind: 'tool-description', chain: [...exposure.chain, locator] }, 'opaque', 'External tool connection declared; descriptions and enabled state require live discovery');
440
+ }
441
+ }
442
+ catch { /* Non-JSON descriptions are inventoried as files without executing tools. */ }
443
+ }
444
+ // Only instruction surfaces follow imports/references; stored knowledge is data.
445
+ if (['memory-store', 'registry', 'tool-description', 'compound-memory', 'native-memory', 'tool-config'].includes(category))
446
+ return;
447
+ const links = [];
448
+ for (const match of content.matchAll(/(?:^|\s)@((?:~\/|\.?\.?\/)?[^\s<>`"']+\.(?:md|mdc|txt|json|ya?ml|toml))/gm)) {
449
+ links.push({ target: match[1], kind: adapter?.context.loadMode === 'native-include' ? 'include' : 'reference' });
450
+ }
451
+ for (const match of content.matchAll(/\[[^\]\n]*\]\(([^)\s]+)(?:\s+"[^"]*")?\)/g)) {
452
+ if (!/^[a-z][a-z\d+.-]*:/i.test(match[1]) && !match[1].startsWith('#'))
453
+ links.push({ target: match[1].split('#')[0], kind: 'reference' });
454
+ }
455
+ if (adapter?.context.configRegistration && path.basename(file) === path.basename(adapter.context.configRegistration.file)) {
456
+ try {
457
+ const parsed = JSON.parse(content);
458
+ const value = parsed[adapter.context.configRegistration.key];
459
+ for (const target of Array.isArray(value) ? value : [])
460
+ if (typeof target === 'string')
461
+ links.push({ target, kind: 'include' });
462
+ }
463
+ catch {
464
+ finding('unverified-registration', [label(file)], 'Instruction registration could not be resolved statically');
465
+ }
466
+ }
467
+ for (const link of links) {
468
+ if (bounded(file, depth))
469
+ break;
470
+ const target = link.target.startsWith('~/') ? path.join(homedir(), link.target.slice(2)) : path.resolve(path.dirname(file), link.target);
471
+ if (/[*?{}]/.test(target)) {
472
+ finding('unresolved-reference', [label(file), link.target], 'Pattern reference requires provider runtime resolution');
473
+ incomplete = true;
474
+ continue;
475
+ }
476
+ const next = { ...exposure, path: label(target), chain: [...exposure.chain, label(target)], kind: link.kind,
477
+ detail: link.kind === 'include' ? 'Declared native import; live loading unverified' : 'Linked context candidate; loading inferred, not a native import claim' };
478
+ const edges = includeGraph[label(file)] ??= [];
479
+ if (!edges.includes(label(target)))
480
+ edges.push(label(target));
481
+ if (exposure.chain.includes(label(target)))
482
+ continue;
483
+ await visit(target, next, depth + 1, 'transitive-context');
484
+ }
485
+ }
486
+ async function scanRoot(file, provider, category) {
487
+ roots.add(file);
488
+ await visit(file, rootExposure(file, provider, category === 'memory-store' || category === 'registry' ? 'store' : 'eligible'), 0, category);
489
+ }
490
+ // Registry reads are preflighted through the same byte bound before an
491
+ // existing memory resolver may consult them. Other projects' bodies stay out
492
+ // of workspace scans.
493
+ if (scope !== 'provider')
494
+ allowedRoots.add(projectMemoryManifestPath());
495
+ const registryMetadata = scope !== 'provider' ? await metadata(projectMemoryManifestPath()) : null;
496
+ if (scope === 'global') {
497
+ allowedRoots.add(projectMemoryHome());
498
+ await scanRoot(projectMemoryHome(), null, 'registry');
499
+ if (registryMetadata?.version === 1 && Array.isArray(registryMetadata.projects))
500
+ for (const entry of registryMetadata.projects) {
501
+ if (entry && typeof entry.memoryRoot === 'string' && path.isAbsolute(entry.memoryRoot)) {
502
+ allowedRoots.add(entry.memoryRoot);
503
+ await scanRoot(entry.memoryRoot, null, 'memory-store');
504
+ }
505
+ }
506
+ }
507
+ else if (scope !== 'provider') {
508
+ for (const name of ['WORKSPACE.md', 'AIWG.md'])
509
+ await scanRoot(path.join(workspace, name), null, 'instruction');
510
+ await scanRoot(projectMemoryManifestPath(), null, 'registry');
511
+ }
512
+ if (scope !== 'provider' && artifactRoot) {
513
+ for (const name of ['context', 'wiki', 'index', 'AIWG.md'])
514
+ await scanRoot(path.join(artifactRoot, name), null, name === 'context' ? 'compound-memory' : name === 'AIWG.md' ? 'instruction' : 'memory-store');
515
+ const configPath = storageConfigPath(storeWorkspace);
516
+ try {
517
+ const raw = await metadata(configPath);
518
+ if (metadataStates.get(configPath) === 'unavailable')
519
+ throw new Error('Storage config was not inspected');
520
+ const config = raw ? validateStorageConfig(raw, configPath) : null;
521
+ for (const subsystem of ['memory', 'kb']) {
522
+ if (config?.backends?.[subsystem] && config.backends[subsystem]?.type !== 'fs') {
523
+ unavailable(`backend:${subsystem}`, rootExposure(`backend:${subsystem}`, null, 'store'), 'opaque', 'External backend connection configured; no network content inspected');
524
+ continue;
525
+ }
526
+ if (subsystem === 'memory' && !config?.roots?.memory && metadataStates.get(projectMemoryManifestPath()) === 'unavailable') {
527
+ // A failed registry read must never turn into an assumed default.
528
+ unavailable(projectMemoryManifestPath(), rootExposure(projectMemoryManifestPath(), null, 'store'), 'inaccessible', 'Memory registry could not be inspected; root resolution skipped');
529
+ continue;
530
+ }
531
+ const root = await resolveSubsystemRootRuntime(subsystem, storeWorkspace, config);
532
+ allowedRoots.add(root);
533
+ await scanRoot(root, null, 'memory-store');
534
+ }
535
+ }
536
+ catch {
537
+ unavailable(configPath, rootExposure(configPath, null, 'store'), 'inaccessible', 'Storage/root resolution failed; no fallback scan performed');
538
+ }
539
+ await scanRoot(path.join(artifactRoot, 'smiths', 'toolsmith'), null, 'tool-description');
540
+ await scanRoot(path.join(artifactRoot, 'smiths', 'tools'), null, 'tool-description');
541
+ }
542
+ async function providerRoots(adapter, base, nested = false) {
543
+ for (const locator of adapter.memoryRoots()) {
544
+ const global = locator.startsWith('~/') || path.isAbsolute(locator);
545
+ if (scope === 'global' ? !global : global)
546
+ continue;
547
+ if (nested && (global || !adapter.context.nestedContext || !adapter.context.startupFiles.includes(locator)))
548
+ continue;
549
+ const expanded = locator.startsWith('~/') ? path.join(homedir(), locator.slice(2)) : path.resolve(base, locator);
550
+ const file = expanded;
551
+ if (scope === 'global')
552
+ allowedRoots.add(expanded.split(/[*?]/)[0].replace(/\/$/, ''));
553
+ if (/[*?]/.test(file)) {
554
+ roots.add(file);
555
+ const prefix = file.slice(0, file.search(/[*?]/));
556
+ const baseDir = prefix.slice(0, prefix.lastIndexOf(path.sep));
557
+ const segments = path.relative(baseDir, file).split(path.sep);
558
+ async function pattern(directory, rest, depth) {
559
+ if (bounded(directory, depth))
560
+ return;
561
+ if (!rest.length) {
562
+ await scanRoot(directory, adapter.providerId, file.includes('/memory') ? 'native-memory' : file.includes('/rules/') ? 'rule' : 'instruction');
563
+ return;
564
+ }
565
+ const [part, ...remaining] = rest;
566
+ if (!/[*?]/.test(part)) {
567
+ await pattern(path.join(directory, part), remaining, depth + 1);
568
+ return;
569
+ }
570
+ try {
571
+ const mode = (await fs.stat(directory)).mode;
572
+ if (!(mode & 0o444) || !(mode & 0o111))
573
+ throw Object.assign(new Error('permission denied'), { code: 'EACCES' });
574
+ for await (const entry of await fs.opendir(directory)) {
575
+ if (bounded(directory, depth))
576
+ break;
577
+ entries++;
578
+ const expression = new RegExp('^' + part.split(/([*?])/).map(p => p === '*' ? '.*' : p === '?' ? '.' : p.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')).join('') + '$');
579
+ if ((entry.isDirectory() || remaining.length === 0) && expression.test(entry.name))
580
+ await pattern(path.join(directory, entry.name), remaining, depth + 1);
581
+ }
582
+ }
583
+ catch (error) {
584
+ unavailable(directory, rootExposure(directory, adapter.providerId), errorStatus(error), 'Native memory pattern root unavailable');
585
+ }
586
+ }
587
+ await pattern(baseDir, segments, 0);
588
+ continue;
589
+ }
590
+ if (nested) {
591
+ try {
592
+ await fs.lstat(file);
593
+ }
594
+ catch (error) {
595
+ if (errorStatus(error) === 'absent')
596
+ continue;
597
+ }
598
+ }
599
+ const definition = getProviderDefinition(adapter.providerId);
600
+ const artifactKind = Object.entries({ ...definition.paths.artifacts, ...definition.paths.contextDiscovery })
601
+ .find(([, value]) => value === locator || (value && path.basename(value) === path.basename(locator)))?.[0];
602
+ const category = artifactKind === 'agents' ? 'role' : artifactKind === 'rules' || artifactKind === 'behaviors' ? 'rule'
603
+ : artifactKind === 'commands' ? 'prompt' : artifactKind === 'skills' || locator.includes('/skills') ? 'skill' : 'instruction';
604
+ await scanRoot(file, adapter.providerId, /(?:^|\/)(?:memories|memory)(?:\/|$)/.test(file) ? 'native-memory' : category);
605
+ }
606
+ }
607
+ for (const adapter of adapters) {
608
+ try {
609
+ await providerRoots(adapter, workspace);
610
+ }
611
+ catch {
612
+ unavailable(`provider:${adapter.providerId}:roots`, rootExposure(`provider:${adapter.providerId}:roots`, adapter.providerId), 'inaccessible', 'Provider root resolution failed; other providers remain independently inventoried');
613
+ }
614
+ const configPath = getProviderConfigPath(adapter.providerId, workspace, { scope: scope === 'global' ? 'user' : 'project' });
615
+ const alternateConfigPath = scope === 'global' ? getProviderConfigPath(adapter.providerId, path.join(workspace, '__scope_probe__'), { scope: 'user' }) : '';
616
+ if (configPath && (scope === 'global' ? configPath === alternateConfigPath : within(workspace, configPath))) {
617
+ if (scope === 'global')
618
+ allowedRoots.add(configPath);
619
+ await scanRoot(configPath, adapter.providerId, 'tool-config');
620
+ }
621
+ const file = `provider:${adapter.providerId}:system`;
622
+ unavailable(file, { path: file, provider: adapter.providerId, kind: 'eligible', chain: [file], detail: 'Provider system content has no static inspection surface' }, 'opaque', 'Hidden provider system content; live injection and enabled state unknown');
623
+ const tools = `provider:${adapter.providerId}:tools`;
624
+ unavailable(tools, { path: tools, provider: adapter.providerId, kind: 'tool-description', chain: [tools], detail: 'Provider runtime tool registry is not statically inspectable' }, 'opaque', 'Native tool descriptions and enabled state unknown; static declarations are inventoried separately');
625
+ }
626
+ // Search subtree instructions without treating ordinary repository files as resident memory.
627
+ if (scope !== 'global') {
628
+ async function subtree(directory, depth) {
629
+ if (bounded(directory, depth))
630
+ return;
631
+ try {
632
+ const mode = (await fs.stat(directory)).mode;
633
+ if (!(mode & 0o444) || !(mode & 0o111))
634
+ throw Object.assign(new Error('permission denied'), { code: 'EACCES' });
635
+ for await (const entry of await fs.opendir(directory)) {
636
+ if (bounded(directory, depth))
637
+ break;
638
+ entries++;
639
+ if (!entry.isDirectory() || entry.name.startsWith('.') || SKIP.has(entry.name))
640
+ continue;
641
+ const child = path.join(directory, entry.name);
642
+ for (const adapter of adapters)
643
+ await providerRoots(adapter, child, true);
644
+ await subtree(child, depth + 1);
645
+ }
646
+ }
647
+ catch (error) {
648
+ unavailable(directory, rootExposure(directory, null), 'inaccessible', `Subtree discovery failed: ${error.code ?? 'unknown'}`);
649
+ }
650
+ }
651
+ await subtree(workspace, 0);
652
+ }
653
+ for (const cycle of detectMemoryIncludeCycles(includeGraph))
654
+ finding('include-cycle', cycle, 'Transitive context cycle; traversal stopped');
655
+ const records = [...items.values()].flatMap(item => item.record ? [item.record] : []);
656
+ try {
657
+ indexMemoryAliases(records);
658
+ }
659
+ catch {
660
+ finding('alias-collision', records.filter(r => r.aliases.length).map(r => r.sourcePath), 'Owner aliases collide; no canonical alias winner was selected');
661
+ }
662
+ const resident = [...items.values()].filter(i => i.record?.placement === 'resident');
663
+ const digests = new Map();
664
+ for (const item of resident)
665
+ if (item.digest)
666
+ digests.set(item.digest, [...digests.get(item.digest) ?? [], item]);
667
+ for (const group of digests.values())
668
+ if (group.length > 1)
669
+ finding('duplicate-resident-copy', group.map(i => i.sourcePath), 'Distinct record identities contain identical resident bytes');
670
+ for (const adapter of adapters) {
671
+ const candidates = resident.filter(i => i.exposures.some(e => e.provider === adapter.providerId && e.kind === 'eligible'));
672
+ if (adapter.limits.includes('nearest-nonempty-context')) {
673
+ const native = candidates.filter(i => path.basename(i.sourcePath) === 'AGENTS.md' && (i.bytes ?? 0) > 0);
674
+ for (const item of native) {
675
+ const ancestors = native.filter(other => other.id !== item.id && within(path.dirname(path.resolve(workspace, other.sourcePath)), path.resolve(workspace, item.sourcePath)));
676
+ if (ancestors.length)
677
+ finding('shadow', [item.sourcePath, ...ancestors.map(i => i.sourcePath)], 'Nearest nonempty OMP context candidate; applicability depends on the selected working directory');
678
+ }
679
+ }
680
+ if (adapter.providerId === 'codex') {
681
+ for (const override of candidates.filter(i => path.basename(i.sourcePath) === 'AGENTS.override.md' && (i.bytes ?? 0) > 0)) {
682
+ const base = candidates.find(i => path.dirname(i.sourcePath) === path.dirname(override.sourcePath) && path.basename(i.sourcePath) === 'AGENTS.md');
683
+ if (base)
684
+ finding('shadow', [override.sourcePath, base.sourcePath], 'Codex override is eligible ahead of AGENTS.md in this directory; runtime loading unverified');
685
+ }
686
+ }
687
+ const byName = new Map();
688
+ for (const item of candidates) {
689
+ const name = path.basename(item.sourcePath);
690
+ byName.set(name, [...byName.get(name) ?? [], item]);
691
+ }
692
+ for (const group of byName.values()) {
693
+ if (new Set(group.map(i => i.digest)).size < 2)
694
+ continue;
695
+ finding('potential-conflict', group.map(i => i.sourcePath), adapter.limits.includes('no-general-precedence')
696
+ ? 'Applicable instruction surfaces differ; no general Copilot precedence is asserted'
697
+ : 'Eligible instruction copies differ; inspect applicability before deciding precedence');
698
+ }
699
+ }
700
+ return MemoryAuditSchema.parse({
701
+ schemaVersion: 'aiwg.memory-audit.v1', at, workspace, scope,
702
+ providers: adapters.map(a => ({ id: a.providerId, support: a.context.support, limits: [...a.limits], diagnostic: a.diagnostic,
703
+ precedence: a.limits.includes('no-general-precedence') ? ['No general precedence; applicable personal, repository and organization instructions combine'] : a.context.precedence })),
704
+ items: [...items.values()].sort((a, b) => a.sourcePath.localeCompare(b.sourcePath)), findings,
705
+ coverage: { mode: 'static', complete: !incomplete, roots: [...roots], limits, limitsHit: [...limitsHit], files, entries,
706
+ exclusions: ['Global roots require --scope global', 'Credential files, network content and hidden system bodies are not read', 'Live activation, injected bytes and external tool descriptions are unknown', 'Linked context is eligible, not proof of provider loading'] },
707
+ });
708
+ }
709
+ export function formatMemoryAudit(report) {
710
+ return [
711
+ `Memory audit (${report.scope}): ${report.workspace}`,
712
+ `Static inventory: ${report.items.length} items, ${report.findings.length} findings; live context unobserved`,
713
+ `Coverage: ${report.coverage.complete ? 'complete within selected static surfaces' : 'incomplete; inspect inaccessible, opaque and truncated surfaces'}`,
714
+ ...report.items.map(i => ` ${i.observation.status.padEnd(12)} ${i.sourcePath} [${i.category}; ${i.exposures.length} exposure(s)]`),
715
+ ...report.findings.map(f => ` ${f.code}: ${f.paths.join(' -> ')} (${f.detail})`),
716
+ ...(report.coverage.limitsHit.length ? [`Bounds reached: ${report.coverage.limitsHit.join(', ')}`] : []),
717
+ ].join('\n');
718
+ }
719
+ //# sourceMappingURL=audit.js.map