omnirush 0.8.6 → 0.9.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/assets/CHANGELOG.md +85 -0
- package/assets/extensions/omnirush/agents-lib.ts +134 -13
- package/assets/extensions/omnirush/agents.ts +51 -107
- package/assets/extensions/omnirush/bgshell-lib.ts +400 -0
- package/assets/extensions/omnirush/bgshell.ts +392 -0
- package/assets/extensions/omnirush/collector.ts +72 -11
- 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 +12 -0
- package/assets/extensions/omnirush/pi-engine.ts +65 -1
- package/assets/extensions/omnirush/sota.ts +52 -4
- package/assets/extensions/omnirush/status-lib.ts +3 -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 +32 -1
- package/src/bin.js +205 -33
- package/src/compat.js +272 -0
- package/src/lib.js +64 -0
- package/scripts/patch-pi-branding.js +0 -251
|
@@ -33,6 +33,10 @@
|
|
|
33
33
|
// a command the user ran)
|
|
34
34
|
// custom / branch summary -> user message with synthetic text parts (what
|
|
35
35
|
// the model was shown; never a turn opener)
|
|
36
|
+
// background command -> the delivery message as above, answered by an
|
|
37
|
+
// results (bgshell.ts) assistant message holding one bash tool part
|
|
38
|
+
// per finished command (its output and exit
|
|
39
|
+
// code; no model step, no tokens)
|
|
36
40
|
// compaction -> user message with a compaction part and the
|
|
37
41
|
// summary as an assistant `summary` message
|
|
38
42
|
// system prompt -> not a message (the desktop records it in
|
|
@@ -49,6 +53,9 @@
|
|
|
49
53
|
import { createHash } from "node:crypto";
|
|
50
54
|
import { isAbsolute, resolve } from "node:path";
|
|
51
55
|
|
|
56
|
+
/** customType of bgshell.ts's finished-background-command message. */
|
|
57
|
+
export const BACKGROUND_BASH_TYPE = "omnirush-bash-result";
|
|
58
|
+
|
|
52
59
|
export type EngineMessage = { info: Record<string, unknown>; parts: Array<Record<string, unknown>> };
|
|
53
60
|
|
|
54
61
|
/** One pi session entry (loosely typed: the files are parsed without validation). */
|
|
@@ -295,6 +302,60 @@ export function engineMessagesFromEntries(entries: readonly PiEntry[], options:
|
|
|
295
302
|
return messageVariant ?? null;
|
|
296
303
|
};
|
|
297
304
|
|
|
305
|
+
/**
|
|
306
|
+
* Finished background shell commands (bgshell.ts delivery): their output
|
|
307
|
+
* as bash tool parts, the way the engine records a command the user ran.
|
|
308
|
+
*/
|
|
309
|
+
const backgroundBash = (parentId: string, entryId: string, details: unknown, at: number | null, index: number): void => {
|
|
310
|
+
const jobs = isRecord(details) && Array.isArray(details.jobs) ? details.jobs.filter(isRecord) : [];
|
|
311
|
+
if (jobs.length === 0) return;
|
|
312
|
+
const shellId = engineMessageId(entryId, "_bash");
|
|
313
|
+
const shellModel = model ?? nextAssistantModel.get(index) ?? null;
|
|
314
|
+
const time = at ?? 0;
|
|
315
|
+
const parts: Array<Record<string, unknown>> = jobs.map((job, jobIndex) => {
|
|
316
|
+
const start = typeof job.started_at === "number" ? job.started_at : time;
|
|
317
|
+
const end = typeof job.finished_at === "number" ? job.finished_at : time;
|
|
318
|
+
const failed = job.status === "failed";
|
|
319
|
+
return {
|
|
320
|
+
type: "tool",
|
|
321
|
+
callID: `bg_${typeof job.id === "string" ? job.id : jobIndex}_${entryId}`,
|
|
322
|
+
tool: "bash",
|
|
323
|
+
state: {
|
|
324
|
+
status: failed ? "error" : "completed",
|
|
325
|
+
input: { command: typeof job.command === "string" ? job.command : "", background_id: job.id ?? null },
|
|
326
|
+
...(failed ? { error: typeof job.error === "string" ? job.error : "failed" } : {}),
|
|
327
|
+
output: typeof job.output === "string" ? job.output : "",
|
|
328
|
+
metadata: {
|
|
329
|
+
background: true,
|
|
330
|
+
...(typeof job.exit_code === "number" ? { exit: job.exit_code } : {}),
|
|
331
|
+
...(typeof job.status === "string" ? { status: job.status } : {}),
|
|
332
|
+
...(job.interrupted === true ? { interrupted: true } : {}),
|
|
333
|
+
...(job.truncated === true ? { truncated: true } : {}),
|
|
334
|
+
...(typeof job.tool_call_id === "string" ? { startedBy: job.tool_call_id } : {}),
|
|
335
|
+
},
|
|
336
|
+
time: { start, end },
|
|
337
|
+
},
|
|
338
|
+
};
|
|
339
|
+
});
|
|
340
|
+
out.push({
|
|
341
|
+
info: {
|
|
342
|
+
id: shellId,
|
|
343
|
+
sessionID: sessionId,
|
|
344
|
+
role: "assistant",
|
|
345
|
+
parentID: parentId,
|
|
346
|
+
time: { created: time, completed: time },
|
|
347
|
+
...(shellModel ? { modelID: shellModel.modelID, providerID: shellModel.providerID } : {}),
|
|
348
|
+
agent,
|
|
349
|
+
mode: agent,
|
|
350
|
+
path: { cwd, root: cwd },
|
|
351
|
+
cost: 0,
|
|
352
|
+
tokens: { input: 0, output: 0, reasoning: 0, cache: { read: 0, write: 0 } },
|
|
353
|
+
finish: "stop",
|
|
354
|
+
},
|
|
355
|
+
parts: parts.map((part, partIndex) => ({ id: partId(shellId, partIndex), sessionID: sessionId, messageID: shellId, ...part })),
|
|
356
|
+
});
|
|
357
|
+
};
|
|
358
|
+
|
|
298
359
|
entries.forEach((entry, index) => {
|
|
299
360
|
const entryId = typeof entry.id === "string" && entry.id ? entry.id : null;
|
|
300
361
|
if (!entryId) return;
|
|
@@ -340,7 +401,9 @@ export function engineMessagesFromEntries(entries: readonly PiEntry[], options:
|
|
|
340
401
|
}
|
|
341
402
|
if (entry.type === "custom_message") {
|
|
342
403
|
const parts = contentParts(entry.content, { synthetic: true, metadata: { customType: entry.customType ?? null } });
|
|
343
|
-
|
|
404
|
+
const id = engineMessageId(entryId);
|
|
405
|
+
if (parts.length > 0) userMessage(id, at, parts, index);
|
|
406
|
+
if (entry.customType === BACKGROUND_BASH_TYPE) backgroundBash(id, entryId, (entry as any).details, at, index);
|
|
344
407
|
return;
|
|
345
408
|
}
|
|
346
409
|
if (entry.type !== "message" || !isRecord(entry.message)) return;
|
|
@@ -356,6 +419,7 @@ export function engineMessagesFromEntries(entries: readonly PiEntry[], options:
|
|
|
356
419
|
case "custom": {
|
|
357
420
|
const parts = contentParts(message.content, { synthetic: true, metadata: { customType: message.customType ?? null } });
|
|
358
421
|
if (parts.length > 0) userMessage(id, created, parts, index);
|
|
422
|
+
if (message.customType === BACKGROUND_BASH_TYPE) backgroundBash(id, entryId, message.details, created, index);
|
|
359
423
|
return;
|
|
360
424
|
}
|
|
361
425
|
case "branchSummary":
|
|
@@ -41,6 +41,14 @@ import {
|
|
|
41
41
|
streamIdleMs,
|
|
42
42
|
withStallWatchdog,
|
|
43
43
|
} from "./sota-lib";
|
|
44
|
+
import { omniDir } from "./auth";
|
|
45
|
+
import {
|
|
46
|
+
ENV_FALLBACK_EFFORT,
|
|
47
|
+
ENV_FALLBACK_MODEL,
|
|
48
|
+
FALLBACK_MARKER,
|
|
49
|
+
subagentFallbackFetch,
|
|
50
|
+
type FallbackRoute,
|
|
51
|
+
} from "./subagents-lib";
|
|
44
52
|
import {
|
|
45
53
|
formatUnavailableSummary,
|
|
46
54
|
isRetryableStatus,
|
|
@@ -222,7 +230,7 @@ async function readErrorBody(response: any): Promise<string> {
|
|
|
222
230
|
*/
|
|
223
231
|
function tapFetch(
|
|
224
232
|
baseFetch: any,
|
|
225
|
-
|
|
233
|
+
expectedModel: string | (() => string),
|
|
226
234
|
onViolation: (expected: string, served: string) => void,
|
|
227
235
|
idleMs: number,
|
|
228
236
|
): any {
|
|
@@ -356,6 +364,8 @@ function tapFetch(
|
|
|
356
364
|
const onData = (data: string) => {
|
|
357
365
|
if (reported) return;
|
|
358
366
|
const served = servedModelFromSseData(data);
|
|
367
|
+
// A sub-agent moved to the main model expects that one.
|
|
368
|
+
const expectedModelId = typeof expectedModel === "function" ? expectedModel() : expectedModel;
|
|
359
369
|
if (served === undefined || !isSotaViolation(expectedModelId, served)) return;
|
|
360
370
|
reported = true;
|
|
361
371
|
onViolation(expectedModelId, served);
|
|
@@ -390,22 +400,60 @@ export default function (pi: any) {
|
|
|
390
400
|
const responses = openAIResponsesApi();
|
|
391
401
|
const strictSota = (): boolean => pi.getFlag("strict-sota") === true;
|
|
392
402
|
|
|
403
|
+
// A sub-agent on a picked model: the main model it falls back to when the
|
|
404
|
+
// gateway refuses the picked one (set by the parent's spawn_agents).
|
|
405
|
+
const fallbackModel = (process.env[ENV_FALLBACK_MODEL] || "").trim();
|
|
406
|
+
const fallbackEffort = (process.env[ENV_FALLBACK_EFFORT] || "").trim() || null;
|
|
407
|
+
|
|
393
408
|
const streamWith =
|
|
394
409
|
(simple: boolean) =>
|
|
395
410
|
(model: any, context: any, options: any) => {
|
|
396
|
-
|
|
411
|
+
let baseFetch = options?.fetch ?? globalThis.fetch;
|
|
412
|
+
const route: FallbackRoute = { expected: model.id, movedTo: null };
|
|
413
|
+
if (fallbackModel && fallbackModel !== model.id) {
|
|
414
|
+
baseFetch = subagentFallbackFetch(baseFetch, {
|
|
415
|
+
model: fallbackModel,
|
|
416
|
+
effort: fallbackEffort,
|
|
417
|
+
dir: omniDir(),
|
|
418
|
+
route,
|
|
419
|
+
report: (event) => {
|
|
420
|
+
// The parent's spawn_agents reads this line from the child's stderr.
|
|
421
|
+
try {
|
|
422
|
+
process.stderr.write(`\n${FALLBACK_MARKER}${JSON.stringify(event)}\n`);
|
|
423
|
+
} catch {
|
|
424
|
+
/* stderr may be closed */
|
|
425
|
+
}
|
|
426
|
+
},
|
|
427
|
+
});
|
|
428
|
+
}
|
|
397
429
|
const tapped = {
|
|
398
430
|
...(options ?? {}),
|
|
399
431
|
fetch: tapFetch(
|
|
400
432
|
baseFetch,
|
|
401
|
-
|
|
433
|
+
() => route.expected,
|
|
402
434
|
(expected, served) => reportViolation(strictSota(), expected, served),
|
|
403
435
|
streamIdleMs(process.env.OMNIRUSH_STREAM_IDLE_MS, options?.timeoutMs),
|
|
404
436
|
),
|
|
405
437
|
};
|
|
406
|
-
|
|
438
|
+
const stream = simple
|
|
407
439
|
? responses.streamSimple(model, context, tapped)
|
|
408
440
|
: responses.stream(model, context, tapped);
|
|
441
|
+
if (fallbackModel && stream && typeof stream.push === "function") {
|
|
442
|
+
// The message records the model (and effort) that really answered.
|
|
443
|
+
const push = stream.push.bind(stream);
|
|
444
|
+
stream.push = (event: any) => {
|
|
445
|
+
if (route.movedTo) {
|
|
446
|
+
for (const message of [event?.partial, event?.message, event?.error]) {
|
|
447
|
+
if (message && typeof message === "object" && message.role === "assistant") {
|
|
448
|
+
message.model = route.movedTo.model;
|
|
449
|
+
if (route.movedTo.effort) message.providerThinkingLevel = route.movedTo.effort;
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
return push(event);
|
|
454
|
+
};
|
|
455
|
+
}
|
|
456
|
+
return stream;
|
|
409
457
|
};
|
|
410
458
|
|
|
411
459
|
pi.registerProvider({
|
|
@@ -52,6 +52,8 @@ export interface StatusInput {
|
|
|
52
52
|
origin: string;
|
|
53
53
|
/** omnirush CLI version ("unknown" when not provided by the launcher). */
|
|
54
54
|
version: string;
|
|
55
|
+
/** The approval mode line (yolo-lib formatModeStatus), when known. */
|
|
56
|
+
yolo?: string | null;
|
|
55
57
|
}
|
|
56
58
|
|
|
57
59
|
/**
|
|
@@ -79,6 +81,7 @@ export function formatStatusLines(input: StatusInput): string[] {
|
|
|
79
81
|
}
|
|
80
82
|
|
|
81
83
|
lines.push(formatCollectorLine(input.collector));
|
|
84
|
+
if (input.yolo) lines.push(input.yolo);
|
|
82
85
|
lines.push(`manager: ${input.origin}`);
|
|
83
86
|
lines.push(`version: ${input.version}`);
|
|
84
87
|
return lines;
|