@mlpal/yodex-ui 0.1.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -4
- package/dist/client/client.d.ts +12 -18
- package/dist/client/types.d.ts +16 -34
- package/dist/client/useYodexSession.d.ts +6 -0
- package/dist/components/ApprovalPrompt.d.ts +7 -0
- package/dist/components/Composer.d.ts +11 -7
- package/dist/components/PermissionPrompt.d.ts +8 -3
- package/dist/components/RunStatus.d.ts +3 -1
- package/dist/components/SessionList.d.ts +3 -10
- package/dist/components/Transcript.d.ts +7 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.js +488 -313
- package/dist/index.js.map +13 -12
- package/dist/styles.css +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -9,6 +9,7 @@ class YodexClient {
|
|
|
9
9
|
seq = 0;
|
|
10
10
|
state = "closed";
|
|
11
11
|
constructor(options) {
|
|
12
|
+
const given = Object.fromEntries(Object.entries(options).filter(([, v]) => v !== undefined));
|
|
12
13
|
this.opts = {
|
|
13
14
|
backoffMs: { min: 500, max: 1e4 },
|
|
14
15
|
requestTimeoutMs: 15000,
|
|
@@ -16,7 +17,7 @@ class YodexClient {
|
|
|
16
17
|
setTimer: (fn, ms) => setTimeout(fn, ms),
|
|
17
18
|
clearTimer: (h) => clearTimeout(h),
|
|
18
19
|
random: Math.random,
|
|
19
|
-
...
|
|
20
|
+
...given
|
|
20
21
|
};
|
|
21
22
|
}
|
|
22
23
|
get url() {
|
|
@@ -165,6 +166,7 @@ import { addUsage, emptyUsage, HUMAN } from "@mlpal/harness-protocol";
|
|
|
165
166
|
function initialState() {
|
|
166
167
|
return {
|
|
167
168
|
connection: "connecting",
|
|
169
|
+
capabilities: [],
|
|
168
170
|
items: [],
|
|
169
171
|
running: false,
|
|
170
172
|
usageTotal: emptyUsage(),
|
|
@@ -294,10 +296,21 @@ function applyEvent(state, ev, live) {
|
|
|
294
296
|
}
|
|
295
297
|
function reduce(state, action) {
|
|
296
298
|
switch (action.type) {
|
|
297
|
-
case "connection":
|
|
298
|
-
|
|
299
|
+
case "connection": {
|
|
300
|
+
if (action.state === "connected" || !state.running)
|
|
301
|
+
return { ...state, connection: action.state };
|
|
302
|
+
return {
|
|
303
|
+
...state,
|
|
304
|
+
connection: action.state,
|
|
305
|
+
running: false,
|
|
306
|
+
activity: undefined,
|
|
307
|
+
pendingPermissions: [],
|
|
308
|
+
pendingQuestion: undefined,
|
|
309
|
+
items: [...dropDraft(state.items), { kind: "error", id: nextId("e"), message: "Connection lost while the agent was working; what streamed so far was kept.", ts: new Date().toISOString() }]
|
|
310
|
+
};
|
|
311
|
+
}
|
|
299
312
|
case "clear":
|
|
300
|
-
return { ...initialState(), connection: state.connection, protocol: state.protocol, sessions: state.sessions, models: state.models, skills: state.skills };
|
|
313
|
+
return { ...initialState(), connection: state.connection, protocol: state.protocol, capabilities: state.capabilities, hop: state.hop, rootCwd: state.rootCwd, sessions: state.sessions, models: state.models, skills: state.skills };
|
|
301
314
|
case "error":
|
|
302
315
|
return { ...state, error: action.message };
|
|
303
316
|
case "local_user":
|
|
@@ -306,7 +319,7 @@ function reduce(state, action) {
|
|
|
306
319
|
items: [...state.items, { kind: "user", id: nextId("u"), text: action.text, author: HUMAN, ts: action.ts, ...action.blocks?.length ? { blocks: action.blocks } : {} }]
|
|
307
320
|
};
|
|
308
321
|
case "run_started":
|
|
309
|
-
return { ...state, running: true, runStartedAt: action.at, activity: "Thinking", error: undefined, lastResult: undefined, ...action.sessionId ? { sessionId: action.sessionId } : {} };
|
|
322
|
+
return { ...state, running: true, runStartedAt: action.at, activity: "Thinking", error: undefined, lastResult: undefined, pendingApproval: undefined, ...action.sessionId ? { sessionId: action.sessionId } : {} };
|
|
310
323
|
case "permission_answered":
|
|
311
324
|
return { ...state, pendingPermissions: state.pendingPermissions.filter((p) => p.id !== action.id) };
|
|
312
325
|
case "question_answered":
|
|
@@ -321,25 +334,46 @@ function reduce(state, action) {
|
|
|
321
334
|
const m = action.message;
|
|
322
335
|
switch (m.type) {
|
|
323
336
|
case "ready":
|
|
324
|
-
return { ...state, connection: "connected", protocol: m.protocol };
|
|
337
|
+
return { ...state, connection: "connected", protocol: m.protocol, capabilities: m.capabilities ?? [], hop: m.hop, rootCwd: m.cwd };
|
|
325
338
|
case "event":
|
|
339
|
+
if (state.sessionId && m.sessionId !== state.sessionId)
|
|
340
|
+
return state;
|
|
326
341
|
return applyEvent(state.sessionId ? state : { ...state, sessionId: m.sessionId }, m.event, true);
|
|
327
342
|
case "permission_request":
|
|
328
|
-
|
|
343
|
+
if (state.sessionId && m.sessionId !== state.sessionId)
|
|
344
|
+
return state;
|
|
345
|
+
return { ...state, pendingPermissions: [...state.pendingPermissions, { id: m.id, sessionId: m.sessionId, toolName: m.toolName, input: m.input, ...m.reason ? { reason: m.reason } : {}, ...m.safetyReason ? { safetyReason: m.safetyReason } : {} }] };
|
|
329
346
|
case "question_request":
|
|
347
|
+
if (state.sessionId && m.sessionId !== state.sessionId)
|
|
348
|
+
return state;
|
|
330
349
|
return { ...state, pendingQuestion: { id: m.id, sessionId: m.sessionId, questions: m.questions } };
|
|
331
|
-
case "question_cleared":
|
|
332
|
-
return state.pendingQuestion?.id === m.id ? { ...state, pendingQuestion: undefined } : state;
|
|
333
350
|
case "sessions":
|
|
334
351
|
return { ...state, sessions: m.sessions };
|
|
335
352
|
case "models":
|
|
336
353
|
return { ...state, models: m.models };
|
|
337
354
|
case "skills":
|
|
338
355
|
return { ...state, skills: m.skills };
|
|
339
|
-
case "session_history":
|
|
340
|
-
|
|
356
|
+
case "session_history": {
|
|
357
|
+
const s = reduce(state, { type: "history", sessionId: m.sessionId, messages: m.messages });
|
|
358
|
+
return {
|
|
359
|
+
...s,
|
|
360
|
+
pendingQuestion: m.pendingQuestion ? { id: m.pendingQuestion.id, sessionId: m.sessionId, questions: m.pendingQuestion.questions } : undefined,
|
|
361
|
+
pendingApproval: m.pendingApproval,
|
|
362
|
+
liveElsewhere: m.live ?? false
|
|
363
|
+
};
|
|
364
|
+
}
|
|
341
365
|
case "done":
|
|
342
|
-
|
|
366
|
+
if (state.sessionId && m.sessionId !== state.sessionId)
|
|
367
|
+
return state;
|
|
368
|
+
return {
|
|
369
|
+
...state,
|
|
370
|
+
running: false,
|
|
371
|
+
activity: undefined,
|
|
372
|
+
pendingPermissions: [],
|
|
373
|
+
sessionId: m.sessionId,
|
|
374
|
+
...m.pendingApproval ? { pendingApproval: m.pendingApproval } : {},
|
|
375
|
+
...m.usage && !state.lastResult ? { usageTotal: addUsage(state.usageTotal, m.usage) } : {}
|
|
376
|
+
};
|
|
343
377
|
case "error":
|
|
344
378
|
return { ...state, error: m.message, items: [...dropDraft(state.items), { kind: "error", id: nextId("e"), message: m.message, ts: new Date().toISOString() }] };
|
|
345
379
|
default:
|
|
@@ -377,12 +411,18 @@ import { useCallback, useEffect, useMemo, useReducer, useRef } from "react";
|
|
|
377
411
|
function useYodexSession(options) {
|
|
378
412
|
const [state, dispatch] = useReducer(reduce, undefined, initialState);
|
|
379
413
|
const sessionIdRef = useRef(options.sessionId);
|
|
414
|
+
const allowed = useRef(new Set);
|
|
415
|
+
const ruleFor = (p) => `${p.toolName}\x00${typeof p.input.command === "string" ? p.input.command : ""}`;
|
|
380
416
|
const client = useMemo(() => new YodexClient({
|
|
381
417
|
url: options.url,
|
|
382
418
|
token: options.token,
|
|
383
419
|
socketFactory: options.socketFactory,
|
|
384
420
|
onState: (s) => dispatch({ type: "connection", state: s }),
|
|
385
421
|
onMessage: (m) => {
|
|
422
|
+
if (m.type === "permission_request" && allowed.current.has(ruleFor(m))) {
|
|
423
|
+
client.respondPermission(m.id, "allow");
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
386
426
|
dispatch({ type: "server", message: m });
|
|
387
427
|
if (m.type === "ready" && sessionIdRef.current) {
|
|
388
428
|
client.loadSession(sessionIdRef.current).catch((e) => dispatch({ type: "error", message: e.message }));
|
|
@@ -398,6 +438,16 @@ function useYodexSession(options) {
|
|
|
398
438
|
useEffect(() => {
|
|
399
439
|
sessionIdRef.current = state.sessionId ?? options.sessionId;
|
|
400
440
|
}, [state.sessionId, options.sessionId]);
|
|
441
|
+
const lastRequested = useRef(options.sessionId);
|
|
442
|
+
useEffect(() => {
|
|
443
|
+
if (options.sessionId === lastRequested.current)
|
|
444
|
+
return;
|
|
445
|
+
lastRequested.current = options.sessionId;
|
|
446
|
+
sessionIdRef.current = options.sessionId;
|
|
447
|
+
dispatch({ type: "clear" });
|
|
448
|
+
if (options.sessionId && client.connected)
|
|
449
|
+
client.loadSession(options.sessionId).catch((e) => dispatch({ type: "error", message: e.message }));
|
|
450
|
+
}, [options.sessionId, client]);
|
|
401
451
|
const guard = useCallback((fn) => {
|
|
402
452
|
try {
|
|
403
453
|
fn();
|
|
@@ -407,17 +457,17 @@ function useYodexSession(options) {
|
|
|
407
457
|
}, []);
|
|
408
458
|
const run = useCallback((text, attachments = [], runOptions = {}) => {
|
|
409
459
|
const blocks = attachments.map((a) => ({ type: "image", source: { type: "base64", media_type: a.mediaType, data: a.data } }));
|
|
410
|
-
if (blocks.length && state.
|
|
411
|
-
dispatch({ type: "error", message: "This server
|
|
460
|
+
if (blocks.length && !state.capabilities.includes("attachments")) {
|
|
461
|
+
dispatch({ type: "error", message: "This server does not accept image attachments; the message was not sent." });
|
|
412
462
|
return;
|
|
413
463
|
}
|
|
414
464
|
guard(() => {
|
|
415
465
|
const sessionId = sessionIdRef.current;
|
|
416
466
|
dispatch({ type: "local_user", text, ts: new Date().toISOString(), blocks });
|
|
417
467
|
dispatch({ type: "run_started", sessionId, at: Date.now() });
|
|
418
|
-
client.run(text, { ...sessionId ? { sessionId } : {}, ...options.run, ...runOptions });
|
|
468
|
+
client.run(text, { ...sessionId ? { sessionId } : {}, ...options.run, ...runOptions, ...blocks.length ? { blocks } : {} });
|
|
419
469
|
});
|
|
420
|
-
}, [client, guard, options.run, state.
|
|
470
|
+
}, [client, guard, options.run, state.capabilities]);
|
|
421
471
|
const stop = useCallback(() => guard(() => client.interrupt()), [client, guard]);
|
|
422
472
|
const allow = useCallback((id) => guard(() => {
|
|
423
473
|
client.respondPermission(id, "allow");
|
|
@@ -438,6 +488,23 @@ function useYodexSession(options) {
|
|
|
438
488
|
dispatch({ type: "run_started", sessionId, at: Date.now() });
|
|
439
489
|
client.respondApproval(sessionId, decision, reason);
|
|
440
490
|
}), [client, guard]);
|
|
491
|
+
const allowForSession = useCallback((id) => {
|
|
492
|
+
const p = state.pendingPermissions.find((x) => x.id === id);
|
|
493
|
+
if (p)
|
|
494
|
+
allowed.current.add(ruleFor(p));
|
|
495
|
+
guard(() => {
|
|
496
|
+
client.respondPermission(id, "allow");
|
|
497
|
+
dispatch({ type: "permission_answered", id });
|
|
498
|
+
});
|
|
499
|
+
}, [client, guard, state.pendingPermissions]);
|
|
500
|
+
const steer = useCallback((text) => guard(() => {
|
|
501
|
+
const sessionId = sessionIdRef.current;
|
|
502
|
+
if (!sessionId)
|
|
503
|
+
throw new Error("no session to steer");
|
|
504
|
+
client.sendPeer(sessionId, text);
|
|
505
|
+
dispatch({ type: "local_user", text, ts: new Date().toISOString() });
|
|
506
|
+
}), [client, guard]);
|
|
507
|
+
const waitingOn = state.pendingQuestion ? "question" : state.pendingPermissions.length ? "permission" : undefined;
|
|
441
508
|
const loadSession = useCallback(async (sessionId) => {
|
|
442
509
|
sessionIdRef.current = sessionId;
|
|
443
510
|
dispatch({ type: "clear" });
|
|
@@ -465,7 +532,7 @@ function useYodexSession(options) {
|
|
|
465
532
|
sessionIdRef.current = undefined;
|
|
466
533
|
dispatch({ type: "clear" });
|
|
467
534
|
}, []);
|
|
468
|
-
return { state, client, run, stop, allow, deny, answer, approve, loadSession, refreshSessions, refreshModels, newSession };
|
|
535
|
+
return { state, client, run, stop, allow, deny, answer, approve, allowForSession, steer, waitingOn, loadSession, refreshSessions, refreshModels, newSession };
|
|
469
536
|
}
|
|
470
537
|
// src/components/Transcript.tsx
|
|
471
538
|
import { useEffect as useEffect2, useRef as useRef2 } from "react";
|
|
@@ -482,73 +549,88 @@ function cn(...inputs) {
|
|
|
482
549
|
}
|
|
483
550
|
|
|
484
551
|
// src/components/AssistantMessage.tsx
|
|
485
|
-
import { jsx } from "react/jsx-runtime";
|
|
552
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
486
553
|
var plugins = [remarkGfm];
|
|
487
554
|
function AssistantMessage({ text, live, className }) {
|
|
488
|
-
return /* @__PURE__ */
|
|
489
|
-
className: cn("yx-assistant
|
|
555
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
556
|
+
className: cn("yx-assistant max-w-none break-words text-[15px] leading-6 text-foreground", className),
|
|
490
557
|
"data-live": live ? "true" : undefined,
|
|
491
|
-
children:
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
...props,
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
...props,
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
...props,
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
558
|
+
children: [
|
|
559
|
+
/* @__PURE__ */ jsx(Markdown, {
|
|
560
|
+
remarkPlugins: plugins,
|
|
561
|
+
skipHtml: true,
|
|
562
|
+
components: {
|
|
563
|
+
a: ({ node: _n, ...props }) => /* @__PURE__ */ jsx("a", {
|
|
564
|
+
...props,
|
|
565
|
+
target: "_blank",
|
|
566
|
+
rel: "noopener noreferrer",
|
|
567
|
+
className: "text-info underline underline-offset-2"
|
|
568
|
+
}),
|
|
569
|
+
pre: ({ node: _n, ...props }) => /* @__PURE__ */ jsx("pre", {
|
|
570
|
+
...props,
|
|
571
|
+
className: "overflow-x-auto rounded-md border border-border bg-card p-3 font-mono text-[12.5px] leading-5"
|
|
572
|
+
}),
|
|
573
|
+
code: ({ node: _n, className: cls, ...props }) => cls ? /* @__PURE__ */ jsx("code", {
|
|
574
|
+
...props,
|
|
575
|
+
className: cls
|
|
576
|
+
}) : /* @__PURE__ */ jsx("code", {
|
|
577
|
+
...props,
|
|
578
|
+
className: "rounded bg-muted px-1 py-0.5 font-mono text-[0.9em]"
|
|
579
|
+
}),
|
|
580
|
+
table: ({ node: _n, ...props }) => /* @__PURE__ */ jsx("div", {
|
|
581
|
+
className: "overflow-x-auto",
|
|
582
|
+
children: /* @__PURE__ */ jsx("table", {
|
|
583
|
+
...props,
|
|
584
|
+
className: "my-2 w-full border-collapse text-sm"
|
|
585
|
+
})
|
|
586
|
+
}),
|
|
587
|
+
th: ({ node: _n, ...props }) => /* @__PURE__ */ jsx("th", {
|
|
515
588
|
...props,
|
|
516
|
-
className: "
|
|
589
|
+
className: "border border-border bg-muted px-2 py-1 text-left font-medium text-foreground"
|
|
590
|
+
}),
|
|
591
|
+
strong: ({ node: _n, ...props }) => /* @__PURE__ */ jsx("strong", {
|
|
592
|
+
...props,
|
|
593
|
+
className: "font-semibold text-foreground"
|
|
594
|
+
}),
|
|
595
|
+
blockquote: ({ node: _n, ...props }) => /* @__PURE__ */ jsx("blockquote", {
|
|
596
|
+
...props,
|
|
597
|
+
className: "border-l-2 border-border pl-3 text-muted-foreground"
|
|
598
|
+
}),
|
|
599
|
+
td: ({ node: _n, ...props }) => /* @__PURE__ */ jsx("td", {
|
|
600
|
+
...props,
|
|
601
|
+
className: "border border-border px-2 py-1 align-top"
|
|
517
602
|
})
|
|
518
|
-
}
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
},
|
|
528
|
-
children: text
|
|
529
|
-
})
|
|
603
|
+
},
|
|
604
|
+
children: text
|
|
605
|
+
}),
|
|
606
|
+
live && /* @__PURE__ */ jsx("span", {
|
|
607
|
+
className: "yx-caret",
|
|
608
|
+
"aria-hidden": true,
|
|
609
|
+
children: "▍"
|
|
610
|
+
})
|
|
611
|
+
]
|
|
530
612
|
});
|
|
531
613
|
}
|
|
532
614
|
|
|
533
615
|
// src/components/PlanChecklist.tsx
|
|
534
616
|
import { Check, Circle, Loader2 } from "lucide-react";
|
|
535
|
-
import { jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
617
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
536
618
|
function PlanChecklist({ items, className }) {
|
|
537
619
|
if (!items.length)
|
|
538
620
|
return null;
|
|
539
621
|
const done = items.filter((t) => t.status === "completed").length;
|
|
540
|
-
return /* @__PURE__ */
|
|
622
|
+
return /* @__PURE__ */ jsxs2("div", {
|
|
541
623
|
className: cn("yx-plan rounded-md border border-border bg-card p-3 text-sm", className),
|
|
542
624
|
"aria-label": "plan",
|
|
543
625
|
children: [
|
|
544
|
-
/* @__PURE__ */
|
|
626
|
+
/* @__PURE__ */ jsxs2("div", {
|
|
545
627
|
className: "mb-2 flex items-center justify-between",
|
|
546
628
|
children: [
|
|
547
629
|
/* @__PURE__ */ jsx2("span", {
|
|
548
630
|
className: "font-medium",
|
|
549
631
|
children: "Plan"
|
|
550
632
|
}),
|
|
551
|
-
/* @__PURE__ */
|
|
633
|
+
/* @__PURE__ */ jsxs2("span", {
|
|
552
634
|
className: "font-mono text-[12px] tabular-nums text-muted-foreground",
|
|
553
635
|
children: [
|
|
554
636
|
done,
|
|
@@ -560,7 +642,7 @@ function PlanChecklist({ items, className }) {
|
|
|
560
642
|
}),
|
|
561
643
|
/* @__PURE__ */ jsx2("ol", {
|
|
562
644
|
className: "space-y-1",
|
|
563
|
-
children: items.map((t) => /* @__PURE__ */
|
|
645
|
+
children: items.map((t) => /* @__PURE__ */ jsxs2("li", {
|
|
564
646
|
className: cn("flex items-start gap-2", t.status === "completed" && "text-muted-foreground line-through"),
|
|
565
647
|
"data-status": t.status,
|
|
566
648
|
children: [
|
|
@@ -621,7 +703,7 @@ function DiffView({ diff, className }) {
|
|
|
621
703
|
}
|
|
622
704
|
|
|
623
705
|
// src/components/ToolCall.tsx
|
|
624
|
-
import { jsx as jsx4, jsxs as
|
|
706
|
+
import { jsx as jsx4, jsxs as jsxs3, Fragment } from "react/jsx-runtime";
|
|
625
707
|
function toolSummary(name, input) {
|
|
626
708
|
const pick = (...keys) => {
|
|
627
709
|
for (const k of keys) {
|
|
@@ -667,12 +749,12 @@ function ToolCall({ item, defaultOpen = false, onOpenFile }) {
|
|
|
667
749
|
const counts = r?.diff ? diffCounts(r.diff) : null;
|
|
668
750
|
const path = typeof item.input.file_path === "string" ? item.input.file_path : typeof item.input.path === "string" ? item.input.path : undefined;
|
|
669
751
|
const Icon = r?.diff ? FileDiff : item.name === "Bash" ? Terminal : Wrench;
|
|
670
|
-
return /* @__PURE__ */
|
|
752
|
+
return /* @__PURE__ */ jsxs3("div", {
|
|
671
753
|
className: cn("yx-tool rounded-md border border-border bg-card text-sm", status === "error" && "border-destructive/40"),
|
|
672
754
|
"data-tool": item.name,
|
|
673
755
|
"data-status": status,
|
|
674
756
|
children: [
|
|
675
|
-
/* @__PURE__ */
|
|
757
|
+
/* @__PURE__ */ jsxs3("button", {
|
|
676
758
|
type: "button",
|
|
677
759
|
onClick: () => setOpen((o) => !o),
|
|
678
760
|
"aria-expanded": open,
|
|
@@ -694,10 +776,10 @@ function ToolCall({ item, defaultOpen = false, onOpenFile }) {
|
|
|
694
776
|
className: "min-w-0 flex-1 truncate font-mono text-[12.5px] text-muted-foreground",
|
|
695
777
|
children: clip(summary)
|
|
696
778
|
}),
|
|
697
|
-
counts && /* @__PURE__ */
|
|
779
|
+
counts && /* @__PURE__ */ jsxs3("span", {
|
|
698
780
|
className: "shrink-0 font-mono text-[12px] tabular-nums",
|
|
699
781
|
children: [
|
|
700
|
-
/* @__PURE__ */
|
|
782
|
+
/* @__PURE__ */ jsxs3("span", {
|
|
701
783
|
className: "text-success",
|
|
702
784
|
children: [
|
|
703
785
|
"+",
|
|
@@ -705,7 +787,7 @@ function ToolCall({ item, defaultOpen = false, onOpenFile }) {
|
|
|
705
787
|
]
|
|
706
788
|
}),
|
|
707
789
|
" ",
|
|
708
|
-
/* @__PURE__ */
|
|
790
|
+
/* @__PURE__ */ jsxs3("span", {
|
|
709
791
|
className: "text-destructive",
|
|
710
792
|
children: [
|
|
711
793
|
"−",
|
|
@@ -728,7 +810,7 @@ function ToolCall({ item, defaultOpen = false, onOpenFile }) {
|
|
|
728
810
|
})
|
|
729
811
|
]
|
|
730
812
|
}),
|
|
731
|
-
open && /* @__PURE__ */
|
|
813
|
+
open && /* @__PURE__ */ jsxs3("div", {
|
|
732
814
|
className: "border-t border-border px-3 py-2",
|
|
733
815
|
children: [
|
|
734
816
|
/* @__PURE__ */ jsx4("div", {
|
|
@@ -739,9 +821,9 @@ function ToolCall({ item, defaultOpen = false, onOpenFile }) {
|
|
|
739
821
|
className: "overflow-x-auto whitespace-pre-wrap break-words font-mono text-[12px] leading-5",
|
|
740
822
|
children: summary && Object.keys(item.input).length === 1 ? summary : JSON.stringify(item.input, null, 2)
|
|
741
823
|
}),
|
|
742
|
-
r && /* @__PURE__ */
|
|
824
|
+
r && /* @__PURE__ */ jsxs3(Fragment, {
|
|
743
825
|
children: [
|
|
744
|
-
/* @__PURE__ */
|
|
826
|
+
/* @__PURE__ */ jsxs3("div", {
|
|
745
827
|
className: "mb-1 mt-2 flex items-center gap-2 text-[11px] uppercase tracking-wide text-muted-foreground",
|
|
746
828
|
children: [
|
|
747
829
|
/* @__PURE__ */ jsx4("span", {
|
|
@@ -771,7 +853,85 @@ function ToolCall({ item, defaultOpen = false, onOpenFile }) {
|
|
|
771
853
|
}
|
|
772
854
|
|
|
773
855
|
// src/components/Transcript.tsx
|
|
774
|
-
import { jsx as jsx5, jsxs as
|
|
856
|
+
import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
857
|
+
function TranscriptRow({ item, onOpenFile }) {
|
|
858
|
+
const it = item;
|
|
859
|
+
switch (it.kind) {
|
|
860
|
+
case "user":
|
|
861
|
+
return /* @__PURE__ */ jsx5("div", {
|
|
862
|
+
className: cn("flex", it.author.type === "human" ? "justify-end" : "justify-start"),
|
|
863
|
+
"data-author": it.author.type,
|
|
864
|
+
children: /* @__PURE__ */ jsxs4("div", {
|
|
865
|
+
className: cn("max-w-[85%] whitespace-pre-wrap break-words rounded-2xl px-4 py-2 text-[15px] leading-6", it.author.type === "human" ? "rounded-br-md bg-primary text-primary-foreground" : "rounded-bl-md border border-border bg-card"),
|
|
866
|
+
children: [
|
|
867
|
+
it.author.type !== "human" && /* @__PURE__ */ jsx5("div", {
|
|
868
|
+
className: "mb-1 text-[11px] text-muted-foreground",
|
|
869
|
+
children: it.author.displayName ?? it.author.type
|
|
870
|
+
}),
|
|
871
|
+
it.blocks?.some((b) => b.type === "image") && /* @__PURE__ */ jsx5("div", {
|
|
872
|
+
className: "mb-1 text-[11px] opacity-80",
|
|
873
|
+
children: "[image]"
|
|
874
|
+
}),
|
|
875
|
+
it.text
|
|
876
|
+
]
|
|
877
|
+
})
|
|
878
|
+
});
|
|
879
|
+
case "assistant":
|
|
880
|
+
return /* @__PURE__ */ jsx5(AssistantMessage, {
|
|
881
|
+
text: it.text,
|
|
882
|
+
live: it.live
|
|
883
|
+
});
|
|
884
|
+
case "tool":
|
|
885
|
+
return /* @__PURE__ */ jsx5(ToolCall, {
|
|
886
|
+
item: it,
|
|
887
|
+
onOpenFile
|
|
888
|
+
});
|
|
889
|
+
case "plan":
|
|
890
|
+
return /* @__PURE__ */ jsx5(PlanChecklist, {
|
|
891
|
+
items: it.items
|
|
892
|
+
});
|
|
893
|
+
case "result": {
|
|
894
|
+
const { label, tone } = resultLabel(it.result);
|
|
895
|
+
return /* @__PURE__ */ jsxs4("div", {
|
|
896
|
+
className: cn("flex items-center gap-2 text-[12px] text-muted-foreground", tone === "bad" && "text-destructive"),
|
|
897
|
+
"data-subtype": it.result.subtype,
|
|
898
|
+
children: [
|
|
899
|
+
/* @__PURE__ */ jsx5("span", {
|
|
900
|
+
children: label
|
|
901
|
+
}),
|
|
902
|
+
/* @__PURE__ */ jsx5("span", {
|
|
903
|
+
children: "·"
|
|
904
|
+
}),
|
|
905
|
+
/* @__PURE__ */ jsxs4("span", {
|
|
906
|
+
className: "font-mono tabular-nums",
|
|
907
|
+
children: [
|
|
908
|
+
it.result.numTurns,
|
|
909
|
+
" step",
|
|
910
|
+
it.result.numTurns === 1 ? "" : "s",
|
|
911
|
+
" · ",
|
|
912
|
+
formatTokens(it.result.usage)
|
|
913
|
+
]
|
|
914
|
+
})
|
|
915
|
+
]
|
|
916
|
+
});
|
|
917
|
+
}
|
|
918
|
+
case "error":
|
|
919
|
+
return /* @__PURE__ */ jsx5("div", {
|
|
920
|
+
role: "alert",
|
|
921
|
+
className: "rounded-md border border-destructive/40 bg-destructive/5 px-3 py-2 text-[13px] text-destructive",
|
|
922
|
+
children: it.message
|
|
923
|
+
});
|
|
924
|
+
case "compact":
|
|
925
|
+
return /* @__PURE__ */ jsxs4("div", {
|
|
926
|
+
className: "text-center text-[11px] text-muted-foreground",
|
|
927
|
+
children: [
|
|
928
|
+
"— context compacted (",
|
|
929
|
+
it.replacedMessages,
|
|
930
|
+
" earlier messages summarised) —"
|
|
931
|
+
]
|
|
932
|
+
});
|
|
933
|
+
}
|
|
934
|
+
}
|
|
775
935
|
function Transcript({ items, autoScroll = true, onOpenFile, footer, emptyState, className }) {
|
|
776
936
|
const ref = useRef2(null);
|
|
777
937
|
const pinned = useRef2(true);
|
|
@@ -794,97 +954,23 @@ function Transcript({ items, autoScroll = true, onOpenFile, footer, emptyState,
|
|
|
794
954
|
ref,
|
|
795
955
|
className: cn("yx-transcript flex-1 overflow-y-auto px-4 py-3", className),
|
|
796
956
|
role: "log",
|
|
797
|
-
|
|
798
|
-
children: /* @__PURE__ */ jsxs3("div", {
|
|
957
|
+
children: /* @__PURE__ */ jsxs4("div", {
|
|
799
958
|
className: "mx-auto flex w-full max-w-3xl flex-col gap-3",
|
|
800
959
|
children: [
|
|
801
960
|
items.length === 0 && emptyState,
|
|
802
|
-
items.map((it) => {
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
className: cn("flex", it.author.type === "human" ? "justify-end" : "justify-start"),
|
|
807
|
-
"data-author": it.author.type,
|
|
808
|
-
children: /* @__PURE__ */ jsxs3("div", {
|
|
809
|
-
className: cn("max-w-[85%] whitespace-pre-wrap break-words rounded-2xl px-4 py-2 text-[15px] leading-6", it.author.type === "human" ? "rounded-br-md bg-primary text-primary-foreground" : "rounded-bl-md border border-border bg-card"),
|
|
810
|
-
children: [
|
|
811
|
-
it.author.type !== "human" && /* @__PURE__ */ jsx5("div", {
|
|
812
|
-
className: "mb-1 text-[11px] text-muted-foreground",
|
|
813
|
-
children: it.author.displayName ?? it.author.type
|
|
814
|
-
}),
|
|
815
|
-
it.blocks?.some((b) => b.type === "image") && /* @__PURE__ */ jsx5("div", {
|
|
816
|
-
className: "mb-1 text-[11px] opacity-80",
|
|
817
|
-
children: "[image]"
|
|
818
|
-
}),
|
|
819
|
-
it.text
|
|
820
|
-
]
|
|
821
|
-
})
|
|
822
|
-
}, it.id);
|
|
823
|
-
case "assistant":
|
|
824
|
-
return /* @__PURE__ */ jsx5(AssistantMessage, {
|
|
825
|
-
text: it.text,
|
|
826
|
-
live: it.live
|
|
827
|
-
}, it.id);
|
|
828
|
-
case "tool":
|
|
829
|
-
return /* @__PURE__ */ jsx5(ToolCall, {
|
|
830
|
-
item: it,
|
|
831
|
-
onOpenFile
|
|
832
|
-
}, it.id);
|
|
833
|
-
case "plan":
|
|
834
|
-
return /* @__PURE__ */ jsx5(PlanChecklist, {
|
|
835
|
-
items: it.items
|
|
836
|
-
}, "plan");
|
|
837
|
-
case "result": {
|
|
838
|
-
const { label, tone } = resultLabel(it.result);
|
|
839
|
-
return /* @__PURE__ */ jsxs3("div", {
|
|
840
|
-
className: cn("flex items-center gap-2 text-[12px] text-muted-foreground", tone === "bad" && "text-destructive"),
|
|
841
|
-
"data-subtype": it.result.subtype,
|
|
842
|
-
children: [
|
|
843
|
-
/* @__PURE__ */ jsx5("span", {
|
|
844
|
-
children: label
|
|
845
|
-
}),
|
|
846
|
-
/* @__PURE__ */ jsx5("span", {
|
|
847
|
-
children: "·"
|
|
848
|
-
}),
|
|
849
|
-
/* @__PURE__ */ jsxs3("span", {
|
|
850
|
-
className: "font-mono tabular-nums",
|
|
851
|
-
children: [
|
|
852
|
-
it.result.numTurns,
|
|
853
|
-
" step",
|
|
854
|
-
it.result.numTurns === 1 ? "" : "s",
|
|
855
|
-
" · ",
|
|
856
|
-
formatTokens(it.result.usage)
|
|
857
|
-
]
|
|
858
|
-
})
|
|
859
|
-
]
|
|
860
|
-
}, it.id);
|
|
861
|
-
}
|
|
862
|
-
case "error":
|
|
863
|
-
return /* @__PURE__ */ jsx5("div", {
|
|
864
|
-
role: "alert",
|
|
865
|
-
className: "rounded-md border border-destructive/40 bg-destructive/5 px-3 py-2 text-[13px] text-destructive",
|
|
866
|
-
children: it.message
|
|
867
|
-
}, it.id);
|
|
868
|
-
case "compact":
|
|
869
|
-
return /* @__PURE__ */ jsxs3("div", {
|
|
870
|
-
className: "text-center text-[11px] text-muted-foreground",
|
|
871
|
-
children: [
|
|
872
|
-
"— context compacted (",
|
|
873
|
-
it.replacedMessages,
|
|
874
|
-
" earlier messages summarised) —"
|
|
875
|
-
]
|
|
876
|
-
}, it.id);
|
|
877
|
-
}
|
|
878
|
-
}),
|
|
961
|
+
items.map((it) => /* @__PURE__ */ jsx5(TranscriptRow, {
|
|
962
|
+
item: it,
|
|
963
|
+
onOpenFile
|
|
964
|
+
}, it.id)),
|
|
879
965
|
footer
|
|
880
966
|
]
|
|
881
967
|
})
|
|
882
968
|
});
|
|
883
969
|
}
|
|
884
970
|
// src/components/PermissionPrompt.tsx
|
|
885
|
-
import { useEffect as useEffect3 } from "react";
|
|
971
|
+
import { useEffect as useEffect3, useState as useState2 } from "react";
|
|
886
972
|
import { ShieldAlert } from "lucide-react";
|
|
887
|
-
import { jsx as jsx6, jsxs as
|
|
973
|
+
import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
888
974
|
function permissionSummary(p) {
|
|
889
975
|
const v = p.input.command ?? p.input.file_path ?? p.input.path ?? p.input.url;
|
|
890
976
|
if (typeof v === "string")
|
|
@@ -892,8 +978,12 @@ function permissionSummary(p) {
|
|
|
892
978
|
const s = JSON.stringify(p.input);
|
|
893
979
|
return s.length > 240 ? `${s.slice(0, 239)}…` : s;
|
|
894
980
|
}
|
|
895
|
-
function PermissionPrompt({ request, onAllow, onDeny, className }) {
|
|
981
|
+
function PermissionPrompt({ request, onAllow, onDeny, onAllowSession, hotkeys = false, className }) {
|
|
982
|
+
const [reasonOpen, setReasonOpen] = useState2(false);
|
|
983
|
+
const [reason, setReason] = useState2("");
|
|
896
984
|
useEffect3(() => {
|
|
985
|
+
if (!hotkeys)
|
|
986
|
+
return;
|
|
897
987
|
const key = (e) => {
|
|
898
988
|
const t = e.target;
|
|
899
989
|
if (t && (t.tagName === "TEXTAREA" || t.tagName === "INPUT" || t.isContentEditable))
|
|
@@ -905,15 +995,19 @@ function PermissionPrompt({ request, onAllow, onDeny, className }) {
|
|
|
905
995
|
};
|
|
906
996
|
document.addEventListener("keydown", key);
|
|
907
997
|
return () => document.removeEventListener("keydown", key);
|
|
908
|
-
}, [request.id, onAllow, onDeny]);
|
|
909
|
-
|
|
998
|
+
}, [hotkeys, request.id, onAllow, onDeny]);
|
|
999
|
+
const kbd = (k) => hotkeys ? /* @__PURE__ */ jsx6("kbd", {
|
|
1000
|
+
className: "ml-1 rounded bg-black/10 px-1 text-[11px]",
|
|
1001
|
+
children: k
|
|
1002
|
+
}) : null;
|
|
1003
|
+
return /* @__PURE__ */ jsxs5("div", {
|
|
910
1004
|
role: "alertdialog",
|
|
911
1005
|
"aria-labelledby": `perm-${request.id}`,
|
|
912
1006
|
className: cn("yx-permission rounded-md border border-warning/50 bg-card p-3 text-sm", className),
|
|
913
1007
|
children: [
|
|
914
|
-
/* @__PURE__ */
|
|
1008
|
+
/* @__PURE__ */ jsxs5("div", {
|
|
915
1009
|
id: `perm-${request.id}`,
|
|
916
|
-
className: "mb-
|
|
1010
|
+
className: "mb-1 flex items-center gap-2 font-medium",
|
|
917
1011
|
children: [
|
|
918
1012
|
/* @__PURE__ */ jsx6(ShieldAlert, {
|
|
919
1013
|
className: "h-4 w-4 text-warning",
|
|
@@ -927,46 +1021,116 @@ function PermissionPrompt({ request, onAllow, onDeny, className }) {
|
|
|
927
1021
|
"?"
|
|
928
1022
|
]
|
|
929
1023
|
}),
|
|
1024
|
+
(request.safetyReason || request.reason) && /* @__PURE__ */ jsx6("div", {
|
|
1025
|
+
className: "mb-2 text-[12px] text-muted-foreground",
|
|
1026
|
+
children: request.safetyReason ?? request.reason
|
|
1027
|
+
}),
|
|
930
1028
|
/* @__PURE__ */ jsx6("pre", {
|
|
931
1029
|
className: "mb-3 overflow-x-auto whitespace-pre-wrap break-words rounded bg-muted p-2 font-mono text-[12.5px] leading-5",
|
|
932
1030
|
children: permissionSummary(request)
|
|
933
1031
|
}),
|
|
934
|
-
/* @__PURE__ */
|
|
935
|
-
className: "flex gap-2",
|
|
1032
|
+
/* @__PURE__ */ jsxs5("div", {
|
|
1033
|
+
className: "flex flex-wrap gap-2",
|
|
936
1034
|
children: [
|
|
937
|
-
/* @__PURE__ */
|
|
1035
|
+
/* @__PURE__ */ jsxs5("button", {
|
|
938
1036
|
type: "button",
|
|
939
1037
|
onClick: () => onAllow(request.id),
|
|
940
1038
|
className: "min-h-11 flex-1 rounded-md bg-primary px-4 font-medium text-primary-foreground hover:opacity-90 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
941
1039
|
children: [
|
|
942
1040
|
"Allow ",
|
|
943
|
-
|
|
944
|
-
className: "ml-1 rounded bg-primary-foreground/20 px-1 text-[11px]",
|
|
945
|
-
children: "a"
|
|
946
|
-
})
|
|
1041
|
+
kbd("a")
|
|
947
1042
|
]
|
|
948
1043
|
}),
|
|
949
|
-
/* @__PURE__ */
|
|
1044
|
+
onAllowSession && /* @__PURE__ */ jsx6("button", {
|
|
1045
|
+
type: "button",
|
|
1046
|
+
onClick: () => onAllowSession(request.id),
|
|
1047
|
+
className: "min-h-11 flex-1 rounded-md border border-border bg-background px-4 font-medium hover:bg-muted",
|
|
1048
|
+
children: "Allow for this session"
|
|
1049
|
+
}),
|
|
1050
|
+
/* @__PURE__ */ jsxs5("button", {
|
|
950
1051
|
type: "button",
|
|
951
|
-
onClick: () => onDeny(request.id),
|
|
1052
|
+
onClick: () => reasonOpen ? onDeny(request.id, reason.trim() || undefined) : onDeny(request.id),
|
|
952
1053
|
className: "min-h-11 flex-1 rounded-md border border-border bg-background px-4 font-medium hover:bg-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
953
1054
|
children: [
|
|
954
1055
|
"Deny ",
|
|
955
|
-
|
|
956
|
-
className: "ml-1 rounded bg-muted px-1 text-[11px]",
|
|
957
|
-
children: "d"
|
|
958
|
-
})
|
|
1056
|
+
kbd("d")
|
|
959
1057
|
]
|
|
960
1058
|
})
|
|
961
1059
|
]
|
|
1060
|
+
}),
|
|
1061
|
+
reasonOpen ? /* @__PURE__ */ jsxs5("label", {
|
|
1062
|
+
className: "mt-2 block",
|
|
1063
|
+
children: [
|
|
1064
|
+
/* @__PURE__ */ jsx6("span", {
|
|
1065
|
+
className: "mb-1 block text-[12px] text-muted-foreground",
|
|
1066
|
+
children: "Tell the agent why (sent with the denial)"
|
|
1067
|
+
}),
|
|
1068
|
+
/* @__PURE__ */ jsx6("input", {
|
|
1069
|
+
value: reason,
|
|
1070
|
+
onChange: (e) => setReason(e.target.value),
|
|
1071
|
+
className: "min-h-11 w-full rounded-md border border-border bg-input-background px-3 text-[16px]"
|
|
1072
|
+
})
|
|
1073
|
+
]
|
|
1074
|
+
}) : /* @__PURE__ */ jsx6("button", {
|
|
1075
|
+
type: "button",
|
|
1076
|
+
onClick: () => setReasonOpen(true),
|
|
1077
|
+
className: "mt-2 min-h-9 text-[12px] text-info underline-offset-2 hover:underline",
|
|
1078
|
+
children: "Deny with a reason…"
|
|
1079
|
+
})
|
|
1080
|
+
]
|
|
1081
|
+
});
|
|
1082
|
+
}
|
|
1083
|
+
// src/components/ApprovalPrompt.tsx
|
|
1084
|
+
import { ShieldAlert as ShieldAlert2 } from "lucide-react";
|
|
1085
|
+
import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1086
|
+
function ApprovalPrompt({ approval, onDecide, className }) {
|
|
1087
|
+
return /* @__PURE__ */ jsxs6("div", {
|
|
1088
|
+
role: "alertdialog",
|
|
1089
|
+
"aria-label": "Approval needed",
|
|
1090
|
+
className: cn("yx-approval rounded-md border border-warning/50 bg-card p-3 text-sm", className),
|
|
1091
|
+
children: [
|
|
1092
|
+
/* @__PURE__ */ jsxs6("div", {
|
|
1093
|
+
className: "mb-1 flex items-center gap-2 font-medium",
|
|
1094
|
+
children: [
|
|
1095
|
+
/* @__PURE__ */ jsx7(ShieldAlert2, {
|
|
1096
|
+
className: "h-4 w-4 text-warning",
|
|
1097
|
+
"aria-hidden": true
|
|
1098
|
+
}),
|
|
1099
|
+
"The agent stopped before this and needs your approval"
|
|
1100
|
+
]
|
|
1101
|
+
}),
|
|
1102
|
+
/* @__PURE__ */ jsx7("div", {
|
|
1103
|
+
className: "mb-2 text-[12px] text-muted-foreground",
|
|
1104
|
+
children: approval.reason
|
|
1105
|
+
}),
|
|
1106
|
+
/* @__PURE__ */ jsx7("pre", {
|
|
1107
|
+
className: "mb-3 overflow-x-auto whitespace-pre-wrap break-words rounded bg-muted p-2 font-mono text-[12.5px] leading-5",
|
|
1108
|
+
children: approval.command
|
|
1109
|
+
}),
|
|
1110
|
+
/* @__PURE__ */ jsxs6("div", {
|
|
1111
|
+
className: "flex gap-2",
|
|
1112
|
+
children: [
|
|
1113
|
+
/* @__PURE__ */ jsx7("button", {
|
|
1114
|
+
type: "button",
|
|
1115
|
+
onClick: () => onDecide("allow"),
|
|
1116
|
+
className: "min-h-11 flex-1 rounded-md bg-primary px-4 font-medium text-primary-foreground hover:opacity-90",
|
|
1117
|
+
children: "Approve and continue"
|
|
1118
|
+
}),
|
|
1119
|
+
/* @__PURE__ */ jsx7("button", {
|
|
1120
|
+
type: "button",
|
|
1121
|
+
onClick: () => onDecide("deny"),
|
|
1122
|
+
className: "min-h-11 flex-1 rounded-md border border-border px-4 font-medium hover:bg-muted",
|
|
1123
|
+
children: "Don't run it"
|
|
1124
|
+
})
|
|
1125
|
+
]
|
|
962
1126
|
})
|
|
963
1127
|
]
|
|
964
1128
|
});
|
|
965
1129
|
}
|
|
966
1130
|
// src/components/QuestionForm.tsx
|
|
967
|
-
import { useState as
|
|
1131
|
+
import { useState as useState3 } from "react";
|
|
968
1132
|
import { HelpCircle, X } from "lucide-react";
|
|
969
|
-
import { jsx as
|
|
1133
|
+
import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
970
1134
|
function emptyDraft(questions) {
|
|
971
1135
|
return { selected: questions.map(() => []), notes: questions.map(() => "") };
|
|
972
1136
|
}
|
|
@@ -989,8 +1153,8 @@ function chip(q, i) {
|
|
|
989
1153
|
}
|
|
990
1154
|
function QuestionForm({ request, onSubmit, onDecline, className }) {
|
|
991
1155
|
const qs = request.questions;
|
|
992
|
-
const [tab, setTab] =
|
|
993
|
-
const [draft, setDraft] =
|
|
1156
|
+
const [tab, setTab] = useState3(0);
|
|
1157
|
+
const [draft, setDraft] = useState3(() => emptyDraft(qs));
|
|
994
1158
|
const q = qs[tab];
|
|
995
1159
|
if (!q)
|
|
996
1160
|
return null;
|
|
@@ -1010,38 +1174,38 @@ function QuestionForm({ request, onSubmit, onDecline, className }) {
|
|
|
1010
1174
|
});
|
|
1011
1175
|
const answered = qs.map((_, i) => (draft.selected[i]?.length ?? 0) > 0 || (draft.notes[i] ?? "").trim().length > 0);
|
|
1012
1176
|
const previewOf = (q.options ?? []).find((o) => picked[0] === o.label)?.preview;
|
|
1013
|
-
return /* @__PURE__ */
|
|
1177
|
+
return /* @__PURE__ */ jsxs7("div", {
|
|
1014
1178
|
role: "dialog",
|
|
1015
1179
|
"aria-label": "The agent has a question",
|
|
1016
1180
|
className: cn("yx-question rounded-md border border-info/50 bg-card text-sm", className),
|
|
1017
1181
|
children: [
|
|
1018
|
-
/* @__PURE__ */
|
|
1182
|
+
/* @__PURE__ */ jsxs7("div", {
|
|
1019
1183
|
className: "flex items-center gap-2 border-b border-border px-3 py-2",
|
|
1020
1184
|
children: [
|
|
1021
|
-
/* @__PURE__ */
|
|
1185
|
+
/* @__PURE__ */ jsx8(HelpCircle, {
|
|
1022
1186
|
className: "h-4 w-4 text-info",
|
|
1023
1187
|
"aria-hidden": true
|
|
1024
1188
|
}),
|
|
1025
|
-
/* @__PURE__ */
|
|
1189
|
+
/* @__PURE__ */ jsx8("span", {
|
|
1026
1190
|
className: "font-medium",
|
|
1027
1191
|
children: "The agent needs your input"
|
|
1028
1192
|
}),
|
|
1029
|
-
/* @__PURE__ */
|
|
1193
|
+
/* @__PURE__ */ jsx8("button", {
|
|
1030
1194
|
type: "button",
|
|
1031
1195
|
onClick: () => onDecline(request.id),
|
|
1032
1196
|
"aria-label": "Close and keep talking",
|
|
1033
1197
|
className: "ml-auto flex h-11 w-11 items-center justify-center rounded-md text-muted-foreground hover:bg-muted",
|
|
1034
|
-
children: /* @__PURE__ */
|
|
1198
|
+
children: /* @__PURE__ */ jsx8(X, {
|
|
1035
1199
|
className: "h-4 w-4",
|
|
1036
1200
|
"aria-hidden": true
|
|
1037
1201
|
})
|
|
1038
1202
|
})
|
|
1039
1203
|
]
|
|
1040
1204
|
}),
|
|
1041
|
-
qs.length > 1 && /* @__PURE__ */
|
|
1205
|
+
qs.length > 1 && /* @__PURE__ */ jsx8("div", {
|
|
1042
1206
|
role: "tablist",
|
|
1043
1207
|
className: "flex gap-1 overflow-x-auto border-b border-border px-2 py-1",
|
|
1044
|
-
children: qs.map((qq, i) => /* @__PURE__ */
|
|
1208
|
+
children: qs.map((qq, i) => /* @__PURE__ */ jsx8("button", {
|
|
1045
1209
|
role: "tab",
|
|
1046
1210
|
type: "button",
|
|
1047
1211
|
"aria-selected": i === tab,
|
|
@@ -1050,37 +1214,37 @@ function QuestionForm({ request, onSubmit, onDecline, className }) {
|
|
|
1050
1214
|
children: chip(qq, i)
|
|
1051
1215
|
}, i))
|
|
1052
1216
|
}),
|
|
1053
|
-
/* @__PURE__ */
|
|
1217
|
+
/* @__PURE__ */ jsxs7("div", {
|
|
1054
1218
|
className: "space-y-3 p-3",
|
|
1055
1219
|
children: [
|
|
1056
|
-
/* @__PURE__ */
|
|
1220
|
+
/* @__PURE__ */ jsx8("p", {
|
|
1057
1221
|
className: "font-medium",
|
|
1058
1222
|
children: q.question
|
|
1059
1223
|
}),
|
|
1060
|
-
(q.options ?? []).length > 0 && /* @__PURE__ */
|
|
1224
|
+
(q.options ?? []).length > 0 && /* @__PURE__ */ jsx8("div", {
|
|
1061
1225
|
role: q.multiSelect ? "group" : "radiogroup",
|
|
1062
1226
|
className: "space-y-2",
|
|
1063
1227
|
children: (q.options ?? []).map((o) => {
|
|
1064
1228
|
const on = picked.includes(o.label);
|
|
1065
|
-
return /* @__PURE__ */
|
|
1229
|
+
return /* @__PURE__ */ jsxs7("button", {
|
|
1066
1230
|
type: "button",
|
|
1067
1231
|
role: q.multiSelect ? "checkbox" : "radio",
|
|
1068
1232
|
"aria-checked": on,
|
|
1069
1233
|
onClick: () => toggle(o.label),
|
|
1070
1234
|
className: cn("flex min-h-11 w-full items-start gap-3 rounded-md border px-3 py-2 text-left hover:bg-muted/60", on ? "border-primary bg-muted" : "border-border"),
|
|
1071
1235
|
children: [
|
|
1072
|
-
/* @__PURE__ */
|
|
1236
|
+
/* @__PURE__ */ jsx8("span", {
|
|
1073
1237
|
className: cn("mt-1 h-4 w-4 shrink-0 border", q.multiSelect ? "rounded" : "rounded-full", on ? "border-primary bg-primary" : "border-muted-foreground"),
|
|
1074
1238
|
"aria-hidden": true
|
|
1075
1239
|
}),
|
|
1076
|
-
/* @__PURE__ */
|
|
1240
|
+
/* @__PURE__ */ jsxs7("span", {
|
|
1077
1241
|
className: "min-w-0",
|
|
1078
1242
|
children: [
|
|
1079
|
-
/* @__PURE__ */
|
|
1243
|
+
/* @__PURE__ */ jsx8("span", {
|
|
1080
1244
|
className: "block font-medium",
|
|
1081
1245
|
children: o.label
|
|
1082
1246
|
}),
|
|
1083
|
-
o.description && /* @__PURE__ */
|
|
1247
|
+
o.description && /* @__PURE__ */ jsx8("span", {
|
|
1084
1248
|
className: "block text-muted-foreground",
|
|
1085
1249
|
children: o.description
|
|
1086
1250
|
})
|
|
@@ -1090,18 +1254,18 @@ function QuestionForm({ request, onSubmit, onDecline, className }) {
|
|
|
1090
1254
|
}, o.label);
|
|
1091
1255
|
})
|
|
1092
1256
|
}),
|
|
1093
|
-
previewOf && /* @__PURE__ */
|
|
1257
|
+
previewOf && /* @__PURE__ */ jsx8("pre", {
|
|
1094
1258
|
className: "overflow-x-auto rounded-md border border-border bg-muted p-2 font-mono text-[12px] leading-5",
|
|
1095
1259
|
children: previewOf
|
|
1096
1260
|
}),
|
|
1097
|
-
/* @__PURE__ */
|
|
1261
|
+
/* @__PURE__ */ jsxs7("label", {
|
|
1098
1262
|
className: "block",
|
|
1099
1263
|
children: [
|
|
1100
|
-
/* @__PURE__ */
|
|
1264
|
+
/* @__PURE__ */ jsx8("span", {
|
|
1101
1265
|
className: "mb-1 block text-[12px] text-muted-foreground",
|
|
1102
1266
|
children: (q.options ?? []).length ? "Add a note (optional)" : "Your answer"
|
|
1103
1267
|
}),
|
|
1104
|
-
/* @__PURE__ */
|
|
1268
|
+
/* @__PURE__ */ jsx8("textarea", {
|
|
1105
1269
|
value: draft.notes[tab] ?? "",
|
|
1106
1270
|
onChange: (e) => setNote(e.target.value),
|
|
1107
1271
|
rows: 2,
|
|
@@ -1109,16 +1273,16 @@ function QuestionForm({ request, onSubmit, onDecline, className }) {
|
|
|
1109
1273
|
})
|
|
1110
1274
|
]
|
|
1111
1275
|
}),
|
|
1112
|
-
/* @__PURE__ */
|
|
1276
|
+
/* @__PURE__ */ jsxs7("div", {
|
|
1113
1277
|
className: "flex gap-2",
|
|
1114
1278
|
children: [
|
|
1115
|
-
tab < qs.length - 1 ? /* @__PURE__ */
|
|
1279
|
+
tab < qs.length - 1 ? /* @__PURE__ */ jsx8("button", {
|
|
1116
1280
|
type: "button",
|
|
1117
1281
|
onClick: () => setTab(tab + 1),
|
|
1118
1282
|
className: "min-h-11 flex-1 rounded-md border border-border px-4 font-medium hover:bg-muted",
|
|
1119
1283
|
children: "Next"
|
|
1120
1284
|
}) : null,
|
|
1121
|
-
/* @__PURE__ */
|
|
1285
|
+
/* @__PURE__ */ jsx8("button", {
|
|
1122
1286
|
type: "button",
|
|
1123
1287
|
onClick: () => onSubmit(request.id, buildOutcome(qs, draft)),
|
|
1124
1288
|
disabled: !answered.some(Boolean),
|
|
@@ -1133,11 +1297,11 @@ function QuestionForm({ request, onSubmit, onDecline, className }) {
|
|
|
1133
1297
|
});
|
|
1134
1298
|
}
|
|
1135
1299
|
// src/components/RunStatus.tsx
|
|
1136
|
-
import { useEffect as useEffect4, useState as
|
|
1300
|
+
import { useEffect as useEffect4, useState as useState4 } from "react";
|
|
1137
1301
|
import { AlertTriangle, Check as Check3, Loader2 as Loader23, Square } from "lucide-react";
|
|
1138
|
-
import { jsx as
|
|
1139
|
-
function RunStatus({ running, startedAt, activity, lastResult, usageTotal, connection, onStop, onApprove, className, now = Date.now }) {
|
|
1140
|
-
const [, tick] =
|
|
1302
|
+
import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
1303
|
+
function RunStatus({ running, startedAt, activity, waitingOn, lastResult, usageTotal, connection, onStop, onApprove, className, now = Date.now }) {
|
|
1304
|
+
const [, tick] = useState4(0);
|
|
1141
1305
|
useEffect4(() => {
|
|
1142
1306
|
if (!running)
|
|
1143
1307
|
return;
|
|
@@ -1145,11 +1309,11 @@ function RunStatus({ running, startedAt, activity, lastResult, usageTotal, conne
|
|
|
1145
1309
|
return () => clearInterval(h);
|
|
1146
1310
|
}, [running]);
|
|
1147
1311
|
if (connection && connection !== "connected") {
|
|
1148
|
-
return /* @__PURE__ */
|
|
1312
|
+
return /* @__PURE__ */ jsxs8("div", {
|
|
1149
1313
|
className: cn("yx-status flex min-h-9 items-center gap-2 text-[12.5px] text-muted-foreground", className),
|
|
1150
1314
|
role: "status",
|
|
1151
1315
|
children: [
|
|
1152
|
-
/* @__PURE__ */
|
|
1316
|
+
/* @__PURE__ */ jsx9(Loader23, {
|
|
1153
1317
|
className: "h-3.5 w-3.5 animate-spin",
|
|
1154
1318
|
"aria-hidden": true
|
|
1155
1319
|
}),
|
|
@@ -1158,32 +1322,34 @@ function RunStatus({ running, startedAt, activity, lastResult, usageTotal, conne
|
|
|
1158
1322
|
});
|
|
1159
1323
|
}
|
|
1160
1324
|
if (running) {
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
"aria-live": "polite",
|
|
1325
|
+
const label2 = waitingOn === "permission" ? "Waiting for your approval" : waitingOn === "question" ? "Waiting for your answer" : activity ?? "Working";
|
|
1326
|
+
return /* @__PURE__ */ jsxs8("div", {
|
|
1327
|
+
className: cn("yx-status flex min-h-11 items-center gap-2 text-[12.5px] text-muted-foreground", className),
|
|
1165
1328
|
children: [
|
|
1166
|
-
/* @__PURE__ */
|
|
1167
|
-
className: "h-3.5 w-3.5 animate-spin text-accent",
|
|
1329
|
+
/* @__PURE__ */ jsx9(Loader23, {
|
|
1330
|
+
className: cn("h-3.5 w-3.5 animate-spin", waitingOn ? "text-warning" : "text-accent"),
|
|
1168
1331
|
"aria-hidden": true
|
|
1169
1332
|
}),
|
|
1170
|
-
/* @__PURE__ */
|
|
1171
|
-
|
|
1333
|
+
/* @__PURE__ */ jsx9("span", {
|
|
1334
|
+
role: "status",
|
|
1335
|
+
"aria-live": "polite",
|
|
1336
|
+
children: label2
|
|
1172
1337
|
}),
|
|
1173
|
-
startedAt !== undefined && /* @__PURE__ */
|
|
1338
|
+
startedAt !== undefined && /* @__PURE__ */ jsxs8("span", {
|
|
1174
1339
|
className: "font-mono tabular-nums",
|
|
1340
|
+
"aria-hidden": true,
|
|
1175
1341
|
children: [
|
|
1176
1342
|
"· ",
|
|
1177
1343
|
elapsedLabel(now() - startedAt)
|
|
1178
1344
|
]
|
|
1179
1345
|
}),
|
|
1180
|
-
onStop && /* @__PURE__ */
|
|
1346
|
+
onStop && /* @__PURE__ */ jsxs8("button", {
|
|
1181
1347
|
type: "button",
|
|
1182
1348
|
onClick: onStop,
|
|
1183
|
-
className: "ml-auto flex min-h-
|
|
1349
|
+
className: "ml-auto flex min-h-11 items-center gap-1 rounded-md px-3 hover:bg-muted",
|
|
1184
1350
|
"aria-label": "Stop",
|
|
1185
1351
|
children: [
|
|
1186
|
-
/* @__PURE__ */
|
|
1352
|
+
/* @__PURE__ */ jsx9(Square, {
|
|
1187
1353
|
className: "h-3.5 w-3.5",
|
|
1188
1354
|
"aria-hidden": true
|
|
1189
1355
|
}),
|
|
@@ -1197,30 +1363,30 @@ function RunStatus({ running, startedAt, activity, lastResult, usageTotal, conne
|
|
|
1197
1363
|
return null;
|
|
1198
1364
|
const { label, tone } = resultLabel(lastResult);
|
|
1199
1365
|
const needsApproval = lastResult.subtype === "needs_approval";
|
|
1200
|
-
return /* @__PURE__ */
|
|
1366
|
+
return /* @__PURE__ */ jsxs8("div", {
|
|
1201
1367
|
className: cn("yx-status flex min-h-9 flex-wrap items-center gap-2 text-[12.5px]", className),
|
|
1202
1368
|
role: "status",
|
|
1203
1369
|
"data-subtype": lastResult.subtype,
|
|
1204
1370
|
children: [
|
|
1205
|
-
tone === "ok" ? /* @__PURE__ */
|
|
1371
|
+
tone === "ok" ? /* @__PURE__ */ jsx9(Check3, {
|
|
1206
1372
|
className: "h-3.5 w-3.5 text-success",
|
|
1207
1373
|
"aria-hidden": true
|
|
1208
|
-
}) : tone === "bad" ? /* @__PURE__ */
|
|
1374
|
+
}) : tone === "bad" ? /* @__PURE__ */ jsx9(AlertTriangle, {
|
|
1209
1375
|
className: "h-3.5 w-3.5 text-destructive",
|
|
1210
1376
|
"aria-hidden": true
|
|
1211
|
-
}) : /* @__PURE__ */
|
|
1377
|
+
}) : /* @__PURE__ */ jsx9(AlertTriangle, {
|
|
1212
1378
|
className: cn("h-3.5 w-3.5", tone === "warn" ? "text-warning" : "text-muted-foreground"),
|
|
1213
1379
|
"aria-hidden": true
|
|
1214
1380
|
}),
|
|
1215
|
-
/* @__PURE__ */
|
|
1381
|
+
/* @__PURE__ */ jsx9("span", {
|
|
1216
1382
|
className: cn(tone === "ok" && "text-success", tone === "bad" && "text-destructive", tone === "warn" && "text-warning", tone === "muted" && "text-muted-foreground"),
|
|
1217
1383
|
children: label
|
|
1218
1384
|
}),
|
|
1219
|
-
/* @__PURE__ */
|
|
1385
|
+
/* @__PURE__ */ jsx9("span", {
|
|
1220
1386
|
className: "text-muted-foreground",
|
|
1221
1387
|
children: "·"
|
|
1222
1388
|
}),
|
|
1223
|
-
/* @__PURE__ */
|
|
1389
|
+
/* @__PURE__ */ jsxs8("span", {
|
|
1224
1390
|
className: "font-mono tabular-nums text-muted-foreground",
|
|
1225
1391
|
children: [
|
|
1226
1392
|
lastResult.numTurns,
|
|
@@ -1230,42 +1396,19 @@ function RunStatus({ running, startedAt, activity, lastResult, usageTotal, conne
|
|
|
1230
1396
|
formatTokens(usageTotal ?? lastResult.usage)
|
|
1231
1397
|
]
|
|
1232
1398
|
}),
|
|
1233
|
-
needsApproval && lastResult.pendingApproval && /* @__PURE__ */
|
|
1234
|
-
className: "basis-full
|
|
1235
|
-
children:
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
}),
|
|
1240
|
-
/* @__PURE__ */ jsx8("pre", {
|
|
1241
|
-
className: "mb-2 overflow-x-auto whitespace-pre-wrap break-words font-mono text-[12px]",
|
|
1242
|
-
children: lastResult.pendingApproval.command
|
|
1243
|
-
}),
|
|
1244
|
-
onApprove && /* @__PURE__ */ jsxs6("div", {
|
|
1245
|
-
className: "flex gap-2",
|
|
1246
|
-
children: [
|
|
1247
|
-
/* @__PURE__ */ jsx8("button", {
|
|
1248
|
-
type: "button",
|
|
1249
|
-
onClick: () => onApprove("allow"),
|
|
1250
|
-
className: "min-h-11 flex-1 rounded-md bg-primary px-3 font-medium text-primary-foreground",
|
|
1251
|
-
children: "Approve and continue"
|
|
1252
|
-
}),
|
|
1253
|
-
/* @__PURE__ */ jsx8("button", {
|
|
1254
|
-
type: "button",
|
|
1255
|
-
onClick: () => onApprove("deny"),
|
|
1256
|
-
className: "min-h-11 flex-1 rounded-md border border-border px-3 font-medium hover:bg-muted",
|
|
1257
|
-
children: "Don't run it"
|
|
1258
|
-
})
|
|
1259
|
-
]
|
|
1260
|
-
})
|
|
1261
|
-
]
|
|
1399
|
+
needsApproval && lastResult.pendingApproval && onApprove && /* @__PURE__ */ jsx9("div", {
|
|
1400
|
+
className: "basis-full",
|
|
1401
|
+
children: /* @__PURE__ */ jsx9(ApprovalPrompt, {
|
|
1402
|
+
approval: lastResult.pendingApproval,
|
|
1403
|
+
onDecide: onApprove
|
|
1404
|
+
})
|
|
1262
1405
|
})
|
|
1263
1406
|
]
|
|
1264
1407
|
});
|
|
1265
1408
|
}
|
|
1266
1409
|
// src/components/SessionList.tsx
|
|
1267
1410
|
import { Plus } from "lucide-react";
|
|
1268
|
-
import { jsx as
|
|
1411
|
+
import { jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
1269
1412
|
function groupSessions(rows) {
|
|
1270
1413
|
const groups = new Map;
|
|
1271
1414
|
for (const r of rows) {
|
|
@@ -1285,74 +1428,75 @@ function when(iso) {
|
|
|
1285
1428
|
}
|
|
1286
1429
|
function SessionList({ sessions, selectedId, onSelect, onNew, className }) {
|
|
1287
1430
|
const groups = groupSessions(sessions);
|
|
1288
|
-
return /* @__PURE__ */
|
|
1431
|
+
return /* @__PURE__ */ jsxs9("nav", {
|
|
1289
1432
|
className: cn("yx-sessions text-sm", className),
|
|
1290
1433
|
"aria-label": "Sessions",
|
|
1291
1434
|
children: [
|
|
1292
|
-
onNew && /* @__PURE__ */
|
|
1435
|
+
onNew && /* @__PURE__ */ jsxs9("button", {
|
|
1293
1436
|
type: "button",
|
|
1294
1437
|
onClick: onNew,
|
|
1295
1438
|
className: "mb-2 flex min-h-11 w-full items-center gap-2 rounded-md border border-dashed border-border px-3 text-left hover:bg-muted",
|
|
1296
1439
|
children: [
|
|
1297
|
-
/* @__PURE__ */
|
|
1440
|
+
/* @__PURE__ */ jsx10(Plus, {
|
|
1298
1441
|
className: "h-4 w-4",
|
|
1299
1442
|
"aria-hidden": true
|
|
1300
1443
|
}),
|
|
1301
1444
|
" New session"
|
|
1302
1445
|
]
|
|
1303
1446
|
}),
|
|
1304
|
-
sessions.length === 0 && /* @__PURE__ */
|
|
1447
|
+
sessions.length === 0 && /* @__PURE__ */ jsx10("p", {
|
|
1305
1448
|
className: "px-3 py-2 text-muted-foreground",
|
|
1306
1449
|
children: "No sessions yet."
|
|
1307
1450
|
}),
|
|
1308
|
-
groups.map((g) => /* @__PURE__ */
|
|
1451
|
+
groups.map((g) => /* @__PURE__ */ jsxs9("div", {
|
|
1309
1452
|
className: "mb-2",
|
|
1310
1453
|
children: [
|
|
1311
|
-
g.project && /* @__PURE__ */
|
|
1454
|
+
g.project && /* @__PURE__ */ jsx10("div", {
|
|
1312
1455
|
className: "px-3 py-1 text-[11px] uppercase tracking-wide text-muted-foreground",
|
|
1313
1456
|
children: g.project
|
|
1314
1457
|
}),
|
|
1315
|
-
/* @__PURE__ */
|
|
1458
|
+
/* @__PURE__ */ jsx10("ul", {
|
|
1316
1459
|
children: g.rows.map((s) => {
|
|
1317
1460
|
const on = s.sessionId === selectedId;
|
|
1318
1461
|
const title = s.name || s.title || s.lastMessagePreview || s.sessionId.slice(0, 8);
|
|
1319
|
-
return /* @__PURE__ */
|
|
1320
|
-
children: /* @__PURE__ */
|
|
1462
|
+
return /* @__PURE__ */ jsx10("li", {
|
|
1463
|
+
children: /* @__PURE__ */ jsxs9("button", {
|
|
1321
1464
|
type: "button",
|
|
1322
1465
|
onClick: () => onSelect(s.sessionId),
|
|
1323
1466
|
"aria-current": on ? "true" : undefined,
|
|
1324
1467
|
className: cn("flex min-h-11 w-full flex-col gap-0.5 rounded-md px-3 py-2 text-left hover:bg-muted", on && "bg-muted"),
|
|
1325
1468
|
"data-status": s.status,
|
|
1326
1469
|
children: [
|
|
1327
|
-
/* @__PURE__ */
|
|
1470
|
+
/* @__PURE__ */ jsxs9("span", {
|
|
1328
1471
|
className: "flex items-center gap-2",
|
|
1329
1472
|
children: [
|
|
1330
|
-
/* @__PURE__ */
|
|
1331
|
-
className: cn("h-2 w-2 shrink-0 rounded-full", s.status === "active" ? "bg-success" : s.status === "listening" ? "bg-info" : s.status === "error" ? "bg-destructive" : "bg-muted-foreground/40"),
|
|
1332
|
-
"aria-label": s.status
|
|
1473
|
+
/* @__PURE__ */ jsx10("span", {
|
|
1474
|
+
className: cn("h-2 w-2 shrink-0 rounded-full", s.live ? "animate-pulse bg-accent" : s.status === "active" ? "bg-success" : s.status === "listening" ? "bg-info" : s.status === "error" ? "bg-destructive" : "bg-muted-foreground/40"),
|
|
1475
|
+
"aria-label": s.live ? "working" : s.status,
|
|
1476
|
+
"data-live": s.live ? "true" : undefined
|
|
1333
1477
|
}),
|
|
1334
|
-
/* @__PURE__ */
|
|
1478
|
+
/* @__PURE__ */ jsx10("span", {
|
|
1335
1479
|
className: "min-w-0 flex-1 truncate font-medium",
|
|
1336
1480
|
children: title
|
|
1337
1481
|
}),
|
|
1338
|
-
s.pendingQuestion && /* @__PURE__ */
|
|
1482
|
+
s.pendingQuestion && /* @__PURE__ */ jsx10("span", {
|
|
1339
1483
|
className: "shrink-0 rounded-full bg-warning px-1.5 text-[10px] font-medium text-warning-foreground",
|
|
1340
1484
|
children: "question"
|
|
1341
1485
|
})
|
|
1342
1486
|
]
|
|
1343
1487
|
}),
|
|
1344
|
-
/* @__PURE__ */
|
|
1488
|
+
/* @__PURE__ */ jsxs9("span", {
|
|
1345
1489
|
className: "flex gap-2 truncate text-[11.5px] text-muted-foreground",
|
|
1346
1490
|
children: [
|
|
1347
|
-
s.hop && /* @__PURE__ */
|
|
1491
|
+
s.hop && /* @__PURE__ */ jsx10("span", {
|
|
1348
1492
|
className: "truncate",
|
|
1349
1493
|
children: s.hop
|
|
1350
1494
|
}),
|
|
1351
|
-
s.relCwd && /* @__PURE__ */
|
|
1495
|
+
s.relCwd && /* @__PURE__ */ jsx10("span", {
|
|
1352
1496
|
className: "truncate",
|
|
1353
1497
|
children: s.relCwd
|
|
1354
1498
|
}),
|
|
1355
|
-
/* @__PURE__ */
|
|
1499
|
+
/* @__PURE__ */ jsx10("span", {
|
|
1356
1500
|
className: "ml-auto shrink-0 tabular-nums",
|
|
1357
1501
|
children: when(s.updatedAt)
|
|
1358
1502
|
})
|
|
@@ -1369,9 +1513,9 @@ function SessionList({ sessions, selectedId, onSelect, onNew, className }) {
|
|
|
1369
1513
|
});
|
|
1370
1514
|
}
|
|
1371
1515
|
// src/components/Composer.tsx
|
|
1372
|
-
import { useCallback as useCallback2, useEffect as useEffect5, useRef as useRef3, useState as
|
|
1516
|
+
import { useCallback as useCallback2, useEffect as useEffect5, useRef as useRef3, useState as useState5 } from "react";
|
|
1373
1517
|
import { ArrowUp, ImagePlus, Square as Square2, X as X2 } from "lucide-react";
|
|
1374
|
-
import { jsx as
|
|
1518
|
+
import { jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
1375
1519
|
var ACCEPT = ["image/png", "image/jpeg", "image/gif", "image/webp"];
|
|
1376
1520
|
async function fileToAttachment(f) {
|
|
1377
1521
|
const buf = new Uint8Array(await f.arrayBuffer());
|
|
@@ -1380,17 +1524,34 @@ async function fileToAttachment(f) {
|
|
|
1380
1524
|
bin += String.fromCharCode(...buf.subarray(i, i + 32768));
|
|
1381
1525
|
return { name: f.name || "image", mediaType: f.type, data: btoa(bin), previewUrl: URL.createObjectURL(f) };
|
|
1382
1526
|
}
|
|
1383
|
-
function
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1527
|
+
function coarsePointer() {
|
|
1528
|
+
return typeof window !== "undefined" && typeof window.matchMedia === "function" && window.matchMedia("(pointer: coarse)").matches;
|
|
1529
|
+
}
|
|
1530
|
+
function Composer({ onSubmit, running, onStop, onSteer, value, onChange, initialValue, submitOnEnter, hideAttach, disabled, disabledReason, placeholder = "Ask yodex to do something…", hopOptions, hop, onHopChange, leading, maxAttachmentBytes = 8 * 1024 * 1024, className, autoFocus }) {
|
|
1531
|
+
const [own, setOwn] = useState5(initialValue ?? "");
|
|
1532
|
+
const text = value ?? own;
|
|
1533
|
+
const setText = (t) => {
|
|
1534
|
+
if (value === undefined)
|
|
1535
|
+
setOwn(t);
|
|
1536
|
+
onChange?.(t);
|
|
1537
|
+
};
|
|
1538
|
+
const [attachments, setAttachments] = useState5([]);
|
|
1539
|
+
const [note, setNote] = useState5(null);
|
|
1387
1540
|
const ref = useRef3(null);
|
|
1388
1541
|
useEffect5(() => {
|
|
1389
1542
|
const el = ref.current;
|
|
1390
1543
|
if (!el)
|
|
1391
1544
|
return;
|
|
1392
|
-
|
|
1393
|
-
|
|
1545
|
+
const fit = () => {
|
|
1546
|
+
el.style.height = "0px";
|
|
1547
|
+
el.style.height = `${Math.min(el.scrollHeight, 240)}px`;
|
|
1548
|
+
};
|
|
1549
|
+
fit();
|
|
1550
|
+
if (typeof ResizeObserver === "undefined")
|
|
1551
|
+
return;
|
|
1552
|
+
const ro = new ResizeObserver(fit);
|
|
1553
|
+
ro.observe(el);
|
|
1554
|
+
return () => ro.disconnect();
|
|
1394
1555
|
}, [text]);
|
|
1395
1556
|
const addFiles = useCallback2(async (files) => {
|
|
1396
1557
|
const list = [...files].filter((f) => ACCEPT.includes(f.type));
|
|
@@ -1411,10 +1572,16 @@ function Composer({ onSubmit, running, onStop, disabled, disabledReason, placeho
|
|
|
1411
1572
|
URL.revokeObjectURL(gone.previewUrl);
|
|
1412
1573
|
return a.filter((_, j) => j !== i);
|
|
1413
1574
|
});
|
|
1414
|
-
const
|
|
1575
|
+
const steering = Boolean(running && onSteer);
|
|
1576
|
+
const canSend = !disabled && (steering ? text.trim().length > 0 : !running && (text.trim().length > 0 || attachments.length > 0));
|
|
1415
1577
|
const submit = () => {
|
|
1416
1578
|
if (!canSend)
|
|
1417
1579
|
return;
|
|
1580
|
+
if (steering) {
|
|
1581
|
+
onSteer(text.trim());
|
|
1582
|
+
setText("");
|
|
1583
|
+
return;
|
|
1584
|
+
}
|
|
1418
1585
|
onSubmit(text.trim(), attachments);
|
|
1419
1586
|
setText("");
|
|
1420
1587
|
for (const a of attachments)
|
|
@@ -1423,10 +1590,13 @@ function Composer({ onSubmit, running, onStop, disabled, disabledReason, placeho
|
|
|
1423
1590
|
setAttachments([]);
|
|
1424
1591
|
};
|
|
1425
1592
|
const onKey = (e) => {
|
|
1426
|
-
if (e.key
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1593
|
+
if (e.key !== "Enter" || e.shiftKey || e.nativeEvent.isComposing)
|
|
1594
|
+
return;
|
|
1595
|
+
const enterSends = submitOnEnter ?? !coarsePointer();
|
|
1596
|
+
if (!enterSends)
|
|
1597
|
+
return;
|
|
1598
|
+
e.preventDefault();
|
|
1599
|
+
submit();
|
|
1430
1600
|
};
|
|
1431
1601
|
const onPaste = (e) => {
|
|
1432
1602
|
const files = [...e.clipboardData.files];
|
|
@@ -1441,39 +1611,42 @@ function Composer({ onSubmit, running, onStop, disabled, disabledReason, placeho
|
|
|
1441
1611
|
addFiles(e.dataTransfer.files);
|
|
1442
1612
|
}
|
|
1443
1613
|
};
|
|
1444
|
-
return /* @__PURE__ */
|
|
1614
|
+
return /* @__PURE__ */ jsxs10("div", {
|
|
1445
1615
|
className: cn("yx-composer rounded-lg border border-border bg-card shadow-sm", disabled && "opacity-70", className),
|
|
1446
1616
|
onDrop,
|
|
1447
1617
|
onDragOver: (e) => e.preventDefault(),
|
|
1448
1618
|
"data-disabled": disabled ? "true" : undefined,
|
|
1449
1619
|
children: [
|
|
1450
|
-
attachments.length > 0 && /* @__PURE__ */
|
|
1620
|
+
attachments.length > 0 && /* @__PURE__ */ jsx11("ul", {
|
|
1451
1621
|
className: "flex flex-wrap gap-2 px-3 pt-3",
|
|
1452
1622
|
"aria-label": "attachments",
|
|
1453
|
-
children: attachments.map((a, i) => /* @__PURE__ */
|
|
1623
|
+
children: attachments.map((a, i) => /* @__PURE__ */ jsxs10("li", {
|
|
1454
1624
|
className: "relative",
|
|
1455
1625
|
children: [
|
|
1456
|
-
a.previewUrl ? /* @__PURE__ */
|
|
1626
|
+
a.previewUrl ? /* @__PURE__ */ jsx11("img", {
|
|
1457
1627
|
src: a.previewUrl,
|
|
1458
1628
|
alt: a.name,
|
|
1459
1629
|
className: "h-14 w-14 rounded-md border border-border object-cover"
|
|
1460
|
-
}) : /* @__PURE__ */
|
|
1630
|
+
}) : /* @__PURE__ */ jsx11("span", {
|
|
1461
1631
|
className: "block h-14 w-14 rounded-md border border-border bg-muted"
|
|
1462
1632
|
}),
|
|
1463
|
-
/* @__PURE__ */
|
|
1633
|
+
/* @__PURE__ */ jsx11("button", {
|
|
1464
1634
|
type: "button",
|
|
1465
1635
|
onClick: () => remove(i),
|
|
1466
1636
|
"aria-label": `Remove ${a.name}`,
|
|
1467
|
-
className: "absolute -right-
|
|
1468
|
-
children: /* @__PURE__ */
|
|
1469
|
-
className: "h-
|
|
1470
|
-
|
|
1637
|
+
className: "absolute -right-4 -top-4 flex h-11 w-11 items-center justify-center rounded-full",
|
|
1638
|
+
children: /* @__PURE__ */ jsx11("span", {
|
|
1639
|
+
className: "flex h-6 w-6 items-center justify-center rounded-full border border-border bg-background",
|
|
1640
|
+
children: /* @__PURE__ */ jsx11(X2, {
|
|
1641
|
+
className: "h-3 w-3",
|
|
1642
|
+
"aria-hidden": true
|
|
1643
|
+
})
|
|
1471
1644
|
})
|
|
1472
1645
|
})
|
|
1473
1646
|
]
|
|
1474
1647
|
}, `${a.name}-${i}`))
|
|
1475
1648
|
}),
|
|
1476
|
-
/* @__PURE__ */
|
|
1649
|
+
/* @__PURE__ */ jsx11("textarea", {
|
|
1477
1650
|
ref,
|
|
1478
1651
|
value: text,
|
|
1479
1652
|
onChange: (e) => setText(e.target.value),
|
|
@@ -1482,35 +1655,35 @@ function Composer({ onSubmit, running, onStop, disabled, disabledReason, placeho
|
|
|
1482
1655
|
disabled,
|
|
1483
1656
|
autoFocus,
|
|
1484
1657
|
rows: 1,
|
|
1485
|
-
placeholder: disabled && disabledReason ? disabledReason : placeholder,
|
|
1486
|
-
"aria-label": "Message",
|
|
1487
|
-
className: "block w-full resize-none bg-transparent px-3 py-3 text-[
|
|
1658
|
+
placeholder: disabled && disabledReason ? disabledReason : steering ? "Steer the agent (it reads this at its next step)…" : placeholder,
|
|
1659
|
+
"aria-label": steering ? "Steer" : "Message",
|
|
1660
|
+
className: "block w-full resize-none bg-transparent px-3 py-3 text-[16px] leading-6 placeholder:text-muted-foreground focus-visible:outline-none"
|
|
1488
1661
|
}),
|
|
1489
|
-
/* @__PURE__ */
|
|
1662
|
+
/* @__PURE__ */ jsxs10("div", {
|
|
1490
1663
|
className: "flex items-center gap-1 px-2 pb-2",
|
|
1491
1664
|
children: [
|
|
1492
1665
|
leading,
|
|
1493
|
-
hopOptions && hopOptions.length > 0 && /* @__PURE__ */
|
|
1666
|
+
hopOptions && hopOptions.length > 0 && /* @__PURE__ */ jsx11("select", {
|
|
1494
1667
|
value: hop ?? "",
|
|
1495
1668
|
onChange: (e) => onHopChange?.(e.target.value),
|
|
1496
1669
|
disabled,
|
|
1497
1670
|
"aria-label": "HOP",
|
|
1498
1671
|
className: "min-h-9 max-w-[45%] rounded-md border border-border bg-background px-2 text-[12.5px]",
|
|
1499
|
-
children: hopOptions.map((o) => /* @__PURE__ */
|
|
1672
|
+
children: hopOptions.map((o) => /* @__PURE__ */ jsx11("option", {
|
|
1500
1673
|
value: o.value,
|
|
1501
1674
|
title: o.description,
|
|
1502
1675
|
children: o.label
|
|
1503
1676
|
}, o.value))
|
|
1504
1677
|
}),
|
|
1505
|
-
/* @__PURE__ */
|
|
1678
|
+
!hideAttach && /* @__PURE__ */ jsxs10("label", {
|
|
1506
1679
|
className: cn("flex h-11 w-11 cursor-pointer items-center justify-center rounded-md text-muted-foreground hover:bg-muted", disabled && "pointer-events-none"),
|
|
1507
1680
|
"aria-label": "Attach image",
|
|
1508
1681
|
children: [
|
|
1509
|
-
/* @__PURE__ */
|
|
1682
|
+
/* @__PURE__ */ jsx11(ImagePlus, {
|
|
1510
1683
|
className: "h-4 w-4",
|
|
1511
1684
|
"aria-hidden": true
|
|
1512
1685
|
}),
|
|
1513
|
-
/* @__PURE__ */
|
|
1686
|
+
/* @__PURE__ */ jsx11("input", {
|
|
1514
1687
|
type: "file",
|
|
1515
1688
|
accept: ACCEPT.join(","),
|
|
1516
1689
|
multiple: true,
|
|
@@ -1520,29 +1693,29 @@ function Composer({ onSubmit, running, onStop, disabled, disabledReason, placeho
|
|
|
1520
1693
|
})
|
|
1521
1694
|
]
|
|
1522
1695
|
}),
|
|
1523
|
-
note && /* @__PURE__ */
|
|
1696
|
+
note && /* @__PURE__ */ jsx11("span", {
|
|
1524
1697
|
className: "truncate text-[12px] text-destructive",
|
|
1525
1698
|
children: note
|
|
1526
1699
|
}),
|
|
1527
|
-
/* @__PURE__ */
|
|
1700
|
+
/* @__PURE__ */ jsx11("span", {
|
|
1528
1701
|
className: "flex-1"
|
|
1529
1702
|
}),
|
|
1530
|
-
running ? /* @__PURE__ */
|
|
1703
|
+
running && onStop && !(steering && text.trim()) ? /* @__PURE__ */ jsx11("button", {
|
|
1531
1704
|
type: "button",
|
|
1532
1705
|
onClick: onStop,
|
|
1533
1706
|
"aria-label": "Stop",
|
|
1534
1707
|
className: "flex h-11 w-11 items-center justify-center rounded-md bg-destructive text-destructive-foreground",
|
|
1535
|
-
children: /* @__PURE__ */
|
|
1708
|
+
children: /* @__PURE__ */ jsx11(Square2, {
|
|
1536
1709
|
className: "h-4 w-4",
|
|
1537
1710
|
"aria-hidden": true
|
|
1538
1711
|
})
|
|
1539
|
-
}) : /* @__PURE__ */
|
|
1712
|
+
}) : /* @__PURE__ */ jsx11("button", {
|
|
1540
1713
|
type: "button",
|
|
1541
1714
|
onClick: submit,
|
|
1542
1715
|
disabled: !canSend,
|
|
1543
1716
|
"aria-label": "Send",
|
|
1544
1717
|
className: "flex h-11 w-11 items-center justify-center rounded-md bg-primary text-primary-foreground disabled:opacity-40",
|
|
1545
|
-
children: /* @__PURE__ */
|
|
1718
|
+
children: /* @__PURE__ */ jsx11(ArrowUp, {
|
|
1546
1719
|
className: "h-4 w-4",
|
|
1547
1720
|
"aria-hidden": true
|
|
1548
1721
|
})
|
|
@@ -1567,6 +1740,7 @@ export {
|
|
|
1567
1740
|
buildOutcome,
|
|
1568
1741
|
applyEvent,
|
|
1569
1742
|
YodexClient,
|
|
1743
|
+
TranscriptRow,
|
|
1570
1744
|
Transcript,
|
|
1571
1745
|
ToolCall,
|
|
1572
1746
|
SessionList,
|
|
@@ -1576,8 +1750,9 @@ export {
|
|
|
1576
1750
|
PermissionPrompt,
|
|
1577
1751
|
DiffView,
|
|
1578
1752
|
Composer,
|
|
1579
|
-
AssistantMessage
|
|
1753
|
+
AssistantMessage,
|
|
1754
|
+
ApprovalPrompt
|
|
1580
1755
|
};
|
|
1581
1756
|
|
|
1582
|
-
//# debugId=
|
|
1757
|
+
//# debugId=C0D62ACAF9016C3D64756E2164756E21
|
|
1583
1758
|
//# sourceMappingURL=index.js.map
|