@melihmucuk/pi-crew 1.0.13 → 1.0.15
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 +19 -18
- package/agents/code-reviewer.md +52 -104
- package/agents/oracle.md +26 -52
- package/agents/planner.md +7 -7
- package/agents/quality-reviewer.md +90 -131
- package/agents/scout.md +3 -2
- package/agents/worker.md +8 -2
- package/extension/agent-discovery.ts +791 -0
- package/extension/bootstrap-session.ts +131 -0
- package/extension/index.ts +63 -0
- package/extension/integration/register-renderers.ts +77 -0
- package/extension/integration/register-tools.ts +39 -0
- package/extension/integration/tool-presentation.ts +50 -0
- package/extension/integration/tools/crew-abort.ts +126 -0
- package/extension/integration/tools/crew-done.ts +46 -0
- package/extension/integration/tools/crew-list.ts +92 -0
- package/extension/integration/tools/crew-respond.ts +59 -0
- package/extension/integration/tools/crew-spawn.ts +87 -0
- package/extension/integration/tools/tool-deps.ts +16 -0
- package/extension/integration.ts +13 -0
- package/extension/runtime/crew-runtime.ts +426 -0
- package/extension/runtime/delivery-coordinator.ts +131 -0
- package/extension/runtime/overflow-recovery.ts +211 -0
- package/extension/runtime/subagent-registry.ts +78 -0
- package/extension/runtime/subagent-state.ts +59 -0
- package/extension/status-widget.ts +107 -0
- package/extension/subagent-messages.ts +124 -0
- package/extension/tool-registry.ts +19 -0
- package/package.json +14 -14
- package/prompts/pi-crew-plan.md +46 -37
- package/prompts/pi-crew-review.md +3 -1
- package/skills/pi-crew/SKILL.md +129 -0
- package/dist/agent-discovery.d.ts +0 -29
- package/dist/agent-discovery.js +0 -527
- package/dist/bootstrap-session.d.ts +0 -21
- package/dist/bootstrap-session.js +0 -74
- package/dist/index.d.ts +0 -2
- package/dist/index.js +0 -46
- package/dist/integration/register-command.d.ts +0 -3
- package/dist/integration/register-command.js +0 -51
- package/dist/integration/register-renderers.d.ts +0 -2
- package/dist/integration/register-renderers.js +0 -59
- package/dist/integration/register-tools.d.ts +0 -3
- package/dist/integration/register-tools.js +0 -25
- package/dist/integration/tool-presentation.d.ts +0 -27
- package/dist/integration/tool-presentation.js +0 -29
- package/dist/integration/tools/crew-abort.d.ts +0 -2
- package/dist/integration/tools/crew-abort.js +0 -79
- package/dist/integration/tools/crew-done.d.ts +0 -2
- package/dist/integration/tools/crew-done.js +0 -28
- package/dist/integration/tools/crew-list.d.ts +0 -2
- package/dist/integration/tools/crew-list.js +0 -74
- package/dist/integration/tools/crew-respond.d.ts +0 -2
- package/dist/integration/tools/crew-respond.js +0 -32
- package/dist/integration/tools/crew-spawn.d.ts +0 -2
- package/dist/integration/tools/crew-spawn.js +0 -48
- package/dist/integration/tools/tool-deps.d.ts +0 -9
- package/dist/integration/tools/tool-deps.js +0 -1
- package/dist/integration.d.ts +0 -3
- package/dist/integration.js +0 -8
- package/dist/runtime/crew-runtime.d.ts +0 -62
- package/dist/runtime/crew-runtime.js +0 -285
- package/dist/runtime/delivery-coordinator.d.ts +0 -26
- package/dist/runtime/delivery-coordinator.js +0 -86
- package/dist/runtime/overflow-recovery.d.ts +0 -3
- package/dist/runtime/overflow-recovery.js +0 -155
- package/dist/runtime/subagent-registry.d.ts +0 -14
- package/dist/runtime/subagent-registry.js +0 -58
- package/dist/runtime/subagent-state.d.ts +0 -35
- package/dist/runtime/subagent-state.js +0 -32
- package/dist/status-widget.d.ts +0 -3
- package/dist/status-widget.js +0 -84
- package/dist/subagent-messages.d.ts +0 -37
- package/dist/subagent-messages.js +0 -68
- package/dist/tool-registry.d.ts +0 -5
- package/dist/tool-registry.js +0 -13
- package/docs/architecture.md +0 -187
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import { randomBytes } from "node:crypto";
|
|
2
|
-
export function generateId(name, existingIds) {
|
|
3
|
-
for (let i = 0; i < 10; i++) {
|
|
4
|
-
const id = `${name}-${randomBytes(4).toString("hex")}`;
|
|
5
|
-
if (!existingIds.has(id))
|
|
6
|
-
return id;
|
|
7
|
-
}
|
|
8
|
-
return `${name}-${randomBytes(8).toString("hex")}`;
|
|
9
|
-
}
|
|
10
|
-
// Status may change externally via abort(). Standalone function avoids TS narrowing.
|
|
11
|
-
export function isAborted(state) {
|
|
12
|
-
return state.status === "aborted";
|
|
13
|
-
}
|
|
14
|
-
export function isAbortableStatus(status) {
|
|
15
|
-
return status === "running" || status === "waiting";
|
|
16
|
-
}
|
|
17
|
-
export function buildActiveAgentSummary(state) {
|
|
18
|
-
return {
|
|
19
|
-
id: state.id,
|
|
20
|
-
agentName: state.agentConfig.name,
|
|
21
|
-
status: state.status,
|
|
22
|
-
turns: state.turns,
|
|
23
|
-
contextTokens: state.contextTokens,
|
|
24
|
-
model: state.model,
|
|
25
|
-
};
|
|
26
|
-
}
|
|
27
|
-
export function buildAbortableAgentSummary(state) {
|
|
28
|
-
return {
|
|
29
|
-
id: state.id,
|
|
30
|
-
agentName: state.agentConfig.name,
|
|
31
|
-
};
|
|
32
|
-
}
|
package/dist/status-widget.d.ts
DELETED
package/dist/status-widget.js
DELETED
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
import { Text } from "@mariozechner/pi-tui";
|
|
2
|
-
const SPINNER_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
|
3
|
-
const SPINNER_INTERVAL_MS = 80;
|
|
4
|
-
function formatTokens(tokens) {
|
|
5
|
-
if (tokens >= 1_000_000)
|
|
6
|
-
return `${(tokens / 1_000_000).toFixed(1)}M`;
|
|
7
|
-
if (tokens >= 1_000)
|
|
8
|
-
return `${(tokens / 1_000).toFixed(1)}k`;
|
|
9
|
-
return String(tokens);
|
|
10
|
-
}
|
|
11
|
-
function buildLine(agent, frame) {
|
|
12
|
-
const model = agent.model ?? "…";
|
|
13
|
-
const icon = agent.status === "waiting" ? "⏳" : frame;
|
|
14
|
-
return `${icon} ${agent.id} (${model}) · turn ${agent.turns} · ${formatTokens(agent.contextTokens)} ctx`;
|
|
15
|
-
}
|
|
16
|
-
let widget;
|
|
17
|
-
function disposeWidget(state) {
|
|
18
|
-
clearInterval(state.timer);
|
|
19
|
-
if (widget === state) {
|
|
20
|
-
widget = undefined;
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
function clearWidget() {
|
|
24
|
-
const current = widget;
|
|
25
|
-
if (!current)
|
|
26
|
-
return;
|
|
27
|
-
disposeWidget(current);
|
|
28
|
-
current.ctx.ui.setWidget("crew-status", undefined);
|
|
29
|
-
}
|
|
30
|
-
function hasRunningAgent(agents) {
|
|
31
|
-
return agents.some((agent) => agent.status === "running");
|
|
32
|
-
}
|
|
33
|
-
function syncWidgetText(state, agents) {
|
|
34
|
-
const frame = SPINNER_FRAMES[state.frameIndex % SPINNER_FRAMES.length];
|
|
35
|
-
const lines = agents.map((agent) => buildLine(agent, frame));
|
|
36
|
-
state.text.setText(lines.join("\n"));
|
|
37
|
-
state.tui.requestRender();
|
|
38
|
-
}
|
|
39
|
-
export function updateWidget(ctx, crew) {
|
|
40
|
-
if (!ctx.hasUI) {
|
|
41
|
-
clearWidget();
|
|
42
|
-
return;
|
|
43
|
-
}
|
|
44
|
-
const ownerSessionId = ctx.sessionManager.getSessionId();
|
|
45
|
-
const running = crew.getActiveSummariesForOwner(ownerSessionId);
|
|
46
|
-
if (running.length === 0) {
|
|
47
|
-
clearWidget();
|
|
48
|
-
return;
|
|
49
|
-
}
|
|
50
|
-
if (widget && widget.ctx !== ctx) {
|
|
51
|
-
clearWidget();
|
|
52
|
-
}
|
|
53
|
-
if (widget) {
|
|
54
|
-
syncWidgetText(widget, running);
|
|
55
|
-
return;
|
|
56
|
-
}
|
|
57
|
-
ctx.ui.setWidget("crew-status", (tui, _theme) => {
|
|
58
|
-
const text = new Text("", 1, 0);
|
|
59
|
-
const state = {
|
|
60
|
-
ctx,
|
|
61
|
-
text,
|
|
62
|
-
tui,
|
|
63
|
-
frameIndex: 0,
|
|
64
|
-
timer: setInterval(() => {
|
|
65
|
-
const agents = crew.getActiveSummariesForOwner(ownerSessionId);
|
|
66
|
-
if (agents.length === 0) {
|
|
67
|
-
clearWidget();
|
|
68
|
-
return;
|
|
69
|
-
}
|
|
70
|
-
if (!hasRunningAgent(agents))
|
|
71
|
-
return;
|
|
72
|
-
state.frameIndex++;
|
|
73
|
-
syncWidgetText(state, agents);
|
|
74
|
-
}, SPINNER_INTERVAL_MS),
|
|
75
|
-
};
|
|
76
|
-
widget = state;
|
|
77
|
-
syncWidgetText(state, running);
|
|
78
|
-
return Object.assign(text, {
|
|
79
|
-
dispose() {
|
|
80
|
-
disposeWidget(state);
|
|
81
|
-
},
|
|
82
|
-
});
|
|
83
|
-
});
|
|
84
|
-
}
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
|
2
|
-
export type SubagentStatus = "running" | "waiting" | "done" | "error" | "aborted";
|
|
3
|
-
export type SendMessageFn = ExtensionAPI["sendMessage"];
|
|
4
|
-
export declare const STATUS_ICON: Record<SubagentStatus, string>;
|
|
5
|
-
export declare const STATUS_LABEL: Record<SubagentStatus, string>;
|
|
6
|
-
export interface SteeringPayload {
|
|
7
|
-
id: string;
|
|
8
|
-
agentName: string;
|
|
9
|
-
sessionFile?: string;
|
|
10
|
-
status: SubagentStatus;
|
|
11
|
-
result?: string;
|
|
12
|
-
error?: string;
|
|
13
|
-
}
|
|
14
|
-
export interface CrewResultMessageDetails {
|
|
15
|
-
agentId: string;
|
|
16
|
-
agentName: string;
|
|
17
|
-
sessionFile?: string;
|
|
18
|
-
status: SubagentStatus;
|
|
19
|
-
body?: string;
|
|
20
|
-
}
|
|
21
|
-
export declare function getCrewResultTitle(details: {
|
|
22
|
-
agentId: string;
|
|
23
|
-
agentName: string;
|
|
24
|
-
status: SubagentStatus;
|
|
25
|
-
}): string;
|
|
26
|
-
export declare function sendSteeringMessage(payload: SteeringPayload, sendMessage: SendMessageFn, opts: {
|
|
27
|
-
isIdle: boolean;
|
|
28
|
-
triggerTurn: boolean;
|
|
29
|
-
}): void;
|
|
30
|
-
export declare function sendRemainingNote(remainingCount: number, sendMessage: SendMessageFn, opts: {
|
|
31
|
-
isIdle: boolean;
|
|
32
|
-
triggerTurn: boolean;
|
|
33
|
-
}): void;
|
|
34
|
-
export declare function sendCrewListActiveWarning(sendMessage: SendMessageFn, opts: {
|
|
35
|
-
isIdle: boolean;
|
|
36
|
-
triggerTurn: boolean;
|
|
37
|
-
}): void;
|
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
export const STATUS_ICON = {
|
|
2
|
-
running: "⏳",
|
|
3
|
-
waiting: "⏳",
|
|
4
|
-
done: "✅",
|
|
5
|
-
error: "❌",
|
|
6
|
-
aborted: "⏹️",
|
|
7
|
-
};
|
|
8
|
-
export const STATUS_LABEL = {
|
|
9
|
-
running: "running",
|
|
10
|
-
waiting: "waiting for response",
|
|
11
|
-
done: "done",
|
|
12
|
-
error: "failed",
|
|
13
|
-
aborted: "aborted",
|
|
14
|
-
};
|
|
15
|
-
export function getCrewResultTitle(details) {
|
|
16
|
-
return `Subagent '${details.agentName}' (${details.agentId}) ${STATUS_LABEL[details.status]}`;
|
|
17
|
-
}
|
|
18
|
-
function getSteeringBody(payload) {
|
|
19
|
-
return (payload.status === "error" || payload.status === "aborted")
|
|
20
|
-
? (payload.error ?? payload.result)
|
|
21
|
-
: (payload.result ?? payload.error);
|
|
22
|
-
}
|
|
23
|
-
export function sendSteeringMessage(payload, sendMessage, opts) {
|
|
24
|
-
const body = getSteeringBody(payload);
|
|
25
|
-
const title = getCrewResultTitle({
|
|
26
|
-
agentId: payload.id,
|
|
27
|
-
agentName: payload.agentName,
|
|
28
|
-
status: payload.status,
|
|
29
|
-
});
|
|
30
|
-
const content = body
|
|
31
|
-
? `**${STATUS_ICON[payload.status]} ${title}**\n\n${body}`
|
|
32
|
-
: `**${STATUS_ICON[payload.status]} ${title}**`;
|
|
33
|
-
const message = {
|
|
34
|
-
customType: "crew-result",
|
|
35
|
-
content,
|
|
36
|
-
display: true,
|
|
37
|
-
details: {
|
|
38
|
-
agentId: payload.id,
|
|
39
|
-
agentName: payload.agentName,
|
|
40
|
-
sessionFile: payload.sessionFile,
|
|
41
|
-
status: payload.status,
|
|
42
|
-
body,
|
|
43
|
-
},
|
|
44
|
-
};
|
|
45
|
-
sendMessage(message, opts.isIdle
|
|
46
|
-
? { triggerTurn: opts.triggerTurn }
|
|
47
|
-
: { deliverAs: "steer", triggerTurn: opts.triggerTurn });
|
|
48
|
-
}
|
|
49
|
-
export function sendRemainingNote(remainingCount, sendMessage, opts) {
|
|
50
|
-
if (remainingCount <= 0)
|
|
51
|
-
return;
|
|
52
|
-
sendMessage({
|
|
53
|
-
customType: "crew-remaining",
|
|
54
|
-
content: `⏳ ${remainingCount} subagent(s) still running`,
|
|
55
|
-
display: true,
|
|
56
|
-
}, opts.isIdle
|
|
57
|
-
? { triggerTurn: opts.triggerTurn }
|
|
58
|
-
: { deliverAs: "steer", triggerTurn: opts.triggerTurn });
|
|
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
DELETED
|
@@ -1,5 +0,0 @@
|
|
|
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];
|
|
3
|
-
export declare const SUPPORTED_TOOL_NAMES: readonly ("read" | "bash" | "edit" | "write" | "grep" | "find" | "ls")[];
|
|
4
|
-
export declare function isSupportedToolName(name: string): name is SupportedToolName;
|
|
5
|
-
export {};
|
package/dist/tool-registry.js
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
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]);
|
|
11
|
-
export function isSupportedToolName(name) {
|
|
12
|
-
return SUPPORTED_TOOL_NAMES.includes(name);
|
|
13
|
-
}
|
package/docs/architecture.md
DELETED
|
@@ -1,187 +0,0 @@
|
|
|
1
|
-
# pi-crew Architecture
|
|
2
|
-
|
|
3
|
-
This document explains the technical architecture of `@melihmucuk/pi-crew`, focusing on what makes this extension unique.
|
|
4
|
-
|
|
5
|
-
For pi fundamentals, see pi docs: `extensions.md`, `sdk.md`, `session.md`. Project-level guardrails are in `AGENTS.md`.
|
|
6
|
-
|
|
7
|
-
## 1. What pi-crew adds
|
|
8
|
-
|
|
9
|
-
`pi-crew` is a non-blocking subagent orchestration extension. It lets one pi session delegate work to isolated subagent sessions without blocking the caller. Results are delivered back as `crew-result` custom messages.
|
|
10
|
-
|
|
11
|
-
Primary components:
|
|
12
|
-
|
|
13
|
-
- `extension/runtime/crew-runtime.ts` - Process-level singleton owning all subagent state
|
|
14
|
-
- `extension/runtime/subagent-registry.ts` - In-memory subagent registry
|
|
15
|
-
- `extension/runtime/delivery-coordinator.ts` - Owner-based result routing
|
|
16
|
-
- `extension/runtime/overflow-recovery.ts` - Context overflow retry tracking for subagent prompts
|
|
17
|
-
- `extension/bootstrap-session.ts` - Subagent session construction with extension filtering
|
|
18
|
-
- `extension/agent-discovery.ts` - Subagent definition discovery and validation
|
|
19
|
-
|
|
20
|
-
## 2. Core runtime components
|
|
21
|
-
|
|
22
|
-
### 2.1 CrewRuntime singleton
|
|
23
|
-
|
|
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.
|
|
25
|
-
|
|
26
|
-
Responsibilities:
|
|
27
|
-
|
|
28
|
-
- Create subagent state records
|
|
29
|
-
- Bootstrap isolated subagent sessions
|
|
30
|
-
- Run subagent prompt cycles with overflow recovery
|
|
31
|
-
- Transition subagents between states
|
|
32
|
-
- Deliver results to owner sessions
|
|
33
|
-
|
|
34
|
-
### 2.2 Delivery coordinator
|
|
35
|
-
|
|
36
|
-
Routes subagent results to the correct session at the correct time. Key behaviors:
|
|
37
|
-
|
|
38
|
-
- Tracks active session via `ActiveRuntimeBinding` (set on `session_start`, cleared on `session_shutdown`)
|
|
39
|
-
- Queues results when owner session is inactive
|
|
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
|
|
41
|
-
- Uses `triggerTurn: false/true` split to preserve ordering between `crew-result` and `crew-remaining`
|
|
42
|
-
|
|
43
|
-
Underlying delivery: see pi's `sendMessage({ deliverAs, triggerTurn })` in extensions.md.
|
|
44
|
-
|
|
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
|
|
58
|
-
|
|
59
|
-
In-memory, process-scoped: `Map<subagentId, SubagentState>`
|
|
60
|
-
|
|
61
|
-
- Owner session filtering
|
|
62
|
-
- Runtime ID generation (`<name>-<hex>`)
|
|
63
|
-
|
|
64
|
-
Does not persist across process restarts. Subagent session files remain for post-hoc inspection.
|
|
65
|
-
|
|
66
|
-
## 3. Session bootstrapping
|
|
67
|
-
|
|
68
|
-
When `crew_spawn` executes:
|
|
69
|
-
|
|
70
|
-
1. Resolve subagent definition from discovery sources
|
|
71
|
-
2. Resolve model (fallback to caller session model if invalid)
|
|
72
|
-
3. Resolve tools, skills
|
|
73
|
-
4. Create `DefaultResourceLoader` with `extensionsOverride` that excludes `pi-crew`
|
|
74
|
-
5. Call `sessionManager.newSession({ parentSession })` for parent-child linkage
|
|
75
|
-
6. Create `AgentSession` with resolved configuration
|
|
76
|
-
7. Send task prompt asynchronously
|
|
77
|
-
|
|
78
|
-
**Extension filtering:** Subagent sessions must not load `pi-crew` again. Prevents recursive orchestration loops.
|
|
79
|
-
|
|
80
|
-
## 4. Delivery model
|
|
81
|
-
|
|
82
|
-
### 4.1 Owner-based routing
|
|
83
|
-
|
|
84
|
-
Results belong to the session that spawned the subagent. Owner identity uses `getSessionId()`, not file path (in-memory sessions have undefined paths).
|
|
85
|
-
|
|
86
|
-
### 4.2 Idle vs streaming
|
|
87
|
-
|
|
88
|
-
Check owner session state before delivery:
|
|
89
|
-
|
|
90
|
-
- **Idle (`isIdle() = true`):** Send with `triggerTurn: true`
|
|
91
|
-
- **Streaming (`isIdle() = false`):** Send with `deliverAs: "steer"` and `triggerTurn: true`
|
|
92
|
-
|
|
93
|
-
Critical: `deliverAs: "steer"` to an idle session leaves the message unprocessed (no active turn loop).
|
|
94
|
-
|
|
95
|
-
### 4.3 Deferred flush
|
|
96
|
-
|
|
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.
|
|
98
|
-
|
|
99
|
-
### 4.4 TTL cleanup
|
|
100
|
-
|
|
101
|
-
Pending messages older than 24 hours are discarded during `flushPending`.
|
|
102
|
-
|
|
103
|
-
## 5. Subagent state lifecycle
|
|
104
|
-
|
|
105
|
-
### 5.1 States
|
|
106
|
-
|
|
107
|
-
- `running` - Actively processing
|
|
108
|
-
- `waiting` - Interactive subagent awaiting `crew_respond` or `crew_done`
|
|
109
|
-
- `done` - Completed successfully
|
|
110
|
-
- `error` - Failed with error
|
|
111
|
-
- `aborted` - Cancelled
|
|
112
|
-
|
|
113
|
-
### 5.2 State transitions
|
|
114
|
-
|
|
115
|
-
After prompt cycle completion, inspect assistant stop reason:
|
|
116
|
-
|
|
117
|
-
- `stopReason: "error"` → status `error`
|
|
118
|
-
- `stopReason: "aborted"` → status `aborted`
|
|
119
|
-
- Normal completion + `interactive: true` → status `waiting`
|
|
120
|
-
- Normal completion + non-interactive → status `done`
|
|
121
|
-
|
|
122
|
-
### 5.3 Interactive subagents
|
|
123
|
-
|
|
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`.
|
|
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
|
-
|
|
130
|
-
## 6. Ownership and isolation
|
|
131
|
-
|
|
132
|
-
Invariants:
|
|
133
|
-
|
|
134
|
-
1. `crew_list`, `crew_abort`, `crew_respond`, `crew_done`, status widget: session-scoped. Only owner sees/controls.
|
|
135
|
-
2. `/pi-crew-abort`: cross-session emergency escape hatch.
|
|
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.
|
|
137
|
-
|
|
138
|
-
## 7. Subagent definition model
|
|
139
|
-
|
|
140
|
-
Discovery priority:
|
|
141
|
-
|
|
142
|
-
1. Project: `<cwd>/.pi/agents/*.md`
|
|
143
|
-
2. User global: `~/.pi/agent/agents/*.md`
|
|
144
|
-
3. Bundled: `agents/` in package
|
|
145
|
-
|
|
146
|
-
Higher priority wins. Same-name duplicates in same directory produce warning.
|
|
147
|
-
|
|
148
|
-
Frontmatter: `name`, `description`, `model`, `thinking`, `tools`, `skills`, `compaction`, `interactive`
|
|
149
|
-
|
|
150
|
-
Tools/skills semantics:
|
|
151
|
-
|
|
152
|
-
- **Omitted:** Use full supported allowlist
|
|
153
|
-
- **Empty list (`tools: []`):** Grant none
|
|
154
|
-
|
|
155
|
-
JSON overrides: `~/.pi/agent/pi-crew.json` (global), `<cwd>/.pi/pi-crew.json` (project). Project wins.
|
|
156
|
-
|
|
157
|
-
## 8. Behavioral invariants
|
|
158
|
-
|
|
159
|
-
1. Spawned subagent must not block caller session.
|
|
160
|
-
2. Results route to owning session (by ID), not currently active session.
|
|
161
|
-
3. Subagent sessions must not load `pi-crew`.
|
|
162
|
-
4. `crew_respond` returns immediately; result delivered asynchronously.
|
|
163
|
-
5. `crew_done` cleans up only; no duplicate result message.
|
|
164
|
-
6. Queued results flush when owner session becomes active.
|
|
165
|
-
7. `crew-result` messages appear before `crew-remaining` notes (ordering via `triggerTurn` split).
|
|
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.
|
|
170
|
-
|
|
171
|
-
## 9. Reading guide
|
|
172
|
-
|
|
173
|
-
1. `README.md` - Product surface
|
|
174
|
-
2. `AGENTS.md` - Architecture guardrails
|
|
175
|
-
3. `extension/index.ts` - Session event wiring
|
|
176
|
-
4. `extension/runtime/crew-runtime.ts` - Orchestration, state transitions
|
|
177
|
-
5. `extension/runtime/delivery-coordinator.ts` - Owner routing, queueing
|
|
178
|
-
6. `extension/bootstrap-session.ts` - Session construction
|
|
179
|
-
7. `extension/agent-discovery.ts` - Definition validation
|
|
180
|
-
8. `extension/integration/` - Tools, command, renderers
|
|
181
|
-
|
|
182
|
-
## 10. Verification
|
|
183
|
-
|
|
184
|
-
```bash
|
|
185
|
-
npm run typecheck
|
|
186
|
-
npm run build
|
|
187
|
-
```
|