@mystilleef/pi-subagent 0.8.0 → 0.10.1
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 +76 -27
- package/package.json +9 -9
- package/src/agent/agent-cache.ts +1 -0
- package/src/agent/agents.ts +25 -4
- package/src/child/child-events.ts +22 -20
- package/src/child/process.ts +288 -125
- package/src/child/termination.ts +320 -5
- package/src/env.d.ts +15 -0
- package/src/notification/delivery.ts +231 -0
- package/src/notification/desktop-notification.ts +73 -0
- package/src/orchestration/run-command.ts +1 -1
- package/src/orchestration/run-registry.ts +1 -2
- package/src/orchestration/subagent-orchestrator.ts +57 -82
- package/src/output/normalize.ts +2 -2
- package/src/output/ui.ts +17 -28
- package/src/progress/progress-format.ts +111 -0
- package/src/progress/progress-state.ts +55 -140
- package/src/progress/progress.ts +40 -31
- package/src/progress/result-details.ts +146 -39
- package/src/shared/types.ts +18 -16
- package/src/shared/utils.ts +60 -11
- package/tsconfig.json +9 -11
package/src/shared/types.ts
CHANGED
|
@@ -2,6 +2,8 @@ import type { Message } from "@earendil-works/pi-ai";
|
|
|
2
2
|
import type { AgentScope } from "../agent/agents.js";
|
|
3
3
|
import type { TerminationMetadata } from "../child/termination.js";
|
|
4
4
|
|
|
5
|
+
export const TOOL_RESULT_FAILED_MESSAGE = "Subagent tool result failed.";
|
|
6
|
+
|
|
5
7
|
export interface UsageStats {
|
|
6
8
|
input: number;
|
|
7
9
|
output: number;
|
|
@@ -15,9 +17,9 @@ export interface UsageStats {
|
|
|
15
17
|
|
|
16
18
|
export interface ToolActivity {
|
|
17
19
|
toolName: string;
|
|
18
|
-
inputSummary?: string;
|
|
19
|
-
instanceName?: string;
|
|
20
|
-
child?: ToolActivity;
|
|
20
|
+
inputSummary?: string | undefined;
|
|
21
|
+
instanceName?: string | undefined;
|
|
22
|
+
child?: ToolActivity | undefined;
|
|
21
23
|
}
|
|
22
24
|
|
|
23
25
|
export interface StreamingProgressToolCall {
|
|
@@ -26,30 +28,30 @@ export interface StreamingProgressToolCall {
|
|
|
26
28
|
}
|
|
27
29
|
|
|
28
30
|
export interface StreamingProgress {
|
|
29
|
-
activityText?: string;
|
|
30
|
-
activeToolActivity?: ToolActivity;
|
|
31
|
+
activityText?: string | undefined;
|
|
32
|
+
activeToolActivity?: ToolActivity | undefined;
|
|
31
33
|
toolCalls: StreamingProgressToolCall[];
|
|
32
|
-
lastToolPreview?: string;
|
|
33
|
-
toolResultCompleted?: boolean;
|
|
34
|
+
lastToolPreview?: string | undefined;
|
|
35
|
+
toolResultCompleted?: boolean | undefined;
|
|
34
36
|
}
|
|
35
37
|
|
|
36
38
|
export interface SingleResult {
|
|
37
39
|
agent: string;
|
|
38
|
-
instanceName?: string;
|
|
40
|
+
instanceName?: string | undefined;
|
|
39
41
|
agentSource: "user" | "project" | "unknown";
|
|
40
42
|
task: string;
|
|
41
43
|
exitCode: number;
|
|
42
44
|
finalOutput: string;
|
|
43
45
|
stderr: string;
|
|
44
46
|
usage: UsageStats;
|
|
45
|
-
model?: string;
|
|
46
|
-
stopReason?: string;
|
|
47
|
-
errorMessage?: string;
|
|
48
|
-
durationMs?: number;
|
|
49
|
-
progress?: StreamingProgress;
|
|
50
|
-
messages?: Message[];
|
|
51
|
-
termination?: TerminationMetadata;
|
|
52
|
-
thinkingWarning?: string;
|
|
47
|
+
model?: string | undefined;
|
|
48
|
+
stopReason?: string | undefined;
|
|
49
|
+
errorMessage?: string | undefined;
|
|
50
|
+
durationMs?: number | undefined;
|
|
51
|
+
progress?: StreamingProgress | undefined;
|
|
52
|
+
messages?: Message[] | undefined;
|
|
53
|
+
termination?: TerminationMetadata | undefined;
|
|
54
|
+
thinkingWarning?: string | undefined;
|
|
53
55
|
}
|
|
54
56
|
|
|
55
57
|
export interface SubagentDetails {
|
package/src/shared/utils.ts
CHANGED
|
@@ -6,38 +6,67 @@ import {
|
|
|
6
6
|
DefaultResourceLoader,
|
|
7
7
|
getAgentDir,
|
|
8
8
|
} from "@earendil-works/pi-coding-agent";
|
|
9
|
+
import type { SingleResult } from "./types.js";
|
|
9
10
|
|
|
10
11
|
export const DEFAULT_MAX_OUTPUT_BYTES = 50_000;
|
|
11
12
|
export const DEFAULT_MAX_OUTPUT_LINES = 500;
|
|
13
|
+
export const DEFAULT_AGENT_END_GRACE_MS = 250;
|
|
14
|
+
export const DEFAULT_MAX_STDERR_BYTES = 10_000;
|
|
15
|
+
export const DEFAULT_MAX_SUBAGENT_DEPTH = 3;
|
|
16
|
+
const MAX_SUBAGENT_DEPTH_CEILING = 10;
|
|
12
17
|
|
|
13
18
|
export interface SubagentOutputLimits {
|
|
14
19
|
maxBytes: number;
|
|
15
20
|
maxLines: number;
|
|
16
21
|
}
|
|
17
22
|
|
|
18
|
-
|
|
23
|
+
export interface SubagentRuntimeLimits {
|
|
24
|
+
agentEndGraceMs: number;
|
|
25
|
+
maxStderrBytes: number;
|
|
26
|
+
maxDepth: number;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
type EnvLimitConfig = Partial<Record<string, string | number | undefined>>;
|
|
19
30
|
|
|
20
31
|
function parsePositiveInteger(
|
|
21
32
|
value: string | number | undefined,
|
|
22
33
|
): number | undefined {
|
|
23
34
|
const parsed = typeof value === "number" ? value : Number(value);
|
|
24
|
-
if (!Number.isFinite(parsed) || parsed < 1)
|
|
25
|
-
|
|
35
|
+
if (!Number.isFinite(parsed) || !Number.isInteger(parsed) || parsed < 1)
|
|
36
|
+
return undefined;
|
|
37
|
+
return parsed;
|
|
26
38
|
}
|
|
27
39
|
|
|
28
40
|
export function getSubagentOutputLimits(
|
|
29
|
-
config:
|
|
41
|
+
config: EnvLimitConfig = process.env,
|
|
30
42
|
): SubagentOutputLimits {
|
|
31
43
|
return {
|
|
32
44
|
maxBytes:
|
|
33
|
-
parsePositiveInteger(config
|
|
45
|
+
parsePositiveInteger(config["PI_SUBAGENT_MAX_OUTPUT_BYTES"]) ??
|
|
34
46
|
DEFAULT_MAX_OUTPUT_BYTES,
|
|
35
47
|
maxLines:
|
|
36
|
-
parsePositiveInteger(config
|
|
48
|
+
parsePositiveInteger(config["PI_SUBAGENT_MAX_OUTPUT_LINES"]) ??
|
|
37
49
|
DEFAULT_MAX_OUTPUT_LINES,
|
|
38
50
|
};
|
|
39
51
|
}
|
|
40
52
|
|
|
53
|
+
export function getSubagentRuntimeLimits(
|
|
54
|
+
config: EnvLimitConfig = process.env,
|
|
55
|
+
): SubagentRuntimeLimits {
|
|
56
|
+
const maxDepth =
|
|
57
|
+
parsePositiveInteger(config["PI_SUBAGENT_MAX_DEPTH"]) ??
|
|
58
|
+
DEFAULT_MAX_SUBAGENT_DEPTH;
|
|
59
|
+
return {
|
|
60
|
+
agentEndGraceMs:
|
|
61
|
+
parsePositiveInteger(config["PI_SUBAGENT_AGENT_END_GRACE_MS"]) ??
|
|
62
|
+
DEFAULT_AGENT_END_GRACE_MS,
|
|
63
|
+
maxStderrBytes:
|
|
64
|
+
parsePositiveInteger(config["PI_SUBAGENT_MAX_STDERR_BYTES"]) ??
|
|
65
|
+
DEFAULT_MAX_STDERR_BYTES,
|
|
66
|
+
maxDepth: Math.min(maxDepth, MAX_SUBAGENT_DEPTH_CEILING),
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
41
70
|
export function truncateOutput(
|
|
42
71
|
text: string,
|
|
43
72
|
limits: SubagentOutputLimits = getSubagentOutputLimits(),
|
|
@@ -102,6 +131,7 @@ async function canonicalPath(filePath: string): Promise<string> {
|
|
|
102
131
|
try {
|
|
103
132
|
return await fs.promises.realpath(filePath);
|
|
104
133
|
} catch {
|
|
134
|
+
/* symlinks or missing paths fall back to absolute path resolution */
|
|
105
135
|
return path.resolve(filePath);
|
|
106
136
|
}
|
|
107
137
|
}
|
|
@@ -181,18 +211,27 @@ export function subagentDepthEnv(): Record<string, string> {
|
|
|
181
211
|
return { PI_SUBAGENT_DEPTH: String(getSubagentDepth() + 1) };
|
|
182
212
|
}
|
|
183
213
|
|
|
184
|
-
export function
|
|
185
|
-
let lastAssistantIdx = -1;
|
|
214
|
+
export function findLastAssistantTextMessage(messages: Message[]): number {
|
|
186
215
|
for (let i = messages.length - 1; i >= 0; i--) {
|
|
187
216
|
const msg = messages[i];
|
|
188
217
|
if (
|
|
189
218
|
msg?.role === "assistant" &&
|
|
190
|
-
msg.content
|
|
219
|
+
Array.isArray(msg.content) &&
|
|
220
|
+
msg.content.some(
|
|
221
|
+
(c) =>
|
|
222
|
+
c.type === "text" &&
|
|
223
|
+
typeof c.text === "string" &&
|
|
224
|
+
c.text.trim().length > 0,
|
|
225
|
+
)
|
|
191
226
|
) {
|
|
192
|
-
|
|
193
|
-
break;
|
|
227
|
+
return i;
|
|
194
228
|
}
|
|
195
229
|
}
|
|
230
|
+
return -1;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
export function detectMessageError(messages: Message[]): boolean {
|
|
234
|
+
const lastAssistantIdx = findLastAssistantTextMessage(messages);
|
|
196
235
|
const from = lastAssistantIdx >= 0 ? lastAssistantIdx + 1 : 0;
|
|
197
236
|
for (let i = messages.length - 1; i >= from; i--) {
|
|
198
237
|
const msg = messages[i];
|
|
@@ -200,3 +239,13 @@ export function detectMessageError(messages: Message[]): boolean {
|
|
|
200
239
|
}
|
|
201
240
|
return false;
|
|
202
241
|
}
|
|
242
|
+
|
|
243
|
+
export function hasSubagentFailed(result: SingleResult): boolean {
|
|
244
|
+
return (
|
|
245
|
+
result.exitCode !== 0 ||
|
|
246
|
+
result.stopReason === "error" ||
|
|
247
|
+
result.stopReason === "aborted" ||
|
|
248
|
+
Boolean(result.errorMessage?.trim()) ||
|
|
249
|
+
detectMessageError(result.messages ?? [])
|
|
250
|
+
);
|
|
251
|
+
}
|
package/tsconfig.json
CHANGED
|
@@ -1,30 +1,28 @@
|
|
|
1
1
|
{
|
|
2
2
|
"compilerOptions": {
|
|
3
|
-
// Environment setup & latest features
|
|
4
3
|
"lib": ["ESNext"],
|
|
5
4
|
"target": "ESNext",
|
|
6
5
|
"module": "Preserve",
|
|
7
6
|
"moduleDetection": "force",
|
|
8
7
|
"jsx": "react-jsx",
|
|
9
|
-
"allowJs": true,
|
|
10
8
|
"types": ["bun"],
|
|
11
|
-
|
|
12
|
-
// Bundler mode
|
|
13
9
|
"moduleResolution": "bundler",
|
|
14
10
|
"allowImportingTsExtensions": true,
|
|
15
11
|
"verbatimModuleSyntax": true,
|
|
16
12
|
"noEmit": true,
|
|
17
|
-
|
|
18
|
-
// Best practices
|
|
19
13
|
"strict": true,
|
|
20
14
|
"skipLibCheck": true,
|
|
21
15
|
"noFallthroughCasesInSwitch": true,
|
|
22
16
|
"noUncheckedIndexedAccess": true,
|
|
23
17
|
"noImplicitOverride": true,
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
"
|
|
27
|
-
"
|
|
28
|
-
"
|
|
18
|
+
"noImplicitReturns": true,
|
|
19
|
+
"erasableSyntaxOnly": true,
|
|
20
|
+
"forceConsistentCasingInFileNames": true,
|
|
21
|
+
"noUnusedLocals": true,
|
|
22
|
+
"noUnusedParameters": true,
|
|
23
|
+
"noPropertyAccessFromIndexSignature": true,
|
|
24
|
+
"exactOptionalPropertyTypes": true,
|
|
25
|
+
"allowUnreachableCode": false,
|
|
26
|
+
"allowUnusedLabels": false
|
|
29
27
|
}
|
|
30
28
|
}
|