@iowarp/clio-coder 0.3.6 → 0.3.8
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 +59 -0
- package/README.md +22 -6
- package/dist/{acp-2BEHC4DL.js → acp-U67UHUK2.js} +14 -14
- package/dist/{agents-LNNFTM53.js → agents-YU6SGALZ.js} +42 -35
- package/dist/assets/codewiki.json +1 -1
- package/dist/{auth-KXXFI2VS.js → auth-5ZPJOIVG.js} +32 -24
- package/dist/builtins-C6JMZVV6.js +17 -0
- package/dist/{chunk-E25LMLRW.js → chunk-26LEYJZH.js} +2 -2
- package/dist/{chunk-TSHXZTOQ.js → chunk-2HEJ2F35.js} +5 -5
- package/dist/{chunk-PBTHKCPN.js → chunk-2HFZQUHL.js} +7 -7
- package/dist/{chunk-FO5ZOVUY.js → chunk-3BINW3FP.js} +5 -5
- package/dist/{chunk-4OC57DA6.js → chunk-4DGYLA73.js} +53 -2
- package/dist/{chunk-CKXWIANG.js → chunk-4SPRNWDE.js} +18 -16
- package/dist/{chunk-E2ER4LJF.js → chunk-5C3AQNDW.js} +25 -1
- package/dist/{chunk-XF5N4U5A.js → chunk-5DHKRSMQ.js} +9 -8
- package/dist/{chunk-43AOLP7E.js → chunk-5FR74PWO.js} +2 -1
- package/dist/{chunk-EKY57CSP.js → chunk-5H3GB5BO.js} +68 -772
- package/dist/{chunk-6XXKFVSN.js → chunk-5Q2VVUKB.js} +4 -4
- package/dist/{chunk-DJVECN66.js → chunk-7RFXX52T.js} +295 -3705
- package/dist/{chunk-ZXF4XRKW.js → chunk-7RGZWPB6.js} +158 -8
- package/dist/{chunk-FYYLNIL5.js → chunk-A2NJGIB3.js} +2 -2
- package/dist/{chunk-XXQNGV4M.js → chunk-A3WNZD3P.js} +548 -72
- package/dist/{chunk-VEZEGCGW.js → chunk-B5CSFE7B.js} +23 -21
- package/dist/{chunk-2SFS6XQE.js → chunk-DGSYXYMX.js} +3 -2
- package/dist/chunk-DR52UMZW.js +21 -0
- package/dist/{chunk-ZWLZP4ZT.js → chunk-DYIM5TJT.js} +98 -12
- package/dist/{chunk-QKMUKYO7.js → chunk-E77JEWSD.js} +270 -125
- package/dist/{chunk-LYF7OHWH.js → chunk-EMYUUSFG.js} +12 -467
- package/dist/{chunk-4VP4KH3K.js → chunk-EQ63NRB7.js} +8 -8
- package/dist/chunk-FBVTI2TJ.js +518 -0
- package/dist/{chunk-R46L2BIR.js → chunk-FHJEP5SW.js} +19 -25
- package/dist/{chunk-WR67VIZY.js → chunk-GN57SG4G.js} +66 -8
- package/dist/{chunk-RD5U66HV.js → chunk-GPIEI3LY.js} +9 -9
- package/dist/{chunk-PCZJO5TI.js → chunk-GU2UIAFZ.js} +13 -178
- package/dist/chunk-GWS3VEIW.js +195 -0
- package/dist/chunk-H7IXIC72.js +103 -0
- package/dist/{chunk-3BPUFZDL.js → chunk-HLE42MG7.js} +3 -3
- package/dist/{chunk-24I7BN55.js → chunk-IFBNV6H6.js} +3 -3
- package/dist/{chunk-QNQHSOLF.js → chunk-IGWKHNIQ.js} +114 -55
- package/dist/chunk-IIZWH4XA.js +172 -0
- package/dist/{chunk-AD2SYQYC.js → chunk-IJ7RPIYJ.js} +124 -6
- package/dist/chunk-J3YUBZWY.js +382 -0
- package/dist/{chunk-5JGRAMKL.js → chunk-JOZYP4GM.js} +8 -6
- package/dist/{chunk-OH3TOQTB.js → chunk-K4XHGFR5.js} +751 -15
- package/dist/{chunk-EYPA3EGJ.js → chunk-KTYTFRMB.js} +178 -14
- package/dist/chunk-LU7P4LHA.js +33 -0
- package/dist/chunk-ME6CCNFO.js +108 -0
- package/dist/chunk-MXKJU4JB.js +1100 -0
- package/dist/{chunk-RY3LY4J5.js → chunk-N22QMJKY.js} +21 -18
- package/dist/chunk-NMPKI6XL.js +3006 -0
- package/dist/chunk-NUGM5KR6.js +165 -0
- package/dist/{chunk-22NAGB7X.js → chunk-P43ETTHK.js} +5 -94
- package/dist/{chunk-NILBFAPG.js → chunk-PMDBGQSJ.js} +2 -2
- package/dist/chunk-PT7HYKEM.js +165 -0
- package/dist/chunk-RVG5JXAL.js +41 -0
- package/dist/{chunk-GEYXPTRF.js → chunk-RWSI4YD7.js} +2 -2
- package/dist/{chunk-4BPJXDWC.js → chunk-TANS5ZJS.js} +35 -19
- package/dist/{verifiers-NCBTHHN2.js → chunk-TB5666IT.js} +67 -324
- package/dist/{chunk-QM3F2GKX.js → chunk-TLQJPP24.js} +7927 -8088
- package/dist/{chunk-G7MUEIGA.js → chunk-TT36MB5S.js} +2 -1
- package/dist/chunk-TTHACPOM.js +961 -0
- package/dist/{chunk-6US73PDB.js → chunk-TYPGUK6W.js} +7 -7
- package/dist/{chunk-MFFY33HR.js → chunk-U6MBIEMB.js} +554 -209
- package/dist/chunk-VAWNZU7Z.js +242 -0
- package/dist/{chunk-IR4CFBFN.js → chunk-VCBR6CU7.js} +12 -12
- package/dist/{chunk-WHJYKASB.js → chunk-VHN4MY6O.js} +2 -2
- package/dist/{chunk-XYDYPRZI.js → chunk-VWZOAB7K.js} +10 -10
- package/dist/{chunk-ZRGEBJ4T.js → chunk-WLFILSD5.js} +48 -48
- package/dist/{chunk-K7T3E2SR.js → chunk-WNIJTQQK.js} +12 -11
- package/dist/{chunk-CYQKWTG3.js → chunk-WSB3FPX7.js} +68 -20
- package/dist/{chunk-KHSFENX2.js → chunk-WWCZ5F23.js} +116 -14
- package/dist/{chunk-XE2VEJHX.js → chunk-WXY7KU3G.js} +2 -2
- package/dist/{chunk-PPAMZ32Z.js → chunk-XK56QHLX.js} +6 -1
- package/dist/{chunk-KOHPCX4K.js → chunk-XWSF374K.js} +5 -5
- package/dist/{chunk-CJUB2JJ2.js → chunk-YS5VLNH5.js} +10 -10
- package/dist/{chunk-ZZMN5OM4.js → chunk-ZNLWCMVZ.js} +2 -2
- package/dist/{chunk-WHGPSPT5.js → chunk-ZVJ5BLO2.js} +2 -2
- package/dist/cli/index.js +33 -31
- package/dist/{clio-M2KGYUFZ.js → clio-QVTYJ57A.js} +10 -10
- package/dist/{code-nav-GQNL7XA6.js → code-nav-FGGFIE7L.js} +5 -5
- package/dist/codewiki/build-worker.js +4 -4
- package/dist/{components-5TTYYX6G.js → components-ZFA3SAER.js} +9 -9
- package/dist/{config-XUUYQIWO.js → config-LW5IJFQN.js} +65 -55
- package/dist/{configure-IHJ7YOMV.js → configure-7XIZCOU4.js} +29 -24
- package/dist/{context-75MIWW3U.js → context-L3WL3X7K.js} +54 -43
- package/dist/{context-ZQ7SIFJV.js → context-N52ZA626.js} +30 -14
- package/dist/{context-74JLXAWD.js → context-Y6Y7QPR6.js} +12 -12
- package/dist/{context-clear-GYKWNUML.js → context-clear-MBQRLSDQ.js} +54 -43
- package/dist/{context-index-SSR5ECNE.js → context-index-HVMFQHK3.js} +5 -5
- package/dist/{context-working-set-UX5KEP4J.js → context-working-set-GS6DSO7F.js} +18 -18
- package/dist/{dispatch-runner-GIJBHNFL.js → dispatch-runner-22ZCNOM3.js} +367 -79
- package/dist/{docs-6FZSCG5B.js → docs-7LQ23DLM.js} +9 -9
- package/dist/{doctor-SVJ5BZCW.js → doctor-M7YEDGAE.js} +27 -23
- package/dist/{eval-CG6LLBLD.js → eval-BEC2WHDA.js} +73 -20
- package/dist/{evidence-ZYFIEN42.js → evidence-REJUMSKM.js} +70 -59
- package/dist/{evolve-QGEXEMDW.js → evolve-PY5ZBA5K.js} +50 -39
- package/dist/{extensions-ADGNCJJD.js → extensions-HVKU65YU.js} +7 -7
- package/dist/{fleet-S5R4ZOQY.js → fleet-7WZEWRFA.js} +236 -377
- package/dist/fleet-commands-UVHWM76J.js +70 -0
- package/dist/fleet-graph-6ULH7PES.js +125 -0
- package/dist/fleet-new-RDVJLHHH.js +48 -0
- package/dist/{fleet-preflight-BHSNPBMH.js → fleet-preflight-J53T6CCE.js} +5 -5
- package/dist/fleet-validate-72PC4SLA.js +79 -0
- package/dist/{init-5DRU55YR.js → init-OG3TPGQG.js} +69 -57
- package/dist/library-CNTMPLRF.js +217 -0
- package/dist/{memory-7YKKR6UC.js → memory-6IS7F275.js} +52 -41
- package/dist/{models-ZPOLRU2C.js → models-ENRJDA5W.js} +37 -31
- package/dist/{monitor-US5F5YGZ.js → monitor-XLDVO7TN.js} +79 -42
- package/dist/{orchestrator-E2AL4T5N.js → orchestrator-6KSPYRHA.js} +4126 -762
- package/dist/{paths-E7KYAQWE.js → paths-DBXMZMDU.js} +6 -6
- package/dist/registry-LG64LTF4.js +11 -0
- package/dist/{reset-KZ652EK6.js → reset-RZ4ER727.js} +12 -12
- package/dist/{run-SRNBKDWD.js → run-Y2CNK5RU.js} +89 -76
- package/dist/{share-CGZE33UP.js → share-A55GYP6Z.js} +37 -13
- package/dist/{skills-S2X4DLY5.js → skills-ALC5J6AT.js} +29 -13
- package/dist/{skills-eval-W2GGIC4R.js → skills-eval-JPBEBYQU.js} +60 -48
- package/dist/support-MIETYA5E.js +38 -0
- package/dist/{targets-54SWINWB.js → targets-VGNXIR3S.js} +46 -36
- package/dist/{terminal-lease-SAIF2OGY.js → terminal-lease-WOBR64YA.js} +4 -4
- package/dist/{uninstall-BVLWXKBT.js → uninstall-ZJF5H5ZN.js} +9 -9
- package/dist/{upgrade-JKAR27XC.js → upgrade-FUSUAGHR.js} +31 -28
- package/dist/{usage-MSAWCLX4.js → usage-N4MKVHKD.js} +140 -68
- package/dist/verifiers-YAWOJ3H2.js +336 -0
- package/dist/{verify-X5HDROLA.js → verify-LTDHYBGY.js} +10 -9
- package/dist/{wiki-generate-GUSOQ6ZP.js → wiki-generate-6M7GHTBJ.js} +75 -62
- package/dist/worker/entry.js +67 -57
- 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 +11 -6
- package/docs/built-in-agents.md +26 -2
- package/docs/capacity-and-scheduling.md +1 -1
- package/docs/commands-and-modes.md +83 -3
- package/docs/configuration-and-targets.md +86 -4
- 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 +74 -10
- 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 +228 -16
- package/docs/git-commit-provenance.md +2 -2
- package/docs/glossary.md +22 -2
- 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 +56 -9
- package/docs/proactive-memory.md +1 -1
- package/docs/prompt-envelope-and-tools.md +4 -2
- package/docs/provider-adapter-cookbook.md +1 -1
- package/docs/release-cut-checklist.md +83 -65
- 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 -2
- package/src/cli/argv.ts +5 -0
- package/src/cli/configure.ts +107 -23
- package/src/cli/doctor.ts +5 -1
- package/src/cli/evidence.ts +30 -25
- 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 +111 -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/shared.ts +1 -0
- package/src/cli/targets.ts +4 -1
- package/src/cli/usage.ts +111 -19
- package/src/cli/validate-model.ts +60 -5
- package/src/core/bus-events.ts +29 -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/core/path-boundary.ts +100 -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/extension.ts +2 -11
- package/src/domains/agents/fleet-contract.ts +304 -24
- package/src/domains/agents/index.ts +14 -0
- package/src/domains/agents/recipe.ts +7 -1
- package/src/domains/agents/registry.ts +73 -5
- package/src/domains/agents/result-contract.ts +360 -15
- package/src/domains/agents/write-boundary.ts +15 -50
- package/src/domains/config/classify.ts +4 -0
- package/src/domains/context/project-rules.ts +51 -1
- package/src/domains/dispatch/active-route-planner.ts +14 -0
- package/src/domains/dispatch/assignment-reconcile.ts +22 -5
- package/src/domains/dispatch/assignment-store.ts +151 -14
- 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 +48 -0
- package/src/domains/dispatch/delegation-plan.ts +164 -0
- package/src/domains/dispatch/execution-plan.ts +76 -5
- package/src/domains/dispatch/execution-role.ts +12 -2
- package/src/domains/dispatch/execution-scheduler.ts +183 -67
- package/src/domains/dispatch/extension.ts +412 -74
- 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 +791 -0
- package/src/domains/dispatch/gate-role-prompts.ts +47 -0
- package/src/domains/dispatch/host-verification.ts +178 -0
- package/src/domains/dispatch/index.ts +41 -1
- package/src/domains/dispatch/intent-requirements.ts +40 -0
- package/src/domains/dispatch/intent.ts +235 -0
- package/src/domains/dispatch/path-scope.ts +370 -0
- package/src/domains/dispatch/receipt-integrity.ts +9 -4
- package/src/domains/dispatch/state.ts +36 -3
- package/src/domains/dispatch/types.ts +64 -12
- package/src/domains/dispatch/validation.ts +69 -6
- package/src/domains/dispatch/write-boundary-enforcer.ts +45 -0
- package/src/domains/dispatch/write-boundary.ts +201 -22
- package/src/domains/eval/metrics/evidence.ts +79 -2
- package/src/domains/eval/runners/clio-run.ts +12 -2
- package/src/domains/evidence/build.ts +69 -11
- package/src/domains/evidence/index.ts +21 -0
- package/src/domains/evidence/provenance.ts +46 -11
- package/src/domains/evidence/trust-projection.ts +274 -0
- package/src/domains/evidence/trust-status.ts +155 -18
- package/src/domains/evidence/types.ts +4 -0
- package/src/domains/extensions/discovery.ts +88 -1
- package/src/domains/extensions/resources.ts +20 -8
- package/src/domains/extensions/state.ts +6 -2
- package/src/domains/extensions/types.ts +4 -1
- package/src/domains/lifecycle/doctor.ts +140 -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/prompts/contract.ts +3 -5
- package/src/domains/providers/extension.ts +30 -2
- package/src/domains/resources/common-loader.ts +3 -0
- package/src/domains/resources/index.ts +20 -0
- package/src/domains/resources/library.ts +326 -0
- package/src/domains/resources/prompts/loader.ts +119 -14
- package/src/domains/resources/skills/marketplace.ts +37 -12
- package/src/domains/safety/policy-engine.ts +5 -5
- package/src/domains/safety/run-effects.ts +64 -1
- package/src/domains/safety/scope.ts +7 -12
- package/src/domains/session/handoff.ts +629 -0
- package/src/domains/share/archive.ts +67 -2
- package/src/engine/acp/server.ts +4 -1
- package/src/engine/prompt-templates.ts +18 -1
- package/src/engine/worker-runtime.ts +6 -3
- 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 +311 -17
- 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 +23 -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 +221 -30
- 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 +439 -7
- 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/view/artifacts.ts +42 -9
- package/src/interactive/view/view-overlay.ts +15 -3
- package/src/interactive/watchdog-run.ts +75 -0
- package/src/interactive/worker-receipts.ts +14 -2
- package/src/interactive/worker-share.ts +56 -1
- package/src/interactive/worker-stream.ts +15 -0
- package/src/tools/bootstrap.ts +3 -0
- package/src/tools/compete-worktrees.ts +13 -79
- package/src/tools/dispatch-admission.ts +251 -18
- package/src/tools/dispatch-arguments.ts +84 -1
- package/src/tools/dispatch-plan.ts +165 -6
- package/src/tools/dispatch-runner.ts +364 -23
- package/src/tools/dispatch-types.ts +20 -1
- package/src/tools/dispatch.ts +72 -2
- package/src/tools/monitor.ts +29 -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 +21 -14
- package/src/worker/spec-contract.ts +3 -1
- package/dist/chunk-HC4CLZ2Y.js +0 -68
|
@@ -24,6 +24,11 @@ import {
|
|
|
24
24
|
type RunStatus,
|
|
25
25
|
runKindSupportsLiveSteering,
|
|
26
26
|
} from "../domains/dispatch/types.js";
|
|
27
|
+
import {
|
|
28
|
+
summarizeTrustStatus,
|
|
29
|
+
type TrustSummaryProjection,
|
|
30
|
+
type TrustVerdict,
|
|
31
|
+
} from "../domains/evidence/trust-projection.js";
|
|
27
32
|
import {
|
|
28
33
|
COST_NOT_MEASURED,
|
|
29
34
|
costAggregateForAmount,
|
|
@@ -35,6 +40,13 @@ import type { CostProvenance } from "../domains/providers/index.js";
|
|
|
35
40
|
import { sanitizeCallTargetText } from "../domains/safety/call-target.js";
|
|
36
41
|
import { type Component, truncateToWidth, visibleWidth, wrapTextWithAnsi } from "../engine/tui.js";
|
|
37
42
|
import { formatWorkerContextMeter } from "./context-meter.js";
|
|
43
|
+
import {
|
|
44
|
+
COUNCIL_SYNTHESIS_LABEL,
|
|
45
|
+
type CouncilGroupView,
|
|
46
|
+
type CouncilMemberView,
|
|
47
|
+
councilGroupBody,
|
|
48
|
+
councilIslandLines,
|
|
49
|
+
} from "./council-grid.js";
|
|
38
50
|
import { formatFooterTokens } from "./footer-panel.js";
|
|
39
51
|
import {
|
|
40
52
|
type ClioTheme,
|
|
@@ -103,6 +115,16 @@ export interface DispatchBoardRow {
|
|
|
103
115
|
node?: string;
|
|
104
116
|
/** Review/compete gate badge (role + cycle). */
|
|
105
117
|
gate?: { role: string; cycle: number };
|
|
118
|
+
council?: { group: string; label: string; color?: string; round: number };
|
|
119
|
+
/** Orchestrator-executed declared verification status. */
|
|
120
|
+
hostVerification?: "verified" | "rejected" | "skipped";
|
|
121
|
+
/**
|
|
122
|
+
* The canonical trust projection of the sealed receipt, read back from
|
|
123
|
+
* disk and authenticated against the ledger row. Absent while the run is
|
|
124
|
+
* live or when the receipt could not be read back, so the card never
|
|
125
|
+
* implies a verdict the terminal bus event cannot carry.
|
|
126
|
+
*/
|
|
127
|
+
trust?: TrustSummaryProjection;
|
|
106
128
|
/** Dead-node failover hops recorded on this run's chain. */
|
|
107
129
|
rerouteCount?: number;
|
|
108
130
|
/** Assignment retry attempts observed on the assignment event stream. */
|
|
@@ -134,8 +156,39 @@ export interface DispatchBoardRow {
|
|
|
134
156
|
retry?: DispatchRetryPresentation;
|
|
135
157
|
/** Most recent worker delivery acknowledgement for an operator steer. */
|
|
136
158
|
steerAcknowledgement?: DispatchSteerAcknowledgement;
|
|
159
|
+
/**
|
|
160
|
+
* Where this run sits in a fleet plan: the wave the step was scheduled in
|
|
161
|
+
* and the step id it executes. Present only for runs a fleet plan
|
|
162
|
+
* dispatched, so a `/run`, a delegation, or a dispatch-tool run leaves the
|
|
163
|
+
* board's phase column empty rather than claiming a position it has none of.
|
|
164
|
+
*/
|
|
165
|
+
phase?: DispatchBoardPhase;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/** One fleet plan position: the wave index and the step the run executes. */
|
|
169
|
+
export interface DispatchBoardPhase {
|
|
170
|
+
wave: number;
|
|
171
|
+
stepId: string;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* The phase cell, or null for a run that is not a fleet step. Fixed width so
|
|
176
|
+
* the column lines up across rows; a long step id is clipped with a marker
|
|
177
|
+
* rather than pushing the row's other facts out of position.
|
|
178
|
+
*/
|
|
179
|
+
export function formatDispatchPhaseCell(
|
|
180
|
+
phase: DispatchBoardPhase | undefined,
|
|
181
|
+
width = PHASE_COLUMN_WIDTH,
|
|
182
|
+
): string | null {
|
|
183
|
+
if (phase === undefined) return null;
|
|
184
|
+
return truncateToWidth(`w${phase.wave} ${phase.stepId}`, Math.max(1, width), "…", false);
|
|
137
185
|
}
|
|
138
186
|
|
|
187
|
+
/** Columns the compact island reserves for the phase cell when it can afford them. */
|
|
188
|
+
export const PHASE_COLUMN_WIDTH = 12;
|
|
189
|
+
/** Below this much room for the agent label, the compact row drops the column entirely. */
|
|
190
|
+
const PHASE_COLUMN_MIN_LABEL_WIDTH = 8;
|
|
191
|
+
|
|
139
192
|
/** Live HTTP/SDK rows are the only rows whose runtime can consume operator guidance. */
|
|
140
193
|
export function isDispatchBoardRowSteerable(row: DispatchBoardRow): boolean {
|
|
141
194
|
return (
|
|
@@ -610,14 +663,34 @@ export function renderDispatchCard(
|
|
|
610
663
|
`${theme.fg("dim", "ttft")} ${theme.fg("muted", ttft)}`,
|
|
611
664
|
`${theme.fg("dim", "cost")} ${theme.fg("muted", cost)}`,
|
|
612
665
|
];
|
|
666
|
+
// The trust line is the canonical projection, styled by its verdict tier;
|
|
667
|
+
// a host check is folded into that projection as validation grounding, so
|
|
668
|
+
// it is named as a secondary fact and never as independent verification.
|
|
669
|
+
const trustUnits = [
|
|
670
|
+
row.trust === undefined
|
|
671
|
+
? theme.fg("dim", isTerminalStatus(row.status) ? "trust: receipt not read back" : "trust: not sealed yet")
|
|
672
|
+
: theme.fg(trustVerdictToken(row.trust.verdict), `${trustVerdictGlyph(row.trust.verdict)} ${row.trust.text}`),
|
|
673
|
+
...(row.hostVerification !== undefined ? [theme.fg("muted", `host checks ${row.hostVerification}`)] : []),
|
|
674
|
+
];
|
|
613
675
|
const contextUnit = formatWorkerContextMeter(row.lastContextTokens ?? 0, row.contextWindow, theme);
|
|
676
|
+
// The phase column: a fleet step says which wave it belongs to and which
|
|
677
|
+
// step it is; every other run leaves the cell empty rather than inventing a
|
|
678
|
+
// position, so the column reads as "not a fleet step" and not as wave zero.
|
|
679
|
+
const phaseCell = formatDispatchPhaseCell(row.phase, contentWidth);
|
|
614
680
|
const bodyLines = [
|
|
615
681
|
cardUnitsLine(theme, "run", [theme.fg("dim", row.runId)], contentWidth),
|
|
682
|
+
cardUnitsLine(
|
|
683
|
+
theme,
|
|
684
|
+
"phase",
|
|
685
|
+
[phaseCell === null ? theme.fg("dim", "—") : theme.fg("info", phaseCell)],
|
|
686
|
+
contentWidth,
|
|
687
|
+
),
|
|
616
688
|
targetLine,
|
|
617
689
|
...(row.taskSummary
|
|
618
690
|
? [truncateToWidth(`${cardKvKey(theme, "task")}${theme.fg("muted", row.taskSummary)}`, contentWidth, "…", false)]
|
|
619
691
|
: []),
|
|
620
692
|
cardUnitsLine(theme, "status", statusUnits, contentWidth),
|
|
693
|
+
cardUnitsLine(theme, "trust", trustUnits, contentWidth),
|
|
621
694
|
...(row.budget !== undefined
|
|
622
695
|
? [
|
|
623
696
|
truncateToWidth(
|
|
@@ -715,11 +788,25 @@ function renderTaskIslandRow(row: DispatchBoardRow, width: number): string[] {
|
|
|
715
788
|
visibleWidth(presentation.label) +
|
|
716
789
|
3 +
|
|
717
790
|
visibleWidth(elapsed);
|
|
718
|
-
|
|
791
|
+
// The phase column rides the compact row only when the row can still show a
|
|
792
|
+
// readable agent label beside it. TASK_ISLAND_WIDTH is fixed, so a row that
|
|
793
|
+
// cannot afford the cell drops the column here and shows the phase in the
|
|
794
|
+
// expanded card instead of clipping the label to make room.
|
|
795
|
+
const phaseCell = formatDispatchPhaseCell(row.phase);
|
|
796
|
+
const phaseColumn = phaseCell === null ? "" : `${dot}${theme.fg("info", padAnsi(phaseCell, PHASE_COLUMN_WIDTH))}`;
|
|
797
|
+
const phaseChrome = phaseCell === null ? 0 : PHASE_COLUMN_WIDTH + 3;
|
|
798
|
+
const labelWidth = width - labelChrome - phaseChrome;
|
|
799
|
+
const showPhase = phaseCell !== null && labelWidth >= PHASE_COLUMN_MIN_LABEL_WIDTH;
|
|
800
|
+
const clampedLabel = truncateToWidth(
|
|
801
|
+
agentLabel,
|
|
802
|
+
Math.max(1, showPhase ? labelWidth : width - labelChrome),
|
|
803
|
+
"…",
|
|
804
|
+
false,
|
|
805
|
+
);
|
|
719
806
|
|
|
720
807
|
// The agent label drops its accent color for plain bold; the status word is
|
|
721
808
|
// 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)}`;
|
|
809
|
+
const line1 = `${glyph} ${rowPrefix.text}${theme.paint(clampedLabel, { bold: true })}${dot}${statusStr}${dot}${theme.fg("muted", elapsed)}${showPhase ? phaseColumn : ""}`;
|
|
723
810
|
|
|
724
811
|
const elapsedSec = row.elapsedMs / 1000;
|
|
725
812
|
const tokensPerSec = elapsedSec > 0.1 ? Math.round(row.outputTokens / elapsedSec) : 0;
|
|
@@ -740,6 +827,131 @@ function renderTaskIslandRow(row: DispatchBoardRow, width: number): string[] {
|
|
|
740
827
|
return [padAnsi(line1, width), ...(task ? [padAnsi(task, width)] : []), padAnsi(telemetry, width)];
|
|
741
828
|
}
|
|
742
829
|
|
|
830
|
+
/**
|
|
831
|
+
* One member column's facts, drawn from the row the board already keeps. The
|
|
832
|
+
* route, status, and answer tail are the same values the run's own card shows,
|
|
833
|
+
* so a council column and a council card can never disagree.
|
|
834
|
+
*/
|
|
835
|
+
function councilMemberView(
|
|
836
|
+
row: DispatchBoardRow,
|
|
837
|
+
council: NonNullable<DispatchBoardRow["council"]>,
|
|
838
|
+
): CouncilMemberView {
|
|
839
|
+
const presentation = dispatchStatusPresentation(row.status, {
|
|
840
|
+
compact: true,
|
|
841
|
+
...(row.status === "running" ? { tick: Math.floor(Date.now() / 100) } : {}),
|
|
842
|
+
});
|
|
843
|
+
return {
|
|
844
|
+
runId: row.runId,
|
|
845
|
+
label: council.label,
|
|
846
|
+
...(council.color !== undefined ? { color: council.color } : {}),
|
|
847
|
+
round: council.round,
|
|
848
|
+
route: `${row.targetId}/${row.wireModelId}`,
|
|
849
|
+
status: { glyph: presentation.glyph, label: presentation.label, token: presentation.token },
|
|
850
|
+
tailText: sanitizeCallTargetText(row.progress?.tailText ?? ""),
|
|
851
|
+
droppedLines: row.progress?.droppedLines ?? 0,
|
|
852
|
+
};
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
/**
|
|
856
|
+
* Fold one council group's rows into the view the grid renders.
|
|
857
|
+
*
|
|
858
|
+
* A council of N members over R rounds seals N x R member runs, and the
|
|
859
|
+
* operator is looking at a council, not at its history: each label keeps its
|
|
860
|
+
* newest round, so the group shows one column per member however many rounds
|
|
861
|
+
* it ran. Columns are ordered by label so a repaint cannot reshuffle them under
|
|
862
|
+
* the cursor while runs settle in whatever order they finish.
|
|
863
|
+
*/
|
|
864
|
+
function foldCouncilGroup(
|
|
865
|
+
group: string,
|
|
866
|
+
rows: ReadonlyArray<DispatchBoardRow>,
|
|
867
|
+
selectedRunId?: string | null,
|
|
868
|
+
): CouncilGroupView {
|
|
869
|
+
const newest = new Map<string, DispatchBoardRow>();
|
|
870
|
+
let synthesisRow: DispatchBoardRow | null = null;
|
|
871
|
+
let rank = Number.POSITIVE_INFINITY;
|
|
872
|
+
let elapsedMs = 0;
|
|
873
|
+
let round = 1;
|
|
874
|
+
for (const row of rows) {
|
|
875
|
+
const council = row.council;
|
|
876
|
+
if (council === undefined) continue;
|
|
877
|
+
rank = Math.min(rank, STATUS_ORDER[row.status]);
|
|
878
|
+
elapsedMs = Math.max(elapsedMs, row.elapsedMs);
|
|
879
|
+
if (council.label === COUNCIL_SYNTHESIS_LABEL) {
|
|
880
|
+
synthesisRow = row;
|
|
881
|
+
continue;
|
|
882
|
+
}
|
|
883
|
+
round = Math.max(round, council.round);
|
|
884
|
+
const previous = newest.get(council.label);
|
|
885
|
+
if (previous === undefined || (previous.council?.round ?? 0) <= council.round) newest.set(council.label, row);
|
|
886
|
+
}
|
|
887
|
+
const aggregate = (Object.entries(STATUS_ORDER) as Array<[DispatchBoardStatus, number]>).find(
|
|
888
|
+
([, value]) => value === rank,
|
|
889
|
+
);
|
|
890
|
+
const presentation = dispatchStatusPresentation(aggregate?.[0] ?? "running", {
|
|
891
|
+
compact: true,
|
|
892
|
+
...(aggregate?.[0] === "running" ? { tick: Math.floor(Date.now() / 100) } : {}),
|
|
893
|
+
});
|
|
894
|
+
const members = [...newest.entries()]
|
|
895
|
+
.sort(([left], [right]) => left.localeCompare(right))
|
|
896
|
+
// biome-ignore lint/style/noNonNullAssertion: every entry was inserted with its own council badge.
|
|
897
|
+
.map(([, row]) => councilMemberView(row, row.council!));
|
|
898
|
+
const selectedInGroup =
|
|
899
|
+
selectedRunId != null && rows.some((row) => row.runId === selectedRunId) ? selectedRunId : undefined;
|
|
900
|
+
return {
|
|
901
|
+
group,
|
|
902
|
+
members,
|
|
903
|
+
// biome-ignore lint/style/noNonNullAssertion: the synthesis row was matched on its own council badge.
|
|
904
|
+
synthesis: synthesisRow === null ? null : councilMemberView(synthesisRow, synthesisRow.council!),
|
|
905
|
+
status: { glyph: presentation.glyph, label: presentation.label, token: presentation.token },
|
|
906
|
+
round,
|
|
907
|
+
elapsed: formatCompactMs(elapsedMs),
|
|
908
|
+
...(selectedInGroup !== undefined ? { selectedRunId: selectedInGroup } : {}),
|
|
909
|
+
};
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
/** A board entry: either one ordinary run, or one whole council in the position of its first row. */
|
|
913
|
+
export type DispatchBoardItem = { kind: "run"; row: DispatchBoardRow } | { kind: "council"; group: CouncilGroupView };
|
|
914
|
+
|
|
915
|
+
/**
|
|
916
|
+
* Board order with councils folded in place. A council occupies the position of
|
|
917
|
+
* its first row and its remaining rows are consumed, so the board never shows a
|
|
918
|
+
* member both inside its group and beside it.
|
|
919
|
+
*/
|
|
920
|
+
export function dispatchBoardItems(
|
|
921
|
+
rows: ReadonlyArray<DispatchBoardRow>,
|
|
922
|
+
selectedRunId?: string | null,
|
|
923
|
+
): DispatchBoardItem[] {
|
|
924
|
+
const byGroup = new Map<string, DispatchBoardRow[]>();
|
|
925
|
+
for (const row of rows) {
|
|
926
|
+
const group = row.council?.group;
|
|
927
|
+
if (group === undefined) continue;
|
|
928
|
+
const bucket = byGroup.get(group);
|
|
929
|
+
if (bucket === undefined) byGroup.set(group, [row]);
|
|
930
|
+
else bucket.push(row);
|
|
931
|
+
}
|
|
932
|
+
const emitted = new Set<string>();
|
|
933
|
+
const items: DispatchBoardItem[] = [];
|
|
934
|
+
for (const row of rows) {
|
|
935
|
+
const group = row.council?.group;
|
|
936
|
+
if (group === undefined) {
|
|
937
|
+
items.push({ kind: "run", row });
|
|
938
|
+
continue;
|
|
939
|
+
}
|
|
940
|
+
if (emitted.has(group)) continue;
|
|
941
|
+
emitted.add(group);
|
|
942
|
+
items.push({ kind: "council", group: foldCouncilGroup(group, byGroup.get(group) ?? [row], selectedRunId) });
|
|
943
|
+
}
|
|
944
|
+
return items;
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
/** The framed council card: the member grid or stack, with the synthesis run full width under it. */
|
|
948
|
+
export function renderCouncilCard(group: CouncilGroupView, width: number): string[] {
|
|
949
|
+
const theme = clioTheme();
|
|
950
|
+
const contentWidth = Math.max(0, width - 4);
|
|
951
|
+
const title = `${theme.fg("info", "council")} ${theme.paint(group.group, { bold: true })}`;
|
|
952
|
+
return frame(theme, title, councilGroupBody(theme, group, contentWidth), width, { rightMeta: group.elapsed });
|
|
953
|
+
}
|
|
954
|
+
|
|
743
955
|
export function formatDispatchBoardLines(
|
|
744
956
|
rows: ReadonlyArray<DispatchBoardRow>,
|
|
745
957
|
width = 76,
|
|
@@ -757,11 +969,15 @@ export function formatDispatchBoardLines(
|
|
|
757
969
|
});
|
|
758
970
|
}
|
|
759
971
|
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
972
|
+
// A council is one question asked of several members, so its rows render as
|
|
973
|
+
// one card holding the whole group rather than as unrelated neighbours.
|
|
974
|
+
const cards = dispatchBoardItems(rows, selectedRunId).map((item) =>
|
|
975
|
+
item.kind === "council"
|
|
976
|
+
? renderCouncilCard(item.group, width)
|
|
977
|
+
: renderDispatchCard(item.row, width, deriveRunEvidenceState(observability, item.row.runId), {
|
|
978
|
+
selected: item.row.runId === selectedRunId,
|
|
979
|
+
expanded: detailExpanded && item.row.runId === selectedRunId,
|
|
980
|
+
}),
|
|
765
981
|
);
|
|
766
982
|
const body: string[] = [];
|
|
767
983
|
for (const card of cards) {
|
|
@@ -850,23 +1066,30 @@ export function createDispatchBoardView(
|
|
|
850
1066
|
}
|
|
851
1067
|
|
|
852
1068
|
export function formatTaskIslandLines(rows: ReadonlyArray<DispatchBoardRow>, maxRows = 4): string[] {
|
|
853
|
-
|
|
1069
|
+
// Councils are folded before the row cap, so a five-member council costs the
|
|
1070
|
+
// island one card and never crowds out the runs beside it.
|
|
1071
|
+
const items = dispatchBoardItems(rows);
|
|
1072
|
+
const visibleItems = items.slice(0, Math.max(1, maxRows));
|
|
854
1073
|
const body: string[] = [];
|
|
855
1074
|
|
|
856
|
-
if (
|
|
1075
|
+
if (visibleItems.length === 0) {
|
|
857
1076
|
const theme = clioTheme();
|
|
858
1077
|
body.push(theme.fg("dim", "No active fleet runs."));
|
|
859
1078
|
body.push(theme.fg("dim", "Use /run or /delegate to spawn agents."));
|
|
860
1079
|
} else {
|
|
861
|
-
for (let i = 0; i <
|
|
862
|
-
const
|
|
863
|
-
if (!
|
|
1080
|
+
for (let i = 0; i < visibleItems.length; i++) {
|
|
1081
|
+
const item = visibleItems[i];
|
|
1082
|
+
if (!item) continue;
|
|
864
1083
|
if (i > 0) {
|
|
865
1084
|
body.push(innerDivider(clioTheme(), TASK_ISLAND_WIDTH));
|
|
866
1085
|
}
|
|
867
|
-
body.push(
|
|
1086
|
+
body.push(
|
|
1087
|
+
...(item.kind === "council"
|
|
1088
|
+
? councilIslandLines(clioTheme(), item.group, TASK_ISLAND_WIDTH)
|
|
1089
|
+
: renderTaskIslandRow(item.row, TASK_ISLAND_WIDTH)),
|
|
1090
|
+
);
|
|
868
1091
|
}
|
|
869
|
-
const hidden =
|
|
1092
|
+
const hidden = items.length - visibleItems.length;
|
|
870
1093
|
if (hidden > 0) {
|
|
871
1094
|
body.push(innerDivider(clioTheme(), TASK_ISLAND_WIDTH));
|
|
872
1095
|
body.push(clioTheme().fg("dim", `+ ${hidden} more`));
|
|
@@ -932,6 +1155,21 @@ function parseGateBadge(value: unknown): { role: string; cycle: number } | undef
|
|
|
932
1155
|
return { role: record.role, cycle };
|
|
933
1156
|
}
|
|
934
1157
|
|
|
1158
|
+
function parseCouncilBadge(value: unknown): DispatchBoardRow["council"] | undefined {
|
|
1159
|
+
if (typeof value !== "object" || value === null) return undefined;
|
|
1160
|
+
const record = value as { group?: unknown; label?: unknown; color?: unknown; round?: unknown };
|
|
1161
|
+
const group = parseNonEmptyString(record.group);
|
|
1162
|
+
const label = parseNonEmptyString(record.label);
|
|
1163
|
+
const round = parsePositiveInt(record.round);
|
|
1164
|
+
if (group === undefined || label === undefined || round === undefined) return undefined;
|
|
1165
|
+
return {
|
|
1166
|
+
group,
|
|
1167
|
+
label,
|
|
1168
|
+
...(typeof record.color === "string" && record.color.length > 0 ? { color: record.color } : {}),
|
|
1169
|
+
round,
|
|
1170
|
+
};
|
|
1171
|
+
}
|
|
1172
|
+
|
|
935
1173
|
function parseFiniteNumberOrZero(value: unknown): number {
|
|
936
1174
|
return typeof value === "number" && Number.isFinite(value) ? value : 0;
|
|
937
1175
|
}
|
|
@@ -942,6 +1180,33 @@ function parseOptionalDetail(value: unknown): string | null {
|
|
|
942
1180
|
return detail.length > 0 ? detail : null;
|
|
943
1181
|
}
|
|
944
1182
|
|
|
1183
|
+
/** Only an authenticated independent pass earns the success token; sealed alone is never green. */
|
|
1184
|
+
function trustVerdictToken(verdict: TrustVerdict): ClioToken {
|
|
1185
|
+
switch (verdict) {
|
|
1186
|
+
case "reviewed":
|
|
1187
|
+
return "success";
|
|
1188
|
+
case "grounded":
|
|
1189
|
+
return "info";
|
|
1190
|
+
case "unverified":
|
|
1191
|
+
return "muted";
|
|
1192
|
+
case "compromised":
|
|
1193
|
+
return "error";
|
|
1194
|
+
default:
|
|
1195
|
+
return "dim";
|
|
1196
|
+
}
|
|
1197
|
+
}
|
|
1198
|
+
|
|
1199
|
+
function trustVerdictGlyph(verdict: TrustVerdict): string {
|
|
1200
|
+
switch (verdict) {
|
|
1201
|
+
case "reviewed":
|
|
1202
|
+
return GLYPH.ok;
|
|
1203
|
+
case "compromised":
|
|
1204
|
+
return GLYPH.error;
|
|
1205
|
+
default:
|
|
1206
|
+
return "◇";
|
|
1207
|
+
}
|
|
1208
|
+
}
|
|
1209
|
+
|
|
945
1210
|
function terminalDetail(row: DispatchBoardRow): string | null {
|
|
946
1211
|
if (row.status !== "failed" && row.status !== "dead" && row.status !== "aborted") return null;
|
|
947
1212
|
return parseOptionalDetail(row.outcomeDetail);
|
|
@@ -1099,11 +1364,15 @@ function toRow(entry: DispatchBoardEntry, now: number): DispatchBoardRow {
|
|
|
1099
1364
|
...(entry.outcomeDetail !== undefined ? { outcomeDetail: entry.outcomeDetail } : {}),
|
|
1100
1365
|
...(entry.node !== undefined ? { node: entry.node } : {}),
|
|
1101
1366
|
...(entry.gate !== undefined ? { gate: { ...entry.gate } } : {}),
|
|
1367
|
+
...(entry.council !== undefined ? { council: { ...entry.council } } : {}),
|
|
1368
|
+
...(entry.hostVerification !== undefined ? { hostVerification: entry.hostVerification } : {}),
|
|
1369
|
+
...(entry.trust !== undefined ? { trust: entry.trust } : {}),
|
|
1102
1370
|
...(entry.rerouteCount !== undefined ? { rerouteCount: entry.rerouteCount } : {}),
|
|
1103
1371
|
...(entry.failoverHops !== undefined ? { failoverHops: entry.failoverHops } : {}),
|
|
1104
1372
|
...(entry.contextWindow !== undefined ? { contextWindow: entry.contextWindow } : {}),
|
|
1105
1373
|
lastContextTokens: entry.lastContextTokens,
|
|
1106
1374
|
...(entry.receiptId !== undefined ? { receiptId: entry.receiptId } : {}),
|
|
1375
|
+
...(entry.phase !== undefined ? { phase: { ...entry.phase } } : {}),
|
|
1107
1376
|
// Both tool fields are views of the one projection, so the compact card and
|
|
1108
1377
|
// the expanded detail can never disagree about what is running.
|
|
1109
1378
|
currentTool: retry ? null : (progress.currentAction?.tool ?? null),
|
|
@@ -1124,7 +1393,7 @@ function sortEntries(a: DispatchBoardEntry, b: DispatchBoardEntry): number {
|
|
|
1124
1393
|
return a.sequence - b.sequence;
|
|
1125
1394
|
}
|
|
1126
1395
|
|
|
1127
|
-
function pruneEntries(entries: Map<string, DispatchBoardEntry
|
|
1396
|
+
function pruneEntries(entries: Map<string, DispatchBoardEntry>, onEvict?: (runId: string) => void): void {
|
|
1128
1397
|
if (entries.size <= MAX_DISPATCH_BOARD_ROWS) return;
|
|
1129
1398
|
const terminalEntries = [...entries.values()]
|
|
1130
1399
|
.filter((entry) => !entry.retry && isTerminalStatus(entry.status))
|
|
@@ -1134,6 +1403,7 @@ function pruneEntries(entries: Map<string, DispatchBoardEntry>): void {
|
|
|
1134
1403
|
for (const entry of evictionQueue) {
|
|
1135
1404
|
if (entries.size <= MAX_DISPATCH_BOARD_ROWS) break;
|
|
1136
1405
|
entries.delete(entry.runId);
|
|
1406
|
+
onEvict?.(entry.runId);
|
|
1137
1407
|
}
|
|
1138
1408
|
}
|
|
1139
1409
|
|
|
@@ -1150,9 +1420,19 @@ export function createDispatchBoardStore(
|
|
|
1150
1420
|
rows(): ReadonlyArray<DispatchBoardRow>;
|
|
1151
1421
|
activeRows(): ReadonlyArray<DispatchBoardRow>;
|
|
1152
1422
|
reconcile(): void;
|
|
1423
|
+
/**
|
|
1424
|
+
* Record the fleet-plan position of a run the caller just dispatched. The
|
|
1425
|
+
* board never infers a position: a run carries one only because whoever
|
|
1426
|
+
* dispatched the plan said so, which is why a non-fleet run can never grow
|
|
1427
|
+
* a phase cell.
|
|
1428
|
+
*/
|
|
1429
|
+
setFleetPhase(runId: string, phase: DispatchBoardPhase): void;
|
|
1153
1430
|
unsubscribe(): void;
|
|
1154
1431
|
} {
|
|
1155
1432
|
const entries = new Map<string, DispatchBoardEntry>();
|
|
1433
|
+
// Positions arrive from the fleet-run path, which may hand one over before
|
|
1434
|
+
// the run's first lifecycle event creates the entry.
|
|
1435
|
+
const fleetPhases = new Map<string, DispatchBoardPhase>();
|
|
1156
1436
|
let nextSequence = 0;
|
|
1157
1437
|
let reconciledAtMs = Date.now();
|
|
1158
1438
|
|
|
@@ -1162,8 +1442,10 @@ export function createDispatchBoardStore(
|
|
|
1162
1442
|
* before it publishes one, so this is the first moment the file exists.
|
|
1163
1443
|
*/
|
|
1164
1444
|
const settleFromReceipt = (entry: DispatchBoardEntry): void => {
|
|
1165
|
-
const
|
|
1445
|
+
const facts = readReceipt?.(entry.runId);
|
|
1446
|
+
const text = facts?.text;
|
|
1166
1447
|
entry.progress.settle(typeof text === "string" && text.trim().length > 0 ? text : undefined);
|
|
1448
|
+
if (facts?.trust !== undefined) entry.trust = summarizeTrustStatus(facts.trust);
|
|
1167
1449
|
};
|
|
1168
1450
|
|
|
1169
1451
|
// Payloads arrive typed off the bus, but the board keeps its runtime
|
|
@@ -1180,10 +1462,12 @@ export function createDispatchBoardStore(
|
|
|
1180
1462
|
const requestOrigin = parseRequestOrigin(raw.requestOrigin, previous?.requestOrigin);
|
|
1181
1463
|
const node = parseNonEmptyString(raw.node) ?? previous?.node;
|
|
1182
1464
|
const gate = parseGateBadge(raw.gate) ?? previous?.gate;
|
|
1465
|
+
const council = parseCouncilBadge(raw.council) ?? previous?.council;
|
|
1183
1466
|
const rerouteCount = parsePositiveInt(raw.rerouteCount) ?? previous?.rerouteCount;
|
|
1184
1467
|
const contextWindow = parsePositiveInt(raw.contextWindow) ?? previous?.contextWindow;
|
|
1185
1468
|
const taskSummary = parseTaskSummary(raw, previous?.taskSummary);
|
|
1186
1469
|
const budget = cloneRunToolBudgetEnvelope(raw.budget) ?? previous?.budget;
|
|
1470
|
+
const phase = fleetPhases.get(runId) ?? previous?.phase;
|
|
1187
1471
|
const entry: DispatchBoardEntry = {
|
|
1188
1472
|
runId,
|
|
1189
1473
|
agentId: parseText(raw.agentId, previous?.agentId ?? "-"),
|
|
@@ -1211,16 +1495,19 @@ export function createDispatchBoardStore(
|
|
|
1211
1495
|
outcomeDetail: previous?.outcomeDetail ?? null,
|
|
1212
1496
|
...(node !== undefined ? { node } : {}),
|
|
1213
1497
|
...(gate !== undefined ? { gate } : {}),
|
|
1498
|
+
...(council !== undefined ? { council } : {}),
|
|
1499
|
+
...(previous?.hostVerification !== undefined ? { hostVerification: previous.hostVerification } : {}),
|
|
1214
1500
|
...(rerouteCount !== undefined ? { rerouteCount } : {}),
|
|
1215
1501
|
...(contextWindow !== undefined ? { contextWindow } : {}),
|
|
1216
1502
|
lastContextTokens: previous?.lastContextTokens ?? 0,
|
|
1217
1503
|
...(previous?.receiptId !== undefined ? { receiptId: previous.receiptId } : {}),
|
|
1504
|
+
...(phase !== undefined ? { phase } : {}),
|
|
1218
1505
|
progress: previous?.progress ?? createWorkerProgressFold(),
|
|
1219
1506
|
...(previous?.retry ? { retry: { ...previous.retry } } : {}),
|
|
1220
1507
|
...(previous?.steerAcknowledgement ? { steerAcknowledgement: { ...previous.steerAcknowledgement } } : {}),
|
|
1221
1508
|
};
|
|
1222
1509
|
entries.set(runId, entry);
|
|
1223
|
-
pruneEntries(entries);
|
|
1510
|
+
pruneEntries(entries, (evicted) => fleetPhases.delete(evicted));
|
|
1224
1511
|
return entry;
|
|
1225
1512
|
};
|
|
1226
1513
|
|
|
@@ -1251,6 +1538,7 @@ export function createDispatchBoardStore(
|
|
|
1251
1538
|
entry.costUsd = parseFiniteNumber(payload.costUsd, entry.costUsd);
|
|
1252
1539
|
entry.costProvenance = payload.costProvenance ?? "unknown";
|
|
1253
1540
|
entry.outcomeDetail = null;
|
|
1541
|
+
if (payload.hostVerification !== undefined) entry.hostVerification = payload.hostVerification;
|
|
1254
1542
|
settleFromReceipt(entry);
|
|
1255
1543
|
// A terminal dispatch event is published only after the run's receipt is
|
|
1256
1544
|
// sealed at receipts/<runId>.json, so the run id is the receipt id here.
|
|
@@ -1279,6 +1567,7 @@ export function createDispatchBoardStore(
|
|
|
1279
1567
|
entry.costUsd = parseFiniteNumber(payload.costUsd, entry.costUsd);
|
|
1280
1568
|
entry.costProvenance = payload.costProvenance ?? "unknown";
|
|
1281
1569
|
entry.outcomeDetail = resolveFailureDetail(payload, entry.outcomeDetail);
|
|
1570
|
+
if (payload.hostVerification !== undefined) entry.hostVerification = payload.hostVerification;
|
|
1282
1571
|
settleFromReceipt(entry);
|
|
1283
1572
|
// A denied retry never reached a run, so no receipt was sealed for it;
|
|
1284
1573
|
// every other failure finalized through recordReceipt like a success.
|
|
@@ -1487,6 +1776,11 @@ export function createDispatchBoardStore(
|
|
|
1487
1776
|
return projectRows(true);
|
|
1488
1777
|
},
|
|
1489
1778
|
reconcile,
|
|
1779
|
+
setFleetPhase(runId: string, phase: DispatchBoardPhase): void {
|
|
1780
|
+
fleetPhases.set(runId, phase);
|
|
1781
|
+
const entry = entries.get(runId);
|
|
1782
|
+
if (entry) entry.phase = { ...phase };
|
|
1783
|
+
},
|
|
1490
1784
|
unsubscribe() {
|
|
1491
1785
|
if (closed) return;
|
|
1492
1786
|
closed = true;
|