@bastani/atomic 0.8.19 → 0.8.20-0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/builtin/intercom/package.json +1 -1
  3. package/dist/builtin/mcp/CHANGELOG.md +5 -0
  4. package/dist/builtin/mcp/package.json +2 -2
  5. package/dist/builtin/subagents/CHANGELOG.md +12 -2
  6. package/dist/builtin/subagents/agents/code-simplifier.md +1 -1
  7. package/dist/builtin/subagents/agents/codebase-analyzer.md +1 -1
  8. package/dist/builtin/subagents/agents/codebase-online-researcher.md +1 -1
  9. package/dist/builtin/subagents/agents/codebase-research-analyzer.md +1 -1
  10. package/dist/builtin/subagents/agents/debugger.md +1 -1
  11. package/dist/builtin/subagents/package.json +1 -1
  12. package/dist/builtin/subagents/skills/subagent/SKILL.md +12 -12
  13. package/dist/builtin/subagents/src/agents/agent-management.ts +16 -11
  14. package/dist/builtin/subagents/src/agents/skills.ts +13 -1
  15. package/dist/builtin/subagents/src/extension/index.ts +14 -3
  16. package/dist/builtin/subagents/src/runs/background/async-execution.ts +8 -0
  17. package/dist/builtin/subagents/src/runs/background/run-status.ts +2 -3
  18. package/dist/builtin/subagents/src/runs/background/subagent-runner.ts +11 -1
  19. package/dist/builtin/subagents/src/runs/foreground/chain-clarify.ts +2 -2
  20. package/dist/builtin/subagents/src/runs/foreground/chain-execution.ts +31 -23
  21. package/dist/builtin/subagents/src/runs/foreground/execution.ts +13 -7
  22. package/dist/builtin/subagents/src/runs/foreground/subagent-executor.ts +160 -93
  23. package/dist/builtin/subagents/src/runs/shared/parallel-utils.ts +1 -0
  24. package/dist/builtin/subagents/src/runs/shared/run-history.ts +1 -1
  25. package/dist/builtin/subagents/src/shared/settings.ts +1 -0
  26. package/dist/builtin/subagents/src/shared/types.ts +78 -4
  27. package/dist/builtin/subagents/src/tui/render.ts +203 -19
  28. package/dist/builtin/web-access/CHANGELOG.md +5 -0
  29. package/dist/builtin/web-access/package.json +2 -2
  30. package/dist/builtin/workflows/CHANGELOG.md +19 -0
  31. package/dist/builtin/workflows/README.md +22 -3
  32. package/dist/builtin/workflows/builtin/deep-research-codebase.ts +1 -1
  33. package/dist/builtin/workflows/builtin/open-claude-design.ts +12 -4
  34. package/dist/builtin/workflows/builtin/ralph.ts +2 -2
  35. package/dist/builtin/workflows/package.json +1 -1
  36. package/dist/builtin/workflows/src/extension/config-loader.ts +68 -0
  37. package/dist/builtin/workflows/src/extension/index.ts +246 -55
  38. package/dist/builtin/workflows/src/extension/lifecycle-notifications.ts +372 -0
  39. package/dist/builtin/workflows/src/extension/render-call.ts +1 -1
  40. package/dist/builtin/workflows/src/extension/wiring.ts +32 -3
  41. package/dist/builtin/workflows/src/runs/background/status.ts +14 -74
  42. package/dist/builtin/workflows/src/shared/persistence-restore.ts +5 -3
  43. package/dist/builtin/workflows/src/tui/chat-surface-message.ts +3 -13
  44. package/dist/builtin/workflows/src/tui/inline-form-overlay.ts +2 -10
  45. package/dist/builtin/workflows/src/tui/overlay-adapter.ts +5 -5
  46. package/dist/builtin/workflows/src/tui/session-confirm.ts +6 -7
  47. package/dist/builtin/workflows/src/tui/session-picker.ts +18 -14
  48. package/dist/builtin/workflows/src/tui/status-list.ts +2 -2
  49. package/dist/builtin/workflows/src/tui/store-widget-installer.ts +125 -30
  50. package/dist/config.d.ts +1 -0
  51. package/dist/config.d.ts.map +1 -1
  52. package/dist/config.js +1 -0
  53. package/dist/config.js.map +1 -1
  54. package/dist/core/agent-session.d.ts +4 -1
  55. package/dist/core/agent-session.d.ts.map +1 -1
  56. package/dist/core/agent-session.js +2 -1
  57. package/dist/core/agent-session.js.map +1 -1
  58. package/dist/core/atomic-guide-command.d.ts.map +1 -1
  59. package/dist/core/atomic-guide-command.js +3 -2
  60. package/dist/core/atomic-guide-command.js.map +1 -1
  61. package/dist/core/extensions/index.d.ts +1 -1
  62. package/dist/core/extensions/index.d.ts.map +1 -1
  63. package/dist/core/extensions/index.js.map +1 -1
  64. package/dist/core/extensions/runner.d.ts +3 -2
  65. package/dist/core/extensions/runner.d.ts.map +1 -1
  66. package/dist/core/extensions/runner.js +6 -1
  67. package/dist/core/extensions/runner.js.map +1 -1
  68. package/dist/core/extensions/types.d.ts +13 -0
  69. package/dist/core/extensions/types.d.ts.map +1 -1
  70. package/dist/core/extensions/types.js.map +1 -1
  71. package/dist/core/model-resolver.d.ts.map +1 -1
  72. package/dist/core/model-resolver.js +63 -17
  73. package/dist/core/model-resolver.js.map +1 -1
  74. package/dist/core/output-guard.d.ts.map +1 -1
  75. package/dist/core/output-guard.js +29 -0
  76. package/dist/core/output-guard.js.map +1 -1
  77. package/dist/core/sdk.d.ts +3 -1
  78. package/dist/core/sdk.d.ts.map +1 -1
  79. package/dist/core/sdk.js +1 -0
  80. package/dist/core/sdk.js.map +1 -1
  81. package/dist/core/system-prompt.d.ts.map +1 -1
  82. package/dist/core/system-prompt.js +1 -1
  83. package/dist/core/system-prompt.js.map +1 -1
  84. package/dist/index.d.ts +2 -2
  85. package/dist/index.d.ts.map +1 -1
  86. package/dist/index.js +1 -1
  87. package/dist/index.js.map +1 -1
  88. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  89. package/dist/modes/interactive/interactive-mode.js +46 -13
  90. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  91. package/dist/utils/pi-user-agent.d.ts.map +1 -1
  92. package/dist/utils/pi-user-agent.js +2 -1
  93. package/dist/utils/pi-user-agent.js.map +1 -1
  94. package/dist/utils/syntax-highlight.d.ts.map +1 -1
  95. package/dist/utils/syntax-highlight.js +1 -1
  96. package/dist/utils/syntax-highlight.js.map +1 -1
  97. package/dist/utils/tools-manager.d.ts.map +1 -1
  98. package/dist/utils/tools-manager.js +3 -5
  99. package/dist/utils/tools-manager.js.map +1 -1
  100. package/docs/models.md +52 -52
  101. package/docs/quickstart.md +2 -2
  102. package/docs/workflows.md +22 -5
  103. package/package.json +9 -9
package/CHANGELOG.md CHANGED
@@ -2,6 +2,19 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [0.8.20-0] - 2026-05-29
6
+
7
+ ### Added
8
+
9
+ - Added session-scoped `orchestrationContext` support to SDK agent sessions and extension contexts for workflow-stage policy enforcement.
10
+ - Added support for the Claude Opus 4.8 model across model configuration, selection, and validation via the `@earendil-works/pi-ai` 0.77.0 upgrade ([#1097](https://github.com/flora131/atomic/issues/1097)).
11
+
12
+ ### Changed
13
+
14
+ - Upgraded the pi runtime packages (`@earendil-works/pi-agent-core`, `@earendil-works/pi-ai`, `@earendil-works/pi-tui`) from 0.75.5 to 0.77.0 and bumped `@modelcontextprotocol/ext-apps` to 1.7.2, `highlight.js` to 11.x, `linkedom` to 0.18.x, `undici` to 8.x, and `vitest` (dev) to 4.x.
15
+ - Switched the `highlight.js` import to the package-root default export and replaced the Node stream pipeline in the tools downloader with `Bun.write()` to align with the upgraded dependencies.
16
+ - Pinned the footer (model + cwd identity) directly under the editor and moved below-editor widgets beneath it (separated by a blank line), so transient run status such as the workflow companion counter renders at the very bottom instead of separating the footer from the input. Stacked below-editor widgets (e.g. the async-subagent widget and the workflow run counter) are also separated from each other by a blank line. Rendering below-editor widgets last keeps a live widget at the bottom of the buffer (within the viewport), preserving the widget resize-flicker fix. Extension-provided custom footers are now swapped in place (rather than appended), so installing a custom footer keeps the below-editor widget container as the last UI child and does not regress this ordering ([#1109](https://github.com/flora131/atomic/issues/1109)).
17
+
5
18
  ## [0.8.19] - 2026-05-27
6
19
 
7
20
  ### Changed
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bastani/intercom",
3
- "version": "0.8.19",
3
+ "version": "0.8.20-0",
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,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.8.20-0] - 2026-05-29
11
+
12
+ ### Changed
13
+ - Bumped `@modelcontextprotocol/ext-apps` to 1.7.2 to align with the pi 0.77.0 upgrade.
14
+
10
15
  ## [0.8.18] - 2026-05-27
11
16
 
12
17
  ### Changed
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bastani/mcp",
3
- "version": "0.8.19",
3
+ "version": "0.8.20-0",
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": [
@@ -48,7 +48,7 @@
48
48
  }
49
49
  },
50
50
  "dependencies": {
51
- "@modelcontextprotocol/ext-apps": "^1.2.2",
51
+ "@modelcontextprotocol/ext-apps": "^1.7.2",
52
52
  "@modelcontextprotocol/sdk": "^1.25.1",
53
53
  "open": "^10.2.0",
54
54
  "typebox": "^1.1.24",
@@ -1,5 +1,17 @@
1
1
  # Changelog
2
2
 
3
+ ## [Unreleased]
4
+
5
+ ## [0.8.20-0] - 2026-05-29
6
+
7
+ ### Fixed
8
+
9
+ - Fixed the subagent running spinner freezing/stuttering and the surrounding TUI flickering while subagents run: the running glyph is now driven by wall-clock time with a steady re-render ticker (result cards, slash result cards, and the async-agents widget), so it animates smoothly and continuously instead of only advancing when progress data changes. Per-frame diffs stay limited to the spinner glyph cell, so the differential renderer keeps doing partial redraws (no full-screen clear / flicker), and the ticker is torn down on completion, reload, and session shutdown ([#1084](https://github.com/flora131/atomic/issues/1084)).
10
+ - Moved the async-agents (background subagent) widget from above the editor to below it (`belowEditor`). The widget animates a running glyph and elapsed labels every tick; pi-tui full-clears the screen+scrollback whenever a changed line sits above the viewport fold, so an above-editor widget flickered once the bottom region grew tall and pushed it above the fold. Rendering below the editor keeps the live line within the bottom viewport (flicker-free) and matches the workflow companion widget's placement ([#1109](https://github.com/flora131/atomic/issues/1109)).
11
+ - Hardened workflow-stage subagent guard propagation tests with an internal executor runtime DI seam ([#1088](https://github.com/flora131/atomic/issues/1088)).
12
+ - Capped subagent fanout spawned from workflow stages to a single child level with a workflow-specific nested-subagent error.
13
+ - Fixed builtin subagent skill resolution from project cwd ([#1087](https://github.com/flora131/atomic/issues/1087)).
14
+
3
15
  ## [0.24.3] - 2026-05-14
4
16
 
5
17
  ### Added
@@ -9,8 +21,6 @@
9
21
  ### Fixed
10
22
  - Let `async: true` chain tool calls run in the background when `clarify` is omitted, and avoid showing the async badge for explicit foreground clarify runs.
11
23
 
12
- ## [Unreleased]
13
-
14
24
  ## [0.8.18] - 2026-05-27
15
25
 
16
26
  ### Changed
@@ -9,7 +9,7 @@ description: |
9
9
  - Code that has gotten messy after several iterations.
10
10
  tools: read, edit, write, grep, find, ls, bash
11
11
  model: openai/gpt-5.5
12
- fallbackModels: openai-codex/gpt-5.5, github-copilot/gpt-5.5, anthropic/claude-opus-4-7, github-copilot/claude-opus-4.7
12
+ fallbackModels: openai-codex/gpt-5.5, github-copilot/gpt-5.5, anthropic/claude-opus-4-8, github-copilot/claude-opus-4.7
13
13
  thinking: low
14
14
  ---
15
15
 
@@ -3,7 +3,7 @@ name: codebase-analyzer
3
3
  description: Analyzes codebase implementation details. Call the codebase-analyzer agent when you need to find detailed information about specific components.
4
4
  tools: read, grep, find, ls, bash
5
5
  model: openai/gpt-5.5
6
- fallbackModels: openai-codex/gpt-5.5, github-copilot/gpt-5.5, anthropic/claude-opus-4-7, github-copilot/claude-opus-4.7
6
+ fallbackModels: openai-codex/gpt-5.5, github-copilot/gpt-5.5, anthropic/claude-opus-4-8, github-copilot/claude-opus-4.7
7
7
  thinking: low
8
8
  ---
9
9
 
@@ -3,7 +3,7 @@ name: codebase-online-researcher
3
3
  description: Online research for up-to-date documentation and library-source knowledge. Use when you need authoritative external information — official docs, ecosystem context, version-specific behavior, GitHub permalinks into open-source libraries, or video tutorials.
4
4
  tools: read, grep, find, ls, bash, write, web_search, fetch_content, get_search_content
5
5
  model: openai/gpt-5.5
6
- fallbackModels: openai-codex/gpt-5.5, github-copilot/gpt-5.5, anthropic/claude-opus-4-7, github-copilot/claude-opus-4.7
6
+ fallbackModels: openai-codex/gpt-5.5, github-copilot/gpt-5.5, anthropic/claude-opus-4-8, github-copilot/claude-opus-4.7
7
7
  thinking: low
8
8
  skills: playwright-cli
9
9
  ---
@@ -3,7 +3,7 @@ name: codebase-research-analyzer
3
3
  description: Analyzes local research documents to extract high-value insights, decisions, and technical details while filtering out noise. Use this when you want to deep dive on a research topic or understand the rationale behind decisions.
4
4
  tools: read, grep, find, ls, bash
5
5
  model: openai/gpt-5.5
6
- fallbackModels: openai-codex/gpt-5.5, github-copilot/gpt-5.5, anthropic/claude-opus-4-7, github-copilot/claude-opus-4.7
6
+ fallbackModels: openai-codex/gpt-5.5, github-copilot/gpt-5.5, anthropic/claude-opus-4-8, github-copilot/claude-opus-4.7
7
7
  thinking: low
8
8
  ---
9
9
 
@@ -3,7 +3,7 @@ name: debugger
3
3
  description: Debug errors, test failures, and unexpected behavior. Use PROACTIVELY when encountering issues, analyzing stack traces, or investigating system problems.
4
4
  tools: read, edit, write, grep, find, ls, bash, web_search, fetch_content, get_search_content
5
5
  model: openai/gpt-5.5
6
- fallbackModels: openai-codex/gpt-5.5, github-copilot/gpt-5.5, anthropic/claude-opus-4-7, github-copilot/claude-opus-4.7
6
+ fallbackModels: openai-codex/gpt-5.5, github-copilot/gpt-5.5, anthropic/claude-opus-4-8, github-copilot/claude-opus-4.7
7
7
  thinking: high
8
8
  skills: tdd, playwright-cli
9
9
  ---
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bastani/subagents",
3
- "version": "0.8.19",
3
+ "version": "0.8.20-0",
4
4
  "private": true,
5
5
  "description": "Atomic extension for delegating tasks to subagents with chains, parallel execution, and TUI clarification. Fork of: https://github.com/nicobailon/pi-subagents",
6
6
  "contributors": [
@@ -118,18 +118,18 @@ Use this after implementation when the user wants cleanup review or when a final
118
118
 
119
119
  Builtin agents load at the lowest priority. Project agents override user agents, and user/project agents override builtins with the same name.
120
120
 
121
- | Agent | Purpose | Default model | Thinking | Tools | Notes |
122
- |-------|---------|---------------|----------|-------|-------|
123
- | `codebase-locator` | Locate files, directories, tests, and configs relevant to a topic | `openai/gpt-5.4-mini` | low | read, grep, find, ls, bash | Read-only finder. Returns a categorized file map; no analysis. |
124
- | `codebase-analyzer` | Explain how specific code currently works | `openai/gpt-5.5` | low | read, grep, find, ls, bash | Read-only. Traces flow with `file:line` references; does not critique. |
125
- | `codebase-pattern-finder` | Find similar implementations or conventions | `openai/gpt-5.4-mini` | low | read, grep, find, ls, bash | Read-only. Returns code snippets with `file:line` references. |
126
- | `codebase-research-locator` | Discover prior `research/` and `specs/` docs | `openai/gpt-5.4-mini` | low | read, grep, find, ls, bash | Read-only. Sorts by date, tiers by recency, flags supersession. |
127
- | `codebase-research-analyzer` | Extract decisions and constraints from prior docs | `openai/gpt-5.5` | low | read, grep, find, ls, bash | Read-only. Filters aggressively for what still applies today. |
128
- | `codebase-online-researcher` | Web research with authoritative sources | `openai/gpt-5.5` | low | read, grep, find, ls, bash, write, web_search, fetch_content, get_search_content | Has the `playwright-cli` skill. Persists keepers to `research/web/`. |
129
- | `code-simplifier` | Clean up recently changed code without changing behavior | `openai/gpt-5.5` | low | read, edit, write, grep, find, ls, bash | **Writer.** Scopes to recently modified code by default; preserves all observable behavior. |
130
- | `debugger` | Reproduce, diagnose, and fix failing behavior | `openai/gpt-5.5` | high | read, edit, write, grep, find, ls, bash, web_search, fetch_content, get_search_content | **Writer.** Has the `tdd` and `playwright-cli` skills. Inspect-only mode requires an explicit instruction. |
131
-
132
- Each builtin declares an explicit `model` and `fallbackModels` chain (typically `github-copilot/<same>`, then `anthropic/claude-opus-4-7`, then `github-copilot/claude-opus-4.7`). The current user-selected model is automatically appended as the last fallback and de-duplicated. Override per run with inline config:
121
+ | Agent | Purpose | Default model | Thinking | Tools | Notes |
122
+ | ---------------------------- | ----------------------------------------------------------------- | --------------------- | -------- | -------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- |
123
+ | `codebase-locator` | Locate files, directories, tests, and configs relevant to a topic | `openai/gpt-5.4-mini` | low | read, grep, find, ls, bash | Read-only finder. Returns a categorized file map; no analysis. |
124
+ | `codebase-analyzer` | Explain how specific code currently works | `openai/gpt-5.5` | low | read, grep, find, ls, bash | Read-only. Traces flow with `file:line` references; does not critique. |
125
+ | `codebase-pattern-finder` | Find similar implementations or conventions | `openai/gpt-5.4-mini` | low | read, grep, find, ls, bash | Read-only. Returns code snippets with `file:line` references. |
126
+ | `codebase-research-locator` | Discover prior `research/` and `specs/` docs | `openai/gpt-5.4-mini` | low | read, grep, find, ls, bash | Read-only. Sorts by date, tiers by recency, flags supersession. |
127
+ | `codebase-research-analyzer` | Extract decisions and constraints from prior docs | `openai/gpt-5.5` | low | read, grep, find, ls, bash | Read-only. Filters aggressively for what still applies today. |
128
+ | `codebase-online-researcher` | Web research with authoritative sources | `openai/gpt-5.5` | low | read, grep, find, ls, bash, write, web_search, fetch_content, get_search_content | Has the `playwright-cli` skill. Persists keepers to `research/web/`. |
129
+ | `code-simplifier` | Clean up recently changed code without changing behavior | `openai/gpt-5.5` | low | read, edit, write, grep, find, ls, bash | **Writer.** Scopes to recently modified code by default; preserves all observable behavior. |
130
+ | `debugger` | Reproduce, diagnose, and fix failing behavior | `openai/gpt-5.5` | high | read, edit, write, grep, find, ls, bash, web_search, fetch_content, get_search_content | **Writer.** Has the `tdd` and `playwright-cli` skills. Inspect-only mode requires an explicit instruction. |
131
+
132
+ Each builtin declares an explicit `model` and `fallbackModels` chain (typically `github-copilot/<same>`, then `anthropic/claude-opus-4-8`, then `github-copilot/claude-opus-4.7`). The current user-selected model is automatically appended as the last fallback and de-duplicated. Override per run with inline config:
133
133
 
134
134
  ```text
135
135
  /run codebase-analyzer[model=anthropic/claude-sonnet-4] "Trace the auth flow"
@@ -1,6 +1,5 @@
1
1
  import * as fs from "node:fs";
2
2
  import * as path from "node:path";
3
- import type { AgentToolResult } from "@earendil-works/pi-agent-core";
4
3
  import { CONFIG_DIR_NAME } from "@bastani/atomic";
5
4
  import type { ExtensionContext } from "@bastani/atomic";
6
5
  import {
@@ -20,7 +19,7 @@ import {
20
19
  import { serializeAgent } from "./agent-serializer.ts";
21
20
  import { serializeChain } from "./chain-serializer.ts";
22
21
  import { discoverAvailableSkills } from "./skills.ts";
23
- import type { Details } from "../shared/types.ts";
22
+ import type { SubagentToolResult } from "../shared/types.ts";
24
23
 
25
24
  type ManagementAction = "list" | "get" | "create" | "update" | "delete";
26
25
  type ManagementScope = "user" | "project";
@@ -34,7 +33,7 @@ interface ManagementParams {
34
33
  config?: unknown;
35
34
  }
36
35
 
37
- function result(text: string, isError = false): AgentToolResult<Details> {
36
+ function result(text: string, isError = false): SubagentToolResult {
38
37
  return { content: [{ type: "text", text }], isError, details: { mode: "management", results: [] } };
39
38
  }
40
39
 
@@ -305,14 +304,20 @@ function applyAgentConfig(target: AgentConfig, cfg: Record<string, unknown>): st
305
304
  return undefined;
306
305
  }
307
306
 
307
+ type MutableDefinition<T extends { source: AgentSource }> = T & { source: ManagementScope };
308
+
309
+ function isMutableDefinition<T extends { source: AgentSource }>(value: T): value is MutableDefinition<T> {
310
+ return value.source === "user" || value.source === "project";
311
+ }
312
+
308
313
  function resolveTarget<T extends { source: AgentSource; filePath: string }>(
309
314
  kind: "agent" | "chain",
310
315
  name: string,
311
316
  matches: T[],
312
317
  cwd: string,
313
318
  scopeHint?: string,
314
- ): T | AgentToolResult<Details> {
315
- const mutable = matches.filter((m) => m.source !== "builtin");
319
+ ): MutableDefinition<T> | SubagentToolResult {
320
+ const mutable = matches.filter(isMutableDefinition);
316
321
  if (mutable.length === 0) {
317
322
  if (matches.length > 0) {
318
323
  return result(`${kind === "agent" ? "Agent" : "Chain"} '${name}' is builtin and cannot be modified. Create a same-named ${kind} in user or project scope to override it.`, true);
@@ -400,7 +405,7 @@ function formatChainDetail(chain: ChainConfig): string {
400
405
  return lines.join("\n");
401
406
  }
402
407
 
403
- export function handleList(params: ManagementParams, ctx: ManagementContext): AgentToolResult<Details> {
408
+ export function handleList(params: ManagementParams, ctx: ManagementContext): SubagentToolResult {
404
409
  const scope = normalizeListScope(params.agentScope) ?? "both";
405
410
  const d = discoverAgentsAll(ctx.cwd);
406
411
  const scopedAgents = allAgents(d).filter((a) => scope === "both" || a.source === "builtin" || a.source === scope).sort((a, b) => a.name.localeCompare(b.name));
@@ -418,7 +423,7 @@ export function handleList(params: ManagementParams, ctx: ManagementContext): Ag
418
423
  return result(lines.join("\n"));
419
424
  }
420
425
 
421
- function handleGet(params: ManagementParams, ctx: ManagementContext): AgentToolResult<Details> {
426
+ function handleGet(params: ManagementParams, ctx: ManagementContext): SubagentToolResult {
422
427
  if (!params.agent && !params.chainName) return result("Specify 'agent' or 'chainName' for get.", true);
423
428
  const hasBoth = Boolean(params.agent && params.chainName);
424
429
  const blocks: string[] = [];
@@ -448,7 +453,7 @@ function handleGet(params: ManagementParams, ctx: ManagementContext): AgentToolR
448
453
  return result(blocks.join("\n\n"), !anyFound);
449
454
  }
450
455
 
451
- export function handleCreate(params: ManagementParams, ctx: ManagementContext): AgentToolResult<Details> {
456
+ export function handleCreate(params: ManagementParams, ctx: ManagementContext): SubagentToolResult {
452
457
  const parsedConfig = configObject(params.config);
453
458
  if (parsedConfig.error) return result(parsedConfig.error, true);
454
459
  const cfg = parsedConfig.value;
@@ -508,7 +513,7 @@ export function handleCreate(params: ManagementParams, ctx: ManagementContext):
508
513
  return result([`Created agent '${runtimeName}' at ${targetPath}.`, ...warnings].join("\n"));
509
514
  }
510
515
 
511
- export function handleUpdate(params: ManagementParams, ctx: ManagementContext): AgentToolResult<Details> {
516
+ export function handleUpdate(params: ManagementParams, ctx: ManagementContext): SubagentToolResult {
512
517
  if (!params.agent && !params.chainName) return result("Specify 'agent' or 'chainName' for update.", true);
513
518
  if (params.agent && params.chainName) return result("Specify either 'agent' or 'chainName', not both.", true);
514
519
  const parsedConfig = configObject(params.config);
@@ -616,7 +621,7 @@ export function handleUpdate(params: ManagementParams, ctx: ManagementContext):
616
621
  return result([headline, ...warnings].join("\n"));
617
622
  }
618
623
 
619
- function handleDelete(params: ManagementParams, ctx: ManagementContext): AgentToolResult<Details> {
624
+ function handleDelete(params: ManagementParams, ctx: ManagementContext): SubagentToolResult {
620
625
  if (!params.agent && !params.chainName) return result("Specify 'agent' or 'chainName' for delete.", true);
621
626
  if (params.agent && params.chainName) return result("Specify either 'agent' or 'chainName', not both.", true);
622
627
  const scopeHint = asDisambiguationScope(params.agentScope);
@@ -637,7 +642,7 @@ function handleDelete(params: ManagementParams, ctx: ManagementContext): AgentTo
637
642
  return result(`Deleted chain '${target.name}' at ${target.filePath}.`);
638
643
  }
639
644
 
640
- export function handleManagementAction(action: string, params: ManagementParams, ctx: ManagementContext): AgentToolResult<Details> {
645
+ export function handleManagementAction(action: string, params: ManagementParams, ctx: ManagementContext): SubagentToolResult {
641
646
  switch (action as ManagementAction) {
642
647
  case "list": return handleList(params, ctx);
643
648
  case "get": return handleGet(params, ctx);
@@ -6,7 +6,7 @@ import { execSync } from "node:child_process";
6
6
  import * as fs from "node:fs";
7
7
  import * as os from "node:os";
8
8
  import * as path from "node:path";
9
- import { getAgentConfigPaths, getAgentDirs, getProjectConfigDirs } from "@bastani/atomic";
9
+ import { getAgentConfigPaths, getAgentDirs, getBuiltinPackagePaths, getProjectConfigDirs } from "@bastani/atomic";
10
10
 
11
11
  export type SkillSource =
12
12
  | "project"
@@ -314,6 +314,17 @@ function collectSettingsPackageSkillPaths(cwd: string): SkillSearchPath[] {
314
314
  return results;
315
315
  }
316
316
 
317
+ function collectBuiltinPackageSkillPaths(): SkillSearchPath[] {
318
+ try {
319
+ return getBuiltinPackagePaths().flatMap((packageRoot) =>
320
+ extractSkillPathsFromPackageRoot(packageRoot, "builtin", true)
321
+ );
322
+ } catch {
323
+ // Builtin package discovery is additive; keep project/user/settings skill resolution working if unavailable.
324
+ return [];
325
+ }
326
+ }
327
+
317
328
  function buildSkillPaths(cwd: string): SkillSearchPath[] {
318
329
  const skillPaths: SkillSearchPath[] = [
319
330
  ...getProjectConfigDirs(cwd).map((configDir) => ({ path: path.join(configDir, "skills"), source: "project" as const })),
@@ -324,6 +335,7 @@ function buildSkillPaths(cwd: string): SkillSearchPath[] {
324
335
  ...collectSettingsPackageSkillPaths(cwd),
325
336
  ...extractSkillPathsFromPackageRoot(cwd, "project-package"),
326
337
  ...collectSettingsSkillPaths(cwd),
338
+ ...collectBuiltinPackageSkillPaths(),
327
339
  ];
328
340
 
329
341
  const deduped = new Map<string, SkillSearchPath>();
@@ -23,7 +23,7 @@ import { discoverAgents } from "../agents/agents.ts";
23
23
  import { cleanupAllArtifactDirs, cleanupOldArtifacts, getArtifactsDir } from "../shared/artifacts.ts";
24
24
  import { resolveCurrentSessionId } from "../shared/session-identity.ts";
25
25
  import { cleanupOldChainDirs } from "../shared/settings.ts";
26
- import { clearLegacyResultAnimationTimer, renderWidget, renderSubagentResult } from "../tui/render.ts";
26
+ import { renderWidget, renderSubagentResult, stopResultAnimations, stopWidgetAnimation, syncResultAnimation } from "../tui/render.ts";
27
27
  import { SubagentParams } from "./schemas.ts";
28
28
  import { createSubagentExecutor, type SubagentParamsLike } from "../runs/foreground/subagent-executor.ts";
29
29
  import { createAsyncJobTracker } from "../runs/background/async-job-tracker.ts";
@@ -135,8 +135,10 @@ function createSlashResultComponent(
135
135
  details: SlashMessageDetails,
136
136
  options: { expanded: boolean },
137
137
  theme: ExtensionContext["ui"]["theme"],
138
+ requestRender: () => void,
138
139
  ): Container {
139
140
  const container = new Container();
141
+ const animationState: { subagentResultAnimationTimer?: ReturnType<typeof setInterval> } = {};
140
142
  let lastVersion = -1;
141
143
  container.render = (width: number): string[] => {
142
144
  const snapshot = getSlashRenderableSnapshot(details);
@@ -144,6 +146,9 @@ function createSlashResultComponent(
144
146
  lastVersion = snapshot.version;
145
147
  rebuildSlashResultContainer(container, snapshot.result, options, theme);
146
148
  }
149
+ // Keep the live slash card's spinner animating by scheduling steady
150
+ // re-renders while it runs (and tearing the timer down once it settles).
151
+ syncResultAnimation(snapshot.result, { state: animationState, invalidate: requestRender });
147
152
  return Container.prototype.render.call(container, width);
148
153
  };
149
154
  return container;
@@ -266,6 +271,8 @@ export default function registerSubagentExtension(pi: ExtensionAPI): void {
266
271
 
267
272
  const runtimeCleanup = () => {
268
273
  stopResultWatcher();
274
+ stopWidgetAnimation();
275
+ stopResultAnimations();
269
276
  clearPendingForegroundControlNotices(state);
270
277
  if (state.poller) {
271
278
  clearInterval(state.poller);
@@ -289,7 +296,7 @@ export default function registerSubagentExtension(pi: ExtensionAPI): void {
289
296
  pi.registerMessageRenderer<SlashMessageDetails>(SLASH_RESULT_TYPE, (message, options, theme) => {
290
297
  const details = resolveSlashMessageDetails(message.details);
291
298
  if (!details) return undefined;
292
- return createSlashResultComponent(details, options, theme);
299
+ return createSlashResultComponent(details, options, theme, () => state.lastUiContext?.ui.requestRender?.());
293
300
  });
294
301
 
295
302
  pi.registerMessageRenderer<SubagentNotifyDetails>("subagent-notify", (message, options, theme) => {
@@ -458,7 +465,9 @@ DIAGNOSTICS:
458
465
  },
459
466
 
460
467
  renderResult(result, options, theme, context) {
461
- clearLegacyResultAnimationTimer(context);
468
+ // Animate the live subagent spinner by scheduling steady re-renders
469
+ // while the result is running; the timer stops once it settles.
470
+ syncResultAnimation(result, context);
462
471
  return renderSubagentResult(result, options, theme);
463
472
  },
464
473
 
@@ -549,6 +558,8 @@ DIAGNOSTICS:
549
558
  delete globalStore[eventUnsubscribeStoreKey];
550
559
  }
551
560
  stopResultWatcher();
561
+ stopWidgetAnimation();
562
+ stopResultAnimations();
552
563
  if (state.poller) clearInterval(state.poller);
553
564
  state.poller = null;
554
565
  clearPendingForegroundControlNotices(state);
@@ -109,6 +109,7 @@ interface AsyncChainParams {
109
109
  chainSkills?: string[];
110
110
  sessionFilesByFlatIndex?: (string | undefined)[];
111
111
  maxSubagentDepth: number;
112
+ workflowStageSubagentGuard?: boolean;
112
113
  worktreeSetupHook?: string;
113
114
  worktreeSetupHookTimeoutMs?: number;
114
115
  controlConfig?: ResolvedControlConfig;
@@ -135,6 +136,7 @@ interface AsyncSingleParams {
135
136
  modelOverride?: string;
136
137
  availableModels?: AvailableModelInfo[];
137
138
  maxSubagentDepth: number;
139
+ workflowStageSubagentGuard?: boolean;
138
140
  worktreeSetupHook?: string;
139
141
  worktreeSetupHookTimeoutMs?: number;
140
142
  controlConfig?: ResolvedControlConfig;
@@ -241,6 +243,7 @@ export function executeAsyncChain(
241
243
  sessionRoot,
242
244
  sessionFilesByFlatIndex,
243
245
  maxSubagentDepth,
246
+ workflowStageSubagentGuard,
244
247
  worktreeSetupHook,
245
248
  worktreeSetupHookTimeoutMs,
246
249
  controlConfig,
@@ -348,6 +351,7 @@ export function executeAsyncChain(
348
351
  outputMode: behavior.outputMode,
349
352
  sessionFile,
350
353
  maxSubagentDepth: resolveChildMaxSubagentDepth(maxSubagentDepth, a.maxSubagentDepth),
354
+ workflowStageSubagentGuard,
351
355
  };
352
356
  };
353
357
 
@@ -427,6 +431,7 @@ export function executeAsyncChain(
427
431
  childIntercomTargets,
428
432
  resultMode,
429
433
  nestedRoute: nestedRoute ?? inheritedNestedRoute,
434
+ workflowStageSubagentGuard,
430
435
  nestedSelf: inheritedNestedRoute && nestedAddress ? {
431
436
  parentRunId: nestedAddress.parentRunId,
432
437
  parentStepIndex: nestedAddress.parentStepIndex,
@@ -551,6 +556,7 @@ export function executeAsyncSingle(
551
556
  sessionRoot,
552
557
  sessionFile,
553
558
  maxSubagentDepth,
559
+ workflowStageSubagentGuard,
554
560
  worktreeSetupHook,
555
561
  worktreeSetupHookTimeoutMs,
556
562
  controlConfig,
@@ -624,6 +630,7 @@ export function executeAsyncSingle(
624
630
  outputMode,
625
631
  sessionFile,
626
632
  maxSubagentDepth: resolveChildMaxSubagentDepth(maxSubagentDepth, agentConfig.maxSubagentDepth),
633
+ workflowStageSubagentGuard,
627
634
  },
628
635
  ],
629
636
  resultPath: inheritedNestedRoute ? nestedResultsPath(inheritedNestedRoute.rootRunId, id) : path.join(RESULTS_DIR, `${id}.json`),
@@ -645,6 +652,7 @@ export function executeAsyncSingle(
645
652
  childIntercomTargets: childIntercomTarget ? [childIntercomTarget(agent, 0)] : undefined,
646
653
  resultMode: "single",
647
654
  nestedRoute: nestedRoute ?? inheritedNestedRoute,
655
+ workflowStageSubagentGuard,
648
656
  nestedSelf: inheritedNestedRoute && nestedAddress ? {
649
657
  parentRunId: nestedAddress.parentRunId,
650
658
  parentStepIndex: nestedAddress.parentStepIndex,
@@ -1,11 +1,10 @@
1
1
  import * as fs from "node:fs";
2
2
  import * as path from "node:path";
3
- import type { AgentToolResult } from "@earendil-works/pi-agent-core";
4
3
  import { formatAsyncRunList, formatAsyncRunOutputPath, formatAsyncRunProgressLabel, listAsyncRuns } from "./async-status.ts";
5
4
  import { formatNestedRunStatusLines } from "../shared/nested-render.ts";
6
5
  import { formatModelThinking } from "../../shared/formatters.ts";
7
6
  import { formatActivityLabel } from "../../shared/status-format.ts";
8
- import { ASYNC_DIR, RESULTS_DIR, type AsyncStatus, type Details, type NestedRunSummary, type SubagentState } from "../../shared/types.ts";
7
+ import { ASYNC_DIR, RESULTS_DIR, type AsyncStatus, type NestedRunSummary, type SubagentState, type SubagentToolResult } from "../../shared/types.ts";
9
8
  import { resolveSubagentIntercomTarget } from "../../intercom/intercom-bridge.ts";
10
9
  import { resolveAsyncRunLocation } from "./async-resume.ts";
11
10
  import { resolveSubagentRunId } from "./run-id-resolver.ts";
@@ -98,7 +97,7 @@ function formatNestedExactStatus(rootRunId: string, run: NestedRunSummary): stri
98
97
  return lines.join("\n");
99
98
  }
100
99
 
101
- export function inspectSubagentStatus(params: RunStatusParams, deps: RunStatusDeps = {}): AgentToolResult<Details> {
100
+ export function inspectSubagentStatus(params: RunStatusParams, deps: RunStatusDeps = {}): SubagentToolResult {
102
101
  const asyncDirRoot = deps.asyncDirRoot ?? ASYNC_DIR;
103
102
  const resultsDir = deps.resultsDir ?? RESULTS_DIR;
104
103
  if (!params.id && !params.runId && !params.dir) {
@@ -95,6 +95,7 @@ interface SubagentRunConfig {
95
95
  childIntercomTargets?: Array<string | undefined>;
96
96
  resultMode?: SubagentRunMode;
97
97
  nestedRoute?: NestedRouteInfo;
98
+ workflowStageSubagentGuard?: boolean;
98
99
  nestedSelf?: { parentRunId: string; parentStepIndex?: number; depth: number; path?: Array<{ runId: string; stepIndex?: number; agent?: string }> };
99
100
  }
100
101
 
@@ -215,13 +216,18 @@ function runPiStreaming(
215
216
  piPackageRoot?: string,
216
217
  piArgv1?: string,
217
218
  maxSubagentDepth?: number,
219
+ workflowStageSubagentGuard?: boolean,
218
220
  childEventContext?: ChildEventContext,
219
221
  registerInterrupt?: (interrupt: (() => void) | undefined) => void,
220
222
  onChildEvent?: (event: ChildEvent) => void,
221
223
  ): Promise<RunPiStreamingResult> {
222
224
  return new Promise((resolve) => {
223
225
  const outputStream = fs.createWriteStream(outputFile, { flags: "w" });
224
- const spawnEnv = { ...process.env, ...(env ?? {}), ...getSubagentDepthEnv(maxSubagentDepth) };
226
+ const spawnEnv = {
227
+ ...process.env,
228
+ ...(env ?? {}),
229
+ ...getSubagentDepthEnv(maxSubagentDepth, { workflowStageSubagentGuard }),
230
+ };
225
231
  const spawnSpec = getPiSpawnCommand(args, {
226
232
  ...(piPackageRoot ? { piPackageRoot } : {}),
227
233
  ...(piArgv1 ? { argv1: piArgv1 } : {}),
@@ -561,6 +567,7 @@ interface SingleStepContext {
561
567
  nestedRoute?: NestedRouteInfo;
562
568
  onAttemptStart?: (attempt: { model?: string; thinking?: string }) => void;
563
569
  onChildEvent?: (event: ChildEvent) => void;
570
+ workflowStageSubagentGuard?: boolean;
564
571
  }
565
572
 
566
573
  /** Run a single pi agent step, returning output and metadata */
@@ -647,6 +654,7 @@ async function runSingleStep(
647
654
  ctx.piPackageRoot,
648
655
  ctx.piArgv1,
649
656
  step.maxSubagentDepth,
657
+ step.workflowStageSubagentGuard ?? ctx.workflowStageSubagentGuard,
650
658
  { eventsPath, runId: ctx.id, stepIndex: ctx.flatIndex, agent: step.agent },
651
659
  ctx.registerInterrupt,
652
660
  ctx.onChildEvent,
@@ -1393,6 +1401,7 @@ async function runSubagent(config: SubagentRunConfig): Promise<void> {
1393
1401
  },
1394
1402
  onAttemptStart: (attempt) => updateStepModel(fi, attempt.model, attempt.thinking),
1395
1403
  onChildEvent: (event) => updateStepFromChildEvent(fi, event),
1404
+ workflowStageSubagentGuard: config.workflowStageSubagentGuard,
1396
1405
  });
1397
1406
  if (task.sessionFile) {
1398
1407
  latestSessionFile = task.sessionFile;
@@ -1539,6 +1548,7 @@ async function runSubagent(config: SubagentRunConfig): Promise<void> {
1539
1548
  },
1540
1549
  onAttemptStart: (attempt) => updateStepModel(flatIndex, attempt.model, attempt.thinking),
1541
1550
  onChildEvent: (event) => updateStepFromChildEvent(flatIndex, event),
1551
+ workflowStageSubagentGuard: config.workflowStageSubagentGuard,
1542
1552
  });
1543
1553
  if (seqStep.sessionFile) {
1544
1554
  latestSessionFile = seqStep.sessionFile;
@@ -775,7 +775,7 @@ export class ChainClarifyComponent implements Component {
775
775
 
776
776
  private handleEditInput(data: string): void {
777
777
  const textWidth = this.width - 4; // Must match render: innerW - 2 = (width - 2) - 2
778
- if (matchesKey(data, "shift+up") || matchesKey(data, "pageup")) {
778
+ if (matchesKey(data, "shift+up") || matchesKey(data, "pageUp")) {
779
779
  const { lines: wrapped, starts } = wrapText(this.editState.buffer, textWidth);
780
780
  const cursorPos = getCursorDisplayPos(this.editState.cursor, starts);
781
781
  const targetLine = Math.max(0, cursorPos.line - this.EDIT_VIEWPORT_HEIGHT);
@@ -785,7 +785,7 @@ export class ChainClarifyComponent implements Component {
785
785
  return;
786
786
  }
787
787
 
788
- if (matchesKey(data, "shift+down") || matchesKey(data, "pagedown")) {
788
+ if (matchesKey(data, "shift+down") || matchesKey(data, "pageDown")) {
789
789
  const { lines: wrapped, starts } = wrapText(this.editState.buffer, textWidth);
790
790
  const cursorPos = getCursorDisplayPos(this.editState.cursor, starts);
791
791
  const targetLine = Math.min(wrapped.length - 1, cursorPos.line + this.EDIT_VIEWPORT_HEIGHT);