@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,13 +1,16 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
2
|
import * as path from "node:path";
|
|
3
|
+
import type { AgentToolResult } from "@earendil-works/pi-agent-core";
|
|
3
4
|
import { getMarkdownTheme, type ExtensionContext } from "@selesai/code";
|
|
4
5
|
import { Key, matchesKey, truncateToWidth, visibleWidth, wrapTextWithAnsi, type Component, type MarkdownTheme } from "@earendil-works/pi-tui";
|
|
5
6
|
import { getArtifactPaths, getArtifactsDir } from "../shared/artifacts.ts";
|
|
6
|
-
import { formatDuration, formatTokens, shortenPath } from "../shared/formatters.ts";
|
|
7
|
-
import { RESULTS_DIR, type AsyncJobState, type ForegroundChildControl, type ForegroundResumeChild, type ForegroundResumeRun, type ForegroundRunControl, type SubagentState } from "../shared/types.ts";
|
|
7
|
+
import { formatDuration, formatModelThinking, formatTokens, shortenPath } from "../shared/formatters.ts";
|
|
8
|
+
import { RESULTS_DIR, type AsyncJobState, type Details, type ForegroundChildControl, type ForegroundResumeChild, type ForegroundResumeRun, type ForegroundRunControl, type SubagentState } from "../shared/types.ts";
|
|
8
9
|
import { readStatus } from "../shared/utils.ts";
|
|
9
10
|
import { formatAsyncRunTranscript } from "../runs/background/fleet-view.ts";
|
|
10
11
|
import { listAsyncRuns, type AsyncRunSummary } from "../runs/background/async-status.ts";
|
|
12
|
+
import { steerAsyncRun } from "../runs/foreground/async-steering-action.ts";
|
|
13
|
+
import { stopAsyncRun } from "../runs/foreground/async-stop-action.ts";
|
|
11
14
|
import { contextModeBadge, contextModeLabel } from "../runs/shared/context-mode.ts";
|
|
12
15
|
import { FLEET_STATUS_WIDGET_KEY } from "./fleet-status.ts";
|
|
13
16
|
import { readFleetTranscript, renderFleetTranscript, type FleetTranscript } from "./fleet-transcript.ts";
|
|
@@ -34,12 +37,23 @@ export interface FleetSnapshot {
|
|
|
34
37
|
error?: string;
|
|
35
38
|
}
|
|
36
39
|
|
|
40
|
+
export interface FleetActionResult {
|
|
41
|
+
text: string;
|
|
42
|
+
isError?: boolean;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface FleetActionHandlers {
|
|
46
|
+
steer(input: { runId: string; asyncDir: string; index?: number; message: string }): Promise<FleetActionResult>;
|
|
47
|
+
stop(input: { runId: string; asyncDir: string; index?: number }): Promise<FleetActionResult> | FleetActionResult;
|
|
48
|
+
}
|
|
49
|
+
|
|
37
50
|
export interface FleetViewOptions {
|
|
38
51
|
asyncDirRoot?: string;
|
|
39
52
|
resultsDir?: string;
|
|
40
53
|
refreshMs?: number;
|
|
41
54
|
initialKey?: string;
|
|
42
55
|
markdownTheme?: MarkdownTheme;
|
|
56
|
+
actions?: FleetActionHandlers;
|
|
43
57
|
}
|
|
44
58
|
|
|
45
59
|
function belongsToCurrentSession(sessionId: string | undefined, currentSessionId: string | null): boolean {
|
|
@@ -209,12 +223,14 @@ function statusGlyph(item: FleetItem, theme: Theme): string {
|
|
|
209
223
|
function foregroundActiveDetail(item: Extract<FleetItem, { kind: "foreground-active" }>): string[] {
|
|
210
224
|
const { control } = item;
|
|
211
225
|
const live = item.activeChild ?? control;
|
|
226
|
+
const modelThinking = formatModelThinking(live.model, live.thinking);
|
|
212
227
|
const lines = [
|
|
213
228
|
`Run: ${item.runId}`,
|
|
214
229
|
"Source: foreground",
|
|
215
230
|
`State: running`,
|
|
216
231
|
`Mode: ${control.mode}`,
|
|
217
232
|
item.index !== undefined ? `Child: ${item.index} (${item.agent})` : `Agent: ${item.agent}`,
|
|
233
|
+
modelThinking ? `Model: ${modelThinking}` : undefined,
|
|
218
234
|
`Started: ${new Date(live.startedAt).toISOString()}`,
|
|
219
235
|
live.currentTool ? `Current tool: ${live.currentTool}${live.currentPath ? ` · ${shortenPath(live.currentPath)}` : ""}` : undefined,
|
|
220
236
|
live.turnCount !== undefined ? `Turns: ${live.turnCount}` : undefined,
|
|
@@ -230,12 +246,14 @@ function foregroundActiveDetail(item: Extract<FleetItem, { kind: "foreground-act
|
|
|
230
246
|
function foregroundRecentDetail(item: Extract<FleetItem, { kind: "foreground-recent" }>): string[] {
|
|
231
247
|
const { child, run } = item;
|
|
232
248
|
const outputPath = child.artifactPaths?.outputPath ?? child.savedOutputPath;
|
|
249
|
+
const modelThinking = formatModelThinking(child.model, child.thinking);
|
|
233
250
|
const lines = [
|
|
234
251
|
`Run: ${item.runId}`,
|
|
235
252
|
"Source: foreground",
|
|
236
253
|
`State: ${child.status}`,
|
|
237
254
|
`Mode: ${run.mode}`,
|
|
238
255
|
`Child: ${child.index} (${child.agent})${contextModeLabel(child.context) ? ` ${contextModeLabel(child.context)}` : ""}`,
|
|
256
|
+
modelThinking ? `Model: ${modelThinking}` : undefined,
|
|
239
257
|
`Updated: ${new Date(child.updatedAt ?? run.updatedAt).toISOString()}`,
|
|
240
258
|
outputPath ? `Output: ${outputPath}` : undefined,
|
|
241
259
|
child.sessionFile ? `Session: ${child.sessionFile}` : undefined,
|
|
@@ -282,6 +300,16 @@ function detailLines(item: FleetItem | undefined, error: string | undefined): st
|
|
|
282
300
|
return lines;
|
|
283
301
|
}
|
|
284
302
|
|
|
303
|
+
function isActionableAsyncState(state: string): boolean {
|
|
304
|
+
return state === "running" || state === "queued" || state === "pending";
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
function firstToolResultText(result: AgentToolResult<Details> | null, fallback: string): FleetActionResult {
|
|
308
|
+
if (!result) return { text: fallback, isError: true };
|
|
309
|
+
const text = result.content.find((item) => item.type === "text")?.text ?? fallback;
|
|
310
|
+
return { text, ...(result.isError ? { isError: true } : {}) };
|
|
311
|
+
}
|
|
312
|
+
|
|
285
313
|
function uniquePaths(values: Array<string | undefined>): string[] {
|
|
286
314
|
return [...new Set(values.filter((value): value is string => Boolean(value)).map((value) => path.resolve(value)))];
|
|
287
315
|
}
|
|
@@ -353,10 +381,12 @@ function itemStats(item: FleetItem): string[] {
|
|
|
353
381
|
let durationMs: number | undefined;
|
|
354
382
|
if (item.kind === "foreground-active") {
|
|
355
383
|
const live = item.activeChild ?? item.control;
|
|
384
|
+
model = formatModelThinking(live.model, live.thinking) || undefined;
|
|
356
385
|
tokens = live.tokens;
|
|
357
386
|
tools = live.toolCount;
|
|
358
387
|
durationMs = Math.max(0, Date.now() - live.startedAt);
|
|
359
388
|
} else if (item.kind === "foreground-recent") {
|
|
389
|
+
model = formatModelThinking(item.child.model, item.child.thinking) || undefined;
|
|
360
390
|
tokens = item.child.tokens;
|
|
361
391
|
tools = item.child.toolCount;
|
|
362
392
|
} else {
|
|
@@ -436,6 +466,10 @@ export class SubagentFleetComponent implements Component {
|
|
|
436
466
|
private detailViewportHeight = 8;
|
|
437
467
|
private bodyHeight = 8;
|
|
438
468
|
private expandedTools = false;
|
|
469
|
+
private actionNotice: FleetActionResult | undefined;
|
|
470
|
+
private steerDraft: string | undefined;
|
|
471
|
+
private stopConfirming = false;
|
|
472
|
+
private actionBusy = false;
|
|
439
473
|
private transcriptCache: FleetTranscriptCache | undefined;
|
|
440
474
|
private disposed = false;
|
|
441
475
|
private readonly timer: ReturnType<typeof setInterval>;
|
|
@@ -463,7 +497,7 @@ export class SubagentFleetComponent implements Component {
|
|
|
463
497
|
this.refresh();
|
|
464
498
|
this.timer = setInterval(() => {
|
|
465
499
|
if (this.disposed) return;
|
|
466
|
-
this.
|
|
500
|
+
this.invalidate();
|
|
467
501
|
this.tui.requestRender();
|
|
468
502
|
}, options.refreshMs ?? REFRESH_MS);
|
|
469
503
|
this.timer.unref?.();
|
|
@@ -482,9 +516,67 @@ export class SubagentFleetComponent implements Component {
|
|
|
482
516
|
this.selected = Math.max(0, Math.min(this.snapshot.items.length - 1, this.selected + delta));
|
|
483
517
|
this.selectedKey = this.snapshot.items[this.selected]?.key;
|
|
484
518
|
this.detailAutoFollow = true;
|
|
519
|
+
this.resetActionInput();
|
|
520
|
+
this.tui.requestRender();
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
private resetActionInput(): void {
|
|
524
|
+
this.steerDraft = undefined;
|
|
525
|
+
this.stopConfirming = false;
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
private selectedAsyncAction(): { item: Extract<FleetItem, { kind: "async" }> } | { reason: string } {
|
|
529
|
+
const item = this.snapshot.items[this.selected];
|
|
530
|
+
if (!item) return { reason: "No child is selected." };
|
|
531
|
+
if (item.kind !== "async") return { reason: "Fleet controls are available for current-session top-level async runs only." };
|
|
532
|
+
if (!isActionableAsyncState(item.run.state) || !isActionableAsyncState(item.state)) return { reason: `Selected child is ${item.state}; controls require a running or queued async child.` };
|
|
533
|
+
return { item };
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
private actionLines(): string[] {
|
|
537
|
+
const lines: string[] = [];
|
|
538
|
+
if (this.actionBusy) lines.push(this.theme.fg("accent", "Action pending..."));
|
|
539
|
+
if (this.steerDraft !== undefined) {
|
|
540
|
+
lines.push(this.theme.fg("accent", `Steer message: ${this.steerDraft}${this.theme.fg("dim", "▌")}`));
|
|
541
|
+
lines.push(this.theme.fg("dim", "Enter sends · Esc cancels · Backspace edits"));
|
|
542
|
+
} else if (this.stopConfirming) {
|
|
543
|
+
const selected = this.snapshot.items[this.selected];
|
|
544
|
+
lines.push(this.theme.fg("warning", `Confirm stop for async run ${selected?.runId ?? "selected run"}?`));
|
|
545
|
+
lines.push(this.theme.fg("dim", "Stop ends the run; use interrupt for a resumable pause. Enter/Y confirms · N returns · Esc cancels"));
|
|
546
|
+
} else if (this.actionNotice) {
|
|
547
|
+
lines.push(this.theme.fg(this.actionNotice.isError ? "error" : "success", this.actionNotice.text));
|
|
548
|
+
}
|
|
549
|
+
return lines;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
private withActionLines(body: string[]): string[] {
|
|
553
|
+
const actionLines = this.actionLines();
|
|
554
|
+
return actionLines.length ? [...actionLines, "", ...body] : body;
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
private setActionNotice(result: FleetActionResult): void {
|
|
558
|
+
this.actionNotice = result;
|
|
559
|
+
this.resetActionInput();
|
|
560
|
+
this.detailAutoFollow = false;
|
|
561
|
+
this.detailScroll = 0;
|
|
562
|
+
this.refresh();
|
|
485
563
|
this.tui.requestRender();
|
|
486
564
|
}
|
|
487
565
|
|
|
566
|
+
private runAction(action: () => Promise<FleetActionResult>): void {
|
|
567
|
+
if (this.actionBusy) return;
|
|
568
|
+
this.actionBusy = true;
|
|
569
|
+
this.actionNotice = undefined;
|
|
570
|
+
this.tui.requestRender();
|
|
571
|
+
void action()
|
|
572
|
+
.then((result) => this.setActionNotice(result))
|
|
573
|
+
.catch((error) => this.setActionNotice({ text: error instanceof Error ? error.message : String(error), isError: true }))
|
|
574
|
+
.finally(() => {
|
|
575
|
+
this.actionBusy = false;
|
|
576
|
+
if (!this.disposed) this.tui.requestRender();
|
|
577
|
+
});
|
|
578
|
+
}
|
|
579
|
+
|
|
488
580
|
private scrollDetail(delta: number): void {
|
|
489
581
|
const maxScroll = Math.max(0, this.detailLineCount - this.detailViewportHeight);
|
|
490
582
|
this.detailScroll = Math.max(0, Math.min(maxScroll, this.detailScroll + delta));
|
|
@@ -493,6 +585,53 @@ export class SubagentFleetComponent implements Component {
|
|
|
493
585
|
}
|
|
494
586
|
|
|
495
587
|
handleInput(data: string): void {
|
|
588
|
+
if (this.steerDraft !== undefined) {
|
|
589
|
+
if (matchesKey(data, "escape") || matchesKey(data, "ctrl+c")) {
|
|
590
|
+
this.resetActionInput();
|
|
591
|
+
this.tui.requestRender();
|
|
592
|
+
return;
|
|
593
|
+
}
|
|
594
|
+
if (matchesKey(data, "return") || data === "\r" || data === "\n") {
|
|
595
|
+
const message = this.steerDraft.trim();
|
|
596
|
+
if (!message) {
|
|
597
|
+
this.setActionNotice({ text: "Steer message cannot be empty.", isError: true });
|
|
598
|
+
return;
|
|
599
|
+
}
|
|
600
|
+
const target = this.selectedAsyncAction();
|
|
601
|
+
if ("reason" in target || !this.options.actions) {
|
|
602
|
+
this.setActionNotice({ text: "reason" in target ? target.reason : "Fleet controls are unavailable in this context.", isError: true });
|
|
603
|
+
return;
|
|
604
|
+
}
|
|
605
|
+
this.runAction(() => this.options.actions!.steer({ runId: target.item.runId, asyncDir: target.item.run.asyncDir, ...(target.item.index !== undefined ? { index: target.item.index } : {}), message }));
|
|
606
|
+
return;
|
|
607
|
+
}
|
|
608
|
+
if (matchesKey(data, "backspace") || data === "\x7f") {
|
|
609
|
+
this.steerDraft = this.steerDraft.slice(0, -1);
|
|
610
|
+
this.tui.requestRender();
|
|
611
|
+
return;
|
|
612
|
+
}
|
|
613
|
+
if (data.length === 1 && data >= " " && data !== "\x7f") {
|
|
614
|
+
this.steerDraft += data;
|
|
615
|
+
this.tui.requestRender();
|
|
616
|
+
}
|
|
617
|
+
return;
|
|
618
|
+
}
|
|
619
|
+
if (this.stopConfirming) {
|
|
620
|
+
if (matchesKey(data, "return") || data.toLowerCase() === "y") {
|
|
621
|
+
const target = this.selectedAsyncAction();
|
|
622
|
+
if ("reason" in target || !this.options.actions) {
|
|
623
|
+
this.setActionNotice({ text: "reason" in target ? target.reason : "Fleet controls are unavailable in this context.", isError: true });
|
|
624
|
+
return;
|
|
625
|
+
}
|
|
626
|
+
this.runAction(() => Promise.resolve(this.options.actions!.stop({ runId: target.item.runId, asyncDir: target.item.run.asyncDir, ...(target.item.index !== undefined ? { index: target.item.index } : {}) })));
|
|
627
|
+
return;
|
|
628
|
+
}
|
|
629
|
+
if (matchesKey(data, "escape") || matchesKey(data, "ctrl+c") || data.toLowerCase() === "n" || matchesKey(data, "backspace")) {
|
|
630
|
+
this.resetActionInput();
|
|
631
|
+
this.tui.requestRender();
|
|
632
|
+
}
|
|
633
|
+
return;
|
|
634
|
+
}
|
|
496
635
|
if (matchesKey(data, "escape") || matchesKey(data, "ctrl+c") || matchesKey(data, "q")) {
|
|
497
636
|
this.done(undefined);
|
|
498
637
|
return;
|
|
@@ -511,6 +650,30 @@ export class SubagentFleetComponent implements Component {
|
|
|
511
650
|
this.tui.requestRender();
|
|
512
651
|
return;
|
|
513
652
|
}
|
|
653
|
+
if (data === "s") {
|
|
654
|
+
const target = this.selectedAsyncAction();
|
|
655
|
+
if ("reason" in target || !this.options.actions) this.setActionNotice({ text: "reason" in target ? target.reason : "Fleet controls are unavailable in this context.", isError: true });
|
|
656
|
+
else {
|
|
657
|
+
this.actionNotice = undefined;
|
|
658
|
+
this.steerDraft = "";
|
|
659
|
+
this.detailAutoFollow = false;
|
|
660
|
+
this.detailScroll = 0;
|
|
661
|
+
this.tui.requestRender();
|
|
662
|
+
}
|
|
663
|
+
return;
|
|
664
|
+
}
|
|
665
|
+
if (data === "D") {
|
|
666
|
+
const target = this.selectedAsyncAction();
|
|
667
|
+
if ("reason" in target || !this.options.actions) this.setActionNotice({ text: "reason" in target ? target.reason : "Fleet controls are unavailable in this context.", isError: true });
|
|
668
|
+
else {
|
|
669
|
+
this.actionNotice = undefined;
|
|
670
|
+
this.stopConfirming = true;
|
|
671
|
+
this.detailAutoFollow = false;
|
|
672
|
+
this.detailScroll = 0;
|
|
673
|
+
this.tui.requestRender();
|
|
674
|
+
}
|
|
675
|
+
return;
|
|
676
|
+
}
|
|
514
677
|
if (data.toLowerCase() === "x" || matchesKey(data, "ctrl+o")) {
|
|
515
678
|
this.expandedTools = !this.expandedTools;
|
|
516
679
|
this.transcriptCache = undefined;
|
|
@@ -567,7 +730,7 @@ export class SubagentFleetComponent implements Component {
|
|
|
567
730
|
: latest?.kind === "tool"
|
|
568
731
|
? `${latest.name} · ${latest.status}`
|
|
569
732
|
: "activity";
|
|
570
|
-
return { header: structuredHeader(selected, width, this.theme, conversationState), body };
|
|
733
|
+
return { header: structuredHeader(selected, width, this.theme, conversationState), body: this.withActionLines(body) };
|
|
571
734
|
}
|
|
572
735
|
}
|
|
573
736
|
}
|
|
@@ -576,7 +739,7 @@ export class SubagentFleetComponent implements Component {
|
|
|
576
739
|
if (transcriptWarning) raw.unshift(`Transcript preview warning: ${transcriptWarning}`, "");
|
|
577
740
|
const lines: string[] = [];
|
|
578
741
|
for (const line of raw) {
|
|
579
|
-
const styled = /^(Run|State|Mode|Source|Child|Agent):/.test(line)
|
|
742
|
+
const styled = /^(Run|State|Mode|Source|Child|Agent|Model):/.test(line)
|
|
580
743
|
? this.theme.bold(line)
|
|
581
744
|
: /^(Transcript|Result transcript tail)/.test(line)
|
|
582
745
|
? this.theme.fg("accent", line)
|
|
@@ -588,7 +751,7 @@ export class SubagentFleetComponent implements Component {
|
|
|
588
751
|
const wrapped = wrapTextWithAnsi(styled, Math.max(1, width));
|
|
589
752
|
lines.push(...(wrapped.length ? wrapped : [""]));
|
|
590
753
|
}
|
|
591
|
-
return { header: [], body: lines };
|
|
754
|
+
return { header: [], body: this.withActionLines(lines) };
|
|
592
755
|
}
|
|
593
756
|
|
|
594
757
|
render(width: number): string[] {
|
|
@@ -612,7 +775,7 @@ export class SubagentFleetComponent implements Component {
|
|
|
612
775
|
];
|
|
613
776
|
const lines = [this.theme.fg("border", `╭${"─".repeat(innerWidth)}╮`)];
|
|
614
777
|
const selected = this.snapshot.items[this.selected];
|
|
615
|
-
const title = ` ${this.theme.bold("Subagent fleet inspector")} ${this.theme.fg("dim", "·
|
|
778
|
+
const title = ` ${this.theme.bold("Subagent fleet inspector")} ${this.theme.fg("dim", "· live controls")}`;
|
|
616
779
|
const selectedStatus = selected
|
|
617
780
|
? `${statusGlyph(selected, this.theme)} ${selected.agent} · ${selected.state} `
|
|
618
781
|
: this.theme.fg("dim", "no children ");
|
|
@@ -629,7 +792,7 @@ export class SubagentFleetComponent implements Component {
|
|
|
629
792
|
}
|
|
630
793
|
lines.push(this.theme.fg("border", `├${"─".repeat(rosterWidth)}┴${"─".repeat(detailWidth)}┤`));
|
|
631
794
|
const position = this.snapshot.items.length ? `${this.selected + 1}/${this.snapshot.items.length}` : "0/0";
|
|
632
|
-
const footer = ` ↑↓/jk agent ·
|
|
795
|
+
const footer = ` ↑↓/jk agent · s steer · D stop · x/Ctrl+O tools · r refresh · Esc close · ${position}`;
|
|
633
796
|
lines.push(this.theme.fg("border", "│") + fit(this.theme.fg("dim", footer), innerWidth) + this.theme.fg("border", "│"));
|
|
634
797
|
lines.push(this.theme.fg("border", `╰${"─".repeat(innerWidth)}╯`));
|
|
635
798
|
return lines.map((line) => truncateToWidth(line, width));
|
|
@@ -650,9 +813,19 @@ export async function openSubagentFleet(ctx: ExtensionContext, state: SubagentSt
|
|
|
650
813
|
const wasOpen = state.fleetInspectorOpen === true;
|
|
651
814
|
state.fleetInspectorOpen = true;
|
|
652
815
|
if (typeof ctx.ui.setWidget === "function") ctx.ui.setWidget(FLEET_STATUS_WIDGET_KEY, undefined);
|
|
816
|
+
const actions = options.actions ?? {
|
|
817
|
+
steer: async (input: { runId: string; asyncDir: string; index?: number; message: string }) => firstToolResultText(await steerAsyncRun({
|
|
818
|
+
state,
|
|
819
|
+
runId: input.runId,
|
|
820
|
+
...(input.index !== undefined ? { index: input.index } : {}),
|
|
821
|
+
message: input.message,
|
|
822
|
+
location: { asyncDir: input.asyncDir, resolvedId: input.runId },
|
|
823
|
+
}), `Failed to steer async run ${input.runId}.`),
|
|
824
|
+
stop: (input: { runId: string; asyncDir: string; index?: number }) => firstToolResultText(stopAsyncRun(state, input.runId, undefined, { asyncDir: input.asyncDir, resolvedId: input.runId }), `Failed to stop async run ${input.runId}.`),
|
|
825
|
+
} satisfies FleetActionHandlers;
|
|
653
826
|
try {
|
|
654
827
|
await ctx.ui.custom<undefined>(
|
|
655
|
-
(tui, theme, _keybindings, done) => new SubagentFleetComponent(tui, theme, state, done, options),
|
|
828
|
+
(tui, theme, _keybindings, done) => new SubagentFleetComponent(tui, theme, state, done, { ...options, actions }),
|
|
656
829
|
);
|
|
657
830
|
} finally {
|
|
658
831
|
state.fleetInspectorOpen = wasOpen;
|
|
@@ -40,6 +40,7 @@ function getTermWidth(): number {
|
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
const segmenter = new Intl.Segmenter(undefined, { granularity: "grapheme" });
|
|
43
|
+
const ansiStylePattern = /\x1b\[[0-9;]*m/y;
|
|
43
44
|
|
|
44
45
|
/**
|
|
45
46
|
* Truncate a line to maxWidth, preserving ANSI styling through the ellipsis.
|
|
@@ -50,7 +51,7 @@ const segmenter = new Intl.Segmenter(undefined, { granularity: "grapheme" });
|
|
|
50
51
|
*
|
|
51
52
|
* Uses Intl.Segmenter for proper Unicode/emoji handling (not char-by-char).
|
|
52
53
|
*/
|
|
53
|
-
function truncLine(text: string, maxWidth: number): string {
|
|
54
|
+
export function truncLine(text: string, maxWidth: number): string {
|
|
54
55
|
if (visibleWidth(text) <= maxWidth) return text;
|
|
55
56
|
|
|
56
57
|
const targetWidth = maxWidth - 1;
|
|
@@ -60,7 +61,8 @@ function truncLine(text: string, maxWidth: number): string {
|
|
|
60
61
|
let i = 0;
|
|
61
62
|
|
|
62
63
|
while (i < text.length) {
|
|
63
|
-
|
|
64
|
+
ansiStylePattern.lastIndex = i;
|
|
65
|
+
const ansiMatch = ansiStylePattern.exec(text);
|
|
64
66
|
if (ansiMatch) {
|
|
65
67
|
const code = ansiMatch[0];
|
|
66
68
|
result += code;
|
|
@@ -74,11 +76,9 @@ function truncLine(text: string, maxWidth: number): string {
|
|
|
74
76
|
continue;
|
|
75
77
|
}
|
|
76
78
|
|
|
77
|
-
let end = i;
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
}
|
|
81
|
-
|
|
79
|
+
let end = text.indexOf("\x1b[", i);
|
|
80
|
+
if (end === i) end = text.indexOf("\x1b[", i + 2);
|
|
81
|
+
if (end === -1) end = text.length;
|
|
82
82
|
const textPortion = text.slice(i, end);
|
|
83
83
|
for (const seg of segmenter.segment(textPortion)) {
|
|
84
84
|
const grapheme = seg.segment;
|
|
@@ -529,17 +529,15 @@ function buildChainStepSpans(details: Pick<Details, "chainAgents" | "workflowGra
|
|
|
529
529
|
return spans;
|
|
530
530
|
}
|
|
531
531
|
|
|
532
|
-
function isChainParallelGroupActive(details: Pick<Details, "mode" | "chainAgents" | "currentStepIndex" | "workflowGraph">): boolean {
|
|
533
|
-
if (details.mode !== "chain") return false;
|
|
534
|
-
if (details.currentStepIndex === undefined) return false;
|
|
535
|
-
return buildChainStepSpans(details).some((span) => span.stepIndex === details.currentStepIndex && span.isParallel);
|
|
536
|
-
}
|
|
537
|
-
|
|
538
532
|
function buildAsyncChainStepSpans(total: number, stepCount: number, parallelGroups: AsyncParallelGroupStatus[] = []): ChainStepSpan[] {
|
|
533
|
+
const groupsByStep = new Map<number, AsyncParallelGroupStatus>();
|
|
534
|
+
for (const group of parallelGroups) {
|
|
535
|
+
if (!groupsByStep.has(group.stepIndex)) groupsByStep.set(group.stepIndex, group);
|
|
536
|
+
}
|
|
539
537
|
const spans: ChainStepSpan[] = [];
|
|
540
538
|
let flatIndex = 0;
|
|
541
539
|
for (let stepIndex = 0; stepIndex < total; stepIndex++) {
|
|
542
|
-
const group =
|
|
540
|
+
const group = groupsByStep.get(stepIndex);
|
|
543
541
|
if (group) {
|
|
544
542
|
spans.push({ stepIndex, start: group.start, count: group.count, isParallel: true });
|
|
545
543
|
flatIndex = Math.max(flatIndex, group.start + group.count);
|
|
@@ -567,6 +565,7 @@ interface ChainRenderResultEntry {
|
|
|
567
565
|
kind: "result";
|
|
568
566
|
resultIndex: number;
|
|
569
567
|
rowNumber: number;
|
|
568
|
+
rowLabel?: string;
|
|
570
569
|
agentName: string;
|
|
571
570
|
}
|
|
572
571
|
|
|
@@ -601,6 +600,7 @@ function buildChainRenderEntries(details: Details, label: MultiProgressLabel): C
|
|
|
601
600
|
kind: "result",
|
|
602
601
|
resultIndex: index,
|
|
603
602
|
rowNumber: index + 1,
|
|
603
|
+
rowLabel: span.isParallel ? `Agent ${index - span.start + 1}/${span.count}` : `Step ${span.stepIndex + 1}`,
|
|
604
604
|
agentName: details.results[index]?.agent ?? details.chainAgents?.[span.stepIndex] ?? `step-${span.stepIndex + 1}`,
|
|
605
605
|
});
|
|
606
606
|
}
|
|
@@ -622,7 +622,9 @@ interface MultiProgressLabel {
|
|
|
622
622
|
function buildMultiProgressLabel(details: Pick<Details, "mode" | "results" | "progress" | "totalSteps" | "currentStepIndex" | "chainAgents" | "workflowGraph">, hasRunning: boolean): MultiProgressLabel {
|
|
623
623
|
const stepSpans = buildChainStepSpans(details);
|
|
624
624
|
const hasParallelInChain = details.mode === "chain" && stepSpans.some((span) => span.isParallel);
|
|
625
|
-
const activeParallelGroup =
|
|
625
|
+
const activeParallelGroup = details.mode === "chain"
|
|
626
|
+
&& details.currentStepIndex !== undefined
|
|
627
|
+
&& stepSpans.some((span) => span.stepIndex === details.currentStepIndex && span.isParallel);
|
|
626
628
|
const itemTitle: "Step" | "Agent" = details.mode === "parallel" || activeParallelGroup ? "Agent" : "Step";
|
|
627
629
|
|
|
628
630
|
if (details.mode === "parallel") {
|
|
@@ -708,15 +710,10 @@ function buildMultiProgressLabel(details: Pick<Details, "mode" | "results" | "pr
|
|
|
708
710
|
return { headerLabel, itemTitle, totalCount, hasParallelInChain, activeParallelGroup, groupStartIndex: 0, groupEndIndex: details.results.length, showActiveGroupOnly: false };
|
|
709
711
|
}
|
|
710
712
|
|
|
711
|
-
function resultRowLabel(
|
|
712
|
-
if (details.mode === "chain" && label.hasParallelInChain) {
|
|
713
|
-
const span = buildChainStepSpans(details).find((candidate) => resultIndex >= candidate.start && resultIndex < candidate.start + candidate.count);
|
|
714
|
-
if (span?.isParallel) return `Agent ${resultIndex - span.start + 1}/${span.count}`;
|
|
715
|
-
if (span) return `Step ${span.stepIndex + 1}`;
|
|
716
|
-
}
|
|
713
|
+
function resultRowLabel(label: MultiProgressLabel, resultIndex: number, stepNumber: number): string {
|
|
717
714
|
if (label.itemTitle === "Agent") {
|
|
718
715
|
const localStepNumber = label.activeParallelGroup
|
|
719
|
-
?
|
|
716
|
+
? resultIndex - label.groupStartIndex + 1
|
|
720
717
|
: stepNumber;
|
|
721
718
|
return `Agent ${localStepNumber}/${label.totalCount}`;
|
|
722
719
|
}
|
|
@@ -1321,7 +1318,7 @@ function renderSingleCompact(d: Details, r: Details["results"][number], theme: T
|
|
|
1321
1318
|
]);
|
|
1322
1319
|
const c = new Container();
|
|
1323
1320
|
const width = getTermWidth() - 4;
|
|
1324
|
-
const modelDisplay = modelThinkingBadge(theme, r.model);
|
|
1321
|
+
const modelDisplay = modelThinkingBadge(theme, r.model ?? r.progress?.model, r.thinking ?? r.progress?.thinking);
|
|
1325
1322
|
c.addChild(new Text(truncLine(`${resultGlyph(r, output, theme, isRunning, undefined, frame)} ${theme.fg("toolTitle", theme.bold(r.agent))}${modelDisplay}${contextBadge}${stats ? ` ${theme.fg("dim", "·")} ${stats}` : ""}`, width), 0, 0));
|
|
1326
1323
|
|
|
1327
1324
|
if (isRunning && r.progress) {
|
|
@@ -1417,7 +1414,7 @@ function renderMultiCompact(d: Details, theme: Theme, frame?: number): Component
|
|
|
1417
1414
|
const rowNumber = entry.rowNumber;
|
|
1418
1415
|
const agentName = entry.agentName;
|
|
1419
1416
|
if (!r) {
|
|
1420
|
-
const pendingLabel =
|
|
1417
|
+
const pendingLabel = entry.rowLabel ?? `${itemTitle} ${rowNumber}`;
|
|
1421
1418
|
c.addChild(new Text(truncLine(theme.fg("dim", ` ◦ ${pendingLabel}: ${agentName} · pending`), width), 0, 0));
|
|
1422
1419
|
continue;
|
|
1423
1420
|
}
|
|
@@ -1430,8 +1427,10 @@ function renderMultiCompact(d: Details, theme: Theme, frame?: number): Component
|
|
|
1430
1427
|
const stepStats = formatProgressStats(theme, rProg);
|
|
1431
1428
|
const glyph = rPending ? theme.fg("dim", "◦") : resultGlyph(r, output, theme, rRunning, progressRunningSeed(rProg), frame);
|
|
1432
1429
|
const pendingLabel = rPending ? ` ${theme.fg("dim", "· pending")}` : "";
|
|
1433
|
-
const stepLabel = resultRowLabel(
|
|
1434
|
-
const
|
|
1430
|
+
const stepLabel = entry.rowLabel ?? resultRowLabel(multiLabel, i, stepNumber);
|
|
1431
|
+
const rowProgressModel = rProg && "status" in rProg ? rProg : undefined;
|
|
1432
|
+
const rowModelDisplay = modelThinkingBadge(theme, r.model ?? rowProgressModel?.model, r.thinking ?? rowProgressModel?.thinking);
|
|
1433
|
+
const line = `${glyph} ${stepLabel}: ${themeBold(theme, agentName)}${contextModeBadge(theme, r.context)}${rowModelDisplay}${stepStats ? ` ${theme.fg("dim", "·")} ${stepStats}` : ""}${pendingLabel}`;
|
|
1435
1434
|
c.addChild(new Text(truncLine(` ${line}`, width), 0, 0));
|
|
1436
1435
|
if (rRunning && rProg && "status" in rProg) {
|
|
1437
1436
|
const activity = compactCurrentActivity(rProg);
|
|
@@ -1717,7 +1716,7 @@ export function renderSubagentResult(
|
|
|
1717
1716
|
const agentName = entry.agentName;
|
|
1718
1717
|
|
|
1719
1718
|
if (!r) {
|
|
1720
|
-
const pendingLabel =
|
|
1719
|
+
const pendingLabel = entry.rowLabel ?? `${itemTitle} ${rowNumber}`;
|
|
1721
1720
|
c.addChild(new Text(fit(theme.fg("dim", ` ${pendingLabel}: ${agentName}`)), 0, 0));
|
|
1722
1721
|
c.addChild(new Text(theme.fg("dim", ` status: pending`), 0, 0));
|
|
1723
1722
|
c.addChild(new Spacer(1));
|
|
@@ -1740,7 +1739,7 @@ export function renderSubagentResult(
|
|
|
1740
1739
|
: theme.fg("success", "done");
|
|
1741
1740
|
const stats = rProg ? ` | ${rProg.toolCount} tools, ${formatDuration(rProg.durationMs)}` : "";
|
|
1742
1741
|
const modelDisplay = modelThinkingBadge(theme, r.model);
|
|
1743
|
-
const stepLabel = resultRowLabel(
|
|
1742
|
+
const stepLabel = entry.rowLabel ?? resultRowLabel(multiLabel, i, stepNumber);
|
|
1744
1743
|
const contextBadge = contextModeBadge(theme, r.context);
|
|
1745
1744
|
const stepHeader = rRunning
|
|
1746
1745
|
? `${statusIcon} ${stepLabel}: ${theme.bold(theme.fg("warning", r.agent))}${contextBadge}${modelDisplay}${stats}`
|
|
@@ -111,6 +111,8 @@ export function buildWatchdogStatus(snapshot: ReturnType<MainWatchdogRuntime["ge
|
|
|
111
111
|
`Main: ${boolLabel(snapshot.enabled)}${!snapshot.config.enabled && snapshot.sessionOverride === undefined ? " (default off)" : ""}`,
|
|
112
112
|
`Runtime: ${statusLabel(snapshot.status)}${snapshot.bufferedDeltas > 0 ? ` · buffered deltas ${snapshot.bufferedDeltas}` : ""}`,
|
|
113
113
|
`Review trigger: ${snapshot.reviewTrigger === "repo-edits" ? "repo edits only" : "every non-empty turn delta"}`,
|
|
114
|
+
`Scope context: ${snapshot.config.scope.enabled ? "on" : "off"}`,
|
|
115
|
+
`Cadence: ${snapshot.config.cadence.everyNTools === null ? "boundary only" : `every ${snapshot.config.cadence.everyNTools} tools + boundary`}`,
|
|
114
116
|
lspLine(snapshot),
|
|
115
117
|
`Session override: ${snapshot.sessionOverride === undefined ? "none" : boolLabel(snapshot.sessionOverride)}`,
|
|
116
118
|
mainModelLine(snapshot, ctx),
|
|
@@ -118,7 +120,7 @@ export function buildWatchdogStatus(snapshot: ReturnType<MainWatchdogRuntime["ge
|
|
|
118
120
|
childrenLine(snapshot),
|
|
119
121
|
recommendationLine(ctx),
|
|
120
122
|
`Agent-end timeout: ${snapshot.config.agentEndTimeoutMs}ms`,
|
|
121
|
-
`Auto-follow:
|
|
123
|
+
`Auto-follow: ${snapshot.enabled && snapshot.config.autoFollow.blockers ? "on for blockers" : "off"} · attempts ${snapshot.autoFollowAttempts}${snapshot.config.autoFollow.maxAttempts === null ? "" : `/${snapshot.config.autoFollow.maxAttempts}`}${snapshot.autoFollowQueued ? " · queued" : ""}${snapshot.autoFollowStalemate ? " · stalemate" : ""}`,
|
|
122
124
|
`Review model call: ${snapshot.reviewDescription}`,
|
|
123
125
|
];
|
|
124
126
|
if (snapshot.failedReviews > 0) lines.push(`Failed reviews: ${snapshot.failedReviews}`);
|
|
@@ -236,7 +238,7 @@ function createTestWarning(severity: "concern" | "blocker", text: string): Watch
|
|
|
236
238
|
summary: text,
|
|
237
239
|
evidence: `Manual /subagents-watchdog test ${severity} message from the main session.`,
|
|
238
240
|
recommendedAction: severity === "blocker"
|
|
239
|
-
? "Verify the renderer
|
|
241
|
+
? "Verify the renderer, transcript delivery, and auto-follow policy."
|
|
240
242
|
: "Verify the renderer and transcript delivery; decide manually whether any action is needed.",
|
|
241
243
|
};
|
|
242
244
|
}
|
|
@@ -381,9 +383,10 @@ export function registerMainWatchdog(pi: ExtensionAPI, options: RegisterMainWatc
|
|
|
381
383
|
review: options.review ?? createMainWatchdogReview(() => currentContext, { getThinkingLevel: () => pi.getThinkingLevel() }),
|
|
382
384
|
reviewDescription: options.review ? "injected seam" : "real model review",
|
|
383
385
|
reviewChangesOnly: true,
|
|
384
|
-
displayWarning: (details) => {
|
|
385
|
-
pi.sendMessage(createWatchdogWarningMessage(details, { display: true, details }));
|
|
386
|
+
displayWarning: (details, delivery) => {
|
|
387
|
+
pi.sendMessage(createWatchdogWarningMessage(details, { display: true, details }), delivery?.deliverAs === "steer" ? { deliverAs: "steer" } : undefined);
|
|
386
388
|
},
|
|
389
|
+
sendUserMessage: (message) => pi.sendUserMessage(message),
|
|
387
390
|
});
|
|
388
391
|
|
|
389
392
|
pi.registerMessageRenderer<WatchdogWarningDetails>(SUBAGENT_WATCHDOG_WARNING_TYPE, (message, renderOptions, theme) => {
|
|
@@ -417,13 +420,17 @@ export function registerMainWatchdog(pi: ExtensionAPI, options: RegisterMainWatc
|
|
|
417
420
|
rememberContext(ctx);
|
|
418
421
|
runtime.handleTurnEnd(event, ctx);
|
|
419
422
|
});
|
|
423
|
+
pi.on("tool_result", (_event, ctx) => {
|
|
424
|
+
rememberContext(ctx);
|
|
425
|
+
runtime.handleToolResult(ctx);
|
|
426
|
+
});
|
|
420
427
|
pi.on("agent_end", (event, ctx) => {
|
|
421
428
|
rememberContext(ctx);
|
|
422
429
|
return runtime.handleAgentEnd(event, ctx);
|
|
423
430
|
});
|
|
424
|
-
pi.on("session_before_switch", () => runtime.reset("session switch", { clearReviewInputSignature: true, clearLspLedger: true }));
|
|
425
|
-
pi.on("session_before_fork", () => runtime.reset("session fork", { clearReviewInputSignature: true, clearLspLedger: true }));
|
|
426
|
-
pi.on("session_compact", () => runtime.reset("session compact"));
|
|
431
|
+
pi.on("session_before_switch", () => runtime.reset("session switch", { clearReviewInputSignature: true, clearLspLedger: true, clearScope: true, resetAutoFollow: true }));
|
|
432
|
+
pi.on("session_before_fork", () => runtime.reset("session fork", { clearReviewInputSignature: true, clearLspLedger: true, clearScope: true, resetAutoFollow: true }));
|
|
433
|
+
pi.on("session_compact", () => runtime.reset("session compact", { clearScope: true }));
|
|
427
434
|
pi.on("session_shutdown", () => {
|
|
428
435
|
currentContext = undefined;
|
|
429
436
|
runtime.dispose();
|
|
@@ -201,18 +201,19 @@ function createWatchdogWarnTool(request: WatchdogReviewRequest): AgentTool<typeo
|
|
|
201
201
|
};
|
|
202
202
|
}
|
|
203
203
|
|
|
204
|
-
function buildWatchdogSystemPrompt(ctx: ExtensionContext): string {
|
|
204
|
+
function buildWatchdogSystemPrompt(ctx: ExtensionContext, options: { hasScope?: boolean } = {}): string {
|
|
205
205
|
return [
|
|
206
206
|
"You are the main-session subagent watchdog for Pi.",
|
|
207
207
|
`Working directory: ${ctx.cwd}`,
|
|
208
208
|
"Review only the supplied parent turn delta. Inspect repository files only when needed to verify a concrete concern.",
|
|
209
|
+
options.hasScope ? "When the review input includes a Current scope block, treat newer scope prompts as superseding/mutating older prompts and use category='scope-drift' for work that serves no current scope item." : undefined,
|
|
209
210
|
"You are read-only. You may use read, grep, find, and ls. Do not edit files, run shell commands, spawn agents, or mutate state.",
|
|
210
211
|
"Emit warnings only by calling watchdog_warn. Freeform assistant text is ignored and must not be used to report warnings.",
|
|
211
212
|
"Emit only medium/high confidence actionable concerns or blockers: missed user constraints, correctness risks, test gaps that matter, unsafe changes, stale facts, loop risks, or scope drift.",
|
|
212
213
|
"Do not emit nits, style preferences, low-confidence guesses, informational notes, praise, or summaries.",
|
|
213
214
|
"If the turn is clean, call no tools and end normally.",
|
|
214
215
|
"Use severity='blocker' only when the issue should stop acceptance until addressed; otherwise use severity='concern'.",
|
|
215
|
-
].join("\n");
|
|
216
|
+
].filter((line): line is string => Boolean(line)).join("\n");
|
|
216
217
|
}
|
|
217
218
|
|
|
218
219
|
function buildReviewPrompt(request: WatchdogReviewRequest, selection: WatchdogReviewModelSelection): string {
|
|
@@ -270,13 +271,13 @@ export function createMainWatchdogReview(provider: WatchdogContextProvider, opti
|
|
|
270
271
|
];
|
|
271
272
|
const agent = new Agent({
|
|
272
273
|
initialState: {
|
|
273
|
-
systemPrompt: buildWatchdogSystemPrompt(ctx),
|
|
274
|
+
systemPrompt: buildWatchdogSystemPrompt(ctx, { hasScope: request.hasScope }),
|
|
274
275
|
model: selection.model,
|
|
275
276
|
thinkingLevel: selection.thinkingLevel,
|
|
276
277
|
tools,
|
|
277
278
|
},
|
|
278
279
|
convertToLlm,
|
|
279
|
-
|
|
280
|
+
streamFn,
|
|
280
281
|
getApiKey: (providerName) => providerName === selection.model.provider ? auth.apiKey : undefined,
|
|
281
282
|
beforeToolCall: async ({ toolCall }) => WATCHDOG_ALLOWED_TOOL_NAMES.has(toolCall.name)
|
|
282
283
|
? undefined
|