@runcore-sh/runcore 0.5.6 → 0.5.7
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/dictionary.json +2 -2
- package/dist/.extensions/ext-byok.json +1 -0
- package/dist/.extensions/ext-hosted.json +1 -0
- package/dist/.extensions/ext-spawn.json +1 -0
- package/dist/agents/autonomous.d.ts.map +1 -1
- package/dist/agents/runtime/bus.d.ts +1 -0
- package/dist/agents/runtime/bus.d.ts.map +1 -1
- package/dist/agents/spawn.d.ts.map +1 -1
- package/dist/auth/middleware.d.ts.map +1 -1
- package/dist/auth/middleware.js +3 -2
- package/dist/auth/middleware.js.map +1 -1
- package/dist/calendar/routes.d.ts.map +1 -1
- package/dist/calendar/routes.js +8 -7
- package/dist/calendar/routes.js.map +1 -1
- package/dist/files/registry.d.ts +4 -5
- package/dist/files/registry.d.ts.map +1 -1
- package/dist/files/registry.js +4 -5
- package/dist/files/registry.js.map +1 -1
- package/dist/files/store.d.ts +2 -0
- package/dist/files/store.d.ts.map +1 -1
- package/dist/files/store.js +5 -1
- package/dist/files/store.js.map +1 -1
- package/dist/instance.d.ts +2 -0
- package/dist/instance.d.ts.map +1 -1
- package/dist/instance.js +2 -0
- package/dist/instance.js.map +1 -1
- package/dist/lib/paths.d.ts.map +1 -1
- package/dist/lib/paths.js +15 -1
- package/dist/lib/paths.js.map +1 -1
- package/dist/library/brain-shadow.d.ts.map +1 -1
- package/dist/library/brain-shadow.js +5 -4
- package/dist/library/brain-shadow.js.map +1 -1
- package/dist/library/routes.d.ts.map +1 -1
- package/dist/library/routes.js +10 -9
- package/dist/library/routes.js.map +1 -1
- package/dist/llm/cache.d.ts.map +1 -1
- package/dist/llm/cache.js +7 -5
- package/dist/llm/cache.js.map +1 -1
- package/dist/llm/complete.js +2 -0
- package/dist/llm/complete.js.map +1 -1
- package/dist/llm/providers/ollama.d.ts.map +1 -1
- package/dist/llm/providers/ollama.js +32 -7
- package/dist/llm/providers/ollama.js.map +1 -1
- package/dist/llm/providers/openrouter.d.ts.map +1 -1
- package/dist/llm/providers/openrouter.js +105 -12
- package/dist/llm/providers/openrouter.js.map +1 -1
- package/dist/llm/providers/types.d.ts +18 -0
- package/dist/llm/providers/types.d.ts.map +1 -1
- package/dist/llm/retry.d.ts.map +1 -1
- package/dist/llm/retry.js +6 -0
- package/dist/llm/retry.js.map +1 -1
- package/dist/llm/tools/handlers.d.ts +27 -0
- package/dist/llm/tools/handlers.d.ts.map +1 -0
- package/dist/llm/tools/handlers.js +842 -0
- package/dist/llm/tools/handlers.js.map +1 -0
- package/dist/llm/tools/index.d.ts +12 -0
- package/dist/llm/tools/index.d.ts.map +1 -0
- package/dist/llm/tools/index.js +10 -0
- package/dist/llm/tools/index.js.map +1 -0
- package/dist/llm/tools/loop.d.ts +47 -0
- package/dist/llm/tools/loop.d.ts.map +1 -0
- package/dist/llm/tools/loop.js +126 -0
- package/dist/llm/tools/loop.js.map +1 -0
- package/dist/llm/tools/registry.d.ts +27 -0
- package/dist/llm/tools/registry.d.ts.map +1 -0
- package/dist/llm/tools/registry.js +60 -0
- package/dist/llm/tools/registry.js.map +1 -0
- package/dist/llm/tools/schemas.d.ts +92 -0
- package/dist/llm/tools/schemas.d.ts.map +1 -0
- package/dist/llm/tools/schemas.js +154 -0
- package/dist/llm/tools/schemas.js.map +1 -0
- package/dist/llm/tools/types.d.ts +44 -0
- package/dist/llm/tools/types.d.ts.map +1 -0
- package/dist/llm/tools/types.js +9 -0
- package/dist/llm/tools/types.js.map +1 -0
- package/dist/mcp-server.d.ts +1 -1
- package/dist/mcp-server.js +249 -5
- package/dist/mcp-server.js.map +1 -1
- package/dist/memory/visual.d.ts.map +1 -1
- package/dist/memory/visual.js +3 -6
- package/dist/memory/visual.js.map +1 -1
- package/dist/openloop/foldback.js +1 -1
- package/dist/openloop/foldback.js.map +1 -1
- package/dist/openloop/resolution-scanner.d.ts.map +1 -1
- package/dist/openloop/resolution-scanner.js +76 -63
- package/dist/openloop/resolution-scanner.js.map +1 -1
- package/dist/plugins/github/index.d.ts +49 -0
- package/dist/plugins/github/index.d.ts.map +1 -0
- package/dist/plugins/github/index.js +153 -0
- package/dist/plugins/github/index.js.map +1 -0
- package/dist/plugins/index.d.ts +1 -2
- package/dist/plugins/index.d.ts.map +1 -1
- package/dist/plugins/index.js +79 -2
- package/dist/plugins/index.js.map +1 -1
- package/dist/plugins/slack/index.d.ts +43 -0
- package/dist/plugins/slack/index.d.ts.map +1 -0
- package/dist/plugins/slack/index.js +158 -0
- package/dist/plugins/slack/index.js.map +1 -0
- package/dist/plugins/twilio/index.d.ts +41 -0
- package/dist/plugins/twilio/index.d.ts.map +1 -0
- package/dist/plugins/twilio/index.js +102 -0
- package/dist/plugins/twilio/index.js.map +1 -0
- package/dist/pulse/tier.d.ts +1 -1
- package/dist/pulse/tier.js +2 -2
- package/dist/pulse/tier.js.map +1 -1
- package/dist/search/gemini.d.ts +27 -0
- package/dist/search/gemini.d.ts.map +1 -0
- package/dist/search/gemini.js +103 -0
- package/dist/search/gemini.js.map +1 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +850 -536
- package/dist/server.js.map +1 -1
- package/dist/services/routine-patterns.d.ts.map +1 -1
- package/dist/services/routine-patterns.js +6 -0
- package/dist/services/routine-patterns.js.map +1 -1
- package/dist/services/traceInsights.d.ts +5 -0
- package/dist/services/traceInsights.d.ts.map +1 -1
- package/dist/services/traceInsights.js +18 -1
- package/dist/services/traceInsights.js.map +1 -1
- package/dist/settings.d.ts +1 -1
- package/dist/settings.d.ts.map +1 -1
- package/dist/types.d.ts +26 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/vault/store.d.ts +1 -1
- package/dist/vault/store.d.ts.map +1 -1
- package/dist/webhooks/mount.d.ts.map +1 -1
- package/dist/whiteboard/store.d.ts +40 -0
- package/dist/whiteboard/store.d.ts.map +1 -0
- package/dist/whiteboard/store.js +280 -0
- package/dist/whiteboard/store.js.map +1 -0
- package/dist/whiteboard/types.d.ts +55 -0
- package/dist/whiteboard/types.d.ts.map +1 -0
- package/dist/whiteboard/types.js +9 -0
- package/dist/whiteboard/types.js.map +1 -0
- package/dist/whiteboard/weight.d.ts +23 -0
- package/dist/whiteboard/weight.d.ts.map +1 -0
- package/dist/whiteboard/weight.js +126 -0
- package/dist/whiteboard/weight.js.map +1 -0
- package/package.json +2 -2
- package/public/avatar/cache/0cdaf9c41eff4347.mp4 +0 -0
- package/public/avatar/cache/3dacc4ea1082ae36.mp4 +0 -0
- package/public/avatar/cache/44f5db0bfdde93c6.mp4 +0 -0
- package/public/avatar/cache/5628fd10fe55e529.mp4 +0 -0
- package/public/avatar/cache/7ee2ab1577690c8a.mp4 +0 -0
- package/public/avatar/cache/8c470929e814b6b0.mp4 +0 -0
- package/public/avatar/cache/8c908421ce52bf91.mp4 +0 -0
- package/public/avatar/cache/9532f8782a42a89c.mp4 +0 -0
- package/public/avatar/cache/9ce0dddd0cc4d7a1.mp4 +0 -0
- package/public/avatar/cache/a6508e00b6711143.mp4 +0 -0
- package/public/avatar/cache/ba61810a8915e0c7.mp4 +0 -0
- package/public/avatar/cache/c07bee3a10c917cf.mp4 +0 -0
- package/public/avatar/cache/d69175900ea4ea2a.mp4 +0 -0
- package/public/avatar/cache/e61039bc8d39cb93.mp4 +0 -0
- package/public/avatar/cache/e61c6b7047e2cbdb.mp4 +0 -0
- package/public/avatar/cache/efd93c9b18930cf6.mp4 +0 -0
- package/public/avatar/cache/f052d74f5c4abab7.mp4 +0 -0
- package/public/avatar/cache/f7c0be3429a4ef97.mp4 +0 -0
- package/public/avatar/cache/fc8e480f63fe4e35.mp4 +0 -0
- package/public/index.html +225 -51
- package/public/search-flyout.js +324 -0
- package/public/whiteboard.html +915 -0
- package/public/avatar/cache/06fa55aececcc478.mp4 +0 -0
- package/public/avatar/cache/07a65738ba170827.mp4 +0 -0
- package/public/avatar/cache/1185fd491f413406.mp4 +0 -0
- package/public/avatar/cache/272c004a41087de5.mp4 +0 -0
- package/public/avatar/cache/332384e088ca214b.mp4 +0 -0
- package/public/avatar/cache/5d9a960bbf71732c.mp4 +0 -0
- package/public/avatar/cache/5e0954401e15af89.mp4 +0 -0
- package/public/avatar/cache/b35f7a3d558f22cb.mp4 +0 -0
- package/public/avatar/cache/be89f49970672374.mp4 +0 -0
- package/public/avatar/cache/c900811e3382ac6d.mp4 +0 -0
- package/public/avatar/cache/d42a73667acf5716.mp4 +0 -0
- package/public/avatar/cache/e539f247a8908603.mp4 +0 -0
- package/public/avatar/cache/ec95af57d33b3f07.mp4 +0 -0
package/public/index.html
CHANGED
|
@@ -1646,6 +1646,33 @@
|
|
|
1646
1646
|
|
|
1647
1647
|
/* Agent card wrapper — sits in chat flow like a message */
|
|
1648
1648
|
/* Agent block — inline in assistant message, Claude Code style */
|
|
1649
|
+
/* --- Tool call indicators --- */
|
|
1650
|
+
.tool-indicator {
|
|
1651
|
+
display: inline-flex;
|
|
1652
|
+
align-items: center;
|
|
1653
|
+
gap: 6px;
|
|
1654
|
+
padding: 4px 10px;
|
|
1655
|
+
margin: 4px 0;
|
|
1656
|
+
font-size: 12px;
|
|
1657
|
+
color: var(--text-dim);
|
|
1658
|
+
background: rgba(109,93,252,0.08);
|
|
1659
|
+
border-radius: 6px;
|
|
1660
|
+
font-family: var(--mono);
|
|
1661
|
+
}
|
|
1662
|
+
.tool-indicator strong { color: var(--accent); font-weight: 500; }
|
|
1663
|
+
.tool-indicator.tool-done { color: var(--green); background: rgba(34,197,94,0.08); }
|
|
1664
|
+
.tool-indicator.tool-error { color: #e57373; background: rgba(229,115,115,0.08); }
|
|
1665
|
+
.tool-spinner {
|
|
1666
|
+
display: inline-block;
|
|
1667
|
+
width: 12px;
|
|
1668
|
+
height: 12px;
|
|
1669
|
+
border: 2px solid rgba(109,93,252,0.3);
|
|
1670
|
+
border-top-color: var(--accent);
|
|
1671
|
+
border-radius: 50%;
|
|
1672
|
+
animation: tool-spin 0.6s linear infinite;
|
|
1673
|
+
}
|
|
1674
|
+
@keyframes tool-spin { to { transform: rotate(360deg); } }
|
|
1675
|
+
|
|
1649
1676
|
.agent-block {
|
|
1650
1677
|
margin: 8px 0;
|
|
1651
1678
|
border: 1px solid rgba(168,85,247,0.15);
|
|
@@ -2675,10 +2702,12 @@
|
|
|
2675
2702
|
<a href="/" style="color:inherit;text-decoration:none;font-weight:600;" id="agent-name-header">Core</a>
|
|
2676
2703
|
<nav class="header-nav" id="header-nav">
|
|
2677
2704
|
<a href="/" class="active">Chat</a>
|
|
2705
|
+
<a href="/whiteboard">Whiteboard</a>
|
|
2678
2706
|
</nav>
|
|
2679
2707
|
<button class="thread-toggle-btn" id="thread-toggle-btn" title="Toggle chats">☰</button>
|
|
2680
2708
|
<span class="thread-current-label" id="thread-current-label" title="Click to toggle chats"></span>
|
|
2681
2709
|
<span class="spacer"></span>
|
|
2710
|
+
<button class="gear-btn" id="search-flyout-btn" onclick="openSearchFlyout()" title="Search (Ctrl+K)"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg></button>
|
|
2682
2711
|
<button class="gear-btn" id="mobile-pair-btn" onclick="openMobileModal()" title="Go Mobile"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="5" y="2" width="14" height="20" rx="2" ry="2"/><line x1="12" y1="18" x2="12.01" y2="18"/></svg></button>
|
|
2683
2712
|
<button class="gear-btn" id="share-btn" onclick="nativeShare()" title="Share"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="18" cy="5" r="3"/><circle cx="6" cy="12" r="3"/><circle cx="18" cy="19" r="3"/><line x1="8.59" y1="13.51" x2="15.42" y2="17.49"/><line x1="15.41" y1="6.51" x2="8.59" y2="10.49"/></svg></button>
|
|
2684
2713
|
<button class="gear-btn" id="theme-toggle-btn" title="Toggle theme"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="5"/><line x1="12" y1="1" x2="12" y2="3"/><line x1="12" y1="21" x2="12" y2="23"/><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/><line x1="1" y1="12" x2="3" y2="12"/><line x1="21" y1="12" x2="23" y2="12"/><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/></svg></button>
|
|
@@ -2923,7 +2952,7 @@
|
|
|
2923
2952
|
</div>
|
|
2924
2953
|
<!-- Model selection (always visible — local tier needs this) -->
|
|
2925
2954
|
<div class="settings-section" id="llm-settings-section">
|
|
2926
|
-
<h3 class="settings-section-title">Model <a href="https://ollama.com/library" target="_blank" rel="noopener" style="font-size:11px;font-weight:400;color:var(--accent);text-decoration:none;margin-left:6px;">
|
|
2955
|
+
<h3 class="settings-section-title">Model <a href="https://ollama.com/library" target="_blank" rel="noopener" style="font-size:11px;font-weight:400;color:var(--accent);text-decoration:none;margin-left:6px;">Ollama →</a> <a href="https://openrouter.ai/models" target="_blank" rel="noopener" style="font-size:11px;font-weight:400;color:var(--accent);text-decoration:none;margin-left:6px;">OpenRouter →</a></h3>
|
|
2927
2956
|
<div class="model-field">
|
|
2928
2957
|
<label>Chat model</label>
|
|
2929
2958
|
<select id="model-chat" style="flex:1;padding:8px 10px;border-radius:6px;border:1px solid var(--border);background:var(--bg);color:var(--text);font-size:14px;font-family:inherit;">
|
|
@@ -3274,12 +3303,6 @@
|
|
|
3274
3303
|
|
|
3275
3304
|
function renderThreadList() {
|
|
3276
3305
|
threadListEl.innerHTML = "";
|
|
3277
|
-
// Add "Main chat" as the default option
|
|
3278
|
-
const mainItem = document.createElement("div");
|
|
3279
|
-
mainItem.className = "thread-item" + (currentThreadId === null ? " active" : "");
|
|
3280
|
-
mainItem.innerHTML = '<div class="thread-item-title">Main</div><div class="thread-item-meta">Always here</div>';
|
|
3281
|
-
mainItem.addEventListener("click", () => switchThread(null));
|
|
3282
|
-
threadListEl.appendChild(mainItem);
|
|
3283
3306
|
|
|
3284
3307
|
for (const t of threads) {
|
|
3285
3308
|
const item = document.createElement("div");
|
|
@@ -3301,7 +3324,7 @@
|
|
|
3301
3324
|
await api("/api/threads/" + encodeURIComponent(tid) + "?sessionId=" + encodeURIComponent(sessionId), {
|
|
3302
3325
|
method: "DELETE",
|
|
3303
3326
|
});
|
|
3304
|
-
if (currentThreadId === tid) await
|
|
3327
|
+
if (currentThreadId === tid) await createAndSwitchNewThread();
|
|
3305
3328
|
loadThreads();
|
|
3306
3329
|
} catch (err) { console.log("Delete thread failed:", err.message); }
|
|
3307
3330
|
});
|
|
@@ -3346,7 +3369,7 @@
|
|
|
3346
3369
|
updateThreadLabel();
|
|
3347
3370
|
|
|
3348
3371
|
if (threadId) {
|
|
3349
|
-
// Load thread
|
|
3372
|
+
// Load thread history
|
|
3350
3373
|
try {
|
|
3351
3374
|
const data = await api("/api/threads/" + encodeURIComponent(threadId) + "/history?sessionId=" + encodeURIComponent(sessionId));
|
|
3352
3375
|
if (data.messages) {
|
|
@@ -3355,29 +3378,25 @@
|
|
|
3355
3378
|
if (typeof content === "string" && content.includes("[AGENT_REQUEST]")) {
|
|
3356
3379
|
content = content.replace(/\s*\[AGENT_REQUEST\][\s\S]*?\[\/AGENT_REQUEST\]\s*/g, "").trim();
|
|
3357
3380
|
}
|
|
3358
|
-
addMessage(msg.role === "user" ? "user" : "assistant", content);
|
|
3359
|
-
|
|
3360
|
-
|
|
3361
|
-
|
|
3362
|
-
|
|
3363
|
-
|
|
3364
|
-
|
|
3365
|
-
|
|
3366
|
-
|
|
3367
|
-
|
|
3368
|
-
if (data.messages) {
|
|
3369
|
-
for (const msg of data.messages) {
|
|
3370
|
-
let content = msg.content;
|
|
3371
|
-
if (typeof content === "string" && content.includes("[AGENT_REQUEST]")) {
|
|
3372
|
-
content = content.replace(/\s*\[AGENT_REQUEST\][\s\S]*?\[\/AGENT_REQUEST\]\s*/g, "").trim();
|
|
3381
|
+
var el = addMessage(msg.role === "user" ? "user" : "assistant", content);
|
|
3382
|
+
if (msg.toolsUsed && msg.toolsUsed.length > 0 && el) {
|
|
3383
|
+
var th = msg.toolsUsed.map(function(t) {
|
|
3384
|
+
var c2 = t.isError ? "tool-indicator tool-error" : "tool-indicator tool-done";
|
|
3385
|
+
return '<div class="' + c2 + '">' + (t.isError ? "✗ " : "✓ ") + '<strong>' + t.name.replace(/</g,"<") + '</strong></div>';
|
|
3386
|
+
}).join("");
|
|
3387
|
+
var td = document.createElement("div");
|
|
3388
|
+
td.innerHTML = th;
|
|
3389
|
+
var cd = el.querySelector(".content");
|
|
3390
|
+
if (cd && cd.parentElement) cd.parentElement.insertBefore(td, cd);
|
|
3373
3391
|
}
|
|
3374
|
-
|
|
3392
|
+
renderPersistedAgents(el, msg.agentsUsed);
|
|
3375
3393
|
}
|
|
3376
3394
|
}
|
|
3377
3395
|
} catch (err) {
|
|
3378
|
-
console.log("Failed to load history:", err.message);
|
|
3396
|
+
console.log("Failed to load thread history:", err.message);
|
|
3379
3397
|
}
|
|
3380
3398
|
}
|
|
3399
|
+
// If no threadId, just show empty — user will get a thread on first message
|
|
3381
3400
|
}
|
|
3382
3401
|
|
|
3383
3402
|
function updateThreadLabel() {
|
|
@@ -3385,11 +3404,11 @@
|
|
|
3385
3404
|
const t = threads.find(t => t.id === currentThreadId);
|
|
3386
3405
|
threadCurrentLabel.textContent = t ? t.title : "Chat";
|
|
3387
3406
|
} else {
|
|
3388
|
-
threadCurrentLabel.textContent = "";
|
|
3407
|
+
threadCurrentLabel.textContent = "New chat";
|
|
3389
3408
|
}
|
|
3390
3409
|
}
|
|
3391
3410
|
|
|
3392
|
-
|
|
3411
|
+
async function createAndSwitchNewThread() {
|
|
3393
3412
|
try {
|
|
3394
3413
|
const data = await fetch("/api/threads", {
|
|
3395
3414
|
method: "POST",
|
|
@@ -3400,15 +3419,19 @@
|
|
|
3400
3419
|
if (data.thread) {
|
|
3401
3420
|
await loadThreads();
|
|
3402
3421
|
await switchThread(data.thread.id);
|
|
3422
|
+
return data.thread.id;
|
|
3403
3423
|
}
|
|
3404
3424
|
} catch (err) {
|
|
3405
3425
|
console.error("Failed to create thread:", err.message);
|
|
3406
3426
|
}
|
|
3407
|
-
|
|
3427
|
+
return null;
|
|
3428
|
+
}
|
|
3429
|
+
|
|
3430
|
+
threadNewBtn.addEventListener("click", () => createAndSwitchNewThread());
|
|
3408
3431
|
|
|
3409
3432
|
// --- Auto-title chats after first exchange ---
|
|
3410
3433
|
var chatAutoTitled = {}; // threadId → true if already titled
|
|
3411
|
-
function autoTitleChat(threadId) {
|
|
3434
|
+
async function autoTitleChat(threadId) {
|
|
3412
3435
|
if (!threadId || chatAutoTitled[threadId]) return;
|
|
3413
3436
|
// Skip if already has a real title
|
|
3414
3437
|
var existing = threads.find(function(x) { return x.id === threadId; });
|
|
@@ -3443,18 +3466,19 @@
|
|
|
3443
3466
|
|
|
3444
3467
|
chatAutoTitled[threadId] = true;
|
|
3445
3468
|
|
|
3446
|
-
// Update server
|
|
3447
|
-
|
|
3448
|
-
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
|
|
3469
|
+
// Update server — await so loadThreads() doesn't race
|
|
3470
|
+
try {
|
|
3471
|
+
await fetch("/api/threads/" + encodeURIComponent(threadId), {
|
|
3472
|
+
method: "PATCH",
|
|
3473
|
+
headers: { "Content-Type": "application/json", ...authHeaders() },
|
|
3474
|
+
body: JSON.stringify({ sessionId: sessionId, title: title }),
|
|
3475
|
+
});
|
|
3452
3476
|
// Update local state and sidebar
|
|
3453
3477
|
var t = threads.find(function(x) { return x.id === threadId; });
|
|
3454
3478
|
if (t) t.title = title;
|
|
3455
3479
|
renderThreadList();
|
|
3456
3480
|
updateThreadLabel();
|
|
3457
|
-
}
|
|
3481
|
+
} catch (e) { /* ignore title failures */ }
|
|
3458
3482
|
}
|
|
3459
3483
|
|
|
3460
3484
|
// --- Attached files ---
|
|
@@ -4169,7 +4193,18 @@
|
|
|
4169
4193
|
if (content.includes("[AGENT_REQUEST]")) {
|
|
4170
4194
|
content = content.replace(/\s*\[AGENT_REQUEST\][\s\S]*?\[\/AGENT_REQUEST\]\s*/g, "").trim();
|
|
4171
4195
|
}
|
|
4172
|
-
addMessage(msg.role === "user" ? "user" : "assistant", content);
|
|
4196
|
+
var hEl = addMessage(msg.role === "user" ? "user" : "assistant", content);
|
|
4197
|
+
if (msg.toolsUsed && msg.toolsUsed.length > 0 && hEl) {
|
|
4198
|
+
var htHtml = msg.toolsUsed.map(function(t) {
|
|
4199
|
+
var hcls = t.isError ? "tool-indicator tool-error" : "tool-indicator tool-done";
|
|
4200
|
+
return '<div class="' + hcls + '">' + (t.isError ? "✗ " : "✓ ") + '<strong>' + t.name.replace(/</g,"<") + '</strong></div>';
|
|
4201
|
+
}).join("");
|
|
4202
|
+
var htDiv = document.createElement("div");
|
|
4203
|
+
htDiv.innerHTML = htHtml;
|
|
4204
|
+
var hcDiv = hEl.querySelector(".content");
|
|
4205
|
+
if (hcDiv && hcDiv.parentElement) hcDiv.parentElement.insertBefore(htDiv, hcDiv);
|
|
4206
|
+
}
|
|
4207
|
+
renderPersistedAgents(hEl, msg.agentsUsed);
|
|
4173
4208
|
}
|
|
4174
4209
|
}
|
|
4175
4210
|
} catch (err) {
|
|
@@ -4232,6 +4267,10 @@
|
|
|
4232
4267
|
if (thread) await switchThread(thread);
|
|
4233
4268
|
if (scroll && messagesEl) requestAnimationFrame(() => { messagesEl.scrollTop = scroll; });
|
|
4234
4269
|
}
|
|
4270
|
+
// Ensure we always have a thread — auto-select the most recent or create one
|
|
4271
|
+
if (!currentThreadId && threads.length > 0) {
|
|
4272
|
+
await switchThread(threads[0].id);
|
|
4273
|
+
}
|
|
4235
4274
|
} catch (e) { console.log("Restore failed:", e); }
|
|
4236
4275
|
|
|
4237
4276
|
// Initialize voice features (non-blocking)
|
|
@@ -4857,7 +4896,20 @@
|
|
|
4857
4896
|
if (m.role === "user" && m.source === "phone") {
|
|
4858
4897
|
label = (userName || "You") + " (phone)";
|
|
4859
4898
|
}
|
|
4860
|
-
addMessage(m.role === "user" ? "user" : "assistant", m.content, label);
|
|
4899
|
+
var msgEl = addMessage(m.role === "user" ? "user" : "assistant", m.content, label);
|
|
4900
|
+
// Render persisted tool usage indicators
|
|
4901
|
+
if (m.toolsUsed && m.toolsUsed.length > 0 && msgEl) {
|
|
4902
|
+
var toolsHtml = m.toolsUsed.map(function(t) {
|
|
4903
|
+
var cls = t.isError ? "tool-indicator tool-error" : "tool-indicator tool-done";
|
|
4904
|
+
var icon = t.isError ? "✗ " : "✓ ";
|
|
4905
|
+
return '<div class="' + cls + '">' + icon + '<strong>' + t.name.replace(/</g,"<") + '</strong></div>';
|
|
4906
|
+
}).join("");
|
|
4907
|
+
var toolsDiv = document.createElement("div");
|
|
4908
|
+
toolsDiv.innerHTML = toolsHtml;
|
|
4909
|
+
var contentDiv = msgEl.querySelector(".content");
|
|
4910
|
+
if (contentDiv && contentDiv.parentElement) contentDiv.parentElement.insertBefore(toolsDiv, contentDiv);
|
|
4911
|
+
}
|
|
4912
|
+
renderPersistedAgents(msgEl, m.agentsUsed);
|
|
4861
4913
|
}
|
|
4862
4914
|
chatPollIndex = data.total;
|
|
4863
4915
|
messagesEl.scrollTop = messagesEl.scrollHeight;
|
|
@@ -4993,9 +5045,23 @@
|
|
|
4993
5045
|
}
|
|
4994
5046
|
|
|
4995
5047
|
try {
|
|
4996
|
-
|
|
5048
|
+
// Ensure we always have a thread — auto-create silently if needed
|
|
5049
|
+
if (!currentThreadId) {
|
|
5050
|
+
try {
|
|
5051
|
+
const td = await fetch("/api/threads", {
|
|
5052
|
+
method: "POST",
|
|
5053
|
+
headers: { "Content-Type": "application/json", ...authHeaders() },
|
|
5054
|
+
body: JSON.stringify({ sessionId }),
|
|
5055
|
+
}).then(r => r.json());
|
|
5056
|
+
if (td.thread) {
|
|
5057
|
+
currentThreadId = td.thread.id;
|
|
5058
|
+
loadThreads(); // refresh sidebar (fire-and-forget)
|
|
5059
|
+
}
|
|
5060
|
+
} catch {}
|
|
5061
|
+
if (!currentThreadId) { addMessage("system", "Failed to create chat thread."); return; }
|
|
5062
|
+
}
|
|
5063
|
+
const payload = { sessionId, message: msgText, threadId: currentThreadId };
|
|
4997
5064
|
if (imgPayload) payload.images = imgPayload;
|
|
4998
|
-
if (currentThreadId) payload.threadId = currentThreadId;
|
|
4999
5065
|
|
|
5000
5066
|
const res = await fetch("/api/chat", {
|
|
5001
5067
|
method: "POST",
|
|
@@ -5065,6 +5131,25 @@
|
|
|
5065
5131
|
contentEl.innerHTML = renderStreamingMarkdown(streamingText);
|
|
5066
5132
|
messagesEl.scrollTop = messagesEl.scrollHeight;
|
|
5067
5133
|
}
|
|
5134
|
+
if (data.toolCall) {
|
|
5135
|
+
// Model is calling a tool — show indicator
|
|
5136
|
+
var toolEl = document.createElement("div");
|
|
5137
|
+
toolEl.className = "tool-indicator";
|
|
5138
|
+
toolEl.innerHTML = '<span class="tool-spinner"></span> Using <strong>' + (data.toolCall.name || "tool").replace(/</g,"<") + '</strong>...';
|
|
5139
|
+
if (contentEl && contentEl.parentElement) {
|
|
5140
|
+
contentEl.parentElement.appendChild(toolEl);
|
|
5141
|
+
messagesEl.scrollTop = messagesEl.scrollHeight;
|
|
5142
|
+
}
|
|
5143
|
+
}
|
|
5144
|
+
if (data.toolResult) {
|
|
5145
|
+
// Tool finished — update indicator
|
|
5146
|
+
var indicators = document.querySelectorAll(".tool-indicator");
|
|
5147
|
+
if (indicators.length > 0) {
|
|
5148
|
+
var last = indicators[indicators.length - 1];
|
|
5149
|
+
last.innerHTML = (data.toolResult.isError ? "✗ " : "✓ ") + '<strong>' + (data.toolResult.name || "tool").replace(/</g,"<") + '</strong>';
|
|
5150
|
+
last.className = "tool-indicator " + (data.toolResult.isError ? "tool-error" : "tool-done");
|
|
5151
|
+
}
|
|
5152
|
+
}
|
|
5068
5153
|
if (data.agentSpawned) {
|
|
5069
5154
|
// Server spawned an agent — render block immediately with real task ID
|
|
5070
5155
|
console.log("[agent] SSE agentSpawned:", data.agentSpawned);
|
|
@@ -5203,9 +5288,9 @@
|
|
|
5203
5288
|
abortController = null;
|
|
5204
5289
|
setButtonSend();
|
|
5205
5290
|
chatInput.focus();
|
|
5206
|
-
// Auto-title chat after first assistant response
|
|
5291
|
+
// Auto-title chat after first assistant response, then refresh sidebar
|
|
5207
5292
|
if (currentThreadId) {
|
|
5208
|
-
autoTitleChat(currentThreadId);
|
|
5293
|
+
await autoTitleChat(currentThreadId);
|
|
5209
5294
|
loadThreads();
|
|
5210
5295
|
}
|
|
5211
5296
|
}
|
|
@@ -5265,6 +5350,69 @@
|
|
|
5265
5350
|
return s < 60 ? s + "s" : Math.floor(s/60) + "m " + (s%60) + "s";
|
|
5266
5351
|
}
|
|
5267
5352
|
|
|
5353
|
+
/** Create a static agent block for history rendering (already completed/failed). */
|
|
5354
|
+
function createPersistedAgentBlock(agent) {
|
|
5355
|
+
var isDone = agent.status === "completed";
|
|
5356
|
+
var isFailed = agent.status === "failed" || agent.status === "cancelled";
|
|
5357
|
+
var block = document.createElement("div");
|
|
5358
|
+
block.className = "agent-block " + (isDone ? "done" : isFailed ? "failed" : "");
|
|
5359
|
+
block.dataset.taskId = agent.taskId || "";
|
|
5360
|
+
var icon = isDone
|
|
5361
|
+
? '<span class="agent-block-check">\u2713</span>'
|
|
5362
|
+
: isFailed
|
|
5363
|
+
? '<span class="agent-block-fail">\u2717</span>'
|
|
5364
|
+
: '<div class="agent-block-spinner"></div>';
|
|
5365
|
+
var timerText = agent.elapsed || agent.status || "";
|
|
5366
|
+
block.innerHTML =
|
|
5367
|
+
'<div class="agent-block-header">'
|
|
5368
|
+
+ icon
|
|
5369
|
+
+ '<span class="agent-block-label">' + (agent.label || "Agent").replace(/</g, "<") + '</span>'
|
|
5370
|
+
+ '<span class="agent-block-timer">' + timerText + '</span>'
|
|
5371
|
+
+ '</div>'
|
|
5372
|
+
+ '<div class="agent-block-body">' + (agent.resultSummary || "").replace(/</g, "<") + '</div>';
|
|
5373
|
+
var header = block.querySelector(".agent-block-header");
|
|
5374
|
+
header.addEventListener("click", function() { block.classList.toggle("expanded"); });
|
|
5375
|
+
return block;
|
|
5376
|
+
}
|
|
5377
|
+
|
|
5378
|
+
/** Render agentsUsed blocks inside a message element. */
|
|
5379
|
+
function renderPersistedAgents(msgEl, agents) {
|
|
5380
|
+
if (!agents || agents.length === 0 || !msgEl) return;
|
|
5381
|
+
for (var i = 0; i < agents.length; i++) {
|
|
5382
|
+
var block = createPersistedAgentBlock(agents[i]);
|
|
5383
|
+
msgEl.appendChild(block);
|
|
5384
|
+
}
|
|
5385
|
+
// If any agents have taskIds and status is "running" or "completed" placeholder, poll once for real status
|
|
5386
|
+
var taskIds = agents.filter(function(a) { return a.taskId; }).map(function(a) { return a.taskId; });
|
|
5387
|
+
if (taskIds.length > 0) {
|
|
5388
|
+
api("/api/agents/tasks").then(function(data) {
|
|
5389
|
+
if (!data.tasks) return;
|
|
5390
|
+
for (var j = 0; j < data.tasks.length; j++) {
|
|
5391
|
+
var task = data.tasks[j];
|
|
5392
|
+
var blockEl = msgEl.querySelector('.agent-block[data-task-id="' + task.id + '"]');
|
|
5393
|
+
if (!blockEl) continue;
|
|
5394
|
+
var isDone = task.status === "completed";
|
|
5395
|
+
var isFailed = task.status === "failed" || task.status === "cancelled";
|
|
5396
|
+
var isRunning = task.status === "running" || task.status === "pending";
|
|
5397
|
+
blockEl.className = "agent-block " + (isDone ? "done" : isFailed ? "failed" : "");
|
|
5398
|
+
var spinnerEl = blockEl.querySelector(".agent-block-spinner, .agent-block-check, .agent-block-fail");
|
|
5399
|
+
if (spinnerEl) {
|
|
5400
|
+
spinnerEl.outerHTML = isDone
|
|
5401
|
+
? '<span class="agent-block-check">\u2713</span>'
|
|
5402
|
+
: isFailed
|
|
5403
|
+
? '<span class="agent-block-fail">\u2717</span>'
|
|
5404
|
+
: '<div class="agent-block-spinner"></div>';
|
|
5405
|
+
}
|
|
5406
|
+
if (isRunning) { blockEl.className = "agent-block"; startAgentPoll(); }
|
|
5407
|
+
var timerEl = blockEl.querySelector(".agent-block-timer");
|
|
5408
|
+
if (timerEl && !isRunning) timerEl.textContent = task.status;
|
|
5409
|
+
var bodyEl = blockEl.querySelector(".agent-block-body");
|
|
5410
|
+
if (bodyEl && task.resultSummary) bodyEl.textContent = task.resultSummary;
|
|
5411
|
+
}
|
|
5412
|
+
}).catch(function() {});
|
|
5413
|
+
}
|
|
5414
|
+
}
|
|
5415
|
+
|
|
5268
5416
|
async function pollAgentTasks() {
|
|
5269
5417
|
try {
|
|
5270
5418
|
var data = await api("/api/agents/tasks");
|
|
@@ -5595,6 +5743,9 @@
|
|
|
5595
5743
|
try {
|
|
5596
5744
|
const data = await api("/api/models");
|
|
5597
5745
|
const models = data.models || [];
|
|
5746
|
+
const ollamaModels = models.filter(function(m) { return m.source === "ollama"; });
|
|
5747
|
+
const openrouterModels = models.filter(function(m) { return m.source === "openrouter"; });
|
|
5748
|
+
|
|
5598
5749
|
var dropdowns = [
|
|
5599
5750
|
{ el: modelChatSelect, current: selectedChat, autoLabel: "Auto (best available)" },
|
|
5600
5751
|
{ el: modelUtilitySelect, current: selectedUtility, autoLabel: "Auto (best available)" },
|
|
@@ -5602,16 +5753,38 @@
|
|
|
5602
5753
|
];
|
|
5603
5754
|
for (var dd of dropdowns) {
|
|
5604
5755
|
dd.el.innerHTML = '<option value="auto">' + dd.autoLabel + '</option>';
|
|
5605
|
-
|
|
5606
|
-
|
|
5607
|
-
|
|
5608
|
-
|
|
5609
|
-
|
|
5610
|
-
|
|
5611
|
-
|
|
5756
|
+
|
|
5757
|
+
// Ollama models
|
|
5758
|
+
if (ollamaModels.length > 0) {
|
|
5759
|
+
var ollamaGroup = document.createElement("optgroup");
|
|
5760
|
+
ollamaGroup.label = "Local (Ollama)";
|
|
5761
|
+
for (const m of ollamaModels) {
|
|
5762
|
+
const opt = document.createElement("option");
|
|
5763
|
+
opt.value = m.name;
|
|
5764
|
+
const sizeMB = m.size ? Math.round(m.size / 1024 / 1024) : 0;
|
|
5765
|
+
opt.textContent = m.name + (sizeMB ? " (" + (sizeMB >= 1024 ? (sizeMB / 1024).toFixed(1) + "GB" : sizeMB + "MB") + ")" : "");
|
|
5766
|
+
if (m.name === dd.current) opt.selected = true;
|
|
5767
|
+
ollamaGroup.appendChild(opt);
|
|
5768
|
+
}
|
|
5769
|
+
dd.el.appendChild(ollamaGroup);
|
|
5612
5770
|
}
|
|
5771
|
+
|
|
5772
|
+
// OpenRouter models
|
|
5773
|
+
if (openrouterModels.length > 0) {
|
|
5774
|
+
var orGroup = document.createElement("optgroup");
|
|
5775
|
+
orGroup.label = "Cloud (OpenRouter)";
|
|
5776
|
+
for (const m of openrouterModels) {
|
|
5777
|
+
const opt = document.createElement("option");
|
|
5778
|
+
opt.value = m.name;
|
|
5779
|
+
opt.textContent = m.name;
|
|
5780
|
+
if (m.name === dd.current) opt.selected = true;
|
|
5781
|
+
orGroup.appendChild(opt);
|
|
5782
|
+
}
|
|
5783
|
+
dd.el.appendChild(orGroup);
|
|
5784
|
+
}
|
|
5785
|
+
|
|
5613
5786
|
// If the saved model isn't in the list, add it as an option anyway
|
|
5614
|
-
if (dd.current && dd.current !== "auto" && !models.some(m
|
|
5787
|
+
if (dd.current && dd.current !== "auto" && !models.some(function(m) { return m.name === dd.current; })) {
|
|
5615
5788
|
const opt = document.createElement("option");
|
|
5616
5789
|
opt.value = dd.current;
|
|
5617
5790
|
opt.textContent = dd.current + " (not found)";
|
|
@@ -6816,6 +6989,7 @@
|
|
|
6816
6989
|
</script>
|
|
6817
6990
|
|
|
6818
6991
|
<script src="/public/share-modal.js"></script>
|
|
6992
|
+
<script src="/public/search-flyout.js"></script>
|
|
6819
6993
|
|
|
6820
6994
|
<!-- UI update toast -->
|
|
6821
6995
|
<div class="ui-update-toast" id="ui-update-toast">
|