@selesai/code 0.13.12 → 0.13.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +9 -0
- package/dist/extensions/pi-subagents/CHANGELOG.md +77 -1
- package/dist/extensions/pi-subagents/VISION.md +12 -1
- package/dist/extensions/pi-subagents/docs/agents.md +13 -6
- package/dist/extensions/pi-subagents/docs/configuration.md +35 -9
- package/dist/extensions/pi-subagents/docs/extension-api.md +1 -1
- package/dist/extensions/pi-subagents/docs/missions.md +1 -1
- package/dist/extensions/pi-subagents/docs/models.md +6 -6
- package/dist/extensions/pi-subagents/docs/observability.md +6 -3
- package/dist/extensions/pi-subagents/docs/tool-reference.md +3 -3
- package/dist/extensions/pi-subagents/docs/watchdog.md +92 -114
- package/dist/extensions/pi-subagents/docs/workflows.md +1 -1
- package/dist/extensions/pi-subagents/install.mjs +1 -2
- package/dist/extensions/pi-subagents/package.json +1 -1
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/constraints-and-recipes.md +1 -1
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/execution-controls.md +6 -6
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/management-authoring-rpc.md +2 -2
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/multi-lane-orchestration.md +1 -1
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/prompting-and-roles.md +4 -4
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/review-and-validation.md +1 -1
- package/dist/extensions/pi-subagents/src/agents/agent-management.ts +41 -4
- package/dist/extensions/pi-subagents/src/agents/agent-serializer.ts +3 -0
- package/dist/extensions/pi-subagents/src/agents/agents.ts +120 -124
- package/dist/extensions/pi-subagents/src/agents/runtime-agent-registry.ts +5 -1
- package/dist/extensions/pi-subagents/src/api/preflight.ts +4 -0
- package/dist/extensions/pi-subagents/src/api/shared-types.ts +3 -0
- package/dist/extensions/pi-subagents/src/extension/config.ts +20 -0
- package/dist/extensions/pi-subagents/src/extension/public-execution.ts +1 -0
- package/dist/extensions/pi-subagents/src/extension/schemas.ts +6 -2
- package/dist/extensions/pi-subagents/src/extension/tool-description.ts +1 -1
- package/dist/extensions/pi-subagents/src/inspectors/herdr/inspector-runner.ts +19 -13
- package/dist/extensions/pi-subagents/src/runs/background/active-async-capacity.ts +26 -8
- package/dist/extensions/pi-subagents/src/runs/background/async-execution.ts +103 -23
- package/dist/extensions/pi-subagents/src/runs/background/async-resume.ts +6 -2
- package/dist/extensions/pi-subagents/src/runs/background/async-status.ts +18 -2
- package/dist/extensions/pi-subagents/src/runs/background/notify.ts +54 -3
- package/dist/extensions/pi-subagents/src/runs/background/process-terminal.ts +16 -0
- package/dist/extensions/pi-subagents/src/runs/background/run-status.ts +22 -2
- package/dist/extensions/pi-subagents/src/runs/background/scheduled-runs.ts +63 -6
- package/dist/extensions/pi-subagents/src/runs/background/steering.ts +4 -1
- package/dist/extensions/pi-subagents/src/runs/background/subagent-runner.ts +69 -12
- package/dist/extensions/pi-subagents/src/runs/background/wait-completions.ts +13 -0
- package/dist/extensions/pi-subagents/src/runs/background/wait-tool.ts +3 -1
- package/dist/extensions/pi-subagents/src/runs/foreground/execution.ts +21 -8
- package/dist/extensions/pi-subagents/src/runs/foreground/subagent-executor.ts +59 -6
- package/dist/extensions/pi-subagents/src/runs/shared/acceptance.ts +95 -18
- package/dist/extensions/pi-subagents/src/runs/shared/async-status-projection.ts +11 -43
- package/dist/extensions/pi-subagents/src/runs/shared/capability-ceiling.ts +1 -0
- package/dist/extensions/pi-subagents/src/runs/shared/dynamic-fanout.ts +1 -1
- package/dist/extensions/pi-subagents/src/runs/shared/lane-metadata.ts +24 -3
- package/dist/extensions/pi-subagents/src/runs/shared/parallel-handoff.ts +4 -0
- package/dist/extensions/pi-subagents/src/runs/shared/parallel-utils.ts +2 -6
- package/dist/extensions/pi-subagents/src/runs/shared/permissions.ts +1 -0
- package/dist/extensions/pi-subagents/src/runs/shared/pi-args.ts +33 -18
- package/dist/extensions/pi-subagents/src/runs/shared/pi-spawn.ts +73 -37
- package/dist/extensions/pi-subagents/src/runs/shared/structured-output.ts +33 -6
- package/dist/extensions/pi-subagents/src/runs/shared/subagent-control.ts +4 -2
- package/dist/extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.ts +20 -3
- package/dist/extensions/pi-subagents/src/runs/shared/task-intent.ts +21 -7
- package/dist/extensions/pi-subagents/src/runs/shared/tool-timeout.ts +1 -1
- package/dist/extensions/pi-subagents/src/runs/shared/worktree.ts +467 -63
- package/dist/extensions/pi-subagents/src/shared/atomic-json.ts +3 -1
- package/dist/extensions/pi-subagents/src/shared/fork-context.ts +0 -12
- package/dist/extensions/pi-subagents/src/shared/fork-session-cwd.ts +27 -0
- package/dist/extensions/pi-subagents/src/shared/launch-contract.ts +3 -0
- package/dist/extensions/pi-subagents/src/shared/types.ts +42 -4
- package/dist/extensions/pi-subagents/src/shared/utils.ts +18 -7
- package/dist/extensions/pi-subagents/src/slash/slash-commands.ts +1 -1
- package/dist/extensions/pi-subagents/src/slash/subagents-admin.ts +26 -12
- package/dist/extensions/pi-subagents/src/tui/fleet-status.ts +61 -2
- package/dist/extensions/pi-subagents/src/tui/fleet.ts +12 -7
- package/dist/extensions/pi-subagents/src/tui/render.ts +227 -14
- package/dist/extensions/pi-subagents/src/watchdog/child-status.ts +56 -34
- package/dist/extensions/pi-subagents/src/watchdog/diff-tool.ts +77 -0
- package/dist/extensions/pi-subagents/src/watchdog/emission-guard.ts +5 -3
- package/dist/extensions/pi-subagents/src/watchdog/guidance.ts +20 -0
- package/dist/extensions/pi-subagents/src/watchdog/register-child.ts +16 -13
- package/dist/extensions/pi-subagents/src/watchdog/register-main.ts +10 -9
- package/dist/extensions/pi-subagents/src/watchdog/render.ts +4 -5
- package/dist/extensions/pi-subagents/src/watchdog/review.ts +15 -4
- package/dist/extensions/pi-subagents/src/watchdog/rules.ts +70 -0
- package/dist/extensions/pi-subagents/src/watchdog/runtime.ts +75 -92
- package/dist/extensions/pi-subagents/src/watchdog/scope.ts +2 -1
- package/dist/extensions/pi-subagents/src/watchdog/settings.ts +48 -104
- package/dist/extensions/pi-subagents/src/watchdog/types.ts +18 -32
- package/dist/extensions/pi-subagents/src/watchdog/warning-format.ts +0 -1
- package/dist/extensions/pi-subagents/src/workflows/chat-progress.ts +3 -2
- package/dist/extensions/pi-subagents/src/workflows/workflow-checklist.ts +439 -0
- package/dist/extensions/pi-subagents/src/workflows/workflow-preflight.ts +28 -1
- package/dist/extensions/pi-subagents/test/integration/async-execution.test.ts +335 -21
- package/dist/extensions/pi-subagents/test/integration/async-job-tracker.test.ts +4 -1
- package/dist/extensions/pi-subagents/test/integration/async-status.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/integration/fork-context-execution.test.ts +22 -18
- package/dist/extensions/pi-subagents/test/integration/intercom-result-delivery.test.ts +0 -1
- package/dist/extensions/pi-subagents/test/integration/render-widget.test.ts +21 -18
- package/dist/extensions/pi-subagents/test/integration/single-execution.test.ts +307 -22
- package/dist/extensions/pi-subagents/test/integration/slash-commands.test.ts +1 -0
- package/dist/extensions/pi-subagents/test/support/helpers.ts +25 -0
- package/dist/extensions/pi-subagents/test/support/isolated-temp-root.mjs +15 -4
- package/dist/extensions/pi-subagents/test/support/mock-pi-script.mjs +17 -2
- package/dist/extensions/pi-subagents/test/unit/acceptance.test.ts +96 -15
- package/dist/extensions/pi-subagents/test/unit/active-async-capacity.test.ts +53 -5
- package/dist/extensions/pi-subagents/test/unit/agent-frontmatter.test.ts +20 -0
- package/dist/extensions/pi-subagents/test/unit/agent-management.test.ts +221 -8
- package/dist/extensions/pi-subagents/test/unit/agent-overrides.test.ts +45 -24
- package/dist/extensions/pi-subagents/test/unit/agent-scan-dirs.test.ts +133 -0
- package/dist/extensions/pi-subagents/test/unit/async-execution.test.ts +28 -2
- package/dist/extensions/pi-subagents/test/unit/async-interrupt-action.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/async-resume.test.ts +2 -0
- package/dist/extensions/pi-subagents/test/unit/async-retention.test.ts +4 -1
- package/dist/extensions/pi-subagents/test/unit/async-status-projection.test.ts +18 -4
- package/dist/extensions/pi-subagents/test/unit/atomic-json.test.ts +16 -0
- package/dist/extensions/pi-subagents/test/unit/completion-guard.test.ts +38 -0
- package/dist/extensions/pi-subagents/test/unit/default-extensions.test.ts +2 -2
- package/dist/extensions/pi-subagents/test/unit/fleet-status.test.ts +21 -0
- package/dist/extensions/pi-subagents/test/unit/fleet.test.ts +19 -6
- package/dist/extensions/pi-subagents/test/unit/get-final-output.test.ts +26 -0
- package/dist/extensions/pi-subagents/test/unit/herdr-inspector.test.ts +22 -1
- package/dist/extensions/pi-subagents/test/unit/index-child-registration.test.ts +2 -6
- package/dist/extensions/pi-subagents/test/unit/notify.test.ts +66 -1
- package/dist/extensions/pi-subagents/test/unit/pi-args-permission-system.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/pi-args.test.ts +116 -10
- package/dist/extensions/pi-subagents/test/unit/pi-spawn.test.ts +136 -18
- package/dist/extensions/pi-subagents/test/unit/preflight.test.ts +20 -0
- package/dist/extensions/pi-subagents/test/unit/process-terminal.test.ts +22 -0
- package/dist/extensions/pi-subagents/test/unit/profiles.test.ts +1 -0
- package/dist/extensions/pi-subagents/test/unit/project-panes-public-api.test.ts +8 -0
- package/dist/extensions/pi-subagents/test/unit/render-helpers.test.ts +127 -48
- package/dist/extensions/pi-subagents/test/unit/run-status.test.ts +68 -0
- package/dist/extensions/pi-subagents/test/unit/runtime-agent-registration.test.ts +17 -0
- package/dist/extensions/pi-subagents/test/unit/scheduled-runs.test.ts +88 -0
- package/dist/extensions/pi-subagents/test/unit/schemas.test.ts +4 -2
- package/dist/extensions/pi-subagents/test/unit/scripted-workflow.test.ts +9 -9
- package/dist/extensions/pi-subagents/test/unit/steering-action.test.ts +2 -2
- package/dist/extensions/pi-subagents/test/unit/steering.test.ts +9 -3
- package/dist/extensions/pi-subagents/test/unit/subagent-control.test.ts +22 -9
- package/dist/extensions/pi-subagents/test/unit/subagent-prompt-runtime.test.ts +91 -8
- package/dist/extensions/pi-subagents/test/unit/task-intent.test.ts +27 -0
- package/dist/extensions/pi-subagents/test/unit/temp-paths.test.ts +50 -0
- package/dist/extensions/pi-subagents/test/unit/tool-description.test.ts +0 -1
- package/dist/extensions/pi-subagents/test/unit/tool-timeout.test.ts +2 -2
- package/dist/extensions/pi-subagents/test/unit/wait-completions.test.ts +34 -0
- package/dist/extensions/pi-subagents/test/unit/wait-subscriptions.test.ts +1 -2
- package/dist/extensions/pi-subagents/test/unit/watchdog-child-status.test.ts +73 -13
- package/dist/extensions/pi-subagents/test/unit/watchdog-diff-tool.test.ts +79 -0
- package/dist/extensions/pi-subagents/test/unit/watchdog-permission-arbiter.test.ts +2 -4
- package/dist/extensions/pi-subagents/test/unit/watchdog-render.test.ts +7 -8
- package/dist/extensions/pi-subagents/test/unit/watchdog-review.test.ts +48 -3
- package/dist/extensions/pi-subagents/test/unit/watchdog-rules.test.ts +37 -0
- package/dist/extensions/pi-subagents/test/unit/watchdog-runtime.test.ts +36 -118
- package/dist/extensions/pi-subagents/test/unit/watchdog-scope.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/watchdog-settings.test.ts +37 -14
- package/dist/extensions/pi-subagents/test/unit/workflow-chat-progress.test.ts +45 -26
- package/dist/extensions/pi-subagents/test/unit/workflow-checklist.test.ts +223 -0
- package/dist/extensions/pi-subagents/test/unit/workflow-launch-params.test.ts +50 -1
- package/dist/extensions/pi-subagents/test/unit/workflow-preflight.test.ts +22 -0
- package/dist/extensions/pi-subagents/test/unit/worktree.test.ts +136 -4
- package/dist/extensions/pi-zentui/README.md +19 -5
- package/dist/extensions/pi-zentui/docs/configuration.md +37 -9
- package/dist/extensions/pi-zentui/extensions/zentui/config.ts +29 -0
- package/dist/extensions/pi-zentui/extensions/zentui/index.ts +31 -2
- package/dist/extensions/pi-zentui/extensions/zentui/prototype-patch-registry.ts +55 -11
- package/dist/extensions/pi-zentui/extensions/zentui/settings-command.ts +115 -3
- package/dist/extensions/pi-zentui/extensions/zentui/settings-previews.ts +119 -2
- package/dist/extensions/pi-zentui/extensions/zentui/thinking-experimental.ts +1768 -0
- package/dist/extensions/pi-zentui/extensions/zentui/thinking-status.ts +69 -0
- package/dist/extensions/pi-zentui/extensions/zentui/thinking-steps.ts +155 -0
- package/dist/extensions/pi-zentui/extensions/zentui/ui.ts +2 -1
- package/dist/extensions/pi-zentui/extensions/zentui/user-message-osc.ts +16 -2
- package/dist/extensions/pi-zentui/extensions/zentui/user-message-styles.ts +1 -1
- package/dist/extensions/pi-zentui/test/config-load-lifecycle.test.ts +124 -0
- package/dist/extensions/pi-zentui/test/config.test.ts +82 -2
- package/dist/extensions/pi-zentui/test/editor-viewport-indicators.test.ts +19 -4
- package/dist/extensions/pi-zentui/test/extension-compliance.test.ts +58 -9
- package/dist/extensions/pi-zentui/test/package-contents.mjs +33 -0
- package/dist/extensions/pi-zentui/test/prototype-patch-registry.test.ts +131 -0
- package/dist/extensions/pi-zentui/test/responsive-dependencies.test.ts +4 -4
- package/dist/extensions/pi-zentui/test/settings-command.test.ts +289 -7
- package/dist/extensions/pi-zentui/test/settings-previews.test.ts +106 -0
- package/dist/extensions/pi-zentui/test/thinking-experimental-docs.test.ts +51 -0
- package/dist/extensions/pi-zentui/test/thinking-experimental-loader-diagnostics.mjs +54 -0
- package/dist/extensions/pi-zentui/test/thinking-experimental-missing-export.test.ts +44 -0
- package/dist/extensions/pi-zentui/test/thinking-experimental-rows.test.ts +297 -0
- package/dist/extensions/pi-zentui/test/thinking-experimental-tui.mjs +2329 -0
- package/dist/extensions/pi-zentui/test/thinking-experimental.test.ts +1668 -0
- package/dist/extensions/pi-zentui/test/thinking-status.test.ts +85 -0
- package/dist/extensions/pi-zentui/test/thinking-steps.test.ts +122 -0
- package/dist/extensions/pi-zentui/test/user-message-osc.test.ts +58 -0
- package/dist/skills/brandkit/SKILL.md +0 -2
- package/dist/skills/{industrial-brutalist-ui → brutalist}/SKILL.md +1 -3
- package/dist/skills/gpt-taste/SKILL.md +0 -2
- package/dist/skills/image-to-code/SKILL.md +0 -2
- package/dist/skills/imagegen-frontend-mobile/SKILL.md +0 -2
- package/dist/skills/imagegen-frontend-web/SKILL.md +0 -2
- package/dist/skills/{minimalist-ui → minimalist}/SKILL.md +1 -3
- package/dist/skills/{full-output-enforcement → output}/SKILL.md +1 -3
- package/dist/skills/{redesign-existing-projects → redesign}/SKILL.md +1 -3
- package/dist/skills/{high-end-visual-design → soft}/SKILL.md +1 -3
- package/dist/skills/stitch/DESIGN.md +121 -0
- package/dist/skills/{stitch-design-taste → stitch}/SKILL.md +1 -3
- package/dist/skills/{design-taste-frontend → taste}/SKILL.md +1 -3
- package/dist/skills/{design-taste-frontend-v1 → taste-v1}/SKILL.md +2 -4
- package/package.json +1 -1
|
@@ -15,6 +15,7 @@ type AtomicJsonWriterOptions = {
|
|
|
15
15
|
mode?: number;
|
|
16
16
|
retryRenameErrors?: boolean;
|
|
17
17
|
retryDirectoryErrors?: boolean;
|
|
18
|
+
ignoreCleanupErrorAfterSuccess?: boolean;
|
|
18
19
|
retryDelaysMs?: readonly number[];
|
|
19
20
|
wait?: (delayMs: number) => void;
|
|
20
21
|
};
|
|
@@ -46,6 +47,7 @@ export function createAtomicJsonWriter(options: AtomicJsonWriterOptions = {}): (
|
|
|
46
47
|
const mode = options.mode;
|
|
47
48
|
const retryRenameErrors = options.retryRenameErrors ?? process.platform === "win32";
|
|
48
49
|
const retryDirectoryErrors = options.retryDirectoryErrors ?? retryRenameErrors;
|
|
50
|
+
const ignoreCleanupErrorAfterSuccess = options.ignoreCleanupErrorAfterSuccess ?? false;
|
|
49
51
|
const retryDelaysMs = options.retryDelaysMs ?? DEFAULT_FILE_SYSTEM_RETRY_DELAYS_MS;
|
|
50
52
|
const renameRetryDelaysMs = retryRenameErrors ? retryDelaysMs : [];
|
|
51
53
|
const directoryRetryDelaysMs = retryDirectoryErrors ? retryDelaysMs : [];
|
|
@@ -71,7 +73,7 @@ export function createAtomicJsonWriter(options: AtomicJsonWriterOptions = {}): (
|
|
|
71
73
|
} catch (cleanupError) {
|
|
72
74
|
// Preserve the write/rename failure: cleanup is best effort and must
|
|
73
75
|
// not hide the error callers need to classify or report.
|
|
74
|
-
if (writeError === undefined) throw cleanupError;
|
|
76
|
+
if (writeError === undefined && !ignoreCleanupErrorAfterSuccess) throw cleanupError;
|
|
75
77
|
}
|
|
76
78
|
}
|
|
77
79
|
};
|
|
@@ -174,18 +174,6 @@ function readSessionEntries(sessionFile: string): BranchSessionEntry[] {
|
|
|
174
174
|
});
|
|
175
175
|
}
|
|
176
176
|
|
|
177
|
-
/** Keep Pi from restoring a forked session into the parent's cwd instead of the child launch cwd. */
|
|
178
|
-
export function alignForkedSessionCwd(sessionFile: string, cwd: string): void {
|
|
179
|
-
const entries = readSessionEntries(sessionFile);
|
|
180
|
-
const header = entries[0];
|
|
181
|
-
if (header?.type !== "session") throw new Error(`Forked session ${sessionFile} does not start with a session header.`);
|
|
182
|
-
const resolvedCwd = path.resolve(cwd);
|
|
183
|
-
const effectiveCwd = fs.realpathSync.native(resolvedCwd);
|
|
184
|
-
if (header.cwd === effectiveCwd) return;
|
|
185
|
-
header.cwd = effectiveCwd;
|
|
186
|
-
fs.writeFileSync(sessionFile, `${entries.map((entry) => JSON.stringify(entry)).join("\n")}\n`, "utf-8");
|
|
187
|
-
}
|
|
188
|
-
|
|
189
177
|
export function createForkContextResolver(
|
|
190
178
|
sessionManager: ForkableSessionManager,
|
|
191
179
|
requestedContext: unknown,
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
|
|
4
|
+
interface SessionEntry {
|
|
5
|
+
type?: string;
|
|
6
|
+
cwd?: string;
|
|
7
|
+
[key: string]: unknown;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/** Keep Pi from restoring a forked session into the parent's cwd instead of the child launch cwd. */
|
|
11
|
+
export function alignForkedSessionCwd(sessionFile: string, cwd: string): void {
|
|
12
|
+
const lines = fs.readFileSync(sessionFile, "utf-8").split("\n").filter((line) => line.trim().length > 0);
|
|
13
|
+
const entries = lines.map((line, index) => {
|
|
14
|
+
try {
|
|
15
|
+
return JSON.parse(line) as SessionEntry;
|
|
16
|
+
} catch (error) {
|
|
17
|
+
const cause = error instanceof Error ? error : new Error(String(error));
|
|
18
|
+
throw new Error(`Unable to inspect forked session ${sessionFile}: invalid JSONL on line ${index + 1}: ${cause.message}`, { cause });
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
const header = entries[0];
|
|
22
|
+
if (header?.type !== "session") throw new Error(`Forked session ${sessionFile} does not start with a session header.`);
|
|
23
|
+
const effectiveCwd = fs.realpathSync.native(path.resolve(cwd));
|
|
24
|
+
if (header.cwd === effectiveCwd) return;
|
|
25
|
+
header.cwd = effectiveCwd;
|
|
26
|
+
fs.writeFileSync(sessionFile, `${entries.map((entry) => JSON.stringify(entry)).join("\n")}\n`, "utf-8");
|
|
27
|
+
}
|
|
@@ -51,6 +51,7 @@ export function projectAgentDefinition(agent: AgentConfig): Record<string, unkno
|
|
|
51
51
|
fast: agent.fast,
|
|
52
52
|
thinking: agent.thinking,
|
|
53
53
|
tools: agent.tools,
|
|
54
|
+
excludeTools: agent.excludeTools,
|
|
54
55
|
allowNestedSubagents: agent.allowNestedSubagents,
|
|
55
56
|
mcpDirectTools: agent.mcpDirectTools,
|
|
56
57
|
extensions: agent.extensions,
|
|
@@ -93,6 +94,7 @@ export interface LaunchBindingInput {
|
|
|
93
94
|
inheritSkills: boolean;
|
|
94
95
|
skills?: string[];
|
|
95
96
|
tools?: string[];
|
|
97
|
+
excludeTools?: string[];
|
|
96
98
|
extensions?: string[];
|
|
97
99
|
subagentOnlyExtensions?: string[];
|
|
98
100
|
mcpDirectTools?: string[];
|
|
@@ -120,6 +122,7 @@ export function projectLaunchBinding(input: LaunchBindingInput): Record<string,
|
|
|
120
122
|
inheritSkills: input.inheritSkills,
|
|
121
123
|
skills: input.skills,
|
|
122
124
|
tools: input.tools,
|
|
125
|
+
excludeTools: input.excludeTools,
|
|
123
126
|
extensions: input.extensions,
|
|
124
127
|
subagentOnlyExtensions: input.subagentOnlyExtensions,
|
|
125
128
|
mcpDirectTools: input.mcpDirectTools,
|
|
@@ -15,6 +15,7 @@ import type { ThinkingLevel } from "./model-info.ts";
|
|
|
15
15
|
import type { GlobalMissionIndexRecord, MissionRecord, MissionStoreConfig } from "../missions/types.ts";
|
|
16
16
|
import type { ExtensionBindings } from "../runs/shared/extension-bindings.ts";
|
|
17
17
|
import type { WorkflowChildPermitContext } from "./workflow-child-permit.ts";
|
|
18
|
+
import type { WatchdogWarningDetails } from "../watchdog/types.ts";
|
|
18
19
|
|
|
19
20
|
// ============================================================================
|
|
20
21
|
// Basic Types
|
|
@@ -382,6 +383,17 @@ export type SubagentResultStatus = "completed" | "failed" | "paused" | "stopped"
|
|
|
382
383
|
export type SubagentOutputState = "present" | "absent" | "unknown";
|
|
383
384
|
export type SubagentRunMode = "single" | "parallel" | "chain" | "workflow";
|
|
384
385
|
export type SubagentResultMode = SubagentRunMode;
|
|
386
|
+
export type WorktreeProvider = "auto" | "native" | "worktrunk";
|
|
387
|
+
export type ManagedWorktreeProvider = Exclude<WorktreeProvider, "auto">;
|
|
388
|
+
|
|
389
|
+
export interface WorktreeNaming {
|
|
390
|
+
requestedBranch: string;
|
|
391
|
+
branchPrefix: string;
|
|
392
|
+
label: string;
|
|
393
|
+
sanitizedPathComponent: string;
|
|
394
|
+
collision?: "branch" | "path" | "both";
|
|
395
|
+
collisionSuffix?: string;
|
|
396
|
+
}
|
|
385
397
|
|
|
386
398
|
export interface ParallelHandoffPatch {
|
|
387
399
|
path: string;
|
|
@@ -425,6 +437,10 @@ export interface ParallelHandoffCleanupTask {
|
|
|
425
437
|
index: number;
|
|
426
438
|
path: string;
|
|
427
439
|
branch: string;
|
|
440
|
+
/** Provider that allocated this worktree; omitted in old manifests. */
|
|
441
|
+
provider?: ManagedWorktreeProvider;
|
|
442
|
+
/** Branch/path naming evidence retained with the cleanup authority. */
|
|
443
|
+
naming?: WorktreeNaming;
|
|
428
444
|
worktreeRemoved: boolean;
|
|
429
445
|
branchRemoved: boolean;
|
|
430
446
|
preserved?: boolean;
|
|
@@ -780,6 +796,7 @@ export interface SteeringRecoveryDescriptor {
|
|
|
780
796
|
thinking?: string;
|
|
781
797
|
thinkingCeiling?: ThinkingLevel;
|
|
782
798
|
tools?: string[];
|
|
799
|
+
excludeTools?: string[];
|
|
783
800
|
allowNestedSubagents?: boolean;
|
|
784
801
|
extensions?: string[];
|
|
785
802
|
subagentOnlyExtensions?: string[];
|
|
@@ -878,13 +895,19 @@ export interface SubagentResultIntercomPayload {
|
|
|
878
895
|
// Progress Tracking
|
|
879
896
|
// ============================================================================
|
|
880
897
|
|
|
898
|
+
export interface ChildWatchdogWarningSummary extends Pick<WatchdogWarningDetails, "severity" | "category" | "summary" | "evidence" | "recommendedAction" | "displayedAt"> {
|
|
899
|
+
/** True when a later assistant turn in the child followed the warning. */
|
|
900
|
+
addressed: boolean;
|
|
901
|
+
stalemate: boolean;
|
|
902
|
+
}
|
|
903
|
+
|
|
881
904
|
export interface ChildWatchdogProgress {
|
|
882
|
-
phase: "idle" | "reviewing" | "
|
|
905
|
+
phase: "idle" | "reviewing" | "stale" | "failed";
|
|
883
906
|
seq: number;
|
|
884
907
|
lastUpdate: number;
|
|
885
|
-
followUpPending: boolean;
|
|
886
908
|
reason?: string;
|
|
887
909
|
timedOut?: boolean;
|
|
910
|
+
warnings?: ChildWatchdogWarningSummary[];
|
|
888
911
|
}
|
|
889
912
|
|
|
890
913
|
export interface AgentProgress {
|
|
@@ -994,6 +1017,7 @@ export interface AcceptanceReviewGate {
|
|
|
994
1017
|
|
|
995
1018
|
export interface AcceptanceConfig {
|
|
996
1019
|
level?: AcceptanceLevel;
|
|
1020
|
+
report?: "on" | "off";
|
|
997
1021
|
criteria?: Array<string | AcceptanceGate>;
|
|
998
1022
|
evidence?: AcceptanceEvidenceKind[];
|
|
999
1023
|
verify?: AcceptanceVerifyCommand[];
|
|
@@ -1200,6 +1224,8 @@ export interface SingleResult {
|
|
|
1200
1224
|
* result row's array position.
|
|
1201
1225
|
*/
|
|
1202
1226
|
index: number;
|
|
1227
|
+
/** Workflow child key that owns this result when returned from workflow details. */
|
|
1228
|
+
workflowKey?: string;
|
|
1203
1229
|
agent: string;
|
|
1204
1230
|
task: string;
|
|
1205
1231
|
/** Human-readable display name for the child's own session (agent + task
|
|
@@ -1305,6 +1331,8 @@ export interface WaitCompletionChild {
|
|
|
1305
1331
|
sessionFile?: string;
|
|
1306
1332
|
success?: boolean;
|
|
1307
1333
|
outputState?: SubagentOutputState;
|
|
1334
|
+
structuredOutput?: unknown;
|
|
1335
|
+
structuredOutputPath?: string;
|
|
1308
1336
|
error?: string;
|
|
1309
1337
|
model?: string;
|
|
1310
1338
|
contextOverflow?: boolean;
|
|
@@ -1342,7 +1370,7 @@ export interface AgentCapabilityRow {
|
|
|
1342
1370
|
restrictionSources?: string[];
|
|
1343
1371
|
aliases?: string[];
|
|
1344
1372
|
runner: { type: "pi" } | { type: "external-cli"; adapter?: string; capabilities: ExternalCliCapabilities } | { type: "external-job"; provider: string; available?: boolean; capabilities: ExternalJobRunnerStatus["capabilities"] };
|
|
1345
|
-
tools: { ambient: boolean; names: string[]; mcpDirectTools: string[]; mutationTools?: string[] };
|
|
1373
|
+
tools: { ambient: boolean; names: string[]; excludeTools?: string[]; mcpDirectTools: string[]; mutationTools?: string[] };
|
|
1346
1374
|
model?: { value?: string; fallbackModels?: string[]; thinking?: string | false };
|
|
1347
1375
|
execution?: { defaultAsync?: boolean; timeoutMs?: number };
|
|
1348
1376
|
output?: { path?: string; mode?: OutputMode };
|
|
@@ -1839,6 +1867,10 @@ export interface AsyncStatus {
|
|
|
1839
1867
|
worktreePath?: string;
|
|
1840
1868
|
/** Display-only branch copied at launch; handoff remains authoritative. */
|
|
1841
1869
|
branch?: string;
|
|
1870
|
+
/** Display-only provisioning provider copied at launch; handoff remains authoritative. */
|
|
1871
|
+
provider?: ManagedWorktreeProvider;
|
|
1872
|
+
/** Display-only naming evidence copied at launch; handoff remains authoritative. */
|
|
1873
|
+
naming?: WorktreeNaming;
|
|
1842
1874
|
/** Child run identity for workflow capacity reconciliation. */
|
|
1843
1875
|
runId?: string;
|
|
1844
1876
|
/** True only when this workflow child owns a detached async runner. */
|
|
@@ -2552,7 +2584,13 @@ export interface ExtensionConfig {
|
|
|
2552
2584
|
worktreeSetupHook?: string;
|
|
2553
2585
|
worktreeSetupHookTimeoutMs?: number;
|
|
2554
2586
|
worktreeBaseDir?: string;
|
|
2555
|
-
/**
|
|
2587
|
+
/** Enable managed worktrees when a launch does not provide an explicit value. */
|
|
2588
|
+
worktree?: boolean;
|
|
2589
|
+
/** Worktree allocator selection. Defaults to auto. */
|
|
2590
|
+
worktreeProvider?: WorktreeProvider;
|
|
2591
|
+
/** Namespace used by managed worktree branches. Defaults to pi-subagents/. */
|
|
2592
|
+
worktreeBranchPrefix?: string;
|
|
2593
|
+
/** Where to store subagent artifact files. Defaults to "session" (the pi session directory, or OS temp when unavailable). Set to "project" for cwd/.pi-subagents. */
|
|
2556
2594
|
artifactDir?: ArtifactDirPreference;
|
|
2557
2595
|
/** Artifact cleanup retention. Set cleanupDays to 0 to disable cleanup. */
|
|
2558
2596
|
artifactConfig?: Pick<ArtifactConfig, "cleanupDays">;
|
|
@@ -237,6 +237,12 @@ export function findLatestSessionFile(sessionDir: string): string | null {
|
|
|
237
237
|
return latest ? latest.path : null;
|
|
238
238
|
}
|
|
239
239
|
|
|
240
|
+
const PI_TURN_TIMING_FOOTER = /(?:\r?\n)*\x1b\[38;2;136;136;136m✻ Turn took [^()\r\n]+ \(Total time [^·\r\n]+ · \d+ turns?\)\x1b\[0m[ \t]*$/u;
|
|
241
|
+
|
|
242
|
+
function stripPiTurnTimingFooter(text: string): string {
|
|
243
|
+
return text.replace(PI_TURN_TIMING_FOOTER, "");
|
|
244
|
+
}
|
|
245
|
+
|
|
240
246
|
export function getFinalOutput(messages: Message[]): string {
|
|
241
247
|
const validTextParts: string[] = [];
|
|
242
248
|
for (let i = messages.length - 1; i >= 0; i--) {
|
|
@@ -246,21 +252,26 @@ export function getFinalOutput(messages: Message[]): string {
|
|
|
246
252
|
|| ("stopReason" in msg && msg.stopReason === "error");
|
|
247
253
|
if (hasAssistantError) continue;
|
|
248
254
|
const messageText = msg.content
|
|
249
|
-
.
|
|
250
|
-
|
|
255
|
+
.flatMap((part) => {
|
|
256
|
+
if (part.type !== "text") return [];
|
|
257
|
+
const text = stripPiTurnTimingFooter(part.text);
|
|
258
|
+
return text.trim().length > 0 ? [text] : [];
|
|
259
|
+
})
|
|
251
260
|
.join("\n");
|
|
252
261
|
for (let j = msg.content.length - 1; j >= 0; j--) {
|
|
253
262
|
const part = msg.content[j];
|
|
254
|
-
if (!part || part.type !== "text"
|
|
255
|
-
|
|
256
|
-
if (
|
|
257
|
-
|
|
263
|
+
if (!part || part.type !== "text") continue;
|
|
264
|
+
const text = stripPiTurnTimingFooter(part.text);
|
|
265
|
+
if (text.trim().length === 0) continue;
|
|
266
|
+
validTextParts.push(text);
|
|
267
|
+
if (/```acceptance[-_]report\s*\n[\s\S]*?```/i.test(text)) return messageText;
|
|
268
|
+
for (const match of text.matchAll(/```(?:json|jsonc|json5)\s*\n([\s\S]*?)```/gi)) {
|
|
258
269
|
const body = match[1] ?? "";
|
|
259
270
|
if (/"(?:criteriaSatisfied|criteria_satisfied)"/.test(body) && /"(?:changedFiles|changed_files|testsAddedOrUpdated|tests_added_or_updated|commandsRun|commands_run|validationOutput|validation_output|residualRisks|residual_risks|noStagedFiles|no_staged_files|diffSummary|diff_summary|reviewFindings|review_findings|manualNotes|manual_notes)"/.test(body)) {
|
|
260
271
|
return messageText;
|
|
261
272
|
}
|
|
262
273
|
}
|
|
263
|
-
if (/ACCEPTANCE_REPORT\s*:/i.test(
|
|
274
|
+
if (/ACCEPTANCE_REPORT\s*:/i.test(text)) return messageText;
|
|
264
275
|
}
|
|
265
276
|
}
|
|
266
277
|
return validTextParts[0] ?? "";
|
|
@@ -417,7 +417,7 @@ function detailsFromSessionEntry(entry: unknown): Details | undefined {
|
|
|
417
417
|
}
|
|
418
418
|
if (record.type !== "message" || !record.message || typeof record.message !== "object") return undefined;
|
|
419
419
|
const message = record.message as { role?: unknown; toolName?: unknown; details?: unknown };
|
|
420
|
-
if (message.role !== "toolResult" || (message.toolName !== "subagent" && message.toolName !== "bg_wait"
|
|
420
|
+
if (message.role !== "toolResult" || (message.toolName !== "subagent" && message.toolName !== "bg_wait")) return undefined;
|
|
421
421
|
return isSubagentDetails(message.details) ? message.details : undefined;
|
|
422
422
|
}
|
|
423
423
|
|
|
@@ -2,7 +2,6 @@ import * as fs from "node:fs";
|
|
|
2
2
|
import * as path from "node:path";
|
|
3
3
|
import type { ExtensionAPI, ExtensionContext } from "@selesai/code";
|
|
4
4
|
import {
|
|
5
|
-
agentHasFrontmatterField,
|
|
6
5
|
discoverAgentsAll,
|
|
7
6
|
EXTRA_AGENT_DIRS_ENV,
|
|
8
7
|
frontmatterNameForConfig,
|
|
@@ -97,6 +96,7 @@ function buildBuiltinBase(agent: AgentConfig): BuiltinAgentOverrideBase {
|
|
|
97
96
|
systemPrompt: agent.systemPrompt,
|
|
98
97
|
...(agent.skills !== undefined ? { skills: [...agent.skills] } : {}),
|
|
99
98
|
...(agent.tools !== undefined ? { tools: [...agent.tools] } : {}),
|
|
99
|
+
...(agent.excludeTools !== undefined ? { excludeTools: [...agent.excludeTools] } : {}),
|
|
100
100
|
...(agent.mcpDirectTools !== undefined ? { mcpDirectTools: [...agent.mcpDirectTools] } : {}),
|
|
101
101
|
...(agent.subagentOnlyExtensions !== undefined ? { subagentOnlyExtensions: [...agent.subagentOnlyExtensions] } : {}),
|
|
102
102
|
...(agent.mutationTools !== undefined ? { mutationTools: [...agent.mutationTools] } : {}),
|
|
@@ -115,18 +115,15 @@ type AgentSelection =
|
|
|
115
115
|
|
|
116
116
|
function savesThroughSettings(agent: AgentConfig, field: EditableOverrideField): boolean {
|
|
117
117
|
if (agent.source === "builtin") return true;
|
|
118
|
-
if (agent.source === "package")
|
|
119
|
-
if (field === "systemPrompt") return false;
|
|
120
|
-
return !agentHasFrontmatterField(agent, field);
|
|
121
|
-
}
|
|
118
|
+
if (agent.source === "package") return true;
|
|
122
119
|
if (!agent.override) return false;
|
|
120
|
+
if (agent.override.fields?.includes(field) === true) return true;
|
|
123
121
|
// A lower-scope override can flow into a higher-scope custom agent with the
|
|
124
122
|
// same name. Persist that agent's edits in its own frontmatter instead of
|
|
125
123
|
// rewriting the shared lower-scope override used by another agent.
|
|
126
124
|
if (agent.source !== agent.override.scope) return false;
|
|
127
|
-
//
|
|
128
|
-
//
|
|
129
|
-
// not redirect edits to an unrelated frontmatter-owned field.
|
|
125
|
+
// Compare the effective value with the pre-override base so an override on one
|
|
126
|
+
// field does not redirect edits to an unrelated frontmatter-owned field.
|
|
130
127
|
return agent[field] !== agent.override.base[field];
|
|
131
128
|
}
|
|
132
129
|
|
|
@@ -188,6 +185,7 @@ function metadataFor(agent: AgentConfig): string {
|
|
|
188
185
|
if (agent.fallbackModels?.length) lines.push(`Fallback models: ${agent.fallbackModels.join(", ")}`);
|
|
189
186
|
if (agent.thinking !== undefined) lines.push(`Thinking: ${agent.thinking === false ? "off" : agent.thinking}`);
|
|
190
187
|
if (tools.length) lines.push(`Tools: ${tools.join(", ")}`);
|
|
188
|
+
if (agent.excludeTools?.length) lines.push(`Excluded tools: ${agent.excludeTools.join(", ")}`);
|
|
191
189
|
if (agent.skills?.length) lines.push(`Skills: ${agent.skills.join(", ")}`);
|
|
192
190
|
lines.push(`System prompt mode: ${agent.systemPromptMode}`);
|
|
193
191
|
lines.push(`Inherit project context: ${agent.inheritProjectContext ? "true" : "false"}`);
|
|
@@ -254,10 +252,12 @@ async function chooseThinking(ctx: ExtensionContext, agent: AgentConfig): Promis
|
|
|
254
252
|
}
|
|
255
253
|
|
|
256
254
|
async function chooseOverrideScope(ctx: ExtensionContext, agent: AgentConfig): Promise<"user" | "project" | undefined> {
|
|
257
|
-
if (agent.override?.scope) return agent.override.scope;
|
|
258
255
|
const d = discoverAgentsAll(ctx.cwd);
|
|
256
|
+
if (agent.override?.scope) {
|
|
257
|
+
return agent.source === "project" && agent.override.scope === "user" && d.projectSettingsPath ? "project" : agent.override.scope;
|
|
258
|
+
}
|
|
259
259
|
if (!d.projectSettingsPath || !ctx.hasUI) return "user";
|
|
260
|
-
const choice = await ctx.ui.select(`Save
|
|
260
|
+
const choice = await ctx.ui.select(`Save subagent override for ${agent.name}`, ["user", "project"]);
|
|
261
261
|
return choice === "user" || choice === "project" ? choice : undefined;
|
|
262
262
|
}
|
|
263
263
|
|
|
@@ -269,6 +269,18 @@ function persistSettingsField(
|
|
|
269
269
|
value: string | undefined,
|
|
270
270
|
): { filePath: string; overridden: boolean } {
|
|
271
271
|
const base = agent.override?.base ?? buildBuiltinBase(agent);
|
|
272
|
+
const shadowsLowerScope = scope === "project" && agent.override?.fieldScopes?.[field]?.includes("user") === true;
|
|
273
|
+
if (shadowsLowerScope && (value === undefined || value === base[field])) {
|
|
274
|
+
const filePath = field === "model"
|
|
275
|
+
? mergeBuiltinAgentOverride(ctx.cwd, agent.name, scope, { model: value ?? base.model ?? false })
|
|
276
|
+
: field === "thinking"
|
|
277
|
+
? mergeBuiltinAgentOverride(ctx.cwd, agent.name, scope, { thinking: value ?? (base.thinking === false ? "off" : base.thinking) ?? false })
|
|
278
|
+
: mergeBuiltinAgentOverride(ctx.cwd, agent.name, scope, { systemPrompt: value ?? base.systemPrompt });
|
|
279
|
+
return {
|
|
280
|
+
filePath,
|
|
281
|
+
overridden: true,
|
|
282
|
+
};
|
|
283
|
+
}
|
|
272
284
|
if (value === undefined || value === base[field]) {
|
|
273
285
|
return {
|
|
274
286
|
filePath: removeBuiltinAgentOverrideFields(ctx.cwd, agent.name, scope, [field]).path,
|
|
@@ -356,13 +368,15 @@ async function saveAgentSystemPrompt(ctx: ExtensionContext, agent: AgentConfig,
|
|
|
356
368
|
}
|
|
357
369
|
|
|
358
370
|
async function editSystemPrompt(ctx: ExtensionContext, agent: AgentConfig): Promise<string | null> {
|
|
359
|
-
|
|
371
|
+
const saveThroughSettings = savesThroughSettings(agent, "systemPrompt");
|
|
372
|
+
if (!saveThroughSettings) {
|
|
360
373
|
const readOnlyMessage = readOnlyAgentMessage(agent, "systemPrompt");
|
|
361
374
|
if (readOnlyMessage) return readOnlyMessage;
|
|
362
375
|
}
|
|
363
376
|
const edited = await ctx.ui.editor(`Edit '${agent.name}' system prompt`, agent.systemPrompt ?? "");
|
|
364
377
|
if (edited === undefined) return null;
|
|
365
|
-
|
|
378
|
+
const projectOwnsLowerScopeOverride = agent.source === "project" && agent.override?.scope === "user" && discoverAgentsAll(ctx.cwd).projectSettingsPath;
|
|
379
|
+
if (edited.replace(/\s+$/, "") === (agent.systemPrompt ?? "").replace(/\s+$/, "") && !(saveThroughSettings && projectOwnsLowerScopeOverride)) {
|
|
366
380
|
return `System prompt for '${agent.name}' left unchanged.`;
|
|
367
381
|
}
|
|
368
382
|
return saveAgentSystemPrompt(ctx, agent, edited);
|
|
@@ -8,6 +8,7 @@ import { contextModeLabel } from "../runs/shared/context-mode.ts";
|
|
|
8
8
|
import { formatWorkflowJsonPreview } from "../workflows/scripted-workflow.ts";
|
|
9
9
|
import { hostStepReportName, hostStepVerdictLabel } from "../runs/shared/host-step-status.ts";
|
|
10
10
|
import { isStaleExtensionContextError } from "../shared/extension-context.ts";
|
|
11
|
+
import { formatWorkflowChecklistBottleneck, formatWorkflowChecklistPhase, formatWorkflowChecklistSummary, projectWorkflowChecklist, type WorkflowChecklistPhase, type WorkflowChecklistProjection } from "../workflows/workflow-checklist.ts";
|
|
11
12
|
|
|
12
13
|
export const FLEET_STATUS_WIDGET_KEY = "subagent-fleet-status";
|
|
13
14
|
|
|
@@ -35,6 +36,7 @@ type FleetStatusEntry = {
|
|
|
35
36
|
projectPane?: HerdrProjectPaneSnapshot;
|
|
36
37
|
nestedChildren?: NestedRunSummary[];
|
|
37
38
|
workflowRows?: AsyncStatusWorkflowRow[];
|
|
39
|
+
workflowChecklist?: WorkflowChecklistProjection;
|
|
38
40
|
};
|
|
39
41
|
|
|
40
42
|
type FleetNestedRow = {
|
|
@@ -50,6 +52,7 @@ type FleetNestedRow = {
|
|
|
50
52
|
type FleetTreeRow =
|
|
51
53
|
| { kind: "owner"; entry: FleetStatusEntry }
|
|
52
54
|
| { kind: "child"; entry: FleetStatusEntry; last: boolean }
|
|
55
|
+
| { kind: "workflow-phase"; ownerKey: string; phase: WorkflowChecklistPhase; last: boolean }
|
|
53
56
|
| { kind: "workflow"; ownerKey: string; row: AsyncStatusWorkflowRow; last: boolean }
|
|
54
57
|
| { kind: "nested"; ownerKey: string; row: FleetNestedRow; last: boolean };
|
|
55
58
|
|
|
@@ -117,6 +120,18 @@ function visibleWorkflowRows(rows: AsyncStatusWorkflowRow[] | undefined, visible
|
|
|
117
120
|
return [{ name: `… +${rows.length - visible.length} hidden workflow steps`, state: "complete", overflow: rows.length - visible.length }, ...visible];
|
|
118
121
|
}
|
|
119
122
|
|
|
123
|
+
function visibleWorkflowPhases(checklist: WorkflowChecklistProjection | undefined, visibleLimit: number): WorkflowChecklistPhase[] {
|
|
124
|
+
const phases = checklist?.phases ?? [];
|
|
125
|
+
if (phases.length <= visibleLimit) return phases;
|
|
126
|
+
const selected = new Set<number>();
|
|
127
|
+
for (const [index, phase] of phases.entries()) {
|
|
128
|
+
if (phase.state !== "complete") selected.add(index);
|
|
129
|
+
if (selected.size >= visibleLimit) break;
|
|
130
|
+
}
|
|
131
|
+
for (let index = phases.length - 1; index >= 0 && selected.size < visibleLimit; index--) selected.add(index);
|
|
132
|
+
return [...selected].sort((left, right) => left - right).map((index) => phases[index]!);
|
|
133
|
+
}
|
|
134
|
+
|
|
120
135
|
function isWorkflowRowTerminal(row: AsyncStatusWorkflowRow): boolean {
|
|
121
136
|
if (row.kind) return row.state === "done" || row.state === "cancelled" || row.state === "error";
|
|
122
137
|
return row.state === "complete" || row.state === "completed";
|
|
@@ -224,10 +239,11 @@ function fleetTreeRows(entries: FleetStatusEntry[]): FleetTreeRow[] {
|
|
|
224
239
|
if (entry.parentKey && entryKeys.has(entry.parentKey)) continue;
|
|
225
240
|
rows.push({ kind: "owner", entry });
|
|
226
241
|
const attached = childrenByParent.get(entry.key) ?? [];
|
|
242
|
+
const workflowPhases = visibleWorkflowPhases(entry.workflowChecklist, attached.length > 0 ? 2 : 4);
|
|
227
243
|
const workflowRows = visibleWorkflowRows(entry.workflowRows, attached.length > 0 ? 2 : 4);
|
|
228
244
|
for (const [index, child] of attached.entries()) {
|
|
229
245
|
const nested = nestedFleetRows(child.nestedChildren, 3);
|
|
230
|
-
const laterRows = index < attached.length - 1 || workflowRows.length > 0 || Boolean(entry.nestedChildren?.length);
|
|
246
|
+
const laterRows = index < attached.length - 1 || workflowPhases.length > 0 || workflowRows.length > 0 || Boolean(entry.nestedChildren?.length);
|
|
231
247
|
rows.push({ kind: "child", entry: child, last: !laterRows && nested.length === 0 });
|
|
232
248
|
for (const [nestedIndex, row] of nested.entries()) rows.push({
|
|
233
249
|
kind: "nested",
|
|
@@ -236,6 +252,12 @@ function fleetTreeRows(entries: FleetStatusEntry[]): FleetTreeRow[] {
|
|
|
236
252
|
last: nestedIndex === nested.length - 1 && !laterRows,
|
|
237
253
|
});
|
|
238
254
|
}
|
|
255
|
+
for (const [index, phase] of workflowPhases.entries()) rows.push({
|
|
256
|
+
kind: "workflow-phase",
|
|
257
|
+
ownerKey: entry.key,
|
|
258
|
+
phase,
|
|
259
|
+
last: index === workflowPhases.length - 1 && workflowRows.length === 0 && !entry.nestedChildren?.length,
|
|
260
|
+
});
|
|
239
261
|
for (const [index, row] of workflowRows.entries()) rows.push({ kind: "workflow", ownerKey: entry.key, row, last: index === workflowRows.length - 1 && !entry.nestedChildren?.length });
|
|
240
262
|
const nested = nestedFleetRows(entry.nestedChildren, attached.length > 0 ? 3 : 4);
|
|
241
263
|
for (const [index, row] of nested.entries()) rows.push({ kind: "nested", ownerKey: entry.key, row, last: index === nested.length - 1 });
|
|
@@ -364,6 +386,14 @@ export function collectFleetStatusEntries(state: SubagentState): FleetStatusEntr
|
|
|
364
386
|
const latestEmit = job.workflow?.emits?.length ? formatWorkflowJsonPreview(job.workflow.emits.at(-1), 120) : undefined;
|
|
365
387
|
const workflowSteps = workflowStepsWithoutMaterializedChildren(job.steps, materializedChildrenByWorkflow.get(`async:${job.asyncId}`));
|
|
366
388
|
const workflowRows = projectAsyncWorkflowRows(workflowSteps, job.workflowGraph ?? job.hostSteps, job.preflight);
|
|
389
|
+
const workflowChecklist = projectWorkflowChecklist({
|
|
390
|
+
graph: job.workflowGraph,
|
|
391
|
+
steps: job.steps,
|
|
392
|
+
hostSteps: job.hostSteps,
|
|
393
|
+
preflight: job.preflight,
|
|
394
|
+
trace: job.workflow?.trace,
|
|
395
|
+
now: job.updatedAt ?? Date.now(),
|
|
396
|
+
});
|
|
367
397
|
entries.push({
|
|
368
398
|
key: `async:${job.asyncId}`,
|
|
369
399
|
...(linkedParentKey ? { parentKey: linkedParentKey } : {}),
|
|
@@ -375,6 +405,7 @@ export function collectFleetStatusEntries(state: SubagentState): FleetStatusEntr
|
|
|
375
405
|
...(job.totalTokens?.window !== undefined ? { window: job.totalTokens.window } : {}),
|
|
376
406
|
state: job.status,
|
|
377
407
|
...(workflowRows.length ? { workflowRows } : {}),
|
|
408
|
+
...(workflowChecklist.total ? { workflowChecklist } : {}),
|
|
378
409
|
...(job.nestedChildren?.length ? { nestedChildren: job.nestedChildren } : {}),
|
|
379
410
|
});
|
|
380
411
|
continue;
|
|
@@ -668,6 +699,8 @@ export class SubagentFleetStatus {
|
|
|
668
699
|
lines.push(this.renderEntry(rosterIndex, selectedIndex, row.entry, width, theme, row.kind === "child" ? (row.last ? "└─" : "├─") : undefined));
|
|
669
700
|
} else if (row.kind === "workflow") {
|
|
670
701
|
lines.push(this.renderWorkflowRow(row.row, row.last, width, theme));
|
|
702
|
+
} else if (row.kind === "workflow-phase") {
|
|
703
|
+
lines.push(this.renderWorkflowPhaseRow(row.phase, row.last, width, theme));
|
|
671
704
|
} else {
|
|
672
705
|
lines.push(this.renderNestedRow(row.row, row.last, width, theme));
|
|
673
706
|
}
|
|
@@ -691,7 +724,10 @@ export class SubagentFleetStatus {
|
|
|
691
724
|
private renderEntry(rosterIndex: number, selectedIndex: number, entry: FleetStatusEntry, width: number, theme: Theme, branch?: string): string {
|
|
692
725
|
const agent = entry.modelThinking ? `${entry.agent} (${entry.modelThinking})` : entry.agent;
|
|
693
726
|
const prefix = branch ? ` ${branch}` : " ";
|
|
694
|
-
const
|
|
727
|
+
const checklist = entry.workflowWrapper && entry.workflowChecklist
|
|
728
|
+
? ` · checklist ${formatWorkflowChecklistSummary(entry.workflowChecklist)}${entry.workflowChecklist.bottleneck ? ` · bottleneck ${formatWorkflowChecklistBottleneck(entry.workflowChecklist.bottleneck)}` : ""}`
|
|
729
|
+
: "";
|
|
730
|
+
const left = `${prefix} ${this.bullet(rosterIndex, selectedIndex, theme)} ${theme.fg("muted", agent)} · ${entry.state}${checklist}`;
|
|
695
731
|
const elapsed = Date.now() - entry.startedAt;
|
|
696
732
|
const rightText = entry.projectPane
|
|
697
733
|
? `${entry.projectPane.summary ?? "—"} · ${formatFleetElapsed(Date.now() - entry.projectPane.refreshedAt)} ago`
|
|
@@ -730,6 +766,20 @@ export class SubagentFleetStatus {
|
|
|
730
766
|
return theme.fg("warning", state);
|
|
731
767
|
}
|
|
732
768
|
|
|
769
|
+
private renderWorkflowPhaseRow(phase: WorkflowChecklistPhase, last: boolean, width: number, theme: Theme): string {
|
|
770
|
+
const marker = last ? "└─" : "├─";
|
|
771
|
+
const glyph = phase.state === "complete"
|
|
772
|
+
? theme.fg("success", "✓")
|
|
773
|
+
: phase.state === "running"
|
|
774
|
+
? theme.fg("accent", "●")
|
|
775
|
+
: phase.state === "blocked" || phase.state === "failed"
|
|
776
|
+
? theme.fg("error", phase.state === "blocked" ? "!" : "✗")
|
|
777
|
+
: phase.state === "queued"
|
|
778
|
+
? theme.fg("muted", "◦")
|
|
779
|
+
: theme.fg("warning", "■");
|
|
780
|
+
return truncateToWidth(` ${marker} ${glyph} ${theme.fg("muted", formatWorkflowChecklistPhase(phase))}`, width);
|
|
781
|
+
}
|
|
782
|
+
|
|
733
783
|
private renderWorkflowRow(row: AsyncStatusWorkflowRow, last: boolean, width: number, theme: Theme): string {
|
|
734
784
|
const marker = last ? "└─" : "├─";
|
|
735
785
|
const indent = " ";
|
|
@@ -809,6 +859,15 @@ export class SubagentFleetStatus {
|
|
|
809
859
|
entry.external,
|
|
810
860
|
Math.round((now - entry.startedAt) / 1000),
|
|
811
861
|
entry.tokens,
|
|
862
|
+
entry.workflowChecklist ? [
|
|
863
|
+
entry.workflowChecklist.total,
|
|
864
|
+
entry.workflowChecklist.done,
|
|
865
|
+
entry.workflowChecklist.running,
|
|
866
|
+
entry.workflowChecklist.queued,
|
|
867
|
+
entry.workflowChecklist.blocked,
|
|
868
|
+
entry.workflowChecklist.failed,
|
|
869
|
+
entry.workflowChecklist.phases.map((phase) => [phase.key, phase.state, phase.done, phase.total, phase.running, phase.queued, phase.blocked, phase.failed, phase.items.map((item) => [item.key, item.state, item.currentTool, item.currentPath, item.durationMs, item.toolCount, item.error])]),
|
|
870
|
+
] : undefined,
|
|
812
871
|
visibleWorkflowRows(entry.workflowRows, entry.parentKey ? 2 : 4).map((row) => [
|
|
813
872
|
row.kind,
|
|
814
873
|
row.name,
|
|
@@ -42,7 +42,7 @@ export const DEFAULT_FLEET_KEYBINDINGS: Record<FleetKeybindingAction, string[]>
|
|
|
42
42
|
pageDown: ["pageDown"],
|
|
43
43
|
refresh: ["r", "R"],
|
|
44
44
|
steer: ["s"],
|
|
45
|
-
inspect: ["H"],
|
|
45
|
+
inspect: ["return", "H"],
|
|
46
46
|
stop: ["D"],
|
|
47
47
|
toggleTools: ["x", "X", "ctrl+o"],
|
|
48
48
|
};
|
|
@@ -952,6 +952,12 @@ export class SubagentFleetComponent implements Component {
|
|
|
952
952
|
return { runId: parent.asyncId, asyncDir: parent.asyncDir };
|
|
953
953
|
}
|
|
954
954
|
|
|
955
|
+
private inspectSelectedHerdr(): void {
|
|
956
|
+
const target = this.selectedHerdrInspectAction();
|
|
957
|
+
if ("reason" in target || !this.options.actions?.inspect) this.setActionNotice({ text: "reason" in target ? target.reason : "Herdr inspector controls are unavailable in this context.", isError: true });
|
|
958
|
+
else this.runAction(() => this.options.actions!.inspect!(target));
|
|
959
|
+
}
|
|
960
|
+
|
|
955
961
|
private actionLines(): string[] {
|
|
956
962
|
const lines: string[] = [];
|
|
957
963
|
if (this.actionBusy) lines.push(this.theme.fg("accent", "Action pending..."));
|
|
@@ -1179,12 +1185,6 @@ export class SubagentFleetComponent implements Component {
|
|
|
1179
1185
|
}
|
|
1180
1186
|
return;
|
|
1181
1187
|
}
|
|
1182
|
-
if (matchesFleetAction(data, this.keybindings, "inspect")) {
|
|
1183
|
-
const target = this.selectedHerdrInspectAction();
|
|
1184
|
-
if ("reason" in target || !this.options.actions?.inspect) this.setActionNotice({ text: "reason" in target ? target.reason : "Herdr inspector controls are unavailable in this context.", isError: true });
|
|
1185
|
-
else this.runAction(() => this.options.actions!.inspect!(target));
|
|
1186
|
-
return;
|
|
1187
|
-
}
|
|
1188
1188
|
if (matchesFleetAction(data, this.keybindings, "stop")) {
|
|
1189
1189
|
const target = this.selectedAsyncAction();
|
|
1190
1190
|
if ("reason" in target || !this.options.actions) this.setActionNotice({ text: "reason" in target ? target.reason : "Fleet controls are unavailable in this context.", isError: true });
|
|
@@ -1201,6 +1201,11 @@ export class SubagentFleetComponent implements Component {
|
|
|
1201
1201
|
this.expandedTools = !this.expandedTools;
|
|
1202
1202
|
this.transcriptCache = undefined;
|
|
1203
1203
|
this.tui.requestRender();
|
|
1204
|
+
return;
|
|
1205
|
+
}
|
|
1206
|
+
if (matchesFleetAction(data, this.keybindings, "inspect")) {
|
|
1207
|
+
this.inspectSelectedHerdr();
|
|
1208
|
+
return;
|
|
1204
1209
|
}
|
|
1205
1210
|
}
|
|
1206
1211
|
|