@ryan_nookpi/pi-extension-subagent 0.5.5 → 0.5.6
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 +1 -1
- package/failure-telemetry.ts +12 -0
- package/package.json +1 -1
- package/retry.ts +10 -0
- package/starter-pack.ts +1 -2
- package/tool-execute.ts +54 -37
- package/seeds/agents/simplifier.md +0 -68
package/README.md
CHANGED
|
@@ -68,7 +68,7 @@ Run `/subagents`, `subagent agents`, or the `list-agents` tool to inspect discov
|
|
|
68
68
|
|
|
69
69
|
### Optional starter pack
|
|
70
70
|
|
|
71
|
-
If no agents are found, `/subagents` can offer an optional, opinionated starter pack. It copies
|
|
71
|
+
If no agents are found, `/subagents` can offer an optional, opinionated starter pack. It copies eight agent templates, two example workflow skills, and missing global `subagent` settings. Existing files and configured values are not overwritten.
|
|
72
72
|
|
|
73
73
|
The starter pack is not required. Decline it if you prefer to define agents manually. It fills a missing `claudeRuntime` setting with `cli`; without that setting, the extension default is `sdk`.
|
|
74
74
|
|
package/failure-telemetry.ts
CHANGED
|
@@ -5,6 +5,7 @@ export type SubagentErrorClass =
|
|
|
5
5
|
| "overloaded"
|
|
6
6
|
| "rate_limit"
|
|
7
7
|
| "tool_error"
|
|
8
|
+
| "network_error"
|
|
8
9
|
| "aborted"
|
|
9
10
|
| "process_error"
|
|
10
11
|
| "unknown";
|
|
@@ -64,6 +65,9 @@ const TOOL_ERROR_PATTERNS = [
|
|
|
64
65
|
/invalid tool/i,
|
|
65
66
|
];
|
|
66
67
|
|
|
68
|
+
const NETWORK_PATTERNS = [/\bwebsocket\b/i, /\bconnection error\b/i];
|
|
69
|
+
const PROCESS_SIGNAL_TERMINATION_PATTERN = /\bprocess terminated by signal\b/i;
|
|
70
|
+
|
|
67
71
|
export function classifySubagentFailure(input: FailureTelemetryInput): SubagentErrorClass | undefined {
|
|
68
72
|
if (!input.failed) return undefined;
|
|
69
73
|
const text = [input.errorMessage, input.stderr, input.output].filter(Boolean).join("\n");
|
|
@@ -73,6 +77,14 @@ export function classifySubagentFailure(input: FailureTelemetryInput): SubagentE
|
|
|
73
77
|
if (OVERLOADED_PATTERNS.some((pattern) => pattern.test(text))) return "overloaded";
|
|
74
78
|
if (RATE_LIMIT_PATTERNS.some((pattern) => pattern.test(text))) return "rate_limit";
|
|
75
79
|
if (TOOL_ERROR_PATTERNS.some((pattern) => pattern.test(text))) return "tool_error";
|
|
80
|
+
const signalTerminated = PROCESS_SIGNAL_TERMINATION_PATTERN.test(input.stderr ?? "");
|
|
81
|
+
if (
|
|
82
|
+
!signalTerminated &&
|
|
83
|
+
(NETWORK_PATTERNS.some((pattern) => pattern.test(text)) ||
|
|
84
|
+
/^terminated\.?$/i.test(input.errorMessage?.trim() ?? ""))
|
|
85
|
+
) {
|
|
86
|
+
return "network_error";
|
|
87
|
+
}
|
|
76
88
|
if ((input.exitCode ?? 0) !== 0) return "process_error";
|
|
77
89
|
return "unknown";
|
|
78
90
|
}
|
package/package.json
CHANGED
package/retry.ts
CHANGED
|
@@ -34,9 +34,13 @@ function buildFailureText(result: SingleResult): string {
|
|
|
34
34
|
.join("\n");
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
+
const PROCESS_SIGNAL_TERMINATION_PATTERN = /\bprocess terminated by signal\b/i;
|
|
38
|
+
|
|
37
39
|
const TRANSIENT_FAILURE_PATTERNS: RegExp[] = [
|
|
38
40
|
/\bnetwork\b/i,
|
|
41
|
+
/\bwebsocket\b/i,
|
|
39
42
|
/\bfetch failed\b/i,
|
|
43
|
+
/\bconnection error\b/i,
|
|
40
44
|
/\bconnection reset\b/i,
|
|
41
45
|
/\bsocket hang up\b/i,
|
|
42
46
|
/\btemporar(?:y|ily) unavailable\b/i,
|
|
@@ -70,12 +74,18 @@ function matchesTransientPattern(text: string): boolean {
|
|
|
70
74
|
|
|
71
75
|
export function diagnoseRetryableResult(result: SingleResult): RetryDecision {
|
|
72
76
|
if (result.stopReason === "aborted") return { retryable: false };
|
|
77
|
+
if (PROCESS_SIGNAL_TERMINATION_PATTERN.test(result.stderr)) return { retryable: false };
|
|
73
78
|
|
|
74
79
|
const finalOutput = getFinalOutput(result.messages).trim();
|
|
75
80
|
if (result.exitCode === 0 && result.stopReason !== "error" && finalOutput) {
|
|
76
81
|
return { retryable: false };
|
|
77
82
|
}
|
|
78
83
|
|
|
84
|
+
const errorMessage = result.errorMessage?.trim();
|
|
85
|
+
if (errorMessage && /^terminated\.?$/i.test(errorMessage)) {
|
|
86
|
+
return { retryable: true, reason: errorMessage };
|
|
87
|
+
}
|
|
88
|
+
|
|
79
89
|
const failureText = buildFailureText(result);
|
|
80
90
|
if (!failureText) return { retryable: false };
|
|
81
91
|
if (!matchesTransientPattern(failureText)) return { retryable: false };
|
package/starter-pack.ts
CHANGED
|
@@ -11,7 +11,6 @@ export const STARTER_AGENT_NAMES = [
|
|
|
11
11
|
"reviewer",
|
|
12
12
|
"searcher",
|
|
13
13
|
"security-auditor",
|
|
14
|
-
"simplifier",
|
|
15
14
|
"verifier",
|
|
16
15
|
"worker",
|
|
17
16
|
] as const;
|
|
@@ -220,7 +219,7 @@ export async function offerStarterPackIfEmpty(
|
|
|
220
219
|
|
|
221
220
|
const accepted = await ctx.ui.confirm(
|
|
222
221
|
"Install starter subagents?",
|
|
223
|
-
"No subagent definitions were found. Install
|
|
222
|
+
"No subagent definitions were found. Install 8 portable English agents, the stress-interview and self-healing skills, and missing subagent settings? Existing files and configured values will not be overwritten.",
|
|
224
223
|
);
|
|
225
224
|
if (!accepted) return { status: "declined", discovery };
|
|
226
225
|
|
package/tool-execute.ts
CHANGED
|
@@ -49,6 +49,7 @@ import {
|
|
|
49
49
|
import { clearPendingGroupCompletion, upsertPendingGroupCompletion } from "./group-pending.js";
|
|
50
50
|
import { enqueueSubagentInvocation } from "./invocation-queue.js";
|
|
51
51
|
import { appendDisplayTaskUpdate, getSessionFileSize } from "./persisted-session.js";
|
|
52
|
+
import { invokeWithAutoRetry, MAX_SUBAGENT_AUTO_RETRIES } from "./retry.js";
|
|
52
53
|
import {
|
|
53
54
|
clearFinishedRuns,
|
|
54
55
|
evictStaleFinishedGroups,
|
|
@@ -1416,44 +1417,60 @@ export function createSubagentToolExecute(pi: ExtensionAPI, store: SubagentStore
|
|
|
1416
1417
|
let claudeCheckpointSent = !!runState.claudeSessionId;
|
|
1417
1418
|
const abortSignal = runState.abortController?.signal;
|
|
1418
1419
|
const recordActivity = createRunActivityRecorder(pi, runState, (model) => resolveContextWindow(ctx, model));
|
|
1419
|
-
return
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
runState.
|
|
1425
|
-
|
|
1426
|
-
runState.
|
|
1427
|
-
|
|
1428
|
-
(
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1420
|
+
return invokeWithAutoRetry({
|
|
1421
|
+
maxRetries: MAX_SUBAGENT_AUTO_RETRIES,
|
|
1422
|
+
signal: abortSignal,
|
|
1423
|
+
onRetryScheduled: ({ retryIndex, maxRetries, delayMs, reason }) => {
|
|
1424
|
+
runState.retryCount = retryIndex;
|
|
1425
|
+
runState.lastRetryReason = reason;
|
|
1426
|
+
runState.lastActivityAt = Date.now();
|
|
1427
|
+
runState.lastLine = `Auto-retrying ${retryIndex}/${maxRetries} in ${Math.ceil(delayMs / 1000)}s: ${reason}`;
|
|
1428
|
+
runState.lastOutput = runState.lastLine;
|
|
1429
|
+
updateCommandRunsWidget(store);
|
|
1430
|
+
},
|
|
1431
|
+
invoke: () => {
|
|
1432
|
+
runState.persistedSessionBaseOffset = getSessionFileSize(runState.sessionFile);
|
|
1433
|
+
return enqueueSubagentInvocation(async () => {
|
|
1434
|
+
if (runState.removed || abortSignal?.aborted) throw new Error("Subagent was aborted");
|
|
1435
|
+
return runSingleAgent(
|
|
1436
|
+
ctx.cwd,
|
|
1437
|
+
agents,
|
|
1438
|
+
runState.agent,
|
|
1439
|
+
taskForAgent,
|
|
1440
|
+
runState.pipelineStepIndex,
|
|
1441
|
+
abortSignal,
|
|
1442
|
+
(partial) => {
|
|
1443
|
+
if (runState.removed || store.disposed) return;
|
|
1444
|
+
const current = partial.details?.results?.[0];
|
|
1445
|
+
if (!current) return;
|
|
1446
|
+
updateRunFromResult(runState, current);
|
|
1447
|
+
recordActivity();
|
|
1448
|
+
if (shouldRunAsync && !claudeCheckpointSent && runState.claudeSessionId) {
|
|
1449
|
+
claudeCheckpointSent = true;
|
|
1450
|
+
pi.sendMessage(buildRunStartMessage(runState, "started"), {
|
|
1451
|
+
deliverAs: "followUp",
|
|
1452
|
+
triggerTurn: false,
|
|
1453
|
+
});
|
|
1454
|
+
}
|
|
1455
|
+
updateCommandRunsWidget(store);
|
|
1456
|
+
},
|
|
1457
|
+
(results) => makeDetails(mode, results),
|
|
1458
|
+
{
|
|
1459
|
+
sessionFile: runState.sessionFile,
|
|
1460
|
+
resumeSessionId: runState.claudeSessionId,
|
|
1461
|
+
sidecarSessionFile: runState.sessionFile,
|
|
1462
|
+
persistedSessionBaseOffset: runState.persistedSessionBaseOffset,
|
|
1463
|
+
onDiagnostic: createRunDiagnosticSink(pi, runState),
|
|
1464
|
+
},
|
|
1465
|
+
);
|
|
1466
|
+
});
|
|
1467
|
+
},
|
|
1468
|
+
}).then(({ result, retryCount }) => {
|
|
1469
|
+
runState.retryCount = retryCount;
|
|
1470
|
+
return runState.removed || abortSignal?.aborted
|
|
1454
1471
|
? finalizeRunError(runState, new Error("Subagent was aborted"))
|
|
1455
|
-
: finalizeRunState(runState, result)
|
|
1456
|
-
);
|
|
1472
|
+
: finalizeRunState(runState, result);
|
|
1473
|
+
});
|
|
1457
1474
|
}
|
|
1458
1475
|
|
|
1459
1476
|
if (hasSingle) {
|
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: simplifier
|
|
3
|
-
description: Code simplification specialist that improves clarity while preserving behavior
|
|
4
|
-
runtime: pi
|
|
5
|
-
thinking: high
|
|
6
|
-
tools: read,grep,find,ls,bash,edit,write
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
<system_prompt agent="simplifier">
|
|
10
|
-
<identity>
|
|
11
|
-
You simplify recently modified code for clarity, consistency, and maintainability without changing observable behavior.
|
|
12
|
-
</identity>
|
|
13
|
-
|
|
14
|
-
<scope_rule>
|
|
15
|
-
<rule>Only simplify the requested or clearly identified changed scope.</rule>
|
|
16
|
-
<rule>Do not broaden into unrelated cleanup or architecture changes.</rule>
|
|
17
|
-
<rule>Preserve outputs, side effects, public contracts, and data flow.</rule>
|
|
18
|
-
<rule>Handle each supplied finding independently; one blocked item must not stop unrelated safe items.</rule>
|
|
19
|
-
</scope_rule>
|
|
20
|
-
|
|
21
|
-
<principles>
|
|
22
|
-
<rule>Prefer explicit, readable control flow over clever compression.</rule>
|
|
23
|
-
<rule>Reduce unnecessary nesting, indirection, and dead intermediates.</rule>
|
|
24
|
-
<rule>Keep abstractions that improve organization, testing, or reuse.</rule>
|
|
25
|
-
<rule>Follow existing repository patterns before introducing a new style.</rule>
|
|
26
|
-
<rule>Choose a no-op over churn when the code is already clear.</rule>
|
|
27
|
-
</principles>
|
|
28
|
-
|
|
29
|
-
<allowed_efficiency_changes>
|
|
30
|
-
<item>Parallelize operations only when independence and ordering are proven.</item>
|
|
31
|
-
<item>Add change-detection guards to avoid no-op updates.</item>
|
|
32
|
-
<item>Remove preflight existence checks when direct operation plus error handling is safer.</item>
|
|
33
|
-
<item>Use an existing batch API when it is behaviorally equivalent.</item>
|
|
34
|
-
<item>Narrow overly broad reads when equivalence is clear.</item>
|
|
35
|
-
</allowed_efficiency_changes>
|
|
36
|
-
|
|
37
|
-
<workflow>
|
|
38
|
-
<step index="1">Identify the exact file regions or enumerate supplied findings.</step>
|
|
39
|
-
<step index="2">Read surrounding code and any utility proposed for reuse.</step>
|
|
40
|
-
<step index="3">Choose the smallest behavior-preserving change for each item.</step>
|
|
41
|
-
<step index="4">Edit only the necessary files.</step>
|
|
42
|
-
<step index="5">Run targeted tests, type checking, linting, and build checks.</step>
|
|
43
|
-
<step index="6">Report each item as applied, skipped, or escalated.</step>
|
|
44
|
-
</workflow>
|
|
45
|
-
|
|
46
|
-
<rules>
|
|
47
|
-
<rule>Do not force a utility substitution when null handling or edge behavior differs.</rule>
|
|
48
|
-
<rule>Do not suppress type errors.</rule>
|
|
49
|
-
<rule>Escalate items that require behavior change or cross-module redesign.</rule>
|
|
50
|
-
<rule>Fix only issues introduced by the requested change.</rule>
|
|
51
|
-
</rules>
|
|
52
|
-
|
|
53
|
-
<output_template>
|
|
54
|
-
<![CDATA[
|
|
55
|
-
### Applied
|
|
56
|
-
- `path:start-end` — {change}
|
|
57
|
-
|
|
58
|
-
### Skipped
|
|
59
|
-
- {item} — {reason}
|
|
60
|
-
|
|
61
|
-
### Escalate
|
|
62
|
-
- {item} — {reason}
|
|
63
|
-
|
|
64
|
-
### Residual Risk
|
|
65
|
-
- {risk or none}
|
|
66
|
-
]]>
|
|
67
|
-
</output_template>
|
|
68
|
-
</system_prompt>
|