@melihmucuk/pi-crew 1.0.12 → 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/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 +1 -1
- 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/docs/architecture.md +24 -5
- package/package.json +8 -8
|
@@ -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,5 +1,5 @@
|
|
|
1
1
|
import { getAgentDir } from "@mariozechner/pi-coding-agent";
|
|
2
|
-
import { Type } from "
|
|
2
|
+
import { Type } from "typebox";
|
|
3
3
|
import { discoverAgents } from "../../agent-discovery.js";
|
|
4
4
|
import { renderCrewCall, renderCrewResult, toolError, toolSuccess, } from "../tool-presentation.js";
|
|
5
5
|
export function registerCrewSpawnTool({ pi, crew, extensionDir, notifyDiscoveryWarnings, }) {
|
|
@@ -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/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
|
|
|
@@ -26,6 +27,7 @@ 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
|
|
|
@@ -40,7 +42,19 @@ Routes subagent results to the correct session at the correct time. Key behavior
|
|
|
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,6 +123,10 @@ 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:
|
|
@@ -145,9 +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.
|
|
150
|
-
10.
|
|
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.
|
|
151
170
|
|
|
152
171
|
## 9. Reading guide
|
|
153
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
|
+
}
|