@pi-unipi/subagents 0.1.10 → 0.1.11
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/package.json +1 -1
- package/src/index.ts +77 -106
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -5,23 +5,24 @@
|
|
|
5
5
|
* ESC propagation: all children abort on parent ESC
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { defineTool, type ExtensionAPI
|
|
9
|
-
import { Text } from "@mariozechner/pi-tui";
|
|
8
|
+
import { defineTool, type ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
|
10
9
|
import { Type } from "@sinclair/typebox";
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
10
|
+
import { existsSync, readdirSync } from "node:fs";
|
|
11
|
+
import { join } from "node:path";
|
|
12
|
+
import { homedir } from "node:os";
|
|
13
|
+
import { emitEvent, MODULES, UNIPI_EVENTS } from "@pi-unipi/core";
|
|
14
|
+
import { AgentManager } from "./agent-manager.js";
|
|
15
|
+
import { initConfig } from "./config.js";
|
|
16
|
+
import { type AgentActivity, BUILTIN_TYPES } from "./types.js";
|
|
17
|
+
import { AgentWidget } from "./widget.js";
|
|
13
18
|
|
|
14
|
-
|
|
19
|
+
/** Get info registry from global */
|
|
15
20
|
function getInfoRegistry() {
|
|
16
21
|
const g = globalThis as any;
|
|
17
22
|
return g.__unipi_info_registry;
|
|
18
23
|
}
|
|
19
|
-
import { AgentManager } from "./agent-manager.js";
|
|
20
|
-
import { initConfig, saveGlobalConfig } from "./config.js";
|
|
21
|
-
import { type AgentActivity, type AgentRecord, BUILTIN_TYPES } from "./types.js";
|
|
22
|
-
import { AgentWidget } from "./widget.js";
|
|
23
24
|
|
|
24
|
-
/** Format tokens safely
|
|
25
|
+
/** Format tokens safely */
|
|
25
26
|
function safeFormatTokens(session: any): string {
|
|
26
27
|
if (!session) return "";
|
|
27
28
|
try {
|
|
@@ -35,7 +36,7 @@ function safeFormatTokens(session: any): string {
|
|
|
35
36
|
}
|
|
36
37
|
}
|
|
37
38
|
|
|
38
|
-
/** Build result text
|
|
39
|
+
/** Build result text */
|
|
39
40
|
function textResult(msg: string, details?: any) {
|
|
40
41
|
return { content: [{ type: "text" as const, text: msg }], details };
|
|
41
42
|
}
|
|
@@ -45,17 +46,21 @@ export default function (pi: ExtensionAPI) {
|
|
|
45
46
|
const config = initConfig(process.cwd());
|
|
46
47
|
if (!config.enabled) return;
|
|
47
48
|
|
|
49
|
+
// Compute paths at factory time
|
|
50
|
+
const homeDir = homedir();
|
|
51
|
+
const cwd = process.cwd();
|
|
52
|
+
const globalAgentsDir = join(homeDir, ".unipi", "config", "agents");
|
|
53
|
+
const workspaceAgentsDir = join(cwd, ".unipi", "config", "agents");
|
|
54
|
+
|
|
48
55
|
// Activity tracking for widget
|
|
49
56
|
const agentActivity = new Map<string, AgentActivity>();
|
|
50
57
|
|
|
51
58
|
// Create manager with completion callback
|
|
52
59
|
const manager = new AgentManager(
|
|
53
60
|
(record) => {
|
|
54
|
-
// On complete: clean up activity, emit event
|
|
55
61
|
agentActivity.delete(record.id);
|
|
56
62
|
widget.markFinished(record.id);
|
|
57
63
|
widget.update();
|
|
58
|
-
|
|
59
64
|
pi.events.emit("subagents:completed", {
|
|
60
65
|
id: record.id,
|
|
61
66
|
type: record.type,
|
|
@@ -67,7 +72,6 @@ export default function (pi: ExtensionAPI) {
|
|
|
67
72
|
},
|
|
68
73
|
config.maxConcurrent,
|
|
69
74
|
(record) => {
|
|
70
|
-
// On start: emit event
|
|
71
75
|
pi.events.emit("subagents:started", {
|
|
72
76
|
id: record.id,
|
|
73
77
|
type: record.type,
|
|
@@ -79,103 +83,78 @@ export default function (pi: ExtensionAPI) {
|
|
|
79
83
|
// Create widget
|
|
80
84
|
const widget = new AgentWidget(manager, agentActivity);
|
|
81
85
|
|
|
82
|
-
//
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
},
|
|
107
|
-
dataProvider: async () => {
|
|
108
|
-
// Get available agent types
|
|
109
|
-
const types = config.types || {};
|
|
110
|
-
const builtinTypes = ["explore", "work"];
|
|
111
|
-
|
|
112
|
-
// Check for custom agent types in filesystem
|
|
113
|
-
const customTypes: string[] = [];
|
|
86
|
+
// Register info group at factory time (not session_start)
|
|
87
|
+
const registry = getInfoRegistry();
|
|
88
|
+
if (registry) {
|
|
89
|
+
registry.registerGroup({
|
|
90
|
+
id: "subagents",
|
|
91
|
+
name: "Subagents",
|
|
92
|
+
icon: "🤖",
|
|
93
|
+
priority: 80,
|
|
94
|
+
config: {
|
|
95
|
+
showByDefault: true,
|
|
96
|
+
stats: [
|
|
97
|
+
{ id: "maxConcurrent", label: "Max Concurrent", show: true },
|
|
98
|
+
{ id: "activeCount", label: "Active Agents", show: true },
|
|
99
|
+
{ id: "enabled", label: "Enabled", show: true },
|
|
100
|
+
{ id: "types", label: "Available Types", show: true },
|
|
101
|
+
],
|
|
102
|
+
},
|
|
103
|
+
dataProvider: async () => {
|
|
104
|
+
const types = config.types || {};
|
|
105
|
+
const builtinTypes = ["explore", "work"];
|
|
106
|
+
|
|
107
|
+
// Scan for custom agent types
|
|
108
|
+
const customTypes: string[] = [];
|
|
109
|
+
for (const dir of [globalAgentsDir, workspaceAgentsDir]) {
|
|
114
110
|
try {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
// Check global agents directory
|
|
119
|
-
if (fs.existsSync(globalAgents)) {
|
|
120
|
-
const files = fs.readdirSync(globalAgents);
|
|
121
|
-
for (const file of files) {
|
|
122
|
-
if (file.endsWith(".md")) {
|
|
111
|
+
if (existsSync(dir)) {
|
|
112
|
+
for (const file of readdirSync(dir)) {
|
|
113
|
+
if (file.endsWith(".md") && !customTypes.includes(file.replace(".md", ""))) {
|
|
123
114
|
customTypes.push(file.replace(".md", ""));
|
|
124
115
|
}
|
|
125
116
|
}
|
|
126
117
|
}
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
maxConcurrent: { value: String(manager.getMaxConcurrent()) },
|
|
158
|
-
activeCount: { value: String(activeAgents) },
|
|
159
|
-
enabled: { value: config.enabled ? "yes" : "no" },
|
|
160
|
-
types: {
|
|
161
|
-
value: allTypes.length > 0 ? allTypes[0] : "none",
|
|
162
|
-
detail: allTypes.length > 1 ? typeList : undefined,
|
|
163
|
-
},
|
|
164
|
-
};
|
|
165
|
-
},
|
|
166
|
-
});
|
|
167
|
-
}
|
|
118
|
+
} catch { /* ignore */ }
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const allTypes = [...new Set([...builtinTypes, ...Object.keys(types), ...customTypes])];
|
|
122
|
+
const typeList = allTypes.map(t => {
|
|
123
|
+
const isEnabled = types[t]?.enabled !== false;
|
|
124
|
+
const isBuiltin = builtinTypes.includes(t);
|
|
125
|
+
const scope = customTypes.includes(t) ? "project" : "global";
|
|
126
|
+
return `${t}(${scope})${isEnabled ? "" : " [disabled]"}`;
|
|
127
|
+
}).join(", ");
|
|
128
|
+
|
|
129
|
+
const activeAgents = manager.listAgents().filter(a => a.status === "running").length;
|
|
130
|
+
|
|
131
|
+
return {
|
|
132
|
+
maxConcurrent: { value: String(manager.getMaxConcurrent()) },
|
|
133
|
+
activeCount: { value: String(activeAgents) },
|
|
134
|
+
enabled: { value: config.enabled ? "yes" : "no" },
|
|
135
|
+
types: {
|
|
136
|
+
value: allTypes.length > 0 ? allTypes[0] : "none",
|
|
137
|
+
detail: allTypes.length > 1 ? typeList : undefined,
|
|
138
|
+
},
|
|
139
|
+
};
|
|
140
|
+
},
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Session start: emit MODULE_READY
|
|
145
|
+
pi.on("session_start", async (_event, ctx) => {
|
|
146
|
+
const globalConfig = `${homeDir}/.unipi/config/subagents.json`;
|
|
147
|
+
const workspaceConfig = `${cwd}/.unipi/config/subagents.json`;
|
|
168
148
|
|
|
169
149
|
ctx.ui.notify(
|
|
170
150
|
`UniPi Subagents config:\n` +
|
|
171
151
|
`• Global: ${globalConfig}\n` +
|
|
172
|
-
`• Global agents: ${
|
|
152
|
+
`• Global agents: ${globalAgentsDir}\n` +
|
|
173
153
|
`• Workspace: ${workspaceConfig}\n` +
|
|
174
|
-
`• Workspace agents: ${
|
|
154
|
+
`• Workspace agents: ${workspaceAgentsDir}`,
|
|
175
155
|
"info",
|
|
176
156
|
);
|
|
177
157
|
|
|
178
|
-
// Emit module ready event
|
|
179
158
|
emitEvent(pi, UNIPI_EVENTS.MODULE_READY, {
|
|
180
159
|
name: MODULES.SUBAGENTS || "subagents",
|
|
181
160
|
version: "0.1.8",
|
|
@@ -305,11 +284,9 @@ Guidelines:
|
|
|
305
284
|
const modelInput = params.model as string | undefined;
|
|
306
285
|
const thinkingLevel = params.thinking as any | undefined;
|
|
307
286
|
|
|
308
|
-
// Create activity tracker
|
|
309
287
|
const { state: bgState, callbacks: bgCallbacks } = createActivityTracker(maxTurns);
|
|
310
288
|
|
|
311
289
|
if (runInBackground) {
|
|
312
|
-
// Background execution
|
|
313
290
|
const id = manager.spawn(pi, ctx, type, prompt, {
|
|
314
291
|
description,
|
|
315
292
|
maxTurns,
|
|
@@ -342,7 +319,6 @@ Guidelines:
|
|
|
342
319
|
// Foreground execution
|
|
343
320
|
let spinnerFrame = 0;
|
|
344
321
|
const startedAt = Date.now();
|
|
345
|
-
let fgId: string | undefined;
|
|
346
322
|
|
|
347
323
|
const streamUpdate = () => {
|
|
348
324
|
onUpdate?.({
|
|
@@ -382,11 +358,6 @@ Guidelines:
|
|
|
382
358
|
|
|
383
359
|
clearInterval(spinnerInterval);
|
|
384
360
|
|
|
385
|
-
if (fgId) {
|
|
386
|
-
agentActivity.delete(fgId);
|
|
387
|
-
widget.markFinished(fgId);
|
|
388
|
-
}
|
|
389
|
-
|
|
390
361
|
const tokenText = safeFormatTokens(bgState.session);
|
|
391
362
|
const durationMs = (record.completedAt ?? Date.now()) - record.startedAt;
|
|
392
363
|
|