@iowarp/clio-coder 0.3.6 → 0.3.7
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/CHANGELOG.md +28 -0
- package/README.md +16 -5
- package/dist/{acp-2BEHC4DL.js → acp-SK4MD6MM.js} +10 -10
- package/dist/{agents-LNNFTM53.js → agents-2FN2K6ME.js} +30 -25
- package/dist/assets/codewiki.json +1 -1
- package/dist/{auth-KXXFI2VS.js → auth-QIYZWM5I.js} +13 -13
- package/dist/{chunk-KHSFENX2.js → chunk-3HAPLH5M.js} +10 -10
- package/dist/{chunk-24I7BN55.js → chunk-465YSENW.js} +2 -2
- package/dist/{chunk-4OC57DA6.js → chunk-4DGYLA73.js} +53 -2
- package/dist/{chunk-CYQKWTG3.js → chunk-4DWFMQDR.js} +4 -4
- package/dist/{chunk-E2ER4LJF.js → chunk-5C3AQNDW.js} +25 -1
- package/dist/{chunk-22NAGB7X.js → chunk-5C77SEEY.js} +5 -94
- package/dist/{chunk-43AOLP7E.js → chunk-5FR74PWO.js} +2 -1
- package/dist/{chunk-K7T3E2SR.js → chunk-5UJ6ECTS.js} +10 -9
- package/dist/{chunk-6US73PDB.js → chunk-6M7VS3J3.js} +5 -5
- package/dist/{chunk-CJUB2JJ2.js → chunk-6TUKSZVF.js} +5 -5
- package/dist/{chunk-5JGRAMKL.js → chunk-AB4XIIVB.js} +8 -6
- package/dist/{chunk-R46L2BIR.js → chunk-BMWK7ZIZ.js} +14 -20
- package/dist/{chunk-4BPJXDWC.js → chunk-C4JBQ5SR.js} +30 -14
- package/dist/{chunk-XXQNGV4M.js → chunk-CEYBNUGC.js} +243 -63
- package/dist/{chunk-VEZEGCGW.js → chunk-D4MDIG46.js} +20 -18
- package/dist/chunk-DJNLUABN.js +843 -0
- package/dist/{chunk-RY3LY4J5.js → chunk-DMD2AGVS.js} +5 -4
- package/dist/{chunk-KOHPCX4K.js → chunk-DOOEX22V.js} +2 -2
- package/dist/chunk-DQA7QLMD.js +123 -0
- package/dist/chunk-DR52UMZW.js +21 -0
- package/dist/{chunk-XF5N4U5A.js → chunk-EBEFWSGL.js} +6 -5
- package/dist/{chunk-EYPA3EGJ.js → chunk-EELBMBT6.js} +120 -13
- package/dist/{chunk-CKXWIANG.js → chunk-EOOQZZDE.js} +16 -14
- package/dist/{chunk-WR67VIZY.js → chunk-FOT2FX5J.js} +63 -5
- package/dist/{chunk-FYYLNIL5.js → chunk-GH5622CP.js} +2 -2
- package/dist/chunk-GWS3VEIW.js +195 -0
- package/dist/{chunk-LYF7OHWH.js → chunk-J7PIKKWC.js} +8 -463
- package/dist/{chunk-NILBFAPG.js → chunk-JNXPYBB4.js} +2 -2
- package/dist/{chunk-4VP4KH3K.js → chunk-JRIO5UD2.js} +4 -4
- package/dist/{chunk-6XXKFVSN.js → chunk-JTSEDYVQ.js} +7 -7
- package/dist/{chunk-QKMUKYO7.js → chunk-KCMKRQX4.js} +236 -84
- package/dist/chunk-KZ2H5X4G.js +1026 -0
- package/dist/{chunk-QNQHSOLF.js → chunk-LADCF22A.js} +12 -12
- package/dist/chunk-M4AKACEO.js +382 -0
- package/dist/{chunk-XYDYPRZI.js → chunk-MXI6J5JF.js} +7 -7
- package/dist/{chunk-G7MUEIGA.js → chunk-OB5HIGJY.js} +1 -1
- package/dist/{chunk-EKY57CSP.js → chunk-OBMAI2DP.js} +61 -767
- package/dist/chunk-PD3MESLB.js +242 -0
- package/dist/{chunk-ZRGEBJ4T.js → chunk-QCTRSGHQ.js} +21 -21
- package/dist/chunk-RVG5JXAL.js +41 -0
- package/dist/{chunk-RD5U66HV.js → chunk-SROCI7ZU.js} +7 -7
- package/dist/{chunk-MFFY33HR.js → chunk-THKY7CD7.js} +466 -205
- package/dist/{chunk-PCZJO5TI.js → chunk-UFQ3F4FW.js} +13 -178
- package/dist/{chunk-AD2SYQYC.js → chunk-UHXRNZ2J.js} +121 -3
- package/dist/chunk-UND3GU2L.js +103 -0
- package/dist/{chunk-QM3F2GKX.js → chunk-UUANF5CR.js} +2247 -2096
- package/dist/chunk-UVDSQ6LW.js +472 -0
- package/dist/{chunk-DJVECN66.js → chunk-VQNODYQ4.js} +14 -14
- package/dist/{chunk-3BPUFZDL.js → chunk-VREKEFLL.js} +3 -3
- package/dist/{chunk-PBTHKCPN.js → chunk-WJHBC77E.js} +6 -6
- package/dist/{chunk-XE2VEJHX.js → chunk-X2KV5FXT.js} +2 -2
- package/dist/{chunk-ZXF4XRKW.js → chunk-XEGB6BCN.js} +157 -7
- package/dist/{chunk-E25LMLRW.js → chunk-YD734TPH.js} +2 -2
- package/dist/{verifiers-NCBTHHN2.js → chunk-YTYFXUI3.js} +65 -322
- package/dist/{chunk-OH3TOQTB.js → chunk-ZGH7FGS5.js} +13 -7
- package/dist/cli/index.js +32 -30
- package/dist/{clio-M2KGYUFZ.js → clio-WBVQEBKO.js} +7 -7
- package/dist/{code-nav-GQNL7XA6.js → code-nav-FGGFIE7L.js} +3 -3
- package/dist/{components-5TTYYX6G.js → components-F7OEATSO.js} +4 -4
- package/dist/{config-XUUYQIWO.js → config-TRBL3RCF.js} +34 -29
- package/dist/{configure-IHJ7YOMV.js → configure-OLCVPHNM.js} +15 -15
- package/dist/{context-74JLXAWD.js → context-MJIJ6GOX.js} +11 -11
- package/dist/{context-ZQ7SIFJV.js → context-WFPKQSM6.js} +19 -3
- package/dist/{context-75MIWW3U.js → context-XEWE3MOJ.js} +31 -26
- package/dist/{context-clear-GYKWNUML.js → context-clear-KNOS2JPB.js} +31 -26
- package/dist/{context-working-set-UX5KEP4J.js → context-working-set-EUXAZI6N.js} +8 -8
- package/dist/{dispatch-runner-GIJBHNFL.js → dispatch-runner-B7MTOVKL.js} +313 -53
- package/dist/{docs-6FZSCG5B.js → docs-FLJTIDSE.js} +4 -4
- package/dist/{doctor-SVJ5BZCW.js → doctor-RN4YKO2X.js} +14 -14
- package/dist/{eval-CG6LLBLD.js → eval-RUBJVSNQ.js} +8 -7
- package/dist/{evidence-ZYFIEN42.js → evidence-JZNBUOQZ.js} +30 -25
- package/dist/{evolve-QGEXEMDW.js → evolve-FJVC4KKI.js} +30 -25
- package/dist/{extensions-ADGNCJJD.js → extensions-IQL36S7K.js} +4 -4
- package/dist/{fleet-S5R4ZOQY.js → fleet-BDKYJFCP.js} +214 -360
- package/dist/fleet-commands-ZFIWZSB3.js +70 -0
- package/dist/fleet-graph-Y6HPXIVF.js +125 -0
- package/dist/fleet-new-RDVJLHHH.js +48 -0
- package/dist/fleet-validate-BIYREGIK.js +79 -0
- package/dist/{init-5DRU55YR.js → init-LQUB5COQ.js} +44 -37
- package/dist/library-NJAHIGG4.js +217 -0
- package/dist/{memory-7YKKR6UC.js → memory-OG6HOYKM.js} +31 -26
- package/dist/{models-ZPOLRU2C.js → models-5ZG5XY7J.js} +21 -20
- package/dist/{monitor-US5F5YGZ.js → monitor-TJ7AMTGB.js} +49 -30
- package/dist/{orchestrator-E2AL4T5N.js → orchestrator-WZYB54DM.js} +3827 -581
- package/dist/{paths-E7KYAQWE.js → paths-XUC7GS6E.js} +4 -4
- package/dist/{reset-KZ652EK6.js → reset-PXQT45IY.js} +7 -7
- package/dist/{run-SRNBKDWD.js → run-FQ74YF62.js} +53 -45
- package/dist/{share-CGZE33UP.js → share-FW7SVCL3.js} +33 -9
- package/dist/{skills-S2X4DLY5.js → skills-7E7IRB3R.js} +24 -8
- package/dist/{skills-eval-W2GGIC4R.js → skills-eval-LI75W6OK.js} +34 -27
- package/dist/{targets-54SWINWB.js → targets-4CIFKCTW.js} +23 -22
- package/dist/{terminal-lease-SAIF2OGY.js → terminal-lease-WUZY7ZV5.js} +4 -4
- package/dist/{uninstall-BVLWXKBT.js → uninstall-7FV7IP4E.js} +4 -4
- package/dist/{upgrade-JKAR27XC.js → upgrade-K2HVIVMQ.js} +20 -19
- package/dist/{usage-MSAWCLX4.js → usage-GTZELZQX.js} +116 -49
- package/dist/verifiers-RLAHT27O.js +336 -0
- package/dist/{verify-X5HDROLA.js → verify-BX3BRKH5.js} +7 -6
- package/dist/{wiki-generate-GUSOQ6ZP.js → wiki-generate-ASIFASCN.js} +45 -37
- package/dist/worker/entry.js +38 -35
- package/docs/README.md +3 -2
- package/docs/acp.md +1 -1
- package/docs/alcf-provider.md +1 -1
- package/docs/architecture.md +2 -2
- package/docs/artifact-versions.md +10 -6
- package/docs/built-in-agents.md +26 -2
- package/docs/capacity-and-scheduling.md +1 -1
- package/docs/commands-and-modes.md +82 -2
- package/docs/configuration-and-targets.md +79 -2
- package/docs/context-engine.md +1 -1
- package/docs/development-pipeline.md +1 -1
- package/docs/dispatch-architecture-rationale.md +1 -1
- package/docs/documentation-coverage.md +3 -3
- package/docs/documentation-guide.md +3 -3
- package/docs/eval-runner.md +1 -1
- package/docs/evals-internal.md +1 -1
- package/docs/evidence-and-memory.md +5 -5
- package/docs/evolution.md +1 -1
- package/docs/exit-codes-and-output.md +4 -1
- package/docs/extensions-and-sharing.md +6 -2
- package/docs/fleet-demo-runbook.md +2 -2
- package/docs/fleet-dispatch.md +197 -10
- package/docs/git-commit-provenance.md +2 -2
- package/docs/glossary.md +1 -1
- package/docs/installation-and-lifecycle.md +2 -2
- package/docs/middleware-and-components.md +2 -1
- package/docs/model-catalog.md +1 -1
- package/docs/observability.md +55 -8
- package/docs/proactive-memory.md +1 -1
- package/docs/prompt-envelope-and-tools.md +1 -1
- package/docs/provider-adapter-cookbook.md +1 -1
- package/docs/release-cut-checklist.md +79 -64
- package/docs/resource-library.md +59 -0
- package/docs/safety-model.md +2 -2
- package/docs/scientific-validation.md +3 -3
- package/docs/session-lifecycle.md +37 -1
- package/docs/skills-marketplace.md +16 -3
- package/docs/tool-usage.md +14 -7
- package/docs/trace-store.md +1 -1
- package/docs/troubleshooting.md +1 -1
- package/docs/tui-design.md +1 -1
- package/docs/worker-dispatch-mechanics.md +3 -3
- package/package.json +1 -1
- package/src/cli/fleet-commands.ts +37 -0
- package/src/cli/fleet-graph.ts +102 -0
- package/src/cli/fleet-new.ts +36 -0
- package/src/cli/fleet-preflight.ts +121 -0
- package/src/cli/fleet-validate.ts +30 -0
- package/src/cli/fleet.ts +173 -335
- package/src/cli/index.ts +3 -1
- package/src/cli/library.ts +190 -0
- package/src/cli/share.ts +13 -1
- package/src/cli/usage.ts +111 -19
- package/src/core/bus-events.ts +4 -0
- package/src/core/commit-attribution.ts +4 -4
- package/src/core/config.ts +130 -0
- package/src/core/defaults.ts +81 -0
- package/src/domains/agents/builtins/architect.md +1 -0
- package/src/domains/agents/builtins/oracle.md +33 -0
- package/src/domains/agents/catalog.ts +13 -1
- package/src/domains/agents/fleet-contract.ts +278 -16
- package/src/domains/agents/index.ts +14 -0
- package/src/domains/agents/result-contract.ts +235 -1
- package/src/domains/config/classify.ts +4 -0
- package/src/domains/dispatch/active-route-planner.ts +14 -0
- package/src/domains/dispatch/backoff.ts +2 -1
- package/src/domains/dispatch/capability-match.ts +1 -0
- package/src/domains/dispatch/checkout-writer-lease.ts +175 -0
- package/src/domains/dispatch/contract.ts +34 -0
- package/src/domains/dispatch/delegation-plan.ts +167 -0
- package/src/domains/dispatch/execution-plan.ts +76 -5
- package/src/domains/dispatch/execution-role.ts +3 -1
- package/src/domains/dispatch/execution-scheduler.ts +183 -67
- package/src/domains/dispatch/extension.ts +258 -9
- package/src/domains/dispatch/fleet-gate.ts +14 -0
- package/src/domains/dispatch/fleet-plan.ts +63 -3
- package/src/domains/dispatch/fleet-run.ts +737 -0
- package/src/domains/dispatch/gate-role-prompts.ts +9 -0
- package/src/domains/dispatch/host-verification.ts +178 -0
- package/src/domains/dispatch/index.ts +38 -0
- package/src/domains/dispatch/intent.ts +159 -0
- package/src/domains/dispatch/receipt-integrity.ts +8 -4
- package/src/domains/dispatch/state.ts +36 -3
- package/src/domains/dispatch/types.ts +51 -6
- package/src/domains/dispatch/validation.ts +66 -6
- package/src/domains/evidence/trust-status.ts +10 -1
- package/src/domains/middleware/index.ts +15 -0
- package/src/domains/middleware/watchdog.ts +281 -0
- package/src/domains/observability/contract.ts +3 -1
- package/src/domains/observability/cost.ts +12 -1
- package/src/domains/observability/extension.ts +2 -2
- package/src/domains/observability/index.ts +10 -0
- package/src/domains/observability/out-of-turn-usage.ts +223 -0
- package/src/domains/resources/index.ts +20 -0
- package/src/domains/resources/library.ts +326 -0
- package/src/domains/resources/skills/marketplace.ts +37 -12
- package/src/domains/session/handoff.ts +629 -0
- package/src/domains/share/archive.ts +67 -2
- package/src/entry/orchestrator.ts +37 -0
- package/src/interactive/bus-notices.ts +26 -0
- package/src/interactive/chat-loop.ts +235 -1
- package/src/interactive/chat-renderer.ts +22 -0
- package/src/interactive/cost-overlay.ts +31 -3
- package/src/interactive/council-dispatch.ts +30 -0
- package/src/interactive/council-grid.ts +213 -0
- package/src/interactive/council.ts +99 -0
- package/src/interactive/dispatch-board.ts +260 -16
- package/src/interactive/fleet-run-preview.ts +307 -0
- package/src/interactive/footer/notifications.ts +219 -0
- package/src/interactive/handoff-round.ts +56 -0
- package/src/interactive/interactive-application.ts +43 -1
- package/src/interactive/interactive-event-projection.ts +9 -1
- package/src/interactive/interactive-slash-runtime.ts +52 -2
- package/src/interactive/interactive-subscriptions.ts +14 -2
- package/src/interactive/oracle.ts +179 -0
- package/src/interactive/overlay-ask-user-lifecycle.ts +6 -0
- package/src/interactive/overlay-general-openers.ts +190 -1
- package/src/interactive/overlay-key-routing.ts +17 -1
- package/src/interactive/overlay-lifecycle.ts +41 -1
- package/src/interactive/overlay-permission-lifecycle.ts +10 -0
- package/src/interactive/overlay-resource-openers.ts +11 -3
- package/src/interactive/overlay-session-lifecycle.ts +234 -2
- package/src/interactive/overlays/fleet-run-approval.ts +208 -0
- package/src/interactive/overlays/handoff-review.ts +185 -0
- package/src/interactive/overlays/library-install-confirm.ts +151 -0
- package/src/interactive/overlays/list-overlay.ts +168 -2
- package/src/interactive/overlays/settings.ts +101 -4
- package/src/interactive/overlays/side-question.ts +139 -0
- package/src/interactive/overlays/skills-hub.ts +401 -15
- package/src/interactive/side-question.ts +171 -0
- package/src/interactive/slash-commands.ts +432 -5
- package/src/interactive/slash-spec.ts +19 -6
- package/src/interactive/theme/tokens.ts +30 -0
- package/src/interactive/turn-middleware.ts +15 -1
- package/src/interactive/watchdog-run.ts +75 -0
- package/src/interactive/worker-share.ts +56 -1
- package/src/interactive/worker-stream.ts +7 -0
- package/src/tools/bootstrap.ts +3 -0
- package/src/tools/compete-worktrees.ts +13 -79
- package/src/tools/dispatch-admission.ts +242 -8
- package/src/tools/dispatch-arguments.ts +57 -1
- package/src/tools/dispatch-plan.ts +136 -6
- package/src/tools/dispatch-runner.ts +319 -13
- package/src/tools/dispatch-types.ts +20 -1
- package/src/tools/dispatch.ts +72 -2
- package/src/tools/monitor.ts +16 -0
- package/src/tools/profiles.ts +18 -4
- package/src/tools/task-worktree.ts +238 -0
- package/src/tools/verify/authoring.ts +61 -1
- package/src/tools/verify/scripts.ts +62 -0
- package/src/tools/worker-evidence.ts +2 -1
- package/src/worker/spec-contract.ts +1 -0
- package/dist/chunk-HC4CLZ2Y.js +0 -68
|
@@ -35,6 +35,13 @@ import type { CostProvenance } from "../domains/providers/index.js";
|
|
|
35
35
|
import { sanitizeCallTargetText } from "../domains/safety/call-target.js";
|
|
36
36
|
import { type Component, truncateToWidth, visibleWidth, wrapTextWithAnsi } from "../engine/tui.js";
|
|
37
37
|
import { formatWorkerContextMeter } from "./context-meter.js";
|
|
38
|
+
import {
|
|
39
|
+
COUNCIL_SYNTHESIS_LABEL,
|
|
40
|
+
type CouncilGroupView,
|
|
41
|
+
type CouncilMemberView,
|
|
42
|
+
councilGroupBody,
|
|
43
|
+
councilIslandLines,
|
|
44
|
+
} from "./council-grid.js";
|
|
38
45
|
import { formatFooterTokens } from "./footer-panel.js";
|
|
39
46
|
import {
|
|
40
47
|
type ClioTheme,
|
|
@@ -103,6 +110,9 @@ export interface DispatchBoardRow {
|
|
|
103
110
|
node?: string;
|
|
104
111
|
/** Review/compete gate badge (role + cycle). */
|
|
105
112
|
gate?: { role: string; cycle: number };
|
|
113
|
+
council?: { group: string; label: string; color?: string; round: number };
|
|
114
|
+
/** Orchestrator-executed declared verification status. */
|
|
115
|
+
hostVerification?: "verified" | "rejected" | "skipped";
|
|
106
116
|
/** Dead-node failover hops recorded on this run's chain. */
|
|
107
117
|
rerouteCount?: number;
|
|
108
118
|
/** Assignment retry attempts observed on the assignment event stream. */
|
|
@@ -134,8 +144,39 @@ export interface DispatchBoardRow {
|
|
|
134
144
|
retry?: DispatchRetryPresentation;
|
|
135
145
|
/** Most recent worker delivery acknowledgement for an operator steer. */
|
|
136
146
|
steerAcknowledgement?: DispatchSteerAcknowledgement;
|
|
147
|
+
/**
|
|
148
|
+
* Where this run sits in a fleet plan: the wave the step was scheduled in
|
|
149
|
+
* and the step id it executes. Present only for runs a fleet plan
|
|
150
|
+
* dispatched, so a `/run`, a delegation, or a dispatch-tool run leaves the
|
|
151
|
+
* board's phase column empty rather than claiming a position it has none of.
|
|
152
|
+
*/
|
|
153
|
+
phase?: DispatchBoardPhase;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/** One fleet plan position: the wave index and the step the run executes. */
|
|
157
|
+
export interface DispatchBoardPhase {
|
|
158
|
+
wave: number;
|
|
159
|
+
stepId: string;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* The phase cell, or null for a run that is not a fleet step. Fixed width so
|
|
164
|
+
* the column lines up across rows; a long step id is clipped with a marker
|
|
165
|
+
* rather than pushing the row's other facts out of position.
|
|
166
|
+
*/
|
|
167
|
+
export function formatDispatchPhaseCell(
|
|
168
|
+
phase: DispatchBoardPhase | undefined,
|
|
169
|
+
width = PHASE_COLUMN_WIDTH,
|
|
170
|
+
): string | null {
|
|
171
|
+
if (phase === undefined) return null;
|
|
172
|
+
return truncateToWidth(`w${phase.wave} ${phase.stepId}`, Math.max(1, width), "…", false);
|
|
137
173
|
}
|
|
138
174
|
|
|
175
|
+
/** Columns the compact island reserves for the phase cell when it can afford them. */
|
|
176
|
+
export const PHASE_COLUMN_WIDTH = 12;
|
|
177
|
+
/** Below this much room for the agent label, the compact row drops the column entirely. */
|
|
178
|
+
const PHASE_COLUMN_MIN_LABEL_WIDTH = 8;
|
|
179
|
+
|
|
139
180
|
/** Live HTTP/SDK rows are the only rows whose runtime can consume operator guidance. */
|
|
140
181
|
export function isDispatchBoardRowSteerable(row: DispatchBoardRow): boolean {
|
|
141
182
|
return (
|
|
@@ -610,14 +651,26 @@ export function renderDispatchCard(
|
|
|
610
651
|
`${theme.fg("dim", "ttft")} ${theme.fg("muted", ttft)}`,
|
|
611
652
|
`${theme.fg("dim", "cost")} ${theme.fg("muted", cost)}`,
|
|
612
653
|
];
|
|
654
|
+
const hostVerification = theme.fg("info", `host_verification=${row.hostVerification ?? "not_requested"}`);
|
|
613
655
|
const contextUnit = formatWorkerContextMeter(row.lastContextTokens ?? 0, row.contextWindow, theme);
|
|
656
|
+
// The phase column: a fleet step says which wave it belongs to and which
|
|
657
|
+
// step it is; every other run leaves the cell empty rather than inventing a
|
|
658
|
+
// position, so the column reads as "not a fleet step" and not as wave zero.
|
|
659
|
+
const phaseCell = formatDispatchPhaseCell(row.phase, contentWidth);
|
|
614
660
|
const bodyLines = [
|
|
615
661
|
cardUnitsLine(theme, "run", [theme.fg("dim", row.runId)], contentWidth),
|
|
662
|
+
cardUnitsLine(
|
|
663
|
+
theme,
|
|
664
|
+
"phase",
|
|
665
|
+
[phaseCell === null ? theme.fg("dim", "—") : theme.fg("info", phaseCell)],
|
|
666
|
+
contentWidth,
|
|
667
|
+
),
|
|
616
668
|
targetLine,
|
|
617
669
|
...(row.taskSummary
|
|
618
670
|
? [truncateToWidth(`${cardKvKey(theme, "task")}${theme.fg("muted", row.taskSummary)}`, contentWidth, "…", false)]
|
|
619
671
|
: []),
|
|
620
672
|
cardUnitsLine(theme, "status", statusUnits, contentWidth),
|
|
673
|
+
cardUnitsLine(theme, "evidence", [hostVerification], contentWidth),
|
|
621
674
|
...(row.budget !== undefined
|
|
622
675
|
? [
|
|
623
676
|
truncateToWidth(
|
|
@@ -715,11 +768,25 @@ function renderTaskIslandRow(row: DispatchBoardRow, width: number): string[] {
|
|
|
715
768
|
visibleWidth(presentation.label) +
|
|
716
769
|
3 +
|
|
717
770
|
visibleWidth(elapsed);
|
|
718
|
-
|
|
771
|
+
// The phase column rides the compact row only when the row can still show a
|
|
772
|
+
// readable agent label beside it. TASK_ISLAND_WIDTH is fixed, so a row that
|
|
773
|
+
// cannot afford the cell drops the column here and shows the phase in the
|
|
774
|
+
// expanded card instead of clipping the label to make room.
|
|
775
|
+
const phaseCell = formatDispatchPhaseCell(row.phase);
|
|
776
|
+
const phaseColumn = phaseCell === null ? "" : `${dot}${theme.fg("info", padAnsi(phaseCell, PHASE_COLUMN_WIDTH))}`;
|
|
777
|
+
const phaseChrome = phaseCell === null ? 0 : PHASE_COLUMN_WIDTH + 3;
|
|
778
|
+
const labelWidth = width - labelChrome - phaseChrome;
|
|
779
|
+
const showPhase = phaseCell !== null && labelWidth >= PHASE_COLUMN_MIN_LABEL_WIDTH;
|
|
780
|
+
const clampedLabel = truncateToWidth(
|
|
781
|
+
agentLabel,
|
|
782
|
+
Math.max(1, showPhase ? labelWidth : width - labelChrome),
|
|
783
|
+
"…",
|
|
784
|
+
false,
|
|
785
|
+
);
|
|
719
786
|
|
|
720
787
|
// The agent label drops its accent color for plain bold; the status word is
|
|
721
788
|
// the only status-colored element on the row, with dim middot separators.
|
|
722
|
-
const line1 = `${glyph} ${rowPrefix.text}${theme.paint(clampedLabel, { bold: true })}${dot}${statusStr}${dot}${theme.fg("muted", elapsed)}`;
|
|
789
|
+
const line1 = `${glyph} ${rowPrefix.text}${theme.paint(clampedLabel, { bold: true })}${dot}${statusStr}${dot}${theme.fg("muted", elapsed)}${showPhase ? phaseColumn : ""}`;
|
|
723
790
|
|
|
724
791
|
const elapsedSec = row.elapsedMs / 1000;
|
|
725
792
|
const tokensPerSec = elapsedSec > 0.1 ? Math.round(row.outputTokens / elapsedSec) : 0;
|
|
@@ -740,6 +807,131 @@ function renderTaskIslandRow(row: DispatchBoardRow, width: number): string[] {
|
|
|
740
807
|
return [padAnsi(line1, width), ...(task ? [padAnsi(task, width)] : []), padAnsi(telemetry, width)];
|
|
741
808
|
}
|
|
742
809
|
|
|
810
|
+
/**
|
|
811
|
+
* One member column's facts, drawn from the row the board already keeps. The
|
|
812
|
+
* route, status, and answer tail are the same values the run's own card shows,
|
|
813
|
+
* so a council column and a council card can never disagree.
|
|
814
|
+
*/
|
|
815
|
+
function councilMemberView(
|
|
816
|
+
row: DispatchBoardRow,
|
|
817
|
+
council: NonNullable<DispatchBoardRow["council"]>,
|
|
818
|
+
): CouncilMemberView {
|
|
819
|
+
const presentation = dispatchStatusPresentation(row.status, {
|
|
820
|
+
compact: true,
|
|
821
|
+
...(row.status === "running" ? { tick: Math.floor(Date.now() / 100) } : {}),
|
|
822
|
+
});
|
|
823
|
+
return {
|
|
824
|
+
runId: row.runId,
|
|
825
|
+
label: council.label,
|
|
826
|
+
...(council.color !== undefined ? { color: council.color } : {}),
|
|
827
|
+
round: council.round,
|
|
828
|
+
route: `${row.targetId}/${row.wireModelId}`,
|
|
829
|
+
status: { glyph: presentation.glyph, label: presentation.label, token: presentation.token },
|
|
830
|
+
tailText: sanitizeCallTargetText(row.progress?.tailText ?? ""),
|
|
831
|
+
droppedLines: row.progress?.droppedLines ?? 0,
|
|
832
|
+
};
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
/**
|
|
836
|
+
* Fold one council group's rows into the view the grid renders.
|
|
837
|
+
*
|
|
838
|
+
* A council of N members over R rounds seals N x R member runs, and the
|
|
839
|
+
* operator is looking at a council, not at its history: each label keeps its
|
|
840
|
+
* newest round, so the group shows one column per member however many rounds
|
|
841
|
+
* it ran. Columns are ordered by label so a repaint cannot reshuffle them under
|
|
842
|
+
* the cursor while runs settle in whatever order they finish.
|
|
843
|
+
*/
|
|
844
|
+
function foldCouncilGroup(
|
|
845
|
+
group: string,
|
|
846
|
+
rows: ReadonlyArray<DispatchBoardRow>,
|
|
847
|
+
selectedRunId?: string | null,
|
|
848
|
+
): CouncilGroupView {
|
|
849
|
+
const newest = new Map<string, DispatchBoardRow>();
|
|
850
|
+
let synthesisRow: DispatchBoardRow | null = null;
|
|
851
|
+
let rank = Number.POSITIVE_INFINITY;
|
|
852
|
+
let elapsedMs = 0;
|
|
853
|
+
let round = 1;
|
|
854
|
+
for (const row of rows) {
|
|
855
|
+
const council = row.council;
|
|
856
|
+
if (council === undefined) continue;
|
|
857
|
+
rank = Math.min(rank, STATUS_ORDER[row.status]);
|
|
858
|
+
elapsedMs = Math.max(elapsedMs, row.elapsedMs);
|
|
859
|
+
if (council.label === COUNCIL_SYNTHESIS_LABEL) {
|
|
860
|
+
synthesisRow = row;
|
|
861
|
+
continue;
|
|
862
|
+
}
|
|
863
|
+
round = Math.max(round, council.round);
|
|
864
|
+
const previous = newest.get(council.label);
|
|
865
|
+
if (previous === undefined || (previous.council?.round ?? 0) <= council.round) newest.set(council.label, row);
|
|
866
|
+
}
|
|
867
|
+
const aggregate = (Object.entries(STATUS_ORDER) as Array<[DispatchBoardStatus, number]>).find(
|
|
868
|
+
([, value]) => value === rank,
|
|
869
|
+
);
|
|
870
|
+
const presentation = dispatchStatusPresentation(aggregate?.[0] ?? "running", {
|
|
871
|
+
compact: true,
|
|
872
|
+
...(aggregate?.[0] === "running" ? { tick: Math.floor(Date.now() / 100) } : {}),
|
|
873
|
+
});
|
|
874
|
+
const members = [...newest.entries()]
|
|
875
|
+
.sort(([left], [right]) => left.localeCompare(right))
|
|
876
|
+
// biome-ignore lint/style/noNonNullAssertion: every entry was inserted with its own council badge.
|
|
877
|
+
.map(([, row]) => councilMemberView(row, row.council!));
|
|
878
|
+
const selectedInGroup =
|
|
879
|
+
selectedRunId != null && rows.some((row) => row.runId === selectedRunId) ? selectedRunId : undefined;
|
|
880
|
+
return {
|
|
881
|
+
group,
|
|
882
|
+
members,
|
|
883
|
+
// biome-ignore lint/style/noNonNullAssertion: the synthesis row was matched on its own council badge.
|
|
884
|
+
synthesis: synthesisRow === null ? null : councilMemberView(synthesisRow, synthesisRow.council!),
|
|
885
|
+
status: { glyph: presentation.glyph, label: presentation.label, token: presentation.token },
|
|
886
|
+
round,
|
|
887
|
+
elapsed: formatCompactMs(elapsedMs),
|
|
888
|
+
...(selectedInGroup !== undefined ? { selectedRunId: selectedInGroup } : {}),
|
|
889
|
+
};
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
/** A board entry: either one ordinary run, or one whole council in the position of its first row. */
|
|
893
|
+
export type DispatchBoardItem = { kind: "run"; row: DispatchBoardRow } | { kind: "council"; group: CouncilGroupView };
|
|
894
|
+
|
|
895
|
+
/**
|
|
896
|
+
* Board order with councils folded in place. A council occupies the position of
|
|
897
|
+
* its first row and its remaining rows are consumed, so the board never shows a
|
|
898
|
+
* member both inside its group and beside it.
|
|
899
|
+
*/
|
|
900
|
+
export function dispatchBoardItems(
|
|
901
|
+
rows: ReadonlyArray<DispatchBoardRow>,
|
|
902
|
+
selectedRunId?: string | null,
|
|
903
|
+
): DispatchBoardItem[] {
|
|
904
|
+
const byGroup = new Map<string, DispatchBoardRow[]>();
|
|
905
|
+
for (const row of rows) {
|
|
906
|
+
const group = row.council?.group;
|
|
907
|
+
if (group === undefined) continue;
|
|
908
|
+
const bucket = byGroup.get(group);
|
|
909
|
+
if (bucket === undefined) byGroup.set(group, [row]);
|
|
910
|
+
else bucket.push(row);
|
|
911
|
+
}
|
|
912
|
+
const emitted = new Set<string>();
|
|
913
|
+
const items: DispatchBoardItem[] = [];
|
|
914
|
+
for (const row of rows) {
|
|
915
|
+
const group = row.council?.group;
|
|
916
|
+
if (group === undefined) {
|
|
917
|
+
items.push({ kind: "run", row });
|
|
918
|
+
continue;
|
|
919
|
+
}
|
|
920
|
+
if (emitted.has(group)) continue;
|
|
921
|
+
emitted.add(group);
|
|
922
|
+
items.push({ kind: "council", group: foldCouncilGroup(group, byGroup.get(group) ?? [row], selectedRunId) });
|
|
923
|
+
}
|
|
924
|
+
return items;
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
/** The framed council card: the member grid or stack, with the synthesis run full width under it. */
|
|
928
|
+
export function renderCouncilCard(group: CouncilGroupView, width: number): string[] {
|
|
929
|
+
const theme = clioTheme();
|
|
930
|
+
const contentWidth = Math.max(0, width - 4);
|
|
931
|
+
const title = `${theme.fg("info", "council")} ${theme.paint(group.group, { bold: true })}`;
|
|
932
|
+
return frame(theme, title, councilGroupBody(theme, group, contentWidth), width, { rightMeta: group.elapsed });
|
|
933
|
+
}
|
|
934
|
+
|
|
743
935
|
export function formatDispatchBoardLines(
|
|
744
936
|
rows: ReadonlyArray<DispatchBoardRow>,
|
|
745
937
|
width = 76,
|
|
@@ -757,11 +949,15 @@ export function formatDispatchBoardLines(
|
|
|
757
949
|
});
|
|
758
950
|
}
|
|
759
951
|
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
952
|
+
// A council is one question asked of several members, so its rows render as
|
|
953
|
+
// one card holding the whole group rather than as unrelated neighbours.
|
|
954
|
+
const cards = dispatchBoardItems(rows, selectedRunId).map((item) =>
|
|
955
|
+
item.kind === "council"
|
|
956
|
+
? renderCouncilCard(item.group, width)
|
|
957
|
+
: renderDispatchCard(item.row, width, deriveRunEvidenceState(observability, item.row.runId), {
|
|
958
|
+
selected: item.row.runId === selectedRunId,
|
|
959
|
+
expanded: detailExpanded && item.row.runId === selectedRunId,
|
|
960
|
+
}),
|
|
765
961
|
);
|
|
766
962
|
const body: string[] = [];
|
|
767
963
|
for (const card of cards) {
|
|
@@ -850,23 +1046,30 @@ export function createDispatchBoardView(
|
|
|
850
1046
|
}
|
|
851
1047
|
|
|
852
1048
|
export function formatTaskIslandLines(rows: ReadonlyArray<DispatchBoardRow>, maxRows = 4): string[] {
|
|
853
|
-
|
|
1049
|
+
// Councils are folded before the row cap, so a five-member council costs the
|
|
1050
|
+
// island one card and never crowds out the runs beside it.
|
|
1051
|
+
const items = dispatchBoardItems(rows);
|
|
1052
|
+
const visibleItems = items.slice(0, Math.max(1, maxRows));
|
|
854
1053
|
const body: string[] = [];
|
|
855
1054
|
|
|
856
|
-
if (
|
|
1055
|
+
if (visibleItems.length === 0) {
|
|
857
1056
|
const theme = clioTheme();
|
|
858
1057
|
body.push(theme.fg("dim", "No active fleet runs."));
|
|
859
1058
|
body.push(theme.fg("dim", "Use /run or /delegate to spawn agents."));
|
|
860
1059
|
} else {
|
|
861
|
-
for (let i = 0; i <
|
|
862
|
-
const
|
|
863
|
-
if (!
|
|
1060
|
+
for (let i = 0; i < visibleItems.length; i++) {
|
|
1061
|
+
const item = visibleItems[i];
|
|
1062
|
+
if (!item) continue;
|
|
864
1063
|
if (i > 0) {
|
|
865
1064
|
body.push(innerDivider(clioTheme(), TASK_ISLAND_WIDTH));
|
|
866
1065
|
}
|
|
867
|
-
body.push(
|
|
1066
|
+
body.push(
|
|
1067
|
+
...(item.kind === "council"
|
|
1068
|
+
? councilIslandLines(clioTheme(), item.group, TASK_ISLAND_WIDTH)
|
|
1069
|
+
: renderTaskIslandRow(item.row, TASK_ISLAND_WIDTH)),
|
|
1070
|
+
);
|
|
868
1071
|
}
|
|
869
|
-
const hidden =
|
|
1072
|
+
const hidden = items.length - visibleItems.length;
|
|
870
1073
|
if (hidden > 0) {
|
|
871
1074
|
body.push(innerDivider(clioTheme(), TASK_ISLAND_WIDTH));
|
|
872
1075
|
body.push(clioTheme().fg("dim", `+ ${hidden} more`));
|
|
@@ -932,6 +1135,21 @@ function parseGateBadge(value: unknown): { role: string; cycle: number } | undef
|
|
|
932
1135
|
return { role: record.role, cycle };
|
|
933
1136
|
}
|
|
934
1137
|
|
|
1138
|
+
function parseCouncilBadge(value: unknown): DispatchBoardRow["council"] | undefined {
|
|
1139
|
+
if (typeof value !== "object" || value === null) return undefined;
|
|
1140
|
+
const record = value as { group?: unknown; label?: unknown; color?: unknown; round?: unknown };
|
|
1141
|
+
const group = parseNonEmptyString(record.group);
|
|
1142
|
+
const label = parseNonEmptyString(record.label);
|
|
1143
|
+
const round = parsePositiveInt(record.round);
|
|
1144
|
+
if (group === undefined || label === undefined || round === undefined) return undefined;
|
|
1145
|
+
return {
|
|
1146
|
+
group,
|
|
1147
|
+
label,
|
|
1148
|
+
...(typeof record.color === "string" && record.color.length > 0 ? { color: record.color } : {}),
|
|
1149
|
+
round,
|
|
1150
|
+
};
|
|
1151
|
+
}
|
|
1152
|
+
|
|
935
1153
|
function parseFiniteNumberOrZero(value: unknown): number {
|
|
936
1154
|
return typeof value === "number" && Number.isFinite(value) ? value : 0;
|
|
937
1155
|
}
|
|
@@ -1099,11 +1317,14 @@ function toRow(entry: DispatchBoardEntry, now: number): DispatchBoardRow {
|
|
|
1099
1317
|
...(entry.outcomeDetail !== undefined ? { outcomeDetail: entry.outcomeDetail } : {}),
|
|
1100
1318
|
...(entry.node !== undefined ? { node: entry.node } : {}),
|
|
1101
1319
|
...(entry.gate !== undefined ? { gate: { ...entry.gate } } : {}),
|
|
1320
|
+
...(entry.council !== undefined ? { council: { ...entry.council } } : {}),
|
|
1321
|
+
...(entry.hostVerification !== undefined ? { hostVerification: entry.hostVerification } : {}),
|
|
1102
1322
|
...(entry.rerouteCount !== undefined ? { rerouteCount: entry.rerouteCount } : {}),
|
|
1103
1323
|
...(entry.failoverHops !== undefined ? { failoverHops: entry.failoverHops } : {}),
|
|
1104
1324
|
...(entry.contextWindow !== undefined ? { contextWindow: entry.contextWindow } : {}),
|
|
1105
1325
|
lastContextTokens: entry.lastContextTokens,
|
|
1106
1326
|
...(entry.receiptId !== undefined ? { receiptId: entry.receiptId } : {}),
|
|
1327
|
+
...(entry.phase !== undefined ? { phase: { ...entry.phase } } : {}),
|
|
1107
1328
|
// Both tool fields are views of the one projection, so the compact card and
|
|
1108
1329
|
// the expanded detail can never disagree about what is running.
|
|
1109
1330
|
currentTool: retry ? null : (progress.currentAction?.tool ?? null),
|
|
@@ -1124,7 +1345,7 @@ function sortEntries(a: DispatchBoardEntry, b: DispatchBoardEntry): number {
|
|
|
1124
1345
|
return a.sequence - b.sequence;
|
|
1125
1346
|
}
|
|
1126
1347
|
|
|
1127
|
-
function pruneEntries(entries: Map<string, DispatchBoardEntry
|
|
1348
|
+
function pruneEntries(entries: Map<string, DispatchBoardEntry>, onEvict?: (runId: string) => void): void {
|
|
1128
1349
|
if (entries.size <= MAX_DISPATCH_BOARD_ROWS) return;
|
|
1129
1350
|
const terminalEntries = [...entries.values()]
|
|
1130
1351
|
.filter((entry) => !entry.retry && isTerminalStatus(entry.status))
|
|
@@ -1134,6 +1355,7 @@ function pruneEntries(entries: Map<string, DispatchBoardEntry>): void {
|
|
|
1134
1355
|
for (const entry of evictionQueue) {
|
|
1135
1356
|
if (entries.size <= MAX_DISPATCH_BOARD_ROWS) break;
|
|
1136
1357
|
entries.delete(entry.runId);
|
|
1358
|
+
onEvict?.(entry.runId);
|
|
1137
1359
|
}
|
|
1138
1360
|
}
|
|
1139
1361
|
|
|
@@ -1150,9 +1372,19 @@ export function createDispatchBoardStore(
|
|
|
1150
1372
|
rows(): ReadonlyArray<DispatchBoardRow>;
|
|
1151
1373
|
activeRows(): ReadonlyArray<DispatchBoardRow>;
|
|
1152
1374
|
reconcile(): void;
|
|
1375
|
+
/**
|
|
1376
|
+
* Record the fleet-plan position of a run the caller just dispatched. The
|
|
1377
|
+
* board never infers a position: a run carries one only because whoever
|
|
1378
|
+
* dispatched the plan said so, which is why a non-fleet run can never grow
|
|
1379
|
+
* a phase cell.
|
|
1380
|
+
*/
|
|
1381
|
+
setFleetPhase(runId: string, phase: DispatchBoardPhase): void;
|
|
1153
1382
|
unsubscribe(): void;
|
|
1154
1383
|
} {
|
|
1155
1384
|
const entries = new Map<string, DispatchBoardEntry>();
|
|
1385
|
+
// Positions arrive from the fleet-run path, which may hand one over before
|
|
1386
|
+
// the run's first lifecycle event creates the entry.
|
|
1387
|
+
const fleetPhases = new Map<string, DispatchBoardPhase>();
|
|
1156
1388
|
let nextSequence = 0;
|
|
1157
1389
|
let reconciledAtMs = Date.now();
|
|
1158
1390
|
|
|
@@ -1180,10 +1412,12 @@ export function createDispatchBoardStore(
|
|
|
1180
1412
|
const requestOrigin = parseRequestOrigin(raw.requestOrigin, previous?.requestOrigin);
|
|
1181
1413
|
const node = parseNonEmptyString(raw.node) ?? previous?.node;
|
|
1182
1414
|
const gate = parseGateBadge(raw.gate) ?? previous?.gate;
|
|
1415
|
+
const council = parseCouncilBadge(raw.council) ?? previous?.council;
|
|
1183
1416
|
const rerouteCount = parsePositiveInt(raw.rerouteCount) ?? previous?.rerouteCount;
|
|
1184
1417
|
const contextWindow = parsePositiveInt(raw.contextWindow) ?? previous?.contextWindow;
|
|
1185
1418
|
const taskSummary = parseTaskSummary(raw, previous?.taskSummary);
|
|
1186
1419
|
const budget = cloneRunToolBudgetEnvelope(raw.budget) ?? previous?.budget;
|
|
1420
|
+
const phase = fleetPhases.get(runId) ?? previous?.phase;
|
|
1187
1421
|
const entry: DispatchBoardEntry = {
|
|
1188
1422
|
runId,
|
|
1189
1423
|
agentId: parseText(raw.agentId, previous?.agentId ?? "-"),
|
|
@@ -1211,16 +1445,19 @@ export function createDispatchBoardStore(
|
|
|
1211
1445
|
outcomeDetail: previous?.outcomeDetail ?? null,
|
|
1212
1446
|
...(node !== undefined ? { node } : {}),
|
|
1213
1447
|
...(gate !== undefined ? { gate } : {}),
|
|
1448
|
+
...(council !== undefined ? { council } : {}),
|
|
1449
|
+
...(previous?.hostVerification !== undefined ? { hostVerification: previous.hostVerification } : {}),
|
|
1214
1450
|
...(rerouteCount !== undefined ? { rerouteCount } : {}),
|
|
1215
1451
|
...(contextWindow !== undefined ? { contextWindow } : {}),
|
|
1216
1452
|
lastContextTokens: previous?.lastContextTokens ?? 0,
|
|
1217
1453
|
...(previous?.receiptId !== undefined ? { receiptId: previous.receiptId } : {}),
|
|
1454
|
+
...(phase !== undefined ? { phase } : {}),
|
|
1218
1455
|
progress: previous?.progress ?? createWorkerProgressFold(),
|
|
1219
1456
|
...(previous?.retry ? { retry: { ...previous.retry } } : {}),
|
|
1220
1457
|
...(previous?.steerAcknowledgement ? { steerAcknowledgement: { ...previous.steerAcknowledgement } } : {}),
|
|
1221
1458
|
};
|
|
1222
1459
|
entries.set(runId, entry);
|
|
1223
|
-
pruneEntries(entries);
|
|
1460
|
+
pruneEntries(entries, (evicted) => fleetPhases.delete(evicted));
|
|
1224
1461
|
return entry;
|
|
1225
1462
|
};
|
|
1226
1463
|
|
|
@@ -1251,6 +1488,7 @@ export function createDispatchBoardStore(
|
|
|
1251
1488
|
entry.costUsd = parseFiniteNumber(payload.costUsd, entry.costUsd);
|
|
1252
1489
|
entry.costProvenance = payload.costProvenance ?? "unknown";
|
|
1253
1490
|
entry.outcomeDetail = null;
|
|
1491
|
+
if (payload.hostVerification !== undefined) entry.hostVerification = payload.hostVerification;
|
|
1254
1492
|
settleFromReceipt(entry);
|
|
1255
1493
|
// A terminal dispatch event is published only after the run's receipt is
|
|
1256
1494
|
// sealed at receipts/<runId>.json, so the run id is the receipt id here.
|
|
@@ -1279,6 +1517,7 @@ export function createDispatchBoardStore(
|
|
|
1279
1517
|
entry.costUsd = parseFiniteNumber(payload.costUsd, entry.costUsd);
|
|
1280
1518
|
entry.costProvenance = payload.costProvenance ?? "unknown";
|
|
1281
1519
|
entry.outcomeDetail = resolveFailureDetail(payload, entry.outcomeDetail);
|
|
1520
|
+
if (payload.hostVerification !== undefined) entry.hostVerification = payload.hostVerification;
|
|
1282
1521
|
settleFromReceipt(entry);
|
|
1283
1522
|
// A denied retry never reached a run, so no receipt was sealed for it;
|
|
1284
1523
|
// every other failure finalized through recordReceipt like a success.
|
|
@@ -1487,6 +1726,11 @@ export function createDispatchBoardStore(
|
|
|
1487
1726
|
return projectRows(true);
|
|
1488
1727
|
},
|
|
1489
1728
|
reconcile,
|
|
1729
|
+
setFleetPhase(runId: string, phase: DispatchBoardPhase): void {
|
|
1730
|
+
fleetPhases.set(runId, phase);
|
|
1731
|
+
const entry = entries.get(runId);
|
|
1732
|
+
if (entry) entry.phase = { ...phase };
|
|
1733
|
+
},
|
|
1490
1734
|
unsubscribe() {
|
|
1491
1735
|
if (closed) return;
|
|
1492
1736
|
closed = true;
|