Skip to content

0.9.10

Choose a tag to compare

@github-actions github-actions released this 20 Jul 15:59
d1c827a

Breaking Changes

  • The bundled workflow tool no longer accepts direct one-off task, tasks, or chain shapes (or their direct-only top-level options), and the bundled workflow SDK no longer exports runTask, runParallel, or runChain. Launch named workflows with workflow + inputs, and author custom definitions with the unchanged ctx.task, ctx.chain, and ctx.parallel primitives.
  • Bundled workflow durability now requires DBOS/Postgres and no longer provides local JSON/SQLite persistence, runtime opt-out/backend selection, session-transcript discovery, or conversion of prior durable data.
  • Added a host-native session picker capability, ctx.ui.hostSessionPicker(request), implemented first-class by every interactive host with one identical API: non-isolated interactive mode mounts the real built-in session selector directly in the terminal process (no IPC), and the isolated interactive engine routes the same capability over a new engine session-picker protocol channel (engine_session_picker_open/update/error/close messages and engine_session_picker_select/cancel/delete commands). The extension ships JSON-safe rows (HostSessionPickerRowSessionInfo with createdAt/modifiedAt epoch millis) and only semantic events ever cross a process boundary; arrow-key navigation and search are zero-IPC and stay responsive even while the extension's event loop is busy, unlike remote-rendered ctx.ui.custom() components, which pay one round trip per keypress. Deletion is extension-owned: the host keeps the row until the extension replies with an update (row removed) or an error. Pickers are disposed cleanly on extension-initiated close and on engine crash/restart. The bundled /workflow resume picker now REQUIRES this capability — its remote-rendered ctx.ui.custom() selector path was removed with no fallback, and hosts without the capability fail the resume command with one actionable error. The member is absent only on non-interactive surfaces (headless RPC, print). See docs/tui.md and docs/extensions.md.

Added

  • Added private compaction-planner diagnostic sidecars for failed persisted-session planner calls. Malformed output, unusable ranges, provider errors, and stream failures now save the full response text plus stop reason, usage, request output budget, and non-secret model metadata beside the session JSONL with 0600 permissions where supported; the compaction error reports that path. Credentials, headers, and request prompts are excluded, while in-memory sessions and sidecar write failures preserve the original RangePlanError unchanged.
  • Replaced the compaction planner output format from JSON {"d":[[start,end],...]} with bare start,end records (one per line, no brackets/fences/prose). The new format is ~1% more token-efficient at equivalent range counts and enables trivial newline-based length-truncation recovery.
  • Added silent recovery of complete deletion records from length-truncated compaction planner responses. When the model output is cut by max_tokens (stopReason: "length"), a deterministic line parser recovers newline-terminated start,end records, discards the final unterminated fragment (never guesses multi-digit integers), and passes results through normal validation. Recovery is silent: no warning, banner, or special status—UI shows the normal ✻ Context compacted message. A private 0600 recovery-diagnostic sidecar is written beside persisted sessions for operational observability (never surfaced in UI). The planner prompt now instructs the model to emit ranges in descending deletion confidence so the highest-priority deletions survive truncation.
  • Added six bundled, composable pattern workflow definitions — classify-and-act, fan-out-and-synthesize, adversarial-verification, generate-and-filter, tournament, and loop-until-done — with typed/defaulted inputs, dedicated artifact-aware stage prompts, declared outputs, named discovery, and @bastani/workflows/builtin exports for nested ctx.workflow(...) composition.
  • Added ctx.ui.hostInputForm(request), a JSON-safe host-native structured-form capability implemented identically by isolated and non-isolated interactive hosts. The terminal host owns inline rendering, focus, validation, configured keybindings, and editing state; under engine isolation only open and submit/cancel semantic events cross the protocol boundary. Headless RPC and print surfaces omit the optional member.
  • Added /workflows [run-id] as a retained workflow-run alias for /workflow resume, with confirmed backend-aware deletion for inactive durable/completed rows that protects in-flight runs and preserves independent session transcripts.
  • Added supervised interactive-engine isolation: extension tools, hooks, commands, workflow code, and custom UI rendering now run in a child process so a synchronous busy loop cannot freeze terminal input, spinners, or rendering. A 50 ms heartbeat watchdog identifies blocked callbacks after 250 ms, exposes interrupt/termination guidance after one second, and reports terminated results as unknown without automatic retry. JSON-safe ctx.ui.custom() components are remotely rendered with asynchronous input forwarding; unsupported synchronous host callback APIs warn instead of falling back in-process.
  • Added a typed, allowlisted host-terminal control channel for isolated ctx.ui.custom() components. An overlay factory's tui.terminal now exposes setMouseScrollTracking(enabled) and setAutowrap(enabled) setters that the terminal host applies to the real host TTY over the engine protocol — the engine child (whose stdout is the JSONL transport, not a TTY) can enable SGR mouse-scroll reporting and Windows autowrap without ever forwarding raw child bytes to the terminal. Controls are component/generation scoped and every enabled mode is reset when the overlay hides, closes, is disposed, or the engine child crashes/restarts. See docs/tui.md.
  • Added live-updating, semantically colored rows to the bundled /workflow resume picker: completed rows render green, paused yellow, and failed/blocked/crashed red (SessionInfo.messageColor now also accepts "error"). Rows re-list on run-store changes and a bounded cross-session poll while the picker is open, and running workflows are never offered as resume targets — a fresh-heartbeat running row is hidden from every session, and only stale (crashed) ones surface, labeled crashed.
  • Added atomic update --models to force-refresh authenticated dynamic provider catalogs with bounded, provider-scoped persistence and actionable failures (#1875).

Changed

  • Promoted the DBOS SDK and embedded-postgres to mandatory dependencies. Atomic configures and launches DBOS lazily on the first workflow action, awaits readiness before every workflow lifecycle path, and surfaces initialization or persistence failures for that action instead of continuing on another backend. Without DBOS_SYSTEM_DATABASE_URL, workflow durability runs on an embedded Postgres from npm-distributed binaries (detached daemon under ~/.atomic/postgres, shared across sessions; Docker dbos-db container only as a platform fallback). Concurrent Atomic sessions sharing one database use per-process executor identities, owner/heartbeat metadata, and first-writer-wins resume claims so one session's live workflow cannot be double-dispatched from another.
  • Documented and enforced /resume-equivalent workflow-history retention: eligible runs are not filtered or garbage-collected by age/count, and the selector viewport does not limit search/navigation.
  • Kept the interactive-engine watchdog's early 250 ms blocking signal internal instead of rendering repetitive "has not yielded" warnings during ordinary slow extension/tool loading. One-second unresponsive heartbeat-watchdog diagnostics are likewise always kept internal, whether or not they attribute a callback (for example, extension.hook tool_execution_end); the isolated host remains responsive and still provides Escape/Ctrl+C recovery controls. Concrete engine failures such as termination and RPC errors continue to surface as chat errors.
  • Changed verbatim compaction to classify the complete active transcript except for exactly the newest preserve_recent context-visible messages (default 2). The protected tail is no longer widened to a user-turn boundary, preserve_recent: 0 now retains no ordinary message, and repeated compaction includes the prior durable verbatim summary while preserving resumable session boundaries.
  • Redesigned the /workflow <name> input form (shown when a named workflow is launched with required inputs missing). The form now renders inside a rounded, theme-aware panel titled WORKFLOW INPUTS with the workflow name and field count, matching the workflow catalogue's visual language: each field shows a focus chevron, an accent field name when active, and a right-aligned required/optional badge (required in the warning color); descriptions use the readable muted tone, validation errors render as a ✗ reason row, the submit control is a [ Run workflow ] button pill (filled accent when focused), and the footer uses accent key chips. Multi-line text inputs blend their inset rules into the panel border, the panel scales to the full terminal width like the other chat surfaces, and the embedded editor cursor stays correctly positioned.
  • Migrated model authentication and refresh to Pi's provider-owned APIs while preserving Atomic's public AuthStorage, ModelRegistry, SDK options, legacy extension OAuth registration, complete request headers, and credential-specific enterprise endpoints. The model picker now shows cached models immediately and reports concise success, partial-error, and timeout status (#1875).

Fixed

  • Fixed the repository publish-release workflow to reconcile an exact release PR merged externally while required checks are pending. It preserves identity/refs/SHA; correlates workflow-qualified Actions reruns by name plus workflow; and supports empty-workflow StatusContext and GitHub App CheckRun evidence. Linked reruns group by inferred kind/name across URL changes; linkless rows inspect both external kinds, accept all-passing candidates, block any pending/failure, and exclude nonempty-workflow Actions. Duplicate aliases reuse exact passing evidence. It rechecks after merge and validates merge/branch evidence. Tag recovery proves verified merge → tag parent → current base; exhaustive history avoids GitHub's 1,000-result ceiling; protected coordination retains its lock through ambiguous dispatch visibility; and recovered success requires exact-SHA integrity evidence.
  • Fixed shared extension chat compaction rendering so manual, threshold, and overflow compaction use the animated working spinner with reason-aware copy instead of a duplicate plain status row plus generic Working...; successful compaction now falls back to the existing typed ✻ Context compacted message when a refreshed live session snapshot is unavailable, while preserving durable session reconstruction and avoiding duplicate boundaries.
  • Fixed workflow stage-chat /compact cancellation and planner/provider failures from escaping their fire-and-forget editor submission promise and terminating the CLI. The authoritative compaction_end event now owns the visible status, animation cleanup, and diagnostic path while the same stage remains usable for retry or follow-up.
  • Fixed OpenAI Responses, Codex Responses, and OpenAI Completions context accounting to sum their normalized uncached and cached input partitions even when the values are nearly equal. Anthropic Messages retains its mirrored-cache guard, preventing missed auto-compaction thresholds, understated footer usage, and negative persisted reduction percentages on Codex sessions.
  • Fixed workflow-stage message admission races by adding a linearizable AgentSession generation boundary. Intercom traffic and async bash/subagent completions received before close now use native queued steering/follow-up delivery and drain before terminal stage publication; close-winning detached results route once to the main chat without reopening or mutating the completed stage. Detached producers remain non-blocking, stable producer identities prevent duplicate delivery, and explicit StageContext.sendUserMessage plus post-mortem stage chat remain available.
  • Fixed asynchronous extension custom-message admission to return its delivery promise through pi.sendMessage() and pi.sendMessages(), allowing workflow late-message routers to propagate main-chat failures and release stable producer keys for retry instead of treating rejected routes as successful.
  • Fixed bundled workflow stages freezing when an active foreground subagent used Intercom or blocking contact_supervisor. Busy stages give the exact child owner's acknowledged detach handshake first refusal before model-visible stage-session queue insertion; owner loss between probe/commit falls back instead of dropping the broker message. In parallel foreground groups, one accepted commit releases aggregate supervision for all active siblings while their child processes and eventual result/status recovery remain owned; run-owned worktrees are retained until every detached child closes and their diffs are captured.
  • Fixed bundled workflow-stage intercom.ask delivery when the target sibling is mid-turn in an in-flight tool call. The open generation now owns the ask before asynchronous foreground-owner coordination begins, waits for its native queue insertion, and drains it before terminal publication; genuinely failed destination admission returns a correlated actionable error rather than consuming the full reply timeout.
  • Fixed /workflow <name> input forms becoming non-responsive under interactive-engine isolation. The RPC extension surface advertised setEditorComponent as a callable compatibility stub even though it could not install a host editor, so the workflow command waited on an editor that was never mounted while Tab and Escape stayed routed to the ordinary prompt. Named workflow forms now prefer the host-native input-form channel, keeping Tab/Shift+Tab, arrows, editing, configured keybindings, Enter, Escape, and Ctrl+C local and responsive in both interactive modes; cancellation does not dispatch the workflow, inline bottom-editor geometry is preserved, and legacy custom-editor/ctx.ui.custom() fallbacks remain available for older hosts.
  • Fixed Ctrl+O (app.tools.expand) being consumed without expanding tool and workflow-node detail in attached workflow stage chats under interactive-engine isolation. The child RPC UI facade retains one expansion state shared by its getter and chat render settings, and the remote custom-UI bridge now filters Kitty key-release events unless the child component explicitly opts into them, preventing one physical Ctrl+O press from immediately toggling expansion back off. Repeated expand/collapse toggles match main chat across active and completed runs, single/parallel/chain views, and narrow terminals without changing prompt, custom-UI, or unrelated key ownership.
  • Fixed bundled workflow-stage intercom.ask calls hanging when the addressed sibling stage had already completed. Atomic now schedules a post-mortem turn in the completed target's retained conversation, preserves the exact child-to-child reply thread, prevents parent or unrelated sessions from satisfying it, and returns bounded actionable errors for unavailable or non-resumable retained targets. Production workflow and Intercom late-message listeners preserve the first owner's completion promise regardless of registration order, so later or duplicate listeners cannot turn revival failures into silent no-ops (#1854).
  • Fixed tool results being able to push the active Pi loop past Atomic's buffered compaction threshold before its next provider request. Atomic now performs one ordinary verbatim compaction preflight after tool results enter the prospective next-turn context, returns the rebuilt protocol-valid context to the same loop without scheduling agent.continue(), leaves below-threshold turns unchanged, surfaces compaction cancellation/failure, and blocks a follow-up request that remains known to exceed the provider hard input limit.
  • Fixed the interactive TUI dropping its working spinner after successful post-tool autocompaction while the same agent stream continued. The normal working indicator is now restored immediately at the mid-turn compaction boundary, without waiting for the user's next interaction.
  • Fixed bundled workflow stage messaging to distinguish idle sessions from active streams: idle followUp/eligible auto sends now start a real prompt and report prompt, streaming follow-ups queue and steering stays in-turn, paused idle-chat resume(message) starts exactly one recorded turn, and interrupted-turn resume, abort/exit guards, and at-most-once continuation behavior remain intact (#1850).
  • Fixed Escape during isolated-engine compaction timing out with Timeout waiting for response to abort_compaction. RPC input now keeps ordinary commands FIFO while validated cancellation and host-response control frames use an independent lane, so abort_compaction reaches the active engine session before the pending compact request settles; duplicate and late aborts remain harmless, and ordinary command ordering is preserved.
  • Fixed interactive non-schema workflow stages treating typed/freeform ask_user_question chat answers as an invisible implicit “stay”. The conversational acknowledgement now completes first, then a real brokered readiness gate exposes awaiting_input/inputRequest.kind: "readiness_gate"; in that chat flow, Not ready permits another stage-chat turn and re-prompts, while Ready releases dependents. Structured-option behavior remains unchanged, and successful schema finalization stays terminal. (#1849)
  • Fixed the bundled workflow BACKGROUND panel's elapsed timer remaining at 0s until the user switched to the orchestrator. Visible running cards now repaint their existing mounted component on exact one-second boundaries from workflow start, without panel navigation or widget remounting; paused timers remain frozen and ended cards retain their one-shot expiry lifecycle.
  • Fixed the interactive TUI crashing with Rendered line N exceeds terminal width (uncaught exception in pi-tui doRender) when the terminal was resized — most visibly when shrinking an active session with chat history (e.g. 112 → 83 columns) or fuzz-resizing mid-turn. Under the isolated interactive engine, tool cards and custom messages render out-of-process and asynchronously: after a resize they kept replaying the previous frame wrapped for the old width until the engine child delivered a re-wrapped one, so pi-tui's differential renderer could see lines wider than the terminal and abort the whole session. Remote frames are now defensively clamped to the current render width while the engine catches up, so grow/shrink resizing (including rapid adversarial flapping) never emits an overflowing line; properly re-wrapped frames still replace the clamped ones as soon as they arrive.
  • Fixed the startup banner logo wrapping and shredding into misaligned art on narrow terminals: the identity header is now width-aware, keeping the side-by-side logo/meta layout only when every combined row fits, stacking the identity text (version, provider/model, cwd) under the logo when the meta column would wrap, and dropping the logo art entirely when the terminal is narrower than the logo itself. The layout re-adapts on every terminal resize.
  • Fixed adjacent assistant thinking blocks rendering as separate sections, Windows package checks leaving npm's terminal title behind, standalone Bun binaries omitting interactive OAuth adapters, and clone/fork attempts before the first assistant response returning an unhelpful failure (#1875).
  • Stopped the isolated interactive engine's extension-level requestRender() from invalidating hidden remote overlay components (#1856). The broadcast now skips components whose remote OverlayHandle is hidden (setHidden(true)/hide()), so widget-local updates do not trigger host render work for hidden graph/stage frames; shown-again components rejoin the broadcast immediately.
  • Reduced keypress latency for all remotely rendered extension custom UI (workflow inputs forms, graph overlays, and other ctx.ui.custom() components) under the isolated interactive engine. The host now pipelines a fresh frame request directly behind every forwarded keypress — engine commands are delivered in order, so the returned frame always reflects the applied input. This cuts the previous input → child-invalidate → render-request → frame path to a single round trip and repaints components that change state on input without self-invalidating.
  • Fixed the mouse wheel scrolling native/main-chat scrollback instead of the workflow graph when a durable workflow overlay was resumed under the isolated interactive engine. The overlay adapter runs inside the engine child, whose stdout is the JSONL transport rather than a TTY, so its process.stdout mouse-tracking escape sequences never put the real host terminal into mouse-reporting mode. The overlay now enables host mouse-scroll reporting (and Windows autowrap) through the new typed host-terminal control channel, so wheel gestures reach the remote GraphView and change graph scroll offsets while a visible overlay captures the mouse; keyboard navigation, stage-chat wheel capture, and Ctrl+T copy mode are unchanged. Resume selection deterministically disposes the inline picker before the fullscreen overlay mounts and takes focus, so a late picker cleanup can no longer steal focus or leave the graph as an inline/bottom component. Non-isolated hosts keep their local process.stdout behavior.
  • Fixed /resume and /workflow resume intermittently failing with Timeout waiting for response to prompt when the picker (or any long-lived interactive prompt) stayed open past 30 seconds. The isolated-engine RPC client now exempts long-lived interactive commands — prompts and custom-UI pickers, queued steer/follow-up sends, long shell and compaction work, fork/clone, session switch/new/import, tree navigation, and shortcut invocation — from the generic 30-second request deadline. Process exit, transport violations, abort, and generation replacement still reject pending requests immediately, and bounded metadata/control requests keep a (now injectable) deadline, so real failure detection is unchanged.
  • Made the /resume session selector mount and paint its header, search, and loading state on the very first frame, then discover and parse sessions off the host UI loop. Directory scans run in bounded cooperative batches and a single very large transcript is parsed in yielding chunks, so input, search, and cancel stay responsive and one large session can no longer visibly freeze the picker. Closing the selector now cancels in-flight loads and ignores stale results, preventing late list updates after close, scope switch, or a newer load.
  • Fixed a startup input race where typing a command-like draft such as a bare / before the header finished loading was reclassified as a submitted cooked-mode command and sent automatically. Raw startup capture now remains authoritative: only Enter-terminated submissions replay, while unfinished slash/bash drafts stay in the editor.
  • Fixed startup changelog and first-run onboarding notices being gated behind the deferred extension reload — and, when a prompt was typed immediately at launch, behind the entire first agent turn. They now render right after the input handler is ready (milliseconds after first paint), matching pi's behavior; the RESOURCES disclosure still waits for the actual extension load it reports on.
  • Reduced deferred extension-load stalls by yielding to the event loop between extension loads only when the current turn has actually run long (≥16 ms) instead of unconditionally — the previous unconditional yields cost a full macrotask turn (~100 ms each while the TUI is live) per bundled extension (~0.5 s of the deferred load).
  • Hardened the isolated interactive engine's runtime and transport. Best-effort RPC rejections are now centrally contained so a fire-and-forget path can no longer crash the host; Escape/cancel recovery is generation-fenced so a cancelled turn survives, the host stays alive, and the engine child is cleanly restarted. The child is the sole authoritative writer of transcript/settings state (host snapshots are side-effect-free, and model/thinking/name operations persist exactly once), and a guardian tracks the full process tree so forced host death leaves no orphaned engine or detached grandchild.
  • Hardened the interactive-engine JSONL transport against unbounded growth and frame loss. Framing is UTF-8 byte-bounded (not UTF-16 char counts), writers in both directions are byte-accounted with backpressure, update coalescing happens before serialization, and terminal/correlated frames are either delivered or fail immediately with an explicit protocol error and same-id rejection instead of a 30-second timeout.
  • Fixed the isolated interactive engine omitting extension slash commands (/workflow, its /workflows alias, /run, /mcp, and others) from autocomplete. Because the host session loads no extensions in the interactive-engine child model, those commands live only in the engine child; the host now fetches the child's command catalog asynchronously after engine bind (never delaying first paint or input), merges it into autocomplete with built-in names reserved and locally-present prompts/skills deduped, and re-fetches after engine restart, reload, and new/resume/fork. Command execution continues to route through the child with no duplicate host handling.
  • Fixed expandable skill, resource, built-in tool, MCP, and subagent headers losing their effective keybinding in the isolated interactive engine (for example, [skill] tmux ( Expand)). Child rendering and injected custom UI now share one reloadable Atomic keybinding manager, so the default displays ctrl+o, remaps display the configured key, and intentionally unbound expansion omits the unavailable shortcut affordance without malformed punctuation.
  • Fixed the TUI flickering with rapid full repaints (~58 Hz, plus idle CPU burn on both processes) whenever a tool card rendered under the isolated interactive engine during a streaming turn — most visibly when the workflow tool's quit/pause action ran while a workflow stage was streaming, though any tool call (e.g. a plain bash echo) reproduced it. The engine child's remote tool-card renderer disposed and recreated the ToolExecutionComponent on every engine_tool_render request, and re-seeding the fresh component unconditionally scheduled a render on its off-screen TUI whose terminal write emitted engine_custom_invalidate back to the host, which marked the card dirty and re-sent the render request — forever. The engine render service now reuses the cached component per remote component id (applying args/result/expansion/image-option updates in place) and the seeding setters (markExecutionStarted/setArgsComplete) are idempotent, so the loop converges after at most one invalidate round trip while legitimate async invalidations (such as image format conversion) still repaint.