@poncho-ai/cli 0.13.0 → 0.14.0
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 +5 -5
- package/CHANGELOG.md +23 -0
- package/dist/{chunk-CUCEDHME.js → chunk-A32BXZKP.js} +1467 -208
- package/dist/cli.js +1 -1
- package/dist/index.js +1 -1
- package/dist/{run-interactive-ink-VZBOYJYS.js → run-interactive-ink-SLWDVTDX.js} +1 -1
- package/package.json +2 -2
- package/src/api-docs.ts +674 -0
- package/src/index.ts +295 -52
- package/src/init-onboarding.ts +14 -1
- package/src/web-ui.ts +559 -155
|
@@ -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,168 @@ 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
|
+
--approve: #78e7a6;
|
|
343
|
+
--approve-border: rgba(58,208,122,0.45);
|
|
344
|
+
--deny: #f59b9b;
|
|
345
|
+
--deny-border: rgba(224,95,95,0.45);
|
|
346
|
+
|
|
347
|
+
--scrollbar: rgba(255,255,255,0.1);
|
|
348
|
+
--scrollbar-hover: rgba(255,255,255,0.16);
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
@media (prefers-color-scheme: light) {
|
|
352
|
+
:root {
|
|
353
|
+
--bg: #ffffff;
|
|
354
|
+
--bg-alt: #f5f5f5;
|
|
355
|
+
--bg-elevated: #e8e8e8;
|
|
356
|
+
|
|
357
|
+
--fg: #1a1a1a;
|
|
358
|
+
--fg-strong: #000;
|
|
359
|
+
--fg-2: #666;
|
|
360
|
+
--fg-3: #555;
|
|
361
|
+
--fg-4: #777;
|
|
362
|
+
--fg-5: #888;
|
|
363
|
+
--fg-6: #888;
|
|
364
|
+
--fg-7: #aaa;
|
|
365
|
+
--fg-8: #bbb;
|
|
366
|
+
|
|
367
|
+
--fg-tool: #666;
|
|
368
|
+
--fg-tool-code: #444;
|
|
369
|
+
--fg-tool-item: #333;
|
|
370
|
+
--fg-approval-label: #666;
|
|
371
|
+
--fg-approval-input: #444;
|
|
372
|
+
--fg-approval-btn: #1a1a1a;
|
|
373
|
+
|
|
374
|
+
--accent: #1a1a1a;
|
|
375
|
+
--accent-fg: #fff;
|
|
376
|
+
--accent-hover: #000;
|
|
377
|
+
|
|
378
|
+
--stop-bg: #d4d4d4;
|
|
379
|
+
--stop-fg: #333;
|
|
380
|
+
--stop-hover: #c4c4c4;
|
|
381
|
+
|
|
382
|
+
--border-1: rgba(0,0,0,0.06);
|
|
383
|
+
--border-2: rgba(0,0,0,0.08);
|
|
384
|
+
--border-3: rgba(0,0,0,0.1);
|
|
385
|
+
--border-4: rgba(0,0,0,0.1);
|
|
386
|
+
--border-5: rgba(0,0,0,0.15);
|
|
387
|
+
--border-focus: rgba(0,0,0,0.2);
|
|
388
|
+
--border-hover: rgba(0,0,0,0.2);
|
|
389
|
+
--border-drag: rgba(0,0,0,0.3);
|
|
390
|
+
|
|
391
|
+
--surface-1: rgba(0,0,0,0.02);
|
|
392
|
+
--surface-2: rgba(0,0,0,0.03);
|
|
393
|
+
--surface-3: rgba(0,0,0,0.03);
|
|
394
|
+
--surface-4: rgba(0,0,0,0.04);
|
|
395
|
+
--surface-5: rgba(0,0,0,0.05);
|
|
396
|
+
--surface-6: rgba(0,0,0,0.07);
|
|
397
|
+
--surface-7: rgba(0,0,0,0.08);
|
|
398
|
+
--surface-8: rgba(0,0,0,0.1);
|
|
399
|
+
|
|
400
|
+
--chip-bg: rgba(255,255,255,0.8);
|
|
401
|
+
--chip-bg-hover: rgba(255,255,255,0.9);
|
|
402
|
+
--backdrop: rgba(0,0,0,0.3);
|
|
403
|
+
--lightbox-bg: rgba(0,0,0,0.75);
|
|
404
|
+
--inset-1: rgba(0,0,0,0.04);
|
|
405
|
+
--inset-2: rgba(0,0,0,0.06);
|
|
406
|
+
|
|
407
|
+
--file-badge-bg: rgba(0,0,0,0.05);
|
|
408
|
+
--file-badge-fg: rgba(0,0,0,0.7);
|
|
409
|
+
|
|
410
|
+
--error: #dc2626;
|
|
411
|
+
--error-soft: #ef4444;
|
|
412
|
+
--error-alt: #ef4444;
|
|
413
|
+
--error-bg: rgba(220,38,38,0.06);
|
|
414
|
+
--error-border: rgba(220,38,38,0.2);
|
|
415
|
+
|
|
416
|
+
--tool-done: #16a34a;
|
|
417
|
+
--tool-error: #dc2626;
|
|
418
|
+
|
|
419
|
+
--approve: #16a34a;
|
|
420
|
+
--approve-border: rgba(22,163,74,0.35);
|
|
421
|
+
--deny: #dc2626;
|
|
422
|
+
--deny-border: rgba(220,38,38,0.3);
|
|
423
|
+
|
|
424
|
+
--scrollbar: rgba(0,0,0,0.12);
|
|
425
|
+
--scrollbar-hover: rgba(0,0,0,0.2);
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
|
|
271
429
|
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
272
430
|
html, body { height: 100vh; overflow: hidden; overscroll-behavior: none; touch-action: pan-y; }
|
|
273
431
|
body {
|
|
274
432
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Helvetica Neue", sans-serif;
|
|
275
|
-
background:
|
|
276
|
-
color:
|
|
433
|
+
background: var(--bg);
|
|
434
|
+
color: var(--fg);
|
|
277
435
|
font-size: 14px;
|
|
278
436
|
line-height: 1.5;
|
|
279
437
|
-webkit-font-smoothing: antialiased;
|
|
@@ -281,7 +439,7 @@ var renderWebUiHtml = (options) => {
|
|
|
281
439
|
}
|
|
282
440
|
button, input, textarea { font: inherit; color: inherit; }
|
|
283
441
|
.hidden { display: none !important; }
|
|
284
|
-
a { color:
|
|
442
|
+
a { color: var(--fg); }
|
|
285
443
|
|
|
286
444
|
/* Auth */
|
|
287
445
|
.auth {
|
|
@@ -289,39 +447,39 @@ var renderWebUiHtml = (options) => {
|
|
|
289
447
|
display: grid;
|
|
290
448
|
place-items: center;
|
|
291
449
|
padding: 20px;
|
|
292
|
-
background:
|
|
450
|
+
background: var(--bg);
|
|
293
451
|
}
|
|
294
452
|
.auth-card {
|
|
295
453
|
width: min(400px, 90vw);
|
|
296
454
|
}
|
|
297
455
|
.auth-shell {
|
|
298
|
-
background:
|
|
299
|
-
border: 1px solid
|
|
456
|
+
background: var(--bg-alt);
|
|
457
|
+
border: 1px solid var(--border-3);
|
|
300
458
|
border-radius: 9999px;
|
|
301
459
|
display: flex;
|
|
302
460
|
align-items: center;
|
|
303
461
|
padding: 4px 6px 4px 18px;
|
|
304
462
|
transition: border-color 0.15s;
|
|
305
463
|
}
|
|
306
|
-
.auth-shell:focus-within { border-color:
|
|
464
|
+
.auth-shell:focus-within { border-color: var(--border-focus); }
|
|
307
465
|
.auth-input {
|
|
308
466
|
flex: 1;
|
|
309
467
|
background: transparent;
|
|
310
468
|
border: 0;
|
|
311
469
|
outline: none;
|
|
312
|
-
color:
|
|
470
|
+
color: var(--fg);
|
|
313
471
|
padding: 10px 0 8px;
|
|
314
472
|
font-size: 14px;
|
|
315
473
|
margin-top: -2px;
|
|
316
474
|
}
|
|
317
|
-
.auth-input::placeholder { color:
|
|
475
|
+
.auth-input::placeholder { color: var(--fg-7); }
|
|
318
476
|
.auth-submit {
|
|
319
477
|
width: 32px;
|
|
320
478
|
height: 32px;
|
|
321
|
-
background:
|
|
479
|
+
background: var(--accent);
|
|
322
480
|
border: 0;
|
|
323
481
|
border-radius: 50%;
|
|
324
|
-
color:
|
|
482
|
+
color: var(--accent-fg);
|
|
325
483
|
cursor: pointer;
|
|
326
484
|
display: grid;
|
|
327
485
|
place-items: center;
|
|
@@ -329,19 +487,19 @@ var renderWebUiHtml = (options) => {
|
|
|
329
487
|
margin-bottom: 2px;
|
|
330
488
|
transition: background 0.15s;
|
|
331
489
|
}
|
|
332
|
-
.auth-submit:hover { background:
|
|
333
|
-
.error { color:
|
|
490
|
+
.auth-submit:hover { background: var(--accent-hover); }
|
|
491
|
+
.error { color: var(--error); font-size: 13px; min-height: 16px; }
|
|
334
492
|
.message-error {
|
|
335
|
-
background:
|
|
336
|
-
border: 1px solid
|
|
493
|
+
background: var(--error-bg);
|
|
494
|
+
border: 1px solid var(--error-border);
|
|
337
495
|
border-radius: 10px;
|
|
338
|
-
color:
|
|
496
|
+
color: var(--error-soft);
|
|
339
497
|
padding: 12px 16px;
|
|
340
498
|
font-size: 13px;
|
|
341
499
|
line-height: 1.5;
|
|
342
500
|
max-width: 600px;
|
|
343
501
|
}
|
|
344
|
-
.message-error strong { color:
|
|
502
|
+
.message-error strong { color: var(--error); }
|
|
345
503
|
|
|
346
504
|
/* Layout - use fixed positioning with explicit dimensions */
|
|
347
505
|
.shell {
|
|
@@ -373,8 +531,8 @@ var renderWebUiHtml = (options) => {
|
|
|
373
531
|
}
|
|
374
532
|
.sidebar {
|
|
375
533
|
width: 260px;
|
|
376
|
-
background:
|
|
377
|
-
border-right: 1px solid
|
|
534
|
+
background: var(--bg);
|
|
535
|
+
border-right: 1px solid var(--border-1);
|
|
378
536
|
display: flex;
|
|
379
537
|
flex-direction: column;
|
|
380
538
|
padding: 12px 8px;
|
|
@@ -382,7 +540,7 @@ var renderWebUiHtml = (options) => {
|
|
|
382
540
|
.new-chat-btn {
|
|
383
541
|
background: transparent;
|
|
384
542
|
border: 0;
|
|
385
|
-
color:
|
|
543
|
+
color: var(--fg-2);
|
|
386
544
|
border-radius: 12px;
|
|
387
545
|
height: 36px;
|
|
388
546
|
padding: 0 10px;
|
|
@@ -393,7 +551,7 @@ var renderWebUiHtml = (options) => {
|
|
|
393
551
|
cursor: pointer;
|
|
394
552
|
transition: background 0.15s, color 0.15s;
|
|
395
553
|
}
|
|
396
|
-
.new-chat-btn:hover { color:
|
|
554
|
+
.new-chat-btn:hover { color: var(--fg); }
|
|
397
555
|
.new-chat-btn svg { width: 16px; height: 16px; }
|
|
398
556
|
.conversation-list {
|
|
399
557
|
flex: 1;
|
|
@@ -413,16 +571,16 @@ var renderWebUiHtml = (options) => {
|
|
|
413
571
|
cursor: pointer;
|
|
414
572
|
font-size: 13px;
|
|
415
573
|
line-height: 36px;
|
|
416
|
-
color:
|
|
574
|
+
color: var(--fg-6);
|
|
417
575
|
white-space: nowrap;
|
|
418
576
|
overflow: hidden;
|
|
419
577
|
text-overflow: ellipsis;
|
|
420
578
|
position: relative;
|
|
421
579
|
transition: color 0.15s;
|
|
422
580
|
}
|
|
423
|
-
.conversation-item:hover { color:
|
|
581
|
+
.conversation-item:hover { color: var(--fg-3); }
|
|
424
582
|
.conversation-item.active {
|
|
425
|
-
color:
|
|
583
|
+
color: var(--fg);
|
|
426
584
|
}
|
|
427
585
|
.conversation-item .delete-btn {
|
|
428
586
|
position: absolute;
|
|
@@ -430,9 +588,9 @@ var renderWebUiHtml = (options) => {
|
|
|
430
588
|
top: 0;
|
|
431
589
|
bottom: 0;
|
|
432
590
|
opacity: 0;
|
|
433
|
-
background:
|
|
591
|
+
background: var(--bg);
|
|
434
592
|
border: 0;
|
|
435
|
-
color:
|
|
593
|
+
color: var(--fg-7);
|
|
436
594
|
padding: 0 8px;
|
|
437
595
|
border-radius: 0 4px 4px 0;
|
|
438
596
|
cursor: pointer;
|
|
@@ -443,7 +601,7 @@ var renderWebUiHtml = (options) => {
|
|
|
443
601
|
transition: opacity 0.15s, color 0.15s;
|
|
444
602
|
}
|
|
445
603
|
.conversation-item:hover .delete-btn { opacity: 1; }
|
|
446
|
-
.conversation-item.active .delete-btn { background:
|
|
604
|
+
.conversation-item.active .delete-btn { background: var(--bg); }
|
|
447
605
|
.conversation-item .delete-btn::before {
|
|
448
606
|
content: "";
|
|
449
607
|
position: absolute;
|
|
@@ -451,23 +609,23 @@ var renderWebUiHtml = (options) => {
|
|
|
451
609
|
top: 0;
|
|
452
610
|
bottom: 0;
|
|
453
611
|
width: 24px;
|
|
454
|
-
background: linear-gradient(to right, transparent,
|
|
612
|
+
background: linear-gradient(to right, transparent, var(--bg));
|
|
455
613
|
pointer-events: none;
|
|
456
614
|
}
|
|
457
615
|
.conversation-item.active .delete-btn::before {
|
|
458
|
-
background: linear-gradient(to right, transparent,
|
|
616
|
+
background: linear-gradient(to right, transparent, var(--bg));
|
|
459
617
|
}
|
|
460
|
-
.conversation-item .delete-btn:hover { color:
|
|
618
|
+
.conversation-item .delete-btn:hover { color: var(--fg-2); }
|
|
461
619
|
.conversation-item .delete-btn.confirming {
|
|
462
620
|
opacity: 1;
|
|
463
621
|
width: auto;
|
|
464
622
|
padding: 0 8px;
|
|
465
623
|
font-size: 11px;
|
|
466
|
-
color:
|
|
624
|
+
color: var(--error);
|
|
467
625
|
border-radius: 3px;
|
|
468
626
|
}
|
|
469
627
|
.conversation-item .delete-btn.confirming:hover {
|
|
470
|
-
color:
|
|
628
|
+
color: var(--error-alt);
|
|
471
629
|
}
|
|
472
630
|
.sidebar-footer {
|
|
473
631
|
margin-top: auto;
|
|
@@ -476,7 +634,7 @@ var renderWebUiHtml = (options) => {
|
|
|
476
634
|
.logout-btn {
|
|
477
635
|
background: transparent;
|
|
478
636
|
border: 0;
|
|
479
|
-
color:
|
|
637
|
+
color: var(--fg-6);
|
|
480
638
|
width: 100%;
|
|
481
639
|
padding: 8px 10px;
|
|
482
640
|
text-align: left;
|
|
@@ -485,10 +643,10 @@ var renderWebUiHtml = (options) => {
|
|
|
485
643
|
font-size: 13px;
|
|
486
644
|
transition: color 0.15s, background 0.15s;
|
|
487
645
|
}
|
|
488
|
-
.logout-btn:hover { color:
|
|
646
|
+
.logout-btn:hover { color: var(--fg-2); }
|
|
489
647
|
|
|
490
648
|
/* Main */
|
|
491
|
-
.main { flex: 1; display: flex; flex-direction: column; min-width: 0; max-width: 100%; background:
|
|
649
|
+
.main { flex: 1; display: flex; flex-direction: column; min-width: 0; max-width: 100%; background: var(--bg); overflow: hidden; }
|
|
492
650
|
.topbar {
|
|
493
651
|
height: calc(52px + env(safe-area-inset-top, 0px));
|
|
494
652
|
padding-top: env(safe-area-inset-top, 0px);
|
|
@@ -497,8 +655,8 @@ var renderWebUiHtml = (options) => {
|
|
|
497
655
|
justify-content: center;
|
|
498
656
|
font-size: 13px;
|
|
499
657
|
font-weight: 500;
|
|
500
|
-
color:
|
|
501
|
-
border-bottom: 1px solid
|
|
658
|
+
color: var(--fg-2);
|
|
659
|
+
border-bottom: 1px solid var(--border-1);
|
|
502
660
|
position: relative;
|
|
503
661
|
flex-shrink: 0;
|
|
504
662
|
}
|
|
@@ -517,7 +675,7 @@ var renderWebUiHtml = (options) => {
|
|
|
517
675
|
bottom: 4px; /* Position from bottom of topbar content area */
|
|
518
676
|
background: transparent;
|
|
519
677
|
border: 0;
|
|
520
|
-
color:
|
|
678
|
+
color: var(--fg-5);
|
|
521
679
|
width: 44px;
|
|
522
680
|
height: 44px;
|
|
523
681
|
border-radius: 6px;
|
|
@@ -527,7 +685,7 @@ var renderWebUiHtml = (options) => {
|
|
|
527
685
|
z-index: 10;
|
|
528
686
|
-webkit-tap-highlight-color: transparent;
|
|
529
687
|
}
|
|
530
|
-
.sidebar-toggle:hover { color:
|
|
688
|
+
.sidebar-toggle:hover { color: var(--fg); }
|
|
531
689
|
.topbar-new-chat {
|
|
532
690
|
display: none;
|
|
533
691
|
position: absolute;
|
|
@@ -535,7 +693,7 @@ var renderWebUiHtml = (options) => {
|
|
|
535
693
|
bottom: 4px;
|
|
536
694
|
background: transparent;
|
|
537
695
|
border: 0;
|
|
538
|
-
color:
|
|
696
|
+
color: var(--fg-5);
|
|
539
697
|
width: 44px;
|
|
540
698
|
height: 44px;
|
|
541
699
|
border-radius: 6px;
|
|
@@ -544,7 +702,7 @@ var renderWebUiHtml = (options) => {
|
|
|
544
702
|
z-index: 10;
|
|
545
703
|
-webkit-tap-highlight-color: transparent;
|
|
546
704
|
}
|
|
547
|
-
.topbar-new-chat:hover { color:
|
|
705
|
+
.topbar-new-chat:hover { color: var(--fg); }
|
|
548
706
|
.topbar-new-chat svg { width: 16px; height: 16px; }
|
|
549
707
|
|
|
550
708
|
/* Messages */
|
|
@@ -556,8 +714,8 @@ var renderWebUiHtml = (options) => {
|
|
|
556
714
|
.assistant-avatar {
|
|
557
715
|
width: 24px;
|
|
558
716
|
height: 24px;
|
|
559
|
-
background:
|
|
560
|
-
color:
|
|
717
|
+
background: var(--accent);
|
|
718
|
+
color: var(--accent-fg);
|
|
561
719
|
border-radius: 6px;
|
|
562
720
|
display: grid;
|
|
563
721
|
place-items: center;
|
|
@@ -568,7 +726,7 @@ var renderWebUiHtml = (options) => {
|
|
|
568
726
|
}
|
|
569
727
|
.assistant-content {
|
|
570
728
|
line-height: 1.65;
|
|
571
|
-
color:
|
|
729
|
+
color: var(--fg);
|
|
572
730
|
font-size: 14px;
|
|
573
731
|
min-width: 0;
|
|
574
732
|
max-width: 100%;
|
|
@@ -580,32 +738,32 @@ var renderWebUiHtml = (options) => {
|
|
|
580
738
|
.assistant-content p:last-child { margin-bottom: 0; }
|
|
581
739
|
.assistant-content ul, .assistant-content ol { margin: 8px 0; padding-left: 20px; }
|
|
582
740
|
.assistant-content li { margin: 4px 0; }
|
|
583
|
-
.assistant-content strong { font-weight: 600; color:
|
|
741
|
+
.assistant-content strong { font-weight: 600; color: var(--fg-strong); }
|
|
584
742
|
.assistant-content h2 {
|
|
585
743
|
font-size: 16px;
|
|
586
744
|
font-weight: 600;
|
|
587
745
|
letter-spacing: -0.02em;
|
|
588
746
|
margin: 20px 0 8px;
|
|
589
|
-
color:
|
|
747
|
+
color: var(--fg-strong);
|
|
590
748
|
}
|
|
591
749
|
.assistant-content h3 {
|
|
592
750
|
font-size: 14px;
|
|
593
751
|
font-weight: 600;
|
|
594
752
|
letter-spacing: -0.01em;
|
|
595
753
|
margin: 16px 0 6px;
|
|
596
|
-
color:
|
|
754
|
+
color: var(--fg-strong);
|
|
597
755
|
}
|
|
598
756
|
.assistant-content code {
|
|
599
|
-
background:
|
|
600
|
-
border: 1px solid
|
|
757
|
+
background: var(--surface-4);
|
|
758
|
+
border: 1px solid var(--border-1);
|
|
601
759
|
padding: 2px 5px;
|
|
602
760
|
border-radius: 4px;
|
|
603
761
|
font-family: ui-monospace, "SF Mono", "Fira Code", monospace;
|
|
604
762
|
font-size: 0.88em;
|
|
605
763
|
}
|
|
606
764
|
.assistant-content pre {
|
|
607
|
-
background:
|
|
608
|
-
border: 1px solid
|
|
765
|
+
background: var(--bg-alt);
|
|
766
|
+
border: 1px solid var(--border-1);
|
|
609
767
|
padding: 14px 16px;
|
|
610
768
|
border-radius: 8px;
|
|
611
769
|
overflow-x: auto;
|
|
@@ -622,33 +780,33 @@ var renderWebUiHtml = (options) => {
|
|
|
622
780
|
margin: 8px 0;
|
|
623
781
|
font-size: 12px;
|
|
624
782
|
line-height: 1.45;
|
|
625
|
-
color:
|
|
783
|
+
color: var(--fg-tool);
|
|
626
784
|
}
|
|
627
785
|
.tool-activity-inline code {
|
|
628
786
|
font-family: ui-monospace, "SF Mono", "Fira Code", monospace;
|
|
629
|
-
background:
|
|
630
|
-
border: 1px solid
|
|
787
|
+
background: var(--surface-3);
|
|
788
|
+
border: 1px solid var(--border-2);
|
|
631
789
|
padding: 4px 8px;
|
|
632
790
|
border-radius: 6px;
|
|
633
|
-
color:
|
|
791
|
+
color: var(--fg-tool-code);
|
|
634
792
|
font-size: 11px;
|
|
635
793
|
}
|
|
636
794
|
.tool-status {
|
|
637
|
-
color:
|
|
795
|
+
color: var(--fg-tool);
|
|
638
796
|
font-style: italic;
|
|
639
797
|
}
|
|
640
798
|
.tool-done {
|
|
641
|
-
color:
|
|
799
|
+
color: var(--tool-done);
|
|
642
800
|
}
|
|
643
801
|
.tool-error {
|
|
644
|
-
color:
|
|
802
|
+
color: var(--tool-error);
|
|
645
803
|
}
|
|
646
804
|
.assistant-content table {
|
|
647
805
|
border-collapse: collapse;
|
|
648
806
|
width: 100%;
|
|
649
807
|
margin: 14px 0;
|
|
650
808
|
font-size: 13px;
|
|
651
|
-
border: 1px solid
|
|
809
|
+
border: 1px solid var(--border-2);
|
|
652
810
|
border-radius: 8px;
|
|
653
811
|
overflow: hidden;
|
|
654
812
|
display: block;
|
|
@@ -657,17 +815,17 @@ var renderWebUiHtml = (options) => {
|
|
|
657
815
|
white-space: nowrap;
|
|
658
816
|
}
|
|
659
817
|
.assistant-content th {
|
|
660
|
-
background:
|
|
818
|
+
background: var(--surface-4);
|
|
661
819
|
padding: 10px 12px;
|
|
662
820
|
text-align: left;
|
|
663
821
|
font-weight: 600;
|
|
664
|
-
border-bottom: 1px solid
|
|
665
|
-
color:
|
|
822
|
+
border-bottom: 1px solid var(--border-4);
|
|
823
|
+
color: var(--fg-strong);
|
|
666
824
|
min-width: 100px;
|
|
667
825
|
}
|
|
668
826
|
.assistant-content td {
|
|
669
827
|
padding: 10px 12px;
|
|
670
|
-
border-bottom: 1px solid
|
|
828
|
+
border-bottom: 1px solid var(--border-1);
|
|
671
829
|
width: 100%;
|
|
672
830
|
min-width: 100px;
|
|
673
831
|
}
|
|
@@ -675,22 +833,22 @@ var renderWebUiHtml = (options) => {
|
|
|
675
833
|
border-bottom: none;
|
|
676
834
|
}
|
|
677
835
|
.assistant-content tbody tr:hover {
|
|
678
|
-
background:
|
|
836
|
+
background: var(--surface-1);
|
|
679
837
|
}
|
|
680
838
|
.assistant-content hr {
|
|
681
839
|
border: 0;
|
|
682
|
-
border-top: 1px solid
|
|
840
|
+
border-top: 1px solid var(--border-3);
|
|
683
841
|
margin: 20px 0;
|
|
684
842
|
}
|
|
685
843
|
.tool-activity {
|
|
686
844
|
margin-top: 12px;
|
|
687
845
|
margin-bottom: 12px;
|
|
688
|
-
border: 1px solid
|
|
689
|
-
background:
|
|
846
|
+
border: 1px solid var(--border-2);
|
|
847
|
+
background: var(--surface-2);
|
|
690
848
|
border-radius: 10px;
|
|
691
849
|
font-size: 12px;
|
|
692
850
|
line-height: 1.45;
|
|
693
|
-
color:
|
|
851
|
+
color: var(--fg-tool-code);
|
|
694
852
|
width: 300px;
|
|
695
853
|
}
|
|
696
854
|
.assistant-content > .tool-activity:first-child {
|
|
@@ -715,12 +873,12 @@ var renderWebUiHtml = (options) => {
|
|
|
715
873
|
font-size: 11px;
|
|
716
874
|
text-transform: uppercase;
|
|
717
875
|
letter-spacing: 0.06em;
|
|
718
|
-
color:
|
|
876
|
+
color: var(--fg-tool);
|
|
719
877
|
font-weight: 600;
|
|
720
878
|
}
|
|
721
879
|
.tool-activity-caret {
|
|
722
880
|
margin-left: auto;
|
|
723
|
-
color:
|
|
881
|
+
color: var(--fg-tool);
|
|
724
882
|
display: inline-flex;
|
|
725
883
|
align-items: center;
|
|
726
884
|
justify-content: center;
|
|
@@ -742,28 +900,28 @@ var renderWebUiHtml = (options) => {
|
|
|
742
900
|
}
|
|
743
901
|
.tool-activity-item {
|
|
744
902
|
font-family: ui-monospace, "SF Mono", "Fira Code", monospace;
|
|
745
|
-
background:
|
|
903
|
+
background: var(--surface-3);
|
|
746
904
|
border-radius: 6px;
|
|
747
905
|
padding: 4px 7px;
|
|
748
|
-
color:
|
|
906
|
+
color: var(--fg-tool-item);
|
|
749
907
|
}
|
|
750
908
|
.approval-requests {
|
|
751
|
-
border-top: 1px solid
|
|
909
|
+
border-top: 1px solid var(--border-2);
|
|
752
910
|
padding: 10px 12px 12px;
|
|
753
911
|
display: grid;
|
|
754
912
|
gap: 8px;
|
|
755
|
-
background:
|
|
913
|
+
background: var(--inset-1);
|
|
756
914
|
}
|
|
757
915
|
.approval-requests-label {
|
|
758
916
|
font-size: 11px;
|
|
759
917
|
text-transform: uppercase;
|
|
760
918
|
letter-spacing: 0.06em;
|
|
761
|
-
color:
|
|
919
|
+
color: var(--fg-approval-label);
|
|
762
920
|
font-weight: 600;
|
|
763
921
|
}
|
|
764
922
|
.approval-request-item {
|
|
765
|
-
border: 1px solid
|
|
766
|
-
background:
|
|
923
|
+
border: 1px solid var(--border-3);
|
|
924
|
+
background: var(--surface-2);
|
|
767
925
|
border-radius: 8px;
|
|
768
926
|
padding: 8px;
|
|
769
927
|
display: grid;
|
|
@@ -771,15 +929,15 @@ var renderWebUiHtml = (options) => {
|
|
|
771
929
|
}
|
|
772
930
|
.approval-request-tool {
|
|
773
931
|
font-size: 12px;
|
|
774
|
-
color:
|
|
932
|
+
color: var(--fg-strong);
|
|
775
933
|
font-weight: 600;
|
|
776
934
|
overflow-wrap: anywhere;
|
|
777
935
|
}
|
|
778
936
|
.approval-request-input {
|
|
779
937
|
font-family: ui-monospace, "SF Mono", "Fira Code", monospace;
|
|
780
938
|
font-size: 11px;
|
|
781
|
-
color:
|
|
782
|
-
background:
|
|
939
|
+
color: var(--fg-approval-input);
|
|
940
|
+
background: var(--inset-2);
|
|
783
941
|
border-radius: 6px;
|
|
784
942
|
padding: 6px;
|
|
785
943
|
overflow-wrap: anywhere;
|
|
@@ -792,32 +950,32 @@ var renderWebUiHtml = (options) => {
|
|
|
792
950
|
}
|
|
793
951
|
.approval-action-btn {
|
|
794
952
|
border-radius: 6px;
|
|
795
|
-
border: 1px solid
|
|
796
|
-
background:
|
|
797
|
-
color:
|
|
953
|
+
border: 1px solid var(--border-5);
|
|
954
|
+
background: var(--surface-4);
|
|
955
|
+
color: var(--fg-approval-btn);
|
|
798
956
|
font-size: 11px;
|
|
799
957
|
font-weight: 600;
|
|
800
958
|
padding: 4px 8px;
|
|
801
959
|
cursor: pointer;
|
|
802
960
|
}
|
|
803
961
|
.approval-action-btn:hover {
|
|
804
|
-
background:
|
|
962
|
+
background: var(--surface-7);
|
|
805
963
|
}
|
|
806
964
|
.approval-action-btn.approve {
|
|
807
|
-
border-color:
|
|
808
|
-
color:
|
|
965
|
+
border-color: var(--approve-border);
|
|
966
|
+
color: var(--approve);
|
|
809
967
|
}
|
|
810
968
|
.approval-action-btn.deny {
|
|
811
|
-
border-color:
|
|
812
|
-
color:
|
|
969
|
+
border-color: var(--deny-border);
|
|
970
|
+
color: var(--deny);
|
|
813
971
|
}
|
|
814
972
|
.approval-action-btn[disabled] {
|
|
815
973
|
opacity: 0.55;
|
|
816
974
|
cursor: not-allowed;
|
|
817
975
|
}
|
|
818
976
|
.user-bubble {
|
|
819
|
-
background:
|
|
820
|
-
border: 1px solid
|
|
977
|
+
background: var(--bg-elevated);
|
|
978
|
+
border: 1px solid var(--border-2);
|
|
821
979
|
padding: 10px 16px;
|
|
822
980
|
border-radius: 18px;
|
|
823
981
|
max-width: 70%;
|
|
@@ -833,7 +991,7 @@ var renderWebUiHtml = (options) => {
|
|
|
833
991
|
justify-content: center;
|
|
834
992
|
height: 100%;
|
|
835
993
|
gap: 16px;
|
|
836
|
-
color:
|
|
994
|
+
color: var(--fg-6);
|
|
837
995
|
}
|
|
838
996
|
.empty-state .assistant-avatar {
|
|
839
997
|
width: 36px;
|
|
@@ -843,7 +1001,7 @@ var renderWebUiHtml = (options) => {
|
|
|
843
1001
|
}
|
|
844
1002
|
.empty-state-text {
|
|
845
1003
|
font-size: 14px;
|
|
846
|
-
color:
|
|
1004
|
+
color: var(--fg-6);
|
|
847
1005
|
}
|
|
848
1006
|
.thinking-indicator {
|
|
849
1007
|
display: inline-block;
|
|
@@ -851,7 +1009,7 @@ var renderWebUiHtml = (options) => {
|
|
|
851
1009
|
font-size: 20px;
|
|
852
1010
|
line-height: 1;
|
|
853
1011
|
vertical-align: middle;
|
|
854
|
-
color:
|
|
1012
|
+
color: var(--fg);
|
|
855
1013
|
opacity: 0.5;
|
|
856
1014
|
}
|
|
857
1015
|
.thinking-status {
|
|
@@ -859,13 +1017,13 @@ var renderWebUiHtml = (options) => {
|
|
|
859
1017
|
align-items: center;
|
|
860
1018
|
gap: 8px;
|
|
861
1019
|
margin-top: 2px;
|
|
862
|
-
color:
|
|
1020
|
+
color: var(--fg-tool);
|
|
863
1021
|
font-size: 14px;
|
|
864
1022
|
line-height: 1.65;
|
|
865
1023
|
font-weight: 400;
|
|
866
1024
|
}
|
|
867
1025
|
.thinking-status-label {
|
|
868
|
-
color:
|
|
1026
|
+
color: var(--fg-tool);
|
|
869
1027
|
font-size: 14px;
|
|
870
1028
|
line-height: 1.65;
|
|
871
1029
|
font-weight: 400;
|
|
@@ -896,26 +1054,26 @@ var renderWebUiHtml = (options) => {
|
|
|
896
1054
|
right: 0;
|
|
897
1055
|
bottom: 100%;
|
|
898
1056
|
height: 48px;
|
|
899
|
-
background: linear-gradient(to top,
|
|
1057
|
+
background: linear-gradient(to top, var(--bg) 0%, transparent 100%);
|
|
900
1058
|
pointer-events: none;
|
|
901
1059
|
}
|
|
902
1060
|
.composer-inner { max-width: 680px; margin: 0 auto; }
|
|
903
1061
|
.composer-shell {
|
|
904
|
-
background:
|
|
905
|
-
border: 1px solid
|
|
1062
|
+
background: var(--bg-alt);
|
|
1063
|
+
border: 1px solid var(--border-3);
|
|
906
1064
|
border-radius: 24px;
|
|
907
1065
|
display: flex;
|
|
908
1066
|
align-items: end;
|
|
909
1067
|
padding: 4px 6px 4px 6px;
|
|
910
1068
|
transition: border-color 0.15s;
|
|
911
1069
|
}
|
|
912
|
-
.composer-shell:focus-within { border-color:
|
|
1070
|
+
.composer-shell:focus-within { border-color: var(--border-focus); }
|
|
913
1071
|
.composer-input {
|
|
914
1072
|
flex: 1;
|
|
915
1073
|
background: transparent;
|
|
916
1074
|
border: 0;
|
|
917
1075
|
outline: none;
|
|
918
|
-
color:
|
|
1076
|
+
color: var(--fg);
|
|
919
1077
|
min-height: 40px;
|
|
920
1078
|
max-height: 200px;
|
|
921
1079
|
resize: none;
|
|
@@ -924,14 +1082,14 @@ var renderWebUiHtml = (options) => {
|
|
|
924
1082
|
line-height: 1.5;
|
|
925
1083
|
margin-top: -4px;
|
|
926
1084
|
}
|
|
927
|
-
.composer-input::placeholder { color:
|
|
1085
|
+
.composer-input::placeholder { color: var(--fg-7); }
|
|
928
1086
|
.send-btn {
|
|
929
1087
|
width: 32px;
|
|
930
1088
|
height: 32px;
|
|
931
|
-
background:
|
|
1089
|
+
background: var(--accent);
|
|
932
1090
|
border: 0;
|
|
933
1091
|
border-radius: 50%;
|
|
934
|
-
color:
|
|
1092
|
+
color: var(--accent-fg);
|
|
935
1093
|
cursor: pointer;
|
|
936
1094
|
display: grid;
|
|
937
1095
|
place-items: center;
|
|
@@ -939,21 +1097,79 @@ var renderWebUiHtml = (options) => {
|
|
|
939
1097
|
margin-bottom: 2px;
|
|
940
1098
|
transition: background 0.15s, opacity 0.15s;
|
|
941
1099
|
}
|
|
942
|
-
.send-btn:hover { background:
|
|
1100
|
+
.send-btn:hover { background: var(--accent-hover); }
|
|
943
1101
|
.send-btn.stop-mode {
|
|
944
|
-
background:
|
|
945
|
-
color:
|
|
1102
|
+
background: var(--stop-bg);
|
|
1103
|
+
color: var(--stop-fg);
|
|
946
1104
|
}
|
|
947
|
-
.send-btn.stop-mode:hover { background:
|
|
1105
|
+
.send-btn.stop-mode:hover { background: var(--stop-hover); }
|
|
948
1106
|
.send-btn:disabled { opacity: 0.2; cursor: default; }
|
|
949
|
-
.send-btn:disabled:hover { background:
|
|
1107
|
+
.send-btn:disabled:hover { background: var(--accent); }
|
|
1108
|
+
.send-btn-wrapper {
|
|
1109
|
+
position: relative;
|
|
1110
|
+
width: 36px;
|
|
1111
|
+
height: 36px;
|
|
1112
|
+
display: grid;
|
|
1113
|
+
place-items: center;
|
|
1114
|
+
flex-shrink: 0;
|
|
1115
|
+
margin-bottom: 0;
|
|
1116
|
+
}
|
|
1117
|
+
.send-btn-wrapper .send-btn {
|
|
1118
|
+
margin-bottom: 0;
|
|
1119
|
+
}
|
|
1120
|
+
.context-ring {
|
|
1121
|
+
position: absolute;
|
|
1122
|
+
inset: 0;
|
|
1123
|
+
width: 36px;
|
|
1124
|
+
height: 36px;
|
|
1125
|
+
pointer-events: none;
|
|
1126
|
+
transform: rotate(-90deg);
|
|
1127
|
+
}
|
|
1128
|
+
.context-ring-fill {
|
|
1129
|
+
fill: none;
|
|
1130
|
+
stroke: var(--bg-alt);
|
|
1131
|
+
stroke-width: 3;
|
|
1132
|
+
stroke-linecap: butt;
|
|
1133
|
+
transition: stroke-dashoffset 0.4s ease, stroke 0.3s ease;
|
|
1134
|
+
}
|
|
1135
|
+
.send-btn-wrapper.stop-mode .context-ring-fill {
|
|
1136
|
+
stroke: var(--fg-3);
|
|
1137
|
+
}
|
|
1138
|
+
.context-ring-fill.warning {
|
|
1139
|
+
stroke: #e5a33d;
|
|
1140
|
+
}
|
|
1141
|
+
.context-ring-fill.critical {
|
|
1142
|
+
stroke: #e55d4a;
|
|
1143
|
+
}
|
|
1144
|
+
.context-tooltip {
|
|
1145
|
+
position: absolute;
|
|
1146
|
+
bottom: calc(100% + 8px);
|
|
1147
|
+
right: 0;
|
|
1148
|
+
background: var(--bg-elevated);
|
|
1149
|
+
border: 1px solid var(--border-3);
|
|
1150
|
+
border-radius: 8px;
|
|
1151
|
+
padding: 6px 10px;
|
|
1152
|
+
font-size: 12px;
|
|
1153
|
+
color: var(--fg-2);
|
|
1154
|
+
white-space: nowrap;
|
|
1155
|
+
pointer-events: none;
|
|
1156
|
+
opacity: 0;
|
|
1157
|
+
transform: translateY(4px);
|
|
1158
|
+
transition: opacity 0.15s, transform 0.15s;
|
|
1159
|
+
z-index: 10;
|
|
1160
|
+
}
|
|
1161
|
+
.send-btn-wrapper:hover .context-tooltip,
|
|
1162
|
+
.send-btn-wrapper:focus-within .context-tooltip {
|
|
1163
|
+
opacity: 1;
|
|
1164
|
+
transform: translateY(0);
|
|
1165
|
+
}
|
|
950
1166
|
.attach-btn {
|
|
951
1167
|
width: 32px;
|
|
952
1168
|
height: 32px;
|
|
953
|
-
background:
|
|
1169
|
+
background: var(--surface-5);
|
|
954
1170
|
border: 0;
|
|
955
1171
|
border-radius: 50%;
|
|
956
|
-
color:
|
|
1172
|
+
color: var(--fg-3);
|
|
957
1173
|
cursor: pointer;
|
|
958
1174
|
display: grid;
|
|
959
1175
|
place-items: center;
|
|
@@ -962,7 +1178,7 @@ var renderWebUiHtml = (options) => {
|
|
|
962
1178
|
margin-right: 8px;
|
|
963
1179
|
transition: color 0.15s, background 0.15s;
|
|
964
1180
|
}
|
|
965
|
-
.attach-btn:hover { color:
|
|
1181
|
+
.attach-btn:hover { color: var(--fg); background: var(--surface-8); }
|
|
966
1182
|
.attachment-preview {
|
|
967
1183
|
display: flex;
|
|
968
1184
|
gap: 8px;
|
|
@@ -973,12 +1189,12 @@ var renderWebUiHtml = (options) => {
|
|
|
973
1189
|
display: inline-flex;
|
|
974
1190
|
align-items: center;
|
|
975
1191
|
gap: 6px;
|
|
976
|
-
background:
|
|
977
|
-
border: 1px solid
|
|
1192
|
+
background: var(--chip-bg);
|
|
1193
|
+
border: 1px solid var(--border-4);
|
|
978
1194
|
border-radius: 9999px;
|
|
979
1195
|
padding: 4px 10px 4px 6px;
|
|
980
1196
|
font-size: 11px;
|
|
981
|
-
color:
|
|
1197
|
+
color: var(--fg-4);
|
|
982
1198
|
max-width: 200px;
|
|
983
1199
|
cursor: pointer;
|
|
984
1200
|
backdrop-filter: blur(6px);
|
|
@@ -986,9 +1202,9 @@ var renderWebUiHtml = (options) => {
|
|
|
986
1202
|
transition: color 0.15s, border-color 0.15s, background 0.15s;
|
|
987
1203
|
}
|
|
988
1204
|
.attachment-chip:hover {
|
|
989
|
-
color:
|
|
990
|
-
border-color:
|
|
991
|
-
background:
|
|
1205
|
+
color: var(--fg);
|
|
1206
|
+
border-color: var(--border-hover);
|
|
1207
|
+
background: var(--chip-bg-hover);
|
|
992
1208
|
}
|
|
993
1209
|
.attachment-chip img {
|
|
994
1210
|
width: 20px;
|
|
@@ -1002,7 +1218,7 @@ var renderWebUiHtml = (options) => {
|
|
|
1002
1218
|
width: 20px;
|
|
1003
1219
|
height: 20px;
|
|
1004
1220
|
border-radius: 50%;
|
|
1005
|
-
background:
|
|
1221
|
+
background: var(--surface-6);
|
|
1006
1222
|
display: grid;
|
|
1007
1223
|
place-items: center;
|
|
1008
1224
|
font-size: 11px;
|
|
@@ -1010,13 +1226,13 @@ var renderWebUiHtml = (options) => {
|
|
|
1010
1226
|
}
|
|
1011
1227
|
.attachment-chip .remove-attachment {
|
|
1012
1228
|
cursor: pointer;
|
|
1013
|
-
color:
|
|
1229
|
+
color: var(--fg-6);
|
|
1014
1230
|
font-size: 14px;
|
|
1015
1231
|
margin-left: 2px;
|
|
1016
1232
|
line-height: 1;
|
|
1017
1233
|
transition: color 0.15s;
|
|
1018
1234
|
}
|
|
1019
|
-
.attachment-chip .remove-attachment:hover { color:
|
|
1235
|
+
.attachment-chip .remove-attachment:hover { color: var(--fg-strong); }
|
|
1020
1236
|
.attachment-chip .filename { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 100px; }
|
|
1021
1237
|
.user-bubble .user-file-attachments {
|
|
1022
1238
|
display: flex;
|
|
@@ -1046,7 +1262,7 @@ var renderWebUiHtml = (options) => {
|
|
|
1046
1262
|
transition: background 0.25s ease, backdrop-filter 0.25s ease;
|
|
1047
1263
|
}
|
|
1048
1264
|
.lightbox.active {
|
|
1049
|
-
background:
|
|
1265
|
+
background: var(--lightbox-bg);
|
|
1050
1266
|
backdrop-filter: blur(8px);
|
|
1051
1267
|
}
|
|
1052
1268
|
.lightbox img {
|
|
@@ -1066,16 +1282,16 @@ var renderWebUiHtml = (options) => {
|
|
|
1066
1282
|
display: inline-flex;
|
|
1067
1283
|
align-items: center;
|
|
1068
1284
|
gap: 4px;
|
|
1069
|
-
background:
|
|
1285
|
+
background: var(--file-badge-bg);
|
|
1070
1286
|
border-radius: 6px;
|
|
1071
1287
|
padding: 4px 8px;
|
|
1072
1288
|
font-size: 12px;
|
|
1073
|
-
color:
|
|
1289
|
+
color: var(--file-badge-fg);
|
|
1074
1290
|
}
|
|
1075
1291
|
.drag-overlay {
|
|
1076
1292
|
position: fixed;
|
|
1077
1293
|
inset: 0;
|
|
1078
|
-
background:
|
|
1294
|
+
background: var(--backdrop);
|
|
1079
1295
|
z-index: 9999;
|
|
1080
1296
|
display: none;
|
|
1081
1297
|
align-items: center;
|
|
@@ -1084,15 +1300,15 @@ var renderWebUiHtml = (options) => {
|
|
|
1084
1300
|
}
|
|
1085
1301
|
.drag-overlay.active { display: flex; }
|
|
1086
1302
|
.drag-overlay-inner {
|
|
1087
|
-
border: 2px dashed
|
|
1303
|
+
border: 2px dashed var(--border-drag);
|
|
1088
1304
|
border-radius: 16px;
|
|
1089
1305
|
padding: 40px 60px;
|
|
1090
|
-
color:
|
|
1306
|
+
color: var(--fg-strong);
|
|
1091
1307
|
font-size: 16px;
|
|
1092
1308
|
}
|
|
1093
1309
|
.disclaimer {
|
|
1094
1310
|
text-align: center;
|
|
1095
|
-
color:
|
|
1311
|
+
color: var(--fg-8);
|
|
1096
1312
|
font-size: 12px;
|
|
1097
1313
|
margin-top: 10px;
|
|
1098
1314
|
}
|
|
@@ -1107,10 +1323,10 @@ var renderWebUiHtml = (options) => {
|
|
|
1107
1323
|
align-items: center;
|
|
1108
1324
|
gap: 6px;
|
|
1109
1325
|
font-size: 11px;
|
|
1110
|
-
color:
|
|
1326
|
+
color: var(--fg-4);
|
|
1111
1327
|
text-decoration: none;
|
|
1112
|
-
background:
|
|
1113
|
-
border: 1px solid
|
|
1328
|
+
background: var(--chip-bg);
|
|
1329
|
+
border: 1px solid var(--border-4);
|
|
1114
1330
|
border-radius: 9999px;
|
|
1115
1331
|
padding: 4px 10px 4px 6px;
|
|
1116
1332
|
backdrop-filter: blur(6px);
|
|
@@ -1118,9 +1334,9 @@ var renderWebUiHtml = (options) => {
|
|
|
1118
1334
|
transition: color 0.15s, border-color 0.15s, background 0.15s;
|
|
1119
1335
|
}
|
|
1120
1336
|
.poncho-badge:hover {
|
|
1121
|
-
color:
|
|
1122
|
-
border-color:
|
|
1123
|
-
background:
|
|
1337
|
+
color: var(--fg);
|
|
1338
|
+
border-color: var(--border-hover);
|
|
1339
|
+
background: var(--chip-bg-hover);
|
|
1124
1340
|
}
|
|
1125
1341
|
.poncho-badge-avatar {
|
|
1126
1342
|
width: 16px;
|
|
@@ -1134,8 +1350,8 @@ var renderWebUiHtml = (options) => {
|
|
|
1134
1350
|
/* Scrollbar */
|
|
1135
1351
|
::-webkit-scrollbar { width: 6px; }
|
|
1136
1352
|
::-webkit-scrollbar-track { background: transparent; }
|
|
1137
|
-
::-webkit-scrollbar-thumb { background:
|
|
1138
|
-
::-webkit-scrollbar-thumb:hover { background:
|
|
1353
|
+
::-webkit-scrollbar-thumb { background: var(--scrollbar); border-radius: 3px; }
|
|
1354
|
+
::-webkit-scrollbar-thumb:hover { background: var(--scrollbar-hover); }
|
|
1139
1355
|
|
|
1140
1356
|
/* Mobile */
|
|
1141
1357
|
@media (max-width: 768px) {
|
|
@@ -1165,7 +1381,7 @@ var renderWebUiHtml = (options) => {
|
|
|
1165
1381
|
.sidebar-backdrop {
|
|
1166
1382
|
position: fixed;
|
|
1167
1383
|
inset: 0;
|
|
1168
|
-
background:
|
|
1384
|
+
background: var(--backdrop);
|
|
1169
1385
|
z-index: 50;
|
|
1170
1386
|
backdrop-filter: blur(2px);
|
|
1171
1387
|
-webkit-backdrop-filter: blur(2px);
|
|
@@ -1240,9 +1456,15 @@ var renderWebUiHtml = (options) => {
|
|
|
1240
1456
|
</button>
|
|
1241
1457
|
<input id="file-input" type="file" multiple accept="image/*,video/*,application/pdf,.txt,.csv,.json,.html" style="display:none" />
|
|
1242
1458
|
<textarea id="prompt" class="composer-input" placeholder="Send a message..." rows="1"></textarea>
|
|
1243
|
-
<
|
|
1244
|
-
<svg
|
|
1245
|
-
|
|
1459
|
+
<div class="send-btn-wrapper" id="send-btn-wrapper">
|
|
1460
|
+
<svg class="context-ring" viewBox="0 0 36 36">
|
|
1461
|
+
<circle class="context-ring-fill" id="context-ring-fill" cx="18" cy="18" r="14.5" />
|
|
1462
|
+
</svg>
|
|
1463
|
+
<button id="send" class="send-btn" type="submit">
|
|
1464
|
+
<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>
|
|
1465
|
+
</button>
|
|
1466
|
+
<div class="context-tooltip" id="context-tooltip"></div>
|
|
1467
|
+
</div>
|
|
1246
1468
|
</div>
|
|
1247
1469
|
</div>
|
|
1248
1470
|
</form>
|
|
@@ -1274,6 +1496,8 @@ var renderWebUiHtml = (options) => {
|
|
|
1274
1496
|
confirmDeleteId: null,
|
|
1275
1497
|
approvalRequestsInFlight: {},
|
|
1276
1498
|
pendingFiles: [],
|
|
1499
|
+
contextTokens: 0,
|
|
1500
|
+
contextWindow: 0,
|
|
1277
1501
|
};
|
|
1278
1502
|
|
|
1279
1503
|
const agentInitial = document.body.dataset.agentInitial || "A";
|
|
@@ -1301,12 +1525,41 @@ var renderWebUiHtml = (options) => {
|
|
|
1301
1525
|
attachmentPreview: $("attachment-preview"),
|
|
1302
1526
|
dragOverlay: $("drag-overlay"),
|
|
1303
1527
|
lightbox: $("lightbox"),
|
|
1528
|
+
contextRingFill: $("context-ring-fill"),
|
|
1529
|
+
contextTooltip: $("context-tooltip"),
|
|
1530
|
+
sendBtnWrapper: $("send-btn-wrapper"),
|
|
1304
1531
|
};
|
|
1305
1532
|
const sendIconMarkup =
|
|
1306
1533
|
'<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
1534
|
const stopIconMarkup =
|
|
1308
1535
|
'<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
1536
|
|
|
1537
|
+
const CONTEXT_RING_CIRCUMFERENCE = 2 * Math.PI * 14.5;
|
|
1538
|
+
const formatTokenCount = (n) => {
|
|
1539
|
+
if (n >= 1_000_000) return (n / 1_000_000).toFixed(1).replace(/\\.0$/, "") + "M";
|
|
1540
|
+
if (n >= 1_000) return (n / 1_000).toFixed(1).replace(/\\.0$/, "") + "k";
|
|
1541
|
+
return String(n);
|
|
1542
|
+
};
|
|
1543
|
+
const updateContextRing = () => {
|
|
1544
|
+
const ring = elements.contextRingFill;
|
|
1545
|
+
const tooltip = elements.contextTooltip;
|
|
1546
|
+
if (!ring || !tooltip) return;
|
|
1547
|
+
if (state.contextWindow <= 0) {
|
|
1548
|
+
ring.style.strokeDasharray = String(CONTEXT_RING_CIRCUMFERENCE);
|
|
1549
|
+
ring.style.strokeDashoffset = String(CONTEXT_RING_CIRCUMFERENCE);
|
|
1550
|
+
tooltip.textContent = "";
|
|
1551
|
+
return;
|
|
1552
|
+
}
|
|
1553
|
+
const ratio = Math.min(state.contextTokens / state.contextWindow, 1);
|
|
1554
|
+
const offset = CONTEXT_RING_CIRCUMFERENCE * (1 - ratio);
|
|
1555
|
+
ring.style.strokeDasharray = String(CONTEXT_RING_CIRCUMFERENCE);
|
|
1556
|
+
ring.style.strokeDashoffset = String(offset);
|
|
1557
|
+
ring.classList.toggle("warning", ratio >= 0.7 && ratio < 0.9);
|
|
1558
|
+
ring.classList.toggle("critical", ratio >= 0.9);
|
|
1559
|
+
const pct = (ratio * 100).toFixed(1).replace(/\\.0$/, "");
|
|
1560
|
+
tooltip.textContent = formatTokenCount(state.contextTokens) + " / " + formatTokenCount(state.contextWindow) + " tokens (" + pct + "%)";
|
|
1561
|
+
};
|
|
1562
|
+
|
|
1310
1563
|
const pushConversationUrl = (conversationId) => {
|
|
1311
1564
|
const target = conversationId ? "/c/" + encodeURIComponent(conversationId) : "/";
|
|
1312
1565
|
if (window.location.pathname !== target) {
|
|
@@ -1626,6 +1879,9 @@ var renderWebUiHtml = (options) => {
|
|
|
1626
1879
|
if (state.activeConversationId === c.conversationId) {
|
|
1627
1880
|
state.activeConversationId = null;
|
|
1628
1881
|
state.activeMessages = [];
|
|
1882
|
+
state.contextTokens = 0;
|
|
1883
|
+
state.contextWindow = 0;
|
|
1884
|
+
updateContextRing();
|
|
1629
1885
|
pushConversationUrl(null);
|
|
1630
1886
|
elements.chatTitle.textContent = "";
|
|
1631
1887
|
renderMessages([]);
|
|
@@ -1722,8 +1978,14 @@ var renderWebUiHtml = (options) => {
|
|
|
1722
1978
|
} else if (shouldRenderEmptyStreamingIndicator) {
|
|
1723
1979
|
content.appendChild(createThinkingIndicator(getThinkingStatusLabel(m)));
|
|
1724
1980
|
} else {
|
|
1725
|
-
//
|
|
1726
|
-
|
|
1981
|
+
// Merge stored sections (persisted) with live sections (from
|
|
1982
|
+
// an active stream). For normal messages only one source
|
|
1983
|
+
// exists; for liveOnly reconnects both contribute.
|
|
1984
|
+
const storedSections = (m.metadata && m.metadata.sections) || [];
|
|
1985
|
+
const liveSections = m._sections || [];
|
|
1986
|
+
const sections = liveSections.length > 0 && storedSections.length > 0
|
|
1987
|
+
? storedSections.concat(liveSections)
|
|
1988
|
+
: liveSections.length > 0 ? liveSections : (storedSections.length > 0 ? storedSections : null);
|
|
1727
1989
|
const pendingApprovals = Array.isArray(m._pendingApprovals) ? m._pendingApprovals : [];
|
|
1728
1990
|
|
|
1729
1991
|
if (sections && sections.length > 0) {
|
|
@@ -1864,11 +2126,24 @@ var renderWebUiHtml = (options) => {
|
|
|
1864
2126
|
payload.conversation.messages || [],
|
|
1865
2127
|
payload.conversation.pendingApprovals || payload.pendingApprovals || [],
|
|
1866
2128
|
);
|
|
2129
|
+
state.contextTokens = 0;
|
|
2130
|
+
state.contextWindow = 0;
|
|
2131
|
+
updateContextRing();
|
|
1867
2132
|
renderMessages(state.activeMessages, false, { forceScrollBottom: true });
|
|
1868
2133
|
elements.prompt.focus();
|
|
2134
|
+
if (payload.hasActiveRun && !state.isStreaming) {
|
|
2135
|
+
setStreaming(true);
|
|
2136
|
+
streamConversationEvents(conversationId, { liveOnly: true }).finally(() => {
|
|
2137
|
+
if (state.activeConversationId === conversationId) {
|
|
2138
|
+
setStreaming(false);
|
|
2139
|
+
renderMessages(state.activeMessages, false);
|
|
2140
|
+
}
|
|
2141
|
+
});
|
|
2142
|
+
}
|
|
1869
2143
|
};
|
|
1870
2144
|
|
|
1871
|
-
const streamConversationEvents = (conversationId) => {
|
|
2145
|
+
const streamConversationEvents = (conversationId, options) => {
|
|
2146
|
+
const liveOnly = options && options.liveOnly;
|
|
1872
2147
|
return new Promise((resolve) => {
|
|
1873
2148
|
const localMessages = state.activeMessages || [];
|
|
1874
2149
|
const renderIfActiveConversation = (streaming) => {
|
|
@@ -1887,20 +2162,36 @@ var renderWebUiHtml = (options) => {
|
|
|
1887
2162
|
_currentText: "",
|
|
1888
2163
|
_currentTools: [],
|
|
1889
2164
|
_pendingApprovals: [],
|
|
2165
|
+
_activeActivities: [],
|
|
1890
2166
|
metadata: { toolActivity: [] },
|
|
1891
2167
|
};
|
|
1892
2168
|
localMessages.push(assistantMessage);
|
|
1893
2169
|
state.activeMessages = localMessages;
|
|
1894
2170
|
}
|
|
1895
|
-
if (
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
2171
|
+
if (liveOnly) {
|
|
2172
|
+
// Live-only mode: keep metadata.sections intact (the stored
|
|
2173
|
+
// base content) and start _sections empty so it only collects
|
|
2174
|
+
// NEW sections from live events. The renderer merges both.
|
|
2175
|
+
assistantMessage._sections = [];
|
|
2176
|
+
assistantMessage._currentText = "";
|
|
2177
|
+
assistantMessage._currentTools = [];
|
|
2178
|
+
if (!assistantMessage._activeActivities) assistantMessage._activeActivities = [];
|
|
2179
|
+
if (!assistantMessage._pendingApprovals) assistantMessage._pendingApprovals = [];
|
|
2180
|
+
if (!assistantMessage.metadata) assistantMessage.metadata = {};
|
|
2181
|
+
if (!assistantMessage.metadata.toolActivity) assistantMessage.metadata.toolActivity = [];
|
|
2182
|
+
} else {
|
|
2183
|
+
// Full replay mode: reset transient state so replayed events
|
|
2184
|
+
// rebuild from scratch (the buffer has the full event history).
|
|
2185
|
+
assistantMessage.content = "";
|
|
2186
|
+
assistantMessage._sections = [];
|
|
2187
|
+
assistantMessage._currentText = "";
|
|
2188
|
+
assistantMessage._currentTools = [];
|
|
2189
|
+
assistantMessage._activeActivities = [];
|
|
2190
|
+
assistantMessage._pendingApprovals = [];
|
|
2191
|
+
assistantMessage.metadata = { toolActivity: [] };
|
|
2192
|
+
}
|
|
2193
|
+
|
|
2194
|
+
const url = "/api/conversations/" + encodeURIComponent(conversationId) + "/events" + (liveOnly ? "?live_only=true" : "");
|
|
1904
2195
|
fetch(url, { credentials: "include" }).then((response) => {
|
|
1905
2196
|
if (!response.ok || !response.body) {
|
|
1906
2197
|
resolve(undefined);
|
|
@@ -1921,6 +2212,11 @@ var renderWebUiHtml = (options) => {
|
|
|
1921
2212
|
if (eventName === "stream:end") {
|
|
1922
2213
|
return;
|
|
1923
2214
|
}
|
|
2215
|
+
if (eventName === "run:started") {
|
|
2216
|
+
if (typeof payload.contextWindow === "number" && payload.contextWindow > 0) {
|
|
2217
|
+
state.contextWindow = payload.contextWindow;
|
|
2218
|
+
}
|
|
2219
|
+
}
|
|
1924
2220
|
if (eventName === "model:chunk") {
|
|
1925
2221
|
const chunk = String(payload.content || "");
|
|
1926
2222
|
if (assistantMessage._currentTools.length > 0 && chunk.length > 0) {
|
|
@@ -1934,6 +2230,12 @@ var renderWebUiHtml = (options) => {
|
|
|
1934
2230
|
assistantMessage._currentText += chunk;
|
|
1935
2231
|
renderIfActiveConversation(true);
|
|
1936
2232
|
}
|
|
2233
|
+
if (eventName === "model:response") {
|
|
2234
|
+
if (typeof payload.usage?.input === "number") {
|
|
2235
|
+
state.contextTokens = payload.usage.input;
|
|
2236
|
+
updateContextRing();
|
|
2237
|
+
}
|
|
2238
|
+
}
|
|
1937
2239
|
if (eventName === "tool:started") {
|
|
1938
2240
|
const toolName = payload.tool || "tool";
|
|
1939
2241
|
const startedActivity = addActiveActivityFromToolStart(
|
|
@@ -2006,6 +2308,75 @@ var renderWebUiHtml = (options) => {
|
|
|
2006
2308
|
assistantMessage.metadata.toolActivity.push(toolText);
|
|
2007
2309
|
renderIfActiveConversation(true);
|
|
2008
2310
|
}
|
|
2311
|
+
if (eventName === "tool:approval:required") {
|
|
2312
|
+
const toolName = payload.tool || "tool";
|
|
2313
|
+
const activeActivity = removeActiveActivityForTool(
|
|
2314
|
+
assistantMessage,
|
|
2315
|
+
toolName,
|
|
2316
|
+
);
|
|
2317
|
+
const detailFromPayload = describeToolStart(payload);
|
|
2318
|
+
const detail =
|
|
2319
|
+
(activeActivity && typeof activeActivity.detail === "string"
|
|
2320
|
+
? activeActivity.detail.trim()
|
|
2321
|
+
: "") ||
|
|
2322
|
+
(detailFromPayload && typeof detailFromPayload.detail === "string"
|
|
2323
|
+
? detailFromPayload.detail.trim()
|
|
2324
|
+
: "");
|
|
2325
|
+
const toolText =
|
|
2326
|
+
"- approval required \\x60" +
|
|
2327
|
+
toolName +
|
|
2328
|
+
"\\x60" +
|
|
2329
|
+
(detail ? " (" + detail + ")" : "");
|
|
2330
|
+
assistantMessage._currentTools.push(toolText);
|
|
2331
|
+
assistantMessage.metadata.toolActivity.push(toolText);
|
|
2332
|
+
const approvalId =
|
|
2333
|
+
typeof payload.approvalId === "string" ? payload.approvalId : "";
|
|
2334
|
+
if (approvalId) {
|
|
2335
|
+
const preview = safeJsonPreview(payload.input ?? {});
|
|
2336
|
+
const inputPreview = preview.length > 600 ? preview.slice(0, 600) + "..." : preview;
|
|
2337
|
+
if (!Array.isArray(assistantMessage._pendingApprovals)) {
|
|
2338
|
+
assistantMessage._pendingApprovals = [];
|
|
2339
|
+
}
|
|
2340
|
+
const exists = assistantMessage._pendingApprovals.some(
|
|
2341
|
+
(req) => req.approvalId === approvalId,
|
|
2342
|
+
);
|
|
2343
|
+
if (!exists) {
|
|
2344
|
+
assistantMessage._pendingApprovals.push({
|
|
2345
|
+
approvalId,
|
|
2346
|
+
tool: toolName,
|
|
2347
|
+
inputPreview,
|
|
2348
|
+
state: "pending",
|
|
2349
|
+
});
|
|
2350
|
+
}
|
|
2351
|
+
}
|
|
2352
|
+
renderIfActiveConversation(true);
|
|
2353
|
+
}
|
|
2354
|
+
if (eventName === "tool:approval:granted") {
|
|
2355
|
+
const toolText = "- approval granted";
|
|
2356
|
+
assistantMessage._currentTools.push(toolText);
|
|
2357
|
+
assistantMessage.metadata.toolActivity.push(toolText);
|
|
2358
|
+
const approvalId =
|
|
2359
|
+
typeof payload.approvalId === "string" ? payload.approvalId : "";
|
|
2360
|
+
if (approvalId && Array.isArray(assistantMessage._pendingApprovals)) {
|
|
2361
|
+
assistantMessage._pendingApprovals = assistantMessage._pendingApprovals.filter(
|
|
2362
|
+
(req) => req.approvalId !== approvalId,
|
|
2363
|
+
);
|
|
2364
|
+
}
|
|
2365
|
+
renderIfActiveConversation(true);
|
|
2366
|
+
}
|
|
2367
|
+
if (eventName === "tool:approval:denied") {
|
|
2368
|
+
const toolText = "- approval denied";
|
|
2369
|
+
assistantMessage._currentTools.push(toolText);
|
|
2370
|
+
assistantMessage.metadata.toolActivity.push(toolText);
|
|
2371
|
+
const approvalId =
|
|
2372
|
+
typeof payload.approvalId === "string" ? payload.approvalId : "";
|
|
2373
|
+
if (approvalId && Array.isArray(assistantMessage._pendingApprovals)) {
|
|
2374
|
+
assistantMessage._pendingApprovals = assistantMessage._pendingApprovals.filter(
|
|
2375
|
+
(req) => req.approvalId !== approvalId,
|
|
2376
|
+
);
|
|
2377
|
+
}
|
|
2378
|
+
renderIfActiveConversation(true);
|
|
2379
|
+
}
|
|
2009
2380
|
if (eventName === "run:completed") {
|
|
2010
2381
|
assistantMessage._activeActivities = [];
|
|
2011
2382
|
if (
|
|
@@ -2052,9 +2423,22 @@ var renderWebUiHtml = (options) => {
|
|
|
2052
2423
|
}
|
|
2053
2424
|
if (eventName === "run:error") {
|
|
2054
2425
|
assistantMessage._activeActivities = [];
|
|
2426
|
+
if (assistantMessage._currentTools.length > 0) {
|
|
2427
|
+
assistantMessage._sections.push({
|
|
2428
|
+
type: "tools",
|
|
2429
|
+
content: assistantMessage._currentTools,
|
|
2430
|
+
});
|
|
2431
|
+
assistantMessage._currentTools = [];
|
|
2432
|
+
}
|
|
2433
|
+
if (assistantMessage._currentText.length > 0) {
|
|
2434
|
+
assistantMessage._sections.push({
|
|
2435
|
+
type: "text",
|
|
2436
|
+
content: assistantMessage._currentText,
|
|
2437
|
+
});
|
|
2438
|
+
assistantMessage._currentText = "";
|
|
2439
|
+
}
|
|
2055
2440
|
const errMsg =
|
|
2056
2441
|
payload.error?.message || "Something went wrong";
|
|
2057
|
-
assistantMessage.content = "";
|
|
2058
2442
|
assistantMessage._error = errMsg;
|
|
2059
2443
|
renderIfActiveConversation(false);
|
|
2060
2444
|
}
|
|
@@ -2127,6 +2511,9 @@ var renderWebUiHtml = (options) => {
|
|
|
2127
2511
|
elements.send.disabled = value ? !canStop : false;
|
|
2128
2512
|
elements.send.innerHTML = value ? stopIconMarkup : sendIconMarkup;
|
|
2129
2513
|
elements.send.classList.toggle("stop-mode", value);
|
|
2514
|
+
if (elements.sendBtnWrapper) {
|
|
2515
|
+
elements.sendBtnWrapper.classList.toggle("stop-mode", value);
|
|
2516
|
+
}
|
|
2130
2517
|
elements.send.setAttribute("aria-label", value ? "Stop response" : "Send message");
|
|
2131
2518
|
elements.send.setAttribute(
|
|
2132
2519
|
"title",
|
|
@@ -2499,8 +2886,17 @@ var renderWebUiHtml = (options) => {
|
|
|
2499
2886
|
}
|
|
2500
2887
|
if (eventName === "run:started") {
|
|
2501
2888
|
state.activeStreamRunId = typeof payload.runId === "string" ? payload.runId : null;
|
|
2889
|
+
if (typeof payload.contextWindow === "number" && payload.contextWindow > 0) {
|
|
2890
|
+
state.contextWindow = payload.contextWindow;
|
|
2891
|
+
}
|
|
2502
2892
|
setStreaming(state.isStreaming);
|
|
2503
2893
|
}
|
|
2894
|
+
if (eventName === "model:response") {
|
|
2895
|
+
if (typeof payload.usage?.input === "number") {
|
|
2896
|
+
state.contextTokens = payload.usage.input;
|
|
2897
|
+
updateContextRing();
|
|
2898
|
+
}
|
|
2899
|
+
}
|
|
2504
2900
|
if (eventName === "tool:started") {
|
|
2505
2901
|
const toolName = payload.tool || "tool";
|
|
2506
2902
|
const startedActivity = addActiveActivityFromToolStart(
|
|
@@ -2663,9 +3059,8 @@ var renderWebUiHtml = (options) => {
|
|
|
2663
3059
|
renderIfActiveConversation(false);
|
|
2664
3060
|
}
|
|
2665
3061
|
if (eventName === "run:error") {
|
|
2666
|
-
|
|
3062
|
+
finalizeAssistantMessage();
|
|
2667
3063
|
const errMsg = payload.error?.message || "Something went wrong";
|
|
2668
|
-
assistantMessage.content = "";
|
|
2669
3064
|
assistantMessage._error = errMsg;
|
|
2670
3065
|
renderIfActiveConversation(false);
|
|
2671
3066
|
}
|
|
@@ -2768,6 +3163,9 @@ var renderWebUiHtml = (options) => {
|
|
|
2768
3163
|
state.activeConversationId = null;
|
|
2769
3164
|
state.activeMessages = [];
|
|
2770
3165
|
state.confirmDeleteId = null;
|
|
3166
|
+
state.contextTokens = 0;
|
|
3167
|
+
state.contextWindow = 0;
|
|
3168
|
+
updateContextRing();
|
|
2771
3169
|
pushConversationUrl(null);
|
|
2772
3170
|
elements.chatTitle.textContent = "";
|
|
2773
3171
|
renderMessages([]);
|
|
@@ -2805,6 +3203,9 @@ var renderWebUiHtml = (options) => {
|
|
|
2805
3203
|
state.confirmDeleteId = null;
|
|
2806
3204
|
state.conversations = [];
|
|
2807
3205
|
state.csrfToken = "";
|
|
3206
|
+
state.contextTokens = 0;
|
|
3207
|
+
state.contextWindow = 0;
|
|
3208
|
+
updateContextRing();
|
|
2808
3209
|
await requireAuth();
|
|
2809
3210
|
});
|
|
2810
3211
|
|
|
@@ -3009,6 +3410,9 @@ var renderWebUiHtml = (options) => {
|
|
|
3009
3410
|
} else {
|
|
3010
3411
|
state.activeConversationId = null;
|
|
3011
3412
|
state.activeMessages = [];
|
|
3413
|
+
state.contextTokens = 0;
|
|
3414
|
+
state.contextWindow = 0;
|
|
3415
|
+
updateContextRing();
|
|
3012
3416
|
elements.chatTitle.textContent = "";
|
|
3013
3417
|
renderMessages([]);
|
|
3014
3418
|
renderConversationList();
|
|
@@ -3050,6 +3454,7 @@ var renderWebUiHtml = (options) => {
|
|
|
3050
3454
|
await createConversation();
|
|
3051
3455
|
}
|
|
3052
3456
|
autoResizePrompt();
|
|
3457
|
+
updateContextRing();
|
|
3053
3458
|
elements.prompt.focus();
|
|
3054
3459
|
})();
|
|
3055
3460
|
|
|
@@ -3266,6 +3671,631 @@ var renderWebUiHtml = (options) => {
|
|
|
3266
3671
|
</html>`;
|
|
3267
3672
|
};
|
|
3268
3673
|
|
|
3674
|
+
// src/api-docs.ts
|
|
3675
|
+
var buildOpenApiSpec = (options) => ({
|
|
3676
|
+
openapi: "3.1.0",
|
|
3677
|
+
info: {
|
|
3678
|
+
title: `${options.agentName} API`,
|
|
3679
|
+
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.",
|
|
3680
|
+
version: "1.0.0"
|
|
3681
|
+
},
|
|
3682
|
+
servers: [{ url: "/", description: "Current host" }],
|
|
3683
|
+
components: {
|
|
3684
|
+
securitySchemes: {
|
|
3685
|
+
bearerAuth: {
|
|
3686
|
+
type: "http",
|
|
3687
|
+
scheme: "bearer",
|
|
3688
|
+
description: "Pass the PONCHO_AUTH_TOKEN value as a Bearer token. Required only when `auth.required: true` in poncho.config.js."
|
|
3689
|
+
}
|
|
3690
|
+
},
|
|
3691
|
+
schemas: {
|
|
3692
|
+
ConversationSummary: {
|
|
3693
|
+
type: "object",
|
|
3694
|
+
properties: {
|
|
3695
|
+
conversationId: { type: "string" },
|
|
3696
|
+
title: { type: "string" },
|
|
3697
|
+
runtimeRunId: { type: "string" },
|
|
3698
|
+
ownerId: { type: "string" },
|
|
3699
|
+
tenantId: { type: ["string", "null"] },
|
|
3700
|
+
createdAt: { type: "number", description: "Unix epoch ms" },
|
|
3701
|
+
updatedAt: { type: "number", description: "Unix epoch ms" },
|
|
3702
|
+
messageCount: { type: "integer" }
|
|
3703
|
+
}
|
|
3704
|
+
},
|
|
3705
|
+
Message: {
|
|
3706
|
+
type: "object",
|
|
3707
|
+
properties: {
|
|
3708
|
+
role: { type: "string", enum: ["user", "assistant"] },
|
|
3709
|
+
content: {
|
|
3710
|
+
oneOf: [
|
|
3711
|
+
{ type: "string" },
|
|
3712
|
+
{
|
|
3713
|
+
type: "array",
|
|
3714
|
+
items: {
|
|
3715
|
+
oneOf: [
|
|
3716
|
+
{
|
|
3717
|
+
type: "object",
|
|
3718
|
+
properties: {
|
|
3719
|
+
type: { type: "string", const: "text" },
|
|
3720
|
+
text: { type: "string" }
|
|
3721
|
+
}
|
|
3722
|
+
},
|
|
3723
|
+
{
|
|
3724
|
+
type: "object",
|
|
3725
|
+
properties: {
|
|
3726
|
+
type: { type: "string", const: "file" },
|
|
3727
|
+
data: { type: "string" },
|
|
3728
|
+
mediaType: { type: "string" },
|
|
3729
|
+
filename: { type: "string" }
|
|
3730
|
+
}
|
|
3731
|
+
}
|
|
3732
|
+
]
|
|
3733
|
+
}
|
|
3734
|
+
}
|
|
3735
|
+
]
|
|
3736
|
+
},
|
|
3737
|
+
metadata: {
|
|
3738
|
+
type: "object",
|
|
3739
|
+
properties: {
|
|
3740
|
+
id: { type: "string" },
|
|
3741
|
+
timestamp: { type: "number" },
|
|
3742
|
+
tokenCount: { type: "number" },
|
|
3743
|
+
step: { type: "number" },
|
|
3744
|
+
toolActivity: { type: "array", items: { type: "string" } }
|
|
3745
|
+
}
|
|
3746
|
+
}
|
|
3747
|
+
}
|
|
3748
|
+
},
|
|
3749
|
+
Conversation: {
|
|
3750
|
+
type: "object",
|
|
3751
|
+
properties: {
|
|
3752
|
+
conversationId: { type: "string" },
|
|
3753
|
+
title: { type: "string" },
|
|
3754
|
+
ownerId: { type: "string" },
|
|
3755
|
+
tenantId: { type: ["string", "null"] },
|
|
3756
|
+
createdAt: { type: "number" },
|
|
3757
|
+
updatedAt: { type: "number" },
|
|
3758
|
+
messages: { type: "array", items: { $ref: "#/components/schemas/Message" } },
|
|
3759
|
+
pendingApprovals: {
|
|
3760
|
+
type: "array",
|
|
3761
|
+
items: { $ref: "#/components/schemas/PendingApproval" }
|
|
3762
|
+
}
|
|
3763
|
+
}
|
|
3764
|
+
},
|
|
3765
|
+
PendingApproval: {
|
|
3766
|
+
type: "object",
|
|
3767
|
+
properties: {
|
|
3768
|
+
approvalId: { type: "string" },
|
|
3769
|
+
runId: { type: "string" },
|
|
3770
|
+
tool: { type: "string" },
|
|
3771
|
+
input: {}
|
|
3772
|
+
}
|
|
3773
|
+
},
|
|
3774
|
+
TokenUsage: {
|
|
3775
|
+
type: "object",
|
|
3776
|
+
properties: {
|
|
3777
|
+
input: { type: "integer" },
|
|
3778
|
+
output: { type: "integer" },
|
|
3779
|
+
cached: { type: "integer" }
|
|
3780
|
+
}
|
|
3781
|
+
},
|
|
3782
|
+
RunResult: {
|
|
3783
|
+
type: "object",
|
|
3784
|
+
properties: {
|
|
3785
|
+
status: { type: "string", enum: ["completed", "error", "cancelled"] },
|
|
3786
|
+
response: { type: "string" },
|
|
3787
|
+
steps: { type: "integer" },
|
|
3788
|
+
tokens: { $ref: "#/components/schemas/TokenUsage" },
|
|
3789
|
+
duration: { type: "number", description: "Duration in ms" },
|
|
3790
|
+
continuation: { type: "boolean" },
|
|
3791
|
+
maxSteps: { type: "integer" }
|
|
3792
|
+
}
|
|
3793
|
+
},
|
|
3794
|
+
FileAttachment: {
|
|
3795
|
+
type: "object",
|
|
3796
|
+
properties: {
|
|
3797
|
+
data: { type: "string", description: "base64-encoded file data" },
|
|
3798
|
+
mediaType: { type: "string" },
|
|
3799
|
+
filename: { type: "string" }
|
|
3800
|
+
},
|
|
3801
|
+
required: ["data", "mediaType"]
|
|
3802
|
+
},
|
|
3803
|
+
Error: {
|
|
3804
|
+
type: "object",
|
|
3805
|
+
properties: {
|
|
3806
|
+
code: { type: "string" },
|
|
3807
|
+
message: { type: "string" }
|
|
3808
|
+
}
|
|
3809
|
+
}
|
|
3810
|
+
}
|
|
3811
|
+
},
|
|
3812
|
+
security: [{ bearerAuth: [] }],
|
|
3813
|
+
paths: {
|
|
3814
|
+
"/health": {
|
|
3815
|
+
get: {
|
|
3816
|
+
tags: ["Health"],
|
|
3817
|
+
summary: "Health check",
|
|
3818
|
+
security: [],
|
|
3819
|
+
responses: {
|
|
3820
|
+
"200": {
|
|
3821
|
+
description: "Server is healthy",
|
|
3822
|
+
content: {
|
|
3823
|
+
"application/json": {
|
|
3824
|
+
schema: {
|
|
3825
|
+
type: "object",
|
|
3826
|
+
properties: { status: { type: "string", const: "ok" } }
|
|
3827
|
+
}
|
|
3828
|
+
}
|
|
3829
|
+
}
|
|
3830
|
+
}
|
|
3831
|
+
}
|
|
3832
|
+
}
|
|
3833
|
+
},
|
|
3834
|
+
"/api/auth/session": {
|
|
3835
|
+
get: {
|
|
3836
|
+
tags: ["Auth"],
|
|
3837
|
+
summary: "Check session status",
|
|
3838
|
+
description: "Returns whether the caller is authenticated and provides a CSRF token for subsequent mutating requests.",
|
|
3839
|
+
security: [],
|
|
3840
|
+
responses: {
|
|
3841
|
+
"200": {
|
|
3842
|
+
description: "Session status",
|
|
3843
|
+
content: {
|
|
3844
|
+
"application/json": {
|
|
3845
|
+
schema: {
|
|
3846
|
+
type: "object",
|
|
3847
|
+
properties: {
|
|
3848
|
+
authenticated: { type: "boolean" },
|
|
3849
|
+
sessionId: { type: "string" },
|
|
3850
|
+
ownerId: { type: "string" },
|
|
3851
|
+
csrfToken: { type: "string" }
|
|
3852
|
+
},
|
|
3853
|
+
required: ["authenticated"]
|
|
3854
|
+
}
|
|
3855
|
+
}
|
|
3856
|
+
}
|
|
3857
|
+
}
|
|
3858
|
+
}
|
|
3859
|
+
}
|
|
3860
|
+
},
|
|
3861
|
+
"/api/auth/login": {
|
|
3862
|
+
post: {
|
|
3863
|
+
tags: ["Auth"],
|
|
3864
|
+
summary: "Authenticate with passphrase",
|
|
3865
|
+
description: "Creates a session cookie. Only needed for browser-based auth; API clients should use Bearer tokens instead.",
|
|
3866
|
+
security: [],
|
|
3867
|
+
requestBody: {
|
|
3868
|
+
required: true,
|
|
3869
|
+
content: {
|
|
3870
|
+
"application/json": {
|
|
3871
|
+
schema: {
|
|
3872
|
+
type: "object",
|
|
3873
|
+
properties: { passphrase: { type: "string" } },
|
|
3874
|
+
required: ["passphrase"]
|
|
3875
|
+
}
|
|
3876
|
+
}
|
|
3877
|
+
}
|
|
3878
|
+
},
|
|
3879
|
+
responses: {
|
|
3880
|
+
"200": {
|
|
3881
|
+
description: "Login successful",
|
|
3882
|
+
content: {
|
|
3883
|
+
"application/json": {
|
|
3884
|
+
schema: {
|
|
3885
|
+
type: "object",
|
|
3886
|
+
properties: {
|
|
3887
|
+
ok: { type: "boolean" },
|
|
3888
|
+
sessionId: { type: "string" },
|
|
3889
|
+
csrfToken: { type: "string" }
|
|
3890
|
+
}
|
|
3891
|
+
}
|
|
3892
|
+
}
|
|
3893
|
+
}
|
|
3894
|
+
},
|
|
3895
|
+
"401": {
|
|
3896
|
+
description: "Invalid passphrase",
|
|
3897
|
+
content: { "application/json": { schema: { $ref: "#/components/schemas/Error" } } }
|
|
3898
|
+
},
|
|
3899
|
+
"429": {
|
|
3900
|
+
description: "Too many login attempts",
|
|
3901
|
+
content: { "application/json": { schema: { $ref: "#/components/schemas/Error" } } }
|
|
3902
|
+
}
|
|
3903
|
+
}
|
|
3904
|
+
}
|
|
3905
|
+
},
|
|
3906
|
+
"/api/auth/logout": {
|
|
3907
|
+
post: {
|
|
3908
|
+
tags: ["Auth"],
|
|
3909
|
+
summary: "End session",
|
|
3910
|
+
responses: {
|
|
3911
|
+
"200": {
|
|
3912
|
+
description: "Logged out",
|
|
3913
|
+
content: {
|
|
3914
|
+
"application/json": {
|
|
3915
|
+
schema: { type: "object", properties: { ok: { type: "boolean" } } }
|
|
3916
|
+
}
|
|
3917
|
+
}
|
|
3918
|
+
}
|
|
3919
|
+
}
|
|
3920
|
+
}
|
|
3921
|
+
},
|
|
3922
|
+
"/api/conversations": {
|
|
3923
|
+
get: {
|
|
3924
|
+
tags: ["Conversations"],
|
|
3925
|
+
summary: "List conversations",
|
|
3926
|
+
responses: {
|
|
3927
|
+
"200": {
|
|
3928
|
+
description: "Conversation list",
|
|
3929
|
+
content: {
|
|
3930
|
+
"application/json": {
|
|
3931
|
+
schema: {
|
|
3932
|
+
type: "object",
|
|
3933
|
+
properties: {
|
|
3934
|
+
conversations: {
|
|
3935
|
+
type: "array",
|
|
3936
|
+
items: { $ref: "#/components/schemas/ConversationSummary" }
|
|
3937
|
+
}
|
|
3938
|
+
}
|
|
3939
|
+
}
|
|
3940
|
+
}
|
|
3941
|
+
}
|
|
3942
|
+
}
|
|
3943
|
+
}
|
|
3944
|
+
},
|
|
3945
|
+
post: {
|
|
3946
|
+
tags: ["Conversations"],
|
|
3947
|
+
summary: "Create a conversation",
|
|
3948
|
+
requestBody: {
|
|
3949
|
+
content: {
|
|
3950
|
+
"application/json": {
|
|
3951
|
+
schema: {
|
|
3952
|
+
type: "object",
|
|
3953
|
+
properties: { title: { type: "string" } }
|
|
3954
|
+
}
|
|
3955
|
+
}
|
|
3956
|
+
}
|
|
3957
|
+
},
|
|
3958
|
+
responses: {
|
|
3959
|
+
"201": {
|
|
3960
|
+
description: "Conversation created",
|
|
3961
|
+
content: {
|
|
3962
|
+
"application/json": {
|
|
3963
|
+
schema: {
|
|
3964
|
+
type: "object",
|
|
3965
|
+
properties: { conversation: { $ref: "#/components/schemas/Conversation" } }
|
|
3966
|
+
}
|
|
3967
|
+
}
|
|
3968
|
+
}
|
|
3969
|
+
}
|
|
3970
|
+
}
|
|
3971
|
+
}
|
|
3972
|
+
},
|
|
3973
|
+
"/api/conversations/{conversationId}": {
|
|
3974
|
+
get: {
|
|
3975
|
+
tags: ["Conversations"],
|
|
3976
|
+
summary: "Get conversation",
|
|
3977
|
+
description: "Returns the full conversation including messages and any pending tool approval requests.",
|
|
3978
|
+
parameters: [
|
|
3979
|
+
{ name: "conversationId", in: "path", required: true, schema: { type: "string" } }
|
|
3980
|
+
],
|
|
3981
|
+
responses: {
|
|
3982
|
+
"200": {
|
|
3983
|
+
description: "Conversation with messages",
|
|
3984
|
+
content: {
|
|
3985
|
+
"application/json": {
|
|
3986
|
+
schema: {
|
|
3987
|
+
type: "object",
|
|
3988
|
+
properties: { conversation: { $ref: "#/components/schemas/Conversation" } }
|
|
3989
|
+
}
|
|
3990
|
+
}
|
|
3991
|
+
}
|
|
3992
|
+
},
|
|
3993
|
+
"404": {
|
|
3994
|
+
description: "Conversation not found",
|
|
3995
|
+
content: { "application/json": { schema: { $ref: "#/components/schemas/Error" } } }
|
|
3996
|
+
}
|
|
3997
|
+
}
|
|
3998
|
+
},
|
|
3999
|
+
patch: {
|
|
4000
|
+
tags: ["Conversations"],
|
|
4001
|
+
summary: "Rename conversation",
|
|
4002
|
+
parameters: [
|
|
4003
|
+
{ name: "conversationId", in: "path", required: true, schema: { type: "string" } }
|
|
4004
|
+
],
|
|
4005
|
+
requestBody: {
|
|
4006
|
+
required: true,
|
|
4007
|
+
content: {
|
|
4008
|
+
"application/json": {
|
|
4009
|
+
schema: {
|
|
4010
|
+
type: "object",
|
|
4011
|
+
properties: { title: { type: "string" } },
|
|
4012
|
+
required: ["title"]
|
|
4013
|
+
}
|
|
4014
|
+
}
|
|
4015
|
+
}
|
|
4016
|
+
},
|
|
4017
|
+
responses: {
|
|
4018
|
+
"200": {
|
|
4019
|
+
description: "Conversation renamed",
|
|
4020
|
+
content: {
|
|
4021
|
+
"application/json": {
|
|
4022
|
+
schema: {
|
|
4023
|
+
type: "object",
|
|
4024
|
+
properties: { conversation: { $ref: "#/components/schemas/Conversation" } }
|
|
4025
|
+
}
|
|
4026
|
+
}
|
|
4027
|
+
}
|
|
4028
|
+
}
|
|
4029
|
+
}
|
|
4030
|
+
},
|
|
4031
|
+
delete: {
|
|
4032
|
+
tags: ["Conversations"],
|
|
4033
|
+
summary: "Delete conversation",
|
|
4034
|
+
parameters: [
|
|
4035
|
+
{ name: "conversationId", in: "path", required: true, schema: { type: "string" } }
|
|
4036
|
+
],
|
|
4037
|
+
responses: {
|
|
4038
|
+
"200": {
|
|
4039
|
+
description: "Conversation deleted",
|
|
4040
|
+
content: {
|
|
4041
|
+
"application/json": {
|
|
4042
|
+
schema: { type: "object", properties: { ok: { type: "boolean" } } }
|
|
4043
|
+
}
|
|
4044
|
+
}
|
|
4045
|
+
}
|
|
4046
|
+
}
|
|
4047
|
+
}
|
|
4048
|
+
},
|
|
4049
|
+
"/api/conversations/{conversationId}/messages": {
|
|
4050
|
+
post: {
|
|
4051
|
+
tags: ["Messages"],
|
|
4052
|
+
summary: "Send a message (streaming)",
|
|
4053
|
+
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.",
|
|
4054
|
+
parameters: [
|
|
4055
|
+
{ name: "conversationId", in: "path", required: true, schema: { type: "string" } }
|
|
4056
|
+
],
|
|
4057
|
+
requestBody: {
|
|
4058
|
+
required: true,
|
|
4059
|
+
content: {
|
|
4060
|
+
"application/json": {
|
|
4061
|
+
schema: {
|
|
4062
|
+
type: "object",
|
|
4063
|
+
properties: {
|
|
4064
|
+
message: { type: "string", description: "User message text" },
|
|
4065
|
+
parameters: {
|
|
4066
|
+
type: "object",
|
|
4067
|
+
additionalProperties: true,
|
|
4068
|
+
description: "Key-value parameters passed to the agent run"
|
|
4069
|
+
},
|
|
4070
|
+
files: {
|
|
4071
|
+
type: "array",
|
|
4072
|
+
items: { $ref: "#/components/schemas/FileAttachment" },
|
|
4073
|
+
description: "Attached files (base64-encoded)"
|
|
4074
|
+
}
|
|
4075
|
+
},
|
|
4076
|
+
required: ["message"]
|
|
4077
|
+
}
|
|
4078
|
+
},
|
|
4079
|
+
"multipart/form-data": {
|
|
4080
|
+
schema: {
|
|
4081
|
+
type: "object",
|
|
4082
|
+
properties: {
|
|
4083
|
+
message: { type: "string" },
|
|
4084
|
+
parameters: { type: "string", description: "JSON-encoded parameters object" },
|
|
4085
|
+
files: { type: "array", items: { type: "string", format: "binary" } }
|
|
4086
|
+
}
|
|
4087
|
+
}
|
|
4088
|
+
}
|
|
4089
|
+
}
|
|
4090
|
+
},
|
|
4091
|
+
responses: {
|
|
4092
|
+
"200": {
|
|
4093
|
+
description: "SSE stream of agent events",
|
|
4094
|
+
content: { "text/event-stream": { schema: { type: "string" } } }
|
|
4095
|
+
},
|
|
4096
|
+
"404": {
|
|
4097
|
+
description: "Conversation not found",
|
|
4098
|
+
content: { "application/json": { schema: { $ref: "#/components/schemas/Error" } } }
|
|
4099
|
+
}
|
|
4100
|
+
}
|
|
4101
|
+
}
|
|
4102
|
+
},
|
|
4103
|
+
"/api/conversations/{conversationId}/events": {
|
|
4104
|
+
get: {
|
|
4105
|
+
tags: ["Messages"],
|
|
4106
|
+
summary: "Attach to live event stream",
|
|
4107
|
+
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.",
|
|
4108
|
+
parameters: [
|
|
4109
|
+
{ name: "conversationId", in: "path", required: true, schema: { type: "string" } }
|
|
4110
|
+
],
|
|
4111
|
+
responses: {
|
|
4112
|
+
"200": {
|
|
4113
|
+
description: "SSE stream (same event format as POST /messages)",
|
|
4114
|
+
content: { "text/event-stream": { schema: { type: "string" } } }
|
|
4115
|
+
},
|
|
4116
|
+
"404": {
|
|
4117
|
+
description: "Conversation not found",
|
|
4118
|
+
content: { "application/json": { schema: { $ref: "#/components/schemas/Error" } } }
|
|
4119
|
+
}
|
|
4120
|
+
}
|
|
4121
|
+
}
|
|
4122
|
+
},
|
|
4123
|
+
"/api/conversations/{conversationId}/stop": {
|
|
4124
|
+
post: {
|
|
4125
|
+
tags: ["Messages"],
|
|
4126
|
+
summary: "Stop an in-flight run",
|
|
4127
|
+
parameters: [
|
|
4128
|
+
{ name: "conversationId", in: "path", required: true, schema: { type: "string" } }
|
|
4129
|
+
],
|
|
4130
|
+
requestBody: {
|
|
4131
|
+
required: true,
|
|
4132
|
+
content: {
|
|
4133
|
+
"application/json": {
|
|
4134
|
+
schema: {
|
|
4135
|
+
type: "object",
|
|
4136
|
+
properties: {
|
|
4137
|
+
runId: { type: "string", description: "The run ID to cancel (from run:started event)" }
|
|
4138
|
+
},
|
|
4139
|
+
required: ["runId"]
|
|
4140
|
+
}
|
|
4141
|
+
}
|
|
4142
|
+
}
|
|
4143
|
+
},
|
|
4144
|
+
responses: {
|
|
4145
|
+
"200": {
|
|
4146
|
+
description: "Stop result",
|
|
4147
|
+
content: {
|
|
4148
|
+
"application/json": {
|
|
4149
|
+
schema: {
|
|
4150
|
+
type: "object",
|
|
4151
|
+
properties: {
|
|
4152
|
+
ok: { type: "boolean" },
|
|
4153
|
+
stopped: { type: "boolean" },
|
|
4154
|
+
runId: { type: "string" }
|
|
4155
|
+
}
|
|
4156
|
+
}
|
|
4157
|
+
}
|
|
4158
|
+
}
|
|
4159
|
+
}
|
|
4160
|
+
}
|
|
4161
|
+
}
|
|
4162
|
+
},
|
|
4163
|
+
"/api/approvals/{approvalId}": {
|
|
4164
|
+
post: {
|
|
4165
|
+
tags: ["Approvals"],
|
|
4166
|
+
summary: "Resolve a tool approval request",
|
|
4167
|
+
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.",
|
|
4168
|
+
parameters: [
|
|
4169
|
+
{ name: "approvalId", in: "path", required: true, schema: { type: "string" } }
|
|
4170
|
+
],
|
|
4171
|
+
requestBody: {
|
|
4172
|
+
required: true,
|
|
4173
|
+
content: {
|
|
4174
|
+
"application/json": {
|
|
4175
|
+
schema: {
|
|
4176
|
+
type: "object",
|
|
4177
|
+
properties: { approved: { type: "boolean" } },
|
|
4178
|
+
required: ["approved"]
|
|
4179
|
+
}
|
|
4180
|
+
}
|
|
4181
|
+
}
|
|
4182
|
+
},
|
|
4183
|
+
responses: {
|
|
4184
|
+
"200": {
|
|
4185
|
+
description: "Approval resolved",
|
|
4186
|
+
content: {
|
|
4187
|
+
"application/json": {
|
|
4188
|
+
schema: {
|
|
4189
|
+
type: "object",
|
|
4190
|
+
properties: {
|
|
4191
|
+
ok: { type: "boolean" },
|
|
4192
|
+
approvalId: { type: "string" },
|
|
4193
|
+
approved: { type: "boolean" }
|
|
4194
|
+
}
|
|
4195
|
+
}
|
|
4196
|
+
}
|
|
4197
|
+
}
|
|
4198
|
+
},
|
|
4199
|
+
"404": {
|
|
4200
|
+
description: "Approval not found or expired",
|
|
4201
|
+
content: { "application/json": { schema: { $ref: "#/components/schemas/Error" } } }
|
|
4202
|
+
}
|
|
4203
|
+
}
|
|
4204
|
+
}
|
|
4205
|
+
},
|
|
4206
|
+
"/api/uploads/{key}": {
|
|
4207
|
+
get: {
|
|
4208
|
+
tags: ["Assets"],
|
|
4209
|
+
summary: "Retrieve an uploaded file",
|
|
4210
|
+
description: "Serves a file previously uploaded during a conversation. The key is returned in file content part references.",
|
|
4211
|
+
parameters: [
|
|
4212
|
+
{
|
|
4213
|
+
name: "key",
|
|
4214
|
+
in: "path",
|
|
4215
|
+
required: true,
|
|
4216
|
+
schema: { type: "string" },
|
|
4217
|
+
description: "Upload key (e.g. filename or storage path)"
|
|
4218
|
+
}
|
|
4219
|
+
],
|
|
4220
|
+
responses: {
|
|
4221
|
+
"200": {
|
|
4222
|
+
description: "File content with appropriate Content-Type",
|
|
4223
|
+
content: { "application/octet-stream": { schema: { type: "string", format: "binary" } } }
|
|
4224
|
+
},
|
|
4225
|
+
"404": {
|
|
4226
|
+
description: "Upload not found",
|
|
4227
|
+
content: { "application/json": { schema: { $ref: "#/components/schemas/Error" } } }
|
|
4228
|
+
}
|
|
4229
|
+
}
|
|
4230
|
+
}
|
|
4231
|
+
},
|
|
4232
|
+
"/api/cron/{jobName}": {
|
|
4233
|
+
get: {
|
|
4234
|
+
tags: ["Cron"],
|
|
4235
|
+
summary: "Trigger a cron job",
|
|
4236
|
+
description: "Triggers a named cron job defined in AGENT.md frontmatter. Supports continuation via the `continue` query parameter.",
|
|
4237
|
+
parameters: [
|
|
4238
|
+
{ name: "jobName", in: "path", required: true, schema: { type: "string" } },
|
|
4239
|
+
{
|
|
4240
|
+
name: "continue",
|
|
4241
|
+
in: "query",
|
|
4242
|
+
schema: { type: "string" },
|
|
4243
|
+
description: "Conversation ID to continue a previous cron run"
|
|
4244
|
+
}
|
|
4245
|
+
],
|
|
4246
|
+
responses: {
|
|
4247
|
+
"200": {
|
|
4248
|
+
description: "Cron job result",
|
|
4249
|
+
content: {
|
|
4250
|
+
"application/json": {
|
|
4251
|
+
schema: {
|
|
4252
|
+
type: "object",
|
|
4253
|
+
properties: {
|
|
4254
|
+
conversationId: { type: "string" },
|
|
4255
|
+
response: { type: "string" },
|
|
4256
|
+
steps: { type: "integer" },
|
|
4257
|
+
status: { type: "string" },
|
|
4258
|
+
continuation: { type: "string", description: "URL to continue this run" }
|
|
4259
|
+
}
|
|
4260
|
+
}
|
|
4261
|
+
}
|
|
4262
|
+
}
|
|
4263
|
+
},
|
|
4264
|
+
"404": {
|
|
4265
|
+
description: "Cron job not found",
|
|
4266
|
+
content: { "application/json": { schema: { $ref: "#/components/schemas/Error" } } }
|
|
4267
|
+
}
|
|
4268
|
+
}
|
|
4269
|
+
}
|
|
4270
|
+
}
|
|
4271
|
+
},
|
|
4272
|
+
tags: [
|
|
4273
|
+
{ name: "Health", description: "Server health check" },
|
|
4274
|
+
{ name: "Auth", description: "Session and authentication management" },
|
|
4275
|
+
{ name: "Conversations", description: "Create, list, read, rename, and delete conversations" },
|
|
4276
|
+
{
|
|
4277
|
+
name: "Messages",
|
|
4278
|
+
description: "Send messages and stream agent responses via SSE"
|
|
4279
|
+
},
|
|
4280
|
+
{ name: "Approvals", description: "Resolve gated tool approval requests" },
|
|
4281
|
+
{ name: "Assets", description: "Retrieve uploaded files" },
|
|
4282
|
+
{ name: "Cron", description: "Trigger cron jobs defined in AGENT.md" }
|
|
4283
|
+
]
|
|
4284
|
+
});
|
|
4285
|
+
var renderApiDocsHtml = (specUrl) => `<!DOCTYPE html>
|
|
4286
|
+
<html lang="en">
|
|
4287
|
+
<head>
|
|
4288
|
+
<meta charset="utf-8" />
|
|
4289
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
4290
|
+
<title>API Documentation</title>
|
|
4291
|
+
<style>body { margin: 0; }</style>
|
|
4292
|
+
</head>
|
|
4293
|
+
<body>
|
|
4294
|
+
<script id="api-reference" data-url="${specUrl}"></script>
|
|
4295
|
+
<script src="https://cdn.jsdelivr.net/npm/@scalar/api-reference"></script>
|
|
4296
|
+
</body>
|
|
4297
|
+
</html>`;
|
|
4298
|
+
|
|
3269
4299
|
// src/index.ts
|
|
3270
4300
|
import { createInterface } from "readline/promises";
|
|
3271
4301
|
|
|
@@ -3521,7 +4551,20 @@ var buildConfigFromOnboardingAnswers = (answers) => {
|
|
|
3521
4551
|
telemetry
|
|
3522
4552
|
};
|
|
3523
4553
|
if (messagingPlatform !== "none") {
|
|
3524
|
-
|
|
4554
|
+
const channelConfig = {
|
|
4555
|
+
platform: messagingPlatform
|
|
4556
|
+
};
|
|
4557
|
+
if (messagingPlatform === "resend") {
|
|
4558
|
+
const mode = String(answers["messaging.resend.mode"] ?? "auto-reply");
|
|
4559
|
+
if (mode === "tool") {
|
|
4560
|
+
channelConfig.mode = "tool";
|
|
4561
|
+
}
|
|
4562
|
+
const recipientsRaw = String(answers["messaging.resend.allowedRecipients"] ?? "");
|
|
4563
|
+
if (recipientsRaw.trim().length > 0) {
|
|
4564
|
+
channelConfig.allowedRecipients = recipientsRaw.split(",").map((s) => s.trim()).filter(Boolean);
|
|
4565
|
+
}
|
|
4566
|
+
}
|
|
4567
|
+
config.messaging = [channelConfig];
|
|
3525
4568
|
}
|
|
3526
4569
|
return config;
|
|
3527
4570
|
};
|
|
@@ -3993,7 +5036,7 @@ cp .env.example .env
|
|
|
3993
5036
|
poncho dev
|
|
3994
5037
|
\`\`\`
|
|
3995
5038
|
|
|
3996
|
-
Open \`http://localhost:3000\` for the web UI.
|
|
5039
|
+
Open \`http://localhost:3000\` for the web UI, or \`http://localhost:3000/api/docs\` for interactive API documentation.
|
|
3997
5040
|
|
|
3998
5041
|
On your first interactive session, the agent introduces its configurable capabilities.
|
|
3999
5042
|
While a response is streaming, you can stop it:
|
|
@@ -4115,7 +5158,7 @@ Core files:
|
|
|
4115
5158
|
|
|
4116
5159
|
- \`AGENT.md\`: behavior, model selection, runtime guidance
|
|
4117
5160
|
- \`poncho.config.js\`: runtime config (storage, auth, telemetry, MCP, tools)
|
|
4118
|
-
- \`.env\`: secrets and environment variables
|
|
5161
|
+
- \`.env\`: secrets and environment variables (loaded before the harness starts, so \`process.env\` is available in skill scripts)
|
|
4119
5162
|
|
|
4120
5163
|
Example \`poncho.config.js\`:
|
|
4121
5164
|
|
|
@@ -4141,18 +5184,20 @@ export default {
|
|
|
4141
5184
|
auth: { type: "bearer", tokenEnv: "GITHUB_TOKEN" },
|
|
4142
5185
|
},
|
|
4143
5186
|
],
|
|
5187
|
+
// Tool access: true (available), false (disabled), 'approval' (requires human approval)
|
|
4144
5188
|
tools: {
|
|
4145
|
-
|
|
4146
|
-
|
|
4147
|
-
read_file: true,
|
|
4148
|
-
write_file: true, // still gated by environment/policy
|
|
4149
|
-
},
|
|
5189
|
+
write_file: true, // gated by environment for writes
|
|
5190
|
+
send_email: 'approval', // requires human approval
|
|
4150
5191
|
byEnvironment: {
|
|
4151
5192
|
production: {
|
|
4152
|
-
|
|
5193
|
+
write_file: false,
|
|
5194
|
+
},
|
|
5195
|
+
development: {
|
|
5196
|
+
send_email: true, // skip approval in dev
|
|
4153
5197
|
},
|
|
4154
5198
|
},
|
|
4155
5199
|
},
|
|
5200
|
+
// webUi: false, // Disable built-in UI for API-only deployments
|
|
4156
5201
|
};
|
|
4157
5202
|
\`\`\`
|
|
4158
5203
|
|
|
@@ -4211,6 +5256,26 @@ Connect your agent to Slack so it responds to @mentions:
|
|
|
4211
5256
|
messaging: [{ platform: 'slack' }]
|
|
4212
5257
|
\`\`\`
|
|
4213
5258
|
|
|
5259
|
+
## Messaging (Email via Resend)
|
|
5260
|
+
|
|
5261
|
+
Connect your agent to email so users can interact by sending emails:
|
|
5262
|
+
|
|
5263
|
+
1. Set up a domain and enable Inbound at [resend.com](https://resend.com)
|
|
5264
|
+
2. Create a webhook for \`email.received\` pointing to \`https://<your-url>/api/messaging/resend\`
|
|
5265
|
+
3. Install the Resend SDK: \`npm install resend\`
|
|
5266
|
+
4. Set env vars:
|
|
5267
|
+
\`\`\`
|
|
5268
|
+
RESEND_API_KEY=re_...
|
|
5269
|
+
RESEND_WEBHOOK_SECRET=whsec_...
|
|
5270
|
+
RESEND_FROM=Agent <agent@yourdomain.com>
|
|
5271
|
+
\`\`\`
|
|
5272
|
+
5. Add to \`poncho.config.js\`:
|
|
5273
|
+
\`\`\`javascript
|
|
5274
|
+
messaging: [{ platform: 'resend' }]
|
|
5275
|
+
\`\`\`
|
|
5276
|
+
|
|
5277
|
+
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.
|
|
5278
|
+
|
|
4214
5279
|
## Deployment
|
|
4215
5280
|
|
|
4216
5281
|
\`\`\`bash
|
|
@@ -4942,20 +6007,162 @@ var createRequestHandler = async (options) => {
|
|
|
4942
6007
|
return { messages: [] };
|
|
4943
6008
|
},
|
|
4944
6009
|
async run(conversationId, input2) {
|
|
4945
|
-
|
|
4946
|
-
|
|
4947
|
-
|
|
6010
|
+
console.log("[messaging-runner] starting run for", conversationId, "task:", input2.task.slice(0, 80));
|
|
6011
|
+
const historyMessages = [...input2.messages];
|
|
6012
|
+
const userContent = input2.task;
|
|
6013
|
+
const updateConversation = async (patch) => {
|
|
6014
|
+
const fresh = await conversationStore.get(conversationId);
|
|
6015
|
+
if (!fresh) return;
|
|
6016
|
+
patch(fresh);
|
|
6017
|
+
fresh.updatedAt = Date.now();
|
|
6018
|
+
await conversationStore.update(fresh);
|
|
6019
|
+
};
|
|
6020
|
+
await updateConversation((c) => {
|
|
6021
|
+
c.messages = [...historyMessages, { role: "user", content: userContent }];
|
|
4948
6022
|
});
|
|
4949
|
-
|
|
4950
|
-
|
|
4951
|
-
|
|
4952
|
-
|
|
4953
|
-
|
|
4954
|
-
|
|
4955
|
-
|
|
6023
|
+
let latestRunId = "";
|
|
6024
|
+
let assistantResponse = "";
|
|
6025
|
+
const toolTimeline = [];
|
|
6026
|
+
const sections = [];
|
|
6027
|
+
let currentTools = [];
|
|
6028
|
+
let currentText = "";
|
|
6029
|
+
const buildMessages = () => {
|
|
6030
|
+
const draftSections = [
|
|
6031
|
+
...sections.map((s) => ({
|
|
6032
|
+
type: s.type,
|
|
6033
|
+
content: Array.isArray(s.content) ? [...s.content] : s.content
|
|
6034
|
+
}))
|
|
4956
6035
|
];
|
|
4957
|
-
|
|
6036
|
+
if (currentTools.length > 0) {
|
|
6037
|
+
draftSections.push({ type: "tools", content: [...currentTools] });
|
|
6038
|
+
}
|
|
6039
|
+
if (currentText.length > 0) {
|
|
6040
|
+
draftSections.push({ type: "text", content: currentText });
|
|
6041
|
+
}
|
|
6042
|
+
const hasDraftContent = assistantResponse.length > 0 || toolTimeline.length > 0 || draftSections.length > 0;
|
|
6043
|
+
if (!hasDraftContent) {
|
|
6044
|
+
return [...historyMessages, { role: "user", content: userContent }];
|
|
6045
|
+
}
|
|
6046
|
+
return [
|
|
6047
|
+
...historyMessages,
|
|
6048
|
+
{ role: "user", content: userContent },
|
|
6049
|
+
{
|
|
6050
|
+
role: "assistant",
|
|
6051
|
+
content: assistantResponse,
|
|
6052
|
+
metadata: toolTimeline.length > 0 || draftSections.length > 0 ? {
|
|
6053
|
+
toolActivity: [...toolTimeline],
|
|
6054
|
+
sections: draftSections.length > 0 ? draftSections : void 0
|
|
6055
|
+
} : void 0
|
|
6056
|
+
}
|
|
6057
|
+
];
|
|
6058
|
+
};
|
|
6059
|
+
const persistDraftAssistantTurn = async () => {
|
|
6060
|
+
if (assistantResponse.length === 0 && toolTimeline.length === 0) return;
|
|
6061
|
+
await updateConversation((c) => {
|
|
6062
|
+
c.messages = buildMessages();
|
|
6063
|
+
});
|
|
6064
|
+
};
|
|
6065
|
+
const runInput = {
|
|
6066
|
+
task: input2.task,
|
|
6067
|
+
conversationId,
|
|
6068
|
+
messages: input2.messages,
|
|
6069
|
+
files: input2.files,
|
|
6070
|
+
parameters: input2.metadata ? {
|
|
6071
|
+
__messaging_platform: input2.metadata.platform,
|
|
6072
|
+
__messaging_sender_id: input2.metadata.sender.id,
|
|
6073
|
+
__messaging_sender_name: input2.metadata.sender.name ?? "",
|
|
6074
|
+
__messaging_thread_id: input2.metadata.threadId
|
|
6075
|
+
} : void 0
|
|
6076
|
+
};
|
|
6077
|
+
try {
|
|
6078
|
+
for await (const event of harness.runWithTelemetry(runInput)) {
|
|
6079
|
+
if (event.type === "run:started") {
|
|
6080
|
+
latestRunId = event.runId;
|
|
6081
|
+
runOwners.set(event.runId, "local-owner");
|
|
6082
|
+
runConversations.set(event.runId, conversationId);
|
|
6083
|
+
}
|
|
6084
|
+
if (event.type === "model:chunk") {
|
|
6085
|
+
if (currentTools.length > 0) {
|
|
6086
|
+
sections.push({ type: "tools", content: currentTools });
|
|
6087
|
+
currentTools = [];
|
|
6088
|
+
}
|
|
6089
|
+
assistantResponse += event.content;
|
|
6090
|
+
currentText += event.content;
|
|
6091
|
+
}
|
|
6092
|
+
if (event.type === "tool:started") {
|
|
6093
|
+
if (currentText.length > 0) {
|
|
6094
|
+
sections.push({ type: "text", content: currentText });
|
|
6095
|
+
currentText = "";
|
|
6096
|
+
}
|
|
6097
|
+
const toolText = `- start \`${event.tool}\``;
|
|
6098
|
+
toolTimeline.push(toolText);
|
|
6099
|
+
currentTools.push(toolText);
|
|
6100
|
+
}
|
|
6101
|
+
if (event.type === "tool:completed") {
|
|
6102
|
+
const toolText = `- done \`${event.tool}\` (${event.duration}ms)`;
|
|
6103
|
+
toolTimeline.push(toolText);
|
|
6104
|
+
currentTools.push(toolText);
|
|
6105
|
+
}
|
|
6106
|
+
if (event.type === "tool:error") {
|
|
6107
|
+
const toolText = `- error \`${event.tool}\`: ${event.error}`;
|
|
6108
|
+
toolTimeline.push(toolText);
|
|
6109
|
+
currentTools.push(toolText);
|
|
6110
|
+
}
|
|
6111
|
+
if (event.type === "step:completed") {
|
|
6112
|
+
await persistDraftAssistantTurn();
|
|
6113
|
+
}
|
|
6114
|
+
if (event.type === "tool:approval:required") {
|
|
6115
|
+
const toolText = `- approval required \`${event.tool}\``;
|
|
6116
|
+
toolTimeline.push(toolText);
|
|
6117
|
+
currentTools.push(toolText);
|
|
6118
|
+
await persistDraftAssistantTurn();
|
|
6119
|
+
await persistConversationPendingApprovals(conversationId);
|
|
6120
|
+
}
|
|
6121
|
+
if (event.type === "tool:approval:granted") {
|
|
6122
|
+
const toolText = `- approval granted (${event.approvalId})`;
|
|
6123
|
+
toolTimeline.push(toolText);
|
|
6124
|
+
currentTools.push(toolText);
|
|
6125
|
+
await persistDraftAssistantTurn();
|
|
6126
|
+
}
|
|
6127
|
+
if (event.type === "tool:approval:denied") {
|
|
6128
|
+
const toolText = `- approval denied (${event.approvalId})`;
|
|
6129
|
+
toolTimeline.push(toolText);
|
|
6130
|
+
currentTools.push(toolText);
|
|
6131
|
+
await persistDraftAssistantTurn();
|
|
6132
|
+
}
|
|
6133
|
+
if (event.type === "run:completed" && assistantResponse.length === 0 && event.result.response) {
|
|
6134
|
+
assistantResponse = event.result.response;
|
|
6135
|
+
}
|
|
6136
|
+
if (event.type === "run:error") {
|
|
6137
|
+
assistantResponse = assistantResponse || `[Error: ${event.error.message}]`;
|
|
6138
|
+
}
|
|
6139
|
+
broadcastEvent(conversationId, event);
|
|
6140
|
+
}
|
|
6141
|
+
} catch (err) {
|
|
6142
|
+
console.error("[messaging-runner] run failed:", err instanceof Error ? err.message : err);
|
|
6143
|
+
assistantResponse = assistantResponse || `[Error: ${err instanceof Error ? err.message : "Unknown error"}]`;
|
|
4958
6144
|
}
|
|
6145
|
+
if (currentTools.length > 0) {
|
|
6146
|
+
sections.push({ type: "tools", content: currentTools });
|
|
6147
|
+
currentTools = [];
|
|
6148
|
+
}
|
|
6149
|
+
if (currentText.length > 0) {
|
|
6150
|
+
sections.push({ type: "text", content: currentText });
|
|
6151
|
+
currentText = "";
|
|
6152
|
+
}
|
|
6153
|
+
await updateConversation((c) => {
|
|
6154
|
+
c.messages = buildMessages();
|
|
6155
|
+
c.runtimeRunId = latestRunId || c.runtimeRunId;
|
|
6156
|
+
c.pendingApprovals = [];
|
|
6157
|
+
});
|
|
6158
|
+
finishConversationStream(conversationId);
|
|
6159
|
+
await persistConversationPendingApprovals(conversationId);
|
|
6160
|
+
if (latestRunId) {
|
|
6161
|
+
runOwners.delete(latestRunId);
|
|
6162
|
+
runConversations.delete(latestRunId);
|
|
6163
|
+
}
|
|
6164
|
+
console.log("[messaging-runner] run complete, response length:", assistantResponse.length);
|
|
6165
|
+
const response = assistantResponse;
|
|
4959
6166
|
return { response };
|
|
4960
6167
|
}
|
|
4961
6168
|
};
|
|
@@ -4982,7 +6189,8 @@ var createRequestHandler = async (options) => {
|
|
|
4982
6189
|
const bridge = new AgentBridge({
|
|
4983
6190
|
adapter,
|
|
4984
6191
|
runner: messagingRunner,
|
|
4985
|
-
waitUntil: waitUntilHook
|
|
6192
|
+
waitUntil: waitUntilHook,
|
|
6193
|
+
ownerId: "local-owner"
|
|
4986
6194
|
});
|
|
4987
6195
|
adapter.registerRoutes(messagingRouteRegistrar);
|
|
4988
6196
|
try {
|
|
@@ -4994,6 +6202,37 @@ var createRequestHandler = async (options) => {
|
|
|
4994
6202
|
` Slack messaging disabled: ${err instanceof Error ? err.message : String(err)}`
|
|
4995
6203
|
);
|
|
4996
6204
|
}
|
|
6205
|
+
} else if (channelConfig.platform === "resend") {
|
|
6206
|
+
const adapter = new ResendAdapter({
|
|
6207
|
+
apiKeyEnv: channelConfig.apiKeyEnv,
|
|
6208
|
+
webhookSecretEnv: channelConfig.webhookSecretEnv,
|
|
6209
|
+
fromEnv: channelConfig.fromEnv,
|
|
6210
|
+
allowedSenders: channelConfig.allowedSenders,
|
|
6211
|
+
mode: channelConfig.mode,
|
|
6212
|
+
allowedRecipients: channelConfig.allowedRecipients,
|
|
6213
|
+
maxSendsPerRun: channelConfig.maxSendsPerRun
|
|
6214
|
+
});
|
|
6215
|
+
const bridge = new AgentBridge({
|
|
6216
|
+
adapter,
|
|
6217
|
+
runner: messagingRunner,
|
|
6218
|
+
waitUntil: waitUntilHook,
|
|
6219
|
+
ownerId: "local-owner"
|
|
6220
|
+
});
|
|
6221
|
+
adapter.registerRoutes(messagingRouteRegistrar);
|
|
6222
|
+
try {
|
|
6223
|
+
await bridge.start();
|
|
6224
|
+
messagingBridges.push(bridge);
|
|
6225
|
+
const adapterTools = adapter.getToolDefinitions?.() ?? [];
|
|
6226
|
+
if (adapterTools.length > 0) {
|
|
6227
|
+
harness.registerTools(adapterTools);
|
|
6228
|
+
}
|
|
6229
|
+
const modeLabel = channelConfig.mode === "tool" ? "tool" : "auto-reply";
|
|
6230
|
+
console.log(` Resend email messaging enabled at /api/messaging/resend (mode: ${modeLabel})`);
|
|
6231
|
+
} catch (err) {
|
|
6232
|
+
console.warn(
|
|
6233
|
+
` Resend email messaging disabled: ${err instanceof Error ? err.message : String(err)}`
|
|
6234
|
+
);
|
|
6235
|
+
}
|
|
4997
6236
|
}
|
|
4998
6237
|
}
|
|
4999
6238
|
}
|
|
@@ -5002,6 +6241,7 @@ var createRequestHandler = async (options) => {
|
|
|
5002
6241
|
const authToken = process.env.PONCHO_AUTH_TOKEN ?? "";
|
|
5003
6242
|
const authRequired = config?.auth?.required ?? false;
|
|
5004
6243
|
const requireAuth = authRequired && authToken.length > 0;
|
|
6244
|
+
const webUiEnabled = config?.webUi !== false;
|
|
5005
6245
|
const isProduction = resolveHarnessEnvironment() === "production";
|
|
5006
6246
|
const secureCookies = isProduction;
|
|
5007
6247
|
const validateBearerToken = (authHeader) => {
|
|
@@ -5023,35 +6263,45 @@ var createRequestHandler = async (options) => {
|
|
|
5023
6263
|
return;
|
|
5024
6264
|
}
|
|
5025
6265
|
const [pathname] = request.url.split("?");
|
|
5026
|
-
if (
|
|
5027
|
-
|
|
5028
|
-
|
|
5029
|
-
|
|
5030
|
-
|
|
5031
|
-
|
|
5032
|
-
|
|
5033
|
-
|
|
5034
|
-
|
|
5035
|
-
|
|
5036
|
-
|
|
5037
|
-
|
|
5038
|
-
|
|
5039
|
-
|
|
5040
|
-
|
|
5041
|
-
|
|
6266
|
+
if (webUiEnabled) {
|
|
6267
|
+
if (request.method === "GET" && (pathname === "/" || pathname.startsWith("/c/"))) {
|
|
6268
|
+
writeHtml(response, 200, renderWebUiHtml({ agentName }));
|
|
6269
|
+
return;
|
|
6270
|
+
}
|
|
6271
|
+
if (pathname === "/manifest.json" && request.method === "GET") {
|
|
6272
|
+
response.writeHead(200, { "Content-Type": "application/manifest+json" });
|
|
6273
|
+
response.end(renderManifest({ agentName }));
|
|
6274
|
+
return;
|
|
6275
|
+
}
|
|
6276
|
+
if (pathname === "/sw.js" && request.method === "GET") {
|
|
6277
|
+
response.writeHead(200, {
|
|
6278
|
+
"Content-Type": "application/javascript",
|
|
6279
|
+
"Service-Worker-Allowed": "/"
|
|
6280
|
+
});
|
|
6281
|
+
response.end(renderServiceWorker());
|
|
6282
|
+
return;
|
|
6283
|
+
}
|
|
6284
|
+
if (pathname === "/icon.svg" && request.method === "GET") {
|
|
6285
|
+
response.writeHead(200, { "Content-Type": "image/svg+xml" });
|
|
6286
|
+
response.end(renderIconSvg({ agentName }));
|
|
6287
|
+
return;
|
|
6288
|
+
}
|
|
6289
|
+
if ((pathname === "/icon-192.png" || pathname === "/icon-512.png") && request.method === "GET") {
|
|
6290
|
+
response.writeHead(302, { Location: "/icon.svg" });
|
|
6291
|
+
response.end();
|
|
6292
|
+
return;
|
|
6293
|
+
}
|
|
5042
6294
|
}
|
|
5043
|
-
if (pathname === "/
|
|
5044
|
-
response
|
|
5045
|
-
response.end(renderIconSvg({ agentName }));
|
|
6295
|
+
if (pathname === "/health" && request.method === "GET") {
|
|
6296
|
+
writeJson(response, 200, { status: "ok" });
|
|
5046
6297
|
return;
|
|
5047
6298
|
}
|
|
5048
|
-
if (
|
|
5049
|
-
response
|
|
5050
|
-
response.end();
|
|
6299
|
+
if (pathname === "/api/openapi.json" && request.method === "GET") {
|
|
6300
|
+
writeJson(response, 200, buildOpenApiSpec({ agentName }));
|
|
5051
6301
|
return;
|
|
5052
6302
|
}
|
|
5053
|
-
if (pathname === "/
|
|
5054
|
-
|
|
6303
|
+
if (pathname === "/api/docs" && request.method === "GET") {
|
|
6304
|
+
writeHtml(response, 200, renderApiDocsHtml("/api/openapi.json"));
|
|
5055
6305
|
return;
|
|
5056
6306
|
}
|
|
5057
6307
|
const messagingByMethod = messagingRoutes.get(pathname ?? "");
|
|
@@ -5249,12 +6499,15 @@ var createRequestHandler = async (options) => {
|
|
|
5249
6499
|
response.end();
|
|
5250
6500
|
return;
|
|
5251
6501
|
}
|
|
5252
|
-
|
|
5253
|
-
|
|
5254
|
-
|
|
5255
|
-
|
|
5256
|
-
|
|
5257
|
-
|
|
6502
|
+
const liveOnly = (request.url ?? "").includes("live_only=true");
|
|
6503
|
+
if (!liveOnly) {
|
|
6504
|
+
for (const bufferedEvent of stream.buffer) {
|
|
6505
|
+
try {
|
|
6506
|
+
response.write(formatSseEvent(bufferedEvent));
|
|
6507
|
+
} catch {
|
|
6508
|
+
response.end();
|
|
6509
|
+
return;
|
|
6510
|
+
}
|
|
5258
6511
|
}
|
|
5259
6512
|
}
|
|
5260
6513
|
if (stream.finished) {
|
|
@@ -5298,11 +6551,14 @@ var createRequestHandler = async (options) => {
|
|
|
5298
6551
|
for (const approval of livePending) {
|
|
5299
6552
|
mergedPendingById.set(approval.approvalId, approval);
|
|
5300
6553
|
}
|
|
6554
|
+
const activeStream = conversationEventStreams.get(conversationId);
|
|
6555
|
+
const hasActiveRun = !!activeStream && !activeStream.finished;
|
|
5301
6556
|
writeJson(response, 200, {
|
|
5302
6557
|
conversation: {
|
|
5303
6558
|
...conversation,
|
|
5304
6559
|
pendingApprovals: Array.from(mergedPendingById.values())
|
|
5305
|
-
}
|
|
6560
|
+
},
|
|
6561
|
+
hasActiveRun
|
|
5306
6562
|
});
|
|
5307
6563
|
return;
|
|
5308
6564
|
}
|
|
@@ -5563,6 +6819,7 @@ var createRequestHandler = async (options) => {
|
|
|
5563
6819
|
})).filter((item) => item.content.length > 0);
|
|
5564
6820
|
for await (const event of harness.runWithTelemetry({
|
|
5565
6821
|
task: messageText,
|
|
6822
|
+
conversationId,
|
|
5566
6823
|
parameters: {
|
|
5567
6824
|
...bodyParameters ?? {},
|
|
5568
6825
|
__conversationRecallCorpus: recallCorpus,
|
|
@@ -5807,6 +7064,7 @@ var createRequestHandler = async (options) => {
|
|
|
5807
7064
|
const softDeadlineMs = platformMaxDurationSec > 0 ? platformMaxDurationSec * 800 : 0;
|
|
5808
7065
|
for await (const event of harness.runWithTelemetry({
|
|
5809
7066
|
task: cronJob.task,
|
|
7067
|
+
conversationId: conversation.conversationId,
|
|
5810
7068
|
parameters: { __activeConversationId: conversation.conversationId },
|
|
5811
7069
|
messages: historyMessages,
|
|
5812
7070
|
abortSignal: abortController.signal
|
|
@@ -5968,6 +7226,7 @@ var startDevServer = async (port, options) => {
|
|
|
5968
7226
|
let currentText = "";
|
|
5969
7227
|
for await (const event of harness.runWithTelemetry({
|
|
5970
7228
|
task: config.task,
|
|
7229
|
+
conversationId: conversation.conversationId,
|
|
5971
7230
|
parameters: { __activeConversationId: conversation.conversationId },
|
|
5972
7231
|
messages: []
|
|
5973
7232
|
})) {
|
|
@@ -6158,7 +7417,7 @@ var runInteractive = async (workingDir, params) => {
|
|
|
6158
7417
|
await harness.initialize();
|
|
6159
7418
|
const identity = await ensureAgentIdentity2(workingDir);
|
|
6160
7419
|
try {
|
|
6161
|
-
const { runInteractiveInk } = await import("./run-interactive-ink-
|
|
7420
|
+
const { runInteractiveInk } = await import("./run-interactive-ink-SLWDVTDX.js");
|
|
6162
7421
|
await runInteractiveInk({
|
|
6163
7422
|
harness,
|
|
6164
7423
|
params,
|