@leing2021/super-pi 0.22.1 → 0.23.0
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/README.md +253 -211
- package/extensions/ce-core/index.ts +190 -7
- package/extensions/ce-core/tools/parallel-subagent.ts +3 -3
- package/extensions/ce-core/tools/subagent.ts +2 -2
- package/package.json +4 -20
- package/rules/common/naming.md +154 -0
- package/skills/02-plan/SKILL.md +3 -2
- package/skills/03-work/SKILL.md +9 -6
- package/skills/04-review/SKILL.md +3 -2
- package/skills/06-next/SKILL.md +38 -10
- package/skills/06-next/references/recommendation-logic.md +2 -2
- package/skills/07-worktree/SKILL.md +9 -8
- package/skills/{09-help → 08-help}/SKILL.md +3 -3
- package/skills/{09-help → 08-help}/references/workflow-sequence.md +2 -1
- package/skills/references/language-detection.md +44 -0
- package/LICENSE +0 -21
- package/THIRD-PARTY-NOTICES.md +0 -35
- package/extensions/subagent/__tests__/async-job-tracker.test.ts +0 -21
- package/extensions/subagent/__tests__/execution-activity.test.ts +0 -15
- package/extensions/subagent/__tests__/parallel-render-stress.test.ts +0 -82
- package/extensions/subagent/__tests__/render-dedup.test.ts +0 -98
- package/extensions/subagent/__tests__/render-widget.test.ts +0 -28
- package/extensions/subagent/__tests__/throttle.test.ts +0 -90
- package/extensions/subagent/agent-management.ts +0 -596
- package/extensions/subagent/agent-manager-chain-detail.ts +0 -163
- package/extensions/subagent/agent-manager-detail.ts +0 -232
- package/extensions/subagent/agent-manager-edit.ts +0 -391
- package/extensions/subagent/agent-manager-list.ts +0 -279
- package/extensions/subagent/agent-manager-parallel.ts +0 -305
- package/extensions/subagent/agent-manager.ts +0 -706
- package/extensions/subagent/agent-scope.ts +0 -9
- package/extensions/subagent/agent-selection.ts +0 -26
- package/extensions/subagent/agent-serializer.ts +0 -124
- package/extensions/subagent/agent-templates.ts +0 -63
- package/extensions/subagent/agents/context-builder.md +0 -37
- package/extensions/subagent/agents/delegate.md +0 -9
- package/extensions/subagent/agents/oracle.md +0 -73
- package/extensions/subagent/agents/planner.md +0 -52
- package/extensions/subagent/agents/researcher.md +0 -50
- package/extensions/subagent/agents/reviewer.md +0 -38
- package/extensions/subagent/agents/scout.md +0 -48
- package/extensions/subagent/agents/worker.md +0 -52
- package/extensions/subagent/agents.ts +0 -762
- package/extensions/subagent/artifacts.ts +0 -101
- package/extensions/subagent/async-execution.ts +0 -521
- package/extensions/subagent/async-job-tracker.ts +0 -240
- package/extensions/subagent/async-status.ts +0 -242
- package/extensions/subagent/chain-clarify.ts +0 -1365
- package/extensions/subagent/chain-execution.ts +0 -854
- package/extensions/subagent/chain-serializer.ts +0 -127
- package/extensions/subagent/completion-dedupe.ts +0 -66
- package/extensions/subagent/doctor.ts +0 -201
- package/extensions/subagent/execution.ts +0 -748
- package/extensions/subagent/file-coalescer.ts +0 -43
- package/extensions/subagent/fork-context.ts +0 -64
- package/extensions/subagent/formatters.ts +0 -123
- package/extensions/subagent/frontmatter.ts +0 -32
- package/extensions/subagent/index.ts +0 -586
- package/extensions/subagent/intercom-bridge.ts +0 -241
- package/extensions/subagent/jsonl-writer.ts +0 -84
- package/extensions/subagent/model-fallback.ts +0 -109
- package/extensions/subagent/notify.ts +0 -111
- package/extensions/subagent/parallel-utils.ts +0 -109
- package/extensions/subagent/pi-args.ts +0 -139
- package/extensions/subagent/pi-spawn.ts +0 -101
- package/extensions/subagent/post-exit-stdio-guard.ts +0 -88
- package/extensions/subagent/prompt-template-bridge.ts +0 -400
- package/extensions/subagent/prompts/gather-context-and-clarify.md +0 -13
- package/extensions/subagent/prompts/parallel-cleanup.md +0 -42
- package/extensions/subagent/prompts/parallel-research.md +0 -50
- package/extensions/subagent/prompts/parallel-review.md +0 -40
- package/extensions/subagent/render-helpers.ts +0 -83
- package/extensions/subagent/render.ts +0 -908
- package/extensions/subagent/result-intercom.ts +0 -238
- package/extensions/subagent/result-watcher.ts +0 -172
- package/extensions/subagent/run-history.ts +0 -58
- package/extensions/subagent/run-status.ts +0 -137
- package/extensions/subagent/schemas.ts +0 -165
- package/extensions/subagent/session-tokens.ts +0 -51
- package/extensions/subagent/settings.ts +0 -368
- package/extensions/subagent/single-output.ts +0 -98
- package/extensions/subagent/skills.ts +0 -627
- package/extensions/subagent/slash-bridge.ts +0 -177
- package/extensions/subagent/slash-commands.ts +0 -304
- package/extensions/subagent/slash-live-state.ts +0 -295
- package/extensions/subagent/subagent-control.ts +0 -151
- package/extensions/subagent/subagent-executor.ts +0 -1915
- package/extensions/subagent/subagent-prompt-runtime.ts +0 -76
- package/extensions/subagent/subagent-runner.ts +0 -1471
- package/extensions/subagent/subagents-status.ts +0 -473
- package/extensions/subagent/text-editor.ts +0 -273
- package/extensions/subagent/throttle.ts +0 -77
- package/extensions/subagent/top-level-async.ts +0 -16
- package/extensions/subagent/types.ts +0 -624
- package/extensions/subagent/utils.ts +0 -457
- package/extensions/subagent/worktree.ts +0 -580
- package/extensions/super-pi-extension/agents/ce-oracle.md +0 -109
- package/extensions/super-pi-extension/agents/ce-planner.md +0 -116
- package/extensions/super-pi-extension/agents/ce-reviewer.md +0 -99
- package/extensions/super-pi-extension/agents/ce-scout.md +0 -80
- package/extensions/super-pi-extension/agents/ce-worker.md +0 -78
- package/extensions/super-pi-extension/chains/ce-parallel-review.chain.md +0 -34
- package/extensions/super-pi-extension/chains/ce-review-only.chain.md +0 -12
- package/extensions/super-pi-extension/chains/ce-standard.chain.md +0 -28
- package/extensions/super-pi-extension/index.ts +0 -129
- package/extensions/super-pi-extension/model-sync.ts +0 -204
- package/skills/08-status/SKILL.md +0 -41
- package/skills/08-status/references/artifact-locations.md +0 -10
- package/skills/10-rules/SKILL.md +0 -76
- package/skills/pi-subagents/SKILL.md +0 -566
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
// SPDX-FileCopyrightText: 2025 Nico Bailon
|
|
2
|
-
// SPDX-License-Identifier: MIT
|
|
3
|
-
// Source: https://github.com/nicobailon/pi-subagents
|
|
4
|
-
import * as fs from "node:fs";
|
|
5
|
-
import * as os from "node:os";
|
|
6
|
-
import * as path from "node:path";
|
|
7
|
-
import { TEMP_ARTIFACTS_DIR, type ArtifactPaths } from "./types.ts";
|
|
8
|
-
const CLEANUP_MARKER_FILE = ".last-cleanup";
|
|
9
|
-
|
|
10
|
-
export function getArtifactsDir(sessionFile: string | null): string {
|
|
11
|
-
if (sessionFile) {
|
|
12
|
-
const sessionDir = path.dirname(sessionFile);
|
|
13
|
-
return path.join(sessionDir, "subagent-artifacts");
|
|
14
|
-
}
|
|
15
|
-
return TEMP_ARTIFACTS_DIR;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export function getArtifactPaths(artifactsDir: string, runId: string, agent: string, index?: number): ArtifactPaths {
|
|
19
|
-
const suffix = index !== undefined ? `_${index}` : "";
|
|
20
|
-
const safeAgent = agent.replace(/[^\w.-]/g, "_");
|
|
21
|
-
const base = `${runId}_${safeAgent}${suffix}`;
|
|
22
|
-
return {
|
|
23
|
-
inputPath: path.join(artifactsDir, `${base}_input.md`),
|
|
24
|
-
outputPath: path.join(artifactsDir, `${base}_output.md`),
|
|
25
|
-
jsonlPath: path.join(artifactsDir, `${base}.jsonl`),
|
|
26
|
-
metadataPath: path.join(artifactsDir, `${base}_meta.json`),
|
|
27
|
-
};
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export function ensureArtifactsDir(dir: string): void {
|
|
31
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export function writeArtifact(filePath: string, content: string): void {
|
|
35
|
-
fs.writeFileSync(filePath, content, "utf-8");
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export function writeMetadata(filePath: string, metadata: object): void {
|
|
39
|
-
fs.writeFileSync(filePath, JSON.stringify(metadata, null, 2), "utf-8");
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export function appendJsonl(filePath: string, line: string): void {
|
|
43
|
-
fs.appendFileSync(filePath, `${line}\n`);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export function cleanupOldArtifacts(dir: string, maxAgeDays: number): void {
|
|
47
|
-
if (!fs.existsSync(dir)) return;
|
|
48
|
-
|
|
49
|
-
const markerPath = path.join(dir, CLEANUP_MARKER_FILE);
|
|
50
|
-
const now = Date.now();
|
|
51
|
-
|
|
52
|
-
if (fs.existsSync(markerPath)) {
|
|
53
|
-
const stat = fs.statSync(markerPath);
|
|
54
|
-
if (now - stat.mtimeMs < 24 * 60 * 60 * 1000) return;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
const maxAgeMs = maxAgeDays * 24 * 60 * 60 * 1000;
|
|
58
|
-
const cutoff = now - maxAgeMs;
|
|
59
|
-
|
|
60
|
-
for (const file of fs.readdirSync(dir)) {
|
|
61
|
-
if (file === CLEANUP_MARKER_FILE) continue;
|
|
62
|
-
const filePath = path.join(dir, file);
|
|
63
|
-
try {
|
|
64
|
-
const stat = fs.statSync(filePath);
|
|
65
|
-
if (stat.mtimeMs < cutoff) {
|
|
66
|
-
fs.unlinkSync(filePath);
|
|
67
|
-
}
|
|
68
|
-
} catch {
|
|
69
|
-
// Artifact cleanup is best-effort housekeeping. Skip files that disappear
|
|
70
|
-
// or become unreadable while scanning so one bad entry does not block the rest.
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
fs.writeFileSync(markerPath, String(now));
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
export function cleanupAllArtifactDirs(maxAgeDays: number): void {
|
|
78
|
-
cleanupOldArtifacts(TEMP_ARTIFACTS_DIR, maxAgeDays);
|
|
79
|
-
|
|
80
|
-
const sessionsBase = path.join(os.homedir(), ".pi", "agent", "sessions");
|
|
81
|
-
if (!fs.existsSync(sessionsBase)) return;
|
|
82
|
-
|
|
83
|
-
let dirs: string[];
|
|
84
|
-
try {
|
|
85
|
-
dirs = fs.readdirSync(sessionsBase);
|
|
86
|
-
} catch {
|
|
87
|
-
// Session artifact cleanup is best-effort. If the sessions root cannot be read,
|
|
88
|
-
// skip cleanup instead of failing extension startup.
|
|
89
|
-
return;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
for (const dir of dirs) {
|
|
93
|
-
const artifactsDir = path.join(sessionsBase, dir, "subagent-artifacts");
|
|
94
|
-
try {
|
|
95
|
-
cleanupOldArtifacts(artifactsDir, maxAgeDays);
|
|
96
|
-
} catch {
|
|
97
|
-
// Session cleanup is best-effort. Keep going so one unreadable session dir
|
|
98
|
-
// does not block cleanup for the rest.
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
}
|
|
@@ -1,521 +0,0 @@
|
|
|
1
|
-
// SPDX-FileCopyrightText: 2025 Nico Bailon
|
|
2
|
-
// SPDX-License-Identifier: MIT
|
|
3
|
-
// Source: https://github.com/nicobailon/pi-subagents
|
|
4
|
-
/**
|
|
5
|
-
* Async execution logic for subagent tool
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { spawn } from "node:child_process";
|
|
9
|
-
import * as fs from "node:fs";
|
|
10
|
-
import * as os from "node:os";
|
|
11
|
-
import * as path from "node:path";
|
|
12
|
-
import { fileURLToPath } from "node:url";
|
|
13
|
-
import { createRequire } from "node:module";
|
|
14
|
-
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
|
15
|
-
import type { AgentConfig } from "./agents.ts";
|
|
16
|
-
import { applyThinkingSuffix } from "./pi-args.ts";
|
|
17
|
-
import { injectSingleOutputInstruction, resolveSingleOutputPath } from "./single-output.ts";
|
|
18
|
-
import { buildChainInstructions, isParallelStep, resolveStepBehavior, writeInitialProgressFile, type ChainStep, type ResolvedStepBehavior, type SequentialStep, type StepOverrides } from "./settings.ts";
|
|
19
|
-
import type { RunnerStep } from "./parallel-utils.ts";
|
|
20
|
-
import { resolvePiPackageRoot } from "./pi-spawn.ts";
|
|
21
|
-
import { buildSkillInjection, normalizeSkillInput, resolveSkillsWithFallback } from "./skills.ts";
|
|
22
|
-
import { resolveChildCwd } from "./utils.ts";
|
|
23
|
-
import { buildModelCandidates, resolveModelCandidate, type AvailableModelInfo } from "./model-fallback.ts";
|
|
24
|
-
import { resolveExpectedWorktreeAgentCwd } from "./worktree.ts";
|
|
25
|
-
import {
|
|
26
|
-
type ArtifactConfig,
|
|
27
|
-
type Details,
|
|
28
|
-
type MaxOutputConfig,
|
|
29
|
-
type ResolvedControlConfig,
|
|
30
|
-
ASYNC_DIR,
|
|
31
|
-
RESULTS_DIR,
|
|
32
|
-
SUBAGENT_ASYNC_STARTED_EVENT,
|
|
33
|
-
TEMP_ROOT_DIR,
|
|
34
|
-
getAsyncConfigPath,
|
|
35
|
-
resolveChildMaxSubagentDepth,
|
|
36
|
-
} from "./types.ts";
|
|
37
|
-
|
|
38
|
-
const require = createRequire(import.meta.url);
|
|
39
|
-
const piPackageRoot = resolvePiPackageRoot();
|
|
40
|
-
const jitiCliPath: string | undefined = (() => {
|
|
41
|
-
const candidates: Array<() => string> = [
|
|
42
|
-
() => path.join(path.dirname(require.resolve("jiti/package.json")), "lib/jiti-cli.mjs"),
|
|
43
|
-
() => path.join(path.dirname(require.resolve("@mariozechner/jiti/package.json")), "lib/jiti-cli.mjs"),
|
|
44
|
-
() => {
|
|
45
|
-
const piEntry = fs.realpathSync(process.argv[1]);
|
|
46
|
-
const piRequire = createRequire(piEntry);
|
|
47
|
-
return path.join(path.dirname(piRequire.resolve("@mariozechner/jiti/package.json")), "lib/jiti-cli.mjs");
|
|
48
|
-
},
|
|
49
|
-
];
|
|
50
|
-
for (const candidate of candidates) {
|
|
51
|
-
try {
|
|
52
|
-
const p = candidate();
|
|
53
|
-
if (fs.existsSync(p)) return p;
|
|
54
|
-
} catch {
|
|
55
|
-
// Candidate not available in this install, continue probing.
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
return undefined;
|
|
59
|
-
})();
|
|
60
|
-
|
|
61
|
-
export interface AsyncExecutionContext {
|
|
62
|
-
pi: ExtensionAPI;
|
|
63
|
-
cwd: string;
|
|
64
|
-
currentSessionId: string;
|
|
65
|
-
currentModelProvider?: string;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
export interface AsyncChainParams {
|
|
69
|
-
chain: ChainStep[];
|
|
70
|
-
resultMode?: "parallel" | "chain";
|
|
71
|
-
agents: AgentConfig[];
|
|
72
|
-
ctx: AsyncExecutionContext;
|
|
73
|
-
availableModels?: AvailableModelInfo[];
|
|
74
|
-
cwd?: string;
|
|
75
|
-
maxOutput?: MaxOutputConfig;
|
|
76
|
-
artifactsDir?: string;
|
|
77
|
-
artifactConfig: ArtifactConfig;
|
|
78
|
-
shareEnabled: boolean;
|
|
79
|
-
sessionRoot?: string;
|
|
80
|
-
chainSkills?: string[];
|
|
81
|
-
sessionFilesByFlatIndex?: (string | undefined)[];
|
|
82
|
-
maxSubagentDepth: number;
|
|
83
|
-
worktreeSetupHook?: string;
|
|
84
|
-
worktreeSetupHookTimeoutMs?: number;
|
|
85
|
-
controlConfig?: ResolvedControlConfig;
|
|
86
|
-
controlIntercomTarget?: string;
|
|
87
|
-
childIntercomTarget?: (agent: string, index: number) => string | undefined;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
export interface AsyncSingleParams {
|
|
91
|
-
agent: string;
|
|
92
|
-
task?: string;
|
|
93
|
-
agentConfig: AgentConfig;
|
|
94
|
-
ctx: AsyncExecutionContext;
|
|
95
|
-
cwd?: string;
|
|
96
|
-
maxOutput?: MaxOutputConfig;
|
|
97
|
-
artifactsDir?: string;
|
|
98
|
-
artifactConfig: ArtifactConfig;
|
|
99
|
-
shareEnabled: boolean;
|
|
100
|
-
sessionRoot?: string;
|
|
101
|
-
sessionFile?: string;
|
|
102
|
-
skills?: string[];
|
|
103
|
-
output?: string | false;
|
|
104
|
-
modelOverride?: string;
|
|
105
|
-
availableModels?: AvailableModelInfo[];
|
|
106
|
-
maxSubagentDepth: number;
|
|
107
|
-
worktreeSetupHook?: string;
|
|
108
|
-
worktreeSetupHookTimeoutMs?: number;
|
|
109
|
-
controlConfig?: ResolvedControlConfig;
|
|
110
|
-
controlIntercomTarget?: string;
|
|
111
|
-
childIntercomTarget?: (agent: string, index: number) => string | undefined;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
export interface AsyncExecutionResult {
|
|
115
|
-
content: Array<{ type: "text"; text: string }>;
|
|
116
|
-
details: Details;
|
|
117
|
-
isError?: boolean;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* Check if jiti is available for async execution
|
|
122
|
-
*/
|
|
123
|
-
export function isAsyncAvailable(): boolean {
|
|
124
|
-
return jitiCliPath !== undefined;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* Spawn the async runner process
|
|
129
|
-
*/
|
|
130
|
-
function spawnRunner(cfg: object, suffix: string, cwd: string): { pid?: number; error?: string } {
|
|
131
|
-
if (!jitiCliPath) {
|
|
132
|
-
return { error: "jiti for TypeScript execution could not be found" };
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
try {
|
|
136
|
-
const cwdStats = fs.statSync(cwd);
|
|
137
|
-
if (!cwdStats.isDirectory()) {
|
|
138
|
-
return { error: `cwd is not a directory: ${cwd}` };
|
|
139
|
-
}
|
|
140
|
-
} catch {
|
|
141
|
-
return { error: `cwd does not exist: ${cwd}` };
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
fs.mkdirSync(TEMP_ROOT_DIR, { recursive: true });
|
|
145
|
-
const cfgPath = getAsyncConfigPath(suffix);
|
|
146
|
-
fs.writeFileSync(cfgPath, JSON.stringify(cfg));
|
|
147
|
-
const runner = path.join(path.dirname(fileURLToPath(import.meta.url)), "subagent-runner.ts");
|
|
148
|
-
|
|
149
|
-
const proc = spawn(process.execPath, [jitiCliPath, runner, cfgPath], {
|
|
150
|
-
cwd,
|
|
151
|
-
detached: true,
|
|
152
|
-
stdio: "ignore",
|
|
153
|
-
windowsHide: true,
|
|
154
|
-
});
|
|
155
|
-
proc.on("error", (error) => {
|
|
156
|
-
console.error(`[pi-subagents] async spawn failed: ${error.message}`);
|
|
157
|
-
});
|
|
158
|
-
if (typeof proc.pid !== "number") {
|
|
159
|
-
return { error: `async runner did not produce a pid for cwd: ${cwd}` };
|
|
160
|
-
}
|
|
161
|
-
proc.unref();
|
|
162
|
-
return { pid: proc.pid };
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
function formatAsyncStartError(mode: "single" | "chain", message: string): AsyncExecutionResult {
|
|
166
|
-
return {
|
|
167
|
-
content: [{ type: "text", text: message }],
|
|
168
|
-
isError: true,
|
|
169
|
-
details: { mode, results: [] },
|
|
170
|
-
};
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
/**
|
|
174
|
-
* Execute a chain asynchronously
|
|
175
|
-
*/
|
|
176
|
-
export function executeAsyncChain(
|
|
177
|
-
id: string,
|
|
178
|
-
params: AsyncChainParams,
|
|
179
|
-
): AsyncExecutionResult {
|
|
180
|
-
const {
|
|
181
|
-
chain,
|
|
182
|
-
agents,
|
|
183
|
-
ctx,
|
|
184
|
-
cwd,
|
|
185
|
-
maxOutput,
|
|
186
|
-
artifactsDir,
|
|
187
|
-
artifactConfig,
|
|
188
|
-
shareEnabled,
|
|
189
|
-
sessionRoot,
|
|
190
|
-
sessionFilesByFlatIndex,
|
|
191
|
-
maxSubagentDepth,
|
|
192
|
-
worktreeSetupHook,
|
|
193
|
-
worktreeSetupHookTimeoutMs,
|
|
194
|
-
controlConfig,
|
|
195
|
-
controlIntercomTarget,
|
|
196
|
-
childIntercomTarget,
|
|
197
|
-
} = params;
|
|
198
|
-
const chainSkills = params.chainSkills ?? [];
|
|
199
|
-
const availableModels = params.availableModels;
|
|
200
|
-
const runnerCwd = resolveChildCwd(ctx.cwd, cwd);
|
|
201
|
-
|
|
202
|
-
for (const s of chain) {
|
|
203
|
-
const stepAgents = isParallelStep(s)
|
|
204
|
-
? s.parallel.map((t) => t.agent)
|
|
205
|
-
: [(s as SequentialStep).agent];
|
|
206
|
-
for (const agentName of stepAgents) {
|
|
207
|
-
if (!agents.find((x) => x.name === agentName)) {
|
|
208
|
-
return {
|
|
209
|
-
content: [{ type: "text", text: `Unknown agent: ${agentName}` }],
|
|
210
|
-
isError: true,
|
|
211
|
-
details: { mode: "chain" as const, results: [] },
|
|
212
|
-
};
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
const asyncDir = path.join(ASYNC_DIR, id);
|
|
218
|
-
try {
|
|
219
|
-
fs.mkdirSync(asyncDir, { recursive: true });
|
|
220
|
-
} catch (error) {
|
|
221
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
222
|
-
return {
|
|
223
|
-
content: [{ type: "text", text: `Failed to create async run directory '${asyncDir}': ${message}` }],
|
|
224
|
-
isError: true,
|
|
225
|
-
details: { mode: "chain" as const, results: [] },
|
|
226
|
-
};
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
let progressInstructionCreated = false;
|
|
230
|
-
const buildStepOverrides = (s: SequentialStep): StepOverrides => {
|
|
231
|
-
const stepSkillInput = normalizeSkillInput(s.skill);
|
|
232
|
-
return {
|
|
233
|
-
...(s.output !== undefined ? { output: s.output } : {}),
|
|
234
|
-
...(s.reads !== undefined ? { reads: s.reads } : {}),
|
|
235
|
-
...(s.progress !== undefined ? { progress: s.progress } : {}),
|
|
236
|
-
...(stepSkillInput !== undefined ? { skills: stepSkillInput } : {}),
|
|
237
|
-
...(s.model ? { model: s.model } : {}),
|
|
238
|
-
};
|
|
239
|
-
};
|
|
240
|
-
const buildSeqStep = (s: SequentialStep, sessionFile?: string, behaviorCwd?: string, progressPrecreated = false, resolvedBehavior?: ResolvedStepBehavior) => {
|
|
241
|
-
const a = agents.find((x) => x.name === s.agent)!;
|
|
242
|
-
const stepCwd = resolveChildCwd(runnerCwd, s.cwd);
|
|
243
|
-
const instructionCwd = behaviorCwd ?? stepCwd;
|
|
244
|
-
const behavior = resolvedBehavior ?? resolveStepBehavior(a, buildStepOverrides(s), chainSkills);
|
|
245
|
-
const skillNames = behavior.skills === false ? [] : behavior.skills;
|
|
246
|
-
const { resolved: resolvedSkills } = resolveSkillsWithFallback(skillNames, stepCwd, ctx.cwd);
|
|
247
|
-
|
|
248
|
-
let systemPrompt = a.systemPrompt?.trim() ?? "";
|
|
249
|
-
if (resolvedSkills.length > 0) {
|
|
250
|
-
const injection = buildSkillInjection(resolvedSkills);
|
|
251
|
-
systemPrompt = systemPrompt ? `${systemPrompt}\n\n${injection}` : injection;
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
const readInstructions = buildChainInstructions({ ...behavior, output: false, progress: false }, instructionCwd, false);
|
|
255
|
-
const isFirstProgressAgent = behavior.progress && !progressPrecreated && !progressInstructionCreated;
|
|
256
|
-
if (behavior.progress) progressInstructionCreated = true;
|
|
257
|
-
const progressInstructions = buildChainInstructions({ ...behavior, output: false, reads: false }, runnerCwd, isFirstProgressAgent);
|
|
258
|
-
const outputPath = resolveSingleOutputPath(behavior.output, ctx.cwd, instructionCwd);
|
|
259
|
-
const task = injectSingleOutputInstruction(`${readInstructions.prefix}${s.task ?? "{previous}"}${progressInstructions.suffix}`, outputPath);
|
|
260
|
-
|
|
261
|
-
const primaryModel = resolveModelCandidate(behavior.model ?? a.model, availableModels, ctx.currentModelProvider);
|
|
262
|
-
return {
|
|
263
|
-
agent: s.agent,
|
|
264
|
-
task,
|
|
265
|
-
cwd: stepCwd,
|
|
266
|
-
model: applyThinkingSuffix(primaryModel, a.thinking),
|
|
267
|
-
modelCandidates: buildModelCandidates(behavior.model ?? a.model, a.fallbackModels, availableModels, ctx.currentModelProvider).map((candidate) =>
|
|
268
|
-
applyThinkingSuffix(candidate, a.thinking),
|
|
269
|
-
),
|
|
270
|
-
tools: a.tools,
|
|
271
|
-
extensions: a.extensions,
|
|
272
|
-
mcpDirectTools: a.mcpDirectTools,
|
|
273
|
-
systemPrompt,
|
|
274
|
-
systemPromptMode: a.systemPromptMode,
|
|
275
|
-
inheritProjectContext: a.inheritProjectContext,
|
|
276
|
-
inheritSkills: a.inheritSkills,
|
|
277
|
-
skills: resolvedSkills.map((r) => r.name),
|
|
278
|
-
outputPath,
|
|
279
|
-
sessionFile,
|
|
280
|
-
maxSubagentDepth: resolveChildMaxSubagentDepth(maxSubagentDepth, a.maxSubagentDepth),
|
|
281
|
-
};
|
|
282
|
-
};
|
|
283
|
-
|
|
284
|
-
let flatStepIndex = 0;
|
|
285
|
-
const nextSessionFile = (): string | undefined => {
|
|
286
|
-
const sessionFile = sessionFilesByFlatIndex?.[flatStepIndex];
|
|
287
|
-
flatStepIndex++;
|
|
288
|
-
return sessionFile;
|
|
289
|
-
};
|
|
290
|
-
|
|
291
|
-
const steps: RunnerStep[] = chain.map((s, stepIndex) => {
|
|
292
|
-
if (isParallelStep(s)) {
|
|
293
|
-
const parallelBehaviors = s.parallel.map((task) => {
|
|
294
|
-
const agent = agents.find((candidate) => candidate.name === task.agent)!;
|
|
295
|
-
return resolveStepBehavior(agent, buildStepOverrides(task), chainSkills);
|
|
296
|
-
});
|
|
297
|
-
const progressPrecreated = parallelBehaviors.some((behavior) => behavior.progress);
|
|
298
|
-
if (progressPrecreated) {
|
|
299
|
-
if (!s.worktree) writeInitialProgressFile(runnerCwd);
|
|
300
|
-
progressInstructionCreated = true;
|
|
301
|
-
}
|
|
302
|
-
return {
|
|
303
|
-
parallel: s.parallel.map((t, taskIndex) => {
|
|
304
|
-
let behaviorCwd: string | undefined;
|
|
305
|
-
if (s.worktree) {
|
|
306
|
-
try {
|
|
307
|
-
behaviorCwd = resolveExpectedWorktreeAgentCwd(runnerCwd, `${id}-s${stepIndex}`, taskIndex);
|
|
308
|
-
} catch {
|
|
309
|
-
behaviorCwd = undefined;
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
return buildSeqStep(t, nextSessionFile(), behaviorCwd, progressPrecreated, parallelBehaviors[taskIndex]);
|
|
313
|
-
}),
|
|
314
|
-
concurrency: s.concurrency,
|
|
315
|
-
failFast: s.failFast,
|
|
316
|
-
worktree: s.worktree,
|
|
317
|
-
};
|
|
318
|
-
}
|
|
319
|
-
return buildSeqStep(s as SequentialStep, nextSessionFile());
|
|
320
|
-
});
|
|
321
|
-
let childTargetIndex = 0;
|
|
322
|
-
const childIntercomTargets = childIntercomTarget ? steps.flatMap((step) => {
|
|
323
|
-
if ("parallel" in step) {
|
|
324
|
-
return step.parallel.map((task) => childIntercomTarget(task.agent, childTargetIndex++));
|
|
325
|
-
}
|
|
326
|
-
return [childIntercomTarget(step.agent, childTargetIndex++)];
|
|
327
|
-
}) : undefined;
|
|
328
|
-
|
|
329
|
-
let spawnResult: { pid?: number; error?: string } = {};
|
|
330
|
-
try {
|
|
331
|
-
spawnResult = spawnRunner(
|
|
332
|
-
{
|
|
333
|
-
id,
|
|
334
|
-
steps,
|
|
335
|
-
resultPath: path.join(RESULTS_DIR, `${id}.json`),
|
|
336
|
-
cwd: runnerCwd,
|
|
337
|
-
placeholder: "{previous}",
|
|
338
|
-
maxOutput,
|
|
339
|
-
artifactsDir: artifactConfig.enabled ? artifactsDir : undefined,
|
|
340
|
-
artifactConfig,
|
|
341
|
-
share: shareEnabled,
|
|
342
|
-
sessionDir: sessionRoot ? path.join(sessionRoot, `async-${id}`) : undefined,
|
|
343
|
-
asyncDir,
|
|
344
|
-
sessionId: ctx.currentSessionId,
|
|
345
|
-
piPackageRoot,
|
|
346
|
-
piArgv1: process.argv[1],
|
|
347
|
-
worktreeSetupHook,
|
|
348
|
-
worktreeSetupHookTimeoutMs,
|
|
349
|
-
controlConfig,
|
|
350
|
-
controlIntercomTarget,
|
|
351
|
-
childIntercomTargets,
|
|
352
|
-
resultMode: params.resultMode ?? "chain",
|
|
353
|
-
},
|
|
354
|
-
id,
|
|
355
|
-
runnerCwd,
|
|
356
|
-
);
|
|
357
|
-
} catch (error) {
|
|
358
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
359
|
-
return formatAsyncStartError("chain", `Failed to start async chain '${id}': ${message}`);
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
if (spawnResult.error) {
|
|
363
|
-
return formatAsyncStartError("chain", `Failed to start async chain '${id}': ${spawnResult.error}`);
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
if (spawnResult.pid) {
|
|
367
|
-
const firstStep = chain[0];
|
|
368
|
-
const firstAgents = isParallelStep(firstStep)
|
|
369
|
-
? firstStep.parallel.map((t) => t.agent)
|
|
370
|
-
: [(firstStep as SequentialStep).agent];
|
|
371
|
-
ctx.pi.events.emit(SUBAGENT_ASYNC_STARTED_EVENT, {
|
|
372
|
-
id,
|
|
373
|
-
pid: spawnResult.pid,
|
|
374
|
-
agent: firstAgents[0],
|
|
375
|
-
task: isParallelStep(firstStep)
|
|
376
|
-
? firstStep.parallel[0]?.task?.slice(0, 50)
|
|
377
|
-
: (firstStep as SequentialStep).task?.slice(0, 50),
|
|
378
|
-
chain: chain.map((s) =>
|
|
379
|
-
isParallelStep(s) ? `[${s.parallel.map((t) => t.agent).join("+")}]` : (s as SequentialStep).agent,
|
|
380
|
-
),
|
|
381
|
-
cwd: runnerCwd,
|
|
382
|
-
asyncDir,
|
|
383
|
-
});
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
const chainDesc = chain
|
|
387
|
-
.map((s) =>
|
|
388
|
-
isParallelStep(s) ? `[${s.parallel.map((t) => t.agent).join("+")}]` : (s as SequentialStep).agent,
|
|
389
|
-
)
|
|
390
|
-
.join(" -> ");
|
|
391
|
-
|
|
392
|
-
return {
|
|
393
|
-
content: [{ type: "text", text: `Async chain: ${chainDesc} [${id}]` }],
|
|
394
|
-
details: { mode: "chain", results: [], asyncId: id, asyncDir },
|
|
395
|
-
};
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
/**
|
|
399
|
-
* Execute a single agent asynchronously
|
|
400
|
-
*/
|
|
401
|
-
export function executeAsyncSingle(
|
|
402
|
-
id: string,
|
|
403
|
-
params: AsyncSingleParams,
|
|
404
|
-
): AsyncExecutionResult {
|
|
405
|
-
const {
|
|
406
|
-
agent,
|
|
407
|
-
agentConfig,
|
|
408
|
-
ctx,
|
|
409
|
-
cwd,
|
|
410
|
-
maxOutput,
|
|
411
|
-
artifactsDir,
|
|
412
|
-
artifactConfig,
|
|
413
|
-
shareEnabled,
|
|
414
|
-
sessionRoot,
|
|
415
|
-
sessionFile,
|
|
416
|
-
maxSubagentDepth,
|
|
417
|
-
worktreeSetupHook,
|
|
418
|
-
worktreeSetupHookTimeoutMs,
|
|
419
|
-
controlConfig,
|
|
420
|
-
controlIntercomTarget,
|
|
421
|
-
childIntercomTarget,
|
|
422
|
-
} = params;
|
|
423
|
-
const task = params.task ?? "";
|
|
424
|
-
const runnerCwd = resolveChildCwd(ctx.cwd, cwd);
|
|
425
|
-
const skillNames = params.skills ?? agentConfig.skills ?? [];
|
|
426
|
-
const availableModels = params.availableModels;
|
|
427
|
-
const { resolved: resolvedSkills } = resolveSkillsWithFallback(skillNames, runnerCwd, ctx.cwd);
|
|
428
|
-
let systemPrompt = agentConfig.systemPrompt?.trim() ?? "";
|
|
429
|
-
if (resolvedSkills.length > 0) {
|
|
430
|
-
const injection = buildSkillInjection(resolvedSkills);
|
|
431
|
-
systemPrompt = systemPrompt ? `${systemPrompt}\n\n${injection}` : injection;
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
const asyncDir = path.join(ASYNC_DIR, id);
|
|
435
|
-
try {
|
|
436
|
-
fs.mkdirSync(asyncDir, { recursive: true });
|
|
437
|
-
} catch (error) {
|
|
438
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
439
|
-
return {
|
|
440
|
-
content: [{ type: "text", text: `Failed to create async run directory '${asyncDir}': ${message}` }],
|
|
441
|
-
isError: true,
|
|
442
|
-
details: { mode: "single" as const, results: [] },
|
|
443
|
-
};
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
const outputPath = resolveSingleOutputPath(params.output, ctx.cwd, runnerCwd);
|
|
447
|
-
const taskWithOutputInstruction = injectSingleOutputInstruction(task, outputPath);
|
|
448
|
-
let spawnResult: { pid?: number; error?: string } = {};
|
|
449
|
-
try {
|
|
450
|
-
spawnResult = spawnRunner(
|
|
451
|
-
{
|
|
452
|
-
id,
|
|
453
|
-
steps: [
|
|
454
|
-
{
|
|
455
|
-
agent,
|
|
456
|
-
task: taskWithOutputInstruction,
|
|
457
|
-
cwd: runnerCwd,
|
|
458
|
-
model: applyThinkingSuffix(resolveModelCandidate(params.modelOverride ?? agentConfig.model, availableModels, ctx.currentModelProvider), agentConfig.thinking),
|
|
459
|
-
modelCandidates: buildModelCandidates(params.modelOverride ?? agentConfig.model, agentConfig.fallbackModels, availableModels, ctx.currentModelProvider).map((candidate) =>
|
|
460
|
-
applyThinkingSuffix(candidate, agentConfig.thinking),
|
|
461
|
-
),
|
|
462
|
-
tools: agentConfig.tools,
|
|
463
|
-
extensions: agentConfig.extensions,
|
|
464
|
-
mcpDirectTools: agentConfig.mcpDirectTools,
|
|
465
|
-
systemPrompt,
|
|
466
|
-
systemPromptMode: agentConfig.systemPromptMode,
|
|
467
|
-
inheritProjectContext: agentConfig.inheritProjectContext,
|
|
468
|
-
inheritSkills: agentConfig.inheritSkills,
|
|
469
|
-
skills: resolvedSkills.map((r) => r.name),
|
|
470
|
-
outputPath,
|
|
471
|
-
sessionFile,
|
|
472
|
-
maxSubagentDepth: resolveChildMaxSubagentDepth(maxSubagentDepth, agentConfig.maxSubagentDepth),
|
|
473
|
-
},
|
|
474
|
-
],
|
|
475
|
-
resultPath: path.join(RESULTS_DIR, `${id}.json`),
|
|
476
|
-
cwd: runnerCwd,
|
|
477
|
-
placeholder: "{previous}",
|
|
478
|
-
maxOutput,
|
|
479
|
-
artifactsDir: artifactConfig.enabled ? artifactsDir : undefined,
|
|
480
|
-
artifactConfig,
|
|
481
|
-
share: shareEnabled,
|
|
482
|
-
sessionDir: sessionRoot ? path.join(sessionRoot, `async-${id}`) : undefined,
|
|
483
|
-
asyncDir,
|
|
484
|
-
sessionId: ctx.currentSessionId,
|
|
485
|
-
piPackageRoot,
|
|
486
|
-
piArgv1: process.argv[1],
|
|
487
|
-
worktreeSetupHook,
|
|
488
|
-
worktreeSetupHookTimeoutMs,
|
|
489
|
-
controlConfig,
|
|
490
|
-
controlIntercomTarget,
|
|
491
|
-
childIntercomTargets: childIntercomTarget ? [childIntercomTarget(agent, 0)] : undefined,
|
|
492
|
-
resultMode: "single",
|
|
493
|
-
},
|
|
494
|
-
id,
|
|
495
|
-
runnerCwd,
|
|
496
|
-
);
|
|
497
|
-
} catch (error) {
|
|
498
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
499
|
-
return formatAsyncStartError("single", `Failed to start async run '${id}': ${message}`);
|
|
500
|
-
}
|
|
501
|
-
|
|
502
|
-
if (spawnResult.error) {
|
|
503
|
-
return formatAsyncStartError("single", `Failed to start async run '${id}': ${spawnResult.error}`);
|
|
504
|
-
}
|
|
505
|
-
|
|
506
|
-
if (spawnResult.pid) {
|
|
507
|
-
ctx.pi.events.emit(SUBAGENT_ASYNC_STARTED_EVENT, {
|
|
508
|
-
id,
|
|
509
|
-
pid: spawnResult.pid,
|
|
510
|
-
agent,
|
|
511
|
-
task: task?.slice(0, 50),
|
|
512
|
-
cwd: runnerCwd,
|
|
513
|
-
asyncDir,
|
|
514
|
-
});
|
|
515
|
-
}
|
|
516
|
-
|
|
517
|
-
return {
|
|
518
|
-
content: [{ type: "text", text: `Async: ${agent} [${id}]` }],
|
|
519
|
-
details: { mode: "single", results: [], asyncId: id, asyncDir },
|
|
520
|
-
};
|
|
521
|
-
}
|