@poncho-ai/cli 0.13.0 → 0.14.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/.turbo/turbo-build.log +6 -6
- package/CHANGELOG.md +42 -0
- package/dist/{chunk-CUCEDHME.js → chunk-AIEVSNGF.js} +1994 -467
- package/dist/cli.js +1 -1
- package/dist/index.js +1 -1
- package/dist/{run-interactive-ink-VZBOYJYS.js → run-interactive-ink-7ULE5JJI.js} +151 -118
- package/package.json +4 -4
- package/src/api-docs.ts +674 -0
- package/src/index.ts +632 -229
- package/src/init-onboarding.ts +14 -1
- package/src/run-interactive-ink.ts +171 -147
- package/src/web-ui.ts +760 -244
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
import { getTextContent } from "@poncho-ai/sdk";
|
|
25
25
|
import {
|
|
26
26
|
AgentBridge,
|
|
27
|
+
ResendAdapter,
|
|
27
28
|
SlackAdapter
|
|
28
29
|
} from "@poncho-ai/messaging";
|
|
29
30
|
import Busboy from "busboy";
|
|
@@ -258,7 +259,8 @@ var renderWebUiHtml = (options) => {
|
|
|
258
259
|
<head>
|
|
259
260
|
<meta charset="utf-8">
|
|
260
261
|
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, viewport-fit=cover">
|
|
261
|
-
<meta name="theme-color" content="#000000">
|
|
262
|
+
<meta name="theme-color" content="#000000" media="(prefers-color-scheme: dark)">
|
|
263
|
+
<meta name="theme-color" content="#ffffff" media="(prefers-color-scheme: light)">
|
|
262
264
|
<meta name="apple-mobile-web-app-capable" content="yes">
|
|
263
265
|
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
|
264
266
|
<meta name="apple-mobile-web-app-title" content="${agentName}">
|
|
@@ -268,12 +270,172 @@ var renderWebUiHtml = (options) => {
|
|
|
268
270
|
<title>${agentName}</title>
|
|
269
271
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Inconsolata:400,700">
|
|
270
272
|
<style>
|
|
273
|
+
:root {
|
|
274
|
+
color-scheme: light dark;
|
|
275
|
+
|
|
276
|
+
--bg: #000;
|
|
277
|
+
--bg-alt: #0a0a0a;
|
|
278
|
+
--bg-elevated: #111;
|
|
279
|
+
|
|
280
|
+
--fg: #ededed;
|
|
281
|
+
--fg-strong: #fff;
|
|
282
|
+
--fg-2: #888;
|
|
283
|
+
--fg-3: #999;
|
|
284
|
+
--fg-4: #777;
|
|
285
|
+
--fg-5: #666;
|
|
286
|
+
--fg-6: #555;
|
|
287
|
+
--fg-7: #444;
|
|
288
|
+
--fg-8: #333;
|
|
289
|
+
|
|
290
|
+
--fg-tool: #8a8a8a;
|
|
291
|
+
--fg-tool-code: #bcbcbc;
|
|
292
|
+
--fg-tool-item: #d6d6d6;
|
|
293
|
+
--fg-approval-label: #b0b0b0;
|
|
294
|
+
--fg-approval-input: #cfcfcf;
|
|
295
|
+
--fg-approval-btn: #f0f0f0;
|
|
296
|
+
|
|
297
|
+
--accent: #ededed;
|
|
298
|
+
--accent-fg: #000;
|
|
299
|
+
--accent-hover: #fff;
|
|
300
|
+
|
|
301
|
+
--stop-bg: #4a4a4a;
|
|
302
|
+
--stop-fg: #fff;
|
|
303
|
+
--stop-hover: #565656;
|
|
304
|
+
|
|
305
|
+
--border-1: rgba(255,255,255,0.06);
|
|
306
|
+
--border-2: rgba(255,255,255,0.08);
|
|
307
|
+
--border-3: rgba(255,255,255,0.1);
|
|
308
|
+
--border-4: rgba(255,255,255,0.12);
|
|
309
|
+
--border-5: rgba(255,255,255,0.18);
|
|
310
|
+
--border-focus: rgba(255,255,255,0.2);
|
|
311
|
+
--border-hover: rgba(255,255,255,0.25);
|
|
312
|
+
--border-drag: rgba(255,255,255,0.4);
|
|
313
|
+
|
|
314
|
+
--surface-1: rgba(255,255,255,0.02);
|
|
315
|
+
--surface-2: rgba(255,255,255,0.03);
|
|
316
|
+
--surface-3: rgba(255,255,255,0.04);
|
|
317
|
+
--surface-4: rgba(255,255,255,0.06);
|
|
318
|
+
--surface-5: rgba(255,255,255,0.08);
|
|
319
|
+
--surface-6: rgba(255,255,255,0.1);
|
|
320
|
+
--surface-7: rgba(255,255,255,0.12);
|
|
321
|
+
--surface-8: rgba(255,255,255,0.14);
|
|
322
|
+
|
|
323
|
+
--chip-bg: rgba(0,0,0,0.6);
|
|
324
|
+
--chip-bg-hover: rgba(0,0,0,0.75);
|
|
325
|
+
--backdrop: rgba(0,0,0,0.6);
|
|
326
|
+
--lightbox-bg: rgba(0,0,0,0.85);
|
|
327
|
+
--inset-1: rgba(0,0,0,0.16);
|
|
328
|
+
--inset-2: rgba(0,0,0,0.25);
|
|
329
|
+
|
|
330
|
+
--file-badge-bg: rgba(0,0,0,0.2);
|
|
331
|
+
--file-badge-fg: rgba(255,255,255,0.8);
|
|
332
|
+
|
|
333
|
+
--error: #ff4444;
|
|
334
|
+
--error-soft: #ff6b6b;
|
|
335
|
+
--error-alt: #ff6666;
|
|
336
|
+
--error-bg: rgba(255,68,68,0.08);
|
|
337
|
+
--error-border: rgba(255,68,68,0.25);
|
|
338
|
+
|
|
339
|
+
--tool-done: #6a9955;
|
|
340
|
+
--tool-error: #f48771;
|
|
341
|
+
|
|
342
|
+
--warning: #e8a735;
|
|
343
|
+
|
|
344
|
+
--approve: #78e7a6;
|
|
345
|
+
--approve-border: rgba(58,208,122,0.45);
|
|
346
|
+
--deny: #f59b9b;
|
|
347
|
+
--deny-border: rgba(224,95,95,0.45);
|
|
348
|
+
|
|
349
|
+
--scrollbar: rgba(255,255,255,0.1);
|
|
350
|
+
--scrollbar-hover: rgba(255,255,255,0.16);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
@media (prefers-color-scheme: light) {
|
|
354
|
+
:root {
|
|
355
|
+
--bg: #ffffff;
|
|
356
|
+
--bg-alt: #f5f5f5;
|
|
357
|
+
--bg-elevated: #e8e8e8;
|
|
358
|
+
|
|
359
|
+
--fg: #1a1a1a;
|
|
360
|
+
--fg-strong: #000;
|
|
361
|
+
--fg-2: #666;
|
|
362
|
+
--fg-3: #555;
|
|
363
|
+
--fg-4: #777;
|
|
364
|
+
--fg-5: #888;
|
|
365
|
+
--fg-6: #888;
|
|
366
|
+
--fg-7: #aaa;
|
|
367
|
+
--fg-8: #bbb;
|
|
368
|
+
|
|
369
|
+
--fg-tool: #666;
|
|
370
|
+
--fg-tool-code: #444;
|
|
371
|
+
--fg-tool-item: #333;
|
|
372
|
+
--fg-approval-label: #666;
|
|
373
|
+
--fg-approval-input: #444;
|
|
374
|
+
--fg-approval-btn: #1a1a1a;
|
|
375
|
+
|
|
376
|
+
--accent: #1a1a1a;
|
|
377
|
+
--accent-fg: #fff;
|
|
378
|
+
--accent-hover: #000;
|
|
379
|
+
|
|
380
|
+
--stop-bg: #d4d4d4;
|
|
381
|
+
--stop-fg: #333;
|
|
382
|
+
--stop-hover: #c4c4c4;
|
|
383
|
+
|
|
384
|
+
--border-1: rgba(0,0,0,0.06);
|
|
385
|
+
--border-2: rgba(0,0,0,0.08);
|
|
386
|
+
--border-3: rgba(0,0,0,0.1);
|
|
387
|
+
--border-4: rgba(0,0,0,0.1);
|
|
388
|
+
--border-5: rgba(0,0,0,0.15);
|
|
389
|
+
--border-focus: rgba(0,0,0,0.2);
|
|
390
|
+
--border-hover: rgba(0,0,0,0.2);
|
|
391
|
+
--border-drag: rgba(0,0,0,0.3);
|
|
392
|
+
|
|
393
|
+
--surface-1: rgba(0,0,0,0.02);
|
|
394
|
+
--surface-2: rgba(0,0,0,0.03);
|
|
395
|
+
--surface-3: rgba(0,0,0,0.03);
|
|
396
|
+
--surface-4: rgba(0,0,0,0.04);
|
|
397
|
+
--surface-5: rgba(0,0,0,0.05);
|
|
398
|
+
--surface-6: rgba(0,0,0,0.07);
|
|
399
|
+
--surface-7: rgba(0,0,0,0.08);
|
|
400
|
+
--surface-8: rgba(0,0,0,0.1);
|
|
401
|
+
|
|
402
|
+
--chip-bg: rgba(255,255,255,0.8);
|
|
403
|
+
--chip-bg-hover: rgba(255,255,255,0.9);
|
|
404
|
+
--backdrop: rgba(0,0,0,0.3);
|
|
405
|
+
--lightbox-bg: rgba(0,0,0,0.75);
|
|
406
|
+
--inset-1: rgba(0,0,0,0.04);
|
|
407
|
+
--inset-2: rgba(0,0,0,0.06);
|
|
408
|
+
|
|
409
|
+
--file-badge-bg: rgba(0,0,0,0.05);
|
|
410
|
+
--file-badge-fg: rgba(0,0,0,0.7);
|
|
411
|
+
|
|
412
|
+
--error: #dc2626;
|
|
413
|
+
--error-soft: #ef4444;
|
|
414
|
+
--error-alt: #ef4444;
|
|
415
|
+
--error-bg: rgba(220,38,38,0.06);
|
|
416
|
+
--error-border: rgba(220,38,38,0.2);
|
|
417
|
+
|
|
418
|
+
--tool-done: #16a34a;
|
|
419
|
+
--tool-error: #dc2626;
|
|
420
|
+
|
|
421
|
+
--warning: #ca8a04;
|
|
422
|
+
|
|
423
|
+
--approve: #16a34a;
|
|
424
|
+
--approve-border: rgba(22,163,74,0.35);
|
|
425
|
+
--deny: #dc2626;
|
|
426
|
+
--deny-border: rgba(220,38,38,0.3);
|
|
427
|
+
|
|
428
|
+
--scrollbar: rgba(0,0,0,0.12);
|
|
429
|
+
--scrollbar-hover: rgba(0,0,0,0.2);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
|
|
271
433
|
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
272
434
|
html, body { height: 100vh; overflow: hidden; overscroll-behavior: none; touch-action: pan-y; }
|
|
273
435
|
body {
|
|
274
436
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Helvetica Neue", sans-serif;
|
|
275
|
-
background:
|
|
276
|
-
color:
|
|
437
|
+
background: var(--bg);
|
|
438
|
+
color: var(--fg);
|
|
277
439
|
font-size: 14px;
|
|
278
440
|
line-height: 1.5;
|
|
279
441
|
-webkit-font-smoothing: antialiased;
|
|
@@ -281,7 +443,7 @@ var renderWebUiHtml = (options) => {
|
|
|
281
443
|
}
|
|
282
444
|
button, input, textarea { font: inherit; color: inherit; }
|
|
283
445
|
.hidden { display: none !important; }
|
|
284
|
-
a { color:
|
|
446
|
+
a { color: var(--fg); }
|
|
285
447
|
|
|
286
448
|
/* Auth */
|
|
287
449
|
.auth {
|
|
@@ -289,39 +451,39 @@ var renderWebUiHtml = (options) => {
|
|
|
289
451
|
display: grid;
|
|
290
452
|
place-items: center;
|
|
291
453
|
padding: 20px;
|
|
292
|
-
background:
|
|
454
|
+
background: var(--bg);
|
|
293
455
|
}
|
|
294
456
|
.auth-card {
|
|
295
457
|
width: min(400px, 90vw);
|
|
296
458
|
}
|
|
297
459
|
.auth-shell {
|
|
298
|
-
background:
|
|
299
|
-
border: 1px solid
|
|
460
|
+
background: var(--bg-alt);
|
|
461
|
+
border: 1px solid var(--border-3);
|
|
300
462
|
border-radius: 9999px;
|
|
301
463
|
display: flex;
|
|
302
464
|
align-items: center;
|
|
303
465
|
padding: 4px 6px 4px 18px;
|
|
304
466
|
transition: border-color 0.15s;
|
|
305
467
|
}
|
|
306
|
-
.auth-shell:focus-within { border-color:
|
|
468
|
+
.auth-shell:focus-within { border-color: var(--border-focus); }
|
|
307
469
|
.auth-input {
|
|
308
470
|
flex: 1;
|
|
309
471
|
background: transparent;
|
|
310
472
|
border: 0;
|
|
311
473
|
outline: none;
|
|
312
|
-
color:
|
|
474
|
+
color: var(--fg);
|
|
313
475
|
padding: 10px 0 8px;
|
|
314
476
|
font-size: 14px;
|
|
315
477
|
margin-top: -2px;
|
|
316
478
|
}
|
|
317
|
-
.auth-input::placeholder { color:
|
|
479
|
+
.auth-input::placeholder { color: var(--fg-7); }
|
|
318
480
|
.auth-submit {
|
|
319
481
|
width: 32px;
|
|
320
482
|
height: 32px;
|
|
321
|
-
background:
|
|
483
|
+
background: var(--accent);
|
|
322
484
|
border: 0;
|
|
323
485
|
border-radius: 50%;
|
|
324
|
-
color:
|
|
486
|
+
color: var(--accent-fg);
|
|
325
487
|
cursor: pointer;
|
|
326
488
|
display: grid;
|
|
327
489
|
place-items: center;
|
|
@@ -329,19 +491,19 @@ var renderWebUiHtml = (options) => {
|
|
|
329
491
|
margin-bottom: 2px;
|
|
330
492
|
transition: background 0.15s;
|
|
331
493
|
}
|
|
332
|
-
.auth-submit:hover { background:
|
|
333
|
-
.error { color:
|
|
494
|
+
.auth-submit:hover { background: var(--accent-hover); }
|
|
495
|
+
.error { color: var(--error); font-size: 13px; min-height: 16px; }
|
|
334
496
|
.message-error {
|
|
335
|
-
background:
|
|
336
|
-
border: 1px solid
|
|
497
|
+
background: var(--error-bg);
|
|
498
|
+
border: 1px solid var(--error-border);
|
|
337
499
|
border-radius: 10px;
|
|
338
|
-
color:
|
|
500
|
+
color: var(--error-soft);
|
|
339
501
|
padding: 12px 16px;
|
|
340
502
|
font-size: 13px;
|
|
341
503
|
line-height: 1.5;
|
|
342
504
|
max-width: 600px;
|
|
343
505
|
}
|
|
344
|
-
.message-error strong { color:
|
|
506
|
+
.message-error strong { color: var(--error); }
|
|
345
507
|
|
|
346
508
|
/* Layout - use fixed positioning with explicit dimensions */
|
|
347
509
|
.shell {
|
|
@@ -373,8 +535,8 @@ var renderWebUiHtml = (options) => {
|
|
|
373
535
|
}
|
|
374
536
|
.sidebar {
|
|
375
537
|
width: 260px;
|
|
376
|
-
background:
|
|
377
|
-
border-right: 1px solid
|
|
538
|
+
background: var(--bg);
|
|
539
|
+
border-right: 1px solid var(--border-1);
|
|
378
540
|
display: flex;
|
|
379
541
|
flex-direction: column;
|
|
380
542
|
padding: 12px 8px;
|
|
@@ -382,7 +544,7 @@ var renderWebUiHtml = (options) => {
|
|
|
382
544
|
.new-chat-btn {
|
|
383
545
|
background: transparent;
|
|
384
546
|
border: 0;
|
|
385
|
-
color:
|
|
547
|
+
color: var(--fg-2);
|
|
386
548
|
border-radius: 12px;
|
|
387
549
|
height: 36px;
|
|
388
550
|
padding: 0 10px;
|
|
@@ -393,7 +555,7 @@ var renderWebUiHtml = (options) => {
|
|
|
393
555
|
cursor: pointer;
|
|
394
556
|
transition: background 0.15s, color 0.15s;
|
|
395
557
|
}
|
|
396
|
-
.new-chat-btn:hover { color:
|
|
558
|
+
.new-chat-btn:hover { color: var(--fg); }
|
|
397
559
|
.new-chat-btn svg { width: 16px; height: 16px; }
|
|
398
560
|
.conversation-list {
|
|
399
561
|
flex: 1;
|
|
@@ -403,6 +565,20 @@ var renderWebUiHtml = (options) => {
|
|
|
403
565
|
flex-direction: column;
|
|
404
566
|
gap: 2px;
|
|
405
567
|
}
|
|
568
|
+
.sidebar-section-label {
|
|
569
|
+
font-size: 11px;
|
|
570
|
+
font-weight: 600;
|
|
571
|
+
color: var(--fg-7);
|
|
572
|
+
text-transform: uppercase;
|
|
573
|
+
letter-spacing: 0.04em;
|
|
574
|
+
padding: 10px 10px 4px;
|
|
575
|
+
}
|
|
576
|
+
.sidebar-section-label:first-child { padding-top: 4px; }
|
|
577
|
+
.sidebar-section-divider {
|
|
578
|
+
height: 1px;
|
|
579
|
+
background: var(--border);
|
|
580
|
+
margin: 6px 10px;
|
|
581
|
+
}
|
|
406
582
|
.conversation-item {
|
|
407
583
|
height: 36px;
|
|
408
584
|
min-height: 36px;
|
|
@@ -413,16 +589,26 @@ var renderWebUiHtml = (options) => {
|
|
|
413
589
|
cursor: pointer;
|
|
414
590
|
font-size: 13px;
|
|
415
591
|
line-height: 36px;
|
|
416
|
-
color:
|
|
592
|
+
color: var(--fg-6);
|
|
417
593
|
white-space: nowrap;
|
|
418
594
|
overflow: hidden;
|
|
419
595
|
text-overflow: ellipsis;
|
|
420
596
|
position: relative;
|
|
421
597
|
transition: color 0.15s;
|
|
422
598
|
}
|
|
423
|
-
.conversation-item
|
|
599
|
+
.conversation-item .approval-dot {
|
|
600
|
+
display: inline-block;
|
|
601
|
+
width: 6px;
|
|
602
|
+
height: 6px;
|
|
603
|
+
border-radius: 50%;
|
|
604
|
+
background: var(--warning, #e8a735);
|
|
605
|
+
margin-right: 6px;
|
|
606
|
+
flex-shrink: 0;
|
|
607
|
+
vertical-align: middle;
|
|
608
|
+
}
|
|
609
|
+
.conversation-item:hover { color: var(--fg-3); }
|
|
424
610
|
.conversation-item.active {
|
|
425
|
-
color:
|
|
611
|
+
color: var(--fg);
|
|
426
612
|
}
|
|
427
613
|
.conversation-item .delete-btn {
|
|
428
614
|
position: absolute;
|
|
@@ -430,9 +616,9 @@ var renderWebUiHtml = (options) => {
|
|
|
430
616
|
top: 0;
|
|
431
617
|
bottom: 0;
|
|
432
618
|
opacity: 0;
|
|
433
|
-
background:
|
|
619
|
+
background: var(--bg);
|
|
434
620
|
border: 0;
|
|
435
|
-
color:
|
|
621
|
+
color: var(--fg-7);
|
|
436
622
|
padding: 0 8px;
|
|
437
623
|
border-radius: 0 4px 4px 0;
|
|
438
624
|
cursor: pointer;
|
|
@@ -443,7 +629,7 @@ var renderWebUiHtml = (options) => {
|
|
|
443
629
|
transition: opacity 0.15s, color 0.15s;
|
|
444
630
|
}
|
|
445
631
|
.conversation-item:hover .delete-btn { opacity: 1; }
|
|
446
|
-
.conversation-item.active .delete-btn { background:
|
|
632
|
+
.conversation-item.active .delete-btn { background: var(--bg); }
|
|
447
633
|
.conversation-item .delete-btn::before {
|
|
448
634
|
content: "";
|
|
449
635
|
position: absolute;
|
|
@@ -451,23 +637,23 @@ var renderWebUiHtml = (options) => {
|
|
|
451
637
|
top: 0;
|
|
452
638
|
bottom: 0;
|
|
453
639
|
width: 24px;
|
|
454
|
-
background: linear-gradient(to right, transparent,
|
|
640
|
+
background: linear-gradient(to right, transparent, var(--bg));
|
|
455
641
|
pointer-events: none;
|
|
456
642
|
}
|
|
457
643
|
.conversation-item.active .delete-btn::before {
|
|
458
|
-
background: linear-gradient(to right, transparent,
|
|
644
|
+
background: linear-gradient(to right, transparent, var(--bg));
|
|
459
645
|
}
|
|
460
|
-
.conversation-item .delete-btn:hover { color:
|
|
646
|
+
.conversation-item .delete-btn:hover { color: var(--fg-2); }
|
|
461
647
|
.conversation-item .delete-btn.confirming {
|
|
462
648
|
opacity: 1;
|
|
463
649
|
width: auto;
|
|
464
650
|
padding: 0 8px;
|
|
465
651
|
font-size: 11px;
|
|
466
|
-
color:
|
|
652
|
+
color: var(--error);
|
|
467
653
|
border-radius: 3px;
|
|
468
654
|
}
|
|
469
655
|
.conversation-item .delete-btn.confirming:hover {
|
|
470
|
-
color:
|
|
656
|
+
color: var(--error-alt);
|
|
471
657
|
}
|
|
472
658
|
.sidebar-footer {
|
|
473
659
|
margin-top: auto;
|
|
@@ -476,7 +662,7 @@ var renderWebUiHtml = (options) => {
|
|
|
476
662
|
.logout-btn {
|
|
477
663
|
background: transparent;
|
|
478
664
|
border: 0;
|
|
479
|
-
color:
|
|
665
|
+
color: var(--fg-6);
|
|
480
666
|
width: 100%;
|
|
481
667
|
padding: 8px 10px;
|
|
482
668
|
text-align: left;
|
|
@@ -485,10 +671,10 @@ var renderWebUiHtml = (options) => {
|
|
|
485
671
|
font-size: 13px;
|
|
486
672
|
transition: color 0.15s, background 0.15s;
|
|
487
673
|
}
|
|
488
|
-
.logout-btn:hover { color:
|
|
674
|
+
.logout-btn:hover { color: var(--fg-2); }
|
|
489
675
|
|
|
490
676
|
/* Main */
|
|
491
|
-
.main { flex: 1; display: flex; flex-direction: column; min-width: 0; max-width: 100%; background:
|
|
677
|
+
.main { flex: 1; display: flex; flex-direction: column; min-width: 0; max-width: 100%; background: var(--bg); overflow: hidden; }
|
|
492
678
|
.topbar {
|
|
493
679
|
height: calc(52px + env(safe-area-inset-top, 0px));
|
|
494
680
|
padding-top: env(safe-area-inset-top, 0px);
|
|
@@ -497,8 +683,8 @@ var renderWebUiHtml = (options) => {
|
|
|
497
683
|
justify-content: center;
|
|
498
684
|
font-size: 13px;
|
|
499
685
|
font-weight: 500;
|
|
500
|
-
color:
|
|
501
|
-
border-bottom: 1px solid
|
|
686
|
+
color: var(--fg-2);
|
|
687
|
+
border-bottom: 1px solid var(--border-1);
|
|
502
688
|
position: relative;
|
|
503
689
|
flex-shrink: 0;
|
|
504
690
|
}
|
|
@@ -517,7 +703,7 @@ var renderWebUiHtml = (options) => {
|
|
|
517
703
|
bottom: 4px; /* Position from bottom of topbar content area */
|
|
518
704
|
background: transparent;
|
|
519
705
|
border: 0;
|
|
520
|
-
color:
|
|
706
|
+
color: var(--fg-5);
|
|
521
707
|
width: 44px;
|
|
522
708
|
height: 44px;
|
|
523
709
|
border-radius: 6px;
|
|
@@ -527,7 +713,7 @@ var renderWebUiHtml = (options) => {
|
|
|
527
713
|
z-index: 10;
|
|
528
714
|
-webkit-tap-highlight-color: transparent;
|
|
529
715
|
}
|
|
530
|
-
.sidebar-toggle:hover { color:
|
|
716
|
+
.sidebar-toggle:hover { color: var(--fg); }
|
|
531
717
|
.topbar-new-chat {
|
|
532
718
|
display: none;
|
|
533
719
|
position: absolute;
|
|
@@ -535,7 +721,7 @@ var renderWebUiHtml = (options) => {
|
|
|
535
721
|
bottom: 4px;
|
|
536
722
|
background: transparent;
|
|
537
723
|
border: 0;
|
|
538
|
-
color:
|
|
724
|
+
color: var(--fg-5);
|
|
539
725
|
width: 44px;
|
|
540
726
|
height: 44px;
|
|
541
727
|
border-radius: 6px;
|
|
@@ -544,7 +730,7 @@ var renderWebUiHtml = (options) => {
|
|
|
544
730
|
z-index: 10;
|
|
545
731
|
-webkit-tap-highlight-color: transparent;
|
|
546
732
|
}
|
|
547
|
-
.topbar-new-chat:hover { color:
|
|
733
|
+
.topbar-new-chat:hover { color: var(--fg); }
|
|
548
734
|
.topbar-new-chat svg { width: 16px; height: 16px; }
|
|
549
735
|
|
|
550
736
|
/* Messages */
|
|
@@ -556,8 +742,8 @@ var renderWebUiHtml = (options) => {
|
|
|
556
742
|
.assistant-avatar {
|
|
557
743
|
width: 24px;
|
|
558
744
|
height: 24px;
|
|
559
|
-
background:
|
|
560
|
-
color:
|
|
745
|
+
background: var(--accent);
|
|
746
|
+
color: var(--accent-fg);
|
|
561
747
|
border-radius: 6px;
|
|
562
748
|
display: grid;
|
|
563
749
|
place-items: center;
|
|
@@ -568,7 +754,7 @@ var renderWebUiHtml = (options) => {
|
|
|
568
754
|
}
|
|
569
755
|
.assistant-content {
|
|
570
756
|
line-height: 1.65;
|
|
571
|
-
color:
|
|
757
|
+
color: var(--fg);
|
|
572
758
|
font-size: 14px;
|
|
573
759
|
min-width: 0;
|
|
574
760
|
max-width: 100%;
|
|
@@ -580,32 +766,32 @@ var renderWebUiHtml = (options) => {
|
|
|
580
766
|
.assistant-content p:last-child { margin-bottom: 0; }
|
|
581
767
|
.assistant-content ul, .assistant-content ol { margin: 8px 0; padding-left: 20px; }
|
|
582
768
|
.assistant-content li { margin: 4px 0; }
|
|
583
|
-
.assistant-content strong { font-weight: 600; color:
|
|
769
|
+
.assistant-content strong { font-weight: 600; color: var(--fg-strong); }
|
|
584
770
|
.assistant-content h2 {
|
|
585
771
|
font-size: 16px;
|
|
586
772
|
font-weight: 600;
|
|
587
773
|
letter-spacing: -0.02em;
|
|
588
774
|
margin: 20px 0 8px;
|
|
589
|
-
color:
|
|
775
|
+
color: var(--fg-strong);
|
|
590
776
|
}
|
|
591
777
|
.assistant-content h3 {
|
|
592
778
|
font-size: 14px;
|
|
593
779
|
font-weight: 600;
|
|
594
780
|
letter-spacing: -0.01em;
|
|
595
781
|
margin: 16px 0 6px;
|
|
596
|
-
color:
|
|
782
|
+
color: var(--fg-strong);
|
|
597
783
|
}
|
|
598
784
|
.assistant-content code {
|
|
599
|
-
background:
|
|
600
|
-
border: 1px solid
|
|
785
|
+
background: var(--surface-4);
|
|
786
|
+
border: 1px solid var(--border-1);
|
|
601
787
|
padding: 2px 5px;
|
|
602
788
|
border-radius: 4px;
|
|
603
789
|
font-family: ui-monospace, "SF Mono", "Fira Code", monospace;
|
|
604
790
|
font-size: 0.88em;
|
|
605
791
|
}
|
|
606
792
|
.assistant-content pre {
|
|
607
|
-
background:
|
|
608
|
-
border: 1px solid
|
|
793
|
+
background: var(--bg-alt);
|
|
794
|
+
border: 1px solid var(--border-1);
|
|
609
795
|
padding: 14px 16px;
|
|
610
796
|
border-radius: 8px;
|
|
611
797
|
overflow-x: auto;
|
|
@@ -622,33 +808,33 @@ var renderWebUiHtml = (options) => {
|
|
|
622
808
|
margin: 8px 0;
|
|
623
809
|
font-size: 12px;
|
|
624
810
|
line-height: 1.45;
|
|
625
|
-
color:
|
|
811
|
+
color: var(--fg-tool);
|
|
626
812
|
}
|
|
627
813
|
.tool-activity-inline code {
|
|
628
814
|
font-family: ui-monospace, "SF Mono", "Fira Code", monospace;
|
|
629
|
-
background:
|
|
630
|
-
border: 1px solid
|
|
815
|
+
background: var(--surface-3);
|
|
816
|
+
border: 1px solid var(--border-2);
|
|
631
817
|
padding: 4px 8px;
|
|
632
818
|
border-radius: 6px;
|
|
633
|
-
color:
|
|
819
|
+
color: var(--fg-tool-code);
|
|
634
820
|
font-size: 11px;
|
|
635
821
|
}
|
|
636
822
|
.tool-status {
|
|
637
|
-
color:
|
|
823
|
+
color: var(--fg-tool);
|
|
638
824
|
font-style: italic;
|
|
639
825
|
}
|
|
640
826
|
.tool-done {
|
|
641
|
-
color:
|
|
827
|
+
color: var(--tool-done);
|
|
642
828
|
}
|
|
643
829
|
.tool-error {
|
|
644
|
-
color:
|
|
830
|
+
color: var(--tool-error);
|
|
645
831
|
}
|
|
646
|
-
.assistant-content table {
|
|
832
|
+
.assistant-content table:not(.approval-request-table) {
|
|
647
833
|
border-collapse: collapse;
|
|
648
834
|
width: 100%;
|
|
649
835
|
margin: 14px 0;
|
|
650
836
|
font-size: 13px;
|
|
651
|
-
border: 1px solid
|
|
837
|
+
border: 1px solid var(--border-2);
|
|
652
838
|
border-radius: 8px;
|
|
653
839
|
overflow: hidden;
|
|
654
840
|
display: block;
|
|
@@ -656,42 +842,46 @@ var renderWebUiHtml = (options) => {
|
|
|
656
842
|
overflow-x: auto;
|
|
657
843
|
white-space: nowrap;
|
|
658
844
|
}
|
|
659
|
-
.assistant-content th {
|
|
660
|
-
background:
|
|
845
|
+
.assistant-content table:not(.approval-request-table) th {
|
|
846
|
+
background: var(--surface-4);
|
|
661
847
|
padding: 10px 12px;
|
|
662
848
|
text-align: left;
|
|
663
849
|
font-weight: 600;
|
|
664
|
-
border-bottom: 1px solid
|
|
665
|
-
color:
|
|
850
|
+
border-bottom: 1px solid var(--border-4);
|
|
851
|
+
color: var(--fg-strong);
|
|
666
852
|
min-width: 100px;
|
|
667
853
|
}
|
|
668
|
-
.assistant-content td {
|
|
854
|
+
.assistant-content table:not(.approval-request-table) td {
|
|
669
855
|
padding: 10px 12px;
|
|
670
|
-
border-bottom: 1px solid
|
|
856
|
+
border-bottom: 1px solid var(--border-1);
|
|
671
857
|
width: 100%;
|
|
672
858
|
min-width: 100px;
|
|
673
859
|
}
|
|
674
|
-
.assistant-content tr:last-child td {
|
|
860
|
+
.assistant-content table:not(.approval-request-table) tr:last-child td {
|
|
675
861
|
border-bottom: none;
|
|
676
862
|
}
|
|
677
|
-
.assistant-content tbody tr:hover {
|
|
678
|
-
background:
|
|
863
|
+
.assistant-content table:not(.approval-request-table) tbody tr:hover {
|
|
864
|
+
background: var(--surface-1);
|
|
679
865
|
}
|
|
680
866
|
.assistant-content hr {
|
|
681
867
|
border: 0;
|
|
682
|
-
border-top: 1px solid
|
|
868
|
+
border-top: 1px solid var(--border-3);
|
|
683
869
|
margin: 20px 0;
|
|
684
870
|
}
|
|
685
871
|
.tool-activity {
|
|
686
872
|
margin-top: 12px;
|
|
687
873
|
margin-bottom: 12px;
|
|
688
|
-
border: 1px solid
|
|
689
|
-
background:
|
|
874
|
+
border: 1px solid var(--border-2);
|
|
875
|
+
background: var(--surface-2);
|
|
690
876
|
border-radius: 10px;
|
|
691
877
|
font-size: 12px;
|
|
692
878
|
line-height: 1.45;
|
|
693
|
-
color:
|
|
879
|
+
color: var(--fg-tool-code);
|
|
694
880
|
width: 300px;
|
|
881
|
+
transition: width 0.2s ease;
|
|
882
|
+
}
|
|
883
|
+
.tool-activity.has-approvals {
|
|
884
|
+
width: 100%;
|
|
695
885
|
}
|
|
696
886
|
.assistant-content > .tool-activity:first-child {
|
|
697
887
|
margin-top: 0;
|
|
@@ -715,12 +905,12 @@ var renderWebUiHtml = (options) => {
|
|
|
715
905
|
font-size: 11px;
|
|
716
906
|
text-transform: uppercase;
|
|
717
907
|
letter-spacing: 0.06em;
|
|
718
|
-
color:
|
|
908
|
+
color: var(--fg-tool);
|
|
719
909
|
font-weight: 600;
|
|
720
910
|
}
|
|
721
911
|
.tool-activity-caret {
|
|
722
912
|
margin-left: auto;
|
|
723
|
-
color:
|
|
913
|
+
color: var(--fg-tool);
|
|
724
914
|
display: inline-flex;
|
|
725
915
|
align-items: center;
|
|
726
916
|
justify-content: center;
|
|
@@ -742,49 +932,69 @@ var renderWebUiHtml = (options) => {
|
|
|
742
932
|
}
|
|
743
933
|
.tool-activity-item {
|
|
744
934
|
font-family: ui-monospace, "SF Mono", "Fira Code", monospace;
|
|
745
|
-
background:
|
|
935
|
+
background: var(--surface-3);
|
|
746
936
|
border-radius: 6px;
|
|
747
937
|
padding: 4px 7px;
|
|
748
|
-
color:
|
|
938
|
+
color: var(--fg-tool-item);
|
|
749
939
|
}
|
|
750
940
|
.approval-requests {
|
|
751
|
-
border-top: 1px solid
|
|
941
|
+
border-top: 1px solid var(--border-2);
|
|
752
942
|
padding: 10px 12px 12px;
|
|
753
943
|
display: grid;
|
|
754
|
-
gap:
|
|
755
|
-
background: rgba(0,0,0,0.16);
|
|
944
|
+
gap: 10px;
|
|
756
945
|
}
|
|
757
946
|
.approval-requests-label {
|
|
758
|
-
font-size:
|
|
947
|
+
font-size: 12px;
|
|
759
948
|
text-transform: uppercase;
|
|
760
949
|
letter-spacing: 0.06em;
|
|
761
|
-
color:
|
|
950
|
+
color: var(--fg-approval-label);
|
|
762
951
|
font-weight: 600;
|
|
763
952
|
}
|
|
953
|
+
.approval-requests-label code {
|
|
954
|
+
font-family: ui-monospace, "SF Mono", "Fira Code", monospace;
|
|
955
|
+
text-transform: none;
|
|
956
|
+
letter-spacing: 0;
|
|
957
|
+
color: var(--fg-strong);
|
|
958
|
+
}
|
|
764
959
|
.approval-request-item {
|
|
765
|
-
border: 1px solid rgba(255,255,255,0.1);
|
|
766
|
-
background: rgba(255,255,255,0.03);
|
|
767
|
-
border-radius: 8px;
|
|
768
|
-
padding: 8px;
|
|
769
960
|
display: grid;
|
|
770
|
-
gap:
|
|
961
|
+
gap: 8px;
|
|
771
962
|
}
|
|
772
|
-
.approval-request-
|
|
773
|
-
|
|
774
|
-
|
|
963
|
+
.approval-request-table {
|
|
964
|
+
width: 100%;
|
|
965
|
+
border-collapse: collapse;
|
|
966
|
+
border: none;
|
|
967
|
+
font-size: 14px;
|
|
968
|
+
line-height: 1.5;
|
|
969
|
+
}
|
|
970
|
+
.approval-request-table tr,
|
|
971
|
+
.approval-request-table td {
|
|
972
|
+
border: none;
|
|
973
|
+
background: none;
|
|
974
|
+
}
|
|
975
|
+
.approval-request-table td {
|
|
976
|
+
padding: 4px 0;
|
|
977
|
+
vertical-align: top;
|
|
978
|
+
}
|
|
979
|
+
.approval-request-table .ak {
|
|
775
980
|
font-weight: 600;
|
|
776
|
-
|
|
981
|
+
color: var(--fg-approval-label);
|
|
982
|
+
white-space: nowrap;
|
|
983
|
+
width: 1%;
|
|
984
|
+
padding-right: 20px;
|
|
777
985
|
}
|
|
778
|
-
.approval-request-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
color: #cfcfcf;
|
|
782
|
-
background: rgba(0,0,0,0.25);
|
|
783
|
-
border-radius: 6px;
|
|
784
|
-
padding: 6px;
|
|
986
|
+
.approval-request-table .av,
|
|
987
|
+
.approval-request-table .av-complex {
|
|
988
|
+
color: var(--fg);
|
|
785
989
|
overflow-wrap: anywhere;
|
|
786
|
-
|
|
990
|
+
white-space: pre-wrap;
|
|
991
|
+
max-height: 200px;
|
|
787
992
|
overflow-y: auto;
|
|
993
|
+
display: block;
|
|
994
|
+
}
|
|
995
|
+
.approval-request-table .av-complex {
|
|
996
|
+
font-family: ui-monospace, "SF Mono", "Fira Code", monospace;
|
|
997
|
+
font-size: 12px;
|
|
788
998
|
}
|
|
789
999
|
.approval-request-actions {
|
|
790
1000
|
display: flex;
|
|
@@ -792,32 +1002,32 @@ var renderWebUiHtml = (options) => {
|
|
|
792
1002
|
}
|
|
793
1003
|
.approval-action-btn {
|
|
794
1004
|
border-radius: 6px;
|
|
795
|
-
border: 1px solid
|
|
796
|
-
background:
|
|
797
|
-
color:
|
|
1005
|
+
border: 1px solid var(--border-5);
|
|
1006
|
+
background: var(--surface-4);
|
|
1007
|
+
color: var(--fg-approval-btn);
|
|
798
1008
|
font-size: 11px;
|
|
799
1009
|
font-weight: 600;
|
|
800
1010
|
padding: 4px 8px;
|
|
801
1011
|
cursor: pointer;
|
|
802
1012
|
}
|
|
803
1013
|
.approval-action-btn:hover {
|
|
804
|
-
background:
|
|
1014
|
+
background: var(--surface-7);
|
|
805
1015
|
}
|
|
806
1016
|
.approval-action-btn.approve {
|
|
807
|
-
border-color:
|
|
808
|
-
color:
|
|
1017
|
+
border-color: var(--approve-border);
|
|
1018
|
+
color: var(--approve);
|
|
809
1019
|
}
|
|
810
1020
|
.approval-action-btn.deny {
|
|
811
|
-
border-color:
|
|
812
|
-
color:
|
|
1021
|
+
border-color: var(--deny-border);
|
|
1022
|
+
color: var(--deny);
|
|
813
1023
|
}
|
|
814
1024
|
.approval-action-btn[disabled] {
|
|
815
1025
|
opacity: 0.55;
|
|
816
1026
|
cursor: not-allowed;
|
|
817
1027
|
}
|
|
818
1028
|
.user-bubble {
|
|
819
|
-
background:
|
|
820
|
-
border: 1px solid
|
|
1029
|
+
background: var(--bg-elevated);
|
|
1030
|
+
border: 1px solid var(--border-2);
|
|
821
1031
|
padding: 10px 16px;
|
|
822
1032
|
border-radius: 18px;
|
|
823
1033
|
max-width: 70%;
|
|
@@ -825,6 +1035,7 @@ var renderWebUiHtml = (options) => {
|
|
|
825
1035
|
line-height: 1.5;
|
|
826
1036
|
overflow-wrap: break-word;
|
|
827
1037
|
word-break: break-word;
|
|
1038
|
+
white-space: pre-wrap;
|
|
828
1039
|
}
|
|
829
1040
|
.empty-state {
|
|
830
1041
|
display: flex;
|
|
@@ -833,7 +1044,7 @@ var renderWebUiHtml = (options) => {
|
|
|
833
1044
|
justify-content: center;
|
|
834
1045
|
height: 100%;
|
|
835
1046
|
gap: 16px;
|
|
836
|
-
color:
|
|
1047
|
+
color: var(--fg-6);
|
|
837
1048
|
}
|
|
838
1049
|
.empty-state .assistant-avatar {
|
|
839
1050
|
width: 36px;
|
|
@@ -843,7 +1054,7 @@ var renderWebUiHtml = (options) => {
|
|
|
843
1054
|
}
|
|
844
1055
|
.empty-state-text {
|
|
845
1056
|
font-size: 14px;
|
|
846
|
-
color:
|
|
1057
|
+
color: var(--fg-6);
|
|
847
1058
|
}
|
|
848
1059
|
.thinking-indicator {
|
|
849
1060
|
display: inline-block;
|
|
@@ -851,7 +1062,7 @@ var renderWebUiHtml = (options) => {
|
|
|
851
1062
|
font-size: 20px;
|
|
852
1063
|
line-height: 1;
|
|
853
1064
|
vertical-align: middle;
|
|
854
|
-
color:
|
|
1065
|
+
color: var(--fg);
|
|
855
1066
|
opacity: 0.5;
|
|
856
1067
|
}
|
|
857
1068
|
.thinking-status {
|
|
@@ -859,13 +1070,13 @@ var renderWebUiHtml = (options) => {
|
|
|
859
1070
|
align-items: center;
|
|
860
1071
|
gap: 8px;
|
|
861
1072
|
margin-top: 2px;
|
|
862
|
-
color:
|
|
1073
|
+
color: var(--fg-tool);
|
|
863
1074
|
font-size: 14px;
|
|
864
1075
|
line-height: 1.65;
|
|
865
1076
|
font-weight: 400;
|
|
866
1077
|
}
|
|
867
1078
|
.thinking-status-label {
|
|
868
|
-
color:
|
|
1079
|
+
color: var(--fg-tool);
|
|
869
1080
|
font-size: 14px;
|
|
870
1081
|
line-height: 1.65;
|
|
871
1082
|
font-weight: 400;
|
|
@@ -896,26 +1107,26 @@ var renderWebUiHtml = (options) => {
|
|
|
896
1107
|
right: 0;
|
|
897
1108
|
bottom: 100%;
|
|
898
1109
|
height: 48px;
|
|
899
|
-
background: linear-gradient(to top,
|
|
1110
|
+
background: linear-gradient(to top, var(--bg) 0%, transparent 100%);
|
|
900
1111
|
pointer-events: none;
|
|
901
1112
|
}
|
|
902
1113
|
.composer-inner { max-width: 680px; margin: 0 auto; }
|
|
903
1114
|
.composer-shell {
|
|
904
|
-
background:
|
|
905
|
-
border: 1px solid
|
|
1115
|
+
background: var(--bg-alt);
|
|
1116
|
+
border: 1px solid var(--border-3);
|
|
906
1117
|
border-radius: 24px;
|
|
907
1118
|
display: flex;
|
|
908
1119
|
align-items: end;
|
|
909
1120
|
padding: 4px 6px 4px 6px;
|
|
910
1121
|
transition: border-color 0.15s;
|
|
911
1122
|
}
|
|
912
|
-
.composer-shell:focus-within { border-color:
|
|
1123
|
+
.composer-shell:focus-within { border-color: var(--border-focus); }
|
|
913
1124
|
.composer-input {
|
|
914
1125
|
flex: 1;
|
|
915
1126
|
background: transparent;
|
|
916
1127
|
border: 0;
|
|
917
1128
|
outline: none;
|
|
918
|
-
color:
|
|
1129
|
+
color: var(--fg);
|
|
919
1130
|
min-height: 40px;
|
|
920
1131
|
max-height: 200px;
|
|
921
1132
|
resize: none;
|
|
@@ -924,14 +1135,14 @@ var renderWebUiHtml = (options) => {
|
|
|
924
1135
|
line-height: 1.5;
|
|
925
1136
|
margin-top: -4px;
|
|
926
1137
|
}
|
|
927
|
-
.composer-input::placeholder { color:
|
|
1138
|
+
.composer-input::placeholder { color: var(--fg-7); }
|
|
928
1139
|
.send-btn {
|
|
929
1140
|
width: 32px;
|
|
930
1141
|
height: 32px;
|
|
931
|
-
background:
|
|
1142
|
+
background: var(--accent);
|
|
932
1143
|
border: 0;
|
|
933
1144
|
border-radius: 50%;
|
|
934
|
-
color:
|
|
1145
|
+
color: var(--accent-fg);
|
|
935
1146
|
cursor: pointer;
|
|
936
1147
|
display: grid;
|
|
937
1148
|
place-items: center;
|
|
@@ -939,21 +1150,79 @@ var renderWebUiHtml = (options) => {
|
|
|
939
1150
|
margin-bottom: 2px;
|
|
940
1151
|
transition: background 0.15s, opacity 0.15s;
|
|
941
1152
|
}
|
|
942
|
-
.send-btn:hover { background:
|
|
1153
|
+
.send-btn:hover { background: var(--accent-hover); }
|
|
943
1154
|
.send-btn.stop-mode {
|
|
944
|
-
background:
|
|
945
|
-
color:
|
|
1155
|
+
background: var(--stop-bg);
|
|
1156
|
+
color: var(--stop-fg);
|
|
946
1157
|
}
|
|
947
|
-
.send-btn.stop-mode:hover { background:
|
|
1158
|
+
.send-btn.stop-mode:hover { background: var(--stop-hover); }
|
|
948
1159
|
.send-btn:disabled { opacity: 0.2; cursor: default; }
|
|
949
|
-
.send-btn:disabled:hover { background:
|
|
1160
|
+
.send-btn:disabled:hover { background: var(--accent); }
|
|
1161
|
+
.send-btn-wrapper {
|
|
1162
|
+
position: relative;
|
|
1163
|
+
width: 36px;
|
|
1164
|
+
height: 36px;
|
|
1165
|
+
display: grid;
|
|
1166
|
+
place-items: center;
|
|
1167
|
+
flex-shrink: 0;
|
|
1168
|
+
margin-bottom: 0;
|
|
1169
|
+
}
|
|
1170
|
+
.send-btn-wrapper .send-btn {
|
|
1171
|
+
margin-bottom: 0;
|
|
1172
|
+
}
|
|
1173
|
+
.context-ring {
|
|
1174
|
+
position: absolute;
|
|
1175
|
+
inset: 0;
|
|
1176
|
+
width: 36px;
|
|
1177
|
+
height: 36px;
|
|
1178
|
+
pointer-events: none;
|
|
1179
|
+
transform: rotate(-90deg);
|
|
1180
|
+
}
|
|
1181
|
+
.context-ring-fill {
|
|
1182
|
+
fill: none;
|
|
1183
|
+
stroke: var(--bg-alt);
|
|
1184
|
+
stroke-width: 3;
|
|
1185
|
+
stroke-linecap: butt;
|
|
1186
|
+
transition: stroke-dashoffset 0.4s ease, stroke 0.3s ease;
|
|
1187
|
+
}
|
|
1188
|
+
.send-btn-wrapper.stop-mode .context-ring-fill {
|
|
1189
|
+
stroke: var(--fg-3);
|
|
1190
|
+
}
|
|
1191
|
+
.context-ring-fill.warning {
|
|
1192
|
+
stroke: #e5a33d;
|
|
1193
|
+
}
|
|
1194
|
+
.context-ring-fill.critical {
|
|
1195
|
+
stroke: #e55d4a;
|
|
1196
|
+
}
|
|
1197
|
+
.context-tooltip {
|
|
1198
|
+
position: absolute;
|
|
1199
|
+
bottom: calc(100% + 8px);
|
|
1200
|
+
right: 0;
|
|
1201
|
+
background: var(--bg-elevated);
|
|
1202
|
+
border: 1px solid var(--border-3);
|
|
1203
|
+
border-radius: 8px;
|
|
1204
|
+
padding: 6px 10px;
|
|
1205
|
+
font-size: 12px;
|
|
1206
|
+
color: var(--fg-2);
|
|
1207
|
+
white-space: nowrap;
|
|
1208
|
+
pointer-events: none;
|
|
1209
|
+
opacity: 0;
|
|
1210
|
+
transform: translateY(4px);
|
|
1211
|
+
transition: opacity 0.15s, transform 0.15s;
|
|
1212
|
+
z-index: 10;
|
|
1213
|
+
}
|
|
1214
|
+
.send-btn-wrapper:hover .context-tooltip,
|
|
1215
|
+
.send-btn-wrapper:focus-within .context-tooltip {
|
|
1216
|
+
opacity: 1;
|
|
1217
|
+
transform: translateY(0);
|
|
1218
|
+
}
|
|
950
1219
|
.attach-btn {
|
|
951
1220
|
width: 32px;
|
|
952
1221
|
height: 32px;
|
|
953
|
-
background:
|
|
1222
|
+
background: var(--surface-5);
|
|
954
1223
|
border: 0;
|
|
955
1224
|
border-radius: 50%;
|
|
956
|
-
color:
|
|
1225
|
+
color: var(--fg-3);
|
|
957
1226
|
cursor: pointer;
|
|
958
1227
|
display: grid;
|
|
959
1228
|
place-items: center;
|
|
@@ -962,7 +1231,7 @@ var renderWebUiHtml = (options) => {
|
|
|
962
1231
|
margin-right: 8px;
|
|
963
1232
|
transition: color 0.15s, background 0.15s;
|
|
964
1233
|
}
|
|
965
|
-
.attach-btn:hover { color:
|
|
1234
|
+
.attach-btn:hover { color: var(--fg); background: var(--surface-8); }
|
|
966
1235
|
.attachment-preview {
|
|
967
1236
|
display: flex;
|
|
968
1237
|
gap: 8px;
|
|
@@ -973,12 +1242,12 @@ var renderWebUiHtml = (options) => {
|
|
|
973
1242
|
display: inline-flex;
|
|
974
1243
|
align-items: center;
|
|
975
1244
|
gap: 6px;
|
|
976
|
-
background:
|
|
977
|
-
border: 1px solid
|
|
1245
|
+
background: var(--chip-bg);
|
|
1246
|
+
border: 1px solid var(--border-4);
|
|
978
1247
|
border-radius: 9999px;
|
|
979
1248
|
padding: 4px 10px 4px 6px;
|
|
980
1249
|
font-size: 11px;
|
|
981
|
-
color:
|
|
1250
|
+
color: var(--fg-4);
|
|
982
1251
|
max-width: 200px;
|
|
983
1252
|
cursor: pointer;
|
|
984
1253
|
backdrop-filter: blur(6px);
|
|
@@ -986,9 +1255,9 @@ var renderWebUiHtml = (options) => {
|
|
|
986
1255
|
transition: color 0.15s, border-color 0.15s, background 0.15s;
|
|
987
1256
|
}
|
|
988
1257
|
.attachment-chip:hover {
|
|
989
|
-
color:
|
|
990
|
-
border-color:
|
|
991
|
-
background:
|
|
1258
|
+
color: var(--fg);
|
|
1259
|
+
border-color: var(--border-hover);
|
|
1260
|
+
background: var(--chip-bg-hover);
|
|
992
1261
|
}
|
|
993
1262
|
.attachment-chip img {
|
|
994
1263
|
width: 20px;
|
|
@@ -1002,7 +1271,7 @@ var renderWebUiHtml = (options) => {
|
|
|
1002
1271
|
width: 20px;
|
|
1003
1272
|
height: 20px;
|
|
1004
1273
|
border-radius: 50%;
|
|
1005
|
-
background:
|
|
1274
|
+
background: var(--surface-6);
|
|
1006
1275
|
display: grid;
|
|
1007
1276
|
place-items: center;
|
|
1008
1277
|
font-size: 11px;
|
|
@@ -1010,13 +1279,13 @@ var renderWebUiHtml = (options) => {
|
|
|
1010
1279
|
}
|
|
1011
1280
|
.attachment-chip .remove-attachment {
|
|
1012
1281
|
cursor: pointer;
|
|
1013
|
-
color:
|
|
1282
|
+
color: var(--fg-6);
|
|
1014
1283
|
font-size: 14px;
|
|
1015
1284
|
margin-left: 2px;
|
|
1016
1285
|
line-height: 1;
|
|
1017
1286
|
transition: color 0.15s;
|
|
1018
1287
|
}
|
|
1019
|
-
.attachment-chip .remove-attachment:hover { color:
|
|
1288
|
+
.attachment-chip .remove-attachment:hover { color: var(--fg-strong); }
|
|
1020
1289
|
.attachment-chip .filename { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 100px; }
|
|
1021
1290
|
.user-bubble .user-file-attachments {
|
|
1022
1291
|
display: flex;
|
|
@@ -1046,7 +1315,7 @@ var renderWebUiHtml = (options) => {
|
|
|
1046
1315
|
transition: background 0.25s ease, backdrop-filter 0.25s ease;
|
|
1047
1316
|
}
|
|
1048
1317
|
.lightbox.active {
|
|
1049
|
-
background:
|
|
1318
|
+
background: var(--lightbox-bg);
|
|
1050
1319
|
backdrop-filter: blur(8px);
|
|
1051
1320
|
}
|
|
1052
1321
|
.lightbox img {
|
|
@@ -1066,16 +1335,16 @@ var renderWebUiHtml = (options) => {
|
|
|
1066
1335
|
display: inline-flex;
|
|
1067
1336
|
align-items: center;
|
|
1068
1337
|
gap: 4px;
|
|
1069
|
-
background:
|
|
1338
|
+
background: var(--file-badge-bg);
|
|
1070
1339
|
border-radius: 6px;
|
|
1071
1340
|
padding: 4px 8px;
|
|
1072
1341
|
font-size: 12px;
|
|
1073
|
-
color:
|
|
1342
|
+
color: var(--file-badge-fg);
|
|
1074
1343
|
}
|
|
1075
1344
|
.drag-overlay {
|
|
1076
1345
|
position: fixed;
|
|
1077
1346
|
inset: 0;
|
|
1078
|
-
background:
|
|
1347
|
+
background: var(--backdrop);
|
|
1079
1348
|
z-index: 9999;
|
|
1080
1349
|
display: none;
|
|
1081
1350
|
align-items: center;
|
|
@@ -1084,15 +1353,15 @@ var renderWebUiHtml = (options) => {
|
|
|
1084
1353
|
}
|
|
1085
1354
|
.drag-overlay.active { display: flex; }
|
|
1086
1355
|
.drag-overlay-inner {
|
|
1087
|
-
border: 2px dashed
|
|
1356
|
+
border: 2px dashed var(--border-drag);
|
|
1088
1357
|
border-radius: 16px;
|
|
1089
1358
|
padding: 40px 60px;
|
|
1090
|
-
color:
|
|
1359
|
+
color: var(--fg-strong);
|
|
1091
1360
|
font-size: 16px;
|
|
1092
1361
|
}
|
|
1093
1362
|
.disclaimer {
|
|
1094
1363
|
text-align: center;
|
|
1095
|
-
color:
|
|
1364
|
+
color: var(--fg-8);
|
|
1096
1365
|
font-size: 12px;
|
|
1097
1366
|
margin-top: 10px;
|
|
1098
1367
|
}
|
|
@@ -1107,10 +1376,10 @@ var renderWebUiHtml = (options) => {
|
|
|
1107
1376
|
align-items: center;
|
|
1108
1377
|
gap: 6px;
|
|
1109
1378
|
font-size: 11px;
|
|
1110
|
-
color:
|
|
1379
|
+
color: var(--fg-4);
|
|
1111
1380
|
text-decoration: none;
|
|
1112
|
-
background:
|
|
1113
|
-
border: 1px solid
|
|
1381
|
+
background: var(--chip-bg);
|
|
1382
|
+
border: 1px solid var(--border-4);
|
|
1114
1383
|
border-radius: 9999px;
|
|
1115
1384
|
padding: 4px 10px 4px 6px;
|
|
1116
1385
|
backdrop-filter: blur(6px);
|
|
@@ -1118,9 +1387,9 @@ var renderWebUiHtml = (options) => {
|
|
|
1118
1387
|
transition: color 0.15s, border-color 0.15s, background 0.15s;
|
|
1119
1388
|
}
|
|
1120
1389
|
.poncho-badge:hover {
|
|
1121
|
-
color:
|
|
1122
|
-
border-color:
|
|
1123
|
-
background:
|
|
1390
|
+
color: var(--fg);
|
|
1391
|
+
border-color: var(--border-hover);
|
|
1392
|
+
background: var(--chip-bg-hover);
|
|
1124
1393
|
}
|
|
1125
1394
|
.poncho-badge-avatar {
|
|
1126
1395
|
width: 16px;
|
|
@@ -1134,8 +1403,8 @@ var renderWebUiHtml = (options) => {
|
|
|
1134
1403
|
/* Scrollbar */
|
|
1135
1404
|
::-webkit-scrollbar { width: 6px; }
|
|
1136
1405
|
::-webkit-scrollbar-track { background: transparent; }
|
|
1137
|
-
::-webkit-scrollbar-thumb { background:
|
|
1138
|
-
::-webkit-scrollbar-thumb:hover { background:
|
|
1406
|
+
::-webkit-scrollbar-thumb { background: var(--scrollbar); border-radius: 3px; }
|
|
1407
|
+
::-webkit-scrollbar-thumb:hover { background: var(--scrollbar-hover); }
|
|
1139
1408
|
|
|
1140
1409
|
/* Mobile */
|
|
1141
1410
|
@media (max-width: 768px) {
|
|
@@ -1165,7 +1434,7 @@ var renderWebUiHtml = (options) => {
|
|
|
1165
1434
|
.sidebar-backdrop {
|
|
1166
1435
|
position: fixed;
|
|
1167
1436
|
inset: 0;
|
|
1168
|
-
background:
|
|
1437
|
+
background: var(--backdrop);
|
|
1169
1438
|
z-index: 50;
|
|
1170
1439
|
backdrop-filter: blur(2px);
|
|
1171
1440
|
-webkit-backdrop-filter: blur(2px);
|
|
@@ -1240,9 +1509,15 @@ var renderWebUiHtml = (options) => {
|
|
|
1240
1509
|
</button>
|
|
1241
1510
|
<input id="file-input" type="file" multiple accept="image/*,video/*,application/pdf,.txt,.csv,.json,.html" style="display:none" />
|
|
1242
1511
|
<textarea id="prompt" class="composer-input" placeholder="Send a message..." rows="1"></textarea>
|
|
1243
|
-
<
|
|
1244
|
-
<svg
|
|
1245
|
-
|
|
1512
|
+
<div class="send-btn-wrapper" id="send-btn-wrapper">
|
|
1513
|
+
<svg class="context-ring" viewBox="0 0 36 36">
|
|
1514
|
+
<circle class="context-ring-fill" id="context-ring-fill" cx="18" cy="18" r="14.5" />
|
|
1515
|
+
</svg>
|
|
1516
|
+
<button id="send" class="send-btn" type="submit">
|
|
1517
|
+
<svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M8 12V4M4 7l4-4 4 4" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
|
1518
|
+
</button>
|
|
1519
|
+
<div class="context-tooltip" id="context-tooltip"></div>
|
|
1520
|
+
</div>
|
|
1246
1521
|
</div>
|
|
1247
1522
|
</div>
|
|
1248
1523
|
</form>
|
|
@@ -1274,6 +1549,8 @@ var renderWebUiHtml = (options) => {
|
|
|
1274
1549
|
confirmDeleteId: null,
|
|
1275
1550
|
approvalRequestsInFlight: {},
|
|
1276
1551
|
pendingFiles: [],
|
|
1552
|
+
contextTokens: 0,
|
|
1553
|
+
contextWindow: 0,
|
|
1277
1554
|
};
|
|
1278
1555
|
|
|
1279
1556
|
const agentInitial = document.body.dataset.agentInitial || "A";
|
|
@@ -1301,12 +1578,41 @@ var renderWebUiHtml = (options) => {
|
|
|
1301
1578
|
attachmentPreview: $("attachment-preview"),
|
|
1302
1579
|
dragOverlay: $("drag-overlay"),
|
|
1303
1580
|
lightbox: $("lightbox"),
|
|
1581
|
+
contextRingFill: $("context-ring-fill"),
|
|
1582
|
+
contextTooltip: $("context-tooltip"),
|
|
1583
|
+
sendBtnWrapper: $("send-btn-wrapper"),
|
|
1304
1584
|
};
|
|
1305
1585
|
const sendIconMarkup =
|
|
1306
1586
|
'<svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M8 12V4M4 7l4-4 4 4" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>';
|
|
1307
1587
|
const stopIconMarkup =
|
|
1308
1588
|
'<svg width="16" height="16" viewBox="0 0 16 16" fill="none"><rect x="4" y="4" width="8" height="8" rx="2" fill="currentColor"/></svg>';
|
|
1309
1589
|
|
|
1590
|
+
const CONTEXT_RING_CIRCUMFERENCE = 2 * Math.PI * 14.5;
|
|
1591
|
+
const formatTokenCount = (n) => {
|
|
1592
|
+
if (n >= 1_000_000) return (n / 1_000_000).toFixed(1).replace(/\\.0$/, "") + "M";
|
|
1593
|
+
if (n >= 1_000) return (n / 1_000).toFixed(1).replace(/\\.0$/, "") + "k";
|
|
1594
|
+
return String(n);
|
|
1595
|
+
};
|
|
1596
|
+
const updateContextRing = () => {
|
|
1597
|
+
const ring = elements.contextRingFill;
|
|
1598
|
+
const tooltip = elements.contextTooltip;
|
|
1599
|
+
if (!ring || !tooltip) return;
|
|
1600
|
+
if (state.contextWindow <= 0) {
|
|
1601
|
+
ring.style.strokeDasharray = String(CONTEXT_RING_CIRCUMFERENCE);
|
|
1602
|
+
ring.style.strokeDashoffset = String(CONTEXT_RING_CIRCUMFERENCE);
|
|
1603
|
+
tooltip.textContent = "";
|
|
1604
|
+
return;
|
|
1605
|
+
}
|
|
1606
|
+
const ratio = Math.min(state.contextTokens / state.contextWindow, 1);
|
|
1607
|
+
const offset = CONTEXT_RING_CIRCUMFERENCE * (1 - ratio);
|
|
1608
|
+
ring.style.strokeDasharray = String(CONTEXT_RING_CIRCUMFERENCE);
|
|
1609
|
+
ring.style.strokeDashoffset = String(offset);
|
|
1610
|
+
ring.classList.toggle("warning", ratio >= 0.7 && ratio < 0.9);
|
|
1611
|
+
ring.classList.toggle("critical", ratio >= 0.9);
|
|
1612
|
+
const pct = (ratio * 100).toFixed(1).replace(/\\.0$/, "");
|
|
1613
|
+
tooltip.textContent = formatTokenCount(state.contextTokens) + " / " + formatTokenCount(state.contextWindow) + " tokens (" + pct + "%)";
|
|
1614
|
+
};
|
|
1615
|
+
|
|
1310
1616
|
const pushConversationUrl = (conversationId) => {
|
|
1311
1617
|
const target = conversationId ? "/c/" + encodeURIComponent(conversationId) : "/";
|
|
1312
1618
|
if (window.location.pathname !== target) {
|
|
@@ -1403,18 +1709,20 @@ var renderWebUiHtml = (options) => {
|
|
|
1403
1709
|
.map((req) => {
|
|
1404
1710
|
const approvalId = typeof req.approvalId === "string" ? req.approvalId : "";
|
|
1405
1711
|
const tool = typeof req.tool === "string" ? req.tool : "tool";
|
|
1406
|
-
const
|
|
1712
|
+
const input = req.input != null ? req.input : {};
|
|
1407
1713
|
const submitting = req.state === "submitting";
|
|
1408
1714
|
const approveLabel = submitting && req.pendingDecision === "approve" ? "Approving..." : "Approve";
|
|
1409
1715
|
const denyLabel = submitting && req.pendingDecision === "deny" ? "Denying..." : "Deny";
|
|
1716
|
+
const errorHtml = req._error
|
|
1717
|
+
? '<div style="color: var(--deny); font-size: 11px; margin-top: 4px;">Submit failed: ' + escapeHtml(req._error) + "</div>"
|
|
1718
|
+
: "";
|
|
1410
1719
|
return (
|
|
1411
1720
|
'<div class="approval-request-item">' +
|
|
1412
|
-
'<div class="approval-
|
|
1721
|
+
'<div class="approval-requests-label">Approval required: <code>' +
|
|
1413
1722
|
escapeHtml(tool) +
|
|
1414
|
-
"</div>" +
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
"</div>" +
|
|
1723
|
+
"</code></div>" +
|
|
1724
|
+
renderInputTable(input) +
|
|
1725
|
+
errorHtml +
|
|
1418
1726
|
'<div class="approval-request-actions">' +
|
|
1419
1727
|
'<button class="approval-action-btn approve" data-approval-id="' +
|
|
1420
1728
|
escapeHtml(approvalId) +
|
|
@@ -1437,7 +1745,6 @@ var renderWebUiHtml = (options) => {
|
|
|
1437
1745
|
.join("");
|
|
1438
1746
|
return (
|
|
1439
1747
|
'<div class="approval-requests">' +
|
|
1440
|
-
'<div class="approval-requests-label">Approval required</div>' +
|
|
1441
1748
|
rows +
|
|
1442
1749
|
"</div>"
|
|
1443
1750
|
);
|
|
@@ -1467,22 +1774,46 @@ var renderWebUiHtml = (options) => {
|
|
|
1467
1774
|
"</details>"
|
|
1468
1775
|
)
|
|
1469
1776
|
: "";
|
|
1777
|
+
const cls = "tool-activity" + (hasApprovals ? " has-approvals" : "");
|
|
1470
1778
|
return (
|
|
1471
|
-
'<div class="
|
|
1779
|
+
'<div class="' + cls + '">' +
|
|
1472
1780
|
disclosure +
|
|
1473
1781
|
renderApprovalRequests(approvalRequests) +
|
|
1474
1782
|
"</div>"
|
|
1475
1783
|
);
|
|
1476
1784
|
};
|
|
1477
1785
|
|
|
1478
|
-
const
|
|
1479
|
-
|
|
1480
|
-
return
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
return
|
|
1786
|
+
const renderInputTable = (input) => {
|
|
1787
|
+
if (!input || typeof input !== "object") {
|
|
1788
|
+
return '<div class="av-complex">' + escapeHtml(String(input ?? "{}")) + "</div>";
|
|
1789
|
+
}
|
|
1790
|
+
const keys = Object.keys(input);
|
|
1791
|
+
if (keys.length === 0) {
|
|
1792
|
+
return '<div class="av-complex">{}</div>';
|
|
1485
1793
|
}
|
|
1794
|
+
const formatValue = (val) => {
|
|
1795
|
+
if (val === null || val === undefined) return escapeHtml("null");
|
|
1796
|
+
if (typeof val === "boolean" || typeof val === "number") return escapeHtml(String(val));
|
|
1797
|
+
if (typeof val === "string") return escapeHtml(val);
|
|
1798
|
+
try {
|
|
1799
|
+
const replacer = (_, v) => typeof v === "bigint" ? String(v) : v;
|
|
1800
|
+
return escapeHtml(JSON.stringify(val, replacer, 2));
|
|
1801
|
+
} catch {
|
|
1802
|
+
return escapeHtml("[unserializable]");
|
|
1803
|
+
}
|
|
1804
|
+
};
|
|
1805
|
+
const rows = keys.map((key) => {
|
|
1806
|
+
const val = input[key];
|
|
1807
|
+
const isComplex = val !== null && typeof val === "object";
|
|
1808
|
+
const cls = isComplex ? "av-complex" : "av";
|
|
1809
|
+
return (
|
|
1810
|
+
"<tr>" +
|
|
1811
|
+
'<td class="ak">' + escapeHtml(key) + "</td>" +
|
|
1812
|
+
'<td><div class="' + cls + '">' + formatValue(val) + "</div></td>" +
|
|
1813
|
+
"</tr>"
|
|
1814
|
+
);
|
|
1815
|
+
}).join("");
|
|
1816
|
+
return '<table class="approval-request-table">' + rows + "</table>";
|
|
1486
1817
|
};
|
|
1487
1818
|
|
|
1488
1819
|
const updatePendingApproval = (approvalId, updater) => {
|
|
@@ -1521,12 +1852,10 @@ var renderWebUiHtml = (options) => {
|
|
|
1521
1852
|
return null;
|
|
1522
1853
|
}
|
|
1523
1854
|
const toolName = item && typeof item.tool === "string" ? item.tool : "tool";
|
|
1524
|
-
const preview = safeJsonPreview(item?.input ?? {});
|
|
1525
|
-
const inputPreview = preview.length > 600 ? preview.slice(0, 600) + "..." : preview;
|
|
1526
1855
|
return {
|
|
1527
1856
|
approvalId,
|
|
1528
1857
|
tool: toolName,
|
|
1529
|
-
|
|
1858
|
+
input: item?.input ?? {},
|
|
1530
1859
|
state: "pending",
|
|
1531
1860
|
};
|
|
1532
1861
|
})
|
|
@@ -1604,50 +1933,87 @@ var renderWebUiHtml = (options) => {
|
|
|
1604
1933
|
elements.shell.classList.toggle("sidebar-open", open);
|
|
1605
1934
|
};
|
|
1606
1935
|
|
|
1607
|
-
const
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
const item = document.createElement("div");
|
|
1611
|
-
item.className = "conversation-item" + (c.conversationId === state.activeConversationId ? " active" : "");
|
|
1612
|
-
item.textContent = c.title;
|
|
1613
|
-
|
|
1614
|
-
const isConfirming = state.confirmDeleteId === c.conversationId;
|
|
1615
|
-
const deleteBtn = document.createElement("button");
|
|
1616
|
-
deleteBtn.className = "delete-btn" + (isConfirming ? " confirming" : "");
|
|
1617
|
-
deleteBtn.textContent = isConfirming ? "sure?" : "\\u00d7";
|
|
1618
|
-
deleteBtn.onclick = async (e) => {
|
|
1619
|
-
e.stopPropagation();
|
|
1620
|
-
if (!isConfirming) {
|
|
1621
|
-
state.confirmDeleteId = c.conversationId;
|
|
1622
|
-
renderConversationList();
|
|
1623
|
-
return;
|
|
1624
|
-
}
|
|
1625
|
-
await api("/api/conversations/" + c.conversationId, { method: "DELETE" });
|
|
1626
|
-
if (state.activeConversationId === c.conversationId) {
|
|
1627
|
-
state.activeConversationId = null;
|
|
1628
|
-
state.activeMessages = [];
|
|
1629
|
-
pushConversationUrl(null);
|
|
1630
|
-
elements.chatTitle.textContent = "";
|
|
1631
|
-
renderMessages([]);
|
|
1632
|
-
}
|
|
1633
|
-
state.confirmDeleteId = null;
|
|
1634
|
-
await loadConversations();
|
|
1635
|
-
};
|
|
1636
|
-
item.appendChild(deleteBtn);
|
|
1936
|
+
const buildConversationItem = (c) => {
|
|
1937
|
+
const item = document.createElement("div");
|
|
1938
|
+
item.className = "conversation-item" + (c.conversationId === state.activeConversationId ? " active" : "");
|
|
1637
1939
|
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1940
|
+
if (c.hasPendingApprovals) {
|
|
1941
|
+
const dot = document.createElement("span");
|
|
1942
|
+
dot.className = "approval-dot";
|
|
1943
|
+
item.appendChild(dot);
|
|
1944
|
+
}
|
|
1945
|
+
|
|
1946
|
+
const titleSpan = document.createElement("span");
|
|
1947
|
+
titleSpan.textContent = c.title;
|
|
1948
|
+
item.appendChild(titleSpan);
|
|
1949
|
+
|
|
1950
|
+
const isConfirming = state.confirmDeleteId === c.conversationId;
|
|
1951
|
+
const deleteBtn = document.createElement("button");
|
|
1952
|
+
deleteBtn.className = "delete-btn" + (isConfirming ? " confirming" : "");
|
|
1953
|
+
deleteBtn.textContent = isConfirming ? "sure?" : "\\u00d7";
|
|
1954
|
+
deleteBtn.onclick = async (e) => {
|
|
1955
|
+
e.stopPropagation();
|
|
1956
|
+
if (!isConfirming) {
|
|
1957
|
+
state.confirmDeleteId = c.conversationId;
|
|
1645
1958
|
renderConversationList();
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
};
|
|
1959
|
+
return;
|
|
1960
|
+
}
|
|
1961
|
+
await api("/api/conversations/" + c.conversationId, { method: "DELETE" });
|
|
1962
|
+
if (state.activeConversationId === c.conversationId) {
|
|
1963
|
+
state.activeConversationId = null;
|
|
1964
|
+
state.activeMessages = [];
|
|
1965
|
+
state.contextTokens = 0;
|
|
1966
|
+
state.contextWindow = 0;
|
|
1967
|
+
updateContextRing();
|
|
1968
|
+
pushConversationUrl(null);
|
|
1969
|
+
elements.chatTitle.textContent = "";
|
|
1970
|
+
renderMessages([]);
|
|
1971
|
+
}
|
|
1972
|
+
state.confirmDeleteId = null;
|
|
1973
|
+
await loadConversations();
|
|
1974
|
+
};
|
|
1975
|
+
item.appendChild(deleteBtn);
|
|
1976
|
+
|
|
1977
|
+
item.onclick = async () => {
|
|
1978
|
+
if (state.confirmDeleteId) {
|
|
1979
|
+
state.confirmDeleteId = null;
|
|
1980
|
+
}
|
|
1981
|
+
state.activeConversationId = c.conversationId;
|
|
1982
|
+
pushConversationUrl(c.conversationId);
|
|
1983
|
+
renderConversationList();
|
|
1984
|
+
await loadConversation(c.conversationId);
|
|
1985
|
+
if (isMobile()) setSidebarOpen(false);
|
|
1986
|
+
};
|
|
1987
|
+
|
|
1988
|
+
return item;
|
|
1989
|
+
};
|
|
1990
|
+
|
|
1991
|
+
const renderConversationList = () => {
|
|
1992
|
+
elements.list.innerHTML = "";
|
|
1993
|
+
const pending = state.conversations.filter(c => c.hasPendingApprovals);
|
|
1994
|
+
const rest = state.conversations.filter(c => !c.hasPendingApprovals);
|
|
1995
|
+
|
|
1996
|
+
if (pending.length > 0) {
|
|
1997
|
+
const label = document.createElement("div");
|
|
1998
|
+
label.className = "sidebar-section-label";
|
|
1999
|
+
label.textContent = "Awaiting approval";
|
|
2000
|
+
elements.list.appendChild(label);
|
|
2001
|
+
for (const c of pending) {
|
|
2002
|
+
elements.list.appendChild(buildConversationItem(c));
|
|
2003
|
+
}
|
|
2004
|
+
if (rest.length > 0) {
|
|
2005
|
+
const divider = document.createElement("div");
|
|
2006
|
+
divider.className = "sidebar-section-divider";
|
|
2007
|
+
elements.list.appendChild(divider);
|
|
2008
|
+
const recentLabel = document.createElement("div");
|
|
2009
|
+
recentLabel.className = "sidebar-section-label";
|
|
2010
|
+
recentLabel.textContent = "Recent";
|
|
2011
|
+
elements.list.appendChild(recentLabel);
|
|
2012
|
+
}
|
|
2013
|
+
}
|
|
1649
2014
|
|
|
1650
|
-
|
|
2015
|
+
for (const c of rest) {
|
|
2016
|
+
elements.list.appendChild(buildConversationItem(c));
|
|
1651
2017
|
}
|
|
1652
2018
|
};
|
|
1653
2019
|
|
|
@@ -1722,8 +2088,14 @@ var renderWebUiHtml = (options) => {
|
|
|
1722
2088
|
} else if (shouldRenderEmptyStreamingIndicator) {
|
|
1723
2089
|
content.appendChild(createThinkingIndicator(getThinkingStatusLabel(m)));
|
|
1724
2090
|
} else {
|
|
1725
|
-
//
|
|
1726
|
-
|
|
2091
|
+
// Merge stored sections (persisted) with live sections (from
|
|
2092
|
+
// an active stream). For normal messages only one source
|
|
2093
|
+
// exists; for liveOnly reconnects both contribute.
|
|
2094
|
+
const storedSections = (m.metadata && m.metadata.sections) || [];
|
|
2095
|
+
const liveSections = m._sections || [];
|
|
2096
|
+
const sections = liveSections.length > 0 && storedSections.length > 0
|
|
2097
|
+
? storedSections.concat(liveSections)
|
|
2098
|
+
: liveSections.length > 0 ? liveSections : (storedSections.length > 0 ? storedSections : null);
|
|
1727
2099
|
const pendingApprovals = Array.isArray(m._pendingApprovals) ? m._pendingApprovals : [];
|
|
1728
2100
|
|
|
1729
2101
|
if (sections && sections.length > 0) {
|
|
@@ -1864,11 +2236,24 @@ var renderWebUiHtml = (options) => {
|
|
|
1864
2236
|
payload.conversation.messages || [],
|
|
1865
2237
|
payload.conversation.pendingApprovals || payload.pendingApprovals || [],
|
|
1866
2238
|
);
|
|
2239
|
+
state.contextTokens = 0;
|
|
2240
|
+
state.contextWindow = 0;
|
|
2241
|
+
updateContextRing();
|
|
1867
2242
|
renderMessages(state.activeMessages, false, { forceScrollBottom: true });
|
|
1868
2243
|
elements.prompt.focus();
|
|
2244
|
+
if (payload.hasActiveRun && !state.isStreaming) {
|
|
2245
|
+
setStreaming(true);
|
|
2246
|
+
streamConversationEvents(conversationId, { liveOnly: true }).finally(() => {
|
|
2247
|
+
if (state.activeConversationId === conversationId) {
|
|
2248
|
+
setStreaming(false);
|
|
2249
|
+
renderMessages(state.activeMessages, false);
|
|
2250
|
+
}
|
|
2251
|
+
});
|
|
2252
|
+
}
|
|
1869
2253
|
};
|
|
1870
2254
|
|
|
1871
|
-
const streamConversationEvents = (conversationId) => {
|
|
2255
|
+
const streamConversationEvents = (conversationId, options) => {
|
|
2256
|
+
const liveOnly = options && options.liveOnly;
|
|
1872
2257
|
return new Promise((resolve) => {
|
|
1873
2258
|
const localMessages = state.activeMessages || [];
|
|
1874
2259
|
const renderIfActiveConversation = (streaming) => {
|
|
@@ -1887,20 +2272,36 @@ var renderWebUiHtml = (options) => {
|
|
|
1887
2272
|
_currentText: "",
|
|
1888
2273
|
_currentTools: [],
|
|
1889
2274
|
_pendingApprovals: [],
|
|
2275
|
+
_activeActivities: [],
|
|
1890
2276
|
metadata: { toolActivity: [] },
|
|
1891
2277
|
};
|
|
1892
2278
|
localMessages.push(assistantMessage);
|
|
1893
2279
|
state.activeMessages = localMessages;
|
|
1894
2280
|
}
|
|
1895
|
-
if (
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
2281
|
+
if (liveOnly) {
|
|
2282
|
+
// Live-only mode: keep metadata.sections intact (the stored
|
|
2283
|
+
// base content) and start _sections empty so it only collects
|
|
2284
|
+
// NEW sections from live events. The renderer merges both.
|
|
2285
|
+
assistantMessage._sections = [];
|
|
2286
|
+
assistantMessage._currentText = "";
|
|
2287
|
+
assistantMessage._currentTools = [];
|
|
2288
|
+
if (!assistantMessage._activeActivities) assistantMessage._activeActivities = [];
|
|
2289
|
+
if (!assistantMessage._pendingApprovals) assistantMessage._pendingApprovals = [];
|
|
2290
|
+
if (!assistantMessage.metadata) assistantMessage.metadata = {};
|
|
2291
|
+
if (!assistantMessage.metadata.toolActivity) assistantMessage.metadata.toolActivity = [];
|
|
2292
|
+
} else {
|
|
2293
|
+
// Full replay mode: reset transient state so replayed events
|
|
2294
|
+
// rebuild from scratch (the buffer has the full event history).
|
|
2295
|
+
assistantMessage.content = "";
|
|
2296
|
+
assistantMessage._sections = [];
|
|
2297
|
+
assistantMessage._currentText = "";
|
|
2298
|
+
assistantMessage._currentTools = [];
|
|
2299
|
+
assistantMessage._activeActivities = [];
|
|
2300
|
+
assistantMessage._pendingApprovals = [];
|
|
2301
|
+
assistantMessage.metadata = { toolActivity: [] };
|
|
2302
|
+
}
|
|
1902
2303
|
|
|
1903
|
-
const url = "/api/conversations/" + encodeURIComponent(conversationId) + "/events";
|
|
2304
|
+
const url = "/api/conversations/" + encodeURIComponent(conversationId) + "/events" + (liveOnly ? "?live_only=true" : "");
|
|
1904
2305
|
fetch(url, { credentials: "include" }).then((response) => {
|
|
1905
2306
|
if (!response.ok || !response.body) {
|
|
1906
2307
|
resolve(undefined);
|
|
@@ -1921,6 +2322,11 @@ var renderWebUiHtml = (options) => {
|
|
|
1921
2322
|
if (eventName === "stream:end") {
|
|
1922
2323
|
return;
|
|
1923
2324
|
}
|
|
2325
|
+
if (eventName === "run:started") {
|
|
2326
|
+
if (typeof payload.contextWindow === "number" && payload.contextWindow > 0) {
|
|
2327
|
+
state.contextWindow = payload.contextWindow;
|
|
2328
|
+
}
|
|
2329
|
+
}
|
|
1924
2330
|
if (eventName === "model:chunk") {
|
|
1925
2331
|
const chunk = String(payload.content || "");
|
|
1926
2332
|
if (assistantMessage._currentTools.length > 0 && chunk.length > 0) {
|
|
@@ -1934,6 +2340,12 @@ var renderWebUiHtml = (options) => {
|
|
|
1934
2340
|
assistantMessage._currentText += chunk;
|
|
1935
2341
|
renderIfActiveConversation(true);
|
|
1936
2342
|
}
|
|
2343
|
+
if (eventName === "model:response") {
|
|
2344
|
+
if (typeof payload.usage?.input === "number") {
|
|
2345
|
+
state.contextTokens = payload.usage.input;
|
|
2346
|
+
updateContextRing();
|
|
2347
|
+
}
|
|
2348
|
+
}
|
|
1937
2349
|
if (eventName === "tool:started") {
|
|
1938
2350
|
const toolName = payload.tool || "tool";
|
|
1939
2351
|
const startedActivity = addActiveActivityFromToolStart(
|
|
@@ -2006,6 +2418,73 @@ var renderWebUiHtml = (options) => {
|
|
|
2006
2418
|
assistantMessage.metadata.toolActivity.push(toolText);
|
|
2007
2419
|
renderIfActiveConversation(true);
|
|
2008
2420
|
}
|
|
2421
|
+
if (eventName === "tool:approval:required") {
|
|
2422
|
+
const toolName = payload.tool || "tool";
|
|
2423
|
+
const activeActivity = removeActiveActivityForTool(
|
|
2424
|
+
assistantMessage,
|
|
2425
|
+
toolName,
|
|
2426
|
+
);
|
|
2427
|
+
const detailFromPayload = describeToolStart(payload);
|
|
2428
|
+
const detail =
|
|
2429
|
+
(activeActivity && typeof activeActivity.detail === "string"
|
|
2430
|
+
? activeActivity.detail.trim()
|
|
2431
|
+
: "") ||
|
|
2432
|
+
(detailFromPayload && typeof detailFromPayload.detail === "string"
|
|
2433
|
+
? detailFromPayload.detail.trim()
|
|
2434
|
+
: "");
|
|
2435
|
+
const toolText =
|
|
2436
|
+
"- approval required \\x60" +
|
|
2437
|
+
toolName +
|
|
2438
|
+
"\\x60" +
|
|
2439
|
+
(detail ? " (" + detail + ")" : "");
|
|
2440
|
+
assistantMessage._currentTools.push(toolText);
|
|
2441
|
+
assistantMessage.metadata.toolActivity.push(toolText);
|
|
2442
|
+
const approvalId =
|
|
2443
|
+
typeof payload.approvalId === "string" ? payload.approvalId : "";
|
|
2444
|
+
if (approvalId) {
|
|
2445
|
+
if (!Array.isArray(assistantMessage._pendingApprovals)) {
|
|
2446
|
+
assistantMessage._pendingApprovals = [];
|
|
2447
|
+
}
|
|
2448
|
+
const exists = assistantMessage._pendingApprovals.some(
|
|
2449
|
+
(req) => req.approvalId === approvalId,
|
|
2450
|
+
);
|
|
2451
|
+
if (!exists) {
|
|
2452
|
+
assistantMessage._pendingApprovals.push({
|
|
2453
|
+
approvalId,
|
|
2454
|
+
tool: toolName,
|
|
2455
|
+
input: payload.input ?? {},
|
|
2456
|
+
state: "pending",
|
|
2457
|
+
});
|
|
2458
|
+
}
|
|
2459
|
+
}
|
|
2460
|
+
renderIfActiveConversation(true);
|
|
2461
|
+
}
|
|
2462
|
+
if (eventName === "tool:approval:granted") {
|
|
2463
|
+
const toolText = "- approval granted";
|
|
2464
|
+
assistantMessage._currentTools.push(toolText);
|
|
2465
|
+
assistantMessage.metadata.toolActivity.push(toolText);
|
|
2466
|
+
const approvalId =
|
|
2467
|
+
typeof payload.approvalId === "string" ? payload.approvalId : "";
|
|
2468
|
+
if (approvalId && Array.isArray(assistantMessage._pendingApprovals)) {
|
|
2469
|
+
assistantMessage._pendingApprovals = assistantMessage._pendingApprovals.filter(
|
|
2470
|
+
(req) => req.approvalId !== approvalId,
|
|
2471
|
+
);
|
|
2472
|
+
}
|
|
2473
|
+
renderIfActiveConversation(true);
|
|
2474
|
+
}
|
|
2475
|
+
if (eventName === "tool:approval:denied") {
|
|
2476
|
+
const toolText = "- approval denied";
|
|
2477
|
+
assistantMessage._currentTools.push(toolText);
|
|
2478
|
+
assistantMessage.metadata.toolActivity.push(toolText);
|
|
2479
|
+
const approvalId =
|
|
2480
|
+
typeof payload.approvalId === "string" ? payload.approvalId : "";
|
|
2481
|
+
if (approvalId && Array.isArray(assistantMessage._pendingApprovals)) {
|
|
2482
|
+
assistantMessage._pendingApprovals = assistantMessage._pendingApprovals.filter(
|
|
2483
|
+
(req) => req.approvalId !== approvalId,
|
|
2484
|
+
);
|
|
2485
|
+
}
|
|
2486
|
+
renderIfActiveConversation(true);
|
|
2487
|
+
}
|
|
2009
2488
|
if (eventName === "run:completed") {
|
|
2010
2489
|
assistantMessage._activeActivities = [];
|
|
2011
2490
|
if (
|
|
@@ -2052,9 +2531,22 @@ var renderWebUiHtml = (options) => {
|
|
|
2052
2531
|
}
|
|
2053
2532
|
if (eventName === "run:error") {
|
|
2054
2533
|
assistantMessage._activeActivities = [];
|
|
2534
|
+
if (assistantMessage._currentTools.length > 0) {
|
|
2535
|
+
assistantMessage._sections.push({
|
|
2536
|
+
type: "tools",
|
|
2537
|
+
content: assistantMessage._currentTools,
|
|
2538
|
+
});
|
|
2539
|
+
assistantMessage._currentTools = [];
|
|
2540
|
+
}
|
|
2541
|
+
if (assistantMessage._currentText.length > 0) {
|
|
2542
|
+
assistantMessage._sections.push({
|
|
2543
|
+
type: "text",
|
|
2544
|
+
content: assistantMessage._currentText,
|
|
2545
|
+
});
|
|
2546
|
+
assistantMessage._currentText = "";
|
|
2547
|
+
}
|
|
2055
2548
|
const errMsg =
|
|
2056
2549
|
payload.error?.message || "Something went wrong";
|
|
2057
|
-
assistantMessage.content = "";
|
|
2058
2550
|
assistantMessage._error = errMsg;
|
|
2059
2551
|
renderIfActiveConversation(false);
|
|
2060
2552
|
}
|
|
@@ -2127,6 +2619,9 @@ var renderWebUiHtml = (options) => {
|
|
|
2127
2619
|
elements.send.disabled = value ? !canStop : false;
|
|
2128
2620
|
elements.send.innerHTML = value ? stopIconMarkup : sendIconMarkup;
|
|
2129
2621
|
elements.send.classList.toggle("stop-mode", value);
|
|
2622
|
+
if (elements.sendBtnWrapper) {
|
|
2623
|
+
elements.sendBtnWrapper.classList.toggle("stop-mode", value);
|
|
2624
|
+
}
|
|
2130
2625
|
elements.send.setAttribute("aria-label", value ? "Stop response" : "Send message");
|
|
2131
2626
|
elements.send.setAttribute(
|
|
2132
2627
|
"title",
|
|
@@ -2499,8 +2994,17 @@ var renderWebUiHtml = (options) => {
|
|
|
2499
2994
|
}
|
|
2500
2995
|
if (eventName === "run:started") {
|
|
2501
2996
|
state.activeStreamRunId = typeof payload.runId === "string" ? payload.runId : null;
|
|
2997
|
+
if (typeof payload.contextWindow === "number" && payload.contextWindow > 0) {
|
|
2998
|
+
state.contextWindow = payload.contextWindow;
|
|
2999
|
+
}
|
|
2502
3000
|
setStreaming(state.isStreaming);
|
|
2503
3001
|
}
|
|
3002
|
+
if (eventName === "model:response") {
|
|
3003
|
+
if (typeof payload.usage?.input === "number") {
|
|
3004
|
+
state.contextTokens = payload.usage.input;
|
|
3005
|
+
updateContextRing();
|
|
3006
|
+
}
|
|
3007
|
+
}
|
|
2504
3008
|
if (eventName === "tool:started") {
|
|
2505
3009
|
const toolName = payload.tool || "tool";
|
|
2506
3010
|
const startedActivity = addActiveActivityFromToolStart(
|
|
@@ -2596,8 +3100,6 @@ var renderWebUiHtml = (options) => {
|
|
|
2596
3100
|
const approvalId =
|
|
2597
3101
|
typeof payload.approvalId === "string" ? payload.approvalId : "";
|
|
2598
3102
|
if (approvalId) {
|
|
2599
|
-
const preview = safeJsonPreview(payload.input ?? {});
|
|
2600
|
-
const inputPreview = preview.length > 600 ? preview.slice(0, 600) + "..." : preview;
|
|
2601
3103
|
if (!Array.isArray(assistantMessage._pendingApprovals)) {
|
|
2602
3104
|
assistantMessage._pendingApprovals = [];
|
|
2603
3105
|
}
|
|
@@ -2608,7 +3110,7 @@ var renderWebUiHtml = (options) => {
|
|
|
2608
3110
|
assistantMessage._pendingApprovals.push({
|
|
2609
3111
|
approvalId,
|
|
2610
3112
|
tool: toolName,
|
|
2611
|
-
|
|
3113
|
+
input: payload.input ?? {},
|
|
2612
3114
|
state: "pending",
|
|
2613
3115
|
});
|
|
2614
3116
|
}
|
|
@@ -2663,9 +3165,8 @@ var renderWebUiHtml = (options) => {
|
|
|
2663
3165
|
renderIfActiveConversation(false);
|
|
2664
3166
|
}
|
|
2665
3167
|
if (eventName === "run:error") {
|
|
2666
|
-
|
|
3168
|
+
finalizeAssistantMessage();
|
|
2667
3169
|
const errMsg = payload.error?.message || "Something went wrong";
|
|
2668
|
-
assistantMessage.content = "";
|
|
2669
3170
|
assistantMessage._error = errMsg;
|
|
2670
3171
|
renderIfActiveConversation(false);
|
|
2671
3172
|
}
|
|
@@ -2768,6 +3269,9 @@ var renderWebUiHtml = (options) => {
|
|
|
2768
3269
|
state.activeConversationId = null;
|
|
2769
3270
|
state.activeMessages = [];
|
|
2770
3271
|
state.confirmDeleteId = null;
|
|
3272
|
+
state.contextTokens = 0;
|
|
3273
|
+
state.contextWindow = 0;
|
|
3274
|
+
updateContextRing();
|
|
2771
3275
|
pushConversationUrl(null);
|
|
2772
3276
|
elements.chatTitle.textContent = "";
|
|
2773
3277
|
renderMessages([]);
|
|
@@ -2805,6 +3309,9 @@ var renderWebUiHtml = (options) => {
|
|
|
2805
3309
|
state.confirmDeleteId = null;
|
|
2806
3310
|
state.conversations = [];
|
|
2807
3311
|
state.csrfToken = "";
|
|
3312
|
+
state.contextTokens = 0;
|
|
3313
|
+
state.contextWindow = 0;
|
|
3314
|
+
updateContextRing();
|
|
2808
3315
|
await requireAuth();
|
|
2809
3316
|
});
|
|
2810
3317
|
|
|
@@ -2952,17 +3459,23 @@ var renderWebUiHtml = (options) => {
|
|
|
2952
3459
|
});
|
|
2953
3460
|
updatePendingApproval(approvalId, () => null);
|
|
2954
3461
|
renderMessages(state.activeMessages, state.isStreaming);
|
|
3462
|
+
loadConversations();
|
|
2955
3463
|
if (!wasStreaming && state.activeConversationId) {
|
|
2956
|
-
await streamConversationEvents(state.activeConversationId);
|
|
3464
|
+
await streamConversationEvents(state.activeConversationId, { liveOnly: true });
|
|
2957
3465
|
}
|
|
2958
3466
|
} catch (error) {
|
|
2959
|
-
const
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
|
|
2965
|
-
|
|
3467
|
+
const isStale = error && error.payload && error.payload.code === "APPROVAL_NOT_FOUND";
|
|
3468
|
+
if (isStale) {
|
|
3469
|
+
updatePendingApproval(approvalId, () => null);
|
|
3470
|
+
} else {
|
|
3471
|
+
const errMsg = error instanceof Error ? error.message : String(error);
|
|
3472
|
+
updatePendingApproval(approvalId, (request) => ({
|
|
3473
|
+
...request,
|
|
3474
|
+
state: "pending",
|
|
3475
|
+
pendingDecision: null,
|
|
3476
|
+
_error: errMsg,
|
|
3477
|
+
}));
|
|
3478
|
+
}
|
|
2966
3479
|
renderMessages(state.activeMessages, state.isStreaming);
|
|
2967
3480
|
} finally {
|
|
2968
3481
|
if (!wasStreaming) {
|
|
@@ -3009,6 +3522,9 @@ var renderWebUiHtml = (options) => {
|
|
|
3009
3522
|
} else {
|
|
3010
3523
|
state.activeConversationId = null;
|
|
3011
3524
|
state.activeMessages = [];
|
|
3525
|
+
state.contextTokens = 0;
|
|
3526
|
+
state.contextWindow = 0;
|
|
3527
|
+
updateContextRing();
|
|
3012
3528
|
elements.chatTitle.textContent = "";
|
|
3013
3529
|
renderMessages([]);
|
|
3014
3530
|
renderConversationList();
|
|
@@ -3050,6 +3566,7 @@ var renderWebUiHtml = (options) => {
|
|
|
3050
3566
|
await createConversation();
|
|
3051
3567
|
}
|
|
3052
3568
|
autoResizePrompt();
|
|
3569
|
+
updateContextRing();
|
|
3053
3570
|
elements.prompt.focus();
|
|
3054
3571
|
})();
|
|
3055
3572
|
|
|
@@ -3266,42 +3783,667 @@ var renderWebUiHtml = (options) => {
|
|
|
3266
3783
|
</html>`;
|
|
3267
3784
|
};
|
|
3268
3785
|
|
|
3269
|
-
// src/
|
|
3270
|
-
|
|
3271
|
-
|
|
3272
|
-
|
|
3273
|
-
|
|
3274
|
-
|
|
3275
|
-
|
|
3276
|
-
|
|
3277
|
-
|
|
3278
|
-
|
|
3279
|
-
|
|
3280
|
-
|
|
3281
|
-
|
|
3282
|
-
|
|
3283
|
-
|
|
3284
|
-
|
|
3285
|
-
|
|
3286
|
-
|
|
3287
|
-
|
|
3288
|
-
|
|
3289
|
-
|
|
3290
|
-
|
|
3291
|
-
|
|
3292
|
-
|
|
3293
|
-
|
|
3294
|
-
|
|
3295
|
-
|
|
3296
|
-
|
|
3297
|
-
|
|
3298
|
-
|
|
3299
|
-
}
|
|
3300
|
-
|
|
3301
|
-
|
|
3302
|
-
|
|
3303
|
-
|
|
3304
|
-
|
|
3786
|
+
// src/api-docs.ts
|
|
3787
|
+
var buildOpenApiSpec = (options) => ({
|
|
3788
|
+
openapi: "3.1.0",
|
|
3789
|
+
info: {
|
|
3790
|
+
title: `${options.agentName} API`,
|
|
3791
|
+
description: "HTTP API for interacting with a Poncho agent. Supports conversation management, streaming message responses via Server-Sent Events (SSE), tool approval workflows, file uploads, and cron job triggers.",
|
|
3792
|
+
version: "1.0.0"
|
|
3793
|
+
},
|
|
3794
|
+
servers: [{ url: "/", description: "Current host" }],
|
|
3795
|
+
components: {
|
|
3796
|
+
securitySchemes: {
|
|
3797
|
+
bearerAuth: {
|
|
3798
|
+
type: "http",
|
|
3799
|
+
scheme: "bearer",
|
|
3800
|
+
description: "Pass the PONCHO_AUTH_TOKEN value as a Bearer token. Required only when `auth.required: true` in poncho.config.js."
|
|
3801
|
+
}
|
|
3802
|
+
},
|
|
3803
|
+
schemas: {
|
|
3804
|
+
ConversationSummary: {
|
|
3805
|
+
type: "object",
|
|
3806
|
+
properties: {
|
|
3807
|
+
conversationId: { type: "string" },
|
|
3808
|
+
title: { type: "string" },
|
|
3809
|
+
runtimeRunId: { type: "string" },
|
|
3810
|
+
ownerId: { type: "string" },
|
|
3811
|
+
tenantId: { type: ["string", "null"] },
|
|
3812
|
+
createdAt: { type: "number", description: "Unix epoch ms" },
|
|
3813
|
+
updatedAt: { type: "number", description: "Unix epoch ms" },
|
|
3814
|
+
messageCount: { type: "integer" }
|
|
3815
|
+
}
|
|
3816
|
+
},
|
|
3817
|
+
Message: {
|
|
3818
|
+
type: "object",
|
|
3819
|
+
properties: {
|
|
3820
|
+
role: { type: "string", enum: ["user", "assistant"] },
|
|
3821
|
+
content: {
|
|
3822
|
+
oneOf: [
|
|
3823
|
+
{ type: "string" },
|
|
3824
|
+
{
|
|
3825
|
+
type: "array",
|
|
3826
|
+
items: {
|
|
3827
|
+
oneOf: [
|
|
3828
|
+
{
|
|
3829
|
+
type: "object",
|
|
3830
|
+
properties: {
|
|
3831
|
+
type: { type: "string", const: "text" },
|
|
3832
|
+
text: { type: "string" }
|
|
3833
|
+
}
|
|
3834
|
+
},
|
|
3835
|
+
{
|
|
3836
|
+
type: "object",
|
|
3837
|
+
properties: {
|
|
3838
|
+
type: { type: "string", const: "file" },
|
|
3839
|
+
data: { type: "string" },
|
|
3840
|
+
mediaType: { type: "string" },
|
|
3841
|
+
filename: { type: "string" }
|
|
3842
|
+
}
|
|
3843
|
+
}
|
|
3844
|
+
]
|
|
3845
|
+
}
|
|
3846
|
+
}
|
|
3847
|
+
]
|
|
3848
|
+
},
|
|
3849
|
+
metadata: {
|
|
3850
|
+
type: "object",
|
|
3851
|
+
properties: {
|
|
3852
|
+
id: { type: "string" },
|
|
3853
|
+
timestamp: { type: "number" },
|
|
3854
|
+
tokenCount: { type: "number" },
|
|
3855
|
+
step: { type: "number" },
|
|
3856
|
+
toolActivity: { type: "array", items: { type: "string" } }
|
|
3857
|
+
}
|
|
3858
|
+
}
|
|
3859
|
+
}
|
|
3860
|
+
},
|
|
3861
|
+
Conversation: {
|
|
3862
|
+
type: "object",
|
|
3863
|
+
properties: {
|
|
3864
|
+
conversationId: { type: "string" },
|
|
3865
|
+
title: { type: "string" },
|
|
3866
|
+
ownerId: { type: "string" },
|
|
3867
|
+
tenantId: { type: ["string", "null"] },
|
|
3868
|
+
createdAt: { type: "number" },
|
|
3869
|
+
updatedAt: { type: "number" },
|
|
3870
|
+
messages: { type: "array", items: { $ref: "#/components/schemas/Message" } },
|
|
3871
|
+
pendingApprovals: {
|
|
3872
|
+
type: "array",
|
|
3873
|
+
items: { $ref: "#/components/schemas/PendingApproval" }
|
|
3874
|
+
}
|
|
3875
|
+
}
|
|
3876
|
+
},
|
|
3877
|
+
PendingApproval: {
|
|
3878
|
+
type: "object",
|
|
3879
|
+
properties: {
|
|
3880
|
+
approvalId: { type: "string" },
|
|
3881
|
+
runId: { type: "string" },
|
|
3882
|
+
tool: { type: "string" },
|
|
3883
|
+
input: {}
|
|
3884
|
+
}
|
|
3885
|
+
},
|
|
3886
|
+
TokenUsage: {
|
|
3887
|
+
type: "object",
|
|
3888
|
+
properties: {
|
|
3889
|
+
input: { type: "integer" },
|
|
3890
|
+
output: { type: "integer" },
|
|
3891
|
+
cached: { type: "integer" }
|
|
3892
|
+
}
|
|
3893
|
+
},
|
|
3894
|
+
RunResult: {
|
|
3895
|
+
type: "object",
|
|
3896
|
+
properties: {
|
|
3897
|
+
status: { type: "string", enum: ["completed", "error", "cancelled"] },
|
|
3898
|
+
response: { type: "string" },
|
|
3899
|
+
steps: { type: "integer" },
|
|
3900
|
+
tokens: { $ref: "#/components/schemas/TokenUsage" },
|
|
3901
|
+
duration: { type: "number", description: "Duration in ms" },
|
|
3902
|
+
continuation: { type: "boolean" },
|
|
3903
|
+
maxSteps: { type: "integer" }
|
|
3904
|
+
}
|
|
3905
|
+
},
|
|
3906
|
+
FileAttachment: {
|
|
3907
|
+
type: "object",
|
|
3908
|
+
properties: {
|
|
3909
|
+
data: { type: "string", description: "base64-encoded file data" },
|
|
3910
|
+
mediaType: { type: "string" },
|
|
3911
|
+
filename: { type: "string" }
|
|
3912
|
+
},
|
|
3913
|
+
required: ["data", "mediaType"]
|
|
3914
|
+
},
|
|
3915
|
+
Error: {
|
|
3916
|
+
type: "object",
|
|
3917
|
+
properties: {
|
|
3918
|
+
code: { type: "string" },
|
|
3919
|
+
message: { type: "string" }
|
|
3920
|
+
}
|
|
3921
|
+
}
|
|
3922
|
+
}
|
|
3923
|
+
},
|
|
3924
|
+
security: [{ bearerAuth: [] }],
|
|
3925
|
+
paths: {
|
|
3926
|
+
"/health": {
|
|
3927
|
+
get: {
|
|
3928
|
+
tags: ["Health"],
|
|
3929
|
+
summary: "Health check",
|
|
3930
|
+
security: [],
|
|
3931
|
+
responses: {
|
|
3932
|
+
"200": {
|
|
3933
|
+
description: "Server is healthy",
|
|
3934
|
+
content: {
|
|
3935
|
+
"application/json": {
|
|
3936
|
+
schema: {
|
|
3937
|
+
type: "object",
|
|
3938
|
+
properties: { status: { type: "string", const: "ok" } }
|
|
3939
|
+
}
|
|
3940
|
+
}
|
|
3941
|
+
}
|
|
3942
|
+
}
|
|
3943
|
+
}
|
|
3944
|
+
}
|
|
3945
|
+
},
|
|
3946
|
+
"/api/auth/session": {
|
|
3947
|
+
get: {
|
|
3948
|
+
tags: ["Auth"],
|
|
3949
|
+
summary: "Check session status",
|
|
3950
|
+
description: "Returns whether the caller is authenticated and provides a CSRF token for subsequent mutating requests.",
|
|
3951
|
+
security: [],
|
|
3952
|
+
responses: {
|
|
3953
|
+
"200": {
|
|
3954
|
+
description: "Session status",
|
|
3955
|
+
content: {
|
|
3956
|
+
"application/json": {
|
|
3957
|
+
schema: {
|
|
3958
|
+
type: "object",
|
|
3959
|
+
properties: {
|
|
3960
|
+
authenticated: { type: "boolean" },
|
|
3961
|
+
sessionId: { type: "string" },
|
|
3962
|
+
ownerId: { type: "string" },
|
|
3963
|
+
csrfToken: { type: "string" }
|
|
3964
|
+
},
|
|
3965
|
+
required: ["authenticated"]
|
|
3966
|
+
}
|
|
3967
|
+
}
|
|
3968
|
+
}
|
|
3969
|
+
}
|
|
3970
|
+
}
|
|
3971
|
+
}
|
|
3972
|
+
},
|
|
3973
|
+
"/api/auth/login": {
|
|
3974
|
+
post: {
|
|
3975
|
+
tags: ["Auth"],
|
|
3976
|
+
summary: "Authenticate with passphrase",
|
|
3977
|
+
description: "Creates a session cookie. Only needed for browser-based auth; API clients should use Bearer tokens instead.",
|
|
3978
|
+
security: [],
|
|
3979
|
+
requestBody: {
|
|
3980
|
+
required: true,
|
|
3981
|
+
content: {
|
|
3982
|
+
"application/json": {
|
|
3983
|
+
schema: {
|
|
3984
|
+
type: "object",
|
|
3985
|
+
properties: { passphrase: { type: "string" } },
|
|
3986
|
+
required: ["passphrase"]
|
|
3987
|
+
}
|
|
3988
|
+
}
|
|
3989
|
+
}
|
|
3990
|
+
},
|
|
3991
|
+
responses: {
|
|
3992
|
+
"200": {
|
|
3993
|
+
description: "Login successful",
|
|
3994
|
+
content: {
|
|
3995
|
+
"application/json": {
|
|
3996
|
+
schema: {
|
|
3997
|
+
type: "object",
|
|
3998
|
+
properties: {
|
|
3999
|
+
ok: { type: "boolean" },
|
|
4000
|
+
sessionId: { type: "string" },
|
|
4001
|
+
csrfToken: { type: "string" }
|
|
4002
|
+
}
|
|
4003
|
+
}
|
|
4004
|
+
}
|
|
4005
|
+
}
|
|
4006
|
+
},
|
|
4007
|
+
"401": {
|
|
4008
|
+
description: "Invalid passphrase",
|
|
4009
|
+
content: { "application/json": { schema: { $ref: "#/components/schemas/Error" } } }
|
|
4010
|
+
},
|
|
4011
|
+
"429": {
|
|
4012
|
+
description: "Too many login attempts",
|
|
4013
|
+
content: { "application/json": { schema: { $ref: "#/components/schemas/Error" } } }
|
|
4014
|
+
}
|
|
4015
|
+
}
|
|
4016
|
+
}
|
|
4017
|
+
},
|
|
4018
|
+
"/api/auth/logout": {
|
|
4019
|
+
post: {
|
|
4020
|
+
tags: ["Auth"],
|
|
4021
|
+
summary: "End session",
|
|
4022
|
+
responses: {
|
|
4023
|
+
"200": {
|
|
4024
|
+
description: "Logged out",
|
|
4025
|
+
content: {
|
|
4026
|
+
"application/json": {
|
|
4027
|
+
schema: { type: "object", properties: { ok: { type: "boolean" } } }
|
|
4028
|
+
}
|
|
4029
|
+
}
|
|
4030
|
+
}
|
|
4031
|
+
}
|
|
4032
|
+
}
|
|
4033
|
+
},
|
|
4034
|
+
"/api/conversations": {
|
|
4035
|
+
get: {
|
|
4036
|
+
tags: ["Conversations"],
|
|
4037
|
+
summary: "List conversations",
|
|
4038
|
+
responses: {
|
|
4039
|
+
"200": {
|
|
4040
|
+
description: "Conversation list",
|
|
4041
|
+
content: {
|
|
4042
|
+
"application/json": {
|
|
4043
|
+
schema: {
|
|
4044
|
+
type: "object",
|
|
4045
|
+
properties: {
|
|
4046
|
+
conversations: {
|
|
4047
|
+
type: "array",
|
|
4048
|
+
items: { $ref: "#/components/schemas/ConversationSummary" }
|
|
4049
|
+
}
|
|
4050
|
+
}
|
|
4051
|
+
}
|
|
4052
|
+
}
|
|
4053
|
+
}
|
|
4054
|
+
}
|
|
4055
|
+
}
|
|
4056
|
+
},
|
|
4057
|
+
post: {
|
|
4058
|
+
tags: ["Conversations"],
|
|
4059
|
+
summary: "Create a conversation",
|
|
4060
|
+
requestBody: {
|
|
4061
|
+
content: {
|
|
4062
|
+
"application/json": {
|
|
4063
|
+
schema: {
|
|
4064
|
+
type: "object",
|
|
4065
|
+
properties: { title: { type: "string" } }
|
|
4066
|
+
}
|
|
4067
|
+
}
|
|
4068
|
+
}
|
|
4069
|
+
},
|
|
4070
|
+
responses: {
|
|
4071
|
+
"201": {
|
|
4072
|
+
description: "Conversation created",
|
|
4073
|
+
content: {
|
|
4074
|
+
"application/json": {
|
|
4075
|
+
schema: {
|
|
4076
|
+
type: "object",
|
|
4077
|
+
properties: { conversation: { $ref: "#/components/schemas/Conversation" } }
|
|
4078
|
+
}
|
|
4079
|
+
}
|
|
4080
|
+
}
|
|
4081
|
+
}
|
|
4082
|
+
}
|
|
4083
|
+
}
|
|
4084
|
+
},
|
|
4085
|
+
"/api/conversations/{conversationId}": {
|
|
4086
|
+
get: {
|
|
4087
|
+
tags: ["Conversations"],
|
|
4088
|
+
summary: "Get conversation",
|
|
4089
|
+
description: "Returns the full conversation including messages and any pending tool approval requests.",
|
|
4090
|
+
parameters: [
|
|
4091
|
+
{ name: "conversationId", in: "path", required: true, schema: { type: "string" } }
|
|
4092
|
+
],
|
|
4093
|
+
responses: {
|
|
4094
|
+
"200": {
|
|
4095
|
+
description: "Conversation with messages",
|
|
4096
|
+
content: {
|
|
4097
|
+
"application/json": {
|
|
4098
|
+
schema: {
|
|
4099
|
+
type: "object",
|
|
4100
|
+
properties: { conversation: { $ref: "#/components/schemas/Conversation" } }
|
|
4101
|
+
}
|
|
4102
|
+
}
|
|
4103
|
+
}
|
|
4104
|
+
},
|
|
4105
|
+
"404": {
|
|
4106
|
+
description: "Conversation not found",
|
|
4107
|
+
content: { "application/json": { schema: { $ref: "#/components/schemas/Error" } } }
|
|
4108
|
+
}
|
|
4109
|
+
}
|
|
4110
|
+
},
|
|
4111
|
+
patch: {
|
|
4112
|
+
tags: ["Conversations"],
|
|
4113
|
+
summary: "Rename conversation",
|
|
4114
|
+
parameters: [
|
|
4115
|
+
{ name: "conversationId", in: "path", required: true, schema: { type: "string" } }
|
|
4116
|
+
],
|
|
4117
|
+
requestBody: {
|
|
4118
|
+
required: true,
|
|
4119
|
+
content: {
|
|
4120
|
+
"application/json": {
|
|
4121
|
+
schema: {
|
|
4122
|
+
type: "object",
|
|
4123
|
+
properties: { title: { type: "string" } },
|
|
4124
|
+
required: ["title"]
|
|
4125
|
+
}
|
|
4126
|
+
}
|
|
4127
|
+
}
|
|
4128
|
+
},
|
|
4129
|
+
responses: {
|
|
4130
|
+
"200": {
|
|
4131
|
+
description: "Conversation renamed",
|
|
4132
|
+
content: {
|
|
4133
|
+
"application/json": {
|
|
4134
|
+
schema: {
|
|
4135
|
+
type: "object",
|
|
4136
|
+
properties: { conversation: { $ref: "#/components/schemas/Conversation" } }
|
|
4137
|
+
}
|
|
4138
|
+
}
|
|
4139
|
+
}
|
|
4140
|
+
}
|
|
4141
|
+
}
|
|
4142
|
+
},
|
|
4143
|
+
delete: {
|
|
4144
|
+
tags: ["Conversations"],
|
|
4145
|
+
summary: "Delete conversation",
|
|
4146
|
+
parameters: [
|
|
4147
|
+
{ name: "conversationId", in: "path", required: true, schema: { type: "string" } }
|
|
4148
|
+
],
|
|
4149
|
+
responses: {
|
|
4150
|
+
"200": {
|
|
4151
|
+
description: "Conversation deleted",
|
|
4152
|
+
content: {
|
|
4153
|
+
"application/json": {
|
|
4154
|
+
schema: { type: "object", properties: { ok: { type: "boolean" } } }
|
|
4155
|
+
}
|
|
4156
|
+
}
|
|
4157
|
+
}
|
|
4158
|
+
}
|
|
4159
|
+
}
|
|
4160
|
+
},
|
|
4161
|
+
"/api/conversations/{conversationId}/messages": {
|
|
4162
|
+
post: {
|
|
4163
|
+
tags: ["Messages"],
|
|
4164
|
+
summary: "Send a message (streaming)",
|
|
4165
|
+
description: "Sends a user message and streams the agent's response via Server-Sent Events.\n\n### SSE protocol\n\nThe response is a stream of SSE frames. Each frame has the format:\n\n```\nevent: <type>\ndata: <json>\n\n```\n\n**Event types:**\n\n| Event | Payload | Description |\n| --- | --- | --- |\n| `run:started` | `{ runId, agentId }` | Agent run has begun |\n| `model:chunk` | `{ content }` | Incremental text token from the model |\n| `model:response` | `{ usage: { input, output, cached } }` | Model call finished |\n| `step:started` | `{ step }` | Agent step started |\n| `step:completed` | `{ step, duration }` | Agent step finished |\n| `tool:started` | `{ tool, input }` | Tool invocation started |\n| `tool:completed` | `{ tool, output, duration }` | Tool finished successfully |\n| `tool:error` | `{ tool, error, recoverable }` | Tool returned an error |\n| `tool:approval:required` | `{ tool, input, approvalId }` | Tool needs human approval |\n| `tool:approval:granted` | `{ approvalId }` | Approval was granted |\n| `tool:approval:denied` | `{ approvalId, reason? }` | Approval was denied |\n| `run:completed` | `{ runId, result: RunResult }` | Agent finished |\n| `run:error` | `{ runId, error: { code, message } }` | Agent failed |\n| `run:cancelled` | `{ runId }` | Run was cancelled via stop endpoint |\n\nTo build the assistant's response, concatenate all `model:chunk` content values.\n\n### Reconnection\n\nIf the SSE connection drops mid-stream, reconnect via `GET /api/conversations/{conversationId}/events` to replay buffered events.",
|
|
4166
|
+
parameters: [
|
|
4167
|
+
{ name: "conversationId", in: "path", required: true, schema: { type: "string" } }
|
|
4168
|
+
],
|
|
4169
|
+
requestBody: {
|
|
4170
|
+
required: true,
|
|
4171
|
+
content: {
|
|
4172
|
+
"application/json": {
|
|
4173
|
+
schema: {
|
|
4174
|
+
type: "object",
|
|
4175
|
+
properties: {
|
|
4176
|
+
message: { type: "string", description: "User message text" },
|
|
4177
|
+
parameters: {
|
|
4178
|
+
type: "object",
|
|
4179
|
+
additionalProperties: true,
|
|
4180
|
+
description: "Key-value parameters passed to the agent run"
|
|
4181
|
+
},
|
|
4182
|
+
files: {
|
|
4183
|
+
type: "array",
|
|
4184
|
+
items: { $ref: "#/components/schemas/FileAttachment" },
|
|
4185
|
+
description: "Attached files (base64-encoded)"
|
|
4186
|
+
}
|
|
4187
|
+
},
|
|
4188
|
+
required: ["message"]
|
|
4189
|
+
}
|
|
4190
|
+
},
|
|
4191
|
+
"multipart/form-data": {
|
|
4192
|
+
schema: {
|
|
4193
|
+
type: "object",
|
|
4194
|
+
properties: {
|
|
4195
|
+
message: { type: "string" },
|
|
4196
|
+
parameters: { type: "string", description: "JSON-encoded parameters object" },
|
|
4197
|
+
files: { type: "array", items: { type: "string", format: "binary" } }
|
|
4198
|
+
}
|
|
4199
|
+
}
|
|
4200
|
+
}
|
|
4201
|
+
}
|
|
4202
|
+
},
|
|
4203
|
+
responses: {
|
|
4204
|
+
"200": {
|
|
4205
|
+
description: "SSE stream of agent events",
|
|
4206
|
+
content: { "text/event-stream": { schema: { type: "string" } } }
|
|
4207
|
+
},
|
|
4208
|
+
"404": {
|
|
4209
|
+
description: "Conversation not found",
|
|
4210
|
+
content: { "application/json": { schema: { $ref: "#/components/schemas/Error" } } }
|
|
4211
|
+
}
|
|
4212
|
+
}
|
|
4213
|
+
}
|
|
4214
|
+
},
|
|
4215
|
+
"/api/conversations/{conversationId}/events": {
|
|
4216
|
+
get: {
|
|
4217
|
+
tags: ["Messages"],
|
|
4218
|
+
summary: "Attach to live event stream",
|
|
4219
|
+
description: "Connects to the SSE event stream for an in-progress run. Replays all buffered events from the current run, then streams live events. If no run is active, sends a `stream:end` event and closes.",
|
|
4220
|
+
parameters: [
|
|
4221
|
+
{ name: "conversationId", in: "path", required: true, schema: { type: "string" } }
|
|
4222
|
+
],
|
|
4223
|
+
responses: {
|
|
4224
|
+
"200": {
|
|
4225
|
+
description: "SSE stream (same event format as POST /messages)",
|
|
4226
|
+
content: { "text/event-stream": { schema: { type: "string" } } }
|
|
4227
|
+
},
|
|
4228
|
+
"404": {
|
|
4229
|
+
description: "Conversation not found",
|
|
4230
|
+
content: { "application/json": { schema: { $ref: "#/components/schemas/Error" } } }
|
|
4231
|
+
}
|
|
4232
|
+
}
|
|
4233
|
+
}
|
|
4234
|
+
},
|
|
4235
|
+
"/api/conversations/{conversationId}/stop": {
|
|
4236
|
+
post: {
|
|
4237
|
+
tags: ["Messages"],
|
|
4238
|
+
summary: "Stop an in-flight run",
|
|
4239
|
+
parameters: [
|
|
4240
|
+
{ name: "conversationId", in: "path", required: true, schema: { type: "string" } }
|
|
4241
|
+
],
|
|
4242
|
+
requestBody: {
|
|
4243
|
+
required: true,
|
|
4244
|
+
content: {
|
|
4245
|
+
"application/json": {
|
|
4246
|
+
schema: {
|
|
4247
|
+
type: "object",
|
|
4248
|
+
properties: {
|
|
4249
|
+
runId: { type: "string", description: "The run ID to cancel (from run:started event)" }
|
|
4250
|
+
},
|
|
4251
|
+
required: ["runId"]
|
|
4252
|
+
}
|
|
4253
|
+
}
|
|
4254
|
+
}
|
|
4255
|
+
},
|
|
4256
|
+
responses: {
|
|
4257
|
+
"200": {
|
|
4258
|
+
description: "Stop result",
|
|
4259
|
+
content: {
|
|
4260
|
+
"application/json": {
|
|
4261
|
+
schema: {
|
|
4262
|
+
type: "object",
|
|
4263
|
+
properties: {
|
|
4264
|
+
ok: { type: "boolean" },
|
|
4265
|
+
stopped: { type: "boolean" },
|
|
4266
|
+
runId: { type: "string" }
|
|
4267
|
+
}
|
|
4268
|
+
}
|
|
4269
|
+
}
|
|
4270
|
+
}
|
|
4271
|
+
}
|
|
4272
|
+
}
|
|
4273
|
+
}
|
|
4274
|
+
},
|
|
4275
|
+
"/api/approvals/{approvalId}": {
|
|
4276
|
+
post: {
|
|
4277
|
+
tags: ["Approvals"],
|
|
4278
|
+
summary: "Resolve a tool approval request",
|
|
4279
|
+
description: "When an agent run encounters a gated tool, it emits a `tool:approval:required` SSE event and pauses. Use this endpoint to approve or deny the tool invocation.",
|
|
4280
|
+
parameters: [
|
|
4281
|
+
{ name: "approvalId", in: "path", required: true, schema: { type: "string" } }
|
|
4282
|
+
],
|
|
4283
|
+
requestBody: {
|
|
4284
|
+
required: true,
|
|
4285
|
+
content: {
|
|
4286
|
+
"application/json": {
|
|
4287
|
+
schema: {
|
|
4288
|
+
type: "object",
|
|
4289
|
+
properties: { approved: { type: "boolean" } },
|
|
4290
|
+
required: ["approved"]
|
|
4291
|
+
}
|
|
4292
|
+
}
|
|
4293
|
+
}
|
|
4294
|
+
},
|
|
4295
|
+
responses: {
|
|
4296
|
+
"200": {
|
|
4297
|
+
description: "Approval resolved",
|
|
4298
|
+
content: {
|
|
4299
|
+
"application/json": {
|
|
4300
|
+
schema: {
|
|
4301
|
+
type: "object",
|
|
4302
|
+
properties: {
|
|
4303
|
+
ok: { type: "boolean" },
|
|
4304
|
+
approvalId: { type: "string" },
|
|
4305
|
+
approved: { type: "boolean" }
|
|
4306
|
+
}
|
|
4307
|
+
}
|
|
4308
|
+
}
|
|
4309
|
+
}
|
|
4310
|
+
},
|
|
4311
|
+
"404": {
|
|
4312
|
+
description: "Approval not found or expired",
|
|
4313
|
+
content: { "application/json": { schema: { $ref: "#/components/schemas/Error" } } }
|
|
4314
|
+
}
|
|
4315
|
+
}
|
|
4316
|
+
}
|
|
4317
|
+
},
|
|
4318
|
+
"/api/uploads/{key}": {
|
|
4319
|
+
get: {
|
|
4320
|
+
tags: ["Assets"],
|
|
4321
|
+
summary: "Retrieve an uploaded file",
|
|
4322
|
+
description: "Serves a file previously uploaded during a conversation. The key is returned in file content part references.",
|
|
4323
|
+
parameters: [
|
|
4324
|
+
{
|
|
4325
|
+
name: "key",
|
|
4326
|
+
in: "path",
|
|
4327
|
+
required: true,
|
|
4328
|
+
schema: { type: "string" },
|
|
4329
|
+
description: "Upload key (e.g. filename or storage path)"
|
|
4330
|
+
}
|
|
4331
|
+
],
|
|
4332
|
+
responses: {
|
|
4333
|
+
"200": {
|
|
4334
|
+
description: "File content with appropriate Content-Type",
|
|
4335
|
+
content: { "application/octet-stream": { schema: { type: "string", format: "binary" } } }
|
|
4336
|
+
},
|
|
4337
|
+
"404": {
|
|
4338
|
+
description: "Upload not found",
|
|
4339
|
+
content: { "application/json": { schema: { $ref: "#/components/schemas/Error" } } }
|
|
4340
|
+
}
|
|
4341
|
+
}
|
|
4342
|
+
}
|
|
4343
|
+
},
|
|
4344
|
+
"/api/cron/{jobName}": {
|
|
4345
|
+
get: {
|
|
4346
|
+
tags: ["Cron"],
|
|
4347
|
+
summary: "Trigger a cron job",
|
|
4348
|
+
description: "Triggers a named cron job defined in AGENT.md frontmatter. Supports continuation via the `continue` query parameter.",
|
|
4349
|
+
parameters: [
|
|
4350
|
+
{ name: "jobName", in: "path", required: true, schema: { type: "string" } },
|
|
4351
|
+
{
|
|
4352
|
+
name: "continue",
|
|
4353
|
+
in: "query",
|
|
4354
|
+
schema: { type: "string" },
|
|
4355
|
+
description: "Conversation ID to continue a previous cron run"
|
|
4356
|
+
}
|
|
4357
|
+
],
|
|
4358
|
+
responses: {
|
|
4359
|
+
"200": {
|
|
4360
|
+
description: "Cron job result",
|
|
4361
|
+
content: {
|
|
4362
|
+
"application/json": {
|
|
4363
|
+
schema: {
|
|
4364
|
+
type: "object",
|
|
4365
|
+
properties: {
|
|
4366
|
+
conversationId: { type: "string" },
|
|
4367
|
+
response: { type: "string" },
|
|
4368
|
+
steps: { type: "integer" },
|
|
4369
|
+
status: { type: "string" },
|
|
4370
|
+
continuation: { type: "string", description: "URL to continue this run" }
|
|
4371
|
+
}
|
|
4372
|
+
}
|
|
4373
|
+
}
|
|
4374
|
+
}
|
|
4375
|
+
},
|
|
4376
|
+
"404": {
|
|
4377
|
+
description: "Cron job not found",
|
|
4378
|
+
content: { "application/json": { schema: { $ref: "#/components/schemas/Error" } } }
|
|
4379
|
+
}
|
|
4380
|
+
}
|
|
4381
|
+
}
|
|
4382
|
+
}
|
|
4383
|
+
},
|
|
4384
|
+
tags: [
|
|
4385
|
+
{ name: "Health", description: "Server health check" },
|
|
4386
|
+
{ name: "Auth", description: "Session and authentication management" },
|
|
4387
|
+
{ name: "Conversations", description: "Create, list, read, rename, and delete conversations" },
|
|
4388
|
+
{
|
|
4389
|
+
name: "Messages",
|
|
4390
|
+
description: "Send messages and stream agent responses via SSE"
|
|
4391
|
+
},
|
|
4392
|
+
{ name: "Approvals", description: "Resolve gated tool approval requests" },
|
|
4393
|
+
{ name: "Assets", description: "Retrieve uploaded files" },
|
|
4394
|
+
{ name: "Cron", description: "Trigger cron jobs defined in AGENT.md" }
|
|
4395
|
+
]
|
|
4396
|
+
});
|
|
4397
|
+
var renderApiDocsHtml = (specUrl) => `<!DOCTYPE html>
|
|
4398
|
+
<html lang="en">
|
|
4399
|
+
<head>
|
|
4400
|
+
<meta charset="utf-8" />
|
|
4401
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
4402
|
+
<title>API Documentation</title>
|
|
4403
|
+
<style>body { margin: 0; }</style>
|
|
4404
|
+
</head>
|
|
4405
|
+
<body>
|
|
4406
|
+
<script id="api-reference" data-url="${specUrl}"></script>
|
|
4407
|
+
<script src="https://cdn.jsdelivr.net/npm/@scalar/api-reference"></script>
|
|
4408
|
+
</body>
|
|
4409
|
+
</html>`;
|
|
4410
|
+
|
|
4411
|
+
// src/index.ts
|
|
4412
|
+
import { createInterface } from "readline/promises";
|
|
4413
|
+
|
|
4414
|
+
// src/init-onboarding.ts
|
|
4415
|
+
import { stdin, stdout } from "process";
|
|
4416
|
+
import { input, password, select } from "@inquirer/prompts";
|
|
4417
|
+
import {
|
|
4418
|
+
fieldsForScope
|
|
4419
|
+
} from "@poncho-ai/sdk";
|
|
4420
|
+
var C = {
|
|
4421
|
+
reset: "\x1B[0m",
|
|
4422
|
+
bold: "\x1B[1m",
|
|
4423
|
+
dim: "\x1B[2m",
|
|
4424
|
+
cyan: "\x1B[36m"
|
|
4425
|
+
};
|
|
4426
|
+
var dim = (s) => `${C.dim}${s}${C.reset}`;
|
|
4427
|
+
var bold = (s) => `${C.bold}${s}${C.reset}`;
|
|
4428
|
+
var INPUT_CARET = "\xBB";
|
|
4429
|
+
var shouldAskField = (field, answers) => {
|
|
4430
|
+
if (!field.dependsOn) {
|
|
4431
|
+
return true;
|
|
4432
|
+
}
|
|
4433
|
+
const value = answers[field.dependsOn.fieldId];
|
|
4434
|
+
if (typeof field.dependsOn.equals !== "undefined") {
|
|
4435
|
+
return value === field.dependsOn.equals;
|
|
4436
|
+
}
|
|
4437
|
+
if (field.dependsOn.oneOf) {
|
|
4438
|
+
return field.dependsOn.oneOf.includes(value);
|
|
4439
|
+
}
|
|
4440
|
+
return true;
|
|
4441
|
+
};
|
|
4442
|
+
var parsePromptValue = (field, answer) => {
|
|
4443
|
+
if (field.kind === "boolean") {
|
|
4444
|
+
const normalized = answer.trim().toLowerCase();
|
|
4445
|
+
if (normalized === "y" || normalized === "yes" || normalized === "true") {
|
|
4446
|
+
return true;
|
|
3305
4447
|
}
|
|
3306
4448
|
if (normalized === "n" || normalized === "no" || normalized === "false") {
|
|
3307
4449
|
return false;
|
|
@@ -3521,7 +4663,20 @@ var buildConfigFromOnboardingAnswers = (answers) => {
|
|
|
3521
4663
|
telemetry
|
|
3522
4664
|
};
|
|
3523
4665
|
if (messagingPlatform !== "none") {
|
|
3524
|
-
|
|
4666
|
+
const channelConfig = {
|
|
4667
|
+
platform: messagingPlatform
|
|
4668
|
+
};
|
|
4669
|
+
if (messagingPlatform === "resend") {
|
|
4670
|
+
const mode = String(answers["messaging.resend.mode"] ?? "auto-reply");
|
|
4671
|
+
if (mode === "tool") {
|
|
4672
|
+
channelConfig.mode = "tool";
|
|
4673
|
+
}
|
|
4674
|
+
const recipientsRaw = String(answers["messaging.resend.allowedRecipients"] ?? "");
|
|
4675
|
+
if (recipientsRaw.trim().length > 0) {
|
|
4676
|
+
channelConfig.allowedRecipients = recipientsRaw.split(",").map((s) => s.trim()).filter(Boolean);
|
|
4677
|
+
}
|
|
4678
|
+
}
|
|
4679
|
+
config.messaging = [channelConfig];
|
|
3525
4680
|
}
|
|
3526
4681
|
return config;
|
|
3527
4682
|
};
|
|
@@ -3993,7 +5148,7 @@ cp .env.example .env
|
|
|
3993
5148
|
poncho dev
|
|
3994
5149
|
\`\`\`
|
|
3995
5150
|
|
|
3996
|
-
Open \`http://localhost:3000\` for the web UI.
|
|
5151
|
+
Open \`http://localhost:3000\` for the web UI, or \`http://localhost:3000/api/docs\` for interactive API documentation.
|
|
3997
5152
|
|
|
3998
5153
|
On your first interactive session, the agent introduces its configurable capabilities.
|
|
3999
5154
|
While a response is streaming, you can stop it:
|
|
@@ -4115,7 +5270,7 @@ Core files:
|
|
|
4115
5270
|
|
|
4116
5271
|
- \`AGENT.md\`: behavior, model selection, runtime guidance
|
|
4117
5272
|
- \`poncho.config.js\`: runtime config (storage, auth, telemetry, MCP, tools)
|
|
4118
|
-
- \`.env\`: secrets and environment variables
|
|
5273
|
+
- \`.env\`: secrets and environment variables (loaded before the harness starts, so \`process.env\` is available in skill scripts)
|
|
4119
5274
|
|
|
4120
5275
|
Example \`poncho.config.js\`:
|
|
4121
5276
|
|
|
@@ -4141,18 +5296,20 @@ export default {
|
|
|
4141
5296
|
auth: { type: "bearer", tokenEnv: "GITHUB_TOKEN" },
|
|
4142
5297
|
},
|
|
4143
5298
|
],
|
|
5299
|
+
// Tool access: true (available), false (disabled), 'approval' (requires human approval)
|
|
4144
5300
|
tools: {
|
|
4145
|
-
|
|
4146
|
-
|
|
4147
|
-
read_file: true,
|
|
4148
|
-
write_file: true, // still gated by environment/policy
|
|
4149
|
-
},
|
|
5301
|
+
write_file: true, // gated by environment for writes
|
|
5302
|
+
send_email: 'approval', // requires human approval
|
|
4150
5303
|
byEnvironment: {
|
|
4151
5304
|
production: {
|
|
4152
|
-
|
|
5305
|
+
write_file: false,
|
|
5306
|
+
},
|
|
5307
|
+
development: {
|
|
5308
|
+
send_email: true, // skip approval in dev
|
|
4153
5309
|
},
|
|
4154
5310
|
},
|
|
4155
5311
|
},
|
|
5312
|
+
// webUi: false, // Disable built-in UI for API-only deployments
|
|
4156
5313
|
};
|
|
4157
5314
|
\`\`\`
|
|
4158
5315
|
|
|
@@ -4211,6 +5368,26 @@ Connect your agent to Slack so it responds to @mentions:
|
|
|
4211
5368
|
messaging: [{ platform: 'slack' }]
|
|
4212
5369
|
\`\`\`
|
|
4213
5370
|
|
|
5371
|
+
## Messaging (Email via Resend)
|
|
5372
|
+
|
|
5373
|
+
Connect your agent to email so users can interact by sending emails:
|
|
5374
|
+
|
|
5375
|
+
1. Set up a domain and enable Inbound at [resend.com](https://resend.com)
|
|
5376
|
+
2. Create a webhook for \`email.received\` pointing to \`https://<your-url>/api/messaging/resend\`
|
|
5377
|
+
3. Install the Resend SDK: \`npm install resend\`
|
|
5378
|
+
4. Set env vars:
|
|
5379
|
+
\`\`\`
|
|
5380
|
+
RESEND_API_KEY=re_...
|
|
5381
|
+
RESEND_WEBHOOK_SECRET=whsec_...
|
|
5382
|
+
RESEND_FROM=Agent <agent@yourdomain.com>
|
|
5383
|
+
\`\`\`
|
|
5384
|
+
5. Add to \`poncho.config.js\`:
|
|
5385
|
+
\`\`\`javascript
|
|
5386
|
+
messaging: [{ platform: 'resend' }]
|
|
5387
|
+
\`\`\`
|
|
5388
|
+
|
|
5389
|
+
For full control over outbound emails, use **tool mode** (\`mode: 'tool'\`) \u2014 the agent gets a \`send_email\` tool instead of auto-replying. See the repo README for details.
|
|
5390
|
+
|
|
4214
5391
|
## Deployment
|
|
4215
5392
|
|
|
4216
5393
|
\`\`\`bash
|
|
@@ -4828,7 +6005,6 @@ var createRequestHandler = async (options) => {
|
|
|
4828
6005
|
const runOwners = /* @__PURE__ */ new Map();
|
|
4829
6006
|
const runConversations = /* @__PURE__ */ new Map();
|
|
4830
6007
|
const activeConversationRuns = /* @__PURE__ */ new Map();
|
|
4831
|
-
const pendingApprovals = /* @__PURE__ */ new Map();
|
|
4832
6008
|
const conversationEventStreams = /* @__PURE__ */ new Map();
|
|
4833
6009
|
const broadcastEvent = (conversationId, event) => {
|
|
4834
6010
|
let stream = conversationEventStreams.get(conversationId);
|
|
@@ -4860,51 +6036,19 @@ var createRequestHandler = async (options) => {
|
|
|
4860
6036
|
setTimeout(() => conversationEventStreams.delete(conversationId), 3e4);
|
|
4861
6037
|
}
|
|
4862
6038
|
};
|
|
4863
|
-
const persistConversationPendingApprovals = async (conversationId) => {
|
|
4864
|
-
const conversation = await conversationStore.get(conversationId);
|
|
4865
|
-
if (!conversation) {
|
|
4866
|
-
return;
|
|
4867
|
-
}
|
|
4868
|
-
conversation.pendingApprovals = Array.from(pendingApprovals.entries()).filter(
|
|
4869
|
-
([, pending]) => pending.ownerId === conversation.ownerId && pending.conversationId === conversationId
|
|
4870
|
-
).map(([approvalId, pending]) => ({
|
|
4871
|
-
approvalId,
|
|
4872
|
-
runId: pending.runId,
|
|
4873
|
-
tool: pending.tool,
|
|
4874
|
-
input: pending.input
|
|
4875
|
-
}));
|
|
4876
|
-
await conversationStore.update(conversation);
|
|
4877
|
-
};
|
|
4878
6039
|
const clearPendingApprovalsForConversation = async (conversationId) => {
|
|
4879
|
-
|
|
4880
|
-
|
|
4881
|
-
|
|
4882
|
-
|
|
4883
|
-
|
|
4884
|
-
pending.resolve(false);
|
|
6040
|
+
const conversation = await conversationStore.get(conversationId);
|
|
6041
|
+
if (!conversation) return;
|
|
6042
|
+
if (Array.isArray(conversation.pendingApprovals) && conversation.pendingApprovals.length > 0) {
|
|
6043
|
+
conversation.pendingApprovals = [];
|
|
6044
|
+
await conversationStore.update(conversation);
|
|
4885
6045
|
}
|
|
4886
|
-
await persistConversationPendingApprovals(conversationId);
|
|
4887
6046
|
};
|
|
4888
6047
|
const uploadStore = await createUploadStore(config?.uploads, workingDir);
|
|
4889
6048
|
const harness = new AgentHarness({
|
|
4890
6049
|
workingDir,
|
|
4891
6050
|
environment: resolveHarnessEnvironment(),
|
|
4892
|
-
uploadStore
|
|
4893
|
-
approvalHandler: async (request) => new Promise((resolveApproval) => {
|
|
4894
|
-
const ownerIdForRun = runOwners.get(request.runId) ?? "local-owner";
|
|
4895
|
-
const conversationIdForRun = runConversations.get(request.runId) ?? null;
|
|
4896
|
-
pendingApprovals.set(request.approvalId, {
|
|
4897
|
-
ownerId: ownerIdForRun,
|
|
4898
|
-
runId: request.runId,
|
|
4899
|
-
conversationId: conversationIdForRun,
|
|
4900
|
-
tool: request.tool,
|
|
4901
|
-
input: request.input,
|
|
4902
|
-
resolve: resolveApproval
|
|
4903
|
-
});
|
|
4904
|
-
if (conversationIdForRun) {
|
|
4905
|
-
void persistConversationPendingApprovals(conversationIdForRun);
|
|
4906
|
-
}
|
|
4907
|
-
})
|
|
6051
|
+
uploadStore
|
|
4908
6052
|
});
|
|
4909
6053
|
await harness.initialize();
|
|
4910
6054
|
const telemetry = new TelemetryEmitter(config?.telemetry);
|
|
@@ -4913,6 +6057,162 @@ var createRequestHandler = async (options) => {
|
|
|
4913
6057
|
workingDir,
|
|
4914
6058
|
agentId: identity.id
|
|
4915
6059
|
});
|
|
6060
|
+
const resumeRunFromCheckpoint = async (conversationId, conversation, checkpoint, toolResults) => {
|
|
6061
|
+
const abortController = new AbortController();
|
|
6062
|
+
activeConversationRuns.set(conversationId, {
|
|
6063
|
+
ownerId: conversation.ownerId,
|
|
6064
|
+
abortController,
|
|
6065
|
+
runId: null
|
|
6066
|
+
});
|
|
6067
|
+
let latestRunId = conversation.runtimeRunId ?? "";
|
|
6068
|
+
let assistantResponse = "";
|
|
6069
|
+
const toolTimeline = [];
|
|
6070
|
+
const sections = [];
|
|
6071
|
+
let currentText = "";
|
|
6072
|
+
let currentTools = [];
|
|
6073
|
+
let checkpointedRun = false;
|
|
6074
|
+
const baseMessages = checkpoint.baseMessageCount != null ? conversation.messages.slice(0, checkpoint.baseMessageCount) : [];
|
|
6075
|
+
const fullCheckpointMessages = [...baseMessages, ...checkpoint.checkpointMessages];
|
|
6076
|
+
try {
|
|
6077
|
+
for await (const event of harness.continueFromToolResult({
|
|
6078
|
+
messages: fullCheckpointMessages,
|
|
6079
|
+
toolResults,
|
|
6080
|
+
conversationId,
|
|
6081
|
+
abortSignal: abortController.signal
|
|
6082
|
+
})) {
|
|
6083
|
+
if (event.type === "run:started") {
|
|
6084
|
+
latestRunId = event.runId;
|
|
6085
|
+
runOwners.set(event.runId, conversation.ownerId);
|
|
6086
|
+
runConversations.set(event.runId, conversationId);
|
|
6087
|
+
const active = activeConversationRuns.get(conversationId);
|
|
6088
|
+
if (active && active.abortController === abortController) {
|
|
6089
|
+
active.runId = event.runId;
|
|
6090
|
+
}
|
|
6091
|
+
}
|
|
6092
|
+
if (event.type === "model:chunk") {
|
|
6093
|
+
if (currentTools.length > 0) {
|
|
6094
|
+
sections.push({ type: "tools", content: currentTools });
|
|
6095
|
+
currentTools = [];
|
|
6096
|
+
}
|
|
6097
|
+
assistantResponse += event.content;
|
|
6098
|
+
currentText += event.content;
|
|
6099
|
+
}
|
|
6100
|
+
if (event.type === "tool:started") {
|
|
6101
|
+
if (currentText.length > 0) {
|
|
6102
|
+
sections.push({ type: "text", content: currentText });
|
|
6103
|
+
currentText = "";
|
|
6104
|
+
}
|
|
6105
|
+
const toolText = `- start \`${event.tool}\``;
|
|
6106
|
+
toolTimeline.push(toolText);
|
|
6107
|
+
currentTools.push(toolText);
|
|
6108
|
+
}
|
|
6109
|
+
if (event.type === "tool:completed") {
|
|
6110
|
+
const toolText = `- done \`${event.tool}\` (${event.duration}ms)`;
|
|
6111
|
+
toolTimeline.push(toolText);
|
|
6112
|
+
currentTools.push(toolText);
|
|
6113
|
+
}
|
|
6114
|
+
if (event.type === "tool:error") {
|
|
6115
|
+
const toolText = `- error \`${event.tool}\`: ${event.error}`;
|
|
6116
|
+
toolTimeline.push(toolText);
|
|
6117
|
+
currentTools.push(toolText);
|
|
6118
|
+
}
|
|
6119
|
+
if (event.type === "tool:approval:required") {
|
|
6120
|
+
const toolText = `- approval required \`${event.tool}\``;
|
|
6121
|
+
toolTimeline.push(toolText);
|
|
6122
|
+
currentTools.push(toolText);
|
|
6123
|
+
}
|
|
6124
|
+
if (event.type === "tool:approval:checkpoint") {
|
|
6125
|
+
const conv = await conversationStore.get(conversationId);
|
|
6126
|
+
if (conv) {
|
|
6127
|
+
conv.pendingApprovals = [{
|
|
6128
|
+
approvalId: event.approvalId,
|
|
6129
|
+
runId: latestRunId,
|
|
6130
|
+
tool: event.tool,
|
|
6131
|
+
toolCallId: event.toolCallId,
|
|
6132
|
+
input: event.input,
|
|
6133
|
+
checkpointMessages: [...fullCheckpointMessages, ...event.checkpointMessages],
|
|
6134
|
+
baseMessageCount: 0,
|
|
6135
|
+
pendingToolCalls: event.pendingToolCalls
|
|
6136
|
+
}];
|
|
6137
|
+
conv.updatedAt = Date.now();
|
|
6138
|
+
await conversationStore.update(conv);
|
|
6139
|
+
}
|
|
6140
|
+
checkpointedRun = true;
|
|
6141
|
+
}
|
|
6142
|
+
if (event.type === "run:completed" && assistantResponse.length === 0 && event.result.response) {
|
|
6143
|
+
assistantResponse = event.result.response;
|
|
6144
|
+
}
|
|
6145
|
+
if (event.type === "run:error") {
|
|
6146
|
+
assistantResponse = assistantResponse || `[Error: ${event.error.message}]`;
|
|
6147
|
+
}
|
|
6148
|
+
await telemetry.emit(event);
|
|
6149
|
+
broadcastEvent(conversationId, event);
|
|
6150
|
+
}
|
|
6151
|
+
} catch (err) {
|
|
6152
|
+
console.error("[resume-run] error:", err instanceof Error ? err.message : err);
|
|
6153
|
+
assistantResponse = assistantResponse || `[Error: ${err instanceof Error ? err.message : "Unknown error"}]`;
|
|
6154
|
+
}
|
|
6155
|
+
if (currentTools.length > 0) {
|
|
6156
|
+
sections.push({ type: "tools", content: currentTools });
|
|
6157
|
+
}
|
|
6158
|
+
if (currentText.length > 0) {
|
|
6159
|
+
sections.push({ type: "text", content: currentText });
|
|
6160
|
+
}
|
|
6161
|
+
if (!checkpointedRun) {
|
|
6162
|
+
const conv = await conversationStore.get(conversationId);
|
|
6163
|
+
if (conv) {
|
|
6164
|
+
const prevMessages = conv.messages;
|
|
6165
|
+
const hasAssistantContent = assistantResponse.length > 0 || toolTimeline.length > 0 || sections.length > 0;
|
|
6166
|
+
if (hasAssistantContent) {
|
|
6167
|
+
const lastMsg = prevMessages[prevMessages.length - 1];
|
|
6168
|
+
if (lastMsg && lastMsg.role === "assistant" && lastMsg.metadata) {
|
|
6169
|
+
const existingToolActivity = lastMsg.metadata.toolActivity;
|
|
6170
|
+
const existingSections = lastMsg.metadata.sections;
|
|
6171
|
+
const mergedTimeline = [
|
|
6172
|
+
...Array.isArray(existingToolActivity) ? existingToolActivity : [],
|
|
6173
|
+
...toolTimeline
|
|
6174
|
+
];
|
|
6175
|
+
const mergedSections = [
|
|
6176
|
+
...Array.isArray(existingSections) ? existingSections : [],
|
|
6177
|
+
...sections
|
|
6178
|
+
];
|
|
6179
|
+
const mergedText = (typeof lastMsg.content === "string" ? lastMsg.content : "") + assistantResponse;
|
|
6180
|
+
conv.messages = [
|
|
6181
|
+
...prevMessages.slice(0, -1),
|
|
6182
|
+
{
|
|
6183
|
+
role: "assistant",
|
|
6184
|
+
content: mergedText,
|
|
6185
|
+
metadata: {
|
|
6186
|
+
toolActivity: mergedTimeline,
|
|
6187
|
+
sections: mergedSections.length > 0 ? mergedSections : void 0
|
|
6188
|
+
}
|
|
6189
|
+
}
|
|
6190
|
+
];
|
|
6191
|
+
} else {
|
|
6192
|
+
conv.messages = [
|
|
6193
|
+
...prevMessages,
|
|
6194
|
+
{
|
|
6195
|
+
role: "assistant",
|
|
6196
|
+
content: assistantResponse,
|
|
6197
|
+
metadata: toolTimeline.length > 0 || sections.length > 0 ? { toolActivity: toolTimeline, sections: sections.length > 0 ? sections : void 0 } : void 0
|
|
6198
|
+
}
|
|
6199
|
+
];
|
|
6200
|
+
}
|
|
6201
|
+
}
|
|
6202
|
+
conv.runtimeRunId = latestRunId || conv.runtimeRunId;
|
|
6203
|
+
conv.pendingApprovals = [];
|
|
6204
|
+
conv.updatedAt = Date.now();
|
|
6205
|
+
await conversationStore.update(conv);
|
|
6206
|
+
}
|
|
6207
|
+
}
|
|
6208
|
+
finishConversationStream(conversationId);
|
|
6209
|
+
activeConversationRuns.delete(conversationId);
|
|
6210
|
+
if (latestRunId) {
|
|
6211
|
+
runOwners.delete(latestRunId);
|
|
6212
|
+
runConversations.delete(latestRunId);
|
|
6213
|
+
}
|
|
6214
|
+
console.log("[resume-run] complete for", conversationId);
|
|
6215
|
+
};
|
|
4916
6216
|
const messagingRoutes = /* @__PURE__ */ new Map();
|
|
4917
6217
|
const messagingRouteRegistrar = (method, path, routeHandler) => {
|
|
4918
6218
|
let byMethod = messagingRoutes.get(path);
|
|
@@ -4942,20 +6242,167 @@ var createRequestHandler = async (options) => {
|
|
|
4942
6242
|
return { messages: [] };
|
|
4943
6243
|
},
|
|
4944
6244
|
async run(conversationId, input2) {
|
|
4945
|
-
|
|
4946
|
-
|
|
4947
|
-
|
|
6245
|
+
console.log("[messaging-runner] starting run for", conversationId, "task:", input2.task.slice(0, 80));
|
|
6246
|
+
const historyMessages = [...input2.messages];
|
|
6247
|
+
const userContent = input2.task;
|
|
6248
|
+
const updateConversation = async (patch) => {
|
|
6249
|
+
const fresh = await conversationStore.get(conversationId);
|
|
6250
|
+
if (!fresh) return;
|
|
6251
|
+
patch(fresh);
|
|
6252
|
+
fresh.updatedAt = Date.now();
|
|
6253
|
+
await conversationStore.update(fresh);
|
|
6254
|
+
};
|
|
6255
|
+
await updateConversation((c) => {
|
|
6256
|
+
c.messages = [...historyMessages, { role: "user", content: userContent }];
|
|
4948
6257
|
});
|
|
4949
|
-
|
|
4950
|
-
|
|
4951
|
-
|
|
4952
|
-
|
|
4953
|
-
|
|
4954
|
-
|
|
4955
|
-
|
|
6258
|
+
let latestRunId = "";
|
|
6259
|
+
let assistantResponse = "";
|
|
6260
|
+
const toolTimeline = [];
|
|
6261
|
+
const sections = [];
|
|
6262
|
+
let currentTools = [];
|
|
6263
|
+
let currentText = "";
|
|
6264
|
+
let checkpointedRun = false;
|
|
6265
|
+
const buildMessages = () => {
|
|
6266
|
+
const draftSections = [
|
|
6267
|
+
...sections.map((s) => ({
|
|
6268
|
+
type: s.type,
|
|
6269
|
+
content: Array.isArray(s.content) ? [...s.content] : s.content
|
|
6270
|
+
}))
|
|
4956
6271
|
];
|
|
4957
|
-
|
|
6272
|
+
if (currentTools.length > 0) {
|
|
6273
|
+
draftSections.push({ type: "tools", content: [...currentTools] });
|
|
6274
|
+
}
|
|
6275
|
+
if (currentText.length > 0) {
|
|
6276
|
+
draftSections.push({ type: "text", content: currentText });
|
|
6277
|
+
}
|
|
6278
|
+
const hasDraftContent = assistantResponse.length > 0 || toolTimeline.length > 0 || draftSections.length > 0;
|
|
6279
|
+
if (!hasDraftContent) {
|
|
6280
|
+
return [...historyMessages, { role: "user", content: userContent }];
|
|
6281
|
+
}
|
|
6282
|
+
return [
|
|
6283
|
+
...historyMessages,
|
|
6284
|
+
{ role: "user", content: userContent },
|
|
6285
|
+
{
|
|
6286
|
+
role: "assistant",
|
|
6287
|
+
content: assistantResponse,
|
|
6288
|
+
metadata: toolTimeline.length > 0 || draftSections.length > 0 ? {
|
|
6289
|
+
toolActivity: [...toolTimeline],
|
|
6290
|
+
sections: draftSections.length > 0 ? draftSections : void 0
|
|
6291
|
+
} : void 0
|
|
6292
|
+
}
|
|
6293
|
+
];
|
|
6294
|
+
};
|
|
6295
|
+
const persistDraftAssistantTurn = async () => {
|
|
6296
|
+
if (assistantResponse.length === 0 && toolTimeline.length === 0) return;
|
|
6297
|
+
await updateConversation((c) => {
|
|
6298
|
+
c.messages = buildMessages();
|
|
6299
|
+
});
|
|
6300
|
+
};
|
|
6301
|
+
const runInput = {
|
|
6302
|
+
task: input2.task,
|
|
6303
|
+
conversationId,
|
|
6304
|
+
messages: input2.messages,
|
|
6305
|
+
files: input2.files,
|
|
6306
|
+
parameters: input2.metadata ? {
|
|
6307
|
+
__messaging_platform: input2.metadata.platform,
|
|
6308
|
+
__messaging_sender_id: input2.metadata.sender.id,
|
|
6309
|
+
__messaging_sender_name: input2.metadata.sender.name ?? "",
|
|
6310
|
+
__messaging_thread_id: input2.metadata.threadId
|
|
6311
|
+
} : void 0
|
|
6312
|
+
};
|
|
6313
|
+
try {
|
|
6314
|
+
for await (const event of harness.runWithTelemetry(runInput)) {
|
|
6315
|
+
if (event.type === "run:started") {
|
|
6316
|
+
latestRunId = event.runId;
|
|
6317
|
+
runOwners.set(event.runId, "local-owner");
|
|
6318
|
+
runConversations.set(event.runId, conversationId);
|
|
6319
|
+
}
|
|
6320
|
+
if (event.type === "model:chunk") {
|
|
6321
|
+
if (currentTools.length > 0) {
|
|
6322
|
+
sections.push({ type: "tools", content: currentTools });
|
|
6323
|
+
currentTools = [];
|
|
6324
|
+
}
|
|
6325
|
+
assistantResponse += event.content;
|
|
6326
|
+
currentText += event.content;
|
|
6327
|
+
}
|
|
6328
|
+
if (event.type === "tool:started") {
|
|
6329
|
+
if (currentText.length > 0) {
|
|
6330
|
+
sections.push({ type: "text", content: currentText });
|
|
6331
|
+
currentText = "";
|
|
6332
|
+
}
|
|
6333
|
+
const toolText = `- start \`${event.tool}\``;
|
|
6334
|
+
toolTimeline.push(toolText);
|
|
6335
|
+
currentTools.push(toolText);
|
|
6336
|
+
}
|
|
6337
|
+
if (event.type === "tool:completed") {
|
|
6338
|
+
const toolText = `- done \`${event.tool}\` (${event.duration}ms)`;
|
|
6339
|
+
toolTimeline.push(toolText);
|
|
6340
|
+
currentTools.push(toolText);
|
|
6341
|
+
}
|
|
6342
|
+
if (event.type === "tool:error") {
|
|
6343
|
+
const toolText = `- error \`${event.tool}\`: ${event.error}`;
|
|
6344
|
+
toolTimeline.push(toolText);
|
|
6345
|
+
currentTools.push(toolText);
|
|
6346
|
+
}
|
|
6347
|
+
if (event.type === "step:completed") {
|
|
6348
|
+
await persistDraftAssistantTurn();
|
|
6349
|
+
}
|
|
6350
|
+
if (event.type === "tool:approval:required") {
|
|
6351
|
+
const toolText = `- approval required \`${event.tool}\``;
|
|
6352
|
+
toolTimeline.push(toolText);
|
|
6353
|
+
currentTools.push(toolText);
|
|
6354
|
+
await persistDraftAssistantTurn();
|
|
6355
|
+
}
|
|
6356
|
+
if (event.type === "tool:approval:checkpoint") {
|
|
6357
|
+
await updateConversation((c) => {
|
|
6358
|
+
c.messages = buildMessages();
|
|
6359
|
+
c.pendingApprovals = [{
|
|
6360
|
+
approvalId: event.approvalId,
|
|
6361
|
+
runId: latestRunId,
|
|
6362
|
+
tool: event.tool,
|
|
6363
|
+
toolCallId: event.toolCallId,
|
|
6364
|
+
input: event.input,
|
|
6365
|
+
checkpointMessages: event.checkpointMessages,
|
|
6366
|
+
baseMessageCount: historyMessages.length,
|
|
6367
|
+
pendingToolCalls: event.pendingToolCalls
|
|
6368
|
+
}];
|
|
6369
|
+
});
|
|
6370
|
+
checkpointedRun = true;
|
|
6371
|
+
}
|
|
6372
|
+
if (event.type === "run:completed" && assistantResponse.length === 0 && event.result.response) {
|
|
6373
|
+
assistantResponse = event.result.response;
|
|
6374
|
+
}
|
|
6375
|
+
if (event.type === "run:error") {
|
|
6376
|
+
assistantResponse = assistantResponse || `[Error: ${event.error.message}]`;
|
|
6377
|
+
}
|
|
6378
|
+
broadcastEvent(conversationId, event);
|
|
6379
|
+
}
|
|
6380
|
+
} catch (err) {
|
|
6381
|
+
console.error("[messaging-runner] run failed:", err instanceof Error ? err.message : err);
|
|
6382
|
+
assistantResponse = assistantResponse || `[Error: ${err instanceof Error ? err.message : "Unknown error"}]`;
|
|
6383
|
+
}
|
|
6384
|
+
if (currentTools.length > 0) {
|
|
6385
|
+
sections.push({ type: "tools", content: currentTools });
|
|
6386
|
+
currentTools = [];
|
|
6387
|
+
}
|
|
6388
|
+
if (currentText.length > 0) {
|
|
6389
|
+
sections.push({ type: "text", content: currentText });
|
|
6390
|
+
currentText = "";
|
|
4958
6391
|
}
|
|
6392
|
+
if (!checkpointedRun) {
|
|
6393
|
+
await updateConversation((c) => {
|
|
6394
|
+
c.messages = buildMessages();
|
|
6395
|
+
c.runtimeRunId = latestRunId || c.runtimeRunId;
|
|
6396
|
+
c.pendingApprovals = [];
|
|
6397
|
+
});
|
|
6398
|
+
}
|
|
6399
|
+
finishConversationStream(conversationId);
|
|
6400
|
+
if (latestRunId) {
|
|
6401
|
+
runOwners.delete(latestRunId);
|
|
6402
|
+
runConversations.delete(latestRunId);
|
|
6403
|
+
}
|
|
6404
|
+
console.log("[messaging-runner] run complete, response length:", assistantResponse.length);
|
|
6405
|
+
const response = assistantResponse;
|
|
4959
6406
|
return { response };
|
|
4960
6407
|
}
|
|
4961
6408
|
};
|
|
@@ -4982,7 +6429,8 @@ var createRequestHandler = async (options) => {
|
|
|
4982
6429
|
const bridge = new AgentBridge({
|
|
4983
6430
|
adapter,
|
|
4984
6431
|
runner: messagingRunner,
|
|
4985
|
-
waitUntil: waitUntilHook
|
|
6432
|
+
waitUntil: waitUntilHook,
|
|
6433
|
+
ownerId: "local-owner"
|
|
4986
6434
|
});
|
|
4987
6435
|
adapter.registerRoutes(messagingRouteRegistrar);
|
|
4988
6436
|
try {
|
|
@@ -4994,6 +6442,37 @@ var createRequestHandler = async (options) => {
|
|
|
4994
6442
|
` Slack messaging disabled: ${err instanceof Error ? err.message : String(err)}`
|
|
4995
6443
|
);
|
|
4996
6444
|
}
|
|
6445
|
+
} else if (channelConfig.platform === "resend") {
|
|
6446
|
+
const adapter = new ResendAdapter({
|
|
6447
|
+
apiKeyEnv: channelConfig.apiKeyEnv,
|
|
6448
|
+
webhookSecretEnv: channelConfig.webhookSecretEnv,
|
|
6449
|
+
fromEnv: channelConfig.fromEnv,
|
|
6450
|
+
allowedSenders: channelConfig.allowedSenders,
|
|
6451
|
+
mode: channelConfig.mode,
|
|
6452
|
+
allowedRecipients: channelConfig.allowedRecipients,
|
|
6453
|
+
maxSendsPerRun: channelConfig.maxSendsPerRun
|
|
6454
|
+
});
|
|
6455
|
+
const bridge = new AgentBridge({
|
|
6456
|
+
adapter,
|
|
6457
|
+
runner: messagingRunner,
|
|
6458
|
+
waitUntil: waitUntilHook,
|
|
6459
|
+
ownerId: "local-owner"
|
|
6460
|
+
});
|
|
6461
|
+
adapter.registerRoutes(messagingRouteRegistrar);
|
|
6462
|
+
try {
|
|
6463
|
+
await bridge.start();
|
|
6464
|
+
messagingBridges.push(bridge);
|
|
6465
|
+
const adapterTools = adapter.getToolDefinitions?.() ?? [];
|
|
6466
|
+
if (adapterTools.length > 0) {
|
|
6467
|
+
harness.registerTools(adapterTools);
|
|
6468
|
+
}
|
|
6469
|
+
const modeLabel = channelConfig.mode === "tool" ? "tool" : "auto-reply";
|
|
6470
|
+
console.log(` Resend email messaging enabled at /api/messaging/resend (mode: ${modeLabel})`);
|
|
6471
|
+
} catch (err) {
|
|
6472
|
+
console.warn(
|
|
6473
|
+
` Resend email messaging disabled: ${err instanceof Error ? err.message : String(err)}`
|
|
6474
|
+
);
|
|
6475
|
+
}
|
|
4997
6476
|
}
|
|
4998
6477
|
}
|
|
4999
6478
|
}
|
|
@@ -5002,6 +6481,7 @@ var createRequestHandler = async (options) => {
|
|
|
5002
6481
|
const authToken = process.env.PONCHO_AUTH_TOKEN ?? "";
|
|
5003
6482
|
const authRequired = config?.auth?.required ?? false;
|
|
5004
6483
|
const requireAuth = authRequired && authToken.length > 0;
|
|
6484
|
+
const webUiEnabled = config?.webUi !== false;
|
|
5005
6485
|
const isProduction = resolveHarnessEnvironment() === "production";
|
|
5006
6486
|
const secureCookies = isProduction;
|
|
5007
6487
|
const validateBearerToken = (authHeader) => {
|
|
@@ -5023,35 +6503,45 @@ var createRequestHandler = async (options) => {
|
|
|
5023
6503
|
return;
|
|
5024
6504
|
}
|
|
5025
6505
|
const [pathname] = request.url.split("?");
|
|
5026
|
-
if (
|
|
5027
|
-
|
|
5028
|
-
|
|
5029
|
-
|
|
5030
|
-
|
|
5031
|
-
|
|
5032
|
-
|
|
5033
|
-
|
|
5034
|
-
|
|
5035
|
-
|
|
5036
|
-
|
|
5037
|
-
|
|
5038
|
-
|
|
5039
|
-
|
|
5040
|
-
|
|
5041
|
-
|
|
6506
|
+
if (webUiEnabled) {
|
|
6507
|
+
if (request.method === "GET" && (pathname === "/" || pathname.startsWith("/c/"))) {
|
|
6508
|
+
writeHtml(response, 200, renderWebUiHtml({ agentName }));
|
|
6509
|
+
return;
|
|
6510
|
+
}
|
|
6511
|
+
if (pathname === "/manifest.json" && request.method === "GET") {
|
|
6512
|
+
response.writeHead(200, { "Content-Type": "application/manifest+json" });
|
|
6513
|
+
response.end(renderManifest({ agentName }));
|
|
6514
|
+
return;
|
|
6515
|
+
}
|
|
6516
|
+
if (pathname === "/sw.js" && request.method === "GET") {
|
|
6517
|
+
response.writeHead(200, {
|
|
6518
|
+
"Content-Type": "application/javascript",
|
|
6519
|
+
"Service-Worker-Allowed": "/"
|
|
6520
|
+
});
|
|
6521
|
+
response.end(renderServiceWorker());
|
|
6522
|
+
return;
|
|
6523
|
+
}
|
|
6524
|
+
if (pathname === "/icon.svg" && request.method === "GET") {
|
|
6525
|
+
response.writeHead(200, { "Content-Type": "image/svg+xml" });
|
|
6526
|
+
response.end(renderIconSvg({ agentName }));
|
|
6527
|
+
return;
|
|
6528
|
+
}
|
|
6529
|
+
if ((pathname === "/icon-192.png" || pathname === "/icon-512.png") && request.method === "GET") {
|
|
6530
|
+
response.writeHead(302, { Location: "/icon.svg" });
|
|
6531
|
+
response.end();
|
|
6532
|
+
return;
|
|
6533
|
+
}
|
|
5042
6534
|
}
|
|
5043
|
-
if (pathname === "/
|
|
5044
|
-
response
|
|
5045
|
-
response.end(renderIconSvg({ agentName }));
|
|
6535
|
+
if (pathname === "/health" && request.method === "GET") {
|
|
6536
|
+
writeJson(response, 200, { status: "ok" });
|
|
5046
6537
|
return;
|
|
5047
6538
|
}
|
|
5048
|
-
if (
|
|
5049
|
-
response
|
|
5050
|
-
response.end();
|
|
6539
|
+
if (pathname === "/api/openapi.json" && request.method === "GET") {
|
|
6540
|
+
writeJson(response, 200, buildOpenApiSpec({ agentName }));
|
|
5051
6541
|
return;
|
|
5052
6542
|
}
|
|
5053
|
-
if (pathname === "/
|
|
5054
|
-
|
|
6543
|
+
if (pathname === "/api/docs" && request.method === "GET") {
|
|
6544
|
+
writeHtml(response, 200, renderApiDocsHtml("/api/openapi.json"));
|
|
5055
6545
|
return;
|
|
5056
6546
|
}
|
|
5057
6547
|
const messagingByMethod = messagingRoutes.get(pathname ?? "");
|
|
@@ -5168,7 +6658,8 @@ var createRequestHandler = async (options) => {
|
|
|
5168
6658
|
tenantId: conversation.tenantId,
|
|
5169
6659
|
createdAt: conversation.createdAt,
|
|
5170
6660
|
updatedAt: conversation.updatedAt,
|
|
5171
|
-
messageCount: conversation.messages.length
|
|
6661
|
+
messageCount: conversation.messages.length,
|
|
6662
|
+
hasPendingApprovals: Array.isArray(conversation.pendingApprovals) && conversation.pendingApprovals.length > 0
|
|
5172
6663
|
}))
|
|
5173
6664
|
});
|
|
5174
6665
|
return;
|
|
@@ -5192,36 +6683,77 @@ var createRequestHandler = async (options) => {
|
|
|
5192
6683
|
const approvalMatch = pathname.match(/^\/api\/approvals\/([^/]+)$/);
|
|
5193
6684
|
if (approvalMatch && request.method === "POST") {
|
|
5194
6685
|
const approvalId = decodeURIComponent(approvalMatch[1] ?? "");
|
|
5195
|
-
const
|
|
5196
|
-
|
|
5197
|
-
|
|
5198
|
-
|
|
5199
|
-
|
|
5200
|
-
|
|
5201
|
-
|
|
5202
|
-
|
|
5203
|
-
|
|
5204
|
-
|
|
5205
|
-
|
|
5206
|
-
|
|
5207
|
-
conversation.pendingApprovals = next;
|
|
5208
|
-
await conversationStore.update(conversation);
|
|
5209
|
-
prunedStale = true;
|
|
5210
|
-
}
|
|
6686
|
+
const body = await readRequestBody(request);
|
|
6687
|
+
const approved = body.approved === true;
|
|
6688
|
+
const conversations = await conversationStore.list(ownerId);
|
|
6689
|
+
let foundConversation;
|
|
6690
|
+
let foundApproval;
|
|
6691
|
+
for (const conv of conversations) {
|
|
6692
|
+
if (!Array.isArray(conv.pendingApprovals)) continue;
|
|
6693
|
+
const match = conv.pendingApprovals.find((a) => a.approvalId === approvalId);
|
|
6694
|
+
if (match) {
|
|
6695
|
+
foundConversation = conv;
|
|
6696
|
+
foundApproval = match;
|
|
6697
|
+
break;
|
|
5211
6698
|
}
|
|
6699
|
+
}
|
|
6700
|
+
if (!foundConversation || !foundApproval) {
|
|
5212
6701
|
writeJson(response, 404, {
|
|
5213
6702
|
code: "APPROVAL_NOT_FOUND",
|
|
5214
|
-
message:
|
|
6703
|
+
message: "Approval request not found"
|
|
5215
6704
|
});
|
|
5216
6705
|
return;
|
|
5217
6706
|
}
|
|
5218
|
-
const
|
|
5219
|
-
|
|
5220
|
-
|
|
5221
|
-
|
|
5222
|
-
|
|
6707
|
+
const conversationId = foundConversation.conversationId;
|
|
6708
|
+
if (!foundApproval.checkpointMessages || !foundApproval.toolCallId) {
|
|
6709
|
+
foundConversation.pendingApprovals = (foundConversation.pendingApprovals ?? []).filter((a) => a.approvalId !== approvalId);
|
|
6710
|
+
await conversationStore.update(foundConversation);
|
|
6711
|
+
writeJson(response, 404, {
|
|
6712
|
+
code: "APPROVAL_NOT_FOUND",
|
|
6713
|
+
message: "Approval request is no longer active (no checkpoint data)"
|
|
6714
|
+
});
|
|
6715
|
+
return;
|
|
5223
6716
|
}
|
|
5224
|
-
|
|
6717
|
+
foundConversation.pendingApprovals = (foundConversation.pendingApprovals ?? []).filter((a) => a.approvalId !== approvalId);
|
|
6718
|
+
await conversationStore.update(foundConversation);
|
|
6719
|
+
broadcastEvent(
|
|
6720
|
+
conversationId,
|
|
6721
|
+
approved ? { type: "tool:approval:granted", approvalId } : { type: "tool:approval:denied", approvalId }
|
|
6722
|
+
);
|
|
6723
|
+
void (async () => {
|
|
6724
|
+
let toolResults;
|
|
6725
|
+
if (approved) {
|
|
6726
|
+
const toolContext = {
|
|
6727
|
+
runId: foundApproval.runId,
|
|
6728
|
+
agentId: identity.id,
|
|
6729
|
+
step: 0,
|
|
6730
|
+
workingDir,
|
|
6731
|
+
parameters: {}
|
|
6732
|
+
};
|
|
6733
|
+
const execResults = await harness.executeTools(
|
|
6734
|
+
[{ id: foundApproval.toolCallId, name: foundApproval.tool, input: foundApproval.input }],
|
|
6735
|
+
toolContext
|
|
6736
|
+
);
|
|
6737
|
+
toolResults = execResults.map((r) => ({
|
|
6738
|
+
callId: r.callId,
|
|
6739
|
+
toolName: r.tool,
|
|
6740
|
+
result: r.output,
|
|
6741
|
+
error: r.error
|
|
6742
|
+
}));
|
|
6743
|
+
} else {
|
|
6744
|
+
toolResults = [{
|
|
6745
|
+
callId: foundApproval.toolCallId,
|
|
6746
|
+
toolName: foundApproval.tool,
|
|
6747
|
+
error: "Tool execution denied by user"
|
|
6748
|
+
}];
|
|
6749
|
+
}
|
|
6750
|
+
await resumeRunFromCheckpoint(
|
|
6751
|
+
conversationId,
|
|
6752
|
+
foundConversation,
|
|
6753
|
+
foundApproval,
|
|
6754
|
+
toolResults
|
|
6755
|
+
);
|
|
6756
|
+
})();
|
|
5225
6757
|
writeJson(response, 200, { ok: true, approvalId, approved });
|
|
5226
6758
|
return;
|
|
5227
6759
|
}
|
|
@@ -5249,12 +6781,15 @@ var createRequestHandler = async (options) => {
|
|
|
5249
6781
|
response.end();
|
|
5250
6782
|
return;
|
|
5251
6783
|
}
|
|
5252
|
-
|
|
5253
|
-
|
|
5254
|
-
|
|
5255
|
-
|
|
5256
|
-
|
|
5257
|
-
|
|
6784
|
+
const liveOnly = (request.url ?? "").includes("live_only=true");
|
|
6785
|
+
if (!liveOnly) {
|
|
6786
|
+
for (const bufferedEvent of stream.buffer) {
|
|
6787
|
+
try {
|
|
6788
|
+
response.write(formatSseEvent(bufferedEvent));
|
|
6789
|
+
} catch {
|
|
6790
|
+
response.end();
|
|
6791
|
+
return;
|
|
6792
|
+
}
|
|
5258
6793
|
}
|
|
5259
6794
|
}
|
|
5260
6795
|
if (stream.finished) {
|
|
@@ -5280,29 +6815,20 @@ var createRequestHandler = async (options) => {
|
|
|
5280
6815
|
return;
|
|
5281
6816
|
}
|
|
5282
6817
|
if (request.method === "GET") {
|
|
5283
|
-
const storedPending = Array.isArray(conversation.pendingApprovals) ? conversation.pendingApprovals
|
|
5284
|
-
|
|
5285
|
-
|
|
5286
|
-
|
|
5287
|
-
|
|
5288
|
-
|
|
5289
|
-
|
|
5290
|
-
|
|
5291
|
-
}));
|
|
5292
|
-
const mergedPendingById = /* @__PURE__ */ new Map();
|
|
5293
|
-
for (const approval of storedPending) {
|
|
5294
|
-
if (approval && typeof approval.approvalId === "string") {
|
|
5295
|
-
mergedPendingById.set(approval.approvalId, approval);
|
|
5296
|
-
}
|
|
5297
|
-
}
|
|
5298
|
-
for (const approval of livePending) {
|
|
5299
|
-
mergedPendingById.set(approval.approvalId, approval);
|
|
5300
|
-
}
|
|
6818
|
+
const storedPending = Array.isArray(conversation.pendingApprovals) ? conversation.pendingApprovals.map((a) => ({
|
|
6819
|
+
approvalId: a.approvalId,
|
|
6820
|
+
runId: a.runId,
|
|
6821
|
+
tool: a.tool,
|
|
6822
|
+
input: a.input
|
|
6823
|
+
})) : [];
|
|
6824
|
+
const activeStream = conversationEventStreams.get(conversationId);
|
|
6825
|
+
const hasActiveRun = !!activeStream && !activeStream.finished;
|
|
5301
6826
|
writeJson(response, 200, {
|
|
5302
6827
|
conversation: {
|
|
5303
6828
|
...conversation,
|
|
5304
|
-
pendingApprovals:
|
|
5305
|
-
}
|
|
6829
|
+
pendingApprovals: storedPending
|
|
6830
|
+
},
|
|
6831
|
+
hasActiveRun
|
|
5306
6832
|
});
|
|
5307
6833
|
return;
|
|
5308
6834
|
}
|
|
@@ -5484,6 +7010,7 @@ var createRequestHandler = async (options) => {
|
|
|
5484
7010
|
let currentText = "";
|
|
5485
7011
|
let currentTools = [];
|
|
5486
7012
|
let runCancelled = false;
|
|
7013
|
+
let checkpointedRun = false;
|
|
5487
7014
|
let userContent = messageText;
|
|
5488
7015
|
if (files.length > 0) {
|
|
5489
7016
|
try {
|
|
@@ -5563,6 +7090,7 @@ var createRequestHandler = async (options) => {
|
|
|
5563
7090
|
})).filter((item) => item.content.length > 0);
|
|
5564
7091
|
for await (const event of harness.runWithTelemetry({
|
|
5565
7092
|
task: messageText,
|
|
7093
|
+
conversationId,
|
|
5566
7094
|
parameters: {
|
|
5567
7095
|
...bodyParameters ?? {},
|
|
5568
7096
|
__conversationRecallCorpus: recallCorpus,
|
|
@@ -5620,17 +7148,36 @@ var createRequestHandler = async (options) => {
|
|
|
5620
7148
|
currentTools.push(toolText);
|
|
5621
7149
|
await persistDraftAssistantTurn();
|
|
5622
7150
|
}
|
|
5623
|
-
if (event.type === "tool:approval:
|
|
5624
|
-
const
|
|
5625
|
-
|
|
5626
|
-
|
|
5627
|
-
|
|
5628
|
-
|
|
5629
|
-
|
|
5630
|
-
|
|
5631
|
-
|
|
5632
|
-
|
|
5633
|
-
|
|
7151
|
+
if (event.type === "tool:approval:checkpoint") {
|
|
7152
|
+
const checkpointSections = [...sections];
|
|
7153
|
+
if (currentTools.length > 0) {
|
|
7154
|
+
checkpointSections.push({ type: "tools", content: [...currentTools] });
|
|
7155
|
+
}
|
|
7156
|
+
if (currentText.length > 0) {
|
|
7157
|
+
checkpointSections.push({ type: "text", content: currentText });
|
|
7158
|
+
}
|
|
7159
|
+
conversation.messages = [
|
|
7160
|
+
...historyMessages,
|
|
7161
|
+
{ role: "user", content: userContent },
|
|
7162
|
+
...assistantResponse.length > 0 || toolTimeline.length > 0 || checkpointSections.length > 0 ? [{
|
|
7163
|
+
role: "assistant",
|
|
7164
|
+
content: assistantResponse,
|
|
7165
|
+
metadata: toolTimeline.length > 0 || checkpointSections.length > 0 ? { toolActivity: [...toolTimeline], sections: checkpointSections.length > 0 ? checkpointSections : void 0 } : void 0
|
|
7166
|
+
}] : []
|
|
7167
|
+
];
|
|
7168
|
+
conversation.pendingApprovals = [{
|
|
7169
|
+
approvalId: event.approvalId,
|
|
7170
|
+
runId: latestRunId,
|
|
7171
|
+
tool: event.tool,
|
|
7172
|
+
toolCallId: event.toolCallId,
|
|
7173
|
+
input: event.input,
|
|
7174
|
+
checkpointMessages: event.checkpointMessages,
|
|
7175
|
+
baseMessageCount: historyMessages.length,
|
|
7176
|
+
pendingToolCalls: event.pendingToolCalls
|
|
7177
|
+
}];
|
|
7178
|
+
conversation.updatedAt = Date.now();
|
|
7179
|
+
await conversationStore.update(conversation);
|
|
7180
|
+
checkpointedRun = true;
|
|
5634
7181
|
}
|
|
5635
7182
|
if (event.type === "run:completed" && assistantResponse.length === 0 && event.result.response) {
|
|
5636
7183
|
assistantResponse = event.result.response;
|
|
@@ -5648,23 +7195,25 @@ var createRequestHandler = async (options) => {
|
|
|
5648
7195
|
if (currentText.length > 0) {
|
|
5649
7196
|
sections.push({ type: "text", content: currentText });
|
|
5650
7197
|
}
|
|
5651
|
-
|
|
5652
|
-
|
|
5653
|
-
|
|
5654
|
-
|
|
5655
|
-
|
|
5656
|
-
|
|
5657
|
-
|
|
5658
|
-
|
|
5659
|
-
|
|
5660
|
-
|
|
5661
|
-
|
|
5662
|
-
|
|
5663
|
-
|
|
5664
|
-
|
|
5665
|
-
|
|
5666
|
-
|
|
5667
|
-
|
|
7198
|
+
if (!checkpointedRun) {
|
|
7199
|
+
const hasAssistantContent = assistantResponse.length > 0 || toolTimeline.length > 0 || sections.length > 0;
|
|
7200
|
+
conversation.messages = hasAssistantContent ? [
|
|
7201
|
+
...historyMessages,
|
|
7202
|
+
{ role: "user", content: userContent },
|
|
7203
|
+
{
|
|
7204
|
+
role: "assistant",
|
|
7205
|
+
content: assistantResponse,
|
|
7206
|
+
metadata: toolTimeline.length > 0 || sections.length > 0 ? {
|
|
7207
|
+
toolActivity: toolTimeline,
|
|
7208
|
+
sections: sections.length > 0 ? sections : void 0
|
|
7209
|
+
} : void 0
|
|
7210
|
+
}
|
|
7211
|
+
] : [...historyMessages, { role: "user", content: userContent }];
|
|
7212
|
+
conversation.runtimeRunId = latestRunId || conversation.runtimeRunId;
|
|
7213
|
+
conversation.pendingApprovals = [];
|
|
7214
|
+
conversation.updatedAt = Date.now();
|
|
7215
|
+
await conversationStore.update(conversation);
|
|
7216
|
+
}
|
|
5668
7217
|
} catch (error) {
|
|
5669
7218
|
if (abortController.signal.aborted || runCancelled) {
|
|
5670
7219
|
const fallbackSections = [...sections];
|
|
@@ -5735,7 +7284,6 @@ var createRequestHandler = async (options) => {
|
|
|
5735
7284
|
activeConversationRuns.delete(conversationId);
|
|
5736
7285
|
}
|
|
5737
7286
|
finishConversationStream(conversationId);
|
|
5738
|
-
await persistConversationPendingApprovals(conversationId);
|
|
5739
7287
|
if (latestRunId) {
|
|
5740
7288
|
runOwners.delete(latestRunId);
|
|
5741
7289
|
runConversations.delete(latestRunId);
|
|
@@ -5807,6 +7355,7 @@ var createRequestHandler = async (options) => {
|
|
|
5807
7355
|
const softDeadlineMs = platformMaxDurationSec > 0 ? platformMaxDurationSec * 800 : 0;
|
|
5808
7356
|
for await (const event of harness.runWithTelemetry({
|
|
5809
7357
|
task: cronJob.task,
|
|
7358
|
+
conversationId: conversation.conversationId,
|
|
5810
7359
|
parameters: { __activeConversationId: conversation.conversationId },
|
|
5811
7360
|
messages: historyMessages,
|
|
5812
7361
|
abortSignal: abortController.signal
|
|
@@ -5968,6 +7517,7 @@ var startDevServer = async (port, options) => {
|
|
|
5968
7517
|
let currentText = "";
|
|
5969
7518
|
for await (const event of harness.runWithTelemetry({
|
|
5970
7519
|
task: config.task,
|
|
7520
|
+
conversationId: conversation.conversationId,
|
|
5971
7521
|
parameters: { __activeConversationId: conversation.conversationId },
|
|
5972
7522
|
messages: []
|
|
5973
7523
|
})) {
|
|
@@ -6132,33 +7682,16 @@ Error: ${event.error.message}
|
|
|
6132
7682
|
var runInteractive = async (workingDir, params) => {
|
|
6133
7683
|
dotenv.config({ path: resolve3(workingDir, ".env") });
|
|
6134
7684
|
const config = await loadPonchoConfig(workingDir);
|
|
6135
|
-
let pendingApproval = null;
|
|
6136
|
-
let onApprovalRequest = null;
|
|
6137
|
-
const approvalHandler = async (request) => {
|
|
6138
|
-
return new Promise((resolveApproval) => {
|
|
6139
|
-
const req = {
|
|
6140
|
-
tool: request.tool,
|
|
6141
|
-
input: request.input,
|
|
6142
|
-
approvalId: request.approvalId,
|
|
6143
|
-
resolve: resolveApproval
|
|
6144
|
-
};
|
|
6145
|
-
pendingApproval = req;
|
|
6146
|
-
if (onApprovalRequest) {
|
|
6147
|
-
onApprovalRequest(req);
|
|
6148
|
-
}
|
|
6149
|
-
});
|
|
6150
|
-
};
|
|
6151
7685
|
const uploadStore = await createUploadStore(config?.uploads, workingDir);
|
|
6152
7686
|
const harness = new AgentHarness({
|
|
6153
7687
|
workingDir,
|
|
6154
7688
|
environment: resolveHarnessEnvironment(),
|
|
6155
|
-
approvalHandler,
|
|
6156
7689
|
uploadStore
|
|
6157
7690
|
});
|
|
6158
7691
|
await harness.initialize();
|
|
6159
7692
|
const identity = await ensureAgentIdentity2(workingDir);
|
|
6160
7693
|
try {
|
|
6161
|
-
const { runInteractiveInk } = await import("./run-interactive-ink-
|
|
7694
|
+
const { runInteractiveInk } = await import("./run-interactive-ink-7ULE5JJI.js");
|
|
6162
7695
|
await runInteractiveInk({
|
|
6163
7696
|
harness,
|
|
6164
7697
|
params,
|
|
@@ -6167,13 +7700,7 @@ var runInteractive = async (workingDir, params) => {
|
|
|
6167
7700
|
conversationStore: createConversationStore(resolveStateConfig(config), {
|
|
6168
7701
|
workingDir,
|
|
6169
7702
|
agentId: identity.id
|
|
6170
|
-
})
|
|
6171
|
-
onSetApprovalCallback: (cb) => {
|
|
6172
|
-
onApprovalRequest = cb;
|
|
6173
|
-
if (pendingApproval) {
|
|
6174
|
-
cb(pendingApproval);
|
|
6175
|
-
}
|
|
6176
|
-
}
|
|
7703
|
+
})
|
|
6177
7704
|
});
|
|
6178
7705
|
} finally {
|
|
6179
7706
|
await harness.shutdown();
|