Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions apps/server/src/harness/atomic/full.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ async function run(
projectPackage = false,
operatorPackage = false,
askingWorkflow = false,
operatorSettings,
afterTurn,
events,
reports,
Expand All @@ -114,6 +115,8 @@ async function run(
operatorPackage?: boolean;
/** Load a workflow whose stage calls ask_user_question, through the operator's extension paths. */
askingWorkflow?: boolean;
/** The operator's Atomic `settings.json`, as the host CLI would have written it. */
operatorSettings?: Record<string, unknown>;
/** Runs once the Planner's turn has ended, while its session and workflow runs are still live. */
afterTurn?: () => Promise<void>;
/** Collects, in order, the tool reports the adapter makes and the stream parts a tool yields. */
Expand Down Expand Up @@ -277,6 +280,9 @@ export default workflow({
);
}
process.env.ATOMIC_CODING_AGENT_DIR = agentDir;
if (operatorSettings) {
await writeFile(join(agentDir, "settings.json"), JSON.stringify(operatorSettings));
}
let humanInput: HostInput = host ?? {
confirm: async () => false,
select: async () => undefined,
Expand Down Expand Up @@ -560,6 +566,27 @@ test(
60_000,
);

test(
"an operator's shared storage selection, even an adapter package, does not move a Planner's workflow journal",
async () => {
let result = await run(true, "launch", {
askingWorkflow: true,
// What `atomic storage join` writes; the adapter package is not installed here.
operatorSettings: {
storage: {
adapter: "@bastani/atomic-storage-libsql",
options: { url: "libsql://chopin-test.invalid" },
},
},
});
let launched = result.requests.at(-1)!.toolResults.join("\n");
expect(launched).toContain("asking-workflow");
expect(launched).not.toContain('"failed"');
expect(result.files).toMatchObject({ journal: true, defaultJournal: false });
},
WORKFLOW_TOOL_TIMEOUT_MS,
);

// Atomic starts a turn for its own notices, and since 0.9.41 for a message queued on an idle session.
test.each(["notice", "queued"])(
"a turn Atomic starts by itself (%s) between the Planner's turns is stopped before it reaches the model",
Expand Down
36 changes: 19 additions & 17 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 6 additions & 1 deletion docs/hosted-agent.md
Original file line number Diff line number Diff line change
Expand Up @@ -678,7 +678,12 @@ 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 and its `ATOMIC_WORKFLOWS_JOURNAL` and
`ATOMIC_STORAGE` selections do not move a Planner session's journal. Atomic
`ATOMIC_STORAGE` selections do not move a Planner session's journal, and
neither does the shared top-level `storage` setting, including an adapter named
by package such as the libSQL selection `atomic storage join` writes. That
setting does reach the Planner's Intercom: its sessions use the machine's
Intercom broker, which keeps its routing in the selected database and so can
reach sessions on every machine that selects the same one. Atomic
still checks every set `ATOMIC_STORAGE`, `ATOMIC_SESSIONS_STORAGE`, and
`ATOMIC_WORKFLOWS_JOURNAL` when it creates a session, so an invalid value fails
every Atomic turn with a `StorageConfigError` naming the variable. A Build is not a workflow run and has no card in Chat: its tasks, their
Expand Down
10 changes: 9 additions & 1 deletion docs/self-hosting.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.41-alpha.1 in the Chopin server process through its
`HARNESS=atomic` runs Atomic 0.9.42 in the Chopin server process through its
headless SDK (`createAgentSession()`). It does not spawn the `atomic` CLI or use
RPC mode.

Expand Down Expand Up @@ -143,6 +143,14 @@ Atomic caveats:
- Harness-level compaction, suspending a turn, and supplied harness skills are
unsupported. A Planner session's own resource discovery does load Atomic
skills. Atomic's automatic retries remain on.
- Since Atomic 0.9.42 a provider's rate limit is shared by the whole server
process. After one request gets a 429, every Atomic session in the server,
Planner turns and workers alike, waits for that provider's cooldown (its
`Retry-After`, otherwise backoff of up to 60 seconds) before sending its next
request, instead of each hitting the limit in turn. A turn waiting there shows
no output yet; stopping it ends the wait without sending the request. Chopin
sets no request budget (`configureProviderRequestBudget()`) or
`resourceAdmission` of its own.
- Under `auto`, refreshing an OAuth token in memory can rotate the refresh token
stored by the host CLI, which may then ask the operator to sign in again.
`auto` also runs `!command` API-key entries in `auth.json` to resolve them.
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,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.41-alpha.1",
"@bastani/atomic": "0.9.42",
"ai": "7.0.135",
"typebox": "1.3.7",
"zod": "4.4.3"
Expand Down
Loading