@bastani/atomic 0.9.26-alpha.5 → 0.9.26

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 (36) hide show
  1. package/CHANGELOG.md +68 -0
  2. package/dist/builtin/intercom/CHANGELOG.md +6 -0
  3. package/dist/builtin/intercom/package.json +2 -2
  4. package/dist/builtin/mcp/package.json +1 -1
  5. package/dist/builtin/subagents/package.json +3 -3
  6. package/dist/builtin/web-access/package.json +2 -2
  7. package/dist/builtin/workflows/CHANGELOG.md +21 -0
  8. package/dist/builtin/workflows/builtin/{chunk-613yaa53.js → chunk-9bhs02gm.js} +195 -10
  9. package/dist/builtin/workflows/builtin/{chunk-6kqrk0pf.js → chunk-gts8wgjr.js} +1 -1
  10. package/dist/builtin/workflows/builtin/{chunk-ea6nrbn3.js → chunk-gw7rghda.js} +1 -1
  11. package/dist/builtin/workflows/builtin/goal.js +2 -2
  12. package/dist/builtin/workflows/builtin/index.js +3 -3
  13. package/dist/builtin/workflows/builtin/ralph.js +2 -2
  14. package/dist/builtin/workflows/package.json +2 -2
  15. package/dist/builtin/workflows/src/extension/index.bundle.mjs +1082 -220
  16. package/dist/builtin/workflows/src/index.js +967 -186
  17. package/dist/core/extensions/workflow-run-control.d.ts +3 -0
  18. package/dist/core/extensions/workflow-run-control.d.ts.map +1 -1
  19. package/dist/core/extensions/workflow-run-control.js.map +1 -1
  20. package/dist/core/fast-model-routing.d.ts.map +1 -1
  21. package/dist/core/fast-model-routing.js +1 -5
  22. package/dist/core/fast-model-routing.js.map +1 -1
  23. package/dist/core/model-config.d.ts +36 -0
  24. package/dist/core/model-config.d.ts.map +1 -1
  25. package/dist/core/model-config.js +14 -2
  26. package/dist/core/model-config.js.map +1 -1
  27. package/dist/core/provider-composer-internal.d.ts +2 -1
  28. package/dist/core/provider-composer-internal.d.ts.map +1 -1
  29. package/dist/core/provider-composer-internal.js +13 -0
  30. package/dist/core/provider-composer-internal.js.map +1 -1
  31. package/docs/models/reference.md +28 -1
  32. package/docs/models.md +4 -0
  33. package/docs/sdk.md +26 -2
  34. package/docs/workflows/operations.md +15 -1
  35. package/npm-shrinkwrap.json +67 -67
  36. package/package.json +8 -8
package/CHANGELOG.md CHANGED
@@ -2,6 +2,74 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [0.9.26] - 2026-10-04
6
+
7
+ ### Breaking Changes
8
+
9
+ - MCP OAuth credentials are now stored per server name and URL, so MCP servers with the same URL can sign in with different accounts. Credentials stored by URL alone move to the first server that uses them ([#10252](https://github.com/earendil-works/pi/issues/10252)). SDK consumers of the exported `McpOAuthCredentialStore` must now pass the server name to `forServer`, `tokens`, `remove` and `removeAsync`.
10
+
11
+ ### Added
12
+
13
+ - Added `samplingParamsByThinkingLevel` to `models.json` for per-thinking-level sampling parameter overrides on OpenAI-compatible APIs, including native fast routes. See [Sampling by Thinking Level](docs/models/reference.md#sampling-by-thinking-level) ([#9776](https://github.com/earendil-works/pi/pull/9776) by [@mrexodia](https://github.com/mrexodia))
14
+ - Added `pi.registerToolRenderer()` to choose renderers for any tool, including tools not yet registered ([#10285](https://github.com/earendil-works/pi/issues/10285)).
15
+ - OAuth sign-in screens now show a copy shortcut for the sign-in URL, including `/mcp login`. Use Ctrl+X or configure `app.auth.copyUrl`; existing message and workflow shortcuts are unchanged.
16
+ - Added project overrides for user-level MCP servers: a `.atomic/mcp.json` entry without `command`, `url`, or `type` sets only `enabled`, `exposure`, and `toolExposure` of the user-level server, and `/mcp` can enable or disable a server for the current project ([#10277](https://github.com/earendil-works/pi/issues/10277)).
17
+ - Added Cloudflare's Clef and Clef Flash classifier models to `cloudflare-workers-ai`, usable from codemode scripts and extensions ([#10316](https://github.com/earendil-works/pi/pull/10316) by [@ndisidore](https://github.com/ndisidore), [#10322](https://github.com/earendil-works/pi/pull/10322) by [@RealAlexandreAI](https://github.com/RealAlexandreAI)).
18
+ - Added `pi.registerProviderAlias({ id, provider, name })` for separate account logins that inherit a provider's model catalog and request behavior without copying its implementation. Existing credentials under the alias ID are preserved.
19
+ - Added `quietStartup: "header"`, which keeps the startup header but hides the model scope line. `/settings` offers it alongside `true` and `false`.
20
+ - Added `models.generateImages()` to codemode scripts. It runs image models such as OpenRouter's with the session's credentials and returns base64 image blocks that `image()` attaches to the result; usage counts toward the session cost like `models.classify()`. Extensions can call `ctx.modelRegistry.generateImages()`. See [Built-in tools](docs/tools.md#codemode).
21
+
22
+ ### Changed
23
+
24
+ - Cancelling a login started from a `/login` menu now returns to that menu.
25
+ - The "Cloud Providers" section of the [Providers](docs/providers.md) docs page is now "Provider Specific Config".
26
+ - Codemode costs fewer prompt tokens. The `codemode` description points to the new [Codemode](docs/codemode.md) reference for the `models` API, which the model reads when it needs it, and declared tools say in one line how scripts call them and what the call resolves to instead of repeating their full declaration. The MCP server section of the system prompt only explains the ways of reaching tools that its servers use, and the system prompt points to the new reference.
27
+ - Codemode errors now say how to recover: reading a tool or `models` member that does not exist names the close matches (`tools.Bash` suggests `tools.bash`), `models.classify()` and `models.generateImages()` reject malformed arguments with the expected shape, an unknown model points to `models.getAvailableOfType()`, an oversized `store()` value explains what the store is for, and a script that generates images without showing them gets a note. Scripts that probed for a tool with `typeof tools.name` must use `"name" in tools`.
28
+ - `/login` and `/logout` now label providers without credentials as "not configured" instead of "unconfigured".
29
+ - OAuth browser pages now show the Atomic logo in color instead of white.
30
+
31
+ ### Fixed
32
+
33
+ - Fixed fresh SDK sessions being unable to inspect or resume crashed durable workflow runs from another process. Hosts using `SessionManager.inMemory()` can recover by full run UUID in the original working directory and workflow database without reopening a session file. Database ownership fencing protects healthy connected owners, including across cloned VM identities, and rejects late durable writes after connection loss. Older unfenced runs support explicit controlled enrollment after their workers stop. Completed checkpoints are preserved; interrupted external effects still require idempotency ([#3419](https://github.com/bastani-inc/atomic/issues/3419)).
34
+ - Fixed managed workflow database monitoring timeouts unnecessarily dropping healthy run ownership. A persistent timeout now reports database unavailability without closing existing ownership connections ([#3419](https://github.com/bastani-inc/atomic/issues/3419)).
35
+ - Fixed managed workflow PostgreSQL staying down after shutdown when its old launch options were missing. Database-unavailable errors and workflow status now show the provider, credential-redacted endpoint, and retained recovery refusal or failure ([#3413](https://github.com/bastani-inc/atomic/issues/3413)).
36
+ - Fixed NVIDIA's built-in default pointing to a model missing from its current catalog. Automatic selection now prefers Nemotron 3 Ultra 550B A55B.
37
+ - Fixed MCP tool calls in resumed sessions and HTML exports rendering fully expanded before their server connects, or when it never connects ([#10285](https://github.com/earendil-works/pi/issues/10285)).
38
+ - Fixed JPEG, GIF, and WebP images rendered by extensions through `Image` not appearing on Kitty-protocol terminals ([#10292](https://github.com/earendil-works/pi/issues/10292)).
39
+ - Fixed fullscreen Kitty images collapsing after scrolling in WezTerm ([#10319](https://github.com/earendil-works/pi/issues/10319)).
40
+ - Fixed `codemode` scripts that print in a loop exhausting memory. Scripts now fail after exceeding 16 Mi characters or 100000 output items ([#10283](https://github.com/earendil-works/pi/issues/10283)).
41
+ - Fixed resumed MCP calls and HTML exports assigning the wrong server to ambiguous tool names. Calls without known renderers now retain their full identifier when the server/tool boundary cannot be recovered.
42
+ - Empty comma-separated entries in `--models` no longer add unintended models to the cycle ([#10334](https://github.com/earendil-works/pi/issues/10334)).
43
+ - Pinned `brace-expansion` to 5.0.12 so installations use the patched dependency ([#10288](https://github.com/earendil-works/pi/issues/10288)).
44
+ - MCP OAuth sign-in now rejects pasted redirect URLs whose origin or path differs from the current sign-in's redirect URI.
45
+ - Fixed `/login` hiding the redirect-URL paste field for native extension providers that declare `auth.oauth.usesCallbackServer`, including built-in OAuth flows cloned under custom provider IDs ([#3400](https://github.com/bastani-inc/atomic/issues/3400)).
46
+ - `/mcp login` now prints its sign-in URL as a clickable link in the terminal UI, even when the URL wraps ([#10186](https://github.com/earendil-works/pi/issues/10186)).
47
+ - `--provider` without `--model` now fails with an error instead of being silently ignored while the default model from another provider runs ([#10236](https://github.com/earendil-works/pi/issues/10236)).
48
+ - MCP servers that ask for more scope (`insufficient_scope`) no longer trigger repeated sign-in requests. The new sign-in now keeps the scopes granted earlier instead of requesting only the missing ones.
49
+ - User messages in the transcript now keep one copy of each rendered line instead of two full-width copies, with identical output.
50
+ - Deferred MCP tools that `tool_search` loaded are no longer dropped on resume and `/reload` when their server reconnects before the next prompt; the session restored its tools before the MCP servers reconnected.
51
+ - The `system` theme keeps pastel terminal palettes pastel instead of making their accent colors more saturated at the lightness the theme needs ([#10255](https://github.com/earendil-works/pi/issues/10255), [#10293](https://github.com/earendil-works/pi/pull/10293) by [@dgtlntv](https://github.com/dgtlntv)).
52
+ - `/login` and `/logout` no longer label every OAuth sign-in as a subscription. Only subscription-backed providers say "subscription"; other OAuth sign-ins say "account".
53
+ - The startup header logo no longer renders with gaps in Apple Terminal; it shows a text `∀ Atomic` wordmark with the version and session details instead.
54
+ - Slash command autocompletion now triggers when the input starts with whitespace ([#10218](https://github.com/earendil-works/pi/pull/10218) by [@haoqixu](https://github.com/haoqixu)).
55
+ - Color no longer bleeds past mouse selections in fullscreen mode when a styled token ends at the selection boundary ([#10169](https://github.com/earendil-works/pi/issues/10169)).
56
+ - The transcript retains less memory per rendered message; a long assistant message keeps about a fifth of the heap it kept before.
57
+
58
+ ### Removed
59
+
60
+ - Removed the daxnuts easter egg shown when selecting `opencode/kimi-k2.5`.
61
+
62
+ ## [0.9.26-alpha.6] - 2026-10-04
63
+
64
+ ### Added
65
+
66
+ - Added `samplingParamsByThinkingLevel` to `models.json` for per-thinking-level sampling parameter overrides on OpenAI-compatible APIs, including native fast routes. See [Sampling by Thinking Level](docs/models/reference.md#sampling-by-thinking-level) ([#9776](https://github.com/earendil-works/pi/pull/9776) by [@mrexodia](https://github.com/mrexodia))
67
+
68
+ ### Fixed
69
+
70
+ - Fixed fresh SDK sessions being unable to inspect or resume crashed durable workflow runs from another process. Hosts using `SessionManager.inMemory()` can recover by full run UUID in the original working directory and workflow database without reopening a session file. Database ownership fencing protects healthy connected owners, including across cloned VM identities, and rejects late durable writes after connection loss. Older unfenced runs support explicit controlled enrollment after their workers stop. Completed checkpoints are preserved; interrupted external effects still require idempotency ([#3419](https://github.com/bastani-inc/atomic/issues/3419)).
71
+ - Fixed managed workflow database monitoring timeouts unnecessarily dropping healthy run ownership. A persistent timeout now reports database unavailability without closing existing ownership connections ([#3419](https://github.com/bastani-inc/atomic/issues/3419)).
72
+
5
73
  ## [0.9.26-alpha.5] - 2026-10-03
6
74
 
7
75
  ### Added
@@ -4,6 +4,12 @@ All notable changes to the `pi-intercom` extension will be documented in this fi
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [0.9.26] - 2026-10-04
8
+
9
+ ### Fixed
10
+
11
+ - Fixed sticky workflow messages disappearing during stage startup, and added per-stage delivery and skip results to `intercom list` so retained future-message counts cannot be mistaken for receipt by every stage ([#3406](https://github.com/bastani-inc/atomic/issues/3406)).
12
+
7
13
  ## [0.9.26-alpha.4] - 2026-10-02
8
14
 
9
15
  ### Fixed
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bastani/intercom",
3
- "version": "0.9.26-alpha.5",
3
+ "version": "0.9.26",
4
4
  "private": true,
5
5
  "description": "Atomic extension providing a private coordination channel between parent and child agent sessions. Fork of: https://github.com/nicobailon/pi-intercom",
6
6
  "contributors": [
@@ -44,7 +44,7 @@
44
44
  },
45
45
  "peerDependencies": {
46
46
  "@bastani/atomic": "*",
47
- "@earendil-works/pi-tui": "^1.0.1",
47
+ "@earendil-works/pi-tui": "^1.0.2",
48
48
  "typebox": "*"
49
49
  },
50
50
  "peerDependenciesMeta": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bastani/mcp",
3
- "version": "0.9.26-alpha.5",
3
+ "version": "0.9.26",
4
4
  "private": true,
5
5
  "description": "Atomic's pi-compatible MCP integration for server connections, OAuth, tools, resources, and terminal management.",
6
6
  "contributors": [
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bastani/subagents",
3
- "version": "0.9.26-alpha.5",
3
+ "version": "0.9.26",
4
4
  "private": true,
5
5
  "description": "Atomic extension for delegating tasks to subagents with parallel execution. Fork of: https://github.com/nicobailon/pi-subagents",
6
6
  "contributors": [
@@ -35,8 +35,8 @@
35
35
  "peerDependencies": {
36
36
  "@bastani/atomic": "*",
37
37
  "@bastani/pi-ai": "*",
38
- "@earendil-works/pi-agent-core": "^1.0.1",
39
- "@earendil-works/pi-tui": "^1.0.1",
38
+ "@earendil-works/pi-agent-core": "^1.0.2",
39
+ "@earendil-works/pi-tui": "^1.0.2",
40
40
  "typebox": "*"
41
41
  },
42
42
  "peerDependenciesMeta": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bastani/web-access",
3
- "version": "0.9.26-alpha.5",
3
+ "version": "0.9.26",
4
4
  "private": true,
5
5
  "description": "Atomic extension for web search, URL fetching, GitHub repo cloning, PDF/video extraction. Fork of: https://github.com/nicobailon/pi-web-access",
6
6
  "contributors": [
@@ -31,7 +31,7 @@
31
31
  },
32
32
  "peerDependencies": {
33
33
  "@bastani/atomic": "*",
34
- "@earendil-works/pi-tui": "^1.0.1"
34
+ "@earendil-works/pi-tui": "^1.0.2"
35
35
  },
36
36
  "peerDependenciesMeta": {
37
37
  "@bastani/atomic": {
@@ -6,6 +6,27 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.9.26] - 2026-10-04
10
+
11
+ ### Added
12
+
13
+ - Added `workflows.durability.systemDatabaseUrl` and `systemDatabaseUrlFile` in global and trusted project settings to choose a persistent workflow database without launcher-specific environment configuration. `DBOS_SYSTEM_DATABASE_URL` retains precedence; explicit selections never provision embedded PostgreSQL or Docker. Interactive and RPC sessions report the selected provider and host without credentials on the first workflow action; headless sessions print only a Docker fallback selection to stderr. Existing workflow-path arrays remain supported alongside the new object form with `paths` ([#3416](https://github.com/bastani-inc/atomic/issues/3416)).
14
+
15
+ ### Fixed
16
+
17
+ - Fixed SDK inspection rejecting earlier-process durable runs and crash recovery retaining the old session owner. Explicit SDK resume atomically adopts an eligible crashed root in the same working directory and workflow database, preserves its UUID and completed checkpoints, and excludes healthy connected owners and competing adopters. Database ownership fencing handles cloned VM identities and rejects disconnected owners' late durable writes. Older unfenced runs support controlled enrollment after their workers stop; unfinished external callbacks may overlap recovery and must use idempotent effects ([#3419](https://github.com/bastani-inc/atomic/issues/3419)).
18
+ - Fixed managed PostgreSQL monitoring connection timeouts unnecessarily closing healthy workflow ownership connections. Monitoring retries once; a persistent timeout reports database unavailability without discarding connected owners or restarting the database ([#3419](https://github.com/bastani-inc/atomic/issues/3419)).
19
+ - Fixed managed PostgreSQL staying down after shutdown when its old launch options were missing. Recovery preserves cluster ownership and identity checks, and database-unavailable admission errors and workflow status now show the provider, credential-redacted endpoint, and retained probe or recovery failure ([#3413](https://github.com/bastani-inc/atomic/issues/3413)).
20
+ - Fixed sticky Intercom broadcasts to `workflow:<rootRunId>/**` missing later stages before their first model turn. Workflow status now reports per-stage delivery and skip results for retained messages ([#3406](https://github.com/bastani-inc/atomic/issues/3406)).
21
+ - Fixed workflow-stage `ask_user_question` questions never reaching an SDK host's `HostInput` when the stage was already awaiting input, which left the run waiting indefinitely ([#3396](https://github.com/bastani-inc/atomic/issues/3396)).
22
+
23
+ ## [0.9.26-alpha.6] - 2026-10-04
24
+
25
+ ### Fixed
26
+
27
+ - Fixed SDK inspection rejecting earlier-process durable runs and crash recovery retaining the old session owner. Explicit SDK resume atomically adopts an eligible crashed root in the same working directory and workflow database, preserves its UUID and completed checkpoints, and excludes healthy connected owners and competing adopters. Database ownership fencing handles cloned VM identities and rejects disconnected owners' late durable writes. Older unfenced runs support controlled enrollment after their workers stop; unfinished external callbacks may overlap recovery and must use idempotent effects ([#3419](https://github.com/bastani-inc/atomic/issues/3419)).
28
+ - Fixed managed PostgreSQL monitoring connection timeouts unnecessarily closing healthy workflow ownership connections. Monitoring retries once; a persistent timeout reports database unavailability without discarding connected owners or restarting the database ([#3419](https://github.com/bastani-inc/atomic/issues/3419)).
29
+
9
30
  ## [0.9.26-alpha.5] - 2026-10-03
10
31
 
11
32
  ### Added
@@ -5305,8 +5305,186 @@ var STATUS_RELATED_MESSAGE_REFINEMENT_CODES = new Set([
5305
5305
  "quota_limited",
5306
5306
  "cancelled"
5307
5307
  ]);
5308
- // dist/builtin/workflows/src/durable/dbos-sdk-handle.ts
5309
- var ATOMIC_EXECUTOR_ID = `atomic-${process.pid.toString(36)}-${crypto.randomUUID().slice(0, 8)}`;
5308
+ // dist/builtin/workflows/src/durable/dbos-owner-fence.ts
5309
+ import { AsyncLocalStorage as AsyncLocalStorage2 } from "node:async_hooks";
5310
+
5311
+ // dist/builtin/workflows/src/durable/dbos-admission-pool.ts
5312
+ var connectionErrnos = new Set([
5313
+ "ECONNRESET",
5314
+ "ECONNREFUSED",
5315
+ "ECONNABORTED",
5316
+ "EHOSTUNREACH",
5317
+ "ENETUNREACH",
5318
+ "ETIMEDOUT",
5319
+ "ENOTFOUND",
5320
+ "EAI_AGAIN",
5321
+ "EPIPE"
5322
+ ]);
5323
+ var connectionMessages = new Set([
5324
+ "Connection terminated unexpectedly",
5325
+ "Connection terminated due to connection timeout",
5326
+ "Client has encountered a connection error and is not queryable",
5327
+ "timeout exceeded when trying to connect",
5328
+ "Client was closed and is not queryable",
5329
+ "Postgres connection pool changed while acquiring a connection"
5330
+ ]);
5331
+
5332
+ // dist/builtin/workflows/src/durable/dbos-row-guard.ts
5333
+ import { createHash } from "node:crypto";
5334
+ var guardSql = `
5335
+ CREATE TABLE IF NOT EXISTS dbos.atomic_owner_generation (
5336
+ root text PRIMARY KEY, owner text, generation bigint,
5337
+ terminal boolean NOT NULL DEFAULT false, claim text, claimant text, protocol integer NOT NULL DEFAULT 1 CHECK(protocol = 1)
5338
+ );
5339
+ CREATE TABLE IF NOT EXISTS dbos.atomic_guard_rows (workflow_uuid text PRIMARY KEY, root text NOT NULL);
5340
+ CREATE OR REPLACE FUNCTION dbos.atomic_metadata(value text) RETURNS jsonb
5341
+ LANGUAGE plpgsql IMMUTABLE SET search_path = pg_catalog AS $$
5342
+ DECLARE decoded jsonb;
5343
+ BEGIN
5344
+ IF value IS NULL THEN RETURN NULL; END IF;
5345
+ BEGIN decoded := value::jsonb; EXCEPTION WHEN invalid_text_representation THEN RETURN NULL; END;
5346
+ IF decoded->>'__dbos_serializer' = 'superjson' THEN decoded := decoded->'json'; END IF;
5347
+ IF decoded->>'__atomicDurableMetadata' IS DISTINCT FROM 'true' OR decoded->>'version' IS DISTINCT FROM '3' THEN RETURN NULL; END IF;
5348
+ RETURN decoded->'metadata';
5349
+ END $$;
5350
+ CREATE OR REPLACE FUNCTION dbos.atomic_guard_row() RETURNS trigger
5351
+ LANGUAGE plpgsql SET search_path = pg_catalog AS $$
5352
+ DECLARE
5353
+ row_value jsonb; id text; root_id text; authority jsonb; state dbos.atomic_owner_generation%ROWTYPE;
5354
+ meta jsonb; prior jsonb; prior_generation bigint; next_generation bigint; absorbing boolean;
5355
+ allowed boolean; cap_owner text; cap_generation bigint; claim_token text;
5356
+ BEGIN
5357
+ row_value := CASE WHEN TG_OP = 'DELETE' THEN to_jsonb(OLD) ELSE to_jsonb(NEW) END;
5358
+ id := row_value->>'workflow_uuid';
5359
+ root_id := split_part(id, ':checkpoint:', 1);
5360
+ IF TG_OP = 'UPDATE' AND to_jsonb(OLD)->>'workflow_uuid' IS DISTINCT FROM id AND (
5361
+ EXISTS(SELECT 1 FROM dbos.atomic_guard_rows WHERE workflow_uuid = to_jsonb(OLD)->>'workflow_uuid') OR
5362
+ (TG_TABLE_NAME = 'workflow_status' AND to_jsonb(OLD)->>'name' IN ('atomicWorkflowHandle','atomicWorkflowCheckpoint')) OR
5363
+ (TG_TABLE_NAME = 'operation_outputs' AND EXISTS(SELECT 1 FROM dbos.workflow_status WHERE workflow_uuid = to_jsonb(OLD)->>'workflow_uuid' AND name IN ('atomicWorkflowHandle','atomicWorkflowCheckpoint')))
5364
+ ) THEN
5365
+ IF NULLIF(current_setting('atomic.row_authority',true),'') IS NULL OR current_setting('atomic.row_authority',true) = 'null' THEN RETURN NULL; END IF;
5366
+ RAISE EXCEPTION 'Atomic workflow identities are immutable';
5367
+ END IF;
5368
+ IF TG_TABLE_NAME = 'workflow_status' AND NOT (
5369
+ COALESCE(row_value->>'name' IN ('atomicWorkflowHandle','atomicWorkflowCheckpoint'), false) OR
5370
+ COALESCE(to_jsonb(OLD)->>'name' IN ('atomicWorkflowHandle','atomicWorkflowCheckpoint'), false) OR
5371
+ EXISTS(SELECT 1 FROM dbos.atomic_guard_rows WHERE workflow_uuid = id)
5372
+ ) THEN
5373
+ IF TG_OP = 'DELETE' THEN RETURN OLD; ELSE RETURN NEW; END IF;
5374
+ END IF;
5375
+ authority := NULLIF(current_setting('atomic.row_authority', true), '')::jsonb;
5376
+ IF TG_TABLE_NAME = 'operation_outputs' AND NOT EXISTS (
5377
+ SELECT 1 FROM dbos.workflow_status WHERE workflow_uuid = id AND name IN ('atomicWorkflowHandle','atomicWorkflowCheckpoint')
5378
+ ) AND NOT EXISTS (SELECT 1 FROM dbos.atomic_guard_rows WHERE workflow_uuid = id) THEN
5379
+ IF TG_OP = 'DELETE' THEN RETURN OLD; ELSE RETURN NEW; END IF;
5380
+ END IF;
5381
+ INSERT INTO dbos.atomic_guard_rows VALUES(id, root_id) ON CONFLICT DO NOTHING;
5382
+ INSERT INTO dbos.atomic_guard_rows SELECT workflow_uuid, root_id FROM dbos.workflow_status
5383
+ WHERE workflow_uuid = root_id AND name = 'atomicWorkflowHandle' ON CONFLICT DO NOTHING;
5384
+ INSERT INTO dbos.atomic_owner_generation(root) VALUES(root_id)
5385
+ ON CONFLICT(root) DO UPDATE SET root = EXCLUDED.root RETURNING * INTO state;
5386
+ IF state.owner IS NULL THEN
5387
+ IF TG_TABLE_NAME <> 'workflow_status' OR TG_OP = 'DELETE' OR row_value->>'status' <> 'SUCCESS'
5388
+ OR id NOT LIKE root_id || ':checkpoint:__atomic_metadata:%'
5389
+ OR authority->>'root' IS DISTINCT FROM root_id OR authority->>'enroll' IS DISTINCT FROM 'true' THEN
5390
+ IF TG_OP = 'DELETE' THEN RETURN OLD; ELSE RETURN NEW; END IF;
5391
+ END IF;
5392
+ meta := dbos.atomic_metadata(row_value->>'output');
5393
+ IF meta IS NULL OR meta->>'workflowId' IS DISTINCT FROM root_id THEN
5394
+ RAISE EXCEPTION 'Atomic metadata encoding is unavailable for enrollment';
5395
+ END IF;
5396
+ SELECT candidate.metadata, candidate.generation INTO prior, prior_generation FROM (
5397
+ SELECT dbos.atomic_metadata(output) AS metadata,
5398
+ split_part(substring(workflow_uuid FROM length(root_id) + 13), ':', 2)::bigint AS generation
5399
+ FROM dbos.workflow_status
5400
+ WHERE workflow_uuid LIKE root_id || ':checkpoint:__atomic_metadata:%' AND status = 'SUCCESS'
5401
+ ) candidate WHERE candidate.metadata IS NOT NULL
5402
+ ORDER BY COALESCE(candidate.metadata->>'status' IN ('completed', 'cancelled') OR
5403
+ (candidate.metadata->>'status' IN ('failed','blocked') AND candidate.metadata->>'resumable' = 'false'), false) DESC,
5404
+ candidate.generation DESC LIMIT 1;
5405
+ IF prior_generation IS DISTINCT FROM (authority->>'generation')::bigint
5406
+ OR prior->>'ownerExecutorId' IS DISTINCT FROM authority->>'owner' THEN
5407
+ RAISE EXCEPTION 'Atomic ownership generation changed before enrollment';
5408
+ END IF;
5409
+ ELSE
5410
+ IF authority->>'root' IS DISTINCT FROM root_id THEN RETURN NULL; END IF;
5411
+ cap_owner := authority->>'owner'; cap_generation := (authority->>'generation')::bigint;
5412
+ allowed := cap_owner = state.owner AND cap_generation = state.generation AND (
5413
+ authority->>'actor' = state.owner OR
5414
+ (authority->>'claim' = state.claim AND authority->>'actor' = state.claimant) OR
5415
+ (authority->>'claim' IS NOT NULL AND authority->>'checkpoint' = id));
5416
+ IF NOT COALESCE(allowed, false) THEN RAISE EXCEPTION 'Atomic ownership generation changed; stale database writes are refused'; END IF;
5417
+ END IF;
5418
+ IF TG_TABLE_NAME = 'workflow_status' AND TG_OP = 'UPDATE' THEN
5419
+ IF OLD.status = 'SUCCESS' AND OLD.name = 'atomicWorkflowCheckpoint' THEN
5420
+ IF NEW.output IS DISTINCT FROM OLD.output OR NEW.status <> 'SUCCESS' THEN RAISE EXCEPTION 'Completed Atomic checkpoints are immutable'; END IF;
5421
+ RETURN NEW;
5422
+ END IF;
5423
+ END IF;
5424
+ IF TG_TABLE_NAME = 'workflow_status' AND TG_OP <> 'DELETE' AND row_value->>'status' = 'SUCCESS'
5425
+ AND id LIKE root_id || ':checkpoint:__atomic_metadata:%' THEN
5426
+ meta := dbos.atomic_metadata(row_value->>'output');
5427
+ IF meta IS NULL OR meta->>'workflowId' IS DISTINCT FROM root_id OR meta->>'ownerExecutorId' IS DISTINCT FROM authority->>'actor' THEN RAISE EXCEPTION 'Atomic metadata encoding or owner is unavailable'; END IF;
5428
+ next_generation := split_part(substring(id FROM length(root_id) + 13), ':', 2)::bigint;
5429
+ absorbing := COALESCE(meta->>'status' IN ('completed', 'cancelled') OR
5430
+ (meta->>'status' IN ('failed','blocked') AND meta->>'resumable' = 'false'), false);
5431
+ claim_token := meta->>'transitionClaimId';
5432
+ IF state.root IS NOT NULL AND (next_generation < state.generation OR (state.terminal AND NOT absorbing)) THEN
5433
+ RAISE EXCEPTION 'Atomic terminal or newer generation cannot be overwritten';
5434
+ END IF;
5435
+ IF state.owner IS NOT NULL AND authority->>'actor' <> state.owner THEN
5436
+ IF id NOT LIKE '%:claim' OR next_generation <> state.generation + 1 OR claim_token IS DISTINCT FROM authority->>'claim' OR meta->>'ownerExecutorId' IS DISTINCT FROM authority->>'actor' THEN
5437
+ RAISE EXCEPTION 'Atomic ownership transfer requires an exact generation claim';
5438
+ END IF;
5439
+ END IF;
5440
+ END IF;
5441
+ IF TG_OP = 'DELETE' THEN RETURN OLD; ELSE RETURN NEW; END IF;
5442
+ END $$;
5443
+ CREATE OR REPLACE FUNCTION dbos.atomic_project_metadata() RETURNS trigger
5444
+ LANGUAGE plpgsql SET search_path = pg_catalog AS $$
5445
+ DECLARE root_id text; meta jsonb; authority jsonb; state dbos.atomic_owner_generation%ROWTYPE; projected_generation bigint; absorbing boolean;
5446
+ BEGIN
5447
+ IF NEW.name <> 'atomicWorkflowCheckpoint' OR NEW.status <> 'SUCCESS' THEN RETURN NEW; END IF;
5448
+ root_id := split_part(NEW.workflow_uuid, ':checkpoint:', 1);
5449
+ IF NEW.workflow_uuid NOT LIKE root_id || ':checkpoint:__atomic_metadata:%' THEN RETURN NEW; END IF;
5450
+ IF TG_OP = 'UPDATE' THEN IF OLD.status = 'SUCCESS' THEN RETURN NEW; END IF; END IF;
5451
+ authority := NULLIF(current_setting('atomic.row_authority', true), '')::jsonb;
5452
+ SELECT * INTO state FROM dbos.atomic_owner_generation WHERE root = root_id FOR UPDATE;
5453
+ IF state.owner IS NULL AND authority->>'enroll' IS DISTINCT FROM 'true' THEN RETURN NEW; END IF;
5454
+ meta := dbos.atomic_metadata(NEW.output);
5455
+ projected_generation := split_part(substring(NEW.workflow_uuid FROM length(root_id) + 13), ':', 2)::bigint;
5456
+ absorbing := COALESCE(meta->>'status' IN ('completed','cancelled') OR (meta->>'status' IN ('failed','blocked') AND meta->>'resumable' = 'false'), false);
5457
+ UPDATE dbos.atomic_owner_generation SET owner = meta->>'ownerExecutorId', generation = projected_generation,
5458
+ terminal = absorbing, claim = CASE WHEN NEW.workflow_uuid LIKE '%:claim' THEN meta->>'transitionClaimId' END,
5459
+ claimant = CASE WHEN NEW.workflow_uuid LIKE '%:claim' THEN authority->>'actor' END WHERE root = root_id;
5460
+ RETURN NEW;
5461
+ END $$;
5462
+ DO $$ BEGIN
5463
+ IF NOT EXISTS (SELECT 1 FROM pg_trigger WHERE tgrelid = 'dbos.workflow_status'::regclass AND tgname = 'atomic_generation_guard') THEN
5464
+ CREATE TRIGGER atomic_generation_guard BEFORE INSERT OR UPDATE OR DELETE ON dbos.workflow_status
5465
+ FOR EACH ROW EXECUTE FUNCTION dbos.atomic_guard_row();
5466
+ END IF;
5467
+ IF NOT EXISTS (SELECT 1 FROM pg_trigger WHERE tgrelid = 'dbos.operation_outputs'::regclass AND tgname = 'atomic_generation_guard') THEN
5468
+ CREATE TRIGGER atomic_generation_guard BEFORE INSERT OR UPDATE OR DELETE ON dbos.operation_outputs
5469
+ FOR EACH ROW EXECUTE FUNCTION dbos.atomic_guard_row();
5470
+ END IF;
5471
+ IF NOT EXISTS (SELECT 1 FROM pg_trigger WHERE tgrelid = 'dbos.workflow_status'::regclass AND tgname = 'atomic_generation_projection') THEN
5472
+ CREATE TRIGGER atomic_generation_projection AFTER INSERT OR UPDATE ON dbos.workflow_status
5473
+ FOR EACH ROW EXECUTE FUNCTION dbos.atomic_project_metadata();
5474
+ END IF;
5475
+ END $$;
5476
+ `;
5477
+ var fingerprint = createHash("sha256").update(guardSql).digest("hex");
5478
+
5479
+ // dist/builtin/workflows/src/durable/dbos-owner-fence.ts
5480
+ var contextKey = Symbol.for("atomic-workflows/dbos-owner-write-context@1");
5481
+ var contextBag = globalThis;
5482
+ contextBag[contextKey] ??= new AsyncLocalStorage2;
5483
+ var writeContext = contextBag[contextKey];
5484
+ var rowContextKey = Symbol.for("atomic-workflows/dbos-row-authority@1");
5485
+ var rowContextBag = globalThis;
5486
+ rowContextBag[rowContextKey] ??= new AsyncLocalStorage2;
5487
+ var rowContext = rowContextBag[rowContextKey];
5310
5488
 
5311
5489
  // dist/builtin/workflows/src/durable/dbos-system-database-url.ts
5312
5490
  function nonEmptyTrimmed(value) {
@@ -5378,7 +5556,7 @@ import { tmpdir, uptime } from "node:os";
5378
5556
  import { basename as basename2, dirname as dirname3, isAbsolute as isAbsolute3, join as join4, relative as relative2, sep as sep2 } from "node:path";
5379
5557
 
5380
5558
  // dist/builtin/workflows/src/durable/dbos-embedded-postgres-root.ts
5381
- import { createHash } from "node:crypto";
5559
+ import { createHash as createHash2 } from "node:crypto";
5382
5560
  import {
5383
5561
  chmod,
5384
5562
  chown,
@@ -5861,8 +6039,8 @@ async function snapshotSourceRuntime(root, publisher, progress) {
5861
6039
  const rootStat = await lstat(root);
5862
6040
  assertRuntimeRoot(root, rootStat);
5863
6041
  const rootRealPath = await realpath(root);
5864
- const sourceHash = createHash("sha256");
5865
- const sealedHash = createHash("sha256");
6042
+ const sourceHash = createHash2("sha256");
6043
+ const sealedHash = createHash2("sha256");
5866
6044
  await hashSourceEntry(sourceHash, sealedHash, root, ".", rootRealPath, publisher, progress);
5867
6045
  return { sourceIdentity: sourceHash.digest("hex"), sealedIdentity: sealedHash.digest("hex") };
5868
6046
  }
@@ -6127,7 +6305,7 @@ async function snapshotSealedManifest(root, progress) {
6127
6305
  const rootStat = await lstat(root);
6128
6306
  assertRuntimeRoot(root, rootStat);
6129
6307
  const rootRealPath = await realpath(root);
6130
- const hash = createHash("sha256");
6308
+ const hash = createHash2("sha256");
6131
6309
  const entries = [];
6132
6310
  await hashSealedEntry(hash, root, ".", rootRealPath, progress, entries);
6133
6311
  return { version: 1, sealedIdentity: hash.digest("hex"), entries };
@@ -6360,6 +6538,13 @@ function resolveEmbeddedPostgresTarget(host) {
6360
6538
  // dist/builtin/workflows/src/durable/dbos-postgres-health.ts
6361
6539
  var HEALTH_INTERVAL_MS = 5000;
6362
6540
  var RECOVERY_ATTEMPTS = 3;
6541
+ function isMonitoringConnectionTimeout(error) {
6542
+ if (!(error instanceof Error))
6543
+ return false;
6544
+ if ("code" in error)
6545
+ return error.code === "ETIMEDOUT";
6546
+ return error.message === "timeout expired" || error.message === "Connection terminated due to connection timeout";
6547
+ }
6363
6548
  function isMonitoringConnectionFailure(error) {
6364
6549
  if (!(error instanceof Error))
6365
6550
  return false;
@@ -6470,7 +6655,7 @@ class PostgresHealth {
6470
6655
  try {
6471
6656
  identity = await this.operations.probe();
6472
6657
  } catch (retryError) {
6473
- if (!isMonitoringConnectionFailure(retryError))
6658
+ if (isMonitoringConnectionTimeout(retryError) || !isMonitoringConnectionFailure(retryError))
6474
6659
  throw retryError;
6475
6660
  this.retainFailure(retryError);
6476
6661
  return;
@@ -6496,7 +6681,7 @@ class PostgresHealth {
6496
6681
  if (ready !== undefined)
6497
6682
  return ready;
6498
6683
  } catch (error) {
6499
- if (isQueryReadTimeout(error)) {
6684
+ if (isQueryReadTimeout(error) || isMonitoringConnectionTimeout(error)) {
6500
6685
  this.retainFailure(new Error("Managed PostgreSQL did not answer a health check in time."));
6501
6686
  throw this.dependencyFailure("Managed PostgreSQL did not answer a health check in time.");
6502
6687
  }
@@ -6803,7 +6988,7 @@ async function probePostgresIdentity(port) {
6803
6988
  const result = await client.query(POSTGRES_IDENTITY_SQL);
6804
6989
  return result.rows[0];
6805
6990
  } catch (error) {
6806
- if (isQueryReadTimeout(error))
6991
+ if (isQueryReadTimeout(error) || isMonitoringConnectionTimeout(error))
6807
6992
  throw error;
6808
6993
  const code = error instanceof Error && "code" in error ? error.code : undefined;
6809
6994
  if (code === "ECONNREFUSED" || code === "ECONNRESET" || code === "EPIPE" || code === "57P01" || code === "57P02" || code === "57P03" || error instanceof Error && /timeout|timed out|connection terminated/i.test(error.message))
@@ -6976,7 +7161,7 @@ async function ensureCluster(options = {}) {
6976
7161
  try {
6977
7162
  return await existingServerProbe()(probePort);
6978
7163
  } catch (error) {
6979
- if (isQueryReadTimeout(error))
7164
+ if (isQueryReadTimeout(error) || isMonitoringConnectionTimeout(error))
6980
7165
  return;
6981
7166
  throw error;
6982
7167
  }
@@ -39,7 +39,7 @@ import {
39
39
  reviewerFailureText,
40
40
  summarizeReviewConvergence,
41
41
  reverify_consolidated_batch
42
- } from "./chunk-613yaa53.js";
42
+ } from "./chunk-9bhs02gm.js";
43
43
  import {
44
44
  fold_usage
45
45
  } from "./chunk-tgt0s5e5.js";
@@ -38,7 +38,7 @@ import {
38
38
  reviewerFailureText,
39
39
  summarizeReviewConvergence,
40
40
  reverify_consolidated_batch
41
- } from "./chunk-613yaa53.js";
41
+ } from "./chunk-9bhs02gm.js";
42
42
  import {
43
43
  VERIFICATION_SCALE
44
44
  } from "./chunk-n1910xc4.js";
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  goal_default
3
- } from "./chunk-ea6nrbn3.js";
4
- import"./chunk-613yaa53.js";
3
+ } from "./chunk-gw7rghda.js";
4
+ import"./chunk-9bhs02gm.js";
5
5
  import"./chunk-2377r57g.js";
6
6
  import"./chunk-z0d03ktm.js";
7
7
  import"./chunk-n1910xc4.js";
@@ -14,11 +14,11 @@ import {
14
14
  import {
15
15
  generate_and_filter_default
16
16
  } from "./chunk-kb7s3gx2.js";
17
- import"./chunk-613yaa53.js";
17
+ import"./chunk-9bhs02gm.js";
18
18
  import"./chunk-z0d03ktm.js";
19
19
  import {
20
20
  goal_default
21
- } from "./chunk-ea6nrbn3.js";
21
+ } from "./chunk-gw7rghda.js";
22
22
  import {
23
23
  open_claude_design_default
24
24
  } from "./chunk-nkj4qhj6.js";
@@ -27,7 +27,7 @@ import {
27
27
  } from "./chunk-94f8zfz1.js";
28
28
  import {
29
29
  ralph_default
30
- } from "./chunk-6kqrk0pf.js";
30
+ } from "./chunk-gts8wgjr.js";
31
31
  import {
32
32
  tournament_default
33
33
  } from "./chunk-wf876n16.js";
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  ralph_default
3
- } from "./chunk-6kqrk0pf.js";
4
- import"./chunk-613yaa53.js";
3
+ } from "./chunk-gts8wgjr.js";
4
+ import"./chunk-9bhs02gm.js";
5
5
  import"./chunk-2377r57g.js";
6
6
  import"./chunk-z0d03ktm.js";
7
7
  import"./chunk-n1910xc4.js";
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bastani/workflows",
3
- "version": "0.9.26-alpha.5",
3
+ "version": "0.9.26",
4
4
  "private": true,
5
5
  "description": "Atomic extension for multi-stage workflow authoring and execution.",
6
6
  "contributors": [
@@ -85,7 +85,7 @@
85
85
  },
86
86
  "peerDependencies": {
87
87
  "@bastani/atomic": "*",
88
- "@earendil-works/pi-tui": "^1.0.1",
88
+ "@earendil-works/pi-tui": "^1.0.2",
89
89
  "typebox": "*"
90
90
  },
91
91
  "peerDependenciesMeta": {