pi-gentic 0.1.0
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 +499 -0
- package/dist/commands.d.ts +93 -0
- package/dist/commands.js +422 -0
- package/dist/config.d.ts +27 -0
- package/dist/config.js +407 -0
- package/dist/core.d.ts +19 -0
- package/dist/core.js +125 -0
- package/dist/extension.d.ts +2 -0
- package/dist/extension.js +476 -0
- package/dist/orchestrator.d.ts +277 -0
- package/dist/orchestrator.js +633 -0
- package/dist/policy.d.ts +43 -0
- package/dist/policy.js +136 -0
- package/dist/prompt.d.ts +12 -0
- package/dist/prompt.js +226 -0
- package/dist/runs.d.ts +99 -0
- package/dist/runs.js +540 -0
- package/dist/runtime.d.ts +127 -0
- package/dist/runtime.js +360 -0
- package/dist/sessions.d.ts +56 -0
- package/dist/sessions.js +487 -0
- package/dist/ui.d.ts +108 -0
- package/dist/ui.js +957 -0
- package/dist/worktrees.d.ts +1 -0
- package/dist/worktrees.js +86 -0
- package/docs/assets/error-card.png +0 -0
- package/docs/assets/load-agent.png +0 -0
- package/docs/assets/orchestration-tree.png +0 -0
- package/docs/assets/send-background.png +0 -0
- package/docs/assets/send-foreground.png +0 -0
- package/package.json +58 -0
|
@@ -0,0 +1,476 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pi extension entrypoint.
|
|
3
|
+
*
|
|
4
|
+
* This file only wires Pi surfaces to the orchestrator: commands, shortcuts,
|
|
5
|
+
* renderers, lifecycle events, and the model-callable agents tool.
|
|
6
|
+
*/
|
|
7
|
+
import { SessionManager } from "@earendil-works/pi-coding-agent";
|
|
8
|
+
import { getErrorMessage, shortSessionId } from "./core.js";
|
|
9
|
+
import { completeAgents, completeSend, isCompletingSendSession, normalizeToolInput, parseAgentCommand, parseSendCommand, } from "./commands.js";
|
|
10
|
+
import { enabledModelPatterns, loadAvailableSkills, loadConfiguration } from "./config.js";
|
|
11
|
+
import { AGENT_CYCLE_SHORTCUT } from "./policy.js";
|
|
12
|
+
import { installLiveSessionBridge, persistSessionImmediately } from "./runtime.js";
|
|
13
|
+
import { PiGenticOrchestrator } from "./orchestrator.js";
|
|
14
|
+
import { buildSessionTree, cachedPersistedSessions, enrichSessionSummaries, findSessionSummary, listSessionSummariesFast, sessionCompletionScope, treeSwitchPath, warmPersistedSessions, } from "./sessions.js";
|
|
15
|
+
import { persistSynchronousToolCard } from "./runs.js";
|
|
16
|
+
import { createSessionTreePicker, renderAgentsCall, renderAgentsResult, showCard, startLiveRefresh, } from "./ui.js";
|
|
17
|
+
const AgentsToolParameters = {
|
|
18
|
+
type: "object",
|
|
19
|
+
properties: {
|
|
20
|
+
action: {
|
|
21
|
+
type: "string",
|
|
22
|
+
description: "One action: list, get, status, load, send, abort, discoverSessions",
|
|
23
|
+
},
|
|
24
|
+
agent: { type: "string" },
|
|
25
|
+
sessionId: { type: "string" },
|
|
26
|
+
message: { type: "string" },
|
|
27
|
+
async: { type: "boolean" },
|
|
28
|
+
fork: { type: "boolean" },
|
|
29
|
+
cwd: {
|
|
30
|
+
type: "string",
|
|
31
|
+
description: "Working directory. When worktree is supplied, this is the worktree folder path.",
|
|
32
|
+
},
|
|
33
|
+
worktree: {
|
|
34
|
+
type: "string",
|
|
35
|
+
description: "Optional git worktree branch name. If empty, the folder name is used; if cwd is omitted too, pi-gentic derives a safe branch and folder from the message under .agentfiles/worktrees/.",
|
|
36
|
+
},
|
|
37
|
+
repo: {
|
|
38
|
+
type: "string",
|
|
39
|
+
description: "Repository to create the worktree from. Relative paths are resolved from the caller cwd. Defaults to the caller cwd.",
|
|
40
|
+
},
|
|
41
|
+
invokeMeLater: { type: "boolean" },
|
|
42
|
+
overrides: { type: "object", additionalProperties: true },
|
|
43
|
+
rx: { type: "number" },
|
|
44
|
+
ry: { type: "number" },
|
|
45
|
+
},
|
|
46
|
+
required: ["action"],
|
|
47
|
+
};
|
|
48
|
+
/** Registers every pi-gentic surface with the host Pi process. */
|
|
49
|
+
export default function piGentic(pi) {
|
|
50
|
+
installLiveSessionBridge();
|
|
51
|
+
const orchestrator = new PiGenticOrchestrator(pi);
|
|
52
|
+
const completionContext = createCompletionContext(pi);
|
|
53
|
+
pi.registerMessageRenderer("pi-gentic:card", (message, options, theme) => {
|
|
54
|
+
const component = renderAgentsResult({
|
|
55
|
+
content: [
|
|
56
|
+
{
|
|
57
|
+
type: "text",
|
|
58
|
+
text: typeof message.content === "string" ? message.content : "",
|
|
59
|
+
},
|
|
60
|
+
],
|
|
61
|
+
details: message.details,
|
|
62
|
+
}, { expanded: options.expanded, isPartial: false }, theme, { args: {}, isError: message.details?.status === "error" });
|
|
63
|
+
return component;
|
|
64
|
+
});
|
|
65
|
+
pi.on("session_start", async (event, ctx) => {
|
|
66
|
+
completionContext.capture(ctx);
|
|
67
|
+
try {
|
|
68
|
+
const defaultResult = await orchestrator.loadDefaultAgent(ctx, event);
|
|
69
|
+
if (defaultResult)
|
|
70
|
+
showCard(pi, defaultResult.text, defaultResult.details);
|
|
71
|
+
else
|
|
72
|
+
await orchestrator.applyCurrentPolicy(ctx);
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
ctx.ui.notify(`pi-gentic: ${getErrorMessage(error)}`, "warning");
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
pi.registerShortcut(AGENT_CYCLE_SHORTCUT, {
|
|
79
|
+
description: "Cycle pi-gentic active agent",
|
|
80
|
+
handler: async (ctx) => {
|
|
81
|
+
completionContext.capture(ctx);
|
|
82
|
+
try {
|
|
83
|
+
const result = await orchestrator.cycleAgent(ctx);
|
|
84
|
+
showCard(pi, result.text, result.details);
|
|
85
|
+
}
|
|
86
|
+
catch (error) {
|
|
87
|
+
showCard(pi, getErrorMessage(error), orchestrator.cardDetails("error", "error", {
|
|
88
|
+
error: getErrorMessage(error),
|
|
89
|
+
}));
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
});
|
|
93
|
+
pi.on("agent_start", async (_event, ctx) => {
|
|
94
|
+
orchestrator.setTitle(ctx, true);
|
|
95
|
+
});
|
|
96
|
+
pi.on("agent_end", async (_event, ctx) => {
|
|
97
|
+
orchestrator.setTitle(ctx, false);
|
|
98
|
+
});
|
|
99
|
+
pi.on("before_agent_start", (event, ctx) => orchestrator.buildPromptAppend(ctx, event));
|
|
100
|
+
pi.registerCommand("agent", {
|
|
101
|
+
description: "Set, clear, or show the active pi-gentic agent",
|
|
102
|
+
getArgumentCompletions: (prefix) => completeAgents(prefix, orchestrator.currentAgentName),
|
|
103
|
+
handler: async (args, ctx) => {
|
|
104
|
+
completionContext.capture(ctx);
|
|
105
|
+
const parsed = parseAgentCommand(args);
|
|
106
|
+
if (!parsed.agent) {
|
|
107
|
+
const active = orchestrator.getActiveAgent(ctx);
|
|
108
|
+
ctx.ui.notify(active
|
|
109
|
+
? `Active agent: ${active.name}\n${active.description ?? ""}`
|
|
110
|
+
: "No active agent.", "info");
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
try {
|
|
114
|
+
if (parsed.sessionId) {
|
|
115
|
+
const config = loadConfiguration({ cwd: ctx.cwd });
|
|
116
|
+
const runtime = await orchestrator.getOrOpenSession(ctx, parsed.sessionId);
|
|
117
|
+
if (parsed.agent === "clear") {
|
|
118
|
+
runtime.session.sessionManager.appendCustomEntry("pi-gentic:state", { agentName: undefined });
|
|
119
|
+
ctx.ui.notify(`Cleared active agent in session ${shortSessionId(runtime.session.sessionManager.getSessionId())}.`, "info");
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
await orchestrator.loadAgentIntoSession(runtime.session, parsed.agent, undefined, config, ctx);
|
|
123
|
+
ctx.ui.notify(`Loaded ${parsed.agent} in session ${shortSessionId(runtime.session.sessionManager.getSessionId())}.`, "info");
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
const result = await orchestrator.loadAgent(ctx, parsed.agent);
|
|
127
|
+
showCard(pi, result.text, result.details);
|
|
128
|
+
}
|
|
129
|
+
catch (error) {
|
|
130
|
+
showCard(pi, getErrorMessage(error), orchestrator.cardDetails("error", "error", {
|
|
131
|
+
error: getErrorMessage(error),
|
|
132
|
+
}));
|
|
133
|
+
}
|
|
134
|
+
},
|
|
135
|
+
});
|
|
136
|
+
pi.registerCommand("orchestration-tree", {
|
|
137
|
+
description: "Open the pi-gentic orchestration tree",
|
|
138
|
+
handler: async (_args, ctx) => {
|
|
139
|
+
completionContext.capture(ctx);
|
|
140
|
+
try {
|
|
141
|
+
const result = await orchestrator.discoverSessions(ctx, { all: true });
|
|
142
|
+
if (result.sessions.length === 0) {
|
|
143
|
+
ctx.ui.notify("No sessions found.", "info");
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
const refreshSessions = async () => (await orchestrator.discoverSessions(ctx, { all: true })).sessions;
|
|
147
|
+
const selected = await ctx.ui.custom((tui, theme, _keybindings, done) => createSessionTreePicker(result.sessions, theme, done, () => tui.requestRender(), { refreshSessions }));
|
|
148
|
+
const currentSelection = selected;
|
|
149
|
+
const sessionPath = currentSelection
|
|
150
|
+
? treeSwitchPath(currentSelection)
|
|
151
|
+
: undefined;
|
|
152
|
+
if (!sessionPath)
|
|
153
|
+
return;
|
|
154
|
+
try {
|
|
155
|
+
await ctx.switchSession(sessionPath);
|
|
156
|
+
}
|
|
157
|
+
catch (error) {
|
|
158
|
+
if (currentSelection?.path && sessionPath !== currentSelection.path)
|
|
159
|
+
await ctx.switchSession(currentSelection.path);
|
|
160
|
+
else
|
|
161
|
+
throw error;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
catch (error) {
|
|
165
|
+
ctx.ui.notify(`pi-gentic orchestration tree failed: ${getErrorMessage(error)}`, "error");
|
|
166
|
+
}
|
|
167
|
+
},
|
|
168
|
+
});
|
|
169
|
+
pi.registerCommand("send", {
|
|
170
|
+
description: "Send a message to a pi-gentic child or target session",
|
|
171
|
+
getArgumentCompletions: async (prefix) => {
|
|
172
|
+
const snapshot = completionContext.current();
|
|
173
|
+
if (!isCompletingSendSession(prefix))
|
|
174
|
+
return completeSend(prefix, snapshot);
|
|
175
|
+
const sessions = await listCompletionSessions(snapshot);
|
|
176
|
+
return completeSend(prefix, {
|
|
177
|
+
cwd: snapshot.cwd,
|
|
178
|
+
sessions,
|
|
179
|
+
currentSessionId: snapshot.currentSessionId,
|
|
180
|
+
});
|
|
181
|
+
},
|
|
182
|
+
handler: async (args, ctx) => {
|
|
183
|
+
completionContext.capture(ctx);
|
|
184
|
+
const parsed = parseSendCommand(args);
|
|
185
|
+
if (!parsed.message) {
|
|
186
|
+
ctx.ui.notify("Usage: /send <message> [--agent <agentName>] [--session <sessionId>] [--fork] [--bg|--fg] [--no-invoke] [--cwd <dir>] [--worktree [branch]] [--repo <dir>] [override flags]", "warning");
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
let stopRefresh = (() => { });
|
|
190
|
+
try {
|
|
191
|
+
let showedProgress = false;
|
|
192
|
+
stopRefresh = startLiveRefresh(ctx, "send-command");
|
|
193
|
+
const result = await orchestrator.send(ctx, parsed, {
|
|
194
|
+
awaitCompletion: false,
|
|
195
|
+
onSettled: () => stopRefresh(),
|
|
196
|
+
onRefresh: () => stopRefresh.refresh?.(),
|
|
197
|
+
onUpdate: (update) => {
|
|
198
|
+
if (showedProgress)
|
|
199
|
+
return;
|
|
200
|
+
showedProgress = true;
|
|
201
|
+
showCard(pi, firstText(update.content) ?? "Sending message...", update.details);
|
|
202
|
+
},
|
|
203
|
+
});
|
|
204
|
+
if (!["running", "queued"].includes(result.details?.status))
|
|
205
|
+
stopRefresh();
|
|
206
|
+
if (!showedProgress)
|
|
207
|
+
showCard(pi, result.text, result.details);
|
|
208
|
+
}
|
|
209
|
+
catch (error) {
|
|
210
|
+
stopRefresh();
|
|
211
|
+
showCard(pi, getErrorMessage(error), orchestrator.cardDetails("send", "error", {
|
|
212
|
+
error: getErrorMessage(error),
|
|
213
|
+
}));
|
|
214
|
+
}
|
|
215
|
+
},
|
|
216
|
+
});
|
|
217
|
+
pi.registerTool({
|
|
218
|
+
name: "agents",
|
|
219
|
+
label: "Agents",
|
|
220
|
+
description: [
|
|
221
|
+
"Perform one pi-gentic orchestration action.",
|
|
222
|
+
"Sessions are durable collaborators: when continuing, retrying, or referring to the same agent or same work, target the existing sessionId instead of creating a new child session; create a new session only for independent work.",
|
|
223
|
+
"Actions: list returns available agent names; get returns one agent definition and requires agent; status reports one session and requires sessionId; load sets the active agent and accepts agent plus optional overrides; send delivers message to an existing sessionId or to a new child when no sessionId is supplied, with optional agent, async, fork, cwd, worktree, repo, invokeMeLater, and overrides; abort stops the current session or the supplied sessionId; discoverSessions returns nearby orchestration sessions and accepts rx and ry.",
|
|
224
|
+
"Use one action per call. Do not send slash commands, prose wrappers, or shell commands as the action.",
|
|
225
|
+
].join(" "),
|
|
226
|
+
promptSnippet: "Orchestrate durable pi-gentic agent sessions; reuse sessionId for the same agent or same work, and use actions list, get, status, load, send, abort, and discoverSessions",
|
|
227
|
+
parameters: AgentsToolParameters,
|
|
228
|
+
renderShell: "self",
|
|
229
|
+
renderCall: renderAgentsCall,
|
|
230
|
+
renderResult: renderAgentsResult,
|
|
231
|
+
async execute(_toolCallId, params, signal, onUpdate, ctx) {
|
|
232
|
+
completionContext.capture(ctx);
|
|
233
|
+
try {
|
|
234
|
+
const input = normalizeToolInput(params);
|
|
235
|
+
const result = await executeAction(orchestrator, ctx, input, onUpdate, signal);
|
|
236
|
+
persistSynchronousToolCard(ctx, input, result, persistSessionImmediately);
|
|
237
|
+
return {
|
|
238
|
+
content: [{ type: "text", text: result.text }],
|
|
239
|
+
details: result.details,
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
catch (error) {
|
|
243
|
+
const message = getErrorMessage(error);
|
|
244
|
+
return {
|
|
245
|
+
content: [{ type: "text", text: message }],
|
|
246
|
+
details: orchestrator.cardDetails(params?.action ?? "error", "error", { error: message }),
|
|
247
|
+
isError: true,
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
},
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
function createCompletionContext(pi) {
|
|
254
|
+
let snapshot = {
|
|
255
|
+
cwd: process.cwd(),
|
|
256
|
+
sessionDir: undefined,
|
|
257
|
+
currentSessionId: undefined,
|
|
258
|
+
currentSessionPath: undefined,
|
|
259
|
+
agents: [],
|
|
260
|
+
models: [],
|
|
261
|
+
tools: [],
|
|
262
|
+
skills: [],
|
|
263
|
+
themes: [],
|
|
264
|
+
systemPromptFiles: [],
|
|
265
|
+
};
|
|
266
|
+
return {
|
|
267
|
+
capture(ctx) {
|
|
268
|
+
if (!ctx)
|
|
269
|
+
return snapshot;
|
|
270
|
+
const cwd = typeof ctx.cwd === "string" ? ctx.cwd : snapshot.cwd;
|
|
271
|
+
const config = loadConfiguration({ cwd });
|
|
272
|
+
snapshot = {
|
|
273
|
+
cwd,
|
|
274
|
+
sessionDir: ctx.sessionManager?.getSessionDir?.() ?? snapshot.sessionDir,
|
|
275
|
+
currentSessionId: ctx.sessionManager?.getSessionId?.() ?? snapshot.currentSessionId,
|
|
276
|
+
currentSessionPath: ctx.sessionManager?.getSessionFile?.() ?? snapshot.currentSessionPath,
|
|
277
|
+
agents: config.agents,
|
|
278
|
+
models: scopedModelSuggestions(ctx),
|
|
279
|
+
tools: safeToolNames(pi),
|
|
280
|
+
skills: loadAvailableSkills({ cwd }).map((skill) => skill.name),
|
|
281
|
+
themes: themeSuggestions(config),
|
|
282
|
+
systemPromptFiles: systemPromptFileSuggestions(config),
|
|
283
|
+
};
|
|
284
|
+
warmCompletionSessions(snapshot);
|
|
285
|
+
return snapshot;
|
|
286
|
+
},
|
|
287
|
+
current() {
|
|
288
|
+
return snapshot;
|
|
289
|
+
},
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
async function listCompletionSessions({ cwd, sessionDir, currentSessionId, currentSessionPath, }) {
|
|
293
|
+
try {
|
|
294
|
+
const persisted = await cachedPersistedSessions(completionSessionCacheKey({ cwd, sessionDir }), () => listCompletionSessionSources(cwd, sessionDir));
|
|
295
|
+
const current = findSessionSummary(persisted, {
|
|
296
|
+
id: currentSessionId,
|
|
297
|
+
sessionId: currentSessionId,
|
|
298
|
+
path: currentSessionPath,
|
|
299
|
+
}) ??
|
|
300
|
+
(currentSessionId || currentSessionPath
|
|
301
|
+
? {
|
|
302
|
+
id: currentSessionId,
|
|
303
|
+
sessionId: currentSessionId,
|
|
304
|
+
path: currentSessionPath,
|
|
305
|
+
}
|
|
306
|
+
: undefined);
|
|
307
|
+
const scoped = sessionCompletionScope(buildSessionTree(current, persisted), current, {
|
|
308
|
+
rx: 4,
|
|
309
|
+
ry: 4,
|
|
310
|
+
});
|
|
311
|
+
return enrichSessionSummaries(scoped, 20);
|
|
312
|
+
}
|
|
313
|
+
catch {
|
|
314
|
+
return [];
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
function warmCompletionSessions({ cwd, sessionDir }) {
|
|
318
|
+
warmPersistedSessions(completionSessionCacheKey({ cwd, sessionDir }), () => listCompletionSessionSources(cwd, sessionDir));
|
|
319
|
+
}
|
|
320
|
+
async function listCompletionSessionSources(cwd, sessionDir) {
|
|
321
|
+
const fast = listSessionSummariesFast(sessionDir);
|
|
322
|
+
return fast.length > 0 ? fast : SessionManager.list(cwd, sessionDir);
|
|
323
|
+
}
|
|
324
|
+
function completionSessionCacheKey({ cwd, sessionDir }) {
|
|
325
|
+
return `${cwd ?? ""}\n${sessionDir ?? ""}`;
|
|
326
|
+
}
|
|
327
|
+
function scopedModelSuggestions(ctx) {
|
|
328
|
+
const patterns = enabledModelPatterns();
|
|
329
|
+
const registry = ctx?.modelRegistry;
|
|
330
|
+
const available = safeAvailableModels(registry);
|
|
331
|
+
if (patterns.length === 0)
|
|
332
|
+
return available;
|
|
333
|
+
return patterns.map((pattern) => {
|
|
334
|
+
const [provider, id] = String(pattern).split(/\/(.*)/).filter(Boolean);
|
|
335
|
+
const match = provider && id ? registry?.find?.(provider, id) : undefined;
|
|
336
|
+
return match ?? { provider, id: id ?? pattern, label: pattern };
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
function safeAvailableModels(modelRegistry) {
|
|
340
|
+
try {
|
|
341
|
+
const models = modelRegistry?.getAvailable?.();
|
|
342
|
+
return Array.isArray(models) ? models : [];
|
|
343
|
+
}
|
|
344
|
+
catch {
|
|
345
|
+
return [];
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
function safeToolNames(pi) {
|
|
349
|
+
try {
|
|
350
|
+
return pi.getAllTools().map((tool) => tool.name).filter(Boolean);
|
|
351
|
+
}
|
|
352
|
+
catch {
|
|
353
|
+
return [];
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
function systemPromptFileSuggestions(config) {
|
|
357
|
+
const settings = recordValue(config.settings);
|
|
358
|
+
const agentless = recordValue(settings.agentlessSession);
|
|
359
|
+
const defaults = recordValue(settings.agentDefaults);
|
|
360
|
+
const files = [
|
|
361
|
+
...toStringArray(agentless.systemPromptFiles),
|
|
362
|
+
...toStringArray(defaults.systemPromptFiles),
|
|
363
|
+
...config.agents.flatMap((agent) => toStringArray(agent.systemPromptFiles)),
|
|
364
|
+
];
|
|
365
|
+
return files.filter((file, index) => files.indexOf(file) === index);
|
|
366
|
+
}
|
|
367
|
+
function recordValue(value) {
|
|
368
|
+
return value && typeof value === "object" ? value : {};
|
|
369
|
+
}
|
|
370
|
+
function toStringArray(value) {
|
|
371
|
+
return Array.isArray(value)
|
|
372
|
+
? value.filter((item) => typeof item === "string")
|
|
373
|
+
: [];
|
|
374
|
+
}
|
|
375
|
+
function themeSuggestions(config) {
|
|
376
|
+
const themes = [
|
|
377
|
+
config.settings?.theme,
|
|
378
|
+
...config.agents.map((agent) => agent.theme),
|
|
379
|
+
].filter((theme) => typeof theme === "string" && Boolean(theme));
|
|
380
|
+
return themes.filter((theme, index) => themes.indexOf(theme) === index);
|
|
381
|
+
}
|
|
382
|
+
/** Maps one agents-tool action to one orchestrator operation. */
|
|
383
|
+
async function executeAction(orchestrator, ctx, input, onUpdate, signal) {
|
|
384
|
+
if (input.action === "list") {
|
|
385
|
+
const config = loadConfiguration({ cwd: ctx.cwd });
|
|
386
|
+
const agents = orchestrator.availableAgents(ctx, config);
|
|
387
|
+
const text = agents
|
|
388
|
+
.map((agent) => `${agent.name}: ${agent.description ?? ""}`)
|
|
389
|
+
.join("\n") || "No agents configured.";
|
|
390
|
+
return {
|
|
391
|
+
text,
|
|
392
|
+
details: orchestrator.cardDetails("list", "done", {
|
|
393
|
+
configuration: { agents: agents.map((agent) => agent.name) },
|
|
394
|
+
}),
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
if (input.action === "get") {
|
|
398
|
+
if (!input.agent)
|
|
399
|
+
throw new Error('Field "agent" is required for get.');
|
|
400
|
+
const config = loadConfiguration({ cwd: ctx.cwd });
|
|
401
|
+
const agent = orchestrator
|
|
402
|
+
.availableAgents(ctx, config)
|
|
403
|
+
.find((item) => item.name === input.agent);
|
|
404
|
+
if (!agent)
|
|
405
|
+
throw new Error(`Unknown or unavailable agent "${input.agent}".`);
|
|
406
|
+
return {
|
|
407
|
+
text: JSON.stringify(agent, null, 2),
|
|
408
|
+
details: orchestrator.cardDetails("get", "done", {
|
|
409
|
+
agentName: agent.name,
|
|
410
|
+
configuration: agent,
|
|
411
|
+
}),
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
if (input.action === "status") {
|
|
415
|
+
if (!input.sessionId)
|
|
416
|
+
throw new Error('Field "sessionId" is required for status.');
|
|
417
|
+
const status = await orchestrator.status(ctx, input.sessionId);
|
|
418
|
+
return {
|
|
419
|
+
text: status.text,
|
|
420
|
+
details: orchestrator.cardDetails("status", "done", {
|
|
421
|
+
sessionId: status.sessionId,
|
|
422
|
+
configuration: status,
|
|
423
|
+
}),
|
|
424
|
+
};
|
|
425
|
+
}
|
|
426
|
+
if (input.action === "load") {
|
|
427
|
+
return orchestrator.loadAgent(ctx, input.agent, {
|
|
428
|
+
overrides: input.overrides,
|
|
429
|
+
});
|
|
430
|
+
}
|
|
431
|
+
if (input.action === "send") {
|
|
432
|
+
if (typeof input.message !== "string" || !input.message.trim())
|
|
433
|
+
throw new Error('Field "message" is required for send.');
|
|
434
|
+
const stopRefresh = startLiveRefresh(ctx, "agents-tool");
|
|
435
|
+
try {
|
|
436
|
+
const result = await orchestrator.send(ctx, input, {
|
|
437
|
+
onUpdate,
|
|
438
|
+
signal,
|
|
439
|
+
onRefresh: () => stopRefresh.refresh?.(),
|
|
440
|
+
onSettled: () => stopRefresh(),
|
|
441
|
+
});
|
|
442
|
+
if (!["running", "queued"].includes(result.details?.status))
|
|
443
|
+
stopRefresh();
|
|
444
|
+
return result;
|
|
445
|
+
}
|
|
446
|
+
catch (error) {
|
|
447
|
+
stopRefresh();
|
|
448
|
+
throw error;
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
if (input.action === "abort") {
|
|
452
|
+
const text = await orchestrator.abort(ctx, input.sessionId);
|
|
453
|
+
return {
|
|
454
|
+
text,
|
|
455
|
+
details: orchestrator.cardDetails("abort", "done", {
|
|
456
|
+
sessionId: input.sessionId,
|
|
457
|
+
}),
|
|
458
|
+
};
|
|
459
|
+
}
|
|
460
|
+
if (input.action === "discoverSessions") {
|
|
461
|
+
const result = await orchestrator.discoverSessions(ctx, input);
|
|
462
|
+
return {
|
|
463
|
+
text: JSON.stringify(result, null, 2),
|
|
464
|
+
details: orchestrator.cardDetails("discoverSessions", "done", {
|
|
465
|
+
configuration: result,
|
|
466
|
+
sessions: result.sessions,
|
|
467
|
+
}),
|
|
468
|
+
};
|
|
469
|
+
}
|
|
470
|
+
throw new Error(`Unknown action "${input.action}".`);
|
|
471
|
+
}
|
|
472
|
+
function firstText(content) {
|
|
473
|
+
return Array.isArray(content)
|
|
474
|
+
? content.find((item) => item.type === "text")?.text
|
|
475
|
+
: undefined;
|
|
476
|
+
}
|