@agentxm/workspace-configuration 0.28.4-bootstrap.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Inline MCP adoption policy: discover import sources from configured agents'
3
+ * native MCP configs, rewrite adopted entries with AXM management metadata,
4
+ * remove entries converted into managed packages, and apply an inline import
5
+ * as one validated workspace transaction. Prompting, planning, and rendering
6
+ * stay with the application.
7
+ *
8
+ * @experimental This API is unstable and may change without notice.
9
+ */
10
+ import * as Effect from "effect/Effect";
11
+ import * as FileSystem from "effect/FileSystem";
12
+ import * as Path from "effect/Path";
13
+ import { type WorkspaceMutationsService } from "@agentxm/workspace-state";
14
+ import { WorkspaceConfigurationFailed } from "./errors.js";
15
+ import type { McpImportAdoption, McpImportCandidate, McpImportSource } from "./mcp-import-preflight.js";
16
+ /**
17
+ * Discover the native MCP config sources the configured agents contribute for
18
+ * this workspace scope, with unsupported-format findings.
19
+ */
20
+ export declare const collectMcpImportSources: (ws: WorkspaceMutationsService, fs: FileSystem.FileSystem, path: Path.Path) => Effect.Effect<{
21
+ sources: McpImportSource[];
22
+ skipped: {
23
+ readonly name: string;
24
+ readonly reason: string;
25
+ }[];
26
+ }, WorkspaceConfigurationFailed | import("@agentxm/workspace-state").WorkspaceSettingsReadFailure, never>;
27
+ /**
28
+ * Remove a native entry converted into a managed package, refusing when the
29
+ * native config changed since the conversion was planned.
30
+ */
31
+ export declare const removeConvertedMcpConfig: (fs: FileSystem.FileSystem, adoption: McpImportAdoption) => Effect.Effect<void, WorkspaceConfigurationFailed>;
32
+ /**
33
+ * Adopt the losslessly importable candidates as inline settings entries and
34
+ * mark their native entries as AXM-managed, in one validated workspace
35
+ * transaction.
36
+ */
37
+ export declare const applyMcpImport: <HookError = never>(candidates: ReadonlyArray<McpImportCandidate>, ws: WorkspaceMutationsService, fs: FileSystem.FileSystem, hooks?: {
38
+ readonly beforeAdoptionWrite?: (adoption: McpImportAdoption) => Effect.Effect<void, HookError>;
39
+ }) => Effect.Effect<void, WorkspaceConfigurationFailed | import("@agentxm/workspace-state").WorkspaceSnapshotError | import("@agentxm/workspace-state").SettingsWriteError | import("@agentxm/workspace-state").WorkspaceRootEscape | import("@agentxm/workspace-state").SettingsIoError | import("@agentxm/workspace-state").SettingsParseError | import("@agentxm/workspace-state").SettingsDecodeError | import("@agentxm/workspace-state").WorkspaceDirectoryError | import("@agentxm/workspace-state").TransitionLockError | import("@agentxm/workspace-state").TransitionLockUnavailable | import("@agentxm/workspace-state").WorkspaceTransitionCompromised | import("@agentxm/workspace-state").WorkspaceRestorationIncomplete | HookError, never>;
40
+ //# sourceMappingURL=mcp-import.d.ts.map
@@ -0,0 +1,276 @@
1
+ /**
2
+ * Inline MCP adoption policy: discover import sources from configured agents'
3
+ * native MCP configs, rewrite adopted entries with AXM management metadata,
4
+ * remove entries converted into managed packages, and apply an inline import
5
+ * as one validated workspace transaction. Prompting, planning, and rendering
6
+ * stay with the application.
7
+ *
8
+ * @experimental This API is unstable and may change without notice.
9
+ */
10
+ import * as Effect from "effect/Effect";
11
+ import * as FileSystem from "effect/FileSystem";
12
+ import * as Option from "effect/Option";
13
+ import * as Path from "effect/Path";
14
+ import { CONFIGURABLE_AGENTS_BY_ID, } from "@agentxm/extension-model/unstable/agent-capabilities";
15
+ import { buildAxmMcpMetadataFromSettingsSource } from "@agentxm/extension-workspace";
16
+ import { AXM_MCP_METADATA_KEY, isAxmManagedMcpEntry, } from "@agentxm/workspace-state";
17
+ import { WorkspaceConfigurationFailed } from "./errors.js";
18
+ const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
19
+ const isCapabilityAgentId = (id) => Object.hasOwn(CONFIGURABLE_AGENTS_BY_ID, id);
20
+ const readAgentMcpConfig = (agent) => {
21
+ if (!isRecord(agent))
22
+ return Option.none();
23
+ const capabilities = agent["capabilities"];
24
+ if (!isRecord(capabilities))
25
+ return Option.none();
26
+ const mcp = capabilities["mcp-server"];
27
+ if (!isRecord(mcp))
28
+ return Option.none();
29
+ const axm = mcp["axm"];
30
+ if (!isRecord(axm))
31
+ return Option.none();
32
+ const writer = axm["writer"];
33
+ if (!isRecord(writer))
34
+ return Option.none();
35
+ const config = writer["config"];
36
+ if (!isRecord(config))
37
+ return Option.none();
38
+ const serversKey = config["serversKey"];
39
+ const targets = config["targets"];
40
+ if (typeof serversKey !== "string" || !Array.isArray(targets))
41
+ return Option.none();
42
+ const parsedTargets = [];
43
+ for (const target of targets) {
44
+ if (!isRecord(target))
45
+ continue;
46
+ const scope = target["scope"];
47
+ const targetPath = target["path"];
48
+ const format = target["format"];
49
+ if (typeof scope === "string" && typeof targetPath === "string" && typeof format === "string") {
50
+ parsedTargets.push({ scope, path: targetPath, format });
51
+ }
52
+ }
53
+ return Option.some({ serversKey, targets: parsedTargets });
54
+ };
55
+ const readJsonObject = (fs, filePath) => Effect.gen(function* () {
56
+ const exists = yield* fs.exists(filePath).pipe(Effect.catch(() => Effect.succeed(false)));
57
+ if (!exists)
58
+ return Option.none();
59
+ const raw = yield* fs.readFileString(filePath).pipe(Effect.mapError((error) => new WorkspaceConfigurationFailed({
60
+ category: "internal",
61
+ detail: `Failed to read MCP config: ${filePath}`,
62
+ cause: error,
63
+ })));
64
+ const parsed = yield* Effect.try({
65
+ try: () => JSON.parse(raw),
66
+ catch: (cause) => new WorkspaceConfigurationFailed({
67
+ category: "validation",
68
+ detail: `Invalid JSON in MCP config: ${filePath}`,
69
+ cause,
70
+ }),
71
+ });
72
+ return isRecord(parsed) ? Option.some(parsed) : Option.none();
73
+ });
74
+ /**
75
+ * Discover the native MCP config sources the configured agents contribute for
76
+ * this workspace scope, with unsupported-format findings.
77
+ */
78
+ export const collectMcpImportSources = (ws, fs, path) => Effect.gen(function* () {
79
+ const sources = [];
80
+ const skipped = new Map();
81
+ const sourceKeys = new Set();
82
+ const addSource = (filePath, serversKey, agentId) => {
83
+ const sourceKey = `${agentId}\0${filePath}\0${serversKey}`;
84
+ if (sourceKeys.has(sourceKey))
85
+ return Effect.void;
86
+ sourceKeys.add(sourceKey);
87
+ return readJsonObject(fs, filePath).pipe(Effect.map(Option.match({
88
+ onNone: () => undefined,
89
+ onSome: (config) => sources.push({ filePath, serversKey, config, agents: [agentId] }),
90
+ })));
91
+ };
92
+ const agentIds = [...(yield* ws.getConfiguredAgents())].sort((left, right) => left.localeCompare(right));
93
+ for (const agentId of agentIds) {
94
+ if (!isCapabilityAgentId(agentId))
95
+ continue;
96
+ const mcpConfig = readAgentMcpConfig(CONFIGURABLE_AGENTS_BY_ID[agentId]);
97
+ if (Option.isNone(mcpConfig))
98
+ continue;
99
+ const targets = mcpConfig.value.targets
100
+ .filter((target) => target.scope === ws.scope)
101
+ .sort((left, right) => left.path.localeCompare(right.path));
102
+ for (const target of targets) {
103
+ const relativeTarget = target.path.startsWith("~/") ? target.path.slice(2) : target.path;
104
+ const configPath = path.resolve(ws.baseDir, relativeTarget);
105
+ if (target.format !== "json") {
106
+ const exists = yield* fs
107
+ .exists(configPath)
108
+ .pipe(Effect.catch(() => Effect.succeed(false)));
109
+ if (exists) {
110
+ const finding = {
111
+ name: path.relative(ws.baseDir, configPath),
112
+ reason: `Unsupported MCP config format: ${target.format}`,
113
+ };
114
+ skipped.set(`${finding.name}\0${finding.reason}`, finding);
115
+ }
116
+ continue;
117
+ }
118
+ yield* addSource(configPath, mcpConfig.value.serversKey, agentId);
119
+ }
120
+ }
121
+ return { sources, skipped: Array.from(skipped.values()) };
122
+ });
123
+ const writeAdoptedMcpConfig = (fs, adoption) => Effect.gen(function* () {
124
+ const config = yield* readJsonObject(fs, adoption.filePath);
125
+ if (Option.isNone(config)) {
126
+ return yield* new WorkspaceConfigurationFailed({
127
+ category: "conflict",
128
+ detail: `MCP config disappeared before import: ${adoption.filePath}`,
129
+ });
130
+ }
131
+ const servers = config.value[adoption.serversKey];
132
+ const entry = isRecord(servers) ? servers[adoption.name] : undefined;
133
+ if (!isRecord(servers) || !isRecord(entry)) {
134
+ return yield* new WorkspaceConfigurationFailed({
135
+ category: "conflict",
136
+ detail: `MCP server ${adoption.name} changed before import`,
137
+ });
138
+ }
139
+ const updatedConfig = {
140
+ ...config.value,
141
+ [adoption.serversKey]: {
142
+ ...servers,
143
+ [adoption.name]: {
144
+ ...entry,
145
+ [AXM_MCP_METADATA_KEY]: buildAxmMcpMetadataFromSettingsSource("inline", adoption.name),
146
+ },
147
+ },
148
+ };
149
+ yield* fs
150
+ .writeFileString(adoption.filePath, `${JSON.stringify(updatedConfig, null, 2)}\n`)
151
+ .pipe(Effect.mapError((error) => new WorkspaceConfigurationFailed({
152
+ category: "internal",
153
+ detail: `Failed to write MCP config: ${adoption.filePath}`,
154
+ cause: error,
155
+ })));
156
+ });
157
+ /**
158
+ * Remove a native entry converted into a managed package, refusing when the
159
+ * native config changed since the conversion was planned.
160
+ */
161
+ export const removeConvertedMcpConfig = (fs, adoption) => Effect.gen(function* () {
162
+ const config = yield* readJsonObject(fs, adoption.filePath);
163
+ if (Option.isNone(config)) {
164
+ return yield* new WorkspaceConfigurationFailed({
165
+ category: "conflict",
166
+ detail: `MCP config disappeared before package conversion: ${adoption.filePath}`,
167
+ });
168
+ }
169
+ const servers = config.value[adoption.serversKey];
170
+ if (!isRecord(servers)) {
171
+ return yield* new WorkspaceConfigurationFailed({
172
+ category: "conflict",
173
+ detail: `MCP server collection changed before package conversion: ${adoption.filePath}`,
174
+ });
175
+ }
176
+ const entry = servers[adoption.name];
177
+ if (entry === undefined)
178
+ return;
179
+ if (!isRecord(entry)) {
180
+ return yield* new WorkspaceConfigurationFailed({
181
+ category: "conflict",
182
+ detail: `MCP server ${adoption.name} changed before package conversion`,
183
+ });
184
+ }
185
+ const remainingServers = Object.fromEntries(Object.entries(servers).filter(([name]) => name !== adoption.name));
186
+ const updatedConfig = {
187
+ ...config.value,
188
+ [adoption.serversKey]: remainingServers,
189
+ };
190
+ yield* fs
191
+ .writeFileString(adoption.filePath, `${JSON.stringify(updatedConfig, null, 2)}\n`)
192
+ .pipe(Effect.mapError((error) => new WorkspaceConfigurationFailed({
193
+ category: "internal",
194
+ detail: `Failed to replace native MCP config: ${adoption.filePath}`,
195
+ cause: error,
196
+ })));
197
+ });
198
+ const recordsEqual = (left, right) => {
199
+ const leftEntries = Object.entries(left ?? {}).sort(([leftKey], [rightKey]) => leftKey.localeCompare(rightKey));
200
+ const rightEntries = Object.entries(right ?? {}).sort(([leftKey], [rightKey]) => leftKey.localeCompare(rightKey));
201
+ return (leftEntries.length === rightEntries.length &&
202
+ leftEntries.every(([key, value], index) => {
203
+ const rightEntry = rightEntries[index];
204
+ return rightEntry !== undefined && key === rightEntry[0] && value === rightEntry[1];
205
+ }));
206
+ };
207
+ const arraysEqual = (left, right) => JSON.stringify([...(left ?? [])].sort((a, b) => a.localeCompare(b))) ===
208
+ JSON.stringify([...(right ?? [])].sort((a, b) => a.localeCompare(b)));
209
+ const candidateMatchesSettings = (candidate, entry) => entry !== undefined &&
210
+ entry.kind === "inline" &&
211
+ entry.enabled &&
212
+ entry.command ===
213
+ (candidate.definition.type === "stdio" ? candidate.definition.command : undefined) &&
214
+ JSON.stringify(entry.args ?? []) ===
215
+ JSON.stringify(candidate.definition.type === "stdio" ? candidate.definition.args : []) &&
216
+ entry.url === (candidate.definition.type === "http" ? candidate.definition.url : undefined) &&
217
+ recordsEqual(entry.headers, candidate.definition.type === "http" ? candidate.definition.headers : undefined) &&
218
+ recordsEqual(entry.env, candidate.env) &&
219
+ arraysEqual(entry.agents, candidate.agents);
220
+ const validateAdoption = (fs, adoption) => Effect.gen(function* () {
221
+ const config = yield* readJsonObject(fs, adoption.filePath);
222
+ const servers = Option.isSome(config) ? config.value[adoption.serversKey] : undefined;
223
+ const entry = isRecord(servers) ? servers[adoption.name] : undefined;
224
+ if (!isRecord(entry) || !isAxmManagedMcpEntry(entry)) {
225
+ return yield* new WorkspaceConfigurationFailed({
226
+ category: "validation",
227
+ detail: `Failed to validate adopted MCP server ${adoption.name}`,
228
+ });
229
+ }
230
+ });
231
+ /**
232
+ * Adopt the losslessly importable candidates as inline settings entries and
233
+ * mark their native entries as AXM-managed, in one validated workspace
234
+ * transaction.
235
+ */
236
+ export const applyMcpImport = (candidates, ws, fs, hooks = {}) => {
237
+ const adoptions = candidates.flatMap((candidate) => candidate.adoptions);
238
+ const settingsEntry = (candidate) => ({
239
+ kind: "inline",
240
+ ...(candidate.definition.type === "stdio"
241
+ ? { command: candidate.definition.command, args: candidate.definition.args }
242
+ : { url: candidate.definition.url, headers: candidate.definition.headers }),
243
+ env: candidate.env,
244
+ enabled: true,
245
+ ...(candidate.agents === undefined ? {} : { agents: candidate.agents }),
246
+ });
247
+ return ws.runTransaction({
248
+ targets: Array.from(new Set(adoptions.map((adoption) => adoption.filePath))).sort(),
249
+ transition: Effect.gen(function* () {
250
+ for (const candidate of candidates) {
251
+ yield* ws.setMcpServerEntry(candidate.name, settingsEntry(candidate));
252
+ }
253
+ for (const adoption of adoptions) {
254
+ if (hooks.beforeAdoptionWrite !== undefined) {
255
+ yield* hooks.beforeAdoptionWrite(adoption);
256
+ }
257
+ yield* writeAdoptedMcpConfig(fs, adoption);
258
+ }
259
+ }),
260
+ validate: () => Effect.gen(function* () {
261
+ const configured = yield* ws.getConfiguredMcpServerEntries();
262
+ for (const candidate of candidates) {
263
+ if (!candidateMatchesSettings(candidate, configured[candidate.name])) {
264
+ return yield* new WorkspaceConfigurationFailed({
265
+ category: "validation",
266
+ detail: `Failed to validate imported MCP server ${candidate.name}`,
267
+ });
268
+ }
269
+ }
270
+ yield* Effect.forEach(adoptions, (adoption) => validateAdoption(fs, adoption), {
271
+ concurrency: 1,
272
+ });
273
+ }),
274
+ });
275
+ };
276
+ //# sourceMappingURL=mcp-import.js.map
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Configured-agent membership policy: agent-id validation for membership
3
+ * changes and the atomic application of membership and materialization steps
4
+ * through one workspace transaction. The application supplies the failure
5
+ * conversion so step categories and details stay byte-identical with its
6
+ * rendered errors.
7
+ *
8
+ * @experimental This API is unstable and may change without notice.
9
+ */
10
+ import * as Effect from "effect/Effect";
11
+ import { StepFailure, type JobStepResult, type PlannedJobStep } from "@agentxm/workspace-operations";
12
+ import type { WorkspaceMutationsService } from "@agentxm/workspace-state";
13
+ import { WorkspaceConfigurationFailed } from "./errors.js";
14
+ export declare const dedupe: (values: ReadonlyArray<string>) => ReadonlyArray<string>;
15
+ export declare const validateAgentIds: (ids: ReadonlyArray<string>) => Effect.Effect<ReadonlyArray<string>, WorkspaceConfigurationFailed>;
16
+ interface AtomicMembershipStepsArgs<Requirements, Output, ValidateError> {
17
+ readonly ws: WorkspaceMutationsService;
18
+ readonly steps: ReadonlyArray<PlannedJobStep<Requirements, Output>>;
19
+ readonly validate: (results: ReadonlyArray<JobStepResult<Output>>) => Effect.Effect<void, ValidateError, Requirements>;
20
+ /** Application-owned conversion into the plan-step failure vocabulary. */
21
+ readonly toStepFailure: (failure: unknown) => StepFailure;
22
+ }
23
+ /**
24
+ * Keep plan-level preview and per-step results while applying every membership
25
+ * and artifact step through one workspace transaction.
26
+ */
27
+ export declare const makeAtomicMembershipSteps: <Requirements, Output, ValidateError>(args: AtomicMembershipStepsArgs<Requirements, Output, ValidateError>) => Effect.Effect<readonly PlannedJobStep<Requirements, Output>[], never, never>;
28
+ export {};
29
+ //# sourceMappingURL=membership.d.ts.map
@@ -0,0 +1,162 @@
1
+ /**
2
+ * Configured-agent membership policy: agent-id validation for membership
3
+ * changes and the atomic application of membership and materialization steps
4
+ * through one workspace transaction. The application supplies the failure
5
+ * conversion so step categories and details stay byte-identical with its
6
+ * rendered errors.
7
+ *
8
+ * @experimental This API is unstable and may change without notice.
9
+ */
10
+ import * as Effect from "effect/Effect";
11
+ import * as Ref from "effect/Ref";
12
+ import { HOSTED_AGENTS_BY_ID, HOSTED_AGENT_IDS, } from "@agentxm/extension-model/unstable/agent-capabilities";
13
+ import { CONFIGURABLE_AGENT_IDS } from "@agentxm/extension-model/unstable/agents/types";
14
+ import { StepFailure, } from "@agentxm/workspace-operations";
15
+ import { WorkspaceConfigurationFailed } from "./errors.js";
16
+ const configurableAgentIds = new Set(CONFIGURABLE_AGENT_IDS);
17
+ const hostedAgentIds = new Set(HOSTED_AGENT_IDS);
18
+ const isHostedAgentId = (id) => hostedAgentIds.has(id);
19
+ const numberAt = (values, index) => values[index] ?? 0;
20
+ const editDistance = (left, right) => {
21
+ const previous = Array.from({ length: right.length + 1 }, (_value, index) => index);
22
+ for (let leftIndex = 0; leftIndex < left.length; leftIndex += 1) {
23
+ const current = [leftIndex + 1];
24
+ for (let rightIndex = 0; rightIndex < right.length; rightIndex += 1) {
25
+ const substitutionCost = left[leftIndex] === right[rightIndex] ? 0 : 1;
26
+ current[rightIndex + 1] = Math.min(numberAt(current, rightIndex) + 1, numberAt(previous, rightIndex + 1) + 1, numberAt(previous, rightIndex) + substitutionCost);
27
+ }
28
+ previous.splice(0, previous.length, ...current);
29
+ }
30
+ return previous[right.length] ?? left.length;
31
+ };
32
+ const nearestAgentId = (input) => {
33
+ let best;
34
+ for (const id of CONFIGURABLE_AGENT_IDS) {
35
+ const distance = editDistance(input, id);
36
+ if (best === undefined || distance < best.distance) {
37
+ best = { id, distance };
38
+ }
39
+ }
40
+ if (best === undefined)
41
+ return undefined;
42
+ return best.distance <= Math.max(3, Math.floor(input.length / 2)) ? best.id : undefined;
43
+ };
44
+ export const dedupe = (values) => Array.from(new Set(values));
45
+ export const validateAgentIds = (ids) => Effect.gen(function* () {
46
+ for (const id of ids) {
47
+ if (id === "universal") {
48
+ return yield* new WorkspaceConfigurationFailed({
49
+ category: "validation",
50
+ detail: "`universal` is always materialized automatically and cannot be added or removed.",
51
+ suggestions: [{ description: "Choose one of the configurable coding-agent IDs." }],
52
+ });
53
+ }
54
+ if (isHostedAgentId(id)) {
55
+ const agent = HOSTED_AGENTS_BY_ID[id];
56
+ return yield* new WorkspaceConfigurationFailed({
57
+ category: "validation",
58
+ detail: `${agent.name} is a hosted agent and cannot be added to local workspace configuration. ${agent.installTarget.instructions}`,
59
+ suggestions: [
60
+ {
61
+ description: `Open the ${agent.name} skill installation guide.`,
62
+ url: agent.installTarget.docs,
63
+ },
64
+ ],
65
+ });
66
+ }
67
+ if (!configurableAgentIds.has(id)) {
68
+ const nearest = nearestAgentId(id);
69
+ return yield* new WorkspaceConfigurationFailed({
70
+ category: "validation",
71
+ detail: `Unknown agent ID: ${id}`,
72
+ suggestions: [
73
+ nearest === undefined
74
+ ? { description: "Inspect supported agent IDs.", cmd: "axm agents list --available" }
75
+ : {
76
+ description: `Did you mean "${nearest}"?`,
77
+ cmd: `axm agents add ${nearest}`,
78
+ },
79
+ ],
80
+ });
81
+ }
82
+ }
83
+ return dedupe(ids);
84
+ });
85
+ const failedResult = (error, message = error.detail) => ({
86
+ result: "error",
87
+ message,
88
+ error,
89
+ });
90
+ const blockedResult = (message) => failedResult(new StepFailure({
91
+ category: "conflict",
92
+ detail: message,
93
+ }), message);
94
+ const rollbackResults = (executable, attempt, transactionError) => {
95
+ const actualFailureIndex = attempt.failedIndex ?? Math.max(0, attempt.results.length - 1);
96
+ const failedLabel = executable[actualFailureIndex]?.label ?? "atomic agent membership validation";
97
+ return executable.map((_, index) => {
98
+ if (index < actualFailureIndex) {
99
+ return blockedResult(`blocked: rolled back after ${failedLabel} failed`);
100
+ }
101
+ if (index > actualFailureIndex) {
102
+ return blockedResult(`blocked by ${failedLabel} failure`);
103
+ }
104
+ const attempted = attempt.results[index];
105
+ return attempted?.result === "error"
106
+ ? attempted
107
+ : failedResult(transactionError, transactionError.detail);
108
+ });
109
+ };
110
+ /**
111
+ * Keep plan-level preview and per-step results while applying every membership
112
+ * and artifact step through one workspace transaction.
113
+ */
114
+ export const makeAtomicMembershipSteps = Effect.fn("Agents.makeAtomicMembershipSteps")(function* (args) {
115
+ if (args.steps.some((step) => step.readiness === "error"))
116
+ return args.steps;
117
+ const executable = args.steps.filter((step) => step.readiness !== "error");
118
+ const attemptRef = yield* Ref.make({ results: [] });
119
+ const transition = args.ws
120
+ .runTransaction({
121
+ transition: Effect.gen(function* () {
122
+ const results = [];
123
+ for (const [index, step] of executable.entries()) {
124
+ const result = yield* step.run.pipe(Effect.catch((error) => Effect.succeed(failedResult(error))));
125
+ results.push(result);
126
+ yield* Ref.set(attemptRef, {
127
+ results: [...results],
128
+ ...(result.result === "error" ? { failedIndex: index } : {}),
129
+ });
130
+ if (result.result === "error") {
131
+ return yield* result.error;
132
+ }
133
+ }
134
+ return results;
135
+ }),
136
+ validate: (results) => args.validate(results).pipe(Effect.mapError(args.toStepFailure)),
137
+ })
138
+ .pipe(Effect.catch((transactionError) => Ref.get(attemptRef).pipe(Effect.map((attempt) => rollbackResults(executable, attempt, transactionError._tag === "StepFailure"
139
+ ? transactionError
140
+ : args.toStepFailure(transactionError))))));
141
+ const sharedTransition = yield* Effect.cached(transition);
142
+ let resultIndex = 0;
143
+ return args.steps.map((step) => {
144
+ if (step.readiness === "error")
145
+ return step;
146
+ const index = resultIndex;
147
+ resultIndex += 1;
148
+ return {
149
+ ...step,
150
+ run: sharedTransition.pipe(Effect.flatMap((results) => {
151
+ const result = results[index];
152
+ return result === undefined
153
+ ? new StepFailure({
154
+ category: "internal",
155
+ detail: `Atomic agent membership transition omitted step ${index + 1}`,
156
+ })
157
+ : Effect.succeed(result);
158
+ })),
159
+ };
160
+ });
161
+ });
162
+ //# sourceMappingURL=membership.js.map
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Deterministic in-memory Layer implementations of the workspace-configuration
3
+ * services. Tests and specifications may import this module; production
4
+ * source composes the application's interaction implementation instead.
5
+ *
6
+ * @experimental This API is unstable and may change without notice.
7
+ */
8
+ export { WorkspaceInitializationInteractionTest, type WorkspaceInitializationInteractionTestState, } from "./initialization-interaction.js";
9
+ //# sourceMappingURL=testing.d.ts.map
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Deterministic in-memory Layer implementations of the workspace-configuration
3
+ * services. Tests and specifications may import this module; production
4
+ * source composes the application's interaction implementation instead.
5
+ *
6
+ * @experimental This API is unstable and may change without notice.
7
+ */
8
+ export { WorkspaceInitializationInteractionTest, } from "./initialization-interaction.js";
9
+ //# sourceMappingURL=testing.js.map
package/package.json ADDED
@@ -0,0 +1,58 @@
1
+ {
2
+ "name": "@agentxm/workspace-configuration",
3
+ "version": "0.28.4-bootstrap.0",
4
+ "description": "AXM workspace-configuration feature: setup, configured-agent membership, instruction management, and inline workspace capabilities for the axm CLI. Unstable and unsupported — use the axm.sh CLI.",
5
+ "type": "module",
6
+ "license": "FSL-1.1-MIT",
7
+ "homepage": "https://axm.sh",
8
+ "bugs": {
9
+ "url": "https://github.com/agentxm/axm/issues"
10
+ },
11
+ "author": "AgentXM <hello@agentxm.ai> (https://agentxm.ai)",
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "https://github.com/agentxm/axm.git",
15
+ "directory": "packages/workspace-configuration"
16
+ },
17
+ "sideEffects": false,
18
+ "exports": {
19
+ ".": {
20
+ "types": "./dist/src/index.d.ts",
21
+ "default": "./dist/src/index.js"
22
+ },
23
+ "./testing": {
24
+ "types": "./dist/src/testing.d.ts",
25
+ "default": "./dist/src/testing.js"
26
+ }
27
+ },
28
+ "files": [
29
+ "dist/src/",
30
+ "!**/*.map"
31
+ ],
32
+ "publishConfig": {
33
+ "access": "public"
34
+ },
35
+ "engines": {
36
+ "node": ">=22.19.0"
37
+ },
38
+ "nx": {
39
+ "includedScripts": []
40
+ },
41
+ "dependencies": {
42
+ "effect": "4.0.0-rc.112",
43
+ "@agentxm/extension-model": "^0.28.4-bootstrap.0",
44
+ "@agentxm/extension-sources": "^0.28.4-bootstrap.0",
45
+ "@agentxm/workspace-state": "^0.28.4-bootstrap.0",
46
+ "@agentxm/extension-workspace": "^0.28.4-bootstrap.0",
47
+ "@agentxm/agent-integration": "^0.28.4-bootstrap.0",
48
+ "@agentxm/workspace-operations": "^0.28.4-bootstrap.0"
49
+ },
50
+ "devDependencies": {
51
+ "@effect/platform-node": "4.0.0-rc.112",
52
+ "@effect/vitest": "4.0.0-rc.112",
53
+ "@types/bun": "^1.3.14",
54
+ "@typescript/native": "npm:typescript@^7.0.2",
55
+ "typescript": "npm:@typescript/typescript6@^6.0.2",
56
+ "vitest": "^4.1.10"
57
+ }
58
+ }