@selesai/code 0.5.27 → 0.5.29
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/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +9 -1
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/config-selector.d.ts.map +1 -1
- package/dist/cli/config-selector.js +1 -1
- package/dist/cli/config-selector.js.map +1 -1
- package/dist/cli/credential-print.d.ts +23 -0
- package/dist/cli/credential-print.d.ts.map +1 -0
- package/dist/cli/credential-print.js +117 -0
- package/dist/cli/credential-print.js.map +1 -0
- package/dist/cli/startup-ui.d.ts.map +1 -1
- package/dist/cli/startup-ui.js +1 -1
- package/dist/cli/startup-ui.js.map +1 -1
- package/dist/core/agent-session-runtime.d.ts.map +1 -1
- package/dist/core/agent-session-runtime.js +3 -0
- package/dist/core/agent-session-runtime.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 +20 -14
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/compaction/compaction.d.ts.map +1 -1
- package/dist/core/compaction/compaction.js +11 -3
- package/dist/core/compaction/compaction.js.map +1 -1
- package/dist/core/extensions/runner.d.ts +1 -0
- package/dist/core/extensions/runner.d.ts.map +1 -1
- package/dist/core/extensions/runner.js +11 -0
- package/dist/core/extensions/runner.js.map +1 -1
- package/dist/core/extensions/types.d.ts +14 -1
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/footer-data-provider.d.ts +10 -0
- package/dist/core/footer-data-provider.d.ts.map +1 -1
- package/dist/core/footer-data-provider.js +1 -1
- package/dist/core/footer-data-provider.js.map +1 -1
- package/dist/core/llama/provider.d.ts.map +1 -1
- package/dist/core/llama/provider.js +8 -3
- package/dist/core/llama/provider.js.map +1 -1
- package/dist/core/model-config.d.ts +30 -0
- package/dist/core/model-config.d.ts.map +1 -1
- package/dist/core/model-config.js +6 -0
- package/dist/core/model-config.js.map +1 -1
- package/dist/core/model-registry.d.ts.map +1 -1
- package/dist/core/model-registry.js +2 -2
- package/dist/core/model-registry.js.map +1 -1
- package/dist/core/model-resolver.d.ts +1 -0
- package/dist/core/model-resolver.d.ts.map +1 -1
- package/dist/core/model-resolver.js +20 -3
- package/dist/core/model-resolver.js.map +1 -1
- package/dist/core/model-runtime.d.ts +2 -0
- package/dist/core/model-runtime.d.ts.map +1 -1
- package/dist/core/model-runtime.js +5 -4
- package/dist/core/model-runtime.js.map +1 -1
- package/dist/core/package-manager.d.ts.map +1 -1
- package/dist/core/package-manager.js +13 -6
- package/dist/core/package-manager.js.map +1 -1
- package/dist/core/remote-catalog-provider.d.ts +1 -1
- package/dist/core/remote-catalog-provider.d.ts.map +1 -1
- package/dist/core/remote-catalog-provider.js +24 -11
- package/dist/core/remote-catalog-provider.js.map +1 -1
- package/dist/core/resource-loader.d.ts +15 -0
- package/dist/core/resource-loader.d.ts.map +1 -1
- package/dist/core/resource-loader.js +66 -9
- package/dist/core/resource-loader.js.map +1 -1
- package/dist/core/settings-manager.d.ts +1 -1
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/system-prompt.d.ts.map +1 -1
- package/dist/core/system-prompt.js +1 -1
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/core/tools/bash.d.ts +2 -0
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +34 -5
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/tool-definition-wrapper.d.ts.map +1 -1
- package/dist/core/tools/tool-definition-wrapper.js +3 -1
- package/dist/core/tools/tool-definition-wrapper.js.map +1 -1
- package/dist/extensions/pi-intercom/CHANGELOG.md +249 -0
- package/dist/extensions/pi-intercom/README.md +102 -39
- package/dist/extensions/pi-intercom/broker/broker.ts +1233 -36
- package/dist/extensions/pi-intercom/broker/client.test.ts +83 -0
- package/dist/extensions/pi-intercom/broker/client.ts +315 -12
- package/dist/extensions/pi-intercom/broker/extension-state.ts +186 -0
- package/dist/extensions/pi-intercom/broker/extension.test.ts +387 -0
- package/dist/extensions/pi-intercom/broker/framing.test.ts +114 -0
- package/dist/extensions/pi-intercom/broker/framing.ts +82 -24
- package/dist/extensions/pi-intercom/broker/paths.test.ts +153 -0
- package/dist/extensions/pi-intercom/broker/paths.ts +117 -8
- package/dist/extensions/pi-intercom/broker/runtime-claim.test.ts +34 -0
- package/dist/extensions/pi-intercom/broker/runtime-claim.ts +21 -0
- package/dist/extensions/pi-intercom/broker/spawn.test.ts +160 -23
- package/dist/extensions/pi-intercom/broker/spawn.ts +113 -27
- package/dist/extensions/pi-intercom/config.test.ts +93 -0
- package/dist/extensions/pi-intercom/config.ts +55 -6
- package/dist/extensions/pi-intercom/cwd.test.ts +40 -0
- package/dist/extensions/pi-intercom/cwd.ts +31 -0
- package/dist/extensions/pi-intercom/extension-api.ts +44 -0
- package/dist/extensions/pi-intercom/format-context.test.ts +31 -0
- package/dist/extensions/pi-intercom/format-context.ts +32 -0
- package/dist/extensions/pi-intercom/index.ts +742 -145
- package/dist/extensions/pi-intercom/intercom.integration.test.ts +2646 -0
- package/dist/extensions/pi-intercom/package.json +15 -5
- package/dist/extensions/pi-intercom/reply-tracker.test.ts +134 -0
- package/dist/extensions/pi-intercom/reply-tracker.ts +31 -13
- package/dist/extensions/pi-intercom/skills/pi-intercom/SKILL.md +13 -11
- package/dist/extensions/pi-intercom/test/inline-message.test.ts +184 -0
- package/dist/extensions/pi-intercom/test/overlay-width.test.ts +66 -0
- package/dist/extensions/pi-intercom/types.ts +94 -4
- package/dist/extensions/pi-intercom/ui/compose.ts +8 -4
- package/dist/extensions/pi-intercom/ui/inline-message.ts +61 -25
- package/dist/extensions/pi-intercom/ui/session-list.ts +7 -3
- package/dist/extensions/pi-subagents/CHANGELOG.md +85 -0
- package/dist/extensions/pi-subagents/LICENSE +21 -0
- package/dist/extensions/pi-subagents/README.md +146 -52
- package/dist/extensions/pi-subagents/agents/architect.md +1 -0
- package/dist/extensions/pi-subagents/agents/builder.md +1 -0
- package/dist/extensions/pi-subagents/package-lock.json +2 -2
- package/dist/extensions/pi-subagents/package.json +3 -3
- package/dist/extensions/pi-subagents/prompts/review-loop.md +1 -1
- package/dist/extensions/pi-subagents/skills/pi-subagents/SKILL.md +21 -989
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/constraints-and-recipes.md +256 -0
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/execution-controls.md +430 -0
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/management-authoring-rpc.md +144 -0
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/prompting-and-roles.md +282 -0
- package/dist/extensions/pi-subagents/src/agents/agent-management.ts +71 -30
- package/dist/extensions/pi-subagents/src/agents/agent-serializer.ts +4 -0
- package/dist/extensions/pi-subagents/src/agents/agents.ts +118 -9
- package/dist/extensions/pi-subagents/src/agents/skills.ts +14 -12
- package/dist/extensions/pi-subagents/src/api/delegation.ts +3 -0
- package/dist/extensions/pi-subagents/src/api/preflight.ts +16 -12
- package/dist/extensions/pi-subagents/src/extension/chain-validation.ts +17 -1
- package/dist/extensions/pi-subagents/src/extension/index.ts +17 -7
- package/dist/extensions/pi-subagents/src/extension/rpc.ts +248 -6
- package/dist/extensions/pi-subagents/src/extension/schemas.ts +31 -6
- package/dist/extensions/pi-subagents/src/extension/tool-description.ts +8 -7
- package/dist/extensions/pi-subagents/src/intercom/intercom-bridge.ts +9 -4
- package/dist/extensions/pi-subagents/src/intercom/result-intercom.ts +33 -4
- package/dist/extensions/pi-subagents/src/runs/background/async-execution.ts +63 -16
- package/dist/extensions/pi-subagents/src/runs/background/async-job-tracker.ts +24 -13
- package/dist/extensions/pi-subagents/src/runs/background/async-resume.ts +7 -5
- package/dist/extensions/pi-subagents/src/runs/background/async-status.ts +18 -2
- package/dist/extensions/pi-subagents/src/runs/background/chain-append.ts +48 -5
- package/dist/extensions/pi-subagents/src/runs/background/control-channel.ts +68 -1
- package/dist/extensions/pi-subagents/src/runs/background/notify.ts +29 -4
- package/dist/extensions/pi-subagents/src/runs/background/result-watcher.ts +29 -6
- package/dist/extensions/pi-subagents/src/runs/background/run-status.ts +29 -3
- package/dist/extensions/pi-subagents/src/runs/background/subagent-runner.ts +372 -28
- package/dist/extensions/pi-subagents/src/runs/foreground/async-stop-action.ts +65 -0
- package/dist/extensions/pi-subagents/src/runs/foreground/chain-clarify.ts +3 -3
- package/dist/extensions/pi-subagents/src/runs/foreground/chain-execution.ts +143 -43
- package/dist/extensions/pi-subagents/src/runs/foreground/execution.ts +527 -247
- package/dist/extensions/pi-subagents/src/runs/foreground/foreground-control.ts +42 -0
- package/dist/extensions/pi-subagents/src/runs/foreground/subagent-executor.ts +426 -123
- package/dist/extensions/pi-subagents/src/runs/shared/acceptance.ts +15 -7
- package/dist/extensions/pi-subagents/src/runs/shared/capability-ceiling.ts +51 -19
- package/dist/extensions/pi-subagents/src/runs/shared/chain-outputs.ts +3 -1
- package/dist/extensions/pi-subagents/src/runs/shared/dynamic-fanout.ts +1 -1
- package/dist/extensions/pi-subagents/src/runs/shared/mcp-direct-tool-allowlist.ts +44 -11
- package/dist/extensions/pi-subagents/src/runs/shared/model-fallback.ts +8 -0
- package/dist/extensions/pi-subagents/src/runs/shared/nested-events.ts +97 -20
- package/dist/extensions/pi-subagents/src/runs/shared/parallel-utils.ts +43 -12
- package/dist/extensions/pi-subagents/src/runs/shared/pi-args.ts +52 -4
- package/dist/extensions/pi-subagents/src/runs/shared/process-signal.ts +19 -0
- package/dist/extensions/pi-subagents/src/runs/shared/run-history.ts +45 -9
- package/dist/extensions/pi-subagents/src/runs/shared/runtime-acknowledged-extensions.ts +71 -0
- package/dist/extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.ts +31 -1
- package/dist/extensions/pi-subagents/src/runs/shared/subagent-startup-retry.ts +101 -0
- package/dist/extensions/pi-subagents/src/runs/shared/task-intent.ts +1 -1
- package/dist/extensions/pi-subagents/src/runs/shared/usage-budget.ts +65 -0
- package/dist/extensions/pi-subagents/src/runs/shared/workflow-graph.ts +26 -1
- package/dist/extensions/pi-subagents/src/shared/settings.ts +17 -1
- package/dist/extensions/pi-subagents/src/shared/types.ts +155 -10
- package/dist/extensions/pi-subagents/src/shared/utils.ts +73 -4
- package/dist/extensions/pi-subagents/src/slash/delegation-adapters.ts +5 -0
- package/dist/extensions/pi-subagents/src/slash/slash-commands.ts +46 -4
- package/dist/extensions/pi-subagents/src/slash/slash-live-state.ts +5 -3
- package/dist/extensions/pi-subagents/src/tui/fleet-status.ts +87 -24
- package/dist/extensions/pi-subagents/src/tui/fleet.ts +182 -9
- package/dist/extensions/pi-subagents/src/tui/render.ts +27 -28
- package/dist/extensions/pi-subagents/src/watchdog/register-main.ts +14 -7
- package/dist/extensions/pi-subagents/src/watchdog/review.ts +5 -4
- package/dist/extensions/pi-subagents/src/watchdog/runtime.ts +170 -17
- package/dist/extensions/pi-subagents/src/watchdog/scope.ts +62 -0
- package/dist/extensions/pi-subagents/src/watchdog/settings.ts +41 -1
- package/dist/extensions/pi-subagents/src/watchdog/types.ts +10 -0
- package/dist/extensions/pi-subagents/test/e2e/real-session-subagent.test.ts +2 -2
- package/dist/extensions/pi-subagents/test/integration/async-execution.test.ts +348 -7
- package/dist/extensions/pi-subagents/test/integration/async-job-tracker.test.ts +84 -0
- package/dist/extensions/pi-subagents/test/integration/async-status.test.ts +4 -2
- package/dist/extensions/pi-subagents/test/integration/chain-clarify.test.ts +51 -2
- package/dist/extensions/pi-subagents/test/integration/chain-execution.test.ts +26 -1
- package/dist/extensions/pi-subagents/test/integration/detect-error.test.ts +48 -0
- package/dist/extensions/pi-subagents/test/integration/fork-context-execution.test.ts +3 -3
- package/dist/extensions/pi-subagents/test/integration/intercom-result-delivery.test.ts +213 -13
- package/dist/extensions/pi-subagents/test/integration/parallel-execution.test.ts +34 -0
- package/dist/extensions/pi-subagents/test/integration/render-fork-badge.test.ts +44 -0
- package/dist/extensions/pi-subagents/test/integration/result-watcher.test.ts +118 -12
- package/dist/extensions/pi-subagents/test/integration/single-execution.test.ts +850 -7
- package/dist/extensions/pi-subagents/test/integration/slash-commands.test.ts +112 -23
- package/dist/extensions/pi-subagents/test/support/mock-pi-script.mjs +17 -5
- package/dist/extensions/pi-subagents/test/support/mock-pi.ts +2 -0
- package/dist/extensions/pi-subagents/test/support/register-loader.mjs +3 -3
- package/dist/extensions/pi-subagents/test/unit/acceptance.test.ts +3 -1
- package/dist/extensions/pi-subagents/test/unit/agent-frontmatter.test.ts +128 -0
- package/dist/extensions/pi-subagents/test/unit/agent-overrides.test.ts +47 -2
- package/dist/extensions/pi-subagents/test/unit/async-recovery-descriptor.test.ts +60 -0
- package/dist/extensions/pi-subagents/test/unit/async-resume.test.ts +8 -0
- package/dist/extensions/pi-subagents/test/unit/capability-ceiling-agent-allowlist.test.ts +102 -0
- package/dist/extensions/pi-subagents/test/unit/capability-ceiling.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/chain-append.test.ts +6 -0
- package/dist/extensions/pi-subagents/test/unit/chain-validation.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/completion-guard.test.ts +48 -48
- package/dist/extensions/pi-subagents/test/unit/config-dir-runtime.test.ts +23 -0
- package/dist/extensions/pi-subagents/test/unit/control-channel.test.ts +27 -0
- package/dist/extensions/pi-subagents/test/unit/delegation-api.test.ts +4 -2
- package/dist/extensions/pi-subagents/test/unit/dynamic-fanout.test.ts +1 -0
- package/dist/extensions/pi-subagents/test/unit/fleet-status.test.ts +176 -8
- package/dist/extensions/pi-subagents/test/unit/fleet.test.ts +164 -5
- package/dist/extensions/pi-subagents/test/unit/foreground-control.test.ts +41 -1
- package/dist/extensions/pi-subagents/test/unit/index-child-registration.test.ts +11 -11
- package/dist/extensions/pi-subagents/test/unit/intercom-bridge.test.ts +18 -2
- package/dist/extensions/pi-subagents/test/unit/nested-control.test.ts +20 -1
- package/dist/extensions/pi-subagents/test/unit/nested-events.test.ts +30 -2
- package/dist/extensions/pi-subagents/test/unit/notify.test.ts +10 -2
- package/dist/extensions/pi-subagents/test/unit/parallel-utils.test.ts +22 -0
- package/dist/extensions/pi-subagents/test/unit/pi-args.test.ts +103 -0
- package/dist/extensions/pi-subagents/test/unit/pi-coding-agent-dir.test.ts +18 -0
- package/dist/extensions/pi-subagents/test/unit/preflight.test.ts +46 -0
- package/dist/extensions/pi-subagents/test/unit/render-helpers.test.ts +13 -1
- package/dist/extensions/pi-subagents/test/unit/result-intercom.test.ts +29 -4
- package/dist/extensions/pi-subagents/test/unit/rpc.test.ts +223 -4
- package/dist/extensions/pi-subagents/test/unit/run-status.test.ts +30 -0
- package/dist/extensions/pi-subagents/test/unit/runtime-acknowledged-extensions.test.ts +52 -0
- package/dist/extensions/pi-subagents/test/unit/schemas.test.ts +34 -2
- package/dist/extensions/pi-subagents/test/unit/skills-fallback.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/steering-action.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/streamed-progress-bounds.test.ts +78 -0
- package/dist/extensions/pi-subagents/test/unit/subagent-prompt-runtime.test.ts +41 -0
- package/dist/extensions/pi-subagents/test/unit/subagent-startup-retry.test.ts +77 -0
- package/dist/extensions/pi-subagents/test/unit/task-intent.test.ts +3 -0
- package/dist/extensions/pi-subagents/test/unit/tool-description.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/total-cost.test.ts +1 -0
- package/dist/extensions/pi-subagents/test/unit/watchdog-runtime.test.ts +210 -1
- package/dist/extensions/pi-subagents/test/unit/watchdog-scope.test.ts +33 -0
- package/dist/extensions/pi-subagents/test/unit/watchdog-settings.test.ts +32 -0
- package/dist/extensions/pi-subagents/test/unit/writer-budget-guidance.test.ts +1 -1
- package/dist/extensions/question/index.ts +14 -1
- package/dist/extensions/question/tests/wizard.test.ts +11 -8
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +43 -0
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/components/custom-message.d.ts +3 -1
- package/dist/modes/interactive/components/custom-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/custom-message.js +10 -2
- package/dist/modes/interactive/components/custom-message.js.map +1 -1
- package/dist/modes/interactive/components/extension-editor.d.ts +1 -2
- package/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
- package/dist/modes/interactive/components/extension-editor.js +16 -46
- package/dist/modes/interactive/components/extension-editor.js.map +1 -1
- package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/model-selector.js +4 -1
- package/dist/modes/interactive/components/model-selector.js.map +1 -1
- package/dist/modes/interactive/components/scoped-models-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/scoped-models-selector.js +22 -13
- package/dist/modes/interactive/components/scoped-models-selector.js.map +1 -1
- package/dist/modes/interactive/external-editor.d.ts +12 -0
- package/dist/modes/interactive/external-editor.d.ts.map +1 -0
- package/dist/modes/interactive/external-editor.js +37 -0
- package/dist/modes/interactive/external-editor.js.map +1 -0
- package/dist/modes/interactive/interactive-mode.d.ts +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +72 -64
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.js +14 -0
- package/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/dist/skills/batch-grill-me/SKILL.md +3 -1
- package/dist/utils/clipboard.d.ts.map +1 -1
- package/dist/utils/clipboard.js +19 -8
- package/dist/utils/clipboard.js.map +1 -1
- package/dist/utils/version-check.d.ts.map +1 -1
- package/dist/utils/version-check.js +1 -1
- package/dist/utils/version-check.js.map +1 -1
- package/docs/compaction.md +1 -1
- package/docs/custom-provider.md +14 -5
- package/docs/environment-variables.md +86 -0
- package/docs/extensions.md +15 -5
- package/docs/index.md +1 -0
- package/docs/models.md +9 -2
- package/docs/providers.md +21 -2
- package/docs/rpc.md +23 -6
- package/docs/session-format.md +2 -0
- package/docs/settings.md +1 -1
- package/docs/usage.md +0 -13
- package/examples/extensions/custom-compaction.ts +5 -2
- package/examples/extensions/custom-provider-anthropic/index.ts +9 -3
- package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/gondolin/package-lock.json +2 -2
- package/examples/extensions/gondolin/package.json +1 -1
- package/examples/extensions/handoff.ts +11 -3
- package/examples/extensions/message-renderer.ts +3 -3
- package/examples/extensions/sandbox/package-lock.json +2 -2
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/summarize.ts +5 -2
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/examples/sdk/12-full-control.ts +3 -1
- package/package.json +15 -6
- package/dist/extensions/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +0 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import assert from "node:assert/strict";
|
|
2
2
|
import { describe, it } from "node:test";
|
|
3
|
-
import { Editor, visibleWidth } from "@earendil-works/pi-tui";
|
|
3
|
+
import { Editor, type EditorComponent, visibleWidth } from "@earendil-works/pi-tui";
|
|
4
4
|
import type { ExtensionContext } from "@selesai/code";
|
|
5
5
|
import type { SubagentState } from "../../src/shared/types.ts";
|
|
6
6
|
import { collectFleetSnapshot } from "../../src/tui/fleet.ts";
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
collectFleetStatusEntries,
|
|
11
11
|
formatFleetElapsed,
|
|
12
12
|
formatFleetTokens,
|
|
13
|
+
resolveFleetViewPlacement,
|
|
13
14
|
} from "../../src/tui/fleet-status.ts";
|
|
14
15
|
|
|
15
16
|
function stateForTest(): SubagentState {
|
|
@@ -45,6 +46,13 @@ describe("below-editor subagent FleetView", () => {
|
|
|
45
46
|
assert.equal(formatFleetTokens(1_250_000), "↓ 1.3M tokens");
|
|
46
47
|
});
|
|
47
48
|
|
|
49
|
+
it("resolves configured FleetView placement with a below-editor fallback", () => {
|
|
50
|
+
assert.equal(resolveFleetViewPlacement(undefined), "belowEditor");
|
|
51
|
+
assert.equal(resolveFleetViewPlacement("belowEditor"), "belowEditor");
|
|
52
|
+
assert.equal(resolveFleetViewPlacement("aboveEditor"), "aboveEditor");
|
|
53
|
+
assert.equal(resolveFleetViewPlacement("side"), "belowEditor");
|
|
54
|
+
});
|
|
55
|
+
|
|
48
56
|
it("renders main plus active children below the editor and bounds every line", () => {
|
|
49
57
|
const state = stateForTest();
|
|
50
58
|
const now = Date.now();
|
|
@@ -56,6 +64,7 @@ describe("below-editor subagent FleetView", () => {
|
|
|
56
64
|
updatedAt: now,
|
|
57
65
|
currentAgent: `worker-${index}`,
|
|
58
66
|
description: index === 0 ? "Inspect\nmodule 0" : `Inspect module ${index}`,
|
|
67
|
+
...(index === 0 ? { model: "anthropic/fable-5", thinking: "low" } : {}),
|
|
59
68
|
tokens: index === 0 ? 13_100 : index,
|
|
60
69
|
});
|
|
61
70
|
}
|
|
@@ -85,7 +94,7 @@ describe("below-editor subagent FleetView", () => {
|
|
|
85
94
|
const component = widgetFactory!({ requestRender() {} }, theme);
|
|
86
95
|
const lines = component.render(80);
|
|
87
96
|
assert.ok(lines.some((line) => line.includes("⏺ main")));
|
|
88
|
-
assert.ok(lines.some((line) => line.includes("worker-0") && line.includes("Inspect module 0")));
|
|
97
|
+
assert.ok(lines.some((line) => line.includes("worker-0 (fable-5 · thinking low)") && line.includes("Inspect module 0")));
|
|
89
98
|
assert.ok(lines.some((line) => line.includes("11s · ↓ 13.1k tokens")));
|
|
90
99
|
assert.ok(lines.some((line) => line.includes("↓ 2 more")));
|
|
91
100
|
for (const line of lines) assert.ok(visibleWidth(line) <= 80, `line exceeded width: ${line}`);
|
|
@@ -94,6 +103,143 @@ describe("below-editor subagent FleetView", () => {
|
|
|
94
103
|
}
|
|
95
104
|
});
|
|
96
105
|
|
|
106
|
+
it("registers above the editor when configured", () => {
|
|
107
|
+
const state = stateForTest();
|
|
108
|
+
state.foregroundControls.set("run-worker", {
|
|
109
|
+
runId: "run-worker",
|
|
110
|
+
mode: "single",
|
|
111
|
+
startedAt: 10,
|
|
112
|
+
updatedAt: 20,
|
|
113
|
+
currentAgent: "worker",
|
|
114
|
+
});
|
|
115
|
+
let placement: string | undefined;
|
|
116
|
+
const ctx = {
|
|
117
|
+
hasUI: true,
|
|
118
|
+
ui: {
|
|
119
|
+
setWidget(_key: string, content: unknown, options?: { placement?: string }) {
|
|
120
|
+
if (content) placement = options?.placement;
|
|
121
|
+
},
|
|
122
|
+
onTerminalInput() { return () => {}; },
|
|
123
|
+
getEditorText() { return ""; },
|
|
124
|
+
notify() {},
|
|
125
|
+
theme,
|
|
126
|
+
},
|
|
127
|
+
} as unknown as ExtensionContext;
|
|
128
|
+
const fleet = new SubagentFleetStatus(state, () => {}, { refreshMs: 60_000, placement: "aboveEditor" });
|
|
129
|
+
try {
|
|
130
|
+
fleet.setContext(ctx);
|
|
131
|
+
assert.equal(placement, "aboveEditor");
|
|
132
|
+
} finally {
|
|
133
|
+
fleet.dispose();
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it("stops refreshing when the captured extension context becomes stale", () => {
|
|
138
|
+
const state = stateForTest();
|
|
139
|
+
state.foregroundControls.set("run-worker", {
|
|
140
|
+
runId: "run-worker",
|
|
141
|
+
mode: "single",
|
|
142
|
+
startedAt: 10,
|
|
143
|
+
updatedAt: 20,
|
|
144
|
+
currentAgent: "worker",
|
|
145
|
+
});
|
|
146
|
+
let stale = false;
|
|
147
|
+
let contextReads = 0;
|
|
148
|
+
let inputUnsubscribes = 0;
|
|
149
|
+
let widgetRemovals = 0;
|
|
150
|
+
const ctx = {
|
|
151
|
+
get hasUI() {
|
|
152
|
+
contextReads++;
|
|
153
|
+
if (stale) {
|
|
154
|
+
throw new Error("This extension ctx is stale after session replacement or reload.");
|
|
155
|
+
}
|
|
156
|
+
return true;
|
|
157
|
+
},
|
|
158
|
+
ui: {
|
|
159
|
+
setWidget(_key: string, content: unknown) {
|
|
160
|
+
if (content === undefined) widgetRemovals++;
|
|
161
|
+
},
|
|
162
|
+
onTerminalInput() { return () => { inputUnsubscribes++; }; },
|
|
163
|
+
getEditorText() { return ""; },
|
|
164
|
+
notify() {},
|
|
165
|
+
theme,
|
|
166
|
+
},
|
|
167
|
+
} as unknown as ExtensionContext;
|
|
168
|
+
const fleet = new SubagentFleetStatus(state, () => {}, { refreshMs: 60_000 });
|
|
169
|
+
try {
|
|
170
|
+
fleet.setContext(ctx);
|
|
171
|
+
stale = true;
|
|
172
|
+
assert.doesNotThrow(() => fleet.refresh());
|
|
173
|
+
assert.equal(inputUnsubscribes, 1);
|
|
174
|
+
assert.equal(widgetRemovals, 1);
|
|
175
|
+
assert.equal((fleet as unknown as { timer?: unknown }).timer, undefined);
|
|
176
|
+
const readsAfterStaleRefresh = contextReads;
|
|
177
|
+
fleet.refresh();
|
|
178
|
+
assert.equal(contextReads, readsAfterStaleRefresh, "later refreshes must not reuse the stale context");
|
|
179
|
+
} finally {
|
|
180
|
+
fleet.dispose();
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
it("does not swallow unrelated widget cleanup errors", () => {
|
|
185
|
+
const state = stateForTest();
|
|
186
|
+
state.foregroundControls.set("run-worker", {
|
|
187
|
+
runId: "run-worker",
|
|
188
|
+
mode: "single",
|
|
189
|
+
startedAt: 10,
|
|
190
|
+
updatedAt: 20,
|
|
191
|
+
currentAgent: "worker",
|
|
192
|
+
});
|
|
193
|
+
const ctx = {
|
|
194
|
+
hasUI: true,
|
|
195
|
+
ui: {
|
|
196
|
+
setWidget(_key: string, content: unknown) {
|
|
197
|
+
if (content === undefined) throw new Error("widget cleanup failed");
|
|
198
|
+
},
|
|
199
|
+
onTerminalInput() { return () => {}; },
|
|
200
|
+
getEditorText() { return ""; },
|
|
201
|
+
notify() {},
|
|
202
|
+
theme,
|
|
203
|
+
},
|
|
204
|
+
} as unknown as ExtensionContext;
|
|
205
|
+
const fleet = new SubagentFleetStatus(state, () => {}, { refreshMs: 60_000 });
|
|
206
|
+
fleet.setContext(ctx);
|
|
207
|
+
assert.throws(() => fleet.dispose(), /widget cleanup failed/);
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
it("preserves multiple unrelated UI cleanup errors", () => {
|
|
211
|
+
const state = stateForTest();
|
|
212
|
+
state.foregroundControls.set("run-worker", {
|
|
213
|
+
runId: "run-worker",
|
|
214
|
+
mode: "single",
|
|
215
|
+
startedAt: 10,
|
|
216
|
+
updatedAt: 20,
|
|
217
|
+
currentAgent: "worker",
|
|
218
|
+
});
|
|
219
|
+
const ctx = {
|
|
220
|
+
hasUI: true,
|
|
221
|
+
ui: {
|
|
222
|
+
setWidget(_key: string, content: unknown) {
|
|
223
|
+
if (content === undefined) throw new Error("widget cleanup failed");
|
|
224
|
+
},
|
|
225
|
+
onTerminalInput() {
|
|
226
|
+
return () => { throw new Error("input cleanup failed"); };
|
|
227
|
+
},
|
|
228
|
+
getEditorText() { return ""; },
|
|
229
|
+
notify() {},
|
|
230
|
+
theme,
|
|
231
|
+
},
|
|
232
|
+
} as unknown as ExtensionContext;
|
|
233
|
+
const fleet = new SubagentFleetStatus(state, () => {}, { refreshMs: 60_000 });
|
|
234
|
+
fleet.setContext(ctx);
|
|
235
|
+
assert.throws(
|
|
236
|
+
() => fleet.dispose(),
|
|
237
|
+
(error: unknown) => error instanceof AggregateError
|
|
238
|
+
&& error.errors.map(String).join("\n").includes("input cleanup failed")
|
|
239
|
+
&& error.errors.map(String).join("\n").includes("widget cleanup failed"),
|
|
240
|
+
);
|
|
241
|
+
});
|
|
242
|
+
|
|
97
243
|
it("keeps widget ownership through invalidation so an empty refresh removes it", () => {
|
|
98
244
|
const state = stateForTest();
|
|
99
245
|
state.foregroundControls.set("run-worker", {
|
|
@@ -268,8 +414,8 @@ describe("below-editor subagent FleetView", () => {
|
|
|
268
414
|
startedAt: 100,
|
|
269
415
|
updatedAt: 200,
|
|
270
416
|
steps: [
|
|
271
|
-
{ agent: "reviewer", index: 0, status: "running", startedAt: 120, tokens: { input: 4_000, output: 200, total: 4_200 } },
|
|
272
|
-
{ agent: "worker", index: 1, status: "
|
|
417
|
+
{ agent: "reviewer", index: 0, status: "running", description: "Review only authentication", startedAt: 120, model: "openai/gpt-5", thinking: "medium", tokens: { input: 4_000, output: 200, total: 4_200 } },
|
|
418
|
+
{ agent: "worker", index: 1, status: "running", description: "Implement only billing", startedAt: 121, tokens: { input: 100, output: 20, total: 120 } },
|
|
273
419
|
],
|
|
274
420
|
});
|
|
275
421
|
const fleet = new SubagentFleetStatus(state, () => {}, { refreshMs: 60_000 });
|
|
@@ -288,9 +434,10 @@ describe("below-editor subagent FleetView", () => {
|
|
|
288
434
|
try {
|
|
289
435
|
fleet.setContext(ctx);
|
|
290
436
|
const lines = widgetFactory!({ requestRender() {} }, theme).render(100);
|
|
291
|
-
assert.ok(lines.some((line) => line.includes("reviewer") && line.includes("Review
|
|
437
|
+
assert.ok(lines.some((line) => line.includes("reviewer (gpt-5 · thinking medium)") && line.includes("Review only authentication")));
|
|
438
|
+
assert.ok(lines.some((line) => line.includes("worker") && line.includes("Implement only billing")));
|
|
439
|
+
assert.ok(lines.every((line) => !line.includes("Review the authentication changes")), "per-child descriptions should replace the run-level fallback when present");
|
|
292
440
|
assert.ok(lines.some((line) => line.includes("↓ 4.2k tokens")));
|
|
293
|
-
assert.ok(!lines.some((line) => line.includes("worker")), "completed async children should leave the status fleet");
|
|
294
441
|
} finally {
|
|
295
442
|
fleet.dispose();
|
|
296
443
|
}
|
|
@@ -331,11 +478,32 @@ describe("below-editor subagent FleetView", () => {
|
|
|
331
478
|
|
|
332
479
|
assert.equal(inputHandler!("\x1b[B"), undefined, "non-empty editor should retain Down");
|
|
333
480
|
editorText = "";
|
|
334
|
-
tui.focusedComponent = {
|
|
481
|
+
tui.focusedComponent = {
|
|
482
|
+
render() { return []; },
|
|
483
|
+
invalidate() {},
|
|
484
|
+
handleInput() {},
|
|
485
|
+
} as unknown as Editor;
|
|
335
486
|
assert.equal(inputHandler!("\x1b[B"), undefined, "non-editor focus should retain Down");
|
|
487
|
+
|
|
488
|
+
const crossModuleCustomEditor = {
|
|
489
|
+
render() { return []; },
|
|
490
|
+
invalidate() {},
|
|
491
|
+
handleInput() {},
|
|
492
|
+
getText() { return ""; },
|
|
493
|
+
setText() {},
|
|
494
|
+
} satisfies EditorComponent;
|
|
495
|
+
assert.equal(crossModuleCustomEditor instanceof Editor, false, "regression setup must cross the instanceof boundary");
|
|
496
|
+
tui.focusedComponent = crossModuleCustomEditor as unknown as Editor;
|
|
497
|
+
assert.equal(inputHandler!("j"), undefined, "inactive FleetView should retain printable navigation keys");
|
|
498
|
+
assert.equal(inputHandler!("k"), undefined, "inactive FleetView should retain printable navigation keys");
|
|
499
|
+
assert.deepEqual(inputHandler!("\x1b[B"), { consume: true }, "custom editors should activate FleetView across jiti boundaries");
|
|
500
|
+
assert.deepEqual(inputHandler!("j"), { consume: true }, "active FleetView should navigate down with j");
|
|
501
|
+
assert.ok(component.render(100).some((line) => line.includes("⏺ worker")));
|
|
502
|
+
assert.deepEqual(inputHandler!("k"), { consume: true }, "active FleetView should navigate up with k");
|
|
503
|
+
assert.ok(component.render(100).some((line) => line.includes("⏺ main")));
|
|
504
|
+
|
|
336
505
|
tui.focusedComponent = Object.create(Editor.prototype) as Editor;
|
|
337
506
|
assert.deepEqual(inputHandler!("\x1b[B"), { consume: true });
|
|
338
|
-
assert.deepEqual(inputHandler!("\x1b[B"), { consume: true });
|
|
339
507
|
assert.ok(component.render(100).some((line) => line.includes("⏺ worker")));
|
|
340
508
|
assert.deepEqual(inputHandler!("\r"), { consume: true });
|
|
341
509
|
await Promise.resolve();
|
|
@@ -368,7 +368,7 @@ describe("native subagent fleet", () => {
|
|
|
368
368
|
currentIndex: 1,
|
|
369
369
|
description: "Review the active task",
|
|
370
370
|
activeChildren: new Map([
|
|
371
|
-
[0, { index: 0, agent: "worker", description: "Implement the active task", startedAt: now - 900, updatedAt: now - 100, tokens: 120 }],
|
|
371
|
+
[0, { index: 0, agent: "worker", description: "Implement the active task", startedAt: now - 900, updatedAt: now - 100, tokens: 120, model: "provider/live-model", thinking: "high" }],
|
|
372
372
|
[1, { index: 1, agent: "reviewer", description: "Review the active task", startedAt: now - 800, updatedAt: now, tokens: 240 }],
|
|
373
373
|
]),
|
|
374
374
|
});
|
|
@@ -388,6 +388,7 @@ describe("native subagent fleet", () => {
|
|
|
388
388
|
assert.ok(lines.some((line) => line.includes("worker")));
|
|
389
389
|
assert.ok(lines.some((line) => line.includes("reviewer")));
|
|
390
390
|
assert.ok(lines.some((line) => line.includes("foreground · live")));
|
|
391
|
+
assert.ok(lines.some((line) => line.includes("live-model · thinking high")));
|
|
391
392
|
assert.ok(lines.some((line) => line.includes("Task") && line.includes("Implement the active task")));
|
|
392
393
|
assert.ok(lines.some((line) => line.includes("Conversation") && line.includes("assistant response")));
|
|
393
394
|
assert.ok(lines.some((line) => line.includes("Worker live result")));
|
|
@@ -442,7 +443,7 @@ describe("native subagent fleet", () => {
|
|
|
442
443
|
cwd: baseCwd,
|
|
443
444
|
sessionId: "session-current",
|
|
444
445
|
updatedAt: 200,
|
|
445
|
-
children: [{ agent: "reviewer", index: 0, status: "completed", transcriptPath: recentTranscript }],
|
|
446
|
+
children: [{ agent: "reviewer", index: 0, status: "completed", transcriptPath: recentTranscript, model: "provider/recent-model", thinking: "xhigh" }],
|
|
446
447
|
});
|
|
447
448
|
state.asyncJobs.set(asyncId, {
|
|
448
449
|
asyncId,
|
|
@@ -469,7 +470,11 @@ describe("native subagent fleet", () => {
|
|
|
469
470
|
{ initialKey, refreshMs: 60_000, markdownTheme },
|
|
470
471
|
);
|
|
471
472
|
try {
|
|
472
|
-
|
|
473
|
+
const lines = component.render(100);
|
|
474
|
+
assert.ok(lines.some((line) => line.includes(expected)), `missing ${expected}`);
|
|
475
|
+
if (initialKey.startsWith("foreground-recent:")) {
|
|
476
|
+
assert.ok(lines.some((line) => line.includes("recent-model · thinking xhigh")));
|
|
477
|
+
}
|
|
473
478
|
} finally {
|
|
474
479
|
component.dispose();
|
|
475
480
|
}
|
|
@@ -506,7 +511,7 @@ describe("native subagent fleet", () => {
|
|
|
506
511
|
assert.equal(state.fleetInspectorOpen, false);
|
|
507
512
|
});
|
|
508
513
|
|
|
509
|
-
it("
|
|
514
|
+
it("focuses the selected child and renders raw foreground model details", () => {
|
|
510
515
|
const state = stateForTest();
|
|
511
516
|
state.foregroundControls.set("run-worker", {
|
|
512
517
|
runId: "run-worker",
|
|
@@ -515,6 +520,8 @@ describe("native subagent fleet", () => {
|
|
|
515
520
|
updatedAt: 20,
|
|
516
521
|
currentAgent: "worker",
|
|
517
522
|
currentIndex: 0,
|
|
523
|
+
model: "provider/raw-model",
|
|
524
|
+
thinking: "medium",
|
|
518
525
|
});
|
|
519
526
|
state.foregroundControls.set("run-reviewer", {
|
|
520
527
|
runId: "run-reviewer",
|
|
@@ -524,6 +531,14 @@ describe("native subagent fleet", () => {
|
|
|
524
531
|
currentAgent: "reviewer",
|
|
525
532
|
currentIndex: 0,
|
|
526
533
|
});
|
|
534
|
+
state.foregroundRuns!.set("run-recent", {
|
|
535
|
+
runId: "run-recent",
|
|
536
|
+
mode: "single",
|
|
537
|
+
cwd: process.cwd(),
|
|
538
|
+
sessionId: "session-current",
|
|
539
|
+
updatedAt: 19,
|
|
540
|
+
children: [{ agent: "reviewer", index: 0, status: "completed", model: "provider/recent-raw-model", thinking: "xhigh" }],
|
|
541
|
+
});
|
|
527
542
|
const component = new SubagentFleetComponent(
|
|
528
543
|
{ terminal: { rows: 28, columns: 90 }, requestRender() {} } as never,
|
|
529
544
|
theme as never,
|
|
@@ -532,11 +547,148 @@ describe("native subagent fleet", () => {
|
|
|
532
547
|
{ initialKey: "foreground-active:run-worker:0", refreshMs: 60_000 },
|
|
533
548
|
);
|
|
534
549
|
try {
|
|
535
|
-
const
|
|
550
|
+
const lines = component.render(90);
|
|
551
|
+
const selectedLine = lines.find((line) => line.includes("›"));
|
|
536
552
|
assert.ok(selectedLine?.includes("run-work"), `unexpected selected row: ${selectedLine}`);
|
|
553
|
+
assert.ok(lines.some((line) => line.includes("Model: raw-model · thinking medium")));
|
|
537
554
|
} finally {
|
|
538
555
|
component.dispose();
|
|
539
556
|
}
|
|
557
|
+
|
|
558
|
+
const recentComponent = new SubagentFleetComponent(
|
|
559
|
+
{ terminal: { rows: 28, columns: 90 }, requestRender() {} } as never,
|
|
560
|
+
theme as never,
|
|
561
|
+
state,
|
|
562
|
+
() => {},
|
|
563
|
+
{ initialKey: "foreground-recent:run-recent:0", refreshMs: 60_000 },
|
|
564
|
+
);
|
|
565
|
+
try {
|
|
566
|
+
assert.ok(recentComponent.render(90).some((line) => line.includes("Model: recent-raw-model · thinking xhigh")));
|
|
567
|
+
} finally {
|
|
568
|
+
recentComponent.dispose();
|
|
569
|
+
}
|
|
570
|
+
});
|
|
571
|
+
|
|
572
|
+
it("steers the selected async child with an inline message", async () => {
|
|
573
|
+
const root = fs.mkdtempSync(path.join(os.tmpdir(), "pi-fleet-steer-"));
|
|
574
|
+
try {
|
|
575
|
+
const asyncDir = writeAsyncRun(root, { id: "async-steer", agents: ["worker", "reviewer"] });
|
|
576
|
+
const state = stateForTest();
|
|
577
|
+
const calls: Array<{ runId: string; asyncDir: string; index?: number; message: string }> = [];
|
|
578
|
+
const component = new SubagentFleetComponent(
|
|
579
|
+
{ terminal: { rows: 28, columns: 100 }, requestRender() {} } as never,
|
|
580
|
+
theme as never,
|
|
581
|
+
state,
|
|
582
|
+
() => {},
|
|
583
|
+
{
|
|
584
|
+
asyncDirRoot: root,
|
|
585
|
+
resultsDir: path.join(root, "results"),
|
|
586
|
+
initialKey: "async:async-steer:0",
|
|
587
|
+
refreshMs: 60_000,
|
|
588
|
+
actions: {
|
|
589
|
+
async steer(input) {
|
|
590
|
+
calls.push(input);
|
|
591
|
+
return { text: "Steering queued." };
|
|
592
|
+
},
|
|
593
|
+
stop() {
|
|
594
|
+
return { text: "unused" };
|
|
595
|
+
},
|
|
596
|
+
},
|
|
597
|
+
},
|
|
598
|
+
);
|
|
599
|
+
try {
|
|
600
|
+
component.handleInput("s");
|
|
601
|
+
assert.ok(component.render(100).some((line) => line.includes("Steer message:")));
|
|
602
|
+
for (const char of "please continue") component.handleInput(char);
|
|
603
|
+
component.handleInput("\r");
|
|
604
|
+
await new Promise((resolve) => setImmediate(resolve));
|
|
605
|
+
assert.deepEqual(calls, [{ runId: "async-steer", asyncDir, index: 0, message: "please continue" }]);
|
|
606
|
+
assert.ok(component.render(100).some((line) => line.includes("Steering queued.")));
|
|
607
|
+
} finally {
|
|
608
|
+
component.dispose();
|
|
609
|
+
}
|
|
610
|
+
} finally {
|
|
611
|
+
fs.rmSync(root, { recursive: true, force: true });
|
|
612
|
+
}
|
|
613
|
+
});
|
|
614
|
+
|
|
615
|
+
it("confirms stop for the selected async child before calling the action", async () => {
|
|
616
|
+
const root = fs.mkdtempSync(path.join(os.tmpdir(), "pi-fleet-stop-"));
|
|
617
|
+
try {
|
|
618
|
+
const asyncDir = writeAsyncRun(root, { id: "async-stop" });
|
|
619
|
+
const state = stateForTest();
|
|
620
|
+
const calls: Array<{ runId: string; asyncDir: string; index?: number }> = [];
|
|
621
|
+
const component = new SubagentFleetComponent(
|
|
622
|
+
{ terminal: { rows: 28, columns: 100 }, requestRender() {} } as never,
|
|
623
|
+
theme as never,
|
|
624
|
+
state,
|
|
625
|
+
() => {},
|
|
626
|
+
{
|
|
627
|
+
asyncDirRoot: root,
|
|
628
|
+
resultsDir: path.join(root, "results"),
|
|
629
|
+
refreshMs: 60_000,
|
|
630
|
+
actions: {
|
|
631
|
+
async steer() {
|
|
632
|
+
return { text: "unused" };
|
|
633
|
+
},
|
|
634
|
+
stop(input) {
|
|
635
|
+
calls.push(input);
|
|
636
|
+
return { text: "Stop requested." };
|
|
637
|
+
},
|
|
638
|
+
},
|
|
639
|
+
},
|
|
640
|
+
);
|
|
641
|
+
try {
|
|
642
|
+
component.handleInput("D");
|
|
643
|
+
assert.ok(component.render(100).some((line) => line.includes("Confirm stop for async run async-stop")));
|
|
644
|
+
component.handleInput("n");
|
|
645
|
+
assert.deepEqual(calls, []);
|
|
646
|
+
component.handleInput("D");
|
|
647
|
+
component.handleInput("y");
|
|
648
|
+
await new Promise((resolve) => setImmediate(resolve));
|
|
649
|
+
assert.deepEqual(calls, [{ runId: "async-stop", asyncDir, index: 0 }]);
|
|
650
|
+
assert.ok(component.render(100).some((line) => line.includes("Stop requested.")));
|
|
651
|
+
} finally {
|
|
652
|
+
component.dispose();
|
|
653
|
+
}
|
|
654
|
+
} finally {
|
|
655
|
+
fs.rmSync(root, { recursive: true, force: true });
|
|
656
|
+
}
|
|
657
|
+
});
|
|
658
|
+
|
|
659
|
+
it("explains unavailable controls for completed async children", () => {
|
|
660
|
+
const root = fs.mkdtempSync(path.join(os.tmpdir(), "pi-fleet-unavailable-"));
|
|
661
|
+
try {
|
|
662
|
+
writeAsyncRun(root, { id: "async-complete", state: "complete" });
|
|
663
|
+
const state = stateForTest();
|
|
664
|
+
const component = new SubagentFleetComponent(
|
|
665
|
+
{ terminal: { rows: 28, columns: 100 }, requestRender() {} } as never,
|
|
666
|
+
theme as never,
|
|
667
|
+
state,
|
|
668
|
+
() => {},
|
|
669
|
+
{
|
|
670
|
+
asyncDirRoot: root,
|
|
671
|
+
resultsDir: path.join(root, "results"),
|
|
672
|
+
refreshMs: 60_000,
|
|
673
|
+
actions: {
|
|
674
|
+
async steer() {
|
|
675
|
+
return { text: "unused" };
|
|
676
|
+
},
|
|
677
|
+
stop() {
|
|
678
|
+
return { text: "unused" };
|
|
679
|
+
},
|
|
680
|
+
},
|
|
681
|
+
},
|
|
682
|
+
);
|
|
683
|
+
try {
|
|
684
|
+
component.handleInput("s");
|
|
685
|
+
assert.ok(component.render(100).some((line) => line.includes("Selected child is complete")));
|
|
686
|
+
} finally {
|
|
687
|
+
component.dispose();
|
|
688
|
+
}
|
|
689
|
+
} finally {
|
|
690
|
+
fs.rmSync(root, { recursive: true, force: true });
|
|
691
|
+
}
|
|
540
692
|
});
|
|
541
693
|
|
|
542
694
|
it("refreshes the roster while the inspector remains open", async () => {
|
|
@@ -552,6 +704,12 @@ describe("native subagent fleet", () => {
|
|
|
552
704
|
() => {},
|
|
553
705
|
{ asyncDirRoot: root, resultsDir: path.join(root, "results"), refreshMs: 10 },
|
|
554
706
|
);
|
|
707
|
+
let invalidations = 0;
|
|
708
|
+
const originalInvalidate = component.invalidate.bind(component);
|
|
709
|
+
component.invalidate = () => {
|
|
710
|
+
invalidations++;
|
|
711
|
+
originalInvalidate();
|
|
712
|
+
};
|
|
555
713
|
try {
|
|
556
714
|
assert.ok(component.render(90).some((line) => line.includes("No tracked children")));
|
|
557
715
|
const initialOutput = Array.from({ length: 40 }, (_, index) => `output line ${index}`).join("\n");
|
|
@@ -565,6 +723,7 @@ describe("native subagent fleet", () => {
|
|
|
565
723
|
lines = component.render(90);
|
|
566
724
|
assert.ok(lines.some((line) => line.includes("LATEST LIVE OUTPUT")), "live transcript should keep following new output");
|
|
567
725
|
assert.ok(renderRequests > 0);
|
|
726
|
+
assert.ok(invalidations > 0, "live refresh must invalidate cached TUI frames before rendering");
|
|
568
727
|
} finally {
|
|
569
728
|
component.dispose();
|
|
570
729
|
}
|
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
import assert from "node:assert/strict";
|
|
2
2
|
import { describe, it } from "node:test";
|
|
3
3
|
import type { AgentProgress, ForegroundRunControl } from "../../src/shared/types.ts";
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
beginForegroundChild,
|
|
6
|
+
finishForegroundChild,
|
|
7
|
+
foregroundSchedulingSettled,
|
|
8
|
+
retainForegroundSchedulingOwner,
|
|
9
|
+
settleForegroundSchedulingOwner,
|
|
10
|
+
updateForegroundChild,
|
|
11
|
+
} from "../../src/runs/foreground/foreground-control.ts";
|
|
5
12
|
|
|
6
13
|
function progress(index: number, agent: string, tokens: number): AgentProgress {
|
|
7
14
|
return {
|
|
@@ -13,6 +20,10 @@ function progress(index: number, agent: string, tokens: number): AgentProgress {
|
|
|
13
20
|
recentOutput: [],
|
|
14
21
|
toolCount: index + 1,
|
|
15
22
|
tokens,
|
|
23
|
+
model: "openai/gpt-5.6-terra:high",
|
|
24
|
+
thinking: "high",
|
|
25
|
+
inputTokens: tokens - 20,
|
|
26
|
+
outputTokens: 20,
|
|
16
27
|
durationMs: 10,
|
|
17
28
|
};
|
|
18
29
|
}
|
|
@@ -28,11 +39,13 @@ describe("foreground child control", () => {
|
|
|
28
39
|
};
|
|
29
40
|
let firstInterrupts = 0;
|
|
30
41
|
let secondInterrupts = 0;
|
|
42
|
+
let firstDetaches = 0;
|
|
31
43
|
beginForegroundChild(control, {
|
|
32
44
|
index: 0,
|
|
33
45
|
agent: "reviewer",
|
|
34
46
|
description: "Review correctness",
|
|
35
47
|
interrupt: () => { firstInterrupts++; return true; },
|
|
48
|
+
detach: () => { firstDetaches++; return true; },
|
|
36
49
|
});
|
|
37
50
|
beginForegroundChild(control, {
|
|
38
51
|
index: 1,
|
|
@@ -46,10 +59,16 @@ describe("foreground child control", () => {
|
|
|
46
59
|
updateForegroundChild(control, 0, progress(0, "reviewer", 120));
|
|
47
60
|
assert.equal(control.currentIndex, 0);
|
|
48
61
|
assert.equal(control.tokens, 120);
|
|
62
|
+
assert.equal(control.inputTokens, 100);
|
|
63
|
+
assert.equal(control.outputTokens, 20);
|
|
64
|
+
assert.equal(control.model, "openai/gpt-5.6-terra:high");
|
|
65
|
+
assert.equal(control.thinking, "high");
|
|
49
66
|
assert.equal(control.activeChildren?.get(1)?.tokens, undefined);
|
|
50
67
|
assert.equal(control.interrupt?.(), true);
|
|
51
68
|
assert.equal(firstInterrupts, 1);
|
|
52
69
|
assert.equal(secondInterrupts, 0);
|
|
70
|
+
assert.equal(control.detach?.(), true);
|
|
71
|
+
assert.equal(firstDetaches, 1);
|
|
53
72
|
|
|
54
73
|
updateForegroundChild(control, 1, progress(1, "reviewer", 240));
|
|
55
74
|
finishForegroundChild(control, 1);
|
|
@@ -60,6 +79,27 @@ describe("foreground child control", () => {
|
|
|
60
79
|
finishForegroundChild(control, 0);
|
|
61
80
|
assert.equal(control.activeChildren?.size, 0);
|
|
62
81
|
assert.equal(control.currentIndex, undefined);
|
|
82
|
+
assert.equal(control.model, undefined);
|
|
83
|
+
assert.equal(control.inputTokens, undefined);
|
|
84
|
+
assert.equal(control.outputTokens, undefined);
|
|
63
85
|
assert.equal(control.interrupt, undefined);
|
|
86
|
+
assert.equal(control.detach, undefined);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it("settles scheduling only after every owner releases", () => {
|
|
90
|
+
const control: ForegroundRunControl = {
|
|
91
|
+
runId: "owned-run",
|
|
92
|
+
mode: "parallel",
|
|
93
|
+
startedAt: 1,
|
|
94
|
+
updatedAt: 1,
|
|
95
|
+
schedulingOwners: 1,
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
retainForegroundSchedulingOwner(control);
|
|
99
|
+
settleForegroundSchedulingOwner(control);
|
|
100
|
+
assert.equal(foregroundSchedulingSettled(control), false);
|
|
101
|
+
settleForegroundSchedulingOwner(control);
|
|
102
|
+
assert.equal(foregroundSchedulingSettled(control), true);
|
|
103
|
+
assert.equal(control.schedulingOwners, 0);
|
|
64
104
|
});
|
|
65
105
|
});
|
|
@@ -60,7 +60,7 @@ describe("subagent extension child mode", () => {
|
|
|
60
60
|
execFileSync(
|
|
61
61
|
process.execPath,
|
|
62
62
|
[
|
|
63
|
-
"--experimental-
|
|
63
|
+
"--experimental-strip-types",
|
|
64
64
|
"--import",
|
|
65
65
|
"./test/support/register-loader.mjs",
|
|
66
66
|
"--input-type=module",
|
|
@@ -104,7 +104,7 @@ describe("subagent extension child mode", () => {
|
|
|
104
104
|
execFileSync(
|
|
105
105
|
process.execPath,
|
|
106
106
|
[
|
|
107
|
-
"--experimental-
|
|
107
|
+
"--experimental-strip-types",
|
|
108
108
|
"--import",
|
|
109
109
|
"./test/support/register-loader.mjs",
|
|
110
110
|
"--input-type=module",
|
|
@@ -155,7 +155,7 @@ describe("subagent extension child mode", () => {
|
|
|
155
155
|
execFileSync(
|
|
156
156
|
process.execPath,
|
|
157
157
|
[
|
|
158
|
-
"--experimental-
|
|
158
|
+
"--experimental-strip-types",
|
|
159
159
|
"--import",
|
|
160
160
|
"./test/support/register-loader.mjs",
|
|
161
161
|
"--input-type=module",
|
|
@@ -207,7 +207,7 @@ describe("subagent extension child mode", () => {
|
|
|
207
207
|
const output = execFileSync(
|
|
208
208
|
process.execPath,
|
|
209
209
|
[
|
|
210
|
-
"--experimental-
|
|
210
|
+
"--experimental-strip-types",
|
|
211
211
|
"--import",
|
|
212
212
|
"./test/support/register-loader.mjs",
|
|
213
213
|
"--input-type=module",
|
|
@@ -328,7 +328,7 @@ describe("subagent extension child mode", () => {
|
|
|
328
328
|
env.SELESAI_CODING_AGENT_DIR = agentDir;
|
|
329
329
|
execFileSync(
|
|
330
330
|
process.execPath,
|
|
331
|
-
["--experimental-
|
|
331
|
+
["--experimental-strip-types", "--import", "./test/support/register-loader.mjs", "--input-type=module", "--eval", script],
|
|
332
332
|
{ cwd: projectRoot, env, stdio: "pipe" },
|
|
333
333
|
);
|
|
334
334
|
} finally {
|
|
@@ -422,7 +422,7 @@ describe("subagent extension child mode", () => {
|
|
|
422
422
|
env.SELESAI_CODING_AGENT_DIR = agentDir;
|
|
423
423
|
execFileSync(
|
|
424
424
|
process.execPath,
|
|
425
|
-
["--experimental-
|
|
425
|
+
["--experimental-strip-types", "--import", "./test/support/register-loader.mjs", "--input-type=module", "--eval", script],
|
|
426
426
|
{ cwd: projectRoot, env, stdio: "pipe" },
|
|
427
427
|
);
|
|
428
428
|
} finally {
|
|
@@ -458,7 +458,7 @@ describe("subagent extension child mode", () => {
|
|
|
458
458
|
execFileSync(
|
|
459
459
|
process.execPath,
|
|
460
460
|
[
|
|
461
|
-
"--experimental-
|
|
461
|
+
"--experimental-strip-types",
|
|
462
462
|
"--import",
|
|
463
463
|
"./test/support/register-loader.mjs",
|
|
464
464
|
"--input-type=module",
|
|
@@ -493,7 +493,7 @@ describe("subagent extension child mode", () => {
|
|
|
493
493
|
execFileSync(
|
|
494
494
|
process.execPath,
|
|
495
495
|
[
|
|
496
|
-
"--experimental-
|
|
496
|
+
"--experimental-strip-types",
|
|
497
497
|
"--import",
|
|
498
498
|
"./test/support/register-loader.mjs",
|
|
499
499
|
"--input-type=module",
|
|
@@ -529,7 +529,7 @@ describe("subagent extension child mode", () => {
|
|
|
529
529
|
execFileSync(
|
|
530
530
|
process.execPath,
|
|
531
531
|
[
|
|
532
|
-
"--experimental-
|
|
532
|
+
"--experimental-strip-types",
|
|
533
533
|
"--import",
|
|
534
534
|
"./test/support/register-loader.mjs",
|
|
535
535
|
"--input-type=module",
|
|
@@ -574,7 +574,7 @@ describe("subagent extension child mode", () => {
|
|
|
574
574
|
execFileSync(
|
|
575
575
|
process.execPath,
|
|
576
576
|
[
|
|
577
|
-
"--experimental-
|
|
577
|
+
"--experimental-strip-types",
|
|
578
578
|
"--import",
|
|
579
579
|
"./test/support/register-loader.mjs",
|
|
580
580
|
"--input-type=module",
|
|
@@ -620,7 +620,7 @@ describe("subagent extension child mode", () => {
|
|
|
620
620
|
execFileSync(
|
|
621
621
|
process.execPath,
|
|
622
622
|
[
|
|
623
|
-
"--experimental-
|
|
623
|
+
"--experimental-strip-types",
|
|
624
624
|
"--import",
|
|
625
625
|
"./test/support/register-loader.mjs",
|
|
626
626
|
"--input-type=module",
|