@agentxm/workspace-state 0.28.4-bootstrap.0

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 (215) hide show
  1. package/LICENSE +110 -0
  2. package/README.md +11 -0
  3. package/dist/src/index.d.ts +72 -0
  4. package/dist/src/index.js +88 -0
  5. package/dist/src/knowledge/discovery-config.d.ts +12 -0
  6. package/dist/src/knowledge/discovery-config.js +9 -0
  7. package/dist/src/lockfile/accepted-registry-version.d.ts +12 -0
  8. package/dist/src/lockfile/accepted-registry-version.js +13 -0
  9. package/dist/src/lockfile/entry-fields.d.ts +189 -0
  10. package/dist/src/lockfile/entry-fields.js +89 -0
  11. package/dist/src/lockfile/errors.d.ts +48 -0
  12. package/dist/src/lockfile/errors.js +27 -0
  13. package/dist/src/lockfile/index.d.ts +18 -0
  14. package/dist/src/lockfile/index.js +16 -0
  15. package/dist/src/lockfile/lockfile.d.ts +2255 -0
  16. package/dist/src/lockfile/lockfile.js +235 -0
  17. package/dist/src/lockfile/resolved-version.d.ts +4 -0
  18. package/dist/src/lockfile/resolved-version.js +7 -0
  19. package/dist/src/lockfile/schema.d.ts +3062 -0
  20. package/dist/src/lockfile/schema.js +406 -0
  21. package/dist/src/schema/index.d.ts +3 -0
  22. package/dist/src/schema/index.js +2 -0
  23. package/dist/src/schema/read-and-validate-json-file.d.ts +20 -0
  24. package/dist/src/schema/read-and-validate-json-file.js +85 -0
  25. package/dist/src/schema/types.d.ts +26 -0
  26. package/dist/src/schema/types.js +2 -0
  27. package/dist/src/settings/errors.d.ts +22 -0
  28. package/dist/src/settings/errors.js +15 -0
  29. package/dist/src/settings/format-preserving-json.d.ts +40 -0
  30. package/dist/src/settings/format-preserving-json.js +80 -0
  31. package/dist/src/settings/index.d.ts +15 -0
  32. package/dist/src/settings/index.js +14 -0
  33. package/dist/src/settings/schema.d.ts +687 -0
  34. package/dist/src/settings/schema.js +977 -0
  35. package/dist/src/settings/settings.d.ts +38 -0
  36. package/dist/src/settings/settings.js +213 -0
  37. package/dist/src/testing.d.ts +17 -0
  38. package/dist/src/testing.js +17 -0
  39. package/dist/src/utils/atomic-write.d.ts +67 -0
  40. package/dist/src/utils/atomic-write.js +71 -0
  41. package/dist/src/utils/environment.d.ts +16 -0
  42. package/dist/src/utils/environment.js +19 -0
  43. package/dist/src/utils/path-safety.d.ts +25 -0
  44. package/dist/src/utils/path-safety.js +34 -0
  45. package/dist/src/utils/resolve-parent-symlinks.d.ts +16 -0
  46. package/dist/src/utils/resolve-parent-symlinks.js +36 -0
  47. package/dist/src/workspace/accepted-canonical-ref.d.ts +58 -0
  48. package/dist/src/workspace/accepted-canonical-ref.js +229 -0
  49. package/dist/src/workspace/artifact-change.d.ts +14 -0
  50. package/dist/src/workspace/artifact-change.js +18 -0
  51. package/dist/src/workspace/canonical-observation.d.ts +39 -0
  52. package/dist/src/workspace/canonical-observation.js +307 -0
  53. package/dist/src/workspace/configured-agent-outcome.d.ts +22 -0
  54. package/dist/src/workspace/configured-agent-outcome.js +33 -0
  55. package/dist/src/workspace/configured-agent-outcomes-provider.d.ts +50 -0
  56. package/dist/src/workspace/configured-agent-outcomes-provider.js +26 -0
  57. package/dist/src/workspace/configured-agent-outcomes.d.ts +48 -0
  58. package/dist/src/workspace/configured-agent-outcomes.js +113 -0
  59. package/dist/src/workspace/configured-entry-resolution/types.d.ts +32 -0
  60. package/dist/src/workspace/configured-entry-resolution/types.js +2 -0
  61. package/dist/src/workspace/configured-entry-resolution/workspace-ref.d.ts +32 -0
  62. package/dist/src/workspace/configured-entry-resolution/workspace-ref.js +176 -0
  63. package/dist/src/workspace/constants.d.ts +6 -0
  64. package/dist/src/workspace/constants.js +6 -0
  65. package/dist/src/workspace/create-symlink.d.ts +26 -0
  66. package/dist/src/workspace/create-symlink.js +87 -0
  67. package/dist/src/workspace/desired-pack-lock.d.ts +18 -0
  68. package/dist/src/workspace/desired-pack-lock.js +132 -0
  69. package/dist/src/workspace/desired-state-enabled.d.ts +18 -0
  70. package/dist/src/workspace/desired-state-enabled.js +16 -0
  71. package/dist/src/workspace/desired-state-graph.d.ts +114 -0
  72. package/dist/src/workspace/desired-state-graph.js +384 -0
  73. package/dist/src/workspace/desired-state-problem-text.d.ts +6 -0
  74. package/dist/src/workspace/desired-state-problem-text.js +27 -0
  75. package/dist/src/workspace/errors.d.ts +184 -0
  76. package/dist/src/workspace/errors.js +79 -0
  77. package/dist/src/workspace/extension-name.d.ts +28 -0
  78. package/dist/src/workspace/extension-name.js +66 -0
  79. package/dist/src/workspace/extension-paths.d.ts +82 -0
  80. package/dist/src/workspace/extension-paths.js +172 -0
  81. package/dist/src/workspace/footprint-recorder.d.ts +30 -0
  82. package/dist/src/workspace/footprint-recorder.js +32 -0
  83. package/dist/src/workspace/layout.d.ts +43 -0
  84. package/dist/src/workspace/layout.js +164 -0
  85. package/dist/src/workspace/lock-entry-to-ref.d.ts +40 -0
  86. package/dist/src/workspace/lock-entry-to-ref.js +433 -0
  87. package/dist/src/workspace/lock-entry-to-source-params.d.ts +26 -0
  88. package/dist/src/workspace/lock-entry-to-source-params.js +81 -0
  89. package/dist/src/workspace/locked-entries.d.ts +30 -0
  90. package/dist/src/workspace/locked-entries.js +34 -0
  91. package/dist/src/workspace/materialized-file-target.d.ts +14 -0
  92. package/dist/src/workspace/materialized-file-target.js +14 -0
  93. package/dist/src/workspace/materialized-tree.d.ts +23 -0
  94. package/dist/src/workspace/materialized-tree.js +90 -0
  95. package/dist/src/workspace/mcp-entry-semantics.d.ts +31 -0
  96. package/dist/src/workspace/mcp-entry-semantics.js +60 -0
  97. package/dist/src/workspace/observed-installed.d.ts +9 -0
  98. package/dist/src/workspace/observed-installed.js +9 -0
  99. package/dist/src/workspace/pack-manifest-content-identity.d.ts +12 -0
  100. package/dist/src/workspace/pack-manifest-content-identity.js +20 -0
  101. package/dist/src/workspace/pack-paths.d.ts +24 -0
  102. package/dist/src/workspace/pack-paths.js +16 -0
  103. package/dist/src/workspace/package-hash.d.ts +25 -0
  104. package/dist/src/workspace/package-hash.js +45 -0
  105. package/dist/src/workspace/paths.d.ts +24 -0
  106. package/dist/src/workspace/paths.js +52 -0
  107. package/dist/src/workspace/read-model/__fixtures__/builder.d.ts +199 -0
  108. package/dist/src/workspace/read-model/__fixtures__/builder.js +588 -0
  109. package/dist/src/workspace/read-model/__fixtures__/decoders.d.ts +15 -0
  110. package/dist/src/workspace/read-model/__fixtures__/decoders.js +15 -0
  111. package/dist/src/workspace/read-model/__fixtures__/occurrences.d.ts +72 -0
  112. package/dist/src/workspace/read-model/__fixtures__/occurrences.js +134 -0
  113. package/dist/src/workspace/read-model/__fixtures__/scenario-harness.d.ts +99 -0
  114. package/dist/src/workspace/read-model/__fixtures__/scenario-harness.js +142 -0
  115. package/dist/src/workspace/read-model/__fixtures__/test-layer.d.ts +60 -0
  116. package/dist/src/workspace/read-model/__fixtures__/test-layer.js +57 -0
  117. package/dist/src/workspace/read-model/agent-presence.d.ts +32 -0
  118. package/dist/src/workspace/read-model/agent-presence.js +21 -0
  119. package/dist/src/workspace/read-model/agent-root-resolver.d.ts +38 -0
  120. package/dist/src/workspace/read-model/agent-root-resolver.js +35 -0
  121. package/dist/src/workspace/read-model/agents/index.d.ts +38 -0
  122. package/dist/src/workspace/read-model/agents/index.js +45 -0
  123. package/dist/src/workspace/read-model/agents/shared.d.ts +27 -0
  124. package/dist/src/workspace/read-model/agents/shared.js +63 -0
  125. package/dist/src/workspace/read-model/agents/types.d.ts +133 -0
  126. package/dist/src/workspace/read-model/agents/types.js +51 -0
  127. package/dist/src/workspace/read-model/diagnostics.d.ts +18 -0
  128. package/dist/src/workspace/read-model/diagnostics.js +14 -0
  129. package/dist/src/workspace/read-model/discovery/index.d.ts +5 -0
  130. package/dist/src/workspace/read-model/discovery/index.js +5 -0
  131. package/dist/src/workspace/read-model/discovery/plugin-manifests.d.ts +16 -0
  132. package/dist/src/workspace/read-model/discovery/plugin-manifests.js +168 -0
  133. package/dist/src/workspace/read-model/discovery/skills.d.ts +58 -0
  134. package/dist/src/workspace/read-model/discovery/skills.js +263 -0
  135. package/dist/src/workspace/read-model/discovery/subagents.d.ts +37 -0
  136. package/dist/src/workspace/read-model/discovery/subagents.js +48 -0
  137. package/dist/src/workspace/read-model/errors.d.ts +93 -0
  138. package/dist/src/workspace/read-model/errors.js +30 -0
  139. package/dist/src/workspace/read-model/extensions/actual-helpers.d.ts +4 -0
  140. package/dist/src/workspace/read-model/extensions/actual-helpers.js +4 -0
  141. package/dist/src/workspace/read-model/extensions/hook.d.ts +104 -0
  142. package/dist/src/workspace/read-model/extensions/hook.js +119 -0
  143. package/dist/src/workspace/read-model/extensions/index.d.ts +19 -0
  144. package/dist/src/workspace/read-model/extensions/index.js +43 -0
  145. package/dist/src/workspace/read-model/extensions/indexByName.d.ts +31 -0
  146. package/dist/src/workspace/read-model/extensions/indexByName.js +30 -0
  147. package/dist/src/workspace/read-model/extensions/inventory.d.ts +99 -0
  148. package/dist/src/workspace/read-model/extensions/inventory.js +111 -0
  149. package/dist/src/workspace/read-model/extensions/knowledge.d.ts +101 -0
  150. package/dist/src/workspace/read-model/extensions/knowledge.js +121 -0
  151. package/dist/src/workspace/read-model/extensions/mcp-server.d.ts +106 -0
  152. package/dist/src/workspace/read-model/extensions/mcp-server.js +122 -0
  153. package/dist/src/workspace/read-model/extensions/pack.d.ts +104 -0
  154. package/dist/src/workspace/read-model/extensions/pack.js +123 -0
  155. package/dist/src/workspace/read-model/extensions/package-root.d.ts +39 -0
  156. package/dist/src/workspace/read-model/extensions/package-root.js +48 -0
  157. package/dist/src/workspace/read-model/extensions/projection.d.ts +137 -0
  158. package/dist/src/workspace/read-model/extensions/projection.js +180 -0
  159. package/dist/src/workspace/read-model/extensions/rule.d.ts +104 -0
  160. package/dist/src/workspace/read-model/extensions/rule.js +113 -0
  161. package/dist/src/workspace/read-model/extensions/skill.d.ts +170 -0
  162. package/dist/src/workspace/read-model/extensions/skill.js +148 -0
  163. package/dist/src/workspace/read-model/extensions/subagent.d.ts +97 -0
  164. package/dist/src/workspace/read-model/extensions/subagent.js +120 -0
  165. package/dist/src/workspace/read-model/scanners/agent-dir.d.ts +49 -0
  166. package/dist/src/workspace/read-model/scanners/agent-dir.js +180 -0
  167. package/dist/src/workspace/read-model/scanners/agent-root.d.ts +73 -0
  168. package/dist/src/workspace/read-model/scanners/agent-root.js +148 -0
  169. package/dist/src/workspace/read-model/scanners/agent-settings.d.ts +43 -0
  170. package/dist/src/workspace/read-model/scanners/agent-settings.js +54 -0
  171. package/dist/src/workspace/read-model/scanners/canonical-extensions.d.ts +48 -0
  172. package/dist/src/workspace/read-model/scanners/canonical-extensions.js +237 -0
  173. package/dist/src/workspace/read-model/scanners/fs-helpers.d.ts +63 -0
  174. package/dist/src/workspace/read-model/scanners/fs-helpers.js +106 -0
  175. package/dist/src/workspace/read-model/scanners/index.d.ts +15 -0
  176. package/dist/src/workspace/read-model/scanners/index.js +34 -0
  177. package/dist/src/workspace/read-model/scanners/mcp-config.d.ts +47 -0
  178. package/dist/src/workspace/read-model/scanners/mcp-config.js +212 -0
  179. package/dist/src/workspace/read-model/scanners/types.d.ts +170 -0
  180. package/dist/src/workspace/read-model/scanners/types.js +86 -0
  181. package/dist/src/workspace/read-model/service.d.ts +103 -0
  182. package/dist/src/workspace/read-model/service.js +391 -0
  183. package/dist/src/workspace/read-model/state.d.ts +35 -0
  184. package/dist/src/workspace/read-model/state.js +97 -0
  185. package/dist/src/workspace/read-model/types.d.ts +75 -0
  186. package/dist/src/workspace/read-model/types.js +26 -0
  187. package/dist/src/workspace/read-model-record-readers.d.ts +34 -0
  188. package/dist/src/workspace/read-model-record-readers.js +467 -0
  189. package/dist/src/workspace/read-model-record-rows.d.ts +45 -0
  190. package/dist/src/workspace/read-model-record-rows.js +33 -0
  191. package/dist/src/workspace/read-model-record-types.d.ts +45 -0
  192. package/dist/src/workspace/read-model-record-types.js +13 -0
  193. package/dist/src/workspace/remove-if-exists.d.ts +13 -0
  194. package/dist/src/workspace/remove-if-exists.js +26 -0
  195. package/dist/src/workspace/rendered-files.d.ts +53 -0
  196. package/dist/src/workspace/rendered-files.js +50 -0
  197. package/dist/src/workspace/scope-refusal.d.ts +26 -0
  198. package/dist/src/workspace/scope-refusal.js +29 -0
  199. package/dist/src/workspace/service-interface.d.ts +479 -0
  200. package/dist/src/workspace/service-interface.js +27 -0
  201. package/dist/src/workspace/service.d.ts +1665 -0
  202. package/dist/src/workspace/service.js +1263 -0
  203. package/dist/src/workspace/setup-scope-support.d.ts +27 -0
  204. package/dist/src/workspace/setup-scope-support.js +299 -0
  205. package/dist/src/workspace/skill-paths.d.ts +33 -0
  206. package/dist/src/workspace/skill-paths.js +14 -0
  207. package/dist/src/workspace/source-metadata.d.ts +7 -0
  208. package/dist/src/workspace/source-metadata.js +10 -0
  209. package/dist/src/workspace/source-to-lock-entry.d.ts +32 -0
  210. package/dist/src/workspace/source-to-lock-entry.js +68 -0
  211. package/dist/src/workspace/test-stubs.d.ts +144 -0
  212. package/dist/src/workspace/test-stubs.js +412 -0
  213. package/dist/src/workspace/transaction.d.ts +218 -0
  214. package/dist/src/workspace/transaction.js +177 -0
  215. package/package.json +57 -0
@@ -0,0 +1,212 @@
1
+ /**
2
+ * MCP-config scanner: plans physical targets from the capability catalog,
3
+ * reads each physical file once, and emits one occurrence per declared server.
4
+ * Shared targets remain shared observations; agent-native targets retain the
5
+ * exact agent id whose private surface supplied the observation.
6
+ *
7
+ * Per-file partial failures (unreadable config, bad JSON, schema-incompatible
8
+ * payload) become diagnostic warnings rather than errors. The error channel
9
+ * stays empty.
10
+ *
11
+ * MCP config layout:
12
+ *
13
+ * ```json
14
+ * {
15
+ * "mcpServers": {
16
+ * "<server-name>": { ... }
17
+ * }
18
+ * }
19
+ * ```
20
+ *
21
+ * The scanner emits one occurrence per `<server-name>` and captures each
22
+ * record-shaped server payload for import/adoption flows.
23
+ */
24
+ import * as Effect from "effect/Effect";
25
+ import * as Option from "effect/Option";
26
+ import * as Schema from "effect/Schema";
27
+ import { parse } from "jsonc-parser";
28
+ import { getHome } from "../../../utils/environment.js";
29
+ import { AGENTS } from "@agentxm/extension-model/unstable/agents/registry";
30
+ import { CONFIGURABLE_AGENTS_BY_ID, } from "@agentxm/extension-model/unstable/agent-capabilities";
31
+ import { ExtensionNameSchema, } from "@agentxm/extension-model/unstable/extensions/common";
32
+ import { makeAbsolutePath } from "@agentxm/extension-model/unstable/path-types";
33
+ import { isPathSafe } from "../../../utils/path-safety.js";
34
+ const SCANNER_NAME = "mcp-config";
35
+ /**
36
+ * Closure helper: returns the dependency-closed scanner effect.
37
+ */
38
+ export const makeMcpConfigScanner = (deps) => scanMcpConfig(deps);
39
+ // ---------------------------------------------------------------------------
40
+ // Helpers: read + parse + extract server names
41
+ // ---------------------------------------------------------------------------
42
+ /**
43
+ * Minimum shape the scanner needs from an MCP config file: a top-level
44
+ * object that may contain an agent-configured server record. Per-server payloads stay
45
+ * opaque (`Schema.Unknown`) — Phase 7's MCP server subject module decodes
46
+ * them through its own schema. The scanner only emits one occurrence per
47
+ * server name.
48
+ */
49
+ const McpConfigShapeSchema = Schema.Record(Schema.String, Schema.Unknown);
50
+ const decodeMcpConfigShape = Schema.decodeUnknownEffect(McpConfigShapeSchema);
51
+ // MCP configs can contain arbitrary, user-authored server names. Only names that
52
+ // are valid AXM extension names can be managed; skip the rest instead of letting
53
+ // a non-conforming name (e.g. uppercase or underscore) crash the whole scan.
54
+ const decodeExtensionNameOption = Schema.decodeUnknownOption(ExtensionNameSchema);
55
+ const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
56
+ const extractServers = (decoded, serversKey) => !isRecord(decoded[serversKey])
57
+ ? []
58
+ : Object.entries(decoded[serversKey]).flatMap(([name, config]) => {
59
+ if (!isRecord(config))
60
+ return [];
61
+ const decodedName = decodeExtensionNameOption(name);
62
+ return Option.isNone(decodedName) ? [] : [{ name: decodedName.value, config }];
63
+ });
64
+ const readMcpConfig = (fs, diagnostics, filePath) => Effect.gen(function* () {
65
+ const exists = yield* Effect.result(fs.exists(filePath));
66
+ if (exists._tag === "Failure") {
67
+ yield* diagnostics.append({
68
+ source: "scanner",
69
+ message: `${SCANNER_NAME}: cannot stat ${filePath}`,
70
+ path: filePath,
71
+ code: "scanner-io",
72
+ });
73
+ return Option.none();
74
+ }
75
+ if (!exists.success)
76
+ return Option.none();
77
+ const read = yield* Effect.result(fs.readFileString(filePath));
78
+ if (read._tag === "Failure") {
79
+ yield* diagnostics.append({
80
+ source: "scanner",
81
+ message: `${SCANNER_NAME}: cannot read ${filePath}`,
82
+ path: filePath,
83
+ code: "scanner-io",
84
+ });
85
+ return Option.none();
86
+ }
87
+ const parsed = yield* Effect.result(Effect.try({
88
+ try: () => {
89
+ const errors = [];
90
+ const value = parse(read.success, errors, { allowTrailingComma: true });
91
+ if (errors.length > 0)
92
+ throw errors;
93
+ return value;
94
+ },
95
+ catch: (cause) => ({ cause }),
96
+ }));
97
+ if (parsed._tag === "Failure") {
98
+ yield* diagnostics.append({
99
+ source: "scanner",
100
+ message: `${SCANNER_NAME}: cannot parse JSON at ${filePath}`,
101
+ path: filePath,
102
+ code: "scanner-parse",
103
+ });
104
+ return Option.none();
105
+ }
106
+ const decoded = yield* Effect.result(decodeMcpConfigShape(parsed.success));
107
+ if (decoded._tag === "Failure") {
108
+ yield* diagnostics.append({
109
+ source: "scanner",
110
+ message: `${SCANNER_NAME}: invalid MCP config shape at ${filePath}: ${decoded.failure.message}`,
111
+ path: filePath,
112
+ code: "scanner-parse",
113
+ });
114
+ return Option.none();
115
+ }
116
+ return Option.some(decoded.success);
117
+ });
118
+ const readMcpConfigCached = (cache, fs, diagnostics, filePath) => {
119
+ const existing = cache.get(filePath);
120
+ if (existing !== undefined)
121
+ return Effect.succeed(existing);
122
+ return readMcpConfig(fs, diagnostics, filePath).pipe(Effect.tap((decoded) => Effect.sync(() => {
123
+ cache.set(filePath, decoded);
124
+ })));
125
+ };
126
+ const hasMcpConfig = (capability) => capability.axm.writer !== null;
127
+ const isCapabilityAgentId = (id) => id in CONFIGURABLE_AGENTS_BY_ID;
128
+ const capabilityFor = (descriptor) => {
129
+ if (!isCapabilityAgentId(descriptor.id))
130
+ return undefined;
131
+ const agent = CONFIGURABLE_AGENTS_BY_ID[descriptor.id];
132
+ const capability = agent.capabilities["mcp-server"];
133
+ return hasMcpConfig(capability) ? capability : undefined;
134
+ };
135
+ const surfacePlanKey = (plan) => plan.surface._tag === "shared"
136
+ ? `shared:${plan.target.scope}:${plan.target.path}`
137
+ : `agent:${plan.surface.agentId}:${plan.target.scope}:${plan.target.path}`;
138
+ const planMcpSurfaces = (scope, registry) => {
139
+ const plans = new Map();
140
+ for (const descriptor of Object.values(registry)) {
141
+ const capability = capabilityFor(descriptor);
142
+ if (capability === undefined)
143
+ continue;
144
+ for (const target of capability.axm.writer.config.targets) {
145
+ if (target.scope !== scope)
146
+ continue;
147
+ const surface = target.attribution === "shared"
148
+ ? { _tag: "shared" }
149
+ : { _tag: "agent", agentId: descriptor.id };
150
+ const plan = {
151
+ surface,
152
+ target,
153
+ serversKey: capability.axm.writer.config.serversKey,
154
+ };
155
+ const key = surfacePlanKey(plan);
156
+ if (!plans.has(key))
157
+ plans.set(key, plan);
158
+ }
159
+ }
160
+ return [...plans.values()];
161
+ };
162
+ const resolveMcpConfigTargetPath = (deps, target) => Effect.gen(function* () {
163
+ const { path, workspaceRoot, diagnostics } = deps;
164
+ const home = yield* getHome;
165
+ const configPath = target.scope === "user"
166
+ ? target.path.startsWith("~/")
167
+ ? path.join(home, target.path.slice(2))
168
+ : path.resolve(home, target.path)
169
+ : path.resolve(workspaceRoot, target.path);
170
+ if (target.scope === "project" && !isPathSafe(path, workspaceRoot, configPath)) {
171
+ yield* diagnostics.append({
172
+ source: "scanner",
173
+ message: `${SCANNER_NAME}: MCP config target escapes workspace root: ${target.path}`,
174
+ path: configPath,
175
+ code: "scanner-config",
176
+ });
177
+ return Option.none();
178
+ }
179
+ return Option.some(configPath);
180
+ });
181
+ const scanMcpSurface = (deps, plan, cache) => Effect.gen(function* () {
182
+ const { fs, path, scope, diagnostics } = deps;
183
+ const filePathOpt = yield* resolveMcpConfigTargetPath(deps, plan.target);
184
+ if (Option.isNone(filePathOpt))
185
+ return [];
186
+ const decoded = yield* readMcpConfigCached(cache, fs, diagnostics, filePathOpt.value);
187
+ if (Option.isNone(decoded))
188
+ return [];
189
+ const servers = extractServers(decoded.value, plan.serversKey);
190
+ const contentLocation = makeAbsolutePath(path, filePathOpt.value);
191
+ return servers.map((server) => ({
192
+ _tag: "mcp-config",
193
+ scope,
194
+ surface: plan.surface,
195
+ name: server.name,
196
+ contentLocation,
197
+ config: server.config,
198
+ }));
199
+ });
200
+ // ---------------------------------------------------------------------------
201
+ // Scanner body
202
+ // ---------------------------------------------------------------------------
203
+ const scanMcpConfig = Effect.fn("workspace.read-model.scanner.mcp-config")(function* (deps) {
204
+ const registry = deps.agentRegistry ?? AGENTS;
205
+ const cache = new Map();
206
+ const plans = planMcpSurfaces(deps.scope, registry);
207
+ const occurrences = yield* Effect.forEach(plans, (plan) => scanMcpSurface(deps, plan, cache), {
208
+ concurrency: 1,
209
+ });
210
+ return occurrences.flat();
211
+ });
212
+ //# sourceMappingURL=mcp-config.js.map
@@ -0,0 +1,170 @@
1
+ /**
2
+ * Shared scanner types: occurrence shape, common per-occurrence fields, and
3
+ * scanner-tier origin discriminators.
4
+ *
5
+ * Per Decision 5 of the workspace read-model design, scanner output is
6
+ * occurrence-shaped (`ReadonlyArray<Occurrence>`) — one entry per observable
7
+ * physical materialization. There is no central `DetectionOrigin` union; each
8
+ * scanner exports the discriminator it observes, and per-subject modules in
9
+ * Phase 7 map these scanner-tier discriminators into subject-specific origin
10
+ * unions.
11
+ *
12
+ * Identity of an actual entry is `(scope, type | agentId, origin, contentLocation)`.
13
+ * Two scanner paths observing the same physical occurrence collapse to one
14
+ * entry; two distinct physical paths under the same name produce two entries
15
+ * with different identities.
16
+ */
17
+ import * as Option from "effect/Option";
18
+ import type { ExtensionName, ExtensionType } from "@agentxm/extension-model/unstable/extensions/common";
19
+ import type { Handle } from "@agentxm/extension-model/unstable/extensions/handle";
20
+ import type { AgentId } from "@agentxm/extension-model/unstable/agents/types";
21
+ import type { AbsolutePath } from "@agentxm/extension-model/unstable/path-types";
22
+ import type { Scope } from "../types.js";
23
+ /**
24
+ * Origin tag for an occurrence emitted by the canonical-extensions scanner.
25
+ * `canonical-axm` covers authored or owner-qualified packages;
26
+ * `external-axm` covers user-scope external-layout packages.
27
+ */
28
+ export type CanonicalExtensionOriginKind = "canonical-axm" | "external-axm";
29
+ /**
30
+ * One canonical package materialization observed in an authored root,
31
+ * `agent_extensions/`, or the user-scope `agent_extensions/` root.
32
+ *
33
+ * Fields:
34
+ *
35
+ * - `scope` — the scoping tier this occurrence belongs to (project | user).
36
+ * - `type` — the subject extension type.
37
+ * - `origin` — scanner-tier origin (canonical-axm | external-axm).
38
+ * - `name` — the subject's directory name (last segment under owner/type).
39
+ * - `owner` — the registry-owner prefix for canonical AXM, `null` for external.
40
+ * - `contentLocation` — absolute path on the synthesized FS to the subject's
41
+ * content root (the directory itself, not a file inside it).
42
+ * - `pathSegments` — `contentLocation` split via the `Path` service. Subject
43
+ * modules reach for this when they need to derive parent paths
44
+ * (`packageRoot`, etc.) without re-parsing the string. Cross-platform safe.
45
+ * - `subjectFile` — `Some(path)` when the subject type has a canonical
46
+ * primary content file (e.g., `<dir>/SKILL.md` for skills). `None` for
47
+ * subject types that do not use a fixed primary file (mcp-server, file,
48
+ * rule, pack).
49
+ * - `subjectFileExists` — `true` if the scanner probed `subjectFile` and found
50
+ * it present. Always `false` when `subjectFile` is `None`. Subject modules
51
+ * consume this rather than hardcoding presence.
52
+ */
53
+ export interface CanonicalExtensionOccurrence {
54
+ readonly _tag: "canonical-extension";
55
+ readonly scope: Scope;
56
+ readonly type: ExtensionType;
57
+ readonly origin: CanonicalExtensionOriginKind;
58
+ readonly name: ExtensionName;
59
+ readonly owner: Handle | null;
60
+ readonly contentLocation: AbsolutePath;
61
+ readonly pathSegments: ReadonlyArray<string>;
62
+ readonly subjectFile: Option.Option<AbsolutePath>;
63
+ readonly subjectFileExists: boolean;
64
+ }
65
+ /**
66
+ * Subject types renderable into per-agent directories. Only the subjects
67
+ * actually observed by the v1 scanner are listed; rules render through
68
+ * subject-specific dirs that the agent registry does not currently encode and
69
+ * are therefore out of scope for the agent-dir scanner.
70
+ */
71
+ export type AgentDirSubjectType = "skill" | "subagent";
72
+ /**
73
+ * One materialization observed under a per-agent rendered directory
74
+ * (`.claude/skills/`, `.codex/agents/`, `.roomodes`,
75
+ * etc.). The origin discriminator carries the agent id so per-subject modules
76
+ * can map cleanly into agent-specific origins.
77
+ *
78
+ * Structural fields mirror `CanonicalExtensionOccurrence`:
79
+ *
80
+ * - `pathSegments` — `contentLocation` split via the `Path` service.
81
+ * - `subjectFile` — `Some(path)` when the subject type has a canonical
82
+ * primary file (skill → `<dir>/SKILL.md`, command → `<dir>/${name}.md`,
83
+ * subagent dir → `<dir>/${name}.md`). For single-file subagent surfaces
84
+ * (e.g., `.roomodes`) the `subjectFile` is the `contentLocation` itself.
85
+ * - `subjectFileExists` — `true` if the scanner probed `subjectFile` and
86
+ * found it present. Always `false` when `subjectFile` is `None`. For
87
+ * single-file subagents this is always `true` because presence of the file
88
+ * is what produced the occurrence.
89
+ */
90
+ export interface AgentDirOccurrence {
91
+ readonly _tag: "agent-dir";
92
+ readonly scope: Scope;
93
+ readonly type: AgentDirSubjectType;
94
+ readonly agentId: AgentId;
95
+ /** Primary write path or the catalog status of an additional read-only path. */
96
+ readonly readPathStatus?: "primary" | "canonical" | "compat" | "deprecated";
97
+ readonly name: string;
98
+ readonly contentLocation: AbsolutePath;
99
+ readonly pathSegments: ReadonlyArray<string>;
100
+ readonly subjectFile: Option.Option<AbsolutePath>;
101
+ readonly subjectFileExists: boolean;
102
+ }
103
+ /** Physical MCP configuration surface that supplied an observation. */
104
+ export type McpConfigSurface = {
105
+ readonly _tag: "shared";
106
+ } | {
107
+ readonly _tag: "agent";
108
+ readonly agentId: AgentId;
109
+ };
110
+ /** One MCP server entry observed on one physical configuration surface. */
111
+ export interface McpConfigOccurrence {
112
+ readonly _tag: "mcp-config";
113
+ readonly scope: Scope;
114
+ readonly surface: McpConfigSurface;
115
+ readonly name: ExtensionName;
116
+ readonly contentLocation: AbsolutePath;
117
+ readonly config: Readonly<Record<string, unknown>>;
118
+ }
119
+ /**
120
+ * One agent-native settings file observed on disk (e.g.,
121
+ * `.claude/settings.json`).
122
+ *
123
+ * `contentLocation` is the absolute path to the settings file. There is no
124
+ * "name" in the subject sense — the file itself is the materialization, keyed
125
+ * by `agentId`.
126
+ */
127
+ export interface AgentSettingsOccurrence {
128
+ readonly _tag: "agent-settings";
129
+ readonly scope: Scope;
130
+ readonly agentId: AgentId;
131
+ readonly contentLocation: AbsolutePath;
132
+ }
133
+ /**
134
+ * Stable occurrence identity tuple. The four-part shape mirrors the spec's
135
+ * "Actual entries carry stable occurrence identity" requirement:
136
+ * `(scope, type | agentId, origin, contentLocation)`. `subjectKey` is the
137
+ * subject discriminator — extension type for extension occurrences, agent id
138
+ * for agent occurrences.
139
+ */
140
+ export interface OccurrenceIdentity {
141
+ readonly scope: Scope;
142
+ readonly subjectKey: string;
143
+ readonly origin: string;
144
+ readonly contentLocation: string;
145
+ }
146
+ /**
147
+ * All occurrence shapes scanners emit. The discriminator on `_tag` lets
148
+ * downstream subject modules narrow without a centralized origin union.
149
+ */
150
+ export type ScannerOccurrence = CanonicalExtensionOccurrence | AgentDirOccurrence | McpConfigOccurrence | AgentSettingsOccurrence;
151
+ /**
152
+ * Compute the identity tuple for one occurrence. Two scanner paths observing
153
+ * the same physical occurrence produce equal identities; distinct physical
154
+ * paths under the same name produce different identities.
155
+ */
156
+ export declare const occurrenceIdentity: (occurrence: ScannerOccurrence) => OccurrenceIdentity;
157
+ /**
158
+ * Serialize an `OccurrenceIdentity` to a stable string key. Two occurrences
159
+ * with identical identity tuples produce equal strings; differences in any
160
+ * tuple field produce different strings. The encoding uses a delimiter that
161
+ * cannot appear in a relative path or origin tag.
162
+ */
163
+ export declare const occurrenceIdentityKey: (id: OccurrenceIdentity) => string;
164
+ /**
165
+ * Convenience: collapse an occurrence array to one entry per identity. The
166
+ * first occurrence with a given identity wins; subsequent observations are
167
+ * dropped. Stable ordering: input order is preserved for first-seen entries.
168
+ */
169
+ export declare const dedupeByIdentity: <T extends ScannerOccurrence>(occurrences: ReadonlyArray<T>) => ReadonlyArray<T>;
170
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Shared scanner types: occurrence shape, common per-occurrence fields, and
3
+ * scanner-tier origin discriminators.
4
+ *
5
+ * Per Decision 5 of the workspace read-model design, scanner output is
6
+ * occurrence-shaped (`ReadonlyArray<Occurrence>`) — one entry per observable
7
+ * physical materialization. There is no central `DetectionOrigin` union; each
8
+ * scanner exports the discriminator it observes, and per-subject modules in
9
+ * Phase 7 map these scanner-tier discriminators into subject-specific origin
10
+ * unions.
11
+ *
12
+ * Identity of an actual entry is `(scope, type | agentId, origin, contentLocation)`.
13
+ * Two scanner paths observing the same physical occurrence collapse to one
14
+ * entry; two distinct physical paths under the same name produce two entries
15
+ * with different identities.
16
+ */
17
+ import * as Option from "effect/Option";
18
+ /**
19
+ * Compute the identity tuple for one occurrence. Two scanner paths observing
20
+ * the same physical occurrence produce equal identities; distinct physical
21
+ * paths under the same name produce different identities.
22
+ */
23
+ export const occurrenceIdentity = (occurrence) => {
24
+ switch (occurrence._tag) {
25
+ case "canonical-extension":
26
+ return {
27
+ scope: occurrence.scope,
28
+ subjectKey: occurrence.type,
29
+ origin: occurrence.origin,
30
+ contentLocation: occurrence.contentLocation,
31
+ };
32
+ case "agent-dir":
33
+ return {
34
+ scope: occurrence.scope,
35
+ subjectKey: `${occurrence.type}:${occurrence.agentId}`,
36
+ origin: `agent-dir:${occurrence.agentId}`,
37
+ contentLocation: occurrence.contentLocation,
38
+ };
39
+ case "mcp-config":
40
+ return occurrence.surface._tag === "shared"
41
+ ? {
42
+ scope: occurrence.scope,
43
+ subjectKey: "mcp-server",
44
+ origin: "shared-mcp-config",
45
+ contentLocation: `${occurrence.contentLocation}#${occurrence.name}`,
46
+ }
47
+ : {
48
+ scope: occurrence.scope,
49
+ subjectKey: `mcp-server:${occurrence.surface.agentId}`,
50
+ origin: `agent-mcp-config:${occurrence.surface.agentId}`,
51
+ contentLocation: `${occurrence.contentLocation}#${occurrence.name}`,
52
+ };
53
+ case "agent-settings":
54
+ return {
55
+ scope: occurrence.scope,
56
+ subjectKey: `agent-settings:${occurrence.agentId}`,
57
+ origin: `agent-settings:${occurrence.agentId}`,
58
+ contentLocation: occurrence.contentLocation,
59
+ };
60
+ }
61
+ };
62
+ /**
63
+ * Serialize an `OccurrenceIdentity` to a stable string key. Two occurrences
64
+ * with identical identity tuples produce equal strings; differences in any
65
+ * tuple field produce different strings. The encoding uses a delimiter that
66
+ * cannot appear in a relative path or origin tag.
67
+ */
68
+ export const occurrenceIdentityKey = (id) => `${id.scope}\u0000${id.subjectKey}\u0000${id.origin}\u0000${id.contentLocation}`;
69
+ /**
70
+ * Convenience: collapse an occurrence array to one entry per identity. The
71
+ * first occurrence with a given identity wins; subsequent observations are
72
+ * dropped. Stable ordering: input order is preserved for first-seen entries.
73
+ */
74
+ export const dedupeByIdentity = (occurrences) => {
75
+ const seen = new Set();
76
+ const out = [];
77
+ for (const occ of occurrences) {
78
+ const key = occurrenceIdentityKey(occurrenceIdentity(occ));
79
+ if (seen.has(key))
80
+ continue;
81
+ seen.add(key);
82
+ out.push(occ);
83
+ }
84
+ return out;
85
+ };
86
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1,103 @@
1
+ /** Per-scope read-only workspace model factory and configuration. */
2
+ import * as Brand from "effect/Brand";
3
+ import * as ServiceMap from "effect/Context";
4
+ import * as Effect from "effect/Effect";
5
+ import * as FileSystem from "effect/FileSystem";
6
+ import * as Option from "effect/Option";
7
+ import * as Path from "effect/Path";
8
+ import { type Handle } from "@agentxm/extension-model/unstable/extensions/handle";
9
+ import type { SourceHostConfig } from "../../settings/schema.js";
10
+ import { type AbsolutePath } from "@agentxm/extension-model/unstable/path-types";
11
+ import { AgentRootResolver } from "./agent-root-resolver.js";
12
+ import { type ScopedAgentsApi } from "./agents/index.js";
13
+ import { type Warning } from "./diagnostics.js";
14
+ import { WorkspaceRootEscape, type LockfileIoError, type SettingsIoError, type SettingsReadError } from "./errors.js";
15
+ import { type HookExtensionsApi, type KnowledgeExtensionsApi, type McpServerExtensionsApi, type PackExtensionsApi, type RuleExtensionsApi, type SkillExtensionsApi, type SubagentExtensionsApi } from "./extensions/index.js";
16
+ import { type CanonicalExtensionOccurrence } from "./scanners/index.js";
17
+ import { type RawSourceBytes, type ScopedStateLoaders } from "./state.js";
18
+ import type { Scope } from "./types.js";
19
+ export type { RawSourceBytes } from "./state.js";
20
+ /** Scoped state cells; `raw(source)` reads cached bytes (absent → `Option.none`). */
21
+ export interface ScopedStateApi {
22
+ readonly settings: ScopedStateLoaders["settings"];
23
+ readonly lockfile: ScopedStateLoaders["lockfile"];
24
+ readonly raw: (source: "settings" | "lockfile") => Effect.Effect<Option.Option<RawSourceBytes>, SettingsIoError | LockfileIoError>;
25
+ }
26
+ /** Scoped source-host views over the cached settings loader. */
27
+ export interface ScopedSourceHostsApi {
28
+ readonly declared: Effect.Effect<ReadonlyArray<SourceHostConfig>, SettingsReadError>;
29
+ readonly effective: Effect.Effect<ReadonlyArray<SourceHostConfig>, SettingsReadError>;
30
+ readonly registryHosts: Effect.Effect<ReadonlyArray<Extract<SourceHostConfig, {
31
+ type: "registry";
32
+ }>>, SettingsReadError>;
33
+ readonly byName: (name: string) => Effect.Effect<Option.Option<SourceHostConfig>, SettingsReadError>;
34
+ }
35
+ /** Scoped owner view over the cached settings loader (declared owner; no fallback). */
36
+ export type ScopedOwnerApi = Effect.Effect<Option.Option<Handle>, SettingsReadError>;
37
+ /**
38
+ * Workspace read model for a single scope (project or user).
39
+ *
40
+ * Returned by {@link makeWorkspaceReadModel}. Each invocation builds its own
41
+ * scoped cells; cells cache for the lifetime of the returned value.
42
+ */
43
+ export interface WorkspaceReadModel {
44
+ readonly scope: Scope;
45
+ readonly skills: SkillExtensionsApi;
46
+ readonly mcpServers: McpServerExtensionsApi;
47
+ readonly subagents: SubagentExtensionsApi;
48
+ readonly rules: RuleExtensionsApi;
49
+ readonly hooks: HookExtensionsApi;
50
+ readonly knowledge: KnowledgeExtensionsApi;
51
+ readonly packs: PackExtensionsApi;
52
+ readonly agents: ScopedAgentsApi;
53
+ readonly state: ScopedStateApi;
54
+ readonly sourceHosts: ScopedSourceHostsApi;
55
+ readonly owner: ScopedOwnerApi;
56
+ readonly diagnostics: Effect.Effect<ReadonlyArray<Warning>>;
57
+ readonly canonicalExtensions: Effect.Effect<ReadonlyArray<CanonicalExtensionOccurrence>>;
58
+ }
59
+ /**
60
+ * The read-model family that carries each catalog extension type, or `null`
61
+ * where no family exists yet.
62
+ *
63
+ * Total by construction: a new extension type fails compile here until its
64
+ * read-model coverage is decided. The parity conformance suite reads this map
65
+ * to check the read-model obligation, and every `null` must be matched by a
66
+ * ledger row.
67
+ *
68
+ * @experimental This API is unstable and may change without notice.
69
+ */
70
+ export declare const READ_MODEL_EXTENSION_FAMILY_BY_TYPE: {
71
+ readonly skill: "skills";
72
+ readonly "mcp-server": "mcpServers";
73
+ readonly subagent: "subagents";
74
+ readonly rule: "rules";
75
+ readonly hook: "hooks";
76
+ readonly knowledge: "knowledge";
77
+ };
78
+ /** Configuration the factory requires beyond `FileSystem` and `Path`. */
79
+ export interface WorkspaceReadModelConfigService {
80
+ readonly projectRoot: AbsolutePath;
81
+ readonly userHome: AbsolutePath;
82
+ readonly allowedRoot: AbsolutePath;
83
+ }
84
+ declare const WorkspaceReadModelConfig_base: ServiceMap.ServiceClass<WorkspaceReadModelConfig, "@agentxm/workspace-state/workspace/read-model/service/WorkspaceReadModelConfig", WorkspaceReadModelConfigService>;
85
+ /** Service tag for the {@link WorkspaceReadModelConfigService} the factory requires. */
86
+ export declare class WorkspaceReadModelConfig extends WorkspaceReadModelConfig_base {
87
+ }
88
+ /** Workspace root that has been validated against `allowedRoot`. */
89
+ export type ResolvedWorkspaceRoot = string & Brand.Brand<"ResolvedWorkspaceRoot">;
90
+ /**
91
+ * Build a {@link WorkspaceReadModel} for the requested scope.
92
+ *
93
+ * Each invocation produces a fresh instance with its own cached cells; call
94
+ * once at the command boundary and pass the value inward. Callers that need
95
+ * both scopes invoke the factory twice.
96
+ *
97
+ * Cross-scope state (the agent-root resolver and its collision warnings) is
98
+ * supplied by {@link AgentRootResolver}, which must be provided in the
99
+ * environment so the same warnings flow into every scope built against the
100
+ * same layer.
101
+ */
102
+ export declare const makeWorkspaceReadModel: (scope: Scope) => Effect.Effect<WorkspaceReadModel, WorkspaceRootEscape, FileSystem.FileSystem | Path.Path | WorkspaceReadModelConfig | AgentRootResolver>;
103
+ //# sourceMappingURL=service.d.ts.map