@selesai/code 0.5.20 → 0.5.21
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/dist/extensions/handoff-new.test.ts +8 -9
- package/dist/extensions/handoff-new.ts +6 -8
- package/dist/extensions/pi-subagents/CHANGELOG.md +1367 -0
- package/dist/extensions/pi-subagents/README.md +602 -220
- package/dist/extensions/pi-subagents/banner.png +0 -0
- package/dist/extensions/pi-subagents/index.ts +1 -0
- package/dist/extensions/pi-subagents/install.mjs +3 -3
- package/dist/extensions/pi-subagents/package-lock.json +3405 -0
- package/dist/extensions/pi-subagents/package.json +26 -14
- package/dist/extensions/pi-subagents/prompts/gather-context-and-clarify.md +1 -1
- package/dist/extensions/pi-subagents/prompts/parallel-cleanup.md +9 -9
- package/dist/extensions/pi-subagents/prompts/parallel-context-build.md +3 -3
- package/dist/extensions/pi-subagents/prompts/parallel-handoff-plan.md +7 -7
- package/dist/extensions/pi-subagents/prompts/parallel-research.md +6 -6
- package/dist/extensions/pi-subagents/prompts/parallel-review.md +8 -8
- package/dist/extensions/pi-subagents/prompts/review-loop.md +13 -11
- package/dist/extensions/pi-subagents/skills/pi-subagents/SKILL.md +267 -176
- package/dist/extensions/pi-subagents/src/agents/agent-management.ts +89 -16
- package/dist/extensions/pi-subagents/src/agents/agent-serializer.ts +19 -0
- package/dist/extensions/pi-subagents/src/agents/agents.ts +218 -120
- package/dist/extensions/pi-subagents/src/agents/frontmatter.ts +67 -13
- package/dist/extensions/pi-subagents/src/agents/proactive-skills.ts +2 -2
- package/dist/extensions/pi-subagents/src/agents/skills.ts +25 -12
- package/dist/extensions/pi-subagents/src/api/background-work.ts +197 -0
- package/dist/extensions/pi-subagents/src/api/capability-ceiling.ts +17 -0
- package/dist/extensions/pi-subagents/src/api/delegation.ts +285 -0
- package/dist/extensions/pi-subagents/src/api/preflight.ts +399 -0
- package/dist/extensions/pi-subagents/src/extension/chain-validation.ts +165 -0
- package/dist/extensions/pi-subagents/src/extension/config.ts +7 -1
- package/dist/extensions/pi-subagents/src/extension/doctor.ts +15 -0
- package/dist/extensions/pi-subagents/src/extension/fanout-child.ts +3 -1
- package/dist/extensions/pi-subagents/src/extension/index.ts +113 -157
- package/dist/extensions/pi-subagents/src/extension/rpc.ts +41 -4
- package/dist/extensions/pi-subagents/src/extension/schemas.ts +45 -15
- package/dist/extensions/pi-subagents/src/extension/steering-notices.ts +35 -0
- package/dist/extensions/pi-subagents/src/extension/tool-description.ts +28 -15
- package/dist/extensions/pi-subagents/src/intercom/intercom-bridge.ts +4 -3
- package/dist/extensions/pi-subagents/src/intercom/native-supervisor-channel.ts +225 -29
- package/dist/extensions/pi-subagents/src/intercom/result-intercom.ts +11 -0
- package/dist/extensions/pi-subagents/src/profiles/profiles.ts +8 -10
- package/dist/extensions/pi-subagents/src/runs/background/async-execution.ts +452 -62
- package/dist/extensions/pi-subagents/src/runs/background/async-job-tracker.ts +60 -9
- package/dist/extensions/pi-subagents/src/runs/background/async-resume.ts +198 -52
- package/dist/extensions/pi-subagents/src/runs/background/async-status.ts +119 -21
- package/dist/extensions/pi-subagents/src/runs/background/auto-drain.ts +67 -0
- package/dist/extensions/pi-subagents/src/runs/background/chain-append.ts +2 -0
- package/dist/extensions/pi-subagents/src/runs/background/chain-root-attachment.ts +16 -8
- package/dist/extensions/pi-subagents/src/runs/background/completion-batcher.ts +6 -4
- package/dist/extensions/pi-subagents/src/runs/background/completion-dedupe.ts +2 -11
- package/dist/extensions/pi-subagents/src/runs/background/control-channel.ts +260 -13
- package/dist/extensions/pi-subagents/src/runs/background/fleet-view.ts +32 -6
- package/dist/extensions/pi-subagents/src/runs/background/notify.ts +171 -90
- package/dist/extensions/pi-subagents/src/runs/background/process-terminal.ts +280 -0
- package/dist/extensions/pi-subagents/src/runs/background/result-watcher.ts +149 -88
- package/dist/extensions/pi-subagents/src/runs/background/run-id-resolver.ts +14 -2
- package/dist/extensions/pi-subagents/src/runs/background/run-status.ts +33 -17
- package/dist/extensions/pi-subagents/src/runs/background/scheduled-runs.ts +9 -1
- package/dist/extensions/pi-subagents/src/runs/background/stale-run-reconciler.ts +38 -10
- package/dist/extensions/pi-subagents/src/runs/background/steering.ts +237 -0
- package/dist/extensions/pi-subagents/src/runs/background/subagent-runner.ts +1297 -307
- package/dist/extensions/pi-subagents/src/runs/background/subagent-wait.ts +610 -0
- package/dist/extensions/pi-subagents/src/runs/background/top-level-async.ts +2 -1
- package/dist/extensions/pi-subagents/src/runs/background/wait-config.ts +36 -0
- package/dist/extensions/pi-subagents/src/runs/background/wait-tool.ts +26 -0
- package/dist/extensions/pi-subagents/src/runs/foreground/async-steering-action.ts +230 -0
- package/dist/extensions/pi-subagents/src/runs/foreground/chain-clarify.ts +22 -6
- package/dist/extensions/pi-subagents/src/runs/foreground/chain-execution.ts +228 -140
- package/dist/extensions/pi-subagents/src/runs/foreground/execution.ts +452 -138
- package/dist/extensions/pi-subagents/src/runs/foreground/foreground-control.ts +90 -0
- package/dist/extensions/pi-subagents/src/runs/foreground/subagent-executor.ts +1001 -416
- package/dist/extensions/pi-subagents/src/runs/shared/acceptance.ts +443 -132
- package/dist/extensions/pi-subagents/src/runs/shared/agent-contract.ts +38 -0
- package/dist/extensions/pi-subagents/src/runs/shared/capability-ceiling.ts +178 -0
- package/dist/extensions/pi-subagents/src/runs/shared/child-protocol.ts +121 -0
- package/dist/extensions/pi-subagents/src/runs/shared/completion-guard.ts +41 -93
- package/dist/extensions/pi-subagents/src/runs/shared/context-mode.ts +44 -0
- package/dist/extensions/pi-subagents/src/runs/shared/dynamic-fanout.ts +11 -9
- package/dist/extensions/pi-subagents/src/runs/shared/long-running-guard.ts +4 -0
- package/dist/extensions/pi-subagents/src/runs/shared/mcp-direct-tool-allowlist.ts +12 -6
- package/dist/extensions/pi-subagents/src/runs/shared/model-fallback.ts +36 -0
- package/dist/extensions/pi-subagents/src/runs/shared/nested-events.ts +44 -7
- package/dist/extensions/pi-subagents/src/runs/shared/nested-render.ts +4 -1
- package/dist/extensions/pi-subagents/src/runs/shared/parallel-handoff.ts +154 -0
- package/dist/extensions/pi-subagents/src/runs/shared/parallel-utils.ts +18 -0
- package/dist/extensions/pi-subagents/src/runs/shared/pi-args.ts +174 -54
- package/dist/extensions/pi-subagents/src/runs/shared/pi-spawn.ts +38 -31
- package/dist/extensions/pi-subagents/src/runs/shared/run-history.ts +90 -5
- package/dist/extensions/pi-subagents/src/runs/shared/session-lease.ts +299 -0
- package/dist/extensions/pi-subagents/src/runs/shared/single-output.ts +61 -6
- package/dist/extensions/pi-subagents/src/runs/shared/spawn-budget.ts +128 -0
- package/dist/extensions/pi-subagents/src/runs/shared/structured-output.ts +112 -7
- package/dist/extensions/pi-subagents/src/runs/shared/subagent-control.ts +14 -6
- package/dist/extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.ts +149 -36
- package/dist/extensions/pi-subagents/src/runs/shared/task-intent.ts +181 -0
- package/dist/extensions/pi-subagents/src/runs/shared/tool-availability.ts +83 -0
- package/dist/extensions/pi-subagents/src/runs/shared/tool-budget.ts +11 -5
- package/dist/extensions/pi-subagents/src/runs/shared/turn-budget.ts +50 -4
- package/dist/extensions/pi-subagents/src/runs/shared/worktree.ts +63 -14
- package/dist/extensions/pi-subagents/src/shared/accessible-dir.ts +25 -0
- package/dist/extensions/pi-subagents/src/shared/artifacts.ts +37 -7
- package/dist/extensions/pi-subagents/src/shared/atomic-json.ts +18 -43
- package/dist/extensions/pi-subagents/src/shared/child-transcript.ts +52 -0
- package/dist/extensions/pi-subagents/src/shared/env.ts +16 -0
- package/dist/extensions/pi-subagents/src/shared/file-system-retry.ts +47 -0
- package/dist/extensions/pi-subagents/src/shared/fork-context.ts +28 -3
- package/dist/extensions/pi-subagents/src/shared/launch-contract.ts +123 -0
- package/dist/extensions/pi-subagents/src/shared/model-info.ts +8 -5
- package/dist/extensions/pi-subagents/src/shared/settings.ts +9 -1
- package/dist/extensions/pi-subagents/src/shared/status-format.ts +7 -1
- package/dist/extensions/pi-subagents/src/shared/types.ts +522 -51
- package/dist/extensions/pi-subagents/src/shared/utils.ts +59 -53
- package/dist/extensions/pi-subagents/src/slash/delegation-adapters.ts +604 -0
- package/dist/extensions/pi-subagents/src/slash/delegation-json.ts +108 -0
- package/dist/extensions/pi-subagents/src/slash/delegation-request.ts +249 -0
- package/dist/extensions/pi-subagents/src/slash/prompt-template-bridge.ts +353 -345
- package/dist/extensions/pi-subagents/src/slash/prompt-workflows.ts +2 -2
- package/dist/extensions/pi-subagents/src/slash/selector.ts +147 -0
- package/dist/extensions/pi-subagents/src/slash/slash-commands.ts +265 -23
- package/dist/extensions/pi-subagents/src/slash/slash-live-state.ts +2 -2
- package/dist/extensions/pi-subagents/src/slash/subagents-admin.ts +428 -0
- package/dist/extensions/pi-subagents/src/tui/fleet-status.ts +362 -0
- package/dist/extensions/pi-subagents/src/tui/fleet-transcript.ts +472 -0
- package/dist/extensions/pi-subagents/src/tui/fleet.ts +664 -0
- package/dist/extensions/pi-subagents/src/tui/render.ts +115 -31
- package/dist/extensions/pi-subagents/src/watchdog/change-signature.ts +220 -0
- package/dist/extensions/pi-subagents/src/watchdog/child-status.ts +205 -0
- package/dist/extensions/pi-subagents/src/watchdog/emission-guard.ts +123 -0
- package/dist/extensions/pi-subagents/src/watchdog/lsp-diagnostics.ts +532 -0
- package/dist/extensions/pi-subagents/src/watchdog/model-selection.ts +167 -0
- package/dist/extensions/pi-subagents/src/watchdog/register-child.ts +117 -0
- package/dist/extensions/pi-subagents/src/watchdog/register-main.ts +433 -0
- package/dist/extensions/pi-subagents/src/watchdog/render.ts +54 -0
- package/dist/extensions/pi-subagents/src/watchdog/review.ts +298 -0
- package/dist/extensions/pi-subagents/src/watchdog/runtime.ts +714 -0
- package/dist/extensions/pi-subagents/src/watchdog/settings.ts +528 -0
- package/dist/extensions/pi-subagents/src/watchdog/tool-actions.ts +155 -0
- package/dist/extensions/pi-subagents/src/watchdog/turn-delta.ts +161 -0
- package/dist/extensions/pi-subagents/src/watchdog/types.ts +188 -0
- package/dist/extensions/pi-subagents/src/watchdog/warning-format.ts +73 -0
- package/dist/extensions/pi-subagents/test/e2e/real-session-subagent.test.ts +130 -2
- package/dist/extensions/pi-subagents/test/integration/acceptance-file-report.test.ts +439 -0
- package/dist/extensions/pi-subagents/test/integration/async-execution.test.ts +1632 -61
- package/dist/extensions/pi-subagents/test/integration/async-job-tracker.test.ts +54 -1
- package/dist/extensions/pi-subagents/test/integration/async-status.test.ts +112 -0
- package/dist/extensions/pi-subagents/test/integration/chain-clarify.test.ts +98 -1
- package/dist/extensions/pi-subagents/test/integration/chain-execution.test.ts +179 -9
- package/dist/extensions/pi-subagents/test/integration/detect-error.test.ts +16 -9
- package/dist/extensions/pi-subagents/test/integration/error-handling.test.ts +6 -5
- package/dist/extensions/pi-subagents/test/integration/fork-context-execution.test.ts +589 -9
- package/dist/extensions/pi-subagents/test/integration/intercom-result-delivery.test.ts +528 -21
- package/dist/extensions/pi-subagents/test/integration/parallel-execution.test.ts +227 -3
- package/dist/extensions/pi-subagents/test/integration/render-fork-badge.test.ts +235 -7
- package/dist/extensions/pi-subagents/test/integration/render-widget.test.ts +27 -0
- package/dist/extensions/pi-subagents/test/integration/result-watcher.test.ts +172 -2
- package/dist/extensions/pi-subagents/test/integration/single-execution.test.ts +1502 -18
- package/dist/extensions/pi-subagents/test/integration/slash-commands.test.ts +993 -4
- package/dist/extensions/pi-subagents/test/integration/slash-live-state.test.ts +27 -0
- package/dist/extensions/pi-subagents/test/integration/top-level-async.test.ts +7 -1
- package/dist/extensions/pi-subagents/test/support/helpers.ts +32 -0
- package/dist/extensions/pi-subagents/test/support/mock-pi-script.mjs +59 -2
- package/dist/extensions/pi-subagents/test/support/mock-pi.ts +32 -6
- package/dist/extensions/pi-subagents/test/support/real-session-child-cli.mjs +25 -24
- package/dist/extensions/pi-subagents/test/support/real-session-runner.ts +59 -35
- package/dist/extensions/pi-subagents/test/support/session-lease-child.mjs +42 -0
- package/dist/extensions/pi-subagents/test/unit/acceptance.test.ts +703 -69
- package/dist/extensions/pi-subagents/test/unit/accessible-dir.test.ts +82 -0
- package/dist/extensions/pi-subagents/test/unit/agent-disabled.test.ts +15 -15
- package/dist/extensions/pi-subagents/test/unit/agent-eject-disable.test.ts +66 -66
- package/dist/extensions/pi-subagents/test/unit/agent-frontmatter.test.ts +478 -19
- package/dist/extensions/pi-subagents/test/unit/agent-management.test.ts +228 -7
- package/dist/extensions/pi-subagents/test/unit/agent-memory.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/agent-overrides.test.ts +198 -78
- package/dist/extensions/pi-subagents/test/unit/async-execution.test.ts +15 -1
- package/dist/extensions/pi-subagents/test/unit/async-interrupt-action.test.ts +102 -7
- package/dist/extensions/pi-subagents/test/unit/async-resume.test.ts +439 -6
- package/dist/extensions/pi-subagents/test/unit/atomic-json.test.ts +41 -1
- package/dist/extensions/pi-subagents/test/unit/auto-drain.test.ts +79 -0
- package/dist/extensions/pi-subagents/test/unit/background-work.test.ts +285 -0
- package/dist/extensions/pi-subagents/test/unit/capability-ceiling-pi-args.test.ts +84 -0
- package/dist/extensions/pi-subagents/test/unit/capability-ceiling.test.ts +68 -0
- package/dist/extensions/pi-subagents/test/unit/chain-validation.test.ts +326 -0
- package/dist/extensions/pi-subagents/test/unit/child-protocol.test.ts +82 -0
- package/dist/extensions/pi-subagents/test/unit/child-transcript.test.ts +46 -4
- package/dist/extensions/pi-subagents/test/unit/completion-batcher.test.ts +3 -1
- package/dist/extensions/pi-subagents/test/unit/completion-dedupe.test.ts +9 -17
- package/dist/extensions/pi-subagents/test/unit/completion-guard.test.ts +175 -3
- package/dist/extensions/pi-subagents/test/unit/config-dir-runtime.test.ts +10 -5
- package/dist/extensions/pi-subagents/test/unit/control-channel.test.ts +126 -6
- package/dist/extensions/pi-subagents/test/unit/default-extensions.test.ts +219 -0
- package/dist/extensions/pi-subagents/test/unit/delegation-api.test.ts +913 -0
- package/dist/extensions/pi-subagents/test/unit/doctor.test.ts +13 -2
- package/dist/extensions/pi-subagents/test/unit/dynamic-fanout.test.ts +3 -3
- package/dist/extensions/pi-subagents/test/unit/fleet-status.test.ts +351 -0
- package/dist/extensions/pi-subagents/test/unit/fleet-transcript.test.ts +280 -0
- package/dist/extensions/pi-subagents/test/unit/fleet.test.ts +575 -0
- package/dist/extensions/pi-subagents/test/unit/foreground-control.test.ts +65 -0
- package/dist/extensions/pi-subagents/test/unit/fork-context.test.ts +94 -1
- package/dist/extensions/pi-subagents/test/unit/get-final-output.test.ts +23 -0
- package/dist/extensions/pi-subagents/test/unit/host-peer-runtime-imports.test.ts +157 -0
- package/dist/extensions/pi-subagents/test/unit/index-child-registration.test.ts +367 -5
- package/dist/extensions/pi-subagents/test/unit/intercom-bridge.test.ts +11 -0
- package/dist/extensions/pi-subagents/test/unit/model-fallback.test.ts +36 -0
- package/dist/extensions/pi-subagents/test/unit/model-info.test.ts +25 -3
- package/dist/extensions/pi-subagents/test/unit/model-scope.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/native-supervisor-channel.test.ts +341 -10
- package/dist/extensions/pi-subagents/test/unit/nested-control.test.ts +16 -0
- package/dist/extensions/pi-subagents/test/unit/nested-events.test.ts +52 -0
- package/dist/extensions/pi-subagents/test/unit/notify.test.ts +168 -7
- package/dist/extensions/pi-subagents/test/unit/package-manifest.test.ts +85 -7
- package/dist/extensions/pi-subagents/test/unit/parallel-handoff.test.ts +175 -0
- package/dist/extensions/pi-subagents/test/unit/path-resolution.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/pi-args.test.ts +243 -16
- package/dist/extensions/pi-subagents/test/unit/pi-coding-agent-dir.test.ts +79 -36
- package/dist/extensions/pi-subagents/test/unit/pi-spawn.test.ts +174 -56
- package/dist/extensions/pi-subagents/test/unit/preflight.test.ts +322 -0
- package/dist/extensions/pi-subagents/test/unit/process-terminal.test.ts +204 -0
- package/dist/extensions/pi-subagents/test/unit/recursion-guard.test.ts +20 -12
- package/dist/extensions/pi-subagents/test/unit/rpc.test.ts +117 -4
- package/dist/extensions/pi-subagents/test/unit/run-status.test.ts +40 -7
- package/dist/extensions/pi-subagents/test/unit/scheduled-runs.test.ts +48 -1
- package/dist/extensions/pi-subagents/test/unit/schemas.test.ts +21 -3
- package/dist/extensions/pi-subagents/test/unit/selector.test.ts +57 -0
- package/dist/extensions/pi-subagents/test/unit/session-lease.test.ts +326 -0
- package/dist/extensions/pi-subagents/test/unit/single-output.test.ts +101 -2
- package/dist/extensions/pi-subagents/test/unit/skills-fallback.test.ts +111 -1
- package/dist/extensions/pi-subagents/test/unit/spawn-budget.test.ts +124 -0
- package/dist/extensions/pi-subagents/test/unit/stale-run-reconciler.test.ts +6 -4
- package/dist/extensions/pi-subagents/test/unit/status-format.test.ts +7 -0
- package/dist/extensions/pi-subagents/test/unit/steering-action.test.ts +397 -0
- package/dist/extensions/pi-subagents/test/unit/steering-notices.test.ts +58 -0
- package/dist/extensions/pi-subagents/test/unit/steering.test.ts +173 -0
- package/dist/extensions/pi-subagents/test/unit/subagent-control.test.ts +25 -4
- package/dist/extensions/pi-subagents/test/unit/subagent-prompt-runtime.test.ts +390 -25
- package/dist/extensions/pi-subagents/test/unit/subagent-wait.test.ts +837 -0
- package/dist/extensions/pi-subagents/test/unit/task-intent.test.ts +86 -0
- package/dist/extensions/pi-subagents/test/unit/tool-budget.test.ts +15 -0
- package/dist/extensions/pi-subagents/test/unit/tool-description.test.ts +47 -1
- package/dist/extensions/pi-subagents/test/unit/turn-budget.test.ts +56 -10
- package/dist/extensions/pi-subagents/test/unit/watchdog-change-signature.test.ts +387 -0
- package/dist/extensions/pi-subagents/test/unit/watchdog-child-status.test.ts +110 -0
- package/dist/extensions/pi-subagents/test/unit/watchdog-emission-guard.test.ts +62 -0
- package/dist/extensions/pi-subagents/test/unit/watchdog-lsp-diagnostics.test.ts +144 -0
- package/dist/extensions/pi-subagents/test/unit/watchdog-model-selection.test.ts +121 -0
- package/dist/extensions/pi-subagents/test/unit/watchdog-render.test.ts +76 -0
- package/dist/extensions/pi-subagents/test/unit/watchdog-review.test.ts +327 -0
- package/dist/extensions/pi-subagents/test/unit/watchdog-runtime.test.ts +863 -0
- package/dist/extensions/pi-subagents/test/unit/watchdog-settings.test.ts +290 -0
- package/dist/extensions/pi-subagents/test/unit/watchdog-tool-actions.test.ts +91 -0
- package/dist/extensions/pi-subagents/test/unit/watchdog-turn-delta.test.ts +130 -0
- package/dist/extensions/pi-subagents/test/unit/widget-nested-render.test.ts +32 -4
- package/dist/extensions/pi-subagents/test/unit/worktree.test.ts +5 -1
- package/dist/extensions/pi-subagents/test/unit/writer-budget-guidance.test.ts +30 -0
- package/dist/extensions/question/batch.ts +39 -0
- package/dist/extensions/question/dialog-adapter.ts +23 -1
- package/dist/extensions/question/index.ts +237 -7
- package/dist/extensions/question/schemas.ts +28 -15
- package/dist/extensions/question/tests/batch.test.ts +26 -0
- package/dist/extensions/question/tests/ui-protocol.test.ts +23 -3
- package/dist/extensions/question/tui-adapter.ts +2 -3
- package/dist/extensions/question/types.ts +37 -0
- package/dist/extensions/question/ui-protocol.ts +3 -3
- package/dist/skills/batch-grill-me/SKILL.md +3 -1
- package/package.json +1 -1
|
@@ -2,16 +2,20 @@ import * as fs from "node:fs";
|
|
|
2
2
|
import * as path from "node:path";
|
|
3
3
|
import { formatDuration, formatModelThinking, formatTokens, shortenPath } from "../../shared/formatters.ts";
|
|
4
4
|
import { formatActivityLabel, formatParallelOutcome } from "../../shared/status-format.ts";
|
|
5
|
-
import { type ActivityState, type AsyncJobStep, type AsyncParallelGroupStatus, type AsyncStatus, type CostSummary, type NestedRunSummary, type SubagentRunMode, type TokenUsage, type TurnBudgetState } from "../../shared/types.ts";
|
|
5
|
+
import { type ActivityState, type AsyncJobStep, type AsyncParallelGroupStatus, type AsyncStatus, type CostSummary, type NestedRunSummary, type SteeringStatus, type SubagentRunMode, type TokenUsage, type TurnBudgetState } from "../../shared/types.ts";
|
|
6
|
+
import type { ResolvedSubagentCapabilityCeiling, SubagentCapabilityAudit } from "../shared/capability-ceiling.ts";
|
|
6
7
|
import { readStatus } from "../../shared/utils.ts";
|
|
7
8
|
import { attachRootChildrenToSteps, buildNestedRouteIndex, type NestedRoute, projectNestedEvents } from "../shared/nested-events.ts";
|
|
8
9
|
import { formatNestedRunStatusLines } from "../shared/nested-render.ts";
|
|
9
10
|
import { flatToLogicalStepIndex, normalizeParallelGroups } from "./parallel-groups.ts";
|
|
11
|
+
import { contextModeLabel, summarizeContextModes, type ContextMode, type ContextSummary } from "../shared/context-mode.ts";
|
|
10
12
|
import { reconcileAsyncRun, reconcileNestedAsyncDescendants } from "./stale-run-reconciler.ts";
|
|
13
|
+
import { readProcessTerminal, sanitizeProcessTerminal } from "./process-terminal.ts";
|
|
11
14
|
|
|
12
15
|
interface AsyncRunStepSummary {
|
|
13
16
|
index: number;
|
|
14
17
|
agent: string;
|
|
18
|
+
context?: ContextMode;
|
|
15
19
|
label?: string;
|
|
16
20
|
phase?: string;
|
|
17
21
|
outputName?: string;
|
|
@@ -27,8 +31,7 @@ interface AsyncRunStepSummary {
|
|
|
27
31
|
recentOutput?: string[];
|
|
28
32
|
turnCount?: number;
|
|
29
33
|
toolCount?: number;
|
|
30
|
-
|
|
31
|
-
lastSteerAt?: number;
|
|
34
|
+
steering?: SteeringStatus;
|
|
32
35
|
durationMs?: number;
|
|
33
36
|
tokens?: TokenUsage;
|
|
34
37
|
totalCost?: CostSummary;
|
|
@@ -36,11 +39,22 @@ interface AsyncRunStepSummary {
|
|
|
36
39
|
model?: string;
|
|
37
40
|
thinking?: string;
|
|
38
41
|
attemptedModels?: string[];
|
|
42
|
+
sessionFile?: string;
|
|
43
|
+
transcriptPath?: string;
|
|
39
44
|
error?: string;
|
|
40
45
|
timedOut?: boolean;
|
|
46
|
+
stopped?: boolean;
|
|
41
47
|
turnBudget?: TurnBudgetState;
|
|
42
48
|
turnBudgetExceeded?: boolean;
|
|
43
49
|
wrapUpRequested?: boolean;
|
|
50
|
+
acceptance?: AsyncJobStep["acceptance"];
|
|
51
|
+
agentContract?: AsyncJobStep["agentContract"];
|
|
52
|
+
execution?: AsyncJobStep["execution"];
|
|
53
|
+
review?: AsyncJobStep["review"];
|
|
54
|
+
effects?: AsyncJobStep["effects"];
|
|
55
|
+
processTerminal?: AsyncJobStep["processTerminal"];
|
|
56
|
+
capabilityCeiling?: ResolvedSubagentCapabilityCeiling;
|
|
57
|
+
capabilityAudit?: SubagentCapabilityAudit;
|
|
44
58
|
children?: NestedRunSummary[];
|
|
45
59
|
}
|
|
46
60
|
|
|
@@ -48,7 +62,7 @@ export interface AsyncRunSummary {
|
|
|
48
62
|
id: string;
|
|
49
63
|
asyncDir: string;
|
|
50
64
|
sessionId?: string;
|
|
51
|
-
state: "queued" | "running" | "complete" | "failed" | "paused";
|
|
65
|
+
state: "queued" | "running" | "complete" | "failed" | "paused" | "stopped";
|
|
52
66
|
error?: string;
|
|
53
67
|
activityState?: ActivityState;
|
|
54
68
|
lastActivityAt?: number;
|
|
@@ -57,9 +71,9 @@ export interface AsyncRunSummary {
|
|
|
57
71
|
currentPath?: string;
|
|
58
72
|
turnCount?: number;
|
|
59
73
|
toolCount?: number;
|
|
60
|
-
|
|
61
|
-
lastSteerAt?: number;
|
|
74
|
+
steering?: SteeringStatus;
|
|
62
75
|
mode: SubagentRunMode;
|
|
76
|
+
context?: ContextSummary;
|
|
63
77
|
cwd?: string;
|
|
64
78
|
startedAt: number;
|
|
65
79
|
lastUpdate?: number;
|
|
@@ -67,6 +81,7 @@ export interface AsyncRunSummary {
|
|
|
67
81
|
timeoutMs?: number;
|
|
68
82
|
deadlineAt?: number;
|
|
69
83
|
timedOut?: boolean;
|
|
84
|
+
stopped?: boolean;
|
|
70
85
|
turnBudget?: TurnBudgetState;
|
|
71
86
|
turnBudgetExceeded?: boolean;
|
|
72
87
|
wrapUpRequested?: boolean;
|
|
@@ -82,6 +97,9 @@ export interface AsyncRunSummary {
|
|
|
82
97
|
sessionFile?: string;
|
|
83
98
|
nestedChildren?: NestedRunSummary[];
|
|
84
99
|
nestedWarnings?: string[];
|
|
100
|
+
processTerminal?: AsyncStatus["processTerminal"];
|
|
101
|
+
capabilityCeiling?: ResolvedSubagentCapabilityCeiling;
|
|
102
|
+
capabilityAudit?: SubagentCapabilityAudit;
|
|
85
103
|
}
|
|
86
104
|
|
|
87
105
|
interface AsyncRunListOptions {
|
|
@@ -92,6 +110,7 @@ interface AsyncRunListOptions {
|
|
|
92
110
|
kill?: (pid: number, signal?: NodeJS.Signals | 0) => boolean;
|
|
93
111
|
now?: () => number;
|
|
94
112
|
reconcile?: boolean;
|
|
113
|
+
runId?: string;
|
|
95
114
|
}
|
|
96
115
|
|
|
97
116
|
function getErrorMessage(error: unknown): string {
|
|
@@ -117,6 +136,45 @@ function isAsyncRunDir(root: string, entry: string): boolean {
|
|
|
117
136
|
}
|
|
118
137
|
}
|
|
119
138
|
|
|
139
|
+
type TargetedAsyncRunResolution =
|
|
140
|
+
| { kind: "exact"; id: string }
|
|
141
|
+
| { kind: "scan" }
|
|
142
|
+
| { kind: "reject" };
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Resolve an exact targeted run without following a run-directory symlink or
|
|
146
|
+
* accepting a path whose canonical location escaped the async root.
|
|
147
|
+
*/
|
|
148
|
+
export function resolveTargetedAsyncRun(asyncDirRoot: string, id: string, sessionId?: string): TargetedAsyncRunResolution {
|
|
149
|
+
if (!id || id === "." || id === ".." || path.basename(id) !== id) return { kind: "reject" };
|
|
150
|
+
const asyncDir = path.join(asyncDirRoot, id);
|
|
151
|
+
let entryStat: fs.Stats;
|
|
152
|
+
try {
|
|
153
|
+
entryStat = fs.lstatSync(asyncDir);
|
|
154
|
+
} catch (error) {
|
|
155
|
+
if (isNotFoundError(error)) return { kind: "scan" };
|
|
156
|
+
throw new Error(`Failed to inspect async run path '${asyncDir}': ${getErrorMessage(error)}`, {
|
|
157
|
+
cause: error instanceof Error ? error : undefined,
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
if (!entryStat.isDirectory() || entryStat.isSymbolicLink()) return { kind: "reject" };
|
|
161
|
+
try {
|
|
162
|
+
const canonicalRoot = fs.realpathSync(asyncDirRoot);
|
|
163
|
+
const canonicalDir = fs.realpathSync(asyncDir);
|
|
164
|
+
if (canonicalDir !== canonicalRoot && !canonicalDir.startsWith(`${canonicalRoot}${path.sep}`)) return { kind: "reject" };
|
|
165
|
+
} catch (error) {
|
|
166
|
+
if (isNotFoundError(error)) return { kind: "reject" };
|
|
167
|
+
throw new Error(`Failed to resolve async run path '${asyncDir}': ${getErrorMessage(error)}`, {
|
|
168
|
+
cause: error instanceof Error ? error : undefined,
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
if (sessionId !== undefined) {
|
|
172
|
+
const status = readStatus(asyncDir);
|
|
173
|
+
if (status?.sessionId !== sessionId) return { kind: "scan" };
|
|
174
|
+
}
|
|
175
|
+
return { kind: "exact", id };
|
|
176
|
+
}
|
|
177
|
+
|
|
120
178
|
function outputFileMtime(outputFile: string | undefined): number | undefined {
|
|
121
179
|
if (!outputFile) return undefined;
|
|
122
180
|
try {
|
|
@@ -144,6 +202,8 @@ function statusToSummary(asyncDir: string, status: AsyncStatus & { cwd?: string
|
|
|
144
202
|
throw new Error(`Invalid async status '${path.join(asyncDir, "status.json")}': sessionId must be a string.`);
|
|
145
203
|
}
|
|
146
204
|
const { activityState, lastActivityAt } = deriveAsyncActivityState(asyncDir, status);
|
|
205
|
+
const processTerminal = readProcessTerminal(asyncDir, { runId: status.runId, runnerProcessInstanceId: status.processTerminal?.runnerProcessInstanceId })
|
|
206
|
+
?? sanitizeProcessTerminal(status.processTerminal, { runId: status.runId, runnerProcessInstanceId: status.processTerminal?.runnerProcessInstanceId }, path.join(asyncDir, "status.json"));
|
|
147
207
|
const steps = status.steps ?? [];
|
|
148
208
|
const chainStepCount = status.chainStepCount ?? steps.length;
|
|
149
209
|
const parallelGroups = normalizeParallelGroups(status.parallelGroups, steps.length, chainStepCount);
|
|
@@ -163,6 +223,7 @@ function statusToSummary(asyncDir: string, status: AsyncStatus & { cwd?: string
|
|
|
163
223
|
return {
|
|
164
224
|
index,
|
|
165
225
|
agent: step.agent,
|
|
226
|
+
...(step.context ? { context: step.context } : {}),
|
|
166
227
|
...(step.label ? { label: step.label } : {}),
|
|
167
228
|
...(step.phase ? { phase: step.phase } : {}),
|
|
168
229
|
...(step.outputName ? { outputName: step.outputName } : {}),
|
|
@@ -178,8 +239,7 @@ function statusToSummary(asyncDir: string, status: AsyncStatus & { cwd?: string
|
|
|
178
239
|
...(step.recentOutput ? { recentOutput: [...step.recentOutput] } : {}),
|
|
179
240
|
...(step.turnCount !== undefined ? { turnCount: step.turnCount } : {}),
|
|
180
241
|
...(step.toolCount !== undefined ? { toolCount: step.toolCount } : {}),
|
|
181
|
-
...(step.
|
|
182
|
-
...(step.lastSteerAt !== undefined ? { lastSteerAt: step.lastSteerAt } : {}),
|
|
242
|
+
...(step.steering ? { steering: step.steering } : {}),
|
|
183
243
|
...(step.durationMs !== undefined ? { durationMs: step.durationMs } : {}),
|
|
184
244
|
...(step.tokens ? { tokens: step.tokens } : {}),
|
|
185
245
|
...(step.totalCost ? { totalCost: step.totalCost } : {}),
|
|
@@ -187,11 +247,23 @@ function statusToSummary(asyncDir: string, status: AsyncStatus & { cwd?: string
|
|
|
187
247
|
...(step.model ? { model: step.model } : {}),
|
|
188
248
|
...(step.thinking ? { thinking: step.thinking } : {}),
|
|
189
249
|
...(step.attemptedModels ? { attemptedModels: step.attemptedModels } : {}),
|
|
250
|
+
...(step.sessionFile ? { sessionFile: step.sessionFile } : {}),
|
|
251
|
+
...(step.transcriptPath ? { transcriptPath: step.transcriptPath } : {}),
|
|
190
252
|
...(step.error ? { error: step.error } : {}),
|
|
191
253
|
...(step.timedOut !== undefined ? { timedOut: step.timedOut } : {}),
|
|
254
|
+
...(step.stopped !== undefined ? { stopped: step.stopped } : {}),
|
|
192
255
|
...(step.turnBudget ? { turnBudget: step.turnBudget } : {}),
|
|
193
256
|
...(step.turnBudgetExceeded !== undefined ? { turnBudgetExceeded: step.turnBudgetExceeded } : {}),
|
|
194
257
|
...(step.wrapUpRequested !== undefined ? { wrapUpRequested: step.wrapUpRequested } : {}),
|
|
258
|
+
...(step.acceptance ? { acceptance: step.acceptance } : {}),
|
|
259
|
+
...(step.agentContract ? { agentContract: step.agentContract } : {}),
|
|
260
|
+
...(step.launchContractDigest ? { launchContractDigest: step.launchContractDigest } : {}),
|
|
261
|
+
...(step.execution ? { execution: step.execution } : {}),
|
|
262
|
+
...(step.review ? { review: step.review } : {}),
|
|
263
|
+
...(step.effects ? { effects: step.effects } : {}),
|
|
264
|
+
...(step.processTerminal ? { processTerminal: sanitizeProcessTerminal(step.processTerminal, { runId: status.runId, runnerProcessInstanceId: step.processTerminal.runnerProcessInstanceId }, `${path.join(asyncDir, "status.json")} step ${index}`) } : {}),
|
|
265
|
+
...(step.capabilityCeiling ? { capabilityCeiling: step.capabilityCeiling } : {}),
|
|
266
|
+
...(step.capabilityAudit ? { capabilityAudit: step.capabilityAudit } : {}),
|
|
195
267
|
...(step.children?.length ? { children: step.children } : {}),
|
|
196
268
|
};
|
|
197
269
|
});
|
|
@@ -209,9 +281,9 @@ function statusToSummary(asyncDir: string, status: AsyncStatus & { cwd?: string
|
|
|
209
281
|
currentPath: status.currentPath,
|
|
210
282
|
turnCount: status.turnCount,
|
|
211
283
|
toolCount: status.toolCount,
|
|
212
|
-
|
|
213
|
-
lastSteerAt: status.lastSteerAt,
|
|
284
|
+
steering: status.steering,
|
|
214
285
|
mode: status.mode,
|
|
286
|
+
...(summarizeContextModes(summarizedSteps.map((step) => step.context)) ? { context: summarizeContextModes(summarizedSteps.map((step) => step.context)) } : {}),
|
|
215
287
|
cwd: status.cwd,
|
|
216
288
|
startedAt: status.startedAt,
|
|
217
289
|
lastUpdate: status.lastUpdate,
|
|
@@ -219,6 +291,7 @@ function statusToSummary(asyncDir: string, status: AsyncStatus & { cwd?: string
|
|
|
219
291
|
...(status.timeoutMs !== undefined ? { timeoutMs: status.timeoutMs } : {}),
|
|
220
292
|
...(status.deadlineAt !== undefined ? { deadlineAt: status.deadlineAt } : {}),
|
|
221
293
|
...(status.timedOut !== undefined ? { timedOut: status.timedOut } : {}),
|
|
294
|
+
...(status.stopped !== undefined ? { stopped: status.stopped } : {}),
|
|
222
295
|
...(status.turnBudget ? { turnBudget: status.turnBudget } : {}),
|
|
223
296
|
...(status.turnBudgetExceeded !== undefined ? { turnBudgetExceeded: status.turnBudgetExceeded } : {}),
|
|
224
297
|
...(status.wrapUpRequested !== undefined ? { wrapUpRequested: status.wrapUpRequested } : {}),
|
|
@@ -229,6 +302,10 @@ function statusToSummary(asyncDir: string, status: AsyncStatus & { cwd?: string
|
|
|
229
302
|
steps: summarizedSteps,
|
|
230
303
|
...(nestedChildren.length ? { nestedChildren } : {}),
|
|
231
304
|
...(nestedWarnings.length ? { nestedWarnings } : {}),
|
|
305
|
+
...(processTerminal ? { processTerminal } : {}),
|
|
306
|
+
...(status.launchContractDigest ? { launchContractDigest: status.launchContractDigest } : {}),
|
|
307
|
+
...(status.capabilityCeiling ? { capabilityCeiling: status.capabilityCeiling } : {}),
|
|
308
|
+
...(status.capabilityAudit ? { capabilityAudit: status.capabilityAudit } : {}),
|
|
232
309
|
...(status.sessionDir ? { sessionDir: status.sessionDir } : {}),
|
|
233
310
|
...(status.outputFile ? { outputFile: status.outputFile } : {}),
|
|
234
311
|
...(status.totalTokens ? { totalTokens: status.totalTokens } : {}),
|
|
@@ -237,12 +314,17 @@ function statusToSummary(asyncDir: string, status: AsyncStatus & { cwd?: string
|
|
|
237
314
|
};
|
|
238
315
|
}
|
|
239
316
|
|
|
317
|
+
export function summarizeAsyncStatus(asyncDir: string, status: AsyncStatus & { cwd?: string }): AsyncRunSummary {
|
|
318
|
+
return statusToSummary(asyncDir, status);
|
|
319
|
+
}
|
|
320
|
+
|
|
240
321
|
function sortRuns(runs: AsyncRunSummary[]): AsyncRunSummary[] {
|
|
241
322
|
const rank = (state: AsyncRunSummary["state"]): number => {
|
|
242
323
|
switch (state) {
|
|
243
324
|
case "running": return 0;
|
|
244
325
|
case "queued": return 1;
|
|
245
326
|
case "failed": return 2;
|
|
327
|
+
case "stopped": return 2;
|
|
246
328
|
case "paused": return 2;
|
|
247
329
|
case "complete": return 3;
|
|
248
330
|
}
|
|
@@ -259,7 +341,19 @@ function sortRuns(runs: AsyncRunSummary[]): AsyncRunSummary[] {
|
|
|
259
341
|
export function listAsyncRuns(asyncDirRoot: string, options: AsyncRunListOptions = {}): AsyncRunSummary[] {
|
|
260
342
|
let entries: string[];
|
|
261
343
|
try {
|
|
262
|
-
|
|
344
|
+
if (options.runId !== undefined) {
|
|
345
|
+
const resolution = resolveTargetedAsyncRun(asyncDirRoot, options.runId, options.sessionId);
|
|
346
|
+
entries = resolution.kind === "exact"
|
|
347
|
+
? [resolution.id]
|
|
348
|
+
: resolution.kind === "scan"
|
|
349
|
+
? fs.readdirSync(asyncDirRoot).filter((entry) =>
|
|
350
|
+
(entry === options.runId || entry.startsWith(options.runId!))
|
|
351
|
+
&& resolveTargetedAsyncRun(asyncDirRoot, entry, options.sessionId).kind === "exact"
|
|
352
|
+
)
|
|
353
|
+
: [];
|
|
354
|
+
} else {
|
|
355
|
+
entries = fs.readdirSync(asyncDirRoot).filter((entry) => isAsyncRunDir(asyncDirRoot, entry));
|
|
356
|
+
}
|
|
263
357
|
} catch (error) {
|
|
264
358
|
if (isNotFoundError(error)) return [];
|
|
265
359
|
throw new Error(`Failed to list async runs in '${asyncDirRoot}': ${getErrorMessage(error)}`, {
|
|
@@ -294,11 +388,13 @@ export function listAsyncRuns(asyncDirRoot: string, options: AsyncRunListOptions
|
|
|
294
388
|
if (options.sessionId && status.sessionId !== options.sessionId) continue;
|
|
295
389
|
const nestedWarnings: string[] = [];
|
|
296
390
|
let nestedRoute: NestedRoute | undefined;
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
391
|
+
if (options.reconcile !== false) {
|
|
392
|
+
try {
|
|
393
|
+
nestedRoute = resolveNestedRoute(status.runId || path.basename(asyncDir));
|
|
394
|
+
if (nestedRoute) reconcileNestedAsyncDescendants(nestedRoute, { resultsDir: options.resultsDir, kill: options.kill, now: options.now });
|
|
395
|
+
} catch (error) {
|
|
396
|
+
nestedWarnings.push(`Nested status unavailable: ${getErrorMessage(error)}`);
|
|
397
|
+
}
|
|
302
398
|
}
|
|
303
399
|
const summary = statusToSummary(asyncDir, status, nestedWarnings, nestedRoute);
|
|
304
400
|
runs.push(summary);
|
|
@@ -308,26 +404,27 @@ export function listAsyncRuns(asyncDirRoot: string, options: AsyncRunListOptions
|
|
|
308
404
|
return options.limit !== undefined ? sorted.slice(0, options.limit) : sorted;
|
|
309
405
|
}
|
|
310
406
|
|
|
311
|
-
function formatActivityFacts(input: { activityState?: ActivityState; lastActivityAt?: number; currentTool?: string; currentToolStartedAt?: number; currentPath?: string; turnCount?: number; toolCount?: number;
|
|
407
|
+
function formatActivityFacts(input: { activityState?: ActivityState; lastActivityAt?: number; currentTool?: string; currentToolStartedAt?: number; currentPath?: string; turnCount?: number; toolCount?: number; steering?: SteeringStatus; turnBudget?: TurnBudgetState; turnBudgetExceeded?: boolean; wrapUpRequested?: boolean }): string | undefined {
|
|
312
408
|
const facts: string[] = [];
|
|
313
409
|
if (input.currentTool && input.currentToolStartedAt !== undefined) facts.push(`tool ${input.currentTool} ${formatDuration(Math.max(0, Date.now() - input.currentToolStartedAt))}`);
|
|
314
410
|
else if (input.currentTool) facts.push(`tool ${input.currentTool}`);
|
|
315
411
|
if (input.currentPath) facts.push(shortenPath(input.currentPath));
|
|
316
412
|
if (input.turnCount !== undefined) facts.push(`${input.turnCount} turns`);
|
|
317
413
|
if (input.turnBudgetExceeded && input.turnBudget) facts.push(`turn budget exceeded ${input.turnBudget.turnCount}/${input.turnBudget.maxTurns}+${input.turnBudget.graceTurns}`);
|
|
414
|
+
else if (input.turnBudget?.outcome === "termination-deferred") facts.push(`turn-budget termination deferred ${input.turnBudget.turnCount}/${input.turnBudget.maxTurns}+${input.turnBudget.graceTurns}`);
|
|
318
415
|
else if (input.wrapUpRequested && input.turnBudget) facts.push(`wrap-up requested ${input.turnBudget.turnCount}/${input.turnBudget.maxTurns}`);
|
|
319
416
|
else if (input.turnBudget) facts.push(`turn budget ${input.turnBudget.turnCount}/${input.turnBudget.maxTurns}+${input.turnBudget.graceTurns}`);
|
|
320
417
|
if (input.toolCount !== undefined) facts.push(`${input.toolCount} tools`);
|
|
321
|
-
if (input.
|
|
322
|
-
if (typeof input.lastSteerAt === "number" && Number.isFinite(input.lastSteerAt)) facts.push(`last steer ${new Date(input.lastSteerAt).toISOString()}`);
|
|
418
|
+
if (input.steering) facts.push(`steering ${input.steering.scheduled} scheduled, ${input.steering.pending} pending, ${input.steering.delivered} delivered, ${input.steering.failed} failed, ${input.steering.recovered} recovered`);
|
|
323
419
|
const activity = formatActivityLabel(input.lastActivityAt, input.activityState);
|
|
324
420
|
return activity || facts.length ? [activity, ...facts].filter(Boolean).join(" | ") : undefined;
|
|
325
421
|
}
|
|
326
422
|
|
|
327
423
|
function formatStepLine(step: AsyncRunStepSummary): string {
|
|
328
424
|
const display = step.label ? `${step.label} (${step.agent})` : step.agent;
|
|
425
|
+
const context = contextModeLabel(step.context);
|
|
329
426
|
const phase = step.phase ? `[${step.phase}] ` : "";
|
|
330
|
-
const parts = [`${step.index + 1}. ${phase}${display}`, step.status];
|
|
427
|
+
const parts = [`${step.index + 1}. ${phase}${display}${context ? ` ${context}` : ""}`, step.status];
|
|
331
428
|
const activity = formatActivityFacts(step);
|
|
332
429
|
if (activity) parts.push(activity);
|
|
333
430
|
const modelThinking = formatModelThinking(step.model, step.thinking);
|
|
@@ -368,7 +465,8 @@ function formatRunHeader(run: AsyncRunSummary): string {
|
|
|
368
465
|
const cwd = run.cwd ? shortenPath(run.cwd) : shortenPath(run.asyncDir);
|
|
369
466
|
const activity = formatActivityFacts(run);
|
|
370
467
|
const pending = run.pendingAppends ? ` | ${run.pendingAppends} pending append${run.pendingAppends === 1 ? "" : "s"}` : "";
|
|
371
|
-
|
|
468
|
+
const context = contextModeLabel(run.context);
|
|
469
|
+
return `${run.id} | ${run.state}${activity ? ` | ${activity}` : ""} | ${run.mode}${context ? ` ${context}` : ""} | ${stepLabel}${pending} | ${cwd}`;
|
|
372
470
|
}
|
|
373
471
|
|
|
374
472
|
export function formatAsyncRunList(runs: AsyncRunSummary[], heading = "Active async runs"): string {
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import type { AgentToolResult } from "@earendil-works/pi-agent-core";
|
|
2
|
+
import { snapshotBackgroundWork } from "../../api/background-work.ts";
|
|
3
|
+
import { ASYNC_DIR, RESULTS_DIR, type Details, type SubagentState } from "../../shared/types.ts";
|
|
4
|
+
import { listAsyncRuns } from "./async-status.ts";
|
|
5
|
+
import { waitForSubagents, type SubagentWaitDeps, type SubagentWaitParams, type WaitEventBus } from "./subagent-wait.ts";
|
|
6
|
+
|
|
7
|
+
export const DEFAULT_AUTO_DRAIN_TIMEOUT_MS = 30 * 60 * 1000;
|
|
8
|
+
|
|
9
|
+
export interface AutoDrainDeps {
|
|
10
|
+
state: SubagentState;
|
|
11
|
+
events?: WaitEventBus;
|
|
12
|
+
timeoutMs?: number;
|
|
13
|
+
now?: () => number;
|
|
14
|
+
wait?: (
|
|
15
|
+
params: SubagentWaitParams,
|
|
16
|
+
signal: AbortSignal | undefined,
|
|
17
|
+
deps: SubagentWaitDeps,
|
|
18
|
+
) => Promise<AgentToolResult<Details>>;
|
|
19
|
+
hasWork?: (sessionId: string, nowMs: number) => boolean;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function resultText(value: AgentToolResult<Details>): string {
|
|
23
|
+
return value.content.map((part) => part.type === "text" ? part.text : "").join(" ").trim();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function hasOutstandingWork(sessionId: string, nowMs: number): boolean {
|
|
27
|
+
const asyncRuns = listAsyncRuns(ASYNC_DIR, {
|
|
28
|
+
states: ["queued", "running"],
|
|
29
|
+
sessionId,
|
|
30
|
+
resultsDir: RESULTS_DIR,
|
|
31
|
+
now: () => nowMs,
|
|
32
|
+
});
|
|
33
|
+
return asyncRuns.length > 0 || snapshotBackgroundWork(sessionId, nowMs).items.length > 0;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** Drain all work owned by the current headless session, including work added while draining. */
|
|
37
|
+
export async function drainOutstandingWork(deps: AutoDrainDeps): Promise<void> {
|
|
38
|
+
const sessionId = deps.state.currentSessionId;
|
|
39
|
+
if (!sessionId) throw new Error("Cannot auto-drain background work without an active session identity.");
|
|
40
|
+
const now = deps.now ?? Date.now;
|
|
41
|
+
const timeoutMs = deps.timeoutMs ?? DEFAULT_AUTO_DRAIN_TIMEOUT_MS;
|
|
42
|
+
if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) throw new Error("Auto-drain timeoutMs must be a positive finite number.");
|
|
43
|
+
const deadlineAt = now() + timeoutMs;
|
|
44
|
+
const hasWork = deps.hasWork ?? hasOutstandingWork;
|
|
45
|
+
const wait = deps.wait ?? waitForSubagents;
|
|
46
|
+
|
|
47
|
+
while (hasWork(sessionId, now())) {
|
|
48
|
+
const remainingMs = deadlineAt - now();
|
|
49
|
+
if (remainingMs <= 0) {
|
|
50
|
+
throw new Error(`Auto-drain timed out after ${timeoutMs}ms with background work still active in session '${sessionId}'.`);
|
|
51
|
+
}
|
|
52
|
+
const waitResult = await wait(
|
|
53
|
+
{ all: true, timeoutMs: remainingMs },
|
|
54
|
+
undefined,
|
|
55
|
+
{
|
|
56
|
+
state: deps.state,
|
|
57
|
+
events: deps.events,
|
|
58
|
+
now,
|
|
59
|
+
stopOnAttention: false,
|
|
60
|
+
failOnFailedRuns: true,
|
|
61
|
+
},
|
|
62
|
+
);
|
|
63
|
+
if (waitResult.isError) {
|
|
64
|
+
throw new Error(`Auto-drain failed for session '${sessionId}': ${resultText(waitResult) || "subagent_wait returned an error without details"}.`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -131,6 +131,7 @@ export function consumeChainAppendRequests(asyncDir: string): ChainAppendRequest
|
|
|
131
131
|
function statusStepForTask(task: RunnerSubagentStep): StatusStep {
|
|
132
132
|
return {
|
|
133
133
|
agent: task.agent,
|
|
134
|
+
...(task.context ? { context: task.context } : {}),
|
|
134
135
|
phase: task.phase,
|
|
135
136
|
label: task.label,
|
|
136
137
|
outputName: task.outputName,
|
|
@@ -151,6 +152,7 @@ function statusStepsForRunnerStep(step: RunnerStep): StatusStep[] {
|
|
|
151
152
|
if (isDynamicRunnerGroup(step)) {
|
|
152
153
|
return [{
|
|
153
154
|
agent: `expand:${step.parallel.agent}`,
|
|
155
|
+
...(step.parallel.context ? { context: step.parallel.context } : {}),
|
|
154
156
|
phase: step.phase ?? step.parallel.phase,
|
|
155
157
|
label: step.label ?? step.parallel.label ?? `Dynamic fanout (${step.collect.as})`,
|
|
156
158
|
outputName: step.collect.as,
|
|
@@ -27,6 +27,7 @@ export interface ImportedAsyncRootResult {
|
|
|
27
27
|
structuredOutputSchemaPath?: string;
|
|
28
28
|
acceptance?: AcceptanceLedger;
|
|
29
29
|
timedOut?: boolean;
|
|
30
|
+
stopped?: boolean;
|
|
30
31
|
}
|
|
31
32
|
|
|
32
33
|
interface AsyncResultFile {
|
|
@@ -35,12 +36,14 @@ interface AsyncResultFile {
|
|
|
35
36
|
summary?: string;
|
|
36
37
|
error?: string;
|
|
37
38
|
timedOut?: boolean;
|
|
39
|
+
stopped?: boolean;
|
|
38
40
|
results?: Array<{
|
|
39
41
|
agent?: string;
|
|
40
42
|
output?: string;
|
|
41
43
|
error?: string;
|
|
42
44
|
success?: boolean;
|
|
43
45
|
timedOut?: boolean;
|
|
46
|
+
stopped?: boolean;
|
|
44
47
|
sessionFile?: string;
|
|
45
48
|
intercomTarget?: string;
|
|
46
49
|
model?: string;
|
|
@@ -54,8 +57,8 @@ interface AsyncResultFile {
|
|
|
54
57
|
}>;
|
|
55
58
|
}
|
|
56
59
|
|
|
57
|
-
const TERMINAL_STATES = new Set(["complete", "failed", "paused"]);
|
|
58
|
-
const TERMINAL_STEP_STATUSES = new Set(["complete", "completed", "failed", "paused"]);
|
|
60
|
+
const TERMINAL_STATES = new Set(["complete", "failed", "paused", "stopped"]);
|
|
61
|
+
const TERMINAL_STEP_STATUSES = new Set(["complete", "completed", "failed", "paused", "stopped"]);
|
|
59
62
|
|
|
60
63
|
function readResultFile(resultPath: string): AsyncResultFile | undefined {
|
|
61
64
|
try {
|
|
@@ -79,11 +82,12 @@ function isTerminalStatus(status: AsyncStatus | null, index: number): boolean {
|
|
|
79
82
|
return TERMINAL_STATES.has(status.state);
|
|
80
83
|
}
|
|
81
84
|
|
|
82
|
-
function resultState(result: AsyncResultFile | undefined, child: NonNullable<AsyncResultFile["results"]>[number] | undefined): "complete" | "failed" | "paused" | undefined {
|
|
85
|
+
function resultState(result: AsyncResultFile | undefined, child: NonNullable<AsyncResultFile["results"]>[number] | undefined): "complete" | "failed" | "paused" | "stopped" | undefined {
|
|
83
86
|
if (!result) return undefined;
|
|
87
|
+
if (child?.stopped === true) return "stopped";
|
|
84
88
|
if (child?.success === true) return "complete";
|
|
85
|
-
if (child?.success === false) return result.state === "paused" ? "paused" : "failed";
|
|
86
|
-
if (result.state === "complete" || result.state === "failed" || result.state === "paused") return result.state;
|
|
89
|
+
if (child?.success === false) return result.state === "stopped" ? "stopped" : result.state === "paused" ? "paused" : "failed";
|
|
90
|
+
if (result.state === "complete" || result.state === "failed" || result.state === "paused" || result.state === "stopped") return result.state;
|
|
87
91
|
if (result.success === true) return "complete";
|
|
88
92
|
if (result.success === false) return "failed";
|
|
89
93
|
return undefined;
|
|
@@ -92,7 +96,8 @@ function resultState(result: AsyncResultFile | undefined, child: NonNullable<Asy
|
|
|
92
96
|
function outputFromTerminalStatus(root: ImportedAsyncRoot, status: AsyncStatus, step: NonNullable<AsyncStatus["steps"]>[number] | undefined): ImportedAsyncRootResult {
|
|
93
97
|
const agent = step?.agent ?? status.steps?.[root.index]?.agent ?? "subagent";
|
|
94
98
|
const timedOut = step?.timedOut === true || status.timedOut === true;
|
|
95
|
-
const
|
|
99
|
+
const stopped = step?.stopped === true || status.stopped === true || status.state === "stopped";
|
|
100
|
+
const message = step?.error ?? status.error ?? (stopped ? "Subagent stopped by user." : `Attached async root ${root.runId} ended without a result file at ${root.resultPath}.`);
|
|
96
101
|
return {
|
|
97
102
|
agent,
|
|
98
103
|
output: message,
|
|
@@ -100,6 +105,7 @@ function outputFromTerminalStatus(root: ImportedAsyncRoot, status: AsyncStatus,
|
|
|
100
105
|
exitCode: 1,
|
|
101
106
|
error: message,
|
|
102
107
|
...(timedOut ? { timedOut: true } : {}),
|
|
108
|
+
...(stopped ? { stopped: true } : {}),
|
|
103
109
|
...(step?.sessionFile ?? status.sessionFile ? { sessionFile: step?.sessionFile ?? status.sessionFile } : {}),
|
|
104
110
|
...(step?.model ? { model: step.model } : {}),
|
|
105
111
|
...(step?.attemptedModels ? { attemptedModels: step.attemptedModels } : {}),
|
|
@@ -136,8 +142,9 @@ function buildImportedResult(root: ImportedAsyncRoot, status: AsyncStatus | null
|
|
|
136
142
|
const agent = child?.agent ?? step?.agent ?? status?.steps?.[root.index]?.agent ?? "subagent";
|
|
137
143
|
const output = child?.output ?? result.summary ?? "";
|
|
138
144
|
const timedOut = child?.timedOut === true || step?.timedOut === true || result.timedOut === true || status?.timedOut === true;
|
|
139
|
-
const
|
|
140
|
-
const
|
|
145
|
+
const stopped = child?.stopped === true || step?.stopped === true || result.stopped === true || status?.stopped === true || state === "stopped";
|
|
146
|
+
const success = state === "complete" && !timedOut && !stopped;
|
|
147
|
+
const error = child?.error ?? (success ? undefined : stopped ? "Subagent stopped by user." : result.error ?? result.summary ?? status?.error ?? `Attached async root ${root.runId} did not complete successfully.`);
|
|
141
148
|
return {
|
|
142
149
|
agent,
|
|
143
150
|
output: success ? output : (output || error || ""),
|
|
@@ -145,6 +152,7 @@ function buildImportedResult(root: ImportedAsyncRoot, status: AsyncStatus | null
|
|
|
145
152
|
exitCode: success ? 0 : 1,
|
|
146
153
|
...(error ? { error } : {}),
|
|
147
154
|
...(timedOut ? { timedOut: true } : {}),
|
|
155
|
+
...(stopped ? { stopped: true } : {}),
|
|
148
156
|
...(child?.sessionFile ?? step?.sessionFile ?? status?.sessionFile ? { sessionFile: child?.sessionFile ?? step?.sessionFile ?? status?.sessionFile } : {}),
|
|
149
157
|
...(child?.intercomTarget ? { intercomTarget: child.intercomTarget } : {}),
|
|
150
158
|
...(child?.model ?? step?.model ? { model: child?.model ?? step?.model } : {}),
|
|
@@ -89,8 +89,8 @@ export interface CompletionBatcher<T> {
|
|
|
89
89
|
push(item: T): void;
|
|
90
90
|
/** Emit any held items immediately as a single group. */
|
|
91
91
|
flush(): void;
|
|
92
|
-
/** Clear timers
|
|
93
|
-
dispose():
|
|
92
|
+
/** Clear timers and return items that were never emitted. */
|
|
93
|
+
dispose(): T[];
|
|
94
94
|
}
|
|
95
95
|
|
|
96
96
|
/**
|
|
@@ -109,7 +109,7 @@ export function createCompletionBatcher<T>(options: CompletionBatcherOptions<T>)
|
|
|
109
109
|
options.emit([item]);
|
|
110
110
|
},
|
|
111
111
|
flush() {},
|
|
112
|
-
dispose() {},
|
|
112
|
+
dispose() { return []; },
|
|
113
113
|
};
|
|
114
114
|
}
|
|
115
115
|
|
|
@@ -160,7 +160,9 @@ export function createCompletionBatcher<T>(options: CompletionBatcherOptions<T>)
|
|
|
160
160
|
flush: emitGroup,
|
|
161
161
|
dispose() {
|
|
162
162
|
clearTimers();
|
|
163
|
+
const abandoned = pending;
|
|
163
164
|
pending = [];
|
|
164
|
-
|
|
165
|
+
return abandoned;
|
|
166
|
+
}
|
|
165
167
|
};
|
|
166
168
|
}
|
|
@@ -20,9 +20,9 @@ function asFiniteNumber(value: unknown): number | undefined {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
export function buildCompletionKey(data: CompletionDataLike, fallback: string): string {
|
|
23
|
-
const id = asNonEmptyString(data.id);
|
|
24
|
-
if (id) return `id:${id}`;
|
|
25
23
|
const sessionId = asNonEmptyString(data.sessionId) ?? "no-session";
|
|
24
|
+
const id = asNonEmptyString(data.id);
|
|
25
|
+
if (id) return `session:${sessionId}:id:${id}`;
|
|
26
26
|
const agent = asNonEmptyString(data.agent) ?? "unknown";
|
|
27
27
|
const timestamp = asFiniteNumber(data.timestamp);
|
|
28
28
|
const taskIndex = asFiniteNumber(data.taskIndex);
|
|
@@ -52,12 +52,3 @@ export function markSeenWithTtl(seen: Map<string, number>, key: string, now: num
|
|
|
52
52
|
seen.set(key, now);
|
|
53
53
|
return false;
|
|
54
54
|
}
|
|
55
|
-
|
|
56
|
-
export function getGlobalSeenMap(storeKey: string): Map<string, number> {
|
|
57
|
-
const globalStore = globalThis as Record<string, unknown>;
|
|
58
|
-
const existing = globalStore[storeKey];
|
|
59
|
-
if (existing instanceof Map) return existing as Map<string, number>;
|
|
60
|
-
const map = new Map<string, number>();
|
|
61
|
-
globalStore[storeKey] = map;
|
|
62
|
-
return map;
|
|
63
|
-
}
|