@bastani/atomic 0.9.9-alpha.3 → 0.9.9
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.
- package/CHANGELOG.md +39 -0
- package/dist/builtin/cursor/CHANGELOG.md +12 -0
- package/dist/builtin/cursor/package.json +2 -2
- package/dist/builtin/intercom/CHANGELOG.md +18 -0
- package/dist/builtin/intercom/package.json +1 -1
- package/dist/builtin/mcp/CHANGELOG.md +13 -0
- package/dist/builtin/mcp/package.json +1 -1
- package/dist/builtin/subagents/CHANGELOG.md +18 -0
- package/dist/builtin/subagents/package.json +1 -1
- package/dist/builtin/subagents/src/extension/startup-maintenance.ts +2 -1
- package/dist/builtin/web-access/CHANGELOG.md +13 -0
- package/dist/builtin/web-access/package.json +1 -1
- package/dist/builtin/workflows/CHANGELOG.md +36 -0
- package/dist/builtin/workflows/README.md +2 -0
- package/dist/builtin/workflows/package.json +1 -1
- package/dist/builtin/workflows/src/extension/extension-runtime-state.ts +102 -48
- package/dist/builtin/workflows/src/extension/render-result.ts +11 -2
- package/dist/builtin/workflows/src/extension/workflow-command-registration.ts +11 -8
- package/dist/builtin/workflows/src/extension/workflow-command-surfaces.ts +23 -0
- package/dist/builtin/workflows/src/extension/workflow-reload-coordinator.ts +66 -0
- package/dist/builtin/workflows/src/extension/workflow-reload-report.ts +39 -0
- package/dist/builtin/workflows/src/extension/workflow-run-control-command.ts +47 -25
- package/dist/builtin/workflows/src/extension/workflow-targets.ts +0 -10
- package/dist/builtin/workflows/src/extension/workflow-tool-control.ts +21 -17
- package/dist/builtin/workflows/src/extension/workflow-tool.ts +2 -1
- package/docs/workflows.md +9 -1
- package/npm-shrinkwrap.json +23 -23
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,45 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [0.9.9] - 2026-07-15
|
|
6
|
+
|
|
7
|
+
### Breaking Changes
|
|
8
|
+
|
|
9
|
+
- Replaced deletion-target context compaction with durable verbatim line compaction. Legacy `context_compaction` entries are now inert archival records, so content they previously hid may re-enter context when old sessions resume; start a new conversation or compact again to establish a new boundary.
|
|
10
|
+
- Removed the deprecated `contextCompact()` SDK method, `context_compact` RPC command, and `context_compaction_start`/`context_compaction_end` events. Use `compact()` and `compaction_start`/`compaction_end`.
|
|
11
|
+
- Changed extension compaction hooks: `session_before_compact` now accepts `cancel` or a complete non-empty `compactedText` override instead of `deletionRequest`; `session_compact` exposes `VerbatimCompactionResult` and the saved `compactionEntry`.
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- Added one-pass contextual line-ranking compaction: Atomic asks the active session model at its active reasoning level, through the normal session stream/provider wrapper, for one whole-region compact `{"d":[[start,end],...]}` deletion plan, then safety-normalizes ranges and mechanically reconstructs verbatim text with cumulative `(filtered N lines)` markers.
|
|
16
|
+
- Added strict pi-style compaction failure semantics: oversized prompts, provider/API errors or overflow, aborts, malformed JSON, and empty/unusable ranges fail without persistence or continuation and without semantic retries, chunking, critical replans, deterministic fallback, or deterministic target correction.
|
|
17
|
+
- Added durable pi-style `CompactionEntry` boundaries discriminated by `details.strategy: "verbatim-lines"`, plus resumable custom-role boundary messages and a collapsible TUI card showing retained-line and token-reduction statistics.
|
|
18
|
+
- Added `pi.sendMessages()` for atomic, array-ordered custom-message admission without waiting for the resulting model turn, allowing companion extensions to keep related preludes and terminal notices contiguous without globally serializing unrelated work.
|
|
19
|
+
|
|
20
|
+
### Changed
|
|
21
|
+
|
|
22
|
+
- Changed `compression_ratio` to explicitly represent the fraction of compactable lines to keep. `preserve_recent` is enforced client-side, widens the tail to a user-turn start, and always protects the final logical turn. Role headers are now ordinary ranked lines; only explicit protected spans are split out of model-selected deletion ranges.
|
|
23
|
+
- Simplified resume reconstruction to load the persisted compacted string followed by original messages from `firstKeptEntryId`; no deletion filters, signed-thinking repair pass, or tool dependency repair is re-derived.
|
|
24
|
+
- Retuned the one-pass compaction classifier's retention guidance to surgically thin long tool results, preserve compact diagnostic/code/state anchors, prune repeated retry and superseded bodies, and treat formatting, stack position, markers, and query matches as contextual rather than hard categories.
|
|
25
|
+
- Updated the Pi runtime dependency set (`@earendil-works/pi-agent-core`, `pi-ai`, and `pi-tui`) from `^0.80.6` to `^0.80.7` across Atomic and its bundled packages, with matching Bun and npm lock metadata. This inherits the upstream provider authentication, session-affinity, reasoning replay, tool-choice, model-catalog, terminal input, and prompt-cache fixes from [Pi v0.80.7](https://github.com/earendil-works/pi/releases/tag/v0.80.7) while preserving versionless `0.0.0` workspace manifests.
|
|
26
|
+
- Consolidated ten closed Dependabot updates into the Pi v0.80.7 branch: `linkedom` 0.18.13, npm `ignore` 7.0.6, `@typescript/native-preview` 7.0.0-dev.20260707.2, `typebox` 1.3.6, TypeScript 7.0.2, and the native dependency refreshes documented in `@bastani/atomic-natives`; regenerated the Bun, npm, and publish shrinkwrap dependency graphs without changing workspace package versions.
|
|
27
|
+
- Added an Atomic `StringEnum` export that preserves Pi's Google-compatible enum schema while bridging Pi 0.80.7's TypeBox identity to the direct TypeBox 1.3.6 types; updated bundled extension examples and documentation to use the portable export, and updated TypeScript fixture configs for TypeScript 7's removal of `baseUrl`.
|
|
28
|
+
|
|
29
|
+
### Fixed
|
|
30
|
+
|
|
31
|
+
- Fixed resumed sessions resurrecting records hidden by earlier Verbatim Compaction entries after later compactions changed signed-thinking turn or tool-call/result dependencies. Persisted logical deletions are now an authoritative lower bound during every context rebuild: Atomic closes unsafe replay structures by adding transient omissions instead of restoring compacted calls/results, pairs each result with its concrete call occurrence so independent exchanges may reuse an opaque call ID, leaves unrelated retained history unchanged, and keeps the append-only session file intact.
|
|
32
|
+
- Fixed successful manual and automatic compaction UI refreshes to render the compacted transcript with exactly one visible `✻ Context compacted` boundary, while aborted and failed compactions remain unchanged.
|
|
33
|
+
- Fixed the bundled workflows `/workflow resume` experience to mix successful completed workflows into the existing globally newest-first, deduplicated picker with green completed styling, resolve full IDs and prefixes across live, durable, and completed targets, and reopen retained stage chats for follow-up without re-running workflow code or replaying side effects. Completed durable state is retained for authoritative inspection; rows need checkpoints and at least one strictly valid retained conversation, invalid per-stage transcript paths cannot open chat, repeated inspection refreshes changed authoritative chat handles, and selector mount failures close safely. ([#1532](https://github.com/bastani-inc/atomic/issues/1532))
|
|
34
|
+
- Fixed workflow-owned transcripts leaking into normal `/resume`, `-r`, `-c`, and `--continue` history by requiring complete workflow ownership markers, persisting classification in initial fork headers, inheriting it across branches, and keeping malformed legacy markers and ordinary user forks visible.
|
|
35
|
+
- Preserved accepted async-child Intercom chronology across lazily activated companion extensions by atomically admitting same-child ordinary messages before pause, completion, and failure notices; unrelated children remain independent and ask/reply behavior is unchanged ([#1802](https://github.com/bastani-inc/atomic/issues/1802)).
|
|
36
|
+
- Fixed in-process workflow reload to atomically publish freshly rescanned project, user, legacy, configured, and package workflow resources across list/get/inputs/help/completion/invocation surfaces. Overlapping requests are serialized and coalesced, stale session generations cannot overwrite current state, fatal refresh failures retain the prior registry, and reload no longer blocks or changes workflows already in flight. `/workflow reload` and the workflow tool now include actionable per-resource diagnostics instead of reporting bare success when malformed or missing resources were skipped.
|
|
37
|
+
|
|
38
|
+
## [0.9.9-alpha.4] - 2026-07-15
|
|
39
|
+
|
|
40
|
+
### Fixed
|
|
41
|
+
|
|
42
|
+
- Fixed in-process workflow reload to atomically publish freshly rescanned project, user, legacy, configured, and package workflow resources across list/get/inputs/help/completion/invocation surfaces. Overlapping requests are serialized and coalesced, stale session generations cannot overwrite current state, fatal refresh failures retain the prior registry, and reload no longer blocks or changes workflows already in flight. `/workflow reload` and the workflow tool now include actionable per-resource diagnostics instead of reporting bare success when malformed or missing resources were skipped.
|
|
43
|
+
|
|
5
44
|
## [0.9.9-alpha.3] - 2026-07-14
|
|
6
45
|
|
|
7
46
|
### Added
|
|
@@ -2,6 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [0.9.9] - 2026-07-15
|
|
6
|
+
|
|
7
|
+
### Changed
|
|
8
|
+
|
|
9
|
+
- Published the stable Atomic 0.9.9 release for the Cursor provider package with its `@earendil-works/pi-ai` dependency aligned to `^0.80.7`; no functional Cursor provider source changes were made after 0.9.8.
|
|
10
|
+
|
|
11
|
+
## [0.9.9-alpha.4] - 2026-07-15
|
|
12
|
+
|
|
13
|
+
### Changed
|
|
14
|
+
|
|
15
|
+
- Published a synchronized Atomic 0.9.9-alpha.4 prerelease for the Cursor provider package; no functional Cursor provider changes were made after 0.9.9-alpha.3.
|
|
16
|
+
|
|
5
17
|
## [0.9.9-alpha.3] - 2026-07-14
|
|
6
18
|
|
|
7
19
|
### Changed
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bastani/cursor",
|
|
3
|
-
"version": "0.9.9
|
|
3
|
+
"version": "0.9.9",
|
|
4
4
|
"private": true,
|
|
5
5
|
"description": "Experimental first-party Atomic extension for Cursor OAuth, model discovery, and streaming provider registration.",
|
|
6
6
|
"contributors": [
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
}
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
|
-
"@bastani/atomic-natives": "0.9.9
|
|
43
|
+
"@bastani/atomic-natives": "0.9.9",
|
|
44
44
|
"@bufbuild/protobuf": "^2.12.1",
|
|
45
45
|
"@earendil-works/pi-ai": "^0.80.7"
|
|
46
46
|
}
|
|
@@ -4,6 +4,24 @@ All notable changes to the `pi-intercom` extension will be documented in this fi
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [0.9.9] - 2026-07-15
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
- Aligned the intercom extension peer dependency with `@earendil-works/pi-tui` `^0.80.7` as part of the consolidated Pi v0.80.7 dependency update; no intercom source changes were needed.
|
|
12
|
+
- Updated the runtime `typebox` range to `^1.3.6`.
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
|
|
16
|
+
- Preserved per-child chronology between busy-parent Intercom messages and terminal async subagent notifications: paused, completed, and failed paths now claim pre-terminal ordinary messages from the exact child session/run in FIFO order and atomically admit the prelude plus terminal notice through the custom-message batch API. A process-local bridge complements extension-bus delivery for lazily activated companions; terminal identity deduplicates both paths even when the successful terminal dispatch has an empty prelude, while failed dispatches remain retryable and distinct resumed-run lifecycle terminals remain independent. Idle flushes use the same ordered batch admission, unrelated children remain independent, and ask/reply correlation is unchanged ([#1802](https://github.com/bastani-inc/atomic/issues/1802)).
|
|
17
|
+
- Made every short session ID printed by `intercom list` directly actionable in `send`, blocking `ask`, and targeted `reply`. Target resolution now preserves exact full IDs and exact case-insensitive names before accepting unique ID prefixes, reports colliding prefixes with the matching sessions instead of guessing, resolves blocking asks to the full sender ID so threaded replies correlate correctly, and rejects short-form self-targets at both tool and broker routing boundaries.
|
|
18
|
+
|
|
19
|
+
## [0.9.9-alpha.4] - 2026-07-15
|
|
20
|
+
|
|
21
|
+
### Changed
|
|
22
|
+
|
|
23
|
+
- Published a synchronized Atomic 0.9.9-alpha.4 prerelease for the intercom extension; no functional intercom changes were made after 0.9.9-alpha.3.
|
|
24
|
+
|
|
7
25
|
## [0.9.9-alpha.3] - 2026-07-14
|
|
8
26
|
|
|
9
27
|
### Fixed
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bastani/intercom",
|
|
3
|
-
"version": "0.9.9
|
|
3
|
+
"version": "0.9.9",
|
|
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": [
|
|
@@ -7,6 +7,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.9.9] - 2026-07-15
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- Aligned the MCP extension peer dependencies with `@earendil-works/pi-ai` and `@earendil-works/pi-tui` `^0.80.7` as part of the consolidated Pi v0.80.7 dependency update; no MCP source changes were needed.
|
|
15
|
+
- Updated the runtime `typebox` range to `^1.3.6`.
|
|
16
|
+
|
|
17
|
+
## [0.9.9-alpha.4] - 2026-07-15
|
|
18
|
+
|
|
19
|
+
### Changed
|
|
20
|
+
|
|
21
|
+
- Published a synchronized Atomic 0.9.9-alpha.4 prerelease for the MCP extension; no functional MCP changes were made after 0.9.9-alpha.3.
|
|
22
|
+
|
|
10
23
|
## [0.9.9-alpha.3] - 2026-07-14
|
|
11
24
|
|
|
12
25
|
### Changed
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bastani/mcp",
|
|
3
|
-
"version": "0.9.9
|
|
3
|
+
"version": "0.9.9",
|
|
4
4
|
"private": true,
|
|
5
5
|
"description": "Atomic extension that adapts MCP (Model Context Protocol) servers into the coding agent. Fork of: https://github.com/nicobailon/pi-mcp-adapter",
|
|
6
6
|
"contributors": [
|
|
@@ -2,6 +2,24 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [0.9.9] - 2026-07-15
|
|
6
|
+
|
|
7
|
+
### Changed
|
|
8
|
+
|
|
9
|
+
- Aligned the subagents extension peer dependencies with the `@earendil-works/pi-agent-core`, `pi-ai`, and `pi-tui` `^0.80.7` runtime set as part of the consolidated Pi v0.80.7 dependency update; no subagent source changes were needed.
|
|
10
|
+
- Updated the runtime `typebox` range to `^1.3.6`.
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- Fixed first-party subagent transcripts launched inside workflow stages to inherit complete workflow ownership metadata in foreground and background execution, while fork-context children also inherit classification through the branched JSONL header.
|
|
15
|
+
- Preserved background async-child completion chronology with Intercom by emitting a terminal-ordering barrier and atomically batching claimed same-child messages before the completion notice, with a compatibility fallback for hosts without batched message admission.
|
|
16
|
+
|
|
17
|
+
## [0.9.9-alpha.4] - 2026-07-15
|
|
18
|
+
|
|
19
|
+
### Changed
|
|
20
|
+
|
|
21
|
+
- Published a synchronized Atomic 0.9.9-alpha.4 prerelease for the subagents extension; no functional subagent changes were made after 0.9.9-alpha.3.
|
|
22
|
+
|
|
5
23
|
## [0.9.9-alpha.3] - 2026-07-14
|
|
6
24
|
|
|
7
25
|
### Fixed
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bastani/subagents",
|
|
3
|
-
"version": "0.9.9
|
|
3
|
+
"version": "0.9.9",
|
|
4
4
|
"private": true,
|
|
5
5
|
"description": "Atomic extension for delegating tasks to subagents with chains, parallel execution, and background runs. Fork of: https://github.com/nicobailon/pi-subagents",
|
|
6
6
|
"contributors": [
|
|
@@ -39,6 +39,7 @@ export function createSubagentStartupMaintenance(
|
|
|
39
39
|
resultsDir: string;
|
|
40
40
|
artifactCleanupDays: number;
|
|
41
41
|
resultTtlMs: number;
|
|
42
|
+
scheduleMacrotask?: (task: () => void) => () => void;
|
|
42
43
|
},
|
|
43
44
|
): SubagentStartupMaintenance {
|
|
44
45
|
const { startResultWatcher, primeExistingResults, stopResultWatcher } = createResultWatcher(
|
|
@@ -49,7 +50,7 @@ export function createSubagentStartupMaintenance(
|
|
|
49
50
|
);
|
|
50
51
|
const cancelTasks = new Set<() => void>();
|
|
51
52
|
const schedule = (task: () => void): void => {
|
|
52
|
-
const cancel = scheduleMacrotask(() => {
|
|
53
|
+
const cancel = (options.scheduleMacrotask ?? scheduleMacrotask)(() => {
|
|
53
54
|
cancelTasks.delete(cancel);
|
|
54
55
|
task();
|
|
55
56
|
});
|
|
@@ -4,6 +4,19 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [0.9.9] - 2026-07-15
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
- Aligned the web-access extension peer dependency with `@earendil-works/pi-tui` `^0.80.7` as part of the consolidated Pi v0.80.7 dependency update; no web-access source changes were needed.
|
|
12
|
+
- Updated `linkedom` from 0.18.12 to 0.18.13 and refreshed its selector dependency graph.
|
|
13
|
+
|
|
14
|
+
## [0.9.9-alpha.4] - 2026-07-15
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
|
|
18
|
+
- Published a synchronized Atomic 0.9.9-alpha.4 prerelease for the web-access extension; no functional web-access changes were made after 0.9.9-alpha.3.
|
|
19
|
+
|
|
7
20
|
## [0.9.9-alpha.3] - 2026-07-14
|
|
8
21
|
|
|
9
22
|
### Changed
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bastani/web-access",
|
|
3
|
-
"version": "0.9.9
|
|
3
|
+
"version": "0.9.9",
|
|
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": [
|
|
@@ -6,6 +6,42 @@ 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.9] - 2026-07-15
|
|
10
|
+
|
|
11
|
+
### Changed
|
|
12
|
+
|
|
13
|
+
- Hardened the shared builtin `goal`/`ralph` prompt contracts against over-implementation drift (observed in eval traces where near-perfect patches lost binary reward to a single invented strictness: returning a read-only mapping proxy where the contract said "dict", marking an optional schema field required, and rejecting duplicate aliases the task's hidden tests expected to be tolerated). The literal objective contract now states that the loud-error preference applies only to error conditions the contract enumerates and that the default for unenumerated behavior is permissive acceptance (never invent a validation error, required field, uniqueness constraint, or rejection the contract does not name); that contract-named types/shapes/formats must be produced exactly, with no defensive substitutes such as read-only proxies, frozen collections, or tuples-for-lists, because consumers may check type identity literally; and that unspecified behavior defaults to preserving input verbatim over normalizing, deduplicating, reordering, or rewriting it.
|
|
14
|
+
- Extended the worker/orchestrator acceptance-matrix contract with rows for literal contract examples (checked character-for-character rather than paraphrased) and for each contract-constrained interface decision (types by identity, required-vs-optional per field, duplicate handling, ordering, raw-vs-normalized text), recording the permissive/preserving default whenever the contract leaves a decision open.
|
|
15
|
+
- Added a shared `CONTRACT_FIDELITY_AUDIT` adversarial divergence pass to the Goal worker and Ralph orchestrator prompts (`<divergence_audit>`): after checks are green and before claiming readiness, re-read each contract clause asking what plausible independent check would fail the implementation, probing the recurring divergence categories — type-identity assertions, optional fields omitted, duplicated/aliased inputs, ordering assumptions, verbatim-vs-normalized text, and any raised error the contract does not enumerate. Forked continuation prompts reference the audit in their established-guidance pointer.
|
|
16
|
+
- Made Goal and Ralph reviewers hunt over-implementation as seriously as gaps via a shared `REVIEWER_OVERIMPLEMENTATION_GUARD`: validation errors, required fields, uniqueness/format constraints, immutability wrappers, or normalization the contract does not require are `required_by_objective` defects, and reviewers must probe at least one contract-permitted input the worker's own tests do not exercise before approving. The independent-verification contract and reviewer action items now include contract-permitted-input and type/shape/text-identity probes alongside the existing boundary/edge/negative probes.
|
|
17
|
+
- Added a shared Intercom coordination protocol to every concurrent builtin Goal and Ralph reviewer prompt: reviewers now check Intercom and discover same-run siblings at review start, communicate validation plans and ownership, serialize conflicting shared-checkout or shared-environment checks, announce starts/completions/releases, share reusable execution evidence, and retain independent patch analysis and verdicts ([#1803](https://github.com/bastani-inc/atomic/issues/1803)).
|
|
18
|
+
- Aligned the workflows extension peer dependency with `@earendil-works/pi-tui` `^0.80.7` as part of the consolidated Pi v0.80.7 dependency update; no workflow source changes were needed.
|
|
19
|
+
- Updated the runtime `typebox` range to `^1.3.6`.
|
|
20
|
+
|
|
21
|
+
### Fixed
|
|
22
|
+
|
|
23
|
+
- Fixed the builtin `open-claude-design` artifact directory fallback to use a per-user OS tmpdir namespace (`open-claude-design-<username>`), preventing `EACCES` failures on shared hosts where another user already owns the plain `<tmpdir>/open-claude-design` directory. The legacy shared path remains a secondary fallback, and the last-resort synthesized path now also uses the per-user namespace so best-effort feedback persistence can actually create it.
|
|
24
|
+
- Prevented the synced Impeccable fixture's nested `git init` from inheriting hook-local Git repository variables and corrupting a linked repository's shared `core.worktree`. Runner-managed reusable worktrees now remap propagated invoking-repository `cwd` values and relative direct outputs into the selected worktree; reject blank, self, nested, foreign, lexical-escape, symlink-escape, and changed cached targets before opening a later session; key cached identity by canonical repository/target across equivalent path and ref spellings; revalidate newly created targets; and expose clearer workflow tool guidance/schema descriptions so natural-language worktree requests cannot be mistaken for runtime configuration. Temporary direct task and parallel worktrees also derive omitted/relative task cwd values from the runner invocation cwd and clean up when startup fails before their workflow callback, while relative outputs are persisted to stable runner-owned artifacts before cleanup and reject parent traversal, child symlink escapes, or a linked trusted artifact root across direct, parallel, and chain modes; blank `chainDir` values no longer bypass reusable-worktree output routing.
|
|
25
|
+
- Fixed the active Workflow Orchestrator graph pane to pan left and right for horizontal trackpad and terminal mouse-wheel events while preserving vertical wheel panning and pane-local input capture ([#1756](https://github.com/bastani-inc/atomic/issues/1756)).
|
|
26
|
+
- Fixed `/workflow resume` so its single globally newest-first, deduplicated picker includes current paused or recoverably failed and durable resumable entries alongside authoritative successful completed workflows. Completed rows use green `✓ completed` styling and open an immutable detail/chat snapshot with transcript follow-up conversation, without durable re-dispatch or workflow side-effect replay. Full IDs take precedence and prefixes resolve across the mixed namespace; completed rows require checkpoints and at least one strictly valid retained conversation, invalid per-stage paths cannot attach chat, repeated inspection refreshes changed authoritative chat handles, and selector mount failures close safely. Existing resumable workflow and ordinary internal-session history behavior remains on the original paths. ([#1532](https://github.com/bastani-inc/atomic/issues/1532))
|
|
27
|
+
- Fixed `/workflow resume` to deduplicate live and durable choices with live-row precedence, then globally sort the combined selector by latest run/stage or durable-update activity, newest first, with deterministic workflow-id tie-breaking independent of source enumeration order ([#1714](https://github.com/bastani-inc/atomic/issues/1714)).
|
|
28
|
+
- Fixed startup and restored-session workflow notices to use the authoritative durable resume catalog's status/progress rules: failed and blocked root workflows are advertised unless explicitly marked `resumable: false`, running and paused workflows require durable progress, nested child workflows and stale cache-only entries are excluded, and legacy direct or payload-wrapped checkpoint entries are parsed correctly.
|
|
29
|
+
- Fixed durable mid-stage workflow resume to refresh pause-adjusted active stage/task duration at repeated session checkpoints, preserve it through file and DBOS hydration, and continue accumulating it across repeated process-boundary resumes without resetting the baseline when concurrent tracked stage calls begin, double-counting earlier pauses, changing replay identity, or re-running completed side effects ([#1713](https://github.com/bastani-inc/atomic/issues/1713)).
|
|
30
|
+
- Fixed incompatible durable workflow records appearing as loadable resume targets by moving file state, DBOS metadata/checkpoint envelopes, and JSONL discovery caches to one shared v2 format contract; exact legacy v1 file and DBOS rows are now suppressed and permanently deleted (including DBOS checkpoint-prefix records), with per-workflow deletion and loadability queries implemented across durable backends and safely isolated from child scopes. Resume selectors, explicit target resolution, completed catalogs, restored-session notices, status/listing state, cache merges, and live resume paths accept only loadable current-format rows; compatibility preparation purges matching restored/live snapshots even when DBOS deletion fails, so failure fallbacks cannot resurrect or dispatch them. Repeated discovery is idempotent, deletion failures stay hidden and retry later, compatible and newly registered runs plus unmarked raw DBOS checkpoint outputs remain supported, and malformed, unavailable, future-version, or unsupported marked-envelope data is conservatively preserved and hidden rather than overwritten, reinterpreted, or broadly deleted.
|
|
31
|
+
- Fixed workflow stage session classification so fresh and fork-context stages persist complete ownership metadata before their transcripts can enter normal resume history, including custom session directories.
|
|
32
|
+
- Made `workflow({ action: "status", runId })` apply the same exact-or-unique-prefix contract as the other run actions. A prefix shared by multiple retained runs now returns the standard ambiguity diagnostic instead of silently inspecting the first match, so abbreviated run IDs printed by status surfaces remain safe and actionable.
|
|
33
|
+
- Fixed workflow resource reload to build and atomically publish a fresh registry for additions, edits, renames, deletions, config paths, conventional/legacy directories, and package resources without restarting Atomic. Reloads now serialize and coalesce, reject stale session generations, retain the active registry on fatal failures, remain safe during in-flight runs, and return visible config/discovery diagnostics through both slash-command and tool surfaces while preserving valid siblings.
|
|
34
|
+
- Fixed exact paused top-level live `/workflow resume <id>` targets to resume before enumerating retained completed durable history, preserving live-over-durable precedence and keeping headless command output responsive when the durable catalog is large, while exact nested child IDs remain excluded from the top-level resume namespace. Slash-dispatch tests now isolate durable state in memory and restore the backend after each test so they never read or populate a user's workflow history.
|
|
35
|
+
- Isolated lazy-startup continuation tests with fresh in-memory durable backends and a completed-catalog regression guard, preventing test runs from enumerating or populating the user's real durable workflow history while preserving failed-run resource-loading coverage.
|
|
36
|
+
|
|
37
|
+
## [0.9.9-alpha.4] - 2026-07-15
|
|
38
|
+
|
|
39
|
+
### Fixed
|
|
40
|
+
|
|
41
|
+
- Fixed workflow resource reload to build and atomically publish a fresh registry for additions, edits, renames, deletions, config paths, conventional/legacy directories, and package resources without restarting Atomic. Reloads now serialize and coalesce, reject stale session generations, retain the active registry on fatal failures, remain safe during in-flight runs, and return visible config/discovery diagnostics through both slash-command and tool surfaces while preserving valid siblings.
|
|
42
|
+
- Fixed exact paused top-level live `/workflow resume <id>` targets to resume before enumerating retained completed durable history, preserving live-over-durable precedence and keeping headless command output responsive when the durable catalog is large, while exact nested child IDs remain excluded from the top-level resume namespace. Slash-dispatch tests now isolate durable state in memory and restore the backend after each test so they never read or populate a user's workflow history.
|
|
43
|
+
- Isolated lazy-startup continuation tests with fresh in-memory durable backends and a completed-catalog regression guard, preventing test runs from enumerating or populating the user's real durable workflow history while preserving failed-run resource-loading coverage.
|
|
44
|
+
|
|
9
45
|
## [0.9.9-alpha.3] - 2026-07-14
|
|
10
46
|
|
|
11
47
|
### Fixed
|
|
@@ -29,6 +29,8 @@ Adding workflow files under `.atomic/workflows/` (project scope) or `~/.atomic/a
|
|
|
29
29
|
}
|
|
30
30
|
```
|
|
31
31
|
|
|
32
|
+
After Atomic is running, use `/workflow reload` or the workflow tool's `reload` action to rescan all workflow sources in process. Additions, edits, renames, deletions, config changes, and package-resource changes become visible immediately to list/get/inputs/help/completion/invocation surfaces. Reload requests are serialized/coalesced and publish a complete replacement registry; an in-flight workflow keeps its original definition while later calls use the new registry. Fatal refresh failures retain the prior registry, and skipped malformed or missing resources are reported with actionable diagnostics while valid siblings remain available.
|
|
33
|
+
|
|
32
34
|
### Workflow lifecycle notifications
|
|
33
35
|
|
|
34
36
|
Workflow lifecycle notices are enabled by default. They send steer prompts into the main chat/model context when a run completes, fails, or ends blocked. Awaiting-input prompts are tracked for dedupe/restore, but they do not wake the main chat agent. Configure lifecycle tracking in the same extension config file:
|
|
@@ -36,7 +36,11 @@ import {
|
|
|
36
36
|
} from "./hil-answer-notifications.js";
|
|
37
37
|
import type { ExtensionAPI, PiModelContext } from "./public-types.js";
|
|
38
38
|
import { makeMcpPort, makePersistencePort } from "./workflow-ports.js";
|
|
39
|
-
import {
|
|
39
|
+
import { createWorkflowReloadCoordinator } from "./workflow-reload-coordinator.js";
|
|
40
|
+
import {
|
|
41
|
+
workflowReloadDiagnostics,
|
|
42
|
+
type WorkflowReloadReport,
|
|
43
|
+
} from "./workflow-reload-report.js";
|
|
40
44
|
|
|
41
45
|
export interface WorkflowExtensionRuntimeState {
|
|
42
46
|
persistenceRef: { current: WorkflowPersistencePort | undefined };
|
|
@@ -49,8 +53,8 @@ export interface WorkflowExtensionRuntimeState {
|
|
|
49
53
|
runtimeForContext(ctx?: PiModelContext): ExtensionRuntime;
|
|
50
54
|
resetWorkflowDiscoveryForSession(): void;
|
|
51
55
|
ensureWorkflowConfigLoaded(): Promise<void>;
|
|
52
|
-
ensureWorkflowResourcesLoaded(
|
|
53
|
-
reloadWorkflowResources(
|
|
56
|
+
ensureWorkflowResourcesLoaded(): Promise<void>;
|
|
57
|
+
reloadWorkflowResources(): Promise<WorkflowReloadReport>;
|
|
54
58
|
startWorkflowDiscoveryWarmup(onSettled?: () => void): void;
|
|
55
59
|
runWithLifecycleSuppressedForPolicy<T>(policy: WorkflowExecutionPolicy, fn: () => Promise<T>): Promise<T>;
|
|
56
60
|
setNotificationsActive(active: boolean): void;
|
|
@@ -198,10 +202,9 @@ export function createWorkflowExtensionRuntimeState(
|
|
|
198
202
|
});
|
|
199
203
|
}
|
|
200
204
|
|
|
201
|
-
let
|
|
202
|
-
|
|
203
|
-
let lazyDiscoveryPromise: Promise<void> | null = null;
|
|
205
|
+
let lazyDiscoveryPromise: Promise<WorkflowReloadReport> | null = null;
|
|
204
206
|
let workflowDiscoveryGeneration = 0;
|
|
207
|
+
let activeResourceGeneration = 0;
|
|
205
208
|
function deferToMacrotask(): Promise<void> {
|
|
206
209
|
return new Promise((resolve) => setImmediate(resolve));
|
|
207
210
|
}
|
|
@@ -245,7 +248,6 @@ export function createWorkflowExtensionRuntimeState(
|
|
|
245
248
|
workflowDiscoveryGeneration += 1;
|
|
246
249
|
discoveryRef.current = null;
|
|
247
250
|
lazyDiscoveryPromise = null;
|
|
248
|
-
workflowReloadQueue = Promise.resolve();
|
|
249
251
|
rebuildRuntime(startupDiscovery.registry);
|
|
250
252
|
}
|
|
251
253
|
|
|
@@ -257,13 +259,36 @@ export function createWorkflowExtensionRuntimeState(
|
|
|
257
259
|
rebuildRuntime();
|
|
258
260
|
}
|
|
259
261
|
|
|
260
|
-
function
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
262
|
+
function supersededReloadReport(
|
|
263
|
+
coalescedRequests: number,
|
|
264
|
+
configResult?: ConfigLoadResult,
|
|
265
|
+
result?: DiscoveryResult,
|
|
266
|
+
): WorkflowReloadReport {
|
|
267
|
+
return {
|
|
268
|
+
outcome: "superseded",
|
|
269
|
+
generation: activeResourceGeneration,
|
|
270
|
+
workflowCount: runtimeRef.current.registry.names().length,
|
|
271
|
+
coalescedRequests,
|
|
272
|
+
diagnostics: workflowReloadDiagnostics(configResult?.diagnostics ?? [], result?.errors ?? []),
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
function failedReloadReport(
|
|
277
|
+
error: unknown,
|
|
278
|
+
coalescedRequests: number,
|
|
279
|
+
configResult?: ConfigLoadResult,
|
|
280
|
+
): WorkflowReloadReport {
|
|
281
|
+
return {
|
|
282
|
+
outcome: "failed",
|
|
283
|
+
error: error instanceof Error ? error.message : String(error),
|
|
284
|
+
generation: activeResourceGeneration,
|
|
285
|
+
workflowCount: runtimeRef.current.registry.names().length,
|
|
286
|
+
coalescedRequests,
|
|
287
|
+
diagnostics: workflowReloadDiagnostics(configResult?.diagnostics ?? [], []),
|
|
288
|
+
};
|
|
264
289
|
}
|
|
265
290
|
|
|
266
|
-
function trackLazyDiscovery(pending: Promise<
|
|
291
|
+
function trackLazyDiscovery(pending: Promise<WorkflowReloadReport>): Promise<WorkflowReloadReport> {
|
|
267
292
|
lazyDiscoveryPromise = pending;
|
|
268
293
|
void pending.finally(() => {
|
|
269
294
|
if (lazyDiscoveryPromise === pending) lazyDiscoveryPromise = null;
|
|
@@ -271,49 +296,74 @@ export function createWorkflowExtensionRuntimeState(
|
|
|
271
296
|
return pending;
|
|
272
297
|
}
|
|
273
298
|
|
|
274
|
-
function reloadWorkflowResources(
|
|
275
|
-
|
|
276
|
-
return trackLazyDiscovery(queueWorkflowResourceReload(options, generation));
|
|
299
|
+
function reloadWorkflowResources(): Promise<WorkflowReloadReport> {
|
|
300
|
+
return trackLazyDiscovery(reloadCoordinator.request(workflowDiscoveryGeneration));
|
|
277
301
|
}
|
|
278
302
|
async function loadPackageWorkflowPaths(): Promise<string[]> {
|
|
279
303
|
const packageResources = (await pi.refreshWorkflowResources?.()) ?? pi.getWorkflowResources?.() ?? [];
|
|
280
304
|
return packageResources.filter((resource) => resource.enabled !== false).map((resource) => resource.path);
|
|
281
305
|
}
|
|
282
|
-
async function reloadWorkflowResourcesNow(
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
console.warn(`Workflow reload bypassed in-flight guard with ${activeRuns} active run(s).`);
|
|
306
|
+
async function reloadWorkflowResourcesNow(
|
|
307
|
+
discoveryGeneration: number,
|
|
308
|
+
coalescedRequests: number,
|
|
309
|
+
): Promise<WorkflowReloadReport> {
|
|
310
|
+
if (!isWorkflowDiscoveryCurrent(discoveryGeneration)) {
|
|
311
|
+
return supersededReloadReport(coalescedRequests);
|
|
289
312
|
}
|
|
290
313
|
const configResult = await loadWorkflowConfig();
|
|
291
|
-
if (!isWorkflowDiscoveryCurrent(
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
314
|
+
if (!isWorkflowDiscoveryCurrent(discoveryGeneration)) {
|
|
315
|
+
return supersededReloadReport(coalescedRequests, configResult);
|
|
316
|
+
}
|
|
317
|
+
try {
|
|
318
|
+
const hasGlobal = configResult.globalConfig != null;
|
|
319
|
+
const hasProject = configResult.projectConfig != null;
|
|
320
|
+
const discoveryConfig = hasGlobal || hasProject
|
|
321
|
+
? toScopedDiscoveryConfig(configResult.globalConfig ?? null, configResult.projectConfig ?? null, { projectRoot: process.cwd() })
|
|
322
|
+
: undefined;
|
|
323
|
+
const packageWorkflowPaths = await loadPackageWorkflowPaths();
|
|
324
|
+
if (!isWorkflowDiscoveryCurrent(discoveryGeneration)) {
|
|
325
|
+
return supersededReloadReport(coalescedRequests, configResult);
|
|
326
|
+
}
|
|
327
|
+
const result = await discoverWorkflows({ config: discoveryConfig, packageWorkflowPaths });
|
|
328
|
+
if (!isWorkflowDiscoveryCurrent(discoveryGeneration)) {
|
|
329
|
+
return supersededReloadReport(coalescedRequests, configResult, result);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// Commit config, diagnostics, and the replacement runtime synchronously,
|
|
333
|
+
// after every fallible/awaited discovery step has completed.
|
|
334
|
+
applyWorkflowConfig(configResult);
|
|
335
|
+
discoveryRef.current = result;
|
|
336
|
+
rebuildRuntime(result.registry);
|
|
337
|
+
activeResourceGeneration += 1;
|
|
338
|
+
return {
|
|
339
|
+
outcome: "applied",
|
|
340
|
+
generation: activeResourceGeneration,
|
|
341
|
+
workflowCount: result.registry.names().length,
|
|
342
|
+
coalescedRequests,
|
|
343
|
+
diagnostics: workflowReloadDiagnostics(configResult.diagnostics, result.errors),
|
|
344
|
+
};
|
|
345
|
+
} catch (error) {
|
|
346
|
+
return failedReloadReport(error, coalescedRequests, configResult);
|
|
347
|
+
}
|
|
304
348
|
}
|
|
305
349
|
|
|
306
|
-
|
|
350
|
+
const reloadCoordinator = createWorkflowReloadCoordinator(async (discoveryGeneration, coalescedRequests) => {
|
|
351
|
+
try {
|
|
352
|
+
return await reloadWorkflowResourcesNow(discoveryGeneration, coalescedRequests);
|
|
353
|
+
} catch (error) {
|
|
354
|
+
return failedReloadReport(error, coalescedRequests);
|
|
355
|
+
}
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
async function ensureWorkflowResourcesLoaded(): Promise<void> {
|
|
307
359
|
while (!pi.disableAsyncDiscovery && discoveryRef.current === null) {
|
|
308
|
-
const
|
|
309
|
-
const pending = lazyDiscoveryPromise
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
if (!isWorkflowDiscoveryCurrent(
|
|
314
|
-
if (discoveryRef.current === null)
|
|
315
|
-
lazyDiscoveryPromise = null;
|
|
316
|
-
}
|
|
360
|
+
const discoveryGeneration = workflowDiscoveryGeneration;
|
|
361
|
+
const pending = lazyDiscoveryPromise
|
|
362
|
+
?? trackLazyDiscovery(reloadCoordinator.request(discoveryGeneration));
|
|
363
|
+
const report = await pending;
|
|
364
|
+
if (report.outcome === "failed") throw new Error(report.error);
|
|
365
|
+
if (!isWorkflowDiscoveryCurrent(discoveryGeneration)) continue;
|
|
366
|
+
if (discoveryRef.current === null) lazyDiscoveryPromise = null;
|
|
317
367
|
}
|
|
318
368
|
}
|
|
319
369
|
|
|
@@ -321,10 +371,14 @@ export function createWorkflowExtensionRuntimeState(
|
|
|
321
371
|
if (pi.disableAsyncDiscovery || discoveryRef.current !== null || lazyDiscoveryPromise !== null) return;
|
|
322
372
|
const notificationStart = notificationGeneration;
|
|
323
373
|
const discoveryStart = workflowDiscoveryGeneration;
|
|
324
|
-
const pending = (async () => {
|
|
374
|
+
const pending = (async (): Promise<WorkflowReloadReport> => {
|
|
325
375
|
await deferToMacrotask();
|
|
326
|
-
if (!isWorkflowDiscoveryCurrent(discoveryStart))
|
|
327
|
-
|
|
376
|
+
if (!isWorkflowDiscoveryCurrent(discoveryStart)) {
|
|
377
|
+
return supersededReloadReport(1);
|
|
378
|
+
}
|
|
379
|
+
const report = await reloadCoordinator.request(discoveryStart);
|
|
380
|
+
if (report.outcome === "failed") throw new Error(report.error);
|
|
381
|
+
return report;
|
|
328
382
|
})();
|
|
329
383
|
lazyDiscoveryPromise = pending;
|
|
330
384
|
const isCurrentWarmup = (): boolean => lazyDiscoveryPromise === pending
|
|
@@ -16,6 +16,7 @@ import type { WorkflowDetails } from "../shared/types.js";
|
|
|
16
16
|
import type { RunDetail } from "../runs/background/status.js";
|
|
17
17
|
import { renderInputsSchema } from "../shared/render-inputs-schema.js";
|
|
18
18
|
import { renderStatusList } from "../tui/status-list.js";
|
|
19
|
+
import type { WorkflowReloadReport } from "./workflow-reload-report.js";
|
|
19
20
|
import { renderRunDetail } from "../tui/run-detail.js";
|
|
20
21
|
import { renderWorkflowList } from "../tui/workflow-list.js";
|
|
21
22
|
import { deriveGraphTheme } from "../tui/graph-theme.js";
|
|
@@ -23,6 +24,7 @@ import { renderDispatchConfirm } from "../tui/dispatch-confirm.js";
|
|
|
23
24
|
import type { WorkflowInputValues, WorkflowOutputValues } from "../shared/types.js";
|
|
24
25
|
import { renderRoundedBox } from "../tui/chat-surface.js";
|
|
25
26
|
import { truncateToWidth } from "../tui/text-helpers.js";
|
|
27
|
+
import { wrapTextWithAnsi } from "@earendil-works/pi-tui";
|
|
26
28
|
|
|
27
29
|
// ---------------------------------------------------------------------------
|
|
28
30
|
// Result variants
|
|
@@ -138,7 +140,7 @@ type TranscriptResult = {
|
|
|
138
140
|
};
|
|
139
141
|
type SendResult = { action: "send"; runId: string; stageId: string; delivery: string; status: "ok" | "noop"; message: string };
|
|
140
142
|
type PauseResult = { action: "pause"; runId: string; status: string; message: string };
|
|
141
|
-
type ReloadResult = { action: "reload"; status: "ok" | "noop"; message: string };
|
|
143
|
+
type ReloadResult = WorkflowReloadReport & { action: "reload"; status: "ok" | "noop"; message: string };
|
|
142
144
|
type InterruptResult = { action: "interrupt"; runId: string; status: string; message: string };
|
|
143
145
|
type KillResult = { action: "kill"; runId: string; status: string; message: string };
|
|
144
146
|
type ResumeResult = { action: "resume"; runId: string; status: string; message: string };
|
|
@@ -201,6 +203,13 @@ function fitLine(line: string, width?: number): string {
|
|
|
201
203
|
return truncateToWidth(line, width, "…");
|
|
202
204
|
}
|
|
203
205
|
|
|
206
|
+
function noticeBodyLines(message: string, width?: number): string[] {
|
|
207
|
+
return message.split(/\r?\n/).flatMap((line) => {
|
|
208
|
+
if (line.length === 0 || width === undefined || width <= 0) return [line];
|
|
209
|
+
return wrapTextWithAnsi(line, width);
|
|
210
|
+
}).map((line) => ` ${line} `);
|
|
211
|
+
}
|
|
212
|
+
|
|
204
213
|
function renderNotice(
|
|
205
214
|
title: string,
|
|
206
215
|
message: string,
|
|
@@ -212,7 +221,7 @@ function renderNotice(
|
|
|
212
221
|
const contentWidth = width && width > 0 ? Math.max(1, width - 4) : undefined;
|
|
213
222
|
return renderRoundedBox({
|
|
214
223
|
title,
|
|
215
|
-
bodyLines:
|
|
224
|
+
bodyLines: noticeBodyLines(message, contentWidth),
|
|
216
225
|
theme,
|
|
217
226
|
width,
|
|
218
227
|
});
|
|
@@ -23,22 +23,25 @@ import {
|
|
|
23
23
|
type WorkflowCommandHandler,
|
|
24
24
|
type WorkflowCommandOutputDetails,
|
|
25
25
|
} from "./workflow-command-utils.js";
|
|
26
|
-
import {
|
|
26
|
+
import {
|
|
27
|
+
emitTerminalRunDetailSurface,
|
|
28
|
+
formatWorkflowReloadReport,
|
|
29
|
+
formatWorkflowResourceLoadWarning,
|
|
30
|
+
} from "./workflow-command-surfaces.js";
|
|
27
31
|
import { handleRunControlCommand, type WorkflowRunControlDeps } from "./workflow-run-control-command.js";
|
|
28
32
|
import { workflowPolicyFromContext } from "./workflow-policy.js";
|
|
29
33
|
import {
|
|
30
|
-
inFlightRunCount,
|
|
31
|
-
reloadBlockedMessage,
|
|
32
34
|
reloadFailureMessage,
|
|
33
35
|
resolveRunIdPrefix,
|
|
34
36
|
overlaySurfaceFromContext,
|
|
35
37
|
} from "./workflow-targets.js";
|
|
38
|
+
import { normalizeWorkflowReloadReport, type WorkflowReloadReport } from "./workflow-reload-report.js";
|
|
36
39
|
|
|
37
40
|
export interface WorkflowSlashCommandDeps {
|
|
38
41
|
runtimeProxy: ExtensionRuntime;
|
|
39
42
|
runtimeForContext: (ctx?: PiCommandContext) => ExtensionRuntime;
|
|
40
43
|
overlay: GraphOverlayPort;
|
|
41
|
-
reloadWorkflowResources: () => Promise<void> | void;
|
|
44
|
+
reloadWorkflowResources: () => Promise<WorkflowReloadReport | void> | void;
|
|
42
45
|
ensureWorkflowResourcesLoaded: () => Promise<void> | void;
|
|
43
46
|
runWithLifecycleSuppressedForPolicy: <T>(
|
|
44
47
|
policy: WorkflowExecutionPolicy,
|
|
@@ -141,11 +144,11 @@ async function workflowSlashHandler(
|
|
|
141
144
|
return;
|
|
142
145
|
}
|
|
143
146
|
if (subcommand === "reload") {
|
|
144
|
-
const activeRuns = inFlightRunCount();
|
|
145
|
-
if (activeRuns > 0) return fail(reloadBlockedMessage(activeRuns));
|
|
146
147
|
try {
|
|
147
|
-
await deps.reloadWorkflowResources();
|
|
148
|
-
|
|
148
|
+
const report = normalizeWorkflowReloadReport(await deps.reloadWorkflowResources());
|
|
149
|
+
const message = formatWorkflowReloadReport(report);
|
|
150
|
+
if (report.outcome === "applied") print(message);
|
|
151
|
+
else fail(message);
|
|
149
152
|
} catch (error) {
|
|
150
153
|
fail(reloadFailureMessage(error));
|
|
151
154
|
}
|
|
@@ -7,6 +7,7 @@ import type { DiscoveryResult } from "./discovery.js";
|
|
|
7
7
|
import type { WorkflowToolResult } from "./render-result.js";
|
|
8
8
|
import type { ExtensionAPI } from "./public-types.js";
|
|
9
9
|
import { isRunStatus } from "./workflow-targets.js";
|
|
10
|
+
import type { WorkflowReloadReport } from "./workflow-reload-report.js";
|
|
10
11
|
|
|
11
12
|
function fallbackRunDetailFromResult(
|
|
12
13
|
workflowName: string,
|
|
@@ -54,6 +55,28 @@ export function formatWorkflowResourceLoadWarning(error: unknown): string {
|
|
|
54
55
|
"Using the currently loaded workflow registry; run `/workflow reload` after fixing the issue.",
|
|
55
56
|
].join("\n");
|
|
56
57
|
}
|
|
58
|
+
|
|
59
|
+
export function formatWorkflowReloadReport(report: WorkflowReloadReport, reason?: string): string {
|
|
60
|
+
const reasonSuffix = reason?.trim() ? ` (${reason.trim()})` : "";
|
|
61
|
+
const legacySuccess = report.generation === 0 && report.workflowCount === 0 && report.diagnostics.length === 0;
|
|
62
|
+
const headline = report.outcome === "applied"
|
|
63
|
+
? legacySuccess
|
|
64
|
+
? `Reloaded workflow resources${reasonSuffix}.`
|
|
65
|
+
: `Reloaded workflow resources${reasonSuffix}. ${report.workflowCount} workflow(s), generation ${report.generation}.`
|
|
66
|
+
: report.outcome === "failed"
|
|
67
|
+
? `Workflow resources could not be refreshed; the current registry was retained: ${report.error}`
|
|
68
|
+
: "Workflow reload was superseded by a newer session; no stale resources were applied.";
|
|
69
|
+
if (report.diagnostics.length === 0) return headline;
|
|
70
|
+
|
|
71
|
+
const lines = report.diagnostics.map((diagnostic) =>
|
|
72
|
+
`- [${diagnostic.level} ${diagnostic.code}] ${diagnostic.source ?? `workflow ${diagnostic.phase}`}: ${diagnostic.message}`
|
|
73
|
+
);
|
|
74
|
+
return [
|
|
75
|
+
headline,
|
|
76
|
+
`Workflow discovery diagnostics (${report.diagnostics.length}): some resources were skipped or need attention.`,
|
|
77
|
+
...lines,
|
|
78
|
+
].join("\n");
|
|
79
|
+
}
|
|
57
80
|
export function formatStartupDiagnostics(
|
|
58
81
|
configResult: ConfigLoadResult | null,
|
|
59
82
|
discoveryResult: DiscoveryResult | null,
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { WorkflowReloadReport } from "./workflow-reload-report.js";
|
|
2
|
+
|
|
3
|
+
type ReloadBatch = {
|
|
4
|
+
readonly discoveryGeneration: number;
|
|
5
|
+
requestCount: number;
|
|
6
|
+
readonly promise: Promise<WorkflowReloadReport>;
|
|
7
|
+
readonly resolve: (report: WorkflowReloadReport) => void;
|
|
8
|
+
readonly reject: (error: Error) => void;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export interface WorkflowReloadCoordinator {
|
|
12
|
+
request(discoveryGeneration: number): Promise<WorkflowReloadReport>;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function createBatch(discoveryGeneration: number): ReloadBatch {
|
|
16
|
+
let resolve: (report: WorkflowReloadReport) => void = () => undefined;
|
|
17
|
+
let reject: (error: Error) => void = () => undefined;
|
|
18
|
+
const promise = new Promise<WorkflowReloadReport>((resolvePromise, rejectPromise) => {
|
|
19
|
+
resolve = resolvePromise;
|
|
20
|
+
reject = (error) => rejectPromise(error);
|
|
21
|
+
});
|
|
22
|
+
return { discoveryGeneration, requestCount: 0, promise, resolve, reject };
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Runs one reload at a time. Requests coalesce only with the latest queued pass
|
|
27
|
+
* from the same session generation; generation boundaries remain ordered.
|
|
28
|
+
*/
|
|
29
|
+
export function createWorkflowReloadCoordinator(
|
|
30
|
+
reload: (discoveryGeneration: number, coalescedRequests: number) => Promise<WorkflowReloadReport>,
|
|
31
|
+
): WorkflowReloadCoordinator {
|
|
32
|
+
const pending: ReloadBatch[] = [];
|
|
33
|
+
let draining = false;
|
|
34
|
+
|
|
35
|
+
const drain = async (): Promise<void> => {
|
|
36
|
+
if (draining) return;
|
|
37
|
+
draining = true;
|
|
38
|
+
try {
|
|
39
|
+
while (pending.length > 0) {
|
|
40
|
+
const batch = pending.shift()!;
|
|
41
|
+
try {
|
|
42
|
+
const report = await reload(batch.discoveryGeneration, batch.requestCount);
|
|
43
|
+
batch.resolve(report);
|
|
44
|
+
} catch (error) {
|
|
45
|
+
batch.reject(error instanceof Error ? error : new Error(String(error)));
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
} finally {
|
|
49
|
+
draining = false;
|
|
50
|
+
if (pending.length > 0) void drain();
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
return {
|
|
55
|
+
request(discoveryGeneration): Promise<WorkflowReloadReport> {
|
|
56
|
+
let batch = pending.at(-1);
|
|
57
|
+
if (batch?.discoveryGeneration !== discoveryGeneration) {
|
|
58
|
+
batch = createBatch(discoveryGeneration);
|
|
59
|
+
pending.push(batch);
|
|
60
|
+
}
|
|
61
|
+
batch.requestCount += 1;
|
|
62
|
+
queueMicrotask(() => void drain());
|
|
63
|
+
return batch.promise;
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { ConfigDiagnostic } from "./config-loader.js";
|
|
2
|
+
import type { DiscoveryDiagnostic } from "./discovery.js";
|
|
3
|
+
|
|
4
|
+
export type WorkflowReloadDiagnostic =
|
|
5
|
+
| (ConfigDiagnostic & { readonly phase: "config" })
|
|
6
|
+
| (DiscoveryDiagnostic & { readonly phase: "discovery" });
|
|
7
|
+
|
|
8
|
+
type WorkflowReloadReportState = {
|
|
9
|
+
readonly generation: number;
|
|
10
|
+
readonly workflowCount: number;
|
|
11
|
+
readonly coalescedRequests: number;
|
|
12
|
+
readonly diagnostics: WorkflowReloadDiagnostic[];
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export type WorkflowReloadReport = WorkflowReloadReportState & (
|
|
16
|
+
| { readonly outcome: "applied" }
|
|
17
|
+
| { readonly outcome: "failed"; readonly error: string }
|
|
18
|
+
| { readonly outcome: "superseded" }
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
export function normalizeWorkflowReloadReport(report: WorkflowReloadReport | void): WorkflowReloadReport {
|
|
22
|
+
return report ?? {
|
|
23
|
+
outcome: "applied",
|
|
24
|
+
generation: 0,
|
|
25
|
+
workflowCount: 0,
|
|
26
|
+
coalescedRequests: 1,
|
|
27
|
+
diagnostics: [],
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function workflowReloadDiagnostics(
|
|
32
|
+
configDiagnostics: readonly ConfigDiagnostic[],
|
|
33
|
+
discoveryDiagnostics: readonly DiscoveryDiagnostic[],
|
|
34
|
+
): WorkflowReloadDiagnostic[] {
|
|
35
|
+
return [
|
|
36
|
+
...configDiagnostics.map((diagnostic) => ({ ...diagnostic, phase: "config" as const })),
|
|
37
|
+
...discoveryDiagnostics.map((diagnostic) => ({ ...diagnostic, phase: "discovery" as const })),
|
|
38
|
+
];
|
|
39
|
+
}
|
|
@@ -288,35 +288,57 @@ export async function handleRunControlCommand(
|
|
|
288
288
|
} else if (action === "resume") {
|
|
289
289
|
const backend = getDurableBackend();
|
|
290
290
|
const exactBeforePreparation = store.runs().find((run) => run.id === target);
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
const
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
} catch (error) {
|
|
301
|
-
preparationError = error instanceof Error ? error.message : String(error);
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
const loadableRuns = topLevelWorkflowRuns(store.runs()).filter((run) => backend.isWorkflowLoadable(run.id));
|
|
305
|
-
const combined = resolveWorkflowResumeTarget(target, loadableRuns, durable, backend.listCompletedWorkflows());
|
|
306
|
-
if (combined.kind === "ambiguous") {
|
|
307
|
-
fail(`Ambiguous workflow prefix "${target}" matches: ${combined.matches.map((match) => `${match.name} (${match.workflowId.slice(0, 8)})`).join(", ")}`);
|
|
291
|
+
const exactHasPausedState = exactBeforePreparation?.status === "paused"
|
|
292
|
+
|| (exactBeforePreparation?.stages.some((stage) => stage.status === "paused") ?? false);
|
|
293
|
+
const exactIsActivelyRunning = exactBeforePreparation !== undefined
|
|
294
|
+
&& exactBeforePreparation.endedAt === undefined
|
|
295
|
+
&& exactBeforePreparation.status === "running"
|
|
296
|
+
&& !exactHasPausedState
|
|
297
|
+
&& exactBeforePreparation.exitReason !== "quit";
|
|
298
|
+
if (exactIsActivelyRunning) {
|
|
299
|
+
fail(`Workflow ${exactBeforePreparation.id.slice(0, 8)} is already running in this session. Attach with \`/workflow connect ${exactBeforePreparation.id.slice(0, 8)}\` instead of resuming.`);
|
|
308
300
|
return true;
|
|
309
301
|
}
|
|
310
|
-
if (
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
302
|
+
if (exactBeforePreparation !== undefined
|
|
303
|
+
&& exactBeforePreparation.parentRunId === undefined
|
|
304
|
+
&& exactHasPausedState
|
|
305
|
+
&& backend.isWorkflowLoadable(exactBeforePreparation.id)) {
|
|
306
|
+
// Exact top-level live state is authoritative. Avoid scanning the
|
|
307
|
+
// potentially large completed catalog while preserving the established
|
|
308
|
+
// top-level target namespace and live-over-durable precedence.
|
|
309
|
+
runId = exactBeforePreparation.id;
|
|
310
|
+
} else {
|
|
311
|
+
let durable: readonly ResumableWorkflowEntry[] = [];
|
|
312
|
+
let preparationError: string | undefined;
|
|
313
|
+
const needsDurablePreparation = exactBeforePreparation === undefined
|
|
314
|
+
|| (!backend.isWorkflowLoadable(exactBeforePreparation.id)
|
|
315
|
+
&& (!exactHasPausedState || backend.hydrateResumableWorkflows !== undefined));
|
|
316
|
+
if (needsDurablePreparation) {
|
|
317
|
+
await ensureWorkflowResourcesVisible();
|
|
318
|
+
const runtime = deps.runtimeForContext(ctx);
|
|
319
|
+
try {
|
|
320
|
+
durable = await runtime.prepareDurableResumable(target);
|
|
321
|
+
} catch (error) {
|
|
322
|
+
preparationError = error instanceof Error ? error.message : String(error);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
const loadableRuns = topLevelWorkflowRuns(store.runs()).filter((run) => backend.isWorkflowLoadable(run.id));
|
|
326
|
+
const combined = resolveWorkflowResumeTarget(target, loadableRuns, durable, backend.listCompletedWorkflows());
|
|
327
|
+
if (combined.kind === "ambiguous") {
|
|
328
|
+
fail(`Ambiguous workflow prefix "${target}" matches: ${combined.matches.map((match) => `${match.name} (${match.workflowId.slice(0, 8)})`).join(", ")}`);
|
|
317
329
|
return true;
|
|
318
330
|
}
|
|
319
|
-
|
|
331
|
+
if (combined.kind === "completed" || combined.kind === "durable") {
|
|
332
|
+
return await handleDurableResume(combined.workflowId, ctx, reporter, deps);
|
|
333
|
+
}
|
|
334
|
+
if (combined.kind === "live") runId = combined.workflowId;
|
|
335
|
+
else {
|
|
336
|
+
if (preparationError !== undefined) {
|
|
337
|
+
fail(`Failed to resolve workflow resume target: ${preparationError}`);
|
|
338
|
+
return true;
|
|
339
|
+
}
|
|
340
|
+
return await handleDurableResume(target, ctx, reporter, deps);
|
|
341
|
+
}
|
|
320
342
|
}
|
|
321
343
|
} else {
|
|
322
344
|
const resolved = resolveRunIdPrefix(target);
|
|
@@ -41,23 +41,13 @@ export function topLevelExpandedSnapshots() {
|
|
|
41
41
|
}));
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
export function reloadBlockedMessage(count = inFlightRunCount()): string {
|
|
45
|
-
return `Reload skipped: ${count} workflow run(s) still in flight. Wait for them to finish, or pause/kill them before reloading workflow resources.`;
|
|
46
|
-
}
|
|
47
44
|
|
|
48
45
|
export function allStageConflictMessage(action: "pause" | "interrupt" | "kill"): string {
|
|
49
46
|
return `Cannot ${action} --all with a stageId; omit stageId or target a single run.`;
|
|
50
47
|
}
|
|
51
48
|
|
|
52
|
-
export class WorkflowReloadBlockedError extends Error {
|
|
53
|
-
constructor(message: string) {
|
|
54
|
-
super(message);
|
|
55
|
-
this.name = "WorkflowReloadBlockedError";
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
49
|
|
|
59
50
|
export function reloadFailureMessage(error: unknown): string {
|
|
60
|
-
if (error instanceof WorkflowReloadBlockedError) return error.message;
|
|
61
51
|
return `Reload failed: ${error instanceof Error ? error.message : String(error)}`;
|
|
62
52
|
}
|
|
63
53
|
|
|
@@ -18,18 +18,17 @@ import {
|
|
|
18
18
|
allStageConflictMessage,
|
|
19
19
|
ambiguousRunMessage,
|
|
20
20
|
formatAlreadyEndedRetainedMessage,
|
|
21
|
-
inFlightRunCount,
|
|
22
|
-
reloadBlockedMessage,
|
|
23
21
|
reloadFailureMessage,
|
|
24
22
|
resolveToolRunTarget,
|
|
25
23
|
resolveToolStageTarget,
|
|
26
24
|
stageFailureMessage,
|
|
27
25
|
} from "./workflow-targets.js";
|
|
28
|
-
import { formatWorkflowResourceLoadWarning } from "./workflow-command-surfaces.js";
|
|
26
|
+
import { formatWorkflowReloadReport, formatWorkflowResourceLoadWarning } from "./workflow-command-surfaces.js";
|
|
27
|
+
import { normalizeWorkflowReloadReport, type WorkflowReloadReport } from "./workflow-reload-report.js";
|
|
29
28
|
|
|
30
29
|
export interface WorkflowControlActionDeps {
|
|
31
30
|
getPersistence: () => WorkflowPersistencePort | undefined;
|
|
32
|
-
reloadWorkflowResources: () => Promise<void> | void;
|
|
31
|
+
reloadWorkflowResources: () => Promise<WorkflowReloadReport | void> | void;
|
|
33
32
|
getRuntime: () => ExtensionRuntime;
|
|
34
33
|
policy: WorkflowExecutionPolicy;
|
|
35
34
|
ensureWorkflowResourcesLoaded: () => Promise<void> | void;
|
|
@@ -71,22 +70,27 @@ export async function workflowReloadAction(
|
|
|
71
70
|
args: WorkflowToolArgs,
|
|
72
71
|
deps: Pick<WorkflowControlActionDeps, "reloadWorkflowResources">,
|
|
73
72
|
): Promise<WorkflowToolResult> {
|
|
74
|
-
const activeRuns = inFlightRunCount();
|
|
75
|
-
if (activeRuns > 0) {
|
|
76
|
-
return { action: "reload", status: "noop", message: reloadBlockedMessage(activeRuns) };
|
|
77
|
-
}
|
|
78
73
|
try {
|
|
79
|
-
await deps.reloadWorkflowResources();
|
|
74
|
+
const report = normalizeWorkflowReloadReport(await deps.reloadWorkflowResources());
|
|
75
|
+
return {
|
|
76
|
+
action: "reload",
|
|
77
|
+
status: report.outcome === "applied" ? "ok" : "noop",
|
|
78
|
+
message: formatWorkflowReloadReport(report, args.reason),
|
|
79
|
+
...report,
|
|
80
|
+
};
|
|
80
81
|
} catch (error) {
|
|
81
|
-
return {
|
|
82
|
+
return {
|
|
83
|
+
action: "reload",
|
|
84
|
+
status: "noop",
|
|
85
|
+
message: reloadFailureMessage(error),
|
|
86
|
+
outcome: "failed",
|
|
87
|
+
error: error instanceof Error ? error.message : String(error),
|
|
88
|
+
generation: 0,
|
|
89
|
+
workflowCount: 0,
|
|
90
|
+
coalescedRequests: 1,
|
|
91
|
+
diagnostics: [],
|
|
92
|
+
};
|
|
82
93
|
}
|
|
83
|
-
return {
|
|
84
|
-
action: "reload",
|
|
85
|
-
status: "ok",
|
|
86
|
-
message: args.reason?.trim()
|
|
87
|
-
? `Reloaded workflow resources (${args.reason.trim()}).`
|
|
88
|
-
: "Reloaded workflow resources.",
|
|
89
|
-
};
|
|
90
94
|
}
|
|
91
95
|
|
|
92
96
|
export function workflowKillAction(
|
|
@@ -32,11 +32,12 @@ import {
|
|
|
32
32
|
topLevelExpandedSnapshots,
|
|
33
33
|
} from "./workflow-targets.js";
|
|
34
34
|
import { formatWorkflowResourceLoadWarning } from "./workflow-command-surfaces.js";
|
|
35
|
+
import type { WorkflowReloadReport } from "./workflow-reload-report.js";
|
|
35
36
|
|
|
36
37
|
export function makeExecuteWorkflowTool(
|
|
37
38
|
runtime: ExtensionRuntime | ((ctx: PiExecuteContext) => ExtensionRuntime),
|
|
38
39
|
getPersistence: () => WorkflowPersistencePort | undefined,
|
|
39
|
-
reloadWorkflowResources: () => Promise<void> | void,
|
|
40
|
+
reloadWorkflowResources: () => Promise<WorkflowReloadReport | void> | void,
|
|
40
41
|
ensureWorkflowResourcesLoaded: () => Promise<void> | void = () => {},
|
|
41
42
|
): (args: WorkflowToolArgs, ctx: PiExecuteContext) => Promise<WorkflowToolResult> {
|
|
42
43
|
return async function executeWorkflowTool(
|
package/docs/workflows.md
CHANGED
|
@@ -781,6 +781,14 @@ export const tableSelectorFactory: WorkflowCustomUiFactory<{ id: string; name: s
|
|
|
781
781
|
|
|
782
782
|
Workflow files are loaded via [jiti](https://github.com/unjs/jiti), so TypeScript works without compilation.
|
|
783
783
|
|
|
784
|
+
## Reloading workflow resources
|
|
785
|
+
|
|
786
|
+
Run `/workflow reload` after adding, editing, renaming, or deleting workflow modules or changing workflow config. Reload rescans project and user conventional directories, legacy `.pi` locations, configured file/directory paths, and package resources without restarting Atomic. The workflow tool's `reload` action uses the same in-process path.
|
|
787
|
+
|
|
788
|
+
Reload builds a complete replacement registry before publishing it. Concurrent requests are serialized and coalesced, stale discovery from an earlier session cannot overwrite newer state, and a fatal refresh failure retains the previous registry. Reload is safe while workflows are running: existing runs keep the definition and runtime snapshot they started with, while subsequent list/get/inputs/help/completion/invocation calls use the newly published registry.
|
|
789
|
+
|
|
790
|
+
A successful rescan may still contain per-resource diagnostics. Both reload surfaces now show `CONFIG_INVALID`, `IMPORT_FAILED`, `INVALID_DEFINITION`, `PATH_NOT_FOUND`, and duplicate-name diagnostics instead of reporting bare success while silently skipping a resource. Valid sibling workflows remain available. Fix the reported source/path and reload again; no process restart is required.
|
|
791
|
+
|
|
784
792
|
## Workflow Configuration
|
|
785
793
|
|
|
786
794
|
Configured workflow paths live in workflow extension config. Project config paths are relative to the project root. Global config paths are relative to `~/.atomic/agent`.
|
|
@@ -1107,7 +1115,7 @@ When Atomic starts with an existing session or resumes one, it shows an informat
|
|
|
1107
1115
|
/workflow resume <workflow-id-or-prefix> # Resume unfinished work or open completed detail/chat
|
|
1108
1116
|
```
|
|
1109
1117
|
|
|
1110
|
-
Explicit full IDs take precedence, while prefixes resolve across live, resumable durable, and completed targets as one namespace. Ambiguous prefixes use the existing-style ambiguity diagnostic. A completed backend row with no checkpoints or no usable retained stage conversation is hidden from the picker; an explicit target reports that it is stale or missing required durable checkpoint/session data. A completed run remains inspectable when at least one stage has a usable transcript; missing, empty, directory, context-empty, or partially malformed transcript paths are omitted from stage chat attachment. Validation uses the final retained transcript for a repeated stage replay key, so an obsolete superseded checkpoint path does not hide an otherwise valid completed run. Reopening inspection refreshes a changed authoritative retained-chat handle. Session-cache-only rows are likewise hidden because the backend is authoritative. Cancelled, killed, non-resumable failed, and other terminal non-success states are never added. Normal `/resume`, `atomic -r`, and `--continue` behavior for internal workflow stage sessions is unchanged.
|
|
1118
|
+
Explicit full IDs take precedence, while prefixes resolve across top-level live, resumable durable, and completed targets as one namespace. An exact loadable paused top-level live target resumes directly from in-session state without enumerating the durable completed-history catalog; this keeps explicit live resume responsive even when retained durable history is large and preserves live-over-durable precedence for duplicate IDs. Nested child runs remain excluded from this top-level target namespace even when addressed by an exact ID. Prefixes and other targets continue through the combined catalog so ambiguity and completed-inspection behavior remain unchanged. Ambiguous prefixes use the existing-style ambiguity diagnostic. A completed backend row with no checkpoints or no usable retained stage conversation is hidden from the picker; an explicit target reports that it is stale or missing required durable checkpoint/session data. A completed run remains inspectable when at least one stage has a usable transcript; missing, empty, directory, context-empty, or partially malformed transcript paths are omitted from stage chat attachment. Validation uses the final retained transcript for a repeated stage replay key, so an obsolete superseded checkpoint path does not hide an otherwise valid completed run. Reopening inspection refreshes a changed authoritative retained-chat handle. Session-cache-only rows are likewise hidden because the backend is authoritative. Cancelled, killed, non-resumable failed, and other terminal non-success states are never added. Normal `/resume`, `atomic -r`, and `--continue` behavior for internal workflow stage sessions is unchanged.
|
|
1111
1119
|
|
|
1112
1120
|
### Cancellation, failure, and retry semantics
|
|
1113
1121
|
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bastani/atomic",
|
|
3
|
-
"version": "0.9.9
|
|
3
|
+
"version": "0.9.9",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@bastani/atomic",
|
|
9
|
-
"version": "0.9.9
|
|
9
|
+
"version": "0.9.9",
|
|
10
10
|
"license": "MIT",
|
|
11
11
|
"dependencies": {
|
|
12
|
-
"@bastani/atomic-natives": "0.9.9
|
|
12
|
+
"@bastani/atomic-natives": "0.9.9",
|
|
13
13
|
"@bufbuild/protobuf": "^2.12.1",
|
|
14
14
|
"@earendil-works/pi-agent-core": "^0.80.7",
|
|
15
15
|
"@earendil-works/pi-ai": "^0.80.7",
|
|
@@ -515,16 +515,16 @@
|
|
|
515
515
|
}
|
|
516
516
|
},
|
|
517
517
|
"node_modules/@bastani/atomic-natives": {
|
|
518
|
-
"version": "0.9.9
|
|
519
|
-
"resolved": "https://registry.npmjs.org/@bastani/atomic-natives/-/atomic-natives-0.9.9
|
|
518
|
+
"version": "0.9.9",
|
|
519
|
+
"resolved": "https://registry.npmjs.org/@bastani/atomic-natives/-/atomic-natives-0.9.9.tgz",
|
|
520
520
|
"license": "MIT",
|
|
521
521
|
"optionalDependencies": {
|
|
522
|
-
"@bastani/atomic-natives-darwin-arm64": "0.9.9
|
|
523
|
-
"@bastani/atomic-natives-darwin-x64": "0.9.9
|
|
524
|
-
"@bastani/atomic-natives-linux-arm64-gnu": "0.9.9
|
|
525
|
-
"@bastani/atomic-natives-linux-x64-gnu": "0.9.9
|
|
526
|
-
"@bastani/atomic-natives-win32-arm64-msvc": "0.9.9
|
|
527
|
-
"@bastani/atomic-natives-win32-x64-msvc": "0.9.9
|
|
522
|
+
"@bastani/atomic-natives-darwin-arm64": "0.9.9",
|
|
523
|
+
"@bastani/atomic-natives-darwin-x64": "0.9.9",
|
|
524
|
+
"@bastani/atomic-natives-linux-arm64-gnu": "0.9.9",
|
|
525
|
+
"@bastani/atomic-natives-linux-x64-gnu": "0.9.9",
|
|
526
|
+
"@bastani/atomic-natives-win32-arm64-msvc": "0.9.9",
|
|
527
|
+
"@bastani/atomic-natives-win32-x64-msvc": "0.9.9"
|
|
528
528
|
},
|
|
529
529
|
"engines": {
|
|
530
530
|
"bun": ">=1.3.14",
|
|
@@ -532,8 +532,8 @@
|
|
|
532
532
|
}
|
|
533
533
|
},
|
|
534
534
|
"node_modules/@bastani/atomic-natives-darwin-arm64": {
|
|
535
|
-
"version": "0.9.9
|
|
536
|
-
"resolved": "https://registry.npmjs.org/@bastani/atomic-natives-darwin-arm64/-/atomic-natives-darwin-arm64-0.9.9
|
|
535
|
+
"version": "0.9.9",
|
|
536
|
+
"resolved": "https://registry.npmjs.org/@bastani/atomic-natives-darwin-arm64/-/atomic-natives-darwin-arm64-0.9.9.tgz",
|
|
537
537
|
"license": "MIT",
|
|
538
538
|
"os": [
|
|
539
539
|
"darwin"
|
|
@@ -544,8 +544,8 @@
|
|
|
544
544
|
"optional": true
|
|
545
545
|
},
|
|
546
546
|
"node_modules/@bastani/atomic-natives-darwin-x64": {
|
|
547
|
-
"version": "0.9.9
|
|
548
|
-
"resolved": "https://registry.npmjs.org/@bastani/atomic-natives-darwin-x64/-/atomic-natives-darwin-x64-0.9.9
|
|
547
|
+
"version": "0.9.9",
|
|
548
|
+
"resolved": "https://registry.npmjs.org/@bastani/atomic-natives-darwin-x64/-/atomic-natives-darwin-x64-0.9.9.tgz",
|
|
549
549
|
"license": "MIT",
|
|
550
550
|
"os": [
|
|
551
551
|
"darwin"
|
|
@@ -556,8 +556,8 @@
|
|
|
556
556
|
"optional": true
|
|
557
557
|
},
|
|
558
558
|
"node_modules/@bastani/atomic-natives-linux-arm64-gnu": {
|
|
559
|
-
"version": "0.9.9
|
|
560
|
-
"resolved": "https://registry.npmjs.org/@bastani/atomic-natives-linux-arm64-gnu/-/atomic-natives-linux-arm64-gnu-0.9.9
|
|
559
|
+
"version": "0.9.9",
|
|
560
|
+
"resolved": "https://registry.npmjs.org/@bastani/atomic-natives-linux-arm64-gnu/-/atomic-natives-linux-arm64-gnu-0.9.9.tgz",
|
|
561
561
|
"license": "MIT",
|
|
562
562
|
"os": [
|
|
563
563
|
"linux"
|
|
@@ -571,8 +571,8 @@
|
|
|
571
571
|
"optional": true
|
|
572
572
|
},
|
|
573
573
|
"node_modules/@bastani/atomic-natives-linux-x64-gnu": {
|
|
574
|
-
"version": "0.9.9
|
|
575
|
-
"resolved": "https://registry.npmjs.org/@bastani/atomic-natives-linux-x64-gnu/-/atomic-natives-linux-x64-gnu-0.9.9
|
|
574
|
+
"version": "0.9.9",
|
|
575
|
+
"resolved": "https://registry.npmjs.org/@bastani/atomic-natives-linux-x64-gnu/-/atomic-natives-linux-x64-gnu-0.9.9.tgz",
|
|
576
576
|
"license": "MIT",
|
|
577
577
|
"os": [
|
|
578
578
|
"linux"
|
|
@@ -586,8 +586,8 @@
|
|
|
586
586
|
"optional": true
|
|
587
587
|
},
|
|
588
588
|
"node_modules/@bastani/atomic-natives-win32-arm64-msvc": {
|
|
589
|
-
"version": "0.9.9
|
|
590
|
-
"resolved": "https://registry.npmjs.org/@bastani/atomic-natives-win32-arm64-msvc/-/atomic-natives-win32-arm64-msvc-0.9.9
|
|
589
|
+
"version": "0.9.9",
|
|
590
|
+
"resolved": "https://registry.npmjs.org/@bastani/atomic-natives-win32-arm64-msvc/-/atomic-natives-win32-arm64-msvc-0.9.9.tgz",
|
|
591
591
|
"license": "MIT",
|
|
592
592
|
"os": [
|
|
593
593
|
"win32"
|
|
@@ -598,8 +598,8 @@
|
|
|
598
598
|
"optional": true
|
|
599
599
|
},
|
|
600
600
|
"node_modules/@bastani/atomic-natives-win32-x64-msvc": {
|
|
601
|
-
"version": "0.9.9
|
|
602
|
-
"resolved": "https://registry.npmjs.org/@bastani/atomic-natives-win32-x64-msvc/-/atomic-natives-win32-x64-msvc-0.9.9
|
|
601
|
+
"version": "0.9.9",
|
|
602
|
+
"resolved": "https://registry.npmjs.org/@bastani/atomic-natives-win32-x64-msvc/-/atomic-natives-win32-x64-msvc-0.9.9.tgz",
|
|
603
603
|
"license": "MIT",
|
|
604
604
|
"os": [
|
|
605
605
|
"win32"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bastani/atomic",
|
|
3
|
-
"version": "0.9.9
|
|
3
|
+
"version": "0.9.9",
|
|
4
4
|
"description": "Atomic coding agent CLI with read, bash, edit, write tools and session management",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"atomicConfig": {
|
|
@@ -77,7 +77,7 @@
|
|
|
77
77
|
"prepublishOnly": "bun run clean && bun run build && bun run shrinkwrap"
|
|
78
78
|
},
|
|
79
79
|
"dependencies": {
|
|
80
|
-
"@bastani/atomic-natives": "0.9.9
|
|
80
|
+
"@bastani/atomic-natives": "0.9.9",
|
|
81
81
|
"@bufbuild/protobuf": "^2.12.1",
|
|
82
82
|
"@earendil-works/pi-agent-core": "^0.80.7",
|
|
83
83
|
"@earendil-works/pi-ai": "^0.80.7",
|