@agentxm/workspace-state 0.28.4 → 0.28.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/dist/src/index.d.ts +3 -2
  2. package/dist/src/index.js +3 -2
  3. package/dist/src/lockfile/lockfile.d.ts +3 -3
  4. package/dist/src/lockfile/schema.d.ts +5 -4
  5. package/dist/src/lockfile/schema.js +5 -4
  6. package/dist/src/settings/schema.d.ts +0 -10
  7. package/dist/src/settings/schema.js +1 -22
  8. package/dist/src/workspace/accepted-canonical-ref.d.ts +3 -3
  9. package/dist/src/workspace/accepted-canonical-ref.js +2 -2
  10. package/dist/src/workspace/canonical-observation.js +8 -5
  11. package/dist/src/workspace/configured-agent-outcomes.d.ts +0 -1
  12. package/dist/src/workspace/configured-agent-outcomes.js +0 -11
  13. package/dist/src/workspace/desired-state-graph.d.ts +13 -1
  14. package/dist/src/workspace/desired-state-graph.js +80 -10
  15. package/dist/src/workspace/mcp-entry-semantics.d.ts +2 -5
  16. package/dist/src/workspace/mcp-entry-semantics.js +2 -4
  17. package/dist/src/workspace/mcp-source-identity.d.ts +17 -0
  18. package/dist/src/workspace/mcp-source-identity.js +40 -0
  19. package/dist/src/workspace/read-model/__fixtures__/builder.js +1 -1
  20. package/dist/src/workspace/read-model/errors.d.ts +12 -2
  21. package/dist/src/workspace/read-model/errors.js +3 -0
  22. package/dist/src/workspace/read-model/extensions/mcp-server.js +46 -11
  23. package/dist/src/workspace/read-model/state.js +18 -2
  24. package/dist/src/workspace/read-model/types.d.ts +1 -1
  25. package/dist/src/workspace/read-model-record-readers.js +1 -7
  26. package/dist/src/workspace/service-interface.d.ts +9 -7
  27. package/dist/src/workspace/service.d.ts +241 -10
  28. package/dist/src/workspace/service.js +50 -18
  29. package/dist/src/workspace/test-stubs.js +3 -2
  30. package/package.json +5 -3
@@ -22,6 +22,7 @@ import * as Path from "effect/Path";
22
22
  import * as Option from "effect/Option";
23
23
  import type { Handle } from "@agentxm/extension-model/unstable/extensions/handle";
24
24
  import { DesiredPackGraphIncomplete, InvalidAgentId, LockedSkillMissing, SettingsEntryMissing, WorkspaceNotInitialized } from "./errors.js";
25
+ import { LockfileVersionUnsupported } from "./read-model/errors.js";
25
26
  import { LockfileValidationError } from "../lockfile/errors.js";
26
27
  import { type HookEntry, type KnowledgeEntry, type InstructionsConfigValue, type McpServerEntry, type SkillEntry, type SubagentEntry, type PackEntry, type RuleEntry, type SourceHostConfig } from "../settings/index.js";
27
28
  import { type ScopedReleaseAgeExcludePattern } from "@agentxm/extension-model/unstable/extensions/release-age";
@@ -36,8 +37,9 @@ export type WorkspaceLayerOptions = WorkspaceMutationsOptions;
36
37
  /**
37
38
  * Create the workspace mutations service from an existing workspace on disk.
38
39
  *
39
- * The workspace must already be initialized. Missing or invalid settings fail
40
- * fast with a typed `WorkspaceMutationsError`. The two operations-side
40
+ * The workspace must already be initialized. Missing or invalid settings and
41
+ * invalid or unsupported lockfiles fail fast with a typed
42
+ * `WorkspaceMutationsError`. The two operations-side
41
43
  * capabilities — the transaction runner and transition acquirer — are
42
44
  * injected through `makeCapabilities`; the composition seam in
43
45
  * `./operations/load-workspace.ts` supplies the live implementation.
@@ -52,7 +54,7 @@ export declare const makeWorkspaceMutations: (options: WorkspaceLayerOptions, ma
52
54
  getLockfileState: () => Effect.Effect<LockfileState, LockfileValidationError | WorkspaceRootEscape>;
53
55
  getDesiredStateGraph: (graphOptions?: Parameters<(graphOptions?: {
54
56
  readonly prospectivePacks?: Parameters<typeof buildDesiredStateGraph>[0]["prospectivePacks"];
55
- }) => Effect.Effect<import("./desired-state-graph.js").DesiredStateGraph, SettingsReadError | LockfileReadError | WorkspaceRootEscape, never>>[0]) => Effect.Effect<import("./desired-state-graph.js").DesiredStateGraph, SettingsReadError | LockfileReadError | WorkspaceRootEscape, never>;
57
+ }) => Effect.Effect<import("./desired-state-graph.js").DesiredStateGraph, import("./read-model/errors.js").SettingsIoError | import("./read-model/errors.js").SettingsParseError | import("./read-model/errors.js").SettingsDecodeError | import("./read-model/errors.js").LockfileIoError | import("./read-model/errors.js").LockfileParseError | import("./read-model/errors.js").LockfileDecodeError | LockfileVersionUnsupported | WorkspaceRootEscape, never>>[0]) => Effect.Effect<import("./desired-state-graph.js").DesiredStateGraph, import("./read-model/errors.js").SettingsIoError | import("./read-model/errors.js").SettingsParseError | import("./read-model/errors.js").SettingsDecodeError | import("./read-model/errors.js").LockfileIoError | import("./read-model/errors.js").LockfileParseError | import("./read-model/errors.js").LockfileDecodeError | LockfileVersionUnsupported | WorkspaceRootEscape, never>;
56
58
  getConfiguredSources: () => Effect.Effect<ReadonlyArray<SourceHostConfig>, WorkspaceSettingsReadFailure>;
57
59
  getConfiguredSourceByName: (name: string) => Effect.Effect<Option.Option<{
58
60
  readonly name: string;
@@ -115,7 +117,6 @@ export declare const makeWorkspaceMutations: (options: WorkspaceLayerOptions, ma
115
117
  readonly [x: string]: string;
116
118
  };
117
119
  readonly url?: string | undefined;
118
- readonly agents?: readonly ("adal" | "aider-desk" | "amp" | "antigravity" | "antigravity-cli" | "augment" | "claude-code" | "cline" | "codearts-agent" | "codebuddy" | "codemaker" | "codestudio" | "codex" | "command-code" | "continue" | "cortex" | "crush" | "cursor" | "deepagents" | "devin" | "dexto" | "droid" | "firebender" | "forgecode" | "gemini-cli" | "github-copilot-cli" | "goose" | "grok-cli" | "hermes" | "ibm-bob" | "iflow-cli" | "junie" | "lingma" | "kilo" | "kimi-cli" | "kiro-cli" | "kode" | "mcpjam" | "minimax-code" | "mistral-vibe" | "mux" | "neovate" | "openclaw" | "opencode" | "openhands" | "ona" | "pi" | "pochi" | "qoder" | "qoder-cn" | "qwen-code" | "replit" | "roo" | "rovodev" | "tabnine-cli" | "trae-cn" | "trae" | "warp" | "windsurf" | "zencoder" | "zed" | "zenflow")[] | undefined;
119
120
  readonly command?: string | undefined;
120
121
  readonly args?: readonly string[] | undefined;
121
122
  readonly headers?: {
@@ -130,7 +131,6 @@ export declare const makeWorkspaceMutations: (options: WorkspaceLayerOptions, ma
130
131
  readonly kind: "inline";
131
132
  readonly url?: string | undefined;
132
133
  readonly source?: undefined;
133
- readonly agents?: readonly ("adal" | "aider-desk" | "amp" | "antigravity" | "antigravity-cli" | "augment" | "claude-code" | "cline" | "codearts-agent" | "codebuddy" | "codemaker" | "codestudio" | "codex" | "command-code" | "continue" | "cortex" | "crush" | "cursor" | "deepagents" | "devin" | "dexto" | "droid" | "firebender" | "forgecode" | "gemini-cli" | "github-copilot-cli" | "goose" | "grok-cli" | "hermes" | "ibm-bob" | "iflow-cli" | "junie" | "lingma" | "kilo" | "kimi-cli" | "kiro-cli" | "kode" | "mcpjam" | "minimax-code" | "mistral-vibe" | "mux" | "neovate" | "openclaw" | "opencode" | "openhands" | "ona" | "pi" | "pochi" | "qoder" | "qoder-cn" | "qwen-code" | "replit" | "roo" | "rovodev" | "tabnine-cli" | "trae-cn" | "trae" | "warp" | "windsurf" | "zencoder" | "zed" | "zenflow")[] | undefined;
134
134
  readonly command?: string | undefined;
135
135
  readonly args?: readonly string[] | undefined;
136
136
  readonly headers?: {
@@ -1534,7 +1534,7 @@ export declare const makeWorkspaceMutations: (options: WorkspaceLayerOptions, ma
1534
1534
  readonly treeIntegrity: string & import("effect/Brand").Brand<"TreeIntegrity">;
1535
1535
  };
1536
1536
  }, LockfileReadError | WorkspaceRootEscape, never>;
1537
- getLockedMcpServer: (name: string) => Effect.Effect<Option.Option<{
1537
+ getLockedMcpServer: (resolutionKey: string) => Effect.Effect<Option.Option<{
1538
1538
  readonly type: "github";
1539
1539
  readonly owner: string;
1540
1540
  readonly sourceType: "github";
@@ -1650,8 +1650,239 @@ export declare const makeWorkspaceMutations: (options: WorkspaceLayerOptions, ma
1650
1650
  readonly publisherBindingId: string;
1651
1651
  readonly treeIntegrity: string & import("effect/Brand").Brand<"TreeIntegrity">;
1652
1652
  }>, LockfileReadError | WorkspaceRootEscape, never>;
1653
- setMcpServer: ({ name, lockEntry, versionRange, env, enabled, agents }: SetMcpServerArgs) => Effect.Effect<void, import("../settings/errors.ts").SettingsWriteError | import("./transaction.ts").WorkspaceSnapshotError | import("../lockfile/errors.js").LockfileWriteError | LockfileValidationError | SettingsReadError | LockfileReadError | WorkspaceRootEscape, never>;
1654
- setMcpServerLock: ({ name, lockEntry }: SetMcpServerArgs) => Effect.Effect<void, import("../lockfile/errors.js").LockfileWriteError | LockfileValidationError | LockfileReadError | WorkspaceRootEscape, never>;
1653
+ getLockedMcpServerForConnection: (localName: string) => Effect.Effect<Option.None<{
1654
+ readonly type: "github";
1655
+ readonly owner: string;
1656
+ readonly sourceType: "github";
1657
+ readonly extensionType: "mcp-server";
1658
+ readonly sourceName: string;
1659
+ readonly packageFormat: "agentxm";
1660
+ readonly endpoint: URL;
1661
+ readonly workspaceName: string & import("effect/Brand").Brand<"ExtensionName">;
1662
+ readonly packageOwner: string & import("effect/Brand").Brand<"Handle">;
1663
+ readonly packageName: string & import("effect/Brand").Brand<"ExtensionName">;
1664
+ readonly repo: string;
1665
+ readonly resolvedCommit: string;
1666
+ readonly resolvedTree: string;
1667
+ readonly contentIdentity: string & import("effect/Brand").Brand<"SourceHash">;
1668
+ readonly treeIntegrity: string & import("effect/Brand").Brand<"TreeIntegrity">;
1669
+ readonly path?: string | undefined;
1670
+ readonly ref?: string | undefined;
1671
+ } | {
1672
+ readonly type: "gitlab";
1673
+ readonly owner: string;
1674
+ readonly sourceType: "gitlab";
1675
+ readonly extensionType: "mcp-server";
1676
+ readonly sourceName: string;
1677
+ readonly packageFormat: "agentxm";
1678
+ readonly endpoint: URL;
1679
+ readonly workspaceName: string & import("effect/Brand").Brand<"ExtensionName">;
1680
+ readonly packageOwner: string & import("effect/Brand").Brand<"Handle">;
1681
+ readonly packageName: string & import("effect/Brand").Brand<"ExtensionName">;
1682
+ readonly repo: string;
1683
+ readonly resolvedCommit: string;
1684
+ readonly resolvedTree: string;
1685
+ readonly contentIdentity: string & import("effect/Brand").Brand<"SourceHash">;
1686
+ readonly treeIntegrity: string & import("effect/Brand").Brand<"TreeIntegrity">;
1687
+ readonly path?: string | undefined;
1688
+ readonly ref?: string | undefined;
1689
+ } | {
1690
+ readonly type: "bitbucket";
1691
+ readonly owner: string;
1692
+ readonly sourceType: "bitbucket";
1693
+ readonly extensionType: "mcp-server";
1694
+ readonly sourceName: string;
1695
+ readonly packageFormat: "agentxm";
1696
+ readonly endpoint: URL;
1697
+ readonly workspaceName: string & import("effect/Brand").Brand<"ExtensionName">;
1698
+ readonly packageOwner: string & import("effect/Brand").Brand<"Handle">;
1699
+ readonly packageName: string & import("effect/Brand").Brand<"ExtensionName">;
1700
+ readonly repo: string;
1701
+ readonly resolvedCommit: string;
1702
+ readonly resolvedTree: string;
1703
+ readonly contentIdentity: string & import("effect/Brand").Brand<"SourceHash">;
1704
+ readonly treeIntegrity: string & import("effect/Brand").Brand<"TreeIntegrity">;
1705
+ readonly path?: string | undefined;
1706
+ readonly ref?: string | undefined;
1707
+ } | {
1708
+ readonly type: "azurerepos";
1709
+ readonly sourceType: "azurerepos";
1710
+ readonly extensionType: "mcp-server";
1711
+ readonly sourceName: string;
1712
+ readonly packageFormat: "agentxm";
1713
+ readonly endpoint: URL;
1714
+ readonly workspaceName: string & import("effect/Brand").Brand<"ExtensionName">;
1715
+ readonly packageOwner: string & import("effect/Brand").Brand<"Handle">;
1716
+ readonly packageName: string & import("effect/Brand").Brand<"ExtensionName">;
1717
+ readonly repo: string;
1718
+ readonly resolvedCommit: string;
1719
+ readonly resolvedTree: string;
1720
+ readonly contentIdentity: string & import("effect/Brand").Brand<"SourceHash">;
1721
+ readonly treeIntegrity: string & import("effect/Brand").Brand<"TreeIntegrity">;
1722
+ readonly organization: string;
1723
+ readonly project: string;
1724
+ readonly path?: string | undefined;
1725
+ readonly ref?: string | undefined;
1726
+ } | {
1727
+ readonly type: "git";
1728
+ readonly url: string;
1729
+ readonly sourceType: "git";
1730
+ readonly extensionType: "mcp-server";
1731
+ readonly sourceName: "git";
1732
+ readonly packageFormat: "agentxm";
1733
+ readonly workspaceName: string & import("effect/Brand").Brand<"ExtensionName">;
1734
+ readonly packageOwner: string & import("effect/Brand").Brand<"Handle">;
1735
+ readonly packageName: string & import("effect/Brand").Brand<"ExtensionName">;
1736
+ readonly resolvedCommit: string;
1737
+ readonly resolvedTree: string;
1738
+ readonly contentIdentity: string & import("effect/Brand").Brand<"SourceHash">;
1739
+ readonly treeIntegrity: string & import("effect/Brand").Brand<"TreeIntegrity">;
1740
+ readonly path?: string | undefined;
1741
+ readonly ref?: string | undefined;
1742
+ } | {
1743
+ readonly type: "local";
1744
+ readonly sourceType: "local";
1745
+ readonly sourceName: "local";
1746
+ readonly extensionType: "mcp-server";
1747
+ readonly workspaceName: string & import("effect/Brand").Brand<"ExtensionName">;
1748
+ readonly packageFormat: "agentxm";
1749
+ readonly packageOwner: string & import("effect/Brand").Brand<"Handle">;
1750
+ readonly packageName: string & import("effect/Brand").Brand<"ExtensionName">;
1751
+ readonly path: string;
1752
+ readonly contentIdentity: string & import("effect/Brand").Brand<"SourceHash">;
1753
+ readonly treeIntegrity: string & import("effect/Brand").Brand<"TreeIntegrity">;
1754
+ } | {
1755
+ readonly type: "registry";
1756
+ readonly sourceType: "registry";
1757
+ readonly endpoint: URL;
1758
+ readonly extensionType: "mcp-server";
1759
+ readonly workspaceName: string & import("effect/Brand").Brand<"ExtensionName">;
1760
+ readonly packageFormat: "agentxm";
1761
+ readonly owner: string & import("effect/Brand").Brand<"Handle">;
1762
+ readonly name: string & import("effect/Brand").Brand<"ExtensionName">;
1763
+ readonly resolvedVersion: string & import("effect/Brand").Brand<"Version">;
1764
+ readonly integrity: string;
1765
+ readonly sourceName: string;
1766
+ readonly publisherBindingId: string;
1767
+ readonly treeIntegrity: string & import("effect/Brand").Brand<"TreeIntegrity">;
1768
+ }> | Option.Some<{
1769
+ readonly type: "github";
1770
+ readonly owner: string;
1771
+ readonly sourceType: "github";
1772
+ readonly extensionType: "mcp-server";
1773
+ readonly sourceName: string;
1774
+ readonly packageFormat: "agentxm";
1775
+ readonly endpoint: URL;
1776
+ readonly workspaceName: string & import("effect/Brand").Brand<"ExtensionName">;
1777
+ readonly packageOwner: string & import("effect/Brand").Brand<"Handle">;
1778
+ readonly packageName: string & import("effect/Brand").Brand<"ExtensionName">;
1779
+ readonly repo: string;
1780
+ readonly resolvedCommit: string;
1781
+ readonly resolvedTree: string;
1782
+ readonly contentIdentity: string & import("effect/Brand").Brand<"SourceHash">;
1783
+ readonly treeIntegrity: string & import("effect/Brand").Brand<"TreeIntegrity">;
1784
+ readonly path?: string | undefined;
1785
+ readonly ref?: string | undefined;
1786
+ } | {
1787
+ readonly type: "gitlab";
1788
+ readonly owner: string;
1789
+ readonly sourceType: "gitlab";
1790
+ readonly extensionType: "mcp-server";
1791
+ readonly sourceName: string;
1792
+ readonly packageFormat: "agentxm";
1793
+ readonly endpoint: URL;
1794
+ readonly workspaceName: string & import("effect/Brand").Brand<"ExtensionName">;
1795
+ readonly packageOwner: string & import("effect/Brand").Brand<"Handle">;
1796
+ readonly packageName: string & import("effect/Brand").Brand<"ExtensionName">;
1797
+ readonly repo: string;
1798
+ readonly resolvedCommit: string;
1799
+ readonly resolvedTree: string;
1800
+ readonly contentIdentity: string & import("effect/Brand").Brand<"SourceHash">;
1801
+ readonly treeIntegrity: string & import("effect/Brand").Brand<"TreeIntegrity">;
1802
+ readonly path?: string | undefined;
1803
+ readonly ref?: string | undefined;
1804
+ } | {
1805
+ readonly type: "bitbucket";
1806
+ readonly owner: string;
1807
+ readonly sourceType: "bitbucket";
1808
+ readonly extensionType: "mcp-server";
1809
+ readonly sourceName: string;
1810
+ readonly packageFormat: "agentxm";
1811
+ readonly endpoint: URL;
1812
+ readonly workspaceName: string & import("effect/Brand").Brand<"ExtensionName">;
1813
+ readonly packageOwner: string & import("effect/Brand").Brand<"Handle">;
1814
+ readonly packageName: string & import("effect/Brand").Brand<"ExtensionName">;
1815
+ readonly repo: string;
1816
+ readonly resolvedCommit: string;
1817
+ readonly resolvedTree: string;
1818
+ readonly contentIdentity: string & import("effect/Brand").Brand<"SourceHash">;
1819
+ readonly treeIntegrity: string & import("effect/Brand").Brand<"TreeIntegrity">;
1820
+ readonly path?: string | undefined;
1821
+ readonly ref?: string | undefined;
1822
+ } | {
1823
+ readonly type: "azurerepos";
1824
+ readonly sourceType: "azurerepos";
1825
+ readonly extensionType: "mcp-server";
1826
+ readonly sourceName: string;
1827
+ readonly packageFormat: "agentxm";
1828
+ readonly endpoint: URL;
1829
+ readonly workspaceName: string & import("effect/Brand").Brand<"ExtensionName">;
1830
+ readonly packageOwner: string & import("effect/Brand").Brand<"Handle">;
1831
+ readonly packageName: string & import("effect/Brand").Brand<"ExtensionName">;
1832
+ readonly repo: string;
1833
+ readonly resolvedCommit: string;
1834
+ readonly resolvedTree: string;
1835
+ readonly contentIdentity: string & import("effect/Brand").Brand<"SourceHash">;
1836
+ readonly treeIntegrity: string & import("effect/Brand").Brand<"TreeIntegrity">;
1837
+ readonly organization: string;
1838
+ readonly project: string;
1839
+ readonly path?: string | undefined;
1840
+ readonly ref?: string | undefined;
1841
+ } | {
1842
+ readonly type: "git";
1843
+ readonly url: string;
1844
+ readonly sourceType: "git";
1845
+ readonly extensionType: "mcp-server";
1846
+ readonly sourceName: "git";
1847
+ readonly packageFormat: "agentxm";
1848
+ readonly workspaceName: string & import("effect/Brand").Brand<"ExtensionName">;
1849
+ readonly packageOwner: string & import("effect/Brand").Brand<"Handle">;
1850
+ readonly packageName: string & import("effect/Brand").Brand<"ExtensionName">;
1851
+ readonly resolvedCommit: string;
1852
+ readonly resolvedTree: string;
1853
+ readonly contentIdentity: string & import("effect/Brand").Brand<"SourceHash">;
1854
+ readonly treeIntegrity: string & import("effect/Brand").Brand<"TreeIntegrity">;
1855
+ readonly path?: string | undefined;
1856
+ readonly ref?: string | undefined;
1857
+ } | {
1858
+ readonly type: "local";
1859
+ readonly sourceType: "local";
1860
+ readonly sourceName: "local";
1861
+ readonly extensionType: "mcp-server";
1862
+ readonly workspaceName: string & import("effect/Brand").Brand<"ExtensionName">;
1863
+ readonly packageFormat: "agentxm";
1864
+ readonly packageOwner: string & import("effect/Brand").Brand<"Handle">;
1865
+ readonly packageName: string & import("effect/Brand").Brand<"ExtensionName">;
1866
+ readonly path: string;
1867
+ readonly contentIdentity: string & import("effect/Brand").Brand<"SourceHash">;
1868
+ readonly treeIntegrity: string & import("effect/Brand").Brand<"TreeIntegrity">;
1869
+ } | {
1870
+ readonly type: "registry";
1871
+ readonly sourceType: "registry";
1872
+ readonly endpoint: URL;
1873
+ readonly extensionType: "mcp-server";
1874
+ readonly workspaceName: string & import("effect/Brand").Brand<"ExtensionName">;
1875
+ readonly packageFormat: "agentxm";
1876
+ readonly owner: string & import("effect/Brand").Brand<"Handle">;
1877
+ readonly name: string & import("effect/Brand").Brand<"ExtensionName">;
1878
+ readonly resolvedVersion: string & import("effect/Brand").Brand<"Version">;
1879
+ readonly integrity: string;
1880
+ readonly sourceName: string;
1881
+ readonly publisherBindingId: string;
1882
+ readonly treeIntegrity: string & import("effect/Brand").Brand<"TreeIntegrity">;
1883
+ }>, import("./read-model/errors.js").SettingsIoError | import("./read-model/errors.js").SettingsParseError | import("./read-model/errors.js").SettingsDecodeError | LockfileReadError | WorkspaceRootEscape, never>;
1884
+ setMcpServer: ({ name, resolutionKey, lockEntry, versionRange, env, enabled, }: SetMcpServerArgs) => Effect.Effect<void, import("../settings/errors.ts").SettingsWriteError | import("./transaction.ts").WorkspaceSnapshotError | import("../lockfile/errors.js").LockfileWriteError | LockfileValidationError | SettingsReadError | LockfileReadError | WorkspaceRootEscape, never>;
1885
+ setMcpServerLock: ({ name: _name, resolutionKey, lockEntry }: SetMcpServerArgs) => Effect.Effect<void, import("../lockfile/errors.js").LockfileWriteError | LockfileValidationError | LockfileReadError | WorkspaceRootEscape, never>;
1655
1886
  updateMcpServerEntry: (name: string, updater: (entry: McpServerEntry) => McpServerEntry) => Effect.Effect<void, import("../settings/errors.ts").SettingsWriteError | import("./transaction.ts").WorkspaceSnapshotError | SettingsEntryMissing | SettingsReadError | WorkspaceRootEscape, never>;
1656
1887
  setMcpServerEntry: (name: string, entry: McpServerEntry) => Effect.Effect<void, import("../settings/errors.ts").SettingsWriteError | import("./transaction.ts").WorkspaceSnapshotError | SettingsReadError | WorkspaceRootEscape, never>;
1657
1888
  removeMcpServer: (name: string) => Effect.Effect<void, import("../settings/errors.ts").SettingsWriteError | import("./transaction.ts").WorkspaceSnapshotError | import("../lockfile/errors.js").LockfileWriteError | LockfileValidationError | SettingsReadError | LockfileReadError | WorkspaceRootEscape, never>;
@@ -1660,6 +1891,6 @@ export declare const makeWorkspaceMutations: (options: WorkspaceLayerOptions, ma
1660
1891
  removeMcpServerLock: (name: string) => Effect.Effect<void, import("../lockfile/errors.js").LockfileWriteError | LockfileValidationError | LockfileReadError | WorkspaceRootEscape, never>;
1661
1892
  removePackSettings: (name: string) => Effect.Effect<void, import("../settings/errors.ts").SettingsWriteError | import("./transaction.ts").WorkspaceSnapshotError | SettingsReadError | WorkspaceRootEscape, never>;
1662
1893
  removePackLock: (name: string) => Effect.Effect<void, import("../lockfile/errors.js").LockfileWriteError | LockfileValidationError | LockfileReadError | WorkspaceRootEscape, never>;
1663
- isExtensionRequiredByInstalledPack: (target: ExtensionTarget) => Effect.Effect<boolean, DesiredPackGraphIncomplete | SettingsReadError | LockfileReadError | WorkspaceRootEscape, never>;
1664
- }, import("./errors.js").WorkspaceLayoutError | WorkspaceNotInitialized | WorkspaceSettingsReadFailure, FileSystem.FileSystem | Path.Path>;
1894
+ isExtensionRequiredByInstalledPack: (target: ExtensionTarget) => Effect.Effect<boolean, DesiredPackGraphIncomplete | import("./read-model/errors.js").SettingsIoError | import("./read-model/errors.js").SettingsParseError | import("./read-model/errors.js").SettingsDecodeError | import("./read-model/errors.js").LockfileIoError | import("./read-model/errors.js").LockfileParseError | import("./read-model/errors.js").LockfileDecodeError | LockfileVersionUnsupported | WorkspaceRootEscape, never>;
1895
+ }, import("./errors.js").WorkspaceLayoutError | WorkspaceNotInitialized | LockfileReadError | WorkspaceSettingsReadFailure, FileSystem.FileSystem | Path.Path>;
1665
1896
  //# sourceMappingURL=service.d.ts.map
@@ -28,6 +28,7 @@ import { computePackPathsForLayout } from "./pack-paths.js";
28
28
  import { sanitizeName } from "./extension-name.js";
29
29
  import { ConfigurableAgentIdSchema, decodeExtensionNameSync, formatFqn, parseSourceQualifiedRegistrySourcePatternParts, } from "@agentxm/extension-model/unstable/extensions";
30
30
  import { DesiredPackGraphIncomplete, InvalidAgentId, LockedSkillMissing, SettingsEntryMissing, WorkspaceNotInitialized, } from "./errors.js";
31
+ import { LockfileVersionUnsupported } from "./read-model/errors.js";
31
32
  import { LockfileValidationError } from "../lockfile/errors.js";
32
33
  import { createDefaultSettings, writeSettingsAtPath, } from "../settings/index.js";
33
34
  import { DEFAULT_MINIMUM_RELEASE_AGE } from "@agentxm/registry-protocol/unstable/registry/release-age-policy";
@@ -71,15 +72,22 @@ const lockEntrySemanticallyEqual = (current, next) => current !== undefined && s
71
72
  const skillLockEntrySemanticallyEqual = (current, next) => lockEntrySemanticallyEqual(current, next);
72
73
  const preserveAcceptedResolutionOnNoop = (current, next) => (lockEntrySemanticallyEqual(current, next) && current !== undefined ? current : next);
73
74
  const subagentLockEntrySemanticallyEqual = (current, next) => lockEntrySemanticallyEqual(current, next);
74
- const requireInitializedWorkspace = (settingsPath, settings) => settings.pipe(Effect.flatMap(Option.match({
75
- onNone: () => Effect.fail(new WorkspaceNotInitialized({ settingsPath })),
75
+ const requireInitializedWorkspace = (settingsPath, settings, lockfile) => settings.pipe(Effect.flatMap(Option.match({
76
+ onNone: () => lockfile.pipe(Effect.matchEffect({
77
+ onFailure: (error) => error instanceof LockfileVersionUnsupported &&
78
+ error.observedVersion > error.supportedVersion
79
+ ? Effect.fail(error)
80
+ : Effect.fail(new WorkspaceNotInitialized({ settingsPath })),
81
+ onSuccess: () => Effect.fail(new WorkspaceNotInitialized({ settingsPath })),
82
+ })),
76
83
  onSome: () => Effect.void,
77
84
  })));
78
85
  /**
79
86
  * Create the workspace mutations service from an existing workspace on disk.
80
87
  *
81
- * The workspace must already be initialized. Missing or invalid settings fail
82
- * fast with a typed `WorkspaceMutationsError`. The two operations-side
88
+ * The workspace must already be initialized. Missing or invalid settings and
89
+ * invalid or unsupported lockfiles fail fast with a typed
90
+ * `WorkspaceMutationsError`. The two operations-side
83
91
  * capabilities — the transaction runner and transition acquirer — are
84
92
  * injected through `makeCapabilities`; the composition seam in
85
93
  * `./operations/load-workspace.ts` supplies the live implementation.
@@ -112,7 +120,8 @@ export const makeWorkspaceMutations = (options, makeCapabilities) => Effect.gen(
112
120
  const readSettingsCell = (dir, sharedScope) => makeWorkspaceReadModel(scopeForDir(dir, sharedScope)).pipe(Effect.flatMap((readModel) => readModel.state.settings), Effect.provide(contextEnv));
113
121
  const readLockfileCell = (dir, sharedScope) => makeWorkspaceReadModel(scopeForDir(dir, sharedScope)).pipe(Effect.flatMap((readModel) => readModel.state.lockfile), Effect.map(Option.getOrElse(createEmptyLockfile)), Effect.provide(contextEnv));
114
122
  if (options.allowUninitialized !== true) {
115
- yield* requireInitializedWorkspace(settingsPath, readSettingsCell(workspaceDir));
123
+ yield* requireInitializedWorkspace(settingsPath, readSettingsCell(workspaceDir), readLockfileCell(workspaceDir));
124
+ yield* readLockfileCell(workspaceDir);
116
125
  }
117
126
  const projectSettings = yield* readSettingsCell(localRuntimeDir, "project").pipe(Effect.map(Option.getOrElse(() => createDefaultSettings())));
118
127
  const userSettings = yield* readSettingsCell(userRuntimeDir, "user").pipe(Effect.map(Option.getOrElse(() => createDefaultSettings())));
@@ -161,15 +170,23 @@ export const makeWorkspaceMutations = (options, makeCapabilities) => Effect.gen(
161
170
  }
162
171
  // An unreadable or corrupt lockfile is actionable workspace state,
163
172
  // not a violated invariant.
164
- return yield* readLockfileSafe(workspaceDir).pipe(Effect.as("ok"), Effect.catchTag(["LockfileIoError", "LockfileParseError", "LockfileDecodeError"], () => Effect.succeed("invalid")));
173
+ return yield* readLockfileSafe(workspaceDir).pipe(Effect.as("ok"), Effect.catchTag([
174
+ "LockfileIoError",
175
+ "LockfileParseError",
176
+ "LockfileDecodeError",
177
+ "LockfileVersionUnsupported",
178
+ ], () => Effect.succeed("invalid")));
165
179
  }).pipe(Effect.withSpan("WorkspaceMutations.getLockfileState"));
166
180
  const readScopedContext = (f) => makeWorkspaceReadModel(scopeForDir(workspaceDir)).pipe(Effect.flatMap(f), Effect.provide(contextEnv));
167
181
  const readDesiredStateGraph = (graphOptions) => Effect.gen(function* () {
168
182
  const settings = yield* readSettingsSafe(workspaceDir);
183
+ const configuredSources = yield* getConfiguredSources();
184
+ const registryAuthorities = Object.fromEntries(configuredSources.flatMap((source) => source.type === "registry" ? [[source.name, source.location]] : []));
169
185
  const graph = yield* buildDesiredStateGraph({
170
186
  baseDir,
171
187
  settings,
172
188
  layout,
189
+ registryAuthorities,
173
190
  ...(graphOptions?.prospectivePacks === undefined
174
191
  ? {}
175
192
  : { prospectivePacks: graphOptions.prospectivePacks }),
@@ -1083,14 +1100,21 @@ export const makeWorkspaceMutations = (options, makeCapabilities) => Effect.gen(
1083
1100
  // MCP Server methods
1084
1101
  // -----------------------------------------------------------------------
1085
1102
  getLockedMcpServers: () => readLockfileSafe(workspaceDir).pipe(Effect.map((lf) => lf.mcpServers ?? {})),
1086
- getLockedMcpServer: (name) => readLockfileSafe(workspaceDir).pipe(Effect.map((lf) => Option.fromUndefinedOr((lf.mcpServers ?? {})[name]))),
1087
- setMcpServer: ({ name, lockEntry, versionRange, env, enabled, agents }) => withMutex(Effect.gen(function* () {
1103
+ getLockedMcpServer: (resolutionKey) => readLockfileSafe(workspaceDir).pipe(Effect.map((lf) => Option.fromUndefinedOr((lf.mcpServers ?? {})[resolutionKey]))),
1104
+ getLockedMcpServerForConnection: (localName) => Effect.gen(function* () {
1105
+ const graph = yield* readDesiredStateGraph();
1106
+ const node = graph.nodes.find((candidate) => candidate.type === "mcp-server" && candidate.name === localName);
1107
+ if (node === undefined || node.authority === "inline")
1108
+ return Option.none();
1109
+ const lockfile = yield* readLockfileSafe(workspaceDir);
1110
+ return Option.fromUndefinedOr((lockfile.mcpServers ?? {})[node.identity]);
1111
+ }),
1112
+ setMcpServer: ({ name, resolutionKey, lockEntry, versionRange, env, enabled, }) => withMutex(Effect.gen(function* () {
1088
1113
  // Update settings (uses "mcpServers" key)
1089
1114
  const currentSettings = yield* readSettingsSafe(workspaceDir);
1090
1115
  const currentMcpServers = currentSettings.mcpServers ?? {};
1091
1116
  const currentEnabled = currentMcpServers[name]?.enabled ?? true;
1092
1117
  const currentEnv = currentMcpServers[name]?.env ?? {};
1093
- const currentAgents = currentMcpServers[name]?.agents;
1094
1118
  const settingsEntry = {
1095
1119
  kind: "sourced",
1096
1120
  source: lockEntry.type === "registry"
@@ -1098,7 +1122,7 @@ export const makeWorkspaceMutations = (options, makeCapabilities) => Effect.gen(
1098
1122
  const fqn = formatFqn({
1099
1123
  owner: lockEntry.owner,
1100
1124
  type: "mcp-server",
1101
- name: decodeExtensionNameSync(name),
1125
+ name: lockEntry.name,
1102
1126
  });
1103
1127
  const locator = Option.isSome(versionRange)
1104
1128
  ? `${fqn}@${versionRange.value}`
@@ -1108,9 +1132,6 @@ export const makeWorkspaceMutations = (options, makeCapabilities) => Effect.gen(
1108
1132
  : printSourceParams(lockEntryToSourceParams(lockEntry)),
1109
1133
  enabled: enabled ?? currentEnabled,
1110
1134
  env: env ?? currentEnv,
1111
- ...((agents ?? currentAgents) === undefined
1112
- ? {}
1113
- : { agents: agents ?? currentAgents }),
1114
1135
  };
1115
1136
  const updatedSettings = {
1116
1137
  ...currentSettings,
@@ -1127,12 +1148,12 @@ export const makeWorkspaceMutations = (options, makeCapabilities) => Effect.gen(
1127
1148
  ...currentLockfile,
1128
1149
  mcpServers: {
1129
1150
  ...currentLockedMcpServers,
1130
- [name]: preserveAcceptedResolutionOnNoop(currentLockedMcpServers[name], lockEntry),
1151
+ [resolutionKey]: preserveAcceptedResolutionOnNoop(currentLockedMcpServers[resolutionKey], lockEntry),
1131
1152
  },
1132
1153
  };
1133
1154
  yield* commitWorkspaceState(currentLockfile, updatedLockfile);
1134
1155
  })).pipe(Effect.withSpan("WorkspaceMutations.setMcpServer")),
1135
- setMcpServerLock: ({ name, lockEntry }) => withMutex(Effect.gen(function* () {
1156
+ setMcpServerLock: ({ name: _name, resolutionKey, lockEntry }) => withMutex(Effect.gen(function* () {
1136
1157
  // Update lockfile only (skip settings) — used for pack dependencies
1137
1158
  const currentLockfile = yield* readLockfileSafe(workspaceDir);
1138
1159
  const currentLockedMcpServers = currentLockfile.mcpServers ?? {};
@@ -1140,7 +1161,7 @@ export const makeWorkspaceMutations = (options, makeCapabilities) => Effect.gen(
1140
1161
  ...currentLockfile,
1141
1162
  mcpServers: {
1142
1163
  ...currentLockedMcpServers,
1143
- [name]: preserveAcceptedResolutionOnNoop(currentLockedMcpServers[name], lockEntry),
1164
+ [resolutionKey]: preserveAcceptedResolutionOnNoop(currentLockedMcpServers[resolutionKey], lockEntry),
1144
1165
  },
1145
1166
  };
1146
1167
  yield* commitWorkspaceState(currentLockfile, updatedLockfile);
@@ -1166,6 +1187,11 @@ export const makeWorkspaceMutations = (options, makeCapabilities) => Effect.gen(
1166
1187
  yield* writeScopedSettings(updatedSettings).pipe(Effect.provide(fsLayer));
1167
1188
  })),
1168
1189
  removeMcpServer: (name) => withMutex(Effect.gen(function* () {
1190
+ const graph = yield* readDesiredStateGraph();
1191
+ const desiredNode = graph.nodes.find((node) => node.type === "mcp-server" && node.name === name);
1192
+ const closure = desiredNode === undefined || desiredNode.authority === "inline"
1193
+ ? undefined
1194
+ : graph.mcpSourceClosures.find((candidate) => candidate.identity === desiredNode.identity);
1169
1195
  // Update settings (uses "mcpServers" key)
1170
1196
  const currentSettings = yield* readSettingsSafe(workspaceDir);
1171
1197
  const currentMcpServers = currentSettings.mcpServers ?? {};
@@ -1182,8 +1208,14 @@ export const makeWorkspaceMutations = (options, makeCapabilities) => Effect.gen(
1182
1208
  // Update lockfile (uses "mcpServers" key)
1183
1209
  const currentLockfile = yield* readLockfileSafe(workspaceDir);
1184
1210
  const currentLockedMcpServers = currentLockfile.mcpServers ?? {};
1185
- if (name in currentLockedMcpServers) {
1186
- const { [name]: __, ...remainingLockedMcpServers } = currentLockedMcpServers;
1211
+ const resolutionKey = desiredNode?.identity;
1212
+ const keepSharedResolution = closure !== undefined &&
1213
+ (closure.localNames.length > 1 ||
1214
+ closure.origins.some((origin) => origin.type === "pack"));
1215
+ if (resolutionKey !== undefined &&
1216
+ !keepSharedResolution &&
1217
+ resolutionKey in currentLockedMcpServers) {
1218
+ const { [resolutionKey]: __, ...remainingLockedMcpServers } = currentLockedMcpServers;
1187
1219
  void __;
1188
1220
  const updatedLockfile = {
1189
1221
  ...currentLockfile,
@@ -188,7 +188,7 @@ export const makeBaseWorkspaceMock = (axmDir = "/tmp/axm", overrides) => {
188
188
  ],
189
189
  };
190
190
  }));
191
- return { complete: true, nodes, problems: [] };
191
+ return { complete: true, nodes, mcpSourceClosures: [], problems: [] };
192
192
  });
193
193
  const runTransaction = (args) => Effect.gen(function* () {
194
194
  const value = yield* args.transition;
@@ -304,6 +304,7 @@ export const makeBaseWorkspaceMock = (axmDir = "/tmp/axm", overrides) => {
304
304
  removeSubagentLock: () => Effect.void,
305
305
  getLockedMcpServers: lockedMcpServers,
306
306
  getLockedMcpServer: entryFrom(lockedMcpServers),
307
+ getLockedMcpServerForConnection: entryFrom(lockedMcpServers),
307
308
  setMcpServer: () => Effect.void,
308
309
  setMcpServerLock: () => Effect.void,
309
310
  updateMcpServerEntry: () => Effect.void,
@@ -337,7 +338,7 @@ export const writeWorkspaceFiles = (runtimeDir, opts = {}) => {
337
338
  ...(opts.sources && { sources: opts.sources }),
338
339
  };
339
340
  const lockfile = {
340
- lockfileVersion: 6,
341
+ lockfileVersion: 7,
341
342
  skills: opts.lockfileSkills ?? {},
342
343
  ...(hasEntries(opts.lockfileMcpServers) && { mcps: opts.lockfileMcpServers }),
343
344
  ...(hasEntries(opts.lockfileSubagents) && { subagents: opts.lockfileSubagents }),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentxm/workspace-state",
3
- "version": "0.28.4",
3
+ "version": "0.28.6",
4
4
  "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.",
5
5
  "type": "module",
6
6
  "license": "FSL-1.1-MIT",
@@ -18,10 +18,12 @@
18
18
  "exports": {
19
19
  ".": {
20
20
  "types": "./dist/src/index.d.ts",
21
+ "axm-source": "./src/index.ts",
21
22
  "default": "./dist/src/index.js"
22
23
  },
23
24
  "./testing": {
24
25
  "types": "./dist/src/testing.d.ts",
26
+ "axm-source": "./src/testing.ts",
25
27
  "default": "./dist/src/testing.js"
26
28
  }
27
29
  },
@@ -43,8 +45,8 @@
43
45
  "jsonc-parser": "^3.3.1",
44
46
  "semver": "^7.8.5",
45
47
  "yaml": "^2.9.0",
46
- "@agentxm/extension-model": "^0.28.4",
47
- "@agentxm/registry-protocol": "^0.28.4"
48
+ "@agentxm/extension-model": "^0.28.6",
49
+ "@agentxm/registry-protocol": "^0.28.6"
48
50
  },
49
51
  "devDependencies": {
50
52
  "@effect/platform-node": "4.0.0-rc.112",