@ai-ecoverse/slicc-spectrum 2.6.0 → 2.8.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.
package/dist/slicc-ui.js CHANGED
@@ -14414,48 +14414,6 @@ var o63 = class extends a8 {
14414
14414
  };
14415
14415
  e26("swc-icon-info-circle", o63);
14416
14416
 
14417
- // src/app/guard.ts
14418
- var prefixes = [
14419
- "sk-",
14420
- "sk_",
14421
- "rk_",
14422
- "pk_live",
14423
- "ghp_",
14424
- "gho_",
14425
- "ghu_",
14426
- "ghs_",
14427
- "ghr_",
14428
- "github_pat_",
14429
- "glpat-",
14430
- "xoxa-",
14431
- "xoxb-",
14432
- "xoxp-",
14433
- "xoxs-",
14434
- "AKIA",
14435
- "ASIA",
14436
- "AIza",
14437
- "npm_",
14438
- "pypi-",
14439
- "hf_",
14440
- "eyJ",
14441
- "-----BEGIN"
14442
- ];
14443
- var keyed = /(?:(?:[\w-]*(?:token|secret|passw(?:or)?d|pwd|api[_-]?key|access[_-]?key|private[_-]?key|credential)[\w-]*|\b(?:auth|authorization)\b)["']?\s*[:=]\s*|(?:^|\s)--?[\w-]*(?:token|secret|password|api-key|key)\s+|\bbearer\s+)/gi;
14444
- var word = /[^\s'"=:,;(){}[\]`]+/g;
14445
- function guard(text4, growing = false) {
14446
- let cut = text4.length;
14447
- for (const match of text4.matchAll(keyed)) cut = Math.min(cut, match.index + match[0].length);
14448
- for (const match of text4.matchAll(word)) {
14449
- const token = match[0];
14450
- const last = growing && match.index + token.length === text4.length;
14451
- const risky = prefixes.some(
14452
- (prefix) => token.startsWith(prefix) || last && token.length >= 2 && prefix.startsWith(token)
14453
- );
14454
- if (risky) cut = Math.min(cut, match.index);
14455
- }
14456
- return cut < text4.length ? `${text4.slice(0, cut).trimEnd()} \u2026` : text4;
14457
- }
14458
-
14459
14417
  // node_modules/mdast-util-to-string/lib/index.js
14460
14418
  var emptyOptions = {};
14461
14419
  function toString(value, options2) {
@@ -24607,7 +24565,7 @@ function createEditSessionFile(file) {
24607
24565
  return editSessionFile;
24608
24566
  }
24609
24567
  var instanceId2 = -1;
24610
- var File = class {
24568
+ var File2 = class {
24611
24569
  options;
24612
24570
  workerManager;
24613
24571
  requestOwnerRender;
@@ -33316,6 +33274,69 @@ var signInCss = i`
33316
33274
  }
33317
33275
  `;
33318
33276
 
33277
+ // src/app/guard.ts
33278
+ var prefixes = [
33279
+ "sk-",
33280
+ "sk_",
33281
+ "rk_",
33282
+ "pk_live",
33283
+ "ghp_",
33284
+ "gho_",
33285
+ "ghu_",
33286
+ "ghs_",
33287
+ "ghr_",
33288
+ "github_pat_",
33289
+ "glpat-",
33290
+ "xoxa-",
33291
+ "xoxb-",
33292
+ "xoxp-",
33293
+ "xoxs-",
33294
+ "AKIA",
33295
+ "ASIA",
33296
+ "AIza",
33297
+ "npm_",
33298
+ "pypi-",
33299
+ "hf_",
33300
+ "eyJ",
33301
+ "-----BEGIN"
33302
+ ];
33303
+ var keyed = /(?:(?:[\w-]*(?:token|secret|passw(?:or)?d|pwd|api[_-]?key|access[_-]?key|private[_-]?key|credential)[\w-]*|\b(?:auth|authorization)\b)["']?\s*[:=]\s*|(?:^|\s)--?[\w-]*(?:token|secret|password|api-key|key)\s+|\bbearer\s+)/gi;
33304
+ var word = /[^\s'"=:,;(){}[\]`]+/g;
33305
+ function guard(text4, growing = false) {
33306
+ let cut = text4.length;
33307
+ for (const match of text4.matchAll(keyed)) cut = Math.min(cut, match.index + match[0].length);
33308
+ for (const match of text4.matchAll(word)) {
33309
+ const token = match[0];
33310
+ const last = growing && match.index + token.length === text4.length;
33311
+ const risky = prefixes.some(
33312
+ (prefix) => token.startsWith(prefix) || last && token.length >= 2 && prefix.startsWith(token)
33313
+ );
33314
+ if (risky) cut = Math.min(cut, match.index);
33315
+ }
33316
+ return cut < text4.length ? `${text4.slice(0, cut).trimEnd()} \u2026` : text4;
33317
+ }
33318
+
33319
+ // src/app/tool-title.ts
33320
+ var shellLead = /^(?:(?:git|npm|npx|pnpm|yarn|bun|node|deno|python3?|pip3?|curl|wget|ls|cd|cat|rm|mv|cp|chmod|chown|mkdir|touch|tar|zip|unzip|docker|kubectl|gh|wrangler|lsof|find|grep|rg|sed|awk|ssh|scp|rsync|make|cargo|go|ruby|perl|php|java|javac|swift|xcodebuild|brew|apt|yum|sudo|env|export|echo|printf|true|false|test|bash|sh|zsh|fish|source)(?=\s|$)|(?:\.\/))/;
33321
+ function rawTitle(title2) {
33322
+ const text4 = title2.trim();
33323
+ if (!text4) return false;
33324
+ if (text4.length > 80) return true;
33325
+ if (/[|;&><`]|\$\(|&&|\|\|/.test(text4)) return true;
33326
+ if (/^-[A-Za-z0-9]/.test(text4)) return true;
33327
+ if (/^[a-z]/.test(text4) && shellLead.test(text4)) return true;
33328
+ if (!/\s/.test(text4) && (/^[./~]/.test(text4) || /\.[A-Za-z0-9]+$/.test(text4))) return true;
33329
+ return false;
33330
+ }
33331
+ function headline(call) {
33332
+ const title2 = call.title.trim();
33333
+ const name = call.name.trim();
33334
+ const growing = call.status === "running";
33335
+ if (!title2) return guard(name, growing);
33336
+ if (rawTitle(title2)) return guard(`${name} ${title2}`.trim(), growing);
33337
+ return guard(title2, growing);
33338
+ }
33339
+
33319
33340
  // src/app/messages.ts
33320
33341
  var alone = { calls: [], live: false };
33321
33342
  var lickLabels = {
@@ -33569,9 +33590,6 @@ function step(call, context, id) {
33569
33590
  </div>`
33570
33591
  ];
33571
33592
  }
33572
- function headline(call) {
33573
- return guard(`${call.name} ${call.title}`.trim(), call.status === "running");
33574
- }
33575
33593
  function phase(status2, calls, live, said = false, stopped = false) {
33576
33594
  if (status2 === "stopped" || stopped) return "stopped";
33577
33595
  if (!calls.length) return status2 === "streaming" && !said ? "active" : null;
@@ -35512,9 +35530,9 @@ var commands = [
35512
35530
  name: "thinking",
35513
35531
  detail: "Set the thinking level",
35514
35532
  args: () => ["off", "low", "medium", "high"].map((value) => ({ value, label: value, detail: "" })),
35515
- run: (model, _id, arg) => {
35533
+ run: (model, id, arg) => {
35516
35534
  if (["off", "low", "medium", "high"].includes(arg))
35517
- model.settings.update({ thinking: arg });
35535
+ model.agent.setThinking(id, arg);
35518
35536
  }
35519
35537
  },
35520
35538
  {
@@ -35643,6 +35661,7 @@ var SliccComposer = class extends ThemedElement {
35643
35661
  entry: { state: true },
35644
35662
  failure: { state: true },
35645
35663
  reattach: { state: true },
35664
+ recording: { state: true },
35646
35665
  agent: {}
35647
35666
  };
35648
35667
  #drafts = /* @__PURE__ */ new Map();
@@ -35651,6 +35670,7 @@ var SliccComposer = class extends ThemedElement {
35651
35670
  #paths = [];
35652
35671
  #agent = "";
35653
35672
  #next = 1;
35673
+ #session = null;
35654
35674
  #abandon;
35655
35675
  #recognition = null;
35656
35676
  #erase;
@@ -35678,6 +35698,7 @@ var SliccComposer = class extends ThemedElement {
35678
35698
  this.entry = null;
35679
35699
  this.failure = null;
35680
35700
  this.reattach = null;
35701
+ this.recording = null;
35681
35702
  this.agent = "";
35682
35703
  }
35683
35704
  static styles = [
@@ -36223,23 +36244,98 @@ ${this.value}` : text4;
36223
36244
  release(this.attachments.filter((attachment2) => attachment2.id === id));
36224
36245
  this.attachments = this.attachments.filter((attachment2) => attachment2.id !== id);
36225
36246
  }
36226
- async screenshot() {
36227
- const browser = this.model?.browser;
36228
- const tab = browser?.list().find((candidate) => candidate.id === browser.active());
36229
- if (!(browser && tab)) return;
36230
- const url = await browser.screenshot(tab.id);
36247
+ #failAttach(name, error, mimeType = "application/octet-stream") {
36231
36248
  this.attachments = [
36232
36249
  ...this.attachments,
36233
36250
  {
36234
36251
  id: this.#id(),
36235
- name: `${tab.title}.png`,
36236
- kind: "image",
36237
- mimeType: "image/png",
36238
- size: url.length,
36239
- url
36252
+ name,
36253
+ kind: "file",
36254
+ mimeType,
36255
+ size: 0,
36256
+ error
36240
36257
  }
36241
36258
  ];
36242
36259
  }
36260
+ #switchOff(key) {
36261
+ const found = this.model?.switches?.list().find((item2) => item2.key === key);
36262
+ return !!found && !found.on;
36263
+ }
36264
+ #captureError(error, fallback, switchKey) {
36265
+ if (!(error instanceof Error)) return fallback;
36266
+ if (error.name === "AbortError") return "";
36267
+ if (error.name === "NotAllowedError" && switchKey && this.#switchOff(switchKey)) {
36268
+ const labels = {
36269
+ screen: "Screen capture",
36270
+ camera: "Camera",
36271
+ microphone: "Microphone"
36272
+ };
36273
+ return `${labels[switchKey]} is switched off. Turn it on in Settings, Capture and devices.`;
36274
+ }
36275
+ if (error.name === "NotAllowedError") return error.message || fallback;
36276
+ if (error.name === "NotSupportedError")
36277
+ return error.message || "Recording isn\u2019t supported here";
36278
+ return error.message || fallback;
36279
+ }
36280
+ #errorDetail(error) {
36281
+ return error.replace(" is switched off.", " is off.").replace(", Capture and devices.", ".");
36282
+ }
36283
+ async screenshot() {
36284
+ const capture = this.model?.capture;
36285
+ if (capture?.still) {
36286
+ try {
36287
+ const shot = await capture.still();
36288
+ await this.addFiles([new File([shot.blob], shot.name, { type: shot.mimeType })]);
36289
+ } catch (error) {
36290
+ const message = this.#captureError(error, "Screen capture was denied", "screen");
36291
+ if (message) this.#failAttach("screenshot.png", message, "image/png");
36292
+ }
36293
+ return;
36294
+ }
36295
+ const browser = this.model?.browser;
36296
+ const tab = browser?.list().find((candidate) => candidate.id === browser.active());
36297
+ if (!(browser && tab)) {
36298
+ this.#failAttach("screenshot.png", "No screen or browser tab to screenshot", "image/png");
36299
+ return;
36300
+ }
36301
+ try {
36302
+ const url = await browser.screenshot(tab.id);
36303
+ this.attachments = [
36304
+ ...this.attachments,
36305
+ {
36306
+ id: this.#id(),
36307
+ name: `${tab.title}.png`,
36308
+ kind: "image",
36309
+ mimeType: "image/png",
36310
+ size: url.length,
36311
+ url
36312
+ }
36313
+ ];
36314
+ } catch (error) {
36315
+ const message = this.#captureError(error, "Could not take a screenshot");
36316
+ if (message) this.#failAttach("screenshot.png", message, "image/png");
36317
+ }
36318
+ }
36319
+ async record(kind) {
36320
+ const capture = this.model?.capture;
36321
+ if (!capture || this.#session) return;
36322
+ try {
36323
+ const session2 = await capture.record(kind);
36324
+ this.#session = session2;
36325
+ this.recording = kind;
36326
+ const take2 = await session2.finished;
36327
+ this.#session = null;
36328
+ this.recording = null;
36329
+ await this.addFiles([new File([take2.blob], take2.name, { type: take2.mimeType })]);
36330
+ } catch (error) {
36331
+ this.#session = null;
36332
+ this.recording = null;
36333
+ const mic = kind === "audio";
36334
+ const fallback = mic ? "Microphone access was denied" : "Camera access was denied";
36335
+ const message = this.#captureError(error, fallback, mic ? "microphone" : "camera");
36336
+ if (message) this.#failAttach("recording.webm", message, mic ? "audio/webm" : "video/webm");
36337
+ }
36338
+ }
36243
36339
  async attachPath(path2) {
36244
36340
  const text4 = await this.model?.files.read(path2).catch(() => null);
36245
36341
  const name = path2.slice(path2.lastIndexOf("/") + 1);
@@ -36613,6 +36709,8 @@ ${this.value}` : text4;
36613
36709
  if (value === "upload")
36614
36710
  this.renderRoot.querySelector("input[type=file]")?.click();
36615
36711
  else if (value === "screenshot") void this.screenshot();
36712
+ else if (value === "record-audio") void this.record("audio");
36713
+ else if (value === "record-video") void this.record("video");
36616
36714
  else {
36617
36715
  this.openPopup(value);
36618
36716
  void this.updateComplete.then(
@@ -36660,7 +36758,7 @@ ${this.value}` : text4;
36660
36758
  return b`<span slot="thumbnail" class="thumb" ?data-error=${!!attachment2.error} aria-hidden="true">${icon}</span>`;
36661
36759
  }
36662
36760
  #attachment(attachment2) {
36663
- const detail = attachment2.error ?? (attachment2.kind === "secret" ? "Secret" : size3(attachment2.size));
36761
+ const detail = attachment2.error ? this.#errorDetail(attachment2.error) : attachment2.kind === "secret" ? "Secret" : size3(attachment2.size);
36664
36762
  return b`<swc-upload-attachment
36665
36763
  slot="attachment"
36666
36764
  type="card"
@@ -36668,12 +36766,12 @@ ${this.value}` : text4;
36668
36766
  data-id=${attachment2.id}
36669
36767
  data-kind=${attachment2.kind}
36670
36768
  ?data-error=${!!attachment2.error}
36671
- title=${attachment2.path ?? attachment2.name}
36769
+ title=${attachment2.error ?? attachment2.path ?? attachment2.name}
36672
36770
  @swc-upload-attachment-dismiss=${() => this.detach(attachment2.id)}
36673
36771
  >
36674
36772
  ${this.#thumbnail(attachment2)}
36675
36773
  <span slot="title">${attachment2.name}</span>
36676
- <span slot="subtitle" class=${attachment2.error ? "failed" : ""}>${detail}</span>
36774
+ <span slot="subtitle" class=${attachment2.error ? "failed" : ""} title=${attachment2.error ?? A}>${detail}</span>
36677
36775
  </swc-upload-attachment>`;
36678
36776
  }
36679
36777
  #popup() {
@@ -36946,7 +37044,9 @@ ${this.value}` : text4;
36946
37044
  <swc-popover placement="top-start" accessible-label="Add to the message" @keydown=${this.#menuKey} @swc-after-open=${() => this.renderRoot.querySelector("sp-menu")?.focus()}>
36947
37045
  <sp-menu label="Add to the message" @change=${this.#add}>
36948
37046
  <sp-menu-item value="upload">Upload from this computer</sp-menu-item>
36949
- <sp-menu-item value="screenshot">Take a screenshot of the browser tab</sp-menu-item>
37047
+ <sp-menu-item value="screenshot" ?disabled=${!!this.recording}>Take a screenshot</sp-menu-item>
37048
+ ${this.model?.capture ? b`<sp-menu-item value="record-audio" data-action="record-audio" ?disabled=${!!this.recording}>Record audio</sp-menu-item>` : A}
37049
+ ${this.model?.capture?.kinds?.includes("video") ? b`<sp-menu-item value="record-video" data-action="record-video" ?disabled=${!!this.recording}>Record video</sp-menu-item>` : A}
36950
37050
  <sp-menu-item value="file">Attach a file from SLICC</sp-menu-item>
36951
37051
  <sp-menu-item value="secret">Share a secret</sp-menu-item>
36952
37052
  ${talk ? b`<sp-menu-divider></sp-menu-divider>
@@ -38474,7 +38574,7 @@ var SliccChat = class _SliccChat extends ThemedElement {
38474
38574
  ${offered.length ? b`<sp-picker size="s" quiet label="Model" value=${agent.model} @change=${(event2) => model.agent.setModel(agent.id, event2.target.value)} @sp-opened=${this.#modelFilter.opened} @sp-closed=${this.#modelFilter.closed} @keydown=${this.#modelFilter.keys}>
38475
38575
  ${this.#modelFilter.items(offered)}
38476
38576
  </sp-picker>` : b`<swc-action-button size="s" quiet data-action="set-up-model" @click=${() => this.#action("settings")}>Set up a model</swc-action-button>`}
38477
- <sp-picker size="s" quiet label="Thinking" value=${model.settings.get().thinking} @change=${(event2) => model.settings.update({ thinking: event2.target.value })}>
38577
+ <sp-picker size="s" quiet label="Thinking" value=${agent.thinking ?? model.settings.get().thinking} @change=${(event2) => model.agent.setThinking(agent.id, event2.target.value)}>
38478
38578
  <sp-menu-item value="off">No thinking</sp-menu-item>
38479
38579
  <sp-menu-item value="low">Think a little</sp-menu-item>
38480
38580
  <sp-menu-item value="medium">Think</sp-menu-item>
@@ -39808,7 +39908,10 @@ function watchTitles(model, update) {
39808
39908
  const seen = /* @__PURE__ */ new Map();
39809
39909
  const check3 = (id) => {
39810
39910
  const agent = model.agent.list().find((candidate) => candidate.id === id);
39811
- if (!agent) return;
39911
+ if (!agent) {
39912
+ if (seen.delete(id)) update();
39913
+ return;
39914
+ }
39812
39915
  const title2 = agentTitle(model, agent);
39813
39916
  if (seen.get(id) === title2) return;
39814
39917
  seen.set(id, title2);
@@ -39816,7 +39919,12 @@ function watchTitles(model, update) {
39816
39919
  };
39817
39920
  const stops = [
39818
39921
  model.agent.on("messages", check3),
39819
- model.agent.on("message", ({ agentId }) => check3(agentId))
39922
+ model.agent.on("message", ({ agentId }) => check3(agentId)),
39923
+ model.agent.on("agents", (agents) => {
39924
+ const live = new Set(agents.map((agent) => agent.id));
39925
+ for (const id of seen.keys()) if (!live.has(id)) check3(id);
39926
+ for (const agent of agents) check3(agent.id);
39927
+ })
39820
39928
  ];
39821
39929
  return () => {
39822
39930
  for (const stop of stops) stop();
@@ -54181,7 +54289,7 @@ var SliccLive = class extends ModelElement {
54181
54289
  // src/app/lucide.ts
54182
54290
  var library = null;
54183
54291
  function load2() {
54184
- library ??= import("./chunk-NMHCUT6N.js");
54292
+ library ??= import("./chunk-3MP6GPRK.js");
54185
54293
  return library;
54186
54294
  }
54187
54295
  function pascal(name) {
@@ -55770,7 +55878,7 @@ var SliccSettings = class extends ThemedElement {
55770
55878
  (item2) => b`<sp-switch .checked=${l(item2.on)} data-id=${item2.key} @change=${(event2) => port.set(item2.key, this.#checked(event2))}>${item2.label}</sp-switch>`
55771
55879
  )}
55772
55880
  </div>
55773
- <p class="hint">Programs can capture only what’s on here. Turning one off stops what’s live under it.</p>
55881
+ <p class="hint">Programs and linked devices can use only what’s on here. Turning one off stops what’s live under it.</p>
55774
55882
  </div>
55775
55883
  </div>
55776
55884
  </div>`;
@@ -57978,9 +58086,14 @@ var SliccStatusLabel = class extends i2 {
57978
58086
  get #anchor() {
57979
58087
  return `${this.agent} ${this.calls[0]?.id ?? ""}`;
57980
58088
  }
58089
+ #needsLabel() {
58090
+ if (this.calls.length >= 2) return true;
58091
+ const only = this.calls[0];
58092
+ return !!only && rawTitle(only.title);
58093
+ }
57981
58094
  #ask() {
57982
58095
  const port = this.model?.agent;
57983
- if (this.calls.length < 2 || !port?.toolLabel) return;
58096
+ if (!this.#needsLabel() || !port?.toolLabel) return;
57984
58097
  const store2 = cache3(port);
57985
58098
  const key = this.#key;
57986
58099
  const anchor = this.#anchor;
@@ -58006,7 +58119,8 @@ var SliccStatusLabel = class extends i2 {
58006
58119
  return store2?.done.get(this.#key) ?? store2?.latest.get(this.#anchor) ?? "";
58007
58120
  }
58008
58121
  #wanted() {
58009
- const base = this.state === "complete" && this.#label() || this.text;
58122
+ if (this.state === "stopped") return this.text;
58123
+ const base = this.#label() || this.text;
58010
58124
  return base && this.note ? `${this.note} \xB7 ${base}` : base;
58011
58125
  }
58012
58126
  #cancel() {
@@ -58427,7 +58541,7 @@ var SliccCodeView = class extends PierreView {
58427
58541
  }
58428
58542
  draw(container) {
58429
58543
  if (this.#contents === null) return this.empty(container, "Nothing to show");
58430
- const viewer = new File(this.options());
58544
+ const viewer = new File2(this.options());
58431
58545
  viewer.render({
58432
58546
  file: { name: this.path, contents: this.#contents },
58433
58547
  containerWrapper: container