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 +7 -3
- package/dist/{chunk-SX5FPS3A.js → chunk-KL6RU4XT.js} +141 -15
- package/dist/cli.js +2 -2
- package/dist/{demo-6Q22AAC7.js → demo-TIIXODJG.js} +36 -4
- package/dist/{server-PIYOIUKJ.js → server-WT4CQ3VX.js} +1 -1
- package/package.json +1 -1
- package/prompts/assistant.md +7 -2
- package/public/app.js +249 -115
- package/public/style.css +136 -11
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
|
|
134
|
-
|
|
135
|
-
|
|
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
|
|
258
|
-
|
|
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
|
-
|
|
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
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
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
|
|
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-
|
|
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-
|
|
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-
|
|
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
|
-
|
|
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);
|
package/package.json
CHANGED
package/prompts/assistant.md
CHANGED
|
@@ -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
|
-
|
|
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
|
|
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,
|
|
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 ==
|
|
74
|
-
return u4.__e =
|
|
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,
|
|
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))),
|
|
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 !==
|
|
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(
|
|
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(
|
|
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,
|
|
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 ((
|
|
191
|
-
u4 =
|
|
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[(
|
|
202
|
-
for (s4 in m4)
|
|
203
|
-
for (s4 in k3)
|
|
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
|
|
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
|
-
|
|
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
|
|
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 =
|
|
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 ===
|
|
603
|
-
this.g =
|
|
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
|
|
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 (
|
|
769
|
-
var n3 =
|
|
770
|
-
|
|
807
|
+
if (d4) {
|
|
808
|
+
var n3 = d4;
|
|
809
|
+
d4 = void 0;
|
|
771
810
|
n3();
|
|
772
811
|
}
|
|
773
|
-
|
|
812
|
+
d4 = i4 && i4.S();
|
|
774
813
|
}
|
|
775
|
-
function
|
|
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
|
-
|
|
806
|
-
Object.defineProperties(l3.prototype, { constructor: { configurable: true, value: void 0 }, type: { configurable: true, value:
|
|
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 =
|
|
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
|
|
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 =
|
|
1121
|
+
var state = y3(null);
|
|
1083
1122
|
function createUi(initial = {}) {
|
|
1084
1123
|
return {
|
|
1085
|
-
selectedId:
|
|
1086
|
-
clickedId:
|
|
1087
|
-
pending:
|
|
1088
|
-
menuFor:
|
|
1089
|
-
statusFor:
|
|
1090
|
-
detailFor:
|
|
1091
|
-
noteDraft:
|
|
1092
|
-
assistantDraft:
|
|
1093
|
-
focusField:
|
|
1094
|
-
openDrawers:
|
|
1095
|
-
ticketMode:
|
|
1096
|
-
agentReportSeen:
|
|
1097
|
-
messageOpen:
|
|
1098
|
-
view:
|
|
1099
|
-
theme:
|
|
1100
|
-
toast:
|
|
1101
|
-
connectionError:
|
|
1102
|
-
settingsReturn:
|
|
1103
|
-
settings:
|
|
1104
|
-
settingsDraft:
|
|
1105
|
-
settingsError:
|
|
1106
|
-
settingsSaving:
|
|
1107
|
-
texts:
|
|
1108
|
-
calendars:
|
|
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
|
|
1870
|
-
return
|
|
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
|
|
1877
|
-
return Number.isNaN(
|
|
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(
|
|
1965
|
+
function dayKey(d5) {
|
|
1905
1966
|
if (dayKeyFmt) {
|
|
1906
|
-
const parts = dayKeyFmt.formatToParts(
|
|
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(
|
|
1911
|
-
const day = String(
|
|
1912
|
-
return `${
|
|
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 [
|
|
1916
|
-
return [
|
|
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 [
|
|
1923
|
-
return keyAt(Date.UTC(
|
|
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 [
|
|
1927
|
-
const lastDay = new Date(Date.UTC(
|
|
1928
|
-
return keyAt(Date.UTC(
|
|
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 [
|
|
1993
|
+
const [y5, m4, d5] = splitKey(value);
|
|
1933
1994
|
const [ty, tm, td] = splitKey(dayKey(now));
|
|
1934
|
-
return Math.round((Date.UTC(
|
|
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
|
|
1961
|
-
if (!
|
|
1962
|
-
const seconds = Math.round((now.getTime() -
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
4146
|
+
el(
|
|
4073
4147
|
"div",
|
|
4074
4148
|
{ class: "assistant__turns" },
|
|
4075
|
-
entry.turns.map((turn) => renderAssistantTurn(turn, label))
|
|
4076
|
-
)
|
|
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" : "
|
|
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
|
|
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
|
-
|
|
4164
|
-
|
|
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
|
-
*
|
|
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
|
-
|
|
1805
|
-
|
|
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
|
-
.
|
|
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
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
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
|
-
.
|
|
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:
|
|
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;
|