@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.
@@ -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
- const box = new Box(1, 1, (text) => theme.bg("customMessageBg", text));
50
- box.addChild(new Text(theme.fg("warning", String(message.content ?? "")), 0, 0));
51
- return box;
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 "@sinclair/typebox";
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,4 +1,4 @@
1
- import { Type } from "@sinclair/typebox";
1
+ import { Type } from "typebox";
2
2
  import { renderCrewCall, renderCrewResult, toolError, toolSuccess, } from "../tool-presentation.js";
3
3
  export function registerCrewDoneTool({ pi, crew }) {
4
4
  pi.registerTool({
@@ -1,7 +1,7 @@
1
1
  import { Text } from "@mariozechner/pi-tui";
2
- import { Type } from "@sinclair/typebox";
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,4 @@
1
- import { Type } from "@sinclair/typebox";
1
+ import { Type } from "typebox";
2
2
  import { renderCrewCall, renderCrewResult, toolError, toolSuccess, } from "../tool-presentation.js";
3
3
  export function registerCrewRespondTool({ pi, crew }) {
4
4
  pi.registerTool({
@@ -1,5 +1,5 @@
1
1
  import { getAgentDir } from "@mariozechner/pi-coding-agent";
2
- import { Type } from "@sinclair/typebox";
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, }) {
@@ -20,7 +20,6 @@ export interface ActiveAgentSummary {
20
20
  id: string;
21
21
  agentName: string;
22
22
  status: SubagentStatus;
23
- taskPreview: string;
24
23
  turns: number;
25
24
  contextTokens: number;
26
25
  model: string | undefined;
@@ -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
+ }
@@ -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
- ### 2.3 Subagent registry
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. Pending messages preserved for inactive sessions; TTL (24h) prevents memory leak.
149
- 9. Active subagent state survives runtime replacement within same process.
150
- 10. Graceful quit aborts subagents through `session_shutdown.reason === "quit"`; replacement paths do not.
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.12",
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
- "@sinclair/typebox": "*"
42
+ "typebox": "*"
43
43
  },
44
44
  "devDependencies": {
45
- "@mariozechner/pi-agent-core": "^0.68.0",
46
- "@mariozechner/pi-ai": "^0.68.0",
47
- "@mariozechner/pi-coding-agent": "^0.68.0",
48
- "@mariozechner/pi-tui": "^0.68.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
+ }