@ryan_nookpi/pi-extension-subagent 0.5.0 → 0.5.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/activity.ts +22 -5
- package/agents.ts +24 -19
- package/commands.ts +13 -2
- package/package.json +1 -1
- package/tool-execute.ts +13 -2
- package/utils/agent-utils.ts +4 -6
package/activity.ts
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
9
9
|
import type { CommandRunState } from "./types.js";
|
|
10
|
+
import { getUsedContextPercent } from "./utils/format-utils.js";
|
|
10
11
|
|
|
11
12
|
export const SUBAGENT_ACTIVITY_CUSTOM_TYPE = "subagent-activity";
|
|
12
13
|
export const SUBAGENT_ACTIVITY_SCHEMA_VERSION = 1;
|
|
@@ -24,6 +25,7 @@ export interface ActivitySnapshot {
|
|
|
24
25
|
toolCallCount: number;
|
|
25
26
|
lastLine?: string;
|
|
26
27
|
contextTokens?: number;
|
|
28
|
+
contextWindow?: number;
|
|
27
29
|
}
|
|
28
30
|
|
|
29
31
|
export interface ActivityPayload {
|
|
@@ -36,12 +38,15 @@ export interface ActivityPayload {
|
|
|
36
38
|
toolCallCount: number;
|
|
37
39
|
lastLine?: string;
|
|
38
40
|
contextTokens?: number;
|
|
41
|
+
contextWindow?: number;
|
|
42
|
+
contextPercent?: number;
|
|
39
43
|
}
|
|
40
44
|
|
|
41
45
|
export interface ActivityThrottleState {
|
|
42
46
|
lastEmittedAt: number;
|
|
43
47
|
lastEmittedToolName?: string;
|
|
44
48
|
lastEmittedToolCallCount?: number;
|
|
49
|
+
lastEmittedContextPercent?: number;
|
|
45
50
|
}
|
|
46
51
|
|
|
47
52
|
export function createActivityThrottleState(): ActivityThrottleState {
|
|
@@ -69,8 +74,8 @@ export function normalizeActivityLine(text: string | undefined): string | undefi
|
|
|
69
74
|
/**
|
|
70
75
|
* Change-driven throttle policy. Returns a payload to emit, or null.
|
|
71
76
|
*
|
|
72
|
-
* Emits only while the run is `running`,
|
|
73
|
-
*
|
|
77
|
+
* Emits only while the run is `running`, when tool metadata or the displayed
|
|
78
|
+
* context percentage changes, and at most once per
|
|
74
79
|
* `ACTIVITY_MIN_INTERVAL_MS` per run. A change suppressed by the interval
|
|
75
80
|
* stays pending and emits on a later evaluation.
|
|
76
81
|
*/
|
|
@@ -84,14 +89,18 @@ export function evaluateActivityEmission(
|
|
|
84
89
|
// on the run state, so a positive count is required regardless of the name.
|
|
85
90
|
if (snapshot.toolCallCount <= 0) return null;
|
|
86
91
|
|
|
92
|
+
const contextPercent = getUsedContextPercent(snapshot.contextTokens, snapshot.contextWindow);
|
|
87
93
|
const changed =
|
|
88
|
-
snapshot.lastToolName !== state.lastEmittedToolName ||
|
|
94
|
+
snapshot.lastToolName !== state.lastEmittedToolName ||
|
|
95
|
+
snapshot.toolCallCount !== state.lastEmittedToolCallCount ||
|
|
96
|
+
contextPercent !== state.lastEmittedContextPercent;
|
|
89
97
|
if (!changed) return null;
|
|
90
98
|
if (state.lastEmittedAt !== 0 && now - state.lastEmittedAt < ACTIVITY_MIN_INTERVAL_MS) return null;
|
|
91
99
|
|
|
92
100
|
state.lastEmittedAt = now;
|
|
93
101
|
state.lastEmittedToolName = snapshot.lastToolName;
|
|
94
102
|
state.lastEmittedToolCallCount = snapshot.toolCallCount;
|
|
103
|
+
state.lastEmittedContextPercent = contextPercent;
|
|
95
104
|
|
|
96
105
|
const payload: ActivityPayload = {
|
|
97
106
|
runId: snapshot.runId,
|
|
@@ -105,10 +114,12 @@ export function evaluateActivityEmission(
|
|
|
105
114
|
const lastLine = normalizeActivityLine(snapshot.lastLine);
|
|
106
115
|
if (lastLine) payload.lastLine = lastLine;
|
|
107
116
|
if (snapshot.contextTokens != null && snapshot.contextTokens > 0) payload.contextTokens = snapshot.contextTokens;
|
|
117
|
+
if (snapshot.contextWindow != null && snapshot.contextWindow > 0) payload.contextWindow = snapshot.contextWindow;
|
|
118
|
+
if (contextPercent !== undefined) payload.contextPercent = contextPercent;
|
|
108
119
|
return payload;
|
|
109
120
|
}
|
|
110
121
|
|
|
111
|
-
export function snapshotFromRunState(runState: CommandRunState): ActivitySnapshot {
|
|
122
|
+
export function snapshotFromRunState(runState: CommandRunState, contextWindow?: number): ActivitySnapshot {
|
|
112
123
|
return {
|
|
113
124
|
status: runState.status,
|
|
114
125
|
runId: runState.id,
|
|
@@ -120,6 +131,7 @@ export function snapshotFromRunState(runState: CommandRunState): ActivitySnapsho
|
|
|
120
131
|
toolCallCount: runState.toolCalls,
|
|
121
132
|
lastLine: runState.lastLine,
|
|
122
133
|
contextTokens: runState.usage?.contextTokens,
|
|
134
|
+
contextWindow,
|
|
123
135
|
};
|
|
124
136
|
}
|
|
125
137
|
|
|
@@ -130,11 +142,16 @@ export function snapshotFromRunState(runState: CommandRunState): ActivitySnapsho
|
|
|
130
142
|
export function createRunActivityRecorder(
|
|
131
143
|
pi: Pick<ExtensionAPI, "appendEntry">,
|
|
132
144
|
runState: CommandRunState,
|
|
145
|
+
resolveContextWindow?: (model?: string) => number | undefined,
|
|
133
146
|
): () => void {
|
|
134
147
|
const throttleState = createActivityThrottleState();
|
|
135
148
|
return () => {
|
|
136
149
|
try {
|
|
137
|
-
const payload = evaluateActivityEmission(
|
|
150
|
+
const payload = evaluateActivityEmission(
|
|
151
|
+
throttleState,
|
|
152
|
+
snapshotFromRunState(runState, resolveContextWindow?.(runState.model)),
|
|
153
|
+
Date.now(),
|
|
154
|
+
);
|
|
138
155
|
if (!payload) return;
|
|
139
156
|
pi.appendEntry(SUBAGENT_ACTIVITY_CUSTOM_TYPE, {
|
|
140
157
|
schemaVersion: SUBAGENT_ACTIVITY_SCHEMA_VERSION,
|
package/agents.ts
CHANGED
|
@@ -140,25 +140,30 @@ function loadAgentsFromDir(dir: string, source: "user" | "project", options: Loa
|
|
|
140
140
|
continue;
|
|
141
141
|
}
|
|
142
142
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
143
|
+
try {
|
|
144
|
+
const { frontmatter, body } = parseFrontmatter<Record<string, unknown>>(content);
|
|
145
|
+
if (typeof frontmatter.name !== "string" || typeof frontmatter.description !== "string") continue;
|
|
146
|
+
if (!frontmatter.name || !frontmatter.description) continue;
|
|
147
|
+
|
|
148
|
+
const tools = normalizeTools(frontmatter.tools, format);
|
|
149
|
+
const model = normalizeModel(typeof frontmatter.model === "string" ? frontmatter.model : undefined, format);
|
|
150
|
+
const thinking = normalizeThinkingLevel(
|
|
151
|
+
typeof frontmatter.thinking === "string" ? frontmatter.thinking : undefined,
|
|
152
|
+
);
|
|
153
|
+
const runtime: AgentRuntime = frontmatter.runtime === "claude" ? "claude" : "pi";
|
|
154
|
+
|
|
155
|
+
agents.push({
|
|
156
|
+
name: frontmatter.name,
|
|
157
|
+
description: frontmatter.description,
|
|
158
|
+
tools,
|
|
159
|
+
model,
|
|
160
|
+
thinking,
|
|
161
|
+
systemPrompt: attachCommonSubagentRule(body, runtime),
|
|
162
|
+
source,
|
|
163
|
+
filePath,
|
|
164
|
+
runtime,
|
|
165
|
+
});
|
|
166
|
+
} catch {}
|
|
162
167
|
}
|
|
163
168
|
|
|
164
169
|
return agents;
|
package/commands.ts
CHANGED
|
@@ -35,7 +35,14 @@ import {
|
|
|
35
35
|
shouldSummarizeSubagentTask,
|
|
36
36
|
summarizeSubagentDisplayTask,
|
|
37
37
|
} from "./display-task.js";
|
|
38
|
-
import {
|
|
38
|
+
import {
|
|
39
|
+
AGENT_NAME_PALETTE,
|
|
40
|
+
agentBgIndex,
|
|
41
|
+
formatUsageStats,
|
|
42
|
+
resolveContextWindow,
|
|
43
|
+
truncateLines,
|
|
44
|
+
truncatePlainToWidth,
|
|
45
|
+
} from "./format.js";
|
|
39
46
|
import {
|
|
40
47
|
clearPendingGroupCompletion,
|
|
41
48
|
consumePendingGroupCompletionsForSession,
|
|
@@ -1250,6 +1257,8 @@ export function registerAll(pi: ExtensionAPI, store: SubagentStore): SubagentReg
|
|
|
1250
1257
|
runState.model = undefined;
|
|
1251
1258
|
runState.retryCount = 0;
|
|
1252
1259
|
runState.lastRetryReason = undefined;
|
|
1260
|
+
runState.errorClass = undefined;
|
|
1261
|
+
runState.autoAbortReason = undefined;
|
|
1253
1262
|
runState.removed = false;
|
|
1254
1263
|
runState.deliveryMode = deliveryMode;
|
|
1255
1264
|
runState.turnCount = Math.max(DEFAULT_TURN_COUNT, runState.turnCount || DEFAULT_TURN_COUNT) + 1;
|
|
@@ -1400,7 +1409,9 @@ export function registerAll(pi: ExtensionAPI, store: SubagentStore): SubagentReg
|
|
|
1400
1409
|
}, RUN_TICK_INTERVAL_MS);
|
|
1401
1410
|
|
|
1402
1411
|
let claudeCheckpointSent = !!runState.claudeSessionId;
|
|
1403
|
-
const recordActivity = hiddenFromMain
|
|
1412
|
+
const recordActivity = hiddenFromMain
|
|
1413
|
+
? () => {}
|
|
1414
|
+
: createRunActivityRecorder(pi, runState, (model) => resolveContextWindow(ctx, model));
|
|
1404
1415
|
void (async () => {
|
|
1405
1416
|
try {
|
|
1406
1417
|
const { result, retryCount } = await invokeWithAutoRetry({
|
package/package.json
CHANGED
package/tool-execute.ts
CHANGED
|
@@ -1254,6 +1254,10 @@ export function createSubagentToolExecute(pi: ExtensionAPI, store: SubagentStore
|
|
|
1254
1254
|
runState.lastOutput = "";
|
|
1255
1255
|
runState.usage = undefined;
|
|
1256
1256
|
runState.model = undefined;
|
|
1257
|
+
runState.retryCount = 0;
|
|
1258
|
+
runState.lastRetryReason = undefined;
|
|
1259
|
+
runState.errorClass = undefined;
|
|
1260
|
+
runState.autoAbortReason = undefined;
|
|
1257
1261
|
runState.removed = false;
|
|
1258
1262
|
runState.turnCount = Math.max(DEFAULT_TURN_COUNT, runState.turnCount || DEFAULT_TURN_COUNT) + 1;
|
|
1259
1263
|
runState.contextMode = runState.contextMode ?? (config.inheritMainContext ? "main" : "sub");
|
|
@@ -1315,7 +1319,7 @@ export function createSubagentToolExecute(pi: ExtensionAPI, store: SubagentStore
|
|
|
1315
1319
|
|
|
1316
1320
|
function launchRunInBackground(runState: CommandRunState, taskForAgent: string): Promise<FinalizedRun> {
|
|
1317
1321
|
let claudeCheckpointSent = !!runState.claudeSessionId;
|
|
1318
|
-
const recordActivity = createRunActivityRecorder(pi, runState);
|
|
1322
|
+
const recordActivity = createRunActivityRecorder(pi, runState, (model) => resolveContextWindow(ctx, model));
|
|
1319
1323
|
return enqueueSubagentInvocation(() =>
|
|
1320
1324
|
runSingleAgent(
|
|
1321
1325
|
ctx.cwd,
|
|
@@ -1979,7 +1983,14 @@ export function createSubagentToolExecute(pi: ExtensionAPI, store: SubagentStore
|
|
|
1979
1983
|
pipeline.stepRunIds.push(runState.id);
|
|
1980
1984
|
chainLaunches.push(toLaunchSummary(runState, "chain"));
|
|
1981
1985
|
|
|
1982
|
-
|
|
1986
|
+
let finalized: FinalizedRun;
|
|
1987
|
+
try {
|
|
1988
|
+
finalized = await launchRunInBackground(runState, taskForAgent);
|
|
1989
|
+
} catch (error: unknown) {
|
|
1990
|
+
finalized = finalizeRunError(runState, error);
|
|
1991
|
+
} finally {
|
|
1992
|
+
clearRunAbortState(runState);
|
|
1993
|
+
}
|
|
1983
1994
|
if (runState.removed) {
|
|
1984
1995
|
terminalStatus = finalized.isError ? "error" : "stopped";
|
|
1985
1996
|
pipeline.stepResults.push({
|
package/utils/agent-utils.ts
CHANGED
|
@@ -88,12 +88,10 @@ export const CLAUDE_MODEL_ALIAS_MAP: Record<string, string> = {
|
|
|
88
88
|
* Normalize a comma-separated tool list according to the given format.
|
|
89
89
|
* For "claude" format, maps Claude tool names to pi equivalents.
|
|
90
90
|
*/
|
|
91
|
-
export function normalizeTools(rawTools:
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
.split(",")
|
|
96
|
-
.map((t) => t.trim())
|
|
91
|
+
export function normalizeTools(rawTools: unknown, format: "pi" | "claude"): string[] | undefined {
|
|
92
|
+
const parsed = (Array.isArray(rawTools) ? rawTools : typeof rawTools === "string" ? rawTools.split(",") : [])
|
|
93
|
+
.filter((tool): tool is string => typeof tool === "string")
|
|
94
|
+
.map((tool) => tool.trim())
|
|
97
95
|
.filter(Boolean);
|
|
98
96
|
|
|
99
97
|
if (parsed.length === 0) return undefined;
|