pi-subagents 0.21.1 → 0.21.2
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 +19 -0
- package/README.md +35 -16
- package/agents/context-builder.md +6 -3
- package/package.json +3 -4
- package/prompts/parallel-context-build.md +53 -0
- package/prompts/parallel-handoff-plan.md +59 -0
- package/prompts/parallel-review.md +4 -0
- package/skills/pi-subagents/SKILL.md +70 -4
- package/{agent-management.ts → src/agents/agent-management.ts} +1 -1
- package/{agents.ts → src/agents/agents.ts} +1 -1
- package/{doctor.ts → src/extension/doctor.ts} +5 -5
- package/{index.ts → src/extension/index.ts} +18 -17
- package/{schemas.ts → src/extension/schemas.ts} +25 -39
- package/{intercom-bridge.ts → src/intercom/intercom-bridge.ts} +2 -2
- package/{result-intercom.ts → src/intercom/result-intercom.ts} +33 -5
- package/{agent-manager-chain-detail.ts → src/manager-ui/agent-manager-chain-detail.ts} +3 -3
- package/{agent-manager-detail.ts → src/manager-ui/agent-manager-detail.ts} +7 -7
- package/{agent-manager-edit.ts → src/manager-ui/agent-manager-edit.ts} +4 -4
- package/{agent-manager-list.ts → src/manager-ui/agent-manager-list.ts} +2 -2
- package/{agent-manager-parallel.ts → src/manager-ui/agent-manager-parallel.ts} +3 -3
- package/{agent-manager.ts → src/manager-ui/agent-manager.ts} +9 -9
- package/{async-execution.ts → src/runs/background/async-execution.ts} +11 -11
- package/{async-job-tracker.ts → src/runs/background/async-job-tracker.ts} +29 -6
- package/src/runs/background/async-resume.ts +305 -0
- package/{async-status.ts → src/runs/background/async-status.ts} +14 -12
- package/{notify.ts → src/runs/background/notify.ts} +1 -1
- package/{result-watcher.ts → src/runs/background/result-watcher.ts} +3 -3
- package/{run-status.ts → src/runs/background/run-status.ts} +63 -28
- package/src/runs/background/stale-run-reconciler.ts +275 -0
- package/{subagent-runner.ts → src/runs/background/subagent-runner.ts} +38 -58
- package/{chain-clarify.ts → src/runs/foreground/chain-clarify.ts} +7 -7
- package/{chain-execution.ts → src/runs/foreground/chain-execution.ts} +10 -10
- package/{execution.ts → src/runs/foreground/execution.ts} +24 -28
- package/{subagent-executor.ts → src/runs/foreground/subagent-executor.ts} +155 -24
- package/{long-running-guard.ts → src/runs/shared/long-running-guard.ts} +3 -3
- package/{model-fallback.ts → src/runs/shared/model-fallback.ts} +1 -1
- package/{subagent-control.ts → src/runs/shared/subagent-control.ts} +4 -8
- package/src/shared/atomic-json.ts +16 -0
- package/{settings.ts → src/shared/settings.ts} +21 -14
- package/{types.ts → src/shared/types.ts} +5 -2
- package/{utils.ts → src/shared/utils.ts} +1 -15
- package/{slash-bridge.ts → src/slash/slash-bridge.ts} +2 -2
- package/{slash-commands.ts → src/slash/slash-commands.ts} +7 -7
- package/{slash-live-state.ts → src/slash/slash-live-state.ts} +2 -2
- package/{render.ts → src/tui/render.ts} +3 -3
- package/{subagents-status.ts → src/tui/subagents-status.ts} +34 -14
- /package/{agent-scope.ts → src/agents/agent-scope.ts} +0 -0
- /package/{agent-selection.ts → src/agents/agent-selection.ts} +0 -0
- /package/{agent-serializer.ts → src/agents/agent-serializer.ts} +0 -0
- /package/{agent-templates.ts → src/agents/agent-templates.ts} +0 -0
- /package/{chain-serializer.ts → src/agents/chain-serializer.ts} +0 -0
- /package/{frontmatter.ts → src/agents/frontmatter.ts} +0 -0
- /package/{skills.ts → src/agents/skills.ts} +0 -0
- /package/{completion-dedupe.ts → src/runs/background/completion-dedupe.ts} +0 -0
- /package/{top-level-async.ts → src/runs/background/top-level-async.ts} +0 -0
- /package/{completion-guard.ts → src/runs/shared/completion-guard.ts} +0 -0
- /package/{parallel-utils.ts → src/runs/shared/parallel-utils.ts} +0 -0
- /package/{pi-args.ts → src/runs/shared/pi-args.ts} +0 -0
- /package/{pi-spawn.ts → src/runs/shared/pi-spawn.ts} +0 -0
- /package/{run-history.ts → src/runs/shared/run-history.ts} +0 -0
- /package/{single-output.ts → src/runs/shared/single-output.ts} +0 -0
- /package/{subagent-prompt-runtime.ts → src/runs/shared/subagent-prompt-runtime.ts} +0 -0
- /package/{worktree.ts → src/runs/shared/worktree.ts} +0 -0
- /package/{artifacts.ts → src/shared/artifacts.ts} +0 -0
- /package/{file-coalescer.ts → src/shared/file-coalescer.ts} +0 -0
- /package/{fork-context.ts → src/shared/fork-context.ts} +0 -0
- /package/{formatters.ts → src/shared/formatters.ts} +0 -0
- /package/{jsonl-writer.ts → src/shared/jsonl-writer.ts} +0 -0
- /package/{post-exit-stdio-guard.ts → src/shared/post-exit-stdio-guard.ts} +0 -0
- /package/{session-tokens.ts → src/shared/session-tokens.ts} +0 -0
- /package/{prompt-template-bridge.ts → src/slash/prompt-template-bridge.ts} +0 -0
- /package/{render-helpers.ts → src/tui/render-helpers.ts} +0 -0
- /package/{text-editor.ts → src/tui/text-editor.ts} +0 -0
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import { Type } from "typebox";
|
|
6
|
+
import { SUBAGENT_ACTIONS } from "../shared/types.ts";
|
|
6
7
|
|
|
7
8
|
const SkillOverride = Type.Unsafe({
|
|
8
|
-
type: ["string", "array", "boolean"],
|
|
9
9
|
anyOf: [
|
|
10
10
|
{ type: "array", items: { type: "string" } },
|
|
11
11
|
{ type: "boolean" },
|
|
@@ -15,12 +15,14 @@ const SkillOverride = Type.Unsafe({
|
|
|
15
15
|
});
|
|
16
16
|
|
|
17
17
|
const OutputOverride = Type.Unsafe({
|
|
18
|
-
|
|
18
|
+
anyOf: [
|
|
19
|
+
{ type: "string" },
|
|
20
|
+
{ type: "boolean" },
|
|
21
|
+
],
|
|
19
22
|
description: "Output filename/path (string), or false to disable file output",
|
|
20
23
|
});
|
|
21
24
|
|
|
22
25
|
const ReadsOverride = Type.Unsafe({
|
|
23
|
-
type: ["array", "boolean"],
|
|
24
26
|
anyOf: [
|
|
25
27
|
{ type: "array", items: { type: "string" } },
|
|
26
28
|
{ type: "boolean" },
|
|
@@ -40,20 +42,6 @@ const TaskItem = Type.Object({
|
|
|
40
42
|
skill: Type.Optional(SkillOverride),
|
|
41
43
|
});
|
|
42
44
|
|
|
43
|
-
// Sequential chain step (single agent)
|
|
44
|
-
const SequentialStepSchema = Type.Object({
|
|
45
|
-
agent: Type.String(),
|
|
46
|
-
task: Type.Optional(Type.String({
|
|
47
|
-
description: "Task template with variables: {task}=original request, {previous}=prior step's text response, {chain_dir}=shared folder. Required for first step, defaults to '{previous}' for subsequent steps."
|
|
48
|
-
})),
|
|
49
|
-
cwd: Type.Optional(Type.String()),
|
|
50
|
-
output: Type.Optional(OutputOverride),
|
|
51
|
-
reads: Type.Optional(ReadsOverride),
|
|
52
|
-
progress: Type.Optional(Type.Boolean({ description: "Enable progress.md tracking in {chain_dir}" })),
|
|
53
|
-
skill: Type.Optional(SkillOverride),
|
|
54
|
-
model: Type.Optional(Type.String({ description: "Override model for this step" })),
|
|
55
|
-
});
|
|
56
|
-
|
|
57
45
|
// Parallel task item (within a parallel step)
|
|
58
46
|
const ParallelTaskSchema = Type.Object({
|
|
59
47
|
agent: Type.String(),
|
|
@@ -67,17 +55,7 @@ const ParallelTaskSchema = Type.Object({
|
|
|
67
55
|
model: Type.Optional(Type.String({ description: "Override model for this task" })),
|
|
68
56
|
});
|
|
69
57
|
|
|
70
|
-
//
|
|
71
|
-
const ParallelStepSchema = Type.Object({
|
|
72
|
-
parallel: Type.Array(ParallelTaskSchema, { minItems: 1, description: "Tasks to run in parallel" }),
|
|
73
|
-
concurrency: Type.Optional(Type.Number({ description: "Max concurrent tasks (default: 4)" })),
|
|
74
|
-
failFast: Type.Optional(Type.Boolean({ description: "Stop on first failure (default: false)" })),
|
|
75
|
-
worktree: Type.Optional(Type.Boolean({
|
|
76
|
-
description: "Create isolated git worktrees for each parallel task."
|
|
77
|
-
})),
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
// Flattened so providers that reject anyOf/oneOf can still accept either sequential or parallel steps.
|
|
58
|
+
// Flattened so chain steps do not need an object-shape anyOf/oneOf union.
|
|
81
59
|
const ChainItem = Type.Object({
|
|
82
60
|
agent: Type.Optional(Type.String({ description: "Sequential step agent name" })),
|
|
83
61
|
task: Type.Optional(Type.String({
|
|
@@ -100,9 +78,9 @@ const ChainItem = Type.Object({
|
|
|
100
78
|
const ControlOverrides = Type.Object({
|
|
101
79
|
enabled: Type.Optional(Type.Boolean({ description: "Enable/disable subagent control attention tracking for this run" })),
|
|
102
80
|
needsAttentionAfterMs: Type.Optional(Type.Integer({ minimum: 1, description: "No-observed-activity window before a run needs attention" })),
|
|
103
|
-
activeNoticeAfterMs: Type.Optional(Type.Integer({ minimum: 1, description: "Active-long-running notice threshold by elapsed ms (default:
|
|
104
|
-
activeNoticeAfterTurns: Type.Optional(Type.Integer({ minimum: 1, description: "
|
|
105
|
-
activeNoticeAfterTokens: Type.Optional(Type.Integer({ minimum: 1, description: "
|
|
81
|
+
activeNoticeAfterMs: Type.Optional(Type.Integer({ minimum: 1, description: "Active-long-running notice threshold by elapsed ms (default: 240000)" })),
|
|
82
|
+
activeNoticeAfterTurns: Type.Optional(Type.Integer({ minimum: 1, description: "Optional active-long-running notice threshold by assistant turns (disabled by default)" })),
|
|
83
|
+
activeNoticeAfterTokens: Type.Optional(Type.Integer({ minimum: 1, description: "Optional active-long-running notice threshold by total tokens (disabled by default)" })),
|
|
106
84
|
failedToolAttemptsBeforeAttention: Type.Optional(Type.Integer({ minimum: 1, description: "Consecutive mutating-tool failures before escalating to needs_attention (default: 3)" })),
|
|
107
85
|
notifyOn: Type.Optional(Type.Array(Type.String({ enum: ["active_long_running", "needs_attention"] }), {
|
|
108
86
|
description: "Control event types that should notify the parent/orchestrator. Defaults to active_long_running and needs_attention.",
|
|
@@ -117,26 +95,31 @@ export const SubagentParams = Type.Object({
|
|
|
117
95
|
task: Type.Optional(Type.String({ description: "Task (SINGLE mode, optional for self-contained agents)" })),
|
|
118
96
|
// Management action (when present, tool operates in management mode)
|
|
119
97
|
action: Type.Optional(Type.String({
|
|
120
|
-
|
|
98
|
+
enum: [...SUBAGENT_ACTIONS],
|
|
99
|
+
description: "Management/control action. Omit for execution mode."
|
|
121
100
|
})),
|
|
122
101
|
id: Type.Optional(Type.String({
|
|
123
|
-
description: "Run id or prefix for action='status' or action='
|
|
102
|
+
description: "Run id or prefix for action='status', action='interrupt', or action='resume'."
|
|
124
103
|
})),
|
|
125
104
|
runId: Type.Optional(Type.String({
|
|
126
|
-
description: "Target run ID for action='interrupt'. Defaults to the most recently active controllable run
|
|
105
|
+
description: "Target run ID for action='interrupt' or action='resume'. Defaults to the most recently active controllable run for interrupt. Prefer id for new calls."
|
|
127
106
|
})),
|
|
128
107
|
dir: Type.Optional(Type.String({
|
|
129
|
-
description: "Async run directory for action='status'."
|
|
108
|
+
description: "Async run directory for action='status' or action='resume'."
|
|
130
109
|
})),
|
|
110
|
+
index: Type.Optional(Type.Integer({ minimum: 0, description: "Zero-based child index for actions that target a specific child." })),
|
|
111
|
+
message: Type.Optional(Type.String({ description: "Follow-up message for action='resume'." })),
|
|
131
112
|
// Chain identifier for management (can't reuse 'chain' — that's the execution array)
|
|
132
113
|
chainName: Type.Optional(Type.String({
|
|
133
114
|
description: "Chain name for get/update/delete management actions"
|
|
134
115
|
})),
|
|
135
116
|
// Agent/chain configuration for create/update (nested to avoid conflicts with execution fields)
|
|
136
117
|
config: Type.Optional(Type.Unsafe({
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
118
|
+
anyOf: [
|
|
119
|
+
{ type: "object", additionalProperties: true },
|
|
120
|
+
{ type: "string" },
|
|
121
|
+
],
|
|
122
|
+
description: "Agent or chain config for create/update. Agent: name, description, scope ('user'|'project', default 'user'), systemPrompt, systemPromptMode, inheritProjectContext, inheritSkills, defaultContext ('fresh'|'fork'), model, tools (comma-separated), extensions (comma-separated), skills (comma-separated), thinking, output, reads, progress, maxSubagentDepth. Chain: name, description, scope, steps (array of {agent, task?, output?, reads?, model?, skill?, progress?}). Presence of 'steps' creates a chain instead of an agent. String values must be valid JSON."
|
|
140
123
|
})),
|
|
141
124
|
tasks: Type.Optional(Type.Array(TaskItem, { description: "PARALLEL mode: [{agent, task, count?, output?, reads?, progress?}, ...]" })),
|
|
142
125
|
concurrency: Type.Optional(Type.Integer({ minimum: 1, description: "Top-level PARALLEL mode only: max concurrent tasks. Defaults to config.parallel.concurrency or 4." })),
|
|
@@ -165,7 +148,10 @@ export const SubagentParams = Type.Object({
|
|
|
165
148
|
control: Type.Optional(ControlOverrides),
|
|
166
149
|
// Solo agent overrides
|
|
167
150
|
output: Type.Optional(Type.Unsafe({
|
|
168
|
-
|
|
151
|
+
anyOf: [
|
|
152
|
+
{ type: "string" },
|
|
153
|
+
{ type: "boolean" },
|
|
154
|
+
],
|
|
169
155
|
description: "Output file for single agent (string), or false to disable. Relative paths resolve against cwd.",
|
|
170
156
|
})),
|
|
171
157
|
skill: Type.Optional(SkillOverride),
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
2
|
import * as os from "node:os";
|
|
3
3
|
import * as path from "node:path";
|
|
4
|
-
import type { AgentConfig } from "
|
|
5
|
-
import type { ExtensionConfig, IntercomBridgeConfig, IntercomBridgeMode } from "
|
|
4
|
+
import type { AgentConfig } from "../agents/agents.ts";
|
|
5
|
+
import type { ExtensionConfig, IntercomBridgeConfig, IntercomBridgeMode } from "../shared/types.ts";
|
|
6
6
|
|
|
7
7
|
function defaultIntercomExtensionDir(): string {
|
|
8
8
|
return path.join(os.homedir(), ".pi", "agent", "extensions", "pi-intercom");
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
type SubagentResultStatus,
|
|
9
9
|
SUBAGENT_RESULT_INTERCOM_DELIVERY_EVENT,
|
|
10
10
|
SUBAGENT_RESULT_INTERCOM_EVENT,
|
|
11
|
-
} from "
|
|
11
|
+
} from "../shared/types.ts";
|
|
12
12
|
|
|
13
13
|
export function resolveSubagentResultStatus(input: {
|
|
14
14
|
exitCode?: number;
|
|
@@ -69,10 +69,24 @@ interface GroupedResultIntercomMessageInput {
|
|
|
69
69
|
chainSteps?: number;
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
+
function asyncResumeGuidance(input: {
|
|
73
|
+
source: "foreground" | "async";
|
|
74
|
+
children: SubagentResultIntercomChild[];
|
|
75
|
+
asyncId?: string;
|
|
76
|
+
}): string | undefined {
|
|
77
|
+
if (input.source !== "async" || !input.asyncId) return undefined;
|
|
78
|
+
if (input.children.length === 1 && typeof input.children[0]?.sessionPath === "string") {
|
|
79
|
+
return `Revive: subagent({ action: "resume", id: "${input.asyncId}", message: "..." })`;
|
|
80
|
+
}
|
|
81
|
+
if (input.children.length > 1) return "Resume: unsupported for multi-child async runs until per-child session files are persisted.";
|
|
82
|
+
return "Resume: unavailable; no single child session file was persisted.";
|
|
83
|
+
}
|
|
84
|
+
|
|
72
85
|
function formatSubagentResultIntercomMessage(input: {
|
|
73
86
|
runId: string;
|
|
74
87
|
mode: "single" | "parallel" | "chain";
|
|
75
88
|
status: SubagentResultStatus;
|
|
89
|
+
source: "foreground" | "async";
|
|
76
90
|
children: SubagentResultIntercomChild[];
|
|
77
91
|
asyncId?: string;
|
|
78
92
|
asyncDir?: string;
|
|
@@ -92,16 +106,20 @@ function formatSubagentResultIntercomMessage(input: {
|
|
|
92
106
|
}
|
|
93
107
|
if (input.asyncId) lines.push(`Async id: ${input.asyncId}`);
|
|
94
108
|
if (input.asyncDir) lines.push(`Async dir: ${input.asyncDir}`);
|
|
109
|
+
const resumeGuidance = asyncResumeGuidance(input);
|
|
110
|
+
if (resumeGuidance) lines.push(resumeGuidance);
|
|
95
111
|
if (input.children.some((child) => child.intercomTarget)) {
|
|
96
112
|
lines.push("");
|
|
97
|
-
lines.push(
|
|
113
|
+
lines.push(input.source === "async"
|
|
114
|
+
? "Previous intercom targets below identify child sessions used while they were running. Inspect artifacts or session logs if resume is unavailable."
|
|
115
|
+
: "Intercom targets below identify child sessions used while they were running; completed child sessions may no longer be reachable. Inspect artifacts or session logs for follow-up.");
|
|
98
116
|
}
|
|
99
117
|
|
|
100
118
|
for (let index = 0; index < input.children.length; index++) {
|
|
101
119
|
const child = input.children[index]!;
|
|
102
120
|
lines.push("");
|
|
103
121
|
lines.push(`${index + 1}. ${child.agent} — ${child.status}`);
|
|
104
|
-
if (child.intercomTarget) lines.push(
|
|
122
|
+
if (child.intercomTarget) lines.push(`${input.source === "async" ? "Previous intercom target" : "Run intercom target"}: ${child.intercomTarget}`);
|
|
105
123
|
if (child.artifactPath) lines.push(`Output artifact: ${child.artifactPath}`);
|
|
106
124
|
if (child.sessionPath) lines.push(`Session: ${child.sessionPath}`);
|
|
107
125
|
lines.push("Summary:");
|
|
@@ -144,9 +162,19 @@ export async function deliverSubagentResultIntercomEvent(
|
|
|
144
162
|
events: IntercomEventBus,
|
|
145
163
|
payload: SubagentResultIntercomPayload,
|
|
146
164
|
timeoutMs = 500,
|
|
165
|
+
): Promise<boolean> {
|
|
166
|
+
return deliverSubagentIntercomMessageEvent(events, payload.to, payload.message, timeoutMs, payload);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export async function deliverSubagentIntercomMessageEvent(
|
|
170
|
+
events: IntercomEventBus,
|
|
171
|
+
to: string,
|
|
172
|
+
message: string,
|
|
173
|
+
timeoutMs = 500,
|
|
174
|
+
extra: Record<string, unknown> = {},
|
|
147
175
|
): Promise<boolean> {
|
|
148
176
|
if (typeof events.on !== "function" || typeof events.emit !== "function") return false;
|
|
149
|
-
const requestId =
|
|
177
|
+
const requestId = typeof extra.requestId === "string" ? extra.requestId : randomUUID();
|
|
150
178
|
return new Promise((resolve) => {
|
|
151
179
|
let settled = false;
|
|
152
180
|
let unsubscribe: (() => void) | undefined;
|
|
@@ -166,7 +194,7 @@ export async function deliverSubagentResultIntercomEvent(
|
|
|
166
194
|
});
|
|
167
195
|
timer = setTimeout(() => finish(false), timeoutMs);
|
|
168
196
|
try {
|
|
169
|
-
events.emit(SUBAGENT_RESULT_INTERCOM_EVENT, { ...
|
|
197
|
+
events.emit(SUBAGENT_RESULT_INTERCOM_EVENT, { ...extra, to, message, requestId });
|
|
170
198
|
} catch {
|
|
171
199
|
finish(false);
|
|
172
200
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { Theme } from "@mariozechner/pi-coding-agent";
|
|
2
2
|
import { matchesKey, truncateToWidth } from "@mariozechner/pi-tui";
|
|
3
|
-
import type { ChainConfig, ChainStepConfig } from "
|
|
4
|
-
import { row, renderFooter, renderHeader, formatPath, formatScrollInfo } from "
|
|
5
|
-
import { isParallelStep, type ChainStep } from "
|
|
3
|
+
import type { ChainConfig, ChainStepConfig } from "../agents/agents.ts";
|
|
4
|
+
import { row, renderFooter, renderHeader, formatPath, formatScrollInfo } from "../tui/render-helpers.ts";
|
|
5
|
+
import { isParallelStep, type ChainStep } from "../shared/settings.ts";
|
|
6
6
|
|
|
7
7
|
export interface ChainDetailState {
|
|
8
8
|
scrollOffset: number;
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import type { Theme } from "@mariozechner/pi-coding-agent";
|
|
2
2
|
import { matchesKey, truncateToWidth } from "@mariozechner/pi-tui";
|
|
3
|
-
import type { AgentConfig } from "
|
|
4
|
-
import { formatDuration } from "
|
|
5
|
-
import type { RunEntry } from "
|
|
6
|
-
import { buildSkillInjection, resolveSkills } from "
|
|
7
|
-
import { ensureCursorVisible, getCursorDisplayPos, renderEditor, wrapText } from "
|
|
8
|
-
import type { TextEditorState } from "
|
|
9
|
-
import { pad, row, renderHeader, renderFooter, formatPath, formatScrollInfo } from "
|
|
3
|
+
import type { AgentConfig } from "../agents/agents.ts";
|
|
4
|
+
import { formatDuration } from "../shared/formatters.ts";
|
|
5
|
+
import type { RunEntry } from "../runs/shared/run-history.ts";
|
|
6
|
+
import { buildSkillInjection, resolveSkills } from "../agents/skills.ts";
|
|
7
|
+
import { ensureCursorVisible, getCursorDisplayPos, renderEditor, wrapText } from "../tui/text-editor.ts";
|
|
8
|
+
import type { TextEditorState } from "../tui/text-editor.ts";
|
|
9
|
+
import { pad, row, renderHeader, renderFooter, formatPath, formatScrollInfo } from "../tui/render-helpers.ts";
|
|
10
10
|
|
|
11
11
|
export interface DetailState {
|
|
12
12
|
resolved: boolean;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import type { Theme } from "@mariozechner/pi-coding-agent";
|
|
2
2
|
import { matchesKey, truncateToWidth } from "@mariozechner/pi-tui";
|
|
3
|
-
import { defaultSystemPromptMode, type AgentConfig, type AgentDefaultContext, type BuiltinAgentOverrideBase } from "
|
|
4
|
-
import { createEditorState, ensureCursorVisible, getCursorDisplayPos, handleEditorInput, renderEditor, wrapText } from "
|
|
5
|
-
import type { TextEditorState } from "
|
|
6
|
-
import { pad, row, renderHeader, renderFooter, formatScrollInfo } from "
|
|
3
|
+
import { defaultSystemPromptMode, type AgentConfig, type AgentDefaultContext, type BuiltinAgentOverrideBase } from "../agents/agents.ts";
|
|
4
|
+
import { createEditorState, ensureCursorVisible, getCursorDisplayPos, handleEditorInput, renderEditor, wrapText } from "../tui/text-editor.ts";
|
|
5
|
+
import type { TextEditorState } from "../tui/text-editor.ts";
|
|
6
|
+
import { pad, row, renderHeader, renderFooter, formatScrollInfo } from "../tui/render-helpers.ts";
|
|
7
7
|
|
|
8
8
|
export interface ModelInfo { provider: string; id: string; fullId: string; }
|
|
9
9
|
export interface SkillInfo { name: string; source: string; description?: string; }
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Theme } from "@mariozechner/pi-coding-agent";
|
|
2
|
-
import type { AgentSource } from "
|
|
2
|
+
import type { AgentSource } from "../agents/agents.ts";
|
|
3
3
|
import { matchesKey, truncateToWidth, visibleWidth } from "@mariozechner/pi-tui";
|
|
4
|
-
import { pad, row, renderHeader, renderFooter, fuzzyFilter, formatScrollInfo } from "
|
|
4
|
+
import { pad, row, renderHeader, renderFooter, fuzzyFilter, formatScrollInfo } from "../tui/render-helpers.ts";
|
|
5
5
|
|
|
6
6
|
export interface ListAgent {
|
|
7
7
|
id: string;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { Theme } from "@mariozechner/pi-coding-agent";
|
|
2
2
|
import { matchesKey, truncateToWidth, visibleWidth } from "@mariozechner/pi-tui";
|
|
3
|
-
import type { TextEditorState } from "
|
|
4
|
-
import { createEditorState, handleEditorInput, renderEditor, wrapText, getCursorDisplayPos, ensureCursorVisible } from "
|
|
5
|
-
import { pad, row, renderHeader, renderFooter, fuzzyFilter } from "
|
|
3
|
+
import type { TextEditorState } from "../tui/text-editor.ts";
|
|
4
|
+
import { createEditorState, handleEditorInput, renderEditor, wrapText, getCursorDisplayPos, ensureCursorVisible } from "../tui/text-editor.ts";
|
|
5
|
+
import { pad, row, renderHeader, renderFooter, fuzzyFilter } from "../tui/render-helpers.ts";
|
|
6
6
|
|
|
7
7
|
interface ParallelSlot {
|
|
8
8
|
agentName: string;
|
|
@@ -14,20 +14,20 @@ import {
|
|
|
14
14
|
type AgentConfig,
|
|
15
15
|
type BuiltinAgentOverrideBase,
|
|
16
16
|
type ChainConfig,
|
|
17
|
-
} from "
|
|
18
|
-
import { serializeAgent } from "
|
|
19
|
-
import { TEMPLATE_ITEMS, type AgentTemplate, type TemplateItem } from "
|
|
20
|
-
import { parseChain, serializeChain } from "
|
|
17
|
+
} from "../agents/agents.ts";
|
|
18
|
+
import { serializeAgent } from "../agents/agent-serializer.ts";
|
|
19
|
+
import { TEMPLATE_ITEMS, type AgentTemplate, type TemplateItem } from "../agents/agent-templates.ts";
|
|
20
|
+
import { parseChain, serializeChain } from "../agents/chain-serializer.ts";
|
|
21
21
|
import { DEFAULT_AGENT_MANAGER_NEW_SHORTCUT, renderList, handleListInput, type ListAgent, type ListShortcuts, type ListState, type ListAction } from "./agent-manager-list.ts";
|
|
22
22
|
import { createParallelState, handleParallelInput, renderParallel, formatParallelTitle, type ParallelState, type AgentOption } from "./agent-manager-parallel.ts";
|
|
23
23
|
import { renderDetail, handleDetailInput, renderTaskInput, type DetailState, type DetailAction, type LaunchToggleState } from "./agent-manager-detail.ts";
|
|
24
24
|
import { renderChainDetail, handleChainDetailInput, type ChainDetailAction, type ChainDetailState } from "./agent-manager-chain-detail.ts";
|
|
25
25
|
import { createEditState, handleEditInput, renderEdit, type EditField, type EditScreen, type EditState, type ModelInfo, type SkillInfo } from "./agent-manager-edit.ts";
|
|
26
|
-
import { createEditorState, ensureCursorVisible, getCursorDisplayPos, handleEditorInput, renderEditor, wrapText } from "
|
|
27
|
-
import type { TextEditorState } from "
|
|
28
|
-
import { loadRunsForAgent } from "
|
|
29
|
-
import { pad, row, renderHeader, renderFooter } from "
|
|
30
|
-
import { isParallelStep, type ChainStep } from "
|
|
26
|
+
import { createEditorState, ensureCursorVisible, getCursorDisplayPos, handleEditorInput, renderEditor, wrapText } from "../tui/text-editor.ts";
|
|
27
|
+
import type { TextEditorState } from "../tui/text-editor.ts";
|
|
28
|
+
import { loadRunsForAgent } from "../runs/shared/run-history.ts";
|
|
29
|
+
import { pad, row, renderHeader, renderFooter } from "../tui/render-helpers.ts";
|
|
30
|
+
import { isParallelStep, type ChainStep } from "../shared/settings.ts";
|
|
31
31
|
|
|
32
32
|
export type ManagerResult =
|
|
33
33
|
| { action: "launch"; agent: string; task: string; skipClarify?: boolean; fork?: boolean; background?: boolean }
|
|
@@ -9,16 +9,16 @@ import * as path from "node:path";
|
|
|
9
9
|
import { fileURLToPath } from "node:url";
|
|
10
10
|
import { createRequire } from "node:module";
|
|
11
11
|
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
|
12
|
-
import type { AgentConfig } from "
|
|
13
|
-
import { applyThinkingSuffix } from "
|
|
14
|
-
import { injectSingleOutputInstruction, resolveSingleOutputPath } from "
|
|
15
|
-
import { buildChainInstructions, isParallelStep, resolveStepBehavior, writeInitialProgressFile, type ChainStep, type ResolvedStepBehavior, type SequentialStep, type StepOverrides } from "
|
|
16
|
-
import type { RunnerStep } from "
|
|
17
|
-
import { resolvePiPackageRoot } from "
|
|
18
|
-
import { buildSkillInjection, normalizeSkillInput, resolveSkillsWithFallback } from "
|
|
19
|
-
import { resolveChildCwd } from "
|
|
20
|
-
import { buildModelCandidates, resolveModelCandidate, type AvailableModelInfo } from "
|
|
21
|
-
import { resolveExpectedWorktreeAgentCwd } from "
|
|
12
|
+
import type { AgentConfig } from "../../agents/agents.ts";
|
|
13
|
+
import { applyThinkingSuffix } from "../shared/pi-args.ts";
|
|
14
|
+
import { injectSingleOutputInstruction, resolveSingleOutputPath } from "../shared/single-output.ts";
|
|
15
|
+
import { buildChainInstructions, isParallelStep, resolveStepBehavior, writeInitialProgressFile, type ChainStep, type ResolvedStepBehavior, type SequentialStep, type StepOverrides } from "../../shared/settings.ts";
|
|
16
|
+
import type { RunnerStep } from "../shared/parallel-utils.ts";
|
|
17
|
+
import { resolvePiPackageRoot } from "../shared/pi-spawn.ts";
|
|
18
|
+
import { buildSkillInjection, normalizeSkillInput, resolveSkillsWithFallback } from "../../agents/skills.ts";
|
|
19
|
+
import { resolveChildCwd } from "../../shared/utils.ts";
|
|
20
|
+
import { buildModelCandidates, resolveModelCandidate, type AvailableModelInfo } from "../shared/model-fallback.ts";
|
|
21
|
+
import { resolveExpectedWorktreeAgentCwd } from "../shared/worktree.ts";
|
|
22
22
|
import {
|
|
23
23
|
type ArtifactConfig,
|
|
24
24
|
type Details,
|
|
@@ -30,7 +30,7 @@ import {
|
|
|
30
30
|
TEMP_ROOT_DIR,
|
|
31
31
|
getAsyncConfigPath,
|
|
32
32
|
resolveChildMaxSubagentDepth,
|
|
33
|
-
} from "
|
|
33
|
+
} from "../../shared/types.ts";
|
|
34
34
|
|
|
35
35
|
const require = createRequire(import.meta.url);
|
|
36
36
|
const piPackageRoot = resolvePiPackageRoot();
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { ExtensionAPI, ExtensionContext } from "@mariozechner/pi-coding-agent";
|
|
2
2
|
import * as fs from "node:fs";
|
|
3
3
|
import * as path from "node:path";
|
|
4
|
-
import { renderWidget } from "
|
|
5
|
-
import { formatControlNoticeMessage } from "
|
|
4
|
+
import { renderWidget } from "../../tui/render.ts";
|
|
5
|
+
import { formatControlNoticeMessage } from "../shared/subagent-control.ts";
|
|
6
6
|
import {
|
|
7
7
|
type AsyncJobState,
|
|
8
8
|
type AsyncParallelGroupStatus,
|
|
@@ -10,10 +10,12 @@ import {
|
|
|
10
10
|
type ControlEvent,
|
|
11
11
|
type SubagentState,
|
|
12
12
|
POLL_INTERVAL_MS,
|
|
13
|
+
RESULTS_DIR,
|
|
13
14
|
SUBAGENT_CONTROL_EVENT,
|
|
14
15
|
SUBAGENT_CONTROL_INTERCOM_EVENT,
|
|
15
|
-
} from "
|
|
16
|
-
import { readStatus } from "
|
|
16
|
+
} from "../../shared/types.ts";
|
|
17
|
+
import { readStatus } from "../../shared/utils.ts";
|
|
18
|
+
import { reconcileAsyncRun } from "./stale-run-reconciler.ts";
|
|
17
19
|
|
|
18
20
|
|
|
19
21
|
function isValidParallelGroup(group: AsyncParallelGroupStatus, stepCount: number, chainStepCount: number): boolean {
|
|
@@ -35,6 +37,9 @@ function normalizeParallelGroups(groups: AsyncParallelGroupStatus[] | undefined,
|
|
|
35
37
|
interface AsyncJobTrackerOptions {
|
|
36
38
|
completionRetentionMs?: number;
|
|
37
39
|
pollIntervalMs?: number;
|
|
40
|
+
resultsDir?: string;
|
|
41
|
+
kill?: (pid: number, signal?: NodeJS.Signals | 0) => boolean;
|
|
42
|
+
now?: () => number;
|
|
38
43
|
}
|
|
39
44
|
|
|
40
45
|
export function createAsyncJobTracker(pi: Pick<ExtensionAPI, "events">, state: SubagentState, asyncDirRoot: string, options: AsyncJobTrackerOptions = {}): {
|
|
@@ -45,6 +50,7 @@ export function createAsyncJobTracker(pi: Pick<ExtensionAPI, "events">, state: S
|
|
|
45
50
|
} {
|
|
46
51
|
const completionRetentionMs = options.completionRetentionMs ?? 10000;
|
|
47
52
|
const pollIntervalMs = options.pollIntervalMs ?? POLL_INTERVAL_MS;
|
|
53
|
+
const resultsDir = options.resultsDir ?? RESULTS_DIR;
|
|
48
54
|
const rerenderWidget = (ctx: ExtensionContext, jobs = Array.from(state.asyncJobs.values())) => {
|
|
49
55
|
renderWidget(ctx, jobs);
|
|
50
56
|
ctx.ui.requestRender?.();
|
|
@@ -132,7 +138,20 @@ export function createAsyncJobTracker(pi: Pick<ExtensionAPI, "events">, state: S
|
|
|
132
138
|
for (const job of state.asyncJobs.values()) {
|
|
133
139
|
try {
|
|
134
140
|
emitNewControlEvents(job);
|
|
135
|
-
const
|
|
141
|
+
const reconciliation = reconcileAsyncRun(job.asyncDir, {
|
|
142
|
+
resultsDir,
|
|
143
|
+
kill: options.kill,
|
|
144
|
+
now: options.now,
|
|
145
|
+
startedRun: {
|
|
146
|
+
runId: job.asyncId,
|
|
147
|
+
pid: job.pid,
|
|
148
|
+
mode: job.mode,
|
|
149
|
+
agents: job.agents,
|
|
150
|
+
startedAt: job.startedAt,
|
|
151
|
+
sessionFile: job.sessionFile,
|
|
152
|
+
},
|
|
153
|
+
});
|
|
154
|
+
const status = reconciliation.status ?? readStatus(job.asyncDir);
|
|
136
155
|
if (status) {
|
|
137
156
|
const previousStatus = job.status;
|
|
138
157
|
job.status = status.state;
|
|
@@ -167,7 +186,7 @@ export function createAsyncJobTracker(pi: Pick<ExtensionAPI, "events">, state: S
|
|
|
167
186
|
job.outputFile = status.outputFile ?? job.outputFile;
|
|
168
187
|
job.totalTokens = status.totalTokens ?? job.totalTokens;
|
|
169
188
|
job.sessionFile = status.sessionFile ?? job.sessionFile;
|
|
170
|
-
if ((job.status === "complete" || job.status === "failed" || job.status === "paused") && previousStatus !== job.status) {
|
|
189
|
+
if ((job.status === "complete" || job.status === "failed" || job.status === "paused") && (previousStatus !== job.status || !state.cleanupTimers.has(job.asyncId))) {
|
|
171
190
|
scheduleCleanup(job.asyncId);
|
|
172
191
|
}
|
|
173
192
|
continue;
|
|
@@ -178,6 +197,9 @@ export function createAsyncJobTracker(pi: Pick<ExtensionAPI, "events">, state: S
|
|
|
178
197
|
console.error(`Failed to read async status for '${job.asyncDir}':`, error);
|
|
179
198
|
job.status = "failed";
|
|
180
199
|
job.updatedAt = Date.now();
|
|
200
|
+
if (!state.cleanupTimers.has(job.asyncId)) {
|
|
201
|
+
scheduleCleanup(job.asyncId);
|
|
202
|
+
}
|
|
181
203
|
}
|
|
182
204
|
}
|
|
183
205
|
|
|
@@ -202,6 +224,7 @@ export function createAsyncJobTracker(pi: Pick<ExtensionAPI, "events">, state: S
|
|
|
202
224
|
asyncId: info.id,
|
|
203
225
|
asyncDir,
|
|
204
226
|
status: "queued",
|
|
227
|
+
pid: typeof info.pid === "number" ? info.pid : undefined,
|
|
205
228
|
mode: info.chain ? "chain" : "single",
|
|
206
229
|
agents,
|
|
207
230
|
stepsTotal: firstGroupCount ?? agents?.length,
|