@a-t-h-i/bot-lobby 0.6.45 → 0.6.47
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/THIRD_PARTY_NOTICES.md +10 -0
- package/package.json +7 -1
- package/prompts/qa.md +12 -4
- package/prompts/reviewer.md +7 -1
- package/src/execution/task-cancellation.ts +27 -0
- package/src/lobby/host.ts +4 -0
- package/src/lobby/prompts.ts +1 -2
- package/src/lobby/service.ts +77 -1
- package/src/master/master.ts +3 -3
- package/src/roles/reviewer.ts +91 -13
- package/src/schemas/findings.ts +2 -0
- package/src/state/persistence.ts +26 -1
- package/src/webui/api/index.ts +13 -6
- package/src/webui/api/tasks.ts +39 -2
- package/src/webui/palette-core.ts +233 -2
- package/src/webui/protocol.ts +6 -74
- package/src/workflow/workflow.ts +18 -4
- package/webui/dist/assets/index-CkQ5Eduy.js +110 -0
- package/webui/dist/assets/index-Dl4yHBRY.css +2 -0
- package/webui/dist/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
- package/webui/dist/build.json +57 -69
- package/webui/dist/index.html +2 -2
- package/src/lobby/models/metrics.ts +0 -14
- package/src/webui/api/metrics.ts +0 -96
- package/webui/dist/assets/index-ClwTDiyo.css +0 -2
- package/webui/dist/assets/index-Ct47Kp7Q.js +0 -110
|
@@ -174,10 +174,11 @@ export const DEFAULT_ID = "mist"
|
|
|
174
174
|
|
|
175
175
|
/**
|
|
176
176
|
* The themes that come with the page. The first is the page's own colours and
|
|
177
|
-
* sets nothing;
|
|
177
|
+
* sets nothing; Blossom through Iris are T3 Code's built-in themes (MIT, Copyright (c) 2026
|
|
178
178
|
* T3 Tools Inc., packages/shared/src/themePalettes.ts), their roles mapped onto
|
|
179
179
|
* the shadcn variables: canvas is the background, surface the card, accent the
|
|
180
|
-
* primary, accent surface the accent, error the destructive colour.
|
|
180
|
+
* primary, accent surface the accent, error the destructive colour. Slate,
|
|
181
|
+
* Sand, Rosewood, Aurora and Midnight are original complementary palettes.
|
|
181
182
|
*/
|
|
182
183
|
export const PRESETS: readonly Palette[] = [
|
|
183
184
|
{ id: DEFAULT_ID, name: "Default" },
|
|
@@ -411,6 +412,236 @@ export const PRESETS: readonly Palette[] = [
|
|
|
411
412
|
"chart-1": "oklch(0.671712 0.169136 293.929)",
|
|
412
413
|
},
|
|
413
414
|
},
|
|
415
|
+
{
|
|
416
|
+
id: "slate",
|
|
417
|
+
name: "Slate",
|
|
418
|
+
light: {
|
|
419
|
+
"background": "#f5f7fa",
|
|
420
|
+
"foreground": "#202b3b",
|
|
421
|
+
"card": "#ffffff",
|
|
422
|
+
"card-foreground": "#202b3b",
|
|
423
|
+
"popover": "#ffffff",
|
|
424
|
+
"popover-foreground": "#202b3b",
|
|
425
|
+
"primary": "#465b78",
|
|
426
|
+
"primary-foreground": "#ffffff",
|
|
427
|
+
"secondary": "#e7ecf2",
|
|
428
|
+
"secondary-foreground": "#202b3b",
|
|
429
|
+
"muted": "#edf1f6",
|
|
430
|
+
"muted-foreground": "#526177",
|
|
431
|
+
"accent": "#e0e7ef",
|
|
432
|
+
"accent-foreground": "#202b3b",
|
|
433
|
+
"destructive": "#a52a34",
|
|
434
|
+
"border": "#cad3df",
|
|
435
|
+
"input": "#b9c5d4",
|
|
436
|
+
"ring": "#465b78",
|
|
437
|
+
"chart-1": "#465b78",
|
|
438
|
+
},
|
|
439
|
+
dark: {
|
|
440
|
+
"background": "#141b26",
|
|
441
|
+
"foreground": "#e8edf5",
|
|
442
|
+
"card": "#1c2533",
|
|
443
|
+
"card-foreground": "#e8edf5",
|
|
444
|
+
"popover": "#202b3b",
|
|
445
|
+
"popover-foreground": "#e8edf5",
|
|
446
|
+
"primary": "#adc0dc",
|
|
447
|
+
"primary-foreground": "#141b26",
|
|
448
|
+
"secondary": "#2a3648",
|
|
449
|
+
"secondary-foreground": "#e8edf5",
|
|
450
|
+
"muted": "#243043",
|
|
451
|
+
"muted-foreground": "#afbdd0",
|
|
452
|
+
"accent": "#33445d",
|
|
453
|
+
"accent-foreground": "#e8edf5",
|
|
454
|
+
"destructive": "#fb8990",
|
|
455
|
+
"border": "#43536a",
|
|
456
|
+
"input": "#53647d",
|
|
457
|
+
"ring": "#adc0dc",
|
|
458
|
+
"chart-1": "#adc0dc",
|
|
459
|
+
},
|
|
460
|
+
},
|
|
461
|
+
{
|
|
462
|
+
id: "sand",
|
|
463
|
+
name: "Sand",
|
|
464
|
+
light: {
|
|
465
|
+
"background": "#faf7f1",
|
|
466
|
+
"foreground": "#352b22",
|
|
467
|
+
"card": "#fffdf8",
|
|
468
|
+
"card-foreground": "#352b22",
|
|
469
|
+
"popover": "#fffdf8",
|
|
470
|
+
"popover-foreground": "#352b22",
|
|
471
|
+
"primary": "#765536",
|
|
472
|
+
"primary-foreground": "#ffffff",
|
|
473
|
+
"secondary": "#eee6da",
|
|
474
|
+
"secondary-foreground": "#352b22",
|
|
475
|
+
"muted": "#f2ece2",
|
|
476
|
+
"muted-foreground": "#6c5b49",
|
|
477
|
+
"accent": "#e9ddcb",
|
|
478
|
+
"accent-foreground": "#352b22",
|
|
479
|
+
"destructive": "#a52a34",
|
|
480
|
+
"border": "#d9cbb8",
|
|
481
|
+
"input": "#c9b79e",
|
|
482
|
+
"ring": "#765536",
|
|
483
|
+
"chart-1": "#765536",
|
|
484
|
+
},
|
|
485
|
+
dark: {
|
|
486
|
+
"background": "#211c17",
|
|
487
|
+
"foreground": "#f4ece0",
|
|
488
|
+
"card": "#2c251e",
|
|
489
|
+
"card-foreground": "#f4ece0",
|
|
490
|
+
"popover": "#352c23",
|
|
491
|
+
"popover-foreground": "#f4ece0",
|
|
492
|
+
"primary": "#dcbc92",
|
|
493
|
+
"primary-foreground": "#211c17",
|
|
494
|
+
"secondary": "#3c3227",
|
|
495
|
+
"secondary-foreground": "#f4ece0",
|
|
496
|
+
"muted": "#352d24",
|
|
497
|
+
"muted-foreground": "#c9b9a5",
|
|
498
|
+
"accent": "#4a3b2b",
|
|
499
|
+
"accent-foreground": "#f4ece0",
|
|
500
|
+
"destructive": "#fb8990",
|
|
501
|
+
"border": "#64503e",
|
|
502
|
+
"input": "#79614b",
|
|
503
|
+
"ring": "#dcbc92",
|
|
504
|
+
"chart-1": "#dcbc92",
|
|
505
|
+
},
|
|
506
|
+
},
|
|
507
|
+
{
|
|
508
|
+
id: "rosewood",
|
|
509
|
+
name: "Rosewood",
|
|
510
|
+
light: {
|
|
511
|
+
"background": "#fbf6f5",
|
|
512
|
+
"foreground": "#3b262c",
|
|
513
|
+
"card": "#fffafa",
|
|
514
|
+
"card-foreground": "#3b262c",
|
|
515
|
+
"popover": "#fffafa",
|
|
516
|
+
"popover-foreground": "#3b262c",
|
|
517
|
+
"primary": "#854252",
|
|
518
|
+
"primary-foreground": "#ffffff",
|
|
519
|
+
"secondary": "#f0e2e5",
|
|
520
|
+
"secondary-foreground": "#3b262c",
|
|
521
|
+
"muted": "#f5eaec",
|
|
522
|
+
"muted-foreground": "#76525d",
|
|
523
|
+
"accent": "#edd6dd",
|
|
524
|
+
"accent-foreground": "#3b262c",
|
|
525
|
+
"destructive": "#a52a34",
|
|
526
|
+
"border": "#dfc5cc",
|
|
527
|
+
"input": "#d0afb9",
|
|
528
|
+
"ring": "#854252",
|
|
529
|
+
"chart-1": "#854252",
|
|
530
|
+
},
|
|
531
|
+
dark: {
|
|
532
|
+
"background": "#241a20",
|
|
533
|
+
"foreground": "#f6e8ed",
|
|
534
|
+
"card": "#30222a",
|
|
535
|
+
"card-foreground": "#f6e8ed",
|
|
536
|
+
"popover": "#3a2932",
|
|
537
|
+
"popover-foreground": "#f6e8ed",
|
|
538
|
+
"primary": "#e3a5b7",
|
|
539
|
+
"primary-foreground": "#241a20",
|
|
540
|
+
"secondary": "#442e38",
|
|
541
|
+
"secondary-foreground": "#f6e8ed",
|
|
542
|
+
"muted": "#3a2932",
|
|
543
|
+
"muted-foreground": "#d2b4c0",
|
|
544
|
+
"accent": "#553441",
|
|
545
|
+
"accent-foreground": "#f6e8ed",
|
|
546
|
+
"destructive": "#fb8990",
|
|
547
|
+
"border": "#70505d",
|
|
548
|
+
"input": "#856170",
|
|
549
|
+
"ring": "#e3a5b7",
|
|
550
|
+
"chart-1": "#e3a5b7",
|
|
551
|
+
},
|
|
552
|
+
},
|
|
553
|
+
{
|
|
554
|
+
id: "aurora",
|
|
555
|
+
name: "Aurora",
|
|
556
|
+
light: {
|
|
557
|
+
"background": "#f2faf8",
|
|
558
|
+
"foreground": "#193a37",
|
|
559
|
+
"card": "#fafffe",
|
|
560
|
+
"card-foreground": "#193a37",
|
|
561
|
+
"popover": "#fafffe",
|
|
562
|
+
"popover-foreground": "#193a37",
|
|
563
|
+
"primary": "#216c63",
|
|
564
|
+
"primary-foreground": "#ffffff",
|
|
565
|
+
"secondary": "#def0eb",
|
|
566
|
+
"secondary-foreground": "#193a37",
|
|
567
|
+
"muted": "#e6f2ef",
|
|
568
|
+
"muted-foreground": "#456c66",
|
|
569
|
+
"accent": "#eee5f6",
|
|
570
|
+
"accent-foreground": "#193a37",
|
|
571
|
+
"destructive": "#a52a34",
|
|
572
|
+
"border": "#bbd7d1",
|
|
573
|
+
"input": "#a7c8c0",
|
|
574
|
+
"ring": "#216c63",
|
|
575
|
+
"chart-1": "#216c63",
|
|
576
|
+
},
|
|
577
|
+
dark: {
|
|
578
|
+
"background": "#142321",
|
|
579
|
+
"foreground": "#e1f5ef",
|
|
580
|
+
"card": "#1d302d",
|
|
581
|
+
"card-foreground": "#e1f5ef",
|
|
582
|
+
"popover": "#253d38",
|
|
583
|
+
"popover-foreground": "#e1f5ef",
|
|
584
|
+
"primary": "#87d5c1",
|
|
585
|
+
"primary-foreground": "#142321",
|
|
586
|
+
"secondary": "#29443e",
|
|
587
|
+
"secondary-foreground": "#e1f5ef",
|
|
588
|
+
"muted": "#243c36",
|
|
589
|
+
"muted-foreground": "#a9cfc3",
|
|
590
|
+
"accent": "#3d3352",
|
|
591
|
+
"accent-foreground": "#e1f5ef",
|
|
592
|
+
"destructive": "#fb8990",
|
|
593
|
+
"border": "#466b60",
|
|
594
|
+
"input": "#568172",
|
|
595
|
+
"ring": "#87d5c1",
|
|
596
|
+
"chart-1": "#87d5c1",
|
|
597
|
+
},
|
|
598
|
+
},
|
|
599
|
+
{
|
|
600
|
+
id: "midnight",
|
|
601
|
+
name: "Midnight",
|
|
602
|
+
light: {
|
|
603
|
+
"background": "#f5f6fb",
|
|
604
|
+
"foreground": "#282e48",
|
|
605
|
+
"card": "#fcfcff",
|
|
606
|
+
"card-foreground": "#282e48",
|
|
607
|
+
"popover": "#fcfcff",
|
|
608
|
+
"popover-foreground": "#282e48",
|
|
609
|
+
"primary": "#4f5f96",
|
|
610
|
+
"primary-foreground": "#ffffff",
|
|
611
|
+
"secondary": "#e5e8f4",
|
|
612
|
+
"secondary-foreground": "#282e48",
|
|
613
|
+
"muted": "#eceef7",
|
|
614
|
+
"muted-foreground": "#565f7d",
|
|
615
|
+
"accent": "#dfe4f6",
|
|
616
|
+
"accent-foreground": "#282e48",
|
|
617
|
+
"destructive": "#a52a34",
|
|
618
|
+
"border": "#c7cee5",
|
|
619
|
+
"input": "#b4bfdc",
|
|
620
|
+
"ring": "#4f5f96",
|
|
621
|
+
"chart-1": "#4f5f96",
|
|
622
|
+
},
|
|
623
|
+
dark: {
|
|
624
|
+
"background": "#151a2b",
|
|
625
|
+
"foreground": "#e8ecfa",
|
|
626
|
+
"card": "#1f263b",
|
|
627
|
+
"card-foreground": "#e8ecfa",
|
|
628
|
+
"popover": "#29314a",
|
|
629
|
+
"popover-foreground": "#e8ecfa",
|
|
630
|
+
"primary": "#acbbed",
|
|
631
|
+
"primary-foreground": "#151a2b",
|
|
632
|
+
"secondary": "#303954",
|
|
633
|
+
"secondary-foreground": "#e8ecfa",
|
|
634
|
+
"muted": "#28314b",
|
|
635
|
+
"muted-foreground": "#b1bedf",
|
|
636
|
+
"accent": "#3a466a",
|
|
637
|
+
"accent-foreground": "#e8ecfa",
|
|
638
|
+
"destructive": "#fb8990",
|
|
639
|
+
"border": "#48577d",
|
|
640
|
+
"input": "#596990",
|
|
641
|
+
"ring": "#acbbed",
|
|
642
|
+
"chart-1": "#acbbed",
|
|
643
|
+
},
|
|
644
|
+
},
|
|
414
645
|
]
|
|
415
646
|
|
|
416
647
|
/** Colours for a preview swatch, per mode: the default's own values by hand, the rest from the theme. */
|
package/src/webui/protocol.ts
CHANGED
|
@@ -276,78 +276,6 @@ export interface PlannerSeedInfo {
|
|
|
276
276
|
body: string;
|
|
277
277
|
}
|
|
278
278
|
|
|
279
|
-
/** The Metrics tab's KPI row as data: runs, success, average run, cost, tasks. */
|
|
280
|
-
export interface MetricsTiles {
|
|
281
|
-
runs: number;
|
|
282
|
-
successes: number;
|
|
283
|
-
stalls: number;
|
|
284
|
-
avgMs: number;
|
|
285
|
-
p90Ms: number;
|
|
286
|
-
cost: number;
|
|
287
|
-
completed: number;
|
|
288
|
-
active: number;
|
|
289
|
-
avgCompleteMs: number;
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
/** One per-model group as `metrics.get` carries it. */
|
|
293
|
-
export interface MetricGroupInfo {
|
|
294
|
-
model: string;
|
|
295
|
-
thinking: string;
|
|
296
|
-
kinds: string[];
|
|
297
|
-
runs: number;
|
|
298
|
-
successes: number;
|
|
299
|
-
timeouts: number;
|
|
300
|
-
avgMs: number;
|
|
301
|
-
p50Ms: number;
|
|
302
|
-
p90Ms: number;
|
|
303
|
-
avgTurns: number;
|
|
304
|
-
avgTools: number;
|
|
305
|
-
avgTokens: number;
|
|
306
|
-
avgCost: number;
|
|
307
|
-
totalCost: number;
|
|
308
|
-
tokensPerSecond: number;
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
/** One agent's share of all run time. */
|
|
312
|
-
export interface MetricsAgentShare {
|
|
313
|
-
agent: string;
|
|
314
|
-
runs: number;
|
|
315
|
-
ms: number;
|
|
316
|
-
share: number;
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
/** Completed tasks' request-to-done time by the oracle's model and thinking. */
|
|
320
|
-
export interface MetricsTaskTime {
|
|
321
|
-
model: string;
|
|
322
|
-
thinking: string;
|
|
323
|
-
tasks: number;
|
|
324
|
-
avgMs: number;
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
/** What the classifier did and spared, when it has run. */
|
|
328
|
-
export interface MetricsClassifier {
|
|
329
|
-
calls: number;
|
|
330
|
-
ok: number;
|
|
331
|
-
p50Ms: number;
|
|
332
|
-
p90Ms: number;
|
|
333
|
-
input: number;
|
|
334
|
-
byPurpose: Record<string, number>;
|
|
335
|
-
seatRunsSkipped: number;
|
|
336
|
-
questionsAnswered: number;
|
|
337
|
-
quickFixesHeld: number;
|
|
338
|
-
routed: number;
|
|
339
|
-
routedOk: number;
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
/** `metrics.get`: the dashboard figures for one grouping and search. */
|
|
343
|
-
export interface MetricsData {
|
|
344
|
-
tiles: MetricsTiles;
|
|
345
|
-
groups: MetricGroupInfo[];
|
|
346
|
-
daily: Array<{ date: string; runs: number; successes: number; cost: number }>;
|
|
347
|
-
timeShare: { byAgent: MetricsAgentShare[]; taskTimes: MetricsTaskTime[] };
|
|
348
|
-
classifier?: MetricsClassifier;
|
|
349
|
-
}
|
|
350
|
-
|
|
351
279
|
/** Who a knowledge file belongs to. */
|
|
352
280
|
export type KnowledgeAgentName = "master" | "designer" | "backend" | "qa";
|
|
353
281
|
|
|
@@ -395,6 +323,8 @@ export interface EntryRefInfo {
|
|
|
395
323
|
|
|
396
324
|
/** `tasks.get`: one task read whole, in the terminal's wording. */
|
|
397
325
|
export interface TaskDetail {
|
|
326
|
+
canAccept: boolean;
|
|
327
|
+
canForceClose: boolean;
|
|
398
328
|
timing?: TimingProjection;
|
|
399
329
|
delivery?: Delivery;
|
|
400
330
|
/** The full request, when it says more than the title. */
|
|
@@ -414,7 +344,7 @@ export interface TaskDetail {
|
|
|
414
344
|
work?: WorkClock;
|
|
415
345
|
amendments: string[];
|
|
416
346
|
/** Approvals waiting on you: `kind` plus `for <domain>: <detail>`. */
|
|
417
|
-
waiting: Array<{ kind: string; detail: string }>;
|
|
347
|
+
waiting: Array<{ id: string; domain: string; kind: string; detail: string }>;
|
|
418
348
|
blockers: Array<{ reason: string; need: string }>;
|
|
419
349
|
/** The last six runs, one line each (`✓ DEV worker · 3m 12s · …`). */
|
|
420
350
|
runs: string[];
|
|
@@ -589,6 +519,9 @@ export interface Api {
|
|
|
589
519
|
"tasks.get": { request: { taskId: string }; result: TaskDetail };
|
|
590
520
|
"tasks.open": { request: { taskId: string }; result: { sessionId?: string; key?: string; notice?: string } };
|
|
591
521
|
"tasks.resume": { request: { taskId: string }; result: { notice: string; key?: string } };
|
|
522
|
+
"tasks.accept": { request: { taskId: string }; result: { notice: string } };
|
|
523
|
+
"tasks.forceClose": { request: { taskId: string; title: string }; result: { notice: string } };
|
|
524
|
+
"tasks.resolveApproval": { request: { taskId: string; approvalId: string; decision: "approved" | "rejected"; note?: string }; result: { notice: string } };
|
|
592
525
|
"tasks.deliveryReview": { request: { taskId: string }; result: { delivery: Delivery } };
|
|
593
526
|
"tasks.deliveryDefer": { request: { taskId: string; reviewId: string }; result: { delivery: Delivery } };
|
|
594
527
|
"tasks.deliver": { request: { taskId: string; reviewId: string; action: DeliveryAction; confirmMain?: boolean }; result: { delivery: Delivery } };
|
|
@@ -624,7 +557,6 @@ export interface Api {
|
|
|
624
557
|
"quickfix.cancel": { request: { id: string }; result: { notice: string } };
|
|
625
558
|
"quickfix.runAnyway": { request: { id: string }; result: { notice: string } };
|
|
626
559
|
"quickfix.movedToTask": { request: { id: string }; result: { notice: string; key?: string } };
|
|
627
|
-
"metrics.get": { request: { groupBy: "model" | "model-kind"; query?: string; from?: string; to?: string; timeZone?: string }; result: MetricsData };
|
|
628
560
|
"projects.browse": { request: { path?: string }; result: FolderListing };
|
|
629
561
|
"projects.open": { request: { path: string }; result: { project: ProjectInfo } };
|
|
630
562
|
"knowledge.files": { request: Record<string, never>; result: { files: KnowledgeFileInfo[]; models?: Partial<Record<KnowledgeAgentName, string>> } };
|
package/src/workflow/workflow.ts
CHANGED
|
@@ -28,6 +28,7 @@ import { spawnPiProcess, type ProcessRunner, type RelayAsk } from "../execution/
|
|
|
28
28
|
import { previewDir } from "../ask/relay.ts";
|
|
29
29
|
import type { AskQuestion, AskResult } from "../ask/types.ts";
|
|
30
30
|
import { mapConcurrent } from "../execution/agent-runner.ts";
|
|
31
|
+
import { taskExecution } from "../execution/task-cancellation.ts";
|
|
31
32
|
import { parseWorkerResult } from "../roles/worker.ts";
|
|
32
33
|
import { autoNote, DESK_TOOLS, DeskSession } from "../desk/session.ts";
|
|
33
34
|
import type { Handover } from "../desk/desk.ts";
|
|
@@ -1171,7 +1172,7 @@ function handoverLog(handovers: readonly Handover[]): string {
|
|
|
1171
1172
|
return `File handovers:\n${lines.join("\n")}`;
|
|
1172
1173
|
}
|
|
1173
1174
|
|
|
1174
|
-
function handleResolveApproval(task: Task, params: OrchestrateParams): string {
|
|
1175
|
+
export function handleResolveApproval(task: Task, params: OrchestrateParams): string {
|
|
1175
1176
|
const id = params.approvalId?.trim();
|
|
1176
1177
|
const decision = params.decision;
|
|
1177
1178
|
if (!id || (decision !== "approved" && decision !== "rejected")) {
|
|
@@ -1288,7 +1289,9 @@ function qaReport(header: string, result: ReviewResult, decision: LoopDecision,
|
|
|
1288
1289
|
: decision === "waived"
|
|
1289
1290
|
? "The user accepted the work as it is, without a QA pass. Call action=complete now with a short summary that names what QA still asked for."
|
|
1290
1291
|
: decision === "iterate"
|
|
1291
|
-
?
|
|
1292
|
+
? result.unread
|
|
1293
|
+
? "QA returned no readable verdict, so nothing is known about the work yet: re-run action=qa; there is nothing to fix first."
|
|
1294
|
+
: "The QA gate did not pass: delegate the required changes to the owning domain, then re-run action=qa."
|
|
1292
1295
|
: "The review limit is reached and the user did not accept the work: mark the task blocked and tell the user what QA still asks for. They can accept it with /bot-lobby accept.",
|
|
1293
1296
|
result.pushback ? pushbackLine(result.pushback, "qa") : "",
|
|
1294
1297
|
issues.length > 0 ? `Issues: ${issues.join("; ")}` : "",
|
|
@@ -1557,7 +1560,9 @@ async function handleComplete(task: Task, params: OrchestrateParams, deps: Workf
|
|
|
1557
1560
|
// The gate refuses before any side effect, so a refusal leaves the task untouched.
|
|
1558
1561
|
const oversized = overThreshold(readDataRoots(deps.root, deps.configDir), deps.config.knowledge.compactionThreshold);
|
|
1559
1562
|
if (oversized.length > 0) throw new Error(`cannot complete: knowledge files over the compaction threshold: ${oversized.map((entry) => `${entry.agent}/${entry.file} (${entry.chars})`).join(", ")}. First record domain-relevant facts where they belong with action=knowledge (domain=designer|backend|qa), then rewrite each oversized file with action=compact, then call complete again.`);
|
|
1563
|
+
deps.signal?.throwIfAborted();
|
|
1560
1564
|
const committed = await commitTaskWork(task, params, deps);
|
|
1565
|
+
deps.signal?.throwIfAborted();
|
|
1561
1566
|
const summary = params.text?.trim() || task.proposal || task.title;
|
|
1562
1567
|
recordCompletion(deps, task, summary);
|
|
1563
1568
|
removeTaskScratchpads(deps.root, deps.configDir, task.id);
|
|
@@ -1598,6 +1603,7 @@ async function commitTaskWork(task: Task, params: OrchestrateParams, deps: Workf
|
|
|
1598
1603
|
}
|
|
1599
1604
|
let result: Awaited<ReturnType<typeof commitFiles>>;
|
|
1600
1605
|
try {
|
|
1606
|
+
deps.signal?.throwIfAborted();
|
|
1601
1607
|
result = await commitFiles(state.top, dirty, message);
|
|
1602
1608
|
} catch (error) {
|
|
1603
1609
|
throw new Error(`cannot complete: git refused the commit: ${(error as Error).message}`);
|
|
@@ -2098,14 +2104,19 @@ export async function runWorkflowAction(params: OrchestrateParams, deps: Workflo
|
|
|
2098
2104
|
}
|
|
2099
2105
|
const gone = missingWorktree(task);
|
|
2100
2106
|
if (gone && params.action !== "status") return { ok: false, taskId: task.id, state: task.state, message: `Rejected: ${gone}` };
|
|
2107
|
+
const execution = taskExecution(deps.root, deps.configDir, task.id, deps.signal);
|
|
2101
2108
|
const finished = new Map<string, AgentRun>();
|
|
2102
2109
|
const identity = { root: deps.root, configDir: deps.configDir, taskId: task.id, sessionId: task.ownerSessionId };
|
|
2103
|
-
const wait = <T>(run: () => Promise<T>) => {
|
|
2110
|
+
const wait = async <T>(run: () => Promise<T>) => {
|
|
2111
|
+
execution.signal.throwIfAborted();
|
|
2104
2112
|
saveTask(deps.root, deps.configDir, task);
|
|
2105
|
-
|
|
2113
|
+
const result = await withBlockingRequest(identity, run);
|
|
2114
|
+
execution.signal.throwIfAborted();
|
|
2115
|
+
return result;
|
|
2106
2116
|
};
|
|
2107
2117
|
const tracked: WorkflowDeps = {
|
|
2108
2118
|
...deps,
|
|
2119
|
+
signal: execution.signal,
|
|
2109
2120
|
choose: (...args) => wait(() => deps.choose(...args)),
|
|
2110
2121
|
ask: (...args) => wait(() => deps.ask(...args)),
|
|
2111
2122
|
...(deps.askQuestions ? { askQuestions: (...args: Parameters<NonNullable<WorkflowDeps["askQuestions"]>>) => wait(() => deps.askQuestions!(...args)) } : {}),
|
|
@@ -2113,6 +2124,7 @@ export async function runWorkflowAction(params: OrchestrateParams, deps: Workflo
|
|
|
2113
2124
|
cwd: taskCwd(task, deps.cwd),
|
|
2114
2125
|
// An agent can work for an hour: what the action has done so far (implementing, reviewing) is saved before it starts, so every window reads it.
|
|
2115
2126
|
runProcess: (args, options) => {
|
|
2127
|
+
execution.signal.throwIfAborted();
|
|
2116
2128
|
saveTask(deps.root, deps.configDir, task);
|
|
2117
2129
|
return (deps.runProcess ?? spawnPiProcess)(args, options);
|
|
2118
2130
|
},
|
|
@@ -2134,6 +2146,7 @@ export async function runWorkflowAction(params: OrchestrateParams, deps: Workflo
|
|
|
2134
2146
|
const working = new Set<string>();
|
|
2135
2147
|
try {
|
|
2136
2148
|
const message = await handler(task, params, tracked);
|
|
2149
|
+
execution.signal.throwIfAborted();
|
|
2137
2150
|
const runs = recordRunLog(task, finished, deps);
|
|
2138
2151
|
saveTask(deps.root, deps.configDir, task);
|
|
2139
2152
|
return { ok: true, taskId: task.id, state: task.state, message: `${message}${runsFooter(runs)}${budgetFooter(task, deps)}`, runs };
|
|
@@ -2142,6 +2155,7 @@ export async function runWorkflowAction(params: OrchestrateParams, deps: Workflo
|
|
|
2142
2155
|
saveTask(deps.root, deps.configDir, task);
|
|
2143
2156
|
return { ok: false, taskId: task.id, state: task.state, message: `Rejected: ${(error as Error).message}${budgetFooter(task, deps)}`, runs };
|
|
2144
2157
|
} finally {
|
|
2158
|
+
execution.release();
|
|
2145
2159
|
for (const runId of working) noteWorkerRun(deps.root, deps.configDir, task.id, { runId, startedAt: "", status: "cancelled" });
|
|
2146
2160
|
}
|
|
2147
2161
|
}
|