pi-subagents 0.17.5 → 0.18.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/CHANGELOG.md +29 -0
- package/README.md +21 -21
- package/agent-management.ts +11 -2
- package/agents/oracle-executor.md +1 -1
- package/agents/scout.md +1 -1
- package/async-execution.ts +24 -4
- package/async-job-tracker.ts +70 -7
- package/async-status.ts +37 -15
- package/chain-execution.ts +29 -4
- package/execution.ts +24 -36
- package/index.ts +219 -139
- package/install.mjs +2 -3
- package/intercom-bridge.ts +9 -0
- package/notify.ts +45 -16
- package/package.json +3 -6
- package/pi-args.ts +5 -0
- package/render.ts +381 -74
- package/result-watcher.ts +3 -5
- package/run-status.ts +134 -0
- package/schemas.ts +64 -25
- package/skills/pi-subagents/SKILL.md +21 -21
- package/skills.ts +61 -0
- package/slash-commands.ts +55 -11
- package/slash-live-state.ts +3 -9
- package/subagent-control.ts +84 -42
- package/subagent-executor.ts +130 -17
- package/subagent-prompt-runtime.ts +6 -0
- package/subagent-runner.ts +118 -10
- package/subagents-status.ts +5 -1
- package/types.ts +29 -9
package/chain-execution.ts
CHANGED
|
@@ -44,6 +44,7 @@ import {
|
|
|
44
44
|
type AgentProgress,
|
|
45
45
|
type ArtifactConfig,
|
|
46
46
|
type ArtifactPaths,
|
|
47
|
+
type ControlEvent,
|
|
47
48
|
type Details,
|
|
48
49
|
type ResolvedControlConfig,
|
|
49
50
|
type SingleResult,
|
|
@@ -84,12 +85,17 @@ interface ParallelChainRunInput {
|
|
|
84
85
|
artifactsDir: string;
|
|
85
86
|
signal?: AbortSignal;
|
|
86
87
|
onUpdate?: (r: AgentToolResult<Details>) => void;
|
|
88
|
+
onControlEvent?: (event: ControlEvent) => void;
|
|
87
89
|
controlConfig: ResolvedControlConfig;
|
|
90
|
+
childIntercomTarget?: (agent: string, index: number) => string | undefined;
|
|
88
91
|
foregroundControl?: {
|
|
89
92
|
updatedAt: number;
|
|
90
93
|
currentAgent?: string;
|
|
91
94
|
currentIndex?: number;
|
|
92
95
|
currentActivityState?: ActivityState;
|
|
96
|
+
lastActivityAt?: number;
|
|
97
|
+
currentTool?: string;
|
|
98
|
+
currentToolStartedAt?: number;
|
|
93
99
|
interrupt?: () => boolean;
|
|
94
100
|
};
|
|
95
101
|
results: SingleResult[];
|
|
@@ -200,12 +206,12 @@ async function runParallelChainTasks(input: ParallelChainRunInput): Promise<Sing
|
|
|
200
206
|
if (input.foregroundControl) {
|
|
201
207
|
input.foregroundControl.currentAgent = task.agent;
|
|
202
208
|
input.foregroundControl.currentIndex = input.globalTaskIndex + taskIndex;
|
|
203
|
-
input.foregroundControl.currentActivityState =
|
|
209
|
+
input.foregroundControl.currentActivityState = undefined;
|
|
204
210
|
input.foregroundControl.updatedAt = Date.now();
|
|
205
211
|
input.foregroundControl.interrupt = () => {
|
|
206
212
|
if (interruptController.signal.aborted) return false;
|
|
207
213
|
interruptController.abort();
|
|
208
|
-
input.foregroundControl!.currentActivityState =
|
|
214
|
+
input.foregroundControl!.currentActivityState = undefined;
|
|
209
215
|
input.foregroundControl!.updatedAt = Date.now();
|
|
210
216
|
return true;
|
|
211
217
|
};
|
|
@@ -225,6 +231,8 @@ async function runParallelChainTasks(input: ParallelChainRunInput): Promise<Sing
|
|
|
225
231
|
outputPath,
|
|
226
232
|
maxSubagentDepth,
|
|
227
233
|
controlConfig: input.controlConfig,
|
|
234
|
+
onControlEvent: input.onControlEvent,
|
|
235
|
+
intercomSessionName: input.childIntercomTarget?.(task.agent, input.globalTaskIndex + taskIndex),
|
|
228
236
|
modelOverride: effectiveModel,
|
|
229
237
|
availableModels: input.availableModels,
|
|
230
238
|
preferredModelProvider: input.ctx.model?.provider,
|
|
@@ -238,6 +246,9 @@ async function runParallelChainTasks(input: ParallelChainRunInput): Promise<Sing
|
|
|
238
246
|
input.foregroundControl.currentAgent = task.agent;
|
|
239
247
|
input.foregroundControl.currentIndex = input.globalTaskIndex + taskIndex;
|
|
240
248
|
input.foregroundControl.currentActivityState = current?.activityState;
|
|
249
|
+
input.foregroundControl.lastActivityAt = current?.lastActivityAt;
|
|
250
|
+
input.foregroundControl.currentTool = current?.currentTool;
|
|
251
|
+
input.foregroundControl.currentToolStartedAt = current?.currentToolStartedAt;
|
|
241
252
|
input.foregroundControl.updatedAt = Date.now();
|
|
242
253
|
}
|
|
243
254
|
input.onUpdate?.({
|
|
@@ -287,12 +298,17 @@ export interface ChainExecutionParams {
|
|
|
287
298
|
includeProgress?: boolean;
|
|
288
299
|
clarify?: boolean;
|
|
289
300
|
onUpdate?: (r: AgentToolResult<Details>) => void;
|
|
301
|
+
onControlEvent?: (event: ControlEvent) => void;
|
|
290
302
|
controlConfig: ResolvedControlConfig;
|
|
303
|
+
childIntercomTarget?: (agent: string, index: number) => string | undefined;
|
|
291
304
|
foregroundControl?: {
|
|
292
305
|
updatedAt: number;
|
|
293
306
|
currentAgent?: string;
|
|
294
307
|
currentIndex?: number;
|
|
295
308
|
currentActivityState?: ActivityState;
|
|
309
|
+
lastActivityAt?: number;
|
|
310
|
+
currentTool?: string;
|
|
311
|
+
currentToolStartedAt?: number;
|
|
296
312
|
interrupt?: () => boolean;
|
|
297
313
|
};
|
|
298
314
|
chainSkills?: string[];
|
|
@@ -332,7 +348,9 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
332
348
|
includeProgress,
|
|
333
349
|
clarify,
|
|
334
350
|
onUpdate,
|
|
351
|
+
onControlEvent,
|
|
335
352
|
controlConfig,
|
|
353
|
+
childIntercomTarget,
|
|
336
354
|
foregroundControl,
|
|
337
355
|
chainSkills: chainSkillsParam,
|
|
338
356
|
chainDir: chainDirBase,
|
|
@@ -533,6 +551,8 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
533
551
|
chainAgents,
|
|
534
552
|
totalSteps,
|
|
535
553
|
controlConfig,
|
|
554
|
+
onControlEvent,
|
|
555
|
+
childIntercomTarget,
|
|
536
556
|
foregroundControl,
|
|
537
557
|
worktreeSetup,
|
|
538
558
|
maxSubagentDepth: params.maxSubagentDepth,
|
|
@@ -674,12 +694,12 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
674
694
|
if (foregroundControl) {
|
|
675
695
|
foregroundControl.currentAgent = seqStep.agent;
|
|
676
696
|
foregroundControl.currentIndex = globalTaskIndex;
|
|
677
|
-
foregroundControl.currentActivityState =
|
|
697
|
+
foregroundControl.currentActivityState = undefined;
|
|
678
698
|
foregroundControl.updatedAt = Date.now();
|
|
679
699
|
foregroundControl.interrupt = () => {
|
|
680
700
|
if (interruptController.signal.aborted) return false;
|
|
681
701
|
interruptController.abort();
|
|
682
|
-
foregroundControl.currentActivityState =
|
|
702
|
+
foregroundControl.currentActivityState = undefined;
|
|
683
703
|
foregroundControl.updatedAt = Date.now();
|
|
684
704
|
return true;
|
|
685
705
|
};
|
|
@@ -699,6 +719,8 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
699
719
|
outputPath,
|
|
700
720
|
maxSubagentDepth,
|
|
701
721
|
controlConfig,
|
|
722
|
+
onControlEvent,
|
|
723
|
+
intercomSessionName: childIntercomTarget?.(seqStep.agent, globalTaskIndex),
|
|
702
724
|
modelOverride: effectiveModel,
|
|
703
725
|
availableModels,
|
|
704
726
|
preferredModelProvider: ctx.model?.provider,
|
|
@@ -712,6 +734,9 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
712
734
|
foregroundControl.currentAgent = seqStep.agent;
|
|
713
735
|
foregroundControl.currentIndex = globalTaskIndex;
|
|
714
736
|
foregroundControl.currentActivityState = current?.activityState;
|
|
737
|
+
foregroundControl.lastActivityAt = current?.lastActivityAt;
|
|
738
|
+
foregroundControl.currentTool = current?.currentTool;
|
|
739
|
+
foregroundControl.currentToolStartedAt = current?.currentToolStartedAt;
|
|
715
740
|
foregroundControl.updatedAt = Date.now();
|
|
716
741
|
}
|
|
717
742
|
onUpdate({
|
package/execution.ts
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import { spawn } from "node:child_process";
|
|
6
|
+
import { existsSync } from "node:fs";
|
|
6
7
|
import type { Message } from "@mariozechner/pi-ai";
|
|
7
8
|
import type { AgentConfig } from "./agents.ts";
|
|
8
9
|
import {
|
|
@@ -28,8 +29,10 @@ import {
|
|
|
28
29
|
import {
|
|
29
30
|
DEFAULT_CONTROL_CONFIG,
|
|
30
31
|
buildControlEvent,
|
|
32
|
+
claimControlNotification,
|
|
31
33
|
deriveActivityState,
|
|
32
34
|
shouldEmitControlEvent,
|
|
35
|
+
shouldNotifyControlEvent,
|
|
33
36
|
} from "./subagent-control.ts";
|
|
34
37
|
import {
|
|
35
38
|
getFinalOutput,
|
|
@@ -135,6 +138,7 @@ async function runSingleAttempt(
|
|
|
135
138
|
systemPrompt: shared.systemPrompt,
|
|
136
139
|
mcpDirectTools: agent.mcpDirectTools,
|
|
137
140
|
promptFileStem: agent.name,
|
|
141
|
+
intercomSessionName: options.intercomSessionName,
|
|
138
142
|
});
|
|
139
143
|
|
|
140
144
|
const result: SingleResult = {
|
|
@@ -150,8 +154,6 @@ async function runSingleAttempt(
|
|
|
150
154
|
};
|
|
151
155
|
const startTime = Date.now();
|
|
152
156
|
const controlConfig = options.controlConfig ?? DEFAULT_CONTROL_CONFIG;
|
|
153
|
-
let hasSeenActivity = false;
|
|
154
|
-
let pausedByInterrupt = false;
|
|
155
157
|
let interruptedByControl = false;
|
|
156
158
|
const allControlEvents: ControlEvent[] = [];
|
|
157
159
|
let pendingControlEvents: ControlEvent[] = [];
|
|
@@ -160,7 +162,6 @@ async function runSingleAttempt(
|
|
|
160
162
|
index: options.index ?? 0,
|
|
161
163
|
agent: agent.name,
|
|
162
164
|
status: "running",
|
|
163
|
-
activityState: controlConfig.enabled ? "starting" : undefined,
|
|
164
165
|
task,
|
|
165
166
|
skills: shared.resolvedSkillNames,
|
|
166
167
|
recentTools: [],
|
|
@@ -274,34 +275,39 @@ async function runSingleAttempt(
|
|
|
274
275
|
return events;
|
|
275
276
|
};
|
|
276
277
|
|
|
278
|
+
const emittedControlEventKeys = new Set<string>();
|
|
279
|
+
const emitControlEvent = (event: ControlEvent) => {
|
|
280
|
+
if (shouldNotifyControlEvent(controlConfig, event) && !claimControlNotification(controlConfig, event, emittedControlEventKeys)) return;
|
|
281
|
+
allControlEvents.push(event);
|
|
282
|
+
pendingControlEvents.push(event);
|
|
283
|
+
options.onControlEvent?.(event);
|
|
284
|
+
};
|
|
285
|
+
|
|
277
286
|
const updateActivityState = (now: number): boolean => {
|
|
278
287
|
const next = deriveActivityState({
|
|
279
288
|
config: controlConfig,
|
|
280
289
|
startedAt: startTime,
|
|
281
290
|
lastActivityAt: progress.lastActivityAt,
|
|
282
|
-
hasSeenActivity,
|
|
283
|
-
paused: pausedByInterrupt,
|
|
284
291
|
now,
|
|
285
292
|
});
|
|
286
|
-
if (
|
|
293
|
+
if (next === progress.activityState) return false;
|
|
287
294
|
const previous = progress.activityState;
|
|
288
295
|
progress.activityState = next;
|
|
289
296
|
if (shouldEmitControlEvent(controlConfig, previous, next)) {
|
|
290
|
-
|
|
297
|
+
emitControlEvent(buildControlEvent({
|
|
291
298
|
from: previous,
|
|
292
299
|
to: next,
|
|
293
300
|
runId: options.runId,
|
|
294
301
|
agent: agent.name,
|
|
295
302
|
index: options.index,
|
|
296
303
|
ts: now,
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
pendingControlEvents.push(event);
|
|
300
|
-
options.onControlEvent?.(event);
|
|
304
|
+
lastActivityAt: progress.lastActivityAt,
|
|
305
|
+
}));
|
|
301
306
|
}
|
|
302
307
|
return true;
|
|
303
308
|
};
|
|
304
309
|
|
|
310
|
+
|
|
305
311
|
const emitUpdateSnapshot = (text: string) => {
|
|
306
312
|
if (!options.onUpdate || processClosed) return;
|
|
307
313
|
const progressSnapshot = snapshotProgress(progress);
|
|
@@ -338,7 +344,6 @@ async function runSingleAttempt(
|
|
|
338
344
|
const now = Date.now();
|
|
339
345
|
progress.durationMs = now - startTime;
|
|
340
346
|
progress.lastActivityAt = now;
|
|
341
|
-
hasSeenActivity = true;
|
|
342
347
|
updateActivityState(now);
|
|
343
348
|
|
|
344
349
|
if (evt.type === "tool_execution_start") {
|
|
@@ -481,27 +486,11 @@ async function runSingleAttempt(
|
|
|
481
486
|
const interrupt = () => {
|
|
482
487
|
if (processClosed || detached || settled) return;
|
|
483
488
|
interruptedByControl = true;
|
|
484
|
-
pausedByInterrupt = true;
|
|
485
489
|
progress.status = "running";
|
|
486
490
|
progress.durationMs = Date.now() - startTime;
|
|
487
491
|
result.interrupted = true;
|
|
488
492
|
result.finalOutput = "Interrupted. Waiting for explicit next action.";
|
|
489
|
-
|
|
490
|
-
const previous = progress.activityState;
|
|
491
|
-
progress.activityState = "paused";
|
|
492
|
-
if (shouldEmitControlEvent(controlConfig, previous, "paused")) {
|
|
493
|
-
const event = buildControlEvent({
|
|
494
|
-
from: previous,
|
|
495
|
-
to: "paused",
|
|
496
|
-
runId: options.runId,
|
|
497
|
-
agent: agent.name,
|
|
498
|
-
index: options.index,
|
|
499
|
-
ts: now,
|
|
500
|
-
});
|
|
501
|
-
allControlEvents.push(event);
|
|
502
|
-
pendingControlEvents.push(event);
|
|
503
|
-
options.onControlEvent?.(event);
|
|
504
|
-
}
|
|
493
|
+
progress.activityState = undefined;
|
|
505
494
|
fireUpdate();
|
|
506
495
|
trySignalChild(proc, "SIGINT");
|
|
507
496
|
setTimeout(() => {
|
|
@@ -523,7 +512,7 @@ async function runSingleAttempt(
|
|
|
523
512
|
result.error = undefined;
|
|
524
513
|
result.finalOutput = result.finalOutput || "Interrupted. Waiting for explicit next action.";
|
|
525
514
|
result.controlEvents = allControlEvents.length ? allControlEvents : undefined;
|
|
526
|
-
progress.activityState =
|
|
515
|
+
progress.activityState = undefined;
|
|
527
516
|
progress.durationMs = Date.now() - startTime;
|
|
528
517
|
result.progressSummary = {
|
|
529
518
|
toolCount: progress.toolCount,
|
|
@@ -738,12 +727,11 @@ export async function runSync(
|
|
|
738
727
|
if (truncationResult.truncated) result.truncation = truncationResult;
|
|
739
728
|
}
|
|
740
729
|
|
|
741
|
-
if (
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
}
|
|
730
|
+
if (options.sessionFile && (existsSync(options.sessionFile) || result.messages?.length)) {
|
|
731
|
+
result.sessionFile = options.sessionFile;
|
|
732
|
+
} else if (shareEnabled && options.sessionDir) {
|
|
733
|
+
const sessionFile = findLatestSessionFile(options.sessionDir);
|
|
734
|
+
if (sessionFile) result.sessionFile = sessionFile;
|
|
747
735
|
}
|
|
748
736
|
|
|
749
737
|
return result;
|