daily-focus 1.2.0-dev.8 → 1.2.0-dev.9

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/README.md CHANGED
@@ -130,9 +130,13 @@ why is CI red, what does this thread actually want from me. With
130
130
  `DAILY_FOCUS_ASSISTANT` set to `claude` or `codex`, every item's panel gets an
131
131
  assistant that runs that CLI headless against the item, with quick actions for the
132
132
  common asks and a field for anything else. Click a card to open its panel, where its
133
- notes live too; the reply streams in there, and you can follow up in the same
134
- conversation. The panel stays on the item it was opened for while you move through
135
- the list.
133
+ notes live too. While the assistant works, the panel shows what it is doing: each
134
+ command it runs and each page, thread or ticket it reads, with a clock, and a cross
135
+ on anything that failed, such as a repository it couldn't see. Once it answers,
136
+ that trail folds away under the answer. The answer has buttons to copy it, or any
137
+ block in it meant for pasting, and to open a draft it created. The field stays at
138
+ the foot of the panel, so you can follow up in the same conversation. The panel
139
+ stays on the item it was opened for while you move through the list.
136
140
 
137
141
  It is told to read and draft, and nothing more. An email reply is created as a Gmail
138
142
  draft on the thread, so it is waiting there when you open the mail; a reply to a
@@ -107,6 +107,8 @@ import { appendFile, mkdir, readFile } from "node:fs/promises";
107
107
  import { createInterface } from "node:readline";
108
108
  var TURN_TIMEOUT_MS = 10 * 6e4;
109
109
  var TURNS_SHOWN = 20;
110
+ var STEPS_KEPT = 100;
111
+ var DETAIL_LENGTH = 200;
110
112
  var QUICK_ACTIONS = [
111
113
  {
112
114
  id: "assess-review",
@@ -193,6 +195,7 @@ function foldAssistantLog(text) {
193
195
  endedAt: null,
194
196
  status: "running",
195
197
  reply: "",
198
+ steps: [],
196
199
  error: null
197
200
  });
198
201
  continue;
@@ -201,6 +204,7 @@ function foldAssistantLog(text) {
201
204
  if (!turn) continue;
202
205
  turn.sessionId = typeof record.sessionId === "string" ? record.sessionId : turn.sessionId;
203
206
  turn.reply = typeof record.reply === "string" ? record.reply : turn.reply;
207
+ turn.steps = readSteps(record.steps);
204
208
  turn.endedAt = record.at;
205
209
  if (record.event === "finished") {
206
210
  turn.status = "done";
@@ -214,6 +218,27 @@ function foldAssistantLog(text) {
214
218
  }
215
219
  return turns;
216
220
  }
221
+ function addStep(turn, step) {
222
+ turn.steps.push(step);
223
+ if (turn.steps.length > STEPS_KEPT) turn.steps.shift();
224
+ }
225
+ function sameWords(a, b) {
226
+ return a.replace(/\s+/g, " ").trim() === b.replace(/\s+/g, " ").trim();
227
+ }
228
+ function readSteps(value) {
229
+ if (!Array.isArray(value)) return [];
230
+ const steps = [];
231
+ for (const candidate of value) {
232
+ if (typeof candidate !== "object" || candidate === null || typeof candidate.text !== "string") continue;
233
+ steps.push({
234
+ kind: candidate.kind === "message" ? "message" : "tool",
235
+ text: candidate.text,
236
+ detail: typeof candidate.detail === "string" ? candidate.detail : null,
237
+ state: candidate.state === "failed" ? "failed" : "done"
238
+ });
239
+ }
240
+ return steps;
241
+ }
217
242
  function parseLine(line) {
218
243
  const trimmed = line.trim();
219
244
  if (!trimmed.startsWith("{")) return null;
@@ -251,11 +276,15 @@ var claudeAdapter = {
251
276
  if (type === "system" && typeof event.session_id === "string") {
252
277
  return { sessionId: event.session_id };
253
278
  }
254
- if (type === "assistant") {
279
+ if (type === "assistant" || type === "user") {
255
280
  const message = event.message;
256
281
  const content = Array.isArray(message?.content) ? message.content : [];
257
- const text = content.filter((block) => block.type === "text" && typeof block.text === "string").map((block) => block.text).join("\n\n");
258
- return text ? { text } : null;
282
+ const parsed = {};
283
+ const text = type === "assistant" ? content.filter((block) => block.type === "text" && typeof block.text === "string").map((block) => block.text).join("\n\n") : "";
284
+ if (text) parsed.text = text;
285
+ const steps = content.flatMap((block) => claudeStep(block) ?? []);
286
+ if (steps.length) parsed.steps = steps;
287
+ return text || steps.length ? parsed : null;
259
288
  }
260
289
  if (type === "result") {
261
290
  const ok = event.is_error !== true && event.subtype === "success";
@@ -293,10 +322,11 @@ var codexAdapter = {
293
322
  if (type === "thread.started" && typeof event.thread_id === "string") {
294
323
  return { sessionId: event.thread_id };
295
324
  }
296
- if (type === "item.completed") {
325
+ if (type === "item.started" || type === "item.completed") {
297
326
  const item = event.item;
298
- if (item?.type === "agent_message" && typeof item.text === "string") return { text: item.text };
299
- return null;
327
+ if (type === "item.completed" && item?.type === "agent_message" && typeof item.text === "string") return { text: item.text };
328
+ const step = item ? codexStep(item, type === "item.completed") : null;
329
+ return step ? { steps: [step] } : null;
300
330
  }
301
331
  if (type === "turn.completed") return { done: { ok: true } };
302
332
  if (type === "turn.failed" || type === "error") {
@@ -311,6 +341,87 @@ var ADAPTERS = {
311
341
  claude: claudeAdapter,
312
342
  codex: codexAdapter
313
343
  };
344
+ var CLAUDE_QUIET_TOOLS = /* @__PURE__ */ new Set(["ToolSearch", "TodoWrite"]);
345
+ function claudeStep(block) {
346
+ if (block.type === "tool_result" && typeof block.tool_use_id === "string") {
347
+ return { id: block.tool_use_id, state: block.is_error === true ? "failed" : "done" };
348
+ }
349
+ if (block.type !== "tool_use" || typeof block.id !== "string" || typeof block.name !== "string") return null;
350
+ if (CLAUDE_QUIET_TOOLS.has(block.name)) return null;
351
+ const input = block.input ?? {};
352
+ const said = (key) => typeof input[key] === "string" && input[key] !== "" ? input[key] : null;
353
+ const step = (text, detail) => ({ id: block.id, text, detail: shorten(detail), state: "running" });
354
+ switch (block.name) {
355
+ case "Bash":
356
+ return step(said("description") ?? "Run a command", said("command"));
357
+ case "WebFetch":
358
+ return step("Fetch a page", said("url"));
359
+ case "WebSearch":
360
+ return step("Search the web", said("query"));
361
+ case "Read":
362
+ return step("Read a file", said("file_path"));
363
+ case "Grep":
364
+ return step("Search files", said("pattern"));
365
+ case "Glob":
366
+ return step("List files", said("pattern"));
367
+ case "Task":
368
+ case "Agent":
369
+ return step("Hand part of it to a subagent", said("description"));
370
+ }
371
+ const mcp = /^mcp__(.+?)__(.+)$/.exec(block.name);
372
+ if (mcp) return step(connectorLabel(mcp[1], mcp[2]), compactArgs(input));
373
+ return step(block.name, compactArgs(input));
374
+ }
375
+ function codexStep(item, completed) {
376
+ if (typeof item.id !== "string") return null;
377
+ const failed = item.status === "failed" || item.error !== void 0 && item.error !== null;
378
+ const state = completed ? failed ? "failed" : "done" : "running";
379
+ if (item.type === "command_execution") {
380
+ const exited = typeof item.exit_code === "number" && item.exit_code !== 0;
381
+ return {
382
+ id: item.id,
383
+ text: "Run a command",
384
+ detail: shorten(unwrapShell(item.command ?? "")),
385
+ state: completed && exited ? "failed" : state
386
+ };
387
+ }
388
+ if (item.type === "mcp_tool_call" && typeof item.server === "string" && typeof item.tool === "string") {
389
+ return { id: item.id, text: connectorLabel(item.server, item.tool), detail: compactArgs(item.arguments), state };
390
+ }
391
+ if (item.type === "web_search") {
392
+ const action = item.action ?? {};
393
+ if (action.type === "open_page") return { id: item.id, text: "Open a page", detail: shorten(action.url ?? null), state };
394
+ if (action.type === "find_in_page") return { id: item.id, text: "Find in a page", detail: shorten(action.pattern ?? action.url ?? null), state };
395
+ return { id: item.id, text: "Search the web", detail: shorten(action.query || item.query || null), state };
396
+ }
397
+ return null;
398
+ }
399
+ function unwrapShell(command) {
400
+ const wrapped = /^(?:\S*\/)?(?:ba|z)?sh\s+-l?c\s+(['"])([\s\S]*)\1$/.exec(command.trim());
401
+ if (!wrapped) return command.trim();
402
+ return wrapped[1] === "'" ? wrapped[2].replaceAll(`'\\''`, `'`) : wrapped[2];
403
+ }
404
+ function connectorLabel(server, tool) {
405
+ const words = (name) => name.replace(/[_-]+/g, " ").trim();
406
+ if (server === "codex_apps") {
407
+ const said = words(tool);
408
+ return said.charAt(0).toUpperCase() + said.slice(1);
409
+ }
410
+ return `${words(server.replace(/^claude_ai_/, ""))}: ${words(tool)}`;
411
+ }
412
+ function compactArgs(input) {
413
+ if (input === null || input === void 0) return null;
414
+ if (typeof input === "object" && Object.keys(input).length === 0) return null;
415
+ return shorten(typeof input === "string" ? input : JSON.stringify(input));
416
+ }
417
+ function shorten(value) {
418
+ if (!value) return null;
419
+ const lines = value.trim().split("\n");
420
+ const first = lines[0].trim();
421
+ const more = lines.length > 1;
422
+ if (first.length > DETAIL_LENGTH) return `${first.slice(0, DETAIL_LENGTH - 1)}\u2026`;
423
+ return more ? `${first} \u2026` : first;
424
+ }
314
425
  function composePrompt(instructions, context, request, profile) {
315
426
  const now = /* @__PURE__ */ new Date();
316
427
  const facts = [`- Profile: ${profile}`, `- Today: ${now.toISOString().slice(0, 10)}`];
@@ -372,6 +483,7 @@ var Assistant = class _Assistant {
372
483
  sessionId: turn.sessionId,
373
484
  reason: turn.error,
374
485
  reply: turn.reply,
486
+ steps: turn.steps,
375
487
  at: turn.endedAt
376
488
  });
377
489
  }
@@ -431,6 +543,7 @@ ${typed}` : action.request : typed;
431
543
  endedAt: null,
432
544
  status: "running",
433
545
  reply: "",
546
+ steps: [],
434
547
  error: null
435
548
  };
436
549
  this.turns.set(turn.id, turn);
@@ -492,18 +605,27 @@ ${typed}` : action.request : typed;
492
605
  stderr.push(chunk.toString("utf8"));
493
606
  if (stderr.length > 50) stderr.shift();
494
607
  });
608
+ const calls = /* @__PURE__ */ new Map();
495
609
  if (child.stdout) {
496
610
  const lines = createInterface({ input: child.stdout });
497
611
  lines.on("line", (line) => {
498
612
  const event = adapter.parse(line);
499
613
  if (!event) return;
500
614
  if (event.sessionId) turn.sessionId = event.sessionId;
501
- if (event.text) {
502
- turn.reply = turn.reply ? `${turn.reply}
503
-
504
- ${event.text}` : event.text;
505
- this.hooks.onChange();
615
+ if (event.text) addStep(turn, { kind: "message", text: event.text, detail: null, state: "done" });
616
+ for (const parsed of event.steps ?? []) {
617
+ const known = calls.get(parsed.id);
618
+ if (known) {
619
+ if (parsed.text) known.text = parsed.text;
620
+ if (parsed.detail) known.detail = parsed.detail;
621
+ known.state = parsed.state;
622
+ } else if (parsed.text) {
623
+ const step = { kind: "tool", text: parsed.text, detail: parsed.detail ?? null, state: parsed.state };
624
+ calls.set(parsed.id, step);
625
+ addStep(turn, step);
626
+ }
506
627
  }
628
+ if (event.text || event.steps) this.hooks.onChange();
507
629
  if (event.done) done = event.done;
508
630
  });
509
631
  }
@@ -535,20 +657,24 @@ ${event.text}` : event.text;
535
657
  }
536
658
  async finish(turn, outcome, aborted = false) {
537
659
  const at = (/* @__PURE__ */ new Date()).toISOString();
538
- const reply = outcome.reply ?? turn.reply;
660
+ const last = turn.steps.findLast((step) => step.kind === "message");
661
+ const reply = outcome.ok ? outcome.reply ?? last?.text ?? "" : "";
662
+ if (outcome.ok && last && sameWords(last.text, reply)) turn.steps.splice(turn.steps.lastIndexOf(last), 1);
663
+ for (const step of turn.steps) if (step.state === "running") step.state = outcome.ok ? "done" : "failed";
539
664
  turn.reply = reply;
540
665
  turn.endedAt = at;
666
+ const steps = turn.steps;
541
667
  if (outcome.ok) {
542
668
  turn.status = "done";
543
- await this.append({ event: "finished", turn: turn.id, sessionId: turn.sessionId, reply, at });
669
+ await this.append({ event: "finished", turn: turn.id, sessionId: turn.sessionId, reply, steps, at });
544
670
  } else if (aborted) {
545
671
  turn.status = "aborted";
546
672
  turn.error = outcome.error ?? "stopped";
547
- await this.append({ event: "aborted", turn: turn.id, sessionId: turn.sessionId, reason: turn.error, reply, at });
673
+ await this.append({ event: "aborted", turn: turn.id, sessionId: turn.sessionId, reason: turn.error, reply, steps, at });
548
674
  } else {
549
675
  turn.status = "failed";
550
676
  turn.error = outcome.error ?? "failed";
551
- await this.append({ event: "failed", turn: turn.id, sessionId: turn.sessionId, error: turn.error, reply, at });
677
+ await this.append({ event: "failed", turn: turn.id, sessionId: turn.sessionId, error: turn.error, reply, steps, at });
552
678
  }
553
679
  this.hooks.onChange();
554
680
  }
package/dist/cli.js CHANGED
@@ -49,7 +49,7 @@ function shouldOpen(flag) {
49
49
  return flag && process.stdout.isTTY === true && !process.env.CI;
50
50
  }
51
51
  async function demo(open, profile) {
52
- const { demoEnv, demoIntegrations, demoProfile, writeDemoStore } = await import("./demo-6Q22AAC7.js");
52
+ const { demoEnv, demoIntegrations, demoProfile, writeDemoStore } = await import("./demo-TIIXODJG.js");
53
53
  const chosen = demoProfile(profile);
54
54
  const store = await mkdtemp(join(tmpdir(), "daily-focus-demo-"));
55
55
  process.on("exit", () => rmSync(store, { recursive: true, force: true }));
@@ -65,7 +65,7 @@ async function demo(open, profile) {
65
65
  );
66
66
  }
67
67
  async function start(open, onStarted, options = {}) {
68
- const { runServer } = await import("./server-PIYOIUKJ.js");
68
+ const { runServer } = await import("./server-WT4CQ3VX.js");
69
69
  await runServer({
70
70
  ...options,
71
71
  onStarted: (url) => {
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  QUICK_ACTIONS
3
- } from "./chunk-SX5FPS3A.js";
3
+ } from "./chunk-KL6RU4XT.js";
4
4
  import {
5
5
  dateFromNow,
6
6
  sampleBrief,
@@ -737,6 +737,8 @@ function demoAgentLog(profile, now) {
737
737
  ];
738
738
  return runs.flatMap(runLines);
739
739
  }
740
+ var call = (text, detail, state = "done") => ({ kind: "tool", text, detail, state });
741
+ var said = (text) => ({ kind: "message", text, detail: null, state: "done" });
740
742
  function turnLines(seed) {
741
743
  const quick = seed.action ? QUICK_ACTIONS.find((candidate) => candidate.id === seed.action) : void 0;
742
744
  const request = quick ? seed.text ? `${quick.request}
@@ -745,7 +747,7 @@ ${seed.text}` : quick.request : seed.text ?? "";
745
747
  const endedAt = new Date(new Date(seed.at).getTime() + seed.seconds * 1e3).toISOString();
746
748
  return [
747
749
  { event: "started", turn: seed.id, item: seed.item, agent: "claude", request, action: quick?.id ?? null, at: seed.at },
748
- { event: "finished", turn: seed.id, sessionId: `sample-${seed.item}`, reply: seed.reply, at: endedAt }
750
+ { event: "finished", turn: seed.id, sessionId: `sample-${seed.item}`, reply: seed.reply, steps: seed.steps, at: endedAt }
749
751
  ];
750
752
  }
751
753
  function demoAssistantLog(profile, now) {
@@ -757,6 +759,12 @@ function demoAssistantLog(profile, now) {
757
759
  action: "summarise-pr",
758
760
  at: minutesAgo(now, 46),
759
761
  seconds: 52,
762
+ steps: [
763
+ call("View the pull request", "gh pr view 3421 --repo acme/webapp --json title,body,files,reviews"),
764
+ call("Read the diff", "gh pr diff 3421 --repo acme/webapp"),
765
+ said("Four files. The recorder change is the substance; the rest is its tests and a type."),
766
+ call("Read the issue it closes", "gh issue view 3388 --repo acme/webapp --comments")
767
+ ],
760
768
  reply: [
761
769
  "**What it changes.** Session replay keeps every recorded mutation batch in an array on the recorder for the life of the tab, and only ever appends; on a tab left open for a day that is hundreds of megabytes. The change caps the buffer per tab, drops batches once the ingest endpoint has acknowledged them, and holds DOM node references through a `WeakRef` so a detached subtree can be collected.",
762
770
  "",
@@ -778,6 +786,11 @@ function demoAssistantLog(profile, now) {
778
786
  text: "Is the Safari fallback actually reachable?",
779
787
  at: minutesAgo(now, 44),
780
788
  seconds: 31,
789
+ steps: [
790
+ call("Find where the fallback is chosen", 'gh search code "WeakRef" --repo acme/webapp'),
791
+ call("Read the SDK docs on browser support", "gh api repos/acme/sdk-docs/contents/support.md", "failed"),
792
+ call("Read the webapp's copy of the support matrix", "gh api repos/acme/webapp/contents/docs/browser-support.md")
793
+ ],
781
794
  reply: "Only on Safari 14.0 and below, which the SDK\u2019s support matrix dropped in v5. Under the current matrix the branch is dead code. I would ask Alice to remove it rather than test it."
782
795
  },
783
796
  {
@@ -786,10 +799,18 @@ function demoAssistantLog(profile, now) {
786
799
  action: "draft-reply",
787
800
  at: minutesAgo(now, 38),
788
801
  seconds: 64,
802
+ steps: [
803
+ call("Gmail: get thread", '{"threadId":"18f2a9c4b7"}'),
804
+ call("Gmail: search threads", '{"query":"Q4 planning doc headcount"}'),
805
+ said("Bob wants numbers per team with a reason each. The planning doc has both."),
806
+ call("Gmail: create draft", '{"threadId":"18f2a9c4b7"}')
807
+ ],
789
808
  reply: [
790
809
  "I read the thread \u2014 four messages, Bob twice. He wants the Q4 team split: how many of the six open roles go to Platform and how many to Webapp, with a line of justification each, by the end of the week so it lands in the planning deck.",
791
810
  "",
792
- "I have created a Gmail draft on the thread, from you, proposing four Platform and two Webapp with the reasons from the planning doc, and asking whether the deck can carry a footnote that the SDK hire depends on the Q3 numbers. It is **not sent**; it is waiting on the thread when you open it."
811
+ "I have created a Gmail draft on the thread, from you, proposing four Platform and two Webapp with the reasons from the planning doc, and asking whether the deck can carry a footnote that the SDK hire depends on the Q3 numbers. It is **not sent**; it is waiting on the thread when you open it.",
812
+ "",
813
+ "Created: [Gmail draft](https://mail.google.com/mail/u/0/#drafts?compose=18f2a9c4b7)"
793
814
  ].join("\n")
794
815
  },
795
816
  {
@@ -798,6 +819,12 @@ function demoAssistantLog(profile, now) {
798
819
  action: "explain-ci",
799
820
  at: minutesAgo(now, 20),
800
821
  seconds: 58,
822
+ steps: [
823
+ call("List the checks", "gh pr checks 3402 --repo acme/webapp"),
824
+ call("Read the failing job\u2019s log", "gh run view 9182734 --repo acme/webapp --log-failed"),
825
+ said("One failure, in tracing/fetch.spec.ts. Reading the retry path it exercises."),
826
+ call("Read the retry code", "gh api repos/acme/webapp/contents/src/tracing/fetch.ts")
827
+ ],
801
828
  reply: [
802
829
  "The failing check is `unit-tests (node-20)`, in `tracing/fetch.spec.ts`:",
803
830
  "",
@@ -817,6 +844,10 @@ function demoAssistantLog(profile, now) {
817
844
  action: "summarise-pr",
818
845
  at: minutesAgo(now, 52),
819
846
  seconds: 47,
847
+ steps: [
848
+ call("View the pull request", "gh pr view 47 --repo mara-l/pedalboard --json title,body,files,commits"),
849
+ call("Read the diff", "gh pr diff 47 --repo mara-l/pedalboard")
850
+ ],
820
851
  reply: [
821
852
  "Tomas adds MIDI clock sync to the looper: the transport listens for MIDI clock at 24 pulses per quarter note and for start and stop, derives the tempo, and quantises the loop start to the next beat. +212/\u221218 across three files, with a test for the tempo derivation.",
822
853
  "",
@@ -835,7 +866,8 @@ function demoAssistantLog(profile, now) {
835
866
  action: "draft-reply",
836
867
  at: minutesAgo(now, 30),
837
868
  seconds: 41,
838
- reply: "The thread is three messages: your request on the 28th, their \u201Cwithin the week\u201D on the 29th, and nothing since. I have created a Gmail draft on it that asks, briefly and politely, whether the quote is still coming this week, and says the other quotes are in and you would like to decide by Friday. It is **not sent**; open the thread to read it."
869
+ steps: [call("Gmail: get thread", '{"threadId":"19a0c4e2f1"}'), call("Gmail: create draft", '{"threadId":"19a0c4e2f1"}')],
870
+ reply: "The thread is three messages: your request on the 28th, their \u201Cwithin the week\u201D on the 29th, and nothing since. I have created a Gmail draft on it that asks, briefly and politely, whether the quote is still coming this week, and says the other quotes are in and you would like to decide by Friday. It is **not sent**; open the thread to read it.\n\nCreated: [Gmail draft](https://mail.google.com/mail/u/0/#drafts?compose=19a0c4e2f1)"
839
871
  }
840
872
  ];
841
873
  return turns.flatMap(turnLines);
@@ -3,7 +3,7 @@ import {
3
3
  Assistant,
4
4
  CodexGate,
5
5
  codexVersionWarning
6
- } from "./chunk-SX5FPS3A.js";
6
+ } from "./chunk-KL6RU4XT.js";
7
7
  import {
8
8
  FOCUS_TEMPLATE,
9
9
  sourcesTemplate
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "daily-focus",
3
- "version": "1.2.0-dev.8",
3
+ "version": "1.2.0-dev.9",
4
4
  "description": "An interactive dashboard of what you should focus on today at work",
5
5
  "keywords": [
6
6
  "dashboard",
@@ -51,7 +51,12 @@ thread the Gmail tool did not find — say so plainly at the top, and answer as
51
51
  the rest allows. A confident answer built on half the material is worse than a
52
52
  shorter one that says what it is missing.
53
53
 
54
- Give the URL of anything you created, so it can be opened from the panel.
54
+ End with one line for each thing you created, in exactly this form, so the panel
55
+ can offer it as a button:
56
+
57
+ ```
58
+ Created: [Gmail draft](https://mail.google.com/mail/u/0/#drafts?compose=…)
59
+ ```
55
60
 
56
61
  ## Stay in this chat
57
62
 
@@ -59,4 +64,4 @@ This conversation is the whole record of what you did. Do not leave notes on the
59
64
  item, do not write to the dashboard's files, and do not try to mark the item done,
60
65
  snoozed or handled: the user does that from the dashboard once they have read your
61
66
  reply. If you created or changed something — a draft, most often — say so in the
62
- reply, with its URL, and stop there.
67
+ reply, end with its `Created:` line, and stop there.
package/public/app.js CHANGED
@@ -69,9 +69,9 @@ function H() {
69
69
  }
70
70
  }
71
71
  function L(n3, l5, u4, t4, i4, r4, o4, e4, f4, c4, a4) {
72
- var s4, h5, p5, v4, y4, _5, g4 = t4 && t4.__k || w, m4 = l5.length;
73
- for (f4 = T(u4, l5, g4, f4, m4), s4 = 0; s4 < m4; s4++) null != (p5 = u4.__k[s4]) && (h5 = -1 != p5.__i && g4[p5.__i] || d, p5.__i = s4, _5 = q(n3, p5, h5, i4, r4, o4, e4, f4, c4, a4), v4 = p5.__e, p5.ref && h5.ref != p5.ref && (h5.ref && J(h5.ref, null, p5), a4.push(p5.ref, p5.__c || v4, p5)), null == y4 && null != v4 && (y4 = v4), 4 & p5.__u ? (f4 = j(p5, f4, n3), h5.__e && (h5.__e = null)) : "function" == typeof p5.type && void 0 !== _5 ? f4 = _5 : v4 && (f4 = v4.nextSibling), p5.__u &= -7);
74
- return u4.__e = y4, f4;
72
+ var s4, h5, p5, v4, y5, _5, g4 = t4 && t4.__k || w, m4 = l5.length;
73
+ for (f4 = T(u4, l5, g4, f4, m4), s4 = 0; s4 < m4; s4++) null != (p5 = u4.__k[s4]) && (h5 = -1 != p5.__i && g4[p5.__i] || d, p5.__i = s4, _5 = q(n3, p5, h5, i4, r4, o4, e4, f4, c4, a4), v4 = p5.__e, p5.ref && h5.ref != p5.ref && (h5.ref && J(h5.ref, null, p5), a4.push(p5.ref, p5.__c || v4, p5)), null == y5 && null != v4 && (y5 = v4), 4 & p5.__u ? (f4 = j(p5, f4, n3), h5.__e && (h5.__e = null)) : "function" == typeof p5.type && void 0 !== _5 ? f4 = _5 : v4 && (f4 = v4.nextSibling), p5.__u &= -7);
74
+ return u4.__e = y5, f4;
75
75
  }
76
76
  function T(n3, l5, u4, t4, i4) {
77
77
  var r4, o4, e4, f4, c4, a4 = u4.length, s4 = a4, h5 = 0;
@@ -131,29 +131,29 @@ function V(n3) {
131
131
  };
132
132
  }
133
133
  function q(n3, u4, t4, i4, r4, o4, e4, f4, c4, a4) {
134
- var s4, h5, p5, v4, y4, d4, _5, k3, x4, M, I2, P2, A3, H2, T4, j4, F2 = u4.type;
134
+ var s4, h5, p5, v4, y5, d5, _5, k3, x4, M, I2, P2, A3, H2, T4, j4, F2 = u4.type;
135
135
  if (void 0 !== u4.constructor) return null;
136
136
  128 & t4.__u && (c4 = !!(32 & t4.__u), o4 = [f4 = u4.__e = t4.__e]), (s4 = l.__b) && s4(u4);
137
137
  n: if ("function" == typeof F2) {
138
138
  h5 = e4.length;
139
139
  try {
140
- if (x4 = u4.props, M = F2.prototype && F2.prototype.render, I2 = (s4 = F2.contextType) && i4[s4.__c], P2 = s4 ? I2 ? I2.props.value : s4.__ : i4, t4.__c ? k3 = (p5 = u4.__c = t4.__c).__ = p5.__E : (M ? u4.__c = p5 = new F2(x4, P2) : (u4.__c = p5 = new C(x4, P2), p5.constructor = F2, p5.render = Q), I2 && I2.sub(p5), p5.state || (p5.state = {}), p5.__n = i4, v4 = p5.__d = true, p5.__h = [], p5._sb = []), M && null == p5.__s && (p5.__s = p5.state), M && null != F2.getDerivedStateFromProps && (p5.__s == p5.state && (p5.__s = m({}, p5.__s)), m(p5.__s, F2.getDerivedStateFromProps(x4, p5.__s))), y4 = p5.props, d4 = p5.state, p5.__v = u4, v4) M && null == F2.getDerivedStateFromProps && null != p5.componentWillMount && p5.componentWillMount(), M && null != p5.componentDidMount && p5.__h.push(p5.componentDidMount);
140
+ if (x4 = u4.props, M = F2.prototype && F2.prototype.render, I2 = (s4 = F2.contextType) && i4[s4.__c], P2 = s4 ? I2 ? I2.props.value : s4.__ : i4, t4.__c ? k3 = (p5 = u4.__c = t4.__c).__ = p5.__E : (M ? u4.__c = p5 = new F2(x4, P2) : (u4.__c = p5 = new C(x4, P2), p5.constructor = F2, p5.render = Q), I2 && I2.sub(p5), p5.state || (p5.state = {}), p5.__n = i4, v4 = p5.__d = true, p5.__h = [], p5._sb = []), M && null == p5.__s && (p5.__s = p5.state), M && null != F2.getDerivedStateFromProps && (p5.__s == p5.state && (p5.__s = m({}, p5.__s)), m(p5.__s, F2.getDerivedStateFromProps(x4, p5.__s))), y5 = p5.props, d5 = p5.state, p5.__v = u4, v4) M && null == F2.getDerivedStateFromProps && null != p5.componentWillMount && p5.componentWillMount(), M && null != p5.componentDidMount && p5.__h.push(p5.componentDidMount);
141
141
  else {
142
- if (M && null == F2.getDerivedStateFromProps && x4 !== y4 && null != p5.componentWillReceiveProps && p5.componentWillReceiveProps(x4, P2), u4.__v == t4.__v || !p5.__e && null != p5.shouldComponentUpdate && false === p5.shouldComponentUpdate(x4, p5.__s, P2)) {
142
+ if (M && null == F2.getDerivedStateFromProps && x4 !== y5 && null != p5.componentWillReceiveProps && p5.componentWillReceiveProps(x4, P2), u4.__v == t4.__v || !p5.__e && null != p5.shouldComponentUpdate && false === p5.shouldComponentUpdate(x4, p5.__s, P2)) {
143
143
  u4.__v != t4.__v && (p5.props = x4, p5.state = p5.__s, p5.__d = false), u4.__e = t4.__e, u4.__k = t4.__k, u4.__k.some(function(n4) {
144
144
  n4 && (n4.__ = u4);
145
145
  }), w.push.apply(p5.__h, p5._sb), p5._sb = [], p5.__h.length && e4.push(p5), f4 = $(t4);
146
146
  break n;
147
147
  }
148
148
  null != p5.componentWillUpdate && p5.componentWillUpdate(x4, p5.__s, P2), M && null != p5.componentDidUpdate && p5.__h.push(function() {
149
- p5.componentDidUpdate(y4, d4, _5);
149
+ p5.componentDidUpdate(y5, d5, _5);
150
150
  });
151
151
  }
152
152
  if (p5.context = P2, p5.props = x4, p5.__P = n3, p5.__e = false, A3 = l.__r, H2 = 0, M) p5.state = p5.__s, p5.__d = false, A3 && A3(u4), s4 = p5.render(p5.props, p5.state, p5.context), w.push.apply(p5.__h, p5._sb), p5._sb = [];
153
153
  else do {
154
154
  p5.__d = false, A3 && A3(u4), s4 = p5.render(p5.props, p5.state, p5.context), p5.state = p5.__s;
155
155
  } while (p5.__d && ++H2 < 25);
156
- p5.state = p5.__s, null != p5.getChildContext && (i4 = m(m({}, i4), p5.getChildContext())), M && !v4 && null != p5.getSnapshotBeforeUpdate && (_5 = p5.getSnapshotBeforeUpdate(y4, d4)), T4 = null != s4 && s4.type === S && null == s4.key ? E(s4.props.children) : s4, f4 = L(n3, g(T4) ? T4 : [T4], u4, t4, i4, r4, o4, e4, f4, c4, a4), p5.base = u4.__e, u4.__u &= -161, p5.__h.length && e4.push(p5), k3 && (p5.__E = p5.__ = null);
156
+ p5.state = p5.__s, null != p5.getChildContext && (i4 = m(m({}, i4), p5.getChildContext())), M && !v4 && null != p5.getSnapshotBeforeUpdate && (_5 = p5.getSnapshotBeforeUpdate(y5, d5)), T4 = null != s4 && s4.type === S && null == s4.key ? E(s4.props.children) : s4, f4 = L(n3, g(T4) ? T4 : [T4], u4, t4, i4, r4, o4, e4, f4, c4, a4), p5.base = u4.__e, u4.__u &= -161, p5.__h.length && e4.push(p5), k3 && (p5.__E = p5.__ = null);
157
157
  } catch (n4) {
158
158
  if (e4.length = h5, u4.__v = null, c4 || null != o4) {
159
159
  if (n4.then) {
@@ -185,10 +185,10 @@ function E(n3) {
185
185
  return "object" != typeof n3 || null == n3 || n3.__b > 0 ? n3 : g(n3) ? n3.map(E) : void 0 !== n3.constructor ? null : m({}, n3);
186
186
  }
187
187
  function G(u4, t4, i4, r4, o4, e4, f4, c4, a4) {
188
- var s4, h5, p5, v4, y4, w5, _5, m4 = i4.props || d, k3 = t4.props, x4 = t4.type;
188
+ var s4, h5, p5, v4, y5, w5, _5, m4 = i4.props || d, k3 = t4.props, x4 = t4.type;
189
189
  if ("svg" == x4 ? o4 = "http://www.w3.org/2000/svg" : "math" == x4 ? o4 = "http://www.w3.org/1998/Math/MathML" : o4 || (o4 = "http://www.w3.org/1999/xhtml"), null != e4) {
190
- for (s4 = 0; s4 < e4.length; s4++) if ((y4 = e4[s4]) && "setAttribute" in y4 == !!x4 && (x4 ? y4.localName == x4 : 3 == y4.nodeType)) {
191
- u4 = y4, e4[s4] = null;
190
+ for (s4 = 0; s4 < e4.length; s4++) if ((y5 = e4[s4]) && "setAttribute" in y5 == !!x4 && (x4 ? y5.localName == x4 : 3 == y5.nodeType)) {
191
+ u4 = y5, e4[s4] = null;
192
192
  break;
193
193
  }
194
194
  }
@@ -198,9 +198,9 @@ function G(u4, t4, i4, r4, o4, e4, f4, c4, a4) {
198
198
  }
199
199
  if (null == x4) m4 === k3 || c4 && u4.data == k3 || (u4.data = k3);
200
200
  else {
201
- if (e4 = "textarea" == x4 && null != k3.defaultValue ? null : e4 && n.call(u4.childNodes), !c4 && null != e4) for (m4 = {}, s4 = 0; s4 < u4.attributes.length; s4++) m4[(y4 = u4.attributes[s4]).name] = y4.value;
202
- for (s4 in m4) y4 = m4[s4], "dangerouslySetInnerHTML" == s4 ? p5 = y4 : "children" == s4 || s4 in k3 || "value" == s4 && "defaultValue" in k3 || "checked" == s4 && "defaultChecked" in k3 || N(u4, s4, null, y4, o4);
203
- for (s4 in k3) y4 = k3[s4], "children" == s4 ? v4 = y4 : "dangerouslySetInnerHTML" == s4 ? h5 = y4 : "value" == s4 ? w5 = y4 : "checked" == s4 ? _5 = y4 : c4 && "function" != typeof y4 || m4[s4] === y4 || N(u4, s4, y4, m4[s4], o4);
201
+ if (e4 = "textarea" == x4 && null != k3.defaultValue ? null : e4 && n.call(u4.childNodes), !c4 && null != e4) for (m4 = {}, s4 = 0; s4 < u4.attributes.length; s4++) m4[(y5 = u4.attributes[s4]).name] = y5.value;
202
+ for (s4 in m4) y5 = m4[s4], "dangerouslySetInnerHTML" == s4 ? p5 = y5 : "children" == s4 || s4 in k3 || "value" == s4 && "defaultValue" in k3 || "checked" == s4 && "defaultChecked" in k3 || N(u4, s4, null, y5, o4);
203
+ for (s4 in k3) y5 = k3[s4], "children" == s4 ? v4 = y5 : "dangerouslySetInnerHTML" == s4 ? h5 = y5 : "value" == s4 ? w5 = y5 : "checked" == s4 ? _5 = y5 : c4 && "function" != typeof y5 || m4[s4] === y5 || N(u4, s4, y5, m4[s4], o4);
204
204
  if (h5) c4 || p5 && (h5.__html == p5.__html || h5.__html == u4.innerHTML) || (u4.innerHTML = h5.__html), t4.__k = [];
205
205
  else if (p5 && (u4.innerHTML = ""), L("template" == t4.type ? u4.content : u4, g(v4) ? v4 : [v4], t4, i4, r4, "foreignObject" == x4 ? "http://www.w3.org/1999/xhtml" : o4, e4, f4, e4 ? e4[0] : i4.__k && $(i4, 0), c4, a4), null != e4) for (s4 = e4.length; s4--; ) b(e4[s4]);
206
206
  c4 && "textarea" != x4 || (s4 = "value", "progress" == x4 && null == w5 ? u4.removeAttribute("value") : null != w5 && (w5 !== u4[s4] || "progress" == x4 && !w5 || "option" == x4 && w5 != m4[s4]) && N(u4, s4, w5, m4[s4], o4), s4 = "checked", null != _5 && _5 != u4[s4] && N(u4, s4, _5, m4[s4], o4));
@@ -274,6 +274,42 @@ function s2(n3, t4) {
274
274
  var u4 = r2.__H || (r2.__H = { __: [], __h: [] });
275
275
  return n3 >= u4.__.length && u4.__.push({}), u4.__[n3];
276
276
  }
277
+ function d2(n3) {
278
+ return o2 = 1, y2(D2, n3);
279
+ }
280
+ function y2(n3, u4, i4) {
281
+ var o4 = s2(t2++, 2);
282
+ if (o4.t = n3, !o4.__c && (o4.__ = [i4 ? i4(u4) : D2(void 0, u4), function(n4) {
283
+ var t4 = o4.__N ? o4.__N[0] : o4.__[0], r4 = o4.t(t4, n4);
284
+ t4 !== r4 && (o4.__N = [r4, o4.__[1]], o4.__c.setState({}));
285
+ }], o4.__c = r2, !r2.__f)) {
286
+ var f4 = function(n4, t4, r4) {
287
+ if (!o4.__c.__H) return true;
288
+ var u5 = false, i5 = o4.__c.props !== n4;
289
+ if (o4.__c.__H.__.some(function(n5) {
290
+ if (n5.__N) {
291
+ u5 = true;
292
+ var t5 = n5.__[0];
293
+ n5.__ = n5.__N, n5.__N = void 0, t5 !== n5.__[0] && (i5 = true);
294
+ }
295
+ }), c4) {
296
+ var f5 = c4.call(this, n4, t4, r4);
297
+ return u5 ? f5 || i5 : f5;
298
+ }
299
+ return !u5 || i5;
300
+ };
301
+ r2.__f = true;
302
+ var c4 = r2.shouldComponentUpdate, e4 = r2.componentWillUpdate;
303
+ r2.componentWillUpdate = function(n4, t4, r4) {
304
+ if (this.__e) {
305
+ var u5 = c4;
306
+ c4 = void 0, f4(n4, t4, r4), c4 = u5;
307
+ }
308
+ e4 && e4.call(this, n4, t4, r4);
309
+ }, r2.shouldComponentUpdate = f4;
310
+ }
311
+ return o4.__N || o4.__;
312
+ }
277
313
  function h2(n3, u4) {
278
314
  var i4 = s2(t2++, 3);
279
315
  !c2.__s && C2(i4.__H, u4) && (i4.__ = n3, i4.u = u4, r2.__H.__h.push(i4));
@@ -360,6 +396,9 @@ function C2(n3, t4) {
360
396
  return t5 !== n3[r4];
361
397
  });
362
398
  }
399
+ function D2(n3, t4) {
400
+ return "function" == typeof t4 ? t4(n3) : t4;
401
+ }
363
402
 
364
403
  // node_modules/@preact/signals-core/dist/signals-core.module.js
365
404
  var i3 = /* @__PURE__ */ Symbol.for("preact-signals");
@@ -430,7 +469,7 @@ var s3 = 0;
430
469
  var u3 = 0;
431
470
  var e3 = 0;
432
471
  var c3 = void 0;
433
- var d2 = 0;
472
+ var d3 = 0;
434
473
  function a3(i4) {
435
474
  if (void 0 !== o3) {
436
475
  var t4 = i4.n;
@@ -543,7 +582,7 @@ Object.defineProperty(l3.prototype, "value", { get: function() {
543
582
  })(this);
544
583
  this.v = i4;
545
584
  this.i++;
546
- d2++;
585
+ d3++;
547
586
  v3++;
548
587
  try {
549
588
  for (var n3 = this.t; void 0 !== n3; n3 = n3.x) n3.t.N();
@@ -552,7 +591,7 @@ Object.defineProperty(l3.prototype, "value", { get: function() {
552
591
  }
553
592
  }
554
593
  } });
555
- function y2(i4, t4) {
594
+ function y3(i4, t4) {
556
595
  return new l3(i4, t4);
557
596
  }
558
597
  function w3(i4) {
@@ -590,7 +629,7 @@ function p3(i4, t4) {
590
629
  l3.call(this, void 0, t4);
591
630
  this.x = i4;
592
631
  this.s = void 0;
593
- this.g = d2 - 1;
632
+ this.g = d3 - 1;
594
633
  this.f = 4;
595
634
  }
596
635
  p3.prototype = new l3();
@@ -599,8 +638,8 @@ p3.prototype.h = function() {
599
638
  if (1 & this.f) return false;
600
639
  if (32 == (36 & this.f)) return true;
601
640
  this.f &= -5;
602
- if (this.g === d2) return true;
603
- this.g = d2;
641
+ if (this.g === d3) return true;
642
+ this.g = d3;
604
643
  this.f |= 1;
605
644
  if (this.i > 0 && !w3(this)) {
606
645
  this.f &= -2;
@@ -754,7 +793,7 @@ function j3(i4, t4) {
754
793
  // node_modules/@preact/signals/dist/signals.module.js
755
794
  var l4;
756
795
  var h4;
757
- var d3;
796
+ var d4;
758
797
  var p4 = "undefined" != typeof window && !!window.__PREACT_SIGNALS_DEVTOOLS__;
759
798
  var _4 = [];
760
799
  j3(function() {
@@ -765,14 +804,14 @@ function g3(i4, r4) {
765
804
  });
766
805
  }
767
806
  function b3(i4) {
768
- if (d3) {
769
- var n3 = d3;
770
- d3 = void 0;
807
+ if (d4) {
808
+ var n3 = d4;
809
+ d4 = void 0;
771
810
  n3();
772
811
  }
773
- d3 = i4 && i4.S();
812
+ d4 = i4 && i4.S();
774
813
  }
775
- function y3(i4) {
814
+ function y4(i4) {
776
815
  var n3 = this, t4 = i4.data, f4 = useSignal(t4);
777
816
  f4.name = "ReactiveDom";
778
817
  f4.value = t4;
@@ -802,8 +841,8 @@ function y3(i4) {
802
841
  }, []), a4 = e4[0], v4 = e4[1];
803
842
  return a4.value ? v4.peek() : v4.value;
804
843
  }
805
- y3.displayName = "ReactiveTextNode";
806
- Object.defineProperties(l3.prototype, { constructor: { configurable: true, value: void 0 }, type: { configurable: true, value: y3 }, props: { configurable: true, get: function() {
844
+ y4.displayName = "ReactiveTextNode";
845
+ Object.defineProperties(l3.prototype, { constructor: { configurable: true, value: void 0 }, type: { configurable: true, value: y4 }, props: { configurable: true, get: function() {
807
846
  var i4 = this;
808
847
  return { data: { get value() {
809
848
  return i4.value;
@@ -887,7 +926,7 @@ g3("diffed", function(i4, n3) {
887
926
  i4(n3);
888
927
  });
889
928
  function w4(i4, n3, r4, t4) {
890
- var o4 = n3 in i4 && void 0 === i4.ownerSVGElement, f4 = y2(r4);
929
+ var o4 = n3 in i4 && void 0 === i4.ownerSVGElement, f4 = y3(r4);
891
930
  return { o: function(i5, n4) {
892
931
  f4.value = i5;
893
932
  t4 = n4;
@@ -955,7 +994,7 @@ C.prototype.shouldComponentUpdate = function(i4, n3) {
955
994
  };
956
995
  function useSignal(i4, n3) {
957
996
  return T2(function() {
958
- return y2(i4, n3);
997
+ return y3(i4, n3);
959
998
  }, []);
960
999
  }
961
1000
  var q2 = function(i4) {
@@ -1079,33 +1118,33 @@ var PANEL_PREFIX = "panel:";
1079
1118
  var VIEW_KEY = "daily-focus:view";
1080
1119
  var AGENT_RUN_SEEN_KEY = "daily-focus:agent-run-seen";
1081
1120
  var DETAIL_KEY = "daily-focus:detail-for";
1082
- var state = y2(null);
1121
+ var state = y3(null);
1083
1122
  function createUi(initial = {}) {
1084
1123
  return {
1085
- selectedId: y2(initial.selectedId ?? null),
1086
- clickedId: y2(initial.clickedId ?? null),
1087
- pending: y2(initial.pending ?? /* @__PURE__ */ new Set()),
1088
- menuFor: y2(initial.menuFor ?? null),
1089
- statusFor: y2(initial.statusFor ?? null),
1090
- detailFor: y2(initial.detailFor ?? null),
1091
- noteDraft: y2(initial.noteDraft ?? ""),
1092
- assistantDraft: y2(initial.assistantDraft ?? ""),
1093
- focusField: y2(initial.focusField ?? null),
1094
- openDrawers: y2(initial.openDrawers ?? /* @__PURE__ */ new Set()),
1095
- ticketMode: y2(initial.ticketMode ?? "sync"),
1096
- agentReportSeen: y2(initial.agentReportSeen ?? null),
1097
- messageOpen: y2(initial.messageOpen ?? /* @__PURE__ */ new Map()),
1098
- view: y2(initial.view ?? "today"),
1099
- theme: y2(initial.theme ?? "system"),
1100
- toast: y2(initial.toast ?? null),
1101
- connectionError: y2(initial.connectionError ?? null),
1102
- settingsReturn: y2(initial.settingsReturn ?? "today"),
1103
- settings: y2(initial.settings ?? null),
1104
- settingsDraft: y2(initial.settingsDraft ?? /* @__PURE__ */ new Map()),
1105
- settingsError: y2(initial.settingsError ?? null),
1106
- settingsSaving: y2(initial.settingsSaving ?? false),
1107
- texts: y2(initial.texts ?? {}),
1108
- calendars: y2(initial.calendars ?? null)
1124
+ selectedId: y3(initial.selectedId ?? null),
1125
+ clickedId: y3(initial.clickedId ?? null),
1126
+ pending: y3(initial.pending ?? /* @__PURE__ */ new Set()),
1127
+ menuFor: y3(initial.menuFor ?? null),
1128
+ statusFor: y3(initial.statusFor ?? null),
1129
+ detailFor: y3(initial.detailFor ?? null),
1130
+ noteDraft: y3(initial.noteDraft ?? ""),
1131
+ assistantDraft: y3(initial.assistantDraft ?? ""),
1132
+ focusField: y3(initial.focusField ?? null),
1133
+ openDrawers: y3(initial.openDrawers ?? /* @__PURE__ */ new Set()),
1134
+ ticketMode: y3(initial.ticketMode ?? "sync"),
1135
+ agentReportSeen: y3(initial.agentReportSeen ?? null),
1136
+ messageOpen: y3(initial.messageOpen ?? /* @__PURE__ */ new Map()),
1137
+ view: y3(initial.view ?? "today"),
1138
+ theme: y3(initial.theme ?? "system"),
1139
+ toast: y3(initial.toast ?? null),
1140
+ connectionError: y3(initial.connectionError ?? null),
1141
+ settingsReturn: y3(initial.settingsReturn ?? "today"),
1142
+ settings: y3(initial.settings ?? null),
1143
+ settingsDraft: y3(initial.settingsDraft ?? /* @__PURE__ */ new Map()),
1144
+ settingsError: y3(initial.settingsError ?? null),
1145
+ settingsSaving: y3(initial.settingsSaving ?? false),
1146
+ texts: y3(initial.texts ?? {}),
1147
+ calendars: y3(initial.calendars ?? null)
1109
1148
  };
1110
1149
  }
1111
1150
  function isView(value) {
@@ -1501,6 +1540,27 @@ async function stopAssistant(id) {
1501
1540
  showToast(`Could not stop: ${err.message}`);
1502
1541
  }
1503
1542
  }
1543
+ async function copy(text) {
1544
+ try {
1545
+ if (navigator.clipboard && window.isSecureContext) {
1546
+ await navigator.clipboard.writeText(text);
1547
+ } else {
1548
+ const field = document.createElement("textarea");
1549
+ field.value = text;
1550
+ field.setAttribute("readonly", "");
1551
+ field.style.position = "fixed";
1552
+ field.style.opacity = "0";
1553
+ document.body.append(field);
1554
+ field.select();
1555
+ const copied = document.execCommand("copy");
1556
+ field.remove();
1557
+ if (!copied) throw new Error("the browser would not copy");
1558
+ }
1559
+ showToast("Copied");
1560
+ } catch (err) {
1561
+ showToast(`Could not copy: ${err.message}`);
1562
+ }
1563
+ }
1504
1564
  async function runAgent() {
1505
1565
  if (!state.value?.agentRun?.enabled || state.value.agentRun.last?.status === "running") return;
1506
1566
  const ok = window.confirm(
@@ -1808,6 +1868,7 @@ var handlers = {
1808
1868
  refreshCalendar: () => void refreshCalendar(),
1809
1869
  ask: (id, body) => void ask(id, body),
1810
1870
  stopAssistant: (id) => void stopAssistant(id),
1871
+ copy: (text) => void copy(text),
1811
1872
  runAgent: () => void runAgent(),
1812
1873
  stopAgent: () => void stopAgent(),
1813
1874
  askAgent: (run, text) => void askAgent(run, text),
@@ -1866,15 +1927,15 @@ var dateFormats = formatters("UTC");
1866
1927
  var DATE_ONLY = /^\d{4}-\d{2}-\d{2}$/;
1867
1928
  function formatted(value, kind) {
1868
1929
  const bare = typeof value === "string" && DATE_ONLY.test(value);
1869
- const d4 = bare ? /* @__PURE__ */ new Date(`${value}T00:00:00Z`) : parseDate(value);
1870
- return d4 && !Number.isNaN(d4.getTime()) ? (bare ? dateFormats : clockFormats)[kind].format(d4) : "";
1930
+ const d5 = bare ? /* @__PURE__ */ new Date(`${value}T00:00:00Z`) : parseDate(value);
1931
+ return d5 && !Number.isNaN(d5.getTime()) ? (bare ? dateFormats : clockFormats)[kind].format(d5) : "";
1871
1932
  }
1872
1933
  function parseDate(value) {
1873
1934
  if (typeof value !== "string" || value === "") return null;
1874
1935
  const dateOnly = /^(\d{4})-(\d{2})-(\d{2})$/.exec(value);
1875
1936
  if (dateOnly) return new Date(+dateOnly[1], +dateOnly[2] - 1, +dateOnly[3]);
1876
- const d4 = new Date(value);
1877
- return Number.isNaN(d4.getTime()) ? null : d4;
1937
+ const d5 = new Date(value);
1938
+ return Number.isNaN(d5.getTime()) ? null : d5;
1878
1939
  }
1879
1940
  function formatTime(value) {
1880
1941
  return formatted(value, "time");
@@ -1901,37 +1962,37 @@ function setCalendarZone(timeZone) {
1901
1962
  clockFormats = formatters();
1902
1963
  }
1903
1964
  }
1904
- function dayKey(d4) {
1965
+ function dayKey(d5) {
1905
1966
  if (dayKeyFmt) {
1906
- const parts = dayKeyFmt.formatToParts(d4);
1967
+ const parts = dayKeyFmt.formatToParts(d5);
1907
1968
  const part = (type) => parts.find((p5) => p5.type === type)?.value ?? "";
1908
1969
  return `${part("year")}-${part("month")}-${part("day")}`;
1909
1970
  }
1910
- const m4 = String(d4.getMonth() + 1).padStart(2, "0");
1911
- const day = String(d4.getDate()).padStart(2, "0");
1912
- return `${d4.getFullYear()}-${m4}-${day}`;
1971
+ const m4 = String(d5.getMonth() + 1).padStart(2, "0");
1972
+ const day = String(d5.getDate()).padStart(2, "0");
1973
+ return `${d5.getFullYear()}-${m4}-${day}`;
1913
1974
  }
1914
1975
  function splitKey(key) {
1915
- const [y4, m4, d4] = key.split("-").map(Number);
1916
- return [y4, m4 - 1, d4];
1976
+ const [y5, m4, d5] = key.split("-").map(Number);
1977
+ return [y5, m4 - 1, d5];
1917
1978
  }
1918
1979
  function keyAt(ms) {
1919
1980
  return new Date(ms).toISOString().slice(0, 10);
1920
1981
  }
1921
1982
  function addDays(key, days) {
1922
- const [y4, m4, d4] = splitKey(key);
1923
- return keyAt(Date.UTC(y4, m4, d4 + days));
1983
+ const [y5, m4, d5] = splitKey(key);
1984
+ return keyAt(Date.UTC(y5, m4, d5 + days));
1924
1985
  }
1925
1986
  function addMonths(key, months) {
1926
- const [y4, m4, d4] = splitKey(key);
1927
- const lastDay = new Date(Date.UTC(y4, m4 + months + 1, 0)).getUTCDate();
1928
- return keyAt(Date.UTC(y4, m4 + months, Math.min(d4, lastDay)));
1987
+ const [y5, m4, d5] = splitKey(key);
1988
+ const lastDay = new Date(Date.UTC(y5, m4 + months + 1, 0)).getUTCDate();
1989
+ return keyAt(Date.UTC(y5, m4 + months, Math.min(d5, lastDay)));
1929
1990
  }
1930
1991
  function daysFromToday(value, now = /* @__PURE__ */ new Date()) {
1931
1992
  if (typeof value === "string" && /^\d{4}-\d{2}-\d{2}$/.test(value)) {
1932
- const [y4, m4, d4] = splitKey(value);
1993
+ const [y5, m4, d5] = splitKey(value);
1933
1994
  const [ty, tm, td] = splitKey(dayKey(now));
1934
- return Math.round((Date.UTC(y4, m4, d4) - Date.UTC(ty, tm, td)) / 864e5);
1995
+ return Math.round((Date.UTC(y5, m4, d5) - Date.UTC(ty, tm, td)) / 864e5);
1935
1996
  }
1936
1997
  const target = parseDate(value);
1937
1998
  if (!target) return null;
@@ -1957,9 +2018,9 @@ function formatDuration(minutes) {
1957
2018
  return `${h5} h ${m4} min`;
1958
2019
  }
1959
2020
  function relativeTime(value, now = /* @__PURE__ */ new Date()) {
1960
- const d4 = parseDate(value);
1961
- if (!d4) return "";
1962
- const seconds = Math.round((now.getTime() - d4.getTime()) / 1e3);
2021
+ const d5 = parseDate(value);
2022
+ if (!d5) return "";
2023
+ const seconds = Math.round((now.getTime() - d5.getTime()) / 1e3);
1963
2024
  if (seconds < 60) return "just now";
1964
2025
  const minutes = Math.round(seconds / 60);
1965
2026
  if (minutes < 60) return `${minutes} min ago`;
@@ -1967,7 +2028,18 @@ function relativeTime(value, now = /* @__PURE__ */ new Date()) {
1967
2028
  if (hours < 48) return `${hours} h ago`;
1968
2029
  const days = Math.round(hours / 24);
1969
2030
  if (days <= 30) return `${days} days ago`;
1970
- return dayKey(d4).slice(0, 4) === dayKey(now).slice(0, 4) ? formatShortDay(value) : formatted(value, "shortDayYear");
2031
+ return dayKey(d5).slice(0, 4) === dayKey(now).slice(0, 4) ? formatShortDay(value) : formatted(value, "shortDayYear");
2032
+ }
2033
+ function durationBetween(start, end) {
2034
+ const from = parseDate(start);
2035
+ const to = parseDate(end);
2036
+ if (!from || !to || to < from) return "";
2037
+ const seconds = Math.round((to.getTime() - from.getTime()) / 1e3);
2038
+ return seconds < 60 ? `${seconds} s` : formatDuration(Math.round(seconds / 60));
2039
+ }
2040
+ function formatClock(seconds) {
2041
+ const whole = Math.max(0, Math.floor(seconds));
2042
+ return `${Math.floor(whole / 60)}:${String(whole % 60).padStart(2, "0")}`;
1971
2043
  }
1972
2044
  function firstLine(text) {
1973
2045
  const line = String(text).split("\n").find((l5) => l5.trim() !== "") ?? "";
@@ -2167,7 +2239,7 @@ function renderMarkdown(value) {
2167
2239
  function anchor(href, label) {
2168
2240
  return el("a", { href: /^https?:\/\//i.test(href) ? href : "#", target: "_blank", rel: "noopener noreferrer" }, label);
2169
2241
  }
2170
- function renderMarkdownBlocks(value) {
2242
+ function renderMarkdownBlocks(value, options = {}) {
2171
2243
  const out = [];
2172
2244
  const lines = String(value).replace(/\r\n/g, "\n").split("\n");
2173
2245
  let i4 = 0;
@@ -2178,7 +2250,16 @@ function renderMarkdownBlocks(value) {
2178
2250
  i4++;
2179
2251
  while (i4 < lines.length && !/^\s*```/.test(lines[i4])) code.push(lines[i4++]);
2180
2252
  i4++;
2181
- out.push(el("pre", null, code.join("\n")));
2253
+ const text = code.join("\n");
2254
+ const { onCopy } = options;
2255
+ out.push(
2256
+ onCopy ? el(
2257
+ "div",
2258
+ { class: "code-block" },
2259
+ el("pre", null, text),
2260
+ el("button", { type: "button", class: "code-block__copy", title: "Copy to the clipboard", onClick: () => onCopy(text) }, "Copy")
2261
+ ) : el("pre", null, text)
2262
+ );
2182
2263
  continue;
2183
2264
  }
2184
2265
  if (line.trim() === "") {
@@ -2188,7 +2269,7 @@ function renderMarkdownBlocks(value) {
2188
2269
  if (/^\s*>/.test(line)) {
2189
2270
  const quoted = [];
2190
2271
  while (i4 < lines.length && /^\s*>/.test(lines[i4])) quoted.push(lines[i4++].replace(/^\s*>\s?/, ""));
2191
- out.push(el("blockquote", null, renderMarkdownBlocks(quoted.join("\n"))));
2272
+ out.push(el("blockquote", null, renderMarkdownBlocks(quoted.join("\n"), options)));
2192
2273
  continue;
2193
2274
  }
2194
2275
  if (/^\s*(?:-{3,}|\*{3,}|_{3,})\s*$/.test(line)) {
@@ -3797,11 +3878,7 @@ function runStatus(run, now, handlers2) {
3797
3878
  );
3798
3879
  }
3799
3880
  function runDuration(run) {
3800
- const from = parseDate(run.startedAt);
3801
- const to = parseDate(run.endedAt);
3802
- if (!from || !to || to < from) return "";
3803
- const seconds = Math.round((to.getTime() - from.getTime()) / 1e3);
3804
- return seconds < 60 ? `${seconds} s` : formatDuration(Math.round(seconds / 60));
3881
+ return durationBetween(run.startedAt, run.endedAt);
3805
3882
  }
3806
3883
  function runConversation(run, busy, answering, enabled, ui2, handlers2, inputRef) {
3807
3884
  const turns = run.turns.length ? el(
@@ -3957,8 +4034,9 @@ function RowPanel({ row, state: state2, ui: ui2, handlers: handlers2 }) {
3957
4034
  ) : el("p", { class: "flyout__empty" }, "Nothing noted yet. A note goes to the morning agent as free text."),
3958
4035
  k(NoteForm, { row, title: facts.title, ui: ui2, handlers: handlers2, inputRef: noteInput })
3959
4036
  ),
3960
- enabled ? renderAssistantSection(row, facts, state2, entry, ui2, handlers2, askInput) : entry.turns.length ? renderAssistantHistory(state2, entry) : null
3961
- )
4037
+ entry.turns.length ? renderAssistantSection(state2, entry, enabled, handlers2) : null
4038
+ ),
4039
+ enabled ? renderAssistantFooter(facts, state2, entry, ui2, handlers2, askInput, row) : null
3962
4040
  ];
3963
4041
  }
3964
4042
  function describeRow(row) {
@@ -4059,21 +4137,27 @@ function NoteForm({
4059
4137
  el("button", { type: "submit", class: "button button--primary flyout__submit" }, "Save")
4060
4138
  );
4061
4139
  }
4062
- function renderAssistantSection(row, facts, state2, entry, ui2, handlers2, inputRef) {
4063
- const source = "source" in row ? row.source : "github";
4064
- const actions = (state2.assistant?.quickActions ?? []).filter(
4065
- (action) => action.sources === null || action.sources.includes(source)
4066
- );
4140
+ function renderAssistantSection(state2, entry, enabled, handlers2) {
4067
4141
  const label = new Map((state2.assistant?.quickActions ?? []).map((action) => [action.id, action]));
4068
4142
  return el(
4069
4143
  "section",
4070
4144
  { class: "flyout__section assistant", "data-running": String(entry.running) },
4071
4145
  el("h3", { class: "flyout__label" }, "Assistant"),
4072
- entry.turns.length ? el(
4146
+ el(
4073
4147
  "div",
4074
4148
  { class: "assistant__turns" },
4075
- entry.turns.map((turn) => renderAssistantTurn(turn, label))
4076
- ) : null,
4149
+ entry.turns.map((turn) => renderAssistantTurn(turn, label, handlers2))
4150
+ ),
4151
+ enabled ? null : el("p", { class: "flyout__empty" }, "The assistant is switched off (DAILY_FOCUS_ASSISTANT), so nothing more can be asked here.")
4152
+ );
4153
+ }
4154
+ function renderAssistantFooter(facts, state2, entry, ui2, handlers2, inputRef, row) {
4155
+ const actions = (state2.assistant?.quickActions ?? []).filter(
4156
+ (action) => action.sources === null || action.sources.includes(facts.source)
4157
+ );
4158
+ return el(
4159
+ "div",
4160
+ { class: "flyout__footer" },
4077
4161
  entry.running ? null : el(
4078
4162
  "div",
4079
4163
  { class: "assistant__quick" },
@@ -4093,20 +4177,6 @@ function renderAssistantSection(row, facts, state2, entry, ui2, handlers2, input
4093
4177
  k(AssistantComposer, { row, title: facts.title, entry, ui: ui2, handlers: handlers2, inputRef })
4094
4178
  );
4095
4179
  }
4096
- function renderAssistantHistory(state2, entry) {
4097
- const label = new Map((state2.assistant?.quickActions ?? []).map((action) => [action.id, action]));
4098
- return el(
4099
- "section",
4100
- { class: "flyout__section assistant", "data-running": "false" },
4101
- el("h3", { class: "flyout__label" }, "Assistant"),
4102
- el(
4103
- "div",
4104
- { class: "assistant__turns" },
4105
- entry.turns.map((turn) => renderAssistantTurn(turn, label))
4106
- ),
4107
- el("p", { class: "flyout__empty" }, "The assistant is switched off (DAILY_FOCUS_ASSISTANT), so nothing more can be asked here.")
4108
- );
4109
- }
4110
4180
  function AssistantComposer({
4111
4181
  row,
4112
4182
  title,
@@ -4135,7 +4205,7 @@ function AssistantComposer({
4135
4205
  rows: 2,
4136
4206
  ref: inputRef,
4137
4207
  value: ui2.assistantDraft.value,
4138
- placeholder: entry.turns.length ? "Follow up\u2026" : "What do you need help with?",
4208
+ placeholder: entry.turns.length ? "Follow up\u2026" : "Ask the assistant about this\u2026",
4139
4209
  "aria-label": `Ask the assistant about ${title}`,
4140
4210
  disabled: entry.running,
4141
4211
  onInput: (event) => {
@@ -4151,19 +4221,83 @@ function AssistantComposer({
4151
4221
  entry.running ? el("button", { type: "button", class: "button flyout__submit", onClick: () => handlers2.stopAssistant(row.id) }, "Stop") : el("button", { type: "submit", class: "button button--primary flyout__submit" }, "Send")
4152
4222
  );
4153
4223
  }
4154
- function renderAssistantTurn(turn, label) {
4224
+ function renderAssistantTurn(turn, label, handlers2) {
4155
4225
  const action = turn.action ? label.get(turn.action) : null;
4156
4226
  const extra = action && turn.request.startsWith(action.request) ? turn.request.slice(action.request.length).trim() : null;
4157
4227
  const asked = action ? [el("strong", null, action.label), extra ? ` \u2014 ${extra}` : ""] : [turn.request];
4158
- const status = turn.status === "running" ? el("p", { class: "assistant__status" }, "Working\u2026") : turn.status === "failed" ? el("p", { class: "assistant__status assistant__status--bad" }, `Failed: ${turn.error}`) : turn.status === "aborted" ? el("p", { class: "assistant__status" }, `Stopped: ${turn.error}`) : null;
4228
+ const { body, created } = turn.status === "done" ? splitCreated(turn.reply) : { body: turn.reply, created: [] };
4229
+ const status = turn.status === "failed" ? el("p", { class: "assistant__status assistant__status--bad" }, `Failed: ${turn.error}`) : turn.status === "aborted" ? el("p", { class: "assistant__status" }, `Stopped: ${turn.error}`) : null;
4159
4230
  return el(
4160
4231
  "div",
4161
- { class: "assistant__turn", "data-status": turn.status },
4232
+ { key: turn.id, class: "assistant__turn", "data-status": turn.status },
4162
4233
  el("p", { class: "assistant__request" }, asked),
4163
- turn.reply ? el("div", { class: "assistant__reply" }, renderMarkdownBlocks(turn.reply)) : null,
4164
- status
4234
+ renderWork(turn),
4235
+ body ? el("div", { class: "assistant__reply" }, renderMarkdownBlocks(body, { onCopy: (text) => handlers2.copy(text) })) : null,
4236
+ status,
4237
+ turn.status === "done" && (body || created.length) ? el(
4238
+ "div",
4239
+ { class: "assistant__after" },
4240
+ created.map(
4241
+ (link) => el("a", { class: "button", href: link.url, target: "_blank", rel: "noopener noreferrer", title: link.url }, `${link.label} \u2197`)
4242
+ ),
4243
+ body ? el("button", { type: "button", class: "button", onClick: () => handlers2.copy(body) }, "Copy reply") : null
4244
+ ) : null
4165
4245
  );
4166
4246
  }
4247
+ function renderWork(turn) {
4248
+ const steps = turn.steps ?? [];
4249
+ if (turn.status === "running") {
4250
+ return el(
4251
+ "div",
4252
+ { class: "assistant__work" },
4253
+ el("p", { class: "assistant__status assistant__status--working" }, "Working \xB7 ", k(Elapsed, { since: turn.startedAt })),
4254
+ steps.length ? renderSteps(steps) : null
4255
+ );
4256
+ }
4257
+ const took = durationBetween(turn.startedAt, turn.endedAt);
4258
+ if (!steps.length) return took ? el("p", { class: "assistant__took" }, `Answered in ${took}`) : null;
4259
+ const failed = steps.filter((step2) => step2.state === "failed").length;
4260
+ const summary = [
4261
+ took ? `${turn.status === "done" ? "Worked" : "Ran"} for ${took}` : null,
4262
+ `${steps.length} ${steps.length === 1 ? "step" : "steps"}`,
4263
+ failed ? `${failed} failed` : null
4264
+ ].filter(Boolean).join(" \xB7 ");
4265
+ return el("details", { class: "assistant__work", open: turn.status === "failed" }, el("summary", null, summary), renderSteps(steps));
4266
+ }
4267
+ function renderSteps(steps) {
4268
+ return el(
4269
+ "ol",
4270
+ { class: "assistant__steps" },
4271
+ steps.map(
4272
+ (step2, index) => step2.kind === "message" ? el("li", { key: index, class: "assistant__step assistant__step--message" }, renderMarkdown(step2.text.trim())) : el(
4273
+ "li",
4274
+ { key: index, class: "assistant__step", "data-state": step2.state },
4275
+ el("span", { class: "assistant__step-text" }, step2.text),
4276
+ step2.detail ? el("code", { class: "assistant__step-detail", title: step2.detail }, step2.detail) : null
4277
+ )
4278
+ )
4279
+ );
4280
+ }
4281
+ function Elapsed({ since }) {
4282
+ const [now, setNow] = d2(() => Date.now());
4283
+ h2(() => {
4284
+ const timer = setInterval(() => setNow(Date.now()), 1e3);
4285
+ return () => clearInterval(timer);
4286
+ }, []);
4287
+ const start = parseDate(since)?.getTime() ?? now;
4288
+ return el("span", { class: "assistant__clock" }, formatClock((now - start) / 1e3));
4289
+ }
4290
+ var CREATED = /^\s*(?:[-*]\s+)?Created:\s*\[([^\]]+)\]\((https?:\/\/[^\s)]+)\)\s*\.?\s*$/i;
4291
+ function splitCreated(reply) {
4292
+ const created = [];
4293
+ const kept = [];
4294
+ for (const line of reply.split("\n")) {
4295
+ const match = CREATED.exec(line);
4296
+ if (match) created.push({ label: match[1].trim(), url: match[2] });
4297
+ else kept.push(line);
4298
+ }
4299
+ return { body: created.length ? kept.join("\n").trim() : reply, created };
4300
+ }
4167
4301
 
4168
4302
  // client/settings.ts
4169
4303
  var VIEW_NAME = {
package/public/style.css CHANGED
@@ -1735,9 +1735,9 @@ a.button {
1735
1735
  /*
1736
1736
  * ---------- the assistant ----------
1737
1737
  *
1738
- * A section of the item panel, so the transcript scrolls with the panel's body
1739
- * and the field sits at its foot. It used to be a box under the card with its own
1740
- * scroll region; now the panel is the box.
1738
+ * A section of the item panel, so the transcript scrolls with the panel's body;
1739
+ * the field is pinned in the panel's foot. It used to be a box under the card
1740
+ * with its own scroll region; now the panel is the box.
1741
1741
  */
1742
1742
 
1743
1743
  .assistant__turns {
@@ -1801,8 +1801,29 @@ a.button {
1801
1801
  border: 0;
1802
1802
  border-top: 1px solid var(--hairline);
1803
1803
  }
1804
- .assistant__turn[data-status='running'] .assistant__reply {
1805
- border-left-style: dashed;
1804
+ /* A fenced block in an answer is usually meant for pasting elsewhere, so it carries a Copy. */
1805
+ .code-block {
1806
+ position: relative;
1807
+ }
1808
+ .code-block pre {
1809
+ padding-right: 52px;
1810
+ }
1811
+ .code-block__copy {
1812
+ position: absolute;
1813
+ top: 5px;
1814
+ right: 5px;
1815
+ padding: 1px 6px;
1816
+ border: 1px solid var(--border);
1817
+ border-radius: var(--radius-sm);
1818
+ background: var(--surface);
1819
+ color: var(--ink-3);
1820
+ font: inherit;
1821
+ font-size: 11px;
1822
+ cursor: pointer;
1823
+ }
1824
+ .code-block__copy:hover {
1825
+ color: var(--ink-1);
1826
+ border-color: var(--ink-3);
1806
1827
  }
1807
1828
 
1808
1829
  .assistant__status {
@@ -1813,16 +1834,25 @@ a.button {
1813
1834
  .assistant__status--bad {
1814
1835
  color: var(--critical);
1815
1836
  }
1816
- .assistant[data-running='true'] .assistant__status:not(.assistant__status--bad)::after {
1837
+ .assistant__clock {
1838
+ font-variant-numeric: tabular-nums;
1839
+ }
1840
+
1841
+ /* A spinner, for the turn while it works and for each call it is waiting on. */
1842
+ .assistant__status--working::after,
1843
+ .assistant__step[data-state='running']::before {
1817
1844
  content: '';
1818
1845
  display: inline-block;
1819
- width: 8px;
1820
- height: 8px;
1821
- margin-left: 6px;
1846
+ box-sizing: border-box;
1847
+ width: 9px;
1848
+ height: 9px;
1822
1849
  border-radius: 50%;
1823
1850
  border: 1.5px solid var(--accent);
1824
1851
  border-top-color: transparent;
1825
1852
  animation: assistant-spin 0.9s linear infinite;
1853
+ }
1854
+ .assistant__status--working::after {
1855
+ margin-left: 6px;
1826
1856
  vertical-align: -1px;
1827
1857
  }
1828
1858
  @keyframes assistant-spin {
@@ -1831,16 +1861,105 @@ a.button {
1831
1861
  }
1832
1862
  }
1833
1863
  @media (prefers-reduced-motion: reduce) {
1834
- .assistant[data-running='true'] .assistant__status::after {
1864
+ .assistant__status--working::after,
1865
+ .assistant__step[data-state='running']::before {
1835
1866
  animation: none;
1836
1867
  }
1837
1868
  }
1838
1869
 
1870
+ /*
1871
+ * The work: each call as one muted line under the request, the newest at the
1872
+ * foot. Folded once the turn is over, under a line that says what it took.
1873
+ */
1874
+ .assistant__work {
1875
+ margin-top: 4px;
1876
+ }
1877
+ .assistant__work > summary {
1878
+ font-size: 12px;
1879
+ color: var(--ink-3);
1880
+ cursor: pointer;
1881
+ }
1882
+ .assistant__work > summary:hover {
1883
+ color: var(--ink-2);
1884
+ }
1885
+ .assistant__took {
1886
+ margin: 4px 0 0;
1887
+ font-size: 12px;
1888
+ color: var(--ink-3);
1889
+ }
1890
+ .assistant__steps {
1891
+ margin: 5px 0 0;
1892
+ padding: 0 0 0 10px;
1893
+ border-left: 2px solid var(--hairline);
1894
+ list-style: none;
1895
+ font-size: 12px;
1896
+ line-height: 1.45;
1897
+ color: var(--ink-2);
1898
+ }
1899
+ .assistant__step {
1900
+ display: flex;
1901
+ align-items: baseline;
1902
+ gap: 6px;
1903
+ min-width: 0;
1904
+ }
1905
+ .assistant__step + .assistant__step {
1906
+ margin-top: 3px;
1907
+ }
1908
+ .assistant__step::before {
1909
+ flex: none;
1910
+ width: 9px;
1911
+ font-size: 11px;
1912
+ text-align: center;
1913
+ }
1914
+ .assistant__step[data-state='done']::before {
1915
+ content: '✓';
1916
+ color: var(--good-ink);
1917
+ }
1918
+ .assistant__step[data-state='failed']::before {
1919
+ content: '✕';
1920
+ color: var(--critical);
1921
+ }
1922
+ .assistant__step[data-state='running']::before {
1923
+ align-self: center;
1924
+ }
1925
+ .assistant__step-text {
1926
+ flex: none;
1927
+ max-width: 60%;
1928
+ overflow: hidden;
1929
+ text-overflow: ellipsis;
1930
+ white-space: nowrap;
1931
+ }
1932
+ .assistant__step-detail {
1933
+ min-width: 0;
1934
+ overflow: hidden;
1935
+ text-overflow: ellipsis;
1936
+ white-space: nowrap;
1937
+ font-size: 11.5px;
1938
+ color: var(--ink-3);
1939
+ }
1940
+ /* What it said on the way: its own words, set apart from the calls by having no mark. */
1941
+ .assistant__step--message {
1942
+ display: block;
1943
+ padding-left: 15px;
1944
+ font-style: italic;
1945
+ color: var(--ink-3);
1946
+ white-space: pre-line;
1947
+ overflow-wrap: anywhere;
1948
+ }
1949
+
1950
+ /* What can be done with a finished answer, without leaving the panel. */
1951
+ .assistant__after {
1952
+ display: flex;
1953
+ flex-wrap: wrap;
1954
+ gap: 6px;
1955
+ margin-top: 6px;
1956
+ }
1957
+
1839
1958
  .assistant__quick {
1840
1959
  display: flex;
1841
1960
  flex-wrap: wrap;
1842
1961
  gap: 6px;
1843
- margin-bottom: 14px;
1962
+ margin-bottom: 10px;
1844
1963
  }
1845
1964
 
1846
1965
  .assistant__form {
@@ -1959,6 +2078,12 @@ body[data-flyout='open'] .layout__side {
1959
2078
  overflow-y: auto;
1960
2079
  padding: 16px 16px 20px;
1961
2080
  }
2081
+ /* The assistant's field and its canned asks, pinned under the body that scrolls. */
2082
+ .flyout__footer {
2083
+ flex: none;
2084
+ padding: 12px 16px 14px;
2085
+ border-top: 1px solid var(--hairline);
2086
+ }
1962
2087
  .flyout__detail {
1963
2088
  margin: 0 0 22px;
1964
2089
  font-size: 13px;