@agentxm/extension-lifecycle 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 (84) hide show
  1. package/LICENSE +110 -0
  2. package/dist/src/configured-entry-resolution.d.ts +110 -0
  3. package/dist/src/configured-entry-resolution.js +723 -0
  4. package/dist/src/errors.d.ts +39 -0
  5. package/dist/src/errors.js +45 -0
  6. package/dist/src/failure-adapter.d.ts +35 -0
  7. package/dist/src/failure-adapter.js +23 -0
  8. package/dist/src/hooks/configured-agent-outcomes-provider.d.ts +18 -0
  9. package/dist/src/hooks/configured-agent-outcomes-provider.js +39 -0
  10. package/dist/src/hooks/manager.d.ts +15 -0
  11. package/dist/src/hooks/manager.js +751 -0
  12. package/dist/src/index.d.ts +38 -0
  13. package/dist/src/index.js +41 -0
  14. package/dist/src/internal/fs-helpers.d.ts +12 -0
  15. package/dist/src/internal/fs-helpers.js +13 -0
  16. package/dist/src/internal/integrity.d.ts +13 -0
  17. package/dist/src/internal/integrity.js +17 -0
  18. package/dist/src/knowledge/manager.d.ts +11 -0
  19. package/dist/src/knowledge/manager.js +655 -0
  20. package/dist/src/live.d.ts +18 -0
  21. package/dist/src/live.js +18 -0
  22. package/dist/src/mcps/manager.d.ts +16 -0
  23. package/dist/src/mcps/manager.js +213 -0
  24. package/dist/src/mcps/operations/artifact.d.ts +24 -0
  25. package/dist/src/mcps/operations/artifact.js +74 -0
  26. package/dist/src/mcps/operations/disable.d.ts +18 -0
  27. package/dist/src/mcps/operations/disable.js +113 -0
  28. package/dist/src/mcps/operations/enable.d.ts +18 -0
  29. package/dist/src/mcps/operations/enable.js +218 -0
  30. package/dist/src/mcps/operations/install.d.ts +59 -0
  31. package/dist/src/mcps/operations/install.js +566 -0
  32. package/dist/src/mcps/operations/sync-outcome.d.ts +10 -0
  33. package/dist/src/mcps/operations/sync-outcome.js +27 -0
  34. package/dist/src/mcps/operations/uninstall.d.ts +41 -0
  35. package/dist/src/mcps/operations/uninstall.js +189 -0
  36. package/dist/src/packs/dependency-resolution.d.ts +65 -0
  37. package/dist/src/packs/dependency-resolution.js +414 -0
  38. package/dist/src/packs/expansion.d.ts +59 -0
  39. package/dist/src/packs/expansion.js +44 -0
  40. package/dist/src/packs/manager.d.ts +16 -0
  41. package/dist/src/packs/manager.js +181 -0
  42. package/dist/src/packs/operations/install.d.ts +67 -0
  43. package/dist/src/packs/operations/install.js +156 -0
  44. package/dist/src/packs/resolved-dependency.d.ts +29 -0
  45. package/dist/src/packs/resolved-dependency.js +28 -0
  46. package/dist/src/registry-materialization.d.ts +52 -0
  47. package/dist/src/registry-materialization.js +67 -0
  48. package/dist/src/resolution-progress.d.ts +18 -0
  49. package/dist/src/resolution-progress.js +11 -0
  50. package/dist/src/resolution-timeout.d.ts +5 -0
  51. package/dist/src/resolution-timeout.js +11 -0
  52. package/dist/src/rules/manager.d.ts +20 -0
  53. package/dist/src/rules/manager.js +475 -0
  54. package/dist/src/skills/manager.d.ts +18 -0
  55. package/dist/src/skills/manager.js +346 -0
  56. package/dist/src/skills/materialization.d.ts +54 -0
  57. package/dist/src/skills/materialization.js +158 -0
  58. package/dist/src/skills/operations/disable.d.ts +31 -0
  59. package/dist/src/skills/operations/disable.js +110 -0
  60. package/dist/src/skills/operations/enable.d.ts +31 -0
  61. package/dist/src/skills/operations/enable.js +113 -0
  62. package/dist/src/skills/operations/install-result.d.ts +33 -0
  63. package/dist/src/skills/operations/install-result.js +10 -0
  64. package/dist/src/skills/operations/install.d.ts +67 -0
  65. package/dist/src/skills/operations/install.js +590 -0
  66. package/dist/src/skills/operations/source-hash.d.ts +2 -0
  67. package/dist/src/skills/operations/source-hash.js +6 -0
  68. package/dist/src/skills/operations/uninstall.d.ts +40 -0
  69. package/dist/src/skills/operations/uninstall.js +157 -0
  70. package/dist/src/skills/utils.d.ts +11 -0
  71. package/dist/src/skills/utils.js +29 -0
  72. package/dist/src/subagents/manager.d.ts +18 -0
  73. package/dist/src/subagents/manager.js +677 -0
  74. package/dist/src/subagents/operations/artifact.d.ts +18 -0
  75. package/dist/src/subagents/operations/artifact.js +43 -0
  76. package/dist/src/subagents/operations/disable.d.ts +32 -0
  77. package/dist/src/subagents/operations/disable.js +116 -0
  78. package/dist/src/subagents/operations/enable.d.ts +30 -0
  79. package/dist/src/subagents/operations/enable.js +160 -0
  80. package/dist/src/workflows/install-command/workflow.d.ts +57 -0
  81. package/dist/src/workflows/install-command/workflow.js +41 -0
  82. package/dist/src/workflows/uninstall-command/workflow.d.ts +42 -0
  83. package/dist/src/workflows/uninstall-command/workflow.js +29 -0
  84. package/package.json +62 -0
@@ -0,0 +1,655 @@
1
+ // @effect-diagnostics anyUnknownInErrorContext:off — schema and filesystem errors are swept into KnowledgeIoFailed inside this manager
2
+ /** Lifecycle manager for isolated Open Knowledge Format bundles. */
3
+ import * as Effect from "effect/Effect";
4
+ import * as FileSystem from "effect/FileSystem";
5
+ import * as HttpClient from "effect/unstable/http/HttpClient";
6
+ import * as Layer from "effect/Layer";
7
+ import * as Option from "effect/Option";
8
+ import * as Path from "effect/Path";
9
+ import * as Ref from "effect/Ref";
10
+ import * as Schema from "effect/Schema";
11
+ import * as Scope from "effect/Scope";
12
+ import * as Result from "effect/Result";
13
+ import { PlatformError } from "effect/PlatformError";
14
+ import { KnowledgeDefinitionInvalid, KnowledgeDesiredStateUnreconcilable, KnowledgeInstallStateMissing, KnowledgeIoFailed, KnowledgeObservableContractViolated, KnowledgeResolutionMissing, KnowledgeUnavailable, applyProjectionPlans, planAggregateProjection, requireCompleteGraph, KNOWLEDGE_REGION_OWNER, reconcileKnowledgeDiscovery, observedKnowledgeContributors, resolveInstructionsConfig, } from "@agentxm/extension-workspace";
15
+ import { canReuseInstalledPackage, materializeExternalPackage } from "@agentxm/extension-workspace";
16
+ import { materializeRegistryPackage } from "../registry-materialization.js";
17
+ import { computeExtensionPathsForLayout, extensionPathSourceFromLockEntry, } from "@agentxm/workspace-state";
18
+ import { computePackageContentHash } from "@agentxm/workspace-state";
19
+ import { computeMaterializedTreeIntegrity } from "@agentxm/workspace-state";
20
+ import { acceptedRegistryVersionForRef, validateExactResolvedVersion, } from "@agentxm/workspace-state";
21
+ import { gitSourceLockFields } from "@agentxm/workspace-state";
22
+ import { SourceHostProviders, WorkspaceCatalog } from "@agentxm/extension-sources";
23
+ import { knowledgeLockEntryToRef } from "@agentxm/workspace-state";
24
+ import { stripFileProtocol } from "../internal/fs-helpers.js";
25
+ import { makeWorkspaceRelativeSourcePath } from "@agentxm/extension-model/unstable/path-types";
26
+ import { recordFootprint } from "@agentxm/workspace-state";
27
+ import { makeWorkspaceRelativePath } from "@agentxm/extension-model/unstable/path-types";
28
+ import { decodeVersionSync } from "@agentxm/extension-model/unstable/version-constraints";
29
+ import { makeConfiguredReleaseAgeEvaluation, resolveConfiguredKnowledge, } from "../configured-entry-resolution.js";
30
+ import { getKnowledgeLockEntries } from "@agentxm/workspace-state";
31
+ import { KnowledgeManager, } from "@agentxm/extension-workspace";
32
+ import { WorkspaceMutations } from "@agentxm/workspace-state";
33
+ import { isObservedInstalled } from "@agentxm/workspace-state";
34
+ import { acceptedCanonicalObservation, prepareAcceptedCanonicalTransition, removableAcceptedCanonicalPath, } from "@agentxm/workspace-state";
35
+ import { protectWorkspacePath } from "@agentxm/workspace-state";
36
+ import { isSourcedDesiredExtension } from "@agentxm/workspace-state";
37
+ import { KNOWLEDGE_EXTENSION_DIR, KNOWLEDGE_MANIFEST_FILENAME, KNOWLEDGE_SOURCE_DIR, KnowledgeManifestSchema, } from "@agentxm/extension-model/unstable/knowledge/manifest-schema";
38
+ import { inspectKnowledgeBundle, } from "@agentxm/registry-protocol/unstable/knowledge/okf";
39
+ import { resolveKnowledgeInstructionEntry } from "@agentxm/extension-workspace";
40
+ import { coupleLifecycleDependencyFailure } from "../errors.js";
41
+ import { LifecycleFailureAdapter } from "../failure-adapter.js";
42
+ const decodeManifest = Schema.decodeUnknownEffect(KnowledgeManifestSchema);
43
+ const registryLockEntry = (ref, treeIntegrity) => ({
44
+ type: "registry",
45
+ sourceType: "registry",
46
+ packageFormat: "agentxm",
47
+ endpoint: ref.source.location,
48
+ extensionType: "knowledge",
49
+ workspaceName: ref.knowledge.name,
50
+ owner: ref.owner,
51
+ name: ref.name,
52
+ resolvedVersion: decodeVersionSync(ref.version),
53
+ integrity: Option.getOrElse(ref.integrity, () => ""),
54
+ sourceName: ref.source.name,
55
+ publisherBindingId: ref.publisherBindingId,
56
+ treeIntegrity,
57
+ });
58
+ const gitLockEntry = (ref, contentIdentity, treeIntegrity) => ({
59
+ ...gitSourceLockFields(ref.source, "knowledge", ref.knowledge.name, Option.fromUndefinedOr(ref.sourcePath), ref.gitCommitSha, ref.gitTreeSha, contentIdentity, ref.owner, ref.name, treeIntegrity),
60
+ });
61
+ const localLockEntry = (ref, relativePath, contentIdentity, treeIntegrity) => ({
62
+ type: "local",
63
+ sourceType: "local",
64
+ sourceName: "local",
65
+ extensionType: "knowledge",
66
+ workspaceName: ref.knowledge.name,
67
+ packageFormat: "agentxm",
68
+ packageOwner: ref.owner,
69
+ packageName: ref.name,
70
+ path: Option.getOrElse(relativePath, () => ref.source.path),
71
+ contentIdentity,
72
+ treeIntegrity,
73
+ });
74
+ export const KnowledgeManagerLive = Layer.effect(KnowledgeManager, Effect.gen(function* () {
75
+ const ws = yield* WorkspaceMutations;
76
+ const fs = yield* FileSystem.FileSystem;
77
+ const httpClient = yield* HttpClient.HttpClient;
78
+ const path = yield* Path.Path;
79
+ const sources = yield* SourceHostProviders;
80
+ const catalog = yield* WorkspaceCatalog;
81
+ const adapter = yield* LifecycleFailureAdapter;
82
+ const baseDir = ws.baseDir;
83
+ const env = Layer.mergeAll(Layer.succeed(FileSystem.FileSystem, fs), Layer.succeed(HttpClient.HttpClient, httpClient), Layer.succeed(Path.Path, path), Layer.succeed(WorkspaceMutations, ws), Layer.succeed(SourceHostProviders, sources), Layer.succeed(WorkspaceCatalog, catalog), Layer.succeed(LifecycleFailureAdapter, adapter));
84
+ const provide = (effect) => Effect.provide(effect, env);
85
+ const lastInstallState = new Map();
86
+ const canonicalPathForRef = (ref) => computeExtensionPathsForLayout(path.join, ws.layout, ref, KNOWLEDGE_EXTENSION_DIR, ref.name)
87
+ .canonicalPath;
88
+ const materializePackage = (ref, options) => {
89
+ const canonicalPath = options?.destinationPath ?? canonicalPathForRef(ref);
90
+ const materializationBaseDir = options?.baseDir ?? baseDir;
91
+ switch (ref.refType) {
92
+ case "registry":
93
+ return Effect.gen(function* () {
94
+ return yield* provide(materializeRegistryPackage({
95
+ baseDir: materializationBaseDir,
96
+ destinationPath: canonicalPath,
97
+ sourceLocation: ref.source.location,
98
+ owner: ref.owner,
99
+ type: "knowledge",
100
+ name: ref.name,
101
+ version: ref.version,
102
+ integrity: ref.integrity,
103
+ messages: {
104
+ integrityMismatchDetail: `Integrity mismatch for knowledge:${ref.name}@${ref.version}`,
105
+ },
106
+ }));
107
+ });
108
+ case "git-hosted":
109
+ case "local":
110
+ return provide(materializeExternalPackage({
111
+ baseDir: materializationBaseDir,
112
+ canonicalPath,
113
+ sourceLocation: ref.location,
114
+ copyFailureCode: "validation",
115
+ copyFailureDetail: (target) => `Failed to copy knowledge package to ${target}`,
116
+ }));
117
+ case "workspace":
118
+ if (ref.scope !== ws.scope ||
119
+ path.resolve(ref.location) !== path.resolve(canonicalPath)) {
120
+ return Effect.fail(new KnowledgeDefinitionInvalid({
121
+ detail: `Invalid workspace knowledge source location: ${ref.location}`,
122
+ }));
123
+ }
124
+ return Effect.succeed(ref.location);
125
+ }
126
+ };
127
+ const inspectPackage = (packageRoot) => Effect.gen(function* () {
128
+ const raw = yield* fs
129
+ .readFileString(path.join(packageRoot, KNOWLEDGE_MANIFEST_FILENAME))
130
+ .pipe(Effect.mapError((cause) => new KnowledgeDefinitionInvalid({
131
+ detail: `Failed to read ${KNOWLEDGE_MANIFEST_FILENAME}`,
132
+ cause,
133
+ })));
134
+ const manifest = yield* Effect.try({
135
+ try: () => JSON.parse(raw),
136
+ catch: (cause) => new KnowledgeDefinitionInvalid({
137
+ detail: `Failed to parse ${KNOWLEDGE_MANIFEST_FILENAME}`,
138
+ cause,
139
+ }),
140
+ }).pipe(Effect.flatMap(decodeManifest), Effect.mapError((cause) => new KnowledgeDefinitionInvalid({
141
+ detail: `Invalid ${KNOWLEDGE_MANIFEST_FILENAME}`,
142
+ cause,
143
+ })));
144
+ const inspection = yield* provide(inspectKnowledgeBundle(path.join(packageRoot, KNOWLEDGE_SOURCE_DIR))).pipe(Effect.mapError((cause) => new KnowledgeDefinitionInvalid({
145
+ detail: "Failed to inspect Open Knowledge Format bundle",
146
+ cause,
147
+ })));
148
+ const errors = inspection.diagnostics.filter((item) => item.severity === "error");
149
+ if (errors.length > 0) {
150
+ return yield* new KnowledgeDefinitionInvalid({
151
+ detail: errors
152
+ .map((item) => item.details?.kind === "frontmatter-parse"
153
+ ? `${item.relativePath}: ${item.message}`
154
+ : item.message)
155
+ .join(" "),
156
+ });
157
+ }
158
+ return { manifest, inspection };
159
+ });
160
+ const preparePackage = (ref, force = false) => Effect.gen(function* () {
161
+ const canonicalPath = canonicalPathForRef(ref);
162
+ if (ref.refType === "workspace") {
163
+ const root = yield* materializePackage(ref);
164
+ yield* inspectPackage(root);
165
+ return {
166
+ root,
167
+ sourceHash: ref.sourceHash,
168
+ commit: Effect.void,
169
+ rollback: Effect.void,
170
+ };
171
+ }
172
+ // Decide against the canonical tree before staging: the staged path
173
+ // never exists, so a decision made there would re-extract every time
174
+ // and revert workspace-owned content on a no-op install.
175
+ if (ref.refType === "registry") {
176
+ const lockedEntry = yield* ws.getLockedKnowledgeEntry(ref.knowledge.name);
177
+ const lockedVersion = acceptedRegistryVersionForRef(lockedEntry, ref);
178
+ const reuse = yield* provide(canReuseInstalledPackage({
179
+ installedPath: canonicalPath,
180
+ force,
181
+ refVersion: ref.version,
182
+ hasIntegrity: Option.isSome(ref.integrity),
183
+ ...(lockedVersion === undefined ? {} : { lockedVersion }),
184
+ existsFailureDetail: (target) => `Failed to inspect knowledge path: ${target}`,
185
+ }));
186
+ if (reuse && Option.isSome(lockedEntry)) {
187
+ const observedTree = yield* provide(computeMaterializedTreeIntegrity(canonicalPath));
188
+ if (observedTree !== lockedEntry.value.treeIntegrity) {
189
+ yield* Effect.logWarning(`Knowledge package ${ref.knowledge.name} differs from its accepted tree; rematerializing`);
190
+ }
191
+ else {
192
+ return {
193
+ root: canonicalPath,
194
+ sourceHash: yield* provide(computePackageContentHash(canonicalPath)),
195
+ treeIntegrity: lockedEntry.value.treeIntegrity,
196
+ commit: Effect.void,
197
+ rollback: Effect.void,
198
+ };
199
+ }
200
+ }
201
+ }
202
+ const tempDir = yield* fs.makeTempDirectoryScoped({ prefix: "axm-knowledge-package-" });
203
+ const stagedPath = path.join(tempDir, "staged");
204
+ const backupPath = path.join(tempDir, "previous");
205
+ const stageState = yield* Ref.make({ phase: "preparing" });
206
+ const restoreStagedPackage = Ref.get(stageState).pipe(Effect.flatMap((state) => {
207
+ if (state.phase !== "staged")
208
+ return Effect.void;
209
+ return fs.remove(canonicalPath, { recursive: true, force: true }).pipe(Effect.mapError((cause) => new KnowledgeIoFailed({
210
+ detail: `Failed to remove staged Knowledge package during rollback: ${canonicalPath}`,
211
+ cause,
212
+ })), Effect.andThen(state.hadCanonical
213
+ ? fs.rename(backupPath, canonicalPath).pipe(Effect.mapError((cause) => new KnowledgeIoFailed({
214
+ detail: `Failed to restore Knowledge package during rollback: ${canonicalPath}`,
215
+ cause,
216
+ })))
217
+ : Effect.void), Effect.andThen(Ref.set(stageState, { phase: "settled" })));
218
+ }));
219
+ yield* Effect.addFinalizer(() => restoreStagedPackage.pipe(Effect.catchCause((cause) => Effect.logError("Failed to restore staged Knowledge package during finalization", cause))));
220
+ const stagedRoot = yield* materializePackage(ref, {
221
+ baseDir: tempDir,
222
+ destinationPath: stagedPath,
223
+ });
224
+ yield* inspectPackage(stagedRoot);
225
+ const sourceHash = yield* provide(computePackageContentHash(stagedRoot));
226
+ const treeIntegrity = yield* provide(computeMaterializedTreeIntegrity(stagedRoot));
227
+ yield* protectWorkspacePath(canonicalPath);
228
+ const hadCanonical = yield* fs.exists(canonicalPath);
229
+ yield* Effect.uninterruptible(Effect.gen(function* () {
230
+ if (hadCanonical)
231
+ yield* fs.rename(canonicalPath, backupPath);
232
+ yield* fs.makeDirectory(path.dirname(canonicalPath), { recursive: true });
233
+ yield* fs.rename(stagedPath, canonicalPath).pipe(Effect.tapError(() => hadCanonical
234
+ ? fs.rename(backupPath, canonicalPath).pipe(Effect.mapError((cause) => new KnowledgeIoFailed({
235
+ detail: `Failed to restore Knowledge package after staging failed: ${canonicalPath}`,
236
+ cause,
237
+ })))
238
+ : Effect.void));
239
+ yield* Ref.set(stageState, { phase: "staged", hadCanonical });
240
+ }));
241
+ yield* recordFootprint({
242
+ path: canonicalPath,
243
+ change: hadCanonical ? "modified" : "created",
244
+ });
245
+ return {
246
+ root: canonicalPath,
247
+ sourceHash,
248
+ treeIntegrity,
249
+ commit: Ref.set(stageState, { phase: "settled" }),
250
+ rollback: restoreStagedPackage,
251
+ };
252
+ }).pipe(Effect.mapError((cause) => cause instanceof PlatformError
253
+ ? new KnowledgeIoFailed({
254
+ detail: `Failed to stage Knowledge bundle ${ref.knowledge.name}`,
255
+ cause,
256
+ })
257
+ : cause));
258
+ const getInstructionsTarget = () => Effect.gen(function* () {
259
+ const config = yield* ws.getInstructionsConfig();
260
+ const enabled = Option.isSome(config) && config.value !== false;
261
+ const resolved = resolveInstructionsConfig(enabled ? config.value : undefined);
262
+ const relative = makeWorkspaceRelativePath(path, baseDir, resolved.fileName);
263
+ if (Option.isNone(relative)) {
264
+ return yield* new KnowledgeDefinitionInvalid({
265
+ detail: `Knowledge discovery instruction target escapes workspace: ${resolved.fileName}`,
266
+ });
267
+ }
268
+ return {
269
+ path: path.resolve(baseDir, relative.value),
270
+ enabled,
271
+ preserveSource: enabled,
272
+ };
273
+ });
274
+ const canonicalRoot = (_name, locked) => computeExtensionPathsForLayout(path.join, ws.layout, extensionPathSourceFromLockEntry(locked), KNOWLEDGE_EXTENSION_DIR, locked.workspaceName).canonicalPath;
275
+ const desiredCanonicalRoot = (node, locked) => {
276
+ if (node.identity.startsWith("workspace:")) {
277
+ return ws.layout.scope === "project"
278
+ ? Effect.succeed(path.join(ws.layout.authoredRoot("knowledge"), node.name))
279
+ : Effect.fail(new KnowledgeDefinitionInvalid({
280
+ detail: "User workspaces do not support workspace-authored Knowledge bundles",
281
+ }));
282
+ }
283
+ return locked === undefined
284
+ ? Effect.fail(new KnowledgeResolutionMissing({ name: node.name }))
285
+ : Effect.succeed(canonicalRoot(node.name, locked));
286
+ };
287
+ const toProjectionBundle = (root, inspected) => ({
288
+ owner: inspected.manifest.owner,
289
+ name: inspected.manifest.name,
290
+ sourceDir: path.join(root, KNOWLEDGE_SOURCE_DIR),
291
+ ...(inspected.manifest.description === undefined
292
+ ? {}
293
+ : { description: inspected.manifest.description }),
294
+ });
295
+ const activeKnowledgeNodes = () => ws
296
+ .getDesiredStateGraph()
297
+ .pipe(Effect.flatMap((graph) => graph.complete
298
+ ? Effect.succeed(graph.nodes
299
+ .filter(isSourcedDesiredExtension)
300
+ .filter((node) => node.type === "knowledge" && node.enabled))
301
+ : new KnowledgeDesiredStateUnreconcilable()));
302
+ const selectKnowledgeBundles = (graph, locked, configured, config, instructionFilesEnabled) => Effect.forEach(graph.nodes.filter((node) => node.type === "knowledge" && node.enabled), (node) => Effect.gen(function* () {
303
+ const root = yield* desiredCanonicalRoot(node, locked[node.name]);
304
+ const inspected = yield* inspectPackage(root);
305
+ const workspaceInstructionEntry = configured[node.name]?.instructionEntry;
306
+ const resolution = resolveKnowledgeInstructionEntry({
307
+ bundleEnabled: node.enabled,
308
+ instructionFilesEnabled,
309
+ knowledgeInstructionsEnabled: config.instructions,
310
+ ...(workspaceInstructionEntry === undefined ? {} : { workspaceInstructionEntry }),
311
+ ...(inspected.manifest.instructionEntry === undefined
312
+ ? {}
313
+ : { manifestInstructionEntry: inspected.manifest.instructionEntry }),
314
+ });
315
+ return resolution.included ? [toProjectionBundle(root, inspected)] : [];
316
+ })).pipe(Effect.map((bundles) => bundles.flat()));
317
+ const resolveKnowledgeProjection = () => Effect.gen(function* () {
318
+ const graph = yield* ws.getDesiredStateGraph();
319
+ const locked = yield* getKnowledgeLockEntries(ws);
320
+ const configured = yield* ws.getConfiguredKnowledgeEntries();
321
+ const config = yield* ws.getKnowledgeDiscoveryConfig();
322
+ const instructionsTarget = yield* getInstructionsTarget();
323
+ return { graph, locked, configured, config, instructionsTarget };
324
+ });
325
+ const runKnowledgeProjectionAdapter = (args) => provide(reconcileKnowledgeDiscovery({
326
+ scopeRoot: baseDir,
327
+ config: args.config,
328
+ bundles: args.bundles,
329
+ instructionsPath: args.instructionsTarget.path,
330
+ instructionManagementEnabled: args.instructionsTarget.enabled,
331
+ preserveInstructionsSource: args.instructionsTarget.preserveSource,
332
+ ...(args.dryRun === undefined ? {} : { dryRun: args.dryRun }),
333
+ }));
334
+ const makeKnowledgeProjectionPlan = () => Effect.gen(function* () {
335
+ const { graph, locked, configured, config, instructionsTarget } = yield* resolveKnowledgeProjection();
336
+ return yield* planAggregateProjection({
337
+ unitId: "knowledge:discovery-region",
338
+ targetFile: instructionsTarget.path,
339
+ graph,
340
+ select: (completeGraph) => selectKnowledgeBundles(completeGraph, locked, configured, config, instructionsTarget.enabled),
341
+ adapter: {
342
+ observe: (input) => runKnowledgeProjectionAdapter({
343
+ bundles: input.contributors,
344
+ config,
345
+ instructionsTarget,
346
+ dryRun: true,
347
+ }).pipe(Effect.map((result) => ({
348
+ unitId: "knowledge:discovery-region",
349
+ path: `${instructionsTarget.path}#knowledge`,
350
+ owner: KNOWLEDGE_REGION_OWNER,
351
+ present: Option.isSome(result.observedRegion),
352
+ current: !result.changed,
353
+ expectedContributors: input.contributors.map(({ owner, name }) => `${owner}/knowledge/${name}`),
354
+ observedContributors: Option.match(result.observedRegion, {
355
+ onNone: () => [],
356
+ onSome: observedKnowledgeContributors,
357
+ }),
358
+ }))),
359
+ apply: (input) => runKnowledgeProjectionAdapter({
360
+ bundles: input.contributors,
361
+ config,
362
+ instructionsTarget,
363
+ }).pipe(Effect.asVoid),
364
+ },
365
+ });
366
+ });
367
+ const projectionPlans = () => makeKnowledgeProjectionPlan().pipe(Effect.map((plan) => [plan]));
368
+ 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((bundles) => runKnowledgeProjectionAdapter({
370
+ bundles,
371
+ config,
372
+ instructionsTarget,
373
+ ...(options?.dryRun === undefined ? {} : { dryRun: options.dryRun }),
374
+ })))));
375
+ const buildLockEntry = (ref) => Effect.gen(function* () {
376
+ const state = lastInstallState.get(ref.knowledge.name);
377
+ switch (ref.refType) {
378
+ case "registry":
379
+ return state?.treeIntegrity === undefined
380
+ ? yield* new KnowledgeInstallStateMissing({
381
+ name: ref.knowledge.name,
382
+ kind: "tree-integrity",
383
+ })
384
+ : Option.some(registryLockEntry(ref, state.treeIntegrity));
385
+ case "git-hosted":
386
+ return state?.treeIntegrity === undefined
387
+ ? yield* new KnowledgeInstallStateMissing({
388
+ name: ref.knowledge.name,
389
+ kind: "content-identity",
390
+ })
391
+ : Option.some(gitLockEntry(ref, state.sourceHash, state.treeIntegrity));
392
+ case "local":
393
+ return state?.treeIntegrity === undefined
394
+ ? yield* new KnowledgeInstallStateMissing({
395
+ name: ref.knowledge.name,
396
+ kind: "content-identity",
397
+ })
398
+ : Option.some(localLockEntry(ref, state.relativeLocalSource, state.sourceHash, state.treeIntegrity));
399
+ case "workspace":
400
+ return Option.none();
401
+ }
402
+ });
403
+ const setKnowledgeSourceEntry = (name, source) => Effect.gen(function* () {
404
+ const configured = yield* ws.getConfiguredKnowledgeEntries();
405
+ const current = configured[name];
406
+ yield* ws.setKnowledgeEntry(name, {
407
+ source,
408
+ enabled: true,
409
+ ...(current?.instructionEntry === undefined
410
+ ? {}
411
+ : { instructionEntry: current.instructionEntry }),
412
+ });
413
+ });
414
+ const restoreLockedPackage = (name, entry) => Effect.gen(function* () {
415
+ const ref = yield* knowledgeLockEntryToRef(name, entry, {
416
+ baseDir,
417
+ path,
418
+ scope: ws.scope,
419
+ getConfiguredSourceByName: (sourceName) => ws.getConfiguredSourceByName(sourceName),
420
+ });
421
+ if (ref.refType === "registry" && Option.isNone(ref.integrity)) {
422
+ return yield* new KnowledgeUnavailable({
423
+ detail: `Locked registry Knowledge bundle has no integrity and cannot be restored: ${name}`,
424
+ });
425
+ }
426
+ if (ref.refType !== "git-hosted") {
427
+ return yield* preparePackage(ref);
428
+ }
429
+ const discovered = yield* sources
430
+ .find(ref.source, {
431
+ names: [name],
432
+ type: "knowledge",
433
+ owner: Option.none(),
434
+ versionRange: Option.none(),
435
+ })
436
+ .pipe(Effect.mapError(coupleLifecycleDependencyFailure));
437
+ const candidate = discovered.find((item) => item.type === "knowledge" && item.refType === "git-hosted");
438
+ if (candidate === undefined ||
439
+ candidate.gitTreeSha !== ref.gitTreeSha ||
440
+ candidate.gitCommitSha !== ref.gitCommitSha) {
441
+ return yield* new KnowledgeUnavailable({
442
+ detail: `Git Knowledge source no longer resolves to locked commit/tree ${ref.gitCommitSha}/${ref.gitTreeSha}: ${name}`,
443
+ });
444
+ }
445
+ return yield* preparePackage(candidate);
446
+ });
447
+ const syncLocked = (dryRun) => Effect.gen(function* () {
448
+ const desired = yield* activeKnowledgeNodes();
449
+ const locked = yield* getKnowledgeLockEntries(ws);
450
+ const prepared = [];
451
+ for (const node of desired) {
452
+ const { name } = node;
453
+ const entry = locked[name];
454
+ const root = yield* desiredCanonicalRoot(node, entry);
455
+ const inspection = yield* Effect.result(inspectPackage(root));
456
+ if (Result.isSuccess(inspection))
457
+ continue;
458
+ if (node.identity.startsWith("workspace:")) {
459
+ return yield* new KnowledgeDefinitionInvalid({
460
+ detail: `Active workspace-authored Knowledge bundle is missing or invalid: ${name}`,
461
+ cause: inspection.failure,
462
+ });
463
+ }
464
+ if (entry === undefined) {
465
+ return yield* new KnowledgeResolutionMissing({ name });
466
+ }
467
+ const restored = yield* Effect.result(restoreLockedPackage(name, entry));
468
+ if (Result.isFailure(restored)) {
469
+ yield* Effect.forEach([...prepared].reverse(), (item) => item.rollback, {
470
+ discard: true,
471
+ });
472
+ const restoreFailure = restored.failure;
473
+ return yield* new KnowledgeUnavailable({
474
+ detail: `Active Knowledge bundle could not be restored: ${name}. ${adapter.describeFailure(restoreFailure)}`,
475
+ cause: restoreFailure,
476
+ });
477
+ }
478
+ else
479
+ prepared.push(restored.success);
480
+ }
481
+ const discovered = yield* Effect.result(reconcileDiscovery({ dryRun: true }));
482
+ if (Result.isFailure(discovered)) {
483
+ yield* Effect.forEach([...prepared].reverse(), (item) => item.rollback, {
484
+ discard: true,
485
+ });
486
+ return yield* discovered.failure;
487
+ }
488
+ if (!dryRun)
489
+ yield* applyKnowledgeProjection;
490
+ yield* Effect.forEach(dryRun ? [...prepared].reverse() : prepared, (item) => (dryRun ? item.rollback : item.commit), { discard: true });
491
+ const discovery = discovered.success;
492
+ return {
493
+ changed: prepared.length > 0 || discovery.changed,
494
+ warnings: [],
495
+ artifacts: discovery.artifacts,
496
+ };
497
+ });
498
+ const installAtomically = (args) => ws
499
+ .runTransaction({
500
+ transition: Effect.gen(function* () {
501
+ const name = args.ref.knowledge.name;
502
+ const relativeLocalSource = args.ref.refType === "local"
503
+ ? makeWorkspaceRelativeSourcePath(path, baseDir, args.ref.sourcePath ?? stripFileProtocol(args.ref.location))
504
+ : Option.none();
505
+ if (args.ref.refType === "local" && Option.isNone(relativeLocalSource)) {
506
+ return yield* new KnowledgeDefinitionInvalid({
507
+ detail: `Local knowledge source must stay within the workspace: ${args.ref.source.path}`,
508
+ });
509
+ }
510
+ const cleanupSupersededCanonical = yield* provide(prepareAcceptedCanonicalTransition({
511
+ workspace: ws,
512
+ type: "knowledge",
513
+ name,
514
+ ref: args.ref,
515
+ }));
516
+ const prepared = yield* preparePackage(args.ref);
517
+ const committed = yield* Effect.gen(function* () {
518
+ lastInstallState.set(name, {
519
+ relativeLocalSource,
520
+ sourceHash: prepared.sourceHash,
521
+ ...(prepared.treeIntegrity === undefined
522
+ ? {}
523
+ : { treeIntegrity: prepared.treeIntegrity }),
524
+ });
525
+ if (args.ref.refType !== "workspace" && prepared.treeIntegrity === undefined) {
526
+ return yield* new KnowledgeInstallStateMissing({
527
+ name,
528
+ kind: "staged-tree-integrity",
529
+ });
530
+ }
531
+ const lockEntry = yield* buildLockEntry(args.ref);
532
+ if (Option.isSome(lockEntry)) {
533
+ yield* ws.setKnowledge({
534
+ name,
535
+ lockEntry: lockEntry.value,
536
+ versionRange: args.versionRange,
537
+ });
538
+ }
539
+ else {
540
+ yield* setKnowledgeSourceEntry(name, "workspace");
541
+ }
542
+ yield* cleanupSupersededCanonical;
543
+ return { name };
544
+ });
545
+ if (args.deferProjection !== true)
546
+ yield* applyKnowledgeProjection;
547
+ // The workspace transaction owns a nested scope. Settle the staged
548
+ // package before that scope closes; a later postcondition failure
549
+ // still restores the transaction snapshot.
550
+ yield* prepared.commit;
551
+ return committed;
552
+ }),
553
+ validate: ({ name }) => isObservedInstalled(ws, "knowledge", name).pipe(Effect.flatMap((installed) => installed ? Effect.void : new KnowledgeObservableContractViolated({ name }))),
554
+ })
555
+ .pipe(Effect.scoped, Effect.tapError(() => Effect.sync(() => {
556
+ lastInstallState.delete(args.ref.knowledge.name);
557
+ })), Effect.asVoid);
558
+ // Canonical removal only. The shared operation flow re-renders the
559
+ // discovery region after settings and lock removal, once the target has
560
+ // left the graph.
561
+ const materializeUninstall = Effect.fn("KnowledgeManager.materializeUninstall")(function* ({ target }) {
562
+ const canonical = yield* provide(acceptedCanonicalObservation({
563
+ workspace: ws,
564
+ type: "knowledge",
565
+ name: target.name,
566
+ }));
567
+ const root = removableAcceptedCanonicalPath(canonical);
568
+ const locked = yield* ws.getLockedKnowledgeEntry(target.name);
569
+ const ownedRoot = Option.orElse(root, () => Option.map(locked, (entry) => canonicalRoot(target.name, entry)));
570
+ if (Option.isSome(ownedRoot)) {
571
+ yield* protectWorkspacePath(ownedRoot.value);
572
+ yield* fs.remove(ownedRoot.value, { recursive: true, force: true }).pipe(Effect.mapError((cause) => new KnowledgeIoFailed({
573
+ detail: `Failed to remove Knowledge package source: ${ownedRoot.value}`,
574
+ cause,
575
+ })));
576
+ }
577
+ }, Effect.asVoid);
578
+ // Deactivation retains canonical content; the caller updates settings
579
+ // first, so re-rendering the whole region drops this bundle's routing.
580
+ const materializeDeactivate = Effect.fn("KnowledgeManager.materializeDeactivate")(() => applyKnowledgeProjection);
581
+ return {
582
+ type: "knowledge",
583
+ projectionPlans,
584
+ runTransaction: ws.runTransaction,
585
+ refreshCatalog: () => ws.runTransaction({
586
+ transition: applyKnowledgeProjection,
587
+ validate: () => Effect.void,
588
+ }),
589
+ sync: ({ dryRun }) => dryRun
590
+ ? Effect.scoped(syncLocked(true))
591
+ : Effect.scoped(ws.runTransaction({
592
+ transition: syncLocked(false),
593
+ validate: () => Effect.void,
594
+ })),
595
+ install: installAtomically,
596
+ isInstalled: ({ target }) => isObservedInstalled(ws, "knowledge", target.name),
597
+ materializeInstall: Effect.fn("KnowledgeManager.materializeInstall")(function* ({ ref, force }) {
598
+ const relativeLocalSource = ref.refType === "local"
599
+ ? makeWorkspaceRelativeSourcePath(path, baseDir, ref.sourcePath ?? stripFileProtocol(ref.location))
600
+ : Option.none();
601
+ if (ref.refType === "local" && Option.isNone(relativeLocalSource)) {
602
+ return yield* new KnowledgeDefinitionInvalid({
603
+ detail: `Local knowledge source must stay within the workspace: ${ref.source.path}`,
604
+ });
605
+ }
606
+ const prepared = yield* preparePackage(ref, force === true);
607
+ yield* prepared.commit;
608
+ lastInstallState.set(ref.knowledge.name, {
609
+ relativeLocalSource,
610
+ sourceHash: prepared.sourceHash,
611
+ ...(prepared.treeIntegrity === undefined
612
+ ? {}
613
+ : { treeIntegrity: prepared.treeIntegrity }),
614
+ });
615
+ }, Effect.asVoid, Effect.scoped),
616
+ prepareSourceTransition: ({ ref }) => provide(prepareAcceptedCanonicalTransition({
617
+ workspace: ws,
618
+ type: "knowledge",
619
+ name: ref.knowledge.name,
620
+ ref,
621
+ })),
622
+ getConfiguredSource: ({ target }) => ws
623
+ .getConfiguredKnowledgeEntries()
624
+ .pipe(Effect.map((entries) => Option.fromUndefinedOr(entries[target.name]?.source))),
625
+ listMaterializable: () => Effect.gen(function* () {
626
+ const nodes = yield* activeKnowledgeNodes();
627
+ const releaseAgeEvaluation = yield* provide(makeConfiguredReleaseAgeEvaluation("enforce")).pipe(Effect.mapError(coupleLifecycleDependencyFailure));
628
+ return yield* Effect.scoped(Effect.forEach(nodes, (node) => provide(resolveConfiguredKnowledge(node.name, node.source, releaseAgeEvaluation)).pipe(Effect.mapError(coupleLifecycleDependencyFailure), Effect.map(({ ref }) => ref)), { concurrency: "unbounded" }));
629
+ }),
630
+ materializeUninstall,
631
+ materializeDeactivate,
632
+ upsertSettingsEntry: ({ ref, versionRange }) => buildLockEntry(ref).pipe(Effect.flatMap((lockEntry) => Option.isSome(lockEntry)
633
+ ? ws.setKnowledge({
634
+ name: ref.knowledge.name,
635
+ lockEntry: lockEntry.value,
636
+ versionRange,
637
+ })
638
+ : setKnowledgeSourceEntry(ref.knowledge.name, "workspace"))),
639
+ removeSettingsEntry: ({ target }) => ws.removeKnowledgeSettings(target.name),
640
+ upsertLockfileEntry: ({ ref }) => buildLockEntry(ref).pipe(Effect.flatMap((lockEntry) => {
641
+ if (Option.isNone(lockEntry))
642
+ return ws.removeKnowledgeLock(ref.knowledge.name);
643
+ const validate = lockEntry.value.type === "registry"
644
+ ? validateExactResolvedVersion(`knowledge.${ref.knowledge.name}.resolvedVersion`, lockEntry.value.resolvedVersion)
645
+ : Effect.void;
646
+ return validate.pipe(Effect.flatMap(() => ws.setKnowledgeLock({
647
+ name: ref.knowledge.name,
648
+ lockEntry: lockEntry.value,
649
+ versionRange: Option.none(),
650
+ })));
651
+ })),
652
+ removeLockfileEntry: ({ target }) => ws.removeKnowledgeLock(target.name),
653
+ };
654
+ }));
655
+ //# sourceMappingURL=manager.js.map
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Environment-backed layers of the extension-lifecycle feature: the seven
3
+ * per-type manager implementations behind the extension-workspace manager
4
+ * contracts, and the hook-backed configured-agent-outcomes provider. Only
5
+ * application composition roots import this module.
6
+ *
7
+ * @experimental All exports from this module are unstable and may change without notice.
8
+ * @packageDocumentation
9
+ */
10
+ export { HookManagerLive } from "./hooks/manager.js";
11
+ export { HookConfiguredAgentOutcomesProviderLive } from "./hooks/configured-agent-outcomes-provider.js";
12
+ export { RuleManagerLive } from "./rules/manager.js";
13
+ export { SkillManagerLive } from "./skills/manager.js";
14
+ export { SubagentManagerLive } from "./subagents/manager.js";
15
+ export { McpServerManagerLive } from "./mcps/manager.js";
16
+ export { PackManagerLive } from "./packs/manager.js";
17
+ export { KnowledgeManagerLive } from "./knowledge/manager.js";
18
+ //# sourceMappingURL=live.d.ts.map