@bastani/atomic 0.9.12 → 0.9.13-alpha.1
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 +20 -0
- package/dist/builtin/intercom/CHANGELOG.md +6 -0
- package/dist/builtin/intercom/README.md +5 -12
- package/dist/builtin/intercom/broker/client.ts +11 -2
- package/dist/builtin/intercom/contact-supervisor-tool.ts +13 -3
- package/dist/builtin/intercom/index-heavy.ts +13 -3
- package/dist/builtin/intercom/index.ts +43 -19
- package/dist/builtin/intercom/intercom-utils.ts +15 -29
- package/dist/builtin/intercom/package.json +1 -1
- package/dist/builtin/intercom/source-ownership.ts +9 -12
- package/dist/builtin/mcp/CHANGELOG.md +6 -0
- package/dist/builtin/mcp/README.md +3 -1
- package/dist/builtin/mcp/index.ts +10 -16
- package/dist/builtin/mcp/package.json +1 -1
- package/dist/builtin/mcp/startup-warmup.ts +20 -8
- package/dist/builtin/subagents/CHANGELOG.md +30 -0
- package/dist/builtin/subagents/README.md +22 -30
- package/dist/builtin/subagents/package.json +1 -1
- package/dist/builtin/subagents/src/extension/doctor.ts +1 -1
- package/dist/builtin/subagents/src/extension/fanout-child.ts +76 -56
- package/dist/builtin/subagents/src/extension/index.ts +23 -32
- package/dist/builtin/subagents/src/extension/startup-maintenance.ts +25 -44
- package/dist/builtin/subagents/src/intercom/result-intercom.ts +7 -7
- package/dist/builtin/subagents/src/runs/background/async-job-tracker.ts +166 -434
- package/dist/builtin/subagents/src/runs/background/notify.ts +29 -8
- package/dist/builtin/subagents/src/runs/foreground/chain-execution-dynamic-step.ts +2 -2
- package/dist/builtin/subagents/src/runs/foreground/chain-execution-parallel-runner.ts +4 -4
- package/dist/builtin/subagents/src/runs/foreground/chain-execution-parallel-step.ts +2 -2
- package/dist/builtin/subagents/src/runs/foreground/chain-execution-sequential-step.ts +2 -2
- package/dist/builtin/subagents/src/runs/foreground/execution-run-sync.ts +1 -281
- package/dist/builtin/subagents/src/runs/foreground/execution-structured-retries.ts +11 -75
- package/dist/builtin/subagents/src/runs/foreground/inprocess-run-sync.ts +457 -0
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor-async.ts +119 -246
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor-chain.ts +1 -1
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor-context.ts +18 -9
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor-parallel-task.ts +1 -1
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor-parallel.ts +4 -4
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor-resume.ts +1 -646
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor-runtime.ts +2 -7
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor-single.ts +5 -5
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor-status.ts +7 -8
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor-types.ts +14 -5
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor.ts +158 -93
- package/dist/builtin/subagents/src/runs/inprocess/background-single.ts +179 -0
- package/dist/builtin/subagents/src/runs/inprocess/background.ts +89 -0
- package/dist/builtin/subagents/src/runs/inprocess/child-policy.ts +27 -0
- package/dist/builtin/subagents/src/runs/inprocess/control-registry.ts +55 -0
- package/dist/builtin/subagents/src/runs/inprocess/control-status.ts +106 -0
- package/dist/builtin/subagents/src/runs/inprocess/index.ts +50 -0
- package/dist/builtin/subagents/src/runs/inprocess/nested-routing.ts +218 -0
- package/dist/builtin/subagents/src/runs/{shared/subagent-prompt-runtime.ts → inprocess/prompt-behavior.ts} +37 -75
- package/dist/builtin/subagents/src/runs/inprocess/runner.ts +1138 -0
- package/dist/builtin/subagents/src/runs/{shared/nested-events.ts → inprocess/runtime-support/nested-api.ts} +13 -7
- package/dist/builtin/subagents/src/runs/{shared/nested-events-control.ts → inprocess/runtime-support/nested-control.ts} +18 -17
- package/dist/builtin/subagents/src/runs/{shared/nested-events-core.ts → inprocess/runtime-support/nested-core.ts} +14 -61
- package/dist/builtin/subagents/src/runs/{shared/nested-events-projection.ts → inprocess/runtime-support/nested-projection.ts} +4 -10
- package/dist/builtin/subagents/src/runs/{shared/nested-events-registry.ts → inprocess/runtime-support/nested-registry.ts} +19 -4
- package/dist/builtin/subagents/src/runs/{shared/nested-render.ts → inprocess/runtime-support/nested-rendering.ts} +3 -3
- package/dist/builtin/subagents/src/runs/{shared/nested-events-sanitize.ts → inprocess/runtime-support/nested-sanitize.ts} +3 -3
- package/dist/builtin/subagents/src/runs/inprocess.ts +1 -0
- package/dist/builtin/subagents/src/runs/shared/dynamic-fanout.ts +10 -4
- package/dist/builtin/subagents/src/runs/shared/model-candidate-filter.ts +0 -1
- package/dist/builtin/subagents/src/runs/shared/model-fallback.ts +15 -492
- package/dist/builtin/subagents/src/runs/shared/parallel-utils.ts +13 -11
- package/dist/builtin/subagents/src/runs/shared/run-history.ts +3 -5
- package/dist/builtin/subagents/src/runs/shared/single-output.ts +4 -4
- package/dist/builtin/subagents/src/runs/shared/workflow-graph.ts +5 -5
- package/dist/builtin/subagents/src/shared/types-async.ts +0 -1
- package/dist/builtin/subagents/src/shared/types-config.ts +18 -0
- package/dist/builtin/subagents/src/shared/types-results.ts +9 -2
- package/dist/builtin/subagents/src/slash/prompt-template-bridge.ts +2 -3
- package/dist/builtin/subagents/src/slash/slash-live-state.ts +2 -2
- package/dist/builtin/subagents/src/tui/render-chain-graph.ts +10 -4
- package/dist/builtin/subagents/src/tui/render-event-formatting.ts +12 -11
- package/dist/builtin/subagents/src/tui/render-result-compact.ts +49 -12
- package/dist/builtin/subagents/src/tui/render-result.ts +46 -17
- package/dist/builtin/subagents/src/tui/render-stable-output.ts +1 -1
- package/dist/builtin/subagents/src/tui/render-status-progress.ts +8 -5
- package/dist/builtin/subagents/src/tui/render-widget-graph.ts +0 -8
- package/dist/builtin/subagents/src/tui/render-widget.ts +0 -24
- package/dist/builtin/web-access/package.json +1 -1
- package/dist/builtin/workflows/CHANGELOG.md +25 -0
- package/dist/builtin/workflows/README.md +13 -3
- package/dist/builtin/workflows/package.json +1 -1
- package/dist/builtin/workflows/src/durable/backend.ts +7 -0
- package/dist/builtin/workflows/src/durable/dbos-metadata.ts +11 -1
- package/dist/builtin/workflows/src/durable/resume-runtime.ts +1 -0
- package/dist/builtin/workflows/src/durable/tool-primitive.ts +4 -28
- package/dist/builtin/workflows/src/durable/types.ts +5 -0
- package/dist/builtin/workflows/src/engine/run.ts +17 -1
- package/dist/builtin/workflows/src/extension/atomic-stage-session.ts +12 -39
- package/dist/builtin/workflows/src/extension/config-file-loader.ts +10 -1
- package/dist/builtin/workflows/src/extension/config-loader.ts +1 -1
- package/dist/builtin/workflows/src/extension/dispatcher.ts +4 -0
- package/dist/builtin/workflows/src/extension/index.bundle.mjs +1151 -737
- package/dist/builtin/workflows/src/extension/lifecycle-notifications.ts +303 -38
- package/dist/builtin/workflows/src/extension/render-result.ts +4 -0
- package/dist/builtin/workflows/src/extension/runtime-durable-resume.ts +6 -3
- package/dist/builtin/workflows/src/extension/runtime.ts +7 -1
- package/dist/builtin/workflows/src/extension/wiring.ts +53 -11
- package/dist/builtin/workflows/src/extension/workflow-command-registration.ts +12 -3
- package/dist/builtin/workflows/src/extension/workflow-durable-resume-command.ts +5 -2
- package/dist/builtin/workflows/src/extension/workflow-run-control-command.ts +9 -7
- package/dist/builtin/workflows/src/extension/workflow-status-summary.ts +11 -0
- package/dist/builtin/workflows/src/extension/workflow-tool-content.ts +18 -1
- package/dist/builtin/workflows/src/extension/workflow-tool-control.ts +10 -8
- package/dist/builtin/workflows/src/extension/workflow-tool.ts +9 -4
- package/dist/builtin/workflows/src/runs/background/quit.ts +28 -4
- package/dist/builtin/workflows/src/runs/background/resume-acknowledgements.ts +2 -1
- package/dist/builtin/workflows/src/runs/background/status.ts +55 -8
- package/dist/builtin/workflows/src/runs/foreground/executor-direct-helpers.ts +4 -0
- package/dist/builtin/workflows/src/runs/foreground/executor-prompt-nodes.ts +3 -1
- package/dist/builtin/workflows/src/runs/foreground/executor-stage-control.ts +1 -1
- package/dist/builtin/workflows/src/runs/foreground/stage-runner-context.ts +1 -1
- package/dist/builtin/workflows/src/runs/foreground/stage-runner-controller.ts +688 -38
- package/dist/builtin/workflows/src/runs/foreground/stage-runner-types.ts +3 -1
- package/dist/builtin/workflows/src/runs/shared/model-fallback-failures.ts +19 -514
- package/dist/builtin/workflows/src/runs/shared/retry.ts +44 -0
- package/dist/builtin/workflows/src/shared/authoring-contract-stage.d.ts +5 -0
- package/dist/builtin/workflows/src/shared/authoring-contract-stage.ts +6 -0
- package/dist/builtin/workflows/src/shared/authoring-contract-ui.d.ts +5 -1
- package/dist/builtin/workflows/src/shared/authoring-contract-ui.ts +5 -0
- package/dist/builtin/workflows/src/shared/persistence-restore-helpers.ts +5 -0
- package/dist/builtin/workflows/src/shared/persistence-restore.ts +3 -0
- package/dist/builtin/workflows/src/shared/persistence-session-entries.ts +4 -0
- package/dist/builtin/workflows/src/shared/run-indicator-status.ts +75 -0
- package/dist/builtin/workflows/src/shared/store-public-types.ts +43 -7
- package/dist/builtin/workflows/src/shared/store-run-methods.ts +42 -8
- package/dist/builtin/workflows/src/shared/store-stage-methods.ts +31 -8
- package/dist/builtin/workflows/src/shared/store-types.ts +34 -0
- package/dist/builtin/workflows/src/shared/timing.ts +10 -0
- package/dist/builtin/workflows/src/tui/chat-surface-message.ts +22 -2
- package/dist/builtin/workflows/src/tui/session-list.ts +1 -1
- package/dist/builtin/workflows/src/tui/session-overlays.ts +14 -10
- package/dist/builtin/workflows/src/tui/session-picker.ts +20 -4
- package/dist/builtin/workflows/src/tui/status-list.ts +31 -47
- package/dist/builtin/workflows/src/tui/store-widget-installer.ts +9 -1
- package/dist/builtin/workflows/src/tui/widget.ts +16 -46
- package/dist/core/agent-session-auto-compaction.d.ts.map +1 -1
- package/dist/core/agent-session-auto-compaction.js +54 -11
- package/dist/core/agent-session-auto-compaction.js.map +1 -1
- package/dist/core/agent-session-events.d.ts.map +1 -1
- package/dist/core/agent-session-events.js +49 -14
- package/dist/core/agent-session-events.js.map +1 -1
- package/dist/core/agent-session-methods.d.ts +13 -0
- package/dist/core/agent-session-methods.d.ts.map +1 -1
- package/dist/core/agent-session-methods.js.map +1 -1
- package/dist/core/agent-session-models.d.ts.map +1 -1
- package/dist/core/agent-session-models.js +12 -1
- package/dist/core/agent-session-models.js.map +1 -1
- package/dist/core/agent-session-prompt.d.ts.map +1 -1
- package/dist/core/agent-session-prompt.js +4 -0
- package/dist/core/agent-session-prompt.js.map +1 -1
- package/dist/core/agent-session-retry.d.ts +21 -4
- package/dist/core/agent-session-retry.d.ts.map +1 -1
- package/dist/core/agent-session-retry.js +232 -106
- package/dist/core/agent-session-retry.js.map +1 -1
- package/dist/core/agent-session-state.d.ts.map +1 -1
- package/dist/core/agent-session-state.js +2 -1
- package/dist/core/agent-session-state.js.map +1 -1
- package/dist/core/agent-session-tool-hooks.d.ts.map +1 -1
- package/dist/core/agent-session-tool-hooks.js +12 -0
- package/dist/core/agent-session-tool-hooks.js.map +1 -1
- package/dist/core/agent-session-tool-registry.d.ts.map +1 -1
- package/dist/core/agent-session-tool-registry.js +1 -1
- package/dist/core/agent-session-tool-registry.js.map +1 -1
- package/dist/core/agent-session-types.d.ts +3 -1
- package/dist/core/agent-session-types.d.ts.map +1 -1
- package/dist/core/agent-session-types.js.map +1 -1
- package/dist/core/agent-session.d.ts +12 -1
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +7 -0
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/extensions/context-types.d.ts +25 -0
- package/dist/core/extensions/context-types.d.ts.map +1 -1
- package/dist/core/extensions/context-types.js.map +1 -1
- package/dist/core/extensions/index.d.ts +1 -1
- package/dist/core/extensions/index.d.ts.map +1 -1
- package/dist/core/extensions/index.js.map +1 -1
- package/dist/core/extensions/runner-context.d.ts +2 -1
- package/dist/core/extensions/runner-context.d.ts.map +1 -1
- package/dist/core/extensions/runner-context.js +4 -0
- package/dist/core/extensions/runner-context.js.map +1 -1
- package/dist/core/extensions/runner.d.ts +3 -2
- package/dist/core/extensions/runner.d.ts.map +1 -1
- package/dist/core/extensions/runner.js +3 -1
- package/dist/core/extensions/runner.js.map +1 -1
- package/dist/core/model-fallback-failures.d.ts +30 -0
- package/dist/core/model-fallback-failures.d.ts.map +1 -0
- package/dist/core/model-fallback-failures.js +610 -0
- package/dist/core/model-fallback-failures.js.map +1 -0
- package/dist/core/retry-policy.d.ts +30 -0
- package/dist/core/retry-policy.d.ts.map +1 -0
- package/dist/core/retry-policy.js +25 -0
- package/dist/core/retry-policy.js.map +1 -0
- package/dist/core/sdk-types.d.ts +8 -2
- package/dist/core/sdk-types.d.ts.map +1 -1
- package/dist/core/sdk-types.js.map +1 -1
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +6 -1
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-manager-core.d.ts +2 -0
- package/dist/core/session-manager-core.d.ts.map +1 -1
- package/dist/core/session-manager-core.js +7 -0
- package/dist/core/session-manager-core.js.map +1 -1
- package/dist/index-extensions.d.ts +1 -1
- package/dist/index-extensions.d.ts.map +1 -1
- package/dist/index-extensions.js.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/docs/intercom.md +5 -10
- package/docs/settings.md +7 -1
- package/docs/subagents.md +19 -19
- package/docs/workflows.md +21 -5
- package/examples/extensions/gondolin/package-lock.json +3 -3
- package/npm-shrinkwrap.json +29 -29
- package/package.json +2 -2
- package/dist/builtin/subagents/src/runs/background/async-event-journal.ts +0 -112
- package/dist/builtin/subagents/src/runs/background/async-execution-chain.ts +0 -543
- package/dist/builtin/subagents/src/runs/background/async-execution-common.ts +0 -140
- package/dist/builtin/subagents/src/runs/background/async-execution-single.ts +0 -279
- package/dist/builtin/subagents/src/runs/background/async-execution-types.ts +0 -99
- package/dist/builtin/subagents/src/runs/background/async-execution.ts +0 -17
- package/dist/builtin/subagents/src/runs/background/async-resume.ts +0 -402
- package/dist/builtin/subagents/src/runs/background/async-status.ts +0 -411
- package/dist/builtin/subagents/src/runs/background/completion-claims.ts +0 -196
- package/dist/builtin/subagents/src/runs/background/completion-dedupe.ts +0 -102
- package/dist/builtin/subagents/src/runs/background/parallel-groups.ts +0 -59
- package/dist/builtin/subagents/src/runs/background/result-delivery-processor.ts +0 -300
- package/dist/builtin/subagents/src/runs/background/result-file-claims.ts +0 -166
- package/dist/builtin/subagents/src/runs/background/result-quarantine.ts +0 -77
- package/dist/builtin/subagents/src/runs/background/result-retry-scheduler.ts +0 -48
- package/dist/builtin/subagents/src/runs/background/result-status.ts +0 -87
- package/dist/builtin/subagents/src/runs/background/result-watcher-data.ts +0 -72
- package/dist/builtin/subagents/src/runs/background/result-watcher.ts +0 -320
- package/dist/builtin/subagents/src/runs/background/run-id-resolver.ts +0 -105
- package/dist/builtin/subagents/src/runs/background/run-status.ts +0 -358
- package/dist/builtin/subagents/src/runs/background/stale-run-reconciler.ts +0 -457
- package/dist/builtin/subagents/src/runs/background/subagent-runner-dynamic.ts +0 -376
- package/dist/builtin/subagents/src/runs/background/subagent-runner-finalize.ts +0 -150
- package/dist/builtin/subagents/src/runs/background/subagent-runner-output.ts +0 -109
- package/dist/builtin/subagents/src/runs/background/subagent-runner-parallel-helpers.ts +0 -128
- package/dist/builtin/subagents/src/runs/background/subagent-runner-parallel.ts +0 -309
- package/dist/builtin/subagents/src/runs/background/subagent-runner-sequential.ts +0 -173
- package/dist/builtin/subagents/src/runs/background/subagent-runner-state.ts +0 -578
- package/dist/builtin/subagents/src/runs/background/subagent-runner-step.ts +0 -330
- package/dist/builtin/subagents/src/runs/background/subagent-runner-streaming.ts +0 -330
- package/dist/builtin/subagents/src/runs/background/subagent-runner-types.ts +0 -210
- package/dist/builtin/subagents/src/runs/background/subagent-runner-utils.ts +0 -69
- package/dist/builtin/subagents/src/runs/background/subagent-runner.ts +0 -90
- package/dist/builtin/subagents/src/runs/background/top-level-async.ts +0 -12
- package/dist/builtin/subagents/src/runs/foreground/execution-attempt-control.ts +0 -140
- package/dist/builtin/subagents/src/runs/foreground/execution-attempt-finalize.ts +0 -108
- package/dist/builtin/subagents/src/runs/foreground/execution-attempt-types.ts +0 -17
- package/dist/builtin/subagents/src/runs/foreground/execution-attempt.ts +0 -558
- package/dist/builtin/subagents/src/runs/shared/attempt-watchdog.ts +0 -126
- package/dist/builtin/subagents/src/runs/shared/final-drain.ts +0 -39
- package/dist/builtin/subagents/src/runs/shared/pi-args.ts +0 -339
- package/dist/builtin/subagents/src/runs/shared/pi-spawn.ts +0 -165
- package/dist/builtin/subagents/src/runs/shared/spawn-env.ts +0 -21
- package/dist/builtin/subagents/src/shared/post-exit-stdio-guard.ts +0 -86
- /package/dist/builtin/subagents/src/runs/{shared/nested-path.ts → inprocess/runtime-support/nested-paths.ts} +0 -0
|
@@ -162,9 +162,9 @@ Workflow invocations receive a stable, non-`default` Intercom group automaticall
|
|
|
162
162
|
|
|
163
163
|
Foreground runs stream progress in the conversation while they run.
|
|
164
164
|
|
|
165
|
-
|
|
165
|
+
`async: true` means **do not wait**. Atomic admits an in-process child, returns its canonical child path immediately, and tracks the live child through the jobs widget. **Async work does not survive parent exit:** the live child is owned by the parent process and ends when Atomic quits; only its canonical identity and session file remain for a later cold resume.
|
|
166
166
|
|
|
167
|
-
|
|
167
|
+
The jobs widget shows the live status-watch state and sends one terminal completion notification. Parallel background runs show per-agent progress, and chains with parallel groups keep their grouped shape in progress and results. There is no detached runner process or PID polling loop.
|
|
168
168
|
|
|
169
169
|
You can also ask naturally:
|
|
170
170
|
|
|
@@ -238,7 +238,7 @@ The child can use one dedicated coordination tool:
|
|
|
238
238
|
|
|
239
239
|
- `contact_supervisor`: the child contacts the parent/supervisor session that delegated the task. Use `reason: "need_decision"` for blocking decisions or clarification, and `reason: "progress_update"` for short non-blocking updates when a discovery changes the plan. Do not ask for clarification when the only conflict is review-only/no-edit versus progress-writing or artifact-writing instructions; no-edit wins.
|
|
240
240
|
|
|
241
|
-
Child-side routine completion handoffs are still not expected. With the intercom bridge active, parent-side
|
|
241
|
+
Child-side routine completion handoffs are still not expected. With the intercom bridge active, parent-side Atomic sends grouped completion results through `pi-intercom`: one grouped message per foreground parent `subagent` run and one per completed async terminal result. Intercom-confirmed foreground delivery returns a compact receipt with artifact/session paths; without that confirmation, the normal full output is preserved. Grouped messages include child intercom targets and full child summaries.
|
|
242
242
|
When the companion is enabled and available, the bridge gives eligible children deterministic Intercom identities and coordination tools without connecting them automatically. Parent and child connections remain tool-driven: if a child may need live coordination, the parent model should invoke `intercom({ action: "status" })` before launch, and the child connects when it invokes `contact_supervisor` or `intercom`. Foreground/background launch and management-only actions do not force Intercom loading or broker startup.
|
|
243
243
|
|
|
244
244
|
For foreground runs, Intercom uses a targeted probe/reservation before delivery: only the exact live child can claim its message. Atomic then commits detach for that child and waits for its acknowledgement before placing claimed asks, sends, decisions, interviews, and progress updates in the parent's model-visible steering queue, so cancellation between phases cannot surface an orphaned request. Blocking calls remain alive for an exact threaded reply and then resume; fire-and-forget calls create no waiter. The retained child later replaces its detached status and artifacts with the real result. Cancellation/replacement invalidates stale handshakes, duplicate delivery cannot recommit, and background or unmatched messages retain queued-until-idle behavior.
|
|
@@ -344,7 +344,7 @@ You can combine them in either order:
|
|
|
344
344
|
/run reviewer "review this diff" --bg --fork
|
|
345
345
|
```
|
|
346
346
|
|
|
347
|
-
Background runs
|
|
347
|
+
Background runs use the same in-process continuation as foreground detach. A successful acknowledgement means the canonical child path was returned and completion is pending; the live child remains owned by the parent process and the jobs widget tracks it. **`async: true` does not survive parent exit.** If the parent exits, the in-flight run ends; its persisted identity/session can be listed and resumed later.
|
|
348
348
|
|
|
349
349
|
The `oracle` and `worker` builtins are designed for an explicit decision loop. A typical pattern is to ask `oracle` for diagnosis and a recommended execution prompt, then only run `worker` after the main agent approves that direction.
|
|
350
350
|
|
|
@@ -356,7 +356,7 @@ The human slash commands remain on their separate parsing and event-bridge path,
|
|
|
356
356
|
|
|
357
357
|
## Agents and chains
|
|
358
358
|
|
|
359
|
-
Agents are markdown files with YAML frontmatter and a system prompt body. They define the specialist that will run in
|
|
359
|
+
Agents are markdown files with YAML frontmatter and a system prompt body. They define the specialist that will run in a child Atomic session.
|
|
360
360
|
|
|
361
361
|
Agent locations, lowest to highest priority:
|
|
362
362
|
|
|
@@ -837,7 +837,7 @@ subagent({ action: "resume", id: "<run-id>", index: 1, message: "follow-up for c
|
|
|
837
837
|
subagent({ action: "doctor" })
|
|
838
838
|
```
|
|
839
839
|
|
|
840
|
-
`resume` sends the follow-up directly when
|
|
840
|
+
`resume` sends the follow-up directly when a child is still reachable. After completion or eviction, it cold-reloads the same canonical child identity from the stored session file. Multi-child async runs and remembered foreground single, parallel, or chain runs can be revived by passing `index` to choose the child; no new OS process is created.
|
|
841
841
|
|
|
842
842
|
## Worktree isolation
|
|
843
843
|
|
|
@@ -919,7 +919,7 @@ Session directory precedence is: `params.sessionDir`, then `config.defaultSessio
|
|
|
919
919
|
{ "maxSubagentDepth": 1 }
|
|
920
920
|
```
|
|
921
921
|
|
|
922
|
-
Controls nested delegation
|
|
922
|
+
Controls nested delegation through typed admission policy. Accepted values are `0` through `5`; higher values are refused at the hard ceiling. Per-agent `maxSubagentDepth` can tighten the limit for that agent’s child runs, but cannot relax an inherited stricter limit.
|
|
923
923
|
|
|
924
924
|
### `intercomBridge`
|
|
925
925
|
|
|
@@ -979,25 +979,24 @@ Debug artifacts live under `{sessionDir}/subagent-artifacts/` or a user-scoped t
|
|
|
979
979
|
- `{runId}_{agent}.jsonl`
|
|
980
980
|
- `{runId}_{agent}_meta.json`
|
|
981
981
|
|
|
982
|
-
Metadata records timing, usage,
|
|
982
|
+
Metadata records timing, usage, typed status, termination cause, final model, attempted models, and fallback attempt outcomes.
|
|
983
983
|
|
|
984
|
-
Session files are stored under a per-run session directory. With `context: "fork"`, each child starts
|
|
984
|
+
Session files are stored under a per-run session directory. With `context: "fork"`, each child starts from the parent’s current leaf through the session manager; this is a real session fork, not an injected summary.
|
|
985
985
|
|
|
986
|
-
Async completions notify only the originating session. The
|
|
986
|
+
Async completions notify only the originating session. The in-process status watch emits live lifecycle updates, and the extension consumes the terminal event to render completion notifications.
|
|
987
987
|
|
|
988
|
-
Async runs
|
|
988
|
+
Async runs persist their durable session and user-facing artifacts beside the parent session:
|
|
989
989
|
|
|
990
990
|
```text
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
991
|
+
{parent-session-dir}/subagent-artifacts/
|
|
992
|
+
{runId}_{agent}_input.md
|
|
993
|
+
{runId}_{agent}_output.md
|
|
994
|
+
{runId}_{agent}.jsonl
|
|
995
|
+
{runId}_{agent}_meta.json
|
|
996
|
+
run-history.jsonl
|
|
996
997
|
```
|
|
997
998
|
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
The result watcher waits for modern run status to become terminal using capped exponential rechecks, so a late repaired status still delivers without a fixed-rate polling loop. Intercom confirmation and local synchronous acceptance are tracked as separate phases; a successful phase is not replayed when another phase retries or the watcher is replaced. Equivalent result aliases coalesce by canonical run identity, but aliases with different user-visible output or parent targets are retained under collision-resistant names in the non-scanned `<results>/.undelivered/` directory. The same directory retains a still-owned result after a finite no-progress retry budget, and Atomic logs the retained path instead of retrying forever, overwriting earlier evidence, or deleting the payload.
|
|
999
|
+
The Rust registry and status watch power the widget and `subagent({ action: "status" })` output. Terminal delivery is an in-memory bounded envelope persisted once with typed `status`, `cause`, and `stats`; there is no `status.json`, `events.jsonl`, PID reconciler, result watcher, or claim pipeline.
|
|
1001
1000
|
|
|
1002
1001
|
## Completion and output
|
|
1003
1002
|
|
|
@@ -1039,17 +1038,10 @@ By default, nesting is capped at five delegated subagent levels below the main s
|
|
|
1039
1038
|
|
|
1040
1039
|
Configure a lower or equal limit with:
|
|
1041
1040
|
|
|
1042
|
-
1. `
|
|
1043
|
-
2. `
|
|
1044
|
-
3. `maxSubagentDepth` in agent frontmatter, which can only tighten the inherited limit
|
|
1045
|
-
|
|
1046
|
-
```bash
|
|
1047
|
-
export ATOMIC_SUBAGENT_MAX_DEPTH=5
|
|
1048
|
-
export ATOMIC_SUBAGENT_MAX_DEPTH=3
|
|
1049
|
-
export ATOMIC_SUBAGENT_MAX_DEPTH=0
|
|
1050
|
-
```
|
|
1041
|
+
1. `config.maxSubagentDepth`
|
|
1042
|
+
2. `maxSubagentDepth` in agent frontmatter, which can only tighten the admitted limit
|
|
1051
1043
|
|
|
1052
|
-
|
|
1044
|
+
The depth policy is typed admission state and is not inherited through an environment variable.
|
|
1053
1045
|
|
|
1054
1046
|
## Events
|
|
1055
1047
|
|
|
@@ -1063,7 +1055,7 @@ Intercom delivery events:
|
|
|
1063
1055
|
- `subagent:control-intercom`
|
|
1064
1056
|
- `subagent:result-intercom`
|
|
1065
1057
|
|
|
1066
|
-
The
|
|
1058
|
+
The in-process status watch emits `subagent:async-complete`; `src/extension/index.ts` registers the notification handler that consumes it. Control/attention events are surfaced as visible parent notices, and typed terminal records carry the canonical path, status, cause, and session statistics. With `pi-intercom`, needs-attention notices and grouped parent-side subagent result deliveries can reach the orchestrator over intercom.
|
|
1067
1059
|
|
|
1068
1060
|
## Prompt-template integration
|
|
1069
1061
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bastani/subagents",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.13-alpha.1",
|
|
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": [
|
|
@@ -3,7 +3,7 @@ import * as path from "node:path";
|
|
|
3
3
|
import { type AgentSource, discoverAgentsAll } from "../agents/agents.ts";
|
|
4
4
|
import { discoverAvailableSkills, type SkillSource } from "../agents/skills.ts";
|
|
5
5
|
import { diagnoseIntercomBridge, type IntercomBridgeDiagnostic } from "../intercom/intercom-bridge.ts";
|
|
6
|
-
import { isAsyncAvailable } from "../runs/background
|
|
6
|
+
import { isAsyncAvailable } from "../runs/inprocess/background.ts";
|
|
7
7
|
import {
|
|
8
8
|
ASYNC_DIR,
|
|
9
9
|
CHAIN_RUNS_DIR,
|
|
@@ -2,19 +2,20 @@ import * as fs from "node:fs";
|
|
|
2
2
|
import * as os from "node:os";
|
|
3
3
|
import * as path from "node:path";
|
|
4
4
|
import type { ExtensionAPI, ToolDefinition } from "@bastani/atomic";
|
|
5
|
-
import { APP_NAME
|
|
5
|
+
import { APP_NAME } from "@bastani/atomic";
|
|
6
6
|
|
|
7
7
|
import { discoverAgents } from "../agents/agents.ts";
|
|
8
8
|
import { resolveSubagentIntercomTarget } from "../intercom/intercom-bridge.ts";
|
|
9
9
|
import { deliverSubagentIntercomMessageEvent } from "../intercom/result-intercom.ts";
|
|
10
10
|
import { createSubagentExecutor, type SubagentParamsLike } from "../runs/foreground/subagent-executor.ts";
|
|
11
|
+
import type { ChildModePolicy } from "../runs/inprocess/child-policy.ts";
|
|
12
|
+
import { hasInProcessNestedRoute, subscribeInProcessControlRequests } from "../runs/inprocess/nested-routing.ts";
|
|
11
13
|
import {
|
|
12
14
|
type NestedRoute,
|
|
13
15
|
readNestedControlRequests,
|
|
14
16
|
resolveNestedRouteFromEnv,
|
|
15
17
|
writeNestedControlResult,
|
|
16
|
-
} from "../runs/
|
|
17
|
-
import { SUBAGENT_CHILD_ENV, SUBAGENT_FANOUT_CHILD_ENV } from "../runs/shared/pi-args.ts";
|
|
18
|
+
} from "../runs/inprocess/runtime-support/nested-api.ts";
|
|
18
19
|
import { getArtifactsDir } from "../shared/artifacts.ts";
|
|
19
20
|
import type { Details, SubagentState } from "../shared/types.ts";
|
|
20
21
|
import { beginApiLifecycle } from "./api-lifecycle.ts";
|
|
@@ -63,8 +64,6 @@ const TRIMMED_SEEN_REQUEST_IDS = 512;
|
|
|
63
64
|
const MAX_PENDING_RESULTS = 256;
|
|
64
65
|
const MAX_PENDING_RESULT_ATTEMPTS = 10;
|
|
65
66
|
const MAX_PENDING_RESULT_AGE_MS = 60_000;
|
|
66
|
-
const NESTED_CONTROL_INBOX_TIMER_KEY = "fanout-child:nested-control-inbox";
|
|
67
|
-
|
|
68
67
|
type NestedControlResultPayload = Parameters<typeof writeNestedControlResult>[1];
|
|
69
68
|
|
|
70
69
|
interface PendingControlResult {
|
|
@@ -139,7 +138,15 @@ function buildNestedControlResult(
|
|
|
139
138
|
})();
|
|
140
139
|
}
|
|
141
140
|
|
|
142
|
-
|
|
141
|
+
const nestedControlListenerCleanups = new WeakMap<ExtensionAPI, () => void>();
|
|
142
|
+
|
|
143
|
+
export function stopNestedControlInboxListener(pi: ExtensionAPI): void {
|
|
144
|
+
nestedControlListenerCleanups.get(pi)?.();
|
|
145
|
+
nestedControlListenerCleanups.delete(pi);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export function startNestedControlInboxListener(pi: ExtensionAPI, state: SubagentState): (() => void) | undefined {
|
|
149
|
+
stopNestedControlInboxListener(pi);
|
|
143
150
|
let route: NestedRoute | undefined;
|
|
144
151
|
try {
|
|
145
152
|
route = resolveNestedRouteFromEnv();
|
|
@@ -150,70 +157,82 @@ export function startNestedControlInboxListener(pi: ExtensionAPI, state: Subagen
|
|
|
150
157
|
const seen = new Set<string>();
|
|
151
158
|
const inFlight = new Set<string>();
|
|
152
159
|
const pendingResults = new Map<string, PendingControlResult>();
|
|
153
|
-
const
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
pendingResults.set(request.requestId, nextPending);
|
|
172
|
-
if (shouldDropPendingResult(nextPending, pendingResults.size, now)) {
|
|
173
|
-
pendingResults.delete(request.requestId);
|
|
174
|
-
rememberSeenRequest(seen, request.requestId);
|
|
175
|
-
dropControlRequestFile(request.filePath, request.requestId);
|
|
176
|
-
console.error(
|
|
177
|
-
`Dropping nested control result for request '${request.requestId}' targeting '${request.targetRunId}' after ${nextPending.attempts} failed write attempts via inbox '${route.controlInbox}':`,
|
|
178
|
-
error,
|
|
179
|
-
);
|
|
180
|
-
} else if (nextPending.attempts === 1) {
|
|
181
|
-
console.error(
|
|
182
|
-
`Failed to write nested control result for request '${request.requestId}' targeting '${request.targetRunId}' via inbox '${route.controlInbox}'; keeping request for retry:`,
|
|
183
|
-
error,
|
|
184
|
-
);
|
|
185
|
-
}
|
|
186
|
-
return;
|
|
187
|
-
}
|
|
160
|
+
const processRequest = (request: ReturnType<typeof readNestedControlRequests>[number]): void => {
|
|
161
|
+
if (seen.has(request.requestId) || inFlight.has(request.requestId)) return;
|
|
162
|
+
inFlight.add(request.requestId);
|
|
163
|
+
void (async () => {
|
|
164
|
+
try {
|
|
165
|
+
const pending = pendingResults.get(request.requestId);
|
|
166
|
+
const result = pending?.result ?? (await buildNestedControlResult(pi, state, request));
|
|
167
|
+
try {
|
|
168
|
+
writeNestedControlResult(route!, result);
|
|
169
|
+
} catch (error) {
|
|
170
|
+
const now = Date.now();
|
|
171
|
+
const nextPending: PendingControlResult = {
|
|
172
|
+
result,
|
|
173
|
+
firstFailureAt: pending?.firstFailureAt ?? now,
|
|
174
|
+
attempts: (pending?.attempts ?? 0) + 1,
|
|
175
|
+
};
|
|
176
|
+
pendingResults.set(request.requestId, nextPending);
|
|
177
|
+
if (shouldDropPendingResult(nextPending, pendingResults.size, now)) {
|
|
188
178
|
pendingResults.delete(request.requestId);
|
|
189
179
|
rememberSeenRequest(seen, request.requestId);
|
|
190
180
|
dropControlRequestFile(request.filePath, request.requestId);
|
|
191
|
-
|
|
192
|
-
|
|
181
|
+
console.error(
|
|
182
|
+
`Dropping nested control result for request '${request.requestId}' targeting '${request.targetRunId}' after ${nextPending.attempts} failed writes via inbox '${route!.controlInbox}':`,
|
|
183
|
+
error,
|
|
184
|
+
);
|
|
185
|
+
} else if (nextPending.attempts === 1) {
|
|
186
|
+
console.error(
|
|
187
|
+
`Failed to write nested control result for request '${request.requestId}' targeting '${request.targetRunId}' via inbox '${route!.controlInbox}'; keeping request for retry:`,
|
|
188
|
+
error,
|
|
189
|
+
);
|
|
193
190
|
}
|
|
194
|
-
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
pendingResults.delete(request.requestId);
|
|
194
|
+
rememberSeenRequest(seen, request.requestId);
|
|
195
|
+
dropControlRequestFile(request.filePath, request.requestId);
|
|
196
|
+
} finally {
|
|
197
|
+
inFlight.delete(request.requestId);
|
|
195
198
|
}
|
|
199
|
+
})();
|
|
200
|
+
};
|
|
201
|
+
const processPending = (): void => {
|
|
202
|
+
for (const request of readNestedControlRequests(route!)) processRequest(request);
|
|
203
|
+
};
|
|
204
|
+
processPending();
|
|
205
|
+
const memoryCleanup = hasInProcessNestedRoute(route)
|
|
206
|
+
? subscribeInProcessControlRequests(route, processRequest)
|
|
207
|
+
: undefined;
|
|
208
|
+
let watcher: fs.FSWatcher | undefined;
|
|
209
|
+
if (!memoryCleanup) {
|
|
210
|
+
try {
|
|
211
|
+
watcher = fs.watch(route.controlInbox, { persistent: false }, () => processPending());
|
|
196
212
|
} catch (error) {
|
|
197
|
-
console.error(
|
|
198
|
-
`Failed to poll nested control inbox '${route.controlInbox}' for root '${route.rootRunId}':`,
|
|
199
|
-
error,
|
|
200
|
-
);
|
|
213
|
+
console.error(`Failed to watch nested control inbox '${route.controlInbox}':`, error);
|
|
201
214
|
}
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
215
|
+
}
|
|
216
|
+
const cleanup = () => {
|
|
217
|
+
memoryCleanup?.();
|
|
218
|
+
watcher?.close();
|
|
219
|
+
if (nestedControlListenerCleanups.get(pi) === cleanup) nestedControlListenerCleanups.delete(pi);
|
|
220
|
+
};
|
|
221
|
+
nestedControlListenerCleanups.set(pi, cleanup);
|
|
222
|
+
return cleanup;
|
|
206
223
|
}
|
|
207
224
|
|
|
208
|
-
export default function registerFanoutChildSubagentExtension(pi: ExtensionAPI): void {
|
|
209
|
-
if (
|
|
225
|
+
export default function registerFanoutChildSubagentExtension(pi: ExtensionAPI, childPolicy?: ChildModePolicy): void {
|
|
226
|
+
if (!childPolicy?.fanoutAuthorized) return;
|
|
210
227
|
|
|
211
228
|
const lifecycle = beginApiLifecycle(pi);
|
|
212
229
|
|
|
213
230
|
try {
|
|
214
231
|
const config = loadConfig();
|
|
215
232
|
const state = createChildSafeState();
|
|
233
|
+
let nestedListenerCleanup: (() => void) | undefined;
|
|
216
234
|
lifecycle.setCleanup(() => {
|
|
235
|
+
nestedListenerCleanup?.();
|
|
217
236
|
for (const timer of state.cleanupTimers.values()) clearInterval(timer);
|
|
218
237
|
state.cleanupTimers.clear();
|
|
219
238
|
});
|
|
@@ -226,7 +245,8 @@ export default function registerFanoutChildSubagentExtension(pi: ExtensionAPI):
|
|
|
226
245
|
getSubagentSessionRoot,
|
|
227
246
|
expandTilde,
|
|
228
247
|
discoverAgents,
|
|
229
|
-
|
|
248
|
+
childPolicy,
|
|
249
|
+
allowMutatingManagementActions: childPolicy ? childPolicy.managementActions === "full" : false,
|
|
230
250
|
});
|
|
231
251
|
|
|
232
252
|
const tool: ToolDefinition<typeof SubagentParams, Details> = {
|
|
@@ -246,7 +266,7 @@ export default function registerFanoutChildSubagentExtension(pi: ExtensionAPI):
|
|
|
246
266
|
};
|
|
247
267
|
|
|
248
268
|
pi.registerTool(tool);
|
|
249
|
-
startNestedControlInboxListener(pi, state);
|
|
269
|
+
nestedListenerCleanup = startNestedControlInboxListener(pi, state);
|
|
250
270
|
pi.on?.("session_shutdown", () => lifecycle.dispose());
|
|
251
271
|
} catch (error) {
|
|
252
272
|
lifecycle.dispose();
|
|
@@ -6,7 +6,6 @@ import {
|
|
|
6
6
|
APP_NAME,
|
|
7
7
|
type ExtensionAPI,
|
|
8
8
|
type ExtensionContext,
|
|
9
|
-
getEnvValue,
|
|
10
9
|
keyHintIfBound,
|
|
11
10
|
type ToolDefinition,
|
|
12
11
|
} from "@bastani/atomic";
|
|
@@ -25,7 +24,6 @@ import { discoverAgents } from "../agents/agents.ts";
|
|
|
25
24
|
import { createAsyncJobTracker } from "../runs/background/async-job-tracker.ts";
|
|
26
25
|
import registerSubagentNotify, { type SubagentNotifyDetails } from "../runs/background/notify.ts";
|
|
27
26
|
import { createSubagentExecutor, type SubagentParamsLike } from "../runs/foreground/subagent-executor.ts";
|
|
28
|
-
import { SUBAGENT_CHILD_ENV, SUBAGENT_FANOUT_CHILD_ENV } from "../runs/shared/pi-args.ts";
|
|
29
27
|
import { getArtifactsDir } from "../shared/artifacts.ts";
|
|
30
28
|
import { formatDuration, shortenPath } from "../shared/formatters.ts";
|
|
31
29
|
import { resolveCurrentSessionId } from "../shared/session-identity.ts";
|
|
@@ -48,7 +46,6 @@ import {
|
|
|
48
46
|
stopWidgetAnimation,
|
|
49
47
|
} from "../tui/render.ts";
|
|
50
48
|
import { loadConfig } from "./config.ts";
|
|
51
|
-
import registerFanoutChildSubagentExtension from "./fanout-child.ts";
|
|
52
49
|
import { parseSubagentNotifyContent } from "./notification-content.ts";
|
|
53
50
|
import { DEFAULT_PROMPT_GUIDANCE } from "./prompt-guidance.ts";
|
|
54
51
|
import { SubagentParams } from "./schemas.ts";
|
|
@@ -60,7 +57,6 @@ import {
|
|
|
60
57
|
ASYNC_DIR,
|
|
61
58
|
DEFAULT_ARTIFACT_CONFIG,
|
|
62
59
|
type Details,
|
|
63
|
-
RESULTS_DIR,
|
|
64
60
|
SLASH_RESULT_TYPE,
|
|
65
61
|
SUBAGENT_ASYNC_COMPLETE_EVENT,
|
|
66
62
|
SUBAGENT_ASYNC_STARTED_EVENT,
|
|
@@ -90,18 +86,6 @@ function getSubagentSessionRoot(parentSessionFile: string | null): string {
|
|
|
90
86
|
function expandTilde(p: string): string {
|
|
91
87
|
return p.startsWith("~/") ? path.join(os.homedir(), p.slice(2)) : p;
|
|
92
88
|
}
|
|
93
|
-
function ensureAccessibleDir(dirPath: string): void {
|
|
94
|
-
fs.mkdirSync(dirPath, { recursive: true });
|
|
95
|
-
try {
|
|
96
|
-
fs.accessSync(dirPath, fs.constants.R_OK | fs.constants.W_OK);
|
|
97
|
-
} catch {
|
|
98
|
-
try {
|
|
99
|
-
fs.rmSync(dirPath, { recursive: true, force: true });
|
|
100
|
-
} catch {}
|
|
101
|
-
fs.mkdirSync(dirPath, { recursive: true });
|
|
102
|
-
fs.accessSync(dirPath, fs.constants.R_OK | fs.constants.W_OK);
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
89
|
function isSlashResultRunning(result: { details?: Details }): boolean {
|
|
106
90
|
return (
|
|
107
91
|
result.details?.progress?.some((entry) => entry.status === "running") ||
|
|
@@ -111,7 +95,7 @@ function isSlashResultRunning(result: { details?: Details }): boolean {
|
|
|
111
95
|
}
|
|
112
96
|
function isSlashResultError(result: { details?: Details }): boolean {
|
|
113
97
|
return (
|
|
114
|
-
result.details?.results.some((entry) => entry.
|
|
98
|
+
result.details?.results.some((entry) => entry.status === "error" && entry.progress?.status !== "running") || false
|
|
115
99
|
);
|
|
116
100
|
}
|
|
117
101
|
type SubagentToolRenderState = SubagentResultRenderState;
|
|
@@ -221,16 +205,10 @@ class SubagentControlNoticeComponent implements Component {
|
|
|
221
205
|
}
|
|
222
206
|
|
|
223
207
|
export default function registerSubagentExtension(pi: ExtensionAPI): void {
|
|
224
|
-
if (getEnvValue(SUBAGENT_CHILD_ENV) === "1") {
|
|
225
|
-
if (getEnvValue(SUBAGENT_FANOUT_CHILD_ENV) === "1") registerFanoutChildSubagentExtension(pi);
|
|
226
|
-
return;
|
|
227
|
-
}
|
|
228
208
|
const lifecycle = beginApiLifecycle(pi);
|
|
229
209
|
const registrationFailureCleanups: Array<() => void> = [];
|
|
230
210
|
let runtimeCleanupInstalled = false;
|
|
231
211
|
try {
|
|
232
|
-
ensureAccessibleDir(RESULTS_DIR);
|
|
233
|
-
ensureAccessibleDir(ASYNC_DIR);
|
|
234
212
|
const config = loadConfig();
|
|
235
213
|
const asyncByDefault = config.asyncByDefault === true;
|
|
236
214
|
const tempArtifactsDir = getArtifactsDir(null);
|
|
@@ -254,18 +232,14 @@ export default function registerSubagentExtension(pi: ExtensionAPI): void {
|
|
|
254
232
|
clear: () => {},
|
|
255
233
|
},
|
|
256
234
|
};
|
|
257
|
-
const maintenance = createSubagentStartupMaintenance(
|
|
258
|
-
resultsDir: RESULTS_DIR,
|
|
235
|
+
const maintenance = createSubagentStartupMaintenance(state, {
|
|
259
236
|
artifactCleanupDays: DEFAULT_ARTIFACT_CONFIG.cleanupDays,
|
|
260
|
-
resultTtlMs: 10 * 60 * 1000,
|
|
261
237
|
});
|
|
262
238
|
maintenance.scheduleStartupCleanup();
|
|
263
239
|
registrationFailureCleanups.push(() => maintenance.stop());
|
|
264
|
-
maintenance.startResultWatcherDeferred();
|
|
265
|
-
maintenance.primeExistingResultsDeferred();
|
|
266
240
|
const { ensurePoller, handleStarted, handleComplete, resetJobs, hydrateActiveJobsDeferred } =
|
|
267
241
|
createAsyncJobTracker(pi, state, ASYNC_DIR);
|
|
268
|
-
const
|
|
242
|
+
const executorDeps = {
|
|
269
243
|
pi,
|
|
270
244
|
state,
|
|
271
245
|
config,
|
|
@@ -274,7 +248,23 @@ export default function registerSubagentExtension(pi: ExtensionAPI): void {
|
|
|
274
248
|
getSubagentSessionRoot,
|
|
275
249
|
expandTilde,
|
|
276
250
|
discoverAgents,
|
|
277
|
-
}
|
|
251
|
+
};
|
|
252
|
+
const executor = createSubagentExecutor(executorDeps);
|
|
253
|
+
const childExecutors = new Map<string, ReturnType<typeof createSubagentExecutor>>();
|
|
254
|
+
const executorForContext = (ctx: ExtensionContext): ReturnType<typeof createSubagentExecutor> => {
|
|
255
|
+
const policy = ctx.subagentPolicy;
|
|
256
|
+
if (!policy) return executor;
|
|
257
|
+
const key = `${policy.managementActions}:${policy.fanoutAuthorized ? "fanout" : "no-fanout"}`;
|
|
258
|
+
const cached = childExecutors.get(key);
|
|
259
|
+
if (cached) return cached;
|
|
260
|
+
const childExecutor = createSubagentExecutor({
|
|
261
|
+
...executorDeps,
|
|
262
|
+
childPolicy: policy,
|
|
263
|
+
allowMutatingManagementActions: policy.managementActions === "full",
|
|
264
|
+
});
|
|
265
|
+
childExecutors.set(key, childExecutor);
|
|
266
|
+
return childExecutor;
|
|
267
|
+
};
|
|
278
268
|
pi.registerMessageRenderer<SlashMessageDetails>(SLASH_RESULT_TYPE, (message, options, theme) => {
|
|
279
269
|
const details = resolveSlashMessageDetails(message.details);
|
|
280
270
|
if (!details) return undefined;
|
|
@@ -304,7 +294,7 @@ export default function registerSubagentExtension(pi: ExtensionAPI): void {
|
|
|
304
294
|
state.lastUiContext = ctx;
|
|
305
295
|
ctx.ui.setToolsExpanded(false);
|
|
306
296
|
}
|
|
307
|
-
return
|
|
297
|
+
return executorForContext(ctx).execute(id, params, signal, onUpdate, ctx);
|
|
308
298
|
};
|
|
309
299
|
const slashBridge = registerSlashSubagentBridge({
|
|
310
300
|
events: pi.events,
|
|
@@ -454,6 +444,7 @@ export default function registerSubagentExtension(pi: ExtensionAPI): void {
|
|
|
454
444
|
...Array.from(state.cleanupTimers.values(), (timer) => () => clearTimeout(timer)),
|
|
455
445
|
() => state.cleanupTimers.clear(),
|
|
456
446
|
() => state.asyncJobs.clear(),
|
|
447
|
+
() => childExecutors.clear(),
|
|
457
448
|
() => clearSlashSnapshots(pi),
|
|
458
449
|
() => slashBridge.cancelAll(),
|
|
459
450
|
() => slashBridge.dispose(),
|
|
@@ -489,7 +480,7 @@ export default function registerSubagentExtension(pi: ExtensionAPI): void {
|
|
|
489
480
|
resetJobs(ctx);
|
|
490
481
|
hydrateActiveJobsDeferred(ctx);
|
|
491
482
|
restoreSlashFinalSnapshots(ctx.sessionManager.getEntries(), pi);
|
|
492
|
-
|
|
483
|
+
restoreSlashFinalSnapshots(ctx.sessionManager.getEntries(), pi);
|
|
493
484
|
};
|
|
494
485
|
pi.on("session_start", (_event, ctx) => {
|
|
495
486
|
if (lifecycle.isCurrent()) resetSessionState(ctx);
|
|
@@ -1,15 +1,12 @@
|
|
|
1
1
|
import * as path from "node:path";
|
|
2
|
-
import type {
|
|
3
|
-
import {
|
|
4
|
-
import { cleanupOldNestedRuntimeDirs } from "../runs/shared/nested-events.ts";
|
|
2
|
+
import type { ExtensionContext } from "@bastani/atomic";
|
|
3
|
+
import { cleanupOldNestedRuntimeDirs } from "../runs/inprocess/runtime-support/nested-api.ts";
|
|
5
4
|
import { cleanupAllArtifactDirs, cleanupOldArtifacts, getArtifactsDir } from "../shared/artifacts.ts";
|
|
6
5
|
import { cleanupOldChainDirs } from "../shared/settings.ts";
|
|
7
6
|
import type { SubagentState } from "../shared/types.ts";
|
|
8
7
|
|
|
9
8
|
export interface SubagentStartupMaintenance {
|
|
10
9
|
scheduleStartupCleanup(): void;
|
|
11
|
-
startResultWatcherDeferred(): void;
|
|
12
|
-
primeExistingResultsDeferred(): void;
|
|
13
10
|
cleanupSessionArtifactsDeferred(ctx: ExtensionContext): void;
|
|
14
11
|
stop(): void;
|
|
15
12
|
}
|
|
@@ -47,54 +44,48 @@ function swallowCleanup(task: () => void): void {
|
|
|
47
44
|
} catch {}
|
|
48
45
|
}
|
|
49
46
|
|
|
47
|
+
interface StartupMaintenanceOptions {
|
|
48
|
+
artifactCleanupDays: number;
|
|
49
|
+
resultsDir?: string;
|
|
50
|
+
resultTtlMs?: number;
|
|
51
|
+
scheduleMacrotask?: (task: () => void) => () => void;
|
|
52
|
+
scheduleDelayed?: (task: () => void, delayMs: number) => () => void;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function createSubagentStartupMaintenance(
|
|
56
|
+
state: SubagentState,
|
|
57
|
+
options: StartupMaintenanceOptions,
|
|
58
|
+
): SubagentStartupMaintenance;
|
|
50
59
|
export function createSubagentStartupMaintenance(
|
|
51
|
-
|
|
60
|
+
_pi: unknown,
|
|
52
61
|
state: SubagentState,
|
|
53
|
-
options:
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
},
|
|
62
|
+
options: StartupMaintenanceOptions,
|
|
63
|
+
): SubagentStartupMaintenance;
|
|
64
|
+
export function createSubagentStartupMaintenance(
|
|
65
|
+
_first: SubagentState | unknown,
|
|
66
|
+
second: StartupMaintenanceOptions | SubagentState,
|
|
67
|
+
third?: StartupMaintenanceOptions,
|
|
60
68
|
): SubagentStartupMaintenance {
|
|
61
|
-
const
|
|
62
|
-
pi,
|
|
63
|
-
state,
|
|
64
|
-
options.resultsDir,
|
|
65
|
-
options.resultTtlMs,
|
|
66
|
-
);
|
|
69
|
+
const options = third ?? (second as StartupMaintenanceOptions);
|
|
67
70
|
const cancelTasks = new Set<() => void>();
|
|
68
|
-
// The sessions root derived from a host-provided (non-default, e.g. isolated
|
|
69
|
-
// programmatic agentDir) session directory. When set, the global artifact scan
|
|
70
|
-
// stays inside it instead of touching the real global/legacy sessions roots.
|
|
71
|
-
// A custom session dir that does not follow the <agentDir>/sessions/<safe-cwd>
|
|
72
|
-
// layout (the default layout the SDK derives for a programmatic agentDir, with
|
|
73
|
-
// <safe-cwd> encoding the current working directory) yields no roots at all, so
|
|
74
|
-
// cleanup never broadens into an arbitrary directory's siblings.
|
|
75
71
|
let contextSessionsRoots: readonly string[] | undefined;
|
|
76
72
|
const noteSessionContext = (ctx: ExtensionContext): void => {
|
|
77
73
|
try {
|
|
78
74
|
if (ctx.sessionManager.usesDefaultSessionDir()) {
|
|
79
|
-
// Only a valid default-session context intentionally restores the
|
|
80
|
-
// env/global cleanup roots.
|
|
81
75
|
contextSessionsRoots = undefined;
|
|
82
76
|
return;
|
|
83
77
|
}
|
|
84
78
|
const sessionDir = ctx.sessionManager.getSessionDir();
|
|
85
|
-
// A partial or throwing context keeps the last validated root: destructive
|
|
86
|
-
// cleanup must never broaden from an isolated root to the global roots on
|
|
87
|
-
// the strength of an unusable context.
|
|
88
79
|
if (!sessionDir) return;
|
|
89
80
|
const parent = path.dirname(sessionDir);
|
|
90
81
|
const safeCwd = `--${path
|
|
91
82
|
.resolve(ctx.cwd)
|
|
92
83
|
.replace(/^[/\\]/, "")
|
|
93
84
|
.replace(/[/\\:]/g, "-")}--`;
|
|
94
|
-
|
|
95
|
-
|
|
85
|
+
contextSessionsRoots =
|
|
86
|
+
path.basename(parent) === "sessions" && path.basename(sessionDir) === safeCwd ? [parent] : [];
|
|
96
87
|
} catch {
|
|
97
|
-
// A stale or partial context leaves cleanup on the
|
|
88
|
+
// A stale or partial context leaves cleanup on the last validated root.
|
|
98
89
|
}
|
|
99
90
|
};
|
|
100
91
|
const schedule = (task: () => void): void => {
|
|
@@ -117,21 +108,12 @@ export function createSubagentStartupMaintenance(
|
|
|
117
108
|
schedule(() => {
|
|
118
109
|
swallowCleanup(cleanupOldChainDirs);
|
|
119
110
|
swallowCleanup(() => cleanupOldNestedRuntimeDirs(options.artifactCleanupDays));
|
|
120
|
-
// The global session-artifact scan can touch thousands of historical session
|
|
121
|
-
// directories, so it waits until well after startup instead of running on the
|
|
122
|
-
// activation macrotask.
|
|
123
111
|
scheduleDelayed(
|
|
124
112
|
() => swallowCleanup(() => cleanupAllArtifactDirs(options.artifactCleanupDays, contextSessionsRoots)),
|
|
125
113
|
STARTUP_ARTIFACT_SCAN_DELAY_MS,
|
|
126
114
|
);
|
|
127
115
|
});
|
|
128
116
|
},
|
|
129
|
-
startResultWatcherDeferred() {
|
|
130
|
-
schedule(startResultWatcher);
|
|
131
|
-
},
|
|
132
|
-
primeExistingResultsDeferred() {
|
|
133
|
-
schedule(primeExistingResults);
|
|
134
|
-
},
|
|
135
117
|
cleanupSessionArtifactsDeferred(ctx) {
|
|
136
118
|
noteSessionContext(ctx);
|
|
137
119
|
let sessionFile: string | null | undefined;
|
|
@@ -148,7 +130,6 @@ export function createSubagentStartupMaintenance(
|
|
|
148
130
|
stop() {
|
|
149
131
|
for (const cancel of cancelTasks) cancel();
|
|
150
132
|
cancelTasks.clear();
|
|
151
|
-
stopResultWatcher();
|
|
152
133
|
},
|
|
153
134
|
};
|
|
154
135
|
}
|