@ai-ecoverse/slicc-spectrum 1.76.0 → 1.77.1

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
@@ -10083,6 +10083,11 @@ var panelCss = i`
10083
10083
  .spacer {
10084
10084
  flex: 1;
10085
10085
  }
10086
+ .bar [data-watch-note] {
10087
+ flex: none;
10088
+ font-size: var(--swc-font-size-75);
10089
+ color: var(--swc-neutral-subdued-content-color-default);
10090
+ }
10086
10091
  .body {
10087
10092
  flex: 1;
10088
10093
  min-height: 0;
@@ -11941,6 +11946,11 @@ var Dips = class extends Emitter {
11941
11946
  this.#storage?.setItem(this.key, JSON.stringify(this.list()));
11942
11947
  this.emit("change", this.list());
11943
11948
  }
11949
+ attach(id) {
11950
+ if (!this.#out.delete(id)) return;
11951
+ this.#storage?.setItem(this.key, JSON.stringify(this.list()));
11952
+ this.emit("change", this.list());
11953
+ }
11944
11954
  };
11945
11955
  function dipsOf(node) {
11946
11956
  let current = node;
@@ -14400,7 +14410,7 @@ function chatAgent(id) {
14400
14410
  function chats(dock) {
14401
14411
  return dock.api.panels.map((panel) => panel.id).filter((id) => chatAgent(id) !== null);
14402
14412
  }
14403
- function openChat(dock, agent, screen, focus = true, reveal = true) {
14413
+ function openChat(dock, agent, screen, focus = true, reveal2 = true) {
14404
14414
  const id = chatId(agent.id);
14405
14415
  if (!dock.has(id)) {
14406
14416
  const sibling = chats(dock)[0];
@@ -14415,7 +14425,7 @@ function openChat(dock, agent, screen, focus = true, reveal = true) {
14415
14425
  });
14416
14426
  }
14417
14427
  if (focus) dock.focusPanel(id);
14418
- else if (reveal) dock.reveal(id);
14428
+ else if (reveal2) dock.reveal(id);
14419
14429
  return id;
14420
14430
  }
14421
14431
  function openDocument(dock, kind, path, screen, focus = true) {
@@ -30885,11 +30895,14 @@ function question(item2, handlers) {
30885
30895
  <swc-button size="s" variant="accent" data-action="answer" ?disabled=${handlers.readOnly} @click=${(event2) => event2.currentTarget.closest("form")?.requestSubmit()}>Answer</swc-button>
30886
30896
  </form>`;
30887
30897
  }
30888
- return b`<div class="question" data-state=${item2.state} data-kind=${item2.kind}>
30898
+ return b`<div class="question decision" data-state=${item2.state} data-kind=${item2.kind}>
30889
30899
  <div class="q">${item2.question}</div>
30890
30900
  ${body4}
30891
30901
  </div>`;
30892
30902
  }
30903
+ function questionTurn(item2, handlers, pile) {
30904
+ 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>`;
30905
+ }
30893
30906
  var delegationVerbs = {
30894
30907
  feed: "fed",
30895
30908
  scoop: "started",
@@ -30947,7 +30960,7 @@ function part(entry, handlers) {
30947
30960
  }
30948
30961
  }
30949
30962
  function silent(entry, showThinking) {
30950
- return entry.type === "thinking" && !showThinking || entry.type === "link" && !!host2(entry.url);
30963
+ return entry.type === "question" || entry.type === "thinking" && !showThinking || entry.type === "link" && !!host2(entry.url);
30951
30964
  }
30952
30965
  function parts(entries, handlers, showThinking, extra = []) {
30953
30966
  const out = [];
@@ -31180,9 +31193,66 @@ var lickIcons = {
31180
31193
  "scoop-wait": "\u23F3",
31181
31194
  "sudo-request": "\u{1F510}"
31182
31195
  };
31183
- function lick(message, handlers, stamp = true) {
31196
+ function pendingApproval(message) {
31197
+ return message.role === "lick" && message.channel === "sudo-request" && message.state === "pending";
31198
+ }
31199
+ function waiting(message, now = Date.now()) {
31200
+ const at = message.role === "lick" ? message.expiresAt : void 0;
31201
+ return pendingApproval(message) && !(at !== void 0 && at <= now);
31202
+ }
31203
+ function clock(ms) {
31204
+ const seconds = Math.ceil(ms / 1e3);
31205
+ return `${Math.floor(seconds / 60)}:${String(seconds % 60).padStart(2, "0")}`;
31206
+ }
31207
+ var still = () => globalThis.matchMedia?.("(prefers-reduced-motion: reduce)").matches;
31208
+ function reveal(event2) {
31209
+ if (event2.target.closest("swc-button, input, textarea")) return;
31210
+ if (event2 instanceof KeyboardEvent && (event2.key !== "Enter" || event2.target !== event2.currentTarget))
31211
+ return;
31212
+ show(event2.currentTarget);
31213
+ }
31214
+ function show(turn) {
31215
+ const log = turn.closest(".log");
31216
+ if (!log) return;
31217
+ const origin2 = log.getBoundingClientRect().top;
31218
+ const pile = turn.getBoundingClientRect().top - origin2;
31219
+ turn.style.position = "static";
31220
+ const flow = turn.getBoundingClientRect();
31221
+ turn.style.position = "";
31222
+ const natural = flow.top - origin2;
31223
+ const bottom = Math.max(16, log.clientHeight - flow.height - 16);
31224
+ if (Math.abs(pile - natural) > 1)
31225
+ log.scrollTo({ top: log.scrollTop + natural - bottom, behavior: still() ? "auto" : "smooth" });
31226
+ }
31227
+ var flatPile = { stack: 0 };
31228
+ function pileStyle(pile) {
31229
+ return `--stack: ${pile.stack}`;
31230
+ }
31231
+ function spokenLeft(left, at) {
31232
+ if (left <= 0) return "Timed out";
31233
+ return left <= 6e4 ? "Less than a minute left" : `Expires at ${time(at)}`;
31234
+ }
31235
+ function decision(message, handlers, pile) {
31236
+ const at = message.expiresAt;
31237
+ const left = at === void 0 ? null : at - Date.now();
31238
+ const expired = left !== null && left <= 0;
31239
+ 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>`;
31240
+ const actions2 = expired ? A : b`<div class="decide">
31241
+ <swc-button size="s" variant="secondary" fill-style="outline" data-action="deny" ?disabled=${handlers.readOnly} @click=${() => handlers.resolve(message.id, "dismissed")}>Deny</swc-button>
31242
+ <swc-button size="s" variant="accent" data-action="allow" ?disabled=${handlers.readOnly} @click=${() => handlers.resolve(message.id, "confirmed")}>Allow</swc-button>
31243
+ </div>`;
31244
+ 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}>
31245
+ <div class="decision" data-kind="approval">
31246
+ <div class="decision-head"><span class="who">${message.title}</span>${timer}</div>
31247
+ <p class="decision-text">${message.text}</p>
31248
+ ${message.body ? b`<pre class="command">${message.body}</pre>` : A}
31249
+ ${actions2}
31250
+ </div>
31251
+ </swc-conversation-turn>`;
31252
+ }
31253
+ function lick(message, handlers, stamp = true, pile = flatPile) {
31254
+ if (pendingApproval(message)) return decision(message, handlers, pile);
31184
31255
  const [label] = lickLabels[message.channel];
31185
- const decide = message.channel === "sudo-request" && message.state === "pending";
31186
31256
  const outcome = message.state === "confirmed" ? "Allowed" : message.state === "dismissed" ? "Denied" : "";
31187
31257
  const head2 = b`${message.severity ? b`<span class="sr">${severities[message.severity].prefix}</span>` : A}
31188
31258
  ${outcome ? b`<span class="sr">${outcome}. </span>` : A}
@@ -31190,28 +31260,20 @@ function lick(message, handlers, stamp = true) {
31190
31260
  ${message.count && message.count > 1 ? b`<span class="times" title="Coalesced events">×${message.count}</span>` : A}`;
31191
31261
  const outcomeIcon = message.state === "confirmed" ? "\u{1F44D}" : message.state === "dismissed" ? "\u{1F44E}" : "";
31192
31262
  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
31263
  const name = `${message.severity ? severities[message.severity].prefix : ""}${label} event`;
31198
31264
  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
31265
  if (!message.body) {
31200
31266
  return outside(
31201
31267
  name,
31202
31268
  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>`
31269
+ 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
31270
  );
31205
31271
  }
31206
31272
  const details = b`<details class="lick" data-id=${message.id} data-channel=${message.channel} data-severity=${message.severity ?? A}>
31207
31273
  <summary class="lick-head">${head2}<swc-icon-chevron-right class="chevron" size="s" aria-hidden="true"></swc-icon-chevron-right></summary>
31208
31274
  <pre class="output">${message.body}</pre>
31209
31275
  </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
- );
31276
+ return outside(name, byline2, b`<div class="lick-frame">${details}${reaction}</div>`);
31215
31277
  }
31216
31278
  var messageCss = i`
31217
31279
  swc-conversation-thread > .message,
@@ -32014,15 +32076,97 @@ var messageCss = i`
32014
32076
  gap: var(--swc-spacing-85);
32015
32077
  padding: 0 var(--swc-spacing-100) var(--swc-spacing-100);
32016
32078
  }
32017
- .pending {
32018
- border: var(--swc-border-width-100) solid var(--swc-notice-visual-color);
32019
- border-radius: var(--swc-corner-radius-700);
32079
+ swc-conversation-turn.decision-turn {
32080
+ container-type: inline-size;
32081
+ transition:
32082
+ top var(--swc-animation-duration-300) var(--swc-animation-ease-out),
32083
+ transform var(--swc-animation-duration-300) var(--swc-animation-ease-out),
32084
+ filter var(--swc-animation-duration-300) var(--swc-animation-ease-out);
32020
32085
  }
32021
- .pending > details.lick {
32022
- border: 0;
32086
+ @media (prefers-reduced-motion: reduce) {
32087
+ swc-conversation-turn.decision-turn {
32088
+ transition: none;
32089
+ }
32023
32090
  }
32024
- .pending > details.lick[data-severity] {
32025
- border-inline-start: var(--swc-border-width-400) solid var(--severity);
32091
+ slicc-sprinkle[inline] {
32092
+ position: relative;
32093
+ inset-inline-start: 50%;
32094
+ inline-size: calc(100cqw + var(--swc-spacing-300) * 2);
32095
+ transform: translateX(-50%);
32096
+ }
32097
+ swc-conversation-turn.decision-turn[data-pending] {
32098
+ position: sticky;
32099
+ top: calc(var(--stack, 0) * (var(--swc-spacing-600) + var(--swc-spacing-75)));
32100
+ z-index: calc(var(--stack, 0) + 1);
32101
+ }
32102
+ swc-conversation-turn.decision-turn[data-pinned] {
32103
+ transform: scale(calc(1 - var(--depth, 0) * 0.03));
32104
+ transform-origin: top center;
32105
+ filter: blur(calc(var(--depth, 0) * 0.6px));
32106
+ }
32107
+ .decision-turn[data-pending] .decision {
32108
+ border-color: var(--swc-gray-300);
32109
+ box-shadow: var(--swc-drop-shadow-elevated);
32110
+ }
32111
+ .decision-turn[data-pending]:focus-visible {
32112
+ outline: none;
32113
+ }
32114
+ .decision-turn[data-pending]:focus-visible .decision {
32115
+ outline: var(--swc-focus-indicator-thickness) solid var(--swc-focus-indicator-color);
32116
+ outline-offset: var(--swc-focus-ring-gap);
32117
+ }
32118
+ .decision {
32119
+ display: flex;
32120
+ flex-direction: column;
32121
+ gap: var(--swc-spacing-100);
32122
+ margin-inline: var(--swc-spacing-700);
32123
+ padding: var(--swc-spacing-200) var(--swc-spacing-300);
32124
+ border: var(--swc-border-width-100) solid var(--swc-gray-200);
32125
+ border-radius: var(--swc-corner-radius-100);
32126
+ background: var(--swc-background-layer-1-color);
32127
+ font-size: var(--swc-font-size-200);
32128
+ }
32129
+ .decision-head {
32130
+ display: flex;
32131
+ align-items: baseline;
32132
+ justify-content: space-between;
32133
+ gap: var(--swc-spacing-100);
32134
+ font-size: var(--swc-font-size-100);
32135
+ }
32136
+ .decision-head .who {
32137
+ font-weight: var(--swc-bold-font-weight);
32138
+ }
32139
+ .timer {
32140
+ color: var(--swc-neutral-subdued-content-color-default);
32141
+ font-variant-numeric: tabular-nums;
32142
+ white-space: nowrap;
32143
+ }
32144
+ .decision-text {
32145
+ margin: 0;
32146
+ }
32147
+ .command {
32148
+ margin: 0;
32149
+ padding: var(--swc-spacing-75) var(--swc-spacing-100);
32150
+ border-radius: var(--swc-corner-radius-100);
32151
+ background: var(--swc-background-layer-2-color);
32152
+ font-size: var(--swc-font-size-75);
32153
+ white-space: pre-wrap;
32154
+ overflow-wrap: anywhere;
32155
+ }
32156
+ .decision .decide,
32157
+ .question.decision .options {
32158
+ justify-content: flex-end;
32159
+ padding: 0;
32160
+ }
32161
+ .question.decision .options {
32162
+ display: flex;
32163
+ flex-wrap: wrap;
32164
+ gap: var(--swc-spacing-85);
32165
+ }
32166
+ @container (inline-size < 480px) {
32167
+ .decision {
32168
+ margin-inline: var(--swc-spacing-300);
32169
+ }
32026
32170
  }
32027
32171
  `;
32028
32172
 
@@ -33291,6 +33435,11 @@ var SliccApp = class extends ThemedElement {
33291
33435
  return cone ? { ...sprinkle, title: `${cone.name} / ${sprinkle.title}` } : sprinkle;
33292
33436
  }).sort((a23, b13) => order.indexOf(a23.agentId) - order.indexOf(b13.agentId));
33293
33437
  }
33438
+ #panelClosed = (event2) => {
33439
+ const id = event2.detail.id;
33440
+ this.panels.closed(id);
33441
+ if (id.startsWith("sprinkle:")) this.dips.attach(id.slice("sprinkle:".length));
33442
+ };
33294
33443
  #detach(id, place3, open) {
33295
33444
  const sprinkle = this.#sprinkle(id);
33296
33445
  if (!sprinkle) return;
@@ -33546,7 +33695,7 @@ var SliccApp = class extends ThemedElement {
33546
33695
  <main>
33547
33696
  ${rails.left}
33548
33697
  <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)}
33698
+ @panel-close=${this.#panelClosed}
33550
33699
  @active-panel-change=${this.#activated}
33551
33700
  @external-drop=${this.#dropped}
33552
33701
  @detach-sprinkle=${(event2) => this.#detach(event2.detail.id, null, true)}
@@ -33787,7 +33936,7 @@ var inCwd = (approval) => approval.alwaysScope === "cwd" && !!approval.cwd;
33787
33936
  function alwaysHint(approval) {
33788
33937
  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
33938
  }
33790
- function waiting(model, now) {
33939
+ function waiting2(model, now) {
33791
33940
  return [...model?.approvals?.list() ?? []].filter((approval) => approval.expiresAt > now).sort((a23, b13) => a23.createdAt - b13.createdAt);
33792
33941
  }
33793
33942
  function gone(reason) {
@@ -33924,16 +34073,16 @@ var SliccApprovals = class extends ModelElement {
33924
34073
  this.#timer = null;
33925
34074
  }
33926
34075
  }
33927
- async decide(approval, decision) {
34076
+ async decide(approval, decision2) {
33928
34077
  const port = this.model?.approvals;
33929
- if (!port?.decide || this.busy || decision !== "deny" && !this.#armed) return;
34078
+ if (!port?.decide || this.busy || decision2 !== "deny" && !this.#armed) return;
33930
34079
  const verdict = secretRules(approval.command, approval.cwd);
33931
- if (decision === "always" && (verdict.certain ?? verdict.maybe)) return;
33932
- this.busy = decision;
34080
+ if (decision2 === "always" && (verdict.certain ?? verdict.maybe)) return;
34081
+ this.busy = decision2;
33933
34082
  this.failure = null;
33934
34083
  this.#pending.add(approval.id);
33935
34084
  try {
33936
- await port.decide(approval.id, decision);
34085
+ await port.decide(approval.id, decision2);
33937
34086
  this.#done.add(approval.id);
33938
34087
  } catch (error) {
33939
34088
  this.failure = {
@@ -33946,7 +34095,7 @@ var SliccApprovals = class extends ModelElement {
33946
34095
  }
33947
34096
  }
33948
34097
  #current() {
33949
- return this.model?.approvals?.decide ? waiting(this.model, this.now) : [];
34098
+ return this.model?.approvals?.decide ? waiting2(this.model, this.now) : [];
33950
34099
  }
33951
34100
  willUpdate(changed) {
33952
34101
  super.willUpdate(changed);
@@ -33992,15 +34141,15 @@ var SliccApprovals = class extends ModelElement {
33992
34141
  if (this.gone) this.close();
33993
34142
  else void this.decide(approval, "deny");
33994
34143
  }
33995
- #button(approval, decision, label, variant) {
34144
+ #button(approval, decision2, label, variant) {
33996
34145
  return b`<swc-button
33997
34146
  slot="button"
33998
34147
  variant=${variant}
33999
34148
  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)}
34149
+ data-action=${decision2}
34150
+ ?pending=${this.busy === decision2}
34151
+ ?disabled=${!!this.busy && this.busy !== decision2}
34152
+ @click=${() => this.decide(approval, decision2)}
34004
34153
  >${label}</swc-button
34005
34154
  >`;
34006
34155
  }
@@ -35455,6 +35604,7 @@ e12([n({
35455
35604
  e25("swc-action-group", o87);
35456
35605
 
35457
35606
  // src/app/changes.ts
35607
+ var watchNote = b`<span data-watch-note>Watch only</span>`;
35458
35608
  var layouts = [
35459
35609
  ["unified", "Unified"],
35460
35610
  ["split", "Split"]
@@ -35778,11 +35928,11 @@ var SliccChanges = class extends ModelElement {
35778
35928
  return b`<div class="bar">
35779
35929
  <span class="summary">${changes2.length} pending ${changes2.length === 1 ? "change" : "changes"}</span>
35780
35930
  <span class="spacer"></span>
35781
- ${changes2.length && !watchOnly(this.model) ? b`<swc-action-button size="s" quiet data-action="accept-all" @click=${() => this.#all("accept")}>Accept all</swc-action-button>
35782
- <swc-action-button size="s" quiet data-action="revert-all" @click=${() => this.#all("revert")}>Revert all</swc-action-button>` : A}
35931
+ ${!changes2.length ? A : watchOnly(this.model) ? watchNote : b`<swc-action-button size="s" quiet data-action="accept-all" @click=${() => this.#all("accept")}>Accept all</swc-action-button>
35932
+ <swc-action-button size="s" quiet data-action="revert-all" @click=${() => this.#all("revert")}>Revert all</swc-action-button>`}
35783
35933
  </div>
35784
35934
  ${errorLine(this.error)}
35785
- ${changes2.length ? this.#list(changes2) : b`<div class="note">No pending changes. Edits by agents show up here for review.</div>`}`;
35935
+ ${changes2.length ? this.#list(changes2) : b`<div class="note">${watchOnly(this.model) ? "No pending changes." : "No pending changes. Edits by agents show up here for review."}</div>`}`;
35786
35936
  }
35787
35937
  };
35788
35938
  var SliccDiffPanel = class extends ThemedElement {
@@ -35892,7 +36042,7 @@ var SliccDiffPanel = class extends ThemedElement {
35892
36042
  >`
35893
36043
  )}
35894
36044
  </swc-action-group>`}
35895
- ${watchOnly(model) ? A : b`<swc-action-button size="s" quiet data-action="accept" accessible-label="Accept" title="Accept" @click=${() => port?.accept(this.path)}>
36045
+ ${watchOnly(model) ? watchNote : b`<swc-action-button size="s" quiet data-action="accept" accessible-label="Accept" title="Accept" @click=${() => port?.accept(this.path)}>
35896
36046
  <swc-icon-checkmark slot="icon"></swc-icon-checkmark><span class="label">Accept</span>
35897
36047
  </swc-action-button>
35898
36048
  <swc-action-button size="s" quiet data-action="revert" accessible-label="Revert" title="Revert" @click=${() => this.#revert(port, change)}>
@@ -36176,6 +36326,19 @@ var navigation = /* @__PURE__ */ new Set([
36176
36326
  ]);
36177
36327
  var upward = /* @__PURE__ */ new Set(["ArrowUp", "PageUp", "Home"]);
36178
36328
  var compactWidth = 560;
36329
+ function pendingKeys(messages) {
36330
+ return messages.flatMap((message) => {
36331
+ if (waiting(message)) return [message.id];
36332
+ if (message.role !== "assistant") return [];
36333
+ return message.parts.flatMap(
36334
+ (part2) => part2.type === "question" && part2.question.state === "open" ? [`${message.id}:${part2.question.id}`] : []
36335
+ );
36336
+ });
36337
+ }
36338
+ function pendingPiles(messages) {
36339
+ const keys = pendingKeys(messages);
36340
+ return new Map(keys.map((key2, stack) => [key2, { stack }]));
36341
+ }
36179
36342
  var failed = (message) => message.role === "assistant" ? message.status === "error" : message.role === "system" && message.kind === "error";
36180
36343
  function carriesFiles(event2) {
36181
36344
  const types = [...event2.dataTransfer?.types ?? []];
@@ -36324,6 +36487,7 @@ var SliccChat = class extends ThemedElement {
36324
36487
  padding: var(--swc-spacing-400) var(--swc-spacing-300) calc(var(--composer-height, 0px) + var(--swc-spacing-500));
36325
36488
  scroll-padding-block-end: calc(var(--composer-height, 0px) + var(--swc-spacing-200));
36326
36489
  font-size: var(--swc-font-size-200);
36490
+ scroll-padding-block-start: var(--pile, 0px);
36327
36491
  }
36328
36492
  @container (min-width: 600px) {
36329
36493
  .column {
@@ -36429,7 +36593,21 @@ var SliccChat = class extends ThemedElement {
36429
36593
  #from = 0;
36430
36594
  #start = 0;
36431
36595
  #settle;
36596
+ #tick;
36432
36597
  #tail = "";
36598
+ #held = "";
36599
+ #focused = (event2) => {
36600
+ const turn = event2.target.closest(".decision-turn[data-pending]");
36601
+ this.#held = turn?.dataset.id ?? "";
36602
+ if (turn && Number(turn.style.getPropertyValue("--depth")) > 0) show(turn);
36603
+ };
36604
+ #refocus() {
36605
+ if (!this.#held || this.renderRoot.querySelector(":focus")) return;
36606
+ this.#held = "";
36607
+ const next = this.renderRoot.querySelector(".decision-turn[data-pending]");
36608
+ if (next) next.focus();
36609
+ else this.renderRoot.querySelector("slicc-composer")?.focus();
36610
+ }
36433
36611
  #sent(agentId) {
36434
36612
  const last = this.model?.agent.messages(agentId).at(-1);
36435
36613
  return last?.role === "user" && last.id !== this.#tail && (last.origin ?? "user") === "user";
@@ -36458,6 +36636,7 @@ var SliccChat = class extends ThemedElement {
36458
36636
  else if (reader && log.scrollTop < this.#last) this.#stuck = false;
36459
36637
  this.#last = log.scrollTop;
36460
36638
  this.#height = log.scrollHeight;
36639
+ this.#pile();
36461
36640
  };
36462
36641
  #intent = {
36463
36642
  passive: true,
@@ -36505,9 +36684,6 @@ var SliccChat = class extends ThemedElement {
36505
36684
  if (composer) this.#fit.observe(composer);
36506
36685
  else this.style.setProperty("--composer-height", "0px");
36507
36686
  }
36508
- updated() {
36509
- this.#attach();
36510
- }
36511
36687
  connectedCallback() {
36512
36688
  super.connectedCallback();
36513
36689
  globalThis.addEventListener("pointerup", this.#release, true);
@@ -36522,6 +36698,59 @@ var SliccChat = class extends ThemedElement {
36522
36698
  this.#fit.disconnect();
36523
36699
  this.#fitted = null;
36524
36700
  clearTimeout(this.#settle);
36701
+ clearInterval(this.#tick);
36702
+ this.#tick = void 0;
36703
+ }
36704
+ updated() {
36705
+ this.#attach();
36706
+ const timed = this.#timed();
36707
+ if (timed && this.#tick === void 0)
36708
+ this.#tick = setInterval(() => this.requestUpdate(), 1e3);
36709
+ if (!timed && this.#tick !== void 0) {
36710
+ clearInterval(this.#tick);
36711
+ this.#tick = void 0;
36712
+ }
36713
+ this.#pile();
36714
+ this.#refocus();
36715
+ }
36716
+ #pile() {
36717
+ const turns = [
36718
+ ...this.renderRoot.querySelectorAll(".decision-turn[data-pending]")
36719
+ ];
36720
+ const drawn = turns.map((turn) => turn.getBoundingClientRect().top);
36721
+ for (const turn of turns) turn.style.position = "static";
36722
+ const flow = turns.map((turn) => turn.getBoundingClientRect().top);
36723
+ for (const turn of turns) turn.style.position = "";
36724
+ const pinned = turns.flatMap(
36725
+ (turn, index) => drawn[index] - flow[index] > 1 ? [{ turn, stack: Number.parseFloat(turn.style.getPropertyValue("--stack")) }] : []
36726
+ );
36727
+ for (const turn of turns) {
36728
+ if (!pinned.some((entry) => entry.turn === turn)) {
36729
+ turn.removeAttribute("data-pinned");
36730
+ turn.style.removeProperty("--depth");
36731
+ }
36732
+ }
36733
+ for (const { turn, stack } of pinned) {
36734
+ turn.setAttribute("data-pinned", "");
36735
+ turn.style.setProperty(
36736
+ "--depth",
36737
+ String(pinned.filter((other) => other.stack > stack).length)
36738
+ );
36739
+ }
36740
+ const log = this.renderRoot.querySelector(".log");
36741
+ const top = log?.getBoundingClientRect().top ?? 0;
36742
+ const reach3 = Math.max(
36743
+ 0,
36744
+ ...pinned.map(({ turn }) => turn.getBoundingClientRect().bottom - top)
36745
+ );
36746
+ log?.style.setProperty("--pile", `${reach3}px`);
36747
+ }
36748
+ #timed() {
36749
+ const model = this.model;
36750
+ if (!model) return false;
36751
+ return model.agent.messages(this.#id()).some(
36752
+ (message) => waiting(message) && "expiresAt" in message && message.expiresAt !== void 0
36753
+ );
36525
36754
  }
36526
36755
  firstUpdated() {
36527
36756
  this.#observe();
@@ -36574,7 +36803,7 @@ var SliccChat = class extends ThemedElement {
36574
36803
  new CustomEvent("show-surface", { detail: { id: "settings" }, bubbles: true, composed: true })
36575
36804
  );
36576
36805
  }
36577
- #message(message, agent, suggestion, changed, extra, owners, stamp) {
36806
+ #message(message, agent, suggestion, changed, extra, owners, stamp, pile) {
36578
36807
  const model = this.model;
36579
36808
  const handlers = { ...this.#handlers, owners };
36580
36809
  switch (message.role) {
@@ -36598,7 +36827,7 @@ var SliccChat = class extends ThemedElement {
36598
36827
  case "system":
36599
36828
  return system(message, this.#handlers);
36600
36829
  default:
36601
- return lick(message, this.#handlers, stamp);
36830
+ return lick(message, this.#handlers, stamp, pile);
36602
36831
  }
36603
36832
  }
36604
36833
  #items(messages, agent) {
@@ -36613,6 +36842,7 @@ var SliccChat = class extends ThemedElement {
36613
36842
  const owners = /* @__PURE__ */ new Map();
36614
36843
  const into = /* @__PURE__ */ new Map();
36615
36844
  const stamped = stamps(messages);
36845
+ const piles = pendingPiles(messages);
36616
36846
  let anchor = null;
36617
36847
  for (const message of messages) {
36618
36848
  const calls = toolOnly(message, showThinking);
@@ -36653,12 +36883,24 @@ var SliccChat = class extends ThemedElement {
36653
36883
  changed,
36654
36884
  extra.get(message.id) ?? [],
36655
36885
  owners,
36656
- stamped.has(message.id)
36886
+ stamped.has(message.id),
36887
+ piles.get(message.id) ?? flatPile
36657
36888
  )
36658
36889
  ]);
36890
+ out.push(...this.#questions(message, piles));
36659
36891
  }
36660
36892
  return out;
36661
36893
  }
36894
+ #questions(message, piles) {
36895
+ if (message.role !== "assistant") return [];
36896
+ return message.parts.flatMap((part2) => {
36897
+ if (part2.type !== "question") return [];
36898
+ const key2 = `${message.id}:${part2.question.id}`;
36899
+ return [
36900
+ [key2, questionTurn(part2.question, this.#handlers, piles.get(key2) ?? flatPile)]
36901
+ ];
36902
+ });
36903
+ }
36662
36904
  #newChat(agent) {
36663
36905
  this.model.agent.clear(agent.id);
36664
36906
  this.renderRoot.querySelector("slicc-composer")?.focus();
@@ -36720,13 +36962,14 @@ var SliccChat = class extends ThemedElement {
36720
36962
  @touchmove=${this.#intent}
36721
36963
  @keydown=${this.#intent}
36722
36964
  @pointerdown=${this.#hold}
36965
+ @focusin=${this.#focused}
36723
36966
  >
36724
36967
  <div class="column" @keydown=${this.#keys}>
36725
36968
  ${agent && messages.length > 0 ? b`<swc-conversation-thread>${c(
36726
36969
  this.#items(messages, agent),
36727
36970
  ([key2]) => key2,
36728
36971
  ([, item2]) => item2
36729
- )}</swc-conversation-thread>` : this.model?.tabs?.state().role === "connecting" ? b`<div class="empty" data-connecting>${connecting}</div>` : b`<div class="empty">No messages yet. Ask ${agent?.name ?? "the agent"} something.</div>`}
36972
+ )}</swc-conversation-thread>` : this.model?.tabs?.state().role === "connecting" ? b`<div class="empty" data-connecting>${connecting}</div>` : b`<div class="empty">${watch ? "No messages yet." : `No messages yet. Ask ${agent?.name ?? "the agent"} something.`}</div>`}
36730
36973
  </div>
36731
36974
  </div>
36732
36975
  ${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}
@@ -37933,14 +38176,14 @@ function release(attachments) {
37933
38176
  }
37934
38177
  function retire(agent, id, attachments) {
37935
38178
  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 ?? "")));
38179
+ const waiting3 = () => agent.queue(id).some((message) => message.attachments?.some((attachment2) => urls.has(attachment2.url ?? "")));
37937
38180
  const later = () => void setTimeout(() => release(attachments), linger);
37938
- if (!waiting2()) {
38181
+ if (!waiting3()) {
37939
38182
  later();
37940
38183
  return;
37941
38184
  }
37942
38185
  const off = agent.on("messages", () => {
37943
- if (waiting2()) return;
38186
+ if (waiting3()) return;
37944
38187
  off();
37945
38188
  later();
37946
38189
  });
@@ -38760,7 +39003,7 @@ var SliccComposer = class extends ThemedElement {
38760
39003
  if (popup2.kind === "secret") {
38761
39004
  const vault = this.model?.vault;
38762
39005
  const valued = !!vault?.put && !blocked(vault);
38763
- const waiting2 = vault?.put ? blocked(vault) : null;
39006
+ const waiting3 = vault?.put ? blocked(vault) : null;
38764
39007
  if (!valued) clearValues(this.renderRoot);
38765
39008
  const submit = (event2) => {
38766
39009
  event2.preventDefault();
@@ -38779,7 +39022,7 @@ var SliccComposer = class extends ThemedElement {
38779
39022
  const described = (name) => this.secretField === name ? "secret-error" : A;
38780
39023
  return b`<form class="popup secret" data-vault=${valued ? "on" : "off"} @submit=${submit} @keydown=${close} @input=${() => this.secretField = null}>
38781
39024
  <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>
39025
+ <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
39026
  <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
39027
  ${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
39028
  ${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 +39745,7 @@ var SliccNotices = class extends ModelElement {
39502
39745
  (notice) => notice.dismissible === false || !this.dismissed.has(notice.id)
39503
39746
  );
39504
39747
  const tabs = this.#tabs();
39505
- const approvals = this.model?.approvals?.decide ? [] : waiting(this.model, Date.now());
39748
+ const approvals = this.model?.approvals?.decide ? [] : waiting2(this.model, Date.now());
39506
39749
  if (!notices.length && !approvals.length && tabs === A) return A;
39507
39750
  return b`<div class="notices">${tabs}${approvals.map((approval) => this.#approval(approval))}${c(
39508
39751
  notices,
@@ -40453,7 +40696,7 @@ var SliccSecrets = class extends ThemedElement {
40453
40696
  }
40454
40697
  #secret(vault, secret, entries, devices, more, locked) {
40455
40698
  const kind = kinds[secret.kind];
40456
- const reveal = revealed(secret.name, entries, Date.now(), more);
40699
+ const reveal2 = revealed(secret.name, entries, Date.now(), more);
40457
40700
  const exec = execLine(secret.exec, secret.execArgv);
40458
40701
  const gone3 = secret.state === "removed";
40459
40702
  const form = this.#offline ? null : this.form;
@@ -40467,7 +40710,7 @@ var SliccSecrets = class extends ThemedElement {
40467
40710
  <dl>
40468
40711
  ${this.#reach(secret, gone3)}
40469
40712
  ${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}
40713
+ ${exec || reveal2 ? b`<dt>Programs</dt><dd data-exec>${exec}${exec && reveal2 ? b`<br />` : A}${reveal2}</dd>` : A}
40471
40714
  ${secret.kind === "model" && secret.provider ? b`<dt>Provider</dt><dd data-provider>${this.#providerName(secret.provider)}</dd>` : A}
40472
40715
  ${secret.kind === "mcp" && secret.server ? b`<dt>Server</dt><dd data-server>${secret.server}</dd>` : A}
40473
40716
  <dt>From</dt>
@@ -40576,7 +40819,7 @@ var SliccSecrets = class extends ThemedElement {
40576
40819
  this.#offline = away(vault);
40577
40820
  if (this.#offline && this.form) clearValues(this.renderRoot);
40578
40821
  const now = Date.now();
40579
- const waiting2 = (status2?.waiting ?? []).filter((request2) => request2.expiresAt > now);
40822
+ const waiting3 = (status2?.waiting ?? []).filter((request2) => request2.expiresAt > now);
40580
40823
  return b`<section aria-labelledby="secrets" data-vault=${status2 ? status2.locked ? "locked" : "unlocked" : "loading"}>
40581
40824
  <div class="head">
40582
40825
  <h2 id="secrets">Secrets</h2>
@@ -40586,7 +40829,7 @@ var SliccSecrets = class extends ThemedElement {
40586
40829
  ${this.#availability()}
40587
40830
  ${this.#feedback()}
40588
40831
  ${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}
40832
+ ${waiting3.length ? b`<h3>Waiting for you</h3>${waiting3.map((request2) => this.#request(vault, request2))}` : A}
40590
40833
  ${this.#backup(vault, status2)}
40591
40834
  ${this.#tools(vault, locked)}
40592
40835
  ${!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 +41717,9 @@ var SliccSprinkle = class extends ThemedElement {
41474
41717
  height: auto;
41475
41718
  background: none;
41476
41719
  }
41720
+ :host([inline]) .handle {
41721
+ margin-inline-start: 20px;
41722
+ }
41477
41723
  iframe {
41478
41724
  display: block;
41479
41725
  width: 100%;