@useorgx/openclaw-plugin 0.7.23 → 0.7.24
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/dashboard/dist/assets/{m2smti3F.js → B6VftyY6.js} +1 -1
- package/dashboard/dist/assets/B6VftyY6.js.br +0 -0
- package/dashboard/dist/assets/B6VftyY6.js.gz +0 -0
- package/dashboard/dist/assets/{D-FuHfT8.js → BANQdlC4.js} +1 -1
- package/dashboard/dist/assets/BANQdlC4.js.br +0 -0
- package/dashboard/dist/assets/BANQdlC4.js.gz +0 -0
- package/dashboard/dist/assets/{DDCPrZRt.js → BPL4CL3c.js} +1 -1
- package/dashboard/dist/assets/BPL4CL3c.js.br +0 -0
- package/dashboard/dist/assets/BPL4CL3c.js.gz +0 -0
- package/dashboard/dist/assets/{D0PN5_vY.js → BZCkOZ20.js} +1 -1
- package/dashboard/dist/assets/BZCkOZ20.js.br +0 -0
- package/dashboard/dist/assets/BZCkOZ20.js.gz +0 -0
- package/dashboard/dist/assets/{DNQ-iFO2.js → B_LdOJUa.js} +1 -1
- package/dashboard/dist/assets/B_LdOJUa.js.br +0 -0
- package/dashboard/dist/assets/B_LdOJUa.js.gz +0 -0
- package/dashboard/dist/assets/Bfp-wdwb.css +1 -0
- package/dashboard/dist/assets/Bfp-wdwb.css.br +0 -0
- package/dashboard/dist/assets/{C1u2SGin.css.gz → Bfp-wdwb.css.gz} +0 -0
- package/dashboard/dist/assets/{CZXS5i_5.js → BvFcH_Iy.js} +1 -1
- package/dashboard/dist/assets/BvFcH_Iy.js.br +0 -0
- package/dashboard/dist/assets/BvFcH_Iy.js.gz +0 -0
- package/dashboard/dist/assets/C0i7ABUU.js +212 -0
- package/dashboard/dist/assets/C0i7ABUU.js.br +0 -0
- package/dashboard/dist/assets/C0i7ABUU.js.gz +0 -0
- package/dashboard/dist/assets/CFB0MM7j.js +1 -0
- package/dashboard/dist/assets/CFB0MM7j.js.br +0 -0
- package/dashboard/dist/assets/CFB0MM7j.js.gz +0 -0
- package/dashboard/dist/assets/{OlLPtzdz.js → CQSRb1yu.js} +1 -1
- package/dashboard/dist/assets/CQSRb1yu.js.br +0 -0
- package/dashboard/dist/assets/CQSRb1yu.js.gz +0 -0
- package/dashboard/dist/assets/{CbVWL74-.js → CUoQoSm-.js} +1 -1
- package/dashboard/dist/assets/CUoQoSm-.js.br +0 -0
- package/dashboard/dist/assets/CUoQoSm-.js.gz +0 -0
- package/dashboard/dist/assets/Ckd1R1iE.js +1 -0
- package/dashboard/dist/assets/Ckd1R1iE.js.br +0 -0
- package/dashboard/dist/assets/Ckd1R1iE.js.gz +0 -0
- package/dashboard/dist/assets/{DhPuHPK7.js → CqRNb2EL.js} +1 -1
- package/dashboard/dist/assets/CqRNb2EL.js.br +0 -0
- package/dashboard/dist/assets/CqRNb2EL.js.gz +0 -0
- package/dashboard/dist/assets/{CGJiHCIx.js → DClUc9rw.js} +1 -1
- package/dashboard/dist/assets/DClUc9rw.js.br +0 -0
- package/dashboard/dist/assets/DClUc9rw.js.gz +0 -0
- package/dashboard/dist/assets/DF2PMTwT.js +1 -0
- package/dashboard/dist/assets/DF2PMTwT.js.br +0 -0
- package/dashboard/dist/assets/DF2PMTwT.js.gz +0 -0
- package/dashboard/dist/assets/{RN4M9u9W.js → DJYl7gyA.js} +1 -1
- package/dashboard/dist/assets/DJYl7gyA.js.br +0 -0
- package/dashboard/dist/assets/DJYl7gyA.js.gz +0 -0
- package/dashboard/dist/assets/{BrMXbzQ-.js → DZtNMX0t.js} +1 -1
- package/dashboard/dist/assets/DZtNMX0t.js.br +0 -0
- package/dashboard/dist/assets/DZtNMX0t.js.gz +0 -0
- package/dashboard/dist/assets/{LOFrVoPD.js → DlEa8PI0.js} +1 -1
- package/dashboard/dist/assets/DlEa8PI0.js.br +0 -0
- package/dashboard/dist/assets/DlEa8PI0.js.gz +0 -0
- package/dashboard/dist/assets/M4QxcXjh.js +1 -0
- package/dashboard/dist/assets/M4QxcXjh.js.br +0 -0
- package/dashboard/dist/assets/M4QxcXjh.js.gz +0 -0
- package/dashboard/dist/assets/{nra1yvJX.js → MrW1ixGx.js} +1 -1
- package/dashboard/dist/assets/MrW1ixGx.js.br +0 -0
- package/dashboard/dist/assets/MrW1ixGx.js.gz +0 -0
- package/dashboard/dist/index.html +2 -2
- package/dashboard/dist/index.html.br +0 -0
- package/dashboard/dist/index.html.gz +0 -0
- package/dist/activity-store.js +68 -8
- package/dist/contracts/shared-types.d.ts +28 -0
- package/dist/http/helpers/auto-continue-engine.js +235 -32
- package/dist/http/helpers/triage-mapper.js +285 -6
- package/dist/http/helpers/value-utils.d.ts +1 -0
- package/dist/http/helpers/value-utils.js +17 -0
- package/dist/http/index.js +89 -3
- package/dist/http/routes/live-triage.js +6 -1
- package/dist/http/routes/mission-control-actions.d.ts +9 -0
- package/dist/http/routes/mission-control-actions.js +157 -7
- package/dist/http/routes/mission-control-read.d.ts +9 -0
- package/dist/http/routes/mission-control-read.js +33 -0
- package/dist/stores/sqlite-state.d.ts +1 -1
- package/dist/stores/sqlite-state.js +153 -2
- package/package.json +1 -1
- package/dashboard/dist/assets/9gFmK3Kr.js +0 -1
- package/dashboard/dist/assets/9gFmK3Kr.js.br +0 -0
- package/dashboard/dist/assets/9gFmK3Kr.js.gz +0 -0
- package/dashboard/dist/assets/BrMXbzQ-.js.br +0 -0
- package/dashboard/dist/assets/BrMXbzQ-.js.gz +0 -0
- package/dashboard/dist/assets/C1u2SGin.css +0 -1
- package/dashboard/dist/assets/C1u2SGin.css.br +0 -0
- package/dashboard/dist/assets/CGJiHCIx.js.br +0 -0
- package/dashboard/dist/assets/CGJiHCIx.js.gz +0 -0
- package/dashboard/dist/assets/CSd4rSuU.js +0 -212
- package/dashboard/dist/assets/CSd4rSuU.js.br +0 -0
- package/dashboard/dist/assets/CSd4rSuU.js.gz +0 -0
- package/dashboard/dist/assets/CZXS5i_5.js.br +0 -0
- package/dashboard/dist/assets/CZXS5i_5.js.gz +0 -0
- package/dashboard/dist/assets/CbVWL74-.js.br +0 -0
- package/dashboard/dist/assets/CbVWL74-.js.gz +0 -0
- package/dashboard/dist/assets/D-FuHfT8.js.br +0 -0
- package/dashboard/dist/assets/D-FuHfT8.js.gz +0 -0
- package/dashboard/dist/assets/D0PN5_vY.js.br +0 -0
- package/dashboard/dist/assets/D0PN5_vY.js.gz +0 -0
- package/dashboard/dist/assets/DDCPrZRt.js.br +0 -0
- package/dashboard/dist/assets/DDCPrZRt.js.gz +0 -0
- package/dashboard/dist/assets/DNQ-iFO2.js.br +0 -0
- package/dashboard/dist/assets/DNQ-iFO2.js.gz +0 -0
- package/dashboard/dist/assets/DhPuHPK7.js.br +0 -0
- package/dashboard/dist/assets/DhPuHPK7.js.gz +0 -0
- package/dashboard/dist/assets/Dhz7qPtn.js +0 -1
- package/dashboard/dist/assets/Dhz7qPtn.js.br +0 -0
- package/dashboard/dist/assets/Dhz7qPtn.js.gz +0 -0
- package/dashboard/dist/assets/LOFrVoPD.js.br +0 -0
- package/dashboard/dist/assets/LOFrVoPD.js.gz +0 -0
- package/dashboard/dist/assets/OlLPtzdz.js.br +0 -0
- package/dashboard/dist/assets/OlLPtzdz.js.gz +0 -0
- package/dashboard/dist/assets/RN4M9u9W.js.br +0 -0
- package/dashboard/dist/assets/RN4M9u9W.js.gz +0 -0
- package/dashboard/dist/assets/VCHu272d.js +0 -1
- package/dashboard/dist/assets/VCHu272d.js.br +0 -0
- package/dashboard/dist/assets/VCHu272d.js.gz +0 -0
- package/dashboard/dist/assets/m2smti3F.js.br +0 -0
- package/dashboard/dist/assets/m2smti3F.js.gz +0 -0
- package/dashboard/dist/assets/nra1yvJX.js.br +0 -0
- package/dashboard/dist/assets/nra1yvJX.js.gz +0 -0
- package/dashboard/dist/assets/qLX6NZ-J.js +0 -1
- package/dashboard/dist/assets/qLX6NZ-J.js.br +0 -0
- package/dashboard/dist/assets/qLX6NZ-J.js.gz +0 -0
|
@@ -9,6 +9,33 @@ const PLAY_QUEUE_LOOKUP_TIMEOUT_MS = (() => {
|
|
|
9
9
|
return 350;
|
|
10
10
|
return Math.max(200, Math.floor(parsed));
|
|
11
11
|
})();
|
|
12
|
+
function playOutcomeMessage(input) {
|
|
13
|
+
const workstreamLabel = input.workstreamTitle?.trim() || "this workstream";
|
|
14
|
+
switch (input.outcome) {
|
|
15
|
+
case "dispatch_pending":
|
|
16
|
+
return `Dispatching ${workstreamLabel}; waiting for slice start.`;
|
|
17
|
+
case "started":
|
|
18
|
+
return `Started ${workstreamLabel}.`;
|
|
19
|
+
case "fallback_started":
|
|
20
|
+
return `Started ${workstreamLabel} using the fallback runner.`;
|
|
21
|
+
case "slice_completed":
|
|
22
|
+
return `${workstreamLabel} completed before the queue refreshed.`;
|
|
23
|
+
case "slice_blocked":
|
|
24
|
+
return input.startReasonLabel?.trim() || `${workstreamLabel} needs input before it can continue.`;
|
|
25
|
+
case "slice_error":
|
|
26
|
+
return `${workstreamLabel} hit an error before it could keep running.`;
|
|
27
|
+
case "initiative_run_active":
|
|
28
|
+
return "Autopilot is already running for this initiative.";
|
|
29
|
+
case "no_dispatchable_task":
|
|
30
|
+
return input.startReasonLabel?.trim() || `No dispatchable task is available for ${workstreamLabel}.`;
|
|
31
|
+
case "spawn_guard_rate_limited":
|
|
32
|
+
return input.fallbackBlockedReason?.trim() || `${workstreamLabel} is rate limited right now.`;
|
|
33
|
+
case "spawn_guard_blocked":
|
|
34
|
+
return input.fallbackBlockedReason?.trim() || `${workstreamLabel} is blocked by the current execution policy.`;
|
|
35
|
+
default:
|
|
36
|
+
return input.startReasonLabel?.trim() || `Unable to dispatch ${workstreamLabel} right now.`;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
12
39
|
const IN_PROGRESS_TASK_STATUSES = new Set([
|
|
13
40
|
"in_progress",
|
|
14
41
|
"inprogress",
|
|
@@ -412,6 +439,10 @@ export function registerMissionControlActionsRoutes(router, deps) {
|
|
|
412
439
|
query.get("projectId") ??
|
|
413
440
|
query.get("project_id") ??
|
|
414
441
|
null;
|
|
442
|
+
const queueWorkstreamTitle = matchedQueueItem?.workstreamTitle ?? null;
|
|
443
|
+
const queueStartReasonCode = matchedQueueItem?.startReasonCode ?? null;
|
|
444
|
+
const queueStartReasonLabel = matchedQueueItem?.startReasonLabel ?? null;
|
|
445
|
+
const queueDispatchableTask = matchedQueueItem?.dispatchableTask ?? null;
|
|
415
446
|
const existingRun = deps.autoContinueRuns.get(initiativeId) ?? null;
|
|
416
447
|
const existingActiveRunIds = Array.isArray(existingRun?.activeSliceRunIds)
|
|
417
448
|
? (existingRun?.activeSliceRunIds)
|
|
@@ -428,6 +459,13 @@ export function registerMissionControlActionsRoutes(router, deps) {
|
|
|
428
459
|
const activeWorkstreamTitle = activeSlice?.workstreamTitle ?? null;
|
|
429
460
|
deps.sendJson(res, 409, {
|
|
430
461
|
ok: false,
|
|
462
|
+
outcome: "initiative_run_active",
|
|
463
|
+
reasonCode: "initiative_run_active",
|
|
464
|
+
message: playOutcomeMessage({
|
|
465
|
+
outcome: "initiative_run_active",
|
|
466
|
+
workstreamTitle: queueWorkstreamTitle,
|
|
467
|
+
startReasonLabel: queueStartReasonLabel,
|
|
468
|
+
}),
|
|
431
469
|
code: "auto_continue_already_running",
|
|
432
470
|
error: activeWorkstreamId || activeWorkstreamTitle
|
|
433
471
|
? `Auto-continue is already running for ${activeWorkstreamTitle ?? activeWorkstreamId}. Stop it before launching another Play run.`
|
|
@@ -436,10 +474,35 @@ export function registerMissionControlActionsRoutes(router, deps) {
|
|
|
436
474
|
activeRunIds: existingActiveRunIds,
|
|
437
475
|
activeWorkstreamId,
|
|
438
476
|
activeWorkstreamTitle,
|
|
477
|
+
dispatchableTask: queueDispatchableTask,
|
|
439
478
|
error_location: "mission-control.next-up.play.concurrent_run",
|
|
440
479
|
});
|
|
441
480
|
return;
|
|
442
481
|
}
|
|
482
|
+
if (matchedQueueItem?.canStartNow === false) {
|
|
483
|
+
const reasonCode = queueStartReasonCode && queueStartReasonCode.trim().length > 0
|
|
484
|
+
? queueStartReasonCode.trim()
|
|
485
|
+
: "no_dispatchable_task";
|
|
486
|
+
const message = playOutcomeMessage({
|
|
487
|
+
outcome: reasonCode,
|
|
488
|
+
workstreamTitle: queueWorkstreamTitle,
|
|
489
|
+
startReasonLabel: queueStartReasonLabel,
|
|
490
|
+
});
|
|
491
|
+
deps.sendJson(res, 409, {
|
|
492
|
+
ok: false,
|
|
493
|
+
outcome: reasonCode,
|
|
494
|
+
reasonCode,
|
|
495
|
+
message,
|
|
496
|
+
code: reasonCode,
|
|
497
|
+
error: message,
|
|
498
|
+
initiativeId,
|
|
499
|
+
workstreamId,
|
|
500
|
+
agentId,
|
|
501
|
+
dispatchableTask: queueDispatchableTask,
|
|
502
|
+
error_location: "mission-control.next-up.play.preflight",
|
|
503
|
+
});
|
|
504
|
+
return;
|
|
505
|
+
}
|
|
443
506
|
const run = await deps.startAutoContinueRun({
|
|
444
507
|
initiativeId,
|
|
445
508
|
workspaceId: requestedWorkspaceId,
|
|
@@ -491,7 +554,7 @@ export function registerMissionControlActionsRoutes(router, deps) {
|
|
|
491
554
|
// Give short-lived workers a brief window to flush output so Play can resolve
|
|
492
555
|
// in one request/response cycle without requiring extra manual ticks.
|
|
493
556
|
if (run.activeRunId) {
|
|
494
|
-
await new Promise((resolve) => setTimeout(resolve,
|
|
557
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
495
558
|
await deps.tickAutoContinueRun(run);
|
|
496
559
|
}
|
|
497
560
|
fallbackDispatch = await maybeDispatchFallback();
|
|
@@ -522,12 +585,20 @@ export function registerMissionControlActionsRoutes(router, deps) {
|
|
|
522
585
|
});
|
|
523
586
|
deps.sendJson(res, 202, {
|
|
524
587
|
ok: true,
|
|
588
|
+
outcome: "dispatch_pending",
|
|
589
|
+
reasonCode: "dispatch_pending",
|
|
590
|
+
message: playOutcomeMessage({
|
|
591
|
+
outcome: "dispatch_pending",
|
|
592
|
+
workstreamTitle: queueWorkstreamTitle,
|
|
593
|
+
startReasonLabel: queueStartReasonLabel,
|
|
594
|
+
}),
|
|
525
595
|
run,
|
|
526
596
|
initiativeId,
|
|
527
597
|
workstreamId,
|
|
528
598
|
agentId,
|
|
529
599
|
...playDispatchEnvelope("pending"),
|
|
530
600
|
sessionId: null,
|
|
601
|
+
dispatchableTask: queueDispatchableTask,
|
|
531
602
|
slice: {
|
|
532
603
|
scope,
|
|
533
604
|
taskIds: matchedQueueItem?.sliceTaskIds ?? [],
|
|
@@ -566,12 +637,20 @@ export function registerMissionControlActionsRoutes(router, deps) {
|
|
|
566
637
|
: "slice_error";
|
|
567
638
|
deps.sendJson(res, 200, {
|
|
568
639
|
ok: true,
|
|
640
|
+
outcome: finalizedDispatchMode,
|
|
641
|
+
reasonCode: finalizedDispatchMode,
|
|
642
|
+
message: playOutcomeMessage({
|
|
643
|
+
outcome: finalizedDispatchMode,
|
|
644
|
+
workstreamTitle: queueWorkstreamTitle,
|
|
645
|
+
startReasonLabel: queueStartReasonLabel,
|
|
646
|
+
}),
|
|
569
647
|
run,
|
|
570
648
|
initiativeId,
|
|
571
649
|
workstreamId,
|
|
572
650
|
agentId,
|
|
573
651
|
...playDispatchEnvelope(finalizedDispatchMode),
|
|
574
652
|
sessionId: run.lastRunId,
|
|
653
|
+
dispatchableTask: queueDispatchableTask,
|
|
575
654
|
slice: {
|
|
576
655
|
scope,
|
|
577
656
|
taskIds: matchedQueueItem?.sliceTaskIds ?? [],
|
|
@@ -589,12 +668,20 @@ export function registerMissionControlActionsRoutes(router, deps) {
|
|
|
589
668
|
if (dispatchMode === "none" && run.status === "running" && !run.stopReason) {
|
|
590
669
|
deps.sendJson(res, 202, {
|
|
591
670
|
ok: true,
|
|
671
|
+
outcome: "dispatch_pending",
|
|
672
|
+
reasonCode: "dispatch_pending",
|
|
673
|
+
message: playOutcomeMessage({
|
|
674
|
+
outcome: "dispatch_pending",
|
|
675
|
+
workstreamTitle: queueWorkstreamTitle,
|
|
676
|
+
startReasonLabel: queueStartReasonLabel,
|
|
677
|
+
}),
|
|
592
678
|
run,
|
|
593
679
|
initiativeId,
|
|
594
680
|
workstreamId,
|
|
595
681
|
agentId,
|
|
596
682
|
...playDispatchEnvelope("pending"),
|
|
597
683
|
sessionId: null,
|
|
684
|
+
dispatchableTask: queueDispatchableTask,
|
|
598
685
|
slice: {
|
|
599
686
|
scope,
|
|
600
687
|
taskIds: matchedQueueItem?.sliceTaskIds ?? [],
|
|
@@ -611,6 +698,15 @@ export function registerMissionControlActionsRoutes(router, deps) {
|
|
|
611
698
|
}
|
|
612
699
|
if (dispatchMode === "none") {
|
|
613
700
|
const fallbackBlockedReason = fallbackDispatch?.blockedReason ?? null;
|
|
701
|
+
const reasonCode = fallbackBlockedReason
|
|
702
|
+
? fallbackDispatch?.retryable
|
|
703
|
+
? "spawn_guard_rate_limited"
|
|
704
|
+
: "spawn_guard_blocked"
|
|
705
|
+
: run.stopReason === "blocked"
|
|
706
|
+
? "no_dispatchable_task"
|
|
707
|
+
: run.stopReason === "completed"
|
|
708
|
+
? "no_dispatchable_task"
|
|
709
|
+
: "dispatch_failed";
|
|
614
710
|
const reason = fallbackBlockedReason ??
|
|
615
711
|
(run.stopReason === "blocked"
|
|
616
712
|
? "No dispatchable task is ready for this workstream yet."
|
|
@@ -619,29 +715,42 @@ export function registerMissionControlActionsRoutes(router, deps) {
|
|
|
619
715
|
: "Unable to dispatch this workstream right now.");
|
|
620
716
|
deps.sendJson(res, fallbackDispatch?.retryable ? 429 : 409, {
|
|
621
717
|
ok: false,
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
:
|
|
718
|
+
outcome: reasonCode,
|
|
719
|
+
reasonCode,
|
|
720
|
+
message: playOutcomeMessage({
|
|
721
|
+
outcome: reasonCode,
|
|
722
|
+
workstreamTitle: queueWorkstreamTitle,
|
|
723
|
+
startReasonLabel: queueStartReasonLabel ?? reason,
|
|
724
|
+
fallbackBlockedReason,
|
|
725
|
+
}),
|
|
726
|
+
code: reasonCode,
|
|
627
727
|
error: reason,
|
|
628
728
|
run,
|
|
629
729
|
initiativeId,
|
|
630
730
|
workstreamId,
|
|
631
731
|
agentId,
|
|
632
732
|
fallbackDispatch,
|
|
733
|
+
dispatchableTask: queueDispatchableTask,
|
|
633
734
|
error_location: "mission-control.next-up.play.dispatch",
|
|
634
735
|
});
|
|
635
736
|
return;
|
|
636
737
|
}
|
|
637
738
|
deps.sendJson(res, 200, {
|
|
638
739
|
ok: true,
|
|
740
|
+
outcome: dispatchMode === "fallback" ? "fallback_started" : "started",
|
|
741
|
+
reasonCode: dispatchMode === "fallback" ? "fallback_started" : "started",
|
|
742
|
+
message: playOutcomeMessage({
|
|
743
|
+
outcome: dispatchMode === "fallback" ? "fallback_started" : "started",
|
|
744
|
+
workstreamTitle: queueWorkstreamTitle,
|
|
745
|
+
startReasonLabel: queueStartReasonLabel,
|
|
746
|
+
}),
|
|
639
747
|
run,
|
|
640
748
|
initiativeId,
|
|
641
749
|
workstreamId,
|
|
642
750
|
agentId,
|
|
643
751
|
...playDispatchEnvelope(dispatchMode),
|
|
644
752
|
sessionId: run.activeRunId ?? fallbackDispatch?.sessionId ?? null,
|
|
753
|
+
dispatchableTask: queueDispatchableTask,
|
|
645
754
|
slice: {
|
|
646
755
|
scope,
|
|
647
756
|
taskIds: matchedQueueItem?.sliceTaskIds ?? [],
|
|
@@ -1614,7 +1723,48 @@ export function registerMissionControlActionsRoutes(router, deps) {
|
|
|
1614
1723
|
initiativeId,
|
|
1615
1724
|
workstreamIds: allowedWorkstreamIds,
|
|
1616
1725
|
});
|
|
1617
|
-
deps.
|
|
1726
|
+
await deps.tickAutoContinueRun(run);
|
|
1727
|
+
const activeRunIds = Array.isArray(run.activeSliceRunIds)
|
|
1728
|
+
? run.activeSliceRunIds.filter((id) => typeof id === "string" && id.trim().length > 0)
|
|
1729
|
+
: typeof run.activeRunId === "string" && run.activeRunId.trim().length > 0
|
|
1730
|
+
? [run.activeRunId]
|
|
1731
|
+
: [];
|
|
1732
|
+
if (activeRunIds.length > 0) {
|
|
1733
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
1734
|
+
await deps.tickAutoContinueRun(run);
|
|
1735
|
+
}
|
|
1736
|
+
const reconciledActiveRunIds = Array.isArray(run.activeSliceRunIds)
|
|
1737
|
+
? run.activeSliceRunIds.filter((id) => typeof id === "string" && id.trim().length > 0)
|
|
1738
|
+
: typeof run.activeRunId === "string" && run.activeRunId.trim().length > 0
|
|
1739
|
+
? [run.activeRunId]
|
|
1740
|
+
: [];
|
|
1741
|
+
const outcome = reconciledActiveRunIds.length > 0 || (run.status === "running" && !run.stopReason)
|
|
1742
|
+
? "started"
|
|
1743
|
+
: run.stopReason === "blocked"
|
|
1744
|
+
? "blocked"
|
|
1745
|
+
: run.stopReason === "completed"
|
|
1746
|
+
? "completed"
|
|
1747
|
+
: run.stopReason === "error"
|
|
1748
|
+
? "error"
|
|
1749
|
+
: "pending";
|
|
1750
|
+
const message = outcome === "started"
|
|
1751
|
+
? "Autopilot enabled and running."
|
|
1752
|
+
: outcome === "blocked"
|
|
1753
|
+
? "Autopilot enabled, but it immediately needs your input."
|
|
1754
|
+
: outcome === "completed"
|
|
1755
|
+
? "Autopilot enabled and immediately completed the available work."
|
|
1756
|
+
: outcome === "error"
|
|
1757
|
+
? "Autopilot hit an error before it could keep running."
|
|
1758
|
+
: "Autopilot is starting.";
|
|
1759
|
+
deps.sendJson(res, 200, {
|
|
1760
|
+
ok: true,
|
|
1761
|
+
outcome,
|
|
1762
|
+
reasonCode: outcome === "blocked" ? "blocked" : outcome,
|
|
1763
|
+
message,
|
|
1764
|
+
...dispatchEnvelope,
|
|
1765
|
+
workstreamIds: allowedWorkstreamIds,
|
|
1766
|
+
run,
|
|
1767
|
+
});
|
|
1618
1768
|
}
|
|
1619
1769
|
catch (err) {
|
|
1620
1770
|
sendRouteException(res, "mission-control.auto-continue.start.handler", err);
|
|
@@ -39,6 +39,15 @@ type NextUpQueueItem = {
|
|
|
39
39
|
sliceTaskCount?: number | null;
|
|
40
40
|
sliceMilestoneId?: string | null;
|
|
41
41
|
milestoneBreakdown?: MilestoneBreakdownEntry[];
|
|
42
|
+
canStartNow?: boolean;
|
|
43
|
+
startReasonCode?: string | null;
|
|
44
|
+
startReasonLabel?: string | null;
|
|
45
|
+
dispatchableTask?: {
|
|
46
|
+
id: string;
|
|
47
|
+
title: string;
|
|
48
|
+
scope: "task" | "milestone" | "workstream";
|
|
49
|
+
milestoneId?: string | null;
|
|
50
|
+
} | null;
|
|
42
51
|
isPinned?: boolean;
|
|
43
52
|
pinnedRank?: number | null;
|
|
44
53
|
compositeScore?: number;
|
|
@@ -464,6 +464,21 @@ function normalizeQueueItems(input) {
|
|
|
464
464
|
? runnerSourceHint ?? "inferred"
|
|
465
465
|
: "fallback";
|
|
466
466
|
const queueState = normalizeQueueState(record.queueState ?? record.queue_state);
|
|
467
|
+
const dispatchableTaskRecord = asRecord(record.dispatchableTask ?? record.dispatchable_task);
|
|
468
|
+
const dispatchableTaskId = asString(dispatchableTaskRecord?.id) ??
|
|
469
|
+
asString(record.dispatchableTaskId) ??
|
|
470
|
+
asString(record.dispatchable_task_id);
|
|
471
|
+
const dispatchableTaskTitle = asString(dispatchableTaskRecord?.title) ??
|
|
472
|
+
asString(record.dispatchableTaskTitle) ??
|
|
473
|
+
asString(record.dispatchable_task_title);
|
|
474
|
+
const dispatchableTaskScopeRaw = asString(dispatchableTaskRecord?.scope) ??
|
|
475
|
+
asString(record.dispatchableTaskScope) ??
|
|
476
|
+
asString(record.dispatchable_task_scope);
|
|
477
|
+
const dispatchableTaskScope = dispatchableTaskScopeRaw === "task" ||
|
|
478
|
+
dispatchableTaskScopeRaw === "milestone" ||
|
|
479
|
+
dispatchableTaskScopeRaw === "workstream"
|
|
480
|
+
? dispatchableTaskScopeRaw
|
|
481
|
+
: null;
|
|
467
482
|
const normalizedSliceScope = normalizeMissionControlStatus(asString(record.sliceScope) ?? asString(record.slice_scope));
|
|
468
483
|
const sliceScope = normalizedSliceScope === "task" ||
|
|
469
484
|
normalizedSliceScope === "milestone" ||
|
|
@@ -502,6 +517,24 @@ function normalizeQueueItems(input) {
|
|
|
502
517
|
? Math.max(0, Math.floor(sliceTaskCountRaw))
|
|
503
518
|
: sliceTaskIds.length,
|
|
504
519
|
sliceMilestoneId: asString(record.sliceMilestoneId) ?? asString(record.slice_milestone_id),
|
|
520
|
+
canStartNow: typeof record.canStartNow === "boolean"
|
|
521
|
+
? record.canStartNow
|
|
522
|
+
: typeof record.can_start_now === "boolean"
|
|
523
|
+
? record.can_start_now
|
|
524
|
+
: queueState === "queued" || queueState === "idle",
|
|
525
|
+
startReasonCode: asString(record.startReasonCode) ?? asString(record.start_reason_code),
|
|
526
|
+
startReasonLabel: asString(record.startReasonLabel) ?? asString(record.start_reason_label),
|
|
527
|
+
dispatchableTask: dispatchableTaskId && dispatchableTaskTitle && dispatchableTaskScope
|
|
528
|
+
? {
|
|
529
|
+
id: dispatchableTaskId,
|
|
530
|
+
title: dispatchableTaskTitle,
|
|
531
|
+
scope: dispatchableTaskScope,
|
|
532
|
+
milestoneId: asString(dispatchableTaskRecord?.milestoneId) ??
|
|
533
|
+
asString(dispatchableTaskRecord?.milestone_id) ??
|
|
534
|
+
asString(record.dispatchableTaskMilestoneId) ??
|
|
535
|
+
asString(record.dispatchable_task_milestone_id),
|
|
536
|
+
}
|
|
537
|
+
: null,
|
|
505
538
|
isPinned: Boolean(record.isPinned ?? record.is_pinned),
|
|
506
539
|
pinnedRank: asNumber(record.pinnedRank ?? record.pinned_rank),
|
|
507
540
|
compositeScore: asNumber(record.compositeScore ?? record.composite_score) ?? undefined,
|
|
@@ -1,11 +1,145 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { execFileSync } from "node:child_process";
|
|
2
|
+
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
|
+
import { createRequire } from "node:module";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
2
6
|
import { getOrgxPluginConfigDir, getOrgxPluginConfigPath } from "../paths.js";
|
|
3
7
|
import { ensureStoreDirSync, parseJsonSafe } from "./json-store.js";
|
|
4
8
|
const STATE_DB_FILENAME = "orgx-state.sqlite";
|
|
5
9
|
const USER_VERSION = 1;
|
|
10
|
+
const BETTER_SQLITE3_REPAIR_MARKER = ".orgx-runtime-deps-version";
|
|
6
11
|
let dbInstance = null;
|
|
7
12
|
let dbInstancePath = "";
|
|
8
13
|
let stateDbHooksRegistered = false;
|
|
14
|
+
let databaseConstructor = null;
|
|
15
|
+
let runtimeDepsRepairAttempted = false;
|
|
16
|
+
const require = createRequire(import.meta.url);
|
|
17
|
+
function currentModuleDir() {
|
|
18
|
+
return dirname(fileURLToPath(import.meta.url));
|
|
19
|
+
}
|
|
20
|
+
function findPluginRoot(startDir) {
|
|
21
|
+
let cursor = startDir;
|
|
22
|
+
while (true) {
|
|
23
|
+
const packageJsonPath = join(cursor, "package.json");
|
|
24
|
+
if (existsSync(packageJsonPath))
|
|
25
|
+
return cursor;
|
|
26
|
+
const parent = dirname(cursor);
|
|
27
|
+
if (parent === cursor)
|
|
28
|
+
return startDir;
|
|
29
|
+
cursor = parent;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
function readExpectedRuntimeDepVersion(pluginRoot) {
|
|
33
|
+
try {
|
|
34
|
+
const raw = readFileSync(join(pluginRoot, "package.json"), "utf8");
|
|
35
|
+
const parsed = JSON.parse(raw);
|
|
36
|
+
return String(parsed?.dependencies?.["better-sqlite3"] || "").trim();
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return "";
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
function writeRuntimeDepMarker(pluginRoot) {
|
|
43
|
+
const expected = readExpectedRuntimeDepVersion(pluginRoot);
|
|
44
|
+
if (!expected)
|
|
45
|
+
return;
|
|
46
|
+
try {
|
|
47
|
+
writeFileSync(join(pluginRoot, BETTER_SQLITE3_REPAIR_MARKER), expected, "utf8");
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
// best effort
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
function betterSqlite3Installed(pluginRoot) {
|
|
54
|
+
return existsSync(join(pluginRoot, "node_modules", "better-sqlite3", "package.json"));
|
|
55
|
+
}
|
|
56
|
+
function resolveBetterSqlite3InstallRoot(pluginRoot) {
|
|
57
|
+
try {
|
|
58
|
+
const packageJsonPath = require.resolve("better-sqlite3/package.json");
|
|
59
|
+
return dirname(dirname(dirname(packageJsonPath)));
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
return pluginRoot;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
function isRecoverableBetterSqlite3Error(error) {
|
|
66
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
67
|
+
return /could not locate the bindings file|cannot find module ['"]better-sqlite3['"]|no native build was found/i.test(message);
|
|
68
|
+
}
|
|
69
|
+
function resolveNpmCommand() {
|
|
70
|
+
const npmCliPath = join(dirname(process.execPath), "..", "lib", "node_modules", "npm", "bin", "npm-cli.js");
|
|
71
|
+
if (existsSync(npmCliPath)) {
|
|
72
|
+
return {
|
|
73
|
+
command: process.execPath,
|
|
74
|
+
argsPrefix: [npmCliPath],
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
return {
|
|
78
|
+
command: "npm",
|
|
79
|
+
argsPrefix: [],
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
function repairBetterSqlite3Binding(pluginRoot) {
|
|
83
|
+
const installRoot = resolveBetterSqlite3InstallRoot(pluginRoot);
|
|
84
|
+
const { command, argsPrefix } = resolveNpmCommand();
|
|
85
|
+
const args = betterSqlite3Installed(installRoot)
|
|
86
|
+
? [...argsPrefix, "rebuild", "better-sqlite3", "--foreground-scripts"]
|
|
87
|
+
: [...argsPrefix, "install", "--omit=dev"];
|
|
88
|
+
try {
|
|
89
|
+
execFileSync(command, args, {
|
|
90
|
+
cwd: installRoot,
|
|
91
|
+
env: process.env,
|
|
92
|
+
stdio: "pipe",
|
|
93
|
+
});
|
|
94
|
+
writeRuntimeDepMarker(pluginRoot);
|
|
95
|
+
}
|
|
96
|
+
catch (error) {
|
|
97
|
+
const stderr = error &&
|
|
98
|
+
typeof error === "object" &&
|
|
99
|
+
"stderr" in error &&
|
|
100
|
+
Buffer.isBuffer(error.stderr)
|
|
101
|
+
? error.stderr.toString("utf8").trim()
|
|
102
|
+
: "";
|
|
103
|
+
const stdout = error &&
|
|
104
|
+
typeof error === "object" &&
|
|
105
|
+
"stdout" in error &&
|
|
106
|
+
Buffer.isBuffer(error.stdout)
|
|
107
|
+
? error.stdout.toString("utf8").trim()
|
|
108
|
+
: "";
|
|
109
|
+
const detail = stderr || stdout;
|
|
110
|
+
const suffix = detail ? ` (${detail})` : "";
|
|
111
|
+
throw new Error(`Failed to repair better-sqlite3 runtime dependency${suffix}`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
function resetDatabaseConstructorCache() {
|
|
115
|
+
try {
|
|
116
|
+
const resolved = require.resolve("better-sqlite3");
|
|
117
|
+
delete require.cache[resolved];
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
// best effort
|
|
121
|
+
}
|
|
122
|
+
databaseConstructor = null;
|
|
123
|
+
}
|
|
124
|
+
function loadDatabaseConstructor() {
|
|
125
|
+
if (databaseConstructor)
|
|
126
|
+
return databaseConstructor;
|
|
127
|
+
try {
|
|
128
|
+
databaseConstructor = require("better-sqlite3");
|
|
129
|
+
return databaseConstructor;
|
|
130
|
+
}
|
|
131
|
+
catch (error) {
|
|
132
|
+
if (!runtimeDepsRepairAttempted && isRecoverableBetterSqlite3Error(error)) {
|
|
133
|
+
runtimeDepsRepairAttempted = true;
|
|
134
|
+
const pluginRoot = findPluginRoot(currentModuleDir());
|
|
135
|
+
repairBetterSqlite3Binding(pluginRoot);
|
|
136
|
+
resetDatabaseConstructorCache();
|
|
137
|
+
databaseConstructor = require("better-sqlite3");
|
|
138
|
+
return databaseConstructor;
|
|
139
|
+
}
|
|
140
|
+
throw error;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
9
143
|
function stateDbPath() {
|
|
10
144
|
return getOrgxPluginConfigPath(STATE_DB_FILENAME);
|
|
11
145
|
}
|
|
@@ -138,7 +272,24 @@ export function getStateDb() {
|
|
|
138
272
|
closeStateDb();
|
|
139
273
|
}
|
|
140
274
|
ensureStateDbDir();
|
|
141
|
-
|
|
275
|
+
let DatabaseCtor = loadDatabaseConstructor();
|
|
276
|
+
let db;
|
|
277
|
+
try {
|
|
278
|
+
db = new DatabaseCtor(nextPath);
|
|
279
|
+
}
|
|
280
|
+
catch (error) {
|
|
281
|
+
if (!runtimeDepsRepairAttempted && isRecoverableBetterSqlite3Error(error)) {
|
|
282
|
+
runtimeDepsRepairAttempted = true;
|
|
283
|
+
const pluginRoot = findPluginRoot(currentModuleDir());
|
|
284
|
+
repairBetterSqlite3Binding(pluginRoot);
|
|
285
|
+
resetDatabaseConstructorCache();
|
|
286
|
+
DatabaseCtor = loadDatabaseConstructor();
|
|
287
|
+
db = new DatabaseCtor(nextPath);
|
|
288
|
+
}
|
|
289
|
+
else {
|
|
290
|
+
throw error;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
142
293
|
initializeDatabase(db);
|
|
143
294
|
dbInstance = db;
|
|
144
295
|
dbInstancePath = nextPath;
|
package/package.json
CHANGED