@symerian/symi 3.3.3 → 3.3.4

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.
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "3.3.3",
3
- "commit": "279709ad23b5cefc9b36d3a898e59b130aacb259",
4
- "builtAt": "2026-05-03T16:52:52.239Z"
2
+ "version": "3.3.4",
3
+ "commit": "c4f56af0d6e1c23da6f31f83e55c2d7bb5d6c4b3",
4
+ "builtAt": "2026-05-03T17:01:36.666Z"
5
5
  }
@@ -1 +1 @@
1
- bc35deb5cc9851b077e8e1bb53e6ef92f070ca49bf914112a1e9e4e4b527df65
1
+ e9bde2d55ad1b3cfcd3469d29271f94436c92a5fb3a4c5511b1796090eb6ddeb
@@ -5484,106 +5484,6 @@ body {
5484
5484
  padding: 2px 0 0;
5485
5485
  }
5486
5486
 
5487
- /* ── Symipulse Panel ────────────────────────────────────────────── */
5488
- .symipulse-panel {
5489
- cursor: pointer;
5490
- transition:
5491
- max-height 0.3s ease,
5492
- border-color 0.3s;
5493
- }
5494
-
5495
- .symipulse-feed {
5496
- max-height: 3.2em;
5497
- overflow: hidden;
5498
- transition: max-height 0.3s ease;
5499
- font-size: 11px;
5500
- line-height: 1.5;
5501
- color: var(--text-dim);
5502
- font-family: var(--font-mono);
5503
- }
5504
-
5505
- .symipulse-panel.expanded .symipulse-feed {
5506
- max-height: 300px;
5507
- overflow-y: auto;
5508
- scrollbar-width: thin;
5509
- scrollbar-color: rgba(0, 212, 255, 0.2) transparent;
5510
- }
5511
- .symipulse-panel.expanded .symipulse-feed::-webkit-scrollbar {
5512
- width: 3px;
5513
- }
5514
- .symipulse-panel.expanded .symipulse-feed::-webkit-scrollbar-track {
5515
- background: transparent;
5516
- }
5517
- .symipulse-panel.expanded .symipulse-feed::-webkit-scrollbar-thumb {
5518
- background: rgba(0, 212, 255, 0.2);
5519
- border-radius: 2px;
5520
- }
5521
-
5522
- .symipulse-entry {
5523
- padding: 3px 0;
5524
- border-bottom: 1px solid rgba(255, 255, 255, 0.04);
5525
- white-space: nowrap;
5526
- overflow: hidden;
5527
- text-overflow: ellipsis;
5528
- display: flex;
5529
- align-items: baseline;
5530
- gap: 6px;
5531
- }
5532
- .symipulse-entry:last-child {
5533
- border-bottom: none;
5534
- }
5535
-
5536
- .symipulse-entry-time {
5537
- color: var(--text-muted);
5538
- font-size: 10px;
5539
- flex-shrink: 0;
5540
- }
5541
-
5542
- .symipulse-entry-text {
5543
- overflow: hidden;
5544
- text-overflow: ellipsis;
5545
- white-space: nowrap;
5546
- color: var(--text-dim);
5547
- }
5548
-
5549
- .symipulse-panel.expanded .symipulse-entry {
5550
- white-space: normal;
5551
- word-break: break-word;
5552
- }
5553
- .symipulse-panel.expanded .symipulse-entry-text {
5554
- white-space: normal;
5555
- overflow: visible;
5556
- }
5557
-
5558
- .symipulse-live-dot {
5559
- display: inline-block;
5560
- width: 6px;
5561
- height: 6px;
5562
- border-radius: 50%;
5563
- background: var(--accent-green);
5564
- margin-left: 6px;
5565
- vertical-align: middle;
5566
- opacity: 0;
5567
- transition: opacity 0.3s ease;
5568
- }
5569
- .symipulse-live-dot.active {
5570
- opacity: 1;
5571
- animation: pulse 1.2s ease-in-out infinite;
5572
- }
5573
-
5574
- .symipulse-empty {
5575
- display: flex;
5576
- align-items: center;
5577
- justify-content: center;
5578
- padding: 10px 0;
5579
- color: var(--text-dim);
5580
- }
5581
- .symipulse-empty-text {
5582
- font-size: 11px;
5583
- font-family: var(--font-mono);
5584
- opacity: 0.5;
5585
- }
5586
-
5587
5487
  /* ── Lazy-load sentinel ──────────────────────────────────────────── */
5588
5488
  .load-more-sentinel {
5589
5489
  text-align: center;
@@ -325,18 +325,6 @@
325
325
  <div class="debug-entries" id="debug-panel-entries"></div>
326
326
  </div>
327
327
 
328
- <!-- Symipulse Panel -->
329
- <div class="glass-panel symipulse-panel" id="symipulse-panel">
330
- <div class="panel-label">
331
- SYMIPULSE
332
- <span class="symipulse-live-dot" id="symipulse-live-dot"></span>
333
- </div>
334
- <div class="symipulse-feed" id="symipulse-feed">
335
- <div class="symipulse-empty" id="symipulse-empty">
336
- <div class="symipulse-empty-text">No heartbeats yet</div>
337
- </div>
338
- </div>
339
- </div>
340
328
  </aside>
341
329
 
342
330
  <!-- Response waterfall area -->
@@ -1049,7 +1037,6 @@
1049
1037
  <script src="js/metrics.js"></script>
1050
1038
  <script src="js/gateway.js"></script>
1051
1039
  <script src="js/render.js"></script>
1052
- <script src="js/symipulse.js"></script>
1053
1040
  <script src="js/app.js"></script>
1054
1041
  <script src="js/settings.js"></script>
1055
1042
  <script src="js/menu.js"></script>
@@ -378,13 +378,10 @@ function renderHistory(messages) {
378
378
  feedCache.save(messages);
379
379
  }
380
380
 
381
- // Clear reasoning + symipulse panels in sync with the feed — prevents double-entries on reconnect
381
+ // Clear reasoning panel in sync with the feed — prevents double-entries on reconnect
382
382
  if (typeof window.clearReasoningPanel === "function") {
383
383
  window.clearReasoningPanel();
384
384
  }
385
- if (typeof window.clearSymipulsePanel === "function") {
386
- window.clearSymipulsePanel();
387
- }
388
385
  responseArea.innerHTML = "";
389
386
  responseArea.prepend(loadMoreSentinel);
390
387
  for (const m of messages) {
@@ -414,21 +411,6 @@ function renderHistory(messages) {
414
411
  continue;
415
412
  }
416
413
 
417
- // Route symipulse messages to dedicated Symipulse Panel
418
- if (typeof window.isSymipulseMessage === "function" && window.isSymipulseMessage(m)) {
419
- const txt = Array.isArray(m.content)
420
- ? m.content
421
- .filter((b) => b.type === "text")
422
- .map((b) => b.text ?? "")
423
- .join("")
424
- .trim()
425
- : extractText(m.content).trim();
426
- if (txt && typeof window.appendToSymipulsePanel === "function") {
427
- window.appendToSymipulsePanel(txt, m.timestamp);
428
- }
429
- continue;
430
- }
431
-
432
414
  // Route plugin context user messages to Reasoning Panel
433
415
  if (m.role === "user") {
434
416
  const txt = Array.isArray(m.content)
@@ -550,20 +532,6 @@ async function loadMoreHistory() {
550
532
  if (m.role === "system") {
551
533
  continue;
552
534
  }
553
- // Route symipulse messages to dedicated Symipulse Panel
554
- if (typeof window.isSymipulseMessage === "function" && window.isSymipulseMessage(m)) {
555
- const txt = Array.isArray(m.content)
556
- ? m.content
557
- .filter((b) => b.type === "text")
558
- .map((b) => b.text ?? "")
559
- .join("")
560
- .trim()
561
- : extractText(m.content).trim();
562
- if (txt && typeof window.appendToSymipulsePanel === "function") {
563
- window.appendToSymipulsePanel(txt, m.timestamp);
564
- }
565
- continue;
566
- }
567
535
 
568
536
  // Route plugin context & verification user messages to Reasoning Panel
569
537
  if (m.role === "user") {
@@ -639,19 +607,11 @@ function handleGatewayEvent(event) {
639
607
  if (event.event === "agent") {
640
608
  const a = event.payload;
641
609
  if (a) {
642
- // Skip heartbeat agent events — background maintenance runs should not
643
- // trigger the working indicator or arm the watchdog. Without this guard,
644
- // heartbeat tool events (exec, read) arm the watchdog, and when the
645
- // heartbeat completes its "final" is routed to the symipulse panel
646
- // without clearing the indicator — leaving the orb stuck in "working".
647
- if (a.isHeartbeat) {
648
- return;
649
- }
650
610
  // Cross-session filter: agent events for OTHER sessions (cron-fired
651
611
  // channel runs, slack/msteams inbound, scheduled jobs) must not arm the
652
- // user's watchdog. Without this guard, every background non-heartbeat
653
- // run resets the watchdog and produces phantom "Run timed out" warnings
654
- // when no follow-up event arrives within WATCHDOG_MS.
612
+ // user's watchdog. Without this guard, every background run resets the
613
+ // watchdog and produces phantom "Run timed out" warnings when no
614
+ // follow-up event arrives within WATCHDOG_MS.
655
615
  if (a.sessionKey && a.sessionKey !== window.SESSION_KEY) {
656
616
  return;
657
617
  }
@@ -703,18 +663,12 @@ function handleGatewayEvent(event) {
703
663
  return;
704
664
  }
705
665
 
706
- // Heartbeat events must never touch the user's watchdog or stream state.
707
- // Even though chat:final/error/aborted handlers below have their own
708
- // heartbeat early-returns, this top-level guard prevents Path A from
709
- // re-arming the watchdog on heartbeat-tagged deltas/finals.
710
- if (p.isHeartbeat) {
711
- // Fall through to the per-state branches below so heartbeat finals can
712
- // still be routed to the symipulse panel — but skip the watchdog re-arm.
713
- } else if (p.runId && currentRunId && p.runId !== currentRunId) {
666
+ if (p.runId && currentRunId && p.runId !== currentRunId) {
714
667
  // Cross-run chat event (e.g. cron-fired channel run, chat.injectMessage,
715
668
  // late event from a finished run). Not relevant to the user's active turn.
716
669
  return;
717
- } else if (isStreaming) {
670
+ }
671
+ if (isStreaming) {
718
672
  // Any chat event for our session and our run proves the agent is alive —
719
673
  // reset watchdog.
720
674
  armWatchdog();
@@ -728,9 +682,6 @@ function handleGatewayEvent(event) {
728
682
 
729
683
  // Handle "thinking" state broadcast from gateway (agent started processing)
730
684
  if (p.state === "thinking") {
731
- if (p.isHeartbeat) {
732
- return; // Heartbeat thinking events never touch user UI
733
- }
734
685
  if (p.runId && currentRunId && p.runId !== currentRunId) {
735
686
  return; // Cross-run thinking event
736
687
  }
@@ -744,18 +695,6 @@ function handleGatewayEvent(event) {
744
695
  }
745
696
 
746
697
  if (p.state === "delta") {
747
- // Route heartbeat/symipulse deltas to dedicated panel
748
- if (p.isHeartbeat) {
749
- const text = extractText(p.message);
750
- if (text && typeof window.appendToSymipulsePanel === "function") {
751
- window.appendToSymipulsePanel(text, Date.now());
752
- }
753
- if (typeof window.setSymipulseLiveDot === "function") {
754
- window.setSymipulseLiveDot(true);
755
- }
756
- return;
757
- }
758
-
759
698
  // Capture runId on first delta so abort can target the exact run
760
699
  if (p.runId && !currentRunId) {
761
700
  currentRunId = p.runId;
@@ -782,34 +721,6 @@ function handleGatewayEvent(event) {
782
721
  window.updateLiveThinking(thinkingText);
783
722
  }
784
723
  } else if (p.state === "final") {
785
- // Route heartbeat/symipulse finals to dedicated panel.
786
- // Check server flag first, then fall back to regex classifier for
787
- // edge cases where the flag is missing (e.g. older gateway versions).
788
- const finalMsg = p.message?.content ? p.message : { role: "assistant", content: p.message };
789
- if (
790
- p.isHeartbeat ||
791
- (typeof window.isSymipulseMessage === "function" && window.isSymipulseMessage(finalMsg))
792
- ) {
793
- const text = extractText(p.message?.content ?? p.message);
794
- if (text && typeof window.appendToSymipulsePanel === "function") {
795
- window.appendToSymipulsePanel(text, Date.now());
796
- }
797
- if (typeof window.setSymipulseLiveDot === "function") {
798
- window.setSymipulseLiveDot(false);
799
- }
800
- // Clean up any stream bubble that was opened before detection
801
- if (streamBubble) {
802
- streamBubble.closest(".message")?.remove();
803
- streamBubble = null;
804
- streamContent = null;
805
- }
806
- if (thinkingEl) {
807
- thinkingEl.remove();
808
- thinkingEl = null;
809
- }
810
- return;
811
- }
812
-
813
724
  stopElapsedTimer();
814
725
  clearWatchdog();
815
726
  if (typeof window.closeLiveThinking === "function") {
@@ -864,10 +775,6 @@ function handleGatewayEvent(event) {
864
775
  enableInput();
865
776
  drainQueue();
866
777
  } else if (p.state === "aborted") {
867
- // Silently ignore heartbeat aborts — don't touch user-run state
868
- if (p.isHeartbeat) {
869
- return;
870
- }
871
778
  // Clean abort (user-initiated or model decided to stop) — not an error
872
779
  stopElapsedTimer();
873
780
  clearWatchdog();
@@ -890,10 +797,6 @@ function handleGatewayEvent(event) {
890
797
  enableInput();
891
798
  drainQueue();
892
799
  } else if (p.state === "error") {
893
- // Silently ignore heartbeat errors — don't inject error bubbles for background runs
894
- if (p.isHeartbeat) {
895
- return;
896
- }
897
800
  // Server-reported error — route through unified failure handler.
898
801
  // The gateway emits the diagnostic at top-level `errorMessage`
899
802
  // (camelCase); fall back to nested `error.message` for forward-compat
@@ -777,11 +777,6 @@ window.renderMessage = function (message) {
777
777
  .trim()
778
778
  : extractText(content).trim();
779
779
 
780
- // Symipulse messages → routed to Symipulse Panel by caller; guard here as safety net
781
- if (typeof window.isSymipulseMessage === "function" && window.isSymipulseMessage(message)) {
782
- return null;
783
- }
784
-
785
780
  // NOTE: Monologue suppression moved server-side to OutputNormalizer.
786
781
 
787
782
  // Plugin context messages (e.g. "[Outlook 365] Not connected...")
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@symerian/symi",
3
- "version": "3.3.3",
3
+ "version": "3.3.4",
4
4
  "description": "Multi-channel AI gateway with extensible messaging integrations",
5
5
  "keywords": [],
6
6
  "homepage": "https://github.com/jaysteelmind/symi#readme",
@@ -1,154 +0,0 @@
1
- // ── Symi UI — Symipulse Panel ─────────────────────────────────────────
2
- // Dedicated panel for heartbeat/symipulse system health messages.
3
- // Routes all symipulse traffic out of the main chat feed into a
4
- // collapsible right-side panel.
5
-
6
- const SYMIPULSE_MAX_ENTRIES = 50;
7
-
8
- // ── Centralized classifier ────────────────────────────────────────────
9
- // Checks server-side tag first (real-time), falls back to regex for
10
- // history messages that predate the isHeartbeat flag.
11
- window.isSymipulseMessage = function (message) {
12
- if (message?.isHeartbeat === true) {
13
- return true;
14
- }
15
-
16
- const role = message?.role;
17
- const txt = Array.isArray(message?.content)
18
- ? message.content
19
- .filter((b) => b.type === "text")
20
- .map((b) => b.text ?? "")
21
- .join("")
22
- .trim()
23
- : (typeof message?.content === "string"
24
- ? message.content
25
- : (window.extractText?.(message?.content) ?? "")
26
- ).trim();
27
-
28
- if (!txt) {
29
- return false;
30
- }
31
-
32
- if (role === "assistant") {
33
- if (
34
- /SYMIPULSE|heartbeat|System\s*Health|Pattern\s*Detection|systemHealth|count\s*→/i.test(txt)
35
- ) {
36
- return true;
37
- }
38
- if (/^[A-Z_]+_OK$/i.test(txt)) {
39
- return true;
40
- }
41
- if (/^\s*NO_REPLY\s*$/i.test(txt)) {
42
- return true;
43
- }
44
- // Health warnings — broadened to catch ⚠️ with health-check keywords
45
- if (/^⚠️.*(heartbeat|Agent failed before reply|unreachable|HTTP:\d{3})/i.test(txt)) {
46
- return true;
47
- }
48
- // [quiet window] prefix — heartbeat output during idle periods
49
- if (/^\[quiet window\]/i.test(txt)) {
50
- return true;
51
- }
52
- // Health log references — "Logged to YYYY-MM-DD.md"
53
- if (/Logged to \d{4}-\d{2}-\d{2}\.md/i.test(txt)) {
54
- return true;
55
- }
56
- // Heartbeat log acknowledgments — "Logged." followed by status text
57
- if (/^Logged\./i.test(txt)) {
58
- return true;
59
- }
60
- }
61
-
62
- if (role === "user") {
63
- // Removed ^ anchor — plugin context preamble may precede "Read SYMIPULSE.md"
64
- if (/Read SYMIPULSE\.md/i.test(txt) || /SYMIPULSE_OK/i.test(txt)) {
65
- return true;
66
- }
67
- }
68
-
69
- return false;
70
- };
71
-
72
- // ── Panel DOM references ──────────────────────────────────────────────
73
- const _spPanel = document.getElementById("symipulse-panel");
74
- const _spFeed = document.getElementById("symipulse-feed");
75
- const _spEmpty = document.getElementById("symipulse-empty");
76
- const _spDot = document.getElementById("symipulse-live-dot");
77
-
78
- // ── Append entry to Symipulse Panel ───────────────────────────────────
79
- window.appendToSymipulsePanel = function (text, timestamp) {
80
- if (!_spFeed || !text) {
81
- return;
82
- }
83
-
84
- // Remove empty placeholder
85
- if (_spEmpty && _spEmpty.parentNode) {
86
- _spEmpty.remove();
87
- }
88
-
89
- const entry = document.createElement("div");
90
- entry.className = "symipulse-entry";
91
-
92
- const time = timestamp
93
- ? new Date(typeof timestamp === "number" && timestamp < 1e12 ? timestamp * 1000 : timestamp)
94
- : new Date();
95
- const hhmm = time.toLocaleTimeString("en-US", {
96
- hour: "2-digit",
97
- minute: "2-digit",
98
- hour12: false,
99
- });
100
-
101
- const timeSpan = document.createElement("span");
102
- timeSpan.className = "symipulse-entry-time";
103
- timeSpan.textContent = hhmm;
104
-
105
- const textSpan = document.createElement("span");
106
- textSpan.className = "symipulse-entry-text";
107
- textSpan.textContent = text;
108
-
109
- entry.appendChild(timeSpan);
110
- entry.appendChild(textSpan);
111
- _spFeed.appendChild(entry);
112
-
113
- // Cap entries
114
- while (_spFeed.querySelectorAll(".symipulse-entry").length > SYMIPULSE_MAX_ENTRIES) {
115
- const oldest = _spFeed.querySelector(".symipulse-entry");
116
- if (!oldest) {
117
- break;
118
- }
119
- oldest.remove();
120
- }
121
-
122
- // Auto-scroll when expanded
123
- if (_spPanel?.classList.contains("expanded")) {
124
- _spFeed.scrollTop = _spFeed.scrollHeight;
125
- }
126
- };
127
-
128
- // ── Clear panel (on new session) ──────────────────────────────────────
129
- window.clearSymipulsePanel = function () {
130
- if (!_spFeed) {
131
- return;
132
- }
133
- _spFeed.innerHTML = `
134
- <div class="symipulse-empty" id="symipulse-empty">
135
- <div class="symipulse-empty-text">No heartbeats yet</div>
136
- </div>`;
137
- };
138
-
139
- // ── Live dot control ──────────────────────────────────────────────────
140
- window.setSymipulseLiveDot = function (active) {
141
- if (_spDot) {
142
- _spDot.classList.toggle("active", active);
143
- }
144
- };
145
-
146
- // ── Toggle expand/collapse on click ───────────────────────────────────
147
- if (_spPanel) {
148
- _spPanel.addEventListener("click", () => {
149
- _spPanel.classList.toggle("expanded");
150
- if (_spPanel.classList.contains("expanded") && _spFeed) {
151
- _spFeed.scrollTop = _spFeed.scrollHeight;
152
- }
153
- });
154
- }