@ai-ecoverse/slicc-spectrum 1.95.0 → 1.96.0

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.
@@ -6,7 +6,7 @@ import {
6
6
  DummySettings,
7
7
  clock,
8
8
  dummySettingsKey
9
- } from "./chunk-QGY4KBEB.js";
9
+ } from "./chunk-CVMI4FSY.js";
10
10
  import {
11
11
  Emitter
12
12
  } from "./chunk-T5NHW4KC.js";
package/dist/slicc-ui.js CHANGED
@@ -30183,28 +30183,56 @@ function decision(message, handlers, pile) {
30183
30183
  </div>
30184
30184
  </swc-conversation-turn>`;
30185
30185
  }
30186
+ var scoopWords = {
30187
+ completed: "finished",
30188
+ failed: "failed",
30189
+ stopped: "stopped"
30190
+ };
30191
+ var scoopBracket = /^\[(?:scoop |@)[^\]\n]* (completed|failed|stopped)\][^\S\n]*(?:\n|$)/;
30192
+ function scoopLine(message) {
30193
+ const icon = lickIcons[message.channel];
30194
+ const [label] = lickLabels[message.channel];
30195
+ const bracket = message.channel === "scoop-notify" && scoopBracket.exec(message.text);
30196
+ if (!bracket) return [message.text, false, icon, label];
30197
+ const name = message.title.replace(/\s*\([^)]*\)$/, "");
30198
+ const word = scoopWords[bracket[1]];
30199
+ if (bracket[1] === "completed") return [`Scoop ${name} ${word}`, false, icon, label];
30200
+ return [`Scoop ${name} ${word}`, true, "\u274C", `Scoop ${word}`];
30201
+ }
30202
+ function lickBody(message) {
30203
+ return message.channel === "scoop-notify" ? (message.body ?? "").replace(scoopBracket, "").trim() : message.body;
30204
+ }
30205
+ function lickContent(message, body4, handlers) {
30206
+ const scoop = message.channel === "scoop-notify";
30207
+ const report = message.report;
30208
+ if (!scoop && !report) return b`<pre class="output">${body4}</pre>`;
30209
+ const shown4 = !body4 ? A : scoop ? b`<div class="text">${markdown(body4)}</div>` : b`<pre class="output">${body4}</pre>`;
30210
+ return b`<div class="report">${shown4}${report ? b`<swc-button size="s" variant="secondary" fill-style="outline" data-action="open-report" @click=${() => handlers.open(report)}>Open the report</swc-button>` : A}</div>`;
30211
+ }
30186
30212
  function lick(message, handlers, stamp = true, pile = flatPile) {
30187
30213
  if (pendingApproval(message)) return decision(message, handlers, pile);
30188
- const [label] = lickLabels[message.channel];
30214
+ const [line, failed2, icon, label] = scoopLine(message);
30215
+ const severity = message.severity ?? (failed2 ? "error" : void 0);
30189
30216
  const outcome = message.state === "confirmed" ? "Allowed" : message.state === "dismissed" ? "Denied" : "";
30190
- const head2 = b`${message.severity ? b`<span class="sr">${severities[message.severity].prefix}</span>` : A}
30217
+ const head2 = b`${severity ? b`<span class="sr">${severities[severity].prefix}</span>` : A}
30191
30218
  ${outcome ? b`<span class="sr">${outcome}. </span>` : A}
30192
- <span class="text">${message.text}</span>
30219
+ <span class="text" data-summary=${message.summary ? "" : A}>${message.summary || line}</span>
30193
30220
  ${message.count && message.count > 1 ? b`<span class="times" title="Coalesced events">×${message.count}</span>` : A}`;
30194
30221
  const outcomeIcon = message.state === "confirmed" ? "\u{1F44D}" : message.state === "dismissed" ? "\u{1F44E}" : "";
30195
- const reaction = b`<span class="reactions"><span class="reaction" title=${label} aria-hidden="true">${lickIcons[message.channel]}</span>${outcomeIcon ? b`<span class="reaction" title=${outcome} aria-hidden="true">${outcomeIcon}</span>` : A}</span>`;
30196
- const name = `${message.severity ? severities[message.severity].prefix : ""}${label} event`;
30197
- const byline2 = b`<div class="meta">${message.severity ? b`<span class="severity-icon">${severities[message.severity].icon}</span>` : A}<span class="who">${message.title}</span>${stamp ? when(message.createdAt) : unseen(message.createdAt)}</div>`;
30198
- if (!message.body) {
30222
+ const reaction = b`<span class="reactions"><span class="reaction" title=${label} aria-hidden="true">${icon}</span>${outcomeIcon ? b`<span class="reaction" title=${outcome} aria-hidden="true">${outcomeIcon}</span>` : A}</span>`;
30223
+ const name = `${severity ? severities[severity].prefix : ""}${label} event`;
30224
+ const byline2 = b`<div class="meta">${severity ? b`<span class="severity-icon">${severities[severity].icon}</span>` : A}<span class="who">${message.title}</span>${stamp ? when(message.createdAt) : unseen(message.createdAt)}</div>`;
30225
+ const body4 = lickBody(message);
30226
+ if (!body4 && !message.report) {
30199
30227
  return outside(
30200
30228
  name,
30201
30229
  byline2,
30202
- b`<div class="lick-frame"><div class="lick" data-id=${message.id} data-channel=${message.channel} data-severity=${message.severity ?? A}><div class="lick-head">${head2}</div></div>${reaction}</div>`
30230
+ b`<div class="lick-frame"><div class="lick" data-id=${message.id} data-channel=${message.channel} data-severity=${severity ?? A}><div class="lick-head">${head2}</div></div>${reaction}</div>`
30203
30231
  );
30204
30232
  }
30205
- const details = b`<details class="lick" data-id=${message.id} data-channel=${message.channel} data-severity=${message.severity ?? A} ?open=${handlers.licks?.has(message.id) ?? false} @toggle=${handlers.licks?.details(message.id) ?? A}>
30233
+ const details = b`<details class="lick" data-id=${message.id} data-channel=${message.channel} data-severity=${severity ?? A} ?open=${handlers.licks?.has(message.id) ?? false} @toggle=${handlers.licks?.details(message.id) ?? A}>
30206
30234
  <summary class="lick-head">${head2}<swc-icon-chevron-right class="chevron" size="s" aria-hidden="true"></swc-icon-chevron-right></summary>
30207
- <pre class="output">${message.body}</pre>
30235
+ ${lickContent(message, body4, handlers)}
30208
30236
  </details>`;
30209
30237
  return outside(name, byline2, b`<div class="lick-frame">${details}${reaction}</div>`);
30210
30238
  }
@@ -30395,16 +30423,35 @@ var messageCss = i`
30395
30423
  details[open] > summary .chevron {
30396
30424
  transform: rotate(90deg);
30397
30425
  }
30398
- .lick .text,
30426
+ .lick-head .text,
30399
30427
  .runs .label {
30400
30428
  min-inline-size: 0;
30401
30429
  overflow: hidden;
30402
30430
  text-overflow: ellipsis;
30403
30431
  white-space: nowrap;
30404
30432
  }
30405
- .lick .text {
30433
+ .lick-head .text {
30406
30434
  flex: 1;
30407
30435
  }
30436
+ .lick-head .text[data-summary] {
30437
+ white-space: normal;
30438
+ }
30439
+ .lick .report {
30440
+ display: flex;
30441
+ flex-direction: column;
30442
+ align-items: flex-start;
30443
+ gap: var(--swc-spacing-200);
30444
+ padding: var(--swc-spacing-75) var(--swc-spacing-200) var(--swc-spacing-200);
30445
+ overflow-wrap: anywhere;
30446
+ }
30447
+ .lick .report .heading {
30448
+ font-size: inherit;
30449
+ }
30450
+ .lick .report > .text {
30451
+ align-self: stretch;
30452
+ max-block-size: calc(var(--swc-spacing-1000) * 4);
30453
+ overflow: auto;
30454
+ }
30408
30455
  swc-system-message > slicc-tool-runs + slicc-tool-runs {
30409
30456
  margin-block-start: var(--swc-spacing-75);
30410
30457
  }
@@ -49689,6 +49736,23 @@ function matchProviders(options2, query) {
49689
49736
  return words.every((word) => text5.includes(word));
49690
49737
  });
49691
49738
  }
49739
+ var mcpRegistrations = "slicc-spectrum.mcp-endpoints";
49740
+ function registrations2() {
49741
+ try {
49742
+ return JSON.parse(globalThis.localStorage.getItem(mcpRegistrations) ?? "{}") ?? {};
49743
+ } catch {
49744
+ return {};
49745
+ }
49746
+ }
49747
+ function register(server, url) {
49748
+ const all3 = registrations2();
49749
+ if (url) all3[server] = url;
49750
+ else delete all3[server];
49751
+ try {
49752
+ globalThis.localStorage.setItem(mcpRegistrations, JSON.stringify(all3));
49753
+ } catch {
49754
+ }
49755
+ }
49692
49756
  var popup = (account) => account.group === "mcp" && account.auth === "oauth";
49693
49757
  var start = (options2, value) => options2.some((option2) => option2.value === value) ? value : options2[0]?.value ?? "";
49694
49758
  var nothingToChoose = "Nothing to choose from yet. You can connect without it.";
@@ -50059,7 +50123,7 @@ var SliccSettings = class extends ThemedElement {
50059
50123
  model.settings.on("settings", settle2),
50060
50124
  model.settings.on("accounts", settle2),
50061
50125
  model.settings.on("providers", update),
50062
- ...model.vault ? [model.vault.on("status", update)] : []
50126
+ ...model.vault ? [model.vault.on("status", update), model.vault.on("list", update)] : []
50063
50127
  ];
50064
50128
  }
50065
50129
  focus() {
@@ -50130,8 +50194,18 @@ var SliccSettings = class extends ThemedElement {
50130
50194
  const options2 = account.group === "mcp" ? { kind: "mcp", server: serverOf(account) } : { kind: "model", provider: account.id };
50131
50195
  this.#busy(id, true);
50132
50196
  this.failure = null;
50197
+ const url = this.#mcpUrl(account);
50133
50198
  try {
50134
50199
  await vault.put?.(recordName(account), value, void 0, options2);
50200
+ if (url) {
50201
+ const server = serverOf(account);
50202
+ const old = registrations2()[server];
50203
+ await vault.endpoints?.({
50204
+ ...old && old !== url ? { remove: { mcp: [{ server, url: old }] } } : {},
50205
+ add: { mcp: [{ server, url }] }
50206
+ });
50207
+ register(server, url);
50208
+ }
50135
50209
  } catch (error) {
50136
50210
  this.#busy(id, false);
50137
50211
  if (this.model === model) this.#refuse(id, "value", failureText(error));
@@ -50272,6 +50346,11 @@ var SliccSettings = class extends ThemedElement {
50272
50346
  const form = event2.currentTarget;
50273
50347
  this.problem = null;
50274
50348
  this.filled = new Set([...this.filled].filter((name) => name !== "value"));
50349
+ const url = this.#mcpUrl(account);
50350
+ if (this.#vault(account) && url) {
50351
+ void this.#confirmSubmit(form, account, url);
50352
+ return;
50353
+ }
50275
50354
  if (this.#vault(account)) {
50276
50355
  const value = take(form.querySelector("input[data-field=value]"));
50277
50356
  const fields2 = this.#fields(form, account);
@@ -50284,6 +50363,67 @@ var SliccSettings = class extends ThemedElement {
50284
50363
  const fields = this.#fields(form, account);
50285
50364
  if (secret && fields !== null) void this.#connectBases(account, secret, fields);
50286
50365
  }
50366
+ async #confirmSubmit(form, account, url) {
50367
+ const input = form.querySelector("input[data-field=value]");
50368
+ const problem = valueProblem(input.value);
50369
+ if (problem) return this.#refuse(account.id, "value", problem);
50370
+ const fields = this.#fields(form, account);
50371
+ if (fields === null || this.pending.has(account.id)) return;
50372
+ const model = this.model;
50373
+ if (!await this.#confirmUrl(url)) return;
50374
+ if (!input.isConnected || this.model !== model) return;
50375
+ await this.keep(account, take(input), fields);
50376
+ }
50377
+ #confirmUrl(url) {
50378
+ return confirm({
50379
+ title: `Connect to ${url}?`,
50380
+ body: `SLICC sends this token only to ${url}.`,
50381
+ action: "Connect",
50382
+ variant: "confirmation"
50383
+ });
50384
+ }
50385
+ #mcpUrl(account) {
50386
+ if (account.group !== "mcp" || !this.model?.vault?.endpoints) return null;
50387
+ return this.#baseOf(account.url) ?? null;
50388
+ }
50389
+ #pinned(account) {
50390
+ const server = serverOf(account);
50391
+ return this.model?.vault?.list()?.secrets.find(
50392
+ (secret) => secret.kind === "mcp" && secret.server === server && secret.state !== "removed" && secret.pinned
50393
+ )?.url;
50394
+ }
50395
+ #moved(account) {
50396
+ const url = this.#mcpUrl(account);
50397
+ const old = registrations2()[serverOf(account)] ?? this.#pinned(account);
50398
+ return url && old && old !== url ? url : null;
50399
+ }
50400
+ async #reconnectMcp(account, url) {
50401
+ const model = this.model;
50402
+ const server = serverOf(account);
50403
+ const old = registrations2()[server];
50404
+ if (!await this.#confirmUrl(url)) return;
50405
+ if (this.model !== model) return;
50406
+ this.#busy(account.id, true);
50407
+ this.failure = null;
50408
+ try {
50409
+ await model.vault?.endpoints?.({
50410
+ ...old ? { remove: { mcp: [{ server, url: old }] } } : {},
50411
+ add: { mcp: [{ server, url }] }
50412
+ });
50413
+ register(server, url);
50414
+ } catch (error) {
50415
+ if (this.model === model) this.failure = { id: account.id, message: failureText(error) };
50416
+ return;
50417
+ } finally {
50418
+ this.#busy(account.id, false);
50419
+ }
50420
+ if (this.model === model) await this.connect(account.id);
50421
+ }
50422
+ #movedNote(account) {
50423
+ const url = this.#moved(account);
50424
+ if (!url) return A;
50425
+ return b`<div class="notes"><p class="notice" data-notice="moved"><swc-icon-alert-triangle size="s" aria-hidden="true"></swc-icon-alert-triangle><span>The server moved to ${url}. Reconnect to send the token there.</span></p></div>`;
50426
+ }
50287
50427
  #submitOnEnter(event2) {
50288
50428
  const target2 = event2.target;
50289
50429
  if (event2.key !== "Enter" || target2.localName !== "input") return;
@@ -50483,8 +50623,16 @@ var SliccSettings = class extends ThemedElement {
50483
50623
  if (!await confirm({ title, body: body4, action: "Disconnect", variant: "confirmation" })) return;
50484
50624
  clearValues(this.renderRoot);
50485
50625
  if (this.model !== model) return;
50626
+ const server = serverOf(account);
50627
+ const pinned = !!this.#pinned(account);
50628
+ const url = account.group === "mcp" ? registrations2()[server] ?? (pinned ? null : this.#mcpUrl(account)) : null;
50486
50629
  if (stored && !await this.#forget(account, stored)) return;
50487
- if (this.model === model) model?.settings.disconnect(account.id);
50630
+ if (this.model !== model) return;
50631
+ model?.settings.disconnect(account.id);
50632
+ if (!url || !stored?.endpoints) return;
50633
+ await stored.endpoints({ remove: { mcp: [{ server, url }] } }).then(() => register(server, void 0)).catch((error) => {
50634
+ if (this.model === model) this.failure = { id: account.id, message: failureText(error) };
50635
+ });
50488
50636
  }
50489
50637
  async #forget(account, vault) {
50490
50638
  this.#busy(account.id, true);
@@ -50566,11 +50714,27 @@ var SliccSettings = class extends ThemedElement {
50566
50714
  <span class="provider">${account.provider}</span>
50567
50715
  <span class="identity">${account.identity}${this.#inVault(account) ? b`<span class="kept" data-store="vault">${account.identity ? " \xB7 " : ""}${kept2(account)}</span>` : A}</span>
50568
50716
  <swc-status-light class="state" variant=${variant}>${label}</swc-status-light>
50569
- ${this.#removable(account) ? b`<div class="actions">${this.#action(account)}${this.#removeButton(account)}</div>` : this.#action(account)}
50717
+ ${this.#actions(account)}
50570
50718
  </div>
50571
50719
  ${this.#offered() ? this.#rowNotes(account) : A}
50720
+ ${this.#movedNote(account)}
50572
50721
  ${this.adding?.id === account.id ? A : b`${this.#key(account)}${failure2}`}`;
50573
50722
  }
50723
+ #actions(account) {
50724
+ const moved2 = account.status === "connected" ? this.#moved(account) : null;
50725
+ if (moved2)
50726
+ return b`<div class="actions"><swc-button
50727
+ size="m"
50728
+ variant="accent"
50729
+ data-action="reconnect-mcp"
50730
+ accessible-label=${`Reconnect ${account.provider}`}
50731
+ ?pending=${this.pending.has(account.id)}
50732
+ @click=${() => this.#reconnectMcp(account, moved2)}
50733
+ >Reconnect</swc-button>${this.#action(account)}</div>`;
50734
+ if (this.#removable(account))
50735
+ return b`<div class="actions">${this.#action(account)}${this.#removeButton(account)}</div>`;
50736
+ return this.#action(account);
50737
+ }
50574
50738
  #removable(account) {
50575
50739
  return !!this.#offered() && !!this.model?.settings.remove && account.group !== "mcp";
50576
50740
  }