@symerian/symi 3.4.30 → 3.4.31
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/{agent-O3PihJOY.js → agent-ZFfYdexc.js} +10 -10
- package/dist/{agents-C2MpIr16.js → agents-C8naPegc.js} +5 -5
- package/dist/{audit-yf3H4Y1h.js → audit-DOfexJi4.js} +8 -8
- package/dist/{auth-choice-CyBcsQ9T.js → auth-choice-DxH-c3vK.js} +4 -4
- package/dist/{banner-D7__HlM-.js → banner-D7zYLyA8.js} +1 -1
- package/dist/{browser-cli-B_6WWHm_.js → browser-cli-MYIql1Ic.js} +3 -3
- package/dist/build-info.json +1 -1
- package/dist/bundled/boot-md/handler.js +4 -4
- package/dist/bundled/session-memory/handler.js +4 -4
- package/dist/{call-IfleM0ap.js → call-D9U2Sfhp.js} +1 -1
- package/dist/{channel-options-B5974x-7.js → channel-options-9PzSNQLG.js} +1 -1
- package/dist/{channels-cli-C6Nv9mzy.js → channels-cli-C3XnNkE5.js} +30 -30
- package/dist/{chrome-DT1fIVG1.js → chrome-B4P7ycw5.js} +7 -7
- package/dist/{chrome-DJCkCRLf.js → chrome-ClVIwINy.js} +1 -1
- package/dist/{cli-DZoms4qg.js → cli-DhTdzhjk.js} +26 -26
- package/dist/{command-registry--D98SJ6k.js → command-registry-BOwku-e8.js} +11 -11
- package/dist/{completion-cli-CHYvpIPw.js → completion-cli-KZYbGY4H.js} +2 -2
- package/dist/{config-BTSBEAnk.js → config-Byd2Y9rr.js} +5 -5
- package/dist/{config-cli-DstpcB33.js → config-cli-CJ1CRfG8.js} +5 -5
- package/dist/{config-guard-CCJrDmON.js → config-guard-zt0zhFHl.js} +2 -2
- package/dist/{config-validation-YCrMlM9Z.js → config-validation-CRazk6Kd.js} +1 -1
- package/dist/{configure-B-UvSZZ7.js → configure-CFY_XEps.js} +10 -10
- package/dist/{control-service-CwRG4M_O.js → control-service-DN6-IdFk.js} +4 -4
- package/dist/{cron-cli-DUETe3Of.js → cron-cli-fmiz43Pz.js} +3 -3
- package/dist/{daemon-cli-Dy5eY4M7.js → daemon-cli-BilAL40g.js} +6 -6
- package/dist/{daemon-runtime-ChztAKDA.js → daemon-runtime-D4KHzQq2.js} +1 -1
- package/dist/{deliver-59sRVaYQ.js → deliver-BkCYBlzi.js} +4 -4
- package/dist/{deliver-BHmK4isn.js → deliver-CnsfN7km.js} +1 -1
- package/dist/{devices-cli-BCZ2HHjT.js → devices-cli-D7HdOwRI.js} +2 -2
- package/dist/{directory-cli-BVGd0l0T.js → directory-cli-ed6QacBC.js} +1 -1
- package/dist/{dns-cli-DH-ksmiI.js → dns-cli-RPa1dGTQ.js} +1 -1
- package/dist/{docs-cli-BuX8FDdh.js → docs-cli-ZTdS_Gak.js} +2 -2
- package/dist/{doctor-completion-C4Sf8CFV.js → doctor-completion-DpGX8dau.js} +1 -1
- package/dist/{doctor-config-flow-BhmF6EAq.js → doctor-config-flow-CIrG_yW_.js} +4 -4
- package/dist/entry.js +3 -3
- package/dist/{exec-approvals-cli-CyXTXgft.js → exec-approvals-cli-65pcsa1y.js} +6 -6
- package/dist/{frontmatter-BH3ExkUY.js → frontmatter-B4levtVg.js} +2 -2
- package/dist/{gateway-cli-fT43bqlv.js → gateway-cli-CTi7Wl2k.js} +50 -50
- package/dist/{gateway-rpc-BEqCo2JK.js → gateway-rpc-2VPLhMhx.js} +1 -1
- package/dist/{glass-ui-ws-BfMKuf9v.js → glass-ui-ws-Bah_A-jn.js} +38 -38
- package/dist/{gmail-setup-utils-Cz9Tb30q.js → gmail-setup-utils-C9WBC6Cd.js} +1 -1
- package/dist/{health-DDxrkioj.js → health-B5rRzhyV.js} +3 -3
- package/dist/{hooks-cli-Btgk__9z.js → hooks-cli-CSDCGO-W.js} +29 -29
- package/dist/{hooks-status-BaV4_YaJ.js → hooks-status-OorbLcc2.js} +2 -2
- package/dist/index.js +0 -0
- package/dist/{lifecycle-core-B0tG8ERp.js → lifecycle-core-BRc-XqTk.js} +2 -2
- package/dist/llm-slug-generator.js +4 -4
- package/dist/{logs-cli-CUhZrDw7.js → logs-cli-D5phVUOT.js} +3 -3
- package/dist/{manager-BeNnzSJv.js → manager-BfhHvsI9.js} +1 -1
- package/dist/{manager-Di9qtuZF.js → manager-DzBH9uQG.js} +1 -1
- package/dist/{memory-BDeydRz3.js → memory-BCj0cj5v.js} +3 -3
- package/dist/{memory-cli-D4hZiGr5.js → memory-cli-9WUMBHol.js} +3 -3
- package/dist/{model-catalog-DPnCNs5N.js → model-catalog-CD2kTz4V.js} +2 -2
- package/dist/{model-picker-DuPommMc.js → model-picker-CVd5UuXV.js} +2 -2
- package/dist/{models-cli-CL5K06Wu.js → models-cli-rd4eX_6P.js} +30 -30
- package/dist/{models-config-CPlvO1b0.js → models-config-oxaGiRUT.js} +1 -1
- package/dist/{models-B0wNKA18.js → models-sbKIrJBG.js} +9 -9
- package/dist/{node-cli-CWWMajKF.js → node-cli-ClLXSdFL.js} +9 -9
- package/dist/{nodes-cli-YmE7S-JB.js → nodes-cli-DY38p5In.js} +3 -3
- package/dist/{onboard-HqUZVKyD.js → onboard-BAyp_UFm.js} +7 -7
- package/dist/{onboard-channels-BbF5-N3g.js → onboard-channels-CUpkV_IJ.js} +1 -1
- package/dist/{onboard-custom-63SI3jrk.js → onboard-custom-BMgLK-vo.js} +2 -2
- package/dist/{onboard-helpers-Cb3WFLl9.js → onboard-helpers-DIQjpQ-s.js} +2 -2
- package/dist/{onboard-hooks-DXXg3YH9.js → onboard-hooks-KZKmj5Dk.js} +3 -3
- package/dist/{onboard-remote-CqtCrqtm.js → onboard-remote-C-_NUgBl.js} +1 -1
- package/dist/{onboard-skills-CS3l5fhH.js → onboard-skills-CwvicFTi.js} +3 -3
- package/dist/{onboarding-Be_HJIFA.js → onboarding-BxCNNo75.js} +11 -11
- package/dist/{onboarding.finalize-dsCUTKCQ.js → onboarding.finalize-DgMPXTtO.js} +18 -18
- package/dist/{onboarding.gateway-config-Ctd1_QW4.js → onboarding.gateway-config-BAaWvPSX.js} +4 -4
- package/dist/{outbound-send-deps-DPsU_Xdy.js → outbound-send-deps-qhFaNYAY.js} +1 -1
- package/dist/{pairing-cli-PtlNv7Gr.js → pairing-cli-CFkiSx8t.js} +1 -1
- package/dist/{pi-embedded-helpers-CVKIHnFv.js → pi-embedded-helpers-CRG5LMS7.js} +1 -1
- package/dist/{pi-tools.policy-8_wTu7v2.js → pi-tools.policy-De6xHjVL.js} +2 -2
- package/dist/{plugin-registry-BG51uZOA.js → plugin-registry-BNe5jtPK.js} +2 -2
- package/dist/plugin-sdk/index.js +6 -6
- package/dist/{plugins-cli-B-zVOiQN.js → plugins-cli-CWj33_Hj.js} +27 -27
- package/dist/{program-BOh0XGsA.js → program-Z2OLlhsB.js} +33 -33
- package/dist/{prompt-select-styled-Bj-hrPCx.js → prompt-select-styled-BNwbBBcH.js} +16 -16
- package/dist/{provider-auth-helpers-BK6NFk2Q.js → provider-auth-helpers-5qb15Hje.js} +2 -2
- package/dist/{push-apns-CMxk3Hxf.js → push-apns-D6qV5P4h.js} +1 -1
- package/dist/{pw-ai-C5MJKzUM.js → pw-ai-BsEf8C15.js} +1 -1
- package/dist/{pw-ai-CE2TBGif.js → pw-ai-C7mvVllB.js} +2 -2
- package/dist/{qr-cli-BeHlC33g.js → qr-cli-oENcXq8d.js} +1 -1
- package/dist/{redact-identifier-B1VHIbnd.js → redact-identifier-ZE8OIUof.js} +1 -1
- package/dist/{register.agent-COv5OPLQ.js → register.agent-DpiHPMca.js} +36 -36
- package/dist/{register.configure-RhKVlfWH.js → register.configure-DRFaroYF.js} +40 -40
- package/dist/{register.maintenance-BAK-DiQY.js → register.maintenance-BGNNTt_1.js} +38 -38
- package/dist/{register.message-CCBmn5ja.js → register.message-R4AIHhlU.js} +28 -28
- package/dist/{register.onboard-B3jO_Goy.js → register.onboard-Cl-_Suyv.js} +13 -13
- package/dist/{register.setup-CYSISTDO.js → register.setup-Dt98v2V3.js} +15 -15
- package/dist/{register.status-health-sessions-CGbg9k6d.js → register.status-health-sessions-DiX5lVyr.js} +23 -23
- package/dist/{register.subclis-loifbhVx.js → register.subclis-DVObuR_P.js} +28 -28
- package/dist/{replies-Dj640LxQ.js → replies-VTJu1jUK.js} +1 -1
- package/dist/{routes-B5y-oAKp.js → routes-DOFg6X5M.js} +3 -3
- package/dist/{rpc-qWm1bRo5.js → rpc-BIHKXdlN.js} +1 -1
- package/dist/{run-main-B7Ria3zD.js → run-main-NAgYKzSu.js} +44 -44
- package/dist/{sandbox-BJgHc4Vu.js → sandbox-EB7cFSML.js} +6 -6
- package/dist/{sandbox-cli-CSudrHYL.js → sandbox-cli-D4dy6sUK.js} +8 -8
- package/dist/{security-cli-B2lCR2xX.js → security-cli-DwCKOrFm.js} +11 -11
- package/dist/{send-CY9na-Jw.js → send-CkRtuQbr.js} +1 -1
- package/dist/{server-context-DdWWaDf6.js → server-context-ww1jytet.js} +5 -5
- package/dist/{server-methods-Dk_EnINI.js → server-methods-BDiZyPvS.js} +23 -23
- package/dist/{server-node-events-BHegSgG9.js → server-node-events-9cY40B4m.js} +29 -29
- package/dist/{session-utils-CAVPV2Hh.js → session-utils-CStjqGE6.js} +3 -3
- package/dist/{sessions-BDM9kNY0.js → sessions-BSRHJH8T.js} +3 -3
- package/dist/{sessions-CqLQkkWg.js → sessions-CInbrqsF.js} +1 -1
- package/dist/{shared-CpwtQRJB.js → shared-DEAo94kI.js} +1 -1
- package/dist/{skill-commands-CRQniaHy.js → skill-commands-BkKfrbHt.js} +2 -2
- package/dist/{skills-CTcjFd_F.js → skills-CFLijAZ-.js} +1 -1
- package/dist/{skills-cli-9Mlb7VRg.js → skills-cli-BbagnO6p.js} +5 -5
- package/dist/{skills-install-D5ikt72d.js → skills-install-dLRO1KGp.js} +2 -2
- package/dist/{skills-remote-BASG1Doo.js → skills-remote-DQVB-3Pj.js} +1 -1
- package/dist/{skills-status-1iVfuVIG.js → skills-status-HWybfj0K.js} +2 -2
- package/dist/{status-Bci6O3aA.js → status-BSrVEUoJ.js} +2 -2
- package/dist/{status-D5RhVO0A.js → status-FIp7UKru.js} +12 -12
- package/dist/{status.update-D5CachQ_.js → status.update-Cm8Fm8QD.js} +1 -1
- package/dist/{subagent-registry-CsXaK_sy.js → subagent-registry-DqryocLT.js} +31 -31
- package/dist/{synthesis-dplLmFIP.js → synthesis-CS-lrEGy.js} +4 -4
- package/dist/{synthesis-BoZf8BeO.js → synthesis-DKhH0YwU.js} +26 -26
- package/dist/{system-cli-D5FffFX5.js → system-cli-DYSI6cIB.js} +3 -3
- package/dist/{systemd-hints-CufcSfMk.js → systemd-hints-EIfW5y8K.js} +1 -1
- package/dist/{tui-DZ8gksSZ.js → tui-C20rHOeB.js} +4 -4
- package/dist/{tui-cli-CyBJYh9u.js → tui-cli-JwlzHCza.js} +11 -11
- package/dist/{unified-runner-BFLLzEHE.js → unified-runner-AM9tFC5q.js} +10 -10
- package/dist/{update-cli-yW6Xl6fs.js → update-cli-BYfA3RPc.js} +41 -41
- package/dist/{update-runner-CIgLduhH.js → update-runner-CBG8p9iG.js} +1 -1
- package/dist/{webhooks-cli-4bmyKzwb.js → webhooks-cli-MuG_QGLp.js} +4 -4
- package/dist/{with-timeout-bWr6yBeX.js → with-timeout-BvxaeAo6.js} +1 -1
- package/dist/{workspace-DxscDsm6.js → workspace-D0d7Gi4-.js} +1 -1
- package/extensions/memory-core/node_modules/.bin/symi +0 -0
- package/extensions/msteams/node_modules/.bin/symi +0 -0
- package/extensions/outlook/node_modules/.bin/symi +0 -0
- package/extensions/slack/node_modules/.bin/symi +0 -0
- package/package.json +108 -79
- package/dist/control-ui/css/revert-red-theme.md +0 -141
- package/dist/control-ui/css/style.css +0 -5843
- package/dist/control-ui/css/style.css.backup-2026-03-03-162525 +0 -3546
- package/dist/control-ui/css/style.css.backup-before-red-2026-03-03-162525 +0 -3546
- package/dist/control-ui/css/style.css.backup-before-red-theme-2026-03-03-162530 +0 -3546
- package/dist/control-ui/css/style.css.pre-2row +0 -2165
- package/dist/control-ui/css/style.css.pre-brand +0 -1776
- package/dist/control-ui/css/style.css.pre-history +0 -1974
- package/dist/control-ui/css/style.css.pre-nav +0 -2264
- package/dist/control-ui/css/style.css.pre-newsession +0 -1898
- package/dist/control-ui/css/style.css.pre-queue +0 -2195
- package/dist/control-ui/css/style.css.pre-red-prompt +0 -2524
- package/dist/control-ui/css/style.css.pre-stop +0 -2239
- package/dist/control-ui/css/style.css.pre-textarea +0 -2184
- package/dist/control-ui/css/style.css.pre-watchdog +0 -1848
- package/dist/control-ui/css/style.css.red-theme +0 -2999
- package/dist/control-ui/index.html +0 -1042
- package/dist/control-ui/js/app.js +0 -1304
- package/dist/control-ui/js/app.js.pre-2row +0 -463
- package/dist/control-ui/js/app.js.pre-heartbeat-filter +0 -595
- package/dist/control-ui/js/app.js.pre-newsession +0 -408
- package/dist/control-ui/js/app.js.pre-queue +0 -476
- package/dist/control-ui/js/app.js.pre-stop +0 -564
- package/dist/control-ui/js/app.js.pre-textarea +0 -467
- package/dist/control-ui/js/app.js.pre-watchdog +0 -293
- package/dist/control-ui/js/connections.js +0 -438
- package/dist/control-ui/js/gateway.js +0 -233
- package/dist/control-ui/js/gateway.js.pre-stop +0 -110
- package/dist/control-ui/js/history.js +0 -732
- package/dist/control-ui/js/logs.js +0 -238
- package/dist/control-ui/js/menu.js +0 -230
- package/dist/control-ui/js/menu.js.pre-nav +0 -66
- package/dist/control-ui/js/metrics.js +0 -53
- package/dist/control-ui/js/models.js +0 -138
- package/dist/control-ui/js/render.js +0 -882
- package/dist/control-ui/js/render.test.js +0 -112
- package/dist/control-ui/js/scheduling.js +0 -461
- package/dist/control-ui/js/settings.js +0 -909
- package/dist/control-ui/js/slash-autocomplete.js +0 -168
- package/dist/control-ui/js/subagents.js +0 -560
- package/dist/control-ui/js/utils.js +0 -29
- package/dist/control-ui/vendor/highlight.min.js +0 -2518
- package/dist/control-ui/vendor/marked.min.js +0 -69
|
@@ -1,467 +0,0 @@
|
|
|
1
|
-
// ── Symi UI — Live Gateway App ────────────────────────────────────────
|
|
2
|
-
|
|
3
|
-
const responseArea = document.getElementById('response-area');
|
|
4
|
-
const promptInput = document.getElementById('prompt-input');
|
|
5
|
-
const sendBtn = document.getElementById('send-btn');
|
|
6
|
-
const newSessionBtn = document.getElementById('new-session-btn');
|
|
7
|
-
|
|
8
|
-
let isStreaming = false;
|
|
9
|
-
let currentRunId = null;
|
|
10
|
-
let streamBubble = null;
|
|
11
|
-
let streamContent = null;
|
|
12
|
-
let streamStart = 0;
|
|
13
|
-
|
|
14
|
-
// ── Watchdog + elapsed timer state ────────────────────────────────
|
|
15
|
-
let watchdogTimer = null; // fires if no activity for WATCHDOG_MS
|
|
16
|
-
let elapsedTimer = null; // ticks every second to show run duration
|
|
17
|
-
let activityStart = 0; // timestamp of when current run began
|
|
18
|
-
let elapsedBaseText = ''; // base sub-text for the elapsed display
|
|
19
|
-
const WATCHDOG_MS = 90000; // 90 seconds of silence → failure
|
|
20
|
-
|
|
21
|
-
// ── Utility ───────────────────────────────────────────────────────
|
|
22
|
-
const sleep = ms => new Promise(r => setTimeout(r, ms));
|
|
23
|
-
function escapeHtml(t) {
|
|
24
|
-
return t.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>');
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
// ── Status indicator (top-right of response area) ─────────────────
|
|
28
|
-
const statusEl = document.createElement('div');
|
|
29
|
-
statusEl.className = 'conn-status';
|
|
30
|
-
statusEl.innerHTML = '<span class="conn-dot dot-yellow pulse"></span><span class="conn-label">connecting…</span>';
|
|
31
|
-
responseArea.before(statusEl);
|
|
32
|
-
|
|
33
|
-
function setStatus(state) { // 'connecting' | 'online' | 'offline'
|
|
34
|
-
const dot = statusEl.querySelector('.conn-dot');
|
|
35
|
-
const label = statusEl.querySelector('.conn-label');
|
|
36
|
-
dot.className = `conn-dot ${state === 'online' ? 'dot-green pulse' : state === 'offline' ? 'dot-red' : 'dot-yellow pulse'}`;
|
|
37
|
-
label.textContent = state === 'online' ? 'live' : state === 'offline' ? 'disconnected' : 'connecting…';
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// ── Render helpers ─────────────────────────────────────────────────
|
|
41
|
-
function addUserMessage(text) {
|
|
42
|
-
const msg = document.createElement('div');
|
|
43
|
-
msg.className = 'message user-msg';
|
|
44
|
-
msg.innerHTML = `<div class="bubble">${escapeHtml(text)}</div><div class="message-clearfix"></div>`;
|
|
45
|
-
responseArea.appendChild(msg);
|
|
46
|
-
msg.scrollIntoView({ behavior: 'smooth', block: 'end' });
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// ── Thinking bubble ────────────────────────────────────────────────
|
|
50
|
-
function createThinkingBubble() {
|
|
51
|
-
const msg = document.createElement('div');
|
|
52
|
-
msg.className = 'message symi-msg thinking-msg';
|
|
53
|
-
msg.innerHTML = `
|
|
54
|
-
<div class="bubble thinking-bubble">
|
|
55
|
-
<div class="think-header">
|
|
56
|
-
<span class="think-badge">◈ PROCESSING</span>
|
|
57
|
-
<span class="think-dots"><span>.</span><span>.</span><span>.</span></span>
|
|
58
|
-
</div>
|
|
59
|
-
<div class="think-bar-wrap"><div class="think-bar"></div></div>
|
|
60
|
-
</div>
|
|
61
|
-
<div class="message-clearfix"></div>
|
|
62
|
-
`;
|
|
63
|
-
responseArea.appendChild(msg);
|
|
64
|
-
msg.scrollIntoView({ behavior: 'smooth', block: 'end' });
|
|
65
|
-
return msg;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
// ── Stream bubble lifecycle ────────────────────────────────────────
|
|
69
|
-
function openStreamBubble() {
|
|
70
|
-
streamStart = Date.now();
|
|
71
|
-
const msg = document.createElement('div');
|
|
72
|
-
msg.className = 'message symi-msg stream-msg';
|
|
73
|
-
msg.innerHTML = `<div class="bubble stream-bubble streaming"><div class="bubble-content"></div></div><div class="message-clearfix"></div>`;
|
|
74
|
-
responseArea.appendChild(msg);
|
|
75
|
-
streamBubble = msg.querySelector('.stream-bubble');
|
|
76
|
-
streamContent = msg.querySelector('.bubble-content');
|
|
77
|
-
msg.scrollIntoView({ behavior: 'smooth', block: 'end' });
|
|
78
|
-
return msg;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
function updateStream(text) {
|
|
82
|
-
if (!streamContent) return;
|
|
83
|
-
streamContent.textContent = text;
|
|
84
|
-
responseArea.scrollTop = responseArea.scrollHeight;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
function closeStreamBubble(model = 'claude-sonnet-4-6') {
|
|
88
|
-
if (!streamBubble) return;
|
|
89
|
-
streamBubble.classList.remove('streaming');
|
|
90
|
-
streamBubble.classList.add('done');
|
|
91
|
-
|
|
92
|
-
// Apply full markdown rendering to the final text
|
|
93
|
-
const rawText = streamContent?.textContent ?? '';
|
|
94
|
-
if (streamContent && rawText) {
|
|
95
|
-
streamContent.innerHTML = '';
|
|
96
|
-
const textBlock = renderBlock({ type: 'text', text: rawText });
|
|
97
|
-
streamContent.appendChild(textBlock);
|
|
98
|
-
window.applyHighlighting(streamContent);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
const elapsed = Date.now() - streamStart;
|
|
102
|
-
const chars = Math.round(rawText.length / 4);
|
|
103
|
-
|
|
104
|
-
// Add copy button
|
|
105
|
-
const copyBtn = document.createElement('button');
|
|
106
|
-
copyBtn.className = 'msg-copy-btn msg-copy-visible';
|
|
107
|
-
copyBtn.title = 'Copy';
|
|
108
|
-
copyBtn.onclick = function() { window.copyMessage(this); };
|
|
109
|
-
copyBtn.innerHTML = `<svg width="12" height="12" viewBox="0 0 24 24" fill="none">
|
|
110
|
-
<rect x="9" y="9" width="13" height="13" rx="2" stroke="currentColor" stroke-width="2"/>
|
|
111
|
-
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" stroke="currentColor" stroke-width="2"/>
|
|
112
|
-
</svg>`;
|
|
113
|
-
streamBubble.appendChild(copyBtn);
|
|
114
|
-
|
|
115
|
-
const footer = document.createElement('div');
|
|
116
|
-
footer.className = 'telemetry';
|
|
117
|
-
footer.innerHTML = `
|
|
118
|
-
<span class="tele-item">✦ ${model}</span>
|
|
119
|
-
<span class="tele-sep">·</span>
|
|
120
|
-
<span class="tele-item">${chars} tokens</span>
|
|
121
|
-
<span class="tele-sep">·</span>
|
|
122
|
-
<span class="tele-item">${elapsed}ms</span>
|
|
123
|
-
`;
|
|
124
|
-
streamBubble.appendChild(footer);
|
|
125
|
-
requestAnimationFrame(() => {
|
|
126
|
-
footer.classList.add('tele-visible');
|
|
127
|
-
// Scroll to bottom immediately after markdown + footer are in the DOM
|
|
128
|
-
responseArea.scrollTop = responseArea.scrollHeight;
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
// Second scroll after the fit-content width transition finishes (0.3s)
|
|
132
|
-
setTimeout(() => { responseArea.scrollTop = responseArea.scrollHeight; }, 350);
|
|
133
|
-
|
|
134
|
-
streamBubble = null;
|
|
135
|
-
streamContent = null;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
// ── Elapsed timer ─────────────────────────────────────────────────
|
|
139
|
-
function startElapsedTimer(baseText) {
|
|
140
|
-
stopElapsedTimer();
|
|
141
|
-
activityStart = Date.now();
|
|
142
|
-
elapsedBaseText = baseText;
|
|
143
|
-
elapsedTimer = setInterval(() => {
|
|
144
|
-
const secs = Math.floor((Date.now() - activityStart) / 1000);
|
|
145
|
-
const m = Math.floor(secs / 60);
|
|
146
|
-
const s = String(secs % 60).padStart(2, '0');
|
|
147
|
-
if (asoSub) asoSub.textContent = `${elapsedBaseText} ${m}:${s}`;
|
|
148
|
-
}, 1000);
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
function stopElapsedTimer() {
|
|
152
|
-
if (elapsedTimer) { clearInterval(elapsedTimer); elapsedTimer = null; }
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
// ── Watchdog ───────────────────────────────────────────────────────
|
|
156
|
-
function armWatchdog() {
|
|
157
|
-
clearWatchdog();
|
|
158
|
-
watchdogTimer = setTimeout(() => handleRunFailure('timeout'), WATCHDOG_MS);
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
function clearWatchdog() {
|
|
162
|
-
if (watchdogTimer) { clearTimeout(watchdogTimer); watchdogTimer = null; }
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
// ── Unified run failure handler ────────────────────────────────────
|
|
166
|
-
// Called when: watchdog fires, gateway disconnects mid-run, or server
|
|
167
|
-
// sends an explicit error event. Cleans up all state and shows the user
|
|
168
|
-
// a clear error message + ERROR orb state.
|
|
169
|
-
function handleRunFailure(reason) {
|
|
170
|
-
// Stop timers immediately
|
|
171
|
-
stopElapsedTimer();
|
|
172
|
-
clearWatchdog();
|
|
173
|
-
|
|
174
|
-
// Clean up any in-flight UI elements
|
|
175
|
-
if (thinkingEl) { thinkingEl.remove(); thinkingEl = null; }
|
|
176
|
-
if (streamBubble) {
|
|
177
|
-
// Don't render markdown on a partial/failed response — just mark it failed
|
|
178
|
-
streamBubble.classList.remove('streaming');
|
|
179
|
-
streamBubble.classList.add('done', 'stream-failed');
|
|
180
|
-
streamBubble = null;
|
|
181
|
-
streamContent = null;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
// Human-readable reason labels
|
|
185
|
-
const labels = {
|
|
186
|
-
timeout: `No response received after ${WATCHDOG_MS / 1000}s — the agent may still be running in the background`,
|
|
187
|
-
disconnected: 'Connection lost mid-run — gateway disconnected',
|
|
188
|
-
};
|
|
189
|
-
const label = labels[reason] || reason;
|
|
190
|
-
|
|
191
|
-
// Inject error bubble into the feed
|
|
192
|
-
const errEl = document.createElement('div');
|
|
193
|
-
errEl.className = 'message symi-msg';
|
|
194
|
-
errEl.innerHTML = `
|
|
195
|
-
<div class="bubble stream-bubble done run-error-bubble">
|
|
196
|
-
<div class="run-error-icon">⚠</div>
|
|
197
|
-
<div class="bubble-content run-error-text">${escapeHtml(label)}</div>
|
|
198
|
-
</div>
|
|
199
|
-
<div class="message-clearfix"></div>
|
|
200
|
-
`;
|
|
201
|
-
responseArea.appendChild(errEl);
|
|
202
|
-
requestAnimationFrame(() => { responseArea.scrollTop = responseArea.scrollHeight; });
|
|
203
|
-
|
|
204
|
-
// Show ERROR state on orb, then reset input
|
|
205
|
-
setAgentStatus('error');
|
|
206
|
-
isStreaming = false;
|
|
207
|
-
currentRunId = null;
|
|
208
|
-
enableInput();
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
// ── Render history (on first connect) ─────────────────────────────
|
|
212
|
-
function renderHistory(messages) {
|
|
213
|
-
responseArea.innerHTML = '';
|
|
214
|
-
for (const m of messages) {
|
|
215
|
-
if (!m || !m.role) continue;
|
|
216
|
-
const hasContent = Array.isArray(m.content)
|
|
217
|
-
? m.content.some(b => (b.text ?? b.thinking ?? b.name ?? '').trim())
|
|
218
|
-
: extractText(m.content).trim();
|
|
219
|
-
if (!hasContent) continue;
|
|
220
|
-
|
|
221
|
-
const el = window.renderMessage(m);
|
|
222
|
-
responseArea.appendChild(el);
|
|
223
|
-
window.applyHighlighting(el);
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
// Scroll to very bottom after history renders
|
|
227
|
-
requestAnimationFrame(() => { responseArea.scrollTop = responseArea.scrollHeight; });
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
// ── Gateway event handler ──────────────────────────────────────────
|
|
231
|
-
let thinkingEl = null;
|
|
232
|
-
|
|
233
|
-
function handleGatewayEvent(event) {
|
|
234
|
-
if (event.event !== 'chat') return;
|
|
235
|
-
const p = event.payload;
|
|
236
|
-
if (!p || p.sessionKey !== window.SESSION_KEY) return;
|
|
237
|
-
|
|
238
|
-
if (p.state === 'delta') {
|
|
239
|
-
// Each delta proves the agent is alive — reset the watchdog
|
|
240
|
-
armWatchdog();
|
|
241
|
-
|
|
242
|
-
// First delta — remove thinking bubble, open stream bubble
|
|
243
|
-
if (thinkingEl) { thinkingEl.remove(); thinkingEl = null; }
|
|
244
|
-
if (!streamBubble) {
|
|
245
|
-
openStreamBubble();
|
|
246
|
-
setAgentStatus('streaming');
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
const text = extractText(p.message);
|
|
250
|
-
if (text) updateStream(text);
|
|
251
|
-
|
|
252
|
-
} else if (p.state === 'final') {
|
|
253
|
-
stopElapsedTimer();
|
|
254
|
-
clearWatchdog();
|
|
255
|
-
closeStreamBubble();
|
|
256
|
-
setAgentStatus('done');
|
|
257
|
-
isStreaming = false;
|
|
258
|
-
currentRunId = null;
|
|
259
|
-
enableInput();
|
|
260
|
-
|
|
261
|
-
} else if (p.state === 'aborted') {
|
|
262
|
-
// Clean abort (user-initiated or model decided to stop) — not an error
|
|
263
|
-
stopElapsedTimer();
|
|
264
|
-
clearWatchdog();
|
|
265
|
-
if (streamBubble) closeStreamBubble();
|
|
266
|
-
if (thinkingEl) { thinkingEl.remove(); thinkingEl = null; }
|
|
267
|
-
setAgentStatus('idle');
|
|
268
|
-
isStreaming = false;
|
|
269
|
-
currentRunId = null;
|
|
270
|
-
enableInput();
|
|
271
|
-
|
|
272
|
-
} else if (p.state === 'error') {
|
|
273
|
-
// Server-reported error — route through unified failure handler
|
|
274
|
-
handleRunFailure(p.error?.message || 'Server reported an error');
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
// ── Agent Status Orb ───────────────────────────────────────────────
|
|
279
|
-
const asoPanel = document.getElementById('agent-status-panel');
|
|
280
|
-
const asoLabel = document.getElementById('aso-label');
|
|
281
|
-
const asoSub = document.getElementById('aso-sub');
|
|
282
|
-
let asoDoneTimer = null;
|
|
283
|
-
|
|
284
|
-
const ASO_STATES = {
|
|
285
|
-
idle: { state: 'idle', label: 'STANDBY', sub: 'Awaiting your prompt' },
|
|
286
|
-
waiting: { state: 'waiting', label: 'WAITING', sub: 'Sending to Symi\u2026' },
|
|
287
|
-
thinking: { state: 'thinking', label: 'PROCESSING', sub: 'Reasoning through request' },
|
|
288
|
-
streaming: { state: 'streaming', label: 'RESPONDING', sub: 'Generating response' },
|
|
289
|
-
done: { state: 'done', label: 'COMPLETE', sub: 'Response ready' },
|
|
290
|
-
error: { state: 'error', label: 'FAILED', sub: 'Run ended unexpectedly' },
|
|
291
|
-
};
|
|
292
|
-
|
|
293
|
-
function setAgentStatus(key) {
|
|
294
|
-
if (!asoPanel) return;
|
|
295
|
-
|
|
296
|
-
// Clear any pending auto-transition timers
|
|
297
|
-
if (asoDoneTimer) { clearTimeout(asoDoneTimer); asoDoneTimer = null; }
|
|
298
|
-
|
|
299
|
-
const s = ASO_STATES[key] || ASO_STATES.idle;
|
|
300
|
-
asoPanel.dataset.state = s.state;
|
|
301
|
-
if (asoLabel) asoLabel.textContent = s.label;
|
|
302
|
-
if (asoSub) asoSub.textContent = s.sub;
|
|
303
|
-
|
|
304
|
-
// ── Elapsed timer management ──────────────────────────────────
|
|
305
|
-
if (key === 'thinking') {
|
|
306
|
-
// Start elapsed timer and watchdog when processing begins
|
|
307
|
-
armWatchdog();
|
|
308
|
-
startElapsedTimer('Reasoning through request');
|
|
309
|
-
} else if (key === 'streaming') {
|
|
310
|
-
// Transition from thinking → streaming: keep watchdog running (will be
|
|
311
|
-
// reset on each delta), switch elapsed timer base text
|
|
312
|
-
elapsedBaseText = 'Generating response';
|
|
313
|
-
} else {
|
|
314
|
-
// Any terminal/non-active state: stop elapsed timer and watchdog
|
|
315
|
-
stopElapsedTimer();
|
|
316
|
-
clearWatchdog();
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
// ── Auto-revert timers ────────────────────────────────────────
|
|
320
|
-
if (key === 'done') {
|
|
321
|
-
asoDoneTimer = setTimeout(() => setAgentStatus('idle'), 3000);
|
|
322
|
-
}
|
|
323
|
-
if (key === 'error') {
|
|
324
|
-
asoDoneTimer = setTimeout(() => setAgentStatus('idle'), 4000);
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
// ── Input state ────────────────────────────────────────────────────
|
|
329
|
-
function disableInput() {
|
|
330
|
-
promptInput.disabled = true;
|
|
331
|
-
sendBtn.disabled = true;
|
|
332
|
-
newSessionBtn.disabled = true;
|
|
333
|
-
sendBtn.classList.add('btn-loading');
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
function enableInput() {
|
|
337
|
-
promptInput.disabled = false;
|
|
338
|
-
sendBtn.disabled = false;
|
|
339
|
-
newSessionBtn.disabled = false;
|
|
340
|
-
sendBtn.classList.remove('btn-loading');
|
|
341
|
-
promptInput.focus();
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
// ── Send handler ───────────────────────────────────────────────────
|
|
345
|
-
async function handleSend() {
|
|
346
|
-
if (isStreaming || !gateway.connected) return;
|
|
347
|
-
const text = promptInput.value.trim();
|
|
348
|
-
if (!text) return;
|
|
349
|
-
|
|
350
|
-
isStreaming = true;
|
|
351
|
-
promptInput.value = '';
|
|
352
|
-
disableInput();
|
|
353
|
-
|
|
354
|
-
addUserMessage(text);
|
|
355
|
-
await sleep(200);
|
|
356
|
-
|
|
357
|
-
setAgentStatus('waiting');
|
|
358
|
-
thinkingEl = createThinkingBubble();
|
|
359
|
-
setAgentStatus('thinking');
|
|
360
|
-
|
|
361
|
-
try {
|
|
362
|
-
await gateway.send(text);
|
|
363
|
-
// Response will arrive via chat events — watchdog is now armed
|
|
364
|
-
} catch (err) {
|
|
365
|
-
// gateway.send() itself failed (e.g. not connected, network error)
|
|
366
|
-
handleRunFailure(err.message || 'Failed to send message');
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
// ── New Session handler ────────────────────────────────────────────
|
|
371
|
-
async function handleNewSession() {
|
|
372
|
-
if (isStreaming || !gateway.connected) return;
|
|
373
|
-
|
|
374
|
-
disableInput();
|
|
375
|
-
setAgentStatus('waiting');
|
|
376
|
-
|
|
377
|
-
try {
|
|
378
|
-
// Send the /new slash command — gateway resets the session
|
|
379
|
-
await gateway.send('/new');
|
|
380
|
-
|
|
381
|
-
// Fade out existing messages, then show cleared state
|
|
382
|
-
responseArea.style.transition = 'opacity 0.25s ease';
|
|
383
|
-
responseArea.style.opacity = '0';
|
|
384
|
-
|
|
385
|
-
await sleep(260);
|
|
386
|
-
|
|
387
|
-
responseArea.innerHTML = '';
|
|
388
|
-
responseArea.style.opacity = '1';
|
|
389
|
-
|
|
390
|
-
// Show empty-state indicator
|
|
391
|
-
const cleared = document.createElement('div');
|
|
392
|
-
cleared.className = 'session-cleared';
|
|
393
|
-
cleared.innerHTML = `
|
|
394
|
-
<div class="session-cleared-icon">◈</div>
|
|
395
|
-
<div class="session-cleared-text">NEW SESSION STARTED</div>
|
|
396
|
-
`;
|
|
397
|
-
responseArea.appendChild(cleared);
|
|
398
|
-
|
|
399
|
-
setAgentStatus('idle');
|
|
400
|
-
|
|
401
|
-
// Show archive toast — lets user jump directly to history if they regret it
|
|
402
|
-
if (typeof window.showArchiveToast === 'function') window.showArchiveToast();
|
|
403
|
-
|
|
404
|
-
// After a short pause, remove the cleared indicator and let history load
|
|
405
|
-
await sleep(1800);
|
|
406
|
-
cleared.style.transition = 'opacity 0.4s ease';
|
|
407
|
-
cleared.style.opacity = '0';
|
|
408
|
-
await sleep(420);
|
|
409
|
-
cleared.remove();
|
|
410
|
-
|
|
411
|
-
} catch (err) {
|
|
412
|
-
handleRunFailure(err.message || 'Failed to start new session');
|
|
413
|
-
return;
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
enableInput();
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
sendBtn.addEventListener('click', handleSend);
|
|
420
|
-
newSessionBtn.addEventListener('click', handleNewSession);
|
|
421
|
-
promptInput.addEventListener('keydown', e => {
|
|
422
|
-
if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); handleSend(); }
|
|
423
|
-
});
|
|
424
|
-
|
|
425
|
-
// Clicking anywhere on the prompt bar (padding, icon, gaps) focuses the input
|
|
426
|
-
// Click anywhere on the main input row (not buttons) focuses the input
|
|
427
|
-
document.getElementById('prompt-bar').addEventListener('click', e => {
|
|
428
|
-
const inSend = sendBtn.contains(e.target) || e.target === sendBtn;
|
|
429
|
-
const inNewSession = newSessionBtn.contains(e.target) || e.target === newSessionBtn;
|
|
430
|
-
const inHistory = document.getElementById('history-btn').contains(e.target);
|
|
431
|
-
// Only focus if clicking in the main row area (not the actions row)
|
|
432
|
-
const inActionsRow = e.target.closest('.prompt-row-actions');
|
|
433
|
-
if (!inSend && !inNewSession && !inHistory && !inActionsRow) {
|
|
434
|
-
promptInput.focus();
|
|
435
|
-
}
|
|
436
|
-
});
|
|
437
|
-
|
|
438
|
-
// ── Gateway setup ──────────────────────────────────────────────────
|
|
439
|
-
const gateway = new SymiGateway();
|
|
440
|
-
|
|
441
|
-
gateway.addEventListener('connect', () => {
|
|
442
|
-
setStatus('online');
|
|
443
|
-
enableInput();
|
|
444
|
-
// History arrives via separate 'history' event pushed by the server
|
|
445
|
-
});
|
|
446
|
-
|
|
447
|
-
gateway.addEventListener('history', (e) => {
|
|
448
|
-
const msgs = Array.isArray(e.detail?.messages) ? e.detail.messages : [];
|
|
449
|
-
renderHistory(msgs);
|
|
450
|
-
});
|
|
451
|
-
|
|
452
|
-
gateway.addEventListener('disconnect', () => {
|
|
453
|
-
setStatus('offline');
|
|
454
|
-
// If the gateway drops while a run is in progress, surface the failure
|
|
455
|
-
// immediately rather than leaving the UI in a permanently locked state.
|
|
456
|
-
if (isStreaming) {
|
|
457
|
-
handleRunFailure('disconnected');
|
|
458
|
-
}
|
|
459
|
-
});
|
|
460
|
-
|
|
461
|
-
gateway.addEventListener('event', (e) => {
|
|
462
|
-
handleGatewayEvent(e.detail);
|
|
463
|
-
});
|
|
464
|
-
|
|
465
|
-
// Start connecting
|
|
466
|
-
disableInput();
|
|
467
|
-
gateway.start();
|