@ai-ecoverse/slicc-spectrum 1.76.0 → 1.77.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
@@ -11941,6 +11941,11 @@ var Dips = class extends Emitter {
11941
11941
  this.#storage?.setItem(this.key, JSON.stringify(this.list()));
11942
11942
  this.emit("change", this.list());
11943
11943
  }
11944
+ attach(id) {
11945
+ if (!this.#out.delete(id)) return;
11946
+ this.#storage?.setItem(this.key, JSON.stringify(this.list()));
11947
+ this.emit("change", this.list());
11948
+ }
11944
11949
  };
11945
11950
  function dipsOf(node) {
11946
11951
  let current = node;
@@ -14400,7 +14405,7 @@ function chatAgent(id) {
14400
14405
  function chats(dock) {
14401
14406
  return dock.api.panels.map((panel) => panel.id).filter((id) => chatAgent(id) !== null);
14402
14407
  }
14403
- function openChat(dock, agent, screen, focus = true, reveal = true) {
14408
+ function openChat(dock, agent, screen, focus = true, reveal2 = true) {
14404
14409
  const id = chatId(agent.id);
14405
14410
  if (!dock.has(id)) {
14406
14411
  const sibling = chats(dock)[0];
@@ -14415,7 +14420,7 @@ function openChat(dock, agent, screen, focus = true, reveal = true) {
14415
14420
  });
14416
14421
  }
14417
14422
  if (focus) dock.focusPanel(id);
14418
- else if (reveal) dock.reveal(id);
14423
+ else if (reveal2) dock.reveal(id);
14419
14424
  return id;
14420
14425
  }
14421
14426
  function openDocument(dock, kind, path, screen, focus = true) {
@@ -30885,11 +30890,14 @@ function question(item2, handlers) {
30885
30890
  <swc-button size="s" variant="accent" data-action="answer" ?disabled=${handlers.readOnly} @click=${(event2) => event2.currentTarget.closest("form")?.requestSubmit()}>Answer</swc-button>
30886
30891
  </form>`;
30887
30892
  }
30888
- return b`<div class="question" data-state=${item2.state} data-kind=${item2.kind}>
30893
+ return b`<div class="question decision" data-state=${item2.state} data-kind=${item2.kind}>
30889
30894
  <div class="q">${item2.question}</div>
30890
30895
  ${body4}
30891
30896
  </div>`;
30892
30897
  }
30898
+ function questionTurn(item2, handlers, pile) {
30899
+ return b`<swc-conversation-turn type="system" class="message decision-turn" data-id=${item2.id} data-state=${item2.state} data-pending=${item2.state === "open" ? "" : A} accessible-label=${`Question: ${item2.question}`} style=${pileStyle(pile)} @click=${reveal} @keydown=${reveal}>${question(item2, handlers)}</swc-conversation-turn>`;
30900
+ }
30893
30901
  var delegationVerbs = {
30894
30902
  feed: "fed",
30895
30903
  scoop: "started",
@@ -30947,7 +30955,7 @@ function part(entry, handlers) {
30947
30955
  }
30948
30956
  }
30949
30957
  function silent(entry, showThinking) {
30950
- return entry.type === "thinking" && !showThinking || entry.type === "link" && !!host2(entry.url);
30958
+ return entry.type === "question" || entry.type === "thinking" && !showThinking || entry.type === "link" && !!host2(entry.url);
30951
30959
  }
30952
30960
  function parts(entries, handlers, showThinking, extra = []) {
30953
30961
  const out = [];
@@ -31180,9 +31188,66 @@ var lickIcons = {
31180
31188
  "scoop-wait": "\u23F3",
31181
31189
  "sudo-request": "\u{1F510}"
31182
31190
  };
31183
- function lick(message, handlers, stamp = true) {
31191
+ function pendingApproval(message) {
31192
+ return message.role === "lick" && message.channel === "sudo-request" && message.state === "pending";
31193
+ }
31194
+ function waiting(message, now = Date.now()) {
31195
+ const at = message.role === "lick" ? message.expiresAt : void 0;
31196
+ return pendingApproval(message) && !(at !== void 0 && at <= now);
31197
+ }
31198
+ function clock(ms) {
31199
+ const seconds = Math.ceil(ms / 1e3);
31200
+ return `${Math.floor(seconds / 60)}:${String(seconds % 60).padStart(2, "0")}`;
31201
+ }
31202
+ var still = () => globalThis.matchMedia?.("(prefers-reduced-motion: reduce)").matches;
31203
+ function reveal(event2) {
31204
+ if (event2.target.closest("swc-button, input, textarea")) return;
31205
+ if (event2 instanceof KeyboardEvent && (event2.key !== "Enter" || event2.target !== event2.currentTarget))
31206
+ return;
31207
+ show(event2.currentTarget);
31208
+ }
31209
+ function show(turn) {
31210
+ const log = turn.closest(".log");
31211
+ if (!log) return;
31212
+ const origin2 = log.getBoundingClientRect().top;
31213
+ const pile = turn.getBoundingClientRect().top - origin2;
31214
+ turn.style.position = "static";
31215
+ const flow = turn.getBoundingClientRect();
31216
+ turn.style.position = "";
31217
+ const natural = flow.top - origin2;
31218
+ const bottom = Math.max(16, log.clientHeight - flow.height - 16);
31219
+ if (Math.abs(pile - natural) > 1)
31220
+ log.scrollTo({ top: log.scrollTop + natural - bottom, behavior: still() ? "auto" : "smooth" });
31221
+ }
31222
+ var flatPile = { stack: 0 };
31223
+ function pileStyle(pile) {
31224
+ return `--stack: ${pile.stack}`;
31225
+ }
31226
+ function spokenLeft(left, at) {
31227
+ if (left <= 0) return "Timed out";
31228
+ return left <= 6e4 ? "Less than a minute left" : `Expires at ${time(at)}`;
31229
+ }
31230
+ function decision(message, handlers, pile) {
31231
+ const at = message.expiresAt;
31232
+ const left = at === void 0 ? null : at - Date.now();
31233
+ const expired = left !== null && left <= 0;
31234
+ const timer = left === null ? A : b`<span class="timer" data-timer aria-hidden="true">${expired ? "Timed out" : clock(left)}</span><span class="sr">${spokenLeft(left, at)}</span>`;
31235
+ const actions2 = expired ? A : b`<div class="decide">
31236
+ <swc-button size="s" variant="secondary" fill-style="outline" data-action="deny" ?disabled=${handlers.readOnly} @click=${() => handlers.resolve(message.id, "dismissed")}>Deny</swc-button>
31237
+ <swc-button size="s" variant="accent" data-action="allow" ?disabled=${handlers.readOnly} @click=${() => handlers.resolve(message.id, "confirmed")}>Allow</swc-button>
31238
+ </div>`;
31239
+ return b`<swc-conversation-turn type="system" class="message decision-turn" accessible-label=${`Permission request from ${message.title}`} data-id=${message.id} data-pending=${expired ? A : ""} data-expired=${expired ? "" : A} style=${pileStyle(pile)} @click=${reveal} @keydown=${reveal}>
31240
+ <div class="decision" data-kind="approval">
31241
+ <div class="decision-head"><span class="who">${message.title}</span>${timer}</div>
31242
+ <p class="decision-text">${message.text}</p>
31243
+ ${message.body ? b`<pre class="command">${message.body}</pre>` : A}
31244
+ ${actions2}
31245
+ </div>
31246
+ </swc-conversation-turn>`;
31247
+ }
31248
+ function lick(message, handlers, stamp = true, pile = flatPile) {
31249
+ if (pendingApproval(message)) return decision(message, handlers, pile);
31184
31250
  const [label] = lickLabels[message.channel];
31185
- const decide = message.channel === "sudo-request" && message.state === "pending";
31186
31251
  const outcome = message.state === "confirmed" ? "Allowed" : message.state === "dismissed" ? "Denied" : "";
31187
31252
  const head2 = b`${message.severity ? b`<span class="sr">${severities[message.severity].prefix}</span>` : A}
31188
31253
  ${outcome ? b`<span class="sr">${outcome}. </span>` : A}
@@ -31190,28 +31255,20 @@ function lick(message, handlers, stamp = true) {
31190
31255
  ${message.count && message.count > 1 ? b`<span class="times" title="Coalesced events">×${message.count}</span>` : A}`;
31191
31256
  const outcomeIcon = message.state === "confirmed" ? "\u{1F44D}" : message.state === "dismissed" ? "\u{1F44E}" : "";
31192
31257
  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>`;
31193
- const actions2 = decide ? b`<div class="decide">
31194
- <swc-button size="s" variant="accent" data-action="allow" ?disabled=${handlers.readOnly} @click=${() => handlers.resolve(message.id, "confirmed")}>Allow</swc-button>
31195
- <swc-button size="s" variant="secondary" fill-style="outline" data-action="deny" ?disabled=${handlers.readOnly} @click=${() => handlers.resolve(message.id, "dismissed")}>Deny</swc-button>
31196
- </div>` : A;
31197
31258
  const name = `${message.severity ? severities[message.severity].prefix : ""}${label} event`;
31198
31259
  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>`;
31199
31260
  if (!message.body) {
31200
31261
  return outside(
31201
31262
  name,
31202
31263
  byline2,
31203
- 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>${actions2}</div>${reaction}</div>`
31264
+ 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>`
31204
31265
  );
31205
31266
  }
31206
31267
  const details = b`<details class="lick" data-id=${message.id} data-channel=${message.channel} data-severity=${message.severity ?? A}>
31207
31268
  <summary class="lick-head">${head2}<swc-icon-chevron-right class="chevron" size="s" aria-hidden="true"></swc-icon-chevron-right></summary>
31208
31269
  <pre class="output">${message.body}</pre>
31209
31270
  </details>`;
31210
- return outside(
31211
- name,
31212
- byline2,
31213
- b`<div class="lick-frame">${decide ? b`<div class="pending">${details}${actions2}</div>` : details}${reaction}</div>`
31214
- );
31271
+ return outside(name, byline2, b`<div class="lick-frame">${details}${reaction}</div>`);
31215
31272
  }
31216
31273
  var messageCss = i`
31217
31274
  swc-conversation-thread > .message,
@@ -32014,15 +32071,97 @@ var messageCss = i`
32014
32071
  gap: var(--swc-spacing-85);
32015
32072
  padding: 0 var(--swc-spacing-100) var(--swc-spacing-100);
32016
32073
  }
32017
- .pending {
32018
- border: var(--swc-border-width-100) solid var(--swc-notice-visual-color);
32019
- border-radius: var(--swc-corner-radius-700);
32074
+ swc-conversation-turn.decision-turn {
32075
+ container-type: inline-size;
32076
+ transition:
32077
+ top var(--swc-animation-duration-300) var(--swc-animation-ease-out),
32078
+ transform var(--swc-animation-duration-300) var(--swc-animation-ease-out),
32079
+ filter var(--swc-animation-duration-300) var(--swc-animation-ease-out);
32020
32080
  }
32021
- .pending > details.lick {
32022
- border: 0;
32081
+ @media (prefers-reduced-motion: reduce) {
32082
+ swc-conversation-turn.decision-turn {
32083
+ transition: none;
32084
+ }
32023
32085
  }
32024
- .pending > details.lick[data-severity] {
32025
- border-inline-start: var(--swc-border-width-400) solid var(--severity);
32086
+ slicc-sprinkle[inline] {
32087
+ position: relative;
32088
+ inset-inline-start: 50%;
32089
+ inline-size: calc(100cqw + var(--swc-spacing-300) * 2);
32090
+ transform: translateX(-50%);
32091
+ }
32092
+ swc-conversation-turn.decision-turn[data-pending] {
32093
+ position: sticky;
32094
+ top: calc(var(--stack, 0) * (var(--swc-spacing-600) + var(--swc-spacing-75)));
32095
+ z-index: calc(var(--stack, 0) + 1);
32096
+ }
32097
+ swc-conversation-turn.decision-turn[data-pinned] {
32098
+ transform: scale(calc(1 - var(--depth, 0) * 0.03));
32099
+ transform-origin: top center;
32100
+ filter: blur(calc(var(--depth, 0) * 0.6px));
32101
+ }
32102
+ .decision-turn[data-pending] .decision {
32103
+ border-color: var(--swc-gray-300);
32104
+ box-shadow: var(--swc-drop-shadow-elevated);
32105
+ }
32106
+ .decision-turn[data-pending]:focus-visible {
32107
+ outline: none;
32108
+ }
32109
+ .decision-turn[data-pending]:focus-visible .decision {
32110
+ outline: var(--swc-focus-indicator-thickness) solid var(--swc-focus-indicator-color);
32111
+ outline-offset: var(--swc-focus-ring-gap);
32112
+ }
32113
+ .decision {
32114
+ display: flex;
32115
+ flex-direction: column;
32116
+ gap: var(--swc-spacing-100);
32117
+ margin-inline: var(--swc-spacing-700);
32118
+ padding: var(--swc-spacing-200) var(--swc-spacing-300);
32119
+ border: var(--swc-border-width-100) solid var(--swc-gray-200);
32120
+ border-radius: var(--swc-corner-radius-100);
32121
+ background: var(--swc-background-layer-1-color);
32122
+ font-size: var(--swc-font-size-200);
32123
+ }
32124
+ .decision-head {
32125
+ display: flex;
32126
+ align-items: baseline;
32127
+ justify-content: space-between;
32128
+ gap: var(--swc-spacing-100);
32129
+ font-size: var(--swc-font-size-100);
32130
+ }
32131
+ .decision-head .who {
32132
+ font-weight: var(--swc-bold-font-weight);
32133
+ }
32134
+ .timer {
32135
+ color: var(--swc-neutral-subdued-content-color-default);
32136
+ font-variant-numeric: tabular-nums;
32137
+ white-space: nowrap;
32138
+ }
32139
+ .decision-text {
32140
+ margin: 0;
32141
+ }
32142
+ .command {
32143
+ margin: 0;
32144
+ padding: var(--swc-spacing-75) var(--swc-spacing-100);
32145
+ border-radius: var(--swc-corner-radius-100);
32146
+ background: var(--swc-background-layer-2-color);
32147
+ font-size: var(--swc-font-size-75);
32148
+ white-space: pre-wrap;
32149
+ overflow-wrap: anywhere;
32150
+ }
32151
+ .decision .decide,
32152
+ .question.decision .options {
32153
+ justify-content: flex-end;
32154
+ padding: 0;
32155
+ }
32156
+ .question.decision .options {
32157
+ display: flex;
32158
+ flex-wrap: wrap;
32159
+ gap: var(--swc-spacing-85);
32160
+ }
32161
+ @container (inline-size < 480px) {
32162
+ .decision {
32163
+ margin-inline: var(--swc-spacing-300);
32164
+ }
32026
32165
  }
32027
32166
  `;
32028
32167
 
@@ -33291,6 +33430,11 @@ var SliccApp = class extends ThemedElement {
33291
33430
  return cone ? { ...sprinkle, title: `${cone.name} / ${sprinkle.title}` } : sprinkle;
33292
33431
  }).sort((a23, b13) => order.indexOf(a23.agentId) - order.indexOf(b13.agentId));
33293
33432
  }
33433
+ #panelClosed = (event2) => {
33434
+ const id = event2.detail.id;
33435
+ this.panels.closed(id);
33436
+ if (id.startsWith("sprinkle:")) this.dips.attach(id.slice("sprinkle:".length));
33437
+ };
33294
33438
  #detach(id, place3, open) {
33295
33439
  const sprinkle = this.#sprinkle(id);
33296
33440
  if (!sprinkle) return;
@@ -33546,7 +33690,7 @@ var SliccApp = class extends ThemedElement {
33546
33690
  <main>
33547
33691
  ${rails.left}
33548
33692
  <slicc-dock empty-text="All panels are closed. Open one from a rail or View." @layout-change=${this.#save}
33549
- @panel-close=${(event2) => this.panels.closed(event2.detail.id)}
33693
+ @panel-close=${this.#panelClosed}
33550
33694
  @active-panel-change=${this.#activated}
33551
33695
  @external-drop=${this.#dropped}
33552
33696
  @detach-sprinkle=${(event2) => this.#detach(event2.detail.id, null, true)}
@@ -33787,7 +33931,7 @@ var inCwd = (approval) => approval.alwaysScope === "cwd" && !!approval.cwd;
33787
33931
  function alwaysHint(approval) {
33788
33932
  return inCwd(approval) ? `Always allow lets ${approval.requester.name} run exactly this command with sudo, in ${approval.cwd}, without asking again.` : `Always allow lets ${approval.requester.name} run exactly this command with sudo without asking again.`;
33789
33933
  }
33790
- function waiting(model, now) {
33934
+ function waiting2(model, now) {
33791
33935
  return [...model?.approvals?.list() ?? []].filter((approval) => approval.expiresAt > now).sort((a23, b13) => a23.createdAt - b13.createdAt);
33792
33936
  }
33793
33937
  function gone(reason) {
@@ -33924,16 +34068,16 @@ var SliccApprovals = class extends ModelElement {
33924
34068
  this.#timer = null;
33925
34069
  }
33926
34070
  }
33927
- async decide(approval, decision) {
34071
+ async decide(approval, decision2) {
33928
34072
  const port = this.model?.approvals;
33929
- if (!port?.decide || this.busy || decision !== "deny" && !this.#armed) return;
34073
+ if (!port?.decide || this.busy || decision2 !== "deny" && !this.#armed) return;
33930
34074
  const verdict = secretRules(approval.command, approval.cwd);
33931
- if (decision === "always" && (verdict.certain ?? verdict.maybe)) return;
33932
- this.busy = decision;
34075
+ if (decision2 === "always" && (verdict.certain ?? verdict.maybe)) return;
34076
+ this.busy = decision2;
33933
34077
  this.failure = null;
33934
34078
  this.#pending.add(approval.id);
33935
34079
  try {
33936
- await port.decide(approval.id, decision);
34080
+ await port.decide(approval.id, decision2);
33937
34081
  this.#done.add(approval.id);
33938
34082
  } catch (error) {
33939
34083
  this.failure = {
@@ -33946,7 +34090,7 @@ var SliccApprovals = class extends ModelElement {
33946
34090
  }
33947
34091
  }
33948
34092
  #current() {
33949
- return this.model?.approvals?.decide ? waiting(this.model, this.now) : [];
34093
+ return this.model?.approvals?.decide ? waiting2(this.model, this.now) : [];
33950
34094
  }
33951
34095
  willUpdate(changed) {
33952
34096
  super.willUpdate(changed);
@@ -33992,15 +34136,15 @@ var SliccApprovals = class extends ModelElement {
33992
34136
  if (this.gone) this.close();
33993
34137
  else void this.decide(approval, "deny");
33994
34138
  }
33995
- #button(approval, decision, label, variant) {
34139
+ #button(approval, decision2, label, variant) {
33996
34140
  return b`<swc-button
33997
34141
  slot="button"
33998
34142
  variant=${variant}
33999
34143
  fill-style=${variant === "accent" ? A : "outline"}
34000
- data-action=${decision}
34001
- ?pending=${this.busy === decision}
34002
- ?disabled=${!!this.busy && this.busy !== decision}
34003
- @click=${() => this.decide(approval, decision)}
34144
+ data-action=${decision2}
34145
+ ?pending=${this.busy === decision2}
34146
+ ?disabled=${!!this.busy && this.busy !== decision2}
34147
+ @click=${() => this.decide(approval, decision2)}
34004
34148
  >${label}</swc-button
34005
34149
  >`;
34006
34150
  }
@@ -36176,6 +36320,19 @@ var navigation = /* @__PURE__ */ new Set([
36176
36320
  ]);
36177
36321
  var upward = /* @__PURE__ */ new Set(["ArrowUp", "PageUp", "Home"]);
36178
36322
  var compactWidth = 560;
36323
+ function pendingKeys(messages) {
36324
+ return messages.flatMap((message) => {
36325
+ if (waiting(message)) return [message.id];
36326
+ if (message.role !== "assistant") return [];
36327
+ return message.parts.flatMap(
36328
+ (part2) => part2.type === "question" && part2.question.state === "open" ? [`${message.id}:${part2.question.id}`] : []
36329
+ );
36330
+ });
36331
+ }
36332
+ function pendingPiles(messages) {
36333
+ const keys = pendingKeys(messages);
36334
+ return new Map(keys.map((key2, stack) => [key2, { stack }]));
36335
+ }
36179
36336
  var failed = (message) => message.role === "assistant" ? message.status === "error" : message.role === "system" && message.kind === "error";
36180
36337
  function carriesFiles(event2) {
36181
36338
  const types = [...event2.dataTransfer?.types ?? []];
@@ -36324,6 +36481,7 @@ var SliccChat = class extends ThemedElement {
36324
36481
  padding: var(--swc-spacing-400) var(--swc-spacing-300) calc(var(--composer-height, 0px) + var(--swc-spacing-500));
36325
36482
  scroll-padding-block-end: calc(var(--composer-height, 0px) + var(--swc-spacing-200));
36326
36483
  font-size: var(--swc-font-size-200);
36484
+ scroll-padding-block-start: var(--pile, 0px);
36327
36485
  }
36328
36486
  @container (min-width: 600px) {
36329
36487
  .column {
@@ -36429,7 +36587,21 @@ var SliccChat = class extends ThemedElement {
36429
36587
  #from = 0;
36430
36588
  #start = 0;
36431
36589
  #settle;
36590
+ #tick;
36432
36591
  #tail = "";
36592
+ #held = "";
36593
+ #focused = (event2) => {
36594
+ const turn = event2.target.closest(".decision-turn[data-pending]");
36595
+ this.#held = turn?.dataset.id ?? "";
36596
+ if (turn && Number(turn.style.getPropertyValue("--depth")) > 0) show(turn);
36597
+ };
36598
+ #refocus() {
36599
+ if (!this.#held || this.renderRoot.querySelector(":focus")) return;
36600
+ this.#held = "";
36601
+ const next = this.renderRoot.querySelector(".decision-turn[data-pending]");
36602
+ if (next) next.focus();
36603
+ else this.renderRoot.querySelector("slicc-composer")?.focus();
36604
+ }
36433
36605
  #sent(agentId) {
36434
36606
  const last = this.model?.agent.messages(agentId).at(-1);
36435
36607
  return last?.role === "user" && last.id !== this.#tail && (last.origin ?? "user") === "user";
@@ -36458,6 +36630,7 @@ var SliccChat = class extends ThemedElement {
36458
36630
  else if (reader && log.scrollTop < this.#last) this.#stuck = false;
36459
36631
  this.#last = log.scrollTop;
36460
36632
  this.#height = log.scrollHeight;
36633
+ this.#pile();
36461
36634
  };
36462
36635
  #intent = {
36463
36636
  passive: true,
@@ -36505,9 +36678,6 @@ var SliccChat = class extends ThemedElement {
36505
36678
  if (composer) this.#fit.observe(composer);
36506
36679
  else this.style.setProperty("--composer-height", "0px");
36507
36680
  }
36508
- updated() {
36509
- this.#attach();
36510
- }
36511
36681
  connectedCallback() {
36512
36682
  super.connectedCallback();
36513
36683
  globalThis.addEventListener("pointerup", this.#release, true);
@@ -36522,6 +36692,59 @@ var SliccChat = class extends ThemedElement {
36522
36692
  this.#fit.disconnect();
36523
36693
  this.#fitted = null;
36524
36694
  clearTimeout(this.#settle);
36695
+ clearInterval(this.#tick);
36696
+ this.#tick = void 0;
36697
+ }
36698
+ updated() {
36699
+ this.#attach();
36700
+ const timed = this.#timed();
36701
+ if (timed && this.#tick === void 0)
36702
+ this.#tick = setInterval(() => this.requestUpdate(), 1e3);
36703
+ if (!timed && this.#tick !== void 0) {
36704
+ clearInterval(this.#tick);
36705
+ this.#tick = void 0;
36706
+ }
36707
+ this.#pile();
36708
+ this.#refocus();
36709
+ }
36710
+ #pile() {
36711
+ const turns = [
36712
+ ...this.renderRoot.querySelectorAll(".decision-turn[data-pending]")
36713
+ ];
36714
+ const drawn = turns.map((turn) => turn.getBoundingClientRect().top);
36715
+ for (const turn of turns) turn.style.position = "static";
36716
+ const flow = turns.map((turn) => turn.getBoundingClientRect().top);
36717
+ for (const turn of turns) turn.style.position = "";
36718
+ const pinned = turns.flatMap(
36719
+ (turn, index) => drawn[index] - flow[index] > 1 ? [{ turn, stack: Number.parseFloat(turn.style.getPropertyValue("--stack")) }] : []
36720
+ );
36721
+ for (const turn of turns) {
36722
+ if (!pinned.some((entry) => entry.turn === turn)) {
36723
+ turn.removeAttribute("data-pinned");
36724
+ turn.style.removeProperty("--depth");
36725
+ }
36726
+ }
36727
+ for (const { turn, stack } of pinned) {
36728
+ turn.setAttribute("data-pinned", "");
36729
+ turn.style.setProperty(
36730
+ "--depth",
36731
+ String(pinned.filter((other) => other.stack > stack).length)
36732
+ );
36733
+ }
36734
+ const log = this.renderRoot.querySelector(".log");
36735
+ const top = log?.getBoundingClientRect().top ?? 0;
36736
+ const reach3 = Math.max(
36737
+ 0,
36738
+ ...pinned.map(({ turn }) => turn.getBoundingClientRect().bottom - top)
36739
+ );
36740
+ log?.style.setProperty("--pile", `${reach3}px`);
36741
+ }
36742
+ #timed() {
36743
+ const model = this.model;
36744
+ if (!model) return false;
36745
+ return model.agent.messages(this.#id()).some(
36746
+ (message) => waiting(message) && "expiresAt" in message && message.expiresAt !== void 0
36747
+ );
36525
36748
  }
36526
36749
  firstUpdated() {
36527
36750
  this.#observe();
@@ -36574,7 +36797,7 @@ var SliccChat = class extends ThemedElement {
36574
36797
  new CustomEvent("show-surface", { detail: { id: "settings" }, bubbles: true, composed: true })
36575
36798
  );
36576
36799
  }
36577
- #message(message, agent, suggestion, changed, extra, owners, stamp) {
36800
+ #message(message, agent, suggestion, changed, extra, owners, stamp, pile) {
36578
36801
  const model = this.model;
36579
36802
  const handlers = { ...this.#handlers, owners };
36580
36803
  switch (message.role) {
@@ -36598,7 +36821,7 @@ var SliccChat = class extends ThemedElement {
36598
36821
  case "system":
36599
36822
  return system(message, this.#handlers);
36600
36823
  default:
36601
- return lick(message, this.#handlers, stamp);
36824
+ return lick(message, this.#handlers, stamp, pile);
36602
36825
  }
36603
36826
  }
36604
36827
  #items(messages, agent) {
@@ -36613,6 +36836,7 @@ var SliccChat = class extends ThemedElement {
36613
36836
  const owners = /* @__PURE__ */ new Map();
36614
36837
  const into = /* @__PURE__ */ new Map();
36615
36838
  const stamped = stamps(messages);
36839
+ const piles = pendingPiles(messages);
36616
36840
  let anchor = null;
36617
36841
  for (const message of messages) {
36618
36842
  const calls = toolOnly(message, showThinking);
@@ -36653,12 +36877,24 @@ var SliccChat = class extends ThemedElement {
36653
36877
  changed,
36654
36878
  extra.get(message.id) ?? [],
36655
36879
  owners,
36656
- stamped.has(message.id)
36880
+ stamped.has(message.id),
36881
+ piles.get(message.id) ?? flatPile
36657
36882
  )
36658
36883
  ]);
36884
+ out.push(...this.#questions(message, piles));
36659
36885
  }
36660
36886
  return out;
36661
36887
  }
36888
+ #questions(message, piles) {
36889
+ if (message.role !== "assistant") return [];
36890
+ return message.parts.flatMap((part2) => {
36891
+ if (part2.type !== "question") return [];
36892
+ const key2 = `${message.id}:${part2.question.id}`;
36893
+ return [
36894
+ [key2, questionTurn(part2.question, this.#handlers, piles.get(key2) ?? flatPile)]
36895
+ ];
36896
+ });
36897
+ }
36662
36898
  #newChat(agent) {
36663
36899
  this.model.agent.clear(agent.id);
36664
36900
  this.renderRoot.querySelector("slicc-composer")?.focus();
@@ -36720,6 +36956,7 @@ var SliccChat = class extends ThemedElement {
36720
36956
  @touchmove=${this.#intent}
36721
36957
  @keydown=${this.#intent}
36722
36958
  @pointerdown=${this.#hold}
36959
+ @focusin=${this.#focused}
36723
36960
  >
36724
36961
  <div class="column" @keydown=${this.#keys}>
36725
36962
  ${agent && messages.length > 0 ? b`<swc-conversation-thread>${c(
@@ -37933,14 +38170,14 @@ function release(attachments) {
37933
38170
  }
37934
38171
  function retire(agent, id, attachments) {
37935
38172
  const urls = new Set(attachments.flatMap((attachment2) => attachment2.url ?? []));
37936
- const waiting2 = () => agent.queue(id).some((message) => message.attachments?.some((attachment2) => urls.has(attachment2.url ?? "")));
38173
+ const waiting3 = () => agent.queue(id).some((message) => message.attachments?.some((attachment2) => urls.has(attachment2.url ?? "")));
37937
38174
  const later = () => void setTimeout(() => release(attachments), linger);
37938
- if (!waiting2()) {
38175
+ if (!waiting3()) {
37939
38176
  later();
37940
38177
  return;
37941
38178
  }
37942
38179
  const off = agent.on("messages", () => {
37943
- if (waiting2()) return;
38180
+ if (waiting3()) return;
37944
38181
  off();
37945
38182
  later();
37946
38183
  });
@@ -38760,7 +38997,7 @@ var SliccComposer = class extends ThemedElement {
38760
38997
  if (popup2.kind === "secret") {
38761
38998
  const vault = this.model?.vault;
38762
38999
  const valued = !!vault?.put && !blocked(vault);
38763
- const waiting2 = vault?.put ? blocked(vault) : null;
39000
+ const waiting3 = vault?.put ? blocked(vault) : null;
38764
39001
  if (!valued) clearValues(this.renderRoot);
38765
39002
  const submit = (event2) => {
38766
39003
  event2.preventDefault();
@@ -38779,7 +39016,7 @@ var SliccComposer = class extends ThemedElement {
38779
39016
  const described = (name) => this.secretField === name ? "secret-error" : A;
38780
39017
  return b`<form class="popup secret" data-vault=${valued ? "on" : "off"} @submit=${submit} @keydown=${close} @input=${() => this.secretField = null}>
38781
39018
  <strong>Share a secret</strong>
38782
- <span class="lead">${valued ? "The value goes to the vault; the agent only sees its name. Leave the value empty to share a secret the vault already has." : waiting2 === "Unlock the vault first" ? "The vault is locked. You can share the name now and add the value once it is unlocked." : waiting2 ? "Waiting for the vault. You can share the name now and add the value in Settings \u203A Secrets later." : "Add the value in Settings \u203A Secrets. The agent only sees the name."}</span>
39019
+ <span class="lead">${valued ? "The value goes to the vault; the agent only sees its name. Leave the value empty to share a secret the vault already has." : waiting3 === "Unlock the vault first" ? "The vault is locked. You can share the name now and add the value once it is unlocked." : waiting3 ? "Waiting for the vault. You can share the name now and add the value in Settings \u203A Secrets later." : "Add the value in Settings \u203A Secrets. The agent only sees the name."}</span>
38783
39020
  <label class="field"><span>Name</span><input name="name" placeholder="API_TOKEN" autocomplete="off" spellcheck="false" aria-invalid=${bad("name")} aria-describedby=${described("name")} /></label>
38784
39021
  ${valued ? b`<label class="field"><span>Value</span><input name="value" type="password" autocomplete="off" spellcheck="false" aria-invalid=${bad("value")} aria-describedby=${described("value")} /></label>` : A}
38785
39022
  ${this.secretError ? b`<span class="secret-error" role="alert" id="secret-error" data-error><swc-icon-alert-triangle size="s" aria-hidden="true"></swc-icon-alert-triangle><span>${this.secretError}</span></span>` : A}
@@ -39502,7 +39739,7 @@ var SliccNotices = class extends ModelElement {
39502
39739
  (notice) => notice.dismissible === false || !this.dismissed.has(notice.id)
39503
39740
  );
39504
39741
  const tabs = this.#tabs();
39505
- const approvals = this.model?.approvals?.decide ? [] : waiting(this.model, Date.now());
39742
+ const approvals = this.model?.approvals?.decide ? [] : waiting2(this.model, Date.now());
39506
39743
  if (!notices.length && !approvals.length && tabs === A) return A;
39507
39744
  return b`<div class="notices">${tabs}${approvals.map((approval) => this.#approval(approval))}${c(
39508
39745
  notices,
@@ -40453,7 +40690,7 @@ var SliccSecrets = class extends ThemedElement {
40453
40690
  }
40454
40691
  #secret(vault, secret, entries, devices, more, locked) {
40455
40692
  const kind = kinds[secret.kind];
40456
- const reveal = revealed(secret.name, entries, Date.now(), more);
40693
+ const reveal2 = revealed(secret.name, entries, Date.now(), more);
40457
40694
  const exec = execLine(secret.exec, secret.execArgv);
40458
40695
  const gone3 = secret.state === "removed";
40459
40696
  const form = this.#offline ? null : this.form;
@@ -40467,7 +40704,7 @@ var SliccSecrets = class extends ThemedElement {
40467
40704
  <dl>
40468
40705
  ${this.#reach(secret, gone3)}
40469
40706
  ${secret.exits ? b`<dt>${gone3 ? "Left through" : "Leaves through"}</dt><dd data-exits>${secret.exits.length ? list4(secret.exits.map((exit) => exitLabel(exit, devices))) : "No exit"}</dd>` : A}
40470
- ${exec || reveal ? b`<dt>Programs</dt><dd data-exec>${exec}${exec && reveal ? b`<br />` : A}${reveal}</dd>` : A}
40707
+ ${exec || reveal2 ? b`<dt>Programs</dt><dd data-exec>${exec}${exec && reveal2 ? b`<br />` : A}${reveal2}</dd>` : A}
40471
40708
  ${secret.kind === "model" && secret.provider ? b`<dt>Provider</dt><dd data-provider>${this.#providerName(secret.provider)}</dd>` : A}
40472
40709
  ${secret.kind === "mcp" && secret.server ? b`<dt>Server</dt><dd data-server>${secret.server}</dd>` : A}
40473
40710
  <dt>From</dt>
@@ -40576,7 +40813,7 @@ var SliccSecrets = class extends ThemedElement {
40576
40813
  this.#offline = away(vault);
40577
40814
  if (this.#offline && this.form) clearValues(this.renderRoot);
40578
40815
  const now = Date.now();
40579
- const waiting2 = (status2?.waiting ?? []).filter((request2) => request2.expiresAt > now);
40816
+ const waiting3 = (status2?.waiting ?? []).filter((request2) => request2.expiresAt > now);
40580
40817
  return b`<section aria-labelledby="secrets" data-vault=${status2 ? status2.locked ? "locked" : "unlocked" : "loading"}>
40581
40818
  <div class="head">
40582
40819
  <h2 id="secrets">Secrets</h2>
@@ -40586,7 +40823,7 @@ var SliccSecrets = class extends ThemedElement {
40586
40823
  ${this.#availability()}
40587
40824
  ${this.#feedback()}
40588
40825
  ${this.failure ? this.#error(this.failure, "vault") : A}
40589
- ${waiting2.length ? b`<h3>Waiting for you</h3>${waiting2.map((request2) => this.#request(vault, request2))}` : A}
40826
+ ${waiting3.length ? b`<h3>Waiting for you</h3>${waiting3.map((request2) => this.#request(vault, request2))}` : A}
40590
40827
  ${this.#backup(vault, status2)}
40591
40828
  ${this.#tools(vault, locked)}
40592
40829
  ${!current ? this.#offline ? A : b`<p class="muted" data-loading>Loading secrets…</p>` : current.secrets.length ? this.#secrets(vault, current, entries, devices, locked) : b`<p data-empty>No secrets yet. Add one here, or enter the value when an agent or program asks for one.</p>`}
@@ -41474,6 +41711,9 @@ var SliccSprinkle = class extends ThemedElement {
41474
41711
  height: auto;
41475
41712
  background: none;
41476
41713
  }
41714
+ :host([inline]) .handle {
41715
+ margin-inline-start: 20px;
41716
+ }
41477
41717
  iframe {
41478
41718
  display: block;
41479
41719
  width: 100%;