omnius 1.0.339 → 1.0.340
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/dist/index.js +120 -0
- package/docs/reference/rest-api.md +1 -0
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -689463,6 +689463,41 @@ window.__omniusStores = {
|
|
|
689463
689463
|
|
|
689464
689464
|
const conv = document.getElementById('conversation');
|
|
689465
689465
|
const input = document.getElementById('input-area');
|
|
689466
|
+
// SUGGESTED FOLLOW-UP — ghost text (placeholder-grey) shown after a reply
|
|
689467
|
+
// completes; Tab accepts it as real text, typing dismisses it.
|
|
689468
|
+
const DEFAULT_INPUT_PLACEHOLDER = 'Type a message...';
|
|
689469
|
+
let chatFollowup = null;
|
|
689470
|
+
function setFollowupSuggestion(text) {
|
|
689471
|
+
text = (text || '').trim();
|
|
689472
|
+
if (!text || streaming) return;
|
|
689473
|
+
if (input.value.trim().length > 0) return; // don't clobber what the user is typing
|
|
689474
|
+
chatFollowup = text;
|
|
689475
|
+
input.placeholder = text + ' (Tab \\u21E5)';
|
|
689476
|
+
}
|
|
689477
|
+
function clearFollowupSuggestion() {
|
|
689478
|
+
if (chatFollowup !== null) { chatFollowup = null; input.placeholder = DEFAULT_INPUT_PLACEHOLDER; }
|
|
689479
|
+
}
|
|
689480
|
+
function acceptFollowupSuggestion() {
|
|
689481
|
+
if (chatFollowup === null || input.value.trim().length > 0) return false;
|
|
689482
|
+
const t = chatFollowup;
|
|
689483
|
+
clearFollowupSuggestion();
|
|
689484
|
+
input.value = t;
|
|
689485
|
+
try { input.dispatchEvent(new Event('input')); } catch (e) {}
|
|
689486
|
+
input.focus();
|
|
689487
|
+
try { input.setSelectionRange(t.length, t.length); } catch (e) {}
|
|
689488
|
+
return true;
|
|
689489
|
+
}
|
|
689490
|
+
async function fetchFollowupSuggestion() {
|
|
689491
|
+
try {
|
|
689492
|
+
if (!chatSessionId || input.value.trim().length > 0) return;
|
|
689493
|
+
const r = await fetch('/v1/chat/suggest-followup', {
|
|
689494
|
+
method: 'POST', headers: headers(), body: JSON.stringify({ session_id: chatSessionId }),
|
|
689495
|
+
});
|
|
689496
|
+
if (!r.ok) return;
|
|
689497
|
+
const d = await r.json();
|
|
689498
|
+
if (d && d.suggestion) setFollowupSuggestion(d.suggestion);
|
|
689499
|
+
} catch (e) { /* best-effort */ }
|
|
689500
|
+
}
|
|
689466
689501
|
const sendBtn = document.getElementById('send-btn');
|
|
689467
689502
|
const modelSelect = document.getElementById('model-select');
|
|
689468
689503
|
// Persist the selected model on every change so a browser refresh /
|
|
@@ -689567,6 +689602,8 @@ if (conv) {
|
|
|
689567
689602
|
|
|
689568
689603
|
// Auto-resize textarea + WO-CHAT-CHECKIN typing detection
|
|
689569
689604
|
input.addEventListener('input', () => {
|
|
689605
|
+
// Dismiss the suggested follow-up the moment the user starts typing.
|
|
689606
|
+
if (input.value.length > 0) clearFollowupSuggestion();
|
|
689570
689607
|
input.style.height = 'auto';
|
|
689571
689608
|
// OWUI-4: bump max from 120 (~6 lines) to 240 (~12 lines).
|
|
689572
689609
|
input.style.height = Math.min(input.scrollHeight, 240) + 'px';
|
|
@@ -689597,6 +689634,10 @@ input.addEventListener('keydown', (e) => {
|
|
|
689597
689634
|
if (e.key === 'Tab') { e.preventDefault(); _slashPaletteAccept(); return; }
|
|
689598
689635
|
if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); _slashPaletteAccept(); return; }
|
|
689599
689636
|
}
|
|
689637
|
+
// Suggested follow-up: Tab accepts the ghost text as real input.
|
|
689638
|
+
if (e.key === 'Tab' && !e.shiftKey && chatFollowup !== null && input.value.trim().length === 0) {
|
|
689639
|
+
if (acceptFollowupSuggestion()) { e.preventDefault(); return; }
|
|
689640
|
+
}
|
|
689600
689641
|
// OWUI-4: Cmd/Ctrl+Enter — always sends, never inserts a newline.
|
|
689601
689642
|
if (e.key === 'Enter' && (e.metaKey || e.ctrlKey)) {
|
|
689602
689643
|
e.preventDefault();
|
|
@@ -690309,6 +690350,7 @@ async function runSlashCommandInline(rawInput) {
|
|
|
690309
690350
|
async function sendMessage() {
|
|
690310
690351
|
const text = input.value.trim();
|
|
690311
690352
|
if (!text || streaming) return;
|
|
690353
|
+
clearFollowupSuggestion(); // a real send supersedes any ghost suggestion
|
|
690312
690354
|
|
|
690313
690355
|
// ─── Slash command passthrough ────────────────────────────────────────
|
|
690314
690356
|
// /foo args → POST /v1/command, render result inline as a system
|
|
@@ -690686,6 +690728,8 @@ async function sendMessage() {
|
|
|
690686
690728
|
chatAbortController = null;
|
|
690687
690729
|
document.getElementById('send-btn').style.display = 'inline-block';
|
|
690688
690730
|
document.getElementById('stop-btn').style.display = 'none';
|
|
690731
|
+
// Reply finished — offer a suggested follow-up as ghost text in the input.
|
|
690732
|
+
try { fetchFollowupSuggestion(); } catch (e) {}
|
|
690689
690733
|
// OWUI-3: belt-and-braces — make sure the indicator is gone if we
|
|
690690
690734
|
// reach this finally{} via an error path before the success branch
|
|
690691
690735
|
// had a chance to call hideStreamingIndicator.
|
|
@@ -697329,6 +697373,7 @@ function getOpenApiSpec() {
|
|
|
697329
697373
|
},
|
|
697330
697374
|
"/v1/chat/sessions": { get: { summary: "List active chat sessions", tags: ["Chat"], responses: { 200: { description: "Session list" } } } },
|
|
697331
697375
|
"/v1/chat/sessions/{id}/summarize": { post: { summary: "Generate + cache an inference-based title/summary for a session — body {force?, root?}", tags: ["Chat"], responses: { 200: { description: "Title + summary" }, 500: { description: "Generation failed" } } } },
|
|
697376
|
+
"/v1/chat/suggest-followup": { post: { summary: "Suggest one short next-message follow-up for a session (ghost-text input) — body {session_id}", tags: ["Chat"], responses: { 200: { description: "{ suggestion }" } } } },
|
|
697332
697377
|
"/v1/chat/sessions/{id}/status": { get: { summary: "Reactive recall: is this session running right now + unseen deltas since ?since=<seq> (running, phase, seq, partial, deltas[])", tags: ["Chat"], responses: { 200: { description: "Live run status + catch-up deltas" } } } },
|
|
697333
697378
|
// ───── AIWG cascade ─────
|
|
697334
697379
|
"/v1/aiwg": { get: { summary: "AIWG installation root + control map", tags: ["AIWG"], responses: { 200: { description: "AIWG installation summary" } } } },
|
|
@@ -698184,6 +698229,62 @@ var init_chat_run_registry = __esm({
|
|
|
698184
698229
|
}
|
|
698185
698230
|
});
|
|
698186
698231
|
|
|
698232
|
+
// packages/cli/src/api/chat-followup.ts
|
|
698233
|
+
function normalizeBaseUrl2(url) {
|
|
698234
|
+
let u = (url || "").trim().replace(/\/+$/, "");
|
|
698235
|
+
if (u.endsWith("/v1")) u = u.slice(0, -3);
|
|
698236
|
+
return u;
|
|
698237
|
+
}
|
|
698238
|
+
function cleanSuggestion(raw) {
|
|
698239
|
+
let s2 = (raw || "").trim();
|
|
698240
|
+
s2 = s2.split("\n")[0].trim();
|
|
698241
|
+
s2 = s2.replace(/^["'`*\-\d.\s]+/, "").replace(/["'`]+$/, "").trim();
|
|
698242
|
+
if (/^(none|n\/?a|no follow|nothing)\b/i.test(s2)) return "";
|
|
698243
|
+
return s2.length > 140 ? s2.slice(0, 139).trimEnd() + "…" : s2;
|
|
698244
|
+
}
|
|
698245
|
+
async function suggestFollowup(args) {
|
|
698246
|
+
const turns = (args.turns || []).filter((t2) => t2 && t2.content && (t2.role === "user" || t2.role === "assistant"));
|
|
698247
|
+
if (!turns.length || !args.config.model || !args.config.backendUrl) return "";
|
|
698248
|
+
const transcript = turns.slice(-6).map((t2) => `${t2.role === "user" ? "User" : "Assistant"}: ${String(t2.content).slice(0, 800)}`).join("\n");
|
|
698249
|
+
try {
|
|
698250
|
+
const url = normalizeBaseUrl2(args.config.backendUrl) + "/v1/chat/completions";
|
|
698251
|
+
const headers = { "Content-Type": "application/json" };
|
|
698252
|
+
if (args.config.apiKey) headers["Authorization"] = `Bearer ${args.config.apiKey}`;
|
|
698253
|
+
const body = {
|
|
698254
|
+
model: args.config.model,
|
|
698255
|
+
messages: [
|
|
698256
|
+
{
|
|
698257
|
+
role: "system",
|
|
698258
|
+
content: "You suggest the single most likely NEXT message the user would send to continue this conversation. Write it in the user's voice (imperative, first person), as if they typed it. Reply with ONLY that one short prompt — no quotes, no preamble, no markdown, max ~12 words. If no natural follow-up exists, reply with exactly: NONE."
|
|
698259
|
+
},
|
|
698260
|
+
{ role: "user", content: `Conversation so far:
|
|
698261
|
+
${transcript}
|
|
698262
|
+
|
|
698263
|
+
The user's likely next message:` }
|
|
698264
|
+
],
|
|
698265
|
+
temperature: 0.4,
|
|
698266
|
+
max_tokens: 40,
|
|
698267
|
+
stream: false
|
|
698268
|
+
};
|
|
698269
|
+
const resp = await fetch(url, {
|
|
698270
|
+
method: "POST",
|
|
698271
|
+
headers,
|
|
698272
|
+
body: JSON.stringify(body),
|
|
698273
|
+
signal: AbortSignal.timeout(args.timeoutMs ?? 12e3)
|
|
698274
|
+
});
|
|
698275
|
+
if (!resp.ok) return "";
|
|
698276
|
+
const data = await resp.json();
|
|
698277
|
+
return cleanSuggestion(data?.choices?.[0]?.message?.content ?? "");
|
|
698278
|
+
} catch {
|
|
698279
|
+
return "";
|
|
698280
|
+
}
|
|
698281
|
+
}
|
|
698282
|
+
var init_chat_followup = __esm({
|
|
698283
|
+
"packages/cli/src/api/chat-followup.ts"() {
|
|
698284
|
+
"use strict";
|
|
698285
|
+
}
|
|
698286
|
+
});
|
|
698287
|
+
|
|
698187
698288
|
// packages/cli/src/docker.ts
|
|
698188
698289
|
import { execSync as execSync59, spawn as spawn32 } from "node:child_process";
|
|
698189
698290
|
import { existsSync as existsSync158, mkdirSync as mkdirSync99, writeFileSync as writeFileSync84 } from "node:fs";
|
|
@@ -706426,6 +706527,24 @@ ${historyLines}
|
|
|
706426
706527
|
});
|
|
706427
706528
|
return;
|
|
706428
706529
|
}
|
|
706530
|
+
if (pathname === "/v1/chat/suggest-followup" && method === "POST") {
|
|
706531
|
+
if (!checkAuth(req3, res, "read")) return;
|
|
706532
|
+
const fbBody = await parseJsonBody(req3);
|
|
706533
|
+
const sid = fbBody?.session_id ? String(fbBody.session_id) : "";
|
|
706534
|
+
const session = sid ? lookupSession(sid) : null;
|
|
706535
|
+
const turns = session ? session.messages.filter((m2) => m2 && (m2.role === "user" || m2.role === "assistant") && typeof m2.content === "string").map((m2) => ({ role: m2.role, content: m2.content })) : [];
|
|
706536
|
+
if (turns.length === 0) {
|
|
706537
|
+
jsonResponse(res, 200, { suggestion: "" });
|
|
706538
|
+
return;
|
|
706539
|
+
}
|
|
706540
|
+
const cfg = loadConfig();
|
|
706541
|
+
const suggestion = await suggestFollowup({
|
|
706542
|
+
turns,
|
|
706543
|
+
config: { backendUrl: cfg.backendUrl, model: cfg.model, apiKey: cfg.apiKey }
|
|
706544
|
+
});
|
|
706545
|
+
jsonResponse(res, 200, { suggestion });
|
|
706546
|
+
return;
|
|
706547
|
+
}
|
|
706429
706548
|
const chatSessionMatch = pathname.match(/^\/v1\/chat\/sessions\/([^/]+)$/);
|
|
706430
706549
|
if (chatSessionMatch) {
|
|
706431
706550
|
const sid = decodeURIComponent(chatSessionMatch[1]);
|
|
@@ -709080,6 +709199,7 @@ var init_serve = __esm({
|
|
|
709080
709199
|
init_omnius_directory();
|
|
709081
709200
|
init_session_summary();
|
|
709082
709201
|
init_chat_run_registry();
|
|
709202
|
+
init_chat_followup();
|
|
709083
709203
|
init_omnius_directory();
|
|
709084
709204
|
init_command_registry();
|
|
709085
709205
|
init_profiles();
|
|
@@ -52,6 +52,7 @@ pnpm docs:check
|
|
|
52
52
|
| `GET` | `/api/tags` | Ollama-compatible model tags |
|
|
53
53
|
| `GET` | `/v1/chat/sessions` | Active chat sessions |
|
|
54
54
|
| `POST` | `/v1/chat/sessions/{id}/summarize` | Generate + cache an inference-based session title/summary |
|
|
55
|
+
| `POST` | `/v1/chat/suggest-followup` | Suggest one short next-message follow-up (ghost-text input) |
|
|
55
56
|
| `GET` | `/v1/chat/sessions/{id}/status` | Reactive recall: live run status + unseen deltas (`?since=<seq>`) |
|
|
56
57
|
| `POST` | `/v1/chat/check-in` | Steering check-in for active chat |
|
|
57
58
|
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "omnius",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.340",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "omnius",
|
|
9
|
-
"version": "1.0.
|
|
9
|
+
"version": "1.0.340",
|
|
10
10
|
"bundleDependencies": [
|
|
11
11
|
"image-to-ascii"
|
|
12
12
|
],
|
package/package.json
CHANGED