pi-subagents-lite 1.3.0 → 1.4.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 +184 -235
- package/package.json +1 -1
- package/src/{agent-discovery.ts → agents/agent-discovery.ts} +10 -7
- package/src/{agent-manager.ts → agents/agent-manager.ts} +34 -74
- package/src/{agent-runner.ts → agents/agent-runner.ts} +130 -181
- package/src/{agent-status.ts → agents/agent-status.ts} +4 -4
- package/src/agents/agent-types.ts +339 -0
- package/src/{default-agents.ts → agents/default-agents.ts} +2 -5
- package/src/{output-file.ts → agents/output-file.ts} +68 -1
- package/src/{tool-execution.ts → agents/tool-execution.ts} +60 -222
- package/src/agents/types.ts +54 -0
- package/src/{usage.ts → agents/usage.ts} +7 -0
- package/src/{config-io.ts → config/config-io.ts} +20 -3
- package/src/config/config-store.ts +472 -0
- package/src/config/types.ts +26 -0
- package/src/events.ts +185 -0
- package/src/index.ts +8 -281
- package/src/{model-precedence.ts → models/model-precedence.ts} +33 -0
- package/src/{model-selector.ts → models/model-selector.ts} +1 -1
- package/src/{context.ts → prompt/context.ts} +1 -1
- package/src/prompt/prompts.ts +180 -0
- package/src/prompt/skill-loader.ts +195 -0
- package/src/registration.ts +101 -0
- package/src/shell.ts +101 -0
- package/src/spawn/spawn-coordinator.ts +232 -0
- package/src/status-note.ts +10 -0
- package/src/types.ts +47 -71
- package/src/ui/agent-widget.ts +61 -49
- package/src/{format.ts → ui/format.ts} +64 -26
- package/src/ui/menu/helpers.ts +93 -0
- package/src/ui/menu/menu-concurrency.ts +192 -0
- package/src/ui/menu/menu-debug.ts +125 -0
- package/src/ui/menu/menu-model-settings.ts +208 -0
- package/src/ui/menu/menu-running-agents.ts +224 -0
- package/src/ui/menu/menu-spawn-options.ts +87 -0
- package/src/ui/menu/menu-spawn-wizard.ts +418 -0
- package/src/ui/menu/menu-system-prompt.ts +109 -0
- package/src/ui/menu/menu-widget-settings.ts +130 -0
- package/src/ui/menu/menus.ts +101 -0
- package/src/ui/menu/submenus/confirm.ts +47 -0
- package/src/ui/menu/submenus/model-select.ts +70 -0
- package/src/ui/menu/submenus/numeric-input.ts +98 -0
- package/src/ui/menu/wrappers/settings-list.ts +205 -0
- package/src/{renderer.ts → ui/renderer.ts} +7 -6
- package/src/{result-viewer.ts → ui/result-viewer.ts} +7 -2
- package/src/ui/types.ts +11 -0
- package/src/agent-types.ts +0 -184
- package/src/config-mutator.ts +0 -183
- package/src/menus.ts +0 -1333
- package/src/prompts.ts +0 -94
- package/src/skill-loader.ts +0 -178
- package/src/state.ts +0 -83
- /package/src/{worktree-validator.ts → spawn/worktree-validator.ts} +0 -0
|
@@ -1,57 +1,40 @@
|
|
|
1
|
+
import { getStatusNote } from "../status-note.js";
|
|
1
2
|
/**
|
|
2
3
|
* tool-execution.ts — Agent tool execution handlers.
|
|
3
4
|
*
|
|
4
|
-
* Contains the execute callbacks registered for the Agent tool
|
|
5
|
-
*
|
|
5
|
+
* Contains the execute callbacks registered for the Agent tool.
|
|
6
|
+
* Spawn coordination, nudge scheduling, and live-view tracking have moved
|
|
7
|
+
* to spawn-coordinator.ts. buildAgentDetails stays here as a pure helper.
|
|
6
8
|
*/
|
|
7
9
|
|
|
8
10
|
import type { ExtensionContext, ToolCallEvent } from "@earendil-works/pi-coding-agent";
|
|
9
11
|
|
|
10
|
-
import type { AgentRecord } from "
|
|
11
|
-
import { SHORT_ID_LENGTH } from "
|
|
12
|
-
import type { SpawnOptions as AgentManagerSpawnOptions } from "./agent-manager.js";
|
|
13
|
-
import type { AgentActivity } from "./ui/agent-widget.js";
|
|
12
|
+
import type { AgentRecord } from "../types.js";
|
|
13
|
+
import { SHORT_ID_LENGTH } from "../types.js";
|
|
14
14
|
import { resolveType, getAgentConfig, discoverNewAgents } from "./agent-types.js";
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import { validateWorktreePath } from "./worktree-validator.js";
|
|
15
|
+
import { getLifetimeTotal, getSessionContextPercent } from "./usage.js";
|
|
16
|
+
import { validateWorktreePath } from "../spawn/worktree-validator.js";
|
|
18
17
|
|
|
19
|
-
|
|
20
|
-
import { parseModelKey, findModelInRegistry, parseThinkingLevel } from "./utils.js";
|
|
18
|
+
import { parseModelKey, findModelInRegistry, parseThinkingLevel } from "../utils.js";
|
|
21
19
|
import {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
20
|
+
getPiInstance,
|
|
21
|
+
getSessionCtx,
|
|
22
|
+
getStore,
|
|
23
|
+
getCoordinator,
|
|
26
24
|
getManager,
|
|
27
|
-
|
|
28
|
-
sessionCtx,
|
|
29
|
-
} from "./state.js";
|
|
30
|
-
|
|
31
|
-
// ============================================================================
|
|
32
|
-
// Module-level state
|
|
33
|
-
// ============================================================================
|
|
34
|
-
|
|
35
|
-
/** Agent IDs that were spawned as background — only these trigger a nudge on completion. */
|
|
36
|
-
export const backgroundAgentIds = new Set<string>();
|
|
37
|
-
|
|
38
|
-
const pendingNudges = new Set<string>();
|
|
39
|
-
let nudgeTimer: ReturnType<typeof setTimeout> | null = null;
|
|
40
|
-
|
|
41
|
-
/** Batch delay for nudges — only emit one update per batch window (ms). */
|
|
42
|
-
const NUDGE_DELAY_MS = 200;
|
|
25
|
+
} from "../shell.js";
|
|
43
26
|
|
|
44
27
|
// ============================================================================
|
|
45
28
|
// Tool result helpers
|
|
46
29
|
// ============================================================================
|
|
47
30
|
|
|
48
31
|
/** Shortcut for a successful tool result. */
|
|
49
|
-
|
|
32
|
+
function successResult(text: string, details?: Record<string, unknown>) {
|
|
50
33
|
return { content: [{ type: "text", text }], details };
|
|
51
34
|
}
|
|
52
35
|
|
|
53
36
|
/** Shortcut for an error tool result. */
|
|
54
|
-
|
|
37
|
+
function errorResult(text: string, details?: Record<string, unknown>) {
|
|
55
38
|
return { content: [{ type: "text", text }], isError: true as const, details };
|
|
56
39
|
}
|
|
57
40
|
|
|
@@ -59,67 +42,6 @@ export function errorResult(text: string, details?: Record<string, unknown>) {
|
|
|
59
42
|
// Activity tracking
|
|
60
43
|
// ============================================================================
|
|
61
44
|
|
|
62
|
-
/**
|
|
63
|
-
* Create an AgentActivity state and spawn callbacks for tracking tool usage.
|
|
64
|
-
* Used by both foreground and background paths to avoid duplication.
|
|
65
|
-
* Exported for use by the menu spawn flow.
|
|
66
|
-
*/
|
|
67
|
-
export function createActivityTracker(maxTurns?: number, onStreamUpdate?: () => void) {
|
|
68
|
-
const state: AgentActivity = {
|
|
69
|
-
activeTools: new Map(),
|
|
70
|
-
toolUses: 0,
|
|
71
|
-
turnCount: 1,
|
|
72
|
-
maxTurns,
|
|
73
|
-
responseText: "",
|
|
74
|
-
session: undefined,
|
|
75
|
-
lifetimeUsage: { input: 0, output: 0, cacheWrite: 0, cost: 0 },
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
const callbacks = {
|
|
79
|
-
onToolActivity: (activity: { type: "start" | "end"; toolName: string }) => {
|
|
80
|
-
if (activity.type === "start") {
|
|
81
|
-
state.activeTools.set(`${activity.toolName}_${Date.now()}`, activity.toolName);
|
|
82
|
-
} else {
|
|
83
|
-
for (const [key, name] of state.activeTools) {
|
|
84
|
-
if (name === activity.toolName) { state.activeTools.delete(key); break; }
|
|
85
|
-
}
|
|
86
|
-
state.toolUses++;
|
|
87
|
-
}
|
|
88
|
-
onStreamUpdate?.();
|
|
89
|
-
},
|
|
90
|
-
onTextDelta: (_delta: string, fullText: string) => {
|
|
91
|
-
state.responseText = fullText;
|
|
92
|
-
onStreamUpdate?.();
|
|
93
|
-
},
|
|
94
|
-
onTurnEnd: (turnCount: number) => {
|
|
95
|
-
state.turnCount = turnCount;
|
|
96
|
-
onStreamUpdate?.();
|
|
97
|
-
},
|
|
98
|
-
onSessionCreated: (session: unknown) => {
|
|
99
|
-
state.session = session as Parameters<typeof getSessionContextPercent>[0];
|
|
100
|
-
},
|
|
101
|
-
onAssistantUsage: (usage: LifetimeUsage) => {
|
|
102
|
-
addUsage(state.lifetimeUsage, usage);
|
|
103
|
-
onStreamUpdate?.();
|
|
104
|
-
},
|
|
105
|
-
};
|
|
106
|
-
|
|
107
|
-
return { state, callbacks };
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
// ============================================================================
|
|
111
|
-
// buildAgentDetails — consolidated stats/details construction
|
|
112
|
-
// ============================================================================
|
|
113
|
-
|
|
114
|
-
interface AgentDetailsOptions {
|
|
115
|
-
/** Include full stats (turns, tokens, context%, compactions, cost). Default: false. */
|
|
116
|
-
includeStats?: boolean;
|
|
117
|
-
/** Include status and outputFile. Default: false. */
|
|
118
|
-
includeStatus?: boolean;
|
|
119
|
-
/** Override the turnCount (e.g. from activity tracker). Default: record.turnCount. */
|
|
120
|
-
turnCount?: number;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
45
|
/**
|
|
124
46
|
* Build a details Record from an AgentRecord, controlled by options.
|
|
125
47
|
*
|
|
@@ -130,6 +52,13 @@ interface AgentDetailsOptions {
|
|
|
130
52
|
* Consolidates the identical field-selection logic previously duplicated
|
|
131
53
|
* across emitIndividualNudge, executeSpawnForeground, and executeSpawnBackground.
|
|
132
54
|
*/
|
|
55
|
+
interface AgentDetailsOptions {
|
|
56
|
+
/** Include full stats (turns, tokens, context%, compactions, cost). Default: false. */
|
|
57
|
+
includeStats?: boolean;
|
|
58
|
+
/** Include status and outputFile. Default: false. */
|
|
59
|
+
includeStatus?: boolean;
|
|
60
|
+
}
|
|
61
|
+
|
|
133
62
|
export function buildAgentDetails(
|
|
134
63
|
record: AgentRecord,
|
|
135
64
|
options?: AgentDetailsOptions,
|
|
@@ -149,13 +78,13 @@ export function buildAgentDetails(
|
|
|
149
78
|
}
|
|
150
79
|
|
|
151
80
|
if (options?.includeStats) {
|
|
152
|
-
const totalTokens = getLifetimeTotal(record.stats.lifetimeUsage);
|
|
153
81
|
const elapsedMs = record.lifecycle.completedAt ? record.lifecycle.completedAt - record.lifecycle.startedAt : 0;
|
|
154
82
|
|
|
155
|
-
details.turnCount =
|
|
83
|
+
details.turnCount = record.stats.turnCount;
|
|
156
84
|
details.maxTurns = record.stats.maxTurns;
|
|
157
85
|
details.toolUses = record.stats.toolUses;
|
|
158
|
-
details.
|
|
86
|
+
details.input = record.stats.lifetimeUsage.input;
|
|
87
|
+
details.output = record.stats.lifetimeUsage.output;
|
|
159
88
|
details.contextPercent = getSessionContextPercent(record.execution.session);
|
|
160
89
|
details.durationMs = elapsedMs;
|
|
161
90
|
details.compactions = record.stats.compactionCount;
|
|
@@ -166,49 +95,6 @@ export function buildAgentDetails(
|
|
|
166
95
|
return details;
|
|
167
96
|
}
|
|
168
97
|
|
|
169
|
-
// ============================================================================
|
|
170
|
-
// Nudge scheduling — batch completion notifications within the hold window
|
|
171
|
-
// ============================================================================
|
|
172
|
-
|
|
173
|
-
export function scheduleNudge(agentId: string): void {
|
|
174
|
-
pendingNudges.add(agentId);
|
|
175
|
-
|
|
176
|
-
if (nudgeTimer) return;
|
|
177
|
-
|
|
178
|
-
nudgeTimer = setTimeout(() => {
|
|
179
|
-
nudgeTimer = null;
|
|
180
|
-
const batch = [...pendingNudges];
|
|
181
|
-
pendingNudges.clear();
|
|
182
|
-
|
|
183
|
-
for (const id of batch) {
|
|
184
|
-
emitIndividualNudge(id, getManager()?.getRecord(id));
|
|
185
|
-
}
|
|
186
|
-
}, NUDGE_DELAY_MS);
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
function emitIndividualNudge(agentId: string, record?: AgentRecord): void {
|
|
190
|
-
if (!record) return;
|
|
191
|
-
|
|
192
|
-
const details = buildAgentDetails(record, {
|
|
193
|
-
includeStats: true,
|
|
194
|
-
includeStatus: true,
|
|
195
|
-
turnCount: record.stats.turnCount ?? agentActivity.get(agentId)?.turnCount,
|
|
196
|
-
});
|
|
197
|
-
|
|
198
|
-
piInstance.sendMessage(
|
|
199
|
-
{
|
|
200
|
-
customType: "subagent-result",
|
|
201
|
-
content: `[Subagent "${record.display.type}" ${record.lifecycle.status}]\n\n${record.result ?? ""}`,
|
|
202
|
-
details,
|
|
203
|
-
display: true,
|
|
204
|
-
},
|
|
205
|
-
{
|
|
206
|
-
deliverAs: "steer",
|
|
207
|
-
triggerTurn: true,
|
|
208
|
-
},
|
|
209
|
-
);
|
|
210
|
-
}
|
|
211
|
-
|
|
212
98
|
// ============================================================================
|
|
213
99
|
// Tool execute handlers
|
|
214
100
|
// ============================================================================
|
|
@@ -226,8 +112,8 @@ export async function executeAgentTool(
|
|
|
226
112
|
let worktreeLabel: string | undefined;
|
|
227
113
|
if (rawWorktreePath && rawWorktreePath.trim() !== "") {
|
|
228
114
|
try {
|
|
229
|
-
const parentCwd =
|
|
230
|
-
const validation = await validateWorktreePath(
|
|
115
|
+
const parentCwd = getSessionCtx()?.cwd ?? ctx.cwd;
|
|
116
|
+
const validation = await validateWorktreePath(getPiInstance(), rawWorktreePath, parentCwd);
|
|
231
117
|
if (!validation.ok) {
|
|
232
118
|
return errorResult(validation.error);
|
|
233
119
|
}
|
|
@@ -266,90 +152,44 @@ export async function executeAgentTool(
|
|
|
266
152
|
|
|
267
153
|
// Resolve thinking: explicit param > agent config (frontmatter) > undefined (inherit)
|
|
268
154
|
const thinkingLevel = parseThinkingLevel(params.thinking as string | undefined)
|
|
269
|
-
?? getAgentConfig(resolvedType)?.
|
|
155
|
+
?? getAgentConfig(resolvedType)?.thinkingLevel;
|
|
270
156
|
|
|
271
|
-
|
|
157
|
+
// Use SpawnCoordinator for unified spawn path
|
|
158
|
+
const coordinator = getCoordinator()!;
|
|
159
|
+
const result = await coordinator.spawn(getPiInstance(), ctx, {
|
|
160
|
+
type: resolvedType,
|
|
161
|
+
prompt,
|
|
272
162
|
description,
|
|
273
163
|
model,
|
|
164
|
+
modelKey,
|
|
274
165
|
maxTurns,
|
|
275
166
|
thinkingLevel,
|
|
276
|
-
|
|
277
|
-
invocation: { modelName },
|
|
278
|
-
graceTurns: __config.agent.graceTurns,
|
|
167
|
+
graceTurns: getStore().agent.graceTurns,
|
|
279
168
|
worktreePath: validatedWorktreePath,
|
|
280
169
|
worktreeLabel,
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
if (runInBackground || __config.agent.forceBackground) {
|
|
284
|
-
return executeSpawnBackground(resolvedType, prompt, ctx, spawnOptions);
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
return executeSpawnForeground(resolvedType, prompt, ctx, spawnOptions);
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
async function executeSpawnBackground(
|
|
291
|
-
resolvedType: string,
|
|
292
|
-
prompt: string,
|
|
293
|
-
ctx: ExtensionContext,
|
|
294
|
-
spawnOptions: AgentManagerSpawnOptions,
|
|
295
|
-
): Promise<any> {
|
|
296
|
-
const { state, callbacks } = createActivityTracker(
|
|
297
|
-
spawnOptions.maxTurns,
|
|
298
|
-
);
|
|
299
|
-
|
|
300
|
-
const agentId = getManager().spawn(piInstance, ctx, resolvedType, prompt, {
|
|
301
|
-
...spawnOptions,
|
|
302
|
-
isBackground: true,
|
|
303
|
-
...callbacks,
|
|
304
|
-
});
|
|
305
|
-
backgroundAgentIds.add(agentId);
|
|
306
|
-
agentActivity.set(agentId, state);
|
|
307
|
-
getWidget()?.ensureTimer();
|
|
308
|
-
getWidget()?.update();
|
|
309
|
-
|
|
310
|
-
const record = getManager().getRecord(agentId)!;
|
|
311
|
-
const details = buildAgentDetails(record);
|
|
312
|
-
const suffix = `A notification will arrive when done - User asks you not to poll, check status or duplicate the delegated work.\n\nAgent ID: ${agentId}`;
|
|
313
|
-
const label = record.lifecycle.status === "queued" ? "Agent queued" : "Agent running";
|
|
314
|
-
|
|
315
|
-
return successResult(`[${label}] ${suffix}`, details);
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
async function executeSpawnForeground(
|
|
319
|
-
resolvedType: string,
|
|
320
|
-
prompt: string,
|
|
321
|
-
ctx: ExtensionContext,
|
|
322
|
-
spawnOptions: AgentManagerSpawnOptions,
|
|
323
|
-
): Promise<any> {
|
|
324
|
-
const { state: fgState, callbacks: fgCallbacks } = createActivityTracker(
|
|
325
|
-
spawnOptions.maxTurns,
|
|
326
|
-
);
|
|
327
|
-
|
|
328
|
-
const fgId = getManager().spawn(piInstance, ctx, resolvedType, prompt, {
|
|
329
|
-
...spawnOptions,
|
|
330
|
-
...fgCallbacks,
|
|
331
|
-
isBackground: false,
|
|
170
|
+
invocation: { modelName },
|
|
171
|
+
runInBackground: runInBackground || getStore().agent.forceBackground,
|
|
332
172
|
});
|
|
333
|
-
agentActivity.set(fgId, fgState);
|
|
334
|
-
getWidget()?.ensureTimer();
|
|
335
173
|
|
|
336
|
-
const record =
|
|
337
|
-
await record.execution.promise;
|
|
174
|
+
const { agentId, record } = result;
|
|
338
175
|
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
176
|
+
if (runInBackground || getStore().agent.forceBackground) {
|
|
177
|
+
// Background: return immediately
|
|
178
|
+
const suffix = `A notification will arrive when done - User asks you not to poll, check status or duplicate the delegated work.\n\nAgent ID: ${agentId}`;
|
|
179
|
+
const label = record.lifecycle.status === "queued" ? "Agent queued" : "Agent running";
|
|
180
|
+
const details = buildAgentDetails(record);
|
|
181
|
+
return successResult(`[${label}] ${suffix}`, details);
|
|
182
|
+
}
|
|
342
183
|
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
turnCount: fgState.turnCount,
|
|
346
|
-
});
|
|
184
|
+
// Foreground: record.execution.promise is already awaited by coordinator.spawn()
|
|
185
|
+
const details = buildAgentDetails(record, { includeStats: true });
|
|
347
186
|
|
|
348
187
|
if (record.lifecycle.status === "error") {
|
|
349
|
-
return errorResult(`Agent failed: ${record.error || "unknown error"}`,
|
|
188
|
+
return errorResult(`Agent failed: ${record.error || "unknown error"}`, details);
|
|
350
189
|
}
|
|
351
190
|
|
|
352
|
-
|
|
191
|
+
const statusNote = getStatusNote(record.lifecycle.status);
|
|
192
|
+
return successResult((record.result ?? "") + statusNote, details);
|
|
353
193
|
}
|
|
354
194
|
|
|
355
195
|
// ============================================================================
|
|
@@ -361,7 +201,7 @@ async function executeSpawnForeground(
|
|
|
361
201
|
* Format: "type·short_id, type·short_id" — one line, easy for LLM to parse.
|
|
362
202
|
*/
|
|
363
203
|
function formatRunningAgents(): string {
|
|
364
|
-
const agents = getManager()
|
|
204
|
+
const agents = getManager()!.listAgents().filter(
|
|
365
205
|
(a) => a.lifecycle.status === "running" || a.lifecycle.status === "queued",
|
|
366
206
|
);
|
|
367
207
|
|
|
@@ -389,7 +229,7 @@ export async function executeStopAgentTool(
|
|
|
389
229
|
return errorResult("agent_id is required");
|
|
390
230
|
}
|
|
391
231
|
|
|
392
|
-
const record = getManager()
|
|
232
|
+
const record = getManager()!.getRecord(agentId);
|
|
393
233
|
|
|
394
234
|
if (!record) {
|
|
395
235
|
// Agent not found → return error + list of running agents
|
|
@@ -406,7 +246,7 @@ export async function executeStopAgentTool(
|
|
|
406
246
|
}
|
|
407
247
|
|
|
408
248
|
// Attempt to stop the running/queued agent
|
|
409
|
-
if (getManager()
|
|
249
|
+
if (getManager()!.abort(agentId)) {
|
|
410
250
|
return successResult(`Stopped agent ${agentId.slice(0, SHORT_ID_LENGTH)}`);
|
|
411
251
|
}
|
|
412
252
|
|
|
@@ -429,13 +269,11 @@ export async function toolCallListener(
|
|
|
429
269
|
|
|
430
270
|
const parentModelId = ctx.model ? `${ctx.model.provider}/${ctx.model.id}` : "";
|
|
431
271
|
|
|
432
|
-
const effectiveModel =
|
|
433
|
-
subagentType
|
|
434
|
-
agentConfig,
|
|
435
|
-
config: __config,
|
|
272
|
+
const effectiveModel = getStore().modelFor(
|
|
273
|
+
subagentType ?? "general-purpose",
|
|
436
274
|
parentModelId,
|
|
437
|
-
|
|
438
|
-
|
|
275
|
+
agentConfig,
|
|
276
|
+
);
|
|
439
277
|
|
|
440
278
|
if (effectiveModel) {
|
|
441
279
|
input.model = effectiveModel;
|
|
@@ -447,7 +285,7 @@ export async function toolCallListener(
|
|
|
447
285
|
}
|
|
448
286
|
|
|
449
287
|
// Inject thinking from agent config if not explicitly passed
|
|
450
|
-
if (input.thinking === undefined && agentConfig?.
|
|
451
|
-
input.thinking = agentConfig.
|
|
288
|
+
if (input.thinking === undefined && agentConfig?.thinkingLevel !== undefined) {
|
|
289
|
+
input.thinking = agentConfig.thinkingLevel;
|
|
452
290
|
}
|
|
453
291
|
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { ThinkingLevel } from "../types.js";
|
|
2
|
+
|
|
3
|
+
/** Agent type: any string name (built-in defaults or user-defined). */
|
|
4
|
+
export type SubagentType = string;
|
|
5
|
+
|
|
6
|
+
/** How the subagent system prompt is constructed. */
|
|
7
|
+
export type SystemPromptMode = "replace" | "inherit" | "custom";
|
|
8
|
+
|
|
9
|
+
/** Unified agent configuration — used for both default and user-defined agents. */
|
|
10
|
+
export interface AgentConfig {
|
|
11
|
+
name: string;
|
|
12
|
+
displayName?: string;
|
|
13
|
+
description: string;
|
|
14
|
+
/** Tools to register with the session (controls availability, not LLM visibility). */
|
|
15
|
+
registeredTools?: string[];
|
|
16
|
+
/**
|
|
17
|
+
* Controls which tool schemas the LLM sees. Can reference built-in tools
|
|
18
|
+
* and extension tools. true = all, string[] = listed, false = none.
|
|
19
|
+
* Supports ext/* syntax to include all tools from an extension.
|
|
20
|
+
* Mutually exclusive with excludeTools.
|
|
21
|
+
*/
|
|
22
|
+
tools?: true | string[] | false;
|
|
23
|
+
/** Tool blacklist — all tools except these are visible. Mutually exclusive with tools (when tools is string[]). */
|
|
24
|
+
excludeTools?: string[];
|
|
25
|
+
/** true = inherit all, string[] = only listed, false = none. undefined = not set (uses global default). Mutually exclusive with excludeExtensions. */
|
|
26
|
+
extensions?: true | string[] | false;
|
|
27
|
+
/** Extension blacklist — all extensions except these load. Mutually exclusive with extensions (when extensions is string[]). */
|
|
28
|
+
excludeExtensions?: string[];
|
|
29
|
+
/** Whitelist of allowed skills (metadata only in system prompt). true = all, string[] = listed, false = none. undefined = not set (uses global default). */
|
|
30
|
+
skills?: true | string[] | false;
|
|
31
|
+
/** Skills to preload with full content into system prompt. string[] = listed, false/undefined = none */
|
|
32
|
+
preloadSkills?: string[] | false;
|
|
33
|
+
model?: string;
|
|
34
|
+
thinkingLevel?: ThinkingLevel;
|
|
35
|
+
maxTurns?: number;
|
|
36
|
+
/** Max output tokens per LLM response. Passed to provider as max_tokens or max_completion_tokens. */
|
|
37
|
+
maxTokens?: number;
|
|
38
|
+
systemPrompt: string;
|
|
39
|
+
|
|
40
|
+
/** true = this is an embedded default agent (informational) */
|
|
41
|
+
isDefault?: boolean;
|
|
42
|
+
/** true = agent is hidden from the schema enum but can still be called by name. */
|
|
43
|
+
hidden?: boolean;
|
|
44
|
+
/** Where this agent was loaded from */
|
|
45
|
+
source?: "project" | "global";
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface AgentInvocation {
|
|
49
|
+
/** Short display name, e.g. "haiku" — only set when different from parent. */
|
|
50
|
+
modelName?: string;
|
|
51
|
+
thinkingLevel?: ThinkingLevel;
|
|
52
|
+
maxTurns?: number;
|
|
53
|
+
runInBackground?: boolean;
|
|
54
|
+
}
|
|
@@ -36,6 +36,13 @@ export function formatTokens(count: number): string {
|
|
|
36
36
|
return `${count}`;
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
/** Format token count for widget display: rounded to whole number for k. */
|
|
40
|
+
export function formatTokensCompact(count: number): string {
|
|
41
|
+
if (count >= 1_000_000) return `${(count / 1_000_000).toFixed(1)}M`;
|
|
42
|
+
if (count >= 1_000) return `${Math.round(count / 1_000)}k`;
|
|
43
|
+
return `${count}`;
|
|
44
|
+
}
|
|
45
|
+
|
|
39
46
|
/** Format cost as a dollar amount: "$0.00", "$0.01", "$1.23". */
|
|
40
47
|
export function formatCost(cost: number): string {
|
|
41
48
|
return `$${cost.toFixed(2)}`;
|
|
@@ -7,26 +7,43 @@
|
|
|
7
7
|
|
|
8
8
|
import * as fs from "node:fs";
|
|
9
9
|
import * as path from "node:path";
|
|
10
|
-
import type { SubagentsConfig } from "
|
|
10
|
+
import type { SubagentsConfig } from "../models/model-precedence.js";
|
|
11
11
|
|
|
12
12
|
const CONFIG_DIR = path.join(process.env.HOME || "", ".pi", "agent");
|
|
13
13
|
const CONFIG_PATH = path.join(CONFIG_DIR, "subagents-lite.json");
|
|
14
14
|
|
|
15
|
+
/** Default number of grace turns before an agent is force-stopped. */
|
|
16
|
+
export const DEFAULT_GRACE_TURNS = 6;
|
|
17
|
+
|
|
15
18
|
/** Default configuration — used when config file doesn't exist or is invalid. */
|
|
16
19
|
export const DEFAULT_CONFIG: SubagentsConfig = {
|
|
17
20
|
agent: {
|
|
18
21
|
default: null,
|
|
19
22
|
forceBackground: false,
|
|
20
|
-
graceTurns:
|
|
23
|
+
graceTurns: DEFAULT_GRACE_TURNS,
|
|
21
24
|
widgetMaxLines: 12,
|
|
22
25
|
// widgetMaxLinesCompact intentionally omitted — derives from widgetMaxLines
|
|
26
|
+
widgetDescLengthFull: 50,
|
|
27
|
+
widgetDescLengthCompact: 30,
|
|
23
28
|
widgetCompact: false,
|
|
24
29
|
widgetShortcut: false,
|
|
30
|
+
systemPromptMode: "replace",
|
|
31
|
+
includeContextFiles: true,
|
|
32
|
+
disableDefaultAgents: false,
|
|
33
|
+
showTools: true,
|
|
34
|
+
showTurns: true,
|
|
35
|
+
showInput: true,
|
|
36
|
+
showOutput: true,
|
|
37
|
+
showContext: true,
|
|
38
|
+
showCost: false,
|
|
39
|
+
showTime: true,
|
|
25
40
|
},
|
|
26
41
|
concurrency: { default: 4 },
|
|
27
42
|
};
|
|
28
43
|
|
|
29
|
-
/**
|
|
44
|
+
/**
|
|
45
|
+
* Read config from disk. Returns defaults if file doesn't exist or is invalid.
|
|
46
|
+
*/
|
|
30
47
|
export function loadConfig(): SubagentsConfig {
|
|
31
48
|
try {
|
|
32
49
|
const raw = fs.readFileSync(CONFIG_PATH, "utf-8");
|