omnius 1.0.27 → 1.0.29
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 +6 -4
- package/dist/index.js +913 -436
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
- package/prompts/agentic/system-large.md +3 -2
package/dist/index.js
CHANGED
|
@@ -258096,50 +258096,91 @@ ${truncated}`, durationMs: performance.now() - start2 };
|
|
|
258096
258096
|
import { readFile as readFile17, writeFile as writeFile19, mkdir as mkdir14 } from "node:fs/promises";
|
|
258097
258097
|
import { resolve as resolve26, join as join46 } from "node:path";
|
|
258098
258098
|
import { randomBytes as randomBytes12 } from "node:crypto";
|
|
258099
|
+
function formatLocalDateTime(date) {
|
|
258100
|
+
return new Intl.DateTimeFormat("en-US", {
|
|
258101
|
+
weekday: "short",
|
|
258102
|
+
year: "numeric",
|
|
258103
|
+
month: "short",
|
|
258104
|
+
day: "numeric",
|
|
258105
|
+
hour: "numeric",
|
|
258106
|
+
minute: "2-digit",
|
|
258107
|
+
timeZoneName: "short"
|
|
258108
|
+
}).format(date);
|
|
258109
|
+
}
|
|
258110
|
+
function withAbsoluteDue(relative15, target) {
|
|
258111
|
+
return `${relative15} (${formatLocalDateTime(target)})`;
|
|
258112
|
+
}
|
|
258113
|
+
function parseRelativeDueTime(input, now) {
|
|
258114
|
+
const lower = input.toLowerCase().trim().replace(/[.!?]+$/g, "").replace(/\s+/g, " ");
|
|
258115
|
+
const match = lower.match(/^(?:in\s+|after\s+)?(\d+)\s*(m|min|mins|minutes?|h|hr|hrs|hours?|d|days?|w|wk|wks|weeks?)(?:\s+(?:from\s+now|later))?$/);
|
|
258116
|
+
if (!match)
|
|
258117
|
+
return null;
|
|
258118
|
+
const amount = parseInt(match[1], 10);
|
|
258119
|
+
if (!Number.isFinite(amount) || amount <= 0)
|
|
258120
|
+
return null;
|
|
258121
|
+
const unit = match[2].charAt(0);
|
|
258122
|
+
const target = new Date(now);
|
|
258123
|
+
if (unit === "m")
|
|
258124
|
+
target.setMinutes(target.getMinutes() + amount);
|
|
258125
|
+
else if (unit === "h")
|
|
258126
|
+
target.setHours(target.getHours() + amount);
|
|
258127
|
+
else if (unit === "d")
|
|
258128
|
+
target.setDate(target.getDate() + amount);
|
|
258129
|
+
else if (unit === "w")
|
|
258130
|
+
target.setDate(target.getDate() + amount * 7);
|
|
258131
|
+
return { target, description: `in ${amount}${unit}` };
|
|
258132
|
+
}
|
|
258099
258133
|
function parseDueTime(due) {
|
|
258100
258134
|
const lower = due.toLowerCase().trim();
|
|
258101
258135
|
const now = /* @__PURE__ */ new Date();
|
|
258102
|
-
const
|
|
258103
|
-
if (
|
|
258104
|
-
|
|
258105
|
-
|
|
258106
|
-
|
|
258107
|
-
|
|
258108
|
-
target.setMinutes(target.getMinutes() + amount);
|
|
258109
|
-
else if (unit === "h")
|
|
258110
|
-
target.setHours(target.getHours() + amount);
|
|
258111
|
-
else if (unit === "d")
|
|
258112
|
-
target.setDate(target.getDate() + amount);
|
|
258113
|
-
else if (unit === "w")
|
|
258114
|
-
target.setDate(target.getDate() + amount * 7);
|
|
258115
|
-
return { isoDate: target.toISOString(), description: `in ${amount}${unit}` };
|
|
258136
|
+
const relative15 = parseRelativeDueTime(lower, now);
|
|
258137
|
+
if (relative15) {
|
|
258138
|
+
return {
|
|
258139
|
+
isoDate: relative15.target.toISOString(),
|
|
258140
|
+
description: withAbsoluteDue(relative15.description, relative15.target)
|
|
258141
|
+
};
|
|
258116
258142
|
}
|
|
258117
258143
|
if (lower === "tomorrow") {
|
|
258118
258144
|
const target = new Date(now);
|
|
258119
258145
|
target.setDate(target.getDate() + 1);
|
|
258120
258146
|
target.setHours(9, 0, 0, 0);
|
|
258121
|
-
return { isoDate: target.toISOString(), description: "tomorrow morning" };
|
|
258147
|
+
return { isoDate: target.toISOString(), description: withAbsoluteDue("tomorrow morning", target) };
|
|
258148
|
+
}
|
|
258149
|
+
const tomorrowAtMatch = lower.match(/^tomorrow(?:\s+at)?\s+(\d{1,2}):(\d{2})$/);
|
|
258150
|
+
if (tomorrowAtMatch) {
|
|
258151
|
+
const hour2 = parseInt(tomorrowAtMatch[1], 10);
|
|
258152
|
+
const minute3 = parseInt(tomorrowAtMatch[2], 10);
|
|
258153
|
+
if (hour2 < 0 || hour2 > 23 || minute3 < 0 || minute3 > 59)
|
|
258154
|
+
return null;
|
|
258155
|
+
const target = new Date(now);
|
|
258156
|
+
target.setDate(target.getDate() + 1);
|
|
258157
|
+
target.setHours(hour2, minute3, 0, 0);
|
|
258158
|
+
return { isoDate: target.toISOString(), description: withAbsoluteDue(`tomorrow at ${String(hour2).padStart(2, "0")}:${String(minute3).padStart(2, "0")}`, target) };
|
|
258122
258159
|
}
|
|
258123
258160
|
if (lower === "next week") {
|
|
258124
258161
|
const target = new Date(now);
|
|
258125
258162
|
target.setDate(target.getDate() + 7);
|
|
258126
258163
|
target.setHours(9, 0, 0, 0);
|
|
258127
|
-
return { isoDate: target.toISOString(), description: "next week" };
|
|
258164
|
+
return { isoDate: target.toISOString(), description: withAbsoluteDue("next week", target) };
|
|
258128
258165
|
}
|
|
258129
258166
|
const todayMatch = lower.match(/^(?:today\s+)?at\s+(\d{1,2}):(\d{2})$/);
|
|
258130
258167
|
if (todayMatch) {
|
|
258168
|
+
const hour2 = parseInt(todayMatch[1], 10);
|
|
258169
|
+
const minute3 = parseInt(todayMatch[2], 10);
|
|
258170
|
+
if (hour2 < 0 || hour2 > 23 || minute3 < 0 || minute3 > 59)
|
|
258171
|
+
return null;
|
|
258131
258172
|
const target = new Date(now);
|
|
258132
|
-
target.setHours(
|
|
258173
|
+
target.setHours(hour2, minute3, 0, 0);
|
|
258133
258174
|
if (target <= now)
|
|
258134
258175
|
target.setDate(target.getDate() + 1);
|
|
258135
|
-
return { isoDate: target.toISOString(), description: `at ${
|
|
258176
|
+
return { isoDate: target.toISOString(), description: withAbsoluteDue(`at ${String(hour2).padStart(2, "0")}:${String(minute3).padStart(2, "0")}`, target) };
|
|
258136
258177
|
}
|
|
258137
258178
|
if (lower === "now" || lower === "next startup" || lower === "startup") {
|
|
258138
|
-
return { isoDate: now.toISOString(), description: "next startup" };
|
|
258179
|
+
return { isoDate: now.toISOString(), description: withAbsoluteDue(lower === "now" ? "now" : "next startup", now) };
|
|
258139
258180
|
}
|
|
258140
258181
|
const parsed = new Date(due);
|
|
258141
258182
|
if (!isNaN(parsed.getTime())) {
|
|
258142
|
-
return { isoDate: parsed.toISOString(), description: parsed
|
|
258183
|
+
return { isoDate: parsed.toISOString(), description: withAbsoluteDue("at requested time", parsed) };
|
|
258143
258184
|
}
|
|
258144
258185
|
return null;
|
|
258145
258186
|
}
|
|
@@ -258375,12 +258416,43 @@ async function saveReminderStore(workingDir, store2) {
|
|
|
258375
258416
|
store2.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
258376
258417
|
await writeFile19(storePath, JSON.stringify(store2, null, 2), "utf-8");
|
|
258377
258418
|
}
|
|
258419
|
+
function sanitizeReminderDeliveryText(input) {
|
|
258420
|
+
let text = input.trim();
|
|
258421
|
+
if (!text)
|
|
258422
|
+
return text;
|
|
258423
|
+
const metaPhrase = /\s*\b(?:you asked me to remind you|you asked to be reminded|you asked me for a reminder|i(?:'ll| will) remind you|i(?:'ll| will) ping you|setting (?:your|a) reminder)\b[^.!?\n]*(?:ago|from now|later|when due|at that time)?[.!?]?/gi;
|
|
258424
|
+
text = text.replace(metaPhrase, "");
|
|
258425
|
+
text = text.replace(/^\s*(?:remind\s+(?:me|us)\s+(?:to\s+)?|reminder\s*:\s*)/i, "");
|
|
258426
|
+
text = text.replace(/\s{2,}/g, " ").replace(/\s+([.!?,;:])/g, "$1").trim();
|
|
258427
|
+
return text;
|
|
258428
|
+
}
|
|
258429
|
+
function firstStringValue(...values) {
|
|
258430
|
+
for (const value2 of values) {
|
|
258431
|
+
const raw = String(value2 ?? "").trim();
|
|
258432
|
+
if (raw)
|
|
258433
|
+
return raw;
|
|
258434
|
+
}
|
|
258435
|
+
return "";
|
|
258436
|
+
}
|
|
258437
|
+
function getDueInput(args) {
|
|
258438
|
+
return firstStringValue(args["due_at"], args["dueAt"], args["do_at"], args["doAt"], args["due"]);
|
|
258439
|
+
}
|
|
258440
|
+
function missingDueError() {
|
|
258441
|
+
const now = /* @__PURE__ */ new Date();
|
|
258442
|
+
return [
|
|
258443
|
+
"due_at is required for reminder(action='set') unless repeat/recurrence is provided.",
|
|
258444
|
+
`Current tool time: ${formatLocalDateTime(now)} (${now.toISOString()}).`,
|
|
258445
|
+
DUE_TIME_FORMAT_HELP
|
|
258446
|
+
].join(" ");
|
|
258447
|
+
}
|
|
258378
258448
|
async function getDueReminders(workingDir, query = {}) {
|
|
258379
258449
|
const store2 = await loadReminderStore(workingDir);
|
|
258380
258450
|
const now = query.now ?? /* @__PURE__ */ new Date();
|
|
258381
258451
|
return store2.reminders.filter((r2) => {
|
|
258382
258452
|
if (r2.status !== "pending")
|
|
258383
258453
|
return false;
|
|
258454
|
+
if (!r2.dueAt)
|
|
258455
|
+
return false;
|
|
258384
258456
|
if (r2.dueAt && new Date(r2.dueAt) > now)
|
|
258385
258457
|
return false;
|
|
258386
258458
|
if (query.deliveryMode && query.deliveryMode !== "any" && (r2.delivery?.mode ?? "minimal") !== query.deliveryMode)
|
|
@@ -258430,6 +258502,20 @@ function normalizeDeliveryKind(value2) {
|
|
|
258430
258502
|
return "action";
|
|
258431
258503
|
return "minimal";
|
|
258432
258504
|
}
|
|
258505
|
+
function normalizeReminderAction(value2) {
|
|
258506
|
+
const raw = String(value2 ?? "list").trim().toLowerCase();
|
|
258507
|
+
if (raw === "create" || raw === "add" || raw === "new")
|
|
258508
|
+
return "set";
|
|
258509
|
+
if (raw === "show" || raw === "pending")
|
|
258510
|
+
return "list";
|
|
258511
|
+
if (raw === "done" || raw === "resolve" || raw === "resolved")
|
|
258512
|
+
return "complete";
|
|
258513
|
+
if (raw === "remove" || raw === "delete" || raw === "cancel")
|
|
258514
|
+
return "dismiss";
|
|
258515
|
+
if (raw === "set" || raw === "list" || raw === "complete" || raw === "dismiss" || raw === "snooze")
|
|
258516
|
+
return raw;
|
|
258517
|
+
return void 0;
|
|
258518
|
+
}
|
|
258433
258519
|
function normalizeSurface(value2) {
|
|
258434
258520
|
const raw = String(value2 ?? "").trim().toLowerCase();
|
|
258435
258521
|
return raw === "tui" || raw === "gui" || raw === "telegram" || raw === "api" || raw === "global" ? raw : void 0;
|
|
@@ -258446,29 +258532,46 @@ function formatScope(scope) {
|
|
|
258446
258532
|
const label = scope.label ? ` (${scope.label})` : "";
|
|
258447
258533
|
return `${scope.surface}/${scope.visibility}/${scope.targetId}${label}`;
|
|
258448
258534
|
}
|
|
258449
|
-
var STORE_FILE, ReminderTool;
|
|
258535
|
+
var DUE_TIME_FORMAT_HELP, STORE_FILE, ReminderTool;
|
|
258450
258536
|
var init_reminder = __esm({
|
|
258451
258537
|
"packages/execution/dist/tools/reminder.js"() {
|
|
258452
258538
|
"use strict";
|
|
258539
|
+
DUE_TIME_FORMAT_HELP = "due_at is required for action='set' unless repeat/recurrence is provided. Fill due_at from the user's requested trigger, not from your reply text. Examples: user says 'remind me in 5 minutes to get coffee' -> due_at='in 5 minutes', message/content='Get coffee'; user says 'tomorrow at 09:00' -> due_at='tomorrow at 09:00'; user says 'at 14:30' -> due_at='at 14:30'; user gives an exact timestamp -> due_at='2026-05-15T14:30:00-07:00'. Supported due_at formats: 'in 5 minutes', 'in 30m', '5m', 'tomorrow', 'tomorrow at 09:00', 'next week', 'today at 14:30', 'at 14:30', 'now', 'next startup', or ISO timestamp.";
|
|
258453
258540
|
STORE_FILE = "reminders.json";
|
|
258454
258541
|
ReminderTool = class {
|
|
258455
258542
|
name = "reminder";
|
|
258456
|
-
description = "Set scoped reminders and time-triggered action requests. Use kind='minimal' for raw text/content delivery and kind='action' when the trigger should start inference/tool-calling from a task_description. Actions: 'set' a new reminder, 'list' all reminders, 'complete' or 'dismiss' a reminder, 'snooze' to postpone. Reminders can have due dates, recurrence, priority, tags, and per-platform scopes.";
|
|
258543
|
+
description = "Set scoped reminders and time-triggered action requests. For action='set', you MUST provide due_at/do_at/due (camelCase aliases dueAt/doAt accepted) unless repeat/recurrence is provided; missing triggers fail instead of defaulting to next startup. " + DUE_TIME_FORMAT_HELP + " Use kind='minimal' for raw text/content delivery and kind='action' when the trigger should start inference/tool-calling from a task_description. Actions: 'set' a new reminder, 'list' all reminders, 'complete' or 'dismiss' a reminder, 'snooze' to postpone. Reminders can have due dates, recurrence, priority, tags, and per-platform scopes.";
|
|
258457
258544
|
parameters = {
|
|
258458
258545
|
type: "object",
|
|
258459
258546
|
properties: {
|
|
258460
258547
|
action: {
|
|
258461
258548
|
type: "string",
|
|
258462
|
-
enum: ["set", "list", "complete", "dismiss", "snooze"],
|
|
258463
|
-
description: "Reminder action to perform"
|
|
258549
|
+
enum: ["set", "create", "add", "list", "complete", "dismiss", "snooze"],
|
|
258550
|
+
description: "Reminder action to perform. create/add are aliases for set."
|
|
258464
258551
|
},
|
|
258465
258552
|
message: {
|
|
258466
258553
|
type: "string",
|
|
258467
|
-
description: "The reminder
|
|
258554
|
+
description: "The reminder payload only. Do not include scheduling prose like 'you asked me to remind you in 5 minutes'."
|
|
258555
|
+
},
|
|
258556
|
+
due_at: {
|
|
258557
|
+
type: "string",
|
|
258558
|
+
description: DUE_TIME_FORMAT_HELP
|
|
258559
|
+
},
|
|
258560
|
+
dueAt: {
|
|
258561
|
+
type: "string",
|
|
258562
|
+
description: "CamelCase alias for due_at. Prefer due_at in new tool calls."
|
|
258563
|
+
},
|
|
258564
|
+
do_at: {
|
|
258565
|
+
type: "string",
|
|
258566
|
+
description: "Alias for due_at. Prefer due_at unless the caller already uses do_at."
|
|
258567
|
+
},
|
|
258568
|
+
doAt: {
|
|
258569
|
+
type: "string",
|
|
258570
|
+
description: "CamelCase alias for do_at/due_at. Prefer due_at in new tool calls."
|
|
258468
258571
|
},
|
|
258469
258572
|
due: {
|
|
258470
258573
|
type: "string",
|
|
258471
|
-
description: "
|
|
258574
|
+
description: "Backward-compatible alias for due_at. Prefer due_at. " + DUE_TIME_FORMAT_HELP
|
|
258472
258575
|
},
|
|
258473
258576
|
priority: {
|
|
258474
258577
|
type: "string",
|
|
@@ -258486,7 +258589,7 @@ var init_reminder = __esm({
|
|
|
258486
258589
|
},
|
|
258487
258590
|
content: {
|
|
258488
258591
|
type: "string",
|
|
258489
|
-
description: "For kind='minimal': raw text/content to deliver. Defaults to message."
|
|
258592
|
+
description: "For kind='minimal': raw text/content to deliver. Defaults to message. Must be payload only, with no due-time explanation."
|
|
258490
258593
|
},
|
|
258491
258594
|
repeat: {
|
|
258492
258595
|
type: "string",
|
|
@@ -258553,7 +258656,7 @@ var init_reminder = __esm({
|
|
|
258553
258656
|
}
|
|
258554
258657
|
async execute(args) {
|
|
258555
258658
|
const start2 = performance.now();
|
|
258556
|
-
const action =
|
|
258659
|
+
const action = normalizeReminderAction(args["action"]);
|
|
258557
258660
|
try {
|
|
258558
258661
|
switch (action) {
|
|
258559
258662
|
case "set":
|
|
@@ -258570,7 +258673,7 @@ var init_reminder = __esm({
|
|
|
258570
258673
|
return {
|
|
258571
258674
|
success: false,
|
|
258572
258675
|
output: "",
|
|
258573
|
-
error: `Unknown action '${action}'. Use: set, list, complete, dismiss, snooze`,
|
|
258676
|
+
error: `Unknown action '${String(args["action"] ?? "list")}'. Use: set/create/add, list, complete, dismiss, snooze`,
|
|
258574
258677
|
durationMs: performance.now() - start2
|
|
258575
258678
|
};
|
|
258576
258679
|
}
|
|
@@ -258584,14 +258687,15 @@ var init_reminder = __esm({
|
|
|
258584
258687
|
}
|
|
258585
258688
|
}
|
|
258586
258689
|
async setReminder(args, start2) {
|
|
258587
|
-
const
|
|
258588
|
-
if (!
|
|
258690
|
+
const rawMessage = String(args["message"] ?? "");
|
|
258691
|
+
if (!rawMessage.trim())
|
|
258589
258692
|
return { success: false, output: "", error: "message is required for set action", durationMs: performance.now() - start2 };
|
|
258693
|
+
const message2 = sanitizeReminderDeliveryText(rawMessage) || rawMessage.trim();
|
|
258590
258694
|
const priority = args["priority"] ?? "normal";
|
|
258591
258695
|
const tagsStr = args["tags"] ?? "";
|
|
258592
258696
|
const tags = tagsStr ? tagsStr.split(",").map((t2) => t2.trim()).filter(Boolean) : [];
|
|
258593
258697
|
const context2 = args["context"] ?? void 0;
|
|
258594
|
-
const dueStr = args
|
|
258698
|
+
const dueStr = getDueInput(args);
|
|
258595
258699
|
const recurrence = parseRecurrence(args);
|
|
258596
258700
|
if (recurrence === null) {
|
|
258597
258701
|
return {
|
|
@@ -258627,7 +258731,7 @@ var init_reminder = __esm({
|
|
|
258627
258731
|
return {
|
|
258628
258732
|
success: false,
|
|
258629
258733
|
output: "",
|
|
258630
|
-
error: `Cannot parse
|
|
258734
|
+
error: `Cannot parse due_at '${dueStr}'. ${DUE_TIME_FORMAT_HELP}`,
|
|
258631
258735
|
durationMs: performance.now() - start2
|
|
258632
258736
|
};
|
|
258633
258737
|
}
|
|
@@ -258636,9 +258740,16 @@ var init_reminder = __esm({
|
|
|
258636
258740
|
} else if (recurrence) {
|
|
258637
258741
|
dueAt = nextDueFromRecurrence(recurrence).toISOString();
|
|
258638
258742
|
dueDescription = describeRecurrence(recurrence);
|
|
258743
|
+
} else {
|
|
258744
|
+
return {
|
|
258745
|
+
success: false,
|
|
258746
|
+
output: "",
|
|
258747
|
+
error: missingDueError(),
|
|
258748
|
+
durationMs: performance.now() - start2
|
|
258749
|
+
};
|
|
258639
258750
|
}
|
|
258640
258751
|
const scope = this.resolveScope(args);
|
|
258641
|
-
const content = String(args["content"] ?? message2)
|
|
258752
|
+
const content = sanitizeReminderDeliveryText(String(args["content"] ?? message2)) || message2;
|
|
258642
258753
|
const id = `rem-${randomBytes12(4).toString("hex")}`;
|
|
258643
258754
|
const entry = {
|
|
258644
258755
|
id,
|
|
@@ -258671,7 +258782,7 @@ var init_reminder = __esm({
|
|
|
258671
258782
|
` Priority: ${priority}`,
|
|
258672
258783
|
` Kind: ${kind}`,
|
|
258673
258784
|
kind === "action" ? ` Task: ${taskDescription}` : "",
|
|
258674
|
-
|
|
258785
|
+
` Due: ${dueDescription}`,
|
|
258675
258786
|
describeRecurrence(recurrence) ? ` Repeats: ${describeRecurrence(recurrence)}` : "",
|
|
258676
258787
|
scope ? ` Scope: ${formatScope(scope)}` : "",
|
|
258677
258788
|
tags.length > 0 ? ` Tags: ${tags.join(", ")}` : "",
|
|
@@ -258693,7 +258804,7 @@ var init_reminder = __esm({
|
|
|
258693
258804
|
if (filtered.length === 0) {
|
|
258694
258805
|
return {
|
|
258695
258806
|
success: true,
|
|
258696
|
-
output: filter2 === "all" ? "No reminders set. Use reminder(action='set', message='...') to create one." : `No ${filter2} reminders. Use reminder(action='list', filter='all') to see all.`,
|
|
258807
|
+
output: filter2 === "all" ? "No reminders set. Use reminder(action='set', message='...', due_at='in 30m') to create one." : `No ${filter2} reminders. Use reminder(action='list', filter='all') to see all.`,
|
|
258697
258808
|
durationMs: performance.now() - start2
|
|
258698
258809
|
};
|
|
258699
258810
|
}
|
|
@@ -258744,7 +258855,7 @@ var init_reminder = __esm({
|
|
|
258744
258855
|
}
|
|
258745
258856
|
async snoozeReminder(args, start2) {
|
|
258746
258857
|
const id = String(args["id"] ?? "");
|
|
258747
|
-
const due =
|
|
258858
|
+
const due = getDueInput(args) || "in 1h";
|
|
258748
258859
|
if (!id)
|
|
258749
258860
|
return { success: false, output: "", error: "id is required for snooze", durationMs: performance.now() - start2 };
|
|
258750
258861
|
const parsed = parseDueTime(due);
|
|
@@ -517144,6 +517255,7 @@ __export(dist_exports, {
|
|
|
517144
517255
|
runTests: () => runTests,
|
|
517145
517256
|
runTypecheck: () => runTypecheck,
|
|
517146
517257
|
runValidationPipeline: () => runValidationPipeline,
|
|
517258
|
+
sanitizeReminderDeliveryText: () => sanitizeReminderDeliveryText,
|
|
517147
517259
|
saveCustomToolDefinition: () => saveCustomToolDefinition,
|
|
517148
517260
|
saveMcpServerToConfig: () => saveMcpServerToConfig,
|
|
517149
517261
|
savePacket: () => savePacket,
|
|
@@ -554193,6 +554305,47 @@ function getAgentToolCommandPolicy(command) {
|
|
|
554193
554305
|
reason: `/${cmd.name} is allowed by registry policy.`
|
|
554194
554306
|
};
|
|
554195
554307
|
}
|
|
554308
|
+
function getSelfModifyCommandPolicy(command) {
|
|
554309
|
+
const name10 = normalizeCommandName(command.split(/\s+/)[0] ?? command);
|
|
554310
|
+
const cmd = findCommand(name10);
|
|
554311
|
+
if (!cmd) {
|
|
554312
|
+
return {
|
|
554313
|
+
allowed: false,
|
|
554314
|
+
name: name10,
|
|
554315
|
+
reason: "Self-modify mode can only invoke registered Omnius slash commands."
|
|
554316
|
+
};
|
|
554317
|
+
}
|
|
554318
|
+
if (cmd.implementationStatus !== "implemented") {
|
|
554319
|
+
return {
|
|
554320
|
+
allowed: false,
|
|
554321
|
+
name: cmd.name,
|
|
554322
|
+
command: cmd,
|
|
554323
|
+
reason: `/${cmd.name} is ${cmd.implementationStatus}, not runnable by self-modify mode.`
|
|
554324
|
+
};
|
|
554325
|
+
}
|
|
554326
|
+
if (cmd.safety.secretBearing) {
|
|
554327
|
+
return {
|
|
554328
|
+
allowed: false,
|
|
554329
|
+
name: cmd.name,
|
|
554330
|
+
command: cmd,
|
|
554331
|
+
reason: `/${cmd.name} handles secrets and remains user-only.`
|
|
554332
|
+
};
|
|
554333
|
+
}
|
|
554334
|
+
if (!SELF_MODIFY_ALLOWED.has(cmd.name)) {
|
|
554335
|
+
return {
|
|
554336
|
+
allowed: false,
|
|
554337
|
+
name: cmd.name,
|
|
554338
|
+
command: cmd,
|
|
554339
|
+
reason: `/${cmd.name} is outside the self-modify command surface.`
|
|
554340
|
+
};
|
|
554341
|
+
}
|
|
554342
|
+
return {
|
|
554343
|
+
allowed: true,
|
|
554344
|
+
name: cmd.name,
|
|
554345
|
+
command: cmd,
|
|
554346
|
+
reason: `/${cmd.name} is allowed by self-modify policy.`
|
|
554347
|
+
};
|
|
554348
|
+
}
|
|
554196
554349
|
function describeAgentToolCommandPolicy() {
|
|
554197
554350
|
const implemented = listCommandRegistry({ includePlanned: false });
|
|
554198
554351
|
const safe = implemented.filter((cmd) => cmd.surfaces.agentTool).map((cmd) => cmd.name).sort();
|
|
@@ -554203,6 +554356,16 @@ function describeAgentToolCommandPolicy() {
|
|
|
554203
554356
|
"Skill commands: /<skill-name> <args> are allowed when no registry entry exists."
|
|
554204
554357
|
].join(" ");
|
|
554205
554358
|
}
|
|
554359
|
+
function describeSelfModifyCommandPolicy() {
|
|
554360
|
+
const allowed = Array.from(SELF_MODIFY_ALLOWED).filter((name10) => {
|
|
554361
|
+
const cmd = findCommand(name10);
|
|
554362
|
+
return cmd?.implementationStatus === "implemented" && !cmd.safety.secretBearing;
|
|
554363
|
+
}).sort();
|
|
554364
|
+
return [
|
|
554365
|
+
`Self-modify commands: ${allowed.join(", ")}.`,
|
|
554366
|
+
"Secret-bearing, gateway, process-destroying, and unregistered commands remain blocked."
|
|
554367
|
+
].join(" ");
|
|
554368
|
+
}
|
|
554206
554369
|
function buildCommandDef(name10, signatures) {
|
|
554207
554370
|
const status = signatures.every((sig) => sig.implementationStatus === "planned") ? "planned" : "implemented";
|
|
554208
554371
|
const safety = {
|
|
@@ -554256,7 +554419,7 @@ function inferArgsHint(signature) {
|
|
|
554256
554419
|
function normalizeCommandName(name10) {
|
|
554257
554420
|
return name10.replace(/^\//, "").trim().toLowerCase();
|
|
554258
554421
|
}
|
|
554259
|
-
var COMMAND_SIGNATURES, PLANNED_SIGNATURES, CATEGORY_OVERRIDES, ALIASES, USER_ONLY, DESTRUCTIVE, NETWORKED, SECRET_BEARING, PROFILE_GATED, REST_BLOCKED, CANONICAL_BY_ALIAS, DYNAMIC_COMMANDS, SLASH_COMMANDS;
|
|
554422
|
+
var COMMAND_SIGNATURES, PLANNED_SIGNATURES, CATEGORY_OVERRIDES, ALIASES, USER_ONLY, DESTRUCTIVE, NETWORKED, SECRET_BEARING, PROFILE_GATED, SELF_MODIFY_ALLOWED, REST_BLOCKED, CANONICAL_BY_ALIAS, DYNAMIC_COMMANDS, SLASH_COMMANDS;
|
|
554260
554423
|
var init_command_registry = __esm({
|
|
554261
554424
|
"packages/cli/src/tui/command-registry.ts"() {
|
|
554262
554425
|
"use strict";
|
|
@@ -554280,6 +554443,9 @@ var init_command_registry = __esm({
|
|
|
554280
554443
|
["/upgrade", "Alias for /update"],
|
|
554281
554444
|
["/update auto", "Enable auto-update after task completion (default)"],
|
|
554282
554445
|
["/update manual", "Disable auto-update (only update on /update)"],
|
|
554446
|
+
["/update check", "Check for Omnius and Ollama updates without opening the menu"],
|
|
554447
|
+
["/update quick", "Install the latest Omnius package non-interactively"],
|
|
554448
|
+
["/update full", "Run full non-interactive update: package, deps, rebuild, Python, cloudflared"],
|
|
554283
554449
|
["/voice", "Toggle TTS voice feedback"],
|
|
554284
554450
|
["/voice <model>", "Set voice: glados, overwatch, kokoro, luxtts, supertonic"],
|
|
554285
554451
|
["/voice clone <file>", "Set voice clone reference audio (wav/mp3/ogg/flac)"],
|
|
@@ -554431,7 +554597,7 @@ var init_command_registry = __esm({
|
|
|
554431
554597
|
["/scheduler list", "List all scheduled tasks and timers"],
|
|
554432
554598
|
["/scheduler kill", "Kill schedulers + active runs (with escalation if needed)"],
|
|
554433
554599
|
["/reminder [list|all]", "List scoped TUI reminders"],
|
|
554434
|
-
["/remind <message>", "Set a scoped minimal reminder
|
|
554600
|
+
["/remind <when> <message>", "Set a scoped minimal reminder; a due time is required"],
|
|
554435
554601
|
["/remind in 30m <message>", "Set a scoped minimal reminder with a relative due time"],
|
|
554436
554602
|
["/reminders", "Alias for /reminder"],
|
|
554437
554603
|
["/daemon", "Show or manage the local API daemon"],
|
|
@@ -554467,6 +554633,10 @@ var init_command_registry = __esm({
|
|
|
554467
554633
|
["/commands auto", "Allow agent to invoke slash commands as tools"],
|
|
554468
554634
|
["/cmds auto", "Alias for /commands auto"],
|
|
554469
554635
|
["/commands manual", "Slash commands are user-only (default)"],
|
|
554636
|
+
["/selfmodify", "Toggle agent self-modify mode for natural-language runtime commands"],
|
|
554637
|
+
["/selfmodify on", "Allow the agent to decide when to invoke self-modifying slash commands"],
|
|
554638
|
+
["/selfmodify off", "Disable agent self-modifying slash-command access (default)"],
|
|
554639
|
+
["/selfmodify status", "Show current self-modify mode"],
|
|
554470
554640
|
["/voicechat", "Start voice chat session (async voice conversation)"],
|
|
554471
554641
|
["/voicechat stop", "Stop voice chat session"],
|
|
554472
554642
|
["/memory", "Toggle memory visualizer - graph/episodes/concepts/timeline"],
|
|
@@ -554588,7 +554758,10 @@ var init_command_registry = __esm({
|
|
|
554588
554758
|
skin: "ui",
|
|
554589
554759
|
prompt: "ui",
|
|
554590
554760
|
personality: "ui",
|
|
554591
|
-
reasoning: "ui"
|
|
554761
|
+
reasoning: "ui",
|
|
554762
|
+
selfmodify: "runtime",
|
|
554763
|
+
selfmod: "runtime",
|
|
554764
|
+
"self-modify": "runtime"
|
|
554592
554765
|
};
|
|
554593
554766
|
ALIASES = {
|
|
554594
554767
|
help: ["h", "?"],
|
|
@@ -554616,6 +554789,7 @@ var init_command_registry = __esm({
|
|
|
554616
554789
|
files: ["workspace"],
|
|
554617
554790
|
skills: ["skill"],
|
|
554618
554791
|
commands: ["cmds"],
|
|
554792
|
+
selfmodify: ["selfmod", "self-modify"],
|
|
554619
554793
|
codegraph: ["cg"],
|
|
554620
554794
|
endpoint: ["ep"],
|
|
554621
554795
|
telegram: ["tg"],
|
|
@@ -554642,6 +554816,9 @@ var init_command_registry = __esm({
|
|
|
554642
554816
|
"parallel",
|
|
554643
554817
|
"commands",
|
|
554644
554818
|
"cmds",
|
|
554819
|
+
"selfmodify",
|
|
554820
|
+
"selfmod",
|
|
554821
|
+
"self-modify",
|
|
554645
554822
|
"mcp",
|
|
554646
554823
|
"mcps",
|
|
554647
554824
|
"update",
|
|
@@ -554748,6 +554925,31 @@ var init_command_registry = __esm({
|
|
|
554748
554925
|
"deny",
|
|
554749
554926
|
"sethome"
|
|
554750
554927
|
]);
|
|
554928
|
+
SELF_MODIFY_ALLOWED = /* @__PURE__ */ new Set([
|
|
554929
|
+
"bruteforce",
|
|
554930
|
+
"color",
|
|
554931
|
+
"colors",
|
|
554932
|
+
"compact",
|
|
554933
|
+
"config",
|
|
554934
|
+
"deep",
|
|
554935
|
+
"emojis",
|
|
554936
|
+
"flow",
|
|
554937
|
+
"help",
|
|
554938
|
+
"model",
|
|
554939
|
+
"models",
|
|
554940
|
+
"personality",
|
|
554941
|
+
"score",
|
|
554942
|
+
"selfmodify",
|
|
554943
|
+
"stats",
|
|
554944
|
+
"stream",
|
|
554945
|
+
"style",
|
|
554946
|
+
"task-type",
|
|
554947
|
+
"theme",
|
|
554948
|
+
"think",
|
|
554949
|
+
"update",
|
|
554950
|
+
"upgrade",
|
|
554951
|
+
"verbose"
|
|
554952
|
+
]);
|
|
554751
554953
|
REST_BLOCKED = /* @__PURE__ */ new Set([
|
|
554752
554954
|
"pause",
|
|
554753
554955
|
"resume",
|
|
@@ -580637,11 +580839,16 @@ function createSlashReminderTool(ctx3) {
|
|
|
580637
580839
|
}
|
|
580638
580840
|
function splitReminderDue(raw) {
|
|
580639
580841
|
const text = raw.trim().replace(/^set\s+/i, "").trim();
|
|
580640
|
-
const
|
|
580641
|
-
|
|
580842
|
+
const duePattern = "(?:(?:in\\s+|after\\s+)?\\d+\\s*(?:m|min|mins|minutes?|h|hr|hrs|hours?|d|days?|w|wk|wks|weeks?)(?:\\s+(?:from\\s+now|later))?|tomorrow(?:\\s+at\\s+\\d{1,2}:\\d{2})?|next\\s+week|next\\s+startup|startup|now|(?:today\\s+)?at\\s+\\d{1,2}:\\d{2})";
|
|
580843
|
+
const prefixMatch = text.match(
|
|
580844
|
+
new RegExp(`^(${duePattern})\\s+(.+)$`, "i")
|
|
580642
580845
|
);
|
|
580643
|
-
if (
|
|
580644
|
-
|
|
580846
|
+
if (prefixMatch) return { due: prefixMatch[1].trim(), message: prefixMatch[2].trim() };
|
|
580847
|
+
const suffixMatch = text.match(
|
|
580848
|
+
new RegExp(`^(.+?)\\s+(${duePattern})$`, "i")
|
|
580849
|
+
);
|
|
580850
|
+
if (suffixMatch) return { due: suffixMatch[2].trim(), message: suffixMatch[1].trim() };
|
|
580851
|
+
return { message: text };
|
|
580645
580852
|
}
|
|
580646
580853
|
async function handleReminderSlash(cmd, arg, ctx3) {
|
|
580647
580854
|
const tool = createSlashReminderTool(ctx3);
|
|
@@ -580663,7 +580870,7 @@ async function handleReminderSlash(cmd, arg, ctx3) {
|
|
|
580663
580870
|
} else {
|
|
580664
580871
|
const { due, message: message2 } = splitReminderDue(trimmed);
|
|
580665
580872
|
if (!message2) {
|
|
580666
|
-
renderInfo("Usage: /remind
|
|
580873
|
+
renderInfo("Usage: /remind <when> <message> (examples: /remind in 30m check coffee, /remind tomorrow at 09:00 call mom)");
|
|
580667
580874
|
return "handled";
|
|
580668
580875
|
}
|
|
580669
580876
|
args = {
|
|
@@ -580671,7 +580878,7 @@ async function handleReminderSlash(cmd, arg, ctx3) {
|
|
|
580671
580878
|
kind: "minimal",
|
|
580672
580879
|
message: message2,
|
|
580673
580880
|
content: message2,
|
|
580674
|
-
...due ? { due } : {}
|
|
580881
|
+
...due ? { due_at: due } : {}
|
|
580675
580882
|
};
|
|
580676
580883
|
}
|
|
580677
580884
|
const result = await tool.execute(args);
|
|
@@ -581124,6 +581331,24 @@ async function handleSlashCommand(input, ctx3) {
|
|
|
581124
581331
|
);
|
|
581125
581332
|
return "handled";
|
|
581126
581333
|
}
|
|
581334
|
+
if (["system", "branding", "custom"].includes(colorAction)) {
|
|
581335
|
+
setThemeMode(colorAction);
|
|
581336
|
+
refreshThemeVars();
|
|
581337
|
+
try {
|
|
581338
|
+
const tasksRenderer = await Promise.resolve().then(() => (init_tui_tasks_renderer(), tui_tasks_renderer_exports));
|
|
581339
|
+
tasksRenderer.refreshTuiTasksThemeVars?.();
|
|
581340
|
+
tasksRenderer.refreshTuiTasks?.();
|
|
581341
|
+
} catch {
|
|
581342
|
+
}
|
|
581343
|
+
ctx3.refreshBanner?.();
|
|
581344
|
+
const settings = { colorTheme: colorAction };
|
|
581345
|
+
const save2 = hasLocal ? ctx3.saveLocalSettings.bind(ctx3) : ctx3.saveSettings.bind(ctx3);
|
|
581346
|
+
save2(settings);
|
|
581347
|
+
renderInfo(
|
|
581348
|
+
`Theme set to: ${colorAction}${hasLocal ? " (project-local)" : ""}`
|
|
581349
|
+
);
|
|
581350
|
+
return "handled";
|
|
581351
|
+
}
|
|
581127
581352
|
await showColorMenu(ctx3);
|
|
581128
581353
|
return "handled";
|
|
581129
581354
|
}
|
|
@@ -583691,6 +583916,39 @@ Clone a new voice: /voice clone <wav-file> [name]`);
|
|
|
583691
583916
|
}
|
|
583692
583917
|
return "handled";
|
|
583693
583918
|
}
|
|
583919
|
+
case "selfmodify":
|
|
583920
|
+
case "selfmod":
|
|
583921
|
+
case "self-modify": {
|
|
583922
|
+
if (!ctx3.setSelfModify) {
|
|
583923
|
+
renderWarning("Self-modify mode not available.");
|
|
583924
|
+
return "handled";
|
|
583925
|
+
}
|
|
583926
|
+
const normalized = arg.toLowerCase();
|
|
583927
|
+
const current = ctx3.getSelfModify?.() ?? false;
|
|
583928
|
+
let next = null;
|
|
583929
|
+
if (!normalized || normalized === "toggle") {
|
|
583930
|
+
next = !current;
|
|
583931
|
+
} else if (["on", "auto", "enable", "enabled", "true", "yes"].includes(normalized)) {
|
|
583932
|
+
next = true;
|
|
583933
|
+
} else if (["off", "manual", "disable", "disabled", "false", "no"].includes(normalized)) {
|
|
583934
|
+
next = false;
|
|
583935
|
+
} else if (normalized === "status") {
|
|
583936
|
+
renderInfo(
|
|
583937
|
+
`Self-modify mode: ${current ? "on" : "off"} — ` + (current ? "agent may decide to invoke permitted slash commands for natural-language runtime/UI requests." : "slash commands stay user-directed unless /commands auto is enabled.")
|
|
583938
|
+
);
|
|
583939
|
+
return "handled";
|
|
583940
|
+
} else {
|
|
583941
|
+
renderWarning("Usage: /selfmodify [on|off|status]");
|
|
583942
|
+
return "handled";
|
|
583943
|
+
}
|
|
583944
|
+
ctx3.setSelfModify(next);
|
|
583945
|
+
const save2 = hasLocal ? ctx3.saveLocalSettings.bind(ctx3) : ctx3.saveSettings.bind(ctx3);
|
|
583946
|
+
save2({ selfModify: next });
|
|
583947
|
+
renderInfo(
|
|
583948
|
+
next ? "Self-modify mode: on — the agent can decide when to invoke permitted slash commands for natural-language runtime/UI requests." : "Self-modify mode: off — natural-language requests will not grant slash-command self-modification."
|
|
583949
|
+
);
|
|
583950
|
+
return "handled";
|
|
583951
|
+
}
|
|
583694
583952
|
case "neovim":
|
|
583695
583953
|
case "nvim":
|
|
583696
583954
|
case "vim": {
|
|
@@ -586349,6 +586607,14 @@ async function showConfigEditor(ctx3) {
|
|
|
586349
586607
|
options: ["manual", "auto"],
|
|
586350
586608
|
settingsKey: "commandsMode"
|
|
586351
586609
|
},
|
|
586610
|
+
{
|
|
586611
|
+
key: "selfModify",
|
|
586612
|
+
label: "selfModify",
|
|
586613
|
+
kind: "boolean",
|
|
586614
|
+
value: String(ctx3.getSelfModify?.() ?? merged.selfModify ?? false),
|
|
586615
|
+
detail: String(ctx3.getSelfModify?.() ?? merged.selfModify ?? false),
|
|
586616
|
+
settingsKey: "selfModify"
|
|
586617
|
+
},
|
|
586352
586618
|
{
|
|
586353
586619
|
key: "updateMode",
|
|
586354
586620
|
label: "updateMode",
|
|
@@ -586527,6 +586793,8 @@ function applyConfigChanges(ctx3, changes) {
|
|
|
586527
586793
|
ctx3.setStyle?.(changes.style);
|
|
586528
586794
|
if (changes.commandsMode !== void 0)
|
|
586529
586795
|
ctx3.setCommandsMode?.(changes.commandsMode);
|
|
586796
|
+
if (changes.selfModify !== void 0)
|
|
586797
|
+
ctx3.setSelfModify?.(changes.selfModify);
|
|
586530
586798
|
}
|
|
586531
586799
|
async function handlePlatformsCommand(ctx3, arg) {
|
|
586532
586800
|
const parts = arg.trim().split(/\s+/).filter(Boolean);
|
|
@@ -590866,6 +591134,29 @@ async function handleUpdate(subcommand, ctx3) {
|
|
|
590866
591134
|
}
|
|
590867
591135
|
} catch {
|
|
590868
591136
|
}
|
|
591137
|
+
const updateActionAliases = {
|
|
591138
|
+
quick: "quick",
|
|
591139
|
+
package: "quick",
|
|
591140
|
+
pkg: "quick",
|
|
591141
|
+
full: "full",
|
|
591142
|
+
deps: "deps_only",
|
|
591143
|
+
"deps-only": "deps_only",
|
|
591144
|
+
deps_only: "deps_only",
|
|
591145
|
+
dependencies: "deps_only",
|
|
591146
|
+
rebuild: "rebuild",
|
|
591147
|
+
native: "rebuild",
|
|
591148
|
+
python: "python"
|
|
591149
|
+
};
|
|
591150
|
+
const requestedAction = updateActionAliases[subcommand] ?? "";
|
|
591151
|
+
if (["check", "status", "ls", "list"].includes(subcommand)) {
|
|
591152
|
+
return;
|
|
591153
|
+
}
|
|
591154
|
+
if (subcommand && !requestedAction) {
|
|
591155
|
+
renderWarning(
|
|
591156
|
+
"Usage: /update [check|quick|full|deps|rebuild|python|auto|manual|ollama]"
|
|
591157
|
+
);
|
|
591158
|
+
return;
|
|
591159
|
+
}
|
|
590869
591160
|
const items = [];
|
|
590870
591161
|
const skipKeys = [];
|
|
590871
591162
|
items.push({
|
|
@@ -590926,69 +591217,71 @@ async function handleUpdate(subcommand, ctx3) {
|
|
|
590926
591217
|
detail: "Upgrade venv packages (moondream, tesseract, etc.)",
|
|
590927
591218
|
kind: "action"
|
|
590928
591219
|
});
|
|
590929
|
-
|
|
590930
|
-
|
|
590931
|
-
|
|
590932
|
-
|
|
590933
|
-
|
|
590934
|
-
|
|
590935
|
-
|
|
590936
|
-
|
|
590937
|
-
|
|
590938
|
-
|
|
590939
|
-
|
|
590940
|
-
|
|
590941
|
-
|
|
590942
|
-
|
|
590943
|
-
|
|
590944
|
-
|
|
590945
|
-
|
|
590946
|
-
|
|
590947
|
-
|
|
590948
|
-
|
|
590949
|
-
|
|
590950
|
-
|
|
590951
|
-
|
|
590952
|
-
|
|
590953
|
-
|
|
590954
|
-
|
|
590955
|
-
|
|
590956
|
-
|
|
590957
|
-
|
|
590958
|
-
|
|
590959
|
-
|
|
590960
|
-
|
|
590961
|
-
|
|
590962
|
-
|
|
590963
|
-
|
|
590964
|
-
|
|
590965
|
-
|
|
590966
|
-
|
|
590967
|
-
|
|
590968
|
-
|
|
591220
|
+
if (!requestedAction) {
|
|
591221
|
+
items.push({
|
|
591222
|
+
key: "hdr_deps",
|
|
591223
|
+
label: selectColors.dim("─── Dependencies ───"),
|
|
591224
|
+
kind: "header"
|
|
591225
|
+
});
|
|
591226
|
+
skipKeys.push("hdr_deps");
|
|
591227
|
+
try {
|
|
591228
|
+
const prefix = await execA("npm prefix -g", { timeout: 5e3 });
|
|
591229
|
+
const { join: pj2 } = await import("node:path");
|
|
591230
|
+
const { existsSync: fe2, readFileSync: rf2 } = await import("node:fs");
|
|
591231
|
+
const globalModules = prefix.endsWith("/lib") ? prefix + "/node_modules" : prefix + "/lib/node_modules";
|
|
591232
|
+
const pkgPath2 = pj2(globalModules, "omnius", "package.json");
|
|
591233
|
+
if (fe2(pkgPath2)) {
|
|
591234
|
+
const pkg2 = JSON.parse(rf2(pkgPath2, "utf8"));
|
|
591235
|
+
const allDeps2 = {
|
|
591236
|
+
...pkg2.dependencies || {},
|
|
591237
|
+
...pkg2.optionalDependencies || {}
|
|
591238
|
+
};
|
|
591239
|
+
const depChecks = await Promise.all(
|
|
591240
|
+
Object.entries(allDeps2).map(async ([name10, range]) => {
|
|
591241
|
+
let installed = "";
|
|
591242
|
+
const localPath = pj2(
|
|
591243
|
+
globalModules,
|
|
591244
|
+
"omnius",
|
|
591245
|
+
"node_modules",
|
|
591246
|
+
name10,
|
|
591247
|
+
"package.json"
|
|
591248
|
+
);
|
|
591249
|
+
const hoistedPath = pj2(globalModules, name10, "package.json");
|
|
591250
|
+
try {
|
|
591251
|
+
if (fe2(localPath))
|
|
591252
|
+
installed = JSON.parse(rf2(localPath, "utf8")).version || "";
|
|
591253
|
+
else if (fe2(hoistedPath))
|
|
591254
|
+
installed = JSON.parse(rf2(hoistedPath, "utf8")).version || "";
|
|
591255
|
+
} catch {
|
|
591256
|
+
}
|
|
591257
|
+
let latest = "";
|
|
591258
|
+
try {
|
|
591259
|
+
latest = await execA(`npm view ${name10} version`, { timeout: 5e3 });
|
|
591260
|
+
} catch {
|
|
591261
|
+
}
|
|
591262
|
+
const outdated = installed && latest && latest !== installed;
|
|
591263
|
+
return { name: name10, range: String(range), installed, latest, outdated };
|
|
591264
|
+
})
|
|
591265
|
+
);
|
|
591266
|
+
for (const dep of depChecks) {
|
|
591267
|
+
const verStr = dep.installed || "missing";
|
|
591268
|
+
const updateTag = dep.outdated ? ` ${c3.bold(c3.yellow("→ " + dep.latest))}` : "";
|
|
591269
|
+
const label = ` ${dep.name} ${c3.dim(verStr)}${updateTag}`;
|
|
591270
|
+
if (dep.outdated) {
|
|
591271
|
+
items.push({
|
|
591272
|
+
key: `dep_${dep.name}`,
|
|
591273
|
+
label,
|
|
591274
|
+
detail: `Update ${dep.name} ${dep.installed} → ${dep.latest}`,
|
|
591275
|
+
kind: "action"
|
|
591276
|
+
});
|
|
591277
|
+
} else {
|
|
591278
|
+
items.push({ key: `dep_${dep.name}`, label, kind: "info" });
|
|
591279
|
+
skipKeys.push(`dep_${dep.name}`);
|
|
590969
591280
|
}
|
|
590970
|
-
const outdated = installed && latest && latest !== installed;
|
|
590971
|
-
return { name: name10, range: String(range), installed, latest, outdated };
|
|
590972
|
-
})
|
|
590973
|
-
);
|
|
590974
|
-
for (const dep of depChecks) {
|
|
590975
|
-
const verStr = dep.installed || "missing";
|
|
590976
|
-
const updateTag = dep.outdated ? ` ${c3.bold(c3.yellow("→ " + dep.latest))}` : "";
|
|
590977
|
-
const label = ` ${dep.name} ${c3.dim(verStr)}${updateTag}`;
|
|
590978
|
-
if (dep.outdated) {
|
|
590979
|
-
items.push({
|
|
590980
|
-
key: `dep_${dep.name}`,
|
|
590981
|
-
label,
|
|
590982
|
-
detail: `Update ${dep.name} ${dep.installed} → ${dep.latest}`,
|
|
590983
|
-
kind: "action"
|
|
590984
|
-
});
|
|
590985
|
-
} else {
|
|
590986
|
-
items.push({ key: `dep_${dep.name}`, label, kind: "info" });
|
|
590987
|
-
skipKeys.push(`dep_${dep.name}`);
|
|
590988
591281
|
}
|
|
590989
591282
|
}
|
|
591283
|
+
} catch {
|
|
590990
591284
|
}
|
|
590991
|
-
} catch {
|
|
590992
591285
|
}
|
|
590993
591286
|
if (ollamaUpdate?.needsUpdate) {
|
|
590994
591287
|
items.push({
|
|
@@ -591022,7 +591315,7 @@ async function handleUpdate(subcommand, ctx3) {
|
|
|
591022
591315
|
detail: "Only update when you run /update",
|
|
591023
591316
|
kind: "action"
|
|
591024
591317
|
});
|
|
591025
|
-
const menuResult = await tuiSelect({
|
|
591318
|
+
const menuResult = requestedAction ? { confirmed: true, key: requestedAction } : await tuiSelect({
|
|
591026
591319
|
items,
|
|
591027
591320
|
title: "Update Manager",
|
|
591028
591321
|
rl: ctx3.rl,
|
|
@@ -599175,11 +599468,16 @@ function renderTelegramLiveProgressHTML(progressLines, accumulated) {
|
|
|
599175
599468
|
}
|
|
599176
599469
|
function splitTelegramReminderDue(raw) {
|
|
599177
599470
|
const text = raw.trim().replace(/^set\s+/i, "").trim();
|
|
599178
|
-
const
|
|
599179
|
-
|
|
599471
|
+
const duePattern = "(?:(?:in\\s+|after\\s+)?\\d+\\s*(?:m|min|mins|minutes?|h|hr|hrs|hours?|d|days?|w|wk|wks|weeks?)(?:\\s+(?:from\\s+now|later))?|tomorrow(?:\\s+at\\s+\\d{1,2}:\\d{2})?|next\\s+week|next\\s+startup|startup|now|(?:today\\s+)?at\\s+\\d{1,2}:\\d{2})";
|
|
599472
|
+
const prefixMatch = text.match(
|
|
599473
|
+
new RegExp(`^(${duePattern})\\s+(.+)$`, "i")
|
|
599474
|
+
);
|
|
599475
|
+
if (prefixMatch) return { due: prefixMatch[1].trim(), message: prefixMatch[2].trim() };
|
|
599476
|
+
const suffixMatch = text.match(
|
|
599477
|
+
new RegExp(`^(.+?)\\s+(${duePattern})$`, "i")
|
|
599180
599478
|
);
|
|
599181
|
-
if (
|
|
599182
|
-
return {
|
|
599479
|
+
if (suffixMatch) return { due: suffixMatch[2].trim(), message: suffixMatch[1].trim() };
|
|
599480
|
+
return { message: text };
|
|
599183
599481
|
}
|
|
599184
599482
|
function telegramSyntheticHelpSignatures() {
|
|
599185
599483
|
return [
|
|
@@ -600093,7 +600391,7 @@ Telegram response contract:
|
|
|
600093
600391
|
} else {
|
|
600094
600392
|
const { due, message: message2 } = splitTelegramReminderDue(arg);
|
|
600095
600393
|
if (!message2) {
|
|
600096
|
-
await this.replyToTelegramMessage(msg, "Usage: /remind
|
|
600394
|
+
await this.replyToTelegramMessage(msg, "Usage: /remind <when> <message> (examples: /remind in 30m check coffee, /remind tomorrow at 09:00 call mom)");
|
|
600097
600395
|
return;
|
|
600098
600396
|
}
|
|
600099
600397
|
args = {
|
|
@@ -600101,7 +600399,7 @@ Telegram response contract:
|
|
|
600101
600399
|
kind: "minimal",
|
|
600102
600400
|
message: message2,
|
|
600103
600401
|
content: message2,
|
|
600104
|
-
...due ? { due } : {}
|
|
600402
|
+
...due ? { due_at: due } : {}
|
|
600105
600403
|
};
|
|
600106
600404
|
}
|
|
600107
600405
|
const result = await tool.execute(args);
|
|
@@ -601743,6 +602041,14 @@ ${mediaContext}` : ""}`
|
|
|
601743
602041
|
const sessionContext = this.buildTelegramSessionContext(msg, ctx3, profile, modelTier);
|
|
601744
602042
|
const contextWindowSize = this.contextWindowSize;
|
|
601745
602043
|
const runnerStateDir = isAdminDM ? void 0 : this.telegramRunnerStateDir(sessionContext.sessionKey);
|
|
602044
|
+
const runtimeContext = buildTelegramRuntimeContext(/* @__PURE__ */ new Date(), isAdminDM ? repoRoot : void 0);
|
|
602045
|
+
const reminderToolContract = [
|
|
602046
|
+
"Reminder tool contract:",
|
|
602047
|
+
"- For reminder/remind action='set', always pass due_at (or do_at/due alias) from the user's time phrase unless repeat/recurrence is present.",
|
|
602048
|
+
"- Do not call reminder set without due_at; the tool will fail instead of defaulting to next startup.",
|
|
602049
|
+
"- Keep message/content as the reminder payload only. Never include prose like 'you asked me to remind you in 5 minutes' in message/content.",
|
|
602050
|
+
"- Examples: 'remind me in 5 minutes to get coffee' -> due_at='in 5 minutes', message='Get coffee', content='Get coffee'. 'tomorrow at 09:00 call mom' -> due_at='tomorrow at 09:00', message='Call mom'."
|
|
602051
|
+
].join("\n");
|
|
601746
602052
|
const backend = new OllamaAgenticBackend(
|
|
601747
602053
|
config.backendUrl,
|
|
601748
602054
|
config.model,
|
|
@@ -601884,6 +602190,8 @@ ${GROUP_REPLY_DISCRETION_PROMPT}` : "";
|
|
|
601884
602190
|
|
|
601885
602191
|
${TELEGRAM_ACTION_RESPONSE_CONTRACT}
|
|
601886
602192
|
|
|
602193
|
+
${reminderToolContract}
|
|
602194
|
+
|
|
601887
602195
|
${profileLine}
|
|
601888
602196
|
|
|
601889
602197
|
Telegram message from admin @${msg.username}:
|
|
@@ -601893,6 +602201,7 @@ ${msg.text}`;
|
|
|
601893
602201
|
"You have access to isolated per-chat memory (memory_write, memory_read, memory_search) scoped to this conversation.",
|
|
601894
602202
|
"memory_search may use scope=group/current_chat for this group or scope=user with user_id/username for a participant in this same group. Other groups, admin chats, and private DMs are not accessible here.",
|
|
601895
602203
|
"You can remember facts about users and retrieve them later. You also have web_search and web_fetch to look up information.",
|
|
602204
|
+
reminderToolContract,
|
|
601896
602205
|
"If the user asks you to create or send a file, image, or audio artifact, create it with the scoped creative tools and call telegram_send_file to upload it. The bridge still auto-attaches generated files as a fallback when tool results record them.",
|
|
601897
602206
|
"For image generation requests, decide from the conversation whether generate_image is appropriate; do not ask the user to use a hardcoded shortcut when the request is clear.",
|
|
601898
602207
|
creativeWorkspace
|
|
@@ -601914,10 +602223,11 @@ Respond concisely and safely. Send the actual chat reply, not router/status/comp
|
|
|
601914
602223
|
[Media attached — processed content below]
|
|
601915
602224
|
${mediaContext}`;
|
|
601916
602225
|
}
|
|
601917
|
-
const systemCtx = isAdminDM ?
|
|
602226
|
+
const systemCtx = isAdminDM ? `${runtimeContext}
|
|
601918
602227
|
Telegram admin: @${msg.username}
|
|
601919
602228
|
Telegram profile: ${profile}
|
|
601920
|
-
Todo/session id: ${sessionContext.sessionId}` :
|
|
602229
|
+
Todo/session id: ${sessionContext.sessionId}` : `${runtimeContext}
|
|
602230
|
+
Telegram ${isGroup ? "group" : "public"} chat. Respond concisely. Safety filter: ACTIVE.${creativeWorkspace ? `
|
|
601921
602231
|
|
|
601922
602232
|
${creativeWorkspace}` : ""}`;
|
|
601923
602233
|
const result = await runner.run(userPrompt, systemCtx);
|
|
@@ -603891,6 +604201,237 @@ var init_chat_session = __esm({
|
|
|
603891
604201
|
}
|
|
603892
604202
|
});
|
|
603893
604203
|
|
|
604204
|
+
// packages/cli/src/api/command-passthrough.ts
|
|
604205
|
+
var command_passthrough_exports = {};
|
|
604206
|
+
__export(command_passthrough_exports, {
|
|
604207
|
+
runCommand: () => runCommand
|
|
604208
|
+
});
|
|
604209
|
+
function stripAnsi5(s2) {
|
|
604210
|
+
return s2.replace(/\x1B(?:\[[\d;?]*[a-zA-Z]|\][^\x07\x1B]*[\x07\x1B]?|[@-Z\\-_])/g, "");
|
|
604211
|
+
}
|
|
604212
|
+
async function runCommand(input, opts) {
|
|
604213
|
+
const start2 = Date.now();
|
|
604214
|
+
const trimmed = input.trim();
|
|
604215
|
+
const slashCmd = trimmed.startsWith("/") ? trimmed : "/" + trimmed;
|
|
604216
|
+
const [rawCmd, ...rest] = slashCmd.slice(1).split(/\s+/);
|
|
604217
|
+
const cmdName = rawCmd ?? "";
|
|
604218
|
+
const argsStr = rest.join(" ");
|
|
604219
|
+
const release = await acquireLock2();
|
|
604220
|
+
try {
|
|
604221
|
+
const quick2 = buildNonInteractiveSummary(cmdName, argsStr, opts?.config);
|
|
604222
|
+
if (quick2) {
|
|
604223
|
+
return {
|
|
604224
|
+
ok: true,
|
|
604225
|
+
command: cmdName,
|
|
604226
|
+
args: argsStr,
|
|
604227
|
+
kind: "handled",
|
|
604228
|
+
output: quick2,
|
|
604229
|
+
ansi: quick2,
|
|
604230
|
+
durationMs: Date.now() - start2
|
|
604231
|
+
};
|
|
604232
|
+
}
|
|
604233
|
+
const buf = [];
|
|
604234
|
+
setContentWriteHook({
|
|
604235
|
+
begin: () => {
|
|
604236
|
+
},
|
|
604237
|
+
end: () => {
|
|
604238
|
+
},
|
|
604239
|
+
redirect: () => (text) => {
|
|
604240
|
+
buf.push(text);
|
|
604241
|
+
}
|
|
604242
|
+
});
|
|
604243
|
+
const origWrite = process.stdout.write.bind(process.stdout);
|
|
604244
|
+
process.stdout.write = function(chunk, ...rest2) {
|
|
604245
|
+
if (typeof chunk === "string") buf.push(chunk);
|
|
604246
|
+
else if (chunk instanceof Buffer) buf.push(chunk.toString("utf-8"));
|
|
604247
|
+
const cb = rest2.find((r2) => typeof r2 === "function");
|
|
604248
|
+
if (cb) cb();
|
|
604249
|
+
return true;
|
|
604250
|
+
};
|
|
604251
|
+
let kind = "handled";
|
|
604252
|
+
let errMsg;
|
|
604253
|
+
try {
|
|
604254
|
+
const ctx3 = buildSyntheticContext(opts?.config, opts?.repoRoot);
|
|
604255
|
+
kind = await handleSlashCommand(slashCmd, ctx3);
|
|
604256
|
+
} catch (e2) {
|
|
604257
|
+
kind = "error";
|
|
604258
|
+
errMsg = e2 instanceof Error ? e2.message : String(e2);
|
|
604259
|
+
buf.push(`
|
|
604260
|
+
[error] ${errMsg}
|
|
604261
|
+
`);
|
|
604262
|
+
} finally {
|
|
604263
|
+
process.stdout.write = origWrite;
|
|
604264
|
+
setContentWriteHook({ begin: () => {
|
|
604265
|
+
}, end: () => {
|
|
604266
|
+
} });
|
|
604267
|
+
}
|
|
604268
|
+
const ansi5 = buf.join("");
|
|
604269
|
+
return {
|
|
604270
|
+
ok: kind !== "error" && kind !== "not_a_command",
|
|
604271
|
+
command: cmdName,
|
|
604272
|
+
args: argsStr,
|
|
604273
|
+
kind,
|
|
604274
|
+
output: stripAnsi5(ansi5).trim(),
|
|
604275
|
+
ansi: ansi5,
|
|
604276
|
+
durationMs: Date.now() - start2,
|
|
604277
|
+
error: errMsg
|
|
604278
|
+
};
|
|
604279
|
+
} finally {
|
|
604280
|
+
release();
|
|
604281
|
+
}
|
|
604282
|
+
}
|
|
604283
|
+
function buildNonInteractiveSummary(cmdName, _args, config) {
|
|
604284
|
+
const cfg = config ?? loadConfig();
|
|
604285
|
+
if (cmdName === "setup" || cmdName === "wizard") {
|
|
604286
|
+
return [
|
|
604287
|
+
"omnius setup",
|
|
604288
|
+
"",
|
|
604289
|
+
"The setup wizard is an interactive terminal flow. In the GUI command bridge it is summarized instead of opening prompts, installing software, starting Ollama, or pulling models.",
|
|
604290
|
+
"",
|
|
604291
|
+
`Current backend: ${cfg.backendType ?? "ollama"}`,
|
|
604292
|
+
`Current endpoint: ${cfg.backendUrl ?? "http://127.0.0.1:11434"}`,
|
|
604293
|
+
`Current model: ${cfg.model ?? "qwen3.5:latest"}`,
|
|
604294
|
+
"",
|
|
604295
|
+
"Available non-interactive setup actions:",
|
|
604296
|
+
" /endpoint <url> Set or inspect the inference endpoint.",
|
|
604297
|
+
" /model <name> Set the active model directly.",
|
|
604298
|
+
" /models Show model-selection guidance.",
|
|
604299
|
+
" /config Inspect persisted configuration.",
|
|
604300
|
+
" /doctor Run diagnostics from the terminal if deeper repair is needed.",
|
|
604301
|
+
"",
|
|
604302
|
+
"Open a terminal and run `omnius`, then use /setup for the full guided wizard."
|
|
604303
|
+
].join("\n");
|
|
604304
|
+
}
|
|
604305
|
+
if (cmdName === "models") {
|
|
604306
|
+
return [
|
|
604307
|
+
"omnius models",
|
|
604308
|
+
"",
|
|
604309
|
+
"The model picker is interactive in the TUI. The GUI bridge does not probe remote model endpoints here, so it cannot hang on a stale backend.",
|
|
604310
|
+
"",
|
|
604311
|
+
`Active model: ${cfg.model ?? "qwen3.5:latest"}`,
|
|
604312
|
+
`Endpoint: ${cfg.backendUrl ?? "http://127.0.0.1:11434"}`,
|
|
604313
|
+
`Backend: ${cfg.backendType ?? "ollama"}`,
|
|
604314
|
+
"",
|
|
604315
|
+
"Use /model <name> to set a model directly, /endpoint to switch providers, or open the TUI for the searchable model picker."
|
|
604316
|
+
].join("\n");
|
|
604317
|
+
}
|
|
604318
|
+
return null;
|
|
604319
|
+
}
|
|
604320
|
+
function acquireLock2() {
|
|
604321
|
+
let release;
|
|
604322
|
+
const next = new Promise((res) => {
|
|
604323
|
+
release = res;
|
|
604324
|
+
});
|
|
604325
|
+
const prev = _passthroughLock;
|
|
604326
|
+
_passthroughLock = next;
|
|
604327
|
+
return prev.then(() => release);
|
|
604328
|
+
}
|
|
604329
|
+
function buildSyntheticContext(config, repoRoot) {
|
|
604330
|
+
const cfg = config ?? loadConfig();
|
|
604331
|
+
const root = repoRoot ?? process.cwd();
|
|
604332
|
+
let colorsEnabled = loadProjectSettings(root).colors ?? loadGlobalSettings().colors ?? true;
|
|
604333
|
+
let selfModifyEnabled = loadProjectSettings(root).selfModify ?? loadGlobalSettings().selfModify ?? false;
|
|
604334
|
+
return {
|
|
604335
|
+
config: cfg,
|
|
604336
|
+
repoRoot: root,
|
|
604337
|
+
rl: makeRejectingReadline(),
|
|
604338
|
+
setModel: (_model) => {
|
|
604339
|
+
},
|
|
604340
|
+
setVerbose: (_verbose) => {
|
|
604341
|
+
},
|
|
604342
|
+
setEndpoint: (_url, _t, _k) => {
|
|
604343
|
+
},
|
|
604344
|
+
deactivateStatusBar: () => {
|
|
604345
|
+
},
|
|
604346
|
+
disableMouse: () => {
|
|
604347
|
+
},
|
|
604348
|
+
enableMouse: () => {
|
|
604349
|
+
},
|
|
604350
|
+
isMouseEnabled: () => false,
|
|
604351
|
+
lockFooter: () => {
|
|
604352
|
+
},
|
|
604353
|
+
unlockFooter: () => {
|
|
604354
|
+
},
|
|
604355
|
+
stopBanner: () => {
|
|
604356
|
+
},
|
|
604357
|
+
killEphemeral: () => {
|
|
604358
|
+
},
|
|
604359
|
+
setKeyPool: (_keys) => {
|
|
604360
|
+
},
|
|
604361
|
+
clearScreen: () => {
|
|
604362
|
+
},
|
|
604363
|
+
newSession: () => {
|
|
604364
|
+
},
|
|
604365
|
+
refreshBanner: () => {
|
|
604366
|
+
},
|
|
604367
|
+
exit: () => {
|
|
604368
|
+
renderError("/quit and /exit are TUI-only — close the browser tab to end the GUI session.");
|
|
604369
|
+
},
|
|
604370
|
+
voiceToggle: async () => {
|
|
604371
|
+
renderError(`voice ${TUI_ONLY_HINT} — use the GUI voice button instead.`);
|
|
604372
|
+
return "voice not available in GUI";
|
|
604373
|
+
},
|
|
604374
|
+
voiceSetModel: async (_id2) => {
|
|
604375
|
+
renderError(`voice model ${TUI_ONLY_HINT}`);
|
|
604376
|
+
return "";
|
|
604377
|
+
},
|
|
604378
|
+
getColors: () => colorsEnabled,
|
|
604379
|
+
setColors: (enabled2) => {
|
|
604380
|
+
colorsEnabled = enabled2;
|
|
604381
|
+
},
|
|
604382
|
+
getSelfModify: () => selfModifyEnabled,
|
|
604383
|
+
setSelfModify: (enabled2) => {
|
|
604384
|
+
selfModifyEnabled = enabled2;
|
|
604385
|
+
},
|
|
604386
|
+
saveSettings: (settings) => {
|
|
604387
|
+
saveProjectSettings(root, settings);
|
|
604388
|
+
saveGlobalSettings(settings);
|
|
604389
|
+
},
|
|
604390
|
+
saveLocalSettings: (settings) => {
|
|
604391
|
+
saveProjectSettings(root, settings);
|
|
604392
|
+
}
|
|
604393
|
+
};
|
|
604394
|
+
}
|
|
604395
|
+
function makeRejectingReadline() {
|
|
604396
|
+
const reject = () => {
|
|
604397
|
+
throw new Error(
|
|
604398
|
+
"interactive prompts are not supported via the GUI command bridge — run this command from the TUI (open a terminal and type `omnius`)."
|
|
604399
|
+
);
|
|
604400
|
+
};
|
|
604401
|
+
const noop2 = () => {
|
|
604402
|
+
};
|
|
604403
|
+
return {
|
|
604404
|
+
question: (_q, _cb) => reject(),
|
|
604405
|
+
close: noop2,
|
|
604406
|
+
write: noop2,
|
|
604407
|
+
on: () => noop2,
|
|
604408
|
+
once: () => noop2,
|
|
604409
|
+
off: () => noop2,
|
|
604410
|
+
removeListener: () => noop2,
|
|
604411
|
+
pause: noop2,
|
|
604412
|
+
resume: noop2,
|
|
604413
|
+
setPrompt: noop2,
|
|
604414
|
+
prompt: noop2,
|
|
604415
|
+
line: "",
|
|
604416
|
+
cursor: 0,
|
|
604417
|
+
terminal: false,
|
|
604418
|
+
input: { isTTY: false, on: noop2, once: noop2, removeListener: noop2, pause: noop2, resume: noop2 },
|
|
604419
|
+
output: { write: noop2, columns: 80, rows: 24, isTTY: false }
|
|
604420
|
+
};
|
|
604421
|
+
}
|
|
604422
|
+
var _passthroughLock, TUI_ONLY_HINT;
|
|
604423
|
+
var init_command_passthrough = __esm({
|
|
604424
|
+
"packages/cli/src/api/command-passthrough.ts"() {
|
|
604425
|
+
"use strict";
|
|
604426
|
+
init_render();
|
|
604427
|
+
init_commands();
|
|
604428
|
+
init_config();
|
|
604429
|
+
init_omnius_directory();
|
|
604430
|
+
_passthroughLock = Promise.resolve();
|
|
604431
|
+
TUI_ONLY_HINT = "(this command is TUI-only — no-op in GUI)";
|
|
604432
|
+
}
|
|
604433
|
+
});
|
|
604434
|
+
|
|
603894
604435
|
// packages/cli/src/api/projects.ts
|
|
603895
604436
|
var projects_exports = {};
|
|
603896
604437
|
__export(projects_exports, {
|
|
@@ -607208,220 +607749,6 @@ var init_graphical_sudo = __esm({
|
|
|
607208
607749
|
}
|
|
607209
607750
|
});
|
|
607210
607751
|
|
|
607211
|
-
// packages/cli/src/api/command-passthrough.ts
|
|
607212
|
-
var command_passthrough_exports = {};
|
|
607213
|
-
__export(command_passthrough_exports, {
|
|
607214
|
-
runCommand: () => runCommand
|
|
607215
|
-
});
|
|
607216
|
-
function stripAnsi5(s2) {
|
|
607217
|
-
return s2.replace(/\x1B(?:\[[\d;?]*[a-zA-Z]|\][^\x07\x1B]*[\x07\x1B]?|[@-Z\\-_])/g, "");
|
|
607218
|
-
}
|
|
607219
|
-
async function runCommand(input, opts) {
|
|
607220
|
-
const start2 = Date.now();
|
|
607221
|
-
const trimmed = input.trim();
|
|
607222
|
-
const slashCmd = trimmed.startsWith("/") ? trimmed : "/" + trimmed;
|
|
607223
|
-
const [rawCmd, ...rest] = slashCmd.slice(1).split(/\s+/);
|
|
607224
|
-
const cmdName = rawCmd ?? "";
|
|
607225
|
-
const argsStr = rest.join(" ");
|
|
607226
|
-
const release = await acquireLock2();
|
|
607227
|
-
try {
|
|
607228
|
-
const quick2 = buildNonInteractiveSummary(cmdName, argsStr, opts?.config);
|
|
607229
|
-
if (quick2) {
|
|
607230
|
-
return {
|
|
607231
|
-
ok: true,
|
|
607232
|
-
command: cmdName,
|
|
607233
|
-
args: argsStr,
|
|
607234
|
-
kind: "handled",
|
|
607235
|
-
output: quick2,
|
|
607236
|
-
ansi: quick2,
|
|
607237
|
-
durationMs: Date.now() - start2
|
|
607238
|
-
};
|
|
607239
|
-
}
|
|
607240
|
-
const buf = [];
|
|
607241
|
-
setContentWriteHook({
|
|
607242
|
-
begin: () => {
|
|
607243
|
-
},
|
|
607244
|
-
end: () => {
|
|
607245
|
-
},
|
|
607246
|
-
redirect: () => (text) => {
|
|
607247
|
-
buf.push(text);
|
|
607248
|
-
}
|
|
607249
|
-
});
|
|
607250
|
-
const origWrite = process.stdout.write.bind(process.stdout);
|
|
607251
|
-
process.stdout.write = function(chunk, ...rest2) {
|
|
607252
|
-
if (typeof chunk === "string") buf.push(chunk);
|
|
607253
|
-
else if (chunk instanceof Buffer) buf.push(chunk.toString("utf-8"));
|
|
607254
|
-
const cb = rest2.find((r2) => typeof r2 === "function");
|
|
607255
|
-
if (cb) cb();
|
|
607256
|
-
return true;
|
|
607257
|
-
};
|
|
607258
|
-
let kind = "handled";
|
|
607259
|
-
let errMsg;
|
|
607260
|
-
try {
|
|
607261
|
-
const ctx3 = buildSyntheticContext(opts?.config, opts?.repoRoot);
|
|
607262
|
-
kind = await handleSlashCommand(slashCmd, ctx3);
|
|
607263
|
-
} catch (e2) {
|
|
607264
|
-
kind = "error";
|
|
607265
|
-
errMsg = e2 instanceof Error ? e2.message : String(e2);
|
|
607266
|
-
buf.push(`
|
|
607267
|
-
[error] ${errMsg}
|
|
607268
|
-
`);
|
|
607269
|
-
} finally {
|
|
607270
|
-
process.stdout.write = origWrite;
|
|
607271
|
-
setContentWriteHook({ begin: () => {
|
|
607272
|
-
}, end: () => {
|
|
607273
|
-
} });
|
|
607274
|
-
}
|
|
607275
|
-
const ansi5 = buf.join("");
|
|
607276
|
-
return {
|
|
607277
|
-
ok: kind !== "error" && kind !== "not_a_command",
|
|
607278
|
-
command: cmdName,
|
|
607279
|
-
args: argsStr,
|
|
607280
|
-
kind,
|
|
607281
|
-
output: stripAnsi5(ansi5).trim(),
|
|
607282
|
-
ansi: ansi5,
|
|
607283
|
-
durationMs: Date.now() - start2,
|
|
607284
|
-
error: errMsg
|
|
607285
|
-
};
|
|
607286
|
-
} finally {
|
|
607287
|
-
release();
|
|
607288
|
-
}
|
|
607289
|
-
}
|
|
607290
|
-
function buildNonInteractiveSummary(cmdName, _args, config) {
|
|
607291
|
-
const cfg = config ?? loadConfig();
|
|
607292
|
-
if (cmdName === "setup" || cmdName === "wizard") {
|
|
607293
|
-
return [
|
|
607294
|
-
"omnius setup",
|
|
607295
|
-
"",
|
|
607296
|
-
"The setup wizard is an interactive terminal flow. In the GUI command bridge it is summarized instead of opening prompts, installing software, starting Ollama, or pulling models.",
|
|
607297
|
-
"",
|
|
607298
|
-
`Current backend: ${cfg.backendType ?? "ollama"}`,
|
|
607299
|
-
`Current endpoint: ${cfg.backendUrl ?? "http://127.0.0.1:11434"}`,
|
|
607300
|
-
`Current model: ${cfg.model ?? "qwen3.5:latest"}`,
|
|
607301
|
-
"",
|
|
607302
|
-
"Available non-interactive setup actions:",
|
|
607303
|
-
" /endpoint <url> Set or inspect the inference endpoint.",
|
|
607304
|
-
" /model <name> Set the active model directly.",
|
|
607305
|
-
" /models Show model-selection guidance.",
|
|
607306
|
-
" /config Inspect persisted configuration.",
|
|
607307
|
-
" /doctor Run diagnostics from the terminal if deeper repair is needed.",
|
|
607308
|
-
"",
|
|
607309
|
-
"Open a terminal and run `omnius`, then use /setup for the full guided wizard."
|
|
607310
|
-
].join("\n");
|
|
607311
|
-
}
|
|
607312
|
-
if (cmdName === "models") {
|
|
607313
|
-
return [
|
|
607314
|
-
"omnius models",
|
|
607315
|
-
"",
|
|
607316
|
-
"The model picker is interactive in the TUI. The GUI bridge does not probe remote model endpoints here, so it cannot hang on a stale backend.",
|
|
607317
|
-
"",
|
|
607318
|
-
`Active model: ${cfg.model ?? "qwen3.5:latest"}`,
|
|
607319
|
-
`Endpoint: ${cfg.backendUrl ?? "http://127.0.0.1:11434"}`,
|
|
607320
|
-
`Backend: ${cfg.backendType ?? "ollama"}`,
|
|
607321
|
-
"",
|
|
607322
|
-
"Use /model <name> to set a model directly, /endpoint to switch providers, or open the TUI for the searchable model picker."
|
|
607323
|
-
].join("\n");
|
|
607324
|
-
}
|
|
607325
|
-
return null;
|
|
607326
|
-
}
|
|
607327
|
-
function acquireLock2() {
|
|
607328
|
-
let release;
|
|
607329
|
-
const next = new Promise((res) => {
|
|
607330
|
-
release = res;
|
|
607331
|
-
});
|
|
607332
|
-
const prev = _passthroughLock;
|
|
607333
|
-
_passthroughLock = next;
|
|
607334
|
-
return prev.then(() => release);
|
|
607335
|
-
}
|
|
607336
|
-
function buildSyntheticContext(config, repoRoot) {
|
|
607337
|
-
const cfg = config ?? loadConfig();
|
|
607338
|
-
return {
|
|
607339
|
-
config: cfg,
|
|
607340
|
-
repoRoot: repoRoot ?? process.cwd(),
|
|
607341
|
-
rl: makeRejectingReadline(),
|
|
607342
|
-
setModel: (_model) => {
|
|
607343
|
-
},
|
|
607344
|
-
setVerbose: (_verbose) => {
|
|
607345
|
-
},
|
|
607346
|
-
setEndpoint: (_url, _t, _k) => {
|
|
607347
|
-
},
|
|
607348
|
-
deactivateStatusBar: () => {
|
|
607349
|
-
},
|
|
607350
|
-
disableMouse: () => {
|
|
607351
|
-
},
|
|
607352
|
-
enableMouse: () => {
|
|
607353
|
-
},
|
|
607354
|
-
isMouseEnabled: () => false,
|
|
607355
|
-
lockFooter: () => {
|
|
607356
|
-
},
|
|
607357
|
-
unlockFooter: () => {
|
|
607358
|
-
},
|
|
607359
|
-
stopBanner: () => {
|
|
607360
|
-
},
|
|
607361
|
-
killEphemeral: () => {
|
|
607362
|
-
},
|
|
607363
|
-
setKeyPool: (_keys) => {
|
|
607364
|
-
},
|
|
607365
|
-
clearScreen: () => {
|
|
607366
|
-
},
|
|
607367
|
-
newSession: () => {
|
|
607368
|
-
},
|
|
607369
|
-
refreshBanner: () => {
|
|
607370
|
-
},
|
|
607371
|
-
exit: () => {
|
|
607372
|
-
renderError("/quit and /exit are TUI-only — close the browser tab to end the GUI session.");
|
|
607373
|
-
},
|
|
607374
|
-
voiceToggle: async () => {
|
|
607375
|
-
renderError(`voice ${TUI_ONLY_HINT} — use the GUI voice button instead.`);
|
|
607376
|
-
return "voice not available in GUI";
|
|
607377
|
-
},
|
|
607378
|
-
voiceSetModel: async (_id2) => {
|
|
607379
|
-
renderError(`voice model ${TUI_ONLY_HINT}`);
|
|
607380
|
-
return "";
|
|
607381
|
-
},
|
|
607382
|
-
saveSettings: (_settings) => {
|
|
607383
|
-
}
|
|
607384
|
-
};
|
|
607385
|
-
}
|
|
607386
|
-
function makeRejectingReadline() {
|
|
607387
|
-
const reject = () => {
|
|
607388
|
-
throw new Error(
|
|
607389
|
-
"interactive prompts are not supported via the GUI command bridge — run this command from the TUI (open a terminal and type `omnius`)."
|
|
607390
|
-
);
|
|
607391
|
-
};
|
|
607392
|
-
const noop2 = () => {
|
|
607393
|
-
};
|
|
607394
|
-
return {
|
|
607395
|
-
question: (_q, _cb) => reject(),
|
|
607396
|
-
close: noop2,
|
|
607397
|
-
write: noop2,
|
|
607398
|
-
on: () => noop2,
|
|
607399
|
-
once: () => noop2,
|
|
607400
|
-
off: () => noop2,
|
|
607401
|
-
removeListener: () => noop2,
|
|
607402
|
-
pause: noop2,
|
|
607403
|
-
resume: noop2,
|
|
607404
|
-
setPrompt: noop2,
|
|
607405
|
-
prompt: noop2,
|
|
607406
|
-
line: "",
|
|
607407
|
-
cursor: 0,
|
|
607408
|
-
terminal: false,
|
|
607409
|
-
input: { isTTY: false, on: noop2, once: noop2, removeListener: noop2, pause: noop2, resume: noop2 },
|
|
607410
|
-
output: { write: noop2, columns: 80, rows: 24, isTTY: false }
|
|
607411
|
-
};
|
|
607412
|
-
}
|
|
607413
|
-
var _passthroughLock, TUI_ONLY_HINT;
|
|
607414
|
-
var init_command_passthrough = __esm({
|
|
607415
|
-
"packages/cli/src/api/command-passthrough.ts"() {
|
|
607416
|
-
"use strict";
|
|
607417
|
-
init_render();
|
|
607418
|
-
init_commands();
|
|
607419
|
-
init_config();
|
|
607420
|
-
_passthroughLock = Promise.resolve();
|
|
607421
|
-
TUI_ONLY_HINT = "(this command is TUI-only — no-op in GUI)";
|
|
607422
|
-
}
|
|
607423
|
-
});
|
|
607424
|
-
|
|
607425
607752
|
// packages/cli/src/api/routes-v1.ts
|
|
607426
607753
|
import { existsSync as existsSync116, readFileSync as readFileSync95, readdirSync as readdirSync39, statSync as statSync40 } from "node:fs";
|
|
607427
607754
|
import { join as join130, resolve as pathResolve2 } from "node:path";
|
|
@@ -610964,7 +611291,7 @@ body { display:flex; flex-direction:column; height:100vh; margin:0; overflow:hid
|
|
|
610964
611291
|
color: var(--color-fg);
|
|
610965
611292
|
}
|
|
610966
611293
|
.msg.assistant::before {
|
|
610967
|
-
content: '
|
|
611294
|
+
content: 'O' !important;
|
|
610968
611295
|
position: absolute;
|
|
610969
611296
|
top: 14px;
|
|
610970
611297
|
left: 0;
|
|
@@ -612301,6 +612628,36 @@ const $sidebarWidth = writable(260);
|
|
|
612301
612628
|
const $config = writable(null); // {endpoint, model, ...}
|
|
612302
612629
|
const $activeTab = writable('chat');
|
|
612303
612630
|
|
|
612631
|
+
function parseInitialGuiRoute() {
|
|
612632
|
+
const path = (location.pathname || '/').replace(//+$/, '') || '/';
|
|
612633
|
+
const pathToTab = {
|
|
612634
|
+
'/chat': 'chat',
|
|
612635
|
+
'/agent': 'agent',
|
|
612636
|
+
'/voice': 'voice',
|
|
612637
|
+
'/projects': 'projects',
|
|
612638
|
+
'/dashboard': 'jobs',
|
|
612639
|
+
'/jobs': 'jobs',
|
|
612640
|
+
'/activity': 'activity',
|
|
612641
|
+
'/settings': 'config',
|
|
612642
|
+
'/config': 'config',
|
|
612643
|
+
};
|
|
612644
|
+
const tab = pathToTab[path] || null;
|
|
612645
|
+
let chatSession = null;
|
|
612646
|
+
if (tab === 'chat') {
|
|
612647
|
+
const qs = location.search.replace(/^?/, '').trim();
|
|
612648
|
+
if (qs) {
|
|
612649
|
+
try {
|
|
612650
|
+
const params = new URLSearchParams(location.search);
|
|
612651
|
+
chatSession = params.get('session') || params.get('id') || params.get('s') || decodeURIComponent(qs.split('&')[0] || '');
|
|
612652
|
+
} catch {
|
|
612653
|
+
chatSession = qs;
|
|
612654
|
+
}
|
|
612655
|
+
}
|
|
612656
|
+
}
|
|
612657
|
+
return { tab, chatSession };
|
|
612658
|
+
}
|
|
612659
|
+
const initialGuiRoute = parseInitialGuiRoute();
|
|
612660
|
+
|
|
612304
612661
|
// Global writes (not project-scoped)
|
|
612305
612662
|
$apiKey.subscribe(v => {
|
|
612306
612663
|
try { v ? localStorage.setItem('omnius-api-key', v) : localStorage.removeItem('omnius-api-key'); } catch {}
|
|
@@ -612350,11 +612707,12 @@ $currentProject.subscribe(async (proj) => {
|
|
|
612350
612707
|
// Server hydrate (overrides local on first load — server is cross-device truth)
|
|
612351
612708
|
const server = await loadServerPrefs();
|
|
612352
612709
|
$selectedModel.set(server?.selectedModel ?? localModel);
|
|
612353
|
-
$chatSessionId.set(server?.currentChatSessionId
|
|
612710
|
+
$chatSessionId.set(initialGuiRoute.chatSession || server?.currentChatSessionId || localChat || null);
|
|
612354
612711
|
$agentRunId.set(server?.currentAgentRunId ?? (localAgent || null));
|
|
612355
612712
|
$sidebarWidth.set(server?.sidebarWidth ?? localWidth);
|
|
612356
612713
|
if (server?.theme && server.theme !== $theme.get()) $theme.set(server.theme);
|
|
612357
|
-
if (
|
|
612714
|
+
if (initialGuiRoute.tab) $activeTab.set(initialGuiRoute.tab);
|
|
612715
|
+
else if (server?.activeTab) $activeTab.set(server.activeTab);
|
|
612358
612716
|
else if (localTab) $activeTab.set(localTab);
|
|
612359
612717
|
});
|
|
612360
612718
|
|
|
@@ -612657,6 +613015,42 @@ function persistSelectedModel() {
|
|
|
612657
613015
|
}).catch(() => { /* offline / read-only — store still saved */ });
|
|
612658
613016
|
}
|
|
612659
613017
|
|
|
613018
|
+
function appendAssistantActions(div, textProvider) {
|
|
613019
|
+
const actions = document.createElement('div');
|
|
613020
|
+
actions.className = 'msg-actions';
|
|
613021
|
+
const mkBtn = (label, title, fn) => {
|
|
613022
|
+
const b = document.createElement('button');
|
|
613023
|
+
b.textContent = label;
|
|
613024
|
+
b.title = title;
|
|
613025
|
+
b.onclick = fn;
|
|
613026
|
+
return b;
|
|
613027
|
+
};
|
|
613028
|
+
actions.appendChild(mkBtn('copy', 'Copy message', () => {
|
|
613029
|
+
const txt = (typeof textProvider === 'function' ? textProvider() : '') || '';
|
|
613030
|
+
try { navigator.clipboard.writeText(txt); } catch {}
|
|
613031
|
+
const first = actions.querySelector('button');
|
|
613032
|
+
if (!first) return;
|
|
613033
|
+
const orig = first.textContent;
|
|
613034
|
+
first.textContent = 'copied';
|
|
613035
|
+
setTimeout(() => { first.textContent = orig; }, 1500);
|
|
613036
|
+
}));
|
|
613037
|
+
actions.appendChild(mkBtn('regen', 'Regenerate this response', () => {
|
|
613038
|
+
try { regenerateAssistantMessage(div); } catch (e) { console.warn('regen failed', e); }
|
|
613039
|
+
}));
|
|
613040
|
+
actions.appendChild(mkBtn('del', 'Delete this message', () => {
|
|
613041
|
+
try { deleteMessageDiv(div); } catch (e) { console.warn('del failed', e); }
|
|
613042
|
+
}));
|
|
613043
|
+
div.appendChild(actions);
|
|
613044
|
+
return actions;
|
|
613045
|
+
}
|
|
613046
|
+
|
|
613047
|
+
function removeAssistantActions(div) {
|
|
613048
|
+
if (!div) return;
|
|
613049
|
+
Array.from(div.children || []).forEach(ch => {
|
|
613050
|
+
if (ch && ch.classList && ch.classList.contains('msg-actions')) ch.remove();
|
|
613051
|
+
});
|
|
613052
|
+
}
|
|
613053
|
+
|
|
612660
613054
|
function addMessage(role, content) {
|
|
612661
613055
|
const div = document.createElement('div');
|
|
612662
613056
|
div.className = 'msg ' + role;
|
|
@@ -612678,32 +613072,7 @@ function addMessage(role, content) {
|
|
|
612678
613072
|
}
|
|
612679
613073
|
// OWUI-3: rich per-message action row on assistant messages.
|
|
612680
613074
|
if (role === 'assistant') {
|
|
612681
|
-
|
|
612682
|
-
actions.className = 'msg-actions';
|
|
612683
|
-
const mkBtn = (label, title, fn) => {
|
|
612684
|
-
const b = document.createElement('button');
|
|
612685
|
-
b.textContent = label;
|
|
612686
|
-
b.title = title;
|
|
612687
|
-
b.onclick = fn;
|
|
612688
|
-
return b;
|
|
612689
|
-
};
|
|
612690
|
-
actions.appendChild(mkBtn('copy', 'Copy message', () => {
|
|
612691
|
-
// Copy what's actually visible (post-render text), not the raw arg —
|
|
612692
|
-
// the closure-captured "content" may be empty when this message was
|
|
612693
|
-
// built incrementally during streaming.
|
|
612694
|
-
const txt = host.innerText || host.textContent || content || '';
|
|
612695
|
-
try { navigator.clipboard.writeText(txt); } catch {}
|
|
612696
|
-
const orig = actions.querySelector('button').textContent;
|
|
612697
|
-
actions.querySelector('button').textContent = 'copied';
|
|
612698
|
-
setTimeout(() => { actions.querySelector('button').textContent = orig; }, 1500);
|
|
612699
|
-
}));
|
|
612700
|
-
actions.appendChild(mkBtn('regen', 'Regenerate this response', () => {
|
|
612701
|
-
try { regenerateAssistantMessage(div); } catch (e) { console.warn('regen failed', e); }
|
|
612702
|
-
}));
|
|
612703
|
-
actions.appendChild(mkBtn('del', 'Delete this message', () => {
|
|
612704
|
-
try { deleteMessageDiv(div); } catch (e) { console.warn('del failed', e); }
|
|
612705
|
-
}));
|
|
612706
|
-
div.appendChild(actions);
|
|
613075
|
+
appendAssistantActions(div, () => host.innerText || host.textContent || content || '');
|
|
612707
613076
|
}
|
|
612708
613077
|
conv.appendChild(div);
|
|
612709
613078
|
// OWUI-3: schedule syntax highlight for any new code blocks.
|
|
@@ -612894,6 +613263,12 @@ function appendExpandableContent(parent, fullText, opts) {
|
|
|
612894
613263
|
return wrapper;
|
|
612895
613264
|
}
|
|
612896
613265
|
|
|
613266
|
+
function isInternalChatTool(toolName) {
|
|
613267
|
+
return toolName === 'task_complete'
|
|
613268
|
+
|| toolName === 'todo_write'
|
|
613269
|
+
|| toolName === 'todo_read';
|
|
613270
|
+
}
|
|
613271
|
+
|
|
612897
613272
|
// WO-CHAT-RESUME-TOOLS — render a single tool_call event into a parent
|
|
612898
613273
|
// container. Used by BOTH the live SSE handler AND the chat history
|
|
612899
613274
|
// restore path so reloads see the same intermediate-state dropdowns
|
|
@@ -612901,12 +613276,13 @@ function appendExpandableContent(parent, fullText, opts) {
|
|
|
612901
613276
|
// SSE chunk shape ({type:'tool_call', tool, args}) OR the persisted
|
|
612902
613277
|
// session message shape ({role:'tool_call', tool, args}).
|
|
612903
613278
|
function renderToolCallEvent(parent, chunkLike) {
|
|
613279
|
+
const toolName = chunkLike.tool || 'tool';
|
|
613280
|
+
if (isInternalChatTool(toolName)) return null;
|
|
612904
613281
|
const details = document.createElement('details');
|
|
612905
613282
|
details.style.cssText = 'background:var(--color-bg-elevated);border-left:2px solid var(--color-brand);margin:2px 0;font-size:0.7rem';
|
|
612906
613283
|
const summary = document.createElement('summary');
|
|
612907
613284
|
summary.style.cssText = 'padding:4px 8px;color:var(--color-brand);cursor:pointer';
|
|
612908
613285
|
|
|
612909
|
-
const toolName = chunkLike.tool || 'tool';
|
|
612910
613286
|
let a = (chunkLike.args && typeof chunkLike.args === 'object') ? chunkLike.args : {};
|
|
612911
613287
|
if (typeof chunkLike.args === 'string') {
|
|
612912
613288
|
try { a = JSON.parse(chunkLike.args); } catch { a = { _raw: chunkLike.args }; }
|
|
@@ -612995,6 +613371,7 @@ function renderToolCallEvent(parent, chunkLike) {
|
|
|
612995
613371
|
// WO-CHAT-RESUME-TOOLS — render a single tool_result event with the
|
|
612996
613372
|
// show-more/hide expandable helper. Used by both live SSE and restore.
|
|
612997
613373
|
function renderToolResultEvent(parent, chunkLike) {
|
|
613374
|
+
if (isInternalChatTool(chunkLike.tool)) return null;
|
|
612998
613375
|
const resultEl = document.createElement('div');
|
|
612999
613376
|
const errStyle = chunkLike.success === false
|
|
613000
613377
|
? 'background:#2a1e1e;border-left:2px solid var(--color-error);color:var(--color-error);'
|
|
@@ -613304,6 +613681,7 @@ async function sendMessage() {
|
|
|
613304
613681
|
// $chatSessionId.set(...) — the subscriber persists to project-
|
|
613305
613682
|
// scoped localStorage AND server prefs. Setting it here is enough.
|
|
613306
613683
|
chatSessionId = sid;
|
|
613684
|
+
syncRouteForTab('chat', true);
|
|
613307
613685
|
// WO-TASK-02: Surface the session ID globally so refreshTodos works
|
|
613308
613686
|
window.currentSessionId = sid;
|
|
613309
613687
|
// Initial refresh in case the agent set todos before the SSE stream
|
|
@@ -613343,16 +613721,20 @@ async function sendMessage() {
|
|
|
613343
613721
|
|| (typeof ta.result === 'string' && ta.result)
|
|
613344
613722
|
|| '';
|
|
613345
613723
|
if (summaryText) {
|
|
613346
|
-
if (fullContent
|
|
613347
|
-
|
|
613724
|
+
if (fullContent.trim() !== summaryText.trim()) {
|
|
613725
|
+
if (fullContent && !fullContent.endsWith('\\n')) fullContent += '\\n\\n';
|
|
613726
|
+
fullContent += summaryText;
|
|
613727
|
+
}
|
|
613348
613728
|
contentDiv.innerHTML = renderMarkdown(fullContent);
|
|
613729
|
+
hideStreamingIndicator(msgDiv);
|
|
613349
613730
|
maybeAutoScroll();
|
|
613350
613731
|
}
|
|
613351
|
-
|
|
613732
|
+
continue;
|
|
613352
613733
|
}
|
|
613353
613734
|
|
|
613354
613735
|
// Tool call event — show live as expandable section
|
|
613355
613736
|
if (chunk.type === 'tool_call') {
|
|
613737
|
+
if (isInternalChatTool(chunk.tool || '')) continue;
|
|
613356
613738
|
chatTools.push(chunk);
|
|
613357
613739
|
const details = document.createElement('details');
|
|
613358
613740
|
details.style.cssText = 'background:var(--color-bg-elevated);border-left:2px solid var(--color-brand);margin:2px 0;font-size:0.7rem';
|
|
@@ -613466,6 +613848,7 @@ async function sendMessage() {
|
|
|
613466
613848
|
// expand to see the FULL output instead of being capped at
|
|
613467
613849
|
// 150 chars. The button sits underneath the result block.
|
|
613468
613850
|
if (chunk.type === 'tool_result') {
|
|
613851
|
+
if (isInternalChatTool(chunk.tool || '')) continue;
|
|
613469
613852
|
const resultEl = document.createElement('div');
|
|
613470
613853
|
resultEl.style.cssText = 'background:var(--color-bg-elevated);padding:4px 8px 4px 18px;margin:0 0 2px 0;color:var(--color-fg-muted);font-size:0.65rem';
|
|
613471
613854
|
appendExpandableContent(resultEl, chunk.output || '', { truncateAt: 150, baseStyle: 'color:var(--color-fg-muted);' });
|
|
@@ -613482,12 +613865,7 @@ async function sendMessage() {
|
|
|
613482
613865
|
// Content delta
|
|
613483
613866
|
const delta = chunk.choices?.[0]?.delta?.content || '';
|
|
613484
613867
|
if (delta) {
|
|
613485
|
-
|
|
613486
|
-
// first delta, and remove on subsequent deltas (it lives at
|
|
613487
|
-
// the bottom of the message, below content).
|
|
613488
|
-
if (!fullContent) {
|
|
613489
|
-
showStreamingIndicator(msgDiv, 'writing');
|
|
613490
|
-
}
|
|
613868
|
+
hideStreamingIndicator(msgDiv);
|
|
613491
613869
|
fullContent += delta;
|
|
613492
613870
|
contentDiv.innerHTML = renderMarkdown(fullContent);
|
|
613493
613871
|
try { _highlightCodeBlocks(contentDiv); } catch {}
|
|
@@ -613502,6 +613880,7 @@ async function sendMessage() {
|
|
|
613502
613880
|
// extract it here so the next message uses the same session.
|
|
613503
613881
|
if (!chatSessionId && metaInfo && metaInfo.session_id) {
|
|
613504
613882
|
chatSessionId = metaInfo.session_id;
|
|
613883
|
+
syncRouteForTab('chat', true);
|
|
613505
613884
|
}
|
|
613506
613885
|
|
|
613507
613886
|
// Save with metadata for session recall
|
|
@@ -613541,14 +613920,8 @@ async function sendMessage() {
|
|
|
613541
613920
|
toolsContainer.appendChild(details);
|
|
613542
613921
|
}
|
|
613543
613922
|
|
|
613544
|
-
|
|
613545
|
-
|
|
613546
|
-
actions.className = 'msg-actions';
|
|
613547
|
-
const copyBtn = document.createElement('button');
|
|
613548
|
-
copyBtn.textContent = 'copy';
|
|
613549
|
-
copyBtn.onclick = () => { navigator.clipboard.writeText(fullContent); copyBtn.textContent = 'copied'; setTimeout(() => copyBtn.textContent = 'copy', 1500); };
|
|
613550
|
-
actions.appendChild(copyBtn);
|
|
613551
|
-
msgDiv.appendChild(actions);
|
|
613923
|
+
removeAssistantActions(msgDiv);
|
|
613924
|
+
appendAssistantActions(msgDiv, () => fullContent);
|
|
613552
613925
|
} catch (err) {
|
|
613553
613926
|
// Match the red left-border styling used by failed tool_result
|
|
613554
613927
|
// and the stop-button. Sits inside the assistant bubble so it
|
|
@@ -614398,9 +614771,56 @@ window.closeRemoteConnection = closeRemoteConnection;
|
|
|
614398
614771
|
} catch {}
|
|
614399
614772
|
})();
|
|
614400
614773
|
|
|
614401
|
-
// Tab switching
|
|
614774
|
+
// Tab switching + browser route sync
|
|
614402
614775
|
const allPanels = ['chat-container','agent-panel','jobs-panel','config-panel','activity-panel','projects-panel','voice-panel'];
|
|
614403
|
-
function
|
|
614776
|
+
function routePathForTab(tab) {
|
|
614777
|
+
const map = {
|
|
614778
|
+
chat: '/chat',
|
|
614779
|
+
agent: '/agent',
|
|
614780
|
+
voice: '/voice',
|
|
614781
|
+
projects: '/projects',
|
|
614782
|
+
jobs: '/dashboard',
|
|
614783
|
+
activity: '/activity',
|
|
614784
|
+
config: '/settings',
|
|
614785
|
+
};
|
|
614786
|
+
return map[tab] || '/chat';
|
|
614787
|
+
}
|
|
614788
|
+
function tabForRoutePath(pathname) {
|
|
614789
|
+
const path = (pathname || '/').replace(//+$/, '') || '/';
|
|
614790
|
+
const map = {
|
|
614791
|
+
'/': 'chat',
|
|
614792
|
+
'/chat': 'chat',
|
|
614793
|
+
'/agent': 'agent',
|
|
614794
|
+
'/voice': 'voice',
|
|
614795
|
+
'/projects': 'projects',
|
|
614796
|
+
'/dashboard': 'jobs',
|
|
614797
|
+
'/jobs': 'jobs',
|
|
614798
|
+
'/activity': 'activity',
|
|
614799
|
+
'/settings': 'config',
|
|
614800
|
+
'/config': 'config',
|
|
614801
|
+
};
|
|
614802
|
+
return map[path] || 'chat';
|
|
614803
|
+
}
|
|
614804
|
+
function chatSessionFromRouteSearch(search) {
|
|
614805
|
+
const qs = String(search || '').replace(/^?/, '').trim();
|
|
614806
|
+
if (!qs) return null;
|
|
614807
|
+
try {
|
|
614808
|
+
const params = new URLSearchParams(search);
|
|
614809
|
+
return params.get('session') || params.get('id') || params.get('s') || decodeURIComponent(qs.split('&')[0] || '');
|
|
614810
|
+
} catch {
|
|
614811
|
+
return qs;
|
|
614812
|
+
}
|
|
614813
|
+
}
|
|
614814
|
+
function syncRouteForTab(tab, replace) {
|
|
614815
|
+
const path = routePathForTab(tab);
|
|
614816
|
+
const query = tab === 'chat' && chatSessionId ? '?' + encodeURIComponent(chatSessionId) : '';
|
|
614817
|
+
const next = path + query;
|
|
614818
|
+
if ((location.pathname + location.search) === next) return;
|
|
614819
|
+
const method = replace ? 'replaceState' : 'pushState';
|
|
614820
|
+
try { history[method]({}, '', next); } catch {}
|
|
614821
|
+
}
|
|
614822
|
+
function switchTab(tab, opts) {
|
|
614823
|
+
opts = opts || {};
|
|
614404
614824
|
const panelMap = {chat:'chat-container',agent:'agent-panel',jobs:'jobs-panel',config:'config-panel',activity:'activity-panel',projects:'projects-panel',voice:'voice-panel'};
|
|
614405
614825
|
allPanels.forEach(id => { const el = document.getElementById(id); if(el) el.style.display = 'none'; });
|
|
614406
614826
|
const panel = document.getElementById(panelMap[tab]);
|
|
@@ -614417,6 +614837,8 @@ function switchTab(tab) {
|
|
|
614417
614837
|
document.querySelectorAll('#omnius-sidebar .sb-nav').forEach(b => {
|
|
614418
614838
|
b.classList.toggle('active', b.getAttribute('data-tab') === tab);
|
|
614419
614839
|
});
|
|
614840
|
+
if ($activeTab.get && $activeTab.get() !== tab) $activeTab.set(tab);
|
|
614841
|
+
if (!opts.fromRoute) syncRouteForTab(tab, !!opts.replaceRoute);
|
|
614420
614842
|
if (tab === 'jobs') loadJobs();
|
|
614421
614843
|
if (tab === 'agent') {
|
|
614422
614844
|
loadProfiles();
|
|
@@ -614432,6 +614854,14 @@ function switchTab(tab) {
|
|
|
614432
614854
|
try { loadVoiceTab(); } catch {}
|
|
614433
614855
|
}
|
|
614434
614856
|
}
|
|
614857
|
+
window.addEventListener('popstate', () => {
|
|
614858
|
+
const tab = tabForRoutePath(location.pathname);
|
|
614859
|
+
if (tab === 'chat') {
|
|
614860
|
+
const sid = chatSessionFromRouteSearch(location.search);
|
|
614861
|
+
if (sid && sid !== chatSessionId) switchSession(sid);
|
|
614862
|
+
}
|
|
614863
|
+
switchTab(tab, { fromRoute: true });
|
|
614864
|
+
});
|
|
614435
614865
|
|
|
614436
614866
|
// Projects tab — list all workspaces the user has run 'omnius' in, and let them
|
|
614437
614867
|
// switch the active workspace. A switch is a server-side state change; the
|
|
@@ -615125,6 +615555,7 @@ function updateSessionSelect() {
|
|
|
615125
615555
|
// still works exactly as before.
|
|
615126
615556
|
function switchChatSession(id) {
|
|
615127
615557
|
switchSession(id);
|
|
615558
|
+
switchTab('chat', { replaceRoute: true });
|
|
615128
615559
|
}
|
|
615129
615560
|
function newChatSession() {
|
|
615130
615561
|
switchSession('');
|
|
@@ -615294,6 +615725,7 @@ function switchSession(id) {
|
|
|
615294
615725
|
chatSessionId = null;
|
|
615295
615726
|
messages = [];
|
|
615296
615727
|
document.getElementById('conversation').innerHTML = '';
|
|
615728
|
+
syncRouteForTab('chat', true);
|
|
615297
615729
|
return;
|
|
615298
615730
|
}
|
|
615299
615731
|
const saved = loadScopedSessions();
|
|
@@ -615345,6 +615777,7 @@ function switchSession(id) {
|
|
|
615345
615777
|
div.appendChild(outerDetails);
|
|
615346
615778
|
}
|
|
615347
615779
|
}
|
|
615780
|
+
syncRouteForTab('chat', true);
|
|
615348
615781
|
}
|
|
615349
615782
|
}
|
|
615350
615783
|
|
|
@@ -616354,32 +616787,14 @@ async function refreshTodos(sessionId) {
|
|
|
616354
616787
|
}
|
|
616355
616788
|
const j = await r.json();
|
|
616356
616789
|
const todos = (j && Array.isArray(j.todos)) ? j.todos : [];
|
|
616357
|
-
// 1)
|
|
616790
|
+
// 1) Keep the full checklist out of the chat transcript. The compact
|
|
616791
|
+
// tasks strip below is the visible progress surface.
|
|
616358
616792
|
if (todoChecklistEl && todoListEl) {
|
|
616359
|
-
|
|
616360
|
-
|
|
616361
|
-
|
|
616362
|
-
|
|
616363
|
-
|
|
616364
|
-
for (const t of todos) {
|
|
616365
|
-
const li = document.createElement('li');
|
|
616366
|
-
li.style.cssText = 'padding:2px 0;display:flex;gap:8px;align-items:flex-start';
|
|
616367
|
-
const mark = document.createElement('span');
|
|
616368
|
-
mark.style.cssText = 'color:var(--color-brand);font-family:monospace;flex-shrink:0';
|
|
616369
|
-
mark.textContent = statusMark(t.status);
|
|
616370
|
-
li.appendChild(mark);
|
|
616371
|
-
const content = document.createElement('span');
|
|
616372
|
-
content.style.cssText = t.status === 'completed'
|
|
616373
|
-
? 'color:var(--color-fg-subtle);text-decoration:line-through'
|
|
616374
|
-
: 'color:var(--color-fg)';
|
|
616375
|
-
content.textContent = t.content + (t.blocker ? ' (blocked: ' + t.blocker + ')' : '');
|
|
616376
|
-
li.appendChild(content);
|
|
616377
|
-
todoListEl.appendChild(li);
|
|
616378
|
-
}
|
|
616379
|
-
}
|
|
616380
|
-
}
|
|
616381
|
-
// 2) Update the compact tasks-row strip in the footer
|
|
616382
|
-
renderTasksRow(todos);
|
|
616793
|
+
todoChecklistEl.style.display = 'none';
|
|
616794
|
+
todoListEl.innerHTML = '';
|
|
616795
|
+
}
|
|
616796
|
+
// 2) Chat mode should not surface transient planning todos in the UI.
|
|
616797
|
+
renderTasksRow([]);
|
|
616383
616798
|
} catch { /* network or parse failure — leave panel as-is */ }
|
|
616384
616799
|
}
|
|
616385
616800
|
|
|
@@ -616456,9 +616871,11 @@ async function restoreChatSession() {
|
|
|
616456
616871
|
currentAssistantTools.style.cssText = 'margin:4px 0;font-size:0.7rem';
|
|
616457
616872
|
msgDiv.appendChild(currentAssistantTools);
|
|
616458
616873
|
} else if (m.role === 'tool_call') {
|
|
616874
|
+
if (isInternalChatTool(m.tool)) continue;
|
|
616459
616875
|
renderToolCallEvent(ensureTools(), { tool: m.tool, args: m.args });
|
|
616460
616876
|
} else if (m.role === 'tool_result') {
|
|
616461
|
-
|
|
616877
|
+
if (isInternalChatTool(m.tool)) continue;
|
|
616878
|
+
renderToolResultEvent(ensureTools(), { tool: m.tool, output: m.output, success: m.success });
|
|
616462
616879
|
} else if (m.role === 'user_checkin') {
|
|
616463
616880
|
// WO-CHAT-CHECKIN — replay the teal user check-in entry
|
|
616464
616881
|
renderCheckinEvent(ensureTools(), m.content);
|
|
@@ -616527,11 +616944,13 @@ function attachInFlightPoller(sessionId, initialJob) {
|
|
|
616527
616944
|
curTools.style.cssText = 'margin:4px 0;font-size:0.7rem';
|
|
616528
616945
|
md.appendChild(curTools);
|
|
616529
616946
|
} else if (m.role === 'tool_call') {
|
|
616947
|
+
if (isInternalChatTool(m.tool)) continue;
|
|
616530
616948
|
if (!curTools) { const md = addMessage('assistant', ''); curTools = document.createElement('div'); curTools.style.cssText = 'margin:4px 0;font-size:0.7rem'; md.appendChild(curTools); }
|
|
616531
616949
|
renderToolCallEvent(curTools, { tool: m.tool, args: m.args });
|
|
616532
616950
|
} else if (m.role === 'tool_result') {
|
|
616951
|
+
if (isInternalChatTool(m.tool)) continue;
|
|
616533
616952
|
if (!curTools) { const md = addMessage('assistant', ''); curTools = document.createElement('div'); curTools.style.cssText = 'margin:4px 0;font-size:0.7rem'; md.appendChild(curTools); }
|
|
616534
|
-
renderToolResultEvent(curTools, { output: m.output, success: m.success });
|
|
616953
|
+
renderToolResultEvent(curTools, { tool: m.tool, output: m.output, success: m.success });
|
|
616535
616954
|
}
|
|
616536
616955
|
}
|
|
616537
616956
|
}
|
|
@@ -616565,11 +616984,13 @@ function attachInFlightPoller(sessionId, initialJob) {
|
|
|
616565
616984
|
curTools.style.cssText = 'margin:4px 0;font-size:0.7rem';
|
|
616566
616985
|
md.appendChild(curTools);
|
|
616567
616986
|
} else if (m.role === 'tool_call') {
|
|
616987
|
+
if (isInternalChatTool(m.tool)) continue;
|
|
616568
616988
|
if (!curTools) { const md = addMessage('assistant', ''); curTools = document.createElement('div'); curTools.style.cssText = 'margin:4px 0;font-size:0.7rem'; md.appendChild(curTools); }
|
|
616569
616989
|
renderToolCallEvent(curTools, { tool: m.tool, args: m.args });
|
|
616570
616990
|
} else if (m.role === 'tool_result') {
|
|
616991
|
+
if (isInternalChatTool(m.tool)) continue;
|
|
616571
616992
|
if (!curTools) { const md = addMessage('assistant', ''); curTools = document.createElement('div'); curTools.style.cssText = 'margin:4px 0;font-size:0.7rem'; md.appendChild(curTools); }
|
|
616572
|
-
renderToolResultEvent(curTools, { output: m.output, success: m.success });
|
|
616993
|
+
renderToolResultEvent(curTools, { tool: m.tool, output: m.output, success: m.success });
|
|
616573
616994
|
}
|
|
616574
616995
|
}
|
|
616575
616996
|
}
|
|
@@ -617003,9 +617424,9 @@ $activeTab.subscribe((tab) => {
|
|
|
617003
617424
|
if (!tab || typeof switchTab !== 'function') return;
|
|
617004
617425
|
// Avoid recursion: switchTab is called by user click and writes back
|
|
617005
617426
|
// via window.switchTab('chat') style; we only DOM-update if needed.
|
|
617006
|
-
const
|
|
617007
|
-
if (
|
|
617008
|
-
try { switchTab(tab); } catch {}
|
|
617427
|
+
const navBtn = document.querySelector('#omnius-sidebar .sb-nav[data-tab="' + tab + '"]');
|
|
617428
|
+
if (!navBtn || !navBtn.classList.contains('active')) {
|
|
617429
|
+
try { switchTab(tab, { replaceRoute: true }); } catch {}
|
|
617009
617430
|
}
|
|
617010
617431
|
});
|
|
617011
617432
|
|
|
@@ -617504,7 +617925,7 @@ function _renderSidebarChats(filter) {
|
|
|
617504
617925
|
const cls = 'sb-chat' + (id === activeId ? ' active' : '');
|
|
617505
617926
|
const safeId = String(id).replace(/'/g, "\\\\'");
|
|
617506
617927
|
const safeTitle = title.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
617507
|
-
return '<div class="' + cls + '" draggable="true" ondragstart="_onChatDragStart(event,\\'' + safeId + '\\')" onclick="
|
|
617928
|
+
return '<div class="' + cls + '" draggable="true" ondragstart="_onChatDragStart(event,\\'' + safeId + '\\')" onclick="switchSession(\\'' + safeId + '\\'); switchTab(\\'chat\\', { replaceRoute: true })" title="' + safeTitle + '">' +
|
|
617508
617929
|
'<span class="sb-chat-title">' + safeTitle + '</span>' +
|
|
617509
617930
|
'<button class="sb-chat-menu" onclick="_showChatRowMenu(event,\\'' + safeId + '\\')" title="More">⋮</button>' +
|
|
617510
617931
|
'</div>';
|
|
@@ -618695,7 +619116,7 @@ function getOpenApiSpec() {
|
|
|
618695
619116
|
get: { summary: "Get daemon configuration and settings", tags: ["Config"], responses: { 200: { description: "Config + settings (secrets redacted)" } } },
|
|
618696
619117
|
patch: {
|
|
618697
619118
|
summary: "Update configuration (PT-01 full settings surface)",
|
|
618698
|
-
description: "Includes TUI personality, deep_context, bruteforce, emojis, colors, stream, voice*, telegram*, cohere, commandsMode, updateMode, style.",
|
|
619119
|
+
description: "Includes TUI personality, deep_context, bruteforce, emojis, colors, stream, voice*, telegram*, cohere, commandsMode, selfModify, updateMode, style.",
|
|
618699
619120
|
tags: ["Config"],
|
|
618700
619121
|
requestBody: { required: true, content: { "application/json": { schema: { type: "object" } } } },
|
|
618701
619122
|
responses: { 200: { description: "Updated" }, 400: { description: "Invalid body", content: { "application/problem+json": { schema: { $ref: "#/components/schemas/ProblemDetails" } } } } }
|
|
@@ -623386,6 +623807,9 @@ async function handlePatchConfig(req2, res) {
|
|
|
623386
623807
|
if (updates["commandsMode"] === "auto" || updates["commandsMode"] === "manual") {
|
|
623387
623808
|
settingsUpdate.commandsMode = updates["commandsMode"];
|
|
623388
623809
|
}
|
|
623810
|
+
if (typeof updates["selfModify"] === "boolean") {
|
|
623811
|
+
settingsUpdate.selfModify = updates["selfModify"];
|
|
623812
|
+
}
|
|
623389
623813
|
if (updates["updateMode"] === "auto" || updates["updateMode"] === "manual") {
|
|
623390
623814
|
settingsUpdate.updateMode = updates["updateMode"];
|
|
623391
623815
|
}
|
|
@@ -623749,7 +624173,19 @@ async function handleRequest(req2, res, ollamaUrl, verbose) {
|
|
|
623749
624173
|
res.end(svg);
|
|
623750
624174
|
return;
|
|
623751
624175
|
}
|
|
623752
|
-
|
|
624176
|
+
const guiRoutes = /* @__PURE__ */ new Set([
|
|
624177
|
+
"/",
|
|
624178
|
+
"/chat",
|
|
624179
|
+
"/agent",
|
|
624180
|
+
"/voice",
|
|
624181
|
+
"/projects",
|
|
624182
|
+
"/dashboard",
|
|
624183
|
+
"/jobs",
|
|
624184
|
+
"/activity",
|
|
624185
|
+
"/settings",
|
|
624186
|
+
"/config"
|
|
624187
|
+
]);
|
|
624188
|
+
if (guiRoutes.has(pathname) && method === "GET" && req2.headers.accept?.includes("text/html")) {
|
|
623753
624189
|
res.writeHead(200, {
|
|
623754
624190
|
"Content-Type": "text/html; charset=utf-8",
|
|
623755
624191
|
"Cache-Control": "no-cache, no-store, must-revalidate, max-age=0",
|
|
@@ -624901,8 +625337,9 @@ data: ${JSON.stringify(data)}
|
|
|
624901
625337
|
session.id,
|
|
624902
625338
|
5
|
|
624903
625339
|
);
|
|
624904
|
-
|
|
624905
|
-
|
|
625340
|
+
const chatTodosEnabled = chatBody["todos"] === true || chatBody["enable_todos"] === true || chatBody["todo_injection"] === true;
|
|
625341
|
+
let currentTodos = chatTodosEnabled ? readTodos(session.id) : [];
|
|
625342
|
+
if (chatTodosEnabled && currentTodos.length === 0) {
|
|
624906
625343
|
const seeded = autoSeedTodosFromPrompt(chatBody.message);
|
|
624907
625344
|
if (seeded.length >= 2) {
|
|
624908
625345
|
try {
|
|
@@ -624915,7 +625352,7 @@ data: ${JSON.stringify(data)}
|
|
|
624915
625352
|
}
|
|
624916
625353
|
}
|
|
624917
625354
|
}
|
|
624918
|
-
const todoBlock = currentTodos.length > 0 ? "CURRENT TODO LIST:\n" + currentTodos.map((t2) => {
|
|
625355
|
+
const todoBlock = chatTodosEnabled && currentTodos.length > 0 ? "CURRENT TODO LIST:\n" + currentTodos.map((t2) => {
|
|
624919
625356
|
const mark = t2.status === "completed" ? "◉" : t2.status === "in_progress" ? "◐" : t2.status === "blocked" ? "◍" : "○";
|
|
624920
625357
|
return `${mark} ${t2.content}` + (t2.blocker ? ` (blocked: ${t2.blocker})` : "");
|
|
624921
625358
|
}).join("\n") + "\n\nUse the todo_write tool to update this list as you complete steps. Mark each item completed once you finish it.\n\n" : "";
|
|
@@ -625227,19 +625664,21 @@ ${historyLines}
|
|
|
625227
625664
|
finishInFlightChat(session.id, "completed", content.trim());
|
|
625228
625665
|
} catch {
|
|
625229
625666
|
}
|
|
625230
|
-
|
|
625231
|
-
|
|
625232
|
-
|
|
625233
|
-
|
|
625234
|
-
|
|
625235
|
-
|
|
625236
|
-
|
|
625237
|
-
|
|
625238
|
-
|
|
625239
|
-
|
|
625240
|
-
|
|
625667
|
+
if (chatTodosEnabled) {
|
|
625668
|
+
try {
|
|
625669
|
+
const finalTodos = readTodos(session.id);
|
|
625670
|
+
if (finalTodos.length > 0 && finalTodos.some((t2) => t2.status !== "completed" && t2.status !== "blocked")) {
|
|
625671
|
+
const updated = finalTodos.map((t2) => ({
|
|
625672
|
+
id: t2.id,
|
|
625673
|
+
content: t2.content,
|
|
625674
|
+
status: t2.status === "blocked" ? "blocked" : "completed",
|
|
625675
|
+
parentId: t2.parentId,
|
|
625676
|
+
blocker: t2.blocker
|
|
625677
|
+
}));
|
|
625678
|
+
writeTodos(session.id, updated);
|
|
625679
|
+
}
|
|
625680
|
+
} catch {
|
|
625241
625681
|
}
|
|
625242
|
-
} catch {
|
|
625243
625682
|
}
|
|
625244
625683
|
writeMemoryEpisodes(session.id, chatBody.message, content.trim(), toolCallCount).catch(() => {
|
|
625245
625684
|
});
|
|
@@ -628694,7 +629133,7 @@ async function runSelfImprovementCycle(repoRoot) {
|
|
|
628694
629133
|
} catch {
|
|
628695
629134
|
}
|
|
628696
629135
|
}
|
|
628697
|
-
function startTask(task, config, repoRoot, voice, stream, taskStores, bruteForce, statusBar, sudoCallback, costTracker, onComplete, taskType, contextWindowSize, modelCaps, personality, deepContext, onCompaction, emotionEngine, flowEnabled, slashCommandHandler, thinkingEnabled, askUserCallback) {
|
|
629136
|
+
function startTask(task, config, repoRoot, voice, stream, taskStores, bruteForce, statusBar, sudoCallback, costTracker, onComplete, taskType, contextWindowSize, modelCaps, personality, deepContext, onCompaction, emotionEngine, flowEnabled, slashCommandHandler, thinkingEnabled, askUserCallback, selfModifyEnabled) {
|
|
628698
629137
|
const voiceStyleMap = {
|
|
628699
629138
|
concise: 1,
|
|
628700
629139
|
balanced: 3,
|
|
@@ -628812,9 +629251,27 @@ ${lines.join("\n")}
|
|
|
628812
629251
|
if (taskType && modelTier !== "small") {
|
|
628813
629252
|
dynamicContext += "\n\n" + buildTaskContext(taskType);
|
|
628814
629253
|
}
|
|
629254
|
+
let persistedSelfModify = false;
|
|
629255
|
+
let persistedCommandsMode = "manual";
|
|
629256
|
+
try {
|
|
629257
|
+
const mergedSettings = {
|
|
629258
|
+
...loadGlobalSettings(),
|
|
629259
|
+
...loadProjectSettings(repoRoot)
|
|
629260
|
+
};
|
|
629261
|
+
persistedSelfModify = mergedSettings.selfModify ?? false;
|
|
629262
|
+
persistedCommandsMode = mergedSettings.commandsMode ?? "manual";
|
|
629263
|
+
} catch {
|
|
629264
|
+
}
|
|
629265
|
+
const effectiveSelfModifyEnabled = selfModifyEnabled ?? persistedSelfModify;
|
|
629266
|
+
const effectiveCommandsMode = persistedCommandsMode;
|
|
628815
629267
|
if (flowEnabled) {
|
|
628816
629268
|
dynamicContext += "\n\n" + FLOWSTATE_PROMPT;
|
|
628817
629269
|
}
|
|
629270
|
+
if (effectiveSelfModifyEnabled) {
|
|
629271
|
+
dynamicContext += "\n\n<self-modify-mode>\nSelf-modify mode is ON. The user has granted permission for you to cognitively decide when a natural-language request is really a request to change Omnius runtime/UI behavior.\nWhen the request is about Omnius itself (for example updating Omnius with 'update quick' or 'update check', changing color/theme, changing style, toggling thinking/streaming/deep/flow/verbose, viewing stats/config), use the slash_command tool with the appropriate slash command instead of only explaining the command.\nDo not use hardcoded keyword aliasing. Infer intent from the full request, prefer the narrowest slash command, and report what command you invoked and the result.\n" + describeSelfModifyCommandPolicy() + "\n</self-modify-mode>";
|
|
629272
|
+
} else {
|
|
629273
|
+
dynamicContext += "\n\n<self-modify-mode>\nSelf-modify mode is OFF by default. Do not convert natural-language requests into self-modifying slash-command calls unless /commands auto separately enables safe command tooling. For runtime/UI changes, tell the user the exact slash command to run or ask them to enable /selfmodify on.\n</self-modify-mode>";
|
|
629274
|
+
}
|
|
628818
629275
|
if (modelCaps?.vision) {
|
|
628819
629276
|
dynamicContext += `
|
|
628820
629277
|
|
|
@@ -629056,6 +629513,14 @@ RULES:
|
|
|
629056
629513
|
} catch {
|
|
629057
629514
|
}
|
|
629058
629515
|
const tools = buildTools(repoRoot, config, contextWindowSize, modelTier);
|
|
629516
|
+
if (!slashCommandHandler && (effectiveSelfModifyEnabled || effectiveCommandsMode === "auto")) {
|
|
629517
|
+
slashCommandHandler = async (command) => {
|
|
629518
|
+
const { runCommand: runCommand3 } = await Promise.resolve().then(() => (init_command_passthrough(), command_passthrough_exports));
|
|
629519
|
+
const result = await runCommand3(command, { repoRoot, config });
|
|
629520
|
+
if (result.ok) return result.output || `Command executed: ${command}`;
|
|
629521
|
+
return result.error || result.output || `Command failed: ${command}`;
|
|
629522
|
+
};
|
|
629523
|
+
}
|
|
629059
629524
|
if (contextWindowSize && contextWindowSize > 0) {
|
|
629060
629525
|
for (const tool of tools) {
|
|
629061
629526
|
if ("setContextWindowSize" in tool && typeof tool.setContextWindowSize === "function") {
|
|
@@ -629453,13 +629918,13 @@ ${entry.fullContent}`
|
|
|
629453
629918
|
{
|
|
629454
629919
|
runner.registerTool({
|
|
629455
629920
|
name: "slash_command",
|
|
629456
|
-
description: "Invoke
|
|
629921
|
+
description: "Invoke an Omnius slash command programmatically. Available when /commands auto or /selfmodify on is set by the user. " + (effectiveSelfModifyEnabled ? describeSelfModifyCommandPolicy() : describeAgentToolCommandPolicy()) + " Use this only when the user intent is to operate Omnius itself: check configuration, run evaluations, discover skills, update Omnius, or adjust runtime/UI modes during a task.",
|
|
629457
629922
|
parameters: {
|
|
629458
629923
|
type: "object",
|
|
629459
629924
|
properties: {
|
|
629460
629925
|
command: {
|
|
629461
629926
|
type: "string",
|
|
629462
|
-
description: "The slash command to run (without leading /), e.g. 'stats', 'skills security', 'verbose', 'config'"
|
|
629927
|
+
description: "The slash command to run (without leading /), e.g. 'stats', 'skills security', 'verbose', 'config', 'color branding', 'update quick'"
|
|
629463
629928
|
}
|
|
629464
629929
|
},
|
|
629465
629930
|
required: ["command"]
|
|
@@ -629474,7 +629939,7 @@ ${entry.fullContent}`
|
|
|
629474
629939
|
};
|
|
629475
629940
|
}
|
|
629476
629941
|
const cmdName = cmd.split(/\s+/)[0].toLowerCase();
|
|
629477
|
-
const commandPolicy = getAgentToolCommandPolicy(cmd);
|
|
629942
|
+
const commandPolicy = effectiveSelfModifyEnabled ? getSelfModifyCommandPolicy(cmd) : getAgentToolCommandPolicy(cmd);
|
|
629478
629943
|
if (!commandPolicy.allowed) {
|
|
629479
629944
|
return {
|
|
629480
629945
|
success: false,
|
|
@@ -629486,7 +629951,7 @@ ${entry.fullContent}`
|
|
|
629486
629951
|
return {
|
|
629487
629952
|
success: false,
|
|
629488
629953
|
output: "",
|
|
629489
|
-
error: "Slash commands are in manual mode. Ask the user to run /commands auto
|
|
629954
|
+
error: "Slash commands are in manual mode. Ask the user to run /selfmodify on for natural-language self-modification, /commands auto for safe command tooling, or ask the user to run the command directly."
|
|
629490
629955
|
};
|
|
629491
629956
|
}
|
|
629492
629957
|
try {
|
|
@@ -630725,6 +631190,7 @@ async function startInteractive(config, repoPath) {
|
|
|
630725
631190
|
let flowEnabled = savedSettings.flow === true;
|
|
630726
631191
|
let cohereEnabled = savedSettings.cohere ?? false;
|
|
630727
631192
|
let commandsMode = savedSettings.commandsMode ?? "manual";
|
|
631193
|
+
let selfModifyEnabled = savedSettings.selfModify ?? false;
|
|
630728
631194
|
if (savedSettings.emojis !== void 0)
|
|
630729
631195
|
setEmojisEnabled(savedSettings.emojis);
|
|
630730
631196
|
if (savedSettings.colors !== void 0)
|
|
@@ -631490,7 +631956,8 @@ Rationale: ${proposal.rationale}${provenanceNote}`;
|
|
|
631490
631956
|
return /^-?\d+$/.test(raw) ? Number(raw) : raw;
|
|
631491
631957
|
};
|
|
631492
631958
|
const reminderText = (reminder) => {
|
|
631493
|
-
const
|
|
631959
|
+
const rawContent2 = reminder.delivery?.content || reminder.message;
|
|
631960
|
+
const content = sanitizeReminderDeliveryText(rawContent2) || rawContent2;
|
|
631494
631961
|
const prefix = reminder.priority === "critical" ? "URGENT reminder" : "Reminder";
|
|
631495
631962
|
return `${prefix}: ${content}`;
|
|
631496
631963
|
};
|
|
@@ -632989,6 +633456,12 @@ The user pasted a clipboard image saved at ${relPath}. Use the OCR, vision analy
|
|
|
632989
633456
|
setCommandsMode(mode) {
|
|
632990
633457
|
commandsMode = mode;
|
|
632991
633458
|
},
|
|
633459
|
+
getSelfModify() {
|
|
633460
|
+
return selfModifyEnabled;
|
|
633461
|
+
},
|
|
633462
|
+
setSelfModify(enabled2) {
|
|
633463
|
+
selfModifyEnabled = enabled2;
|
|
633464
|
+
},
|
|
632992
633465
|
setStyle(preset) {
|
|
632993
633466
|
currentStyle = preset;
|
|
632994
633467
|
},
|
|
@@ -634716,7 +635189,7 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
|
|
|
634716
635189
|
pasteIndicatorShown = true;
|
|
634717
635190
|
}
|
|
634718
635191
|
const buildSlashCommandHandler = () => {
|
|
634719
|
-
if (commandsMode !== "auto") return void 0;
|
|
635192
|
+
if (commandsMode !== "auto" && !selfModifyEnabled) return void 0;
|
|
634720
635193
|
return async (command) => {
|
|
634721
635194
|
const captured = [];
|
|
634722
635195
|
const origWrite = process.stdout.write;
|
|
@@ -634924,7 +635397,8 @@ Execute this skill now. Follow the behavioral guidance above.`;
|
|
|
634924
635397
|
flowEnabled,
|
|
634925
635398
|
buildSlashCommandHandler(),
|
|
634926
635399
|
thinkingEnabled,
|
|
634927
|
-
handleAskUser
|
|
635400
|
+
handleAskUser,
|
|
635401
|
+
selfModifyEnabled
|
|
634928
635402
|
);
|
|
634929
635403
|
activeTask = task;
|
|
634930
635404
|
setTerminalTitle(input.slice(0, 60), version4);
|
|
@@ -635308,7 +635782,8 @@ NEW TASK: ${fullInput}`;
|
|
|
635308
635782
|
flowEnabled,
|
|
635309
635783
|
buildSlashCommandHandler(),
|
|
635310
635784
|
thinkingEnabled,
|
|
635311
|
-
handleAskUser
|
|
635785
|
+
handleAskUser,
|
|
635786
|
+
selfModifyEnabled
|
|
635312
635787
|
);
|
|
635313
635788
|
activeTask = task;
|
|
635314
635789
|
_recallText = null;
|
|
@@ -636667,6 +637142,7 @@ function handleShow(opts, config) {
|
|
|
636667
637142
|
printKeyValue("voiceModel", String(resolved.voiceModel ?? "glados"), 2);
|
|
636668
637143
|
printSection("Agent Autonomy");
|
|
636669
637144
|
printKeyValue("commandsMode", String(resolved.commandsMode ?? "manual"), 2);
|
|
637145
|
+
printKeyValue("selfModify", String(resolved.selfModify ?? false), 2);
|
|
636670
637146
|
printKeyValue("updateMode", String(resolved.updateMode ?? "auto"), 2);
|
|
636671
637147
|
printSection("Integrations");
|
|
636672
637148
|
printKeyValue("telegramKey", resolved.telegramKey ? redactIfSensitive("telegramKey", resolved.telegramKey) : "[not set]", 2);
|
|
@@ -636807,6 +637283,7 @@ var init_config6 = __esm({
|
|
|
636807
637283
|
voiceModel: "TTS voice model: glados, overwatch",
|
|
636808
637284
|
// -- Agent autonomy --
|
|
636809
637285
|
commandsMode: "Agent slash-command access: auto (agent can invoke) or manual (user-only)",
|
|
637286
|
+
selfModify: "Agent self-modify slash-command access for natural-language runtime/UI requests (true/false)",
|
|
636810
637287
|
updateMode: "Update behaviour: auto (after task completion) or manual (/update only)",
|
|
636811
637288
|
// -- Integrations --
|
|
636812
637289
|
telegramKey: "Telegram bot API token for /telegram bridge",
|
|
@@ -636814,7 +637291,7 @@ var init_config6 = __esm({
|
|
|
636814
637291
|
};
|
|
636815
637292
|
SENSITIVE_KEYS = /* @__PURE__ */ new Set(["apiKey", "api_key", "secret", "password", "token", "telegramKey"]);
|
|
636816
637293
|
INT_KEYS = /* @__PURE__ */ new Set(["maxRetries", "timeoutMs"]);
|
|
636817
|
-
BOOL_KEYS = /* @__PURE__ */ new Set(["dryRun", "verbose", "voice", "stream", "bruteforce", "deepContext", "emojis", "colors"]);
|
|
637294
|
+
BOOL_KEYS = /* @__PURE__ */ new Set(["dryRun", "verbose", "voice", "stream", "bruteforce", "deepContext", "emojis", "colors", "selfModify"]);
|
|
636818
637295
|
ENUM_KEYS = {
|
|
636819
637296
|
commandsMode: ["auto", "manual"],
|
|
636820
637297
|
updateMode: ["auto", "manual"],
|