@melihmucuk/pi-crew 1.0.7 → 1.0.8
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/agent-discovery.d.ts +0 -5
- package/dist/agent-discovery.js +1 -1
- package/dist/bootstrap-session.d.ts +13 -4
- package/dist/bootstrap-session.js +25 -16
- package/dist/index.js +37 -7
- package/dist/integration/register-command.d.ts +2 -2
- package/dist/integration/register-command.js +5 -5
- package/dist/integration/register-renderers.js +3 -0
- package/dist/integration/register-tools.d.ts +2 -2
- package/dist/integration/register-tools.js +2 -2
- package/dist/integration/tool-presentation.d.ts +2 -3
- package/dist/integration/tool-presentation.js +7 -8
- package/dist/integration/tools/crew-abort.d.ts +1 -1
- package/dist/integration/tools/crew-abort.js +3 -3
- package/dist/integration/tools/crew-done.d.ts +1 -1
- package/dist/integration/tools/crew-done.js +2 -2
- package/dist/integration/tools/crew-list.d.ts +1 -1
- package/dist/integration/tools/crew-list.js +3 -3
- package/dist/integration/tools/crew-respond.d.ts +1 -1
- package/dist/integration/tools/crew-respond.js +6 -7
- package/dist/integration/tools/crew-spawn.d.ts +1 -1
- package/dist/integration/tools/crew-spawn.js +17 -14
- package/dist/integration/tools/tool-deps.d.ts +3 -2
- package/dist/integration.d.ts +2 -2
- package/dist/integration.js +3 -3
- package/dist/runtime/crew-runtime.d.ts +61 -0
- package/dist/{crew-manager.js → runtime/crew-runtime.js} +84 -58
- package/dist/runtime/delivery-coordinator.d.ts +16 -7
- package/dist/runtime/delivery-coordinator.js +46 -20
- package/dist/runtime/subagent-registry.d.ts +1 -0
- package/dist/runtime/subagent-registry.js +3 -0
- package/dist/runtime/subagent-state.d.ts +2 -0
- package/dist/status-widget.d.ts +2 -2
- package/dist/status-widget.js +3 -3
- package/dist/subagent-messages.d.ts +5 -2
- package/dist/subagent-messages.js +5 -4
- package/docs/architecture.md +106 -843
- package/package.json +1 -1
- package/dist/crew-manager.d.ts +0 -44
|
@@ -25,10 +25,5 @@ interface AgentDiscoveryResult {
|
|
|
25
25
|
agents: AgentConfig[];
|
|
26
26
|
warnings: AgentDiscoveryWarning[];
|
|
27
27
|
}
|
|
28
|
-
interface ParseResult {
|
|
29
|
-
agent: AgentConfig | null;
|
|
30
|
-
warnings: AgentDiscoveryWarning[];
|
|
31
|
-
}
|
|
32
|
-
export declare function parseAgentDefinition(content: string, filePath: string): ParseResult;
|
|
33
28
|
export declare function discoverAgents(cwd?: string): AgentDiscoveryResult;
|
|
34
29
|
export {};
|
package/dist/agent-discovery.js
CHANGED
|
@@ -1,11 +1,20 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type AgentSession, type ModelRegistry } from "@mariozechner/pi-coding-agent";
|
|
2
|
+
import type { Api, Model } from "@mariozechner/pi-ai";
|
|
2
3
|
import type { AgentConfig } from "./agent-discovery.js";
|
|
4
|
+
export interface BootstrapContext {
|
|
5
|
+
model: Model<Api> | undefined;
|
|
6
|
+
modelRegistry: ModelRegistry;
|
|
7
|
+
parentSessionFile?: string;
|
|
8
|
+
}
|
|
3
9
|
interface BootstrapOptions {
|
|
4
10
|
agentConfig: AgentConfig;
|
|
5
11
|
cwd: string;
|
|
6
|
-
ctx:
|
|
12
|
+
ctx: BootstrapContext;
|
|
7
13
|
extensionResolvedPath: string;
|
|
8
|
-
parentSessionFile?: string;
|
|
9
14
|
}
|
|
10
|
-
export
|
|
15
|
+
export interface BootstrapResult {
|
|
16
|
+
session: AgentSession;
|
|
17
|
+
warnings: string[];
|
|
18
|
+
}
|
|
19
|
+
export declare function bootstrapSession(opts: BootstrapOptions): Promise<BootstrapResult>;
|
|
11
20
|
export {};
|
|
@@ -4,29 +4,35 @@ function resolveTools(agentConfig, cwd) {
|
|
|
4
4
|
return createSupportedTools(agentConfig.tools ?? SUPPORTED_TOOL_NAMES, cwd);
|
|
5
5
|
}
|
|
6
6
|
function resolveModel(agentConfig, ctx) {
|
|
7
|
+
const warnings = [];
|
|
7
8
|
const model = ctx.model;
|
|
8
9
|
if (!agentConfig.parsedModel)
|
|
9
|
-
return model;
|
|
10
|
+
return { model, warnings };
|
|
10
11
|
const found = ctx.modelRegistry.find(agentConfig.parsedModel.provider, agentConfig.parsedModel.modelId);
|
|
11
12
|
if (found)
|
|
12
|
-
return found;
|
|
13
|
-
|
|
14
|
-
return model;
|
|
13
|
+
return { model: found, warnings };
|
|
14
|
+
warnings.push(`Model "${agentConfig.model}" not found, using current session model`);
|
|
15
|
+
return { model, warnings };
|
|
15
16
|
}
|
|
16
|
-
function
|
|
17
|
+
function getSkillWarnings(agentConfig, resourceLoader) {
|
|
18
|
+
const warnings = [];
|
|
17
19
|
if (!agentConfig.skills)
|
|
18
|
-
return;
|
|
20
|
+
return warnings;
|
|
19
21
|
const availableSkillNames = new Set(resourceLoader.getSkills().skills.map((skill) => skill.name));
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
for (const skillName of agentConfig.skills) {
|
|
23
|
+
if (!availableSkillNames.has(skillName)) {
|
|
24
|
+
warnings.push(`Unknown skill "${skillName}" in subagent config, skipping`);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return warnings;
|
|
24
28
|
}
|
|
25
29
|
export async function bootstrapSession(opts) {
|
|
26
|
-
const
|
|
30
|
+
const warnings = [];
|
|
31
|
+
const { agentConfig, cwd, ctx, extensionResolvedPath } = opts;
|
|
27
32
|
const authStorage = ctx.modelRegistry.authStorage;
|
|
28
33
|
const modelRegistry = ctx.modelRegistry;
|
|
29
|
-
const model = resolveModel(agentConfig, ctx);
|
|
34
|
+
const { model, warnings: modelWarnings } = resolveModel(agentConfig, ctx);
|
|
35
|
+
warnings.push(...modelWarnings);
|
|
30
36
|
const tools = resolveTools(agentConfig, cwd);
|
|
31
37
|
const resourceLoader = new DefaultResourceLoader({
|
|
32
38
|
cwd,
|
|
@@ -40,16 +46,18 @@ export async function bootstrapSession(opts) {
|
|
|
40
46
|
diagnostics: base.diagnostics,
|
|
41
47
|
})
|
|
42
48
|
: undefined,
|
|
43
|
-
appendSystemPromptOverride: (base) => agentConfig.systemPrompt.trim()
|
|
49
|
+
appendSystemPromptOverride: (base) => agentConfig.systemPrompt.trim()
|
|
50
|
+
? [...base, agentConfig.systemPrompt]
|
|
51
|
+
: base,
|
|
44
52
|
});
|
|
45
53
|
await resourceLoader.reload();
|
|
46
|
-
|
|
54
|
+
warnings.push(...getSkillWarnings(agentConfig, resourceLoader));
|
|
47
55
|
const settingsManager = SettingsManager.inMemory({
|
|
48
56
|
compaction: { enabled: agentConfig.compaction ?? true },
|
|
49
57
|
});
|
|
50
58
|
const sessionManager = SessionManager.create(cwd);
|
|
51
|
-
sessionManager.newSession({ parentSession: parentSessionFile });
|
|
52
|
-
|
|
59
|
+
sessionManager.newSession({ parentSession: ctx.parentSessionFile });
|
|
60
|
+
const result = await createAgentSession({
|
|
53
61
|
cwd,
|
|
54
62
|
model,
|
|
55
63
|
thinkingLevel: agentConfig.thinking,
|
|
@@ -60,4 +68,5 @@ export async function bootstrapSession(opts) {
|
|
|
60
68
|
authStorage,
|
|
61
69
|
modelRegistry,
|
|
62
70
|
});
|
|
71
|
+
return { session: result.session, warnings };
|
|
63
72
|
}
|
package/dist/index.js
CHANGED
|
@@ -1,18 +1,33 @@
|
|
|
1
1
|
import { dirname } from "node:path";
|
|
2
2
|
import { fileURLToPath } from "node:url";
|
|
3
3
|
import { discoverAgents } from "./agent-discovery.js";
|
|
4
|
-
import {
|
|
4
|
+
import { crewRuntime, } from "./runtime/crew-runtime.js";
|
|
5
5
|
import { registerCrewIntegration } from "./integration.js";
|
|
6
6
|
import { formatAgentsForPrompt } from "./prompt-injection.js";
|
|
7
7
|
import { updateWidget } from "./status-widget.js";
|
|
8
8
|
const extensionDir = dirname(fileURLToPath(import.meta.url));
|
|
9
|
+
// Process-level cleanup for subagents on exit
|
|
10
|
+
let processHooksSetup = false;
|
|
11
|
+
function setupProcessHooks() {
|
|
12
|
+
if (processHooksSetup)
|
|
13
|
+
return;
|
|
14
|
+
processHooksSetup = true;
|
|
15
|
+
const abortAndExit = (signal) => {
|
|
16
|
+
crewRuntime.abortAll();
|
|
17
|
+
// Re-raise to restore default Node termination behavior
|
|
18
|
+
process.exit(128 + (signal === 'SIGINT' ? 2 : 15));
|
|
19
|
+
};
|
|
20
|
+
process.once('SIGINT', () => abortAndExit('SIGINT'));
|
|
21
|
+
process.once('SIGTERM', () => abortAndExit('SIGTERM'));
|
|
22
|
+
process.on('beforeExit', () => crewRuntime.abortAll());
|
|
23
|
+
}
|
|
9
24
|
export default function (pi) {
|
|
10
|
-
const crewManager = new CrewManager(extensionDir);
|
|
11
25
|
let currentCtx;
|
|
12
26
|
let cachedPromptSuffix = "";
|
|
27
|
+
setupProcessHooks();
|
|
13
28
|
const refreshWidget = () => {
|
|
14
29
|
if (currentCtx)
|
|
15
|
-
updateWidget(currentCtx,
|
|
30
|
+
updateWidget(currentCtx, crewRuntime);
|
|
16
31
|
};
|
|
17
32
|
const rebuildPromptCache = (cwd) => {
|
|
18
33
|
const { agents } = discoverAgents(cwd);
|
|
@@ -20,16 +35,31 @@ export default function (pi) {
|
|
|
20
35
|
};
|
|
21
36
|
const activateSession = (ctx) => {
|
|
22
37
|
currentCtx = ctx;
|
|
23
|
-
|
|
38
|
+
crewRuntime.activateSession({
|
|
39
|
+
sessionId: ctx.sessionManager.getSessionId(),
|
|
40
|
+
isIdle: () => ctx.isIdle(),
|
|
41
|
+
sendMessage: pi.sendMessage.bind(pi),
|
|
42
|
+
}, refreshWidget);
|
|
24
43
|
refreshWidget();
|
|
25
44
|
};
|
|
26
|
-
crewManager.onWidgetUpdate = refreshWidget;
|
|
27
45
|
pi.on("session_start", (_event, ctx) => {
|
|
28
46
|
rebuildPromptCache(ctx.cwd);
|
|
29
47
|
activateSession(ctx);
|
|
30
48
|
});
|
|
49
|
+
pi.on("session_before_switch", () => {
|
|
50
|
+
// Session is about to switch - no action needed here.
|
|
51
|
+
// Subagent cleanup is handled by process hooks, not session_shutdown.
|
|
52
|
+
});
|
|
53
|
+
pi.on("session_before_fork", () => {
|
|
54
|
+
// Session is about to fork - no action needed here.
|
|
55
|
+
// Subagent cleanup is handled by process hooks, not session_shutdown.
|
|
56
|
+
});
|
|
31
57
|
pi.on("session_shutdown", (_event, ctx) => {
|
|
32
|
-
|
|
58
|
+
const sessionId = ctx.sessionManager.getSessionId();
|
|
59
|
+
// Deactivate delivery to this session, but don't abort subagents.
|
|
60
|
+
// Subagents continue running and will complete normally.
|
|
61
|
+
// Real cleanup happens in process exit hooks.
|
|
62
|
+
crewRuntime.deactivateSession(sessionId);
|
|
33
63
|
});
|
|
34
64
|
pi.on("before_agent_start", (event) => {
|
|
35
65
|
if (!cachedPromptSuffix)
|
|
@@ -43,5 +73,5 @@ export default function (pi) {
|
|
|
43
73
|
const after = event.systemPrompt.slice(idx);
|
|
44
74
|
return { systemPrompt: before + cachedPromptSuffix + after };
|
|
45
75
|
});
|
|
46
|
-
registerCrewIntegration(pi,
|
|
76
|
+
registerCrewIntegration(pi, crewRuntime, extensionDir);
|
|
47
77
|
}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
|
2
|
-
import type {
|
|
3
|
-
export declare function registerCrewCommand(pi: ExtensionAPI,
|
|
2
|
+
import type { CrewRuntime } from "../runtime/crew-runtime.js";
|
|
3
|
+
export declare function registerCrewCommand(pi: ExtensionAPI, crew: CrewRuntime): void;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
export function registerCrewCommand(pi,
|
|
1
|
+
export function registerCrewCommand(pi, crew) {
|
|
2
2
|
pi.registerCommand("pi-crew-abort", {
|
|
3
3
|
description: "Abort an active subagent",
|
|
4
4
|
getArgumentCompletions(argumentPrefix) {
|
|
5
|
-
const activeAgents =
|
|
5
|
+
const activeAgents = crew.getAbortableAgents();
|
|
6
6
|
if (activeAgents.length === 0)
|
|
7
7
|
return null;
|
|
8
8
|
return activeAgents
|
|
@@ -15,7 +15,7 @@ export function registerCrewCommand(pi, crewManager) {
|
|
|
15
15
|
async handler(args, ctx) {
|
|
16
16
|
const trimmed = args.trim();
|
|
17
17
|
if (trimmed) {
|
|
18
|
-
const success =
|
|
18
|
+
const success = crew.abort(trimmed, { reason: "Aborted by user command" });
|
|
19
19
|
if (!success) {
|
|
20
20
|
ctx.ui.notify(`No active subagent with id "${trimmed}"`, "error");
|
|
21
21
|
}
|
|
@@ -24,7 +24,7 @@ export function registerCrewCommand(pi, crewManager) {
|
|
|
24
24
|
}
|
|
25
25
|
return;
|
|
26
26
|
}
|
|
27
|
-
const activeAgents =
|
|
27
|
+
const activeAgents = crew.getAbortableAgents();
|
|
28
28
|
if (activeAgents.length === 0) {
|
|
29
29
|
ctx.ui.notify("No active subagents", "info");
|
|
30
30
|
return;
|
|
@@ -39,7 +39,7 @@ export function registerCrewCommand(pi, crewManager) {
|
|
|
39
39
|
const selectedOption = options.find((option) => option.label === selected);
|
|
40
40
|
if (!selectedOption)
|
|
41
41
|
return;
|
|
42
|
-
const success =
|
|
42
|
+
const success = crew.abort(selectedOption.id, { reason: "Aborted by user command" });
|
|
43
43
|
if (success) {
|
|
44
44
|
ctx.ui.notify(`Subagent ${selectedOption.id} aborted`, "info");
|
|
45
45
|
}
|
|
@@ -26,6 +26,9 @@ export function registerCrewMessageRenderers(pi) {
|
|
|
26
26
|
const body = details?.body ?? (!details && message.content ? String(message.content) : undefined);
|
|
27
27
|
const box = new Box(1, 1, (text) => theme.bg("customMessageBg", text));
|
|
28
28
|
box.addChild(new Text(header, 0, 0));
|
|
29
|
+
if (details?.sessionFile) {
|
|
30
|
+
box.addChild(new Text(theme.fg("muted", `📁 ${details.sessionFile}`), 0, 0));
|
|
31
|
+
}
|
|
29
32
|
if (body) {
|
|
30
33
|
if (expanded) {
|
|
31
34
|
box.addChild(new Text("", 0, 0));
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
|
2
|
-
import type {
|
|
3
|
-
export declare function registerCrewTools(pi: ExtensionAPI,
|
|
2
|
+
import type { CrewRuntime } from "../runtime/crew-runtime.js";
|
|
3
|
+
export declare function registerCrewTools(pi: ExtensionAPI, crew: CrewRuntime, extensionDir: string): void;
|
|
@@ -3,7 +3,7 @@ import { registerCrewDoneTool } from "./tools/crew-done.js";
|
|
|
3
3
|
import { registerCrewListTool } from "./tools/crew-list.js";
|
|
4
4
|
import { registerCrewRespondTool } from "./tools/crew-respond.js";
|
|
5
5
|
import { registerCrewSpawnTool } from "./tools/crew-spawn.js";
|
|
6
|
-
export function registerCrewTools(pi,
|
|
6
|
+
export function registerCrewTools(pi, crew, extensionDir) {
|
|
7
7
|
const shownDiscoveryWarnings = new Set();
|
|
8
8
|
const notifyDiscoveryWarnings = (ctx, warnings) => {
|
|
9
9
|
if (!ctx.hasUI)
|
|
@@ -16,7 +16,7 @@ export function registerCrewTools(pi, crewManager) {
|
|
|
16
16
|
ctx.ui.notify(`${warning.message} (${warning.filePath})`, "error");
|
|
17
17
|
}
|
|
18
18
|
};
|
|
19
|
-
const deps = { pi,
|
|
19
|
+
const deps = { pi, crew, extensionDir, notifyDiscoveryWarnings };
|
|
20
20
|
registerCrewListTool(deps);
|
|
21
21
|
registerCrewSpawnTool(deps);
|
|
22
22
|
registerCrewAbortTool(deps);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
|
2
|
-
import { Text } from "@mariozechner/pi-tui";
|
|
2
|
+
import { Box, Text } from "@mariozechner/pi-tui";
|
|
3
3
|
export type ToolTheme = Parameters<Exclude<Parameters<ExtensionAPI["registerTool"]>[0]["renderCall"], undefined>>[1];
|
|
4
4
|
export type ToolResult = {
|
|
5
5
|
content: {
|
|
@@ -25,6 +25,5 @@ export declare function toolSuccess(text: string, details?: Record<string, unkno
|
|
|
25
25
|
}[];
|
|
26
26
|
details: Record<string, unknown>;
|
|
27
27
|
};
|
|
28
|
-
export declare function
|
|
29
|
-
export declare function renderCrewCall(theme: ToolTheme, name: string, id: string, preview?: string): Text;
|
|
28
|
+
export declare function renderCrewCall(theme: ToolTheme, name: string, id: string, preview?: string): Box;
|
|
30
29
|
export declare function renderCrewResult(result: ToolResult, theme: ToolTheme): Text;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Text } from "@mariozechner/pi-tui";
|
|
1
|
+
import { Box, Text } from "@mariozechner/pi-tui";
|
|
2
2
|
export function toolError(text) {
|
|
3
3
|
return {
|
|
4
4
|
content: [{ type: "text", text }],
|
|
@@ -12,14 +12,13 @@ export function toolSuccess(text, details = {}) {
|
|
|
12
12
|
details,
|
|
13
13
|
};
|
|
14
14
|
}
|
|
15
|
-
export function truncatePreview(text, max) {
|
|
16
|
-
return text.length > max ? `${text.slice(0, max)}...` : text;
|
|
17
|
-
}
|
|
18
15
|
export function renderCrewCall(theme, name, id, preview) {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
16
|
+
const box = new Box(1, 1);
|
|
17
|
+
box.addChild(new Text(theme.fg("toolTitle", theme.bold(`${name} `)) + theme.fg("accent", id), 0, 0));
|
|
18
|
+
if (preview) {
|
|
19
|
+
box.addChild(new Text(theme.fg("dim", preview), 0, 0));
|
|
20
|
+
}
|
|
21
|
+
return box;
|
|
23
22
|
}
|
|
24
23
|
export function renderCrewResult(result, theme) {
|
|
25
24
|
const text = result.content[0];
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import type { CrewToolDeps } from "./tool-deps.js";
|
|
2
|
-
export declare function registerCrewAbortTool({ pi,
|
|
2
|
+
export declare function registerCrewAbortTool({ pi, crew }: CrewToolDeps): void;
|
|
@@ -13,7 +13,7 @@ function formatAbortToolMessage(result) {
|
|
|
13
13
|
}
|
|
14
14
|
return parts.join("\n");
|
|
15
15
|
}
|
|
16
|
-
export function registerCrewAbortTool({ pi,
|
|
16
|
+
export function registerCrewAbortTool({ pi, crew }) {
|
|
17
17
|
pi.registerTool({
|
|
18
18
|
name: "crew_abort",
|
|
19
19
|
label: "Abort Crew",
|
|
@@ -38,7 +38,7 @@ export function registerCrewAbortTool({ pi, crewManager }) {
|
|
|
38
38
|
return toolError("Provide exactly one of: subagent_id, subagent_ids, or all=true.");
|
|
39
39
|
}
|
|
40
40
|
if (params.all) {
|
|
41
|
-
const abortedIds =
|
|
41
|
+
const abortedIds = crew.abortAllOwned(callerSessionId, {
|
|
42
42
|
reason: "Aborted by tool request",
|
|
43
43
|
});
|
|
44
44
|
if (abortedIds.length === 0) {
|
|
@@ -49,7 +49,7 @@ export function registerCrewAbortTool({ pi, crewManager }) {
|
|
|
49
49
|
const ids = params.subagent_id
|
|
50
50
|
? [params.subagent_id]
|
|
51
51
|
: (params.subagent_ids ?? []);
|
|
52
|
-
const result =
|
|
52
|
+
const result = crew.abortOwned(ids, callerSessionId, {
|
|
53
53
|
reason: "Aborted by tool request",
|
|
54
54
|
});
|
|
55
55
|
const message = formatAbortToolMessage(result);
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import type { CrewToolDeps } from "./tool-deps.js";
|
|
2
|
-
export declare function registerCrewDoneTool({ pi,
|
|
2
|
+
export declare function registerCrewDoneTool({ pi, crew }: CrewToolDeps): void;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Type } from "@sinclair/typebox";
|
|
2
2
|
import { renderCrewCall, renderCrewResult, toolError, toolSuccess, } from "../tool-presentation.js";
|
|
3
|
-
export function registerCrewDoneTool({ pi,
|
|
3
|
+
export function registerCrewDoneTool({ pi, crew }) {
|
|
4
4
|
pi.registerTool({
|
|
5
5
|
name: "crew_done",
|
|
6
6
|
label: "Done with Crew",
|
|
@@ -11,7 +11,7 @@ export function registerCrewDoneTool({ pi, crewManager }) {
|
|
|
11
11
|
promptSnippet: "Close an interactive subagent session when done.",
|
|
12
12
|
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
13
13
|
const callerSessionId = ctx.sessionManager.getSessionId();
|
|
14
|
-
const { error } =
|
|
14
|
+
const { error } = crew.done(params.subagent_id, callerSessionId);
|
|
15
15
|
if (error)
|
|
16
16
|
return toolError(error);
|
|
17
17
|
return toolSuccess(`Subagent ${params.subagent_id} closed.`, {
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import type { CrewToolDeps } from "./tool-deps.js";
|
|
2
|
-
export declare function registerCrewListTool({ pi,
|
|
2
|
+
export declare function registerCrewListTool({ pi, crew, notifyDiscoveryWarnings, }: CrewToolDeps): void;
|
|
@@ -2,18 +2,18 @@ import { Text } from "@mariozechner/pi-tui";
|
|
|
2
2
|
import { Type } from "@sinclair/typebox";
|
|
3
3
|
import { discoverAgents } from "../../agent-discovery.js";
|
|
4
4
|
import { STATUS_ICON } from "../../subagent-messages.js";
|
|
5
|
-
export function registerCrewListTool({ pi,
|
|
5
|
+
export function registerCrewListTool({ pi, crew, notifyDiscoveryWarnings, }) {
|
|
6
6
|
pi.registerTool({
|
|
7
7
|
name: "crew_list",
|
|
8
8
|
label: "List Crew",
|
|
9
|
-
description: "List available subagent definitions
|
|
9
|
+
description: "List available subagent definitions and currently running subagents with their status.",
|
|
10
10
|
parameters: Type.Object({}),
|
|
11
11
|
promptSnippet: "List subagent definitions and active subagents",
|
|
12
12
|
async execute(_toolCallId, _params, _signal, _onUpdate, ctx) {
|
|
13
13
|
const { agents, warnings } = discoverAgents(ctx.cwd);
|
|
14
14
|
notifyDiscoveryWarnings(ctx, warnings);
|
|
15
15
|
const callerSessionId = ctx.sessionManager.getSessionId();
|
|
16
|
-
const running =
|
|
16
|
+
const running = crew.getActiveSummariesForOwner(callerSessionId);
|
|
17
17
|
const lines = [];
|
|
18
18
|
lines.push("## Available subagents");
|
|
19
19
|
if (agents.length === 0) {
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import type { CrewToolDeps } from "./tool-deps.js";
|
|
2
|
-
export declare function registerCrewRespondTool({ pi,
|
|
2
|
+
export declare function registerCrewRespondTool({ pi, crew }: CrewToolDeps): void;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { Type } from "@sinclair/typebox";
|
|
2
|
-
import { renderCrewCall, renderCrewResult, toolError, toolSuccess,
|
|
3
|
-
export function registerCrewRespondTool({ pi,
|
|
2
|
+
import { renderCrewCall, renderCrewResult, toolError, toolSuccess, } from "../tool-presentation.js";
|
|
3
|
+
export function registerCrewRespondTool({ pi, crew }) {
|
|
4
4
|
pi.registerTool({
|
|
5
5
|
name: "crew_respond",
|
|
6
6
|
label: "Respond to Crew",
|
|
7
|
-
description: "Send a follow-up message to an interactive subagent that is waiting for a response.
|
|
7
|
+
description: "Send a follow-up message to an interactive subagent that is waiting for a response.",
|
|
8
8
|
parameters: Type.Object({
|
|
9
9
|
subagent_id: Type.String({
|
|
10
10
|
description: "ID of the waiting subagent (from crew_list or crew_spawn result)",
|
|
@@ -14,14 +14,13 @@ export function registerCrewRespondTool({ pi, crewManager }) {
|
|
|
14
14
|
promptSnippet: "Send a follow-up message to a waiting interactive subagent.",
|
|
15
15
|
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
16
16
|
const callerSessionId = ctx.sessionManager.getSessionId();
|
|
17
|
-
const { error } =
|
|
17
|
+
const { error } = crew.respond(params.subagent_id, params.message, callerSessionId);
|
|
18
18
|
if (error)
|
|
19
19
|
return toolError(error);
|
|
20
|
-
return toolSuccess(`Message sent to subagent ${params.subagent_id}. Response will be delivered as a steering message.`, { id: params.subagent_id });
|
|
20
|
+
return toolSuccess(`Message sent to subagent ${params.subagent_id}. Response will be delivered as a steering message.`, { id: params.subagent_id, message: params.message });
|
|
21
21
|
},
|
|
22
22
|
renderCall(args, theme, _context) {
|
|
23
|
-
|
|
24
|
-
return renderCrewCall(theme, "crew_respond", args.subagent_id || "...", preview);
|
|
23
|
+
return renderCrewCall(theme, "crew_respond", args.subagent_id || "...", args.message);
|
|
25
24
|
},
|
|
26
25
|
renderResult(result, _options, theme, _context) {
|
|
27
26
|
return renderCrewResult(result, theme);
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import type { CrewToolDeps } from "./tool-deps.js";
|
|
2
|
-
export declare function registerCrewSpawnTool({ pi,
|
|
2
|
+
export declare function registerCrewSpawnTool({ pi, crew, extensionDir, notifyDiscoveryWarnings, }: CrewToolDeps): void;
|
|
@@ -1,24 +1,23 @@
|
|
|
1
1
|
import { Type } from "@sinclair/typebox";
|
|
2
2
|
import { discoverAgents } from "../../agent-discovery.js";
|
|
3
|
-
import { renderCrewCall, renderCrewResult, toolError, toolSuccess,
|
|
4
|
-
export function registerCrewSpawnTool({ pi,
|
|
3
|
+
import { renderCrewCall, renderCrewResult, toolError, toolSuccess, } from "../tool-presentation.js";
|
|
4
|
+
export function registerCrewSpawnTool({ pi, crew, extensionDir, notifyDiscoveryWarnings, }) {
|
|
5
5
|
pi.registerTool({
|
|
6
6
|
name: "crew_spawn",
|
|
7
7
|
label: "Spawn Crew",
|
|
8
|
-
description: "Spawn a non-blocking subagent that runs in an isolated session. The subagent works independently while your session stays interactive. Results are delivered back to your session as steering messages
|
|
8
|
+
description: "Spawn a non-blocking subagent that runs in an isolated session. The subagent works independently while your session stays interactive. Results are delivered back to your session as steering messages.",
|
|
9
9
|
parameters: Type.Object({
|
|
10
10
|
subagent: Type.String({ description: "Subagent name from crew_list" }),
|
|
11
11
|
task: Type.String({ description: "Task to delegate to the subagent" }),
|
|
12
12
|
}),
|
|
13
13
|
promptSnippet: "Spawn a non-blocking subagent. Use crew_list first to see available subagents.",
|
|
14
14
|
promptGuidelines: [
|
|
15
|
-
"Use
|
|
16
|
-
"crew_spawn:
|
|
17
|
-
"crew_spawn:
|
|
18
|
-
"crew_spawn:
|
|
19
|
-
"crew_spawn:
|
|
20
|
-
"crew_spawn:
|
|
21
|
-
"crew_spawn: Interactive subagents (marked with 'interactive' in crew_list) stay alive after responding. Use crew_respond to continue the conversation and crew_done to close when finished.",
|
|
15
|
+
"Use crew_list first to see available subagents before spawning.",
|
|
16
|
+
"crew_spawn: The subagent runs in isolation with no access to your session. Include file paths, requirements, and known locations directly in the task parameter.",
|
|
17
|
+
"crew_spawn: DELEGATE means STOP. After spawning, either work on an UNRELATED task or end your turn. Never continue the delegated task yourself.",
|
|
18
|
+
"crew_spawn: To avoid duplication, gather only enough context to write a useful task (key files, entry points). Do not pre-investigate the full problem.",
|
|
19
|
+
"crew_spawn: Results arrive asynchronously as steering messages. Do not predict or fabricate results. Wait for all crew-result messages before acting on them.",
|
|
20
|
+
"crew_spawn: Interactive subagents stay alive after responding. Use crew_respond to continue and crew_done to close when finished.",
|
|
22
21
|
],
|
|
23
22
|
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
24
23
|
const { agents, warnings } = discoverAgents(ctx.cwd);
|
|
@@ -29,12 +28,16 @@ export function registerCrewSpawnTool({ pi, crewManager, notifyDiscoveryWarnings
|
|
|
29
28
|
return toolError(`Unknown subagent: "${params.subagent}". Available: ${available}`);
|
|
30
29
|
}
|
|
31
30
|
const ownerSessionId = ctx.sessionManager.getSessionId();
|
|
32
|
-
const id =
|
|
33
|
-
|
|
31
|
+
const id = crew.spawn(subagent, params.task, ctx.cwd, ownerSessionId, {
|
|
32
|
+
model: ctx.model,
|
|
33
|
+
modelRegistry: ctx.modelRegistry,
|
|
34
|
+
parentSessionFile: ctx.sessionManager.getSessionFile(),
|
|
35
|
+
onWarning: (msg) => ctx.ui.notify(msg, "warning"),
|
|
36
|
+
}, extensionDir);
|
|
37
|
+
return toolSuccess(`Subagent '${subagent.name}' spawned as ${id}. Result will be delivered as a steering message when done.`, { id, agentName: subagent.name, task: params.task });
|
|
34
38
|
},
|
|
35
39
|
renderCall(args, theme, _context) {
|
|
36
|
-
|
|
37
|
-
return renderCrewCall(theme, "crew_spawn", args.subagent || "...", preview);
|
|
40
|
+
return renderCrewCall(theme, "crew_spawn", args.subagent || "...", args.task);
|
|
38
41
|
},
|
|
39
42
|
renderResult(result, _options, theme, _context) {
|
|
40
43
|
return renderCrewResult(result, theme);
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import type { ExtensionAPI, ExtensionContext } from "@mariozechner/pi-coding-agent";
|
|
2
2
|
import type { AgentDiscoveryWarning } from "../../agent-discovery.js";
|
|
3
|
-
import type {
|
|
3
|
+
import type { CrewRuntime } from "../../runtime/crew-runtime.js";
|
|
4
4
|
export interface CrewToolDeps {
|
|
5
5
|
pi: ExtensionAPI;
|
|
6
|
-
|
|
6
|
+
crew: CrewRuntime;
|
|
7
|
+
extensionDir: string;
|
|
7
8
|
notifyDiscoveryWarnings: (ctx: ExtensionContext, warnings: AgentDiscoveryWarning[]) => void;
|
|
8
9
|
}
|
package/dist/integration.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
|
2
|
-
import type {
|
|
3
|
-
export declare function registerCrewIntegration(pi: ExtensionAPI,
|
|
2
|
+
import type { CrewRuntime } from "./runtime/crew-runtime.js";
|
|
3
|
+
export declare function registerCrewIntegration(pi: ExtensionAPI, crew: CrewRuntime, extensionDir: string): void;
|
package/dist/integration.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { registerCrewCommand } from "./integration/register-command.js";
|
|
2
2
|
import { registerCrewMessageRenderers } from "./integration/register-renderers.js";
|
|
3
3
|
import { registerCrewTools } from "./integration/register-tools.js";
|
|
4
|
-
export function registerCrewIntegration(pi,
|
|
5
|
-
registerCrewTools(pi,
|
|
6
|
-
registerCrewCommand(pi,
|
|
4
|
+
export function registerCrewIntegration(pi, crew, extensionDir) {
|
|
5
|
+
registerCrewTools(pi, crew, extensionDir);
|
|
6
|
+
registerCrewCommand(pi, crew);
|
|
7
7
|
registerCrewMessageRenderers(pi);
|
|
8
8
|
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { Api, Model } from "@mariozechner/pi-ai";
|
|
2
|
+
import type { ModelRegistry } from "@mariozechner/pi-coding-agent";
|
|
3
|
+
import type { AgentConfig } from "../agent-discovery.js";
|
|
4
|
+
import { type ActiveRuntimeBinding } from "./delivery-coordinator.js";
|
|
5
|
+
import { type AbortableAgentSummary, type ActiveAgentSummary } from "./subagent-state.js";
|
|
6
|
+
export type { AbortableAgentSummary, ActiveAgentSummary, } from "./subagent-state.js";
|
|
7
|
+
export interface AbortOwnedResult {
|
|
8
|
+
abortedIds: string[];
|
|
9
|
+
missingIds: string[];
|
|
10
|
+
foreignIds: string[];
|
|
11
|
+
}
|
|
12
|
+
interface AbortOptions {
|
|
13
|
+
reason: string;
|
|
14
|
+
}
|
|
15
|
+
export interface SpawnContext {
|
|
16
|
+
model: Model<Api> | undefined;
|
|
17
|
+
modelRegistry: ModelRegistry;
|
|
18
|
+
parentSessionFile?: string;
|
|
19
|
+
onWarning?: (message: string) => void;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Process-level singleton that owns all durable subagent state.
|
|
23
|
+
*
|
|
24
|
+
* This survives extension instance replacement caused by runtime
|
|
25
|
+
* teardown/recreation on /resume, /new, /fork (pi 0.65.0+).
|
|
26
|
+
* Each new extension instance rebinds delivery and widget hooks
|
|
27
|
+
* via activateSession/deactivateSession.
|
|
28
|
+
*/
|
|
29
|
+
declare class CrewRuntime {
|
|
30
|
+
private readonly registry;
|
|
31
|
+
private readonly delivery;
|
|
32
|
+
private readonly refreshCallbacks;
|
|
33
|
+
private refreshWidgetFor;
|
|
34
|
+
activateSession(binding: ActiveRuntimeBinding, refreshWidget?: () => void): void;
|
|
35
|
+
deactivateSession(sessionId: string): void;
|
|
36
|
+
spawn(agentConfig: AgentConfig, task: string, cwd: string, ownerSessionId: string, ctx: SpawnContext, extensionResolvedPath: string): string;
|
|
37
|
+
private attachSessionListeners;
|
|
38
|
+
private attachSpawnedSession;
|
|
39
|
+
private settleAgent;
|
|
40
|
+
private disposeAgent;
|
|
41
|
+
private runPromptCycle;
|
|
42
|
+
private spawnSession;
|
|
43
|
+
respond(id: string, message: string, callerSessionId: string): {
|
|
44
|
+
error?: string;
|
|
45
|
+
};
|
|
46
|
+
done(id: string, callerSessionId: string): {
|
|
47
|
+
error?: string;
|
|
48
|
+
};
|
|
49
|
+
abort(id: string, opts: AbortOptions): boolean;
|
|
50
|
+
abortOwned(ids: string[], callerSessionId: string, opts: AbortOptions): AbortOwnedResult;
|
|
51
|
+
abortAllOwned(callerSessionId: string, opts: AbortOptions): string[];
|
|
52
|
+
/**
|
|
53
|
+
* Abort all running subagents (process-level cleanup).
|
|
54
|
+
* Called from process exit hooks.
|
|
55
|
+
*/
|
|
56
|
+
abortAll(): void;
|
|
57
|
+
getAbortableAgents(): AbortableAgentSummary[];
|
|
58
|
+
getActiveSummariesForOwner(ownerSessionId: string): ActiveAgentSummary[];
|
|
59
|
+
}
|
|
60
|
+
export declare const crewRuntime: CrewRuntime;
|
|
61
|
+
export type { CrewRuntime };
|