@agentxm/extension-lifecycle 0.28.4 → 0.28.6
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.
- package/dist/src/configured-entry-resolution.js +6 -3
- package/dist/src/hooks/manager.js +24 -12
- package/dist/src/index.d.ts +1 -3
- package/dist/src/index.js +1 -3
- package/dist/src/knowledge/manager.js +61 -16
- package/dist/src/mcps/manager.js +40 -6
- package/dist/src/mcps/operations/disable.js +6 -42
- package/dist/src/mcps/operations/enable.js +1 -34
- package/dist/src/mcps/operations/install.d.ts +22 -3
- package/dist/src/mcps/operations/install.js +112 -82
- package/dist/src/mcps/operations/uninstall.js +34 -15
- package/dist/src/registry-materialization.js +5 -0
- package/dist/src/rules/manager.js +16 -16
- package/dist/src/subagents/manager.js +48 -3
- package/dist/src/workflows/install-command/workflow.d.ts +1 -2
- package/dist/src/workflows/install-command/workflow.js +6 -6
- package/package.json +10 -8
- package/dist/src/resolution-progress.d.ts +0 -18
- package/dist/src/resolution-progress.js +0 -11
- package/dist/src/skills/operations/uninstall.d.ts +0 -40
- package/dist/src/skills/operations/uninstall.js +0 -157
|
@@ -122,7 +122,9 @@ export const resolveConfiguredRegistryEntry = (name, source, expectedType, relea
|
|
|
122
122
|
detail: `Configured ${expectedType} "${name}" uses a ${pluralType} Registry source`,
|
|
123
123
|
});
|
|
124
124
|
}
|
|
125
|
-
if (
|
|
125
|
+
if (expectedType !== "mcp-server" &&
|
|
126
|
+
parsedPattern?.name !== undefined &&
|
|
127
|
+
parsedPattern.name !== name) {
|
|
126
128
|
return yield* new ExtensionLifecycleFailed({
|
|
127
129
|
category: "validation",
|
|
128
130
|
detail: `Configured ${expectedType} "${name}" points to Registry extension "${parsedPattern.name}"`,
|
|
@@ -136,13 +138,14 @@ export const resolveConfiguredRegistryEntry = (name, source, expectedType, relea
|
|
|
136
138
|
});
|
|
137
139
|
}
|
|
138
140
|
const versionRange = Option.fromUndefinedOr(parsedPattern?.versionRange);
|
|
141
|
+
const registryName = expectedType === "mcp-server" ? (parsedPattern?.name ?? name) : name;
|
|
139
142
|
const workspace = yield* WorkspaceMutations;
|
|
140
143
|
const acceptedRef = yield* acceptedResolutionRef({
|
|
141
144
|
workspace,
|
|
142
145
|
type: expectedType,
|
|
143
146
|
name,
|
|
144
147
|
});
|
|
145
|
-
const accepted = Option.flatMap(acceptedRef, (ref) => ref.refType === "registry" && ref.owner === owner && ref.name ===
|
|
148
|
+
const accepted = Option.flatMap(acceptedRef, (ref) => ref.refType === "registry" && ref.owner === owner && ref.name === registryName
|
|
146
149
|
? Option.some({
|
|
147
150
|
version: ref.version,
|
|
148
151
|
publisherBindingId: ref.publisherBindingId,
|
|
@@ -150,7 +153,7 @@ export const resolveConfiguredRegistryEntry = (name, source, expectedType, relea
|
|
|
150
153
|
: Option.none());
|
|
151
154
|
const providers = yield* SourceHostProviders;
|
|
152
155
|
const resolution = yield* providers.resolveNamedRegistry(resolvedSource, {
|
|
153
|
-
name,
|
|
156
|
+
name: registryName,
|
|
154
157
|
type: expectedType,
|
|
155
158
|
owner,
|
|
156
159
|
versionRange,
|
|
@@ -11,7 +11,7 @@ import * as Option from "effect/Option";
|
|
|
11
11
|
import * as Path from "effect/Path";
|
|
12
12
|
import * as Schema from "effect/Schema";
|
|
13
13
|
import * as Semaphore from "effect/Semaphore";
|
|
14
|
-
import { HookConfigInvalid, HookDefinitionInvalid, HookInstallStateMissing, HookIoFailed, activeContributors, applyProjectionPlans, planAggregateProjection, runWithTransientFileBackup, reconcileManagedRegionFile, managedHookCommands, readManagedHookCommands, updateHooksJson, evaluateHookAgentOutcome, WriteBackupRetained, resolveInstructionsConfig, } from "@agentxm/extension-workspace";
|
|
14
|
+
import { HookConfigInvalid, HookDefinitionInvalid, HookInstallStateMissing, HookIoFailed, activeContributors, applyProjectionPlans, planAggregateProjection, runWithTransientFileBackup, reconcileManagedRegionFile, projectionGeneration, managedHookCommands, readManagedHookCommands, updateHooksJson, evaluateHookAgentOutcome, WriteBackupRetained, resolveInstructionsConfig, } from "@agentxm/extension-workspace";
|
|
15
15
|
import { AGENTS as CAPABILITY_AGENTS, installable, } from "@agentxm/extension-model/unstable/agent-capabilities";
|
|
16
16
|
import { computePackageContentHash } from "@agentxm/workspace-state";
|
|
17
17
|
import { computeMaterializedTreeIntegrity } from "@agentxm/workspace-state";
|
|
@@ -437,12 +437,25 @@ export const HookManagerLive = Layer.effect(HookManager, Effect.gen(function* ()
|
|
|
437
437
|
const rendered = input.contributors
|
|
438
438
|
.map((hook) => `### ${hook.manifest.title ?? hook.name}\n\nFor agents without a usable native hook mapping (${hook.fallbackAgentIds.join(", ")}), treat this as a managed advisory rule. After the matching lifecycle event (${hook.manifest.bindings.map((binding) => binding.on).join(", ")}), run \`${hook.command}\` and address any findings before continuing.`)
|
|
439
439
|
.join("\n\n");
|
|
440
|
+
const generation = projectionGeneration([
|
|
441
|
+
"hook-fallback-region-v1",
|
|
442
|
+
target.workspaceRelative,
|
|
443
|
+
HOOK_FALLBACKS_REGION_OWNER,
|
|
444
|
+
...input.contributors.flatMap((contributor) => [
|
|
445
|
+
contributor.name,
|
|
446
|
+
contributor.marker,
|
|
447
|
+
contributor.command,
|
|
448
|
+
JSON.stringify(contributor.fallbackAgentIds),
|
|
449
|
+
JSON.stringify(contributor.manifest),
|
|
450
|
+
]),
|
|
451
|
+
]);
|
|
440
452
|
const { changed, observedRegion } = yield* provide(reconcileManagedRegionFile({
|
|
441
453
|
targetPath: target.targetPath,
|
|
442
454
|
displayPath: target.workspaceRelative,
|
|
443
455
|
region: HOOK_FALLBACKS_REGION,
|
|
444
456
|
owner: HOOK_FALLBACKS_REGION_OWNER,
|
|
445
457
|
rendered,
|
|
458
|
+
generation,
|
|
446
459
|
...(options?.dryRun === undefined ? {} : { dryRun: options.dryRun }),
|
|
447
460
|
unsupportedTargetDetail: `Hook fallback target does not support managed regions: ${target.workspaceRelative}`,
|
|
448
461
|
}));
|
|
@@ -464,12 +477,6 @@ export const HookManagerLive = Layer.effect(HookManager, Effect.gen(function* ()
|
|
|
464
477
|
present: Option.isSome(observedRegion),
|
|
465
478
|
current: !changed,
|
|
466
479
|
expectedContributors: input.contributors.map(({ marker }) => marker),
|
|
467
|
-
observedContributors: Option.match(observedRegion, {
|
|
468
|
-
onNone: () => [],
|
|
469
|
-
onSome: (region) => input.contributors
|
|
470
|
-
.filter(({ command }) => region.includes(command))
|
|
471
|
-
.map(({ marker }) => marker),
|
|
472
|
-
}),
|
|
473
480
|
},
|
|
474
481
|
};
|
|
475
482
|
});
|
|
@@ -532,11 +539,16 @@ export const HookManagerLive = Layer.effect(HookManager, Effect.gen(function* ()
|
|
|
532
539
|
.map(({ agentId }) => agentId);
|
|
533
540
|
return fallbackAgentIds.length === 0 ? [] : [{ ...contributor, fallbackAgentIds }];
|
|
534
541
|
});
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
(
|
|
539
|
-
|
|
542
|
+
// Every reachable Hook contributor is decided from desired state
|
|
543
|
+
// alone, so hook units never exclude one.
|
|
544
|
+
const selectNative = (agentId) => () => Effect.succeed({
|
|
545
|
+
contributors: contributors.filter((contributor) => outcomes.some((outcome) => outcome.name === contributor.name &&
|
|
546
|
+
outcome.agentId === agentId &&
|
|
547
|
+
outcome.mechanism === "native" &&
|
|
548
|
+
(outcome.outcome === "projected" || outcome.outcome === "current"))),
|
|
549
|
+
exclusions: [],
|
|
550
|
+
});
|
|
551
|
+
const selectFallback = () => Effect.succeed({ contributors: fallbackContributors, exclusions: [] });
|
|
540
552
|
const fallbackAgentIds = Array.from(new Set(fallbackContributors.flatMap(({ fallbackAgentIds }) => fallbackAgentIds)));
|
|
541
553
|
const materialization = {
|
|
542
554
|
agents: configuredAgents,
|
package/dist/src/index.d.ts
CHANGED
|
@@ -10,7 +10,6 @@
|
|
|
10
10
|
*/
|
|
11
11
|
export { ExtensionLifecycleFailed } from "./errors.js";
|
|
12
12
|
export { LifecycleFailureAdapter, withAdaptedStepFailures, type LifecycleFailureAdapterService, } from "./failure-adapter.js";
|
|
13
|
-
export { LifecycleResolutionProgress, type LifecycleResolutionProgressService, } from "./resolution-progress.js";
|
|
14
13
|
export { makeConfiguredReleaseAgeEvaluation, resolveConfiguredHook, resolveConfiguredKnowledge, resolveConfiguredMcpServer, resolveConfiguredPack, resolveConfiguredRegistryEntry, resolveConfiguredRule, resolveConfiguredSkill, resolveConfiguredSubagent, } from "./configured-entry-resolution.js";
|
|
15
14
|
export { CONFIGURED_ENTRY_RESOLUTION_TIMEOUT, withConfiguredEntryResolutionTimeout, } from "./resolution-timeout.js";
|
|
16
15
|
export { buildInstallCommandPlan, runInstallCommandWorkflow, type InstallExtensionCommandWorkflowActions, } from "./workflows/install-command/workflow.js";
|
|
@@ -20,10 +19,9 @@ export { ensureSkillAgentArtifact, removeSkillAgentArtifact } from "./skills/mat
|
|
|
20
19
|
export { getSkillDisplayName } from "./skills/utils.js";
|
|
21
20
|
export { computeSkillSourceHash, gitHostedSkillArtifactSource, installSkill, type InstallSkillOperation, type InstallSkillOperationArgs, } from "./skills/operations/install.js";
|
|
22
21
|
export type { InstallResult } from "./skills/operations/install-result.js";
|
|
23
|
-
export { uninstallSkill, type UninstallSkillOperation, type UninstallSkillOperationArgs, } from "./skills/operations/uninstall.js";
|
|
24
22
|
export { enableSkill, type EnableSkillOperation } from "./skills/operations/enable.js";
|
|
25
23
|
export { disableSkill, type DisableSkillOperation } from "./skills/operations/disable.js";
|
|
26
|
-
export { installMcpServer, type InstallMcpServerOperation, type InstallMcpServerOperationArgs, } from "./mcps/operations/install.js";
|
|
24
|
+
export { collectSecretInputNames, deleteMcpSecrets, installMcpServer, mcpSecretAccount, readMcpServerManifest, type InstallMcpServerOperation, type InstallMcpServerOperationArgs, } from "./mcps/operations/install.js";
|
|
27
25
|
export { uninstallMcpServer, type UninstallMcpServerOperation, type UninstallMcpServerOperationArgs, } from "./mcps/operations/uninstall.js";
|
|
28
26
|
export { enableMcpServer, type EnableMcpServerOperation } from "./mcps/operations/enable.js";
|
|
29
27
|
export { disableMcpServer, type DisableMcpServerOperation } from "./mcps/operations/disable.js";
|
package/dist/src/index.js
CHANGED
|
@@ -10,7 +10,6 @@
|
|
|
10
10
|
*/
|
|
11
11
|
export { ExtensionLifecycleFailed } from "./errors.js";
|
|
12
12
|
export { LifecycleFailureAdapter, withAdaptedStepFailures, } from "./failure-adapter.js";
|
|
13
|
-
export { LifecycleResolutionProgress, } from "./resolution-progress.js";
|
|
14
13
|
export { makeConfiguredReleaseAgeEvaluation, resolveConfiguredHook, resolveConfiguredKnowledge, resolveConfiguredMcpServer, resolveConfiguredPack, resolveConfiguredRegistryEntry, resolveConfiguredRule, resolveConfiguredSkill, resolveConfiguredSubagent, } from "./configured-entry-resolution.js";
|
|
15
14
|
export { CONFIGURED_ENTRY_RESOLUTION_TIMEOUT, withConfiguredEntryResolutionTimeout, } from "./resolution-timeout.js";
|
|
16
15
|
export { buildInstallCommandPlan, runInstallCommandWorkflow, } from "./workflows/install-command/workflow.js";
|
|
@@ -20,11 +19,10 @@ export { materializeRegistryPackage, materializeRegistryPackageWithTreeIntegrity
|
|
|
20
19
|
export { ensureSkillAgentArtifact, removeSkillAgentArtifact } from "./skills/materialization.js";
|
|
21
20
|
export { getSkillDisplayName } from "./skills/utils.js";
|
|
22
21
|
export { computeSkillSourceHash, gitHostedSkillArtifactSource, installSkill, } from "./skills/operations/install.js";
|
|
23
|
-
export { uninstallSkill, } from "./skills/operations/uninstall.js";
|
|
24
22
|
export { enableSkill } from "./skills/operations/enable.js";
|
|
25
23
|
export { disableSkill } from "./skills/operations/disable.js";
|
|
26
24
|
// MCP server lifecycle operations
|
|
27
|
-
export { installMcpServer, } from "./mcps/operations/install.js";
|
|
25
|
+
export { collectSecretInputNames, deleteMcpSecrets, installMcpServer, mcpSecretAccount, readMcpServerManifest, } from "./mcps/operations/install.js";
|
|
28
26
|
export { uninstallMcpServer, } from "./mcps/operations/uninstall.js";
|
|
29
27
|
export { enableMcpServer } from "./mcps/operations/enable.js";
|
|
30
28
|
export { disableMcpServer } from "./mcps/operations/disable.js";
|
|
@@ -11,7 +11,7 @@ import * as Schema from "effect/Schema";
|
|
|
11
11
|
import * as Scope from "effect/Scope";
|
|
12
12
|
import * as Result from "effect/Result";
|
|
13
13
|
import { PlatformError } from "effect/PlatformError";
|
|
14
|
-
import { KnowledgeDefinitionInvalid, KnowledgeDesiredStateUnreconcilable, KnowledgeInstallStateMissing, KnowledgeIoFailed, KnowledgeObservableContractViolated, KnowledgeResolutionMissing, KnowledgeUnavailable, applyProjectionPlans, planAggregateProjection, requireCompleteGraph, KNOWLEDGE_REGION_OWNER, reconcileKnowledgeDiscovery,
|
|
14
|
+
import { KnowledgeDefinitionInvalid, KnowledgeDesiredStateUnreconcilable, KnowledgeInstallStateMissing, KnowledgeIoFailed, KnowledgeObservableContractViolated, KnowledgeResolutionMissing, KnowledgeUnavailable, applyProjectionPlans, formatProjectionExclusions, planAggregateProjection, requireCompleteGraph, KNOWLEDGE_REGION_OWNER, reconcileKnowledgeDiscovery, resolveInstructionsConfig, } from "@agentxm/extension-workspace";
|
|
15
15
|
import { canReuseInstalledPackage, materializeExternalPackage } from "@agentxm/extension-workspace";
|
|
16
16
|
import { materializeRegistryPackage } from "../registry-materialization.js";
|
|
17
17
|
import { computeExtensionPathsForLayout, extensionPathSourceFromLockEntry, } from "@agentxm/workspace-state";
|
|
@@ -299,21 +299,64 @@ export const KnowledgeManagerLive = Layer.effect(KnowledgeManager, Effect.gen(fu
|
|
|
299
299
|
.filter(isSourcedDesiredExtension)
|
|
300
300
|
.filter((node) => node.type === "knowledge" && node.enabled))
|
|
301
301
|
: new KnowledgeDesiredStateUnreconcilable()));
|
|
302
|
+
/**
|
|
303
|
+
* Classify a bundle whose package could not be inspected. Only the
|
|
304
|
+
* bundle's own content is in question here: resolution facts have already
|
|
305
|
+
* succeeded, so the two truthful answers are that the package is gone or
|
|
306
|
+
* that its content is invalid.
|
|
307
|
+
*/
|
|
308
|
+
const excludedContributor = (name, root, failure) => Effect.gen(function* () {
|
|
309
|
+
const probe = yield* Effect.result(fs.exists(path.join(root, KNOWLEDGE_MANIFEST_FILENAME)));
|
|
310
|
+
const manifestPresent = Result.isSuccess(probe) && probe.success;
|
|
311
|
+
if (!manifestPresent)
|
|
312
|
+
return { contributor: name, reason: "package-missing" };
|
|
313
|
+
return {
|
|
314
|
+
contributor: name,
|
|
315
|
+
reason: "package-invalid",
|
|
316
|
+
detail: failure._tag === "KnowledgeDefinitionInvalid"
|
|
317
|
+
? failure.detail
|
|
318
|
+
: adapter.describeFailure(failure),
|
|
319
|
+
};
|
|
320
|
+
});
|
|
321
|
+
/**
|
|
322
|
+
* Resolve the contributor set for the discovery region. A bundle that
|
|
323
|
+
* fails inspection cannot supply a row, so it is excluded and reported
|
|
324
|
+
* rather than failing every other bundle's command with it. Resolution
|
|
325
|
+
* failures — a missing lock entry, an unsupported workspace-authored
|
|
326
|
+
* bundle — remain fail-closed: they are AXM state problems, not bundle
|
|
327
|
+
* content problems.
|
|
328
|
+
*/
|
|
302
329
|
const selectKnowledgeBundles = (graph, locked, configured, config, instructionFilesEnabled) => Effect.forEach(graph.nodes.filter((node) => node.type === "knowledge" && node.enabled), (node) => Effect.gen(function* () {
|
|
303
330
|
const root = yield* desiredCanonicalRoot(node, locked[node.name]);
|
|
304
|
-
const inspected = yield* inspectPackage(root);
|
|
305
331
|
const workspaceInstructionEntry = configured[node.name]?.instructionEntry;
|
|
306
|
-
const
|
|
332
|
+
const resolveInclusion = (manifestInstructionEntry) => resolveKnowledgeInstructionEntry({
|
|
307
333
|
bundleEnabled: node.enabled,
|
|
308
334
|
instructionFilesEnabled,
|
|
309
335
|
knowledgeInstructionsEnabled: config.instructions,
|
|
310
336
|
...(workspaceInstructionEntry === undefined ? {} : { workspaceInstructionEntry }),
|
|
311
|
-
...(
|
|
312
|
-
? {}
|
|
313
|
-
: { manifestInstructionEntry: inspected.manifest.instructionEntry }),
|
|
337
|
+
...(manifestInstructionEntry === undefined ? {} : { manifestInstructionEntry }),
|
|
314
338
|
});
|
|
315
|
-
|
|
316
|
-
|
|
339
|
+
const inspection = yield* Effect.result(inspectPackage(root));
|
|
340
|
+
if (Result.isFailure(inspection)) {
|
|
341
|
+
// A bundle the workspace would not publish anyway is simply
|
|
342
|
+
// absent from the region; reporting it would be noise.
|
|
343
|
+
return resolveInclusion().included
|
|
344
|
+
? {
|
|
345
|
+
contributors: [],
|
|
346
|
+
exclusions: [yield* excludedContributor(node.name, root, inspection.failure)],
|
|
347
|
+
}
|
|
348
|
+
: { contributors: [], exclusions: [] };
|
|
349
|
+
}
|
|
350
|
+
const inspected = inspection.success;
|
|
351
|
+
const resolution = resolveInclusion(inspected.manifest.instructionEntry);
|
|
352
|
+
return {
|
|
353
|
+
contributors: resolution.included ? [toProjectionBundle(root, inspected)] : [],
|
|
354
|
+
exclusions: [],
|
|
355
|
+
};
|
|
356
|
+
})).pipe(Effect.map((selections) => ({
|
|
357
|
+
contributors: selections.flatMap(({ contributors }) => contributors),
|
|
358
|
+
exclusions: selections.flatMap(({ exclusions }) => exclusions),
|
|
359
|
+
})));
|
|
317
360
|
const resolveKnowledgeProjection = () => Effect.gen(function* () {
|
|
318
361
|
const graph = yield* ws.getDesiredStateGraph();
|
|
319
362
|
const locked = yield* getKnowledgeLockEntries(ws);
|
|
@@ -351,10 +394,6 @@ export const KnowledgeManagerLive = Layer.effect(KnowledgeManager, Effect.gen(fu
|
|
|
351
394
|
present: Option.isSome(result.observedRegion),
|
|
352
395
|
current: !result.changed,
|
|
353
396
|
expectedContributors: input.contributors.map(({ owner, name }) => `${owner}/knowledge/${name}`),
|
|
354
|
-
observedContributors: Option.match(result.observedRegion, {
|
|
355
|
-
onNone: () => [],
|
|
356
|
-
onSome: observedKnowledgeContributors,
|
|
357
|
-
}),
|
|
358
397
|
}))),
|
|
359
398
|
apply: (input) => runKnowledgeProjectionAdapter({
|
|
360
399
|
bundles: input.contributors,
|
|
@@ -366,12 +405,18 @@ export const KnowledgeManagerLive = Layer.effect(KnowledgeManager, Effect.gen(fu
|
|
|
366
405
|
});
|
|
367
406
|
const projectionPlans = () => makeKnowledgeProjectionPlan().pipe(Effect.map((plan) => [plan]));
|
|
368
407
|
const applyKnowledgeProjection = projectionPlans().pipe(Effect.flatMap(applyProjectionPlans));
|
|
369
|
-
const reconcileDiscovery = (options) => resolveKnowledgeProjection().pipe(Effect.flatMap(({ graph, locked, configured, config, instructionsTarget }) => requireCompleteGraph(graph).pipe(Effect.flatMap((completeGraph) => selectKnowledgeBundles(completeGraph, locked, configured, config, instructionsTarget.enabled)), Effect.flatMap((
|
|
370
|
-
bundles,
|
|
408
|
+
const reconcileDiscovery = (options) => resolveKnowledgeProjection().pipe(Effect.flatMap(({ graph, locked, configured, config, instructionsTarget }) => requireCompleteGraph(graph).pipe(Effect.flatMap((completeGraph) => selectKnowledgeBundles(completeGraph, locked, configured, config, instructionsTarget.enabled)), Effect.flatMap(({ contributors, exclusions }) => runKnowledgeProjectionAdapter({
|
|
409
|
+
bundles: contributors,
|
|
371
410
|
config,
|
|
372
411
|
instructionsTarget,
|
|
373
412
|
...(options?.dryRun === undefined ? {} : { dryRun: options.dryRun }),
|
|
374
|
-
}))
|
|
413
|
+
}).pipe(Effect.map((result) => ({
|
|
414
|
+
...result,
|
|
415
|
+
warnings: formatProjectionExclusions({
|
|
416
|
+
exclusions,
|
|
417
|
+
targetFile: instructionsTarget.path,
|
|
418
|
+
}),
|
|
419
|
+
})))))));
|
|
375
420
|
const buildLockEntry = (ref) => Effect.gen(function* () {
|
|
376
421
|
const state = lastInstallState.get(ref.knowledge.name);
|
|
377
422
|
switch (ref.refType) {
|
|
@@ -491,7 +536,7 @@ export const KnowledgeManagerLive = Layer.effect(KnowledgeManager, Effect.gen(fu
|
|
|
491
536
|
const discovery = discovered.success;
|
|
492
537
|
return {
|
|
493
538
|
changed: prepared.length > 0 || discovery.changed,
|
|
494
|
-
warnings:
|
|
539
|
+
warnings: discovery.warnings,
|
|
495
540
|
artifacts: discovery.artifacts,
|
|
496
541
|
};
|
|
497
542
|
});
|
package/dist/src/mcps/manager.js
CHANGED
|
@@ -15,7 +15,7 @@ import * as Option from "effect/Option";
|
|
|
15
15
|
import { McpConfigIoFailed, McpInstallStateMissing, McpRegistryOnlyInstall, removeMcpServerFromManifest, applyProjectionPlans, planSingletonProjection, } from "@agentxm/extension-workspace";
|
|
16
16
|
import { McpServerManager } from "@agentxm/extension-workspace";
|
|
17
17
|
import { configuredMcpServersToDiskRefs } from "@agentxm/extension-workspace";
|
|
18
|
-
import { WorkspaceMutations } from "@agentxm/workspace-state";
|
|
18
|
+
import { mcpRegistryResolutionKey, WorkspaceMutations } from "@agentxm/workspace-state";
|
|
19
19
|
import { canReuseInstalledPackage } from "@agentxm/extension-workspace";
|
|
20
20
|
import { materializeRegistryPackageWithTreeIntegrity } from "../registry-materialization.js";
|
|
21
21
|
import { computeExtensionPathsForLayout } from "@agentxm/workspace-state";
|
|
@@ -55,6 +55,7 @@ export const McpServerManagerLive = Layer.effect(McpServerManager, Effect.gen(fu
|
|
|
55
55
|
const fsPathLayer = Layer.mergeAll(Layer.succeed(FileSystem.FileSystem, fs), Layer.succeed(Path.Path, path), Layer.succeed(HttpClient.HttpClient, httpClient));
|
|
56
56
|
const provide = (effect) => Effect.provide(effect, fsPathLayer);
|
|
57
57
|
const lastTreeIntegrities = new Map();
|
|
58
|
+
const pendingRemoval = new Map();
|
|
58
59
|
const materializeInstall = Effect.fn("McpServerManager.materializeInstall")(function* ({ ref, force }) {
|
|
59
60
|
if (ref.refType !== "registry") {
|
|
60
61
|
return yield* new McpRegistryOnlyInstall({
|
|
@@ -64,7 +65,11 @@ export const McpServerManagerLive = Layer.effect(McpServerManager, Effect.gen(fu
|
|
|
64
65
|
}
|
|
65
66
|
const registryRef = ref;
|
|
66
67
|
const canonicalPath = computeExtensionPathsForLayout(path.join, ws.layout, registryRef, "mcps", registryRef.name).canonicalPath;
|
|
67
|
-
const lockedEntry = yield* ws.getLockedMcpServer(
|
|
68
|
+
const lockedEntry = yield* ws.getLockedMcpServer(mcpRegistryResolutionKey({
|
|
69
|
+
authority: registryRef.source.location,
|
|
70
|
+
owner: registryRef.owner,
|
|
71
|
+
name: registryRef.server.name,
|
|
72
|
+
}));
|
|
68
73
|
const lockedVersion = acceptedRegistryVersionForRef(lockedEntry, registryRef);
|
|
69
74
|
const useExisting = yield* provide(canReuseInstalledPackage({
|
|
70
75
|
installedPath: canonicalPath,
|
|
@@ -97,6 +102,18 @@ export const McpServerManagerLive = Layer.effect(McpServerManager, Effect.gen(fu
|
|
|
97
102
|
lastTreeIntegrities.set(registryRef.server.name, materialized.treeIntegrity);
|
|
98
103
|
}, Effect.asVoid);
|
|
99
104
|
const makeMaterializeRemoval = (retainCanonical) => Effect.fn("McpServerManager.materializeRemoval")(function* ({ target }) {
|
|
105
|
+
const graph = yield* ws.getDesiredStateGraph();
|
|
106
|
+
const desiredNode = graph.nodes.find((node) => node.type === "mcp-server" && node.name === target.name);
|
|
107
|
+
const closure = desiredNode === undefined || desiredNode.authority === "inline"
|
|
108
|
+
? undefined
|
|
109
|
+
: graph.mcpSourceClosures.find((candidate) => candidate.identity === desiredNode.identity);
|
|
110
|
+
const retainShared = closure !== undefined && closure.localNames.some((name) => name !== target.name);
|
|
111
|
+
pendingRemoval.set(target.name, {
|
|
112
|
+
...(desiredNode === undefined || desiredNode.authority === "inline"
|
|
113
|
+
? {}
|
|
114
|
+
: { resolutionKey: desiredNode.identity }),
|
|
115
|
+
retainShared,
|
|
116
|
+
});
|
|
100
117
|
const configuredAgents = yield* ws.getConfiguredAgents();
|
|
101
118
|
yield* applyProjectionPlans(configuredAgents.map((agentId) => planSingletonProjection({
|
|
102
119
|
unitId: "mcp-server:native-config-entry",
|
|
@@ -119,7 +136,7 @@ export const McpServerManagerLive = Layer.effect(McpServerManager, Effect.gen(fu
|
|
|
119
136
|
})).pipe(Effect.asVoid),
|
|
120
137
|
},
|
|
121
138
|
})));
|
|
122
|
-
if (retainCanonical)
|
|
139
|
+
if (retainCanonical || retainShared)
|
|
123
140
|
return;
|
|
124
141
|
const canonical = yield* provide(acceptedCanonicalObservation({
|
|
125
142
|
workspace: ws,
|
|
@@ -178,6 +195,11 @@ export const McpServerManagerLive = Layer.effect(McpServerManager, Effect.gen(fu
|
|
|
178
195
|
const lockEntry = buildMcpServerLockEntry(registryRef, treeIntegrity);
|
|
179
196
|
return ws.setMcpServer({
|
|
180
197
|
name: ref.server.name,
|
|
198
|
+
resolutionKey: mcpRegistryResolutionKey({
|
|
199
|
+
authority: registryRef.source.location,
|
|
200
|
+
owner: registryRef.owner,
|
|
201
|
+
name: registryRef.server.name,
|
|
202
|
+
}),
|
|
181
203
|
lockEntry,
|
|
182
204
|
versionRange,
|
|
183
205
|
});
|
|
@@ -200,14 +222,26 @@ export const McpServerManagerLive = Layer.effect(McpServerManager, Effect.gen(fu
|
|
|
200
222
|
const lockEntry = buildMcpServerLockEntry(registryRef, treeIntegrity);
|
|
201
223
|
return ws.setMcpServerLock({
|
|
202
224
|
name: ref.server.name,
|
|
225
|
+
resolutionKey: mcpRegistryResolutionKey({
|
|
226
|
+
authority: registryRef.source.location,
|
|
227
|
+
owner: registryRef.owner,
|
|
228
|
+
name: registryRef.server.name,
|
|
229
|
+
}),
|
|
203
230
|
lockEntry,
|
|
204
231
|
versionRange: Option.none(),
|
|
205
232
|
});
|
|
206
233
|
}), Effect.withSpan("McpServerManager.upsertLockfileEntry"));
|
|
207
234
|
},
|
|
208
|
-
removeLockfileEntry: ({ target }) =>
|
|
209
|
-
.
|
|
210
|
-
.
|
|
235
|
+
removeLockfileEntry: ({ target }) => {
|
|
236
|
+
const removal = pendingRemoval.get(target.name);
|
|
237
|
+
pendingRemoval.delete(target.name);
|
|
238
|
+
if (removal?.retainShared === true || removal?.resolutionKey === undefined) {
|
|
239
|
+
return Effect.void.pipe(Effect.withSpan("McpServerManager.removeLockfileEntry"));
|
|
240
|
+
}
|
|
241
|
+
return ws
|
|
242
|
+
.removeMcpServerLock(removal.resolutionKey)
|
|
243
|
+
.pipe(Effect.withSpan("McpServerManager.removeLockfileEntry"));
|
|
244
|
+
},
|
|
211
245
|
};
|
|
212
246
|
}));
|
|
213
247
|
//# sourceMappingURL=manager.js.map
|
|
@@ -6,12 +6,11 @@
|
|
|
6
6
|
import * as Effect from "effect/Effect";
|
|
7
7
|
import * as FileSystem from "effect/FileSystem";
|
|
8
8
|
import * as Path from "effect/Path";
|
|
9
|
-
import { CodingAgentRepository
|
|
9
|
+
import { CodingAgentRepository } from "@agentxm/extension-workspace";
|
|
10
10
|
import { appendWarningsToMessage } from "@agentxm/workspace-operations";
|
|
11
11
|
import { WorkspaceMutations } from "@agentxm/workspace-state";
|
|
12
12
|
import { agentConfigTargets, mcpServerArtifact, mcpSettingsTarget } from "./artifact.js";
|
|
13
13
|
import { mcpSyncWarnings, requireSuccessfulMcpSync } from "./sync-outcome.js";
|
|
14
|
-
import { isMcpServerApplicableToAgent } from "@agentxm/workspace-state";
|
|
15
14
|
import { LifecycleFailureAdapter, withAdaptedStepFailures } from "../../failure-adapter.js";
|
|
16
15
|
import { ExtensionLifecycleFailed } from "../../errors.js";
|
|
17
16
|
export const disableMcpServer = (op) => Effect.gen(function* () {
|
|
@@ -28,48 +27,13 @@ export const disableMcpServer = (op) => Effect.gen(function* () {
|
|
|
28
27
|
});
|
|
29
28
|
}
|
|
30
29
|
const agents = yield* agentRepo.getConfiguredAgents();
|
|
31
|
-
const sharedTargetConflict = sharedMcpTargetPolicyConflict({
|
|
32
|
-
entry,
|
|
33
|
-
agentIds: agents.map((agent) => agent.id),
|
|
34
|
-
scope: ws.scope,
|
|
35
|
-
});
|
|
36
|
-
if (sharedTargetConflict !== undefined) {
|
|
37
|
-
return yield* new ExtensionLifecycleFailed({
|
|
38
|
-
category: "conflict",
|
|
39
|
-
detail: sharedTargetConflict,
|
|
40
|
-
});
|
|
41
|
-
}
|
|
42
30
|
const outcomes = yield* ws.runTransaction({
|
|
43
31
|
transition: Effect.gen(function* () {
|
|
44
|
-
const synced = yield* Effect.forEach(agents, (agent) =>
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
serverName: op.args.serverName,
|
|
50
|
-
disableOnly: true,
|
|
51
|
-
});
|
|
52
|
-
}
|
|
53
|
-
const inspection = yield* inspectAgentMcpServer({
|
|
54
|
-
workspaceRoot: ws.baseDir,
|
|
55
|
-
scope: ws.scope,
|
|
56
|
-
agentId: agent.id,
|
|
57
|
-
serverName: op.args.serverName,
|
|
58
|
-
entry,
|
|
59
|
-
});
|
|
60
|
-
if (inspection.status === "unmanaged") {
|
|
61
|
-
return yield* new ExtensionLifecycleFailed({
|
|
62
|
-
category: "conflict",
|
|
63
|
-
detail: `${agent.id} has an unmanaged MCP server named ${op.args.serverName}; AXM will not remove it while applying the target policy`,
|
|
64
|
-
});
|
|
65
|
-
}
|
|
66
|
-
return inspection.status === "drift"
|
|
67
|
-
? yield* agent.removeMcpServer({
|
|
68
|
-
workspaceRoot: ws.baseDir,
|
|
69
|
-
scope: ws.scope,
|
|
70
|
-
serverName: op.args.serverName,
|
|
71
|
-
})
|
|
72
|
-
: { _tag: "success", targets: [] };
|
|
32
|
+
const synced = yield* Effect.forEach(agents, (agent) => agent.removeMcpServer({
|
|
33
|
+
workspaceRoot: ws.baseDir,
|
|
34
|
+
scope: ws.scope,
|
|
35
|
+
serverName: op.args.serverName,
|
|
36
|
+
disableOnly: true,
|
|
73
37
|
}), { concurrency: "unbounded" });
|
|
74
38
|
yield* requireSuccessfulMcpSync(op.args.serverName, agents.map((agent, index) => ({
|
|
75
39
|
agentId: agent.id,
|
|
@@ -7,14 +7,13 @@ import * as FileSystem from "effect/FileSystem";
|
|
|
7
7
|
import * as Path from "effect/Path";
|
|
8
8
|
import * as Effect from "effect/Effect";
|
|
9
9
|
import * as Option from "effect/Option";
|
|
10
|
-
import { CodingAgentRepository, syncInlineMcpServerToAgents,
|
|
10
|
+
import { CodingAgentRepository, syncInlineMcpServerToAgents, applyProjectionPlansWithResults, planSingletonProjection, } from "@agentxm/extension-workspace";
|
|
11
11
|
import { normalizeHandle, parseExtensionFqnParts, } from "@agentxm/extension-model/unstable/extensions";
|
|
12
12
|
import { appendWarningsToMessage } from "@agentxm/workspace-operations";
|
|
13
13
|
import { WorkspaceMutations } from "@agentxm/workspace-state";
|
|
14
14
|
import { agentConfigTargets, mcpServerArtifact, mcpSettingsTarget } from "./artifact.js";
|
|
15
15
|
import { usableAcceptedCanonicalObservation } from "@agentxm/workspace-state";
|
|
16
16
|
import { mcpSyncWarnings, requireSuccessfulMcpSync } from "./sync-outcome.js";
|
|
17
|
-
import { isMcpServerApplicableToAgent } from "@agentxm/workspace-state";
|
|
18
17
|
import { LifecycleFailureAdapter, withAdaptedStepFailures } from "../../failure-adapter.js";
|
|
19
18
|
import { ExtensionLifecycleFailed } from "../../errors.js";
|
|
20
19
|
const enableArtifact = (args) => {
|
|
@@ -116,17 +115,6 @@ export const enableMcpServer = (op) => Effect.gen(function* () {
|
|
|
116
115
|
const owner = trustedIdentity?.type === "mcp-server" ? trustedIdentity.owner : normalizeHandle("@local");
|
|
117
116
|
const resolvedVersion = accepted?.type === "registry" ? accepted.resolvedVersion : "0.0.0";
|
|
118
117
|
const agents = yield* agentRepo.getConfiguredAgents();
|
|
119
|
-
const sharedTargetConflict = sharedMcpTargetPolicyConflict({
|
|
120
|
-
entry,
|
|
121
|
-
agentIds: agents.map((agent) => agent.id),
|
|
122
|
-
scope: ws.scope,
|
|
123
|
-
});
|
|
124
|
-
if (sharedTargetConflict !== undefined) {
|
|
125
|
-
return yield* new ExtensionLifecycleFailed({
|
|
126
|
-
category: "conflict",
|
|
127
|
-
detail: sharedTargetConflict,
|
|
128
|
-
});
|
|
129
|
-
}
|
|
130
118
|
const outcomes = yield* ws.runTransaction({
|
|
131
119
|
transition: Effect.gen(function* () {
|
|
132
120
|
const synced = yield* applyProjectionPlansWithResults(agents.map((agent) => planSingletonProjection({
|
|
@@ -143,27 +131,6 @@ export const enableMcpServer = (op) => Effect.gen(function* () {
|
|
|
143
131
|
observedContributors: [],
|
|
144
132
|
}),
|
|
145
133
|
apply: () => Effect.gen(function* () {
|
|
146
|
-
if (!isMcpServerApplicableToAgent(entry, agent.id)) {
|
|
147
|
-
const inspection = yield* inspectAgentMcpServer({
|
|
148
|
-
workspaceRoot: ws.baseDir,
|
|
149
|
-
scope: ws.scope,
|
|
150
|
-
agentId: agent.id,
|
|
151
|
-
serverName: op.args.serverName,
|
|
152
|
-
entry,
|
|
153
|
-
});
|
|
154
|
-
if (inspection.status === "unmanaged") {
|
|
155
|
-
return yield* new McpSharedTargetConflict({
|
|
156
|
-
reason: `${agent.id} has an unmanaged MCP server named ${op.args.serverName}; AXM will not remove it while applying the target policy`,
|
|
157
|
-
});
|
|
158
|
-
}
|
|
159
|
-
return inspection.status === "drift"
|
|
160
|
-
? yield* agent.removeMcpServer({
|
|
161
|
-
workspaceRoot: ws.baseDir,
|
|
162
|
-
scope: ws.scope,
|
|
163
|
-
serverName: op.args.serverName,
|
|
164
|
-
})
|
|
165
|
-
: { _tag: "success", targets: [] };
|
|
166
|
-
}
|
|
167
134
|
return yield* agent.addMcpServer({
|
|
168
135
|
workspaceRoot: ws.baseDir,
|
|
169
136
|
scope: ws.scope,
|
|
@@ -12,17 +12,19 @@ import * as Path from "effect/Path";
|
|
|
12
12
|
import * as Effect from "effect/Effect";
|
|
13
13
|
import * as Option from "effect/Option";
|
|
14
14
|
import { CodingAgentRepository } from "@agentxm/extension-workspace";
|
|
15
|
-
import type { ConfigurableAgentId } from "@agentxm/extension-model/unstable/agent-capabilities";
|
|
16
15
|
import type { StepFailure } from "@agentxm/workspace-operations";
|
|
17
16
|
import type { JobStepResult, Operation } from "@agentxm/workspace-operations";
|
|
18
17
|
import { WorkspaceMutations } from "@agentxm/workspace-state";
|
|
19
18
|
import type { McpServerExtensionRef } from "@agentxm/extension-model/unstable/extensions/refs/mcp-server";
|
|
19
|
+
import { type McpServerManifest } from "@agentxm/extension-model/unstable/mcps/manifest-schema";
|
|
20
20
|
import { LifecycleFailureAdapter } from "../../failure-adapter.js";
|
|
21
21
|
/**
|
|
22
22
|
* Args for the install-mcp-server operation.
|
|
23
23
|
*/
|
|
24
24
|
export type InstallMcpServerOperationArgs = {
|
|
25
25
|
readonly ref: McpServerExtensionRef;
|
|
26
|
+
/** Local connection identity and exact agent-native MCP key. */
|
|
27
|
+
readonly localName?: string;
|
|
26
28
|
readonly force: boolean;
|
|
27
29
|
/** Explicitly permit a workspace-authored relocation during reconciliation. */
|
|
28
30
|
readonly allowWorkspaceSourceTransition?: boolean;
|
|
@@ -40,8 +42,6 @@ export type InstallMcpServerOperationArgs = {
|
|
|
40
42
|
* The transport boundary resolves flag, CI, and TTY state.
|
|
41
43
|
*/
|
|
42
44
|
readonly nonInteractive: boolean;
|
|
43
|
-
/** Restrict this server to a reviewed subset of configured agents. */
|
|
44
|
-
readonly agents?: ReadonlyArray<ConfigurableAgentId>;
|
|
45
45
|
};
|
|
46
46
|
/**
|
|
47
47
|
* Add an MCP server to the workspace.
|
|
@@ -49,6 +49,25 @@ export type InstallMcpServerOperationArgs = {
|
|
|
49
49
|
* @experimental This API is unstable and may change without notice.
|
|
50
50
|
*/
|
|
51
51
|
export type InstallMcpServerOperation = Operation<"install-mcp-server", InstallMcpServerOperationArgs>;
|
|
52
|
+
export declare const mcpSecretAccount: (args: {
|
|
53
|
+
readonly scopeRoot: string;
|
|
54
|
+
readonly localName: string;
|
|
55
|
+
readonly sourceIdentity: string;
|
|
56
|
+
readonly inputName: string;
|
|
57
|
+
}) => string;
|
|
58
|
+
export type McpSecretDeletionOutcome = {
|
|
59
|
+
readonly _tag: "deleted";
|
|
60
|
+
readonly inputName: string;
|
|
61
|
+
} | {
|
|
62
|
+
readonly _tag: "absent";
|
|
63
|
+
readonly inputName: string;
|
|
64
|
+
} | {
|
|
65
|
+
readonly _tag: "failed";
|
|
66
|
+
readonly inputName: string;
|
|
67
|
+
};
|
|
68
|
+
export declare const collectSecretInputNames: (manifest: McpServerManifest) => ReadonlySet<string>;
|
|
69
|
+
export declare const readMcpServerManifest: (canonicalPath: string) => Effect.Effect<Option.Option<McpServerManifest>, never, FileSystem.FileSystem | Path.Path>;
|
|
70
|
+
export declare const deleteMcpSecrets: (identity: Omit<Parameters<typeof mcpSecretAccount>[0], "inputName">, secretNames: ReadonlySet<string>) => Effect.Effect<ReadonlyArray<McpSecretDeletionOutcome>>;
|
|
52
71
|
/**
|
|
53
72
|
* Install-mcp-server operation handler.
|
|
54
73
|
*
|