@ai-ecoverse/slicc-spectrum 1.75.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 = [];
@@ -31012,7 +31020,7 @@ function user(message, stamp = true) {
31012
31020
  ${byline(message, who2, stamp)}${stamp ? A : unseen(message.createdAt)}
31013
31021
  ${message.text ? b`<swc-user-message class="body"><span class="text">${message.text}</span></swc-user-message>` : A}
31014
31022
  ${message.attachments?.length ? b`<div class="attachments">${message.attachments.map(attachment)}</div>` : A}
31015
- ${remote ? b`<span class="reaction" aria-hidden="true">🤖</span>` : A}
31023
+ ${remote ? b`<span class="reactions"><span class="reaction" aria-hidden="true">🤖</span></span>` : A}
31016
31024
  </div>
31017
31025
  </swc-conversation-turn>`;
31018
31026
  }
@@ -31110,6 +31118,9 @@ var compactionTitles = {
31110
31118
  function event(label, content) {
31111
31119
  return b`<swc-conversation-turn type="system" class="event" accessible-label=${label}>${content}</swc-conversation-turn>`;
31112
31120
  }
31121
+ function outside(label, byline2, content) {
31122
+ return b`<swc-conversation-turn type="user" class="event lick-turn" accessible-label=${label}><div class="bubble">${byline2}${content}</div></swc-conversation-turn>`;
31123
+ }
31113
31124
  function system(message, handlers) {
31114
31125
  if (message.kind === "compaction") {
31115
31126
  const failed2 = message.state === "fallback" || message.state === "discarded";
@@ -31158,37 +31169,106 @@ var severities = {
31158
31169
  icon: b`<swc-icon-alert-triangle slot="icon" size="s" aria-hidden="true"></swc-icon-alert-triangle>`
31159
31170
  }
31160
31171
  };
31161
- function severityBadge(message, label) {
31162
- if (!message.severity) return null;
31163
- const { prefix, variant, icon } = severities[message.severity];
31164
- return b`<span class="sr">${prefix}</span><swc-badge class="severity" size="s" variant=${variant} subtle>${icon}${label}</swc-badge>`;
31172
+ var lickIcons = {
31173
+ webhook: "\u{1FA9D}",
31174
+ cron: "\u23F0",
31175
+ sprinkle: "\u2728",
31176
+ fswatch: "\u{1F440}",
31177
+ "session-reload": "\u{1F504}",
31178
+ navigate: "\u{1F9ED}",
31179
+ discovery: "\u{1F50E}",
31180
+ upgrade: "\u2B06\uFE0F",
31181
+ workflow: "\u{1F4CB}",
31182
+ bash: "\u{1F4BB}",
31183
+ jshd: "\u{1F4DC}",
31184
+ preview: "\u{1F441}\uFE0F",
31185
+ cherry: "\u{1F352}",
31186
+ "scoop-notify": "\u2705",
31187
+ "scoop-idle": "\u{1F4A4}",
31188
+ "scoop-wait": "\u23F3",
31189
+ "sudo-request": "\u{1F510}"
31190
+ };
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")}`;
31165
31201
  }
31166
- function lick(message, handlers, stamp = true) {
31167
- const [label, variant] = lickLabels[message.channel];
31168
- const decide = message.channel === "sudo-request" && message.state === "pending";
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);
31250
+ const [label] = lickLabels[message.channel];
31169
31251
  const outcome = message.state === "confirmed" ? "Allowed" : message.state === "dismissed" ? "Denied" : "";
31170
- const head2 = b`${severityBadge(message, label) ?? b`<swc-badge class="channel" size="s" variant=${variant} subtle>${label}</swc-badge>`}
31171
- <strong>${message.title}</strong>
31252
+ const head2 = b`${message.severity ? b`<span class="sr">${severities[message.severity].prefix}</span>` : A}
31253
+ ${outcome ? b`<span class="sr">${outcome}. </span>` : A}
31172
31254
  <span class="text">${message.text}</span>
31173
- ${message.count && message.count > 1 ? b`<span class="times" title="Coalesced events">×${message.count}</span>` : A}
31174
- ${outcome ? b`<swc-badge class="tag" size="s" variant=${message.state === "confirmed" ? "positive" : "neutral"} subtle>${outcome}</swc-badge>` : A}
31175
- <span class=${stamp ? "when" : "sr"}>${time(message.createdAt)}</span>`;
31176
- const actions2 = decide ? b`<div class="decide">
31177
- <swc-button size="s" variant="accent" data-action="allow" ?disabled=${handlers.readOnly} @click=${() => handlers.resolve(message.id, "confirmed")}>Allow</swc-button>
31178
- <swc-button size="s" variant="secondary" fill-style="outline" data-action="deny" ?disabled=${handlers.readOnly} @click=${() => handlers.resolve(message.id, "dismissed")}>Deny</swc-button>
31179
- </div>` : A;
31255
+ ${message.count && message.count > 1 ? b`<span class="times" title="Coalesced events">×${message.count}</span>` : A}`;
31256
+ const outcomeIcon = message.state === "confirmed" ? "\u{1F44D}" : message.state === "dismissed" ? "\u{1F44E}" : "";
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>`;
31180
31258
  const name = `${message.severity ? severities[message.severity].prefix : ""}${label} event`;
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>`;
31181
31260
  if (!message.body) {
31182
- return event(
31261
+ return outside(
31183
31262
  name,
31184
- b`<div class="lick" data-id=${message.id} data-channel=${message.channel} data-severity=${message.severity ?? A}><div class="lick-head">${head2}</div>${actions2}</div>`
31263
+ byline2,
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>`
31185
31265
  );
31186
31266
  }
31187
31267
  const details = b`<details class="lick" data-id=${message.id} data-channel=${message.channel} data-severity=${message.severity ?? A}>
31188
- <summary class="lick-head"><span class="chevron" aria-hidden="true">▸</span>${head2}</summary>
31268
+ <summary class="lick-head">${head2}<swc-icon-chevron-right class="chevron" size="s" aria-hidden="true"></swc-icon-chevron-right></summary>
31189
31269
  <pre class="output">${message.body}</pre>
31190
31270
  </details>`;
31191
- return event(name, decide ? b`<div class="pending">${details}${actions2}</div>` : details);
31271
+ return outside(name, byline2, b`<div class="lick-frame">${details}${reaction}</div>`);
31192
31272
  }
31193
31273
  var messageCss = i`
31194
31274
  swc-conversation-thread > .message,
@@ -31258,10 +31338,14 @@ var messageCss = i`
31258
31338
  position: relative;
31259
31339
  margin-block-end: var(--swc-spacing-300);
31260
31340
  }
31261
- .reaction {
31341
+ .reactions {
31262
31342
  position: absolute;
31263
31343
  inset-block-end: calc(var(--swc-spacing-300) * -1);
31264
31344
  inset-inline-end: var(--swc-spacing-200);
31345
+ display: inline-flex;
31346
+ gap: var(--swc-spacing-50);
31347
+ }
31348
+ .reaction {
31265
31349
  display: inline-flex;
31266
31350
  align-items: center;
31267
31351
  justify-content: center;
@@ -31408,9 +31492,9 @@ var messageCss = i`
31408
31492
  }
31409
31493
  .lick {
31410
31494
  border: var(--swc-border-width-100) solid var(--swc-gray-200);
31411
- border-radius: var(--swc-corner-radius-100);
31495
+ border-radius: var(--swc-corner-radius-700);
31412
31496
  background: var(--swc-background-layer-1-color);
31413
- font-size: var(--swc-font-size-100);
31497
+ font-size: var(--swc-font-size-200);
31414
31498
  }
31415
31499
  summary,
31416
31500
  .lick-head {
@@ -31941,15 +32025,25 @@ var messageCss = i`
31941
32025
  color: var(--swc-neutral-subdued-content-color-default);
31942
32026
  text-align: center;
31943
32027
  }
32028
+ .lick-frame {
32029
+ position: relative;
32030
+ min-inline-size: 0;
32031
+ max-inline-size: 100%;
32032
+ margin-block-end: var(--swc-spacing-300);
32033
+ }
31944
32034
  .lick {
31945
32035
  border-style: dashed;
31946
32036
  }
31947
- .channel,
31948
- .lick .severity {
31949
- flex: none;
32037
+ .severity-icon {
32038
+ display: inline-flex;
32039
+ align-self: center;
32040
+ color: var(--severity);
31950
32041
  }
31951
- .lick strong {
31952
- white-space: nowrap;
32042
+ .lick-turn:has(.lick[data-severity='error']) {
32043
+ --severity: var(--swc-negative-visual-color);
32044
+ }
32045
+ .lick-turn:has(.lick[data-severity='warn']) {
32046
+ --severity: var(--swc-notice-visual-color);
31953
32047
  }
31954
32048
  .lick[data-severity] {
31955
32049
  border-inline-start: var(--swc-border-width-400) solid var(--severity);
@@ -31968,8 +32062,7 @@ var messageCss = i`
31968
32062
  clip-path: inset(50%);
31969
32063
  white-space: nowrap;
31970
32064
  }
31971
- .lick .times,
31972
- .lick .when {
32065
+ .lick .times {
31973
32066
  color: var(--swc-neutral-subdued-content-color-default);
31974
32067
  white-space: nowrap;
31975
32068
  }
@@ -31978,15 +32071,97 @@ var messageCss = i`
31978
32071
  gap: var(--swc-spacing-85);
31979
32072
  padding: 0 var(--swc-spacing-100) var(--swc-spacing-100);
31980
32073
  }
31981
- .pending {
31982
- border: var(--swc-border-width-100) solid var(--swc-notice-visual-color);
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);
32080
+ }
32081
+ @media (prefers-reduced-motion: reduce) {
32082
+ swc-conversation-turn.decision-turn {
32083
+ transition: none;
32084
+ }
32085
+ }
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);
31983
32120
  border-radius: var(--swc-corner-radius-100);
32121
+ background: var(--swc-background-layer-1-color);
32122
+ font-size: var(--swc-font-size-200);
31984
32123
  }
31985
- .pending > details.lick {
31986
- border: 0;
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);
31987
32130
  }
31988
- .pending > details.lick[data-severity] {
31989
- border-inline-start: var(--swc-border-width-400) solid var(--severity);
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
+ }
31990
32165
  }
31991
32166
  `;
31992
32167
 
@@ -33255,6 +33430,11 @@ var SliccApp = class extends ThemedElement {
33255
33430
  return cone ? { ...sprinkle, title: `${cone.name} / ${sprinkle.title}` } : sprinkle;
33256
33431
  }).sort((a23, b13) => order.indexOf(a23.agentId) - order.indexOf(b13.agentId));
33257
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
+ };
33258
33438
  #detach(id, place3, open) {
33259
33439
  const sprinkle = this.#sprinkle(id);
33260
33440
  if (!sprinkle) return;
@@ -33510,7 +33690,7 @@ var SliccApp = class extends ThemedElement {
33510
33690
  <main>
33511
33691
  ${rails.left}
33512
33692
  <slicc-dock empty-text="All panels are closed. Open one from a rail or View." @layout-change=${this.#save}
33513
- @panel-close=${(event2) => this.panels.closed(event2.detail.id)}
33693
+ @panel-close=${this.#panelClosed}
33514
33694
  @active-panel-change=${this.#activated}
33515
33695
  @external-drop=${this.#dropped}
33516
33696
  @detach-sprinkle=${(event2) => this.#detach(event2.detail.id, null, true)}
@@ -33751,7 +33931,7 @@ var inCwd = (approval) => approval.alwaysScope === "cwd" && !!approval.cwd;
33751
33931
  function alwaysHint(approval) {
33752
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.`;
33753
33933
  }
33754
- function waiting(model, now) {
33934
+ function waiting2(model, now) {
33755
33935
  return [...model?.approvals?.list() ?? []].filter((approval) => approval.expiresAt > now).sort((a23, b13) => a23.createdAt - b13.createdAt);
33756
33936
  }
33757
33937
  function gone(reason) {
@@ -33888,16 +34068,16 @@ var SliccApprovals = class extends ModelElement {
33888
34068
  this.#timer = null;
33889
34069
  }
33890
34070
  }
33891
- async decide(approval, decision) {
34071
+ async decide(approval, decision2) {
33892
34072
  const port = this.model?.approvals;
33893
- if (!port?.decide || this.busy || decision !== "deny" && !this.#armed) return;
34073
+ if (!port?.decide || this.busy || decision2 !== "deny" && !this.#armed) return;
33894
34074
  const verdict = secretRules(approval.command, approval.cwd);
33895
- if (decision === "always" && (verdict.certain ?? verdict.maybe)) return;
33896
- this.busy = decision;
34075
+ if (decision2 === "always" && (verdict.certain ?? verdict.maybe)) return;
34076
+ this.busy = decision2;
33897
34077
  this.failure = null;
33898
34078
  this.#pending.add(approval.id);
33899
34079
  try {
33900
- await port.decide(approval.id, decision);
34080
+ await port.decide(approval.id, decision2);
33901
34081
  this.#done.add(approval.id);
33902
34082
  } catch (error) {
33903
34083
  this.failure = {
@@ -33910,7 +34090,7 @@ var SliccApprovals = class extends ModelElement {
33910
34090
  }
33911
34091
  }
33912
34092
  #current() {
33913
- return this.model?.approvals?.decide ? waiting(this.model, this.now) : [];
34093
+ return this.model?.approvals?.decide ? waiting2(this.model, this.now) : [];
33914
34094
  }
33915
34095
  willUpdate(changed) {
33916
34096
  super.willUpdate(changed);
@@ -33956,15 +34136,15 @@ var SliccApprovals = class extends ModelElement {
33956
34136
  if (this.gone) this.close();
33957
34137
  else void this.decide(approval, "deny");
33958
34138
  }
33959
- #button(approval, decision, label, variant) {
34139
+ #button(approval, decision2, label, variant) {
33960
34140
  return b`<swc-button
33961
34141
  slot="button"
33962
34142
  variant=${variant}
33963
34143
  fill-style=${variant === "accent" ? A : "outline"}
33964
- data-action=${decision}
33965
- ?pending=${this.busy === decision}
33966
- ?disabled=${!!this.busy && this.busy !== decision}
33967
- @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)}
33968
34148
  >${label}</swc-button
33969
34149
  >`;
33970
34150
  }
@@ -36140,6 +36320,19 @@ var navigation = /* @__PURE__ */ new Set([
36140
36320
  ]);
36141
36321
  var upward = /* @__PURE__ */ new Set(["ArrowUp", "PageUp", "Home"]);
36142
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
+ }
36143
36336
  var failed = (message) => message.role === "assistant" ? message.status === "error" : message.role === "system" && message.kind === "error";
36144
36337
  function carriesFiles(event2) {
36145
36338
  const types = [...event2.dataTransfer?.types ?? []];
@@ -36288,6 +36481,7 @@ var SliccChat = class extends ThemedElement {
36288
36481
  padding: var(--swc-spacing-400) var(--swc-spacing-300) calc(var(--composer-height, 0px) + var(--swc-spacing-500));
36289
36482
  scroll-padding-block-end: calc(var(--composer-height, 0px) + var(--swc-spacing-200));
36290
36483
  font-size: var(--swc-font-size-200);
36484
+ scroll-padding-block-start: var(--pile, 0px);
36291
36485
  }
36292
36486
  @container (min-width: 600px) {
36293
36487
  .column {
@@ -36393,7 +36587,21 @@ var SliccChat = class extends ThemedElement {
36393
36587
  #from = 0;
36394
36588
  #start = 0;
36395
36589
  #settle;
36590
+ #tick;
36396
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
+ }
36397
36605
  #sent(agentId) {
36398
36606
  const last = this.model?.agent.messages(agentId).at(-1);
36399
36607
  return last?.role === "user" && last.id !== this.#tail && (last.origin ?? "user") === "user";
@@ -36422,6 +36630,7 @@ var SliccChat = class extends ThemedElement {
36422
36630
  else if (reader && log.scrollTop < this.#last) this.#stuck = false;
36423
36631
  this.#last = log.scrollTop;
36424
36632
  this.#height = log.scrollHeight;
36633
+ this.#pile();
36425
36634
  };
36426
36635
  #intent = {
36427
36636
  passive: true,
@@ -36469,9 +36678,6 @@ var SliccChat = class extends ThemedElement {
36469
36678
  if (composer) this.#fit.observe(composer);
36470
36679
  else this.style.setProperty("--composer-height", "0px");
36471
36680
  }
36472
- updated() {
36473
- this.#attach();
36474
- }
36475
36681
  connectedCallback() {
36476
36682
  super.connectedCallback();
36477
36683
  globalThis.addEventListener("pointerup", this.#release, true);
@@ -36486,6 +36692,59 @@ var SliccChat = class extends ThemedElement {
36486
36692
  this.#fit.disconnect();
36487
36693
  this.#fitted = null;
36488
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
+ );
36489
36748
  }
36490
36749
  firstUpdated() {
36491
36750
  this.#observe();
@@ -36538,7 +36797,7 @@ var SliccChat = class extends ThemedElement {
36538
36797
  new CustomEvent("show-surface", { detail: { id: "settings" }, bubbles: true, composed: true })
36539
36798
  );
36540
36799
  }
36541
- #message(message, agent, suggestion, changed, extra, owners, stamp) {
36800
+ #message(message, agent, suggestion, changed, extra, owners, stamp, pile) {
36542
36801
  const model = this.model;
36543
36802
  const handlers = { ...this.#handlers, owners };
36544
36803
  switch (message.role) {
@@ -36562,7 +36821,7 @@ var SliccChat = class extends ThemedElement {
36562
36821
  case "system":
36563
36822
  return system(message, this.#handlers);
36564
36823
  default:
36565
- return lick(message, this.#handlers, stamp);
36824
+ return lick(message, this.#handlers, stamp, pile);
36566
36825
  }
36567
36826
  }
36568
36827
  #items(messages, agent) {
@@ -36577,6 +36836,7 @@ var SliccChat = class extends ThemedElement {
36577
36836
  const owners = /* @__PURE__ */ new Map();
36578
36837
  const into = /* @__PURE__ */ new Map();
36579
36838
  const stamped = stamps(messages);
36839
+ const piles = pendingPiles(messages);
36580
36840
  let anchor = null;
36581
36841
  for (const message of messages) {
36582
36842
  const calls = toolOnly(message, showThinking);
@@ -36617,12 +36877,24 @@ var SliccChat = class extends ThemedElement {
36617
36877
  changed,
36618
36878
  extra.get(message.id) ?? [],
36619
36879
  owners,
36620
- stamped.has(message.id)
36880
+ stamped.has(message.id),
36881
+ piles.get(message.id) ?? flatPile
36621
36882
  )
36622
36883
  ]);
36884
+ out.push(...this.#questions(message, piles));
36623
36885
  }
36624
36886
  return out;
36625
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
+ }
36626
36898
  #newChat(agent) {
36627
36899
  this.model.agent.clear(agent.id);
36628
36900
  this.renderRoot.querySelector("slicc-composer")?.focus();
@@ -36684,6 +36956,7 @@ var SliccChat = class extends ThemedElement {
36684
36956
  @touchmove=${this.#intent}
36685
36957
  @keydown=${this.#intent}
36686
36958
  @pointerdown=${this.#hold}
36959
+ @focusin=${this.#focused}
36687
36960
  >
36688
36961
  <div class="column" @keydown=${this.#keys}>
36689
36962
  ${agent && messages.length > 0 ? b`<swc-conversation-thread>${c(
@@ -37897,14 +38170,14 @@ function release(attachments) {
37897
38170
  }
37898
38171
  function retire(agent, id, attachments) {
37899
38172
  const urls = new Set(attachments.flatMap((attachment2) => attachment2.url ?? []));
37900
- 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 ?? "")));
37901
38174
  const later = () => void setTimeout(() => release(attachments), linger);
37902
- if (!waiting2()) {
38175
+ if (!waiting3()) {
37903
38176
  later();
37904
38177
  return;
37905
38178
  }
37906
38179
  const off = agent.on("messages", () => {
37907
- if (waiting2()) return;
38180
+ if (waiting3()) return;
37908
38181
  off();
37909
38182
  later();
37910
38183
  });
@@ -38724,7 +38997,7 @@ var SliccComposer = class extends ThemedElement {
38724
38997
  if (popup2.kind === "secret") {
38725
38998
  const vault = this.model?.vault;
38726
38999
  const valued = !!vault?.put && !blocked(vault);
38727
- const waiting2 = vault?.put ? blocked(vault) : null;
39000
+ const waiting3 = vault?.put ? blocked(vault) : null;
38728
39001
  if (!valued) clearValues(this.renderRoot);
38729
39002
  const submit = (event2) => {
38730
39003
  event2.preventDefault();
@@ -38743,7 +39016,7 @@ var SliccComposer = class extends ThemedElement {
38743
39016
  const described = (name) => this.secretField === name ? "secret-error" : A;
38744
39017
  return b`<form class="popup secret" data-vault=${valued ? "on" : "off"} @submit=${submit} @keydown=${close} @input=${() => this.secretField = null}>
38745
39018
  <strong>Share a secret</strong>
38746
- <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>
38747
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>
38748
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}
38749
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}
@@ -39466,7 +39739,7 @@ var SliccNotices = class extends ModelElement {
39466
39739
  (notice) => notice.dismissible === false || !this.dismissed.has(notice.id)
39467
39740
  );
39468
39741
  const tabs = this.#tabs();
39469
- const approvals = this.model?.approvals?.decide ? [] : waiting(this.model, Date.now());
39742
+ const approvals = this.model?.approvals?.decide ? [] : waiting2(this.model, Date.now());
39470
39743
  if (!notices.length && !approvals.length && tabs === A) return A;
39471
39744
  return b`<div class="notices">${tabs}${approvals.map((approval) => this.#approval(approval))}${c(
39472
39745
  notices,
@@ -40417,7 +40690,7 @@ var SliccSecrets = class extends ThemedElement {
40417
40690
  }
40418
40691
  #secret(vault, secret, entries, devices, more, locked) {
40419
40692
  const kind = kinds[secret.kind];
40420
- const reveal = revealed(secret.name, entries, Date.now(), more);
40693
+ const reveal2 = revealed(secret.name, entries, Date.now(), more);
40421
40694
  const exec = execLine(secret.exec, secret.execArgv);
40422
40695
  const gone3 = secret.state === "removed";
40423
40696
  const form = this.#offline ? null : this.form;
@@ -40431,7 +40704,7 @@ var SliccSecrets = class extends ThemedElement {
40431
40704
  <dl>
40432
40705
  ${this.#reach(secret, gone3)}
40433
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}
40434
- ${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}
40435
40708
  ${secret.kind === "model" && secret.provider ? b`<dt>Provider</dt><dd data-provider>${this.#providerName(secret.provider)}</dd>` : A}
40436
40709
  ${secret.kind === "mcp" && secret.server ? b`<dt>Server</dt><dd data-server>${secret.server}</dd>` : A}
40437
40710
  <dt>From</dt>
@@ -40540,7 +40813,7 @@ var SliccSecrets = class extends ThemedElement {
40540
40813
  this.#offline = away(vault);
40541
40814
  if (this.#offline && this.form) clearValues(this.renderRoot);
40542
40815
  const now = Date.now();
40543
- const waiting2 = (status2?.waiting ?? []).filter((request2) => request2.expiresAt > now);
40816
+ const waiting3 = (status2?.waiting ?? []).filter((request2) => request2.expiresAt > now);
40544
40817
  return b`<section aria-labelledby="secrets" data-vault=${status2 ? status2.locked ? "locked" : "unlocked" : "loading"}>
40545
40818
  <div class="head">
40546
40819
  <h2 id="secrets">Secrets</h2>
@@ -40550,7 +40823,7 @@ var SliccSecrets = class extends ThemedElement {
40550
40823
  ${this.#availability()}
40551
40824
  ${this.#feedback()}
40552
40825
  ${this.failure ? this.#error(this.failure, "vault") : A}
40553
- ${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}
40554
40827
  ${this.#backup(vault, status2)}
40555
40828
  ${this.#tools(vault, locked)}
40556
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>`}
@@ -41438,6 +41711,9 @@ var SliccSprinkle = class extends ThemedElement {
41438
41711
  height: auto;
41439
41712
  background: none;
41440
41713
  }
41714
+ :host([inline]) .handle {
41715
+ margin-inline-start: 20px;
41716
+ }
41441
41717
  iframe {
41442
41718
  display: block;
41443
41719
  width: 100%;
@@ -80171,7 +80447,7 @@ function createDismissableLayer(options) {
80171
80447
  const capture = (_options$capture = options.capture) !== null && _options$capture !== void 0 ? _options$capture : false;
80172
80448
  const escape2 = (_options$escape = options.escape) !== null && _options$escape !== void 0 ? _options$escape : true;
80173
80449
  const keys = (_options$keys = options.keys) !== null && _options$keys !== void 0 ? _options$keys : [];
80174
- const outside = (_options$outsidePoint = options.outsidePointerDown) !== null && _options$outsidePoint !== void 0 ? _options$outsidePoint : true;
80450
+ const outside2 = (_options$outsidePoint = options.outsidePointerDown) !== null && _options$outsidePoint !== void 0 ? _options$outsidePoint : true;
80175
80451
  const grace = (_options$pointerDownG = options.pointerDownGraceMs) !== null && _options$pointerDownG !== void 0 ? _options$pointerDownG : 0;
80176
80452
  const now = (_options$now = options.now) !== null && _options$now !== void 0 ? _options$now : Date.now;
80177
80453
  const openedAt = now();
@@ -80184,13 +80460,13 @@ function createDismissableLayer(options) {
80184
80460
  if (escape2 || keys.length > 0) disposables.addDisposables(addDisposableListener(win, "keydown", (event2) => {
80185
80461
  if (escape2 && event2.key === "Escape" || keys.includes(event2.key)) options.onDismiss();
80186
80462
  }, capture));
80187
- if (outside || options.onInsidePointerDown) disposables.addDisposables(addDisposableListener(win, "pointerdown", (event2) => {
80463
+ if (outside2 || options.onInsidePointerDown) disposables.addDisposables(addDisposableListener(win, "pointerdown", (event2) => {
80188
80464
  if (isInside(event2)) {
80189
80465
  var _options$onInsidePoin;
80190
80466
  (_options$onInsidePoin = options.onInsidePointerDown) === null || _options$onInsidePoin === void 0 || _options$onInsidePoin.call(options, event2);
80191
80467
  return;
80192
80468
  }
80193
- if (!outside || now() - openedAt < grace) return;
80469
+ if (!outside2 || now() - openedAt < grace) return;
80194
80470
  options.onDismiss();
80195
80471
  }, capture));
80196
80472
  if (options.resize) disposables.addDisposables(addDisposableListener(win, "resize", () => {