@agentxm/workspace-projection 0.29.2 → 0.29.4

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.
@@ -10,6 +10,7 @@
10
10
  import * as Effect from "effect/Effect";
11
11
  import * as FileSystem from "effect/FileSystem";
12
12
  import * as Path from "effect/Path";
13
+ import { type SkillEntry, type WorkspaceLayout } from "@agentxm/workspace-state";
13
14
  import type { PerAgentType } from "@agentxm/extension-model/unstable/extensions/common";
14
15
  import type { WorkspaceScope } from "@agentxm/extension-model/unstable/workspace-scope";
15
16
  import { CodingAgentRepository } from "./agents/coding-agent-repository.js";
@@ -36,6 +37,11 @@ export interface ObserveAgentOutputsArgs {
36
37
  readonly desiredAgentIds: ReadonlySet<string>;
37
38
  readonly expectedNames: Readonly<Record<PerAgentType, ReadonlySet<string>>>;
38
39
  readonly skillOwnershipRoots: ReadonlyArray<string>;
40
+ /** Local declarations, including disabled skills; independent of graph resolution. */
41
+ readonly authoredSkills: {
42
+ readonly layout: WorkspaceLayout;
43
+ readonly entries: Readonly<Record<string, SkillEntry>>;
44
+ };
39
45
  }
40
46
  /** Observe every known agent output without writing to the workspace. */
41
47
  export declare const observeAgentOutputs: (args: ObserveAgentOutputsArgs) => Effect.Effect<AgentOutputInventory, never, CodingAgentRepository | FileSystem.FileSystem | Path.Path>;
@@ -55,5 +61,6 @@ export declare const observeWorkspaceOwnershipIssues: (args: {
55
61
  readonly scope: WorkspaceScope;
56
62
  readonly configuredAgentIds: ReadonlySet<string>;
57
63
  readonly skillOwnershipRoots: ReadonlyArray<string>;
64
+ readonly authoredSkills: ObserveAgentOutputsArgs["authoredSkills"];
58
65
  }) => Effect.Effect<ReadonlyArray<WorkspaceOwnershipIssue>, never, CodingAgentRepository | FileSystem.FileSystem | Path.Path>;
59
66
  //# sourceMappingURL=agent-output-observation.d.ts.map
@@ -10,6 +10,8 @@
10
10
  import * as Effect from "effect/Effect";
11
11
  import * as FileSystem from "effect/FileSystem";
12
12
  import * as Path from "effect/Path";
13
+ import * as Option from "effect/Option";
14
+ import { resolveWorkspaceExtensionRef, } from "@agentxm/workspace-state";
13
15
  import { AGENTS as CAPABILITY_AGENTS } from "@agentxm/extension-model/unstable/agent-capabilities";
14
16
  import { CodingAgentRepository } from "./agents/coding-agent-repository.js";
15
17
  import { extensionNameFromFilename, hasAxmManagedMarker, safeReadDirectory, safeReadFileString, } from "./managed-file-discovery.js";
@@ -32,6 +34,26 @@ const groupContainers = (containers) => {
32
34
  }));
33
35
  };
34
36
  const containerIsDesired = (claimantAgentIds, desiredAgentIds) => claimantAgentIds.some((agentId) => desiredAgentIds.has(agentId));
37
+ /** A declared source package is not an agent output eligible for retirement. */
38
+ const isAuthoredSkillPackage = (args, artifactPath, name) => Effect.gen(function* () {
39
+ const path = yield* Path.Path;
40
+ const entry = args.entries[name];
41
+ if (args.layout.scope !== "project" ||
42
+ entry?.source !== "workspace" ||
43
+ entry.origin === "bundled" ||
44
+ artifactPath !== path.join(args.layout.authoredRoot("skill"), name))
45
+ return false;
46
+ // Failed validation supplies no exclusion: the output remains visible to
47
+ // ownership diagnostics and the package's own canonical-content diagnostics.
48
+ const source = yield* resolveWorkspaceExtensionRef({
49
+ settingsName: name,
50
+ source: entry.source,
51
+ expectedType: "skill",
52
+ layout: args.layout,
53
+ scope: args.layout.scope,
54
+ }).pipe(Effect.option);
55
+ return Option.isSome(source);
56
+ });
35
57
  /** Observe every known agent output without writing to the workspace. */
36
58
  export const observeAgentOutputs = (args) => Effect.gen(function* () {
37
59
  const fs = yield* FileSystem.FileSystem;
@@ -61,6 +83,8 @@ export const observeAgentOutputs = (args) => Effect.gen(function* () {
61
83
  else {
62
84
  const stat = yield* fs.stat(artifactPath).pipe(Effect.option);
63
85
  if (stat._tag === "Some" && stat.value.type === "Directory") {
86
+ if (yield* isAuthoredSkillPackage(args.authoredSkills, artifactPath, entry))
87
+ continue;
64
88
  const content = yield* safeReadFileString(fs, path.join(artifactPath, "SKILL.md"));
65
89
  if (hasAxmManagedMarker(content))
66
90
  proof = "managed-banner";
@@ -214,6 +238,7 @@ export const observeWorkspaceOwnershipIssues = (args) => observeAgentOutputs({
214
238
  hook: new Set(),
215
239
  },
216
240
  skillOwnershipRoots: args.skillOwnershipRoots,
241
+ authoredSkills: args.authoredSkills,
217
242
  }).pipe(Effect.map((observed) => observed.unownedFootprints.map((output) => ({
218
243
  kind: output.extensionType === "hook"
219
244
  ? "hook-ownership-ambiguous"
@@ -9,7 +9,7 @@ import * as Effect from "effect/Effect";
9
9
  import { type McpConfigTarget } from "@agentxm/extension-model/unstable/agent-capabilities";
10
10
  import { type McpInspectionError } from "./errors.js";
11
11
  import type { McpServerEntry } from "@agentxm/workspace-state";
12
- export type AgentMcpInspectionStatus = "unsupported" | "absent" | "match" | "drift" | "unmanaged";
12
+ export type AgentMcpInspectionStatus = "unsupported" | "blocked" | "absent" | "match" | "drift" | "unmanaged";
13
13
  export interface AgentMcpServerInspection {
14
14
  readonly agentId: string;
15
15
  readonly path: string;
@@ -47,6 +47,8 @@ export declare const inspectMcpServerAcrossAgents: (args: {
47
47
  readonly agentIds: ReadonlyArray<string>;
48
48
  readonly serverName: string;
49
49
  readonly entry: McpServerEntry;
50
+ readonly canonicalPaths?: ReadonlyArray<string>;
51
+ readonly state?: "projected" | "current";
50
52
  }) => Effect.Effect<ReadonlyArray<AgentMcpServerInspection>, McpInspectionError, FileSystem.FileSystem | Path.Path>;
51
53
  export declare const collectManagedAgentMcpServers: (args: CollectManagedAgentMcpServersArgs) => Effect.Effect<ReadonlyArray<ManagedAgentMcpServer>, McpInspectionError, FileSystem.FileSystem | Path.Path>;
52
54
  //# sourceMappingURL=inspection.d.ts.map
@@ -10,7 +10,8 @@ import * as Option from "effect/Option";
10
10
  import { parse } from "jsonc-parser";
11
11
  import { CONFIGURABLE_AGENTS_BY_ID, } from "@agentxm/extension-model/unstable/agent-capabilities";
12
12
  import {} from "./errors.js";
13
- import { groupConfiguredMcpTargets, inferInlineRemoteTransport, isAxmManagedMcpEntry, managedKeyedBlockNames, managedYamlNames as readManagedYamlNames, McpConfigInvalid, McpConfigIoFailed, McpDefinitionInvalid, McpOwnershipMarkerInvalid, McpSharedTargetConflict, parseTomlValue, projectExpectedEntry, readYamlEntry, reconcileKeyedBlock, resolveAgentMcpConfigTargetPath, resolveSharedMcpTarget, stringifyTomlKey, } from "@agentxm/agent-integration";
13
+ import { decodeMcpServerManifestAt, groupConfiguredMcpTargets, inferInlineRemoteTransport, isAxmManagedMcpEntry, managedKeyedBlockNames, managedYamlNames as readManagedYamlNames, McpConfigInvalid, McpConfigIoFailed, McpDefinitionInvalid, McpOwnershipMarkerInvalid, McpSharedTargetConflict, parseTomlValue, projectExpectedEntry, readYamlEntry, reconcileKeyedBlock, resolveAgentMcpConfigTargetPath, resolveMcpServer, resolveSharedMcpTarget, stringifyTomlKey, validateManifestMcpServerTargets, } from "@agentxm/agent-integration";
14
+ import { MCP_SERVER_MANIFEST_FILENAME } from "@agentxm/extension-model/unstable/mcps/manifest-schema";
14
15
  import { diffAgentEntry } from "./drift.js";
15
16
  const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
16
17
  const hasMcpConfig = (capability) => capability.axm.writer !== null && "transports" in capability.native;
@@ -198,16 +199,17 @@ const inspectAgentMcpServerInternal = (args) => Effect.gen(function* () {
198
199
  reason: `${args.agentId} has no ${args.scope} MCP config target`,
199
200
  };
200
201
  }
201
- const projected = args.entry.command === undefined && args.entry.url === undefined
202
- ? { _tag: "managed" }
203
- : projectExpectedEntry({
204
- serverName: args.serverName,
205
- entry: args.entry,
206
- stdio: config.stdio,
207
- remote: config.remote,
208
- activationField: config.activationField,
209
- envExpansion: capability.native.mcpEnvExpansion,
210
- });
202
+ const projected = args.projection?.expected ??
203
+ (args.entry.command === undefined && args.entry.url === undefined
204
+ ? { _tag: "managed" }
205
+ : projectExpectedEntry({
206
+ serverName: args.serverName,
207
+ entry: args.entry,
208
+ stdio: config.stdio,
209
+ remote: config.remote,
210
+ activationField: config.activationField,
211
+ envExpansion: capability.native.mcpEnvExpansion,
212
+ }));
211
213
  const absolutePath = yield* resolveAgentMcpConfigTargetPath(args.workspaceRoot, target);
212
214
  if (projected._tag === "unsupported") {
213
215
  return {
@@ -220,6 +222,17 @@ const inspectAgentMcpServerInternal = (args) => Effect.gen(function* () {
220
222
  reason: `${args.agentId} ${projected.reason}`,
221
223
  };
222
224
  }
225
+ if (args.state === "projected") {
226
+ return {
227
+ agentId: args.agentId,
228
+ path: target.path,
229
+ absolutePath,
230
+ status: "match",
231
+ fields: [],
232
+ warnings: args.projection?.warnings ?? (projected._tag === "projected" ? projected.warnings : []),
233
+ ...(projected._tag === "projected" ? { expected: projected.entry } : {}),
234
+ };
235
+ }
223
236
  const actual = yield* inspectActual({
224
237
  target,
225
238
  configPath: absolutePath,
@@ -233,7 +246,7 @@ const inspectAgentMcpServerInternal = (args) => Effect.gen(function* () {
233
246
  absolutePath,
234
247
  status: actual.status,
235
248
  fields: actual.fields,
236
- warnings: projected._tag === "projected" ? projected.warnings : [],
249
+ warnings: args.projection?.warnings ?? (projected._tag === "projected" ? projected.warnings : []),
237
250
  ...(projected._tag === "projected" ? { expected: projected.entry } : {}),
238
251
  ...(actual.actual === undefined ? {} : { actual: actual.actual }),
239
252
  };
@@ -255,6 +268,140 @@ const inspectionTransportForEntry = (entry) => {
255
268
  };
256
269
  export const inspectMcpServerAcrossAgents = (args) => Effect.gen(function* () {
257
270
  if (args.entry.command === undefined && args.entry.url === undefined) {
271
+ const fs = yield* FileSystem.FileSystem;
272
+ const path = yield* Path.Path;
273
+ let canonicalPath;
274
+ for (const candidate of args.canonicalPaths ?? []) {
275
+ const resolved = path.resolve(args.workspaceRoot, candidate);
276
+ const manifestPath = path.join(resolved, MCP_SERVER_MANIFEST_FILENAME);
277
+ if (yield* fs.exists(manifestPath).pipe(Effect.catch(() => Effect.succeed(false)))) {
278
+ canonicalPath = resolved;
279
+ break;
280
+ }
281
+ }
282
+ if (canonicalPath !== undefined) {
283
+ const manifest = yield* decodeMcpServerManifestAt(path.join(canonicalPath, MCP_SERVER_MANIFEST_FILENAME));
284
+ yield* validateManifestMcpServerTargets({
285
+ manifest,
286
+ agentIds: args.agentIds,
287
+ scope: args.scope,
288
+ serverName: args.serverName,
289
+ values: args.entry.env,
290
+ enabled: true,
291
+ });
292
+ const projectedTargets = new Map();
293
+ for (const group of groupConfiguredMcpTargets({
294
+ agentIds: args.agentIds,
295
+ scope: args.scope,
296
+ })) {
297
+ const firstRunnable = group.members
298
+ .flatMap((member) => {
299
+ if (!isCapabilityAgentId(member.agentId))
300
+ return [];
301
+ const capability = CONFIGURABLE_AGENTS_BY_ID[member.agentId].capabilities["mcp-server"];
302
+ if (!hasMcpConfig(capability))
303
+ return [];
304
+ const resolution = resolveMcpServer({
305
+ manifest,
306
+ localName: args.serverName,
307
+ capability,
308
+ values: args.entry.env,
309
+ enabled: true,
310
+ });
311
+ return resolution._tag === "resolved" || resolution._tag === "needs-input"
312
+ ? [resolution]
313
+ : [];
314
+ })
315
+ .at(0);
316
+ if (firstRunnable === undefined)
317
+ continue;
318
+ const shared = resolveSharedMcpTarget({
319
+ members: group.members,
320
+ transport: firstRunnable.transport,
321
+ });
322
+ if (shared._tag === "conflict") {
323
+ return yield* new McpSharedTargetConflict({ reason: shared.reason });
324
+ }
325
+ for (const member of group.members) {
326
+ projectedTargets.set(member.agentId, { config: shared.config, target: member.target });
327
+ }
328
+ }
329
+ return yield* Effect.forEach(args.agentIds, (agentId) => Effect.gen(function* () {
330
+ if (!isCapabilityAgentId(agentId)) {
331
+ return yield* inspectAgentMcpServer({ ...args, agentId });
332
+ }
333
+ const capability = CONFIGURABLE_AGENTS_BY_ID[agentId].capabilities["mcp-server"];
334
+ if (!hasMcpConfig(capability)) {
335
+ return yield* inspectAgentMcpServer({ ...args, agentId });
336
+ }
337
+ const nativeConfig = capability.axm.writer.config;
338
+ const projection = projectedTargets.get(agentId);
339
+ const config = projection?.config ?? nativeConfig;
340
+ const target = projection?.target ??
341
+ nativeConfig.targets.find(({ scope }) => scope === args.scope);
342
+ if (target === undefined) {
343
+ return yield* inspectAgentMcpServer({ ...args, agentId });
344
+ }
345
+ const absolutePath = yield* resolveAgentMcpConfigTargetPath(args.workspaceRoot, target);
346
+ const projectedCapability = {
347
+ ...capability,
348
+ axm: { ...capability.axm, writer: { config } },
349
+ };
350
+ const resolution = resolveMcpServer({
351
+ manifest,
352
+ localName: args.serverName,
353
+ capability: projectedCapability,
354
+ values: args.entry.env,
355
+ enabled: true,
356
+ });
357
+ if (resolution._tag === "no-distribution" || resolution._tag === "nothing-runnable") {
358
+ return {
359
+ agentId,
360
+ path: target.path,
361
+ absolutePath,
362
+ status: "unsupported",
363
+ fields: [],
364
+ warnings: [],
365
+ reason: resolution.reason,
366
+ };
367
+ }
368
+ if (resolution._tag === "needs-input") {
369
+ return {
370
+ agentId,
371
+ path: target.path,
372
+ absolutePath,
373
+ status: "blocked",
374
+ fields: [],
375
+ warnings: resolution.warnings,
376
+ reason: resolution.warnings.join("; "),
377
+ };
378
+ }
379
+ if (args.state === "projected") {
380
+ return {
381
+ agentId,
382
+ path: target.path,
383
+ absolutePath,
384
+ status: "match",
385
+ fields: [],
386
+ warnings: resolution.warnings,
387
+ expected: resolution.entry,
388
+ };
389
+ }
390
+ return yield* inspectAgentMcpServerInternal({
391
+ workspaceRoot: args.workspaceRoot,
392
+ scope: args.scope,
393
+ agentId,
394
+ serverName: args.serverName,
395
+ entry: args.entry,
396
+ projection: {
397
+ config,
398
+ target,
399
+ expected: { _tag: "projected", entry: resolution.entry, warnings: [] },
400
+ warnings: resolution.warnings,
401
+ },
402
+ });
403
+ }), { concurrency: "unbounded" });
404
+ }
258
405
  return yield* Effect.forEach(args.agentIds, (agentId) => inspectAgentMcpServer({
259
406
  workspaceRoot: args.workspaceRoot,
260
407
  scope: args.scope,
@@ -278,6 +425,7 @@ export const inspectMcpServerAcrossAgents = (args) => Effect.gen(function* () {
278
425
  agentId: member.agentId,
279
426
  serverName: args.serverName,
280
427
  entry: args.entry,
428
+ ...(args.state === undefined ? {} : { state: args.state }),
281
429
  projection: {
282
430
  config: resolution.config,
283
431
  target: member.target,
@@ -290,12 +438,13 @@ export const inspectMcpServerAcrossAgents = (args) => Effect.gen(function* () {
290
438
  return yield* Effect.forEach(args.agentIds, (agentId) => {
291
439
  const resolved = byAgentId.get(agentId);
292
440
  return resolved === undefined
293
- ? inspectAgentMcpServer({
441
+ ? inspectAgentMcpServerInternal({
294
442
  workspaceRoot: args.workspaceRoot,
295
443
  scope: args.scope,
296
444
  agentId,
297
445
  serverName: args.serverName,
298
446
  entry: args.entry,
447
+ ...(args.state === undefined ? {} : { state: args.state }),
299
448
  })
300
449
  : Effect.succeed(resolved);
301
450
  }, { concurrency: "unbounded" });
package/package.json CHANGED
@@ -4,12 +4,12 @@
4
4
  "url": "https://github.com/agentxm/axm/issues"
5
5
  },
6
6
  "dependencies": {
7
- "@agentxm/agent-integration": "^0.29.2",
8
- "@agentxm/extension-content": "^0.29.2",
9
- "@agentxm/extension-model": "^0.29.2",
10
- "@agentxm/extension-sources": "^0.29.2",
11
- "@agentxm/workspace-state": "^0.29.2",
12
- "@agentxm/workspace-transactions": "^0.29.2",
7
+ "@agentxm/agent-integration": "^0.29.4",
8
+ "@agentxm/extension-content": "^0.29.4",
9
+ "@agentxm/extension-model": "^0.29.4",
10
+ "@agentxm/extension-sources": "^0.29.4",
11
+ "@agentxm/workspace-state": "^0.29.4",
12
+ "@agentxm/workspace-transactions": "^0.29.4",
13
13
  "effect": "4.0.0-rc.115",
14
14
  "jsonc-parser": "^3.3.1",
15
15
  "semver": "^7.8.5"
@@ -64,5 +64,5 @@
64
64
  },
65
65
  "sideEffects": false,
66
66
  "type": "module",
67
- "version": "0.29.2"
67
+ "version": "0.29.4"
68
68
  }