@ai-ecoverse/slicc-spectrum 1.96.1 → 1.98.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
@@ -9766,11 +9766,11 @@ e25("swc-icon-user-following", o49);
9766
9766
 
9767
9767
  // src/app/follow.ts
9768
9768
  var lights = {
9769
- connecting: ["info", (owner) => `Connecting to ${owner}`],
9770
- connected: ["positive", (owner) => `Following ${owner}`],
9771
- reconnecting: ["notice", (owner) => `Reconnecting to ${owner}`],
9772
- unlinked: ["negative", (owner) => `Unlinked from ${owner}`],
9773
- failed: ["negative", (owner) => `Can\u2019t reach ${owner}`]
9769
+ connecting: ["info", (owner2) => `Connecting to ${owner2}`],
9770
+ connected: ["positive", (owner2) => `Following ${owner2}`],
9771
+ reconnecting: ["notice", (owner2) => `Reconnecting to ${owner2}`],
9772
+ unlinked: ["negative", (owner2) => `Unlinked from ${owner2}`],
9773
+ failed: ["negative", (owner2) => `Can\u2019t reach ${owner2}`]
9774
9774
  };
9775
9775
  function refusedNow(state2) {
9776
9776
  return !!state2?.refused && state2.link === "connected";
@@ -9782,31 +9782,31 @@ function followLight(state2) {
9782
9782
  }
9783
9783
  function followPage(state2) {
9784
9784
  if (!state2) return null;
9785
- const { owner } = state2;
9785
+ const { owner: owner2 } = state2;
9786
9786
  if (state2.link === "connecting")
9787
9787
  return {
9788
9788
  kind: "connecting",
9789
- title: `Connecting to ${owner}\u2026`,
9790
- body: `This device follows ${owner}. Its agent shows here once it\u2019s connected.`
9789
+ title: `Connecting to ${owner2}\u2026`,
9790
+ body: `This device follows ${owner2}. Its agent shows here once it\u2019s connected.`
9791
9791
  };
9792
9792
  if (state2.link === "unlinked")
9793
9793
  return {
9794
9794
  kind: "unlinked",
9795
- title: `${owner} unlinked this device`,
9796
- body: `To follow it again, open a new link from ${owner}.`
9795
+ title: `${owner2} unlinked this device`,
9796
+ body: `To follow it again, open a new link from ${owner2}.`
9797
9797
  };
9798
9798
  if (state2.link === "failed")
9799
9799
  return {
9800
9800
  kind: "failed",
9801
- title: `Can\u2019t follow ${owner}`,
9802
- body: state2.error ?? `The link to ${owner} failed.`
9801
+ title: `Can\u2019t follow ${owner2}`,
9802
+ body: state2.error ?? `The link to ${owner2} failed.`
9803
9803
  };
9804
9804
  if (state2.access.view) return null;
9805
9805
  const where2 = state2.name ? `next to ${state2.name}` : "for this device";
9806
9806
  return {
9807
9807
  kind: "waiting",
9808
- title: `Waiting for ${owner} to allow this device`,
9809
- body: `On ${owner}, choose Allow ${where2} in Network \u203A Linked devices.`
9808
+ title: `Waiting for ${owner2} to allow this device`,
9809
+ body: `On ${owner2}, choose Allow ${where2} in Network \u203A Linked devices.`
9810
9810
  };
9811
9811
  }
9812
9812
  function list(items, last) {
@@ -31430,9 +31430,9 @@ function tabsLight(state2) {
31430
31430
  var stores = /* @__PURE__ */ new WeakMap();
31431
31431
  var loose = {};
31432
31432
  function slotOf(model, key2, initial) {
31433
- const owner = model ?? loose;
31434
- const slots = stores.get(owner) ?? /* @__PURE__ */ new Map();
31435
- stores.set(owner, slots);
31433
+ const owner2 = model ?? loose;
31434
+ const slots = stores.get(owner2) ?? /* @__PURE__ */ new Map();
31435
+ stores.set(owner2, slots);
31436
31436
  const found = slots.get(key2);
31437
31437
  if (found) return found;
31438
31438
  const slot = { value: initial(), listeners: /* @__PURE__ */ new Set() };
@@ -31881,6 +31881,7 @@ var SliccChat = class extends ThemedElement {
31881
31881
  model.agent.on("agents", settle2),
31882
31882
  ...model.tabs ? [model.tabs.on("tabs", () => this.requestUpdate())] : [],
31883
31883
  ...onWatch(model, settle2),
31884
+ ...model.agent.starting ? [model.agent.on("starting", () => this.requestUpdate())] : [],
31884
31885
  model.agent.on("messages", (agentId) => {
31885
31886
  if (agentId === this.#id()) this.#refresh(this.#sent(agentId));
31886
31887
  }),
@@ -32335,7 +32336,7 @@ var SliccChat = class extends ThemedElement {
32335
32336
  this.#items(messages, agent),
32336
32337
  ([key2]) => key2,
32337
32338
  ([, item2]) => item2
32338
- )}</swc-conversation-thread>` : this.model?.tabs?.state().role === "connecting" ? b`<div class="empty" data-connecting>${connecting}</div>` : b`<div class="empty" data-empty>${empty3(watch, seat, agent?.name ?? "the agent")}</div>`}
32339
+ )}</swc-conversation-thread>` : this.model?.tabs?.state().role === "connecting" ? b`<div class="empty" data-connecting>${connecting}</div>` : this.model?.agent.starting?.() ? A : b`<div class="empty" data-empty>${empty3(watch, seat, agent?.name ?? "the agent")}</div>`}
32339
32340
  </div>
32340
32341
  </div>
32341
32342
  ${this.dropping ? b`<swc-dropzone dragged size="l" aria-label="Attach files" dragged-message="Drop to attach"><p>Drop to attach</p></swc-dropzone>` : A}
@@ -38130,18 +38131,22 @@ function openTerminal(dock, info, screen) {
38130
38131
  }
38131
38132
  return id;
38132
38133
  }
38133
- function defaultLayout(dock, screen, agent, items = surfaces2, terminals = []) {
38134
+ function openChatSlot(dock, item2, screen, agent, agentless) {
38135
+ if (agent) openChat(dock, agent, screen, false);
38136
+ else if (agentless) openSurface(dock, item2, screen, false);
38137
+ }
38138
+ function defaultLayout(dock, screen, agent, items = surfaces2, terminals = [], agentless = false) {
38134
38139
  dock.clear();
38135
38140
  const order = ["center", "left", "right"];
38136
38141
  for (const side of order) {
38137
38142
  for (const item2 of items) {
38138
38143
  if (item2.side !== side || !item2.open.includes(screen)) continue;
38139
38144
  if (item2.id === "terminal") for (const info of terminals) openTerminal(dock, info, screen);
38140
- else if (item2.id !== "chat") openSurface(dock, item2, screen);
38141
- else if (agent) openChat(dock, agent, screen, false);
38145
+ else if (item2.id === "chat") openChatSlot(dock, item2, screen, agent, agentless);
38146
+ else openSurface(dock, item2, screen);
38142
38147
  }
38143
38148
  }
38144
- const chat = chats(dock)[0];
38149
+ const chat = chats(dock)[0] ?? (dock.has("chat") ? "chat" : null);
38145
38150
  if (chat) dock.api.getPanel(chat)?.api.setActive();
38146
38151
  }
38147
38152
  function shown(dock, id) {
@@ -42949,10 +42954,19 @@ var SliccApp = class extends ThemedElement {
42949
42954
  const saved = this.#saved();
42950
42955
  const restored = this.#offered(saved, dock.factories) && dock.restore(saved);
42951
42956
  if (!restored) {
42952
- defaultLayout(dock, this.screen, this.#active(), this.#available(), model.terminals.list());
42957
+ defaultLayout(
42958
+ dock,
42959
+ this.screen,
42960
+ this.#active(),
42961
+ this.#available(),
42962
+ model.terminals.list(),
42963
+ !!model.agent.starting?.()
42964
+ );
42953
42965
  }
42954
42966
  this.#started = this.screen;
42955
42967
  this.#prune();
42968
+ if (!(this.#active() || chats(dock).length || dock.has("chat")) && model.agent.starting?.())
42969
+ this.#open("chat", false);
42956
42970
  this.#saving = true;
42957
42971
  this.#save();
42958
42972
  }
@@ -42982,14 +42996,29 @@ var SliccApp = class extends ThemedElement {
42982
42996
  #prune() {
42983
42997
  const dock = this.renderRoot.querySelector("slicc-dock");
42984
42998
  if (!dock || !this.#started) return;
42985
- const known = new Set(this.model?.agent.list().map((agent) => agent.id));
42999
+ const known = new Set(this.model?.agent.list().map((agent2) => agent2.id));
43000
+ const agent = this.#active();
43001
+ const held = !!agent && !!dock.content("chat")?.shadowRoot?.activeElement;
42986
43002
  this.panels.quietly(() => {
42987
43003
  for (const id of chats(dock)) {
42988
43004
  if (!known.has(chatAgent(id))) dock.close(id);
42989
43005
  }
43006
+ if (agent && dock.has("chat")) {
43007
+ if (this.#offers("chat") && !dock.has(chatId(agent.id)))
43008
+ dock.open({
43009
+ id: chatId(agent.id),
43010
+ component: "chat",
43011
+ title: agent.name,
43012
+ params: { agent: agent.id },
43013
+ inactive: true,
43014
+ position: { referencePanel: "chat", direction: "within" }
43015
+ });
43016
+ dock.close("chat");
43017
+ }
42990
43018
  if (this.model && this.#offers("terminal"))
42991
43019
  this.terminalPanels.sync(dock, this.model.terminals, this.screen);
42992
43020
  });
43021
+ if (agent && held && this.#offers("chat")) openChat(dock, agent, this.screen, true);
42993
43022
  }
42994
43023
  #activated(event2) {
42995
43024
  const { id } = event2.detail;
@@ -43001,7 +43030,14 @@ var SliccApp = class extends ThemedElement {
43001
43030
  resetLayout() {
43002
43031
  const terminals = this.model?.terminals.list();
43003
43032
  this.panels.quietly(
43004
- () => defaultLayout(this.dock, this.screen, this.#active(), this.#available(), terminals)
43033
+ () => defaultLayout(
43034
+ this.dock,
43035
+ this.screen,
43036
+ this.#active(),
43037
+ this.#available(),
43038
+ terminals,
43039
+ !!this.model?.agent.starting?.()
43040
+ )
43005
43041
  );
43006
43042
  this.#save();
43007
43043
  }
@@ -43504,7 +43540,7 @@ function shield(text5) {
43504
43540
  return out;
43505
43541
  }
43506
43542
  function quoted(segment2) {
43507
- const words = [];
43543
+ const words2 = [];
43508
43544
  let word = "";
43509
43545
  let quote = "";
43510
43546
  for (let index2 = 0; index2 < segment2.length; index2++) {
@@ -43517,20 +43553,20 @@ function quoted(segment2) {
43517
43553
  } else if (char === "'" || char === '"') quote = char;
43518
43554
  else if (char === "\\") word += segment2[++index2] ?? "";
43519
43555
  else if (/[\s<>=]/.test(char)) {
43520
- words.push(word);
43556
+ words2.push(word);
43521
43557
  word = "";
43522
43558
  } else word += char;
43523
43559
  }
43524
- return [...words, word];
43560
+ return [...words2, word];
43525
43561
  }
43526
- function commas(words) {
43527
- return words.flatMap((word) => word.includes(",") ? [word, ...word.split(",")] : [word]).filter(Boolean);
43562
+ function commas(words2) {
43563
+ return words2.flatMap((word) => word.includes(",") ? [word, ...word.split(",")] : [word]).filter(Boolean);
43528
43564
  }
43529
- function scan2(words, cwds, whole) {
43565
+ function scan2(words2, cwds, whole) {
43530
43566
  let cd = false;
43531
- for (const [index2, word] of words.entries()) {
43567
+ for (const [index2, word] of words2.entries()) {
43532
43568
  let hit = null;
43533
- if (index2 === 0 || words[index2 - 1] === "-c") {
43569
+ if (index2 === 0 || words2[index2 - 1] === "-c") {
43534
43570
  cd = ["cd", "pushd"].includes(command2(word));
43535
43571
  hit = touched2(cwds, word, whole);
43536
43572
  } else if (!cd) hit = touched2(cwds, word, whole);
@@ -43551,9 +43587,9 @@ function reach2(command3, cwd) {
43551
43587
  const plain2 = text5.split(separators).map((segment2) => commas(segment2.split(/[\s<>=]+/).map((word) => word.replace(/['"\\]/g, ""))));
43552
43588
  const aware = shield(text5).split(separators).map((segment2) => commas(quoted(unshield(segment2))));
43553
43589
  const segments = JSON.stringify(aware) === JSON.stringify(plain2) ? plain2 : [...plain2, ...aware];
43554
- const whole = segments.some((words) => words.length > 0 && !trusted(words[0]));
43555
- for (const words of segments) {
43556
- const hit = scan2(words, cwds, whole);
43590
+ const whole = segments.some((words2) => words2.length > 0 && !trusted(words2[0]));
43591
+ for (const words2 of segments) {
43592
+ const hit = scan2(words2, cwds, whole);
43557
43593
  if (hit) return hit;
43558
43594
  }
43559
43595
  const named2 = segments.flat().find((word) => !word.startsWith("/") && /fnox|secrets-policy/.test(word));
@@ -46668,7 +46704,7 @@ function execLine(exec, argv = []) {
46668
46704
  if (exec === "shell") return "Revealed in every bash";
46669
46705
  if (exec === false) return null;
46670
46706
  if (!argv.length) return "Revealed only to listed commands";
46671
- return `Revealed only to ${list5(argv.map((words) => words.map(shellWord).join(" ")))}`;
46707
+ return `Revealed only to ${list5(argv.map((words2) => words2.map(shellWord).join(" ")))}`;
46672
46708
  }
46673
46709
  function lower(line) {
46674
46710
  return `${line[0].toLowerCase()}${line.slice(1)}`;
@@ -46912,6 +46948,51 @@ var SliccVaultButton = class extends i2 {
46912
46948
 
46913
46949
  // src/app/composer.ts
46914
46950
  var limit = 25 * 1024 * 1024;
46951
+ var pages = /* @__PURE__ */ new WeakMap();
46952
+ function owner(node2) {
46953
+ let at = node2;
46954
+ while (at) {
46955
+ if (at instanceof Element && at.localName === "slicc-app") return at;
46956
+ at = at.parentNode ?? (at instanceof ShadowRoot ? at.host : null);
46957
+ }
46958
+ return node2.ownerDocument;
46959
+ }
46960
+ var PageQueue = class {
46961
+ #host;
46962
+ #owner = null;
46963
+ constructor(host4) {
46964
+ this.#host = host4;
46965
+ host4.addController(this);
46966
+ }
46967
+ #page() {
46968
+ this.#owner ??= this.#host.isConnected ? owner(this.#host) : null;
46969
+ const page = this.#owner ?? this.#host.ownerDocument;
46970
+ const found = pages.get(page) ?? { rows: [], draft: null, hosts: /* @__PURE__ */ new Set() };
46971
+ pages.set(page, found);
46972
+ return found;
46973
+ }
46974
+ get value() {
46975
+ return this.#page().rows;
46976
+ }
46977
+ set value(rows2) {
46978
+ const page = this.#page();
46979
+ page.rows = rows2;
46980
+ for (const host4 of [...page.hosts]) host4.requestUpdate();
46981
+ }
46982
+ get draft() {
46983
+ return this.#page().draft;
46984
+ }
46985
+ set draft(draft2) {
46986
+ this.#page().draft = draft2;
46987
+ }
46988
+ hostConnected() {
46989
+ this.#owner = owner(this.#host);
46990
+ this.#page().hosts.add(this.#host);
46991
+ }
46992
+ hostDisconnected() {
46993
+ this.#page().hosts.delete(this.#host);
46994
+ }
46995
+ };
46915
46996
  function available(model) {
46916
46997
  return commands2.filter((command3) => command3.available?.(model) ?? true);
46917
46998
  }
@@ -47052,6 +47133,7 @@ var SliccComposer = class extends ThemedElement {
47052
47133
  agent: {}
47053
47134
  };
47054
47135
  #drafts = /* @__PURE__ */ new Map();
47136
+ #waiting = new PageQueue(this);
47055
47137
  #history = -1;
47056
47138
  #paths = [];
47057
47139
  #agent = "";
@@ -47140,6 +47222,7 @@ var SliccComposer = class extends ThemedElement {
47140
47222
  margin: 0 0 var(--swc-spacing-100);
47141
47223
  padding: 0;
47142
47224
  display: grid;
47225
+ grid-template-columns: minmax(0, 1fr);
47143
47226
  gap: var(--swc-spacing-75);
47144
47227
  }
47145
47228
  .queued {
@@ -47184,7 +47267,8 @@ var SliccComposer = class extends ThemedElement {
47184
47267
  .hold .held {
47185
47268
  color: var(--swc-neutral-subdued-content-color-default);
47186
47269
  }
47187
- .queued swc-icon-clock {
47270
+ .queued swc-icon-clock,
47271
+ .queued > swc-icon-file {
47188
47272
  flex: none;
47189
47273
  color: var(--swc-neutral-subdued-content-color-default);
47190
47274
  }
@@ -47193,6 +47277,11 @@ var SliccComposer = class extends ThemedElement {
47193
47277
  font-size: var(--swc-font-size-75);
47194
47278
  color: var(--swc-neutral-subdued-content-color-default);
47195
47279
  }
47280
+ .queued [data-files] {
47281
+ display: inline-flex;
47282
+ align-items: center;
47283
+ gap: var(--swc-spacing-50);
47284
+ }
47196
47285
  .queued .text {
47197
47286
  flex: 1;
47198
47287
  min-width: 0;
@@ -47348,6 +47437,9 @@ var SliccComposer = class extends ThemedElement {
47348
47437
  if (changed.has("agent") && this.agent && this.model && this.agent !== this.#agent) {
47349
47438
  this.#switch(this.agent);
47350
47439
  }
47440
+ const edited = changed.has("value") || changed.has("attachments");
47441
+ if (edited && this.hasUpdated && !this.#agent && this.#starting())
47442
+ this.#waiting.draft = { value: this.value, attachments: this.attachments };
47351
47443
  }
47352
47444
  connectedCallback() {
47353
47445
  super.connectedCallback();
@@ -47364,8 +47456,9 @@ var SliccComposer = class extends ThemedElement {
47364
47456
  globalThis.removeEventListener("keyup", this.#release);
47365
47457
  globalThis.removeEventListener("blur", this.#release);
47366
47458
  clearValues(this.renderRoot);
47459
+ const kept3 = this.#waiting.draft?.attachments;
47367
47460
  const held = [
47368
- this.attachments,
47461
+ this.attachments === kept3 ? [] : this.attachments,
47369
47462
  ...[...this.#drafts.values()].map((draft2) => draft2.attachments)
47370
47463
  ];
47371
47464
  if (held.flat().some((attachment2) => attachment2.url?.startsWith("blob:")))
@@ -47398,6 +47491,7 @@ var SliccComposer = class extends ThemedElement {
47398
47491
  this.#paths = entries.filter((entry) => entry.kind === "file").map((entry) => entry.path);
47399
47492
  });
47400
47493
  const update = () => this.requestUpdate();
47494
+ queueMicrotask(() => this.#arrived());
47401
47495
  return [
47402
47496
  ...super.subscribe(model),
47403
47497
  model.agent.on("active", (id) => {
@@ -47407,7 +47501,18 @@ var SliccComposer = class extends ThemedElement {
47407
47501
  if (hold?.reason !== "locked") this.blocked = false;
47408
47502
  update();
47409
47503
  }),
47410
- model.agent.on("agents", update),
47504
+ model.agent.on("agents", () => {
47505
+ this.#arrived();
47506
+ update();
47507
+ }),
47508
+ ...model.agent.starting ? [
47509
+ model.agent.on("starting", () => {
47510
+ if (this.#starting() && this.popup?.kind !== "file" && this.popup?.kind !== "secret")
47511
+ this.popup = null;
47512
+ this.#arrived();
47513
+ update();
47514
+ })
47515
+ ] : [],
47411
47516
  model.agent.on("messages", update),
47412
47517
  model.agent.on("message", update),
47413
47518
  model.settings.on("settings", update),
@@ -47415,6 +47520,27 @@ var SliccComposer = class extends ThemedElement {
47415
47520
  ...model.vault ? [model.vault.on("availability", update), model.vault.on("status", update)] : []
47416
47521
  ];
47417
47522
  }
47523
+ #starting() {
47524
+ return this.model?.agent.starting?.() ?? null;
47525
+ }
47526
+ #arrived() {
47527
+ const model = this.model;
47528
+ const waiting3 = this.#waiting.value;
47529
+ const agents = model?.agent.list() ?? [];
47530
+ if (!(model && waiting3.length && agents.length) || this.#starting()) return;
47531
+ const active = model.agent.active();
47532
+ const id = agents.some((agent) => agent.id === active) ? active : agents[0].id;
47533
+ this.#waiting.value = [];
47534
+ const [first2, ...rest] = waiting3;
47535
+ this.#deliver(model, id, first2, "queue");
47536
+ for (const { text: text5, attachments } of rest)
47537
+ this.#post(model, id, { text: text5, attachments, mode: "queue" });
47538
+ }
47539
+ #unwait(id) {
47540
+ const waiting3 = this.#waiting.value;
47541
+ release(waiting3.find((message) => message.id === id)?.attachments ?? []);
47542
+ this.#waiting.value = waiting3.filter((message) => message.id !== id);
47543
+ }
47418
47544
  #held() {
47419
47545
  const state2 = this.model?.follow?.state();
47420
47546
  return state2?.link === "reconnecting" ? state2.owner : null;
@@ -47422,7 +47548,9 @@ var SliccComposer = class extends ThemedElement {
47422
47548
  #switch(id) {
47423
47549
  if (this.#agent)
47424
47550
  this.#drafts.set(this.#agent, { value: this.value, attachments: this.attachments });
47425
- const draft2 = this.#drafts.get(id);
47551
+ const shared2 = this.#agent || !this.isConnected ? null : this.#waiting.draft;
47552
+ if (shared2 && id && !this.#drafts.has(id)) this.#waiting.draft = null;
47553
+ const draft2 = this.#drafts.get(id) ?? shared2 ?? void 0;
47426
47554
  this.#agent = id;
47427
47555
  this.value = draft2?.value ?? "";
47428
47556
  this.attachments = draft2?.attachments ?? [];
@@ -47462,16 +47590,26 @@ var SliccComposer = class extends ThemedElement {
47462
47590
  this.attachments = [];
47463
47591
  this.popup = null;
47464
47592
  this.#history = -1;
47593
+ if (this.#starting()) {
47594
+ this.#waiting.value = [...this.#waiting.value, { id: this.#id(), text: text5, attachments }];
47595
+ return;
47596
+ }
47465
47597
  if (known) {
47466
47598
  release(attachments);
47467
47599
  known.run(model, this.#agent, (command3[2] ?? "").trim());
47468
47600
  return;
47469
47601
  }
47470
- const busy = this.#busy();
47602
+ this.#deliver(model, this.#agent, { text: text5, attachments }, mode2);
47603
+ }
47604
+ #deliver(model, id, message, mode2) {
47605
+ const busy = model.agent.busy(id);
47471
47606
  const resolved = mode2 === "queue" ? busy ? "queue" : "send" : busy || mode2 === "steer" ? "steer" : "send";
47472
- const id = this.#agent;
47607
+ this.#post(model, id, { text: message.text, attachments: message.attachments, mode: resolved });
47608
+ }
47609
+ #post(model, id, message) {
47610
+ const attachments = message.attachments ?? [];
47473
47611
  const done2 = () => retire(model.agent, id, attachments);
47474
- model.agent.send(id, { text: text5, attachments, mode: resolved }).then(done2, done2);
47612
+ model.agent.send(id, { ...message, attachments }).then(done2, done2);
47475
47613
  }
47476
47614
  #propose(model, text5) {
47477
47615
  this.value = "";
@@ -47651,6 +47789,10 @@ ${this.value}` : text5;
47651
47789
  #refresh() {
47652
47790
  const area = this.#textarea;
47653
47791
  if (!area || this.model?.seat) return;
47792
+ if (this.#starting()) {
47793
+ this.popup = null;
47794
+ return;
47795
+ }
47654
47796
  if (this.popup?.kind === "file" || this.popup?.kind === "secret") return;
47655
47797
  const found = trigger(this.value, area.selectionStart ?? this.value.length);
47656
47798
  const name = this.value.match(/^\/(\w+)/)?.[1];
@@ -47990,7 +48132,23 @@ ${this.value}` : text5;
47990
48132
  )}
47991
48133
  </ul>`;
47992
48134
  }
47993
- #waiting() {
48135
+ #starters() {
48136
+ const waiting3 = this.#waiting.value;
48137
+ if (waiting3.length === 0) return A;
48138
+ return b`<ul class="queue" aria-label="Waiting for the agent">
48139
+ ${waiting3.map(
48140
+ (message) => b`<li class="queued" data-id=${message.id} data-mode="starting">
48141
+ <swc-icon-clock size="s" aria-hidden="true"></swc-icon-clock>
48142
+ ${message.text ? A : b`<swc-icon-file size="xs" aria-hidden="true"></swc-icon-file>`}
48143
+ <span class="text"><span class="sr">Queued: </span>${message.text || message.attachments.map((attachment2) => attachment2.name).join(", ")}</span>
48144
+ ${message.text && message.attachments.length ? b`<span class="next" data-files><swc-icon-file size="xs" aria-hidden="true"></swc-icon-file><span class="sr">, </span>${message.attachments.length === 1 ? "1 file" : `${message.attachments.length} files`}</span>` : A}
48145
+ <span class="next">Sent when the agent starts</span>
48146
+ <swc-close-button size="s" data-action="unqueue" accessible-label="Remove from queue" @click=${() => this.#unwait(message.id)}></swc-close-button>
48147
+ </li>`
48148
+ )}
48149
+ </ul>`;
48150
+ }
48151
+ #review() {
47994
48152
  const queued = this.model?.agent.queue(this.#agent) ?? [];
47995
48153
  if (queued.length === 0) return A;
47996
48154
  return b`<ul class="queue" aria-label="Waiting for review">
@@ -48008,7 +48166,7 @@ ${this.value}` : text5;
48008
48166
  const cone = agent?.name ?? "the agent";
48009
48167
  return b`<div class="column">
48010
48168
  <span id="keys" class="sr" aria-hidden="true">${this.#instructions(false)}</span>
48011
- ${this.#waiting()}
48169
+ ${this.#review()}
48012
48170
  ${this.failure ? b`<p class="send-error" role="alert" data-send-error><swc-icon-alert-triangle size="s" aria-hidden="true"></swc-icon-alert-triangle><span>${this.failure}</span></p>` : A}
48013
48171
  <swc-prompt-field
48014
48172
  label="Message"
@@ -48035,9 +48193,11 @@ ${this.value}` : text5;
48035
48193
  const pushToTalk = this.model?.settings.get().pushToTalk ?? false;
48036
48194
  const talk = !!speech();
48037
48195
  const held = this.#held();
48196
+ const starting = this.#starting();
48038
48197
  return b`<div class="column">
48039
48198
  <span id="keys" class="sr" aria-hidden="true">${this.#instructions(busy)}</span>
48040
48199
  ${this.#hold()}
48200
+ ${this.#starters()}
48041
48201
  ${this.#queue()}
48042
48202
  ${this.#popup()}
48043
48203
  <swc-prompt-field
@@ -48049,7 +48209,7 @@ ${this.value}` : text5;
48049
48209
  max-rows="10"
48050
48210
  ?generating=${busy && !canSend}
48051
48211
  ?disabled=${!!held}
48052
- placeholder=${held ? `Reconnecting to ${held}\u2026` : suggestion ? `${suggestion} (Tab)` : `Message ${agent?.name ?? ""}`}
48212
+ placeholder=${held ? `Reconnecting to ${held}\u2026` : starting ? starting : suggestion ? `${suggestion} (Tab)` : `Message ${agent?.name ?? ""}`}
48053
48213
  .value=${l(this.value)}
48054
48214
  @keydown=${this.#keys}
48055
48215
  @swc-prompt-field-input=${this.#input}
@@ -48828,9 +48988,9 @@ function auditLine(entry, devices, named2) {
48828
48988
  return b`<code>${entry.file}</code> ${entry.accepted ? "changed" : "changed and was refused"}${changes(entry, devices).map(([kind, line]) => b`<span class="change" data-change=${kind}>${line}</span>`)}${entry.warning ? b`<span class="warning" data-warning>${entry.warning}</span>` : A}${entry.accepted ? b`<span class="hint" data-hint>If you didn’t mean this, change the file back with sudo.</span>` : A}`;
48829
48989
  }
48830
48990
  }
48831
- function merged(...pages) {
48991
+ function merged(...pages2) {
48832
48992
  const bySeq = /* @__PURE__ */ new Map();
48833
- for (const page of pages) for (const entry of page) bySeq.set(entry.seq, entry);
48993
+ for (const page of pages2) for (const entry of page) bySeq.set(entry.seq, entry);
48834
48994
  return [...bySeq.values()].sort((a23, b13) => b13.seq - a23.seq);
48835
48995
  }
48836
48996
  function replaceable(secret) {
@@ -49938,10 +50098,10 @@ function draft(option2) {
49938
50098
  };
49939
50099
  }
49940
50100
  function matchProviders(options2, query) {
49941
- const words = query.trim().toLowerCase().split(/\s+/).filter(Boolean);
50101
+ const words2 = query.trim().toLowerCase().split(/\s+/).filter(Boolean);
49942
50102
  return options2.filter((option2) => {
49943
50103
  const text5 = `${option2.name} ${option2.id} ${option2.description ?? ""}`.toLowerCase();
49944
- return words.every((word) => text5.includes(word));
50104
+ return words2.every((word) => text5.includes(word));
49945
50105
  });
49946
50106
  }
49947
50107
  var mcpRegistrations = "slicc-spectrum.mcp-endpoints";
@@ -49961,6 +50121,9 @@ function register(server, url) {
49961
50121
  } catch {
49962
50122
  }
49963
50123
  }
50124
+ function words(message, word) {
50125
+ return new RegExp(`\\b${word.replace(/[^a-z0-9 ]/gi, "\\$&")}\\b`, "i").test(message);
50126
+ }
49964
50127
  var popup = (account) => account.group === "mcp" && account.auth === "oauth";
49965
50128
  var start = (options2, value) => options2.some((option2) => option2.value === value) ? value : options2[0]?.value ?? "";
49966
50129
  var nothingToChoose = "Nothing to choose from yet. You can connect without it.";
@@ -50393,18 +50556,22 @@ var SliccSettings = class extends ThemedElement {
50393
50556
  const model = this.model;
50394
50557
  const vault = this.#vault(account);
50395
50558
  const { id } = account;
50396
- const problem = valueProblem(value);
50559
+ const keyed = value !== "" || !this.#keyOptional(account);
50560
+ const problem = keyed ? valueProblem(value) : null;
50397
50561
  if (problem) {
50398
50562
  this.#refuse(id, "value", problem);
50399
50563
  return;
50400
50564
  }
50401
50565
  if (vault.status()?.locked) return;
50402
- const options2 = account.group === "mcp" ? { kind: "mcp", server: serverOf(account) } : { kind: "model", provider: account.id };
50566
+ const settings = this.#settingsOf(account, fields);
50567
+ const options2 = account.group === "mcp" ? { kind: "mcp", server: serverOf(account) } : { kind: "model", provider: account.id, ...settings ? { settings } : {} };
50403
50568
  this.#busy(id, true);
50404
50569
  this.failure = null;
50405
50570
  const url = this.#mcpUrl(account);
50571
+ const before = keyed ? void 0 : this.#record(account)?.settings;
50406
50572
  try {
50407
- await vault.put?.(recordName(account), value, void 0, options2);
50573
+ if (keyed) await vault.put?.(recordName(account), value, void 0, options2);
50574
+ else if (settings) await vault.settings?.(recordName(account), "model", settings);
50408
50575
  if (url) {
50409
50576
  const server = serverOf(account);
50410
50577
  const old = registrations2()[server];
@@ -50416,7 +50583,7 @@ var SliccSettings = class extends ThemedElement {
50416
50583
  }
50417
50584
  } catch (error) {
50418
50585
  this.#busy(id, false);
50419
- if (this.model === model) this.#refuse(id, "value", failureText(error));
50586
+ if (this.model === model) this.#refused(account, failureText(error), keyed);
50420
50587
  return;
50421
50588
  }
50422
50589
  if (this.model !== model || this.entering !== id) {
@@ -50424,6 +50591,44 @@ var SliccSettings = class extends ThemedElement {
50424
50591
  return;
50425
50592
  }
50426
50593
  await this.#connectBases(account, void 0, fields, vault);
50594
+ const failed2 = [this.failure, this.problem].some((issue) => issue?.id === id);
50595
+ if (failed2 && settings && !keyed)
50596
+ await vault.settings?.(recordName(account), "model", before ?? {}).catch(() => {
50597
+ });
50598
+ }
50599
+ #settingsOf(account, fields) {
50600
+ const settable = (account.fields ?? []).filter((field) => field.vaultSetting);
50601
+ if (account.group === "mcp" || !settable.length) return void 0;
50602
+ return Object.fromEntries(
50603
+ settable.flatMap((field) => {
50604
+ const value = fields?.[field.name];
50605
+ if (!value || field.customBase && this.#baseOf(value)) return [];
50606
+ return [[field.vaultSetting, value]];
50607
+ })
50608
+ );
50609
+ }
50610
+ #record(account) {
50611
+ const name = recordName(account);
50612
+ return this.model?.vault?.list()?.secrets.find(
50613
+ (secret) => secret.name === name && secret.kind === "model" && secret.state !== "removed"
50614
+ );
50615
+ }
50616
+ #keyOptional(account) {
50617
+ const vault = this.#vault(account);
50618
+ const fields = account.fields ?? [];
50619
+ if (!vault || !fields.some((field) => field.vaultSetting || field.customBase)) return false;
50620
+ if (fields.some((field) => field.vaultSetting) && !vault.settings) return false;
50621
+ return !!this.#record(account);
50622
+ }
50623
+ #refused(account, message, keyed) {
50624
+ const field = (account.fields ?? []).find(
50625
+ (candidate) => [candidate.vaultSetting, candidate.name, candidate.label].some(
50626
+ (word) => word && words(message, word)
50627
+ )
50628
+ );
50629
+ if (field) this.#refuse(account.id, field.name, message);
50630
+ else if (keyed) this.#refuse(account.id, "value", message);
50631
+ else this.failure = { id: account.id, message };
50427
50632
  }
50428
50633
  #baseOf(value) {
50429
50634
  const trimmed = value?.trim() ?? "";
@@ -50493,7 +50698,7 @@ var SliccSettings = class extends ThemedElement {
50493
50698
  const message = this.failure.message;
50494
50699
  const field = (account.fields ?? []).find(
50495
50700
  (candidate) => candidate.customBase && !!fields?.[candidate.name] && [candidate.name, candidate.label, "https", "url", "port", "userinfo"].some(
50496
- (word) => new RegExp(`\\b${word.replace(/[^a-z0-9 ]/gi, "\\$&")}\\b`, "i").test(message)
50701
+ (word) => words(message, word)
50497
50702
  )
50498
50703
  );
50499
50704
  if (!field) return;
@@ -50728,7 +50933,8 @@ var SliccSettings = class extends ThemedElement {
50728
50933
  if (account.blocked) return false;
50729
50934
  if (!this.#offered()) return true;
50730
50935
  const required = (account.fields ?? []).filter((field) => field.required && !field.options);
50731
- return ["value", ...required.map((field) => field.name)].every((name) => this.filled.has(name));
50936
+ const key2 = this.#keyOptional(account) ? [] : ["value"];
50937
+ return [...key2, ...required.map((field) => field.name)].every((name) => this.filled.has(name));
50732
50938
  }
50733
50939
  #fill(event2) {
50734
50940
  const input = event2.target;
@@ -51639,10 +51845,10 @@ var SliccSprinkle = class extends ThemedElement {
51639
51845
  }
51640
51846
  #seen = /* @__PURE__ */ new Set();
51641
51847
  #frozen() {
51642
- const owner = this.#data?.agentId;
51848
+ const owner2 = this.#data?.agentId;
51643
51849
  const port = this.model?.agent;
51644
- if (!port || !owner) return false;
51645
- const id = plainId(owner);
51850
+ if (!port || !owner2) return false;
51851
+ const id = plainId(owner2);
51646
51852
  const agents = port.list();
51647
51853
  const live = agents.find((agent) => plainId(agent.id) === id);
51648
51854
  if (live) {
@@ -67019,17 +67225,17 @@ var ResizeManager = class ResizeManager2 {
67019
67225
  }
67020
67226
  observe(element3) {
67021
67227
  const { managersByElement } = ResizeManager2;
67022
- const owner = managersByElement.get(element3);
67023
- if (owner === this) return;
67024
- else if (owner != null && owner !== this) throw new Error("ResizeManager.observe: element is already owned by another ResizeManager");
67228
+ const owner2 = managersByElement.get(element3);
67229
+ if (owner2 === this) return;
67230
+ else if (owner2 != null && owner2 !== this) throw new Error("ResizeManager.observe: element is already owned by another ResizeManager");
67025
67231
  managersByElement.set(element3, this);
67026
67232
  ResizeManager2.getResizeObserver().observe(element3);
67027
67233
  }
67028
67234
  unobserve(element3) {
67029
67235
  const { managersByElement, resizeObserver } = ResizeManager2;
67030
- const owner = managersByElement.get(element3);
67031
- if (owner == null) return;
67032
- else if (owner !== this) throw new Error("ResizeManager.unobserve: element is owned by another ResizeManager");
67236
+ const owner2 = managersByElement.get(element3);
67237
+ if (owner2 == null) return;
67238
+ else if (owner2 !== this) throw new Error("ResizeManager.unobserve: element is owned by another ResizeManager");
67033
67239
  managersByElement.delete(element3);
67034
67240
  resizeObserver?.unobserve(element3);
67035
67241
  if (resizeObserver != null && managersByElement.size === 0) {