omnirush 0.8.6 → 0.9.1
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/assets/CHANGELOG.md +120 -0
- package/assets/extensions/omnirush/agents-lib.ts +501 -63
- package/assets/extensions/omnirush/agents.ts +140 -115
- package/assets/extensions/omnirush/bgshell-lib.ts +432 -0
- package/assets/extensions/omnirush/bgshell.ts +392 -0
- package/assets/extensions/omnirush/capture/workspace-collector.ts +86 -22
- package/assets/extensions/omnirush/collector.ts +275 -43
- package/assets/extensions/omnirush/commands.ts +2 -0
- package/assets/extensions/omnirush/deliveries.ts +145 -0
- package/assets/extensions/omnirush/guard/UPSTREAM +2 -0
- package/assets/extensions/omnirush/guard/git-command-policy.ts +885 -0
- package/assets/extensions/omnirush/guard-lib.ts +230 -0
- package/assets/extensions/omnirush/guard.ts +340 -0
- package/assets/extensions/omnirush/index.ts +32 -6
- package/assets/extensions/omnirush/mcp.ts +103 -8
- package/assets/extensions/omnirush/memory-lib.ts +516 -0
- package/assets/extensions/omnirush/pi-engine.ts +115 -3
- package/assets/extensions/omnirush/plan-lib.ts +38 -9
- package/assets/extensions/omnirush/plan.ts +85 -30
- package/assets/extensions/omnirush/sota.ts +52 -4
- package/assets/extensions/omnirush/status-lib.ts +3 -0
- package/assets/extensions/omnirush/subagent-marker.ts +21 -0
- package/assets/extensions/omnirush/subagents-lib.ts +623 -0
- package/assets/extensions/omnirush/subagents.ts +305 -0
- package/assets/extensions/omnirush/swarm-lib.ts +142 -0
- package/assets/extensions/omnirush/swarm.ts +95 -0
- package/assets/extensions/omnirush/voice/capture.ts +502 -0
- package/assets/extensions/omnirush/voice/core/UPSTREAM +16 -0
- package/assets/extensions/omnirush/voice/core/file-source.ts +70 -0
- package/assets/extensions/omnirush/voice/core/index.ts +21 -0
- package/assets/extensions/omnirush/voice/core/keyterms.ts +117 -0
- package/assets/extensions/omnirush/voice/core/resample.ts +63 -0
- package/assets/extensions/omnirush/voice/core/segmenter.ts +231 -0
- package/assets/extensions/omnirush/voice/core/session.ts +403 -0
- package/assets/extensions/omnirush/voice/core/text.ts +81 -0
- package/assets/extensions/omnirush/voice/core/transcriber.ts +135 -0
- package/assets/extensions/omnirush/voice/core/types.ts +102 -0
- package/assets/extensions/omnirush/voice/core/wav.ts +95 -0
- package/assets/extensions/omnirush/voice/keys.ts +435 -0
- package/assets/extensions/omnirush/voice/kitty.ts +64 -0
- package/assets/extensions/omnirush/voice/pvrecorder-worker.cjs +43 -0
- package/assets/extensions/omnirush/voice/settings.ts +67 -0
- package/assets/extensions/omnirush/voice.ts +838 -0
- package/assets/extensions/omnirush/yolo-lib.ts +80 -0
- package/assets/extensions/omnirush/yolo.ts +85 -0
- package/package.json +7 -3
- package/scripts/brand-engine.js +526 -0
- package/scripts/build-all-packages.py +29 -1
- package/scripts/smoke-packages.py +33 -1
- package/src/bin.js +232 -33
- package/src/compat.js +272 -0
- package/src/lib.js +64 -0
- package/src/sessions.js +222 -0
- package/scripts/patch-pi-branding.js +0 -251
|
@@ -4,7 +4,11 @@
|
|
|
4
4
|
// the SAME workspace:
|
|
5
5
|
// - role presets: code-searcher (read-only exploration),
|
|
6
6
|
// researcher-web (web_fetch research), general-worker (full tools)
|
|
7
|
-
// - parallel execution, every task at once unless max_parallel throttles
|
|
7
|
+
// - parallel execution, every task at once unless max_parallel throttles;
|
|
8
|
+
// no count cap, but memory-aware admission (memory-lib.ts): a child
|
|
9
|
+
// starts while the machine keeps enough RAM free and the agents' process
|
|
10
|
+
// tree stays under its budget, the rest wait ("queued: waiting for
|
|
11
|
+
// memory") and start as memory frees up
|
|
8
12
|
// - no wall-clock limit: a child runs until it is done. Only an explicit
|
|
9
13
|
// timeout_minutes, the inactivity watchdog (no output at all for
|
|
10
14
|
// OMNIRUSH_AGENT_INACTIVITY_MINUTES, default 30, longer while one of
|
|
@@ -20,7 +24,8 @@
|
|
|
20
24
|
// running children
|
|
21
25
|
//
|
|
22
26
|
// Tracing is free: every child writes its own session JSONL under
|
|
23
|
-
// <
|
|
27
|
+
// <session dir>/subagents/ (children run without --no-session; that
|
|
28
|
+
// folder keeps them out of /resume and --continue)
|
|
24
29
|
// under a session id the parent picks, and the parent session's
|
|
25
30
|
// collector records each child's conversation as a sub-agent of the
|
|
26
31
|
// turn (session.child events with parent and depth, as the desktop
|
|
@@ -29,6 +34,11 @@
|
|
|
29
34
|
// stopped and recorded as interrupted. Children get
|
|
30
35
|
// OMNIRUSH_PARENT_SESSION, so a child process uploads nothing of its own.
|
|
31
36
|
//
|
|
37
|
+
// Nesting: a sub-agent may start sub-agents of its own, up to
|
|
38
|
+
// MAX_SUBAGENT_DEPTH (3) layers below the main session (the desktop's
|
|
39
|
+
// OMNIRUSH_SUBAGENT_DEPTH). Each child gets OMNIRUSH_AGENT_DEPTH (its layer)
|
|
40
|
+
// and a note saying it; layer 3 has no spawn_agents.
|
|
41
|
+
//
|
|
32
42
|
// `/agents` lists this session's subagents (running + finished);
|
|
33
43
|
// `/agents cancel <id|all>` stops running ones.
|
|
34
44
|
|
|
@@ -36,6 +46,8 @@ import { StringEnum } from "@earendil-works/pi-ai";
|
|
|
36
46
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
37
47
|
import { Type } from "typebox";
|
|
38
48
|
|
|
49
|
+
import { deliveryHub } from "./deliveries";
|
|
50
|
+
import { subagentTasks } from "./subagents";
|
|
39
51
|
import {
|
|
40
52
|
AGENT_ROLES,
|
|
41
53
|
AGENTS_HOOK,
|
|
@@ -44,15 +56,25 @@ import {
|
|
|
44
56
|
type AgentRunner,
|
|
45
57
|
type AgentsHook,
|
|
46
58
|
type AgentRecord,
|
|
59
|
+
agentDepth,
|
|
60
|
+
canDelegate,
|
|
47
61
|
childInactivityMs,
|
|
48
62
|
renderAgentStatus,
|
|
63
|
+
subagentLayerNote,
|
|
49
64
|
renderChildResults,
|
|
50
65
|
renderDelivery,
|
|
51
|
-
modelSelectionFromContext,
|
|
52
66
|
runChildAgent,
|
|
67
|
+
childModelFallback,
|
|
68
|
+
subagentSessionDir,
|
|
53
69
|
type ChildResult,
|
|
54
70
|
type ChildTask,
|
|
55
71
|
} from "./agents-lib";
|
|
72
|
+
import { SWARM_GUIDELINE } from "./swarm-lib";
|
|
73
|
+
import { omniDir } from "./auth";
|
|
74
|
+
import { MemoryAdmission, readMemorySettings } from "./memory-lib";
|
|
75
|
+
|
|
76
|
+
/** Blocking spawn_agents: progress updates at most this often (ms). */
|
|
77
|
+
const PROGRESS_UPDATE_MS = 500;
|
|
56
78
|
|
|
57
79
|
/** A timeout_minutes below this is taken for a placeholder, not a limit. */
|
|
58
80
|
const MIN_TIMEOUT_MINUTES = 1;
|
|
@@ -68,7 +90,7 @@ const SpawnAgentsParams = Type.Object({
|
|
|
68
90
|
}),
|
|
69
91
|
task: Type.String({ description: "Self-contained task description for the child agent (it sees ONLY this, not your conversation)" }),
|
|
70
92
|
model: Type.Optional(Type.String({
|
|
71
|
-
description: 'Optional model[:effort] for THIS task (e.g. "muse-spark-1.3", "gpt-6-sol:high"). Omit (or leave empty)
|
|
93
|
+
description: 'Optional model[:effort] for THIS task (e.g. "muse-spark-1.3", "muse-spark-1.2-contributor" for cheap swarm workers, "meta-muse-spark", "muse-spark-1.1:low", "gpt-6-astra", "gpt-6-sol:high", "gpt-5.6-sol"). Omit (or leave empty) for the sub-agent model the user picked with /subagents, else the parent session\'s current model and effort',
|
|
72
94
|
})),
|
|
73
95
|
}),
|
|
74
96
|
{ description: "Tasks to delegate; they all run in parallel", minItems: 1 },
|
|
@@ -106,21 +128,28 @@ function renderSome(records: AgentRecord[], unknown: string[] = []): string {
|
|
|
106
128
|
return parts.join("\n\n") || "No matching sub-agents.";
|
|
107
129
|
}
|
|
108
130
|
|
|
109
|
-
export default function (pi: ExtensionAPI, options: { run?: AgentRunner } = {}) {
|
|
131
|
+
export default function (pi: ExtensionAPI, options: { run?: AgentRunner; admission?: MemoryAdmission | null; env?: NodeJS.ProcessEnv } = {}) {
|
|
132
|
+
// Sub-agents nest up to MAX_SUBAGENT_DEPTH layers below the main session,
|
|
133
|
+
// as in the desktop app: every sub-agent is told its layer, and the last
|
|
134
|
+
// layer gets no spawn_agents (nor the tools that manage sub-agents).
|
|
135
|
+
const depth = agentDepth(options.env ?? process.env);
|
|
136
|
+
if (depth > 0) {
|
|
137
|
+
pi.on("before_agent_start", async (event: any) => {
|
|
138
|
+
const base = typeof event?.systemPrompt === "string" ? event.systemPrompt : "";
|
|
139
|
+
return { systemPrompt: `${base}\n\n${subagentLayerNote(depth)}` };
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
if (!canDelegate(depth)) return;
|
|
143
|
+
|
|
110
144
|
const watchdogMinutes = Math.round(childInactivityMs() / 60_000);
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
let cwd = process.cwd();
|
|
114
|
-
let inheritedModel = "";
|
|
145
|
+
const hub = deliveryHub(pi);
|
|
146
|
+
/** The last context seen (the TUI status bar for running agents). */
|
|
115
147
|
let lastContext: any = null;
|
|
116
148
|
let statusTimer: ReturnType<typeof setTimeout> | null = null;
|
|
117
|
-
/** An agent run is under way (one-shot runs deliver through the settle loop otherwise). */
|
|
118
|
-
let busy = false;
|
|
119
|
-
let oneShot = false;
|
|
120
149
|
|
|
121
150
|
function publishStatus(): void {
|
|
122
151
|
statusTimer = null;
|
|
123
|
-
const session = currentSession;
|
|
152
|
+
const session = hub.currentSession;
|
|
124
153
|
if (!session || !lastContext?.ui?.setStatus) return;
|
|
125
154
|
const records = manager.list(session);
|
|
126
155
|
if (records.length > 0) lastContext.ui.setStatus("omnirush-agents", agentStatusSummary(records));
|
|
@@ -132,40 +161,60 @@ export default function (pi: ExtensionAPI, options: { run?: AgentRunner } = {})
|
|
|
132
161
|
statusTimer.unref?.();
|
|
133
162
|
}
|
|
134
163
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
details: { results: records.map(resultOf) },
|
|
142
|
-
},
|
|
143
|
-
// Busy: into the running turn at its next step. Idle: a new turn.
|
|
144
|
-
{ triggerTurn: true, deliverAs: "steer" },
|
|
145
|
-
);
|
|
164
|
+
// Memory-aware admission of children; thresholds from the environment or
|
|
165
|
+
// settings.json "subagentMemory" (memory-lib.ts). Built on first use.
|
|
166
|
+
let admission: MemoryAdmission | null = null;
|
|
167
|
+
const admissionOf = (): MemoryAdmission => {
|
|
168
|
+
admission ??= options.admission ?? new MemoryAdmission({ settings: readMemorySettings(omniDir()) });
|
|
169
|
+
return admission;
|
|
146
170
|
};
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
return;
|
|
153
|
-
}
|
|
154
|
-
for (const group of manager.takeDeliverable(currentSession)) send(group.records);
|
|
171
|
+
const lazyAdmission = {
|
|
172
|
+
check: () => admissionOf().check(),
|
|
173
|
+
started: (key: object, pid?: number | null) => admissionOf().started(key, pid ?? null),
|
|
174
|
+
spawned: (key: object, pid: number | null | undefined) => admissionOf().spawned(key, pid),
|
|
175
|
+
finished: (key: object) => admissionOf().finished(key),
|
|
155
176
|
};
|
|
156
177
|
|
|
178
|
+
/** Tell the user when children wait for memory (a warning when it is critically low). */
|
|
179
|
+
function memoryWait(info: { critical: boolean; detail: string; queued: number } | null): void {
|
|
180
|
+
scheduleStatus();
|
|
181
|
+
if (!info) return;
|
|
182
|
+
const text = info.critical
|
|
183
|
+
? `omnirush: ${info.detail}. Not starting more sub-agents until memory frees up (${info.queued} queued); running ones continue.`
|
|
184
|
+
: `omnirush: ${info.queued} sub-agent${info.queued === 1 ? "" : "s"} queued: waiting for memory (${info.detail}).`;
|
|
185
|
+
if (lastContext?.hasUI && lastContext?.ui?.notify) lastContext.ui.notify(text, info.critical ? "warning" : "info");
|
|
186
|
+
// Headless: the top agent says it on stderr (a sub-agent's stderr only reaches its parent).
|
|
187
|
+
else if (info.critical || !String(process.env.OMNIRUSH_PARENT_SESSION ?? "").trim()) process.stderr.write(`${text}\n`);
|
|
188
|
+
}
|
|
189
|
+
|
|
157
190
|
const manager = new AgentManager({
|
|
158
191
|
run: options.run ?? ((task, options) => runChildAgent(task, {
|
|
159
|
-
cwd: options.cwd ?? cwd,
|
|
192
|
+
cwd: options.cwd ?? hub.cwd,
|
|
160
193
|
parentSessionId: options.parentSessionId,
|
|
161
194
|
sessionId: options.sessionId,
|
|
195
|
+
...(options.sessionDir ? { sessionDir: options.sessionDir } : {}),
|
|
162
196
|
signal: options.signal,
|
|
163
197
|
onActivity: options.onActivity,
|
|
198
|
+
...(options.onSpawn ? { onSpawn: options.onSpawn } : {}),
|
|
199
|
+
extraEnv: admissionOf().childEnv(),
|
|
164
200
|
...(options.timeoutMs !== undefined ? { timeoutMs: options.timeoutMs } : {}),
|
|
165
201
|
})),
|
|
166
202
|
// After the settle's own bookkeeping (waiters took theirs).
|
|
167
|
-
onDeliverable: () =>
|
|
203
|
+
onDeliverable: () => hub.notify(),
|
|
168
204
|
onChanged: scheduleStatus,
|
|
205
|
+
admission: options.admission === null ? null : lazyAdmission,
|
|
206
|
+
onMemoryWait: memoryWait,
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
hub.addSource({
|
|
210
|
+
hasPending: (session) => manager.hasPending(session),
|
|
211
|
+
// One message per finished batch (or per child with notify "each").
|
|
212
|
+
take: (session) => manager.takeDeliverable(session).map(({ records }) => ({
|
|
213
|
+
customType: DELIVERY_TYPE,
|
|
214
|
+
content: renderDelivery(records),
|
|
215
|
+
details: { results: records.map(resultOf) },
|
|
216
|
+
})),
|
|
217
|
+
changed: () => manager.changed(),
|
|
169
218
|
});
|
|
170
219
|
|
|
171
220
|
const hook: AgentsHook = {
|
|
@@ -177,17 +226,8 @@ export default function (pi: ExtensionAPI, options: { run?: AgentRunner } = {})
|
|
|
177
226
|
(globalThis as any)[AGENTS_HOOK] = hook;
|
|
178
227
|
|
|
179
228
|
const sessionOf = (ctx: any): string => {
|
|
180
|
-
const id = String(ctx?.sessionManager?.getSessionId?.() ?? "");
|
|
181
|
-
if (id && id !== currentSession) {
|
|
182
|
-
currentSession = id;
|
|
183
|
-
inheritedModel = "";
|
|
184
|
-
}
|
|
185
229
|
lastContext = ctx ?? lastContext;
|
|
186
|
-
|
|
187
|
-
if (selected) inheritedModel = selected;
|
|
188
|
-
if (ctx?.cwd) cwd = String(ctx.cwd);
|
|
189
|
-
if (ctx?.mode === "print" || ctx?.mode === "json") oneShot = true;
|
|
190
|
-
return id;
|
|
230
|
+
return hub.sessionOf(ctx);
|
|
191
231
|
};
|
|
192
232
|
|
|
193
233
|
pi.registerTool({
|
|
@@ -212,6 +252,7 @@ export default function (pi: ExtensionAPI, options: { run?: AgentRunner } = {})
|
|
|
212
252
|
"Short fan-outs whose results you need right away (quick searches, lookups): the default blocking call.",
|
|
213
253
|
"Long work (implementation chunks, builds, test suites, long research — anything that may take many minutes) while you have other things to do: spawn_agents with wait:false, then keep working; the results arrive on their own as a message, so do not poll agents_status in a loop. Call agents_wait only when you have nothing else to do and need the results before going on.",
|
|
214
254
|
"Do not use spawn_agents for a single quick action — doing it yourself is cheaper.",
|
|
255
|
+
SWARM_GUIDELINE,
|
|
215
256
|
],
|
|
216
257
|
parameters: SpawnAgentsParams,
|
|
217
258
|
|
|
@@ -222,16 +263,22 @@ export default function (pi: ExtensionAPI, options: { run?: AgentRunner } = {})
|
|
|
222
263
|
if (!raw || typeof raw.task !== "string" || !raw.task.trim()) {
|
|
223
264
|
throw new Error("invalid tasks: every entry needs a non-empty task string");
|
|
224
265
|
}
|
|
225
|
-
//
|
|
226
|
-
//
|
|
266
|
+
// A task's own model[:effort] wins; an omitted (or empty) one takes
|
|
267
|
+
// the /subagents pick, else the parent session's current model and
|
|
268
|
+
// effort (resolved below by subagentTasks).
|
|
227
269
|
const requestedModel = typeof raw.model === "string" ? raw.model.trim() : "";
|
|
228
270
|
tasks.push({
|
|
229
271
|
role: raw.role,
|
|
230
272
|
task: raw.task.trim(),
|
|
231
|
-
...(
|
|
273
|
+
...(requestedModel ? { model: requestedModel } : {}),
|
|
232
274
|
});
|
|
233
275
|
}
|
|
234
276
|
if (tasks.length === 0) throw new Error("no tasks given");
|
|
277
|
+
// The model and effort each child runs on (subagents.ts): the task's
|
|
278
|
+
// own model[:effort], else the /subagents pick, else the parent
|
|
279
|
+
// session's current model and effort; the main model when a pick is
|
|
280
|
+
// unavailable.
|
|
281
|
+
const resolvedTasks = subagentTasks(pi, ctx, tasks);
|
|
235
282
|
if (!parentSessionId) throw new Error("no parent session id — subagents cannot be traced");
|
|
236
283
|
const workdir = ctx?.cwd ? String(ctx.cwd) : process.cwd();
|
|
237
284
|
// Only a deliberate limit counts: models fill optional numbers with
|
|
@@ -246,8 +293,10 @@ export default function (pi: ExtensionAPI, options: { run?: AgentRunner } = {})
|
|
|
246
293
|
? Math.floor(params.max_parallel)
|
|
247
294
|
: undefined;
|
|
248
295
|
const background = params.wait === false;
|
|
296
|
+
let progressTimer: ReturnType<typeof setTimeout> | null = null;
|
|
297
|
+
let finished = false;
|
|
249
298
|
|
|
250
|
-
const dispatched = manager.dispatch(parentSessionId,
|
|
299
|
+
const dispatched = manager.dispatch(parentSessionId, resolvedTasks, {
|
|
251
300
|
background,
|
|
252
301
|
concurrency,
|
|
253
302
|
notify: params.notify === "each" ? "each" : "batch",
|
|
@@ -260,18 +309,31 @@ export default function (pi: ExtensionAPI, options: { run?: AgentRunner } = {})
|
|
|
260
309
|
});
|
|
261
310
|
},
|
|
262
311
|
onChanged: () => {
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
312
|
+
// Every sign of life of every child lands here: the progress update
|
|
313
|
+
// (which carries the finished results) goes out at most every
|
|
314
|
+
// PROGRESS_UPDATE_MS, not once per output chunk.
|
|
315
|
+
if (background || progressTimer) return;
|
|
316
|
+
progressTimer = setTimeout(() => {
|
|
317
|
+
progressTimer = null;
|
|
318
|
+
if (finished) return;
|
|
319
|
+
onUpdate?.({
|
|
320
|
+
content: [{ type: "text", text: agentStatusSummary(manager.list(parentSessionId)) }],
|
|
321
|
+
details: { results: manager.list(parentSessionId).filter((candidate) => candidate.result).map(resultOf) },
|
|
322
|
+
});
|
|
323
|
+
}, PROGRESS_UPDATE_MS);
|
|
324
|
+
progressTimer.unref?.();
|
|
268
325
|
},
|
|
269
326
|
...(timeoutMs !== undefined ? { timeoutMs } : {}),
|
|
270
327
|
cwd: workdir,
|
|
328
|
+
// Children's sessions stay out of the user's /resume list.
|
|
329
|
+
sessionDir: subagentSessionDir(ctx?.sessionManager?.getSessionDir?.()),
|
|
271
330
|
});
|
|
272
331
|
|
|
273
332
|
if (background) {
|
|
274
|
-
const lines = dispatched.records.map((record) =>
|
|
333
|
+
const lines = dispatched.records.map((record, index) => {
|
|
334
|
+
const note = childModelFallback(resolvedTasks[index])?.note;
|
|
335
|
+
return `- ${record.id}: ${record.role}${record.model ? ` [${record.model}${record.effort ? ` · ${record.effort}` : ""}]` : ""} — ${record.task.length > 100 ? `${record.task.slice(0, 99)}…` : record.task}${note ? ` (${note})` : ""}`;
|
|
336
|
+
});
|
|
275
337
|
return {
|
|
276
338
|
content: [{
|
|
277
339
|
type: "text",
|
|
@@ -288,20 +350,27 @@ export default function (pi: ExtensionAPI, options: { run?: AgentRunner } = {})
|
|
|
288
350
|
details: {
|
|
289
351
|
background: true,
|
|
290
352
|
batch: dispatched.batch,
|
|
291
|
-
results: dispatched.records.map((record) =>
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
353
|
+
results: dispatched.records.map((record, index) => {
|
|
354
|
+
const modelFallback = childModelFallback(resolvedTasks[index]);
|
|
355
|
+
return {
|
|
356
|
+
agent_id: record.id,
|
|
357
|
+
role: record.role,
|
|
358
|
+
task: record.task,
|
|
359
|
+
session_id: record.sessionId,
|
|
360
|
+
...(record.model ? { model: record.model } : {}),
|
|
361
|
+
...(record.effort ? { effort: record.effort } : {}),
|
|
362
|
+
...(modelFallback ? { model_fallback: modelFallback } : {}),
|
|
363
|
+
parallel_limit: record.parallelLimit,
|
|
364
|
+
status: "running",
|
|
365
|
+
};
|
|
366
|
+
}),
|
|
300
367
|
},
|
|
301
368
|
};
|
|
302
369
|
}
|
|
303
370
|
|
|
304
371
|
await dispatched.done;
|
|
372
|
+
finished = true;
|
|
373
|
+
if (progressTimer) clearTimeout(progressTimer);
|
|
305
374
|
const records = dispatched.records;
|
|
306
375
|
return {
|
|
307
376
|
content: [{ type: "text", text: renderChildResults(records.map((record) => record.result as ChildResult), records.map((record) => record.id)) }],
|
|
@@ -404,55 +473,6 @@ export default function (pi: ExtensionAPI, options: { run?: AgentRunner } = {})
|
|
|
404
473
|
},
|
|
405
474
|
});
|
|
406
475
|
|
|
407
|
-
pi.on("session_start", (_event: any, ctx: any) => {
|
|
408
|
-
sessionOf(ctx);
|
|
409
|
-
});
|
|
410
|
-
|
|
411
|
-
pi.on("agent_start", (_event: any, ctx: any) => {
|
|
412
|
-
sessionOf(ctx);
|
|
413
|
-
busy = true;
|
|
414
|
-
});
|
|
415
|
-
|
|
416
|
-
/**
|
|
417
|
-
* One-shot runs (print/json mode) end when the agent settles: background
|
|
418
|
-
* children still out would be lost. The run waits for them here and
|
|
419
|
-
* continues with their results (a new run nested in this settle, whose
|
|
420
|
-
* own settle resumes this one when everything is done).
|
|
421
|
-
*/
|
|
422
|
-
let resumeOuter: (() => void) | null = null;
|
|
423
|
-
pi.on("agent_settled", async (_event: any, ctx: any) => {
|
|
424
|
-
busy = false;
|
|
425
|
-
const session = sessionOf(ctx);
|
|
426
|
-
if (!oneShot) {
|
|
427
|
-
// Anything that finished right as the run ended.
|
|
428
|
-
deliver();
|
|
429
|
-
return;
|
|
430
|
-
}
|
|
431
|
-
const outer = resumeOuter;
|
|
432
|
-
resumeOuter = null;
|
|
433
|
-
try {
|
|
434
|
-
for (;;) {
|
|
435
|
-
if (typeof ctx?.isIdle === "function" && !ctx.isIdle()) {
|
|
436
|
-
// A run started (a delivery): its settle resumes this one.
|
|
437
|
-
await new Promise<void>((resolve) => { resumeOuter = resolve; });
|
|
438
|
-
break;
|
|
439
|
-
}
|
|
440
|
-
if (!session || !manager.hasPending(session)) break;
|
|
441
|
-
const groups = manager.takeDeliverable(session);
|
|
442
|
-
if (groups.length === 0) {
|
|
443
|
-
await manager.changed();
|
|
444
|
-
continue;
|
|
445
|
-
}
|
|
446
|
-
busy = true;
|
|
447
|
-
const records = groups.flatMap((group) => group.records);
|
|
448
|
-
send(records);
|
|
449
|
-
if (typeof ctx?.isIdle === "function" && ctx.isIdle()) busy = false;
|
|
450
|
-
}
|
|
451
|
-
} finally {
|
|
452
|
-
outer?.();
|
|
453
|
-
}
|
|
454
|
-
});
|
|
455
|
-
|
|
456
476
|
pi.on("session_shutdown", async () => {
|
|
457
477
|
// Children the session ends under are stopped (the collector, when it
|
|
458
478
|
// runs, already did this before its last capture).
|
|
@@ -460,7 +480,7 @@ export default function (pi: ExtensionAPI, options: { run?: AgentRunner } = {})
|
|
|
460
480
|
});
|
|
461
481
|
|
|
462
482
|
pi.registerCommand("agents", {
|
|
463
|
-
description: "List this session's subagents (running and finished); /agents cancel <id|all> stops running ones",
|
|
483
|
+
description: "List this session's subagents (running and finished) and the memory they may use; /agents cancel <id|all> stops running ones; /agents memory shows the memory admission",
|
|
464
484
|
handler: async (args: string, ctx: any) => {
|
|
465
485
|
const session = sessionOf(ctx);
|
|
466
486
|
const words = String(args ?? "").trim().split(/\s+/).filter(Boolean);
|
|
@@ -472,11 +492,16 @@ export default function (pi: ExtensionAPI, options: { run?: AgentRunner } = {})
|
|
|
472
492
|
return;
|
|
473
493
|
}
|
|
474
494
|
const records = manager.list(session);
|
|
495
|
+
const memory = options.admission === null ? "" : `\n${admissionOf().describe()}`;
|
|
496
|
+
if (words[0] === "memory") {
|
|
497
|
+
ctx?.ui?.notify?.(memory.trim() || "Memory admission is off.");
|
|
498
|
+
return;
|
|
499
|
+
}
|
|
475
500
|
if (records.length === 0) {
|
|
476
|
-
ctx?.ui?.notify?.(
|
|
501
|
+
ctx?.ui?.notify?.(`No subagents have been spawned in this session yet.${memory}`);
|
|
477
502
|
return;
|
|
478
503
|
}
|
|
479
|
-
ctx?.ui?.notify?.(renderAgentStatus(records));
|
|
504
|
+
ctx?.ui?.notify?.(`${renderAgentStatus(records)}${memory}`);
|
|
480
505
|
},
|
|
481
506
|
});
|
|
482
507
|
}
|