@symerian/symi 3.5.11 → 3.5.12

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.5.11",
3
- "commit": "0b0214f0883885802eb1f14b3287222b044689f2",
4
- "builtAt": "2026-05-06T02:28:12.827Z"
2
+ "version": "3.5.12",
3
+ "commit": "ba937470adc8cb729ea183e67afa9e9b23fa2245",
4
+ "builtAt": "2026-05-06T02:37:25.750Z"
5
5
  }
@@ -1 +1 @@
1
- 1191210216ce547e23cadf39bf60dc8d937eb3b4357c931c6ab5afdad1515eef
1
+ 74ed545b32825cef7fb4fa2f000acd88a717aaea0e2acf93d7d7c1b8ed2be576
@@ -397,80 +397,57 @@ body {
397
397
  letter-spacing: 0.05em;
398
398
  }
399
399
 
400
- /* ── Model Toggle ─────────────────────────────────────────────────── */
401
- .model-toggle {
400
+ /* ── Model Routing list ────────────────────────────────────────────── */
401
+ .model-routing-list {
402
402
  display: flex;
403
- gap: 6px;
404
- margin-bottom: 6px;
405
- }
406
- .model-toggle-btn {
407
- flex: 1;
408
- padding: 8px 4px;
409
- border: 1px solid var(--glass-border);
410
- border-radius: 6px;
411
- background: rgba(255, 255, 255, 0.03);
412
- color: var(--text-dim);
413
- font-family: var(--font-mono);
414
- font-size: 10px;
415
- letter-spacing: 0.04em;
416
- cursor: pointer;
417
- transition: all 0.2s ease;
418
- white-space: nowrap;
403
+ flex-direction: column;
404
+ gap: 10px;
405
+ margin-bottom: 8px;
419
406
  }
420
- .model-toggle-btn:hover {
421
- border-color: var(--accent-cyan);
422
- color: var(--text);
407
+ .model-section {
408
+ display: flex;
409
+ flex-direction: column;
410
+ gap: 3px;
423
411
  }
424
- .model-toggle-btn.active {
425
- background: rgba(0, 212, 255, 0.08);
426
- border-color: var(--accent-cyan);
427
- color: var(--accent-cyan);
428
- box-shadow: 0 0 8px rgba(0, 212, 255, 0.12);
412
+ .model-section-header {
413
+ font-family: var(--font-mono);
414
+ font-size: 9px;
415
+ letter-spacing: 0.08em;
416
+ text-transform: uppercase;
417
+ color: var(--text-dim);
418
+ opacity: 0.6;
419
+ padding: 0 2px 2px;
420
+ border-bottom: 1px solid rgba(255, 255, 255, 0.06);
421
+ margin-bottom: 2px;
429
422
  }
430
- .model-toggle-select {
431
- flex: 1;
432
- padding: 8px 22px 8px 8px;
423
+ .model-item {
424
+ width: 100%;
425
+ display: block;
426
+ text-align: left;
427
+ padding: 6px 8px;
433
428
  border: 1px solid var(--glass-border);
434
- border-radius: 6px;
429
+ border-radius: 5px;
435
430
  background: rgba(255, 255, 255, 0.03);
436
431
  color: var(--text-dim);
437
432
  font-family: var(--font-mono);
438
433
  font-size: 10px;
439
- letter-spacing: 0.04em;
434
+ letter-spacing: 0.03em;
435
+ line-height: 1.3;
440
436
  cursor: pointer;
441
- transition: all 0.2s ease;
442
- white-space: nowrap;
443
- appearance: none;
444
- -webkit-appearance: none;
445
- background-image:
446
- linear-gradient(45deg, transparent 50%, currentColor 50%),
447
- linear-gradient(135deg, currentColor 50%, transparent 50%);
448
- background-position:
449
- calc(100% - 10px) 50%,
450
- calc(100% - 6px) 50%;
451
- background-size:
452
- 4px 4px,
453
- 4px 4px;
454
- background-repeat: no-repeat;
437
+ transition: all 0.15s ease;
438
+ white-space: normal;
439
+ word-break: break-word;
455
440
  }
456
- .model-toggle-select:hover {
441
+ .model-item:hover {
457
442
  border-color: var(--accent-cyan);
458
443
  color: var(--text);
459
444
  }
460
- .model-toggle-select.active {
461
- background-color: rgba(0, 212, 255, 0.08);
445
+ .model-item.active {
446
+ background: rgba(0, 212, 255, 0.08);
462
447
  border-color: var(--accent-cyan);
463
448
  color: var(--accent-cyan);
464
449
  box-shadow: 0 0 8px rgba(0, 212, 255, 0.12);
465
450
  }
466
- .model-toggle-select:focus {
467
- outline: none;
468
- border-color: var(--accent-cyan);
469
- }
470
- .model-toggle-select option {
471
- background: #0a0a0a;
472
- color: var(--text);
473
- }
474
451
  .model-status {
475
452
  font-size: 10px;
476
453
  color: var(--text-dim);
@@ -120,21 +120,40 @@
120
120
 
121
121
  <div class="glass-panel" id="model-routing-panel">
122
122
  <div class="panel-label">MODEL ROUTING</div>
123
- <div class="model-toggle" id="model-toggle">
124
- <select class="model-toggle-select" id="model-toggle-spider" data-model="spider">
125
- <option value="" disabled selected>Spider ▾</option>
126
- <option value="spider-gemma4">Gemma 4 31B Uncensored</option>
127
- <option value="spider-qwen35">Qwen 3.5 35B</option>
128
- <option value="spider-qwen35-uncen">Qwen 3.5 Uncensored</option>
129
- <option value="spider-qwen36">Qwen 3.6 35B</option>
130
- </select>
131
- <select class="model-toggle-select" id="model-toggle-coreweave" data-model="coreweave">
132
- <option value="" disabled selected>CoreWeave ▾</option>
133
- <option value="coreweave1">CoreWeave 1 (Qwen 3.5 35B)</option>
134
- <option value="coreweave2">CoreWeave 2 (Qwen 3.6 35B)</option>
135
- <option value="coreweave3">CoreWeave 3 (Qwen 3.6 27B)</option>
136
- </select>
137
- <button class="model-toggle-btn" data-model="api">API</button>
123
+ <div class="model-routing-list" id="model-toggle">
124
+ <div class="model-section">
125
+ <div class="model-section-header">SPIDER</div>
126
+ <button class="model-item" data-group="spider" data-value="spider-gemma4">
127
+ Gemma 4 31B Uncensored
128
+ </button>
129
+ <button class="model-item" data-group="spider" data-value="spider-qwen35">
130
+ Qwen 3.5 35B
131
+ </button>
132
+ <button class="model-item" data-group="spider" data-value="spider-qwen35-uncen">
133
+ Qwen 3.5 Uncensored
134
+ </button>
135
+ <button class="model-item" data-group="spider" data-value="spider-qwen36">
136
+ Qwen 3.6 35B
137
+ </button>
138
+ </div>
139
+ <div class="model-section">
140
+ <div class="model-section-header">COREWEAVE</div>
141
+ <button class="model-item" data-group="coreweave" data-value="coreweave1">
142
+ CoreWeave 1 (Qwen 3.5 35B)
143
+ </button>
144
+ <button class="model-item" data-group="coreweave" data-value="coreweave2">
145
+ CoreWeave 2 (Qwen 3.6 35B)
146
+ </button>
147
+ <button class="model-item" data-group="coreweave" data-value="coreweave3">
148
+ CoreWeave 3 (Qwen 3.6 27B)
149
+ </button>
150
+ </div>
151
+ <div class="model-section">
152
+ <div class="model-section-header">API</div>
153
+ <button class="model-item" data-group="api" data-value="api">
154
+ Anthropic Claude Sonnet 4.6
155
+ </button>
156
+ </div>
138
157
  </div>
139
158
  <div class="model-status" id="model-status">loading…</div>
140
159
  </div>
@@ -1,78 +1,51 @@
1
- // ── Model Routing Toggle ─────────────────────────────────────────────
2
- // Spider dropdown (4 models) CoreWeave dropdown (3 endpoints) API (Anthropic).
3
- // Each <select> maps an option value (the provider key) to a fully
4
- // qualified model id; clicking switches `agents.defaults.model.primary`
5
- // via config.patch.
1
+ // ── Model Routing list ───────────────────────────────────────────────
2
+ // Vertical sectioned list (SPIDER / COREWEAVE / API) each .model-item
3
+ // row is a click target that switches `agents.defaults.model.primary`
4
+ // via config.patch. Replaced the side-by-side dropdowns + button design
5
+ // in 3.5.12 because the three controls were forcing horizontal scroll
6
+ // inside the panel; sectioned vertical layout has no width constraint
7
+ // and lets long model names wrap naturally.
6
8
 
7
9
  (function () {
8
- // Button keysfull model id (single-button surfaces)
9
- const BUTTON_MODELS = {
10
- api: "anthropic/claude-sonnet-4-6",
11
- };
12
-
13
- // Spider option values → full model id. The option value is the
14
- // provider key in symi.json; the model id includes the HuggingFace
15
- // path after the provider prefix (parseModelRef splits on the first
16
- // slash only, so slashes in the model id survive).
17
- const SPIDER_MODELS = {
10
+ // data-value (per-item)fully-qualified model id understood by the
11
+ // gateway (provider/model split on first "/"; later slashes survive
12
+ // and are part of the model id, e.g. for HuggingFace paths).
13
+ const MODEL_IDS = {
14
+ // SPIDER
18
15
  "spider-gemma4": "spider-gemma4/TrevorJS/gemma-4-31B-it-uncensored",
19
16
  "spider-qwen35": "spider-qwen35/Qwen/Qwen3.5-35B-A3B-FP8",
20
17
  "spider-qwen35-uncen":
21
18
  "spider-qwen35-uncen/Li101/Qwen3.5-35B-A3B-Uncensored-Aggressive-safetensors",
22
19
  "spider-qwen36": "spider-qwen36/Qwen/Qwen3.6-35B-A3B-FP8",
23
- };
24
-
25
- // CoreWeave option values → full model id. Same shape as SPIDER_MODELS;
26
- // each entry corresponds to a provider block in symi.json
27
- // (qwen-cw / qwen-cw2 / qwen-cw3) all hosted at *.coreweave.app.
28
- const COREWEAVE_MODELS = {
20
+ // COREWEAVE
29
21
  coreweave1: "qwen-cw/qwen35-35b-fp8",
30
22
  coreweave2: "qwen-cw2/qwen36-35b-fp8",
31
23
  coreweave3: "qwen-cw3/qwen36-27b-fp8",
24
+ // API
25
+ api: "anthropic/claude-sonnet-4-6",
32
26
  };
33
27
 
34
- const buttons = document.querySelectorAll("#model-toggle .model-toggle-btn");
35
- const spiderSelect = document.getElementById("model-toggle-spider");
36
- const coreweaveSelect = document.getElementById("model-toggle-coreweave");
28
+ // Reverse: model id → data-value (for setActive lookup).
29
+ const idToValue = {};
30
+ for (const [value, id] of Object.entries(MODEL_IDS)) {
31
+ idToValue[id] = value;
32
+ }
33
+
34
+ const list = document.getElementById("model-toggle");
35
+ const items = list ? list.querySelectorAll(".model-item") : [];
37
36
  const statusEl = document.getElementById("model-status");
38
37
 
39
- if (!buttons.length && !spiderSelect && !coreweaveSelect) {
38
+ if (!list || items.length === 0) {
40
39
  return;
41
40
  }
42
41
 
43
42
  let currentHash = null;
44
43
 
45
- // Reverse lookups: model id → option value, per surface
46
- const idToButtonKey = {};
47
- for (const [key, id] of Object.entries(BUTTON_MODELS)) {
48
- idToButtonKey[id] = key;
49
- }
50
- const idToSpiderKey = {};
51
- for (const [key, id] of Object.entries(SPIDER_MODELS)) {
52
- idToSpiderKey[id] = key;
53
- }
54
- const idToCoreweaveKey = {};
55
- for (const [key, id] of Object.entries(COREWEAVE_MODELS)) {
56
- idToCoreweaveKey[id] = key;
57
- }
58
-
59
44
  function setActive(primaryId) {
60
- const buttonKey = idToButtonKey[primaryId];
61
- buttons.forEach((btn) => {
62
- btn.classList.toggle("active", btn.dataset.model === buttonKey);
45
+ const activeValue = idToValue[primaryId];
46
+ items.forEach((item) => {
47
+ item.classList.toggle("active", item.dataset.value === activeValue);
63
48
  });
64
-
65
- const spiderKey = idToSpiderKey[primaryId];
66
- if (spiderSelect) {
67
- spiderSelect.classList.toggle("active", Boolean(spiderKey));
68
- spiderSelect.value = spiderKey ?? "";
69
- }
70
-
71
- const coreweaveKey = idToCoreweaveKey[primaryId];
72
- if (coreweaveSelect) {
73
- coreweaveSelect.classList.toggle("active", Boolean(coreweaveKey));
74
- coreweaveSelect.value = coreweaveKey ?? "";
75
- }
76
49
  }
77
50
 
78
51
  async function init() {
@@ -131,33 +104,19 @@
131
104
  }
132
105
  });
133
106
 
134
- buttons.forEach((btn) => {
135
- btn.addEventListener("click", () => {
136
- const id = BUTTON_MODELS[btn.dataset.model];
137
- if (id) {
138
- void switchToModelId(id);
139
- }
140
- });
107
+ // Single delegated click on the list → look up data-value → switch.
108
+ list.addEventListener("click", (e) => {
109
+ const item = e.target.closest(".model-item");
110
+ if (!item) {
111
+ return;
112
+ }
113
+ const value = item.dataset.value;
114
+ const id = MODEL_IDS[value];
115
+ if (id) {
116
+ void switchToModelId(id);
117
+ }
141
118
  });
142
119
 
143
- if (spiderSelect) {
144
- spiderSelect.addEventListener("change", () => {
145
- const id = SPIDER_MODELS[spiderSelect.value];
146
- if (id) {
147
- void switchToModelId(id);
148
- }
149
- });
150
- }
151
-
152
- if (coreweaveSelect) {
153
- coreweaveSelect.addEventListener("change", () => {
154
- const id = COREWEAVE_MODELS[coreweaveSelect.value];
155
- if (id) {
156
- void switchToModelId(id);
157
- }
158
- });
159
- }
160
-
161
120
  window.addEventListener("gateway:connected", () => init());
162
121
  if (window.gateway && window.gateway.connected) {
163
122
  void init();
@@ -4,7 +4,7 @@ import path from "node:path";
4
4
  import fs, { constants, readFileSync } from "node:fs";
5
5
  import os from "node:os";
6
6
  import { Logger } from "tslog";
7
- import JSON5 from "json5";
7
+ import json5 from "json5";
8
8
  import chalk, { Chalk } from "chalk";
9
9
  import fs$1, { mkdtemp, rm } from "node:fs/promises";
10
10
  import { z } from "zod";
@@ -294,7 +294,7 @@ function readLoggingConfig() {
294
294
  try {
295
295
  if (!fs.existsSync(configPath)) return;
296
296
  const raw = fs.readFileSync(configPath, "utf-8");
297
- const logging = JSON5.parse(raw)?.logging;
297
+ const logging = json5.parse(raw)?.logging;
298
298
  if (!logging || typeof logging !== "object" || Array.isArray(logging)) return;
299
299
  return logging;
300
300
  } catch {
@@ -7392,7 +7392,7 @@ function safeRealpath(target) {
7392
7392
  }
7393
7393
  const defaultResolver = {
7394
7394
  readFile: (p) => fs.readFileSync(p, "utf-8"),
7395
- parseJson: (raw) => JSON5.parse(raw)
7395
+ parseJson: (raw) => json5.parse(raw)
7396
7396
  };
7397
7397
  /**
7398
7398
  * Resolves all $include directives in a parsed config object.
@@ -10623,7 +10623,7 @@ function resolveConfigPathForDeps(deps) {
10623
10623
  function normalizeDeps(overrides = {}) {
10624
10624
  return {
10625
10625
  fs: overrides.fs ?? fs,
10626
- json5: overrides.json5 ?? JSON5,
10626
+ json5: overrides.json5 ?? json5,
10627
10627
  env: overrides.env ?? process.env,
10628
10628
  homedir: overrides.homedir ?? (() => resolveRequiredHomeDir(overrides.env ?? process.env, os.homedir)),
10629
10629
  configPath: overrides.configPath ?? "",
@@ -10634,11 +10634,11 @@ function maybeLoadDotEnvForConfig(env) {
10634
10634
  if (env !== process.env) return;
10635
10635
  loadDotEnv({ quiet: true });
10636
10636
  }
10637
- function parseConfigJson5(raw, json5 = JSON5) {
10637
+ function parseConfigJson5(raw, json5$1 = json5) {
10638
10638
  try {
10639
10639
  return {
10640
10640
  ok: true,
10641
- parsed: json5.parse(raw)
10641
+ parsed: json5$1.parse(raw)
10642
10642
  };
10643
10643
  } catch (err) {
10644
10644
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@symerian/symi",
3
- "version": "3.5.11",
3
+ "version": "3.5.12",
4
4
  "description": "Multi-channel AI gateway with extensible messaging integrations",
5
5
  "keywords": [],
6
6
  "homepage": "https://github.com/jaysteelmind/symi#readme",