diff --git a/apps/server/src/harness/atomic/adapter.ts b/apps/server/src/harness/atomic/adapter.ts index 65912288d..8845644c6 100644 --- a/apps/server/src/harness/atomic/adapter.ts +++ b/apps/server/src/harness/atomic/adapter.ts @@ -156,9 +156,9 @@ export type AtomicSettings = { extensions?: readonly string[]; /** * The SQLite file the workflows of Planner sessions are journaled in, in place - * of Atomic's default beside its agent directory. Atomic reads the journal - * location only from `ATOMIC_WORKFLOW_JOURNAL_PATH` and the on-disk settings, - * so the adapter sets that variable until it shuts down. + * of Atomic's default beside its agent directory. Each Planner session is given + * it through `createAgentSession({ journal })`, which takes precedence over + * `ATOMIC_WORKFLOW_JOURNAL_PATH`; the process environment is left unchanged. */ journal?: string; }; @@ -472,8 +472,6 @@ export function createAtomicAdapter( } let runtime: Promise | undefined; let open = new Set(); - let journal = process.env.ATOMIC_WORKFLOW_JOURNAL_PATH; - if (settings.journal) process.env.ATOMIC_WORKFLOW_JOURNAL_PATH = settings.journal; return { specificationVersion: "harness-v1", @@ -660,6 +658,7 @@ export function createAtomicAdapter( builtins: full ? undefined : BUILTINS_OFF, tools: full ? undefined : [...hostNames, ATOMIC_RESULT_TOOL_NAME], extensionBindings: full ? { humanInput: full.humanInput } : undefined, + journal: full && settings.journal ? { path: settings.journal } : undefined, customTools: turn.tools.map(hostTool), }); let session = created.session; @@ -838,9 +837,6 @@ export function createAtomicAdapter( async shutdown() { await Promise.allSettled([...open].map(session => session.doDestroy())); - if (!settings.journal) return; - if (journal === undefined) delete process.env.ATOMIC_WORKFLOW_JOURNAL_PATH; - else process.env.ATOMIC_WORKFLOW_JOURNAL_PATH = journal; }, }; } diff --git a/apps/server/src/harness/atomic/full.test.ts b/apps/server/src/harness/atomic/full.test.ts index 12765c1bc..4b9fe904a 100644 --- a/apps/server/src/harness/atomic/full.test.ts +++ b/apps/server/src/harness/atomic/full.test.ts @@ -320,6 +320,7 @@ export default workflow({ operator: await Bun.file(join(agentDir, "settings.json")).exists(), journal: await Bun.file(journal).exists(), defaultJournal: await Bun.file(join(root, "workflows", "journal.sqlite")).exists(), + environment: process.env.ATOMIC_WORKFLOW_JOURNAL_PATH, project: projectPackage ? await Bun.file(join(cwd, ".atomic", "settings.json")).text() : undefined, @@ -531,8 +532,10 @@ test( expect(stage.toolNames).toContain("write"); expect(stage.toolNames).not.toContain(ATOMIC_RESULT_TOOL_NAME); expect(stage.system).not.toContain("CHOPIN-INSTRUCTIONS-MARKER"); - // The run is journaled where Chopin chose, not in Atomic's default beside its agent directory. + // The run is journaled where Chopin chose, not in Atomic's default beside its agent directory, + // and through the session rather than the process-wide environment variable. expect(result.files).toMatchObject({ journal: true, defaultJournal: false }); + expect(result.files?.environment).toBeUndefined(); } finally { if (environment !== undefined) process.env.NODE_ENV = environment; await room.close(); diff --git a/apps/server/src/harness/atomic/workspace.ts b/apps/server/src/harness/atomic/workspace.ts index 79e49e141..9a8cc6a96 100644 --- a/apps/server/src/harness/atomic/workspace.ts +++ b/apps/server/src/harness/atomic/workspace.ts @@ -53,10 +53,12 @@ export function stateDirectory(): string { /** * Where Planner sessions journal their workflow runs. A run resumes only with * the journal and working directory it started with, so the journal lives - * beside the channel directories rather than in Atomic's own home. + * beside the channel directories rather than in Atomic's own home. An + * operator's non-empty `ATOMIC_WORKFLOW_JOURNAL_PATH` still names the file. */ export function workflowJournal(): string { - return join(stateDirectory(), "atomic", "workflows.sqlite"); + return process.env.ATOMIC_WORKFLOW_JOURNAL_PATH + || join(stateDirectory(), "atomic", "workflows.sqlite"); } /** diff --git a/apps/server/src/harness/harnesses.test.ts b/apps/server/src/harness/harnesses.test.ts index 297fc1a4d..b6bca700e 100644 --- a/apps/server/src/harness/harnesses.test.ts +++ b/apps/server/src/harness/harnesses.test.ts @@ -2,6 +2,7 @@ import { afterEach, expect, it } from "bun:test"; import { tmpdir } from "node:os"; import { join } from "node:path"; +import { workflowJournal } from "./atomic/workspace"; import { harnesses, harnessFor, registerCredential, shutdownHarnesses } from "./harnesses"; afterEach(shutdownHarnesses); @@ -60,20 +61,22 @@ it("journals Atomic workflow runs in Chopin's state directory unless the operato let state = join(tmpdir(), `chopin-state-${crypto.randomUUID()}`); let restore = (name: string, value: string | undefined) => value === undefined ? delete process.env[name] : process.env[name] = value; - let journal = (): string | undefined => process.env.ATOMIC_WORKFLOW_JOURNAL_PATH; try { process.env.XDG_STATE_HOME = state; delete process.env.ATOMIC_WORKFLOW_JOURNAL_PATH; - harnessFor({ harness: "atomic", harnessAuth: "ai-gateway", host: "0.0.0.0" }); if (process.platform === "linux") { - expect(journal()).toBe(join(state, "chopin", "atomic", "workflows.sqlite")); + expect(workflowJournal()).toBe(join(state, "chopin", "atomic", "workflows.sqlite")); } + // Each Planner session is given its journal; the process environment is left alone. + harnessFor({ harness: "atomic", harnessAuth: "ai-gateway", host: "0.0.0.0" }); + expect(process.env.ATOMIC_WORKFLOW_JOURNAL_PATH).toBeUndefined(); await shutdownHarnesses(); - expect(journal()).toBeUndefined(); process.env.ATOMIC_WORKFLOW_JOURNAL_PATH = "/srv/atomic/runs.sqlite"; + expect(workflowJournal()).toBe("/srv/atomic/runs.sqlite"); harnessFor({ harness: "atomic", harnessAuth: "ai-gateway", host: "0.0.0.0" }); - expect(journal()).toBe("/srv/atomic/runs.sqlite"); + await shutdownHarnesses(); + expect(process.env.ATOMIC_WORKFLOW_JOURNAL_PATH).toBe("/srv/atomic/runs.sqlite"); } finally { await shutdownHarnesses(); restore("XDG_STATE_HOME", previous.state); diff --git a/apps/server/src/harness/harnesses.ts b/apps/server/src/harness/harnesses.ts index 9aede6d57..67dc5149e 100644 --- a/apps/server/src/harness/harnesses.ts +++ b/apps/server/src/harness/harnesses.ts @@ -22,7 +22,7 @@ function createAtomicHarness( return createAtomicAdapter({ auth: settings.auth as AtomicAuthMode, extensions: settings.extensions, - journal: process.env.ATOMIC_WORKFLOW_JOURNAL_PATH || workflowJournal(), + journal: workflowJournal(), }); } diff --git a/bun.lock b/bun.lock index 5d0914a8f..c3f2b17a5 100644 --- a/bun.lock +++ b/bun.lock @@ -309,7 +309,7 @@ "@ai-sdk/harness": "1.0.146", "@ai-sdk/mcp": "2.0.72", "@ai-sdk/sandbox-just-bash": "1.0.146", - "@bastani/atomic": "0.9.38-alpha.9", + "@bastani/atomic": "0.9.39", "ai": "7.0.135", "typebox": "1.3.7", "zod": "4.4.3", @@ -450,27 +450,27 @@ "@base-ui/utils": ["@base-ui/utils@0.3.2", "", { "dependencies": { "@babel/runtime": "^7.29.2", "@floating-ui/utils": "^0.2.12", "reselect": "^5.2.0", "use-sync-external-store": "^1.6.0" }, "peerDependencies": { "@types/react": "^17 || ^18 || ^19", "react": "^17 || ^18 || ^19", "react-dom": "^17 || ^18 || ^19" }, "optionalPeers": ["@types/react"] }, "sha512-oWy1aq/I2GmYjpl4PhEAhzflF8VPGKgZeq0xAWTbfD5KBWyxcN0ZP2+WHSUm/5Z6lVMBDLReLcoXwSYoRc/zNQ=="], - "@bastani/atomic": ["@bastani/atomic@0.9.38-alpha.9", "", { "dependencies": { "@bastani/atomic-natives": "0.9.38-alpha.9", "@bastani/pi-ai": "0.9.38-alpha.9", "@earendil-works/pi-agent-core": "1.3.0", "@earendil-works/pi-codemode": "1.3.0", "@earendil-works/pi-tui": "1.3.0", "@modelcontextprotocol/ext-apps": "^1.7.5", "@modelcontextprotocol/sdk": "1.32.1", "@mozilla/readability": "^0.6.0", "@silvia-odwyer/photon-node": "0.3.4", "@typesafe-ai/sdk": "0.6.0", "brace-expansion": "5.0.12", "chalk": "6.0.0", "cross-spawn": "7.0.6", "diff": "8.0.4", "glob": "13.0.6", "grok-mermaid": "0.2.3", "highlight.js": "10.7.3", "hosted-git-info": "9.0.3", "ignore": "7.0.8", "jiti": "2.7.0", "linkedom": "^0.18.13", "lru-cache": "11.5.3", "markit-ai": "0.5.3", "minimatch": "10.2.6", "open": "^11.0.1", "p-limit": "^7.3.2", "proper-lockfile": "4.1.2", "quickjs-wasi": "3.6.2", "semver": "7.8.5", "turndown": "^7.2.0", "typebox": "1.3.27", "undici": "8.10.2", "unpdf": "1.8.1", "yaml": "2.9.0", "zod": "^3.25.0 || ^4.0.0" }, "optionalDependencies": { "@mariozechner/clipboard": "0.3.9" }, "bin": { "atomic": "dist/cli.js" } }, "sha512-6i8lc2intSDy5kP3/lqCcUDD8HlA+AUiayhMp8Qzmfs5IiYZB2GrbddmzAlHJ9Qr8rPTaanIC8y6Qnv8ID07sQ=="], + "@bastani/atomic": ["@bastani/atomic@0.9.39", "", { "dependencies": { "@bastani/atomic-natives": "0.9.39", "@bastani/pi-ai": "0.9.39", "@earendil-works/pi-agent-core": "1.3.0", "@earendil-works/pi-codemode": "1.3.0", "@earendil-works/pi-tui": "1.3.0", "@modelcontextprotocol/ext-apps": "^1.7.5", "@modelcontextprotocol/sdk": "1.32.1", "@mozilla/readability": "^0.6.0", "@silvia-odwyer/photon-node": "0.3.4", "@typesafe-ai/sdk": "0.6.0", "brace-expansion": "5.0.12", "chalk": "6.0.0", "cross-spawn": "7.0.6", "diff": "8.0.4", "glob": "13.0.6", "grok-mermaid": "0.2.3", "highlight.js": "10.7.3", "hosted-git-info": "9.0.3", "ignore": "7.0.8", "jiti": "2.7.0", "linkedom": "^0.18.13", "lru-cache": "11.5.3", "markit-ai": "0.5.3", "minimatch": "10.2.6", "open": "^11.0.1", "p-limit": "^7.3.2", "proper-lockfile": "4.1.2", "quickjs-wasi": "3.6.2", "semver": "7.8.5", "turndown": "^7.2.0", "typebox": "1.3.27", "undici": "8.10.2", "unpdf": "1.8.1", "yaml": "2.9.0", "zod": "^3.25.0 || ^4.0.0" }, "optionalDependencies": { "@mariozechner/clipboard": "0.3.9" }, "bin": { "atomic": "dist/cli.js" } }, "sha512-H3y7cfb0aTrhGc9b3UrK0QEN+i7OEYbapbN5EmNDc75CUj7ATi+Ri+E+AzpGck+6eup5Ty7elCy/0sNXKrYbSg=="], - "@bastani/atomic-natives": ["@bastani/atomic-natives@0.9.38-alpha.9", "", { "optionalDependencies": { "@bastani/atomic-natives-darwin-arm64": "0.9.38-alpha.9", "@bastani/atomic-natives-darwin-x64": "0.9.38-alpha.9", "@bastani/atomic-natives-linux-arm64-gnu": "0.9.38-alpha.9", "@bastani/atomic-natives-linux-arm64-musl": "0.9.38-alpha.9", "@bastani/atomic-natives-linux-x64-gnu": "0.9.38-alpha.9", "@bastani/atomic-natives-linux-x64-musl": "0.9.38-alpha.9", "@bastani/atomic-natives-win32-arm64-msvc": "0.9.38-alpha.9", "@bastani/atomic-natives-win32-x64-msvc": "0.9.38-alpha.9" } }, "sha512-yN7eLytoNYRKmxm2HABjV1qMk70ey+NmQP7gEo5kmEwecOJIkPdLsnbRJs3l5KBIC9DUrP7/J/cFWJ9PIxr4Fg=="], + "@bastani/atomic-natives": ["@bastani/atomic-natives@0.9.39", "", { "optionalDependencies": { "@bastani/atomic-natives-darwin-arm64": "0.9.39", "@bastani/atomic-natives-darwin-x64": "0.9.39", "@bastani/atomic-natives-linux-arm64-gnu": "0.9.39", "@bastani/atomic-natives-linux-arm64-musl": "0.9.39", "@bastani/atomic-natives-linux-x64-gnu": "0.9.39", "@bastani/atomic-natives-linux-x64-musl": "0.9.39", "@bastani/atomic-natives-win32-arm64-msvc": "0.9.39", "@bastani/atomic-natives-win32-x64-msvc": "0.9.39" } }, "sha512-40juvjggMlCQHSsCHqKUnoa3jhTCrUWMJwjhdvKczpg82fQwwWcoMHHsUXEgh+TXh3gbWyYElpX/dZeQcfw4Vw=="], - "@bastani/atomic-natives-darwin-arm64": ["@bastani/atomic-natives-darwin-arm64@0.9.38-alpha.9", "", { "os": "darwin", "cpu": "arm64" }, "sha512-r3YA8iXAGp+QsxQeTPJHFFCFMqL34lHjg5bXGWWeVN8qQdxt54yvk1QPfe1DplhnIXhYrDNbNtXTmzQAo1ShvQ=="], + "@bastani/atomic-natives-darwin-arm64": ["@bastani/atomic-natives-darwin-arm64@0.9.39", "", { "os": "darwin", "cpu": "arm64" }, "sha512-M59UUF0SEmET2etrKoSJMcbq/lWsLP3wolSqhLGA56oVQFD6XqiF6gx1VOn0KEsk0cD4psB3MUvW70DCYlRNjw=="], - "@bastani/atomic-natives-darwin-x64": ["@bastani/atomic-natives-darwin-x64@0.9.38-alpha.9", "", { "os": "darwin", "cpu": "x64" }, "sha512-cBQByncHUKUaJUNTHIutK8J5Z2MX9jAw/EFIy6NldxdbIc4WStjXqwRoAbiRArzGRpLO7uCqPCmi3iKatXF2fQ=="], + "@bastani/atomic-natives-darwin-x64": ["@bastani/atomic-natives-darwin-x64@0.9.39", "", { "os": "darwin", "cpu": "x64" }, "sha512-rYWzmUqii1vL1eyOgQf6mvIDHGe25/XCszMqrnyqTuBsABKUp2SAZbfZ6CqbbVTIbOAwJUm82X1o2FKC+CMqow=="], - "@bastani/atomic-natives-linux-arm64-gnu": ["@bastani/atomic-natives-linux-arm64-gnu@0.9.38-alpha.9", "", { "os": "linux", "cpu": "arm64" }, "sha512-yOvXM7qSSIb8YX0eDDyXxvHV0CbiPFh/KE/CPBkpfyVE+538tLQpQb3pQHTNcDFbhNl4evZivHSoNk4QYhrujQ=="], + "@bastani/atomic-natives-linux-arm64-gnu": ["@bastani/atomic-natives-linux-arm64-gnu@0.9.39", "", { "os": "linux", "cpu": "arm64" }, "sha512-6HSW145Dzhe19zJRSeogISuCUi9cVqRnEItWPd2YbOOT/bFu5BS3VU81OtfGCFnkLyyZB6ovmfhaO4woOBNuLw=="], - "@bastani/atomic-natives-linux-arm64-musl": ["@bastani/atomic-natives-linux-arm64-musl@0.9.38-alpha.9", "", { "os": "linux", "cpu": "arm64" }, "sha512-VQyhpKNLwxHMY1xUQFetLTRi1BayqQ7C4sVpYpwB38rNltInufLhjbbK6XwIVYq1OrkIFccIqwbKuFUGHKGzFw=="], + "@bastani/atomic-natives-linux-arm64-musl": ["@bastani/atomic-natives-linux-arm64-musl@0.9.39", "", { "os": "linux", "cpu": "arm64" }, "sha512-FxXJS8QWO2giy8YGxUYopUW7Q9OeyHFGryASpOLouirasaOwpaNOeMXsZCN3rXd3b6dkfJrcVRYw+pCih8w0Mw=="], - "@bastani/atomic-natives-linux-x64-gnu": ["@bastani/atomic-natives-linux-x64-gnu@0.9.38-alpha.9", "", { "os": "linux", "cpu": "x64" }, "sha512-4JGo4I8stKgAXWudGKwJNMgzeO6ZJBjTHW5odcNWKnn2rQIpjhZGBSCq5sJpEcYOJD1JGUJ+iZs2KvyIPKXARg=="], + "@bastani/atomic-natives-linux-x64-gnu": ["@bastani/atomic-natives-linux-x64-gnu@0.9.39", "", { "os": "linux", "cpu": "x64" }, "sha512-9gvfFYDxWgZRWDf+bo9IXGZimGnEaQ/bJ5gCB6KTJRxc2OF4tpUYUrQlt+JRR3HaziHVbGneI0E74mZziPZRCQ=="], - "@bastani/atomic-natives-linux-x64-musl": ["@bastani/atomic-natives-linux-x64-musl@0.9.38-alpha.9", "", { "os": "linux", "cpu": "x64" }, "sha512-jtetHCxqz4GaBz/+3R4PWlX178snWWI/7PwxzEqUpONbJBMCA/JolYSXa9YZLm9VA8kRndW2DxIMVr0cRxy+Lg=="], + "@bastani/atomic-natives-linux-x64-musl": ["@bastani/atomic-natives-linux-x64-musl@0.9.39", "", { "os": "linux", "cpu": "x64" }, "sha512-yIrNUcZVi1UAm4nEK+u+CTskYatU3FIMJg262Ebiu+vyCi+YgqkfMjIZ/pOc0+80i0JotNCFSl94kaw6Qy2d0w=="], - "@bastani/atomic-natives-win32-arm64-msvc": ["@bastani/atomic-natives-win32-arm64-msvc@0.9.38-alpha.9", "", { "os": "win32", "cpu": "arm64" }, "sha512-CTMtzHbHtdRwJ0dnHyy/ZK9m+89LYGIPUBp2Ee4ydREgtc4tlLsWW3iFpx20/tTOisQPieuTz7uZ0ze4CIoKZw=="], + "@bastani/atomic-natives-win32-arm64-msvc": ["@bastani/atomic-natives-win32-arm64-msvc@0.9.39", "", { "os": "win32", "cpu": "arm64" }, "sha512-JtZ34FBgEBOvDZzG6NP5GVJxpTMTwVjTTi4aa0TcCFGQy+s1igqVapoin/646KUWJQfZhCxBWMZqTgf1r7kpPw=="], - "@bastani/atomic-natives-win32-x64-msvc": ["@bastani/atomic-natives-win32-x64-msvc@0.9.38-alpha.9", "", { "os": "win32", "cpu": "x64" }, "sha512-AbTr4sCS3BdxUMWoiWq8XFKvPH2AtHf222sr+vZMsTQ9wB/eILdv/wPy38+gB66HOWf5OTUyCPvDNrGeVtV4oQ=="], + "@bastani/atomic-natives-win32-x64-msvc": ["@bastani/atomic-natives-win32-x64-msvc@0.9.39", "", { "os": "win32", "cpu": "x64" }, "sha512-X8jczUdnorN5lsFyMwoQCI/u/LvzxcsdQge9r//AwDQdW56QaNqhimuP1yv5OMPsWdf5Q8RD5iGctchg0PzXTQ=="], - "@bastani/pi-ai": ["@bastani/pi-ai@0.9.38-alpha.9", "", { "dependencies": { "@anthropic-ai/sdk": "0.129.0", "@aws-sdk/client-bedrock-runtime": "3.1127.0", "@earendil-works/pi-telemetry": "1.3.0", "@google/genai": "2.21.0", "@smithy/node-http-handler": "4.12.1", "http-proxy-agent": "9.1.0", "https-proxy-agent": "9.1.0", "openai": "7.19.0", "partial-json": "0.1.7", "typebox": "1.3.27" }, "bin": { "pi-ai": "dist/cli.js" } }, "sha512-pVnnc3AlV3PXNZrQCPrmIPgrIrHvFT/CQnA/ok3HUM74/BugfYGRPMZSj1XQQYb5ZZDTGnBZygoLQ3+DPsOkSA=="], + "@bastani/pi-ai": ["@bastani/pi-ai@0.9.39", "", { "dependencies": { "@anthropic-ai/sdk": "0.129.0", "@aws-sdk/client-bedrock-runtime": "3.1127.0", "@earendil-works/pi-telemetry": "1.3.0", "@google/genai": "2.21.0", "@smithy/node-http-handler": "4.12.1", "http-proxy-agent": "9.1.0", "https-proxy-agent": "9.1.0", "openai": "7.19.0", "partial-json": "0.1.7", "typebox": "1.3.27" }, "bin": { "pi-ai": "dist/cli.js" } }, "sha512-NY4dYidvG3OP6L16/1xkjtYaGeWK0/FVm7WPW3BHVOHpVCfCKTeAB57vEuGduG3e7g/XLIUgz4NjQYqatjPE1A=="], "@borewit/text-codec": ["@borewit/text-codec@0.2.2", "", {}, "sha512-DDaRehssg1aNrH4+2hnj1B7vnUGEjU6OIlyRdkMd0aUdIUvKXrJfXsy8LVtXAy7DRvYVluWbMspsRhz2lcW0mQ=="], diff --git a/docs/hosted-agent.md b/docs/hosted-agent.md index ac5d23f88..d30206579 100644 --- a/docs/hosted-agent.md +++ b/docs/hosted-agent.md @@ -612,7 +612,9 @@ workflow journal, so runs interrupted that way can be resumed from a later session. Chopin keeps that journal at `$XDG_STATE_HOME/chopin/atomic/workflows.sqlite` under the same per-user state directory as the documents' own directories, because a run resumes only with -the journal and working directory it started in. A non-empty +the journal and working directory it started in. Each Planner session is +given that file through Atomic's `createAgentSession({ journal })`, so the +server's process environment is left unchanged. An operator's non-empty `ATOMIC_WORKFLOW_JOURNAL_PATH` names another file instead; Atomic's `workflows.journal` setting is not used for Planner sessions. A Build is not a workflow run and has no card in Chat: its tasks, their state and pull requests show under **Build**, whichever machine runs it. The diff --git a/docs/self-hosting.md b/docs/self-hosting.md index 0d7eabe3e..4a7a22365 100644 --- a/docs/self-hosting.md +++ b/docs/self-hosting.md @@ -75,7 +75,7 @@ each owner's GitHub App user token and does not consume `HARNESS_AUTH`; setting `auto` on loopback passes the startup guard but does not enable host login in that adapter. -`HARNESS=atomic` runs Atomic 0.9.38-alpha.9 in the Chopin server process through its +`HARNESS=atomic` runs Atomic 0.9.39 in the Chopin server process through its headless SDK (`createAgentSession()`). It does not spawn the `atomic` CLI or use RPC mode. @@ -152,7 +152,8 @@ Atomic caveats: x64, including glibc and musl native modules. Expect a larger image. - Atomic workflows need no database server. Their runs are journaled in a SQLite file, `$XDG_STATE_HOME/chopin/atomic/workflows.sqlite`, beside the - documents' Planner directories (see + documents' Planner directories, which Chopin passes to each Planner session + as its `journal` rather than through the process environment (see [Full Atomic Planner](hosted-agent.md#full-atomic-planner)). The Docker image sets `XDG_STATE_HOME=/var/lib/chopin`; mount a volume there to keep runs and those directories when the container is replaced. diff --git a/package.json b/package.json index 3567a2541..cddac5fc3 100644 --- a/package.json +++ b/package.json @@ -66,7 +66,7 @@ "@ai-sdk/harness": "1.0.146", "@ai-sdk/mcp": "2.0.72", "@ai-sdk/sandbox-just-bash": "1.0.146", - "@bastani/atomic": "0.9.38-alpha.9", + "@bastani/atomic": "0.9.39", "ai": "7.0.135", "typebox": "1.3.7", "zod": "4.4.3"