@agentxm/workspace-state 0.29.4 → 0.30.2
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/index.d.ts +4 -1
- package/dist/src/index.js +3 -0
- package/dist/src/lockfile/lockfile.d.ts +5 -5
- package/dist/src/lockfile/lockfile.js +2 -1
- package/dist/src/workspace/accepted-canonical-ref.d.ts +3 -1
- package/dist/src/workspace/accepted-canonical-ref.js +9 -6
- package/dist/src/workspace/accepted-resolution-writer.js +1 -3
- package/dist/src/workspace/desired-pack-lock.d.ts +3 -2
- package/dist/src/workspace/desired-pack-lock.js +21 -20
- package/dist/src/workspace/desired-state-graph.js +4 -2
- package/dist/src/workspace/desired-state-reader.d.ts +9 -3
- package/dist/src/workspace/desired-state-reader.js +10 -3
- package/dist/src/workspace/install-root.d.ts +69 -0
- package/dist/src/workspace/install-root.js +149 -0
- package/dist/src/workspace/read-model/extensions/inventory.d.ts +21 -4
- package/dist/src/workspace/read-model/extensions/inventory.js +35 -13
- package/dist/src/workspace/read-model/scanners/canonical-extensions.js +2 -1
- package/dist/src/workspace/read-model/service.d.ts +3 -0
- package/dist/src/workspace/read-model/service.js +1 -0
- package/dist/src/workspace/read-model-record-readers.js +49 -20
- package/dist/src/workspace/service-interface.d.ts +4 -5
- package/dist/src/workspace/test-stubs.d.ts +2 -0
- package/dist/src/workspace/test-stubs.js +4 -0
- package/package.json +14 -17
package/dist/src/index.d.ts
CHANGED
|
@@ -22,6 +22,7 @@ export { SETTINGS_KNOWN_KEYS } from "./settings/schema.js";
|
|
|
22
22
|
export { gitSourceLockFields } from "./lockfile/entry-fields.js";
|
|
23
23
|
export { LOCK_ENTRY_SCHEMA_BY_TYPE } from "./lockfile/schema.js";
|
|
24
24
|
export { ACQUIRED_EXTENSIONS_DIR, LOCK_FILENAME } from "./workspace/constants.js";
|
|
25
|
+
export { isInstallRootStagingName, observeInstallRoot, type InstallRootEntry, type InstallRootInventory, type InstalledPackageEntry, type ObserveInstallRootArgs, type UnrecognizedInstallRootEntry, } from "./workspace/install-root.js";
|
|
25
26
|
export { acquiredExtensionDisplayPath, acquiredExtensionDisplayPathFromLockEntry, computeExtensionPathsForLayout, extensionPathSourceFromLockEntry, extensionContentFilename, extensionContentPath, type ExtensionPathLockEntry, type ExtensionPathSource, type ExtensionDirPaths, } from "./workspace/extension-paths.js";
|
|
26
27
|
export { RenderedFilePathSchema, RenderedFilesMapSchema, computeSourceHash, type RenderedFilePath, type RenderedFilesMap, } from "./workspace/rendered-files.js";
|
|
27
28
|
export { computePackageContentHash } from "./workspace/package-hash.js";
|
|
@@ -67,16 +68,18 @@ export { WorkspaceLocation, type WorkspaceLocationService } from "./workspace/lo
|
|
|
67
68
|
export { SettingsReader, type SettingsReaderService } from "./workspace/settings-reader.js";
|
|
68
69
|
export { LockfileReader, type LockfileReaderService } from "./workspace/lockfile-reader.js";
|
|
69
70
|
export { readOtherScopeState, type OtherScopeState } from "./workspace/other-scope-reader.js";
|
|
70
|
-
export { DesiredStateReader, type DesiredStateReaderService, } from "./workspace/desired-state-reader.js";
|
|
71
|
+
export { DesiredStateReader, type DesiredStateReaderService, type DesiredStateGraphInputs, } from "./workspace/desired-state-reader.js";
|
|
71
72
|
export { WorkspaceRecords, type WorkspaceRecordsService } from "./workspace/workspace-records.js";
|
|
72
73
|
export { ExtensionPaths, type ExtensionPathsService } from "./workspace/extension-paths-service.js";
|
|
73
74
|
export { SettingsWriter, type SettingsWriterService } from "./workspace/settings-writer.js";
|
|
74
75
|
export { AcceptedResolutionWriter, type AcceptedResolutionWriterService, } from "./workspace/accepted-resolution-writer.js";
|
|
75
76
|
export { DesiredStateWriter, type DeclareArgsByType, type DesiredStateWriterService, } from "./workspace/desired-state-writer.js";
|
|
77
|
+
export { settingsEntries, lockEntries } from "./workspace/entry-accessors.js";
|
|
76
78
|
export type { LockEntriesOf, LockEntryByType, SettingsEntriesOf, SettingsEntryByType, } from "./workspace/entry-accessors.js";
|
|
77
79
|
export { WorkspaceMutations, type WorkspaceMutationsService, type WorkspaceMutationsError, type WorkspaceMutationsOptions, type WorkspaceSettingsReadFailure, type WorkspaceLockfileReadFailure, type WorkspaceStateReadFailure, type WorkspaceSettingsMutationFailure, type WorkspaceLockfileMutationFailure, type WorkspaceStateMutationFailure, type SetSkillArgs, type SetPackArgs, type SetMcpServerArgs, type SetSubagentArgs, type SetRuleArgs, type SetHookArgs, type SetKnowledgeArgs, type PackDirPath, type ExtensionTarget, type ExtensionTargetFor, type LockfileState, type SkillExtensionTarget, type PackExtensionTarget, type McpServerExtensionTarget, type SubagentExtensionTarget, type RuleExtensionTarget, type HookExtensionTarget, type KnowledgeExtensionTarget, } from "./workspace/service-interface.js";
|
|
78
80
|
export { LockfileDecodeError, LockfileIoError, LockfileParseError, LockfileVersionUnsupported, SettingsDecodeError, SettingsIoError, SettingsParseError, SkillDiscoveryRootInvalid, SubagentScanFailed, WorkspaceRootEscape, type LockfileReadError, type SettingsReadError, } from "./workspace/read-model/errors.js";
|
|
79
81
|
export { AcceptedResolutionMissing, CanonicalPathRemovalError, DesiredPackGraphIncomplete, InlineExtensionSourceMissing, InvalidAgentId, LockedSkillMissing, LockEntryEndpointConflict, LockEntryNameInvalid, LockEntrySourceMissing, LockEntrySourceTypeConflict, LockEntryUrlInvalid, PackageContentHashFailed, SettingsEntryMissing, SupersededCanonicalRemovalFailed, SymlinkCreationError, WorkspaceLayoutError, WorkspaceNotInitialized, WorkspaceSourceInvalid, } from "./workspace/errors.js";
|
|
80
82
|
export { setupScopeSupport, setupScopeSupportOutcomes, type SetupScopeSupportCategory, type SetupScopeSupportOutcome, type SetupScopeSupportReasonCode, type SetupScopeSupportStatus, } from "./workspace/setup-scope-support.js";
|
|
81
83
|
export { configuredAgentLifecycleOutcomes, EXTENSION_CONFIGURED_AGENT_POLICY, type ConfiguredAgentLifecycleState, } from "./workspace/configured-agent-outcomes.js";
|
|
84
|
+
export { lockEntrySemanticallyEqual } from "./workspace/accepted-resolution-writer.js";
|
|
82
85
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/src/index.js
CHANGED
|
@@ -27,6 +27,7 @@ export { gitSourceLockFields } from "./lockfile/entry-fields.js";
|
|
|
27
27
|
export { LOCK_ENTRY_SCHEMA_BY_TYPE } from "./lockfile/schema.js";
|
|
28
28
|
// Extension path and identity vocabulary
|
|
29
29
|
export { ACQUIRED_EXTENSIONS_DIR, LOCK_FILENAME } from "./workspace/constants.js";
|
|
30
|
+
export { isInstallRootStagingName, observeInstallRoot, } from "./workspace/install-root.js";
|
|
30
31
|
export { acquiredExtensionDisplayPath, acquiredExtensionDisplayPathFromLockEntry, computeExtensionPathsForLayout, extensionPathSourceFromLockEntry, extensionContentFilename, extensionContentPath, } from "./workspace/extension-paths.js";
|
|
31
32
|
export { RenderedFilePathSchema, RenderedFilesMapSchema, computeSourceHash, } from "./workspace/rendered-files.js";
|
|
32
33
|
export { computePackageContentHash } from "./workspace/package-hash.js";
|
|
@@ -85,6 +86,7 @@ export { ExtensionPaths } from "./workspace/extension-paths-service.js";
|
|
|
85
86
|
export { SettingsWriter } from "./workspace/settings-writer.js";
|
|
86
87
|
export { AcceptedResolutionWriter, } from "./workspace/accepted-resolution-writer.js";
|
|
87
88
|
export { DesiredStateWriter, } from "./workspace/desired-state-writer.js";
|
|
89
|
+
export { settingsEntries, lockEntries } from "./workspace/entry-accessors.js";
|
|
88
90
|
// TRANSITIONAL workspace mutation facade. No CLI handler composes it any
|
|
89
91
|
// more; the domain packages under packages/core still do, and it is removed
|
|
90
92
|
// when the last of them reads and writes through the narrow services above.
|
|
@@ -95,4 +97,5 @@ export { LockfileDecodeError, LockfileIoError, LockfileParseError, LockfileVersi
|
|
|
95
97
|
export { AcceptedResolutionMissing, CanonicalPathRemovalError, DesiredPackGraphIncomplete, InlineExtensionSourceMissing, InvalidAgentId, LockedSkillMissing, LockEntryEndpointConflict, LockEntryNameInvalid, LockEntrySourceMissing, LockEntrySourceTypeConflict, LockEntryUrlInvalid, PackageContentHashFailed, SettingsEntryMissing, SupersededCanonicalRemovalFailed, SymlinkCreationError, WorkspaceLayoutError, WorkspaceNotInitialized, WorkspaceSourceInvalid, } from "./workspace/errors.js";
|
|
96
98
|
export { setupScopeSupport, setupScopeSupportOutcomes, } from "./workspace/setup-scope-support.js";
|
|
97
99
|
export { configuredAgentLifecycleOutcomes, EXTENSION_CONFIGURED_AGENT_POLICY, } from "./workspace/configured-agent-outcomes.js";
|
|
100
|
+
export { lockEntrySemanticallyEqual } from "./workspace/accepted-resolution-writer.js";
|
|
98
101
|
//# sourceMappingURL=index.js.map
|
|
@@ -38,8 +38,8 @@ export type LockfileUpdate = (lockfile: Lockfile) => Lockfile;
|
|
|
38
38
|
*
|
|
39
39
|
* @experimental This API is unstable and may change without notice.
|
|
40
40
|
*/
|
|
41
|
-
export declare const writeLockfileAtPath: (lockfilePath: string, lockfile: Lockfile) => Effect.Effect<void, LockfileWriteError, FileSystem.FileSystem | Path.Path>;
|
|
42
|
-
export declare const writeLockfile: (axmDir: string, lockfile: Lockfile) => Effect.Effect<void, LockfileWriteError, FileSystem.FileSystem | Path.Path>;
|
|
41
|
+
export declare const writeLockfileAtPath: (lockfilePath: string, lockfile: Lockfile) => Effect.Effect<void, import("@agentxm/workspace-transactions").WorkspaceSnapshotError | LockfileWriteError, FileSystem.FileSystem | Path.Path>;
|
|
42
|
+
export declare const writeLockfile: (axmDir: string, lockfile: Lockfile) => Effect.Effect<void, import("@agentxm/workspace-transactions").WorkspaceSnapshotError | LockfileWriteError, FileSystem.FileSystem | Path.Path>;
|
|
43
43
|
/**
|
|
44
44
|
* Applies lockfile updates in order without writing to disk.
|
|
45
45
|
*
|
|
@@ -782,7 +782,7 @@ export declare const commitLockfileUpdates: (axmDir: string, lockfile: Lockfile,
|
|
|
782
782
|
readonly treeIntegrity: string & import("effect/Brand").Brand<"TreeIntegrity">;
|
|
783
783
|
};
|
|
784
784
|
} | undefined;
|
|
785
|
-
}, LockfileWriteError | LockfileValidationError, FileSystem.FileSystem | Path.Path>;
|
|
785
|
+
}, import("@agentxm/workspace-transactions").WorkspaceSnapshotError | LockfileWriteError | LockfileValidationError, FileSystem.FileSystem | Path.Path>;
|
|
786
786
|
/**
|
|
787
787
|
* Commits a caller-computed lockfile snapshot as an entry-level patch.
|
|
788
788
|
*
|
|
@@ -1522,7 +1522,7 @@ export declare const commitLockfileSnapshotUpdateAtPath: (lockfilePath: string,
|
|
|
1522
1522
|
readonly treeIntegrity: string & import("effect/Brand").Brand<"TreeIntegrity">;
|
|
1523
1523
|
};
|
|
1524
1524
|
} | undefined;
|
|
1525
|
-
}, LockfileWriteError | LockfileValidationError, FileSystem.FileSystem | Path.Path>;
|
|
1525
|
+
}, import("@agentxm/workspace-transactions").WorkspaceSnapshotError | LockfileWriteError | LockfileValidationError, FileSystem.FileSystem | Path.Path>;
|
|
1526
1526
|
export declare const commitLockfileSnapshotUpdate: (axmDir: string, base: Lockfile, next: Lockfile) => Effect.Effect<{
|
|
1527
1527
|
readonly skills: {
|
|
1528
1528
|
readonly [x: string]: {
|
|
@@ -2251,5 +2251,5 @@ export declare const commitLockfileSnapshotUpdate: (axmDir: string, base: Lockfi
|
|
|
2251
2251
|
readonly treeIntegrity: string & import("effect/Brand").Brand<"TreeIntegrity">;
|
|
2252
2252
|
};
|
|
2253
2253
|
} | undefined;
|
|
2254
|
-
}, LockfileWriteError | LockfileValidationError, FileSystem.FileSystem | Path.Path>;
|
|
2254
|
+
}, import("@agentxm/workspace-transactions").WorkspaceSnapshotError | LockfileWriteError | LockfileValidationError, FileSystem.FileSystem | Path.Path>;
|
|
2255
2255
|
//# sourceMappingURL=lockfile.d.ts.map
|
|
@@ -14,7 +14,7 @@ import * as Effect from "effect/Effect";
|
|
|
14
14
|
import * as Schema from "effect/Schema";
|
|
15
15
|
import * as Semaphore from "effect/Semaphore";
|
|
16
16
|
import YAML from "yaml";
|
|
17
|
-
import { recordFootprint, sweepStaleAtomicWriteTemps, writeFileAtomic, } from "@agentxm/workspace-transactions";
|
|
17
|
+
import { protectWorkspacePath, recordFootprint, sweepStaleAtomicWriteTemps, writeFileAtomic, } from "@agentxm/workspace-transactions";
|
|
18
18
|
import { LockfileValidationError, LockfileWriteError } from "./errors.js";
|
|
19
19
|
import { LOCKFILE_VERSION, LockfileSchema } from "./schema.js";
|
|
20
20
|
// -----------------------------------------------------------------------------
|
|
@@ -132,6 +132,7 @@ const withLockfileLock = (lockfilePath, effect) => Effect.gen(function* () {
|
|
|
132
132
|
const writeLockfileUnlocked = (lockfilePath, lockfile) => Effect.gen(function* () {
|
|
133
133
|
const fs = yield* FileSystem.FileSystem;
|
|
134
134
|
const yamlContent = yield* encodeLockfileYaml(lockfilePath, lockfile);
|
|
135
|
+
yield* protectWorkspacePath(lockfilePath);
|
|
135
136
|
const existed = yield* fs.exists(lockfilePath).pipe(Effect.orElseSucceed(() => true));
|
|
136
137
|
yield* sweepStaleAtomicWriteTemps(fs, lockfilePath);
|
|
137
138
|
const written = yield* writeFileAtomic(fs, {
|
|
@@ -14,6 +14,8 @@ interface AcceptedCanonicalRefArgs {
|
|
|
14
14
|
readonly workspace: WorkspaceMutationsService;
|
|
15
15
|
readonly type: DesiredExtensionNode["type"];
|
|
16
16
|
readonly name: string;
|
|
17
|
+
/** Proposed authority when preparing a transition before publishing intent. */
|
|
18
|
+
readonly desired?: DesiredExtensionNode;
|
|
17
19
|
}
|
|
18
20
|
/** Every failure the accepted-canonical reconstruction functions can produce. */
|
|
19
21
|
export type AcceptedCanonicalRefError = WorkspaceStateReadFailure | LockEntryToRefError | AcceptedResolutionMissing | InlineExtensionSourceMissing | WorkspaceSourceInvalid | PathTraversalDetected | PackageContentHashFailed;
|
|
@@ -50,7 +52,7 @@ export declare const acceptedLockedResolutionRef: (args: AcceptedCanonicalRefArg
|
|
|
50
52
|
* even when its canonical materialization is absent or divergent.
|
|
51
53
|
*/
|
|
52
54
|
export declare const acceptedResolutionRef: (args: AcceptedCanonicalRefArgs) => Effect.Effect<Option.Option<ExtensionRef>, AcceptedCanonicalRefError, FileSystem.FileSystem | Path.Path>;
|
|
53
|
-
export declare const acceptedCanonicalObservation: ({ workspace, type, name, }: AcceptedCanonicalRefArgs) => Effect.Effect<Option.Option<AcceptedCanonicalObservation>, AcceptedCanonicalRefError, FileSystem.FileSystem | Path.Path>;
|
|
55
|
+
export declare const acceptedCanonicalObservation: ({ workspace, type, name, desired: proposed, }: AcceptedCanonicalRefArgs) => Effect.Effect<Option.Option<AcceptedCanonicalObservation>, AcceptedCanonicalRefError, FileSystem.FileSystem | Path.Path>;
|
|
54
56
|
export declare const usableAcceptedCanonicalObservation: (args: AcceptedCanonicalRefArgs) => Effect.Effect<Option.Option<UsableAcceptedCanonicalObservation>, AcceptedCanonicalRefError, FileSystem.FileSystem | Path.Path>;
|
|
55
57
|
export declare const usableAcceptedCanonical: (args: AcceptedCanonicalRefArgs) => Effect.Effect<Option.Option<UsableAcceptedCanonical>, AcceptedCanonicalRefError, FileSystem.FileSystem | Path.Path>;
|
|
56
58
|
export declare const usableAcceptedCanonicalRef: (args: AcceptedCanonicalRefArgs) => Effect.Effect<Option.Option<ExtensionRef>, AcceptedCanonicalRefError, FileSystem.FileSystem | Path.Path>;
|
|
@@ -188,16 +188,16 @@ const refForDesired = (workspace, desired) => Effect.gen(function* () {
|
|
|
188
188
|
* even when its canonical materialization is absent or divergent.
|
|
189
189
|
*/
|
|
190
190
|
export const acceptedResolutionRef = (args) => Effect.gen(function* () {
|
|
191
|
-
const
|
|
192
|
-
|
|
191
|
+
const desired = args.desired ??
|
|
192
|
+
(yield* args.workspace.getDesiredStateGraph()).nodes.find((node) => node.type === args.type && node.name === args.name);
|
|
193
193
|
if (desired === undefined || desired.identity.startsWith("workspace:")) {
|
|
194
194
|
return Option.none();
|
|
195
195
|
}
|
|
196
196
|
return yield* acceptedLockedResolutionRef(args);
|
|
197
197
|
});
|
|
198
|
-
export const acceptedCanonicalObservation = ({ workspace, type, name, }) => Effect.gen(function* () {
|
|
199
|
-
const
|
|
200
|
-
|
|
198
|
+
export const acceptedCanonicalObservation = ({ workspace, type, name, desired: proposed, }) => Effect.gen(function* () {
|
|
199
|
+
const desired = proposed ??
|
|
200
|
+
(yield* workspace.getDesiredStateGraph()).nodes.find((node) => node.type === type && node.name === name);
|
|
201
201
|
if (desired === undefined)
|
|
202
202
|
return Option.none();
|
|
203
203
|
const accepted = yield* getAcceptedResolution(workspace, type, name);
|
|
@@ -223,7 +223,10 @@ export const usableAcceptedCanonical = (args) => Effect.gen(function* () {
|
|
|
223
223
|
if (Option.isNone(canonical))
|
|
224
224
|
return Option.none();
|
|
225
225
|
const ref = yield* refForDesired(args.workspace, canonical.value.desired);
|
|
226
|
-
|
|
226
|
+
// A usable local acquisition is the accepted copy, even if the original
|
|
227
|
+
// directory has changed or disappeared. Keep sourcePath as its identity.
|
|
228
|
+
const retainedRef = ref.refType === "local" ? { ...ref, location: canonical.value.observation.path } : ref;
|
|
229
|
+
return Option.some({ ...canonical.value, ref: retainedRef });
|
|
227
230
|
});
|
|
228
231
|
export const usableAcceptedCanonicalRef = (args) => usableAcceptedCanonical(args).pipe(Effect.map(Option.map((canonical) => canonical.ref)));
|
|
229
232
|
//# sourceMappingURL=accepted-canonical-ref.js.map
|
|
@@ -37,8 +37,6 @@ export const stableCompare = (left, right) => JSON.stringify(normalizeForStableC
|
|
|
37
37
|
export const lockEntrySemanticallyEqual = (current, next) => current !== undefined && stableCompare(current, next);
|
|
38
38
|
/** Keep the recorded object when the replacement carries the same facts. */
|
|
39
39
|
export const preserveAcceptedResolutionOnNoop = (current, next) => (lockEntrySemanticallyEqual(current, next) && current !== undefined ? current : next);
|
|
40
|
-
/** Types whose semantically unchanged resolution skips the commit entirely. */
|
|
41
|
-
const skipsUnchangedCommit = (type) => type === "skill" || type === "subagent";
|
|
42
40
|
export const makeAcceptedResolutionWriter = (location, mutex) => {
|
|
43
41
|
const current = readLockfileCell(location, location.runtimeDir);
|
|
44
42
|
const commit = (base, next) => commitLockfileSnapshotUpdateAtPath(location.lockPath, base, next);
|
|
@@ -48,7 +46,7 @@ export const makeAcceptedResolutionWriter = (location, mutex) => {
|
|
|
48
46
|
const lockfile = yield* current;
|
|
49
47
|
const accessor = lockEntries[type];
|
|
50
48
|
const previous = accessor.entries(lockfile)[key];
|
|
51
|
-
if (
|
|
49
|
+
if (lockEntrySemanticallyEqual(previous, entry))
|
|
52
50
|
return;
|
|
53
51
|
yield* commit(lockfile, accessor.set(lockfile, key, preserveAcceptedResolutionOnNoop(previous, entry)));
|
|
54
52
|
})).pipe(Effect.withSpan("AcceptedResolutionWriter.setAccepted")),
|
|
@@ -2,17 +2,18 @@ import * as Effect from "effect/Effect";
|
|
|
2
2
|
import * as FileSystem from "effect/FileSystem";
|
|
3
3
|
import * as Path from "effect/Path";
|
|
4
4
|
import type { Lockfile } from "../lockfile/schema.js";
|
|
5
|
-
import { type DesiredStateGraph } from "./desired-state-graph.js";
|
|
5
|
+
import { type DesiredStateGraph, type ProspectivePackRef } from "./desired-state-graph.js";
|
|
6
6
|
import type { WorkspaceLayout } from "./layout.js";
|
|
7
7
|
interface ValidateDesiredPackLockArgs {
|
|
8
8
|
readonly graph: DesiredStateGraph;
|
|
9
9
|
readonly lockfile: Lockfile;
|
|
10
10
|
readonly layout: WorkspaceLayout;
|
|
11
|
+
readonly prospectivePacks?: ReadonlyArray<ProspectivePackRef>;
|
|
11
12
|
}
|
|
12
13
|
/**
|
|
13
14
|
* Authorize enabled external Pack manifests against their accepted lock row.
|
|
14
15
|
* Workspace-authored Pack manifests are desired authority and need no lock row.
|
|
15
16
|
*/
|
|
16
|
-
export declare const validateDesiredPackLock: ({ graph, lockfile, layout, }: ValidateDesiredPackLockArgs) => Effect.Effect<DesiredStateGraph, never, FileSystem.FileSystem | Path.Path>;
|
|
17
|
+
export declare const validateDesiredPackLock: ({ graph, lockfile, layout, prospectivePacks, }: ValidateDesiredPackLockArgs) => Effect.Effect<DesiredStateGraph, never, FileSystem.FileSystem | Path.Path>;
|
|
17
18
|
export {};
|
|
18
19
|
//# sourceMappingURL=desired-pack-lock.d.ts.map
|
|
@@ -52,7 +52,7 @@ const decodeManifest = Schema.decodeUnknownSync(PackManifestSchema, {
|
|
|
52
52
|
* Authorize enabled external Pack manifests against their accepted lock row.
|
|
53
53
|
* Workspace-authored Pack manifests are desired authority and need no lock row.
|
|
54
54
|
*/
|
|
55
|
-
export const validateDesiredPackLock = ({ graph, lockfile, layout, }) => Effect.gen(function* () {
|
|
55
|
+
export const validateDesiredPackLock = ({ graph, lockfile, layout, prospectivePacks = [], }) => Effect.gen(function* () {
|
|
56
56
|
const fs = yield* FileSystem.FileSystem;
|
|
57
57
|
const path = yield* Path.Path;
|
|
58
58
|
const problems = [];
|
|
@@ -82,24 +82,25 @@ export const validateDesiredPackLock = ({ graph, lockfile, layout, }) => Effect.
|
|
|
82
82
|
continue;
|
|
83
83
|
}
|
|
84
84
|
const manifestPath = path.join(computePackPathsForLayout(path.join, layout, entry.sourceName, identity.owner, identity.name).canonicalPath, PACK_MANIFEST_FILENAME);
|
|
85
|
-
const
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
85
|
+
const prospective = prospectivePacks.find((ref) => ref.owner === identity.owner && ref.pack.name === identity.name);
|
|
86
|
+
const observedManifest = prospective === undefined
|
|
87
|
+
? yield* Effect.gen(function* () {
|
|
88
|
+
const readResult = yield* Effect.result(fs.readFileString(manifestPath));
|
|
89
|
+
if (Result.isFailure(readResult))
|
|
90
|
+
return undefined;
|
|
91
|
+
const decoded = Result.try({
|
|
92
|
+
try: () => decodeManifest(JSON.parse(readResult.success)),
|
|
93
|
+
catch: () => undefined,
|
|
94
|
+
});
|
|
95
|
+
return Result.isSuccess(decoded) ? decoded.success : undefined;
|
|
96
|
+
})
|
|
97
|
+
: {
|
|
98
|
+
owner: prospective.owner,
|
|
99
|
+
type: "pack",
|
|
100
|
+
name: prospective.pack.name,
|
|
101
|
+
version: prospective.version,
|
|
102
|
+
dependencies: prospective.pack.dependencies,
|
|
103
|
+
};
|
|
103
104
|
const observedContentIdentity = observedManifest === undefined
|
|
104
105
|
? undefined
|
|
105
106
|
: computePackManifestContentIdentity(observedManifest);
|
|
@@ -109,7 +110,7 @@ export const validateDesiredPackLock = ({ graph, lockfile, layout, }) => Effect.
|
|
|
109
110
|
type: "pack-manifest-content-mismatch",
|
|
110
111
|
pack: node.identity,
|
|
111
112
|
path: manifestPath,
|
|
112
|
-
status: "changed",
|
|
113
|
+
status: observedManifest === undefined ? "missing" : "changed",
|
|
113
114
|
acceptedVersion: entry.resolvedVersion,
|
|
114
115
|
acceptedContentIdentity: entry.manifestContentIdentity,
|
|
115
116
|
...(observedManifest === undefined || observedContentIdentity === undefined
|
|
@@ -242,6 +242,8 @@ export const buildDesiredStateGraph = ({ baseDir, settings, layout, prospectiveP
|
|
|
242
242
|
});
|
|
243
243
|
const workspacePack = isWorkspaceSourceLocator(entry.source);
|
|
244
244
|
const configuredRegistrySource = registryLocator(entry.source)?.sourceName ?? "agentxm";
|
|
245
|
+
if (entry.enabled === false)
|
|
246
|
+
continue;
|
|
245
247
|
const manifestPath = path.join(layout === undefined
|
|
246
248
|
? path.join(baseDir, workspacePack
|
|
247
249
|
? configuredAuthoredDirectory(settings, "pack")
|
|
@@ -308,7 +310,7 @@ export const buildDesiredStateGraph = ({ baseDir, settings, layout, prospectiveP
|
|
|
308
310
|
identity: dependencyIdentity,
|
|
309
311
|
authority: "sourced",
|
|
310
312
|
source: `${fqn}@${constraint}`,
|
|
311
|
-
enabled:
|
|
313
|
+
enabled: true,
|
|
312
314
|
constraint,
|
|
313
315
|
origin: {
|
|
314
316
|
type: "pack",
|
|
@@ -316,7 +318,7 @@ export const buildDesiredStateGraph = ({ baseDir, settings, layout, prospectiveP
|
|
|
316
318
|
manifestPath: path.relative(baseDir, manifestPath),
|
|
317
319
|
source: fqn,
|
|
318
320
|
constraint,
|
|
319
|
-
enabled:
|
|
321
|
+
enabled: true,
|
|
320
322
|
},
|
|
321
323
|
});
|
|
322
324
|
}
|
|
@@ -11,15 +11,21 @@ import type * as FileSystem from "effect/FileSystem";
|
|
|
11
11
|
import * as Layer from "effect/Layer";
|
|
12
12
|
import type * as Path from "effect/Path";
|
|
13
13
|
import { type DesiredStateGraph, type ProspectivePackRef } from "./desired-state-graph.js";
|
|
14
|
+
import type { Settings } from "../settings/index.js";
|
|
15
|
+
import type { Lockfile } from "../lockfile/index.js";
|
|
14
16
|
import { DesiredPackGraphIncomplete } from "./errors.js";
|
|
15
17
|
import { WorkspaceLocation, type WorkspaceLocationService } from "./location.js";
|
|
16
18
|
import type { ExtensionTarget, WorkspaceStateReadFailure } from "./service-interface.js";
|
|
17
19
|
import { SettingsReader, type SettingsReaderService } from "./settings-reader.js";
|
|
20
|
+
/** Authoritative inputs for evaluating a candidate without publishing it. */
|
|
21
|
+
export interface DesiredStateGraphInputs {
|
|
22
|
+
readonly settings?: Settings;
|
|
23
|
+
readonly acceptedResolutions?: Lockfile;
|
|
24
|
+
readonly prospectivePacks?: ReadonlyArray<ProspectivePackRef>;
|
|
25
|
+
}
|
|
18
26
|
export interface DesiredStateReaderService {
|
|
19
27
|
/** Build desired extension state from settings and installed or prospective Pack manifests. */
|
|
20
|
-
readonly graph: (options?:
|
|
21
|
-
readonly prospectivePacks?: ReadonlyArray<ProspectivePackRef>;
|
|
22
|
-
}) => Effect.Effect<DesiredStateGraph, WorkspaceStateReadFailure, FileSystem.FileSystem | Path.Path>;
|
|
28
|
+
readonly graph: (options?: DesiredStateGraphInputs) => Effect.Effect<DesiredStateGraph, WorkspaceStateReadFailure, FileSystem.FileSystem | Path.Path>;
|
|
23
29
|
/** Whether an installed Pack's dependency maps reference the target. */
|
|
24
30
|
readonly isRequiredByInstalledPack: (target: ExtensionTarget) => Effect.Effect<boolean, WorkspaceStateReadFailure | DesiredPackGraphIncomplete, FileSystem.FileSystem | Path.Path>;
|
|
25
31
|
}
|
|
@@ -19,7 +19,7 @@ export class DesiredStateReader extends ServiceMap.Service()("@agentxm/workspace
|
|
|
19
19
|
}
|
|
20
20
|
export const makeDesiredStateReader = (location, settings) => {
|
|
21
21
|
const graph = (options) => Effect.gen(function* () {
|
|
22
|
-
const current = yield* settings.settings;
|
|
22
|
+
const current = options?.settings ?? (yield* settings.settings);
|
|
23
23
|
const configuredSources = yield* settings.configuredSources;
|
|
24
24
|
const layout = yield* Ref.get(location.layout);
|
|
25
25
|
const registryAuthorities = Object.fromEntries(configuredSources.flatMap((source) => source.type === "registry" ? [[source.name, source.location]] : []));
|
|
@@ -32,8 +32,15 @@ export const makeDesiredStateReader = (location, settings) => {
|
|
|
32
32
|
? {}
|
|
33
33
|
: { prospectivePacks: options.prospectivePacks }),
|
|
34
34
|
});
|
|
35
|
-
const lockfile = yield* readLockfileCell(location, location.runtimeDir);
|
|
36
|
-
return yield* validateDesiredPackLock({
|
|
35
|
+
const lockfile = options?.acceptedResolutions ?? (yield* readLockfileCell(location, location.runtimeDir));
|
|
36
|
+
return yield* validateDesiredPackLock({
|
|
37
|
+
graph: built,
|
|
38
|
+
lockfile,
|
|
39
|
+
layout,
|
|
40
|
+
...(options?.prospectivePacks === undefined
|
|
41
|
+
? {}
|
|
42
|
+
: { prospectivePacks: options.prospectivePacks }),
|
|
43
|
+
});
|
|
37
44
|
}).pipe(Effect.withSpan("DesiredStateReader.graph"));
|
|
38
45
|
return {
|
|
39
46
|
graph,
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Install-root inventory.
|
|
3
|
+
*
|
|
4
|
+
* Classifies every entry in a scope's install root (`agent_extensions`): an
|
|
5
|
+
* installed package whose path gives a well-formed extension identity or is
|
|
6
|
+
* proven by an accepted resolution, AXM's own interrupted staging, or an
|
|
7
|
+
* unrecognized entry. Each installed package records whether desired state
|
|
8
|
+
* still reaches it; one that nothing reaches is a leftover.
|
|
9
|
+
*
|
|
10
|
+
* Location inside the install root is AXM's install proof for canonical
|
|
11
|
+
* package directories only. It never proves ownership of agent projections.
|
|
12
|
+
*
|
|
13
|
+
* @experimental This API is unstable and may change without notice.
|
|
14
|
+
* @packageDocumentation
|
|
15
|
+
*/
|
|
16
|
+
import * as Effect from "effect/Effect";
|
|
17
|
+
import * as FileSystem from "effect/FileSystem";
|
|
18
|
+
import * as Path from "effect/Path";
|
|
19
|
+
import { type ExtensionType } from "@agentxm/extension-model/unstable/extensions";
|
|
20
|
+
import type { DesiredStateGraph } from "./desired-state-graph.js";
|
|
21
|
+
import type { WorkspaceLayout } from "./layout.js";
|
|
22
|
+
import type { LockfileReaderService } from "./lockfile-reader.js";
|
|
23
|
+
/** An installed package directory in the install root. */
|
|
24
|
+
export interface InstalledPackageEntry {
|
|
25
|
+
readonly kind: "package";
|
|
26
|
+
readonly type: ExtensionType;
|
|
27
|
+
readonly name: string;
|
|
28
|
+
/** The owner handle the path names, when the path gives one. */
|
|
29
|
+
readonly owner: string | undefined;
|
|
30
|
+
/** The source directory the package sits under, when the path gives one. */
|
|
31
|
+
readonly sourceDirectory: string | undefined;
|
|
32
|
+
/** Absolute canonical package path. */
|
|
33
|
+
readonly path: string;
|
|
34
|
+
/** The lock key of the accepted resolution whose canonical path this is. */
|
|
35
|
+
readonly lockKey: string | undefined;
|
|
36
|
+
/** Whether any desired route reaches this canonical package. */
|
|
37
|
+
readonly reached: boolean;
|
|
38
|
+
}
|
|
39
|
+
/** An install-root entry that is neither an installed package nor AXM staging. */
|
|
40
|
+
export interface UnrecognizedInstallRootEntry {
|
|
41
|
+
readonly kind: "unrecognized";
|
|
42
|
+
readonly path: string;
|
|
43
|
+
readonly entryKind: "file" | "directory" | "symlink";
|
|
44
|
+
}
|
|
45
|
+
export type InstallRootEntry = InstalledPackageEntry | UnrecognizedInstallRootEntry;
|
|
46
|
+
export interface InstallRootInventory {
|
|
47
|
+
readonly root: string;
|
|
48
|
+
readonly packages: ReadonlyArray<InstalledPackageEntry>;
|
|
49
|
+
readonly leftovers: ReadonlyArray<InstalledPackageEntry>;
|
|
50
|
+
readonly unrecognized: ReadonlyArray<UnrecognizedInstallRootEntry>;
|
|
51
|
+
}
|
|
52
|
+
/** AXM's own interrupted replacement directories beside a canonical package. */
|
|
53
|
+
export declare const isInstallRootStagingName: (name: string) => boolean;
|
|
54
|
+
export interface ObserveInstallRootArgs {
|
|
55
|
+
readonly layout: WorkspaceLayout;
|
|
56
|
+
readonly graph: DesiredStateGraph;
|
|
57
|
+
readonly locks: LockfileReaderService;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Observe the install root. When desired state is incomplete, every installed
|
|
61
|
+
* package counts as reached: no leftover is claimed without full reachability.
|
|
62
|
+
*/
|
|
63
|
+
export declare const observeInstallRoot: ({ layout, graph, locks }: ObserveInstallRootArgs) => Effect.Effect<{
|
|
64
|
+
root: string & import("effect/Brand").Brand<"AbsolutePath">;
|
|
65
|
+
packages: InstalledPackageEntry[];
|
|
66
|
+
leftovers: InstalledPackageEntry[];
|
|
67
|
+
unrecognized: UnrecognizedInstallRootEntry[];
|
|
68
|
+
}, import("./service-interface.ts").WorkspaceLockfileReadFailure, FileSystem.FileSystem | Path.Path>;
|
|
69
|
+
//# sourceMappingURL=install-root.d.ts.map
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Install-root inventory.
|
|
3
|
+
*
|
|
4
|
+
* Classifies every entry in a scope's install root (`agent_extensions`): an
|
|
5
|
+
* installed package whose path gives a well-formed extension identity or is
|
|
6
|
+
* proven by an accepted resolution, AXM's own interrupted staging, or an
|
|
7
|
+
* unrecognized entry. Each installed package records whether desired state
|
|
8
|
+
* still reaches it; one that nothing reaches is a leftover.
|
|
9
|
+
*
|
|
10
|
+
* Location inside the install root is AXM's install proof for canonical
|
|
11
|
+
* package directories only. It never proves ownership of agent projections.
|
|
12
|
+
*
|
|
13
|
+
* @experimental This API is unstable and may change without notice.
|
|
14
|
+
* @packageDocumentation
|
|
15
|
+
*/
|
|
16
|
+
import * as Effect from "effect/Effect";
|
|
17
|
+
import * as FileSystem from "effect/FileSystem";
|
|
18
|
+
import * as Path from "effect/Path";
|
|
19
|
+
import { extensionTypes, isExtensionTypePlural, toExtensionType, toExtensionTypePlural, } from "@agentxm/extension-model/unstable/extensions";
|
|
20
|
+
import { sanitizeName } from "./extension-name.js";
|
|
21
|
+
import { computeExtensionPathsForLayout, extensionPathSourceFromLockEntry, } from "./extension-paths.js";
|
|
22
|
+
/** AXM's own interrupted replacement directories beside a canonical package. */
|
|
23
|
+
export const isInstallRootStagingName = (name) => name.endsWith(".axm-staging") || name.endsWith(".axm-backup");
|
|
24
|
+
const PLATFORM_METADATA_FILES = new Set([".DS_Store", "Thumbs.db"]);
|
|
25
|
+
const isCanonicalName = (name) => sanitizeName(name) === name;
|
|
26
|
+
const isOwnerSegment = (segment) => segment.startsWith("@") && segment.length > 1 && isCanonicalName(segment.slice(1));
|
|
27
|
+
/**
|
|
28
|
+
* `<source>/@<owner>/<plural>/<name>` for acquired packages, or
|
|
29
|
+
* `@<owner>/<plural>/<name>` for user-scope authored packages.
|
|
30
|
+
*/
|
|
31
|
+
const identityOf = (segments) => {
|
|
32
|
+
const [first = "", second = "", third = "", fourth = ""] = segments;
|
|
33
|
+
const shape = segments.length === 4 && !first.startsWith("@")
|
|
34
|
+
? { sourceDirectory: first, owner: second, plural: third, name: fourth }
|
|
35
|
+
: segments.length === 3
|
|
36
|
+
? { sourceDirectory: undefined, owner: first, plural: second, name: third }
|
|
37
|
+
: undefined;
|
|
38
|
+
if (shape === undefined || !isOwnerSegment(shape.owner) || !isCanonicalName(shape.name))
|
|
39
|
+
return undefined;
|
|
40
|
+
if (!isExtensionTypePlural(shape.plural))
|
|
41
|
+
return undefined;
|
|
42
|
+
return {
|
|
43
|
+
type: toExtensionType(shape.plural),
|
|
44
|
+
name: shape.name,
|
|
45
|
+
owner: shape.owner,
|
|
46
|
+
sourceDirectory: shape.sourceDirectory,
|
|
47
|
+
};
|
|
48
|
+
};
|
|
49
|
+
/** Whether a directory can still lead to a well-formed identity path. */
|
|
50
|
+
const isIdentityPrefix = (segments) => {
|
|
51
|
+
const [first = "", second = "", third = ""] = segments;
|
|
52
|
+
switch (segments.length) {
|
|
53
|
+
case 1:
|
|
54
|
+
return true;
|
|
55
|
+
case 2:
|
|
56
|
+
return first.startsWith("@")
|
|
57
|
+
? isOwnerSegment(first) && isExtensionTypePlural(second)
|
|
58
|
+
: isOwnerSegment(second);
|
|
59
|
+
case 3:
|
|
60
|
+
return !first.startsWith("@") && isExtensionTypePlural(third);
|
|
61
|
+
default:
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
/**
|
|
66
|
+
* Observe the install root. When desired state is incomplete, every installed
|
|
67
|
+
* package counts as reached: no leftover is claimed without full reachability.
|
|
68
|
+
*/
|
|
69
|
+
export const observeInstallRoot = ({ layout, graph, locks }) => Effect.gen(function* () {
|
|
70
|
+
const fs = yield* FileSystem.FileSystem;
|
|
71
|
+
const path = yield* Path.Path;
|
|
72
|
+
const root = layout.acquiredRoot;
|
|
73
|
+
const accepted = (yield* Effect.forEach(extensionTypes, (type) => locks
|
|
74
|
+
.entries(type)
|
|
75
|
+
.pipe(Effect.map((entries) => Object.entries(entries).map(([key, entry]) => ({ type, key, entry })))))).flat();
|
|
76
|
+
const nodeReaches = (type, key) => graph.nodes.some((node) => node.type === type && (type === "mcp-server" ? node.identity === key : node.name === key));
|
|
77
|
+
const lockedPaths = new Map(accepted.map((row) => [
|
|
78
|
+
computeExtensionPathsForLayout(path.join, layout, extensionPathSourceFromLockEntry(row.entry), toExtensionTypePlural(row.type), row.entry.workspaceName).canonicalPath,
|
|
79
|
+
{ ...row, reached: nodeReaches(row.type, row.key) },
|
|
80
|
+
]));
|
|
81
|
+
// A desired extension whose resolution is not yet accepted may still be
|
|
82
|
+
// materialized into this path, so it reaches every same-named package.
|
|
83
|
+
const unlockedDesired = graph.nodes.filter((node) => !accepted.some((row) => row.type === node.type &&
|
|
84
|
+
(node.type === "mcp-server" ? row.key === node.identity : row.key === node.name)));
|
|
85
|
+
const packages = [];
|
|
86
|
+
const unrecognized = [];
|
|
87
|
+
const isLink = (target) => fs.readLink(target).pipe(Effect.as(true), Effect.orElseSucceed(() => false));
|
|
88
|
+
const leadsToLockedPath = (directory) => [...lockedPaths.keys()].some((locked) => {
|
|
89
|
+
const relative = path.relative(directory, locked);
|
|
90
|
+
return relative !== "" && !relative.startsWith("..") && !path.isAbsolute(relative);
|
|
91
|
+
});
|
|
92
|
+
const visit = (directory, segments) => Effect.gen(function* () {
|
|
93
|
+
const names = yield* fs.readDirectory(directory).pipe(Effect.orElseSucceed(() => []));
|
|
94
|
+
for (const name of [...names].sort()) {
|
|
95
|
+
const absolute = path.join(directory, name);
|
|
96
|
+
const childSegments = [...segments, name];
|
|
97
|
+
if (isInstallRootStagingName(name) || PLATFORM_METADATA_FILES.has(name))
|
|
98
|
+
continue;
|
|
99
|
+
if (yield* isLink(absolute)) {
|
|
100
|
+
unrecognized.push({ kind: "unrecognized", path: absolute, entryKind: "symlink" });
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
const info = yield* fs.stat(absolute).pipe(Effect.option);
|
|
104
|
+
if (info._tag === "None")
|
|
105
|
+
continue;
|
|
106
|
+
if (info.value.type !== "Directory") {
|
|
107
|
+
unrecognized.push({ kind: "unrecognized", path: absolute, entryKind: "file" });
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
const locked = lockedPaths.get(absolute);
|
|
111
|
+
const identity = identityOf(childSegments);
|
|
112
|
+
const type = locked?.type ?? identity?.type;
|
|
113
|
+
const packageName = locked?.entry.workspaceName ?? identity?.name;
|
|
114
|
+
if (type !== undefined && packageName !== undefined) {
|
|
115
|
+
// A path without a source directory is authored content, which is
|
|
116
|
+
// never inferred to be undesired.
|
|
117
|
+
const reached = !graph.complete ||
|
|
118
|
+
(locked === undefined && identity?.sourceDirectory === undefined) ||
|
|
119
|
+
(locked?.reached ?? false) ||
|
|
120
|
+
unlockedDesired.some((node) => node.type === type && node.name === packageName);
|
|
121
|
+
packages.push({
|
|
122
|
+
kind: "package",
|
|
123
|
+
type,
|
|
124
|
+
name: packageName,
|
|
125
|
+
owner: identity?.owner,
|
|
126
|
+
sourceDirectory: identity?.sourceDirectory,
|
|
127
|
+
path: absolute,
|
|
128
|
+
lockKey: locked?.key,
|
|
129
|
+
reached,
|
|
130
|
+
});
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
if (isIdentityPrefix(childSegments) || leadsToLockedPath(absolute)) {
|
|
134
|
+
yield* visit(absolute, childSegments);
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
unrecognized.push({ kind: "unrecognized", path: absolute, entryKind: "directory" });
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
if (yield* fs.exists(root).pipe(Effect.orElseSucceed(() => false)))
|
|
141
|
+
yield* visit(root, []);
|
|
142
|
+
return {
|
|
143
|
+
root,
|
|
144
|
+
packages,
|
|
145
|
+
leftovers: packages.filter((entry) => !entry.reached),
|
|
146
|
+
unrecognized,
|
|
147
|
+
};
|
|
148
|
+
});
|
|
149
|
+
//# sourceMappingURL=install-root.js.map
|
|
@@ -1,11 +1,22 @@
|
|
|
1
1
|
import * as Schema from "effect/Schema";
|
|
2
2
|
import { type ConfiguredAgentOutcome } from "../../configured-agent-outcome.js";
|
|
3
3
|
import type { ExtensionKey } from "../types.js";
|
|
4
|
-
|
|
4
|
+
/**
|
|
5
|
+
* How desired state explains one detected extension.
|
|
6
|
+
*
|
|
7
|
+
* - `configured` — declared directly in workspace settings.
|
|
8
|
+
* - `implicit` — reached indirectly, such as through a desired Pack.
|
|
9
|
+
* - `leftover` — an installed package in the install root that desired state
|
|
10
|
+
* no longer reaches.
|
|
11
|
+
* - `undeclared` — an authored package in its standard authoring folder with no
|
|
12
|
+
* workspace declaration.
|
|
13
|
+
* - `unmanaged` — native agent content AXM does not own.
|
|
14
|
+
*/
|
|
15
|
+
export declare const ExtensionInventoryLifecycleSchema: Schema.Literals<readonly ["configured", "implicit", "leftover", "undeclared", "unmanaged"]>;
|
|
5
16
|
export type ExtensionInventoryLifecycle = typeof ExtensionInventoryLifecycleSchema.Type;
|
|
6
17
|
export declare const ExtensionInventoryClassificationSchema: Schema.Struct<{
|
|
7
18
|
readonly kind: Schema.Literal<"lifecycle">;
|
|
8
|
-
readonly lifecycle: Schema.Literals<readonly ["configured", "implicit", "unmanaged"]>;
|
|
19
|
+
readonly lifecycle: Schema.Literals<readonly ["configured", "implicit", "leftover", "undeclared", "unmanaged"]>;
|
|
9
20
|
}>;
|
|
10
21
|
export type ExtensionInventoryClassification = typeof ExtensionInventoryClassificationSchema.Type;
|
|
11
22
|
export declare const ExtensionInventoryRowSchema: Schema.Struct<{
|
|
@@ -14,7 +25,7 @@ export declare const ExtensionInventoryRowSchema: Schema.Struct<{
|
|
|
14
25
|
readonly name: Schema.String;
|
|
15
26
|
readonly classification: Schema.Struct<{
|
|
16
27
|
readonly kind: Schema.Literal<"lifecycle">;
|
|
17
|
-
readonly lifecycle: Schema.Literals<readonly ["configured", "implicit", "unmanaged"]>;
|
|
28
|
+
readonly lifecycle: Schema.Literals<readonly ["configured", "implicit", "leftover", "undeclared", "unmanaged"]>;
|
|
18
29
|
}>;
|
|
19
30
|
readonly enabled: Schema.NullOr<Schema.Boolean>;
|
|
20
31
|
readonly installed: Schema.Boolean;
|
|
@@ -47,7 +58,7 @@ export declare const ExtensionInventorySchema: Schema.Struct<{
|
|
|
47
58
|
readonly name: Schema.String;
|
|
48
59
|
readonly classification: Schema.Struct<{
|
|
49
60
|
readonly kind: Schema.Literal<"lifecycle">;
|
|
50
|
-
readonly lifecycle: Schema.Literals<readonly ["configured", "implicit", "unmanaged"]>;
|
|
61
|
+
readonly lifecycle: Schema.Literals<readonly ["configured", "implicit", "leftover", "undeclared", "unmanaged"]>;
|
|
51
62
|
}>;
|
|
52
63
|
readonly enabled: Schema.NullOr<Schema.Boolean>;
|
|
53
64
|
readonly installed: Schema.Boolean;
|
|
@@ -76,6 +87,8 @@ export declare const ExtensionInventorySchema: Schema.Struct<{
|
|
|
76
87
|
readonly configuredCount: Schema.Number;
|
|
77
88
|
readonly implicitCount: Schema.Number;
|
|
78
89
|
readonly installedCount: Schema.Number;
|
|
90
|
+
readonly leftoverCount: Schema.Number;
|
|
91
|
+
readonly undeclaredCount: Schema.Number;
|
|
79
92
|
readonly unmanagedCount: Schema.Number;
|
|
80
93
|
}>;
|
|
81
94
|
export type ExtensionInventory = typeof ExtensionInventorySchema.Type;
|
|
@@ -95,5 +108,9 @@ export interface ProjectExtensionInventoryInput {
|
|
|
95
108
|
readonly lifecycle: ReadonlyArray<LifecycleInventoryCandidate>;
|
|
96
109
|
readonly agents?: ReadonlyArray<string>;
|
|
97
110
|
}
|
|
111
|
+
/** Whether desired state reaches rows with this lifecycle. */
|
|
112
|
+
export declare const isDesiredInventoryLifecycle: (lifecycle: ExtensionInventoryLifecycle) => boolean;
|
|
113
|
+
/** Aggregate counts for an inventory's already-filtered items. */
|
|
114
|
+
export declare const countExtensionInventory: (items: ReadonlyArray<ExtensionInventoryRow>) => ExtensionInventory;
|
|
98
115
|
export declare const projectExtensionInventory: (input: ProjectExtensionInventoryInput) => ExtensionInventory;
|
|
99
116
|
//# sourceMappingURL=inventory.d.ts.map
|
|
@@ -1,9 +1,22 @@
|
|
|
1
1
|
import * as Schema from "effect/Schema";
|
|
2
2
|
import { ExtensionTypeSchema } from "@agentxm/extension-model/unstable/extensions/common";
|
|
3
3
|
import { ConfiguredAgentOutcomeSchema, } from "../../configured-agent-outcome.js";
|
|
4
|
+
/**
|
|
5
|
+
* How desired state explains one detected extension.
|
|
6
|
+
*
|
|
7
|
+
* - `configured` — declared directly in workspace settings.
|
|
8
|
+
* - `implicit` — reached indirectly, such as through a desired Pack.
|
|
9
|
+
* - `leftover` — an installed package in the install root that desired state
|
|
10
|
+
* no longer reaches.
|
|
11
|
+
* - `undeclared` — an authored package in its standard authoring folder with no
|
|
12
|
+
* workspace declaration.
|
|
13
|
+
* - `unmanaged` — native agent content AXM does not own.
|
|
14
|
+
*/
|
|
4
15
|
export const ExtensionInventoryLifecycleSchema = Schema.Literals([
|
|
5
16
|
"configured",
|
|
6
17
|
"implicit",
|
|
18
|
+
"leftover",
|
|
19
|
+
"undeclared",
|
|
7
20
|
"unmanaged",
|
|
8
21
|
]);
|
|
9
22
|
export const ExtensionInventoryClassificationSchema = Schema.Struct({
|
|
@@ -35,12 +48,32 @@ export const ExtensionInventorySchema = Schema.Struct({
|
|
|
35
48
|
configuredCount: Schema.Number,
|
|
36
49
|
implicitCount: Schema.Number,
|
|
37
50
|
installedCount: Schema.Number,
|
|
51
|
+
leftoverCount: Schema.Number,
|
|
52
|
+
undeclaredCount: Schema.Number,
|
|
38
53
|
unmanagedCount: Schema.Number,
|
|
39
54
|
});
|
|
40
55
|
const lifecyclePriority = {
|
|
41
56
|
configured: 0,
|
|
42
57
|
implicit: 1,
|
|
43
|
-
|
|
58
|
+
leftover: 2,
|
|
59
|
+
undeclared: 3,
|
|
60
|
+
unmanaged: 4,
|
|
61
|
+
};
|
|
62
|
+
/** Whether desired state reaches rows with this lifecycle. */
|
|
63
|
+
export const isDesiredInventoryLifecycle = (lifecycle) => lifecycle === "configured" || lifecycle === "implicit";
|
|
64
|
+
/** Aggregate counts for an inventory's already-filtered items. */
|
|
65
|
+
export const countExtensionInventory = (items) => {
|
|
66
|
+
const withLifecycle = (lifecycle) => items.filter((item) => item.classification.lifecycle === lifecycle).length;
|
|
67
|
+
return {
|
|
68
|
+
items,
|
|
69
|
+
count: items.length,
|
|
70
|
+
configuredCount: withLifecycle("configured"),
|
|
71
|
+
implicitCount: withLifecycle("implicit"),
|
|
72
|
+
installedCount: items.filter((item) => item.installed).length,
|
|
73
|
+
leftoverCount: withLifecycle("leftover"),
|
|
74
|
+
undeclaredCount: withLifecycle("undeclared"),
|
|
75
|
+
unmanagedCount: withLifecycle("unmanaged"),
|
|
76
|
+
};
|
|
44
77
|
};
|
|
45
78
|
const keyString = (key) => `${key.scope}:${key.type}:${key.name}`;
|
|
46
79
|
const addAll = (target, values) => {
|
|
@@ -95,17 +128,6 @@ export const projectExtensionInventory = (input) => {
|
|
|
95
128
|
origins: sorted(aggregate.origins),
|
|
96
129
|
paths: sorted(aggregate.paths),
|
|
97
130
|
}));
|
|
98
|
-
|
|
99
|
-
const configuredCount = items.filter((item) => item.classification.lifecycle === "configured").length;
|
|
100
|
-
const implicitCount = items.filter((item) => item.classification.lifecycle === "implicit").length;
|
|
101
|
-
const unmanagedCount = items.filter((item) => item.classification.lifecycle === "unmanaged").length;
|
|
102
|
-
return {
|
|
103
|
-
items,
|
|
104
|
-
count: items.length,
|
|
105
|
-
configuredCount,
|
|
106
|
-
implicitCount,
|
|
107
|
-
installedCount: items.filter((item) => item.installed).length,
|
|
108
|
-
unmanagedCount,
|
|
109
|
-
};
|
|
131
|
+
return countExtensionInventory(lifecycleRows.sort((left, right) => left.name.localeCompare(right.name)));
|
|
110
132
|
};
|
|
111
133
|
//# sourceMappingURL=inventory.js.map
|
|
@@ -37,6 +37,7 @@ import { MANIFEST_FILENAME_BY_TYPE } from "@agentxm/extension-content";
|
|
|
37
37
|
import { parseSkillMd } from "@agentxm/extension-content";
|
|
38
38
|
import { DISCOVERY_SKIPPED_DIRECTORIES } from "@agentxm/extension-model/unstable/discovery-walk";
|
|
39
39
|
import { makeAbsolutePath } from "@agentxm/extension-model/unstable/path-types";
|
|
40
|
+
import { isInstallRootStagingName } from "../../install-root.js";
|
|
40
41
|
import { childEntries, fileExists, filterDirectories, splitAbsolutePathSegments, } from "./fs-helpers.js";
|
|
41
42
|
const SCANNER_NAME = "canonical-extensions";
|
|
42
43
|
/**
|
|
@@ -187,7 +188,7 @@ const scanAcquiredDirectory = (deps, dir) => Effect.gen(function* () {
|
|
|
187
188
|
}
|
|
188
189
|
}
|
|
189
190
|
const childCandidates = entries
|
|
190
|
-
.filter((entry) => !DISCOVERY_SKIPPED_DIRECTORIES.has(entry))
|
|
191
|
+
.filter((entry) => !DISCOVERY_SKIPPED_DIRECTORIES.has(entry) && !isInstallRootStagingName(entry))
|
|
191
192
|
.map((entry) => deps.path.join(dir, entry));
|
|
192
193
|
const childDirs = yield* filterDirectories(deps.fs, childCandidates);
|
|
193
194
|
const nested = yield* Effect.forEach(childDirs, (childDir) => scanAcquiredDirectory(deps, childDir), { concurrency: "unbounded" });
|
|
@@ -8,6 +8,7 @@ import * as Path from "effect/Path";
|
|
|
8
8
|
import { type Handle } from "@agentxm/extension-model/unstable/extensions/handle";
|
|
9
9
|
import type { SourceHostConfig } from "../../settings/schema.js";
|
|
10
10
|
import { type AbsolutePath } from "@agentxm/extension-model/unstable/path-types";
|
|
11
|
+
import { type WorkspaceLayout } from "../layout.js";
|
|
11
12
|
import { AgentRootResolver } from "./agent-root-resolver.js";
|
|
12
13
|
import { type ScopedAgentsApi } from "./agents/index.js";
|
|
13
14
|
import { type Warning } from "./diagnostics.js";
|
|
@@ -55,6 +56,8 @@ export interface WorkspaceReadModel {
|
|
|
55
56
|
readonly owner: ScopedOwnerApi;
|
|
56
57
|
readonly diagnostics: Effect.Effect<ReadonlyArray<Warning>>;
|
|
57
58
|
readonly canonicalExtensions: Effect.Effect<ReadonlyArray<CanonicalExtensionOccurrence>>;
|
|
59
|
+
/** The resolved storage layout, or none when the layout could not be resolved. */
|
|
60
|
+
readonly layout: Option.Option<WorkspaceLayout>;
|
|
58
61
|
}
|
|
59
62
|
/**
|
|
60
63
|
* The read-model family that carries each catalog extension type, or `null`
|
|
@@ -343,6 +343,7 @@ const buildScope = Effect.fn("workspace.read-model.build-scope")(function* (deps
|
|
|
343
343
|
owner,
|
|
344
344
|
diagnostics: diagnostics.snapshot,
|
|
345
345
|
canonicalExtensions: canonicalScanner,
|
|
346
|
+
layout: Result.isFailure(layoutResult) ? Option.none() : Option.some(layoutResult.success),
|
|
346
347
|
};
|
|
347
348
|
});
|
|
348
349
|
// ---------------------------------------------------------------------------
|
|
@@ -10,7 +10,7 @@ import { installableExtensionTypes, } from "@agentxm/extension-model/unstable/ex
|
|
|
10
10
|
import { isAxmManagedMcpEntry } from "@agentxm/agent-integration";
|
|
11
11
|
import { createDefaultSettings } from "../settings/index.js";
|
|
12
12
|
import { configuredAgentLifecycleOutcomes } from "./configured-agent-outcomes.js";
|
|
13
|
-
import { projectExtensionInventory, } from "./read-model/extensions/inventory.js";
|
|
13
|
+
import { countExtensionInventory, isDesiredInventoryLifecycle, projectExtensionInventory, } from "./read-model/extensions/inventory.js";
|
|
14
14
|
import { deriveSourceMetaFromLockType } from "./source-metadata.js";
|
|
15
15
|
export const makeReadModelRecordReaders = (args) => {
|
|
16
16
|
const packagingKindForSource = (type, source) => {
|
|
@@ -88,9 +88,38 @@ export const makeReadModelRecordReaders = (args) => {
|
|
|
88
88
|
paths: observations.paths,
|
|
89
89
|
};
|
|
90
90
|
};
|
|
91
|
-
const
|
|
91
|
+
const isWithin = (root, candidate) => {
|
|
92
|
+
const relative = args.path.relative(root, candidate);
|
|
93
|
+
return !relative.startsWith("..") && !args.path.isAbsolute(relative);
|
|
94
|
+
};
|
|
95
|
+
/**
|
|
96
|
+
* Classify one occurrence desired state does not explain: an AXM package
|
|
97
|
+
* under the install root is `leftover`, an AXM package under its type's
|
|
98
|
+
* standard authoring folder is `undeclared`, and anything else — native
|
|
99
|
+
* agent content — is `unmanaged`.
|
|
100
|
+
*/
|
|
101
|
+
const unexplainedLifecycle = (layout, key, actual) => {
|
|
102
|
+
if (Option.isNone(layout) || typeof actual !== "object" || actual === null)
|
|
103
|
+
return "unmanaged";
|
|
104
|
+
const originTag = stringProperty("origin" in actual ? actual.origin : undefined, "_tag");
|
|
105
|
+
const packageLocation = stringProperty(actual, "packageRoot") ?? stringProperty(actual, "contentRoot");
|
|
106
|
+
if (originTag === null || packageLocation === null)
|
|
107
|
+
return "unmanaged";
|
|
108
|
+
const canonical = originTag.startsWith("canonical-axm-");
|
|
109
|
+
if (!canonical && !originTag.startsWith("external-axm-"))
|
|
110
|
+
return "unmanaged";
|
|
111
|
+
if (isWithin(layout.value.acquiredRoot, packageLocation))
|
|
112
|
+
return "leftover";
|
|
113
|
+
if (canonical &&
|
|
114
|
+
layout.value.scope === "project" &&
|
|
115
|
+
isWithin(layout.value.authoredRoot(key.type), packageLocation)) {
|
|
116
|
+
return "undeclared";
|
|
117
|
+
}
|
|
118
|
+
return "unmanaged";
|
|
119
|
+
};
|
|
120
|
+
const lifecycleCandidateFromUnexplained = (layout, row) => ({
|
|
92
121
|
key: row.key,
|
|
93
|
-
lifecycle:
|
|
122
|
+
lifecycle: unexplainedLifecycle(layout, row.key, row.actual),
|
|
94
123
|
enabled: null,
|
|
95
124
|
installed: true,
|
|
96
125
|
...observationFromActual(row.actual),
|
|
@@ -273,7 +302,7 @@ export const makeReadModelRecordReaders = (args) => {
|
|
|
273
302
|
paths: [],
|
|
274
303
|
}));
|
|
275
304
|
const implicitObserved = desiredUnmanaged.map((row) => ({
|
|
276
|
-
...
|
|
305
|
+
...lifecycleCandidateFromUnexplained(input.layout, row),
|
|
277
306
|
lifecycle: "implicit",
|
|
278
307
|
enabled: true,
|
|
279
308
|
}));
|
|
@@ -287,8 +316,8 @@ export const makeReadModelRecordReaders = (args) => {
|
|
|
287
316
|
...implicitMissing,
|
|
288
317
|
...input.unmanaged
|
|
289
318
|
.filter((row) => !desiredPackMembers.has(row.key.name))
|
|
290
|
-
.map(
|
|
291
|
-
...stalePackActuals.map(
|
|
319
|
+
.map((row) => lifecycleCandidateFromUnexplained(input.layout, row)),
|
|
320
|
+
...stalePackActuals.map((row) => lifecycleCandidateFromUnexplained(input.layout, row)),
|
|
292
321
|
],
|
|
293
322
|
agents: input.agents,
|
|
294
323
|
});
|
|
@@ -304,9 +333,8 @@ export const makeReadModelRecordReaders = (args) => {
|
|
|
304
333
|
const withOutcomes = inventory.items.map((row) => {
|
|
305
334
|
return {
|
|
306
335
|
...row,
|
|
307
|
-
agentOutcomes: row.classification.lifecycle
|
|
308
|
-
?
|
|
309
|
-
: configuredAgentLifecycleOutcomes({
|
|
336
|
+
agentOutcomes: isDesiredInventoryLifecycle(row.classification.lifecycle)
|
|
337
|
+
? configuredAgentLifecycleOutcomes({
|
|
310
338
|
type: row.type,
|
|
311
339
|
name: row.name,
|
|
312
340
|
agentIds: configuredAgents,
|
|
@@ -315,22 +343,14 @@ export const makeReadModelRecordReaders = (args) => {
|
|
|
315
343
|
targetState: row.enabled === false ? "disabled" : "enabled",
|
|
316
344
|
installed: row.installed,
|
|
317
345
|
observedAgentIds: row.agents,
|
|
318
|
-
})
|
|
346
|
+
})
|
|
347
|
+
: [],
|
|
319
348
|
};
|
|
320
349
|
});
|
|
321
350
|
const items = withOutcomes.filter((row) => agents.length === 0 ||
|
|
322
351
|
agents.some((agentId) => row.agents.includes(agentId) ||
|
|
323
352
|
row.agentOutcomes.some((outcome) => outcome.agentId === agentId)));
|
|
324
|
-
return
|
|
325
|
-
items,
|
|
326
|
-
count: items.length,
|
|
327
|
-
configuredCount: items.filter((item) => item.classification.lifecycle === "configured").length,
|
|
328
|
-
implicitCount: items.filter((item) => item.classification.lifecycle === "implicit")
|
|
329
|
-
.length,
|
|
330
|
-
installedCount: items.filter((item) => item.installed).length,
|
|
331
|
-
unmanagedCount: items.filter((item) => item.classification.lifecycle === "unmanaged")
|
|
332
|
-
.length,
|
|
333
|
-
};
|
|
353
|
+
return countExtensionInventory(items);
|
|
334
354
|
};
|
|
335
355
|
switch (type) {
|
|
336
356
|
case "skill": {
|
|
@@ -339,6 +359,7 @@ export const makeReadModelRecordReaders = (args) => {
|
|
|
339
359
|
const unmanaged = yield* scoped.skills.unmanaged;
|
|
340
360
|
return finalizeInventory(projectStandardInventory({
|
|
341
361
|
scope: scoped.scope,
|
|
362
|
+
layout: scoped.layout,
|
|
342
363
|
type,
|
|
343
364
|
installed,
|
|
344
365
|
resolved: Option.getOrElse(resolved, () => []),
|
|
@@ -354,6 +375,7 @@ export const makeReadModelRecordReaders = (args) => {
|
|
|
354
375
|
const unmanaged = yield* scoped.mcpServers.unmanaged;
|
|
355
376
|
return finalizeInventory(projectStandardInventory({
|
|
356
377
|
scope: scoped.scope,
|
|
378
|
+
layout: scoped.layout,
|
|
357
379
|
type,
|
|
358
380
|
installed,
|
|
359
381
|
resolved: Option.getOrElse(resolved, () => []),
|
|
@@ -369,6 +391,7 @@ export const makeReadModelRecordReaders = (args) => {
|
|
|
369
391
|
const unmanaged = yield* scoped.subagents.unmanaged;
|
|
370
392
|
return finalizeInventory(projectStandardInventory({
|
|
371
393
|
scope: scoped.scope,
|
|
394
|
+
layout: scoped.layout,
|
|
372
395
|
type,
|
|
373
396
|
installed,
|
|
374
397
|
resolved: Option.getOrElse(resolved, () => []),
|
|
@@ -384,6 +407,7 @@ export const makeReadModelRecordReaders = (args) => {
|
|
|
384
407
|
const unmanaged = yield* scoped.packs.unmanaged;
|
|
385
408
|
return finalizeInventory(projectStandardInventory({
|
|
386
409
|
scope: scoped.scope,
|
|
410
|
+
layout: scoped.layout,
|
|
387
411
|
type,
|
|
388
412
|
installed,
|
|
389
413
|
resolved: Option.getOrElse(resolved, () => []),
|
|
@@ -399,6 +423,7 @@ export const makeReadModelRecordReaders = (args) => {
|
|
|
399
423
|
const unmanaged = yield* scoped.rules.unmanaged;
|
|
400
424
|
return finalizeInventory(projectStandardInventory({
|
|
401
425
|
scope: scoped.scope,
|
|
426
|
+
layout: scoped.layout,
|
|
402
427
|
type,
|
|
403
428
|
installed,
|
|
404
429
|
resolved: Option.getOrElse(resolved, () => []),
|
|
@@ -414,6 +439,7 @@ export const makeReadModelRecordReaders = (args) => {
|
|
|
414
439
|
const unmanaged = yield* scoped.hooks.unmanaged;
|
|
415
440
|
return finalizeInventory(projectStandardInventory({
|
|
416
441
|
scope: scoped.scope,
|
|
442
|
+
layout: scoped.layout,
|
|
417
443
|
type,
|
|
418
444
|
installed,
|
|
419
445
|
resolved: Option.getOrElse(resolved, () => []),
|
|
@@ -429,6 +455,7 @@ export const makeReadModelRecordReaders = (args) => {
|
|
|
429
455
|
const unmanaged = yield* scoped.knowledge.unmanaged;
|
|
430
456
|
return finalizeInventory(projectStandardInventory({
|
|
431
457
|
scope: scoped.scope,
|
|
458
|
+
layout: scoped.layout,
|
|
432
459
|
type,
|
|
433
460
|
installed,
|
|
434
461
|
resolved: Option.getOrElse(resolved, () => []),
|
|
@@ -457,6 +484,8 @@ export const makeReadModelRecordReaders = (args) => {
|
|
|
457
484
|
configuredCount: inventories.reduce((total, inventory) => total + inventory.configuredCount, 0),
|
|
458
485
|
implicitCount: inventories.reduce((total, inventory) => total + inventory.implicitCount, 0),
|
|
459
486
|
installedCount: inventories.reduce((total, inventory) => total + inventory.installedCount, 0),
|
|
487
|
+
leftoverCount: inventories.reduce((total, inventory) => total + inventory.leftoverCount, 0),
|
|
488
|
+
undeclaredCount: inventories.reduce((total, inventory) => total + inventory.undeclaredCount, 0),
|
|
460
489
|
unmanagedCount: inventories.reduce((total, inventory) => total + inventory.unmanagedCount, 0),
|
|
461
490
|
};
|
|
462
491
|
});
|
|
@@ -28,7 +28,8 @@ import type { ReadModelRecordRow } from "./read-model-record-types.js";
|
|
|
28
28
|
import type { WorkspaceScope } from "@agentxm/extension-model/unstable/workspace-scope";
|
|
29
29
|
import type { ExtensionInventory } from "./read-model/extensions/inventory.js";
|
|
30
30
|
import type { ResolvedKnowledgeDiscoveryConfig } from "../knowledge/discovery-config.js";
|
|
31
|
-
import type {
|
|
31
|
+
import type { DesiredStateGraphInputs } from "./desired-state-reader.js";
|
|
32
|
+
import type { DesiredStateGraph } from "./desired-state-graph.js";
|
|
32
33
|
import type { AbsolutePath } from "@agentxm/extension-model/unstable/path-types";
|
|
33
34
|
import type { WorkspaceLayout } from "./layout.js";
|
|
34
35
|
import type { ExtensionPathSource } from "./extension-paths.js";
|
|
@@ -105,7 +106,7 @@ export type WorkspaceStateReadFailure = SettingsReadError | LockfileReadError |
|
|
|
105
106
|
*/
|
|
106
107
|
export type WorkspaceSettingsMutationFailure = WorkspaceSettingsReadFailure | SettingsWriteError | WorkspaceSnapshotError;
|
|
107
108
|
/** Lockfile read-modify-write through the snapshot-commit path. */
|
|
108
|
-
export type WorkspaceLockfileMutationFailure = WorkspaceLockfileReadFailure | LockfileValidationError | LockfileWriteError;
|
|
109
|
+
export type WorkspaceLockfileMutationFailure = WorkspaceLockfileReadFailure | LockfileValidationError | LockfileWriteError | WorkspaceSnapshotError;
|
|
109
110
|
/** Coupled settings-and-lockfile mutation. */
|
|
110
111
|
export type WorkspaceStateMutationFailure = WorkspaceSettingsMutationFailure | WorkspaceLockfileMutationFailure;
|
|
111
112
|
export interface WorkspaceReadModelRecords {
|
|
@@ -211,9 +212,7 @@ export interface WorkspaceMutationsService {
|
|
|
211
212
|
/** Probe lockfile state for policy decisions: ok | missing | invalid. */
|
|
212
213
|
readonly getLockfileState: () => Effect.Effect<LockfileState, LockfileValidationError | WorkspaceRootEscape>;
|
|
213
214
|
/** Build desired extension state from settings and installed or prospective Pack manifests. */
|
|
214
|
-
readonly getDesiredStateGraph: (options?:
|
|
215
|
-
readonly prospectivePacks?: ReadonlyArray<ProspectivePackRef>;
|
|
216
|
-
}) => Effect.Effect<DesiredStateGraph, WorkspaceStateReadFailure>;
|
|
215
|
+
readonly getDesiredStateGraph: (options?: DesiredStateGraphInputs) => Effect.Effect<DesiredStateGraph, WorkspaceStateReadFailure>;
|
|
217
216
|
/** Merged sources from project, user-scope, and built-in defaults. Cached per workspace lifetime. */
|
|
218
217
|
readonly getConfiguredSources: () => Effect.Effect<ReadonlyArray<SourceHostConfig>, WorkspaceSettingsReadFailure>;
|
|
219
218
|
/** Lookup a source by name from the merged sources list. */
|
|
@@ -61,6 +61,8 @@ export declare const readModelRecordStubs: {
|
|
|
61
61
|
configuredCount: number;
|
|
62
62
|
implicitCount: number;
|
|
63
63
|
installedCount: number;
|
|
64
|
+
leftoverCount: number;
|
|
65
|
+
undeclaredCount: number;
|
|
64
66
|
unmanagedCount: number;
|
|
65
67
|
}, never, never>;
|
|
66
68
|
readonly getExtensionInventory: () => Effect.Effect<ExtensionInventory, WorkspaceStateReadFailure>;
|
|
@@ -23,6 +23,8 @@ const emptyInventory = () => Effect.succeed({
|
|
|
23
23
|
configuredCount: 0,
|
|
24
24
|
implicitCount: 0,
|
|
25
25
|
installedCount: 0,
|
|
26
|
+
leftoverCount: 0,
|
|
27
|
+
undeclaredCount: 0,
|
|
26
28
|
unmanagedCount: 0,
|
|
27
29
|
});
|
|
28
30
|
const fs = (() => {
|
|
@@ -94,6 +96,8 @@ export const readModelRecordStubs = {
|
|
|
94
96
|
configuredCount: 0,
|
|
95
97
|
implicitCount: 0,
|
|
96
98
|
installedCount: 0,
|
|
99
|
+
leftoverCount: 0,
|
|
100
|
+
undeclaredCount: 0,
|
|
97
101
|
unmanagedCount: 0,
|
|
98
102
|
}),
|
|
99
103
|
getExtensionInventory: emptyInventory,
|
package/package.json
CHANGED
|
@@ -4,20 +4,20 @@
|
|
|
4
4
|
"url": "https://github.com/agentxm/axm/issues"
|
|
5
5
|
},
|
|
6
6
|
"dependencies": {
|
|
7
|
-
"@agentxm/agent-integration": "^0.
|
|
8
|
-
"@agentxm/extension-content": "^0.
|
|
9
|
-
"@agentxm/extension-model": "^0.
|
|
10
|
-
"@agentxm/registry-protocol": "^0.
|
|
11
|
-
"@agentxm/workspace-transactions": "^0.
|
|
7
|
+
"@agentxm/agent-integration": "^0.30.2",
|
|
8
|
+
"@agentxm/extension-content": "^0.30.2",
|
|
9
|
+
"@agentxm/extension-model": "^0.30.2",
|
|
10
|
+
"@agentxm/registry-protocol": "^0.30.2",
|
|
11
|
+
"@agentxm/workspace-transactions": "^0.30.2",
|
|
12
12
|
"effect": "4.0.0-rc.115",
|
|
13
13
|
"jsonc-parser": "^3.3.1",
|
|
14
14
|
"semver": "^7.8.5",
|
|
15
|
-
"smol-toml": "1.
|
|
15
|
+
"smol-toml": "^1.7.1",
|
|
16
16
|
"yaml": "^2.9.0"
|
|
17
17
|
},
|
|
18
18
|
"description": "AXM workspace state kernel: settings, lockfile, read model, and desired-state vocabulary for the axm CLI. Unstable and unsupported — use the axm.sh CLI.",
|
|
19
19
|
"devDependencies": {
|
|
20
|
-
"@agentxm/specification-metadata": "^0.
|
|
20
|
+
"@agentxm/specification-metadata": "^0.30.2",
|
|
21
21
|
"@effect/platform-node": "4.0.0-rc.115",
|
|
22
22
|
"@effect/vitest": "4.0.0-rc.115",
|
|
23
23
|
"@fast-check/vitest": "^0.5.0",
|
|
@@ -31,19 +31,16 @@
|
|
|
31
31
|
},
|
|
32
32
|
"exports": {
|
|
33
33
|
".": {
|
|
34
|
-
"
|
|
35
|
-
"default": "./dist/src/index.js"
|
|
36
|
-
"types": "./dist/src/index.d.ts"
|
|
34
|
+
"types": "./dist/src/index.d.ts",
|
|
35
|
+
"default": "./dist/src/index.js"
|
|
37
36
|
},
|
|
38
37
|
"./live": {
|
|
39
|
-
"
|
|
40
|
-
"default": "./dist/src/live.js"
|
|
41
|
-
"types": "./dist/src/live.d.ts"
|
|
38
|
+
"types": "./dist/src/live.d.ts",
|
|
39
|
+
"default": "./dist/src/live.js"
|
|
42
40
|
},
|
|
43
41
|
"./testing": {
|
|
44
|
-
"
|
|
45
|
-
"default": "./dist/src/testing.js"
|
|
46
|
-
"types": "./dist/src/testing.d.ts"
|
|
42
|
+
"types": "./dist/src/testing.d.ts",
|
|
43
|
+
"default": "./dist/src/testing.js"
|
|
47
44
|
}
|
|
48
45
|
},
|
|
49
46
|
"files": [
|
|
@@ -66,5 +63,5 @@
|
|
|
66
63
|
},
|
|
67
64
|
"sideEffects": false,
|
|
68
65
|
"type": "module",
|
|
69
|
-
"version": "0.
|
|
66
|
+
"version": "0.30.2"
|
|
70
67
|
}
|