@tangle-network/agent-app 0.43.41 → 0.43.43
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assistant/index.d.ts +2 -1
- package/dist/assistant/index.js +3 -2
- package/dist/assistant/index.js.map +1 -1
- package/dist/chat-routes/index.d.ts +17 -3
- package/dist/chat-routes/index.js +91 -6
- package/dist/chat-routes/index.js.map +1 -1
- package/dist/chat-store/index.d.ts +4 -3
- package/dist/chat-store/index.js +5 -1
- package/dist/chat-store/index.js.map +1 -1
- package/dist/{chunk-Y4QHNQ75.js → chunk-6KVH5SP5.js} +110 -6
- package/dist/chunk-6KVH5SP5.js.map +1 -0
- package/dist/{chunk-XYWCGFII.js → chunk-B5JD3DXD.js} +48 -2
- package/dist/chunk-B5JD3DXD.js.map +1 -0
- package/dist/{chunk-I2R2XT4M.js → chunk-I2ATYB7R.js} +18 -2
- package/dist/chunk-I2ATYB7R.js.map +1 -0
- package/dist/{chunk-KM766NN3.js → chunk-PEUBCJXF.js} +1772 -1224
- package/dist/chunk-PEUBCJXF.js.map +1 -0
- package/dist/{chunk-UMSOSUEU.js → chunk-QYOD3K56.js} +2 -2
- package/dist/chunk-QYOD3K56.js.map +1 -0
- package/dist/chunk-SIXYZ2FB.js +101 -0
- package/dist/chunk-SIXYZ2FB.js.map +1 -0
- package/dist/{chunk-4TXDD6P2.js → chunk-XAWFPMAR.js} +38 -3
- package/dist/chunk-XAWFPMAR.js.map +1 -0
- package/dist/chunk-ZU5GNSOJ.js +920 -0
- package/dist/chunk-ZU5GNSOJ.js.map +1 -0
- package/dist/{contract-DYbTzEDf.d.ts → contract-KfqJh_au.d.ts} +23 -2
- package/dist/design-canvas-react/index.js +4 -4
- package/dist/durable-chat/index.d.ts +419 -0
- package/dist/durable-chat/index.js +59 -0
- package/dist/durable-chat/index.js.map +1 -0
- package/dist/index.d.ts +5 -3
- package/dist/index.js +124 -46
- package/dist/interactions/index.d.ts +58 -3
- package/dist/interactions/index.js +7 -3
- package/dist/object-store/index.d.ts +6 -4
- package/dist/object-store/index.js +1 -1
- package/dist/{parts-BcbitSNp.d.ts → parts-DjX0RRTS.d.ts} +9 -3
- package/dist/plans/index.d.ts +69 -0
- package/dist/plans/index.js +21 -0
- package/dist/plans/index.js.map +1 -0
- package/dist/stream/index.js +3 -1
- package/dist/teams/index.js +9 -9
- package/dist/teams/invitations-api.js +3 -3
- package/dist/web-react/index.d.ts +252 -93
- package/dist/web-react/index.js +32 -3
- package/package.json +11 -1
- package/dist/chunk-4TXDD6P2.js.map +0 -1
- package/dist/chunk-I2R2XT4M.js.map +0 -1
- package/dist/chunk-KM766NN3.js.map +0 -1
- package/dist/chunk-UMSOSUEU.js.map +0 -1
- package/dist/chunk-XYWCGFII.js.map +0 -1
- package/dist/chunk-Y4QHNQ75.js.map +0 -1
|
@@ -6,16 +6,22 @@ import {
|
|
|
6
6
|
fieldAcceptsFreeText,
|
|
7
7
|
interactionFromWireRequest,
|
|
8
8
|
isTerminalInteractionStatus,
|
|
9
|
+
parseInteractionCancel,
|
|
9
10
|
parseInteractionRequest,
|
|
11
|
+
persistedPartToInteraction,
|
|
10
12
|
questionInteractionContentSignature
|
|
11
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-XAWFPMAR.js";
|
|
14
|
+
import {
|
|
15
|
+
parsePlanSubmittedEvent,
|
|
16
|
+
persistedPartToPlan
|
|
17
|
+
} from "./chunk-SIXYZ2FB.js";
|
|
12
18
|
import {
|
|
13
19
|
snapHarnessToModel,
|
|
14
20
|
snapModelToHarness
|
|
15
21
|
} from "./chunk-E7QYOOON.js";
|
|
16
22
|
|
|
17
23
|
// src/web-react/index.tsx
|
|
18
|
-
import { useEffect as
|
|
24
|
+
import { useEffect as useEffect8, useMemo as useMemo7, useRef as useRef8, useState as useState12, memo } from "react";
|
|
19
25
|
|
|
20
26
|
// src/web-react/smooth-text.ts
|
|
21
27
|
import { useEffect, useRef, useState } from "react";
|
|
@@ -372,1003 +378,1484 @@ function BrandMark({ size = 24, className }) {
|
|
|
372
378
|
return /* @__PURE__ */ jsx3(Suspense, { fallback: /* @__PURE__ */ jsx3(MarkSpacer, { size, className }), children: /* @__PURE__ */ jsx3(LazyKnot, { size, className }) });
|
|
373
379
|
}
|
|
374
380
|
|
|
375
|
-
// src/web-react/
|
|
376
|
-
|
|
377
|
-
let receivedContent = false;
|
|
378
|
-
let turnId;
|
|
379
|
-
if (!line.trim()) return { receivedContent };
|
|
380
|
-
let parsed;
|
|
381
|
-
try {
|
|
382
|
-
parsed = JSON.parse(line);
|
|
383
|
-
} catch {
|
|
384
|
-
return { receivedContent };
|
|
385
|
-
}
|
|
386
|
-
if (parsed.kind === "tool_result") {
|
|
387
|
-
cb.onToolResult?.({
|
|
388
|
-
toolCallId: parsed.toolCallId,
|
|
389
|
-
toolName: parsed.toolName,
|
|
390
|
-
label: parsed.label,
|
|
391
|
-
outcome: parsed.outcome ?? parsed.result
|
|
392
|
-
});
|
|
393
|
-
return { receivedContent: true };
|
|
394
|
-
}
|
|
395
|
-
const evt = parsed.kind === "event" ? parsed.event : parsed;
|
|
396
|
-
if (!evt || typeof evt !== "object") return { receivedContent };
|
|
397
|
-
switch (evt.type) {
|
|
398
|
-
case "turn":
|
|
399
|
-
if (typeof evt.turnId === "string") turnId = evt.turnId;
|
|
400
|
-
break;
|
|
401
|
-
case "text":
|
|
402
|
-
if (typeof evt.text === "string") {
|
|
403
|
-
cb.onText?.(evt.text);
|
|
404
|
-
receivedContent = true;
|
|
405
|
-
}
|
|
406
|
-
break;
|
|
407
|
-
case "reasoning":
|
|
408
|
-
if (typeof evt.text === "string") {
|
|
409
|
-
cb.onReasoning?.(evt.text);
|
|
410
|
-
receivedContent = true;
|
|
411
|
-
}
|
|
412
|
-
break;
|
|
413
|
-
case "tool_call": {
|
|
414
|
-
const call = evt.call ?? evt;
|
|
415
|
-
cb.onToolCall?.({
|
|
416
|
-
toolCallId: call.toolCallId ?? call.id,
|
|
417
|
-
toolName: String(call.toolName ?? call.name ?? "unknown"),
|
|
418
|
-
args: call.args ?? {}
|
|
419
|
-
});
|
|
420
|
-
receivedContent = true;
|
|
421
|
-
break;
|
|
422
|
-
}
|
|
423
|
-
case "tool_result":
|
|
424
|
-
cb.onToolResult?.({
|
|
425
|
-
toolCallId: evt.toolCallId,
|
|
426
|
-
toolName: evt.toolName,
|
|
427
|
-
label: evt.label,
|
|
428
|
-
outcome: evt.outcome ?? evt.result
|
|
429
|
-
});
|
|
430
|
-
receivedContent = true;
|
|
431
|
-
break;
|
|
432
|
-
case "usage": {
|
|
433
|
-
const u = evt.usage;
|
|
434
|
-
if (u) cb.onUsage?.({ promptTokens: u.promptTokens ?? 0, completionTokens: u.completionTokens ?? 0 });
|
|
435
|
-
break;
|
|
436
|
-
}
|
|
437
|
-
case "metadata":
|
|
438
|
-
cb.onMetadata?.(evt.data ?? {});
|
|
439
|
-
break;
|
|
440
|
-
case "interaction": {
|
|
441
|
-
const parsed2 = parseInteractionRequest(evt.data);
|
|
442
|
-
if (parsed2.succeeded) {
|
|
443
|
-
cb.onInteraction?.(interactionFromWireRequest(parsed2.value));
|
|
444
|
-
receivedContent = true;
|
|
445
|
-
} else {
|
|
446
|
-
console.error("[chat-stream] dropping malformed interaction line:", parsed2.error);
|
|
447
|
-
}
|
|
448
|
-
break;
|
|
449
|
-
}
|
|
450
|
-
case "error": {
|
|
451
|
-
const data = evt.data;
|
|
452
|
-
const message = String(data?.message ?? evt.details ?? evt.error ?? "Unknown stream error");
|
|
453
|
-
if (cb.onErrorEvent) {
|
|
454
|
-
cb.onErrorEvent(message);
|
|
455
|
-
} else {
|
|
456
|
-
console.error("[chat-stream] unhandled stream error event:", message);
|
|
457
|
-
cb.onText?.(`
|
|
381
|
+
// src/web-react/durable-plan-card.tsx
|
|
382
|
+
import { useEffect as useEffect4, useState as useState4 } from "react";
|
|
458
383
|
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
break;
|
|
466
|
-
}
|
|
467
|
-
return { turnId, receivedContent };
|
|
384
|
+
// src/web-react/interaction-question-card.tsx
|
|
385
|
+
import { useEffect as useEffect3, useMemo as useMemo2, useRef as useRef3, useState as useState3 } from "react";
|
|
386
|
+
|
|
387
|
+
// src/web-react/interaction-card-support.ts
|
|
388
|
+
function interactionStatusLabels(labels) {
|
|
389
|
+
return { cancelled: "Withdrawn", expired: "Expired", ...labels };
|
|
468
390
|
}
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
let receivedContent = false;
|
|
475
|
-
const handle = (line) => {
|
|
476
|
-
const r = dispatchChatStreamLine(line, cb);
|
|
477
|
-
if (r.turnId) {
|
|
478
|
-
turnId = r.turnId;
|
|
479
|
-
cb.onTurnId?.(r.turnId);
|
|
480
|
-
}
|
|
481
|
-
if (r.receivedContent) receivedContent = true;
|
|
391
|
+
function interactionTerminalNotes(noun, extra) {
|
|
392
|
+
return {
|
|
393
|
+
expired: `This ${noun} expired \u2014 send a new message to continue.`,
|
|
394
|
+
cancelled: `The agent withdrew this ${noun}.`,
|
|
395
|
+
...extra
|
|
482
396
|
};
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
397
|
+
}
|
|
398
|
+
function fieldValuesFromAnswers(fields, answers) {
|
|
399
|
+
if (!answers) return {};
|
|
400
|
+
const values = {};
|
|
401
|
+
for (const field of fields) {
|
|
402
|
+
const answer = answers[field.name];
|
|
403
|
+
if (answer === void 0) continue;
|
|
404
|
+
if (field.type === "select") {
|
|
405
|
+
values[field.name] = { selected: Array.isArray(answer) ? [...answer] : [String(answer)] };
|
|
406
|
+
} else if (field.type === "boolean") {
|
|
407
|
+
values[field.name] = { selected: [String(answer)] };
|
|
408
|
+
} else {
|
|
409
|
+
values[field.name] = { text: String(answer) };
|
|
488
410
|
}
|
|
489
|
-
buffer += decoder.decode(value, { stream: true });
|
|
490
|
-
const lines = buffer.split("\n");
|
|
491
|
-
buffer = lines.pop() ?? "";
|
|
492
|
-
for (const line of lines) handle(line);
|
|
493
411
|
}
|
|
494
|
-
return
|
|
412
|
+
return values;
|
|
495
413
|
}
|
|
496
|
-
|
|
497
|
-
const
|
|
498
|
-
if (
|
|
499
|
-
const
|
|
500
|
-
|
|
414
|
+
function fieldAnswer(field, values) {
|
|
415
|
+
const value = values[field.name] ?? {};
|
|
416
|
+
if (field.type === "select") {
|
|
417
|
+
const custom = field.allowCustom === true ? value.custom?.trim() : void 0;
|
|
418
|
+
const chosen = [...value.selected ?? [], ...custom ? [custom] : []];
|
|
419
|
+
if (field.multi !== true && custom) return [custom];
|
|
420
|
+
return chosen.length > 0 ? chosen : null;
|
|
501
421
|
}
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
422
|
+
if (field.type === "number") {
|
|
423
|
+
const parsed = Number(value.text);
|
|
424
|
+
return value.text?.trim() && Number.isFinite(parsed) ? parsed : null;
|
|
425
|
+
}
|
|
426
|
+
if (field.type === "boolean") return value.selected ? value.selected[0] === "true" : null;
|
|
427
|
+
const text = value.text?.trim();
|
|
428
|
+
return text ? text : null;
|
|
429
|
+
}
|
|
430
|
+
function buildAnswerData(fields, values) {
|
|
431
|
+
const data = {};
|
|
432
|
+
for (const field of fields) {
|
|
433
|
+
const answer = fieldAnswer(field, values);
|
|
434
|
+
if (answer === null) {
|
|
435
|
+
if (field.required === false) continue;
|
|
436
|
+
return null;
|
|
508
437
|
}
|
|
509
|
-
|
|
510
|
-
try {
|
|
511
|
-
return await consumeChatStream(res.body, cb);
|
|
512
|
-
} catch (transportErr) {
|
|
513
|
-
if (!turnId || !opts.resume) throw transportErr;
|
|
514
|
-
opts.onResetForResume?.();
|
|
515
|
-
const resumed = await opts.resume(turnId, 0);
|
|
516
|
-
if (!resumed.ok || !resumed.body) throw transportErr;
|
|
517
|
-
return await consumeChatStream(resumed.body, cb);
|
|
438
|
+
data[field.name] = answer;
|
|
518
439
|
}
|
|
440
|
+
return data;
|
|
519
441
|
}
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
import {
|
|
523
|
-
useCallback,
|
|
524
|
-
useEffect as useEffect3,
|
|
525
|
-
useRef as useRef3,
|
|
526
|
-
useState as useState3
|
|
527
|
-
} from "react";
|
|
528
|
-
import { Fragment as Fragment2, jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
529
|
-
function SendGlyph({ className }) {
|
|
530
|
-
return /* @__PURE__ */ jsx4("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: /* @__PURE__ */ jsx4("path", { d: "M22 2 11 13M22 2l-7 20-4-9-9-4 20-7z" }) });
|
|
442
|
+
function isLateAnswerableStatus(status) {
|
|
443
|
+
return status === "expired" || status === "cancelled";
|
|
531
444
|
}
|
|
532
|
-
function
|
|
533
|
-
return
|
|
445
|
+
function hasSecretField(fields) {
|
|
446
|
+
return fields.some((field) => field.type === "secret");
|
|
534
447
|
}
|
|
535
|
-
function
|
|
536
|
-
return
|
|
448
|
+
function optionLabel(field, value) {
|
|
449
|
+
return field.options.find((option) => option.value === value)?.label ?? value;
|
|
537
450
|
}
|
|
538
|
-
function
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
451
|
+
function answerText(field, answer) {
|
|
452
|
+
if (field.type === "select" && Array.isArray(answer)) {
|
|
453
|
+
return answer.map((value) => optionLabel(field, value)).join(", ");
|
|
454
|
+
}
|
|
455
|
+
if (field.type === "boolean") return answer === true ? "Yes" : "No";
|
|
456
|
+
if (field.type === "secret") return "[secret omitted]";
|
|
457
|
+
return String(answer);
|
|
543
458
|
}
|
|
544
|
-
function
|
|
545
|
-
|
|
459
|
+
function lateAnswerMessage(interaction, data) {
|
|
460
|
+
const title = interaction.title.trim() || "the earlier question";
|
|
461
|
+
const body = interaction.body?.trim();
|
|
462
|
+
const answers = interaction.fields.map((field) => {
|
|
463
|
+
const answer = data[field.name];
|
|
464
|
+
if (answer === void 0) return null;
|
|
465
|
+
return { label: field.label.trim(), text: answerText(field, answer).trim() };
|
|
466
|
+
}).filter((item) => !!item && item.text.length > 0);
|
|
467
|
+
const only = answers.length === 1 ? answers[0] : void 0;
|
|
468
|
+
const answerSummary = only ? only.text : answers.map((item) => `${item.label || "Answer"}: ${item.text}`).join("\n");
|
|
469
|
+
return [
|
|
470
|
+
`Regarding your earlier question: "${title}"`,
|
|
471
|
+
body ? `Context: ${body}` : null,
|
|
472
|
+
`My answer: ${answerSummary}`
|
|
473
|
+
].filter((line) => !!line).join("\n");
|
|
546
474
|
}
|
|
547
|
-
|
|
548
|
-
|
|
475
|
+
var INTERACTION_SUBMIT_TIMEOUT_MS = 3e4;
|
|
476
|
+
var INTERACTION_SUBMIT_TIMEOUT_MESSAGE = "Could not reach the agent. Try again.";
|
|
477
|
+
async function responseErrorMessage(res) {
|
|
478
|
+
const text = await res.text().catch(() => "");
|
|
479
|
+
if (text) {
|
|
480
|
+
try {
|
|
481
|
+
const parsed = JSON.parse(text);
|
|
482
|
+
const message = typeof parsed.error === "string" && parsed.error.trim() ? parsed.error : typeof parsed.message === "string" && parsed.message.trim() ? parsed.message : null;
|
|
483
|
+
if (message) return { ...typeof parsed.code === "string" ? { code: parsed.code } : {}, message };
|
|
484
|
+
} catch {
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
return { message: `Answer failed (${res.status})` };
|
|
549
488
|
}
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
const isControlled = value !== void 0;
|
|
577
|
-
const [internal, setInternal] = useState3(initialValue ?? "");
|
|
578
|
-
const text = isControlled ? value : internal;
|
|
579
|
-
const textareaRef = useRef3(null);
|
|
580
|
-
const fileInputRef = useRef3(null);
|
|
581
|
-
const folderInputRef = useRef3(null);
|
|
582
|
-
const [dragOver, setDragOver] = useState3(false);
|
|
583
|
-
const dragDepth = useRef3(0);
|
|
584
|
-
const setText = useCallback(
|
|
585
|
-
(next) => {
|
|
586
|
-
if (!isControlled) setInternal(next);
|
|
587
|
-
onValueChange?.(next);
|
|
588
|
-
},
|
|
589
|
-
[isControlled, onValueChange]
|
|
590
|
-
);
|
|
591
|
-
useEffect3(() => {
|
|
592
|
-
const el = textareaRef.current;
|
|
593
|
-
if (!el) return;
|
|
594
|
-
el.style.height = "auto";
|
|
595
|
-
el.style.height = `${Math.min(el.scrollHeight, MAX_HEIGHT)}px`;
|
|
596
|
-
}, [text]);
|
|
597
|
-
const prevSeedRef = useRef3(null);
|
|
598
|
-
const pendingCaretRef = useRef3(null);
|
|
599
|
-
useEffect3(() => {
|
|
600
|
-
const prev = prevSeedRef.current;
|
|
601
|
-
prevSeedRef.current = seed ?? null;
|
|
602
|
-
if (seed == null || seed === prev || isControlled) return;
|
|
603
|
-
setText(seed);
|
|
604
|
-
onSeedApplied?.();
|
|
605
|
-
const el = textareaRef.current;
|
|
606
|
-
if (el && el.value === seed) {
|
|
607
|
-
el.focus();
|
|
608
|
-
el.setSelectionRange(seed.length, seed.length);
|
|
609
|
-
} else {
|
|
610
|
-
pendingCaretRef.current = seed;
|
|
611
|
-
}
|
|
612
|
-
}, [seed, setText, onSeedApplied, isControlled]);
|
|
613
|
-
useEffect3(() => {
|
|
614
|
-
if (pendingCaretRef.current == null || pendingCaretRef.current !== text)
|
|
615
|
-
return;
|
|
616
|
-
pendingCaretRef.current = null;
|
|
617
|
-
const el = textareaRef.current;
|
|
618
|
-
if (!el) return;
|
|
619
|
-
el.focus();
|
|
620
|
-
el.setSelectionRange(text.length, text.length);
|
|
621
|
-
}, [text]);
|
|
622
|
-
useEffect3(() => {
|
|
623
|
-
if (!focusShortcut || disabled) return;
|
|
624
|
-
function onKeyDown(e) {
|
|
625
|
-
if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === "l") {
|
|
626
|
-
e.preventDefault();
|
|
627
|
-
textareaRef.current?.focus();
|
|
489
|
+
function createInteractionAnswerSubmitter(options) {
|
|
490
|
+
const timeoutMs = options.timeoutMs ?? INTERACTION_SUBMIT_TIMEOUT_MS;
|
|
491
|
+
return async (submission) => {
|
|
492
|
+
const doFetch = options.fetchImpl ?? fetch;
|
|
493
|
+
const url = typeof options.url === "function" ? options.url(submission) : options.url;
|
|
494
|
+
const extra = typeof options.body === "function" ? options.body(submission) : options.body ?? {};
|
|
495
|
+
const controller = new AbortController();
|
|
496
|
+
const timer = setTimeout(() => controller.abort(INTERACTION_SUBMIT_TIMEOUT_MESSAGE), timeoutMs);
|
|
497
|
+
try {
|
|
498
|
+
const res = await doFetch(url, {
|
|
499
|
+
method: "POST",
|
|
500
|
+
headers: { "Content-Type": "application/json" },
|
|
501
|
+
signal: controller.signal,
|
|
502
|
+
body: JSON.stringify({
|
|
503
|
+
...extra,
|
|
504
|
+
id: submission.id,
|
|
505
|
+
outcome: submission.outcome,
|
|
506
|
+
...submission.data ? { data: submission.data } : {}
|
|
507
|
+
})
|
|
508
|
+
});
|
|
509
|
+
if (res.ok) return { ok: true };
|
|
510
|
+
const failure = await responseErrorMessage(res);
|
|
511
|
+
return { ok: false, expired: res.status === 410, message: failure.message };
|
|
512
|
+
} catch (err) {
|
|
513
|
+
if (controller.signal.aborted) {
|
|
514
|
+
return { ok: false, expired: false, message: INTERACTION_SUBMIT_TIMEOUT_MESSAGE };
|
|
628
515
|
}
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
}, [focusShortcut, disabled]);
|
|
633
|
-
const readyParts = pendingFiles.filter((f) => f.status === "ready" && f.part).map((f) => f.part);
|
|
634
|
-
const hasSendable = onSendParts ? text.trim().length > 0 || readyParts.length > 0 : text.trim().length > 0;
|
|
635
|
-
const canSend = hasSendable && !isStreaming && !disabled;
|
|
636
|
-
const send = useCallback(() => {
|
|
637
|
-
const trimmed = text.trim();
|
|
638
|
-
if (isStreaming || disabled) return;
|
|
639
|
-
if (onSendParts) {
|
|
640
|
-
const parts = pendingFiles.filter((f) => f.status === "ready" && f.part).map((f) => f.part);
|
|
641
|
-
if (!trimmed && parts.length === 0) return;
|
|
642
|
-
onSendParts(trimmed, parts);
|
|
643
|
-
setText("");
|
|
644
|
-
return;
|
|
645
|
-
}
|
|
646
|
-
if (!trimmed) return;
|
|
647
|
-
onSend?.(trimmed);
|
|
648
|
-
setText("");
|
|
649
|
-
}, [text, isStreaming, disabled, onSend, onSendParts, pendingFiles, setText]);
|
|
650
|
-
const handleKeyDown = (e) => {
|
|
651
|
-
if (e.nativeEvent.isComposing) return;
|
|
652
|
-
if (e.key === "Enter" && !e.shiftKey) {
|
|
653
|
-
e.preventDefault();
|
|
654
|
-
send();
|
|
516
|
+
return { ok: false, expired: false, message: err instanceof Error ? err.message : "Failed to submit the answer" };
|
|
517
|
+
} finally {
|
|
518
|
+
clearTimeout(timer);
|
|
655
519
|
}
|
|
656
520
|
};
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
// src/web-react/interaction-question-card.tsx
|
|
524
|
+
import { Fragment as Fragment2, jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
525
|
+
function CheckGlyph({ className }) {
|
|
526
|
+
return /* @__PURE__ */ jsx4("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: /* @__PURE__ */ jsx4("path", { d: "M20 6 9 17l-5-5" }) });
|
|
527
|
+
}
|
|
528
|
+
var BADGE_VARIANT_CLASSES = {
|
|
529
|
+
outline: "border-border text-foreground",
|
|
530
|
+
default: "border-transparent bg-primary text-primary-foreground",
|
|
531
|
+
destructive: "border-transparent bg-destructive/15 text-destructive"
|
|
532
|
+
};
|
|
533
|
+
function InteractionBadge({ variant, children }) {
|
|
534
|
+
return /* @__PURE__ */ jsx4("span", { className: `inline-flex items-center rounded-md border px-2 py-0.5 text-xs font-medium ${BADGE_VARIANT_CLASSES[variant]}`, children });
|
|
535
|
+
}
|
|
536
|
+
function InteractionActionButton({
|
|
537
|
+
variant = "primary",
|
|
538
|
+
onClick,
|
|
539
|
+
disabled,
|
|
540
|
+
children
|
|
541
|
+
}) {
|
|
542
|
+
const variantClasses = variant === "primary" ? "bg-primary text-primary-foreground shadow-sm hover:bg-primary/90" : "border border-border bg-transparent text-foreground hover:bg-accent/40";
|
|
543
|
+
return /* @__PURE__ */ jsx4(
|
|
544
|
+
"button",
|
|
545
|
+
{
|
|
546
|
+
type: "button",
|
|
547
|
+
onClick,
|
|
548
|
+
disabled,
|
|
549
|
+
className: `inline-flex items-center gap-1.5 rounded-lg px-3 py-1.5 text-sm font-medium transition disabled:cursor-not-allowed disabled:opacity-40 focus:outline-none focus-visible:ring-2 focus-visible:ring-ring ${variantClasses}`,
|
|
550
|
+
children
|
|
678
551
|
}
|
|
679
|
-
}, []);
|
|
680
|
-
const handleDragOver = useCallback((e) => {
|
|
681
|
-
e.preventDefault();
|
|
682
|
-
e.stopPropagation();
|
|
683
|
-
if (e.dataTransfer) e.dataTransfer.dropEffect = "copy";
|
|
684
|
-
}, []);
|
|
685
|
-
const handleDrop = useCallback(
|
|
686
|
-
(e) => {
|
|
687
|
-
e.preventDefault();
|
|
688
|
-
e.stopPropagation();
|
|
689
|
-
dragDepth.current = 0;
|
|
690
|
-
setDragOver(false);
|
|
691
|
-
const files = e.dataTransfer?.files;
|
|
692
|
-
if (files?.length) onAttach?.(files);
|
|
693
|
-
},
|
|
694
|
-
[onAttach]
|
|
695
552
|
);
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
553
|
+
}
|
|
554
|
+
var FIELD_INPUT_CLASSES = "w-full rounded-lg border border-border bg-background px-3 py-2 text-sm text-foreground outline-none focus:border-primary disabled:opacity-50";
|
|
555
|
+
function QuestionOptionList({
|
|
556
|
+
groupName,
|
|
557
|
+
idPrefix,
|
|
558
|
+
options,
|
|
559
|
+
multi,
|
|
560
|
+
selectedValues,
|
|
561
|
+
disabled,
|
|
562
|
+
onToggle
|
|
563
|
+
}) {
|
|
564
|
+
return /* @__PURE__ */ jsx4(Fragment2, { children: options.map((option, optionIndex) => {
|
|
565
|
+
const inputId = `${idPrefix}-${optionIndex}`;
|
|
566
|
+
const checked = selectedValues.includes(option.value);
|
|
567
|
+
return /* @__PURE__ */ jsxs3("label", { htmlFor: inputId, className: "flex cursor-pointer gap-2 rounded-lg border border-border/70 p-3 transition-colors hover:bg-muted/50", children: [
|
|
568
|
+
/* @__PURE__ */ jsx4(
|
|
569
|
+
"input",
|
|
570
|
+
{
|
|
571
|
+
id: inputId,
|
|
572
|
+
type: multi ? "checkbox" : "radio",
|
|
573
|
+
name: groupName,
|
|
574
|
+
value: option.value,
|
|
575
|
+
checked,
|
|
576
|
+
disabled,
|
|
577
|
+
onChange: () => onToggle(option.value),
|
|
578
|
+
"aria-labelledby": `${inputId}-label`,
|
|
579
|
+
"aria-describedby": option.description ? `${inputId}-description` : void 0,
|
|
580
|
+
className: "mt-0.5 h-4 w-4 shrink-0 accent-primary"
|
|
581
|
+
}
|
|
582
|
+
),
|
|
583
|
+
/* @__PURE__ */ jsxs3("span", { className: "min-w-0", children: [
|
|
584
|
+
/* @__PURE__ */ jsx4("span", { id: `${inputId}-label`, className: "block text-sm font-medium leading-5 text-foreground", children: option.label }),
|
|
585
|
+
option.description && /* @__PURE__ */ jsx4("span", { id: `${inputId}-description`, className: "mt-0.5 block text-xs leading-5 text-muted-foreground", children: option.description })
|
|
586
|
+
] })
|
|
587
|
+
] }, `${option.value}-${optionIndex}`);
|
|
588
|
+
}) });
|
|
589
|
+
}
|
|
590
|
+
function selectField(field) {
|
|
591
|
+
return field.type === "select" ? field : null;
|
|
592
|
+
}
|
|
593
|
+
function valuesWithSelected(values, field, optionValue) {
|
|
594
|
+
const current = values[field.name]?.selected ?? [];
|
|
595
|
+
let selected = [optionValue];
|
|
596
|
+
if (field.multi === true) {
|
|
597
|
+
selected = current.includes(optionValue) ? current.filter((item) => item !== optionValue) : [...current, optionValue];
|
|
598
|
+
}
|
|
599
|
+
return { ...values, [field.name]: { ...values[field.name], selected } };
|
|
600
|
+
}
|
|
601
|
+
var STATUS_LABELS = interactionStatusLabels({
|
|
602
|
+
pending: "Waiting for your answer",
|
|
603
|
+
answered: "Answered",
|
|
604
|
+
declined: "Declined"
|
|
605
|
+
});
|
|
606
|
+
var TERMINAL_NOTES = interactionTerminalNotes("question", {
|
|
607
|
+
expired: "The original run ended. Answer now to send a new message with this context.",
|
|
608
|
+
cancelled: "The agent withdrew this question. Answer now to send a new message with this context."
|
|
609
|
+
});
|
|
610
|
+
function InteractionQuestionCard({
|
|
611
|
+
interaction,
|
|
612
|
+
canWrite,
|
|
613
|
+
submitAnswer,
|
|
614
|
+
onResolved,
|
|
615
|
+
onLateAnswer,
|
|
616
|
+
className
|
|
617
|
+
}) {
|
|
618
|
+
const [values, setValues] = useState3(() => fieldValuesFromAnswers(interaction.fields, interaction.answers));
|
|
619
|
+
const [submitting, setSubmitting] = useState3(false);
|
|
620
|
+
const [localStatus, setLocalStatus] = useState3(null);
|
|
621
|
+
const [lateAnswerSent, setLateAnswerSent] = useState3(false);
|
|
622
|
+
const [error, setError] = useState3(null);
|
|
623
|
+
const submitInFlightRef = useRef3(false);
|
|
624
|
+
useEffect3(() => {
|
|
625
|
+
if (!interaction.answers) return;
|
|
626
|
+
setValues(fieldValuesFromAnswers(interaction.fields, interaction.answers));
|
|
627
|
+
}, [interaction.answers, interaction.fields]);
|
|
628
|
+
const status = isTerminalInteractionStatus(interaction.status) ? interaction.status : localStatus ?? interaction.status;
|
|
629
|
+
const answered = status === "answered";
|
|
630
|
+
const lateAnswerable = isLateAnswerableStatus(status) && onLateAnswer !== void 0;
|
|
631
|
+
const secretLateAnswerBlocked = lateAnswerable && hasSecretField(interaction.fields);
|
|
632
|
+
const canLateAnswer = canWrite && lateAnswerable && !lateAnswerSent && !secretLateAnswerBlocked;
|
|
633
|
+
const disabled = !canWrite || status !== "pending" && !canLateAnswer || submitting;
|
|
634
|
+
const answerData = useMemo2(() => buildAnswerData(interaction.fields, values), [interaction.fields, values]);
|
|
635
|
+
const setFieldValue = (name, patch) => {
|
|
636
|
+
setValues((prev) => ({ ...prev, [name]: { ...prev[name], ...patch } }));
|
|
637
|
+
};
|
|
638
|
+
const toggleSelected = (field, optionValue) => {
|
|
639
|
+
setValues((prev) => valuesWithSelected(prev, field, optionValue));
|
|
640
|
+
};
|
|
641
|
+
async function submitLateAnswer() {
|
|
642
|
+
if (submitInFlightRef.current || !canLateAnswer || !onLateAnswer) return;
|
|
643
|
+
const data = buildAnswerData(interaction.fields, values);
|
|
644
|
+
if (!data) return;
|
|
645
|
+
submitInFlightRef.current = true;
|
|
646
|
+
setSubmitting(true);
|
|
647
|
+
setError(null);
|
|
648
|
+
let accepted;
|
|
649
|
+
try {
|
|
650
|
+
accepted = await onLateAnswer(lateAnswerMessage(interaction, data));
|
|
651
|
+
} catch {
|
|
652
|
+
accepted = false;
|
|
653
|
+
} finally {
|
|
654
|
+
submitInFlightRef.current = false;
|
|
655
|
+
setSubmitting(false);
|
|
656
|
+
}
|
|
657
|
+
if (accepted === false) {
|
|
658
|
+
setError("The new message was not sent. Try again from this card.");
|
|
659
|
+
return;
|
|
660
|
+
}
|
|
661
|
+
setLateAnswerSent(true);
|
|
662
|
+
}
|
|
663
|
+
async function submit() {
|
|
664
|
+
if (lateAnswerable) {
|
|
665
|
+
await submitLateAnswer();
|
|
666
|
+
return;
|
|
667
|
+
}
|
|
668
|
+
if (submitInFlightRef.current || disabled || !answerData) return;
|
|
669
|
+
submitInFlightRef.current = true;
|
|
670
|
+
setSubmitting(true);
|
|
671
|
+
setError(null);
|
|
672
|
+
try {
|
|
673
|
+
const result = await submitAnswer({ id: interaction.id, outcome: "accepted", data: answerData });
|
|
674
|
+
if (result.ok) {
|
|
675
|
+
setLocalStatus("answered");
|
|
676
|
+
onResolved?.(interaction.id, "answered", answerData);
|
|
677
|
+
return;
|
|
678
|
+
}
|
|
679
|
+
if (result.expired) {
|
|
680
|
+
setLocalStatus("expired");
|
|
681
|
+
onResolved?.(interaction.id, "expired");
|
|
682
|
+
return;
|
|
683
|
+
}
|
|
684
|
+
setError(result.message);
|
|
685
|
+
} finally {
|
|
686
|
+
submitInFlightRef.current = false;
|
|
687
|
+
setSubmitting(false);
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
const terminalNote = secretLateAnswerBlocked ? "This question asked for a secret, so it cannot be sent as a new chat message. Ask the agent to request it again." : TERMINAL_NOTES[status];
|
|
691
|
+
const showSubmitButton = status === "pending" || canWrite && lateAnswerable && !lateAnswerSent;
|
|
692
|
+
let submitLabel = "Submit answer";
|
|
693
|
+
if (lateAnswerable) {
|
|
694
|
+
submitLabel = submitting ? "Sending\u2026" : "Send as new message";
|
|
695
|
+
} else if (submitting) {
|
|
696
|
+
submitLabel = "Submitting\u2026";
|
|
697
|
+
}
|
|
698
|
+
return /* @__PURE__ */ jsxs3("div", { className: `rounded-xl border border-border bg-card p-4 shadow-sm ${className ?? ""}`, children: [
|
|
699
|
+
/* @__PURE__ */ jsxs3("div", { className: "mb-3 flex flex-wrap items-center justify-between gap-2", children: [
|
|
700
|
+
/* @__PURE__ */ jsxs3("div", { className: "flex flex-wrap items-center gap-2", children: [
|
|
701
|
+
/* @__PURE__ */ jsx4(InteractionBadge, { variant: "outline", children: "Question" }),
|
|
702
|
+
/* @__PURE__ */ jsx4(InteractionBadge, { variant: answered ? "default" : status === "expired" || status === "declined" ? "destructive" : "outline", children: STATUS_LABELS[status] })
|
|
703
|
+
] }),
|
|
704
|
+
/* @__PURE__ */ jsx4("span", { className: "text-xs text-muted-foreground", children: "The agent asked for input" })
|
|
705
|
+
] }),
|
|
706
|
+
interaction.title.trim() && interaction.fields.every((field) => field.label !== interaction.title) && /* @__PURE__ */ jsx4("p", { className: "mb-3 text-sm font-medium leading-5 text-foreground", children: interaction.title }),
|
|
707
|
+
interaction.body && /* @__PURE__ */ jsx4("p", { className: "mb-3 text-sm leading-5 text-muted-foreground", children: interaction.body }),
|
|
708
|
+
/* @__PURE__ */ jsx4("div", { className: "space-y-4", children: interaction.fields.map((field) => {
|
|
709
|
+
const value = values[field.name] ?? {};
|
|
710
|
+
const select = selectField(field);
|
|
711
|
+
return /* @__PURE__ */ jsxs3("fieldset", { className: "space-y-2", children: [
|
|
712
|
+
/* @__PURE__ */ jsx4("p", { className: "text-sm font-medium leading-5 text-foreground", children: field.label }),
|
|
713
|
+
select ? /* @__PURE__ */ jsxs3("div", { className: "space-y-2", children: [
|
|
783
714
|
/* @__PURE__ */ jsx4(
|
|
784
|
-
|
|
715
|
+
QuestionOptionList,
|
|
785
716
|
{
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
717
|
+
groupName: `${interaction.id}-${field.name}`,
|
|
718
|
+
idPrefix: `${interaction.id}-${field.name}`,
|
|
719
|
+
options: select.options,
|
|
720
|
+
multi: select.multi === true,
|
|
721
|
+
selectedValues: value.selected ?? [],
|
|
791
722
|
disabled,
|
|
792
|
-
|
|
793
|
-
"aria-label": "Message input",
|
|
794
|
-
className: "max-h-[168px] min-h-[40px] flex-1 resize-none bg-transparent px-1.5 py-2 text-[15px] leading-6 text-foreground outline-none placeholder:text-muted-foreground disabled:opacity-50"
|
|
723
|
+
onToggle: (optionValue) => toggleSelected(select, optionValue)
|
|
795
724
|
}
|
|
796
725
|
),
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
"button",
|
|
726
|
+
select.allowCustom === true && /* @__PURE__ */ jsx4(
|
|
727
|
+
"input",
|
|
800
728
|
{
|
|
801
|
-
type: "
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
]
|
|
729
|
+
type: "text",
|
|
730
|
+
value: value.custom ?? "",
|
|
731
|
+
disabled,
|
|
732
|
+
onChange: (event) => setFieldValue(field.name, { custom: event.target.value }),
|
|
733
|
+
placeholder: "Other \u2014 type your own answer",
|
|
734
|
+
"aria-label": `Custom answer for ${field.label}`,
|
|
735
|
+
className: FIELD_INPUT_CLASSES
|
|
809
736
|
}
|
|
810
|
-
)
|
|
811
|
-
|
|
737
|
+
)
|
|
738
|
+
] }) : field.type === "boolean" ? /* @__PURE__ */ jsx4("div", { className: "flex gap-4", children: ["true", "false"].map((boolValue) => /* @__PURE__ */ jsxs3("label", { className: "flex cursor-pointer items-center gap-2 text-sm text-foreground", children: [
|
|
739
|
+
/* @__PURE__ */ jsx4(
|
|
740
|
+
"input",
|
|
812
741
|
{
|
|
813
|
-
type: "
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
/* @__PURE__ */ jsx4("span", { children: sendLabel })
|
|
821
|
-
]
|
|
742
|
+
type: "radio",
|
|
743
|
+
name: `${interaction.id}-${field.name}`,
|
|
744
|
+
value: boolValue,
|
|
745
|
+
checked: (value.selected ?? [])[0] === boolValue,
|
|
746
|
+
disabled,
|
|
747
|
+
onChange: () => setFieldValue(field.name, { selected: [boolValue] }),
|
|
748
|
+
className: "h-4 w-4 accent-primary"
|
|
822
749
|
}
|
|
823
|
-
)
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
750
|
+
),
|
|
751
|
+
boolValue === "true" ? "Yes" : "No"
|
|
752
|
+
] }, boolValue)) }) : field.type === "number" ? /* @__PURE__ */ jsx4(
|
|
753
|
+
"input",
|
|
754
|
+
{
|
|
755
|
+
type: "number",
|
|
756
|
+
value: value.text ?? "",
|
|
757
|
+
disabled,
|
|
758
|
+
"aria-label": field.label,
|
|
759
|
+
onChange: (event) => setFieldValue(field.name, { text: event.target.value }),
|
|
760
|
+
className: FIELD_INPUT_CLASSES
|
|
761
|
+
}
|
|
762
|
+
) : field.type === "secret" ? /* @__PURE__ */ jsx4(
|
|
763
|
+
"input",
|
|
764
|
+
{
|
|
765
|
+
type: "password",
|
|
766
|
+
value: value.text ?? "",
|
|
767
|
+
disabled,
|
|
768
|
+
"aria-label": field.label,
|
|
769
|
+
onChange: (event) => setFieldValue(field.name, { text: event.target.value }),
|
|
770
|
+
placeholder: field.placeholder,
|
|
771
|
+
className: FIELD_INPUT_CLASSES
|
|
772
|
+
}
|
|
773
|
+
) : /* @__PURE__ */ jsx4(
|
|
774
|
+
"textarea",
|
|
775
|
+
{
|
|
776
|
+
value: value.text ?? "",
|
|
777
|
+
disabled,
|
|
778
|
+
"aria-label": field.label,
|
|
779
|
+
onChange: (event) => setFieldValue(field.name, { text: event.target.value }),
|
|
780
|
+
rows: 3,
|
|
781
|
+
placeholder: field.type === "text" ? field.placeholder : void 0,
|
|
782
|
+
className: FIELD_INPUT_CLASSES
|
|
783
|
+
}
|
|
784
|
+
)
|
|
785
|
+
] }, field.name);
|
|
786
|
+
}) }),
|
|
787
|
+
error && /* @__PURE__ */ jsx4("p", { className: "mt-3 text-xs text-destructive", children: error }),
|
|
788
|
+
terminalNote && /* @__PURE__ */ jsx4("p", { className: "mt-3 text-xs text-muted-foreground", children: terminalNote }),
|
|
789
|
+
showSubmitButton && /* @__PURE__ */ jsx4("div", { className: "mt-4 flex items-center justify-end gap-2", children: /* @__PURE__ */ jsx4(InteractionActionButton, { onClick: () => void submit(), disabled: disabled || !answerData, children: submitLabel }) }),
|
|
790
|
+
answered && /* @__PURE__ */ jsx4("div", { className: "mt-4 flex items-center justify-end", children: /* @__PURE__ */ jsxs3("span", { className: "inline-flex items-center gap-1 text-xs text-muted-foreground", children: [
|
|
791
|
+
/* @__PURE__ */ jsx4(CheckGlyph, { className: "h-3 w-3" }),
|
|
792
|
+
"Answered"
|
|
793
|
+
] }) }),
|
|
794
|
+
lateAnswerSent && /* @__PURE__ */ jsx4("div", { className: "mt-4 flex items-center justify-end", children: /* @__PURE__ */ jsxs3("span", { className: "inline-flex items-center gap-1 text-xs text-muted-foreground", children: [
|
|
795
|
+
/* @__PURE__ */ jsx4(CheckGlyph, { className: "h-3 w-3" }),
|
|
796
|
+
"Sent as new message"
|
|
797
|
+
] }) })
|
|
798
|
+
] });
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
// src/web-react/durable-plan-card.tsx
|
|
802
|
+
import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
803
|
+
function statusLabel(plan) {
|
|
804
|
+
switch (plan.status) {
|
|
805
|
+
case "pending":
|
|
806
|
+
return "Waiting for your decision";
|
|
807
|
+
case "approved":
|
|
808
|
+
return "Approved";
|
|
809
|
+
case "rejected":
|
|
810
|
+
return "Changes requested";
|
|
811
|
+
case "superseded":
|
|
812
|
+
return "Superseded";
|
|
813
|
+
case "withdrawn":
|
|
814
|
+
return "Withdrawn";
|
|
815
|
+
default:
|
|
816
|
+
return "Preparing";
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
function DurablePlanCard({
|
|
820
|
+
plan,
|
|
821
|
+
canWrite,
|
|
822
|
+
decide,
|
|
823
|
+
deciding = null,
|
|
824
|
+
error,
|
|
825
|
+
renderMarkdown,
|
|
826
|
+
className
|
|
827
|
+
}) {
|
|
828
|
+
const [feedback, setFeedback] = useState4("");
|
|
829
|
+
const [expanded, setExpanded] = useState4(false);
|
|
830
|
+
const [localError, setLocalError] = useState4(null);
|
|
831
|
+
useEffect4(() => setLocalError(null), [plan.planId, plan.revision, plan.status]);
|
|
832
|
+
const actionable = plan.status === "pending";
|
|
833
|
+
const disabled = !canWrite || !actionable || deciding !== null;
|
|
834
|
+
async function submit(decision) {
|
|
835
|
+
const trimmed = feedback.trim();
|
|
836
|
+
if (decision === "rejected" && !trimmed) {
|
|
837
|
+
setLocalError("Describe what you want changed before requesting a revision.");
|
|
838
|
+
return;
|
|
831
839
|
}
|
|
832
|
-
|
|
840
|
+
setLocalError(null);
|
|
841
|
+
await decide(decision, decision === "rejected" ? trimmed : void 0);
|
|
842
|
+
}
|
|
843
|
+
return /* @__PURE__ */ jsxs4("div", { className: `rounded-xl border border-primary/40 bg-card p-4 shadow-sm ${className ?? ""}`, children: [
|
|
844
|
+
/* @__PURE__ */ jsxs4("div", { className: "mb-3 flex flex-wrap items-center justify-between gap-2", children: [
|
|
845
|
+
/* @__PURE__ */ jsxs4("div", { className: "flex flex-wrap items-center gap-2", children: [
|
|
846
|
+
/* @__PURE__ */ jsx5(InteractionBadge, { variant: "outline", children: "Plan decision" }),
|
|
847
|
+
/* @__PURE__ */ jsx5(InteractionBadge, { variant: plan.status === "approved" ? "default" : plan.status === "rejected" || plan.status === "withdrawn" ? "destructive" : "outline", children: statusLabel(plan) })
|
|
848
|
+
] }),
|
|
849
|
+
/* @__PURE__ */ jsxs4("span", { className: "text-xs text-muted-foreground", children: [
|
|
850
|
+
"Revision ",
|
|
851
|
+
plan.revision
|
|
852
|
+
] })
|
|
853
|
+
] }),
|
|
854
|
+
plan.title && /* @__PURE__ */ jsx5("p", { className: "mb-3 text-sm font-medium leading-5 text-foreground", children: plan.title }),
|
|
855
|
+
/* @__PURE__ */ jsxs4("div", { className: "relative", children: [
|
|
856
|
+
/* @__PURE__ */ jsx5("div", { className: "overflow-hidden text-sm", style: expanded ? void 0 : { maxHeight: 320 }, children: renderMarkdown ? renderMarkdown(plan.body) : /* @__PURE__ */ jsx5("p", { className: "whitespace-pre-wrap leading-5", children: plan.body }) }),
|
|
857
|
+
!expanded && /* @__PURE__ */ jsx5("div", { className: "pointer-events-none absolute inset-x-0 bottom-0 h-12 bg-gradient-to-t from-card to-transparent" }),
|
|
858
|
+
/* @__PURE__ */ jsx5(
|
|
859
|
+
"button",
|
|
860
|
+
{
|
|
861
|
+
type: "button",
|
|
862
|
+
onClick: () => setExpanded((value) => !value),
|
|
863
|
+
className: "mt-1 text-xs text-muted-foreground hover:text-foreground focus:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
864
|
+
children: expanded ? "Collapse plan" : "Show full plan"
|
|
865
|
+
}
|
|
866
|
+
)
|
|
867
|
+
] }),
|
|
868
|
+
actionable && /* @__PURE__ */ jsxs4("div", { className: "mt-3 space-y-2", children: [
|
|
869
|
+
/* @__PURE__ */ jsx5("label", { className: "block text-sm font-medium leading-5 text-foreground", htmlFor: `durable-plan-feedback-${plan.planId}-${plan.revision}`, children: "Feedback for requested changes" }),
|
|
870
|
+
/* @__PURE__ */ jsx5(
|
|
871
|
+
"textarea",
|
|
872
|
+
{
|
|
873
|
+
id: `durable-plan-feedback-${plan.planId}-${plan.revision}`,
|
|
874
|
+
value: feedback,
|
|
875
|
+
disabled,
|
|
876
|
+
onChange: (event) => setFeedback(event.target.value),
|
|
877
|
+
rows: 2,
|
|
878
|
+
placeholder: "Describe what you want changed in the plan",
|
|
879
|
+
className: "w-full rounded-lg border border-border bg-background px-3 py-2 text-sm outline-none focus:border-primary disabled:opacity-50"
|
|
880
|
+
}
|
|
881
|
+
)
|
|
882
|
+
] }),
|
|
883
|
+
(localError ?? error) && /* @__PURE__ */ jsx5("p", { className: "mt-3 text-xs text-destructive", children: localError ?? error }),
|
|
884
|
+
actionable && /* @__PURE__ */ jsxs4("div", { className: "mt-4 flex items-center justify-end gap-2", children: [
|
|
885
|
+
/* @__PURE__ */ jsx5(InteractionActionButton, { variant: "outline", onClick: () => void submit("rejected"), disabled, children: deciding === "rejected" ? "Sending\u2026" : "Request changes" }),
|
|
886
|
+
/* @__PURE__ */ jsx5(InteractionActionButton, { onClick: () => void submit("approved"), disabled, children: deciding === "approved" ? "Approving\u2026" : "Approve plan" })
|
|
887
|
+
] })
|
|
888
|
+
] });
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
// src/web-react/interaction-plan-card.tsx
|
|
892
|
+
import { useMemo as useMemo3, useRef as useRef4, useState as useState5 } from "react";
|
|
893
|
+
import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
894
|
+
function CheckGlyph2({ className }) {
|
|
895
|
+
return /* @__PURE__ */ jsx6("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: /* @__PURE__ */ jsx6("path", { d: "M20 6 9 17l-5-5" }) });
|
|
896
|
+
}
|
|
897
|
+
function ChevronDownGlyph({ className }) {
|
|
898
|
+
return /* @__PURE__ */ jsx6("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: /* @__PURE__ */ jsx6("path", { d: "m6 9 6 6 6-6" }) });
|
|
899
|
+
}
|
|
900
|
+
var STATUS_LABELS2 = interactionStatusLabels({
|
|
901
|
+
pending: "Waiting for your approval",
|
|
902
|
+
answered: "Approved",
|
|
903
|
+
declined: "Rejected"
|
|
904
|
+
});
|
|
905
|
+
var TERMINAL_NOTES2 = interactionTerminalNotes("plan", {
|
|
906
|
+
declined: "The agent was asked to revise the plan."
|
|
907
|
+
});
|
|
908
|
+
var COLLAPSED_MAX_HEIGHT = 320;
|
|
909
|
+
function InteractionPlanCard({
|
|
910
|
+
interaction,
|
|
911
|
+
canWrite,
|
|
912
|
+
submitAnswer,
|
|
913
|
+
onResolved,
|
|
914
|
+
renderMarkdown,
|
|
915
|
+
className
|
|
916
|
+
}) {
|
|
917
|
+
const [values, setValues] = useState5({});
|
|
918
|
+
const [expanded, setExpanded] = useState5(false);
|
|
919
|
+
const [submitting, setSubmitting] = useState5(null);
|
|
920
|
+
const [localStatus, setLocalStatus] = useState5(null);
|
|
921
|
+
const [error, setError] = useState5(null);
|
|
922
|
+
const submitInFlightRef = useRef4(false);
|
|
923
|
+
const status = isTerminalInteractionStatus(interaction.status) ? interaction.status : localStatus ?? interaction.status;
|
|
924
|
+
const disabled = !canWrite || status !== "pending" || submitting !== null;
|
|
925
|
+
const approveData = useMemo3(() => buildAnswerData(interaction.fields, values), [interaction.fields, values]);
|
|
926
|
+
const rejectData = useMemo3(() => {
|
|
927
|
+
const data = {};
|
|
928
|
+
for (const field of interaction.fields) {
|
|
929
|
+
const answer = fieldAnswer(field, values);
|
|
930
|
+
if (answer !== null) data[field.name] = answer;
|
|
931
|
+
}
|
|
932
|
+
return data;
|
|
933
|
+
}, [interaction.fields, values]);
|
|
934
|
+
async function submit(outcome) {
|
|
935
|
+
const data = outcome === "accepted" ? approveData : rejectData;
|
|
936
|
+
if (submitInFlightRef.current || disabled || data === null) return;
|
|
937
|
+
submitInFlightRef.current = true;
|
|
938
|
+
setSubmitting(outcome === "accepted" ? "approve" : "reject");
|
|
939
|
+
setError(null);
|
|
940
|
+
try {
|
|
941
|
+
const result = await submitAnswer({ id: interaction.id, outcome, data });
|
|
942
|
+
if (result.ok) {
|
|
943
|
+
const resolved = outcome === "accepted" ? "answered" : "declined";
|
|
944
|
+
setLocalStatus(resolved);
|
|
945
|
+
onResolved?.(interaction.id, resolved);
|
|
946
|
+
return;
|
|
947
|
+
}
|
|
948
|
+
if (result.expired) {
|
|
949
|
+
setLocalStatus("expired");
|
|
950
|
+
onResolved?.(interaction.id, "expired");
|
|
951
|
+
return;
|
|
952
|
+
}
|
|
953
|
+
setError(result.message);
|
|
954
|
+
} finally {
|
|
955
|
+
submitInFlightRef.current = false;
|
|
956
|
+
setSubmitting(null);
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
const terminalNote = TERMINAL_NOTES2[status];
|
|
960
|
+
const approved = status === "answered";
|
|
961
|
+
return /* @__PURE__ */ jsxs5("div", { className: `rounded-xl border border-border bg-card p-4 shadow-sm ${className ?? ""}`, children: [
|
|
962
|
+
/* @__PURE__ */ jsxs5("div", { className: "mb-3 flex flex-wrap items-center justify-between gap-2", children: [
|
|
963
|
+
/* @__PURE__ */ jsxs5("div", { className: "flex flex-wrap items-center gap-2", children: [
|
|
964
|
+
/* @__PURE__ */ jsx6(InteractionBadge, { variant: "outline", children: "Plan" }),
|
|
965
|
+
/* @__PURE__ */ jsx6(InteractionBadge, { variant: approved ? "default" : status === "expired" || status === "declined" ? "destructive" : "outline", children: STATUS_LABELS2[status] })
|
|
966
|
+
] }),
|
|
967
|
+
/* @__PURE__ */ jsx6("span", { className: "text-xs text-muted-foreground", children: "The agent proposed a plan" })
|
|
968
|
+
] }),
|
|
969
|
+
interaction.title.trim() && /* @__PURE__ */ jsx6("p", { className: "mb-3 text-sm font-medium leading-5 text-foreground", children: interaction.title }),
|
|
970
|
+
interaction.body && /* @__PURE__ */ jsxs5("div", { className: "relative", children: [
|
|
971
|
+
/* @__PURE__ */ jsx6(
|
|
972
|
+
"div",
|
|
973
|
+
{
|
|
974
|
+
className: "overflow-hidden text-sm text-foreground",
|
|
975
|
+
style: expanded ? void 0 : { maxHeight: COLLAPSED_MAX_HEIGHT },
|
|
976
|
+
children: renderMarkdown ? renderMarkdown(interaction.body) : /* @__PURE__ */ jsx6("p", { className: "whitespace-pre-wrap leading-5", children: interaction.body })
|
|
977
|
+
}
|
|
978
|
+
),
|
|
979
|
+
!expanded && /* @__PURE__ */ jsx6("div", { className: "pointer-events-none absolute inset-x-0 bottom-0 h-12 bg-gradient-to-t from-card to-transparent" }),
|
|
980
|
+
/* @__PURE__ */ jsxs5(
|
|
981
|
+
"button",
|
|
982
|
+
{
|
|
983
|
+
type: "button",
|
|
984
|
+
onClick: () => setExpanded((prev) => !prev),
|
|
985
|
+
className: "mt-1 inline-flex items-center gap-1 text-xs text-muted-foreground transition hover:text-foreground focus:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
986
|
+
children: [
|
|
987
|
+
/* @__PURE__ */ jsx6(ChevronDownGlyph, { className: `h-3 w-3 transition-transform ${expanded ? "rotate-180" : ""}` }),
|
|
988
|
+
expanded ? "Collapse plan" : "Show full plan"
|
|
989
|
+
]
|
|
990
|
+
}
|
|
991
|
+
)
|
|
992
|
+
] }),
|
|
993
|
+
interaction.fields.length > 0 && /* @__PURE__ */ jsx6("div", { className: "mt-3 space-y-4", children: interaction.fields.map((field) => /* @__PURE__ */ jsxs5("fieldset", { className: "space-y-2", children: [
|
|
994
|
+
/* @__PURE__ */ jsx6("p", { className: "text-sm font-medium leading-5 text-foreground", children: field.label }),
|
|
995
|
+
fieldAcceptsFreeText(field) ? /* @__PURE__ */ jsx6(
|
|
996
|
+
"textarea",
|
|
997
|
+
{
|
|
998
|
+
value: values[field.name]?.text ?? "",
|
|
999
|
+
disabled,
|
|
1000
|
+
"aria-label": field.label,
|
|
1001
|
+
onChange: (event) => setValues((prev) => ({ ...prev, [field.name]: { ...prev[field.name], text: event.target.value } })),
|
|
1002
|
+
rows: 2,
|
|
1003
|
+
placeholder: field.type === "text" ? field.placeholder ?? "Optional feedback for the agent" : void 0,
|
|
1004
|
+
className: "w-full rounded-lg border border-border bg-background px-3 py-2 text-sm text-foreground outline-none focus:border-primary disabled:opacity-50"
|
|
1005
|
+
}
|
|
1006
|
+
) : /* @__PURE__ */ jsx6(
|
|
1007
|
+
"input",
|
|
1008
|
+
{
|
|
1009
|
+
type: "text",
|
|
1010
|
+
value: values[field.name]?.text ?? "",
|
|
1011
|
+
disabled,
|
|
1012
|
+
"aria-label": field.label,
|
|
1013
|
+
onChange: (event) => setValues((prev) => ({ ...prev, [field.name]: { ...prev[field.name], text: event.target.value } })),
|
|
1014
|
+
className: "w-full rounded-lg border border-border bg-background px-3 py-2 text-sm text-foreground outline-none focus:border-primary disabled:opacity-50"
|
|
1015
|
+
}
|
|
1016
|
+
)
|
|
1017
|
+
] }, field.name)) }),
|
|
1018
|
+
error && /* @__PURE__ */ jsx6("p", { className: "mt-3 text-xs text-destructive", children: error }),
|
|
1019
|
+
terminalNote && /* @__PURE__ */ jsx6("p", { className: "mt-3 text-xs text-muted-foreground", children: terminalNote }),
|
|
1020
|
+
status === "pending" && /* @__PURE__ */ jsxs5("div", { className: "mt-4 flex items-center justify-end gap-2", children: [
|
|
1021
|
+
/* @__PURE__ */ jsx6(InteractionActionButton, { variant: "outline", onClick: () => void submit("declined"), disabled, children: submitting === "reject" ? "Sending\u2026" : "Request changes" }),
|
|
1022
|
+
/* @__PURE__ */ jsx6(InteractionActionButton, { onClick: () => void submit("accepted"), disabled: disabled || approveData === null, children: submitting === "approve" ? "Approving\u2026" : "Approve plan" })
|
|
1023
|
+
] }),
|
|
1024
|
+
approved && /* @__PURE__ */ jsx6("div", { className: "mt-4 flex items-center justify-end", children: /* @__PURE__ */ jsxs5("span", { className: "inline-flex items-center gap-1 text-xs text-muted-foreground", children: [
|
|
1025
|
+
/* @__PURE__ */ jsx6(CheckGlyph2, { className: "h-3 w-3" }),
|
|
1026
|
+
"Approved"
|
|
1027
|
+
] }) })
|
|
1028
|
+
] });
|
|
833
1029
|
}
|
|
834
1030
|
|
|
835
|
-
// src/web-react/
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
}
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
}
|
|
846
|
-
function fieldAnswer(field, values) {
|
|
847
|
-
const value = values[field.name] ?? {};
|
|
848
|
-
if (field.type === "select") {
|
|
849
|
-
const custom = field.allowCustom === true ? value.custom?.trim() : void 0;
|
|
850
|
-
const chosen = [...value.selected ?? [], ...custom ? [custom] : []];
|
|
851
|
-
if (field.multi !== true && custom) return [custom];
|
|
852
|
-
return chosen.length > 0 ? chosen : null;
|
|
853
|
-
}
|
|
854
|
-
if (field.type === "number") {
|
|
855
|
-
const parsed = Number(value.text);
|
|
856
|
-
return value.text?.trim() && Number.isFinite(parsed) ? parsed : null;
|
|
1031
|
+
// src/web-react/durable-chat-cards.tsx
|
|
1032
|
+
import { jsx as jsx7 } from "react/jsx-runtime";
|
|
1033
|
+
function planIdentity(planId, revision) {
|
|
1034
|
+
return `${planId}:${revision}`;
|
|
1035
|
+
}
|
|
1036
|
+
function durableChatCardsFromParts(parts) {
|
|
1037
|
+
const durablePlans = /* @__PURE__ */ new Set();
|
|
1038
|
+
for (const part of parts) {
|
|
1039
|
+
const plan = persistedPartToPlan(part);
|
|
1040
|
+
if (plan) durablePlans.add(planIdentity(plan.planId, plan.revision));
|
|
857
1041
|
}
|
|
858
|
-
|
|
859
|
-
const
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
for (const field of fields) {
|
|
865
|
-
const answer = fieldAnswer(field, values);
|
|
866
|
-
if (answer === null) {
|
|
867
|
-
if (field.required === false) continue;
|
|
868
|
-
return null;
|
|
1042
|
+
const cards = [];
|
|
1043
|
+
for (const part of parts) {
|
|
1044
|
+
const plan = persistedPartToPlan(part);
|
|
1045
|
+
if (plan) {
|
|
1046
|
+
cards.push({ kind: "plan", key: `plan:${planIdentity(plan.planId, plan.revision)}`, plan });
|
|
1047
|
+
continue;
|
|
869
1048
|
}
|
|
870
|
-
|
|
1049
|
+
const interaction = persistedPartToInteraction(part);
|
|
1050
|
+
if (!interaction) continue;
|
|
1051
|
+
const correlatedPlan = interaction.kind === "plan" && typeof part.planId === "string" && typeof part.revision === "number" && durablePlans.has(planIdentity(part.planId, part.revision));
|
|
1052
|
+
if (correlatedPlan) continue;
|
|
1053
|
+
cards.push({ kind: "interaction", key: `interaction:${interaction.id}`, interaction });
|
|
871
1054
|
}
|
|
872
|
-
return
|
|
873
|
-
}
|
|
874
|
-
function isLateAnswerableStatus(status) {
|
|
875
|
-
return status === "expired" || status === "cancelled";
|
|
876
|
-
}
|
|
877
|
-
function hasSecretField(fields) {
|
|
878
|
-
return fields.some((field) => field.type === "secret");
|
|
1055
|
+
return cards;
|
|
879
1056
|
}
|
|
880
|
-
function
|
|
881
|
-
|
|
1057
|
+
function DurableChatCards({
|
|
1058
|
+
parts,
|
|
1059
|
+
canWrite,
|
|
1060
|
+
submitInteraction,
|
|
1061
|
+
decidePlan,
|
|
1062
|
+
decidingPlan,
|
|
1063
|
+
planError,
|
|
1064
|
+
onInteractionResolved,
|
|
1065
|
+
onLateAnswer,
|
|
1066
|
+
renderMarkdown,
|
|
1067
|
+
className
|
|
1068
|
+
}) {
|
|
1069
|
+
const cards = durableChatCardsFromParts(parts);
|
|
1070
|
+
if (cards.length === 0) return null;
|
|
1071
|
+
return /* @__PURE__ */ jsx7("div", { className: `space-y-3 ${className ?? ""}`, children: cards.map((card) => {
|
|
1072
|
+
if (card.kind === "plan") {
|
|
1073
|
+
return /* @__PURE__ */ jsx7(
|
|
1074
|
+
DurablePlanCard,
|
|
1075
|
+
{
|
|
1076
|
+
plan: card.plan,
|
|
1077
|
+
canWrite,
|
|
1078
|
+
decide: (decision, feedback) => decidePlan(card.plan, decision, feedback),
|
|
1079
|
+
deciding: decidingPlan?.(card.plan),
|
|
1080
|
+
error: planError?.(card.plan),
|
|
1081
|
+
renderMarkdown
|
|
1082
|
+
},
|
|
1083
|
+
card.key
|
|
1084
|
+
);
|
|
1085
|
+
}
|
|
1086
|
+
if (card.interaction.kind === "plan") {
|
|
1087
|
+
return /* @__PURE__ */ jsx7(
|
|
1088
|
+
InteractionPlanCard,
|
|
1089
|
+
{
|
|
1090
|
+
interaction: card.interaction,
|
|
1091
|
+
canWrite,
|
|
1092
|
+
submitAnswer: submitInteraction,
|
|
1093
|
+
onResolved: onInteractionResolved,
|
|
1094
|
+
renderMarkdown
|
|
1095
|
+
},
|
|
1096
|
+
card.key
|
|
1097
|
+
);
|
|
1098
|
+
}
|
|
1099
|
+
return /* @__PURE__ */ jsx7(
|
|
1100
|
+
InteractionQuestionCard,
|
|
1101
|
+
{
|
|
1102
|
+
interaction: card.interaction,
|
|
1103
|
+
canWrite,
|
|
1104
|
+
submitAnswer: submitInteraction,
|
|
1105
|
+
onResolved: onInteractionResolved,
|
|
1106
|
+
onLateAnswer
|
|
1107
|
+
},
|
|
1108
|
+
card.key
|
|
1109
|
+
);
|
|
1110
|
+
}) });
|
|
882
1111
|
}
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
1112
|
+
|
|
1113
|
+
// src/web-react/chat-stream.ts
|
|
1114
|
+
function dispatchChatStreamLine(line, cb) {
|
|
1115
|
+
let receivedContent = false;
|
|
1116
|
+
let turnId;
|
|
1117
|
+
if (!line.trim()) return { receivedContent };
|
|
1118
|
+
let parsed;
|
|
1119
|
+
try {
|
|
1120
|
+
parsed = JSON.parse(line);
|
|
1121
|
+
} catch {
|
|
1122
|
+
return { receivedContent };
|
|
886
1123
|
}
|
|
887
|
-
if (
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
}
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
1124
|
+
if (parsed.kind === "tool_result") {
|
|
1125
|
+
cb.onToolResult?.({
|
|
1126
|
+
toolCallId: parsed.toolCallId,
|
|
1127
|
+
toolName: parsed.toolName,
|
|
1128
|
+
label: parsed.label,
|
|
1129
|
+
outcome: parsed.outcome ?? parsed.result
|
|
1130
|
+
});
|
|
1131
|
+
return { receivedContent: true };
|
|
1132
|
+
}
|
|
1133
|
+
const evt = parsed.kind === "event" ? parsed.event : parsed;
|
|
1134
|
+
if (!evt || typeof evt !== "object") return { receivedContent };
|
|
1135
|
+
switch (evt.type) {
|
|
1136
|
+
case "turn":
|
|
1137
|
+
if (typeof evt.turnId === "string") turnId = evt.turnId;
|
|
1138
|
+
break;
|
|
1139
|
+
case "text":
|
|
1140
|
+
if (typeof evt.text === "string") {
|
|
1141
|
+
cb.onText?.(evt.text);
|
|
1142
|
+
receivedContent = true;
|
|
1143
|
+
}
|
|
1144
|
+
break;
|
|
1145
|
+
case "reasoning":
|
|
1146
|
+
if (typeof evt.text === "string") {
|
|
1147
|
+
cb.onReasoning?.(evt.text);
|
|
1148
|
+
receivedContent = true;
|
|
1149
|
+
}
|
|
1150
|
+
break;
|
|
1151
|
+
case "tool_call": {
|
|
1152
|
+
const call = evt.call ?? evt;
|
|
1153
|
+
cb.onToolCall?.({
|
|
1154
|
+
toolCallId: call.toolCallId ?? call.id,
|
|
1155
|
+
toolName: String(call.toolName ?? call.name ?? "unknown"),
|
|
1156
|
+
args: call.args ?? {}
|
|
1157
|
+
});
|
|
1158
|
+
receivedContent = true;
|
|
1159
|
+
break;
|
|
1160
|
+
}
|
|
1161
|
+
case "tool_result":
|
|
1162
|
+
cb.onToolResult?.({
|
|
1163
|
+
toolCallId: evt.toolCallId,
|
|
1164
|
+
toolName: evt.toolName,
|
|
1165
|
+
label: evt.label,
|
|
1166
|
+
outcome: evt.outcome ?? evt.result
|
|
1167
|
+
});
|
|
1168
|
+
receivedContent = true;
|
|
1169
|
+
break;
|
|
1170
|
+
case "usage": {
|
|
1171
|
+
const u = evt.usage;
|
|
1172
|
+
if (u) cb.onUsage?.({ promptTokens: u.promptTokens ?? 0, completionTokens: u.completionTokens ?? 0 });
|
|
1173
|
+
break;
|
|
1174
|
+
}
|
|
1175
|
+
case "metadata":
|
|
1176
|
+
cb.onMetadata?.(evt.data ?? {});
|
|
1177
|
+
break;
|
|
1178
|
+
case "interaction": {
|
|
1179
|
+
const parsed2 = parseInteractionRequest(evt.data);
|
|
1180
|
+
if (parsed2.succeeded) {
|
|
1181
|
+
cb.onInteraction?.(interactionFromWireRequest(parsed2.value));
|
|
1182
|
+
receivedContent = true;
|
|
1183
|
+
} else {
|
|
1184
|
+
console.error("[chat-stream] dropping malformed interaction line:", parsed2.error);
|
|
1185
|
+
}
|
|
1186
|
+
break;
|
|
1187
|
+
}
|
|
1188
|
+
case "interaction.cancel": {
|
|
1189
|
+
const cancelled = parseInteractionCancel(evt.data);
|
|
1190
|
+
if (cancelled.succeeded) {
|
|
1191
|
+
cb.onInteractionCancel?.(cancelled.value);
|
|
1192
|
+
receivedContent = true;
|
|
1193
|
+
} else {
|
|
1194
|
+
console.error("[chat-stream] dropping malformed interaction.cancel line:", cancelled.error);
|
|
1195
|
+
}
|
|
1196
|
+
break;
|
|
1197
|
+
}
|
|
1198
|
+
case "error": {
|
|
1199
|
+
const data = evt.data;
|
|
1200
|
+
const message = String(data?.message ?? evt.details ?? evt.error ?? "Unknown stream error");
|
|
1201
|
+
if (cb.onErrorEvent) {
|
|
1202
|
+
cb.onErrorEvent(message);
|
|
1203
|
+
} else {
|
|
1204
|
+
console.error("[chat-stream] unhandled stream error event:", message);
|
|
1205
|
+
cb.onText?.(`
|
|
1206
|
+
|
|
1207
|
+
The agent hit an error and this turn stopped: ${message}`);
|
|
1208
|
+
receivedContent = true;
|
|
1209
|
+
}
|
|
1210
|
+
break;
|
|
1211
|
+
}
|
|
1212
|
+
default: {
|
|
1213
|
+
if (typeof evt.type === "string" && evt.type.startsWith("plan.")) {
|
|
1214
|
+
const submitted = parsePlanSubmittedEvent(evt);
|
|
1215
|
+
const planRecord = evt.data?.plan ?? evt.properties?.plan;
|
|
1216
|
+
const plan = submitted.succeeded ? submitted.value : planRecord ? persistedPartToPlan({ type: "plan", ...planRecord }) : null;
|
|
1217
|
+
if (plan) {
|
|
1218
|
+
cb.onPlan?.(plan);
|
|
1219
|
+
receivedContent = true;
|
|
1220
|
+
} else {
|
|
1221
|
+
console.error("[chat-stream] dropping malformed durable plan line:", evt.type);
|
|
1222
|
+
}
|
|
1223
|
+
}
|
|
1224
|
+
break;
|
|
1225
|
+
}
|
|
1226
|
+
}
|
|
1227
|
+
return { turnId, receivedContent };
|
|
1228
|
+
}
|
|
1229
|
+
async function consumeChatStream(body, cb) {
|
|
1230
|
+
const reader = body.getReader();
|
|
1231
|
+
const decoder = new TextDecoder();
|
|
1232
|
+
let buffer = "";
|
|
1233
|
+
let turnId = null;
|
|
1234
|
+
let receivedContent = false;
|
|
1235
|
+
const handle = (line) => {
|
|
1236
|
+
const r = dispatchChatStreamLine(line, cb);
|
|
1237
|
+
if (r.turnId) {
|
|
1238
|
+
turnId = r.turnId;
|
|
1239
|
+
cb.onTurnId?.(r.turnId);
|
|
1240
|
+
}
|
|
1241
|
+
if (r.receivedContent) receivedContent = true;
|
|
1242
|
+
};
|
|
1243
|
+
for (; ; ) {
|
|
1244
|
+
const { done, value } = await reader.read();
|
|
1245
|
+
if (done) {
|
|
1246
|
+
if (buffer.trim()) handle(buffer);
|
|
1247
|
+
break;
|
|
917
1248
|
}
|
|
1249
|
+
buffer += decoder.decode(value, { stream: true });
|
|
1250
|
+
const lines = buffer.split("\n");
|
|
1251
|
+
buffer = lines.pop() ?? "";
|
|
1252
|
+
for (const line of lines) handle(line);
|
|
918
1253
|
}
|
|
919
|
-
return {
|
|
1254
|
+
return { turnId, receivedContent };
|
|
920
1255
|
}
|
|
921
|
-
function
|
|
922
|
-
const
|
|
923
|
-
|
|
924
|
-
const
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
signal: controller.signal,
|
|
934
|
-
body: JSON.stringify({
|
|
935
|
-
...extra,
|
|
936
|
-
id: submission.id,
|
|
937
|
-
outcome: submission.outcome,
|
|
938
|
-
...submission.data ? { data: submission.data } : {}
|
|
939
|
-
})
|
|
940
|
-
});
|
|
941
|
-
if (res.ok) return { ok: true };
|
|
942
|
-
const failure = await responseErrorMessage(res);
|
|
943
|
-
return { ok: false, expired: res.status === 410, message: failure.message };
|
|
944
|
-
} catch (err) {
|
|
945
|
-
if (controller.signal.aborted) {
|
|
946
|
-
return { ok: false, expired: false, message: INTERACTION_SUBMIT_TIMEOUT_MESSAGE };
|
|
947
|
-
}
|
|
948
|
-
return { ok: false, expired: false, message: err instanceof Error ? err.message : "Failed to submit the answer" };
|
|
949
|
-
} finally {
|
|
950
|
-
clearTimeout(timer);
|
|
1256
|
+
async function streamChatTurn(opts) {
|
|
1257
|
+
const res = await opts.start();
|
|
1258
|
+
if (!res.ok || !res.body) {
|
|
1259
|
+
const err = await res.json().catch(() => ({ error: `HTTP ${res.status}` }));
|
|
1260
|
+
throw new Error(err.error ?? `HTTP ${res.status}`);
|
|
1261
|
+
}
|
|
1262
|
+
let turnId = null;
|
|
1263
|
+
const cb = {
|
|
1264
|
+
...opts.callbacks,
|
|
1265
|
+
onTurnId: (id) => {
|
|
1266
|
+
turnId = id;
|
|
1267
|
+
opts.callbacks.onTurnId?.(id);
|
|
951
1268
|
}
|
|
952
1269
|
};
|
|
1270
|
+
try {
|
|
1271
|
+
return await consumeChatStream(res.body, cb);
|
|
1272
|
+
} catch (transportErr) {
|
|
1273
|
+
if (!turnId || !opts.resume) throw transportErr;
|
|
1274
|
+
opts.onResetForResume?.();
|
|
1275
|
+
const resumed = await opts.resume(turnId, 0);
|
|
1276
|
+
if (!resumed.ok || !resumed.body) throw transportErr;
|
|
1277
|
+
return await consumeChatStream(resumed.body, cb);
|
|
1278
|
+
}
|
|
953
1279
|
}
|
|
954
1280
|
|
|
955
|
-
// src/web-react/
|
|
956
|
-
import {
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
1281
|
+
// src/web-react/chat-composer.tsx
|
|
1282
|
+
import {
|
|
1283
|
+
useCallback,
|
|
1284
|
+
useEffect as useEffect5,
|
|
1285
|
+
useRef as useRef5,
|
|
1286
|
+
useState as useState6
|
|
1287
|
+
} from "react";
|
|
1288
|
+
import { Fragment as Fragment3, jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1289
|
+
function SendGlyph({ className }) {
|
|
1290
|
+
return /* @__PURE__ */ jsx8("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: /* @__PURE__ */ jsx8("path", { d: "M22 2 11 13M22 2l-7 20-4-9-9-4 20-7z" }) });
|
|
960
1291
|
}
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
default: "border-transparent bg-primary text-primary-foreground",
|
|
964
|
-
destructive: "border-transparent bg-destructive/15 text-destructive"
|
|
965
|
-
};
|
|
966
|
-
function InteractionBadge({ variant, children }) {
|
|
967
|
-
return /* @__PURE__ */ jsx5("span", { className: `inline-flex items-center rounded-md border px-2 py-0.5 text-xs font-medium ${BADGE_VARIANT_CLASSES[variant]}`, children });
|
|
1292
|
+
function StopGlyph({ className }) {
|
|
1293
|
+
return /* @__PURE__ */ jsx8("svg", { className, viewBox: "0 0 24 24", fill: "currentColor", "aria-hidden": true, children: /* @__PURE__ */ jsx8("rect", { x: "6", y: "6", width: "12", height: "12", rx: "2" }) });
|
|
968
1294
|
}
|
|
969
|
-
function
|
|
970
|
-
|
|
971
|
-
onClick,
|
|
972
|
-
disabled,
|
|
973
|
-
children
|
|
974
|
-
}) {
|
|
975
|
-
const variantClasses = variant === "primary" ? "bg-primary text-primary-foreground shadow-sm hover:bg-primary/90" : "border border-border bg-transparent text-foreground hover:bg-accent/40";
|
|
976
|
-
return /* @__PURE__ */ jsx5(
|
|
977
|
-
"button",
|
|
978
|
-
{
|
|
979
|
-
type: "button",
|
|
980
|
-
onClick,
|
|
981
|
-
disabled,
|
|
982
|
-
className: `inline-flex items-center gap-1.5 rounded-lg px-3 py-1.5 text-sm font-medium transition disabled:cursor-not-allowed disabled:opacity-40 focus:outline-none focus-visible:ring-2 focus-visible:ring-ring ${variantClasses}`,
|
|
983
|
-
children
|
|
984
|
-
}
|
|
985
|
-
);
|
|
1295
|
+
function PaperclipGlyph({ className }) {
|
|
1296
|
+
return /* @__PURE__ */ jsx8("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: /* @__PURE__ */ jsx8("path", { d: "m21.44 11.05-9.19 9.19a6 6 0 0 1-8.49-8.49l9.19-9.19a4 4 0 0 1 5.66 5.66l-9.2 9.19a2 2 0 0 1-2.83-2.83l8.49-8.48" }) });
|
|
986
1297
|
}
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
multi,
|
|
993
|
-
selectedValues,
|
|
994
|
-
disabled,
|
|
995
|
-
onToggle
|
|
996
|
-
}) {
|
|
997
|
-
return /* @__PURE__ */ jsx5(Fragment3, { children: options.map((option, optionIndex) => {
|
|
998
|
-
const inputId = `${idPrefix}-${optionIndex}`;
|
|
999
|
-
const checked = selectedValues.includes(option.value);
|
|
1000
|
-
return /* @__PURE__ */ jsxs4("label", { htmlFor: inputId, className: "flex cursor-pointer gap-2 rounded-lg border border-border/70 p-3 transition-colors hover:bg-muted/50", children: [
|
|
1001
|
-
/* @__PURE__ */ jsx5(
|
|
1002
|
-
"input",
|
|
1003
|
-
{
|
|
1004
|
-
id: inputId,
|
|
1005
|
-
type: multi ? "checkbox" : "radio",
|
|
1006
|
-
name: groupName,
|
|
1007
|
-
value: option.value,
|
|
1008
|
-
checked,
|
|
1009
|
-
disabled,
|
|
1010
|
-
onChange: () => onToggle(option.value),
|
|
1011
|
-
"aria-labelledby": `${inputId}-label`,
|
|
1012
|
-
"aria-describedby": option.description ? `${inputId}-description` : void 0,
|
|
1013
|
-
className: "mt-0.5 h-4 w-4 shrink-0 accent-primary"
|
|
1014
|
-
}
|
|
1015
|
-
),
|
|
1016
|
-
/* @__PURE__ */ jsxs4("span", { className: "min-w-0", children: [
|
|
1017
|
-
/* @__PURE__ */ jsx5("span", { id: `${inputId}-label`, className: "block text-sm font-medium leading-5 text-foreground", children: option.label }),
|
|
1018
|
-
option.description && /* @__PURE__ */ jsx5("span", { id: `${inputId}-description`, className: "mt-0.5 block text-xs leading-5 text-muted-foreground", children: option.description })
|
|
1019
|
-
] })
|
|
1020
|
-
] }, `${option.value}-${optionIndex}`);
|
|
1021
|
-
}) });
|
|
1298
|
+
function FolderGlyph({ className }) {
|
|
1299
|
+
return /* @__PURE__ */ jsxs6("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: [
|
|
1300
|
+
/* @__PURE__ */ jsx8("path", { d: "M4 20h16a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.93a2 2 0 0 1-1.66-.9l-.82-1.2A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13c0 1.1.9 2 2 2Z" }),
|
|
1301
|
+
/* @__PURE__ */ jsx8("path", { d: "M12 10v6m-3-3h6" })
|
|
1302
|
+
] });
|
|
1022
1303
|
}
|
|
1023
|
-
function
|
|
1024
|
-
return
|
|
1304
|
+
function CloseGlyph({ className }) {
|
|
1305
|
+
return /* @__PURE__ */ jsx8("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", "aria-hidden": true, children: /* @__PURE__ */ jsx8("path", { d: "M18 6 6 18M6 6l12 12" }) });
|
|
1025
1306
|
}
|
|
1026
|
-
function
|
|
1027
|
-
|
|
1028
|
-
let selected = [optionValue];
|
|
1029
|
-
if (field.multi === true) {
|
|
1030
|
-
selected = current.includes(optionValue) ? current.filter((item) => item !== optionValue) : [...current, optionValue];
|
|
1031
|
-
}
|
|
1032
|
-
return { ...values, [field.name]: { ...values[field.name], selected } };
|
|
1307
|
+
function UploadGlyph({ className }) {
|
|
1308
|
+
return /* @__PURE__ */ jsx8("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: /* @__PURE__ */ jsx8("path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4M17 8l-5-5-5 5M12 3v12" }) });
|
|
1033
1309
|
}
|
|
1034
|
-
var
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1310
|
+
var MAX_HEIGHT = 168;
|
|
1311
|
+
function ChatComposer({
|
|
1312
|
+
onSend,
|
|
1313
|
+
onSendParts,
|
|
1314
|
+
onCancel,
|
|
1315
|
+
isStreaming = false,
|
|
1316
|
+
disabled = false,
|
|
1317
|
+
placeholder = "Message the agent\u2026",
|
|
1318
|
+
value,
|
|
1319
|
+
onValueChange,
|
|
1320
|
+
initialValue,
|
|
1321
|
+
seed,
|
|
1322
|
+
onSeedApplied,
|
|
1323
|
+
controls,
|
|
1324
|
+
controlsPlacement = "above",
|
|
1325
|
+
onAttach,
|
|
1326
|
+
onAttachFolder,
|
|
1327
|
+
pendingFiles = [],
|
|
1328
|
+
onRemoveFile,
|
|
1329
|
+
accept,
|
|
1330
|
+
dropTitle = "Drop files to add context",
|
|
1331
|
+
dropDescription = "They attach to your next message.",
|
|
1332
|
+
focusShortcut = true,
|
|
1333
|
+
sendLabel = "Send",
|
|
1049
1334
|
className
|
|
1050
1335
|
}) {
|
|
1051
|
-
const
|
|
1052
|
-
const [
|
|
1053
|
-
const
|
|
1054
|
-
const
|
|
1055
|
-
const
|
|
1056
|
-
const
|
|
1057
|
-
const
|
|
1058
|
-
const
|
|
1059
|
-
const
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1336
|
+
const isControlled = value !== void 0;
|
|
1337
|
+
const [internal, setInternal] = useState6(initialValue ?? "");
|
|
1338
|
+
const text = isControlled ? value : internal;
|
|
1339
|
+
const textareaRef = useRef5(null);
|
|
1340
|
+
const fileInputRef = useRef5(null);
|
|
1341
|
+
const folderInputRef = useRef5(null);
|
|
1342
|
+
const [dragOver, setDragOver] = useState6(false);
|
|
1343
|
+
const dragDepth = useRef5(0);
|
|
1344
|
+
const setText = useCallback(
|
|
1345
|
+
(next) => {
|
|
1346
|
+
if (!isControlled) setInternal(next);
|
|
1347
|
+
onValueChange?.(next);
|
|
1348
|
+
},
|
|
1349
|
+
[isControlled, onValueChange]
|
|
1350
|
+
);
|
|
1351
|
+
useEffect5(() => {
|
|
1352
|
+
const el = textareaRef.current;
|
|
1353
|
+
if (!el) return;
|
|
1354
|
+
el.style.height = "auto";
|
|
1355
|
+
el.style.height = `${Math.min(el.scrollHeight, MAX_HEIGHT)}px`;
|
|
1356
|
+
}, [text]);
|
|
1357
|
+
const prevSeedRef = useRef5(null);
|
|
1358
|
+
const pendingCaretRef = useRef5(null);
|
|
1359
|
+
useEffect5(() => {
|
|
1360
|
+
const prev = prevSeedRef.current;
|
|
1361
|
+
prevSeedRef.current = seed ?? null;
|
|
1362
|
+
if (seed == null || seed === prev || isControlled) return;
|
|
1363
|
+
setText(seed);
|
|
1364
|
+
onSeedApplied?.();
|
|
1365
|
+
const el = textareaRef.current;
|
|
1366
|
+
if (el && el.value === seed) {
|
|
1367
|
+
el.focus();
|
|
1368
|
+
el.setSelectionRange(seed.length, seed.length);
|
|
1369
|
+
} else {
|
|
1370
|
+
pendingCaretRef.current = seed;
|
|
1371
|
+
}
|
|
1372
|
+
}, [seed, setText, onSeedApplied, isControlled]);
|
|
1373
|
+
useEffect5(() => {
|
|
1374
|
+
if (pendingCaretRef.current == null || pendingCaretRef.current !== text)
|
|
1375
|
+
return;
|
|
1376
|
+
pendingCaretRef.current = null;
|
|
1377
|
+
const el = textareaRef.current;
|
|
1378
|
+
if (!el) return;
|
|
1379
|
+
el.focus();
|
|
1380
|
+
el.setSelectionRange(text.length, text.length);
|
|
1381
|
+
}, [text]);
|
|
1382
|
+
useEffect5(() => {
|
|
1383
|
+
if (!focusShortcut || disabled) return;
|
|
1384
|
+
function onKeyDown(e) {
|
|
1385
|
+
if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === "l") {
|
|
1386
|
+
e.preventDefault();
|
|
1387
|
+
textareaRef.current?.focus();
|
|
1388
|
+
}
|
|
1085
1389
|
}
|
|
1086
|
-
|
|
1087
|
-
|
|
1390
|
+
document.addEventListener("keydown", onKeyDown);
|
|
1391
|
+
return () => document.removeEventListener("keydown", onKeyDown);
|
|
1392
|
+
}, [focusShortcut, disabled]);
|
|
1393
|
+
const readyParts = pendingFiles.filter((f) => f.status === "ready" && f.part).map((f) => f.part);
|
|
1394
|
+
const hasSendable = onSendParts ? text.trim().length > 0 || readyParts.length > 0 : text.trim().length > 0;
|
|
1395
|
+
const canSend = hasSendable && !isStreaming && !disabled;
|
|
1396
|
+
const send = useCallback(() => {
|
|
1397
|
+
const trimmed = text.trim();
|
|
1398
|
+
if (isStreaming || disabled) return;
|
|
1399
|
+
if (onSendParts) {
|
|
1400
|
+
const parts = pendingFiles.filter((f) => f.status === "ready" && f.part).map((f) => f.part);
|
|
1401
|
+
if (!trimmed && parts.length === 0) return;
|
|
1402
|
+
onSendParts(trimmed, parts);
|
|
1403
|
+
setText("");
|
|
1088
1404
|
return;
|
|
1089
1405
|
}
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1406
|
+
if (!trimmed) return;
|
|
1407
|
+
onSend?.(trimmed);
|
|
1408
|
+
setText("");
|
|
1409
|
+
}, [text, isStreaming, disabled, onSend, onSendParts, pendingFiles, setText]);
|
|
1410
|
+
const handleKeyDown = (e) => {
|
|
1411
|
+
if (e.nativeEvent.isComposing) return;
|
|
1412
|
+
if (e.key === "Enter" && !e.shiftKey) {
|
|
1413
|
+
e.preventDefault();
|
|
1414
|
+
send();
|
|
1096
1415
|
}
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1416
|
+
};
|
|
1417
|
+
const handleFileChange = (e) => {
|
|
1418
|
+
if (e.target.files?.length) onAttach?.(e.target.files);
|
|
1419
|
+
e.target.value = "";
|
|
1420
|
+
};
|
|
1421
|
+
const handleFolderChange = (e) => {
|
|
1422
|
+
if (e.target.files?.length) (onAttachFolder ?? onAttach)?.(e.target.files);
|
|
1423
|
+
e.target.value = "";
|
|
1424
|
+
};
|
|
1425
|
+
const handleDragEnter = useCallback((e) => {
|
|
1426
|
+
e.preventDefault();
|
|
1427
|
+
e.stopPropagation();
|
|
1428
|
+
dragDepth.current++;
|
|
1429
|
+
if (e.dataTransfer?.types.includes("Files")) setDragOver(true);
|
|
1430
|
+
}, []);
|
|
1431
|
+
const handleDragLeave = useCallback((e) => {
|
|
1432
|
+
e.preventDefault();
|
|
1433
|
+
e.stopPropagation();
|
|
1434
|
+
dragDepth.current--;
|
|
1435
|
+
if (dragDepth.current <= 0) {
|
|
1436
|
+
dragDepth.current = 0;
|
|
1437
|
+
setDragOver(false);
|
|
1117
1438
|
}
|
|
1118
|
-
}
|
|
1119
|
-
const
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1439
|
+
}, []);
|
|
1440
|
+
const handleDragOver = useCallback((e) => {
|
|
1441
|
+
e.preventDefault();
|
|
1442
|
+
e.stopPropagation();
|
|
1443
|
+
if (e.dataTransfer) e.dataTransfer.dropEffect = "copy";
|
|
1444
|
+
}, []);
|
|
1445
|
+
const handleDrop = useCallback(
|
|
1446
|
+
(e) => {
|
|
1447
|
+
e.preventDefault();
|
|
1448
|
+
e.stopPropagation();
|
|
1449
|
+
dragDepth.current = 0;
|
|
1450
|
+
setDragOver(false);
|
|
1451
|
+
const files = e.dataTransfer?.files;
|
|
1452
|
+
if (files?.length) onAttach?.(files);
|
|
1453
|
+
},
|
|
1454
|
+
[onAttach]
|
|
1455
|
+
);
|
|
1456
|
+
const folderChips = pendingFiles.filter((f) => f.kind === "folder");
|
|
1457
|
+
const fileChips = pendingFiles.filter((f) => f.kind !== "folder");
|
|
1458
|
+
const showFooter = controls != null && controlsPlacement === "footer";
|
|
1459
|
+
const showAbove = controls != null && controlsPlacement === "above";
|
|
1460
|
+
return /* @__PURE__ */ jsxs6(
|
|
1461
|
+
"div",
|
|
1462
|
+
{
|
|
1463
|
+
className: `relative ${className ?? ""}`,
|
|
1464
|
+
onDragEnter: onAttach ? handleDragEnter : void 0,
|
|
1465
|
+
onDragLeave: onAttach ? handleDragLeave : void 0,
|
|
1466
|
+
onDragOver: onAttach ? handleDragOver : void 0,
|
|
1467
|
+
onDrop: onAttach ? handleDrop : void 0,
|
|
1468
|
+
children: [
|
|
1469
|
+
dragOver && /* @__PURE__ */ jsx8("div", { className: "pointer-events-none absolute inset-0 z-10 flex items-center justify-center rounded-2xl border-2 border-dashed border-primary/50 bg-card/95", children: /* @__PURE__ */ jsxs6("div", { className: "text-center", children: [
|
|
1470
|
+
/* @__PURE__ */ jsx8("span", { className: "mx-auto mb-2 flex h-11 w-11 items-center justify-center rounded-xl bg-primary/10 text-primary", children: /* @__PURE__ */ jsx8(UploadGlyph, { className: "h-5 w-5" }) }),
|
|
1471
|
+
/* @__PURE__ */ jsx8("p", { className: "text-sm font-semibold text-foreground", children: dropTitle }),
|
|
1472
|
+
/* @__PURE__ */ jsx8("p", { className: "mt-0.5 text-xs text-muted-foreground", children: dropDescription })
|
|
1473
|
+
] }) }),
|
|
1474
|
+
showAbove && /* @__PURE__ */ jsx8("div", { className: "mb-1.5 flex flex-wrap items-center gap-1.5 px-1", children: controls }),
|
|
1475
|
+
pendingFiles.length > 0 && /* @__PURE__ */ jsx8("div", { className: "mb-2 flex flex-wrap gap-1.5", children: [...folderChips, ...fileChips].map((f) => /* @__PURE__ */ jsxs6(
|
|
1476
|
+
"span",
|
|
1477
|
+
{
|
|
1478
|
+
className: `inline-flex items-center gap-1.5 rounded-full border px-2.5 py-1 text-xs ${f.status === "error" ? "border-destructive/40 text-destructive" : "border-border bg-muted/50 text-foreground"}`,
|
|
1479
|
+
children: [
|
|
1480
|
+
f.kind === "folder" ? /* @__PURE__ */ jsx8(FolderGlyph, { className: "h-3 w-3 shrink-0" }) : /* @__PURE__ */ jsx8(PaperclipGlyph, { className: "h-3 w-3 shrink-0" }),
|
|
1481
|
+
/* @__PURE__ */ jsx8("span", { className: "max-w-[150px] truncate", children: f.name }),
|
|
1482
|
+
f.fileCount !== void 0 && /* @__PURE__ */ jsxs6("span", { className: "text-muted-foreground", children: [
|
|
1483
|
+
"(",
|
|
1484
|
+
f.fileCount,
|
|
1485
|
+
")"
|
|
1486
|
+
] }),
|
|
1487
|
+
f.status === "uploading" && /* @__PURE__ */ jsx8("span", { className: "h-3 w-3 animate-spin rounded-full border-2 border-primary border-t-transparent" }),
|
|
1488
|
+
onRemoveFile && /* @__PURE__ */ jsx8(
|
|
1489
|
+
"button",
|
|
1490
|
+
{
|
|
1491
|
+
type: "button",
|
|
1492
|
+
"aria-label": `Remove ${f.name}`,
|
|
1493
|
+
onClick: () => onRemoveFile(f.id),
|
|
1494
|
+
className: "rounded p-0.5 text-muted-foreground transition hover:text-foreground focus:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
1495
|
+
children: /* @__PURE__ */ jsx8(CloseGlyph, { className: "h-3 w-3" })
|
|
1496
|
+
}
|
|
1497
|
+
)
|
|
1498
|
+
]
|
|
1499
|
+
},
|
|
1500
|
+
f.id
|
|
1501
|
+
)) }),
|
|
1502
|
+
/* @__PURE__ */ jsxs6("div", { className: "flex items-end gap-2 rounded-2xl border border-border bg-card px-2.5 py-2 transition focus-within:border-primary/40 focus-within:ring-2 focus-within:ring-primary/15", children: [
|
|
1503
|
+
onAttach && /* @__PURE__ */ jsxs6(Fragment3, { children: [
|
|
1504
|
+
/* @__PURE__ */ jsx8(
|
|
1505
|
+
"button",
|
|
1506
|
+
{
|
|
1507
|
+
type: "button",
|
|
1508
|
+
onClick: () => fileInputRef.current?.click(),
|
|
1509
|
+
disabled,
|
|
1510
|
+
"aria-label": "Attach files",
|
|
1511
|
+
title: "Attach files",
|
|
1512
|
+
className: "mb-0.5 shrink-0 rounded-lg p-2 text-muted-foreground transition hover:bg-accent/40 hover:text-foreground disabled:opacity-50 focus:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
1513
|
+
children: /* @__PURE__ */ jsx8(PaperclipGlyph, { className: "h-4 w-4" })
|
|
1514
|
+
}
|
|
1515
|
+
),
|
|
1516
|
+
/* @__PURE__ */ jsx8("input", { ref: fileInputRef, type: "file", multiple: true, className: "hidden", accept, onChange: handleFileChange })
|
|
1517
|
+
] }),
|
|
1518
|
+
onAttachFolder && /* @__PURE__ */ jsxs6(Fragment3, { children: [
|
|
1519
|
+
/* @__PURE__ */ jsx8(
|
|
1520
|
+
"button",
|
|
1521
|
+
{
|
|
1522
|
+
type: "button",
|
|
1523
|
+
onClick: () => folderInputRef.current?.click(),
|
|
1524
|
+
disabled,
|
|
1525
|
+
"aria-label": "Attach folder",
|
|
1526
|
+
title: "Attach folder",
|
|
1527
|
+
className: "mb-0.5 shrink-0 rounded-lg p-2 text-muted-foreground transition hover:bg-accent/40 hover:text-foreground disabled:opacity-50 focus:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
1528
|
+
children: /* @__PURE__ */ jsx8(FolderGlyph, { className: "h-4 w-4" })
|
|
1529
|
+
}
|
|
1530
|
+
),
|
|
1531
|
+
/* @__PURE__ */ jsx8(
|
|
1532
|
+
"input",
|
|
1533
|
+
{
|
|
1534
|
+
ref: folderInputRef,
|
|
1535
|
+
type: "file",
|
|
1536
|
+
multiple: true,
|
|
1537
|
+
className: "hidden",
|
|
1538
|
+
onChange: handleFolderChange,
|
|
1539
|
+
...{ webkitdirectory: "" }
|
|
1540
|
+
}
|
|
1541
|
+
)
|
|
1542
|
+
] }),
|
|
1543
|
+
/* @__PURE__ */ jsx8(
|
|
1544
|
+
"textarea",
|
|
1145
1545
|
{
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1546
|
+
ref: textareaRef,
|
|
1547
|
+
value: text,
|
|
1548
|
+
onChange: (e) => setText(e.target.value),
|
|
1549
|
+
onKeyDown: handleKeyDown,
|
|
1550
|
+
placeholder,
|
|
1151
1551
|
disabled,
|
|
1152
|
-
|
|
1552
|
+
rows: 1,
|
|
1553
|
+
"aria-label": "Message input",
|
|
1554
|
+
className: "max-h-[168px] min-h-[40px] flex-1 resize-none bg-transparent px-1.5 py-2 text-[15px] leading-6 text-foreground outline-none placeholder:text-muted-foreground disabled:opacity-50"
|
|
1153
1555
|
}
|
|
1154
1556
|
),
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
type: "text",
|
|
1159
|
-
value: value.custom ?? "",
|
|
1160
|
-
disabled,
|
|
1161
|
-
onChange: (event) => setFieldValue(field.name, { custom: event.target.value }),
|
|
1162
|
-
placeholder: "Other \u2014 type your own answer",
|
|
1163
|
-
"aria-label": `Custom answer for ${field.label}`,
|
|
1164
|
-
className: FIELD_INPUT_CLASSES
|
|
1165
|
-
}
|
|
1166
|
-
)
|
|
1167
|
-
] }) : field.type === "boolean" ? /* @__PURE__ */ jsx5("div", { className: "flex gap-4", children: ["true", "false"].map((boolValue) => /* @__PURE__ */ jsxs4("label", { className: "flex cursor-pointer items-center gap-2 text-sm text-foreground", children: [
|
|
1168
|
-
/* @__PURE__ */ jsx5(
|
|
1169
|
-
"input",
|
|
1557
|
+
showFooter && /* @__PURE__ */ jsx8("div", { className: "mb-0.5 flex shrink-0 items-center gap-1.5", children: controls }),
|
|
1558
|
+
isStreaming ? /* @__PURE__ */ jsxs6(
|
|
1559
|
+
"button",
|
|
1170
1560
|
{
|
|
1171
|
-
type: "
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
"textarea",
|
|
1204
|
-
{
|
|
1205
|
-
value: value.text ?? "",
|
|
1206
|
-
disabled,
|
|
1207
|
-
"aria-label": field.label,
|
|
1208
|
-
onChange: (event) => setFieldValue(field.name, { text: event.target.value }),
|
|
1209
|
-
rows: 3,
|
|
1210
|
-
placeholder: field.type === "text" ? field.placeholder : void 0,
|
|
1211
|
-
className: FIELD_INPUT_CLASSES
|
|
1212
|
-
}
|
|
1213
|
-
)
|
|
1214
|
-
] }, field.name);
|
|
1215
|
-
}) }),
|
|
1216
|
-
error && /* @__PURE__ */ jsx5("p", { className: "mt-3 text-xs text-destructive", children: error }),
|
|
1217
|
-
terminalNote && /* @__PURE__ */ jsx5("p", { className: "mt-3 text-xs text-muted-foreground", children: terminalNote }),
|
|
1218
|
-
showSubmitButton && /* @__PURE__ */ jsx5("div", { className: "mt-4 flex items-center justify-end gap-2", children: /* @__PURE__ */ jsx5(InteractionActionButton, { onClick: () => void submit(), disabled: disabled || !answerData, children: submitLabel }) }),
|
|
1219
|
-
answered && /* @__PURE__ */ jsx5("div", { className: "mt-4 flex items-center justify-end", children: /* @__PURE__ */ jsxs4("span", { className: "inline-flex items-center gap-1 text-xs text-muted-foreground", children: [
|
|
1220
|
-
/* @__PURE__ */ jsx5(CheckGlyph, { className: "h-3 w-3" }),
|
|
1221
|
-
"Answered"
|
|
1222
|
-
] }) }),
|
|
1223
|
-
lateAnswerSent && /* @__PURE__ */ jsx5("div", { className: "mt-4 flex items-center justify-end", children: /* @__PURE__ */ jsxs4("span", { className: "inline-flex items-center gap-1 text-xs text-muted-foreground", children: [
|
|
1224
|
-
/* @__PURE__ */ jsx5(CheckGlyph, { className: "h-3 w-3" }),
|
|
1225
|
-
"Sent as new message"
|
|
1226
|
-
] }) })
|
|
1227
|
-
] });
|
|
1561
|
+
type: "button",
|
|
1562
|
+
onClick: onCancel,
|
|
1563
|
+
"aria-label": "Stop response",
|
|
1564
|
+
className: "mb-0.5 inline-flex shrink-0 items-center gap-1.5 rounded-full bg-destructive/15 px-3.5 py-2 text-sm font-medium text-destructive transition hover:bg-destructive/25 focus:outline-none focus-visible:ring-2 focus-visible:ring-destructive/50",
|
|
1565
|
+
children: [
|
|
1566
|
+
/* @__PURE__ */ jsx8(StopGlyph, { className: "h-3.5 w-3.5" }),
|
|
1567
|
+
/* @__PURE__ */ jsx8("span", { children: "Stop" })
|
|
1568
|
+
]
|
|
1569
|
+
}
|
|
1570
|
+
) : /* @__PURE__ */ jsxs6(
|
|
1571
|
+
"button",
|
|
1572
|
+
{
|
|
1573
|
+
type: "button",
|
|
1574
|
+
onClick: send,
|
|
1575
|
+
disabled: !canSend,
|
|
1576
|
+
"aria-label": sendLabel,
|
|
1577
|
+
className: "mb-0.5 inline-flex shrink-0 items-center gap-1.5 rounded-full bg-primary px-3.5 py-2 text-sm font-medium text-primary-foreground shadow-sm transition hover:bg-primary/90 disabled:cursor-not-allowed disabled:opacity-40 focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-card",
|
|
1578
|
+
children: [
|
|
1579
|
+
/* @__PURE__ */ jsx8(SendGlyph, { className: "h-3.5 w-3.5" }),
|
|
1580
|
+
/* @__PURE__ */ jsx8("span", { children: sendLabel })
|
|
1581
|
+
]
|
|
1582
|
+
}
|
|
1583
|
+
)
|
|
1584
|
+
] }),
|
|
1585
|
+
focusShortcut && /* @__PURE__ */ jsx8("div", { className: "mt-1.5 flex justify-end px-1", children: /* @__PURE__ */ jsxs6("span", { className: "text-xs text-muted-foreground", children: [
|
|
1586
|
+
/* @__PURE__ */ jsx8("kbd", { className: "rounded border border-border bg-background px-1 py-0.5 text-[10px]", children: "Cmd" }),
|
|
1587
|
+
/* @__PURE__ */ jsx8("kbd", { className: "ml-0.5 rounded border border-border bg-background px-1 py-0.5 text-[10px]", children: "L" }),
|
|
1588
|
+
/* @__PURE__ */ jsx8("span", { className: "ml-1", children: "to focus" })
|
|
1589
|
+
] }) })
|
|
1590
|
+
]
|
|
1591
|
+
}
|
|
1592
|
+
);
|
|
1228
1593
|
}
|
|
1229
1594
|
|
|
1230
|
-
// src/web-react/
|
|
1231
|
-
import {
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1595
|
+
// src/web-react/durable-plan-flow.ts
|
|
1596
|
+
import { useCallback as useCallback2, useEffect as useEffect6, useRef as useRef6, useState as useState7 } from "react";
|
|
1597
|
+
var DurablePlanClientError = class extends Error {
|
|
1598
|
+
constructor(message, status, code, currentPlan) {
|
|
1599
|
+
super(message);
|
|
1600
|
+
this.status = status;
|
|
1601
|
+
this.code = code;
|
|
1602
|
+
this.currentPlan = currentPlan;
|
|
1603
|
+
this.name = "DurablePlanClientError";
|
|
1604
|
+
}
|
|
1605
|
+
status;
|
|
1606
|
+
code;
|
|
1607
|
+
currentPlan;
|
|
1608
|
+
};
|
|
1609
|
+
function recordOf(value) {
|
|
1610
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
1611
|
+
}
|
|
1612
|
+
function readPlan(value) {
|
|
1613
|
+
const plan = recordOf(value);
|
|
1614
|
+
if (!plan) return null;
|
|
1615
|
+
const planId = typeof plan.planId === "string" ? plan.planId : typeof plan.id === "string" ? plan.id : null;
|
|
1616
|
+
if (!planId || typeof plan.revision !== "number" || typeof plan.body !== "string" || typeof plan.submittedAt !== "string" || typeof plan.status !== "string") return null;
|
|
1617
|
+
return { ...plan, planId };
|
|
1618
|
+
}
|
|
1619
|
+
function receiptIdentity(plan, followUp) {
|
|
1620
|
+
if (typeof followUp.receiptId === "string" && followUp.receiptId) return followUp.receiptId;
|
|
1621
|
+
const turnId = typeof followUp.turnId === "string" ? followUp.turnId : "";
|
|
1622
|
+
return `${plan.planId}:${plan.revision}:${turnId}`;
|
|
1623
|
+
}
|
|
1624
|
+
function parseDecisionResult(value) {
|
|
1625
|
+
const body = recordOf(value);
|
|
1626
|
+
const plan = readPlan(body?.plan);
|
|
1627
|
+
if (!body || !plan) return null;
|
|
1628
|
+
const rawFollowUp = recordOf(body.followUp) ?? recordOf(body.receipt);
|
|
1629
|
+
const followUp = rawFollowUp && typeof rawFollowUp.turnId === "string" ? {
|
|
1630
|
+
receiptId: receiptIdentity(plan, rawFollowUp),
|
|
1631
|
+
planId: plan.planId,
|
|
1632
|
+
revision: plan.revision,
|
|
1633
|
+
turnId: rawFollowUp.turnId,
|
|
1634
|
+
state: typeof rawFollowUp.state === "string" ? rawFollowUp.state : "unknown"
|
|
1635
|
+
} : void 0;
|
|
1636
|
+
return {
|
|
1637
|
+
plan,
|
|
1638
|
+
...followUp ? { followUp } : {},
|
|
1639
|
+
idempotent: body.idempotent === true || body.replayed === true,
|
|
1640
|
+
...body.projectionPending === true ? { projectionPending: true } : {},
|
|
1641
|
+
...body.effectPending === true ? { effectPending: true } : {}
|
|
1642
|
+
};
|
|
1235
1643
|
}
|
|
1236
|
-
function
|
|
1237
|
-
return
|
|
1644
|
+
async function responseBody(response) {
|
|
1645
|
+
return recordOf(await response.json().catch(() => null)) ?? {};
|
|
1238
1646
|
}
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1647
|
+
function createDurablePlanDecisionClient(options) {
|
|
1648
|
+
const fetchImpl = options.fetchImpl ?? fetch;
|
|
1649
|
+
const urlFor = (input) => typeof options.url === "function" ? options.url(input) : options.url;
|
|
1650
|
+
const read = async (response) => {
|
|
1651
|
+
const body = await responseBody(response);
|
|
1652
|
+
const result = parseDecisionResult(body);
|
|
1653
|
+
if (response.ok && result) return result;
|
|
1654
|
+
const currentPlan = readPlan(body.plan) ?? void 0;
|
|
1655
|
+
const message = typeof body.error === "string" ? body.error : typeof body.message === "string" ? body.message : `Plan request failed (${response.status})`;
|
|
1656
|
+
throw new DurablePlanClientError(
|
|
1657
|
+
message,
|
|
1658
|
+
response.status,
|
|
1659
|
+
typeof body.code === "string" ? body.code : void 0,
|
|
1660
|
+
currentPlan
|
|
1661
|
+
);
|
|
1662
|
+
};
|
|
1663
|
+
return {
|
|
1664
|
+
async current(input) {
|
|
1665
|
+
const rawUrl = urlFor(input);
|
|
1666
|
+
const url = new URL(rawUrl, globalThis.location?.origin ?? "http://localhost");
|
|
1667
|
+
url.searchParams.set("planId", input.planId);
|
|
1668
|
+
if (input.revision !== void 0) url.searchParams.set("revision", String(input.revision));
|
|
1669
|
+
const target = /^https?:/.test(rawUrl) ? url.toString() : `${url.pathname}${url.search}`;
|
|
1670
|
+
return read(await fetchImpl(target, { method: "GET" }));
|
|
1671
|
+
},
|
|
1672
|
+
async decide(input) {
|
|
1673
|
+
const extra = typeof options.body === "function" ? options.body(input) : options.body ?? {};
|
|
1674
|
+
return read(await fetchImpl(urlFor(input), {
|
|
1675
|
+
method: "POST",
|
|
1676
|
+
headers: { "Content-Type": "application/json" },
|
|
1677
|
+
body: JSON.stringify({ ...extra, ...input })
|
|
1678
|
+
}));
|
|
1270
1679
|
}
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1680
|
+
};
|
|
1681
|
+
}
|
|
1682
|
+
function useDurablePlanFlow(options) {
|
|
1683
|
+
const [plan, setPlan] = useState7(options.plan);
|
|
1684
|
+
const [deciding, setDeciding] = useState7(null);
|
|
1685
|
+
const [restoring, setRestoring] = useState7(false);
|
|
1686
|
+
const [error, setError] = useState7(null);
|
|
1687
|
+
const attachments = useRef6(/* @__PURE__ */ new Map());
|
|
1688
|
+
const decisionInFlight = useRef6(false);
|
|
1689
|
+
useEffect6(() => setPlan(options.plan), [options.plan]);
|
|
1690
|
+
const apply = useCallback2(async (result) => {
|
|
1691
|
+
setPlan(result.plan);
|
|
1692
|
+
options.onUpdated?.(result.plan);
|
|
1693
|
+
const receipt = result.followUp;
|
|
1694
|
+
if (!receipt || !options.attachFollowUp) return;
|
|
1695
|
+
let pending = attachments.current.get(receipt.receiptId);
|
|
1696
|
+
if (!pending) {
|
|
1697
|
+
pending = Promise.resolve(options.attachFollowUp(receipt));
|
|
1698
|
+
attachments.current.set(receipt.receiptId, pending);
|
|
1699
|
+
void pending.finally(() => attachments.current.delete(receipt.receiptId));
|
|
1700
|
+
}
|
|
1701
|
+
await pending;
|
|
1702
|
+
}, [options.attachFollowUp, options.onUpdated]);
|
|
1703
|
+
const decide = useCallback2(async (decision, feedback) => {
|
|
1704
|
+
if (decisionInFlight.current) return null;
|
|
1705
|
+
decisionInFlight.current = true;
|
|
1706
|
+
setDeciding(decision);
|
|
1278
1707
|
setError(null);
|
|
1279
1708
|
try {
|
|
1280
|
-
const result = await
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1709
|
+
const result = await options.client.decide({
|
|
1710
|
+
planId: plan.planId,
|
|
1711
|
+
revision: plan.revision,
|
|
1712
|
+
decision,
|
|
1713
|
+
...feedback?.trim() ? { feedback: feedback.trim() } : {}
|
|
1714
|
+
});
|
|
1715
|
+
await apply(result);
|
|
1716
|
+
return result;
|
|
1717
|
+
} catch (cause) {
|
|
1718
|
+
if (cause instanceof DurablePlanClientError && cause.currentPlan) {
|
|
1719
|
+
setPlan(cause.currentPlan);
|
|
1720
|
+
options.onUpdated?.(cause.currentPlan);
|
|
1291
1721
|
}
|
|
1292
|
-
setError(
|
|
1722
|
+
setError(cause instanceof Error ? cause.message : "Could not decide the plan.");
|
|
1723
|
+
return null;
|
|
1293
1724
|
} finally {
|
|
1294
|
-
|
|
1295
|
-
|
|
1725
|
+
decisionInFlight.current = false;
|
|
1726
|
+
setDeciding(null);
|
|
1727
|
+
}
|
|
1728
|
+
}, [apply, options.client, options.onUpdated, plan.planId, plan.revision]);
|
|
1729
|
+
const restore = useCallback2(async () => {
|
|
1730
|
+
setRestoring(true);
|
|
1731
|
+
setError(null);
|
|
1732
|
+
try {
|
|
1733
|
+
const result = await options.client.current({ planId: plan.planId, revision: plan.revision });
|
|
1734
|
+
await apply(result);
|
|
1735
|
+
return result;
|
|
1736
|
+
} catch (cause) {
|
|
1737
|
+
setError(cause instanceof Error ? cause.message : "Could not restore the plan.");
|
|
1738
|
+
return null;
|
|
1739
|
+
} finally {
|
|
1740
|
+
setRestoring(false);
|
|
1296
1741
|
}
|
|
1742
|
+
}, [apply, options.client, plan.planId, plan.revision]);
|
|
1743
|
+
return { plan, deciding, restoring, error, decide, restore, clearError: () => setError(null) };
|
|
1744
|
+
}
|
|
1745
|
+
|
|
1746
|
+
// src/web-react/durable-interaction-submit.ts
|
|
1747
|
+
function attemptStorageKey(namespace, interactionId) {
|
|
1748
|
+
return `${namespace}:${encodeURIComponent(interactionId)}`;
|
|
1749
|
+
}
|
|
1750
|
+
function storedAttempts(storage, key) {
|
|
1751
|
+
try {
|
|
1752
|
+
const value = JSON.parse(storage.getItem(key) ?? "{}");
|
|
1753
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
1754
|
+
} catch {
|
|
1755
|
+
return {};
|
|
1297
1756
|
}
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
return
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1757
|
+
}
|
|
1758
|
+
function createSessionInteractionAttemptStore(storage, namespace = "agent-app:interaction-attempt") {
|
|
1759
|
+
return {
|
|
1760
|
+
get(id, signature) {
|
|
1761
|
+
return storedAttempts(storage, attemptStorageKey(namespace, id))[signature] ?? null;
|
|
1762
|
+
},
|
|
1763
|
+
set(id, signature, attemptKey) {
|
|
1764
|
+
const key = attemptStorageKey(namespace, id);
|
|
1765
|
+
storage.setItem(key, JSON.stringify({ ...storedAttempts(storage, key), [signature]: attemptKey }));
|
|
1766
|
+
},
|
|
1767
|
+
delete(id, signature) {
|
|
1768
|
+
const key = attemptStorageKey(namespace, id);
|
|
1769
|
+
const attempts = storedAttempts(storage, key);
|
|
1770
|
+
delete attempts[signature];
|
|
1771
|
+
if (Object.keys(attempts).length === 0) storage.removeItem(key);
|
|
1772
|
+
else storage.setItem(key, JSON.stringify(attempts));
|
|
1773
|
+
}
|
|
1774
|
+
};
|
|
1775
|
+
}
|
|
1776
|
+
function createMemoryInteractionAttemptStore() {
|
|
1777
|
+
const attempts = /* @__PURE__ */ new Map();
|
|
1778
|
+
const key = (id, signature) => `${id}\0${signature}`;
|
|
1779
|
+
return {
|
|
1780
|
+
get: (id, signature) => attempts.get(key(id, signature)) ?? null,
|
|
1781
|
+
set: (id, signature, attemptKey) => attempts.set(key(id, signature), attemptKey),
|
|
1782
|
+
delete: (id, signature) => {
|
|
1783
|
+
attempts.delete(key(id, signature));
|
|
1784
|
+
}
|
|
1785
|
+
};
|
|
1786
|
+
}
|
|
1787
|
+
function stableValue(value) {
|
|
1788
|
+
if (Array.isArray(value)) return value.map(stableValue);
|
|
1789
|
+
if (!value || typeof value !== "object") return value;
|
|
1790
|
+
return Object.fromEntries(Object.entries(value).sort(([left], [right]) => left.localeCompare(right)).map(([key, nested]) => [key, stableValue(nested)]));
|
|
1791
|
+
}
|
|
1792
|
+
function interactionSubmissionSignature(submission) {
|
|
1793
|
+
return JSON.stringify(stableValue(submission));
|
|
1794
|
+
}
|
|
1795
|
+
function defaultAttemptKey() {
|
|
1796
|
+
if (globalThis.crypto?.randomUUID) return globalThis.crypto.randomUUID();
|
|
1797
|
+
return `attempt-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
1798
|
+
}
|
|
1799
|
+
function createDurableInteractionAnswerSubmitter(options) {
|
|
1800
|
+
const timeoutMs = options.timeoutMs ?? INTERACTION_SUBMIT_TIMEOUT_MS;
|
|
1801
|
+
const fetchImpl = options.fetchImpl ?? fetch;
|
|
1802
|
+
return async (submission) => {
|
|
1803
|
+
const signature = interactionSubmissionSignature(submission);
|
|
1804
|
+
let attemptKey;
|
|
1805
|
+
try {
|
|
1806
|
+
attemptKey = options.attempts.get(submission.id, signature) ?? "";
|
|
1807
|
+
if (!attemptKey) {
|
|
1808
|
+
attemptKey = (options.createAttemptKey ?? defaultAttemptKey)();
|
|
1809
|
+
options.attempts.set(submission.id, signature, attemptKey);
|
|
1810
|
+
}
|
|
1811
|
+
} catch (cause) {
|
|
1812
|
+
return {
|
|
1813
|
+
ok: false,
|
|
1814
|
+
expired: false,
|
|
1815
|
+
message: cause instanceof Error ? cause.message : "Failed to submit the answer"
|
|
1816
|
+
};
|
|
1817
|
+
}
|
|
1818
|
+
const url = typeof options.url === "function" ? options.url(submission) : options.url;
|
|
1819
|
+
const extra = typeof options.body === "function" ? options.body(submission) : options.body ?? {};
|
|
1820
|
+
const controller = new AbortController();
|
|
1821
|
+
const timer = setTimeout(() => controller.abort(INTERACTION_SUBMIT_TIMEOUT_MESSAGE), timeoutMs);
|
|
1822
|
+
try {
|
|
1823
|
+
const response = await fetchImpl(url, {
|
|
1824
|
+
method: "POST",
|
|
1825
|
+
headers: { "Content-Type": "application/json" },
|
|
1826
|
+
signal: controller.signal,
|
|
1827
|
+
body: JSON.stringify({
|
|
1828
|
+
...extra,
|
|
1829
|
+
id: submission.id,
|
|
1830
|
+
outcome: submission.outcome,
|
|
1831
|
+
attemptKey,
|
|
1832
|
+
...submission.data ? { data: submission.data } : {}
|
|
1833
|
+
})
|
|
1834
|
+
});
|
|
1835
|
+
if (response.ok) {
|
|
1836
|
+
options.attempts.delete(submission.id, signature);
|
|
1837
|
+
return { ok: true };
|
|
1838
|
+
}
|
|
1839
|
+
const failure = await responseErrorMessage(response);
|
|
1840
|
+
if (response.status < 500) options.attempts.delete(submission.id, signature);
|
|
1841
|
+
return { ok: false, expired: response.status === 410, message: failure.message };
|
|
1842
|
+
} catch (cause) {
|
|
1843
|
+
if (controller.signal.aborted) {
|
|
1844
|
+
return { ok: false, expired: false, message: INTERACTION_SUBMIT_TIMEOUT_MESSAGE };
|
|
1845
|
+
}
|
|
1846
|
+
return {
|
|
1847
|
+
ok: false,
|
|
1848
|
+
expired: false,
|
|
1849
|
+
message: cause instanceof Error ? cause.message : "Failed to submit the answer"
|
|
1850
|
+
};
|
|
1851
|
+
} finally {
|
|
1852
|
+
clearTimeout(timer);
|
|
1853
|
+
}
|
|
1854
|
+
};
|
|
1368
1855
|
}
|
|
1369
1856
|
|
|
1370
1857
|
// src/web-react/use-chat-interactions.ts
|
|
1371
|
-
import { useCallback as
|
|
1858
|
+
import { useCallback as useCallback3, useMemo as useMemo4, useState as useState8 } from "react";
|
|
1372
1859
|
function hasPendingContentDuplicate(list, interaction) {
|
|
1373
1860
|
if (interaction.status !== "pending") return false;
|
|
1374
1861
|
const signature = questionInteractionContentSignature(interaction);
|
|
@@ -1382,7 +1869,15 @@ function upsertChatInteraction(list, interaction) {
|
|
|
1382
1869
|
return [...list, interaction];
|
|
1383
1870
|
}
|
|
1384
1871
|
const existing = list[index];
|
|
1385
|
-
if (!existing
|
|
1872
|
+
if (!existing) return list;
|
|
1873
|
+
if (isTerminalInteractionStatus(existing.status)) {
|
|
1874
|
+
if (existing.status === interaction.status && (!existing.answers && interaction.answers || !existing.cancelReason && interaction.cancelReason)) {
|
|
1875
|
+
const next2 = [...list];
|
|
1876
|
+
next2[index] = { ...existing, ...interaction };
|
|
1877
|
+
return next2;
|
|
1878
|
+
}
|
|
1879
|
+
return list;
|
|
1880
|
+
}
|
|
1386
1881
|
const next = [...list];
|
|
1387
1882
|
next[index] = interaction;
|
|
1388
1883
|
return next;
|
|
@@ -1399,50 +1894,74 @@ function cancelChatInteraction(list, cancel) {
|
|
|
1399
1894
|
};
|
|
1400
1895
|
return next;
|
|
1401
1896
|
}
|
|
1402
|
-
function resolveChatInteraction(list, id, status) {
|
|
1897
|
+
function resolveChatInteraction(list, id, status, answers) {
|
|
1403
1898
|
const index = list.findIndex((item) => item.id === id);
|
|
1404
1899
|
const existing = list[index];
|
|
1405
1900
|
if (!existing || existing.status !== "pending") return list;
|
|
1406
1901
|
const next = [...list];
|
|
1407
|
-
next[index] = { ...existing, status };
|
|
1902
|
+
next[index] = { ...existing, status, ...answers ? { answers } : {} };
|
|
1408
1903
|
return next;
|
|
1409
1904
|
}
|
|
1410
1905
|
function terminalizePendingChatInteractions(list, status) {
|
|
1411
1906
|
if (!list.some((item) => item.status === "pending")) return list;
|
|
1412
1907
|
return list.map((item) => item.status === "pending" ? { ...item, status } : item);
|
|
1413
1908
|
}
|
|
1414
|
-
function restoreChatInteractions(list, outstanding) {
|
|
1415
|
-
|
|
1416
|
-
let next = list.map((item) => item.status === "pending" && !outstandingIds.has(item.id) ? { ...item, status: "answered" } : item);
|
|
1909
|
+
function restoreChatInteractions(list, outstanding, options = {}) {
|
|
1910
|
+
let next = list;
|
|
1417
1911
|
for (const request of outstanding) {
|
|
1418
|
-
|
|
1912
|
+
const interaction = interactionFromWireRequest(request);
|
|
1913
|
+
const exact = next.findIndex((item) => item.id === interaction.id);
|
|
1914
|
+
if (exact !== -1) {
|
|
1915
|
+
next = upsertChatInteraction(next, interaction);
|
|
1916
|
+
continue;
|
|
1917
|
+
}
|
|
1918
|
+
const signature = questionInteractionContentSignature(interaction);
|
|
1919
|
+
const obsolete = signature ? next.findIndex((item) => item.status === "pending" && questionInteractionContentSignature(item) === signature) : -1;
|
|
1920
|
+
if (obsolete === -1) {
|
|
1921
|
+
next = [...next, interaction];
|
|
1922
|
+
continue;
|
|
1923
|
+
}
|
|
1924
|
+
next = [...next];
|
|
1925
|
+
next[obsolete] = interaction;
|
|
1926
|
+
}
|
|
1927
|
+
if (options.mode !== "durable") {
|
|
1928
|
+
const outstandingIds = new Set(outstanding.map((request) => request.id));
|
|
1929
|
+
next = next.map((item) => item.status === "pending" && !outstandingIds.has(item.id) ? { ...item, status: "answered" } : item);
|
|
1419
1930
|
}
|
|
1420
1931
|
return next;
|
|
1421
1932
|
}
|
|
1422
|
-
function
|
|
1423
|
-
|
|
1424
|
-
|
|
1933
|
+
function hydrateChatInteractions(list, persisted) {
|
|
1934
|
+
return persisted.reduce(upsertChatInteraction, list);
|
|
1935
|
+
}
|
|
1936
|
+
function useChatInteractions(options = {}) {
|
|
1937
|
+
const [interactions, setInteractions] = useState8([]);
|
|
1938
|
+
const upsert = useCallback3((interaction) => {
|
|
1425
1939
|
setInteractions((prev) => upsertChatInteraction(prev, interaction));
|
|
1426
1940
|
}, []);
|
|
1427
|
-
const applyCancel =
|
|
1941
|
+
const applyCancel = useCallback3((cancel) => {
|
|
1428
1942
|
setInteractions((prev) => cancelChatInteraction(prev, cancel));
|
|
1429
1943
|
}, []);
|
|
1430
|
-
const markResolved =
|
|
1431
|
-
setInteractions((prev) => resolveChatInteraction(prev, id, status));
|
|
1944
|
+
const markResolved = useCallback3((id, status, answers) => {
|
|
1945
|
+
setInteractions((prev) => resolveChatInteraction(prev, id, status, answers));
|
|
1432
1946
|
}, []);
|
|
1433
|
-
const restore =
|
|
1434
|
-
setInteractions((prev) => restoreChatInteractions(prev, outstanding
|
|
1947
|
+
const restore = useCallback3((outstanding, restoreOptions) => {
|
|
1948
|
+
setInteractions((prev) => restoreChatInteractions(prev, outstanding, {
|
|
1949
|
+
mode: restoreOptions?.mode ?? options.mode
|
|
1950
|
+
}));
|
|
1951
|
+
}, [options.mode]);
|
|
1952
|
+
const hydrate = useCallback3((persisted) => {
|
|
1953
|
+
setInteractions((prev) => hydrateChatInteractions(prev, persisted));
|
|
1435
1954
|
}, []);
|
|
1436
|
-
const terminalizePending =
|
|
1955
|
+
const terminalizePending = useCallback3((status) => {
|
|
1437
1956
|
setInteractions((prev) => terminalizePendingChatInteractions(prev, status));
|
|
1438
1957
|
}, []);
|
|
1439
|
-
const reset =
|
|
1958
|
+
const reset = useCallback3(() => setInteractions([]), []);
|
|
1440
1959
|
const pending = useMemo4(() => interactions.filter((item) => item.status === "pending"), [interactions]);
|
|
1441
|
-
return { interactions, pending, upsert, applyCancel, markResolved, restore, terminalizePending, reset };
|
|
1960
|
+
return { interactions, pending, upsert, applyCancel, markResolved, restore, hydrate, terminalizePending, reset };
|
|
1442
1961
|
}
|
|
1443
1962
|
|
|
1444
1963
|
// src/web-react/use-file-mentions.ts
|
|
1445
|
-
import { useCallback as
|
|
1964
|
+
import { useCallback as useCallback4, useMemo as useMemo5, useRef as useRef7, useState as useState9 } from "react";
|
|
1446
1965
|
var FILE_MENTION_KIND = "file";
|
|
1447
1966
|
function toMentionItem(file) {
|
|
1448
1967
|
return { id: file.path, label: file.name, detail: file.path, kind: FILE_MENTION_KIND };
|
|
@@ -1500,12 +2019,12 @@ function useFileMentions(options) {
|
|
|
1500
2019
|
emptyText = DEFAULT_MENTION_EMPTY_TEXT
|
|
1501
2020
|
} = options;
|
|
1502
2021
|
const fetchImpl = options.fetchImpl ?? fetch;
|
|
1503
|
-
const [state, setState] =
|
|
1504
|
-
const stateRef =
|
|
2022
|
+
const [state, setState] = useState9({ kind: "idle" });
|
|
2023
|
+
const stateRef = useRef7(state);
|
|
1505
2024
|
stateRef.current = state;
|
|
1506
|
-
const inFlightRef =
|
|
1507
|
-
const [mentions, setMentions] =
|
|
1508
|
-
const load =
|
|
2025
|
+
const inFlightRef = useRef7(null);
|
|
2026
|
+
const [mentions, setMentions] = useState9([]);
|
|
2027
|
+
const load = useCallback4(() => {
|
|
1509
2028
|
if (inFlightRef.current) return inFlightRef.current;
|
|
1510
2029
|
if (stateRef.current.kind === "idle") {
|
|
1511
2030
|
stateRef.current = { kind: "loading" };
|
|
@@ -1537,10 +2056,10 @@ function useFileMentions(options) {
|
|
|
1537
2056
|
inFlightRef.current = attempt;
|
|
1538
2057
|
return attempt;
|
|
1539
2058
|
}, [fetchImpl, indexUrl]);
|
|
1540
|
-
const refresh =
|
|
2059
|
+
const refresh = useCallback4(async () => {
|
|
1541
2060
|
await load();
|
|
1542
2061
|
}, [load]);
|
|
1543
|
-
const fetchItems =
|
|
2062
|
+
const fetchItems = useCallback4(
|
|
1544
2063
|
async (query) => {
|
|
1545
2064
|
let current = stateRef.current;
|
|
1546
2065
|
if (current.kind === "idle" || current.kind === "loading") {
|
|
@@ -1555,10 +2074,10 @@ function useFileMentions(options) {
|
|
|
1555
2074
|
},
|
|
1556
2075
|
[load, limit, refreshAfterMs]
|
|
1557
2076
|
);
|
|
1558
|
-
const onMentionsChange =
|
|
2077
|
+
const onMentionsChange = useCallback4((items) => {
|
|
1559
2078
|
setMentions(items.filter((item) => item.kind === void 0 || item.kind === FILE_MENTION_KIND).map(toFileMention));
|
|
1560
2079
|
}, []);
|
|
1561
|
-
const clearMentions =
|
|
2080
|
+
const clearMentions = useCallback4(() => setMentions([]), []);
|
|
1562
2081
|
const mention = useMemo5(
|
|
1563
2082
|
() => ({
|
|
1564
2083
|
fetchItems,
|
|
@@ -1571,8 +2090,8 @@ function useFileMentions(options) {
|
|
|
1571
2090
|
}
|
|
1572
2091
|
|
|
1573
2092
|
// src/web-react/mission-activity.tsx
|
|
1574
|
-
import { useCallback as
|
|
1575
|
-
import { Fragment as Fragment4, jsx as
|
|
2093
|
+
import { useCallback as useCallback5, useEffect as useEffect7, useState as useState10 } from "react";
|
|
2094
|
+
import { Fragment as Fragment4, jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1576
2095
|
var LIVE_STATUSES = /* @__PURE__ */ new Set(["pending", "running"]);
|
|
1577
2096
|
var OK_STATUSES = /* @__PURE__ */ new Set(["completed", "done", "succeeded"]);
|
|
1578
2097
|
var ERROR_STATUSES = /* @__PURE__ */ new Set(["failed", "error", "cancelled", "aborted"]);
|
|
@@ -1619,20 +2138,20 @@ function waterfallLayout(trace) {
|
|
|
1619
2138
|
});
|
|
1620
2139
|
}
|
|
1621
2140
|
function ChevronGlyph({ className }) {
|
|
1622
|
-
return /* @__PURE__ */
|
|
2141
|
+
return /* @__PURE__ */ jsx9("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: /* @__PURE__ */ jsx9("path", { d: "m6 9 6 6 6-6" }) });
|
|
1623
2142
|
}
|
|
1624
2143
|
function RefreshGlyph({ className }) {
|
|
1625
|
-
return /* @__PURE__ */
|
|
2144
|
+
return /* @__PURE__ */ jsx9("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: /* @__PURE__ */ jsx9("path", { d: "M21 12a9 9 0 1 1-2.64-6.36M21 3v6h-6" }) });
|
|
1626
2145
|
}
|
|
1627
2146
|
function CopyGlyph({ className }) {
|
|
1628
|
-
return /* @__PURE__ */
|
|
1629
|
-
/* @__PURE__ */
|
|
1630
|
-
/* @__PURE__ */
|
|
2147
|
+
return /* @__PURE__ */ jsxs7("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: [
|
|
2148
|
+
/* @__PURE__ */ jsx9("rect", { x: "9", y: "9", width: "13", height: "13", rx: "2" }),
|
|
2149
|
+
/* @__PURE__ */ jsx9("path", { d: "M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" })
|
|
1631
2150
|
] });
|
|
1632
2151
|
}
|
|
1633
2152
|
function TraceIdCopy({ traceId }) {
|
|
1634
|
-
const [copied, setCopied] =
|
|
1635
|
-
const copy =
|
|
2153
|
+
const [copied, setCopied] = useState10(false);
|
|
2154
|
+
const copy = useCallback5(() => {
|
|
1636
2155
|
void navigator.clipboard?.writeText(traceId).then(
|
|
1637
2156
|
() => {
|
|
1638
2157
|
setCopied(true);
|
|
@@ -1642,7 +2161,7 @@ function TraceIdCopy({ traceId }) {
|
|
|
1642
2161
|
}
|
|
1643
2162
|
);
|
|
1644
2163
|
}, [traceId]);
|
|
1645
|
-
return /* @__PURE__ */
|
|
2164
|
+
return /* @__PURE__ */ jsxs7(
|
|
1646
2165
|
"button",
|
|
1647
2166
|
{
|
|
1648
2167
|
type: "button",
|
|
@@ -1651,23 +2170,23 @@ function TraceIdCopy({ traceId }) {
|
|
|
1651
2170
|
"aria-label": "Copy trace id",
|
|
1652
2171
|
className: "inline-flex min-w-0 items-center gap-1.5 rounded text-left font-mono text-muted-foreground transition hover:text-foreground focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-card",
|
|
1653
2172
|
children: [
|
|
1654
|
-
/* @__PURE__ */
|
|
1655
|
-
/* @__PURE__ */
|
|
1656
|
-
copied && /* @__PURE__ */
|
|
2173
|
+
/* @__PURE__ */ jsx9("span", { className: "truncate", children: traceId }),
|
|
2174
|
+
/* @__PURE__ */ jsx9(CopyGlyph, { className: "h-3 w-3 shrink-0" }),
|
|
2175
|
+
copied && /* @__PURE__ */ jsx9("span", { className: "shrink-0 not-italic text-success", children: "copied" })
|
|
1657
2176
|
]
|
|
1658
2177
|
}
|
|
1659
2178
|
);
|
|
1660
2179
|
}
|
|
1661
2180
|
function StatusDot({ tone }) {
|
|
1662
|
-
return /* @__PURE__ */
|
|
1663
|
-
/* @__PURE__ */
|
|
2181
|
+
return /* @__PURE__ */ jsxs7("span", { className: "inline-flex items-center", children: [
|
|
2182
|
+
/* @__PURE__ */ jsx9(
|
|
1664
2183
|
"span",
|
|
1665
2184
|
{
|
|
1666
2185
|
"aria-hidden": true,
|
|
1667
2186
|
className: `h-2 w-2 shrink-0 rounded-full ${tone === "live" ? "animate-pulse bg-warning" : tone === "ok" ? "bg-success" : tone === "error" ? "bg-destructive" : "bg-muted-foreground/40"}`
|
|
1668
2187
|
}
|
|
1669
2188
|
),
|
|
1670
|
-
/* @__PURE__ */
|
|
2189
|
+
/* @__PURE__ */ jsx9("span", { className: "sr-only", children: tone })
|
|
1671
2190
|
] });
|
|
1672
2191
|
}
|
|
1673
2192
|
var BAR_CLASS = {
|
|
@@ -1679,19 +2198,19 @@ function FlowWaterfall({ trace }) {
|
|
|
1679
2198
|
const rows = waterfallLayout(trace);
|
|
1680
2199
|
if (rows.length === 0) return null;
|
|
1681
2200
|
const cost = formatActivityCost(trace.costUsd);
|
|
1682
|
-
return /* @__PURE__ */
|
|
1683
|
-
rows.map((row, i) => /* @__PURE__ */
|
|
1684
|
-
/* @__PURE__ */
|
|
1685
|
-
/* @__PURE__ */
|
|
2201
|
+
return /* @__PURE__ */ jsxs7("div", { className: "space-y-1", children: [
|
|
2202
|
+
rows.map((row, i) => /* @__PURE__ */ jsxs7("div", { className: "grid grid-cols-[minmax(0,2fr)_minmax(0,3fr)_auto] items-center gap-2", children: [
|
|
2203
|
+
/* @__PURE__ */ jsx9("span", { className: "truncate font-mono text-[11px] text-muted-foreground", title: row.name, children: row.name }),
|
|
2204
|
+
/* @__PURE__ */ jsx9("div", { className: "relative h-2 rounded-sm bg-muted/40", children: /* @__PURE__ */ jsx9(
|
|
1686
2205
|
"div",
|
|
1687
2206
|
{
|
|
1688
2207
|
className: `absolute inset-y-0 rounded-sm ${row.ok ? BAR_CLASS[row.kind] : "bg-destructive/80"} ${row.approx ? "opacity-70" : ""}`,
|
|
1689
2208
|
style: { left: `${row.offsetPct}%`, width: `${row.widthPct}%` }
|
|
1690
2209
|
}
|
|
1691
2210
|
) }),
|
|
1692
|
-
/* @__PURE__ */
|
|
2211
|
+
/* @__PURE__ */ jsx9("span", { className: "shrink-0 font-mono text-[10px] tabular-nums text-muted-foreground/70", children: row.durationLabel })
|
|
1693
2212
|
] }, i)),
|
|
1694
|
-
/* @__PURE__ */
|
|
2213
|
+
/* @__PURE__ */ jsxs7("p", { className: "pt-0.5 text-right font-mono text-[10px] tabular-nums text-muted-foreground/60", children: [
|
|
1695
2214
|
(trace.totalMs / 1e3).toFixed(1),
|
|
1696
2215
|
"s",
|
|
1697
2216
|
cost ? ` \xB7 ${cost}` : ""
|
|
@@ -1699,43 +2218,43 @@ function FlowWaterfall({ trace }) {
|
|
|
1699
2218
|
] });
|
|
1700
2219
|
}
|
|
1701
2220
|
function MissionActivityLane({ activity, startedAt, nowMs }) {
|
|
1702
|
-
const [expanded, setExpanded] =
|
|
2221
|
+
const [expanded, setExpanded] = useState10(false);
|
|
1703
2222
|
if (activity.length === 0) return null;
|
|
1704
|
-
return /* @__PURE__ */
|
|
2223
|
+
return /* @__PURE__ */ jsxs7("div", { className: "mt-1 border-l border-border/50 pl-3", children: [
|
|
1705
2224
|
activity.map((run) => {
|
|
1706
2225
|
const tone = activityTone(run.status);
|
|
1707
2226
|
const cost = formatActivityCost(run.costUsd);
|
|
1708
2227
|
const duration = formatActivityDuration(run.durationMs);
|
|
1709
|
-
return /* @__PURE__ */
|
|
1710
|
-
/* @__PURE__ */
|
|
1711
|
-
/* @__PURE__ */
|
|
1712
|
-
/* @__PURE__ */
|
|
1713
|
-
/* @__PURE__ */
|
|
2228
|
+
return /* @__PURE__ */ jsxs7("div", { className: "flex items-center gap-2 py-1 text-xs", children: [
|
|
2229
|
+
/* @__PURE__ */ jsx9(StatusDot, { tone }),
|
|
2230
|
+
/* @__PURE__ */ jsxs7("span", { className: "min-w-0 flex-1 truncate", children: [
|
|
2231
|
+
/* @__PURE__ */ jsx9("span", { className: "font-medium", children: run.tool }),
|
|
2232
|
+
/* @__PURE__ */ jsxs7("span", { className: "text-muted-foreground", children: [
|
|
1714
2233
|
" \u2014 ",
|
|
1715
2234
|
run.detail
|
|
1716
2235
|
] })
|
|
1717
2236
|
] }),
|
|
1718
|
-
tone === "live" && (run.iteration !== void 0 || run.phase !== void 0) && /* @__PURE__ */
|
|
1719
|
-
/* @__PURE__ */
|
|
1720
|
-
tone !== "live" && tone !== "ok" && /* @__PURE__ */
|
|
1721
|
-
cost && /* @__PURE__ */
|
|
1722
|
-
duration && /* @__PURE__ */
|
|
2237
|
+
tone === "live" && (run.iteration !== void 0 || run.phase !== void 0) && /* @__PURE__ */ jsx9("span", { className: "shrink-0 rounded-full bg-warning/10 px-1.5 py-0.5 font-mono text-[10px] text-warning", children: [run.iteration !== void 0 ? `iter ${run.iteration}` : null, run.phase ?? null].filter(Boolean).join(" \xB7 ") }),
|
|
2238
|
+
/* @__PURE__ */ jsxs7("span", { className: "flex shrink-0 items-center gap-1.5 font-mono text-[10px] tabular-nums text-muted-foreground/70", children: [
|
|
2239
|
+
tone !== "live" && tone !== "ok" && /* @__PURE__ */ jsx9("span", { children: run.status }),
|
|
2240
|
+
cost && /* @__PURE__ */ jsx9("span", { children: cost }),
|
|
2241
|
+
duration && /* @__PURE__ */ jsx9("span", { children: duration })
|
|
1723
2242
|
] })
|
|
1724
2243
|
] }, run.taskId);
|
|
1725
2244
|
}),
|
|
1726
|
-
/* @__PURE__ */
|
|
2245
|
+
/* @__PURE__ */ jsxs7(
|
|
1727
2246
|
"button",
|
|
1728
2247
|
{
|
|
1729
2248
|
type: "button",
|
|
1730
2249
|
onClick: () => setExpanded((v) => !v),
|
|
1731
2250
|
className: "flex items-center gap-1 py-0.5 text-[10px] font-medium text-muted-foreground/70 transition hover:text-foreground",
|
|
1732
2251
|
children: [
|
|
1733
|
-
/* @__PURE__ */
|
|
2252
|
+
/* @__PURE__ */ jsx9(ChevronGlyph, { className: `h-3 w-3 transition-transform ${expanded ? "rotate-180" : ""}` }),
|
|
1734
2253
|
"timeline"
|
|
1735
2254
|
]
|
|
1736
2255
|
}
|
|
1737
2256
|
),
|
|
1738
|
-
expanded && /* @__PURE__ */
|
|
2257
|
+
expanded && /* @__PURE__ */ jsx9("div", { className: "rounded-md border border-border/50 bg-muted/10 p-2", children: /* @__PURE__ */ jsx9(
|
|
1739
2258
|
FlowWaterfall,
|
|
1740
2259
|
{
|
|
1741
2260
|
trace: stepActivityFlowTrace(activity, {
|
|
@@ -1750,45 +2269,45 @@ function ActivityRow({
|
|
|
1750
2269
|
record,
|
|
1751
2270
|
renderMissionRef
|
|
1752
2271
|
}) {
|
|
1753
|
-
const [open, setOpen] =
|
|
2272
|
+
const [open, setOpen] = useState10(false);
|
|
1754
2273
|
const tone = activityTone(record.status);
|
|
1755
2274
|
const cost = formatActivityCost(record.costUsd);
|
|
1756
2275
|
const duration = formatActivityDuration(record.durationMs);
|
|
1757
|
-
return /* @__PURE__ */
|
|
1758
|
-
/* @__PURE__ */
|
|
1759
|
-
/* @__PURE__ */
|
|
1760
|
-
/* @__PURE__ */
|
|
1761
|
-
/* @__PURE__ */
|
|
1762
|
-
/* @__PURE__ */
|
|
2276
|
+
return /* @__PURE__ */ jsxs7("div", { className: "rounded-lg border border-border/60 bg-card", children: [
|
|
2277
|
+
/* @__PURE__ */ jsxs7("button", { type: "button", onClick: () => setOpen((v) => !v), className: "flex w-full items-center gap-2.5 px-3 py-2 text-left text-sm", children: [
|
|
2278
|
+
/* @__PURE__ */ jsx9(StatusDot, { tone }),
|
|
2279
|
+
/* @__PURE__ */ jsxs7("span", { className: "min-w-0 flex-1 truncate", children: [
|
|
2280
|
+
/* @__PURE__ */ jsx9("span", { className: "font-medium", children: record.tool }),
|
|
2281
|
+
/* @__PURE__ */ jsxs7("span", { className: "text-muted-foreground", children: [
|
|
1763
2282
|
" \u2014 ",
|
|
1764
2283
|
record.detail
|
|
1765
2284
|
] })
|
|
1766
2285
|
] }),
|
|
1767
|
-
tone === "live" && (record.iteration !== void 0 || record.phase !== void 0) && /* @__PURE__ */
|
|
1768
|
-
/* @__PURE__ */
|
|
2286
|
+
tone === "live" && (record.iteration !== void 0 || record.phase !== void 0) && /* @__PURE__ */ jsx9("span", { className: "shrink-0 rounded-full bg-warning/10 px-2 py-0.5 font-mono text-[10px] text-warning", children: [record.iteration !== void 0 ? `iter ${record.iteration}` : null, record.phase ?? null].filter(Boolean).join(" \xB7 ") }),
|
|
2287
|
+
/* @__PURE__ */ jsx9(
|
|
1769
2288
|
"span",
|
|
1770
2289
|
{
|
|
1771
2290
|
className: `shrink-0 rounded-full px-2 py-0.5 text-[10px] font-medium ${tone === "ok" ? "bg-success/10 text-success" : tone === "error" ? "bg-destructive/10 text-destructive" : tone === "live" ? "bg-warning/10 text-warning" : "bg-muted/60 text-muted-foreground"}`,
|
|
1772
2291
|
children: record.status
|
|
1773
2292
|
}
|
|
1774
2293
|
),
|
|
1775
|
-
cost && /* @__PURE__ */
|
|
1776
|
-
/* @__PURE__ */
|
|
2294
|
+
cost && /* @__PURE__ */ jsx9("span", { className: "shrink-0 font-mono text-[11px] tabular-nums text-muted-foreground", children: cost }),
|
|
2295
|
+
/* @__PURE__ */ jsx9(ChevronGlyph, { className: `h-3 w-3 shrink-0 text-muted-foreground transition-transform ${open ? "rotate-180" : ""}` })
|
|
1777
2296
|
] }),
|
|
1778
|
-
open && /* @__PURE__ */
|
|
1779
|
-
record.durationMs !== void 0 && /* @__PURE__ */
|
|
1780
|
-
/* @__PURE__ */
|
|
1781
|
-
/* @__PURE__ */
|
|
1782
|
-
/* @__PURE__ */
|
|
1783
|
-
/* @__PURE__ */
|
|
1784
|
-
/* @__PURE__ */
|
|
1785
|
-
duration && /* @__PURE__ */
|
|
1786
|
-
/* @__PURE__ */
|
|
1787
|
-
/* @__PURE__ */
|
|
2297
|
+
open && /* @__PURE__ */ jsxs7("div", { className: "space-y-2.5 border-t border-border/40 px-3 py-2.5", children: [
|
|
2298
|
+
record.durationMs !== void 0 && /* @__PURE__ */ jsx9("div", { className: "rounded-md border border-border/50 bg-muted/10 p-2", children: /* @__PURE__ */ jsx9(FlowWaterfall, { trace: stepActivityFlowTrace([record]) }) }),
|
|
2299
|
+
/* @__PURE__ */ jsxs7("dl", { className: "grid grid-cols-[auto_1fr] gap-x-3 gap-y-1 font-mono text-[11px]", children: [
|
|
2300
|
+
/* @__PURE__ */ jsx9("dt", { className: "text-muted-foreground/60", children: "task" }),
|
|
2301
|
+
/* @__PURE__ */ jsx9("dd", { className: "truncate text-muted-foreground", children: record.taskId }),
|
|
2302
|
+
/* @__PURE__ */ jsx9("dt", { className: "text-muted-foreground/60", children: "started" }),
|
|
2303
|
+
/* @__PURE__ */ jsx9("dd", { className: "text-muted-foreground", children: new Date(record.startedAt).toLocaleString() }),
|
|
2304
|
+
duration && /* @__PURE__ */ jsxs7(Fragment4, { children: [
|
|
2305
|
+
/* @__PURE__ */ jsx9("dt", { className: "text-muted-foreground/60", children: "duration" }),
|
|
2306
|
+
/* @__PURE__ */ jsx9("dd", { className: "text-muted-foreground", children: duration })
|
|
1788
2307
|
] }),
|
|
1789
|
-
record.traceId && /* @__PURE__ */
|
|
1790
|
-
/* @__PURE__ */
|
|
1791
|
-
/* @__PURE__ */
|
|
2308
|
+
record.traceId && /* @__PURE__ */ jsxs7(Fragment4, { children: [
|
|
2309
|
+
/* @__PURE__ */ jsx9("dt", { className: "text-muted-foreground/60", children: "trace" }),
|
|
2310
|
+
/* @__PURE__ */ jsx9("dd", { className: "min-w-0", children: /* @__PURE__ */ jsx9(TraceIdCopy, { traceId: record.traceId }) })
|
|
1792
2311
|
] })
|
|
1793
2312
|
] }),
|
|
1794
2313
|
record.missionRef && renderMissionRef?.(record.missionRef, record)
|
|
@@ -1796,11 +2315,11 @@ function ActivityRow({
|
|
|
1796
2315
|
] });
|
|
1797
2316
|
}
|
|
1798
2317
|
function AgentActivityPanel({ fetchActivity, renderMissionRef, title = "Agent activity", emptyLabel = "No agent runs yet." }) {
|
|
1799
|
-
const [rows, setRows] =
|
|
1800
|
-
const [cursor, setCursor] =
|
|
1801
|
-
const [loading, setLoading] =
|
|
1802
|
-
const [error, setError] =
|
|
1803
|
-
const load =
|
|
2318
|
+
const [rows, setRows] = useState10([]);
|
|
2319
|
+
const [cursor, setCursor] = useState10(void 0);
|
|
2320
|
+
const [loading, setLoading] = useState10(false);
|
|
2321
|
+
const [error, setError] = useState10(null);
|
|
2322
|
+
const load = useCallback5(
|
|
1804
2323
|
async (from) => {
|
|
1805
2324
|
setLoading(true);
|
|
1806
2325
|
setError(null);
|
|
@@ -1816,13 +2335,13 @@ function AgentActivityPanel({ fetchActivity, renderMissionRef, title = "Agent ac
|
|
|
1816
2335
|
},
|
|
1817
2336
|
[fetchActivity]
|
|
1818
2337
|
);
|
|
1819
|
-
|
|
2338
|
+
useEffect7(() => {
|
|
1820
2339
|
void load();
|
|
1821
2340
|
}, [load]);
|
|
1822
|
-
return /* @__PURE__ */
|
|
1823
|
-
/* @__PURE__ */
|
|
1824
|
-
/* @__PURE__ */
|
|
1825
|
-
/* @__PURE__ */
|
|
2341
|
+
return /* @__PURE__ */ jsxs7("div", { className: "space-y-2", children: [
|
|
2342
|
+
/* @__PURE__ */ jsxs7("div", { className: "flex items-center gap-2", children: [
|
|
2343
|
+
/* @__PURE__ */ jsx9("h2", { className: "flex-1 text-sm font-semibold", children: title }),
|
|
2344
|
+
/* @__PURE__ */ jsx9(
|
|
1826
2345
|
"button",
|
|
1827
2346
|
{
|
|
1828
2347
|
type: "button",
|
|
@@ -1830,14 +2349,14 @@ function AgentActivityPanel({ fetchActivity, renderMissionRef, title = "Agent ac
|
|
|
1830
2349
|
disabled: loading,
|
|
1831
2350
|
"aria-label": "Refresh",
|
|
1832
2351
|
className: "rounded-md p-1.5 text-muted-foreground transition hover:bg-accent/30 hover:text-foreground disabled:opacity-50",
|
|
1833
|
-
children: /* @__PURE__ */
|
|
2352
|
+
children: /* @__PURE__ */ jsx9(RefreshGlyph, { className: `h-3.5 w-3.5 ${loading ? "animate-spin" : ""}` })
|
|
1834
2353
|
}
|
|
1835
2354
|
)
|
|
1836
2355
|
] }),
|
|
1837
|
-
error && /* @__PURE__ */
|
|
1838
|
-
!error && rows.length === 0 && !loading && /* @__PURE__ */
|
|
1839
|
-
/* @__PURE__ */
|
|
1840
|
-
cursor && /* @__PURE__ */
|
|
2356
|
+
error && /* @__PURE__ */ jsx9("p", { role: "alert", className: "rounded-md border border-destructive/40 bg-destructive/5 px-3 py-2 text-xs text-destructive", children: error }),
|
|
2357
|
+
!error && rows.length === 0 && !loading && /* @__PURE__ */ jsx9("p", { className: "px-1 text-sm text-muted-foreground", children: emptyLabel }),
|
|
2358
|
+
/* @__PURE__ */ jsx9("div", { className: "space-y-1.5", children: rows.map((record) => /* @__PURE__ */ jsx9(ActivityRow, { record, renderMissionRef }, record.taskId)) }),
|
|
2359
|
+
cursor && /* @__PURE__ */ jsx9(
|
|
1841
2360
|
"button",
|
|
1842
2361
|
{
|
|
1843
2362
|
type: "button",
|
|
@@ -1851,9 +2370,9 @@ function AgentActivityPanel({ fetchActivity, renderMissionRef, title = "Agent ac
|
|
|
1851
2370
|
}
|
|
1852
2371
|
|
|
1853
2372
|
// src/web-react/seat-paywall.tsx
|
|
1854
|
-
import { jsx as
|
|
2373
|
+
import { jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
1855
2374
|
function CheckGlyph3() {
|
|
1856
|
-
return /* @__PURE__ */
|
|
2375
|
+
return /* @__PURE__ */ jsx10(
|
|
1857
2376
|
"svg",
|
|
1858
2377
|
{
|
|
1859
2378
|
className: "h-4 w-4 shrink-0 text-primary",
|
|
@@ -1864,14 +2383,14 @@ function CheckGlyph3() {
|
|
|
1864
2383
|
strokeLinecap: "round",
|
|
1865
2384
|
strokeLinejoin: "round",
|
|
1866
2385
|
"aria-hidden": true,
|
|
1867
|
-
children: /* @__PURE__ */
|
|
2386
|
+
children: /* @__PURE__ */ jsx10("path", { d: "M20 6 9 17l-5-5" })
|
|
1868
2387
|
}
|
|
1869
2388
|
);
|
|
1870
2389
|
}
|
|
1871
2390
|
function Benefit({ children }) {
|
|
1872
|
-
return /* @__PURE__ */
|
|
1873
|
-
/* @__PURE__ */
|
|
1874
|
-
/* @__PURE__ */
|
|
2391
|
+
return /* @__PURE__ */ jsxs8("li", { className: "flex items-start gap-2.5 text-sm text-foreground", children: [
|
|
2392
|
+
/* @__PURE__ */ jsx10("span", { className: "mt-0.5", children: /* @__PURE__ */ jsx10(CheckGlyph3, {}) }),
|
|
2393
|
+
/* @__PURE__ */ jsx10("span", { children })
|
|
1875
2394
|
] });
|
|
1876
2395
|
}
|
|
1877
2396
|
function SeatPaywall({
|
|
@@ -1885,30 +2404,30 @@ function SeatPaywall({
|
|
|
1885
2404
|
footnote
|
|
1886
2405
|
}) {
|
|
1887
2406
|
const { pending, run } = usePending();
|
|
1888
|
-
return /* @__PURE__ */
|
|
1889
|
-
/* @__PURE__ */
|
|
1890
|
-
/* @__PURE__ */
|
|
2407
|
+
return /* @__PURE__ */ jsx10("div", { className: "flex min-h-[60vh] w-full items-center justify-center p-6", children: /* @__PURE__ */ jsxs8("div", { className: "w-full max-w-md rounded-2xl border border-border bg-card p-8 shadow-sm", children: [
|
|
2408
|
+
/* @__PURE__ */ jsx10("p", { className: "text-xs font-medium uppercase tracking-wide text-muted-foreground", children: product }),
|
|
2409
|
+
/* @__PURE__ */ jsxs8("h1", { className: "mt-2 text-2xl font-semibold tracking-tight text-foreground", children: [
|
|
1891
2410
|
"Unlock ",
|
|
1892
2411
|
product
|
|
1893
2412
|
] }),
|
|
1894
|
-
tagline && /* @__PURE__ */
|
|
1895
|
-
/* @__PURE__ */
|
|
1896
|
-
/* @__PURE__ */
|
|
2413
|
+
tagline && /* @__PURE__ */ jsx10("p", { className: "mt-2 text-sm text-muted-foreground", children: tagline }),
|
|
2414
|
+
/* @__PURE__ */ jsxs8("div", { className: "mt-6 flex items-baseline gap-1.5", children: [
|
|
2415
|
+
/* @__PURE__ */ jsxs8("span", { className: "text-3xl font-semibold text-foreground", children: [
|
|
1897
2416
|
"$",
|
|
1898
2417
|
priceUsd
|
|
1899
2418
|
] }),
|
|
1900
|
-
/* @__PURE__ */
|
|
2419
|
+
/* @__PURE__ */ jsx10("span", { className: "text-sm text-muted-foreground", children: "/mo" })
|
|
1901
2420
|
] }),
|
|
1902
|
-
/* @__PURE__ */
|
|
2421
|
+
/* @__PURE__ */ jsxs8("p", { className: "mt-1 text-sm text-muted-foreground", children: [
|
|
1903
2422
|
"Includes $",
|
|
1904
2423
|
includedUsageUsd,
|
|
1905
2424
|
"/mo of AI usage"
|
|
1906
2425
|
] }),
|
|
1907
|
-
/* @__PURE__ */
|
|
2426
|
+
/* @__PURE__ */ jsx10("ul", { className: "mt-6 space-y-2.5", children: (benefits ?? [
|
|
1908
2427
|
`Full access to ${product}`,
|
|
1909
2428
|
`$${includedUsageUsd}/mo of AI usage included, every month`
|
|
1910
|
-
]).map((benefit, i) => /* @__PURE__ */
|
|
1911
|
-
/* @__PURE__ */
|
|
2429
|
+
]).map((benefit, i) => /* @__PURE__ */ jsx10(Benefit, { children: benefit }, i)) }),
|
|
2430
|
+
/* @__PURE__ */ jsx10(
|
|
1912
2431
|
"button",
|
|
1913
2432
|
{
|
|
1914
2433
|
type: "button",
|
|
@@ -1918,13 +2437,13 @@ function SeatPaywall({
|
|
|
1918
2437
|
children: pending ? "Opening checkout\u2026" : ctaLabel ?? `Unlock ${product}`
|
|
1919
2438
|
}
|
|
1920
2439
|
),
|
|
1921
|
-
footnote && /* @__PURE__ */
|
|
2440
|
+
footnote && /* @__PURE__ */ jsx10("p", { className: "mt-3 text-center text-xs text-muted-foreground/70", children: footnote })
|
|
1922
2441
|
] }) });
|
|
1923
2442
|
}
|
|
1924
2443
|
|
|
1925
2444
|
// src/web-react/agent-session-controls.tsx
|
|
1926
|
-
import { useMemo as useMemo6, useState as
|
|
1927
|
-
import { jsx as
|
|
2445
|
+
import { useMemo as useMemo6, useState as useState11 } from "react";
|
|
2446
|
+
import { jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
1928
2447
|
var HARNESS_LABELS = {
|
|
1929
2448
|
opencode: "OpenCode (any model)",
|
|
1930
2449
|
"claude-code": "Claude Code (Anthropic)",
|
|
@@ -1944,12 +2463,12 @@ function harnessLabel(h) {
|
|
|
1944
2463
|
return HARNESS_LABELS[h] ?? h;
|
|
1945
2464
|
}
|
|
1946
2465
|
function ChevronDown2({ className }) {
|
|
1947
|
-
return /* @__PURE__ */
|
|
2466
|
+
return /* @__PURE__ */ jsx11("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: /* @__PURE__ */ jsx11("path", { d: "m6 9 6 6 6-6" }) });
|
|
1948
2467
|
}
|
|
1949
2468
|
function GearGlyph({ className }) {
|
|
1950
|
-
return /* @__PURE__ */
|
|
1951
|
-
/* @__PURE__ */
|
|
1952
|
-
/* @__PURE__ */
|
|
2469
|
+
return /* @__PURE__ */ jsxs9("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: [
|
|
2470
|
+
/* @__PURE__ */ jsx11("circle", { cx: "12", cy: "12", r: "3" }),
|
|
2471
|
+
/* @__PURE__ */ jsx11("path", { d: "M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1Z" })
|
|
1953
2472
|
] });
|
|
1954
2473
|
}
|
|
1955
2474
|
var FOCUS_RING = "focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background";
|
|
@@ -1958,11 +2477,11 @@ function HarnessPicker({
|
|
|
1958
2477
|
onChange,
|
|
1959
2478
|
available
|
|
1960
2479
|
}) {
|
|
1961
|
-
const [open, setOpen] =
|
|
2480
|
+
const [open, setOpen] = useState11(false);
|
|
1962
2481
|
const { containerRef, triggerProps } = usePopover(open, setOpen);
|
|
1963
2482
|
const options = available ?? Object.keys(HARNESS_LABELS);
|
|
1964
|
-
return /* @__PURE__ */
|
|
1965
|
-
/* @__PURE__ */
|
|
2483
|
+
return /* @__PURE__ */ jsxs9("div", { ref: containerRef, className: "relative inline-flex", children: [
|
|
2484
|
+
/* @__PURE__ */ jsxs9(
|
|
1966
2485
|
"button",
|
|
1967
2486
|
{
|
|
1968
2487
|
type: "button",
|
|
@@ -1971,12 +2490,12 @@ function HarnessPicker({
|
|
|
1971
2490
|
title: "Agent backend",
|
|
1972
2491
|
className: `inline-flex w-full items-center justify-between gap-1.5 rounded-lg border border-border bg-card px-3 py-1.5 text-sm font-medium text-foreground transition hover:bg-accent/30 ${FOCUS_RING}`,
|
|
1973
2492
|
children: [
|
|
1974
|
-
/* @__PURE__ */
|
|
1975
|
-
/* @__PURE__ */
|
|
2493
|
+
/* @__PURE__ */ jsx11("span", { className: "truncate", children: harnessLabel(value) }),
|
|
2494
|
+
/* @__PURE__ */ jsx11(ChevronDown2, { className: "h-3.5 w-3.5 text-muted-foreground" })
|
|
1976
2495
|
]
|
|
1977
2496
|
}
|
|
1978
2497
|
),
|
|
1979
|
-
open && /* @__PURE__ */
|
|
2498
|
+
open && /* @__PURE__ */ jsx11("div", { role: "menu", className: "absolute bottom-full left-0 z-50 mb-2 max-h-64 w-full min-w-[220px] overflow-y-auto rounded-xl border border-border bg-card p-1 shadow-lg", children: options.map((h) => /* @__PURE__ */ jsx11(
|
|
1980
2499
|
"button",
|
|
1981
2500
|
{
|
|
1982
2501
|
type: "button",
|
|
@@ -2023,11 +2542,11 @@ function AgentSessionControls(props) {
|
|
|
2023
2542
|
className
|
|
2024
2543
|
} = props;
|
|
2025
2544
|
const { onModel, onHarness } = useCoherentHandlers(props);
|
|
2026
|
-
const [open, setOpen] =
|
|
2545
|
+
const [open, setOpen] = useState11(false);
|
|
2027
2546
|
const { containerRef: popoverRef, triggerProps } = usePopover(open, setOpen);
|
|
2028
2547
|
const selectedModel = models.find((m) => m.id === model);
|
|
2029
2548
|
const showEffort = selectedModel?.supportsReasoning ?? true;
|
|
2030
|
-
const modelPicker = /* @__PURE__ */
|
|
2549
|
+
const modelPicker = /* @__PURE__ */ jsx11(
|
|
2031
2550
|
ModelPicker,
|
|
2032
2551
|
{
|
|
2033
2552
|
value: model,
|
|
@@ -2038,17 +2557,17 @@ function AgentSessionControls(props) {
|
|
|
2038
2557
|
}
|
|
2039
2558
|
);
|
|
2040
2559
|
if (layout === "inline") {
|
|
2041
|
-
return /* @__PURE__ */
|
|
2560
|
+
return /* @__PURE__ */ jsxs9("div", { className: `flex items-center gap-1.5 ${className ?? ""}`, children: [
|
|
2042
2561
|
modelPicker,
|
|
2043
|
-
showHarness && /* @__PURE__ */
|
|
2044
|
-
showEffort && /* @__PURE__ */
|
|
2562
|
+
showHarness && /* @__PURE__ */ jsx11(HarnessPicker, { value: harness, onChange: onHarness, available: availableHarnesses }),
|
|
2563
|
+
showEffort && /* @__PURE__ */ jsx11(EffortPicker, { value: effort, onChange: onEffortChange })
|
|
2045
2564
|
] });
|
|
2046
2565
|
}
|
|
2047
2566
|
const hasAdvanced = showHarness || showEffort;
|
|
2048
|
-
return /* @__PURE__ */
|
|
2567
|
+
return /* @__PURE__ */ jsxs9("div", { className: `flex items-center gap-1.5 ${className ?? ""}`, children: [
|
|
2049
2568
|
modelPicker,
|
|
2050
|
-
hasAdvanced && /* @__PURE__ */
|
|
2051
|
-
/* @__PURE__ */
|
|
2569
|
+
hasAdvanced && /* @__PURE__ */ jsxs9("div", { ref: popoverRef, className: "relative inline-flex", children: [
|
|
2570
|
+
/* @__PURE__ */ jsx11(
|
|
2052
2571
|
"button",
|
|
2053
2572
|
{
|
|
2054
2573
|
type: "button",
|
|
@@ -2057,19 +2576,19 @@ function AgentSessionControls(props) {
|
|
|
2057
2576
|
title: "Model settings \u2014 pick the agent backend and how hard it thinks",
|
|
2058
2577
|
className: `flex h-8 w-8 items-center justify-center rounded-full text-muted-foreground transition-colors hover:bg-muted hover:text-foreground data-[state=open]:bg-muted ${FOCUS_RING}`,
|
|
2059
2578
|
"data-state": open ? "open" : "closed",
|
|
2060
|
-
children: /* @__PURE__ */
|
|
2579
|
+
children: /* @__PURE__ */ jsx11(GearGlyph, { className: "h-4 w-4" })
|
|
2061
2580
|
}
|
|
2062
2581
|
),
|
|
2063
|
-
open && /* @__PURE__ */
|
|
2064
|
-
showHarness && /* @__PURE__ */
|
|
2065
|
-
/* @__PURE__ */
|
|
2066
|
-
/* @__PURE__ */
|
|
2067
|
-
/* @__PURE__ */
|
|
2582
|
+
open && /* @__PURE__ */ jsxs9("div", { className: "absolute bottom-full left-0 z-50 mb-2 w-72 space-y-3 rounded-xl border border-border bg-card p-3 shadow-lg", children: [
|
|
2583
|
+
showHarness && /* @__PURE__ */ jsxs9("div", { className: "space-y-1.5", children: [
|
|
2584
|
+
/* @__PURE__ */ jsx11("p", { className: "text-xs font-medium text-foreground", children: "Agent backend" }),
|
|
2585
|
+
/* @__PURE__ */ jsx11(HarnessPicker, { value: harness, onChange: onHarness, available: availableHarnesses }),
|
|
2586
|
+
/* @__PURE__ */ jsx11("p", { className: "text-[11px] leading-snug text-muted-foreground", children: "The engine that runs the agent. Switching it keeps your model choice compatible." })
|
|
2068
2587
|
] }),
|
|
2069
|
-
showEffort && /* @__PURE__ */
|
|
2070
|
-
/* @__PURE__ */
|
|
2071
|
-
/* @__PURE__ */
|
|
2072
|
-
/* @__PURE__ */
|
|
2588
|
+
showEffort && /* @__PURE__ */ jsxs9("div", { className: "space-y-1.5", children: [
|
|
2589
|
+
/* @__PURE__ */ jsx11("p", { className: "text-xs font-medium text-foreground", children: "Thinking" }),
|
|
2590
|
+
/* @__PURE__ */ jsx11(EffortPicker, { value: effort, onChange: onEffortChange, label: "" }),
|
|
2591
|
+
/* @__PURE__ */ jsx11("p", { className: "text-[11px] leading-snug text-muted-foreground", children: "How hard the agent thinks before answering. Higher is slower but more thorough." })
|
|
2073
2592
|
] })
|
|
2074
2593
|
] })
|
|
2075
2594
|
] })
|
|
@@ -2077,7 +2596,7 @@ function AgentSessionControls(props) {
|
|
|
2077
2596
|
}
|
|
2078
2597
|
|
|
2079
2598
|
// src/web-react/index.tsx
|
|
2080
|
-
import { Fragment as Fragment5, jsx as
|
|
2599
|
+
import { Fragment as Fragment5, jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
2081
2600
|
function formatModelCost(msg, models) {
|
|
2082
2601
|
if (msg.promptTokens == null && msg.completionTokens == null) return null;
|
|
2083
2602
|
const pricing = models.find((m) => m.id === msg.modelUsed)?.pricing;
|
|
@@ -2091,41 +2610,41 @@ function formatTokensPerSecond(msg) {
|
|
|
2091
2610
|
return `${Math.round(msg.completionTokens / (msg.durationMs / 1e3))} tok/s`;
|
|
2092
2611
|
}
|
|
2093
2612
|
function RunDrillIn({ run, onClose }) {
|
|
2094
|
-
return /* @__PURE__ */
|
|
2095
|
-
/* @__PURE__ */
|
|
2096
|
-
/* @__PURE__ */
|
|
2613
|
+
return /* @__PURE__ */ jsxs10("div", { className: "fixed inset-y-0 right-0 z-50 flex w-[480px] max-w-full flex-col border-l border-border bg-card shadow-xl", children: [
|
|
2614
|
+
/* @__PURE__ */ jsxs10("div", { className: "flex items-center gap-2 border-b border-border px-4 py-3", children: [
|
|
2615
|
+
/* @__PURE__ */ jsx12(
|
|
2097
2616
|
"span",
|
|
2098
2617
|
{
|
|
2099
2618
|
className: `h-2 w-2 shrink-0 rounded-full ${run.status === "running" ? "bg-warning" : run.status === "error" ? "bg-destructive" : "bg-success"}`
|
|
2100
2619
|
}
|
|
2101
2620
|
),
|
|
2102
|
-
/* @__PURE__ */
|
|
2103
|
-
/* @__PURE__ */
|
|
2104
|
-
/* @__PURE__ */
|
|
2621
|
+
/* @__PURE__ */ jsxs10("div", { className: "min-w-0 flex-1", children: [
|
|
2622
|
+
/* @__PURE__ */ jsx12("p", { className: "truncate text-sm font-semibold", children: run.title }),
|
|
2623
|
+
/* @__PURE__ */ jsx12("p", { className: "truncate font-mono text-[11px] text-muted-foreground", children: run.toolName })
|
|
2105
2624
|
] }),
|
|
2106
|
-
/* @__PURE__ */
|
|
2625
|
+
/* @__PURE__ */ jsx12(
|
|
2107
2626
|
"button",
|
|
2108
2627
|
{
|
|
2109
2628
|
type: "button",
|
|
2110
2629
|
onClick: onClose,
|
|
2111
2630
|
"aria-label": "Close",
|
|
2112
2631
|
className: "rounded-md p-1.5 text-muted-foreground transition hover:bg-accent/30 hover:text-foreground",
|
|
2113
|
-
children: /* @__PURE__ */
|
|
2632
|
+
children: /* @__PURE__ */ jsx12("svg", { className: "h-4 w-4", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", "aria-hidden": true, children: /* @__PURE__ */ jsx12("path", { d: "M18 6 6 18M6 6l12 12" }) })
|
|
2114
2633
|
}
|
|
2115
2634
|
)
|
|
2116
2635
|
] }),
|
|
2117
|
-
/* @__PURE__ */
|
|
2118
|
-
run.steps.length === 0 && /* @__PURE__ */
|
|
2119
|
-
run.steps.map((step, i) => /* @__PURE__ */
|
|
2120
|
-
/* @__PURE__ */
|
|
2121
|
-
/* @__PURE__ */
|
|
2122
|
-
/* @__PURE__ */
|
|
2123
|
-
/* @__PURE__ */
|
|
2636
|
+
/* @__PURE__ */ jsxs10("div", { className: "flex-1 space-y-3 overflow-y-auto p-4", children: [
|
|
2637
|
+
run.steps.length === 0 && /* @__PURE__ */ jsx12("p", { className: "text-sm text-muted-foreground", children: "No steps recorded yet." }),
|
|
2638
|
+
run.steps.map((step, i) => /* @__PURE__ */ jsxs10("div", { className: "rounded-lg border border-border/60 bg-background", children: [
|
|
2639
|
+
/* @__PURE__ */ jsxs10("div", { className: "flex items-baseline gap-2 border-b border-border/40 px-3 py-1.5", children: [
|
|
2640
|
+
/* @__PURE__ */ jsx12("span", { className: `font-mono text-[11px] ${step.status === "error" ? "text-destructive" : "text-muted-foreground"}`, children: step.status === "error" ? "\u2717" : "$" }),
|
|
2641
|
+
/* @__PURE__ */ jsx12("code", { className: "min-w-0 flex-1 truncate font-mono text-xs", children: step.label }),
|
|
2642
|
+
/* @__PURE__ */ jsx12("span", { className: "shrink-0 text-[10px] text-muted-foreground", children: new Date(step.at).toLocaleTimeString() })
|
|
2124
2643
|
] }),
|
|
2125
|
-
step.detail && /* @__PURE__ */
|
|
2644
|
+
step.detail && /* @__PURE__ */ jsx12("pre", { className: "max-h-48 overflow-auto whitespace-pre-wrap px-3 py-2 font-mono text-[11px] leading-relaxed text-muted-foreground", children: step.detail })
|
|
2126
2645
|
] }, i))
|
|
2127
2646
|
] }),
|
|
2128
|
-
/* @__PURE__ */
|
|
2647
|
+
/* @__PURE__ */ jsx12("p", { className: "border-t border-border px-4 py-2 text-[11px] text-muted-foreground", children: "Readonly drill-in. Follow up in the main chat." })
|
|
2129
2648
|
] });
|
|
2130
2649
|
}
|
|
2131
2650
|
function pendingApprovalOf(call) {
|
|
@@ -2139,20 +2658,20 @@ function ChatEmptyState({
|
|
|
2139
2658
|
subline = "Describe the outcome you want. The agent works through it step by step, and pauses for your approval before anything irreversible.",
|
|
2140
2659
|
doors
|
|
2141
2660
|
}) {
|
|
2142
|
-
return /* @__PURE__ */
|
|
2143
|
-
/* @__PURE__ */
|
|
2144
|
-
/* @__PURE__ */
|
|
2145
|
-
/* @__PURE__ */
|
|
2146
|
-
subline && /* @__PURE__ */
|
|
2147
|
-
doors && doors.length > 0 && /* @__PURE__ */
|
|
2661
|
+
return /* @__PURE__ */ jsxs10("div", { className: "mx-auto flex w-full max-w-2xl flex-col items-center px-6 py-12 text-center sm:py-20", children: [
|
|
2662
|
+
/* @__PURE__ */ jsx12("span", { className: "mb-5 inline-flex h-14 w-14 items-center justify-center rounded-2xl bg-primary/10 ring-1 ring-primary/15", children: /* @__PURE__ */ jsx12(BrandMark, { size: 32, className: "shrink-0" }) }),
|
|
2663
|
+
/* @__PURE__ */ jsx12("p", { className: "text-[11px] font-semibold uppercase tracking-[0.18em] text-muted-foreground", children: productName }),
|
|
2664
|
+
/* @__PURE__ */ jsx12("h2", { className: "mt-1.5 text-balance text-2xl font-semibold leading-tight text-foreground sm:text-[28px]", children: headline }),
|
|
2665
|
+
subline && /* @__PURE__ */ jsx12("p", { className: "mt-3 max-w-md text-[15px] leading-relaxed text-muted-foreground", children: subline }),
|
|
2666
|
+
doors && doors.length > 0 && /* @__PURE__ */ jsx12("div", { className: "mt-7 grid w-full gap-2.5 sm:grid-cols-3", children: doors.slice(0, 3).map((door, i) => /* @__PURE__ */ jsxs10(
|
|
2148
2667
|
"button",
|
|
2149
2668
|
{
|
|
2150
2669
|
type: "button",
|
|
2151
2670
|
onClick: door.onSelect,
|
|
2152
2671
|
className: "group flex min-h-[44px] flex-col items-start rounded-xl border border-border bg-card px-4 py-3 text-left transition hover:border-primary/40 hover:bg-accent/30 focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background",
|
|
2153
2672
|
children: [
|
|
2154
|
-
/* @__PURE__ */
|
|
2155
|
-
door.description && /* @__PURE__ */
|
|
2673
|
+
/* @__PURE__ */ jsx12("span", { className: "text-sm font-semibold text-foreground", children: door.label }),
|
|
2674
|
+
door.description && /* @__PURE__ */ jsx12("span", { className: "mt-0.5 text-[12px] leading-snug text-muted-foreground", children: door.description })
|
|
2156
2675
|
]
|
|
2157
2676
|
},
|
|
2158
2677
|
i
|
|
@@ -2161,26 +2680,26 @@ function ChatEmptyState({
|
|
|
2161
2680
|
}
|
|
2162
2681
|
function ToolGlyph({ name, className }) {
|
|
2163
2682
|
if (name.startsWith("sandbox_")) {
|
|
2164
|
-
return /* @__PURE__ */
|
|
2165
|
-
/* @__PURE__ */
|
|
2166
|
-
/* @__PURE__ */
|
|
2683
|
+
return /* @__PURE__ */ jsxs10("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: [
|
|
2684
|
+
/* @__PURE__ */ jsx12("polyline", { points: "4 17 10 11 4 5" }),
|
|
2685
|
+
/* @__PURE__ */ jsx12("line", { x1: "12", y1: "19", x2: "20", y2: "19" })
|
|
2167
2686
|
] });
|
|
2168
2687
|
}
|
|
2169
2688
|
if (name === "submit_proposal") {
|
|
2170
|
-
return /* @__PURE__ */
|
|
2171
|
-
/* @__PURE__ */
|
|
2172
|
-
/* @__PURE__ */
|
|
2689
|
+
return /* @__PURE__ */ jsxs10("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: [
|
|
2690
|
+
/* @__PURE__ */ jsx12("path", { d: "M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" }),
|
|
2691
|
+
/* @__PURE__ */ jsx12("path", { d: "M14 2v6h6M9 15l2 2 4-4" })
|
|
2173
2692
|
] });
|
|
2174
2693
|
}
|
|
2175
2694
|
if (name === "schedule_followup") {
|
|
2176
|
-
return /* @__PURE__ */
|
|
2177
|
-
/* @__PURE__ */
|
|
2178
|
-
/* @__PURE__ */
|
|
2695
|
+
return /* @__PURE__ */ jsxs10("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", "aria-hidden": true, children: [
|
|
2696
|
+
/* @__PURE__ */ jsx12("circle", { cx: "12", cy: "12", r: "9" }),
|
|
2697
|
+
/* @__PURE__ */ jsx12("path", { d: "M12 7v5l3 3" })
|
|
2179
2698
|
] });
|
|
2180
2699
|
}
|
|
2181
|
-
return /* @__PURE__ */
|
|
2182
|
-
/* @__PURE__ */
|
|
2183
|
-
/* @__PURE__ */
|
|
2700
|
+
return /* @__PURE__ */ jsxs10("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: [
|
|
2701
|
+
/* @__PURE__ */ jsx12("path", { d: "M12 3v3m0 12v3M3 12h3m12 0h3" }),
|
|
2702
|
+
/* @__PURE__ */ jsx12("circle", { cx: "12", cy: "12", r: "4" })
|
|
2184
2703
|
] });
|
|
2185
2704
|
}
|
|
2186
2705
|
function toolOutcomeOf(call) {
|
|
@@ -2242,42 +2761,47 @@ function proposalPreview(call) {
|
|
|
2242
2761
|
}
|
|
2243
2762
|
function truncate(v, max = 240) {
|
|
2244
2763
|
const s = typeof v === "string" ? v : JSON.stringify(v);
|
|
2764
|
+
if (typeof s !== "string") return "";
|
|
2245
2765
|
return s.length > max ? `${s.slice(0, max)}\u2026` : s;
|
|
2246
2766
|
}
|
|
2247
2767
|
function KvRows({ data }) {
|
|
2248
2768
|
const entries = Object.entries(data).filter(([, v]) => v !== void 0 && v !== null && v !== "");
|
|
2249
2769
|
if (!entries.length) return null;
|
|
2250
|
-
return /* @__PURE__ */
|
|
2251
|
-
/* @__PURE__ */
|
|
2252
|
-
/* @__PURE__ */
|
|
2770
|
+
return /* @__PURE__ */ jsx12("dl", { className: "grid grid-cols-[auto_1fr] gap-x-3 gap-y-1", children: entries.map(([k, v]) => /* @__PURE__ */ jsxs10("div", { className: "contents", children: [
|
|
2771
|
+
/* @__PURE__ */ jsx12("dt", { className: "font-mono text-[11px] text-muted-foreground", children: k }),
|
|
2772
|
+
/* @__PURE__ */ jsx12("dd", { className: "min-w-0 whitespace-pre-wrap break-words font-mono text-[11px] text-muted-foreground", children: truncate(v) })
|
|
2253
2773
|
] }, k)) });
|
|
2254
2774
|
}
|
|
2255
2775
|
function ShellDetail({ call }) {
|
|
2256
2776
|
const outcome = toolOutcomeOf(call);
|
|
2257
2777
|
const r = outcome?.result ?? {};
|
|
2258
|
-
return /* @__PURE__ */
|
|
2259
|
-
/* @__PURE__ */
|
|
2260
|
-
/* @__PURE__ */
|
|
2261
|
-
/* @__PURE__ */
|
|
2262
|
-
r.exitCode != null && /* @__PURE__ */
|
|
2778
|
+
return /* @__PURE__ */ jsxs10("div", { className: "overflow-hidden rounded-md bg-zinc-900 font-mono text-[11px] leading-relaxed", children: [
|
|
2779
|
+
/* @__PURE__ */ jsxs10("div", { className: "flex items-center gap-2 px-3 pt-2 text-zinc-400", children: [
|
|
2780
|
+
/* @__PURE__ */ jsx12("span", { className: "select-none text-zinc-500", children: "$" }),
|
|
2781
|
+
/* @__PURE__ */ jsx12("span", { className: "min-w-0 flex-1 truncate text-zinc-200", children: String(call.args?.command ?? "") }),
|
|
2782
|
+
r.exitCode != null && /* @__PURE__ */ jsxs10("span", { className: r.exitCode === 0 ? "text-success" : "text-destructive", children: [
|
|
2263
2783
|
"exit ",
|
|
2264
2784
|
r.exitCode
|
|
2265
2785
|
] })
|
|
2266
2786
|
] }),
|
|
2267
|
-
/* @__PURE__ */
|
|
2787
|
+
/* @__PURE__ */ jsx12("pre", { className: "max-h-56 overflow-auto whitespace-pre-wrap px-3 pb-2.5 pt-1.5 text-zinc-300", children: outcome?.ok === false ? outcome.message ?? "failed" : [r.stdout, r.stderr].filter(Boolean).join("\n") || "(no output)" })
|
|
2268
2788
|
] });
|
|
2269
2789
|
}
|
|
2270
2790
|
function DefaultToolDetail({ call }) {
|
|
2271
|
-
const
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
/* @__PURE__ */
|
|
2791
|
+
const result = call.result;
|
|
2792
|
+
const envelope = typeof result === "object" && result !== null ? result : null;
|
|
2793
|
+
return /* @__PURE__ */ jsxs10("div", { className: "space-y-2", children: [
|
|
2794
|
+
call.args && Object.keys(call.args).length > 0 && /* @__PURE__ */ jsxs10("div", { children: [
|
|
2795
|
+
/* @__PURE__ */ jsx12("p", { className: "mb-1 text-[10px] font-semibold uppercase tracking-wide text-muted-foreground", children: "Called with" }),
|
|
2796
|
+
/* @__PURE__ */ jsx12(KvRows, { data: call.args })
|
|
2276
2797
|
] }),
|
|
2277
|
-
|
|
2278
|
-
/* @__PURE__ */
|
|
2279
|
-
|
|
2280
|
-
] })
|
|
2798
|
+
envelope ? /* @__PURE__ */ jsxs10("div", { children: [
|
|
2799
|
+
/* @__PURE__ */ jsx12("p", { className: "mb-1 text-[10px] font-semibold uppercase tracking-wide text-muted-foreground", children: envelope.ok === false ? "Failed" : "Result" }),
|
|
2800
|
+
envelope.ok === false ? /* @__PURE__ */ jsx12("p", { className: "text-xs text-destructive", children: envelope.message ?? "Tool failed" }) : envelope.result && typeof envelope.result === "object" ? /* @__PURE__ */ jsx12(KvRows, { data: envelope.result }) : envelope.result != null ? /* @__PURE__ */ jsx12("p", { className: "font-mono text-[11px] text-muted-foreground", children: truncate(envelope.result) }) : null
|
|
2801
|
+
] }) : result != null ? /* @__PURE__ */ jsxs10("div", { children: [
|
|
2802
|
+
/* @__PURE__ */ jsx12("p", { className: "mb-1 text-[10px] font-semibold uppercase tracking-wide text-muted-foreground", children: "Result" }),
|
|
2803
|
+
/* @__PURE__ */ jsx12("p", { className: "font-mono text-[11px] text-muted-foreground", children: truncate(result) })
|
|
2804
|
+
] }) : null
|
|
2281
2805
|
] });
|
|
2282
2806
|
}
|
|
2283
2807
|
function ProposalCard({
|
|
@@ -2287,23 +2811,23 @@ function ProposalCard({
|
|
|
2287
2811
|
approval,
|
|
2288
2812
|
renderers
|
|
2289
2813
|
}) {
|
|
2290
|
-
const [expanded, setExpanded] =
|
|
2814
|
+
const [expanded, setExpanded] = useState12(false);
|
|
2291
2815
|
const { summary, meta } = proposalPreview(call);
|
|
2292
2816
|
const custom = renderers?.[call.name]?.(call, message);
|
|
2293
2817
|
const { pending: deciding, run: decide } = usePending();
|
|
2294
|
-
return /* @__PURE__ */
|
|
2295
|
-
/* @__PURE__ */
|
|
2296
|
-
/* @__PURE__ */
|
|
2297
|
-
/* @__PURE__ */
|
|
2298
|
-
/* @__PURE__ */
|
|
2299
|
-
/* @__PURE__ */
|
|
2300
|
-
summary && /* @__PURE__ */
|
|
2301
|
-
meta.length > 0 && /* @__PURE__ */
|
|
2818
|
+
return /* @__PURE__ */ jsxs10("div", { className: "w-full max-w-full rounded-xl border border-warning/50 bg-warning/[0.06] text-sm shadow-sm ring-1 ring-warning/10", children: [
|
|
2819
|
+
/* @__PURE__ */ jsxs10("div", { className: "flex items-start gap-2.5 px-4 pt-3.5", children: [
|
|
2820
|
+
/* @__PURE__ */ jsx12("span", { className: "mt-0.5 inline-flex h-6 w-6 shrink-0 items-center justify-center rounded-full bg-warning/15 text-warning", children: /* @__PURE__ */ jsx12(ToolGlyph, { name: call.name, className: "h-3.5 w-3.5" }) }),
|
|
2821
|
+
/* @__PURE__ */ jsxs10("div", { className: "min-w-0 flex-1", children: [
|
|
2822
|
+
/* @__PURE__ */ jsx12("p", { className: "text-[10px] font-semibold uppercase tracking-wider text-warning-foreground", children: "Needs your approval" }),
|
|
2823
|
+
/* @__PURE__ */ jsx12("p", { className: "mt-0.5 text-[15px] font-semibold leading-snug text-foreground", children: friendlyToolTitle(call) }),
|
|
2824
|
+
summary && /* @__PURE__ */ jsx12("p", { className: "mt-1 text-[13px] leading-relaxed text-muted-foreground", children: summary }),
|
|
2825
|
+
meta.length > 0 && /* @__PURE__ */ jsx12("div", { className: "mt-1.5 flex flex-wrap items-center gap-1.5", children: meta.map((m, i) => /* @__PURE__ */ jsx12("span", { className: "rounded-full bg-muted/60 px-2 py-0.5 text-[11px] font-medium text-muted-foreground", children: m }, i)) })
|
|
2302
2826
|
] })
|
|
2303
2827
|
] }),
|
|
2304
|
-
/* @__PURE__ */
|
|
2305
|
-
approval ? /* @__PURE__ */
|
|
2306
|
-
/* @__PURE__ */
|
|
2828
|
+
/* @__PURE__ */ jsxs10("div", { className: "flex flex-wrap items-center gap-2 px-4 pb-3.5 pt-3", children: [
|
|
2829
|
+
approval ? /* @__PURE__ */ jsxs10(Fragment5, { children: [
|
|
2830
|
+
/* @__PURE__ */ jsx12(
|
|
2307
2831
|
"button",
|
|
2308
2832
|
{
|
|
2309
2833
|
type: "button",
|
|
@@ -2313,7 +2837,7 @@ function ProposalCard({
|
|
|
2313
2837
|
children: "Approve & run"
|
|
2314
2838
|
}
|
|
2315
2839
|
),
|
|
2316
|
-
/* @__PURE__ */
|
|
2840
|
+
/* @__PURE__ */ jsx12(
|
|
2317
2841
|
"button",
|
|
2318
2842
|
{
|
|
2319
2843
|
type: "button",
|
|
@@ -2323,8 +2847,8 @@ function ProposalCard({
|
|
|
2323
2847
|
children: "Reject"
|
|
2324
2848
|
}
|
|
2325
2849
|
)
|
|
2326
|
-
] }) : /* @__PURE__ */
|
|
2327
|
-
/* @__PURE__ */
|
|
2850
|
+
] }) : /* @__PURE__ */ jsx12("span", { className: "text-[12px] font-medium text-muted-foreground", children: "Awaiting approval\u2026" }),
|
|
2851
|
+
/* @__PURE__ */ jsxs10(
|
|
2328
2852
|
"button",
|
|
2329
2853
|
{
|
|
2330
2854
|
type: "button",
|
|
@@ -2333,23 +2857,23 @@ function ProposalCard({
|
|
|
2333
2857
|
className: "ml-auto inline-flex items-center gap-1 rounded text-[12px] font-medium text-muted-foreground transition hover:text-foreground focus:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
2334
2858
|
children: [
|
|
2335
2859
|
expanded ? "Hide details" : "View details",
|
|
2336
|
-
/* @__PURE__ */
|
|
2860
|
+
/* @__PURE__ */ jsx12(ChevronDown, { className: `h-3 w-3 transition-transform ${expanded ? "rotate-180" : ""}` })
|
|
2337
2861
|
]
|
|
2338
2862
|
}
|
|
2339
2863
|
)
|
|
2340
2864
|
] }),
|
|
2341
|
-
expanded && /* @__PURE__ */
|
|
2865
|
+
expanded && /* @__PURE__ */ jsx12("div", { className: "border-t border-warning/20 px-4 py-3 text-xs", children: custom ?? /* @__PURE__ */ jsx12(DefaultToolDetail, { call }) })
|
|
2342
2866
|
] });
|
|
2343
2867
|
}
|
|
2344
2868
|
function FollowupCard({ call }) {
|
|
2345
2869
|
const a = call.args ?? {};
|
|
2346
2870
|
const when = typeof a.when === "string" ? a.when : typeof a.at === "string" ? a.at : typeof a.schedule === "string" ? a.schedule : null;
|
|
2347
|
-
return /* @__PURE__ */
|
|
2348
|
-
/* @__PURE__ */
|
|
2349
|
-
/* @__PURE__ */
|
|
2350
|
-
/* @__PURE__ */
|
|
2871
|
+
return /* @__PURE__ */ jsxs10("div", { className: "w-fit min-w-[260px] max-w-full rounded-lg border border-border/60 border-l-2 border-l-primary/60 bg-muted/20 px-3 py-2 text-sm", children: [
|
|
2872
|
+
/* @__PURE__ */ jsxs10("div", { className: "flex items-center gap-2", children: [
|
|
2873
|
+
/* @__PURE__ */ jsx12(ToolGlyph, { name: call.name, className: "h-3.5 w-3.5 shrink-0 text-primary/80" }),
|
|
2874
|
+
/* @__PURE__ */ jsx12("span", { className: "min-w-0 flex-1 truncate font-medium text-foreground", children: friendlyToolTitle(call) })
|
|
2351
2875
|
] }),
|
|
2352
|
-
when && /* @__PURE__ */
|
|
2876
|
+
when && /* @__PURE__ */ jsx12("p", { className: "mt-0.5 pl-[22px] text-[12px] text-muted-foreground", children: when })
|
|
2353
2877
|
] });
|
|
2354
2878
|
}
|
|
2355
2879
|
function ToolCallCard({
|
|
@@ -2359,13 +2883,13 @@ function ToolCallCard({
|
|
|
2359
2883
|
onOpenRun,
|
|
2360
2884
|
renderers
|
|
2361
2885
|
}) {
|
|
2362
|
-
const [expanded, setExpanded] =
|
|
2886
|
+
const [expanded, setExpanded] = useState12(false);
|
|
2363
2887
|
const pending = call.status === "done" ? pendingApprovalOf(call) : null;
|
|
2364
2888
|
const kind = blockKindOf(call);
|
|
2365
2889
|
const failed = call.status === "error" || toolOutcomeOf(call)?.ok === false;
|
|
2366
2890
|
const custom = renderers?.[call.name]?.(call, message);
|
|
2367
2891
|
if (pending) {
|
|
2368
|
-
return /* @__PURE__ */
|
|
2892
|
+
return /* @__PURE__ */ jsx12(
|
|
2369
2893
|
ProposalCard,
|
|
2370
2894
|
{
|
|
2371
2895
|
call,
|
|
@@ -2377,16 +2901,16 @@ function ToolCallCard({
|
|
|
2377
2901
|
);
|
|
2378
2902
|
}
|
|
2379
2903
|
if (kind === "followup" && !failed) {
|
|
2380
|
-
return /* @__PURE__ */
|
|
2904
|
+
return /* @__PURE__ */ jsx12(FollowupCard, { call });
|
|
2381
2905
|
}
|
|
2382
2906
|
const isCommand = kind === "command";
|
|
2383
|
-
return /* @__PURE__ */
|
|
2907
|
+
return /* @__PURE__ */ jsxs10(
|
|
2384
2908
|
"div",
|
|
2385
2909
|
{
|
|
2386
2910
|
className: `w-fit min-w-[280px] max-w-full rounded-lg border text-xs transition ${failed ? "border-destructive/40 bg-destructive/5" : "border-border/60 bg-muted/20"}`,
|
|
2387
2911
|
children: [
|
|
2388
|
-
/* @__PURE__ */
|
|
2389
|
-
/* @__PURE__ */
|
|
2912
|
+
/* @__PURE__ */ jsxs10("div", { className: "flex w-full items-center gap-2 px-3 py-2", children: [
|
|
2913
|
+
/* @__PURE__ */ jsxs10(
|
|
2390
2914
|
"button",
|
|
2391
2915
|
{
|
|
2392
2916
|
type: "button",
|
|
@@ -2394,14 +2918,14 @@ function ToolCallCard({
|
|
|
2394
2918
|
"aria-expanded": expanded,
|
|
2395
2919
|
className: "flex min-w-0 flex-1 items-center gap-2 rounded text-left focus:outline-none focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
2396
2920
|
children: [
|
|
2397
|
-
/* @__PURE__ */
|
|
2921
|
+
/* @__PURE__ */ jsx12(
|
|
2398
2922
|
"span",
|
|
2399
2923
|
{
|
|
2400
2924
|
className: `h-2 w-2 shrink-0 rounded-full ${call.status === "running" ? "animate-pulse bg-warning" : failed ? "bg-destructive" : "bg-success"}`
|
|
2401
2925
|
}
|
|
2402
2926
|
),
|
|
2403
|
-
/* @__PURE__ */
|
|
2404
|
-
/* @__PURE__ */
|
|
2927
|
+
/* @__PURE__ */ jsx12(ToolGlyph, { name: call.name, className: "h-3.5 w-3.5 shrink-0 text-muted-foreground" }),
|
|
2928
|
+
/* @__PURE__ */ jsx12(
|
|
2405
2929
|
"span",
|
|
2406
2930
|
{
|
|
2407
2931
|
className: `min-w-0 flex-1 truncate ${isCommand ? "font-mono text-[12px] tracking-tight text-foreground/90" : "font-medium"}`,
|
|
@@ -2411,8 +2935,8 @@ function ToolCallCard({
|
|
|
2411
2935
|
]
|
|
2412
2936
|
}
|
|
2413
2937
|
),
|
|
2414
|
-
/* @__PURE__ */
|
|
2415
|
-
/* @__PURE__ */
|
|
2938
|
+
/* @__PURE__ */ jsx12("span", { className: "shrink-0 text-[11px] text-muted-foreground", children: call.status === "running" ? "running\u2026" : failed ? "failed" : "done" }),
|
|
2939
|
+
/* @__PURE__ */ jsx12(
|
|
2416
2940
|
"button",
|
|
2417
2941
|
{
|
|
2418
2942
|
type: "button",
|
|
@@ -2420,13 +2944,13 @@ function ToolCallCard({
|
|
|
2420
2944
|
"aria-label": expanded ? "Collapse details" : "Expand details",
|
|
2421
2945
|
"aria-expanded": expanded,
|
|
2422
2946
|
className: "shrink-0 rounded p-0.5 focus:outline-none focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
2423
|
-
children: /* @__PURE__ */
|
|
2947
|
+
children: /* @__PURE__ */ jsx12(ChevronDown, { className: `h-3 w-3 text-muted-foreground transition-transform ${expanded ? "rotate-180" : ""}` })
|
|
2424
2948
|
}
|
|
2425
2949
|
)
|
|
2426
2950
|
] }),
|
|
2427
|
-
expanded && /* @__PURE__ */
|
|
2428
|
-
custom ?? (call.name === "sandbox_run_command" ? /* @__PURE__ */
|
|
2429
|
-
onOpenRun && call.name.startsWith("sandbox_") && /* @__PURE__ */
|
|
2951
|
+
expanded && /* @__PURE__ */ jsxs10("div", { className: "border-t border-border/40 px-3 py-2.5", children: [
|
|
2952
|
+
custom ?? (call.name === "sandbox_run_command" ? /* @__PURE__ */ jsx12(ShellDetail, { call }) : /* @__PURE__ */ jsx12(DefaultToolDetail, { call })),
|
|
2953
|
+
onOpenRun && call.name.startsWith("sandbox_") && /* @__PURE__ */ jsx12(
|
|
2430
2954
|
"button",
|
|
2431
2955
|
{
|
|
2432
2956
|
type: "button",
|
|
@@ -2441,7 +2965,7 @@ function ToolCallCard({
|
|
|
2441
2965
|
);
|
|
2442
2966
|
}
|
|
2443
2967
|
function StreamingCaret() {
|
|
2444
|
-
return /* @__PURE__ */
|
|
2968
|
+
return /* @__PURE__ */ jsx12(
|
|
2445
2969
|
"span",
|
|
2446
2970
|
{
|
|
2447
2971
|
className: "ml-0.5 inline-block h-[1.1em] w-[3px] translate-y-[2px] animate-pulse rounded-sm bg-foreground/70",
|
|
@@ -2458,9 +2982,9 @@ function SegmentText({
|
|
|
2458
2982
|
const text = useSmoothText(content, streaming);
|
|
2459
2983
|
const body = useMemo7(() => renderBody(text), [renderBody, text]);
|
|
2460
2984
|
if (!content.trim() && !showCaret) return null;
|
|
2461
|
-
return /* @__PURE__ */
|
|
2985
|
+
return /* @__PURE__ */ jsxs10("div", { className: "text-base leading-[1.75]", children: [
|
|
2462
2986
|
body,
|
|
2463
|
-
showCaret && /* @__PURE__ */
|
|
2987
|
+
showCaret && /* @__PURE__ */ jsx12(StreamingCaret, {})
|
|
2464
2988
|
] });
|
|
2465
2989
|
}
|
|
2466
2990
|
var COLLAPSE_TOOL_RUN_AT = 3;
|
|
@@ -2483,7 +3007,7 @@ function SegmentedBody({
|
|
|
2483
3007
|
const leftoverToolCalls = (msg.toolCalls ?? []).filter(
|
|
2484
3008
|
(tc) => !segmentToolIds.has(tc.id)
|
|
2485
3009
|
);
|
|
2486
|
-
const renderToolCard = (call) => /* @__PURE__ */
|
|
3010
|
+
const renderToolCard = (call) => /* @__PURE__ */ jsx12(
|
|
2487
3011
|
ToolCallCard,
|
|
2488
3012
|
{
|
|
2489
3013
|
call,
|
|
@@ -2506,9 +3030,9 @@ function SegmentedBody({
|
|
|
2506
3030
|
else groups.push({ kind: "tools", index: i, calls: [seg.call] });
|
|
2507
3031
|
}
|
|
2508
3032
|
}
|
|
2509
|
-
return /* @__PURE__ */
|
|
3033
|
+
return /* @__PURE__ */ jsxs10("div", { className: "flex flex-col gap-2", children: [
|
|
2510
3034
|
groups.map(
|
|
2511
|
-
(g) => g.kind === "text" ? /* @__PURE__ */
|
|
3035
|
+
(g) => g.kind === "text" ? /* @__PURE__ */ jsx12(
|
|
2512
3036
|
SegmentText,
|
|
2513
3037
|
{
|
|
2514
3038
|
content: g.content,
|
|
@@ -2517,24 +3041,24 @@ function SegmentedBody({
|
|
|
2517
3041
|
renderBody
|
|
2518
3042
|
},
|
|
2519
3043
|
`text-${g.index}`
|
|
2520
|
-
) : !streaming && g.calls.length >= COLLAPSE_TOOL_RUN_AT && !g.calls.some(isImportantTool) ? /* @__PURE__ */
|
|
3044
|
+
) : !streaming && g.calls.length >= COLLAPSE_TOOL_RUN_AT && !g.calls.some(isImportantTool) ? /* @__PURE__ */ jsxs10(
|
|
2521
3045
|
"details",
|
|
2522
3046
|
{
|
|
2523
3047
|
className: "rounded-lg border-l-2 border-border/70 bg-muted/20 px-3 py-2",
|
|
2524
3048
|
children: [
|
|
2525
|
-
/* @__PURE__ */
|
|
3049
|
+
/* @__PURE__ */ jsxs10("summary", { className: "cursor-pointer select-none text-xs font-medium text-muted-foreground", children: [
|
|
2526
3050
|
"Worked through ",
|
|
2527
3051
|
g.calls.length,
|
|
2528
3052
|
" steps"
|
|
2529
3053
|
] }),
|
|
2530
|
-
/* @__PURE__ */
|
|
3054
|
+
/* @__PURE__ */ jsx12("div", { className: "mt-2 flex flex-col gap-2", children: g.calls.map(renderToolCard) })
|
|
2531
3055
|
]
|
|
2532
3056
|
},
|
|
2533
3057
|
`tools-${g.index}`
|
|
2534
|
-
) : /* @__PURE__ */
|
|
3058
|
+
) : /* @__PURE__ */ jsx12("div", { className: "flex flex-col gap-2", children: g.calls.map(renderToolCard) }, `tools-${g.index}`)
|
|
2535
3059
|
),
|
|
2536
3060
|
leftoverToolCalls.map(renderToolCard),
|
|
2537
|
-
streaming && segments[lastIndex]?.kind === "tool" && /* @__PURE__ */
|
|
3061
|
+
streaming && segments[lastIndex]?.kind === "tool" && /* @__PURE__ */ jsx12(StreamingCaret, {})
|
|
2538
3062
|
] });
|
|
2539
3063
|
}
|
|
2540
3064
|
function AssistantMessageImpl({
|
|
@@ -2546,44 +3070,45 @@ function AssistantMessageImpl({
|
|
|
2546
3070
|
approval,
|
|
2547
3071
|
onToolCallClick,
|
|
2548
3072
|
toolRenderers,
|
|
2549
|
-
renderExtras
|
|
3073
|
+
renderExtras,
|
|
3074
|
+
durableCards
|
|
2550
3075
|
}) {
|
|
2551
3076
|
const content = useSmoothText(msg.content, streaming);
|
|
2552
3077
|
const reasoning = useSmoothText(msg.reasoning ?? "", streaming);
|
|
2553
3078
|
const body = useMemo7(() => renderBody(content), [renderBody, content]);
|
|
2554
3079
|
const segments = msg.segments;
|
|
2555
3080
|
const hasAnswerText = content !== "" || (segments?.some((s) => s.kind === "text" && s.content.trim() !== "") ?? false);
|
|
2556
|
-
const reasoningScrollRef =
|
|
2557
|
-
const thinkStartRef =
|
|
2558
|
-
const thinkMsRef =
|
|
3081
|
+
const reasoningScrollRef = useRef8(null);
|
|
3082
|
+
const thinkStartRef = useRef8(null);
|
|
3083
|
+
const thinkMsRef = useRef8(null);
|
|
2559
3084
|
if (streaming && reasoning && !hasAnswerText && thinkStartRef.current === null) {
|
|
2560
3085
|
thinkStartRef.current = performance.now();
|
|
2561
3086
|
}
|
|
2562
3087
|
if (hasAnswerText && thinkStartRef.current !== null && thinkMsRef.current === null) {
|
|
2563
3088
|
thinkMsRef.current = performance.now() - thinkStartRef.current;
|
|
2564
3089
|
}
|
|
2565
|
-
|
|
3090
|
+
useEffect8(() => {
|
|
2566
3091
|
const el = reasoningScrollRef.current;
|
|
2567
3092
|
if (el && streaming && !hasAnswerText) el.scrollTop = el.scrollHeight;
|
|
2568
3093
|
}, [reasoning, streaming, hasAnswerText]);
|
|
2569
3094
|
const thinkingSeconds = useThinkingSeconds(
|
|
2570
3095
|
streaming && !!reasoning && !hasAnswerText
|
|
2571
3096
|
);
|
|
2572
|
-
return /* @__PURE__ */
|
|
2573
|
-
/* @__PURE__ */
|
|
2574
|
-
/* @__PURE__ */
|
|
2575
|
-
msg.modelUsed && /* @__PURE__ */
|
|
2576
|
-
formatTokensPerSecond(msg) && /* @__PURE__ */
|
|
2577
|
-
formatModelCost(msg, models) && /* @__PURE__ */
|
|
3097
|
+
return /* @__PURE__ */ jsxs10("div", { className: "mx-auto w-full max-w-3xl px-6 py-3", children: [
|
|
3098
|
+
/* @__PURE__ */ jsxs10("div", { className: "mb-1 flex items-baseline gap-2 text-[11px] tracking-wide text-muted-foreground", children: [
|
|
3099
|
+
/* @__PURE__ */ jsx12("span", { className: "font-semibold uppercase", children: agentLabel }),
|
|
3100
|
+
msg.modelUsed && /* @__PURE__ */ jsx12("span", { className: "font-mono normal-case", children: msg.modelUsed }),
|
|
3101
|
+
formatTokensPerSecond(msg) && /* @__PURE__ */ jsx12("span", { children: formatTokensPerSecond(msg) }),
|
|
3102
|
+
formatModelCost(msg, models) && /* @__PURE__ */ jsx12("span", { children: formatModelCost(msg, models) })
|
|
2578
3103
|
] }),
|
|
2579
|
-
reasoning && /* @__PURE__ */
|
|
2580
|
-
/* @__PURE__ */
|
|
3104
|
+
reasoning && /* @__PURE__ */ jsxs10("details", { className: "mb-2 rounded-lg border-l-2 border-border/70 bg-muted/20 px-3 py-2", open: !hasAnswerText, children: [
|
|
3105
|
+
/* @__PURE__ */ jsx12("summary", { className: "cursor-pointer select-none text-xs font-medium text-muted-foreground", children: !hasAnswerText ? /* @__PURE__ */ jsxs10("span", { className: "animate-pulse", children: [
|
|
2581
3106
|
"Thinking",
|
|
2582
3107
|
thinkingSeconds >= 3 ? ` \xB7 ${thinkingSeconds}s` : "\u2026"
|
|
2583
3108
|
] }) : thinkMsRef.current != null ? `Thought for ${Math.max(1, Math.round(thinkMsRef.current / 1e3))}s` : "Thought process" }),
|
|
2584
|
-
/* @__PURE__ */
|
|
3109
|
+
/* @__PURE__ */ jsx12("div", { ref: reasoningScrollRef, className: "mt-2 max-h-48 overflow-y-auto whitespace-pre-wrap text-[13px] leading-relaxed text-muted-foreground", children: reasoning })
|
|
2585
3110
|
] }),
|
|
2586
|
-
segments && segments.length > 0 ? /* @__PURE__ */
|
|
3111
|
+
segments && segments.length > 0 ? /* @__PURE__ */ jsx12(
|
|
2587
3112
|
SegmentedBody,
|
|
2588
3113
|
{
|
|
2589
3114
|
segments,
|
|
@@ -2594,12 +3119,12 @@ function AssistantMessageImpl({
|
|
|
2594
3119
|
onToolCallClick,
|
|
2595
3120
|
toolRenderers
|
|
2596
3121
|
}
|
|
2597
|
-
) : /* @__PURE__ */
|
|
2598
|
-
/* @__PURE__ */
|
|
3122
|
+
) : /* @__PURE__ */ jsxs10(Fragment5, { children: [
|
|
3123
|
+
/* @__PURE__ */ jsxs10("div", { className: "text-base leading-[1.75]", children: [
|
|
2599
3124
|
body,
|
|
2600
|
-
streaming && content && !msg.toolCalls?.length && /* @__PURE__ */
|
|
3125
|
+
streaming && content && !msg.toolCalls?.length && /* @__PURE__ */ jsx12(StreamingCaret, {})
|
|
2601
3126
|
] }),
|
|
2602
|
-
msg.toolCalls && msg.toolCalls.length > 0 && /* @__PURE__ */
|
|
3127
|
+
msg.toolCalls && msg.toolCalls.length > 0 && /* @__PURE__ */ jsx12("div", { className: "mt-2 flex flex-col gap-1.5", children: msg.toolCalls.map((tc) => /* @__PURE__ */ jsx12(
|
|
2603
3128
|
ToolCallCard,
|
|
2604
3129
|
{
|
|
2605
3130
|
call: tc,
|
|
@@ -2611,13 +3136,22 @@ function AssistantMessageImpl({
|
|
|
2611
3136
|
tc.id
|
|
2612
3137
|
)) })
|
|
2613
3138
|
] }),
|
|
3139
|
+
durableCards && msg.parts && /* @__PURE__ */ jsx12(
|
|
3140
|
+
DurableChatCards,
|
|
3141
|
+
{
|
|
3142
|
+
...durableCards,
|
|
3143
|
+
parts: msg.parts,
|
|
3144
|
+
renderMarkdown: renderBody,
|
|
3145
|
+
className: "mt-3"
|
|
3146
|
+
}
|
|
3147
|
+
),
|
|
2614
3148
|
renderExtras?.(msg)
|
|
2615
3149
|
] });
|
|
2616
3150
|
}
|
|
2617
3151
|
var AssistantMessage = memo(AssistantMessageImpl);
|
|
2618
3152
|
function useThinkingSeconds(active) {
|
|
2619
|
-
const [seconds, setSeconds] =
|
|
2620
|
-
|
|
3153
|
+
const [seconds, setSeconds] = useState12(0);
|
|
3154
|
+
useEffect8(() => {
|
|
2621
3155
|
if (!active) return;
|
|
2622
3156
|
setSeconds(0);
|
|
2623
3157
|
const id = setInterval(() => setSeconds((s) => s + 1), 1e3);
|
|
@@ -2627,23 +3161,23 @@ function useThinkingSeconds(active) {
|
|
|
2627
3161
|
}
|
|
2628
3162
|
function ThinkingRow({ agentLabel }) {
|
|
2629
3163
|
const seconds = useThinkingSeconds(true);
|
|
2630
|
-
return /* @__PURE__ */
|
|
2631
|
-
/* @__PURE__ */
|
|
2632
|
-
/* @__PURE__ */
|
|
2633
|
-
/* @__PURE__ */
|
|
3164
|
+
return /* @__PURE__ */ jsxs10("div", { className: "mx-auto w-full max-w-3xl px-6 py-3", children: [
|
|
3165
|
+
/* @__PURE__ */ jsx12("p", { className: "mb-1 text-[11px] font-semibold uppercase tracking-wide text-muted-foreground", children: agentLabel }),
|
|
3166
|
+
/* @__PURE__ */ jsxs10("div", { className: "flex items-center gap-2 text-base text-muted-foreground", children: [
|
|
3167
|
+
/* @__PURE__ */ jsx12("svg", { className: "h-4 w-4 animate-spin", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", "aria-hidden": true, children: /* @__PURE__ */ jsx12("path", { d: "M21 12a9 9 0 1 1-6.219-8.56", strokeLinecap: "round" }) }),
|
|
2634
3168
|
"Thinking",
|
|
2635
3169
|
seconds >= 3 ? ` \xB7 ${seconds}s` : "..."
|
|
2636
3170
|
] })
|
|
2637
3171
|
] });
|
|
2638
3172
|
}
|
|
2639
3173
|
function StreamErrorRow({ message, onRetry }) {
|
|
2640
|
-
return /* @__PURE__ */
|
|
2641
|
-
/* @__PURE__ */
|
|
2642
|
-
/* @__PURE__ */
|
|
2643
|
-
/* @__PURE__ */
|
|
3174
|
+
return /* @__PURE__ */ jsx12("div", { className: "mx-auto w-full max-w-3xl px-6 py-3", children: /* @__PURE__ */ jsxs10("div", { role: "alert", className: "flex items-start gap-2.5 rounded-lg border border-destructive/40 bg-destructive/5 px-3 py-2.5 text-sm text-destructive", children: [
|
|
3175
|
+
/* @__PURE__ */ jsxs10("svg", { className: "mt-0.5 h-4 w-4 shrink-0", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: [
|
|
3176
|
+
/* @__PURE__ */ jsx12("circle", { cx: "12", cy: "12", r: "9" }),
|
|
3177
|
+
/* @__PURE__ */ jsx12("path", { d: "M12 8v4m0 4h.01" })
|
|
2644
3178
|
] }),
|
|
2645
|
-
/* @__PURE__ */
|
|
2646
|
-
onRetry && /* @__PURE__ */
|
|
3179
|
+
/* @__PURE__ */ jsx12("span", { className: "min-w-0 flex-1 break-words", children: message }),
|
|
3180
|
+
onRetry && /* @__PURE__ */ jsx12(
|
|
2647
3181
|
"button",
|
|
2648
3182
|
{
|
|
2649
3183
|
type: "button",
|
|
@@ -2659,6 +3193,7 @@ function ChatMessages({
|
|
|
2659
3193
|
models = [],
|
|
2660
3194
|
renderMarkdown,
|
|
2661
3195
|
renderExtras,
|
|
3196
|
+
durableCards,
|
|
2662
3197
|
userLabel = "User",
|
|
2663
3198
|
agentLabel = "Agent",
|
|
2664
3199
|
loading,
|
|
@@ -2672,24 +3207,24 @@ function ChatMessages({
|
|
|
2672
3207
|
header
|
|
2673
3208
|
}) {
|
|
2674
3209
|
const renderBody = useMemo7(
|
|
2675
|
-
() => renderMarkdown ?? ((content) => /* @__PURE__ */
|
|
3210
|
+
() => renderMarkdown ?? ((content) => /* @__PURE__ */ jsx12("p", { className: "whitespace-pre-wrap", children: content })),
|
|
2676
3211
|
[renderMarkdown]
|
|
2677
3212
|
);
|
|
2678
3213
|
const lastIsUser = messages[messages.length - 1]?.role === "user";
|
|
2679
3214
|
if (messages.length === 0 && !loading && !error) {
|
|
2680
|
-
const empty = renderEmpty ? renderEmpty() : /* @__PURE__ */
|
|
2681
|
-
return /* @__PURE__ */
|
|
3215
|
+
const empty = renderEmpty ? renderEmpty() : /* @__PURE__ */ jsx12(ChatEmptyState, { ...emptyState });
|
|
3216
|
+
return /* @__PURE__ */ jsxs10(Fragment5, { children: [
|
|
2682
3217
|
header,
|
|
2683
3218
|
empty
|
|
2684
3219
|
] });
|
|
2685
3220
|
}
|
|
2686
|
-
return /* @__PURE__ */
|
|
3221
|
+
return /* @__PURE__ */ jsxs10(Fragment5, { children: [
|
|
2687
3222
|
header,
|
|
2688
3223
|
messages.map(
|
|
2689
|
-
(msg) => msg.role === "user" ? /* @__PURE__ */
|
|
2690
|
-
/* @__PURE__ */
|
|
2691
|
-
/* @__PURE__ */
|
|
2692
|
-
] }) }, msg.id) : /* @__PURE__ */
|
|
3224
|
+
(msg) => msg.role === "user" ? /* @__PURE__ */ jsx12("div", { className: "mx-auto w-full max-w-3xl px-6 py-3", children: /* @__PURE__ */ jsxs10("div", { className: "ml-auto w-fit max-w-[85%]", children: [
|
|
3225
|
+
/* @__PURE__ */ jsx12("p", { className: "mb-1 text-right text-[11px] font-semibold uppercase tracking-wide text-muted-foreground", children: userLabel }),
|
|
3226
|
+
/* @__PURE__ */ jsx12("div", { className: "rounded-2xl rounded-tr-md bg-primary/10 px-4 py-2.5 text-base leading-relaxed", children: /* @__PURE__ */ jsx12("p", { className: "whitespace-pre-wrap", children: msg.content }) })
|
|
3227
|
+
] }) }, msg.id) : /* @__PURE__ */ jsx12(
|
|
2693
3228
|
AssistantMessage,
|
|
2694
3229
|
{
|
|
2695
3230
|
msg,
|
|
@@ -2700,13 +3235,14 @@ function ChatMessages({
|
|
|
2700
3235
|
approval,
|
|
2701
3236
|
onToolCallClick,
|
|
2702
3237
|
toolRenderers,
|
|
2703
|
-
renderExtras
|
|
3238
|
+
renderExtras,
|
|
3239
|
+
durableCards
|
|
2704
3240
|
},
|
|
2705
3241
|
msg.id
|
|
2706
3242
|
)
|
|
2707
3243
|
),
|
|
2708
|
-
loading && lastIsUser && /* @__PURE__ */
|
|
2709
|
-
error && !loading && /* @__PURE__ */
|
|
3244
|
+
loading && lastIsUser && /* @__PURE__ */ jsx12(ThinkingRow, { agentLabel }),
|
|
3245
|
+
error && !loading && /* @__PURE__ */ jsx12(StreamErrorRow, { message: error, onRetry })
|
|
2710
3246
|
] });
|
|
2711
3247
|
}
|
|
2712
3248
|
|
|
@@ -2719,12 +3255,9 @@ export {
|
|
|
2719
3255
|
ModelPicker,
|
|
2720
3256
|
DEFAULT_EFFORT_LEVELS,
|
|
2721
3257
|
EffortPicker,
|
|
2722
|
-
dispatchChatStreamLine,
|
|
2723
|
-
consumeChatStream,
|
|
2724
|
-
streamChatTurn,
|
|
2725
|
-
ChatComposer,
|
|
2726
3258
|
interactionStatusLabels,
|
|
2727
3259
|
interactionTerminalNotes,
|
|
3260
|
+
fieldValuesFromAnswers,
|
|
2728
3261
|
fieldAnswer,
|
|
2729
3262
|
buildAnswerData,
|
|
2730
3263
|
isLateAnswerableStatus,
|
|
@@ -2738,12 +3271,27 @@ export {
|
|
|
2738
3271
|
InteractionActionButton,
|
|
2739
3272
|
QuestionOptionList,
|
|
2740
3273
|
InteractionQuestionCard,
|
|
3274
|
+
DurablePlanCard,
|
|
2741
3275
|
InteractionPlanCard,
|
|
3276
|
+
durableChatCardsFromParts,
|
|
3277
|
+
DurableChatCards,
|
|
3278
|
+
dispatchChatStreamLine,
|
|
3279
|
+
consumeChatStream,
|
|
3280
|
+
streamChatTurn,
|
|
3281
|
+
ChatComposer,
|
|
3282
|
+
DurablePlanClientError,
|
|
3283
|
+
createDurablePlanDecisionClient,
|
|
3284
|
+
useDurablePlanFlow,
|
|
3285
|
+
createSessionInteractionAttemptStore,
|
|
3286
|
+
createMemoryInteractionAttemptStore,
|
|
3287
|
+
interactionSubmissionSignature,
|
|
3288
|
+
createDurableInteractionAnswerSubmitter,
|
|
2742
3289
|
upsertChatInteraction,
|
|
2743
3290
|
cancelChatInteraction,
|
|
2744
3291
|
resolveChatInteraction,
|
|
2745
3292
|
terminalizePendingChatInteractions,
|
|
2746
3293
|
restoreChatInteractions,
|
|
3294
|
+
hydrateChatInteractions,
|
|
2747
3295
|
useChatInteractions,
|
|
2748
3296
|
rankFileMentions,
|
|
2749
3297
|
DEFAULT_MENTION_LIMIT,
|
|
@@ -2768,4 +3316,4 @@ export {
|
|
|
2768
3316
|
useThinkingSeconds,
|
|
2769
3317
|
ChatMessages
|
|
2770
3318
|
};
|
|
2771
|
-
//# sourceMappingURL=chunk-
|
|
3319
|
+
//# sourceMappingURL=chunk-PEUBCJXF.js.map
|