@melihmucuk/pi-crew 1.0.11 → 1.0.13
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 +3 -1
- package/dist/bootstrap-session.d.ts +1 -0
- package/dist/bootstrap-session.js +6 -4
- package/dist/index.js +7 -18
- package/dist/integration/register-renderers.js +9 -3
- package/dist/integration/tool-presentation.d.ts +6 -8
- package/dist/integration/tool-presentation.js +2 -1
- package/dist/integration/tools/crew-abort.js +3 -3
- package/dist/integration/tools/crew-done.js +1 -1
- package/dist/integration/tools/crew-list.js +10 -8
- package/dist/integration/tools/crew-respond.js +1 -1
- package/dist/integration/tools/crew-spawn.js +3 -1
- package/dist/runtime/crew-runtime.d.ts +3 -2
- package/dist/runtime/crew-runtime.js +4 -3
- package/dist/runtime/subagent-state.d.ts +0 -1
- package/dist/runtime/subagent-state.js +0 -2
- package/dist/subagent-messages.d.ts +4 -0
- package/dist/subagent-messages.js +9 -0
- package/dist/tool-registry.d.ts +2 -73
- package/dist/tool-registry.js +11 -15
- package/docs/architecture.md +27 -7
- package/package.json +8 -8
package/README.md
CHANGED
|
@@ -58,7 +58,7 @@ Supported modes:
|
|
|
58
58
|
"abort all active subagents"
|
|
59
59
|
```
|
|
60
60
|
|
|
61
|
-
Tool-triggered aborts are reported back as steering messages with the reason `Aborted by tool request`.
|
|
61
|
+
Tool-triggered aborts are reported back as steering messages with the reason `Aborted by tool request`. User-command aborts and shutdown-triggered aborts use distinct reasons.
|
|
62
62
|
|
|
63
63
|
### `crew_respond`
|
|
64
64
|
|
|
@@ -195,6 +195,8 @@ Override values replace the matching frontmatter fields for the named subagent a
|
|
|
195
195
|
|
|
196
196
|
When the current session owns active subagents, a live status widget appears in the TUI for that session, showing each subagent's ID, model, turn count, and context token usage.
|
|
197
197
|
|
|
198
|
+
On session replacement paths such as `/new`, `/resume`, `/fork`, and `/reload`, subagents keep running and reconnect to the owner session when it becomes active again. On real quit, pi-crew aborts running subagents during shutdown.
|
|
199
|
+
|
|
198
200
|
```
|
|
199
201
|
⠹ scout-a1b2 (claude-haiku-4-5) · turn 3 · 12.5k ctx
|
|
200
202
|
⠸ worker-c3d4 (claude-sonnet-4-6) · turn 7 · 45.2k ctx
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createAgentSession, DefaultResourceLoader, SessionManager, SettingsManager, } from "@mariozechner/pi-coding-agent";
|
|
2
|
-
import {
|
|
3
|
-
function resolveTools(agentConfig
|
|
4
|
-
return
|
|
2
|
+
import { SUPPORTED_TOOL_NAMES } from "./tool-registry.js";
|
|
3
|
+
function resolveTools(agentConfig) {
|
|
4
|
+
return [...(agentConfig.tools ?? SUPPORTED_TOOL_NAMES)];
|
|
5
5
|
}
|
|
6
6
|
function resolveModel(agentConfig, ctx) {
|
|
7
7
|
const warnings = [];
|
|
@@ -33,9 +33,10 @@ export async function bootstrapSession(opts) {
|
|
|
33
33
|
const modelRegistry = ctx.modelRegistry;
|
|
34
34
|
const { model, warnings: modelWarnings } = resolveModel(agentConfig, ctx);
|
|
35
35
|
warnings.push(...modelWarnings);
|
|
36
|
-
const tools = resolveTools(agentConfig
|
|
36
|
+
const tools = resolveTools(agentConfig);
|
|
37
37
|
const resourceLoader = new DefaultResourceLoader({
|
|
38
38
|
cwd,
|
|
39
|
+
agentDir: ctx.agentDir,
|
|
39
40
|
extensionsOverride: (base) => ({
|
|
40
41
|
...base,
|
|
41
42
|
extensions: base.extensions.filter((ext) => !ext.resolvedPath.startsWith(extensionResolvedPath)),
|
|
@@ -59,6 +60,7 @@ export async function bootstrapSession(opts) {
|
|
|
59
60
|
sessionManager.newSession({ parentSession: ctx.parentSessionFile });
|
|
60
61
|
const result = await createAgentSession({
|
|
61
62
|
cwd,
|
|
63
|
+
agentDir: ctx.agentDir,
|
|
62
64
|
model,
|
|
63
65
|
thinkingLevel: agentConfig.thinking,
|
|
64
66
|
tools,
|
package/dist/index.js
CHANGED
|
@@ -10,13 +10,10 @@ function setupProcessHooks() {
|
|
|
10
10
|
if (processHooksSetup)
|
|
11
11
|
return;
|
|
12
12
|
processHooksSetup = true;
|
|
13
|
-
|
|
13
|
+
process.once('SIGINT', () => {
|
|
14
14
|
crewRuntime.abortAll();
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
};
|
|
18
|
-
process.once('SIGINT', () => abortAndExit('SIGINT'));
|
|
19
|
-
process.once('SIGTERM', () => abortAndExit('SIGTERM'));
|
|
15
|
+
process.exit(130);
|
|
16
|
+
});
|
|
20
17
|
process.on('beforeExit', () => crewRuntime.abortAll());
|
|
21
18
|
}
|
|
22
19
|
export default function (pi) {
|
|
@@ -38,20 +35,12 @@ export default function (pi) {
|
|
|
38
35
|
pi.on("session_start", (_event, ctx) => {
|
|
39
36
|
activateSession(ctx);
|
|
40
37
|
});
|
|
41
|
-
pi.on("
|
|
42
|
-
// Session is about to switch - no action needed here.
|
|
43
|
-
// Subagent cleanup is handled by process hooks, not session_shutdown.
|
|
44
|
-
});
|
|
45
|
-
pi.on("session_before_fork", () => {
|
|
46
|
-
// Session is about to fork - no action needed here.
|
|
47
|
-
// Subagent cleanup is handled by process hooks, not session_shutdown.
|
|
48
|
-
});
|
|
49
|
-
pi.on("session_shutdown", (_event, ctx) => {
|
|
38
|
+
pi.on("session_shutdown", (event, ctx) => {
|
|
50
39
|
const sessionId = ctx.sessionManager.getSessionId();
|
|
51
|
-
// Deactivate delivery to this session, but don't abort subagents.
|
|
52
|
-
// Subagents continue running and will complete normally.
|
|
53
|
-
// Real cleanup happens in process exit hooks.
|
|
54
40
|
crewRuntime.deactivateSession(sessionId);
|
|
41
|
+
if (event.reason === "quit") {
|
|
42
|
+
crewRuntime.abortAll();
|
|
43
|
+
}
|
|
55
44
|
});
|
|
56
45
|
registerCrewIntegration(pi, crewRuntime, extensionDir);
|
|
57
46
|
}
|
|
@@ -15,6 +15,11 @@ function getStatusColor(status) {
|
|
|
15
15
|
return "muted";
|
|
16
16
|
}
|
|
17
17
|
}
|
|
18
|
+
function renderWarningMessage(content, theme) {
|
|
19
|
+
const box = new Box(1, 1, (text) => theme.bg("customMessageBg", text));
|
|
20
|
+
box.addChild(new Text(theme.fg("warning", String(content ?? "")), 0, 0));
|
|
21
|
+
return box;
|
|
22
|
+
}
|
|
18
23
|
export function registerCrewMessageRenderers(pi) {
|
|
19
24
|
pi.registerMessageRenderer("crew-result", (message, { expanded }, theme) => {
|
|
20
25
|
const details = message.details;
|
|
@@ -46,8 +51,9 @@ export function registerCrewMessageRenderers(pi) {
|
|
|
46
51
|
return box;
|
|
47
52
|
});
|
|
48
53
|
pi.registerMessageRenderer("crew-remaining", (message, _options, theme) => {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
54
|
+
return renderWarningMessage(message.content, theme);
|
|
55
|
+
});
|
|
56
|
+
pi.registerMessageRenderer("crew-list-warning", (message, _options, theme) => {
|
|
57
|
+
return renderWarningMessage(message.content, theme);
|
|
52
58
|
});
|
|
53
59
|
}
|
|
@@ -1,13 +1,8 @@
|
|
|
1
|
+
import type { AgentToolResult } from "@mariozechner/pi-agent-core";
|
|
1
2
|
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
|
2
3
|
import { Box, Text } from "@mariozechner/pi-tui";
|
|
3
4
|
export type ToolTheme = Parameters<Exclude<Parameters<ExtensionAPI["registerTool"]>[0]["renderCall"], undefined>>[1];
|
|
4
|
-
export type ToolResult =
|
|
5
|
-
content: {
|
|
6
|
-
type: string;
|
|
7
|
-
text?: string;
|
|
8
|
-
}[];
|
|
9
|
-
details: unknown;
|
|
10
|
-
};
|
|
5
|
+
export type ToolResult = AgentToolResult<unknown>;
|
|
11
6
|
export declare function toolError(text: string): {
|
|
12
7
|
content: {
|
|
13
8
|
type: "text";
|
|
@@ -18,7 +13,10 @@ export declare function toolError(text: string): {
|
|
|
18
13
|
error: boolean;
|
|
19
14
|
};
|
|
20
15
|
};
|
|
21
|
-
export declare function toolSuccess(text: string, details?: Record<string, unknown
|
|
16
|
+
export declare function toolSuccess(text: string, details?: Record<string, unknown>, options?: {
|
|
17
|
+
terminate?: boolean;
|
|
18
|
+
}): {
|
|
19
|
+
terminate?: boolean | undefined;
|
|
22
20
|
content: {
|
|
23
21
|
type: "text";
|
|
24
22
|
text: string;
|
|
@@ -6,10 +6,11 @@ export function toolError(text) {
|
|
|
6
6
|
details: { error: true },
|
|
7
7
|
};
|
|
8
8
|
}
|
|
9
|
-
export function toolSuccess(text, details = {}) {
|
|
9
|
+
export function toolSuccess(text, details = {}, options = {}) {
|
|
10
10
|
return {
|
|
11
11
|
content: [{ type: "text", text }],
|
|
12
12
|
details,
|
|
13
|
+
...(options.terminate ? { terminate: true } : {}),
|
|
13
14
|
};
|
|
14
15
|
}
|
|
15
16
|
export function renderCrewCall(theme, name, id, preview) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Type } from "
|
|
1
|
+
import { Type } from "typebox";
|
|
2
2
|
import { renderCrewCall, renderCrewResult, toolError, toolSuccess, } from "../tool-presentation.js";
|
|
3
3
|
function formatAbortToolMessage(result) {
|
|
4
4
|
const parts = [];
|
|
@@ -44,7 +44,7 @@ export function registerCrewAbortTool({ pi, crew }) {
|
|
|
44
44
|
if (abortedIds.length === 0) {
|
|
45
45
|
return toolError("No active subagents in the current session.");
|
|
46
46
|
}
|
|
47
|
-
return toolSuccess(`Aborted ${abortedIds.length} subagent(s): ${abortedIds.join(", ")}`, { ids: abortedIds });
|
|
47
|
+
return toolSuccess(`Aborted ${abortedIds.length} subagent(s): ${abortedIds.join(", ")}`, { ids: abortedIds }, { terminate: true });
|
|
48
48
|
}
|
|
49
49
|
const ids = params.subagent_id
|
|
50
50
|
? [params.subagent_id]
|
|
@@ -60,7 +60,7 @@ export function registerCrewAbortTool({ pi, crew }) {
|
|
|
60
60
|
ids: result.abortedIds,
|
|
61
61
|
missing_ids: result.missingIds,
|
|
62
62
|
foreign_ids: result.foreignIds,
|
|
63
|
-
});
|
|
63
|
+
}, { terminate: true });
|
|
64
64
|
},
|
|
65
65
|
renderCall(args, theme, _context) {
|
|
66
66
|
if (args.all) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Text } from "@mariozechner/pi-tui";
|
|
2
|
-
import { Type } from "
|
|
2
|
+
import { Type } from "typebox";
|
|
3
3
|
import { discoverAgents } from "../../agent-discovery.js";
|
|
4
|
-
import { STATUS_ICON } from "../../subagent-messages.js";
|
|
4
|
+
import { STATUS_ICON, sendCrewListActiveWarning } from "../../subagent-messages.js";
|
|
5
5
|
export function registerCrewListTool({ pi, crew, notifyDiscoveryWarnings, }) {
|
|
6
6
|
pi.registerTool({
|
|
7
7
|
name: "crew_list",
|
|
@@ -19,10 +19,6 @@ export function registerCrewListTool({ pi, crew, notifyDiscoveryWarnings, }) {
|
|
|
19
19
|
const callerSessionId = ctx.sessionManager.getSessionId();
|
|
20
20
|
const running = crew.getActiveSummariesForOwner(callerSessionId);
|
|
21
21
|
const lines = [];
|
|
22
|
-
if (running.length > 0) {
|
|
23
|
-
lines.push("⚠ Active subagents detected. Do not poll crew_list for completion — results arrive as steering messages. Continue with unrelated work or end your turn and wait for the steering messages.");
|
|
24
|
-
lines.push("");
|
|
25
|
-
}
|
|
26
22
|
lines.push("## Available Subagents");
|
|
27
23
|
if (agents.length === 0) {
|
|
28
24
|
lines.push("No valid subagent definitions found. Add `.md` files to `<cwd>/.pi/agents/` or `~/.pi/agent/agents/`.");
|
|
@@ -54,11 +50,17 @@ export function registerCrewListTool({ pi, crew, notifyDiscoveryWarnings, }) {
|
|
|
54
50
|
lines.push(`id: ${agent.id}`);
|
|
55
51
|
lines.push(`name: ${agent.agentName}`);
|
|
56
52
|
lines.push(`status: ${icon} ${agent.status}`);
|
|
57
|
-
lines.push(`task: ${agent.taskPreview}`);
|
|
58
|
-
lines.push(`turns: ${agent.turns}`);
|
|
59
53
|
}
|
|
60
54
|
}
|
|
61
55
|
const text = lines.join("\n");
|
|
56
|
+
if (running.length > 0) {
|
|
57
|
+
Promise.resolve().then(() => {
|
|
58
|
+
sendCrewListActiveWarning(pi.sendMessage.bind(pi), {
|
|
59
|
+
isIdle: ctx.isIdle(),
|
|
60
|
+
triggerTurn: true,
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
}
|
|
62
64
|
return { content: [{ type: "text", text }], details: {} };
|
|
63
65
|
},
|
|
64
66
|
renderCall(_args, theme, _context) {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { getAgentDir } from "@mariozechner/pi-coding-agent";
|
|
2
|
+
import { Type } from "typebox";
|
|
2
3
|
import { discoverAgents } from "../../agent-discovery.js";
|
|
3
4
|
import { renderCrewCall, renderCrewResult, toolError, toolSuccess, } from "../tool-presentation.js";
|
|
4
5
|
export function registerCrewSpawnTool({ pi, crew, extensionDir, notifyDiscoveryWarnings, }) {
|
|
@@ -31,6 +32,7 @@ export function registerCrewSpawnTool({ pi, crew, extensionDir, notifyDiscoveryW
|
|
|
31
32
|
const id = crew.spawn(subagent, params.task, ctx.cwd, ownerSessionId, {
|
|
32
33
|
model: ctx.model,
|
|
33
34
|
modelRegistry: ctx.modelRegistry,
|
|
35
|
+
agentDir: getAgentDir(),
|
|
34
36
|
parentSessionFile: ctx.sessionManager.getSessionFile(),
|
|
35
37
|
onWarning: (msg) => ctx.ui.notify(msg, "warning"),
|
|
36
38
|
}, extensionDir);
|
|
@@ -15,6 +15,7 @@ interface AbortOptions {
|
|
|
15
15
|
export interface SpawnContext {
|
|
16
16
|
model: Model<Api> | undefined;
|
|
17
17
|
modelRegistry: ModelRegistry;
|
|
18
|
+
agentDir: string;
|
|
18
19
|
parentSessionFile?: string;
|
|
19
20
|
onWarning?: (message: string) => void;
|
|
20
21
|
}
|
|
@@ -50,8 +51,8 @@ declare class CrewRuntime {
|
|
|
50
51
|
abortOwned(ids: string[], callerSessionId: string, opts: AbortOptions): AbortOwnedResult;
|
|
51
52
|
abortAllOwned(callerSessionId: string, opts: AbortOptions): string[];
|
|
52
53
|
/**
|
|
53
|
-
* Abort all running subagents
|
|
54
|
-
* Called from
|
|
54
|
+
* Abort all running subagents during shutdown cleanup.
|
|
55
|
+
* Called from SIGINT, session_shutdown(reason="quit"), and beforeExit fallback paths.
|
|
55
56
|
*/
|
|
56
57
|
abortAll(): void;
|
|
57
58
|
getAbortableAgents(): AbortableAgentSummary[];
|
|
@@ -7,6 +7,7 @@ function toBootstrapContext(ctx) {
|
|
|
7
7
|
return {
|
|
8
8
|
model: ctx.model,
|
|
9
9
|
modelRegistry: ctx.modelRegistry,
|
|
10
|
+
agentDir: ctx.agentDir,
|
|
10
11
|
parentSessionFile: ctx.parentSessionFile,
|
|
11
12
|
};
|
|
12
13
|
}
|
|
@@ -265,13 +266,13 @@ class CrewRuntime {
|
|
|
265
266
|
return ids;
|
|
266
267
|
}
|
|
267
268
|
/**
|
|
268
|
-
* Abort all running subagents
|
|
269
|
-
* Called from
|
|
269
|
+
* Abort all running subagents during shutdown cleanup.
|
|
270
|
+
* Called from SIGINT, session_shutdown(reason="quit"), and beforeExit fallback paths.
|
|
270
271
|
*/
|
|
271
272
|
abortAll() {
|
|
272
273
|
const allAgents = this.registry.getAllRunning();
|
|
273
274
|
for (const state of allAgents) {
|
|
274
|
-
this.abort(state.id, { reason: "Aborted
|
|
275
|
+
this.abort(state.id, { reason: "Aborted during shutdown" });
|
|
275
276
|
}
|
|
276
277
|
}
|
|
277
278
|
getAbortableAgents() {
|
|
@@ -15,12 +15,10 @@ export function isAbortableStatus(status) {
|
|
|
15
15
|
return status === "running" || status === "waiting";
|
|
16
16
|
}
|
|
17
17
|
export function buildActiveAgentSummary(state) {
|
|
18
|
-
const taskPreview = state.task.length > 80 ? `${state.task.slice(0, 80)}...` : state.task;
|
|
19
18
|
return {
|
|
20
19
|
id: state.id,
|
|
21
20
|
agentName: state.agentConfig.name,
|
|
22
21
|
status: state.status,
|
|
23
|
-
taskPreview,
|
|
24
22
|
turns: state.turns,
|
|
25
23
|
contextTokens: state.contextTokens,
|
|
26
24
|
model: state.model,
|
|
@@ -31,3 +31,7 @@ export declare function sendRemainingNote(remainingCount: number, sendMessage: S
|
|
|
31
31
|
isIdle: boolean;
|
|
32
32
|
triggerTurn: boolean;
|
|
33
33
|
}): void;
|
|
34
|
+
export declare function sendCrewListActiveWarning(sendMessage: SendMessageFn, opts: {
|
|
35
|
+
isIdle: boolean;
|
|
36
|
+
triggerTurn: boolean;
|
|
37
|
+
}): void;
|
|
@@ -57,3 +57,12 @@ export function sendRemainingNote(remainingCount, sendMessage, opts) {
|
|
|
57
57
|
? { triggerTurn: opts.triggerTurn }
|
|
58
58
|
: { deliverAs: "steer", triggerTurn: opts.triggerTurn });
|
|
59
59
|
}
|
|
60
|
+
export function sendCrewListActiveWarning(sendMessage, opts) {
|
|
61
|
+
sendMessage({
|
|
62
|
+
customType: "crew-list-warning",
|
|
63
|
+
content: "⚠ Active subagents detected. Do not poll crew_list for completion — results arrive as steering messages. Continue with unrelated work or end your turn and wait for the steering messages.",
|
|
64
|
+
display: true,
|
|
65
|
+
}, opts.isIdle
|
|
66
|
+
? { triggerTurn: opts.triggerTurn }
|
|
67
|
+
: { deliverAs: "steer", triggerTurn: opts.triggerTurn });
|
|
68
|
+
}
|
package/dist/tool-registry.d.ts
CHANGED
|
@@ -1,76 +1,5 @@
|
|
|
1
|
-
declare const
|
|
2
|
-
|
|
3
|
-
path: import("@sinclair/typebox").TString;
|
|
4
|
-
offset: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TNumber>;
|
|
5
|
-
limit: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TNumber>;
|
|
6
|
-
}>, any>;
|
|
7
|
-
bash: (cwd: string) => import("@mariozechner/pi-agent-core").AgentTool<import("@sinclair/typebox").TObject<{
|
|
8
|
-
command: import("@sinclair/typebox").TString;
|
|
9
|
-
timeout: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TNumber>;
|
|
10
|
-
}>, any>;
|
|
11
|
-
edit: (cwd: string) => import("@mariozechner/pi-agent-core").AgentTool<import("@sinclair/typebox").TObject<{
|
|
12
|
-
path: import("@sinclair/typebox").TString;
|
|
13
|
-
edits: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TObject<{
|
|
14
|
-
oldText: import("@sinclair/typebox").TString;
|
|
15
|
-
newText: import("@sinclair/typebox").TString;
|
|
16
|
-
}>>;
|
|
17
|
-
}>, any>;
|
|
18
|
-
write: (cwd: string) => import("@mariozechner/pi-agent-core").AgentTool<import("@sinclair/typebox").TObject<{
|
|
19
|
-
path: import("@sinclair/typebox").TString;
|
|
20
|
-
content: import("@sinclair/typebox").TString;
|
|
21
|
-
}>, any>;
|
|
22
|
-
grep: (cwd: string) => import("@mariozechner/pi-agent-core").AgentTool<import("@sinclair/typebox").TObject<{
|
|
23
|
-
pattern: import("@sinclair/typebox").TString;
|
|
24
|
-
path: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
25
|
-
glob: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
26
|
-
ignoreCase: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TBoolean>;
|
|
27
|
-
literal: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TBoolean>;
|
|
28
|
-
context: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TNumber>;
|
|
29
|
-
limit: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TNumber>;
|
|
30
|
-
}>, any>;
|
|
31
|
-
find: (cwd: string) => import("@mariozechner/pi-agent-core").AgentTool<import("@sinclair/typebox").TObject<{
|
|
32
|
-
pattern: import("@sinclair/typebox").TString;
|
|
33
|
-
path: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
34
|
-
limit: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TNumber>;
|
|
35
|
-
}>, any>;
|
|
36
|
-
ls: (cwd: string) => import("@mariozechner/pi-agent-core").AgentTool<import("@sinclair/typebox").TObject<{
|
|
37
|
-
path: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
38
|
-
limit: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TNumber>;
|
|
39
|
-
}>, any>;
|
|
40
|
-
};
|
|
41
|
-
export type SupportedToolName = keyof typeof TOOL_FACTORIES;
|
|
1
|
+
declare const SUPPORTED_TOOL_NAMES_LITERAL: readonly ["read", "bash", "edit", "write", "grep", "find", "ls"];
|
|
2
|
+
export type SupportedToolName = (typeof SUPPORTED_TOOL_NAMES_LITERAL)[number];
|
|
42
3
|
export declare const SUPPORTED_TOOL_NAMES: readonly ("read" | "bash" | "edit" | "write" | "grep" | "find" | "ls")[];
|
|
43
4
|
export declare function isSupportedToolName(name: string): name is SupportedToolName;
|
|
44
|
-
export declare function createSupportedTools(toolNames: readonly SupportedToolName[], cwd: string): (import("@mariozechner/pi-agent-core").AgentTool<import("@sinclair/typebox").TObject<{
|
|
45
|
-
path: import("@sinclair/typebox").TString;
|
|
46
|
-
offset: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TNumber>;
|
|
47
|
-
limit: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TNumber>;
|
|
48
|
-
}>, any> | import("@mariozechner/pi-agent-core").AgentTool<import("@sinclair/typebox").TObject<{
|
|
49
|
-
command: import("@sinclair/typebox").TString;
|
|
50
|
-
timeout: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TNumber>;
|
|
51
|
-
}>, any> | import("@mariozechner/pi-agent-core").AgentTool<import("@sinclair/typebox").TObject<{
|
|
52
|
-
path: import("@sinclair/typebox").TString;
|
|
53
|
-
edits: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TObject<{
|
|
54
|
-
oldText: import("@sinclair/typebox").TString;
|
|
55
|
-
newText: import("@sinclair/typebox").TString;
|
|
56
|
-
}>>;
|
|
57
|
-
}>, any> | import("@mariozechner/pi-agent-core").AgentTool<import("@sinclair/typebox").TObject<{
|
|
58
|
-
path: import("@sinclair/typebox").TString;
|
|
59
|
-
content: import("@sinclair/typebox").TString;
|
|
60
|
-
}>, any> | import("@mariozechner/pi-agent-core").AgentTool<import("@sinclair/typebox").TObject<{
|
|
61
|
-
pattern: import("@sinclair/typebox").TString;
|
|
62
|
-
path: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
63
|
-
glob: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
64
|
-
ignoreCase: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TBoolean>;
|
|
65
|
-
literal: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TBoolean>;
|
|
66
|
-
context: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TNumber>;
|
|
67
|
-
limit: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TNumber>;
|
|
68
|
-
}>, any> | import("@mariozechner/pi-agent-core").AgentTool<import("@sinclair/typebox").TObject<{
|
|
69
|
-
pattern: import("@sinclair/typebox").TString;
|
|
70
|
-
path: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
71
|
-
limit: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TNumber>;
|
|
72
|
-
}>, any> | import("@mariozechner/pi-agent-core").AgentTool<import("@sinclair/typebox").TObject<{
|
|
73
|
-
path: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
74
|
-
limit: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TNumber>;
|
|
75
|
-
}>, any>)[];
|
|
76
5
|
export {};
|
package/dist/tool-registry.js
CHANGED
|
@@ -1,17 +1,13 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
export const SUPPORTED_TOOL_NAMES = Object.freeze(Object.keys(TOOL_FACTORIES));
|
|
1
|
+
const SUPPORTED_TOOL_NAMES_LITERAL = [
|
|
2
|
+
"read",
|
|
3
|
+
"bash",
|
|
4
|
+
"edit",
|
|
5
|
+
"write",
|
|
6
|
+
"grep",
|
|
7
|
+
"find",
|
|
8
|
+
"ls",
|
|
9
|
+
];
|
|
10
|
+
export const SUPPORTED_TOOL_NAMES = Object.freeze([...SUPPORTED_TOOL_NAMES_LITERAL]);
|
|
12
11
|
export function isSupportedToolName(name) {
|
|
13
|
-
return name
|
|
14
|
-
}
|
|
15
|
-
export function createSupportedTools(toolNames, cwd) {
|
|
16
|
-
return toolNames.map((toolName) => TOOL_FACTORIES[toolName](cwd));
|
|
12
|
+
return SUPPORTED_TOOL_NAMES.includes(name);
|
|
17
13
|
}
|
package/docs/architecture.md
CHANGED
|
@@ -13,6 +13,7 @@ Primary components:
|
|
|
13
13
|
- `extension/runtime/crew-runtime.ts` - Process-level singleton owning all subagent state
|
|
14
14
|
- `extension/runtime/subagent-registry.ts` - In-memory subagent registry
|
|
15
15
|
- `extension/runtime/delivery-coordinator.ts` - Owner-based result routing
|
|
16
|
+
- `extension/runtime/overflow-recovery.ts` - Context overflow retry tracking for subagent prompts
|
|
16
17
|
- `extension/bootstrap-session.ts` - Subagent session construction with extension filtering
|
|
17
18
|
- `extension/agent-discovery.ts` - Subagent definition discovery and validation
|
|
18
19
|
|
|
@@ -20,12 +21,13 @@ Primary components:
|
|
|
20
21
|
|
|
21
22
|
### 2.1 CrewRuntime singleton
|
|
22
23
|
|
|
23
|
-
`CrewRuntime` is a process-level singleton that survives pi runtime replacement (`/resume`, `/new`, `/fork`). When pi discards an old extension instance and creates a new one, the new instance reconnects to the same `crewRuntime` and picks up existing subagent state.
|
|
24
|
+
`CrewRuntime` is a process-level singleton that survives pi runtime replacement (`/resume`, `/new`, `/fork`, `/reload`). When pi discards an old extension instance and creates a new one, the new instance reconnects to the same `crewRuntime` and picks up existing subagent state.
|
|
24
25
|
|
|
25
26
|
Responsibilities:
|
|
26
27
|
|
|
27
28
|
- Create subagent state records
|
|
28
29
|
- Bootstrap isolated subagent sessions
|
|
30
|
+
- Run subagent prompt cycles with overflow recovery
|
|
29
31
|
- Transition subagents between states
|
|
30
32
|
- Deliver results to owner sessions
|
|
31
33
|
|
|
@@ -35,12 +37,24 @@ Routes subagent results to the correct session at the correct time. Key behavior
|
|
|
35
37
|
|
|
36
38
|
- Tracks active session via `ActiveRuntimeBinding` (set on `session_start`, cleared on `session_shutdown`)
|
|
37
39
|
- Queues results when owner session is inactive
|
|
38
|
-
- Flushes queued results when owner session activates
|
|
40
|
+
- Flushes queued results when owner session activates on any `session_start`; resume/fork are the important replacement paths because subagents survive runtime replacement within the same process
|
|
39
41
|
- Uses `triggerTurn: false/true` split to preserve ordering between `crew-result` and `crew-remaining`
|
|
40
42
|
|
|
41
43
|
Underlying delivery: see pi's `sendMessage({ deliverAs, triggerTurn })` in extensions.md.
|
|
42
44
|
|
|
43
|
-
|
|
45
|
+
`crew_list` uses the same idle/streaming delivery rules for its `crew-list-warning` custom message when active subagents exist. The warning is separate from tool output so the list remains a one-time snapshot while anti-polling guidance is delivered as a visible message.
|
|
46
|
+
|
|
47
|
+
### 2.3 Overflow recovery
|
|
48
|
+
|
|
49
|
+
Subagent prompt cycles are wrapped by overflow recovery tracking. The tracker observes `agent_end`, `compaction_start`, `compaction_end`, `auto_retry_start`, and `auto_retry_end` events to distinguish normal completion from context-overflow compaction and retry.
|
|
50
|
+
|
|
51
|
+
Outcomes:
|
|
52
|
+
|
|
53
|
+
- No overflow observed → prompt outcome is based on the final assistant message.
|
|
54
|
+
- Overflow compaction completes with retry and the retry reaches a terminal `agent_end` → recovered; prompt outcome is based on the final assistant message.
|
|
55
|
+
- Overflow handling times out, is cancelled, or compaction does not retry → failed; the subagent settles as `error` unless the final assistant message already reported an error.
|
|
56
|
+
|
|
57
|
+
### 2.4 Subagent registry
|
|
44
58
|
|
|
45
59
|
In-memory, process-scoped: `Map<subagentId, SubagentState>`
|
|
46
60
|
|
|
@@ -80,7 +94,7 @@ Critical: `deliverAs: "steer"` to an idle session leaves the message unprocessed
|
|
|
80
94
|
|
|
81
95
|
### 4.3 Deferred flush
|
|
82
96
|
|
|
83
|
-
Pending message flush after `session_start` is deferred to next macrotask. Synchronous delivery loses custom message persistence (pi-core emits `session_start` before reconnecting agent listener during resume).
|
|
97
|
+
Pending message flush after `session_start` is deferred to next macrotask. Synchronous delivery loses custom message persistence (pi-core emits `session_start` before reconnecting agent listener during resume). While a flush is scheduled, new deliveries for that owner are queued so ordering is preserved.
|
|
84
98
|
|
|
85
99
|
### 4.4 TTL cleanup
|
|
86
100
|
|
|
@@ -109,13 +123,17 @@ After prompt cycle completion, inspect assistant stop reason:
|
|
|
109
123
|
|
|
110
124
|
`interactive: true` subagents enter `waiting` after each response. They accept follow-up messages via `crew_respond` until explicitly closed with `crew_done`. Closing does NOT emit a duplicate `crew-result`.
|
|
111
125
|
|
|
126
|
+
### 5.4 Tool completion behavior
|
|
127
|
+
|
|
128
|
+
`crew_respond` returns immediately and delivers the subagent response asynchronously. Successful `crew_abort` results terminate the current tool turn after aborting owned subagents.
|
|
129
|
+
|
|
112
130
|
## 6. Ownership and isolation
|
|
113
131
|
|
|
114
132
|
Invariants:
|
|
115
133
|
|
|
116
134
|
1. `crew_list`, `crew_abort`, `crew_respond`, `crew_done`, status widget: session-scoped. Only owner sees/controls.
|
|
117
135
|
2. `/pi-crew-abort`: cross-session emergency escape hatch.
|
|
118
|
-
3. `session_shutdown` deactivates delivery binding
|
|
136
|
+
3. `session_shutdown` always deactivates delivery binding. On replacement paths (`reload`, `new`, `resume`, `fork`), subagents continue running. On `quit`, the extension aborts all running subagents. `SIGINT` also aborts via a process hook, and `beforeExit` remains a fallback.
|
|
119
137
|
|
|
120
138
|
## 7. Subagent definition model
|
|
121
139
|
|
|
@@ -145,8 +163,10 @@ JSON overrides: `~/.pi/agent/pi-crew.json` (global), `<cwd>/.pi/pi-crew.json` (p
|
|
|
145
163
|
5. `crew_done` cleans up only; no duplicate result message.
|
|
146
164
|
6. Queued results flush when owner session becomes active.
|
|
147
165
|
7. `crew-result` messages appear before `crew-remaining` notes (ordering via `triggerTurn` split).
|
|
148
|
-
8.
|
|
149
|
-
9.
|
|
166
|
+
8. `crew-list-warning` is delivered as a separate custom message when `crew_list` is called while the owner has active subagents.
|
|
167
|
+
9. Pending messages preserved for inactive sessions; TTL (24h) prevents memory leak.
|
|
168
|
+
10. Active subagent state survives runtime replacement within same process.
|
|
169
|
+
11. Graceful quit aborts subagents through `session_shutdown.reason === "quit"`; replacement paths do not.
|
|
150
170
|
|
|
151
171
|
## 9. Reading guide
|
|
152
172
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@melihmucuk/pi-crew",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.13",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Non-blocking subagent orchestration for pi coding agent",
|
|
6
6
|
"files": [
|
|
@@ -39,15 +39,15 @@
|
|
|
39
39
|
"@mariozechner/pi-ai": "*",
|
|
40
40
|
"@mariozechner/pi-coding-agent": "*",
|
|
41
41
|
"@mariozechner/pi-tui": "*",
|
|
42
|
-
"
|
|
42
|
+
"typebox": "*"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
|
-
"@mariozechner/pi-agent-core": "^0.
|
|
46
|
-
"@mariozechner/pi-ai": "^0.
|
|
47
|
-
"@mariozechner/pi-coding-agent": "^0.
|
|
48
|
-
"@mariozechner/pi-tui": "^0.
|
|
49
|
-
"@sinclair/typebox": "^0.34.49",
|
|
45
|
+
"@mariozechner/pi-agent-core": "^0.70.0",
|
|
46
|
+
"@mariozechner/pi-ai": "^0.70.0",
|
|
47
|
+
"@mariozechner/pi-coding-agent": "^0.70.0",
|
|
48
|
+
"@mariozechner/pi-tui": "^0.70.0",
|
|
50
49
|
"@types/node": "^22.19.17",
|
|
50
|
+
"typebox": "^1.1.33",
|
|
51
51
|
"typescript": "^5.9.3"
|
|
52
52
|
}
|
|
53
|
-
}
|
|
53
|
+
}
|