@iowarp/clio-coder 0.3.4 → 0.3.6
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 +37 -2
- package/CONTRIBUTING.md +6 -6
- package/README.md +2 -2
- package/dist/{acp-S5R4RR5B.js → acp-2BEHC4DL.js} +4 -4
- package/dist/{agents-P6DMMVZY.js → agents-LNNFTM53.js} +13 -11
- package/dist/assets/codewiki.json +1 -1
- package/dist/{auth-2XCZLPKS.js → auth-KXXFI2VS.js} +6 -6
- package/dist/{chunk-YCWGATWI.js → chunk-24I7BN55.js} +2 -2
- package/dist/{chunk-EKMEHE4H.js → chunk-33YXPOE3.js} +2 -3
- package/dist/chunk-3BPUFZDL.js +37 -0
- package/dist/{chunk-WPQLXFOZ.js → chunk-43AOLP7E.js} +2 -2
- package/dist/{chunk-N4CZJQRK.js → chunk-5JGRAMKL.js} +4 -4
- package/dist/{chunk-BRXQQJFP.js → chunk-6US73PDB.js} +568 -47
- package/dist/{chunk-K6WL7QZT.js → chunk-6XXKFVSN.js} +2 -2
- package/dist/{chunk-QQK64KLB.js → chunk-CJUB2JJ2.js} +138 -20
- package/dist/{chunk-HV5X7OR2.js → chunk-CKXWIANG.js} +12 -12
- package/dist/{chunk-UZHIZC5S.js → chunk-CYQKWTG3.js} +61 -76
- package/dist/{chunk-QWU7ZBO7.js → chunk-DJVECN66.js} +204 -45
- package/dist/{chunk-ZWMF7253.js → chunk-E2ER4LJF.js} +304 -9
- package/dist/{chunk-7RXG6QRZ.js → chunk-EKY57CSP.js} +2 -75
- package/dist/{chunk-EDRHSCIE.js → chunk-EYPA3EGJ.js} +10 -2
- package/dist/{chunk-TTNYS3EA.js → chunk-G7MUEIGA.js} +1 -1
- package/dist/{chunk-BPGS2WCQ.js → chunk-GEYXPTRF.js} +2 -1
- package/dist/{chunk-BEY543CS.js → chunk-GOXNB3AO.js} +5 -2
- package/dist/{chunk-G4BMMOKF.js → chunk-HVDIIIQW.js} +2 -2
- package/dist/chunk-HWUFFB6L.js +83 -0
- package/dist/{chunk-35MKKU5R.js → chunk-K7T3E2SR.js} +15 -8
- package/dist/{chunk-VAWWTKDP.js → chunk-KHSFENX2.js} +2 -2
- package/dist/chunk-LCGCVYZ4.js +57 -0
- package/dist/{chunk-X6COSD2O.js → chunk-LYF7OHWH.js} +41 -14
- package/dist/{chunk-POHLU5DW.js → chunk-M6L6IDJG.js} +3 -3
- package/dist/{chunk-X4RCMKVQ.js → chunk-NDINPTJ4.js} +2 -2
- package/dist/{chunk-5M54SPOL.js → chunk-ODFEOB4F.js} +161 -5
- package/dist/{chunk-3JLKSKD7.js → chunk-OH3TOQTB.js} +5 -1
- package/dist/{chunk-MEQ45TQ4.js → chunk-PBTHKCPN.js} +18 -4
- package/dist/{chunk-ED4KHGC3.js → chunk-PPAMZ32Z.js} +9 -2
- package/dist/{chunk-QQL5RT5M.js → chunk-QM3F2GKX.js} +94 -36
- package/dist/{chunk-A2GZF7DC.js → chunk-QNQHSOLF.js} +4 -4
- package/dist/{chunk-KRPY7NTG.js → chunk-R46L2BIR.js} +3 -3
- package/dist/{chunk-BP4OYD6A.js → chunk-RY3LY4J5.js} +20 -2
- package/dist/{chunk-34475P3I.js → chunk-TSHXZTOQ.js} +5 -4
- package/dist/{chunk-VJWL6YS5.js → chunk-UUVG37B4.js} +2 -2
- package/dist/{chunk-2TZWSW76.js → chunk-WHGPSPT5.js} +2 -2
- package/dist/{chunk-TW3WDMVS.js → chunk-WHJYKASB.js} +2 -2
- package/dist/{chunk-YHZX5GEU.js → chunk-XAKHZX5N.js} +2 -2
- package/dist/{chunk-HXG4IURW.js → chunk-XE2VEJHX.js} +2 -2
- package/dist/{chunk-3HZ5RWN2.js → chunk-XF5N4U5A.js} +7 -6
- package/dist/{chunk-ZYKPLLNQ.js → chunk-XXQNGV4M.js} +590 -32
- package/dist/{chunk-4JUF2NNX.js → chunk-XYDYPRZI.js} +4 -4
- package/dist/{chunk-VMNQ6OZA.js → chunk-ZRGEBJ4T.js} +971 -794
- package/dist/{chunk-2LZI5CAG.js → chunk-ZXF4XRKW.js} +75 -33
- package/dist/{chunk-VSNATDE6.js → chunk-ZZMN5OM4.js} +2 -2
- package/dist/cli/index.js +31 -31
- package/dist/{clio-J5JIOIDS.js → clio-M2KGYUFZ.js} +2 -2
- package/dist/{code-nav-AXCXSBHX.js → code-nav-GQNL7XA6.js} +5 -5
- package/dist/codewiki/build-worker.js +4 -4
- package/dist/{components-KELWS457.js → components-5TTYYX6G.js} +3 -3
- package/dist/{config-OEBMIN2U.js → config-XUUYQIWO.js} +27 -25
- package/dist/{configure-PUQOSIXQ.js → configure-IHJ7YOMV.js} +7 -7
- package/dist/{context-URSXPBCK.js → context-74JLXAWD.js} +12 -12
- package/dist/{context-MGSE4Z2T.js → context-75MIWW3U.js} +24 -22
- package/dist/{context-EKDCKUUZ.js → context-ZQ7SIFJV.js} +8 -7
- package/dist/{context-clear-KDAJRNUK.js → context-clear-GYKWNUML.js} +24 -22
- package/dist/{context-index-BZ4UYMTC.js → context-index-SSR5ECNE.js} +3 -3
- package/dist/{context-working-set-SBKMPPI2.js → context-working-set-UX5KEP4J.js} +11 -10
- package/dist/{dispatch-runner-MSWN72NK.js → dispatch-runner-GIJBHNFL.js} +21 -20
- package/dist/{docs-2C2LTVT2.js → docs-6FZSCG5B.js} +3 -3
- package/dist/{doctor-7BSE27PJ.js → doctor-SVJ5BZCW.js} +4 -4
- package/dist/{eval-IZGDOO4H.js → eval-CG6LLBLD.js} +47 -232
- package/dist/{evidence-SR7WXB5B.js → evidence-ZYFIEN42.js} +19 -18
- package/dist/{evolve-K7VE2CBX.js → evolve-QGEXEMDW.js} +19 -18
- package/dist/{extensions-QVDOHDGJ.js → extensions-ADGNCJJD.js} +3 -3
- package/dist/{fleet-7XMJNQNF.js → fleet-S5R4ZOQY.js} +49 -30
- package/dist/{fleet-preflight-AQNAH644.js → fleet-preflight-BHSNPBMH.js} +2 -2
- package/dist/{init-JGNPAYXT.js → init-5DRU55YR.js} +31 -29
- package/dist/memory-7YKKR6UC.js +467 -0
- package/dist/{models-ZMMLFJNN.js → models-ZPOLRU2C.js} +10 -10
- package/dist/{monitor-2F3T5KHP.js → monitor-US5F5YGZ.js} +33 -18
- package/dist/{orchestrator-ORHT43JB.js → orchestrator-E2AL4T5N.js} +1092 -659
- package/dist/{paths-UXLN5YYZ.js → paths-E7KYAQWE.js} +3 -3
- package/dist/{reset-NXGTYNUO.js → reset-KZ652EK6.js} +3 -3
- package/dist/{run-RF4WJGMT.js → run-SRNBKDWD.js} +52 -40
- package/dist/{share-UT3W6E4M.js → share-CGZE33UP.js} +3 -3
- package/dist/{skills-PSACKC5Q.js → skills-S2X4DLY5.js} +4 -4
- package/dist/{skills-eval-WJSI55RZ.js → skills-eval-W2GGIC4R.js} +19 -18
- package/dist/{targets-PIIRAOYS.js → targets-54SWINWB.js} +14 -12
- package/dist/{terminal-lease-ULWXWNVY.js → terminal-lease-SAIF2OGY.js} +5 -4
- package/dist/{uninstall-FZCQCDKC.js → uninstall-BVLWXKBT.js} +3 -3
- package/dist/{upgrade-346TZ6AV.js → upgrade-JKAR27XC.js} +8 -8
- package/dist/{usage-6KKXR32N.js → usage-MSAWCLX4.js} +60 -27
- package/dist/{verifiers-4UUM6TEE.js → verifiers-NCBTHHN2.js} +60 -54
- package/dist/{wiki-generate-7STOCIFZ.js → wiki-generate-GUSOQ6ZP.js} +30 -28
- package/dist/worker/entry.js +69 -58
- package/dist/{workspace-G4ZWUIPR.js → workspace-ZJ6BFM3Q.js} +4 -4
- package/docs/README.md +3 -3
- package/docs/acp.md +1 -1
- package/docs/alcf-provider.md +1 -1
- package/docs/architecture.md +2 -2
- package/docs/artifact-placement.md +1 -2
- package/docs/artifact-versions.md +1 -1
- package/docs/built-in-agents.md +1 -1
- package/docs/capacity-and-scheduling.md +1 -1
- package/docs/commands-and-modes.md +9 -7
- package/docs/configuration-and-targets.md +12 -1
- package/docs/context-engine.md +4 -2
- package/docs/context-working-set.md +4 -4
- package/docs/development-pipeline.md +1 -1
- package/docs/documentation-coverage.md +3 -3
- package/docs/documentation-guide.md +2 -2
- package/docs/eval-runner.md +1 -1
- package/docs/evals-internal.md +4 -45
- package/docs/evidence-and-memory.md +67 -7
- package/docs/evolution.md +1 -1
- package/docs/exit-codes-and-output.md +1 -1
- package/docs/extensions-and-sharing.md +2 -2
- package/docs/fleet-dispatch.md +28 -2
- package/docs/installation-and-lifecycle.md +2 -2
- package/docs/middleware-and-components.md +19 -2
- package/docs/model-catalog.md +1 -1
- package/docs/observability.md +3 -3
- package/docs/proactive-memory.md +26 -16
- package/docs/prompt-envelope-and-tools.md +4 -2
- package/docs/provider-adapter-cookbook.md +1 -1
- package/docs/release-cut-checklist.md +38 -35
- package/docs/safety-model.md +29 -7
- package/docs/scientific-validation.md +3 -3
- package/docs/session-lifecycle.md +1 -1
- package/docs/skills-marketplace.md +1 -1
- package/docs/tool-usage.md +2 -2
- package/docs/trace-store.md +1 -1
- package/docs/troubleshooting.md +1 -1
- package/docs/tui-design.md +38 -4
- package/docs/worker-dispatch-mechanics.md +1 -1
- package/package.json +7 -4
- package/src/cli/agents.ts +2 -3
- package/src/cli/argv.ts +14 -1
- package/src/cli/fleet.ts +15 -0
- package/src/cli/index.ts +1 -1
- package/src/cli/memory.ts +272 -10
- package/src/cli/modes/json-stream.ts +2 -2
- package/src/cli/modes/print.ts +12 -1
- package/src/cli/run.ts +22 -2
- package/src/cli/targets.ts +12 -3
- package/src/cli/usage.ts +55 -7
- package/src/core/bus-events.ts +3 -0
- package/src/core/response-model-id.ts +134 -0
- package/src/core/toml.ts +62 -0
- package/src/core/workspace-files.ts +0 -1
- package/src/domains/agents/builtins/architect.md +1 -1
- package/src/domains/agents/catalog.ts +5 -4
- package/src/domains/agents/recipe.ts +54 -14
- package/src/domains/agents/result-contract.ts +7 -4
- package/src/domains/context/bootstrap.ts +36 -27
- package/src/domains/context/project-metadata.ts +19 -63
- package/src/domains/context/prompt-context.ts +8 -0
- package/src/domains/context/working-set/policies/index.ts +3 -4
- package/src/domains/dispatch/budget-envelope.ts +396 -0
- package/src/domains/dispatch/contract.ts +2 -0
- package/src/domains/dispatch/extension.ts +81 -27
- package/src/domains/dispatch/orphan-recovery.ts +1 -0
- package/src/domains/dispatch/receipt-integrity.ts +4 -0
- package/src/domains/dispatch/state.ts +1 -0
- package/src/domains/dispatch/types.ts +10 -3
- package/src/domains/dispatch/validation.ts +14 -0
- package/src/domains/dispatch/worker-spawn.ts +14 -3
- package/src/domains/eval/metrics/evidence.ts +0 -116
- package/src/domains/eval/metrics/invariants.ts +1 -1
- package/src/domains/eval/runners/clio-run.ts +1 -10
- package/src/domains/eval/runners/external-command.ts +2 -29
- package/src/domains/eval/schema/suite.ts +0 -7
- package/src/domains/eval/suites/run.ts +1 -7
- package/src/domains/memory/index.ts +22 -0
- package/src/domains/memory/operations.ts +58 -1
- package/src/domains/memory/promotion.ts +281 -0
- package/src/domains/memory/prompt-section.ts +25 -5
- package/src/domains/memory/proposal.ts +51 -7
- package/src/domains/memory/task-bank.ts +3 -2
- package/src/domains/memory/task-memory-handoff.ts +181 -24
- package/src/domains/memory/task-memory-policy.ts +3 -1
- package/src/domains/memory/types.ts +37 -0
- package/src/domains/memory/validate.ts +178 -0
- package/src/domains/middleware/memory-intervention.ts +35 -25
- package/src/domains/middleware/runtime.ts +6 -0
- package/src/domains/middleware/skills-reminder.ts +19 -4
- package/src/domains/middleware/stalled-turn.ts +43 -1
- package/src/domains/middleware/types.ts +10 -0
- package/src/domains/observability/contract.ts +6 -1
- package/src/domains/observability/cost.ts +20 -4
- package/src/domains/observability/extension.ts +2 -2
- package/src/domains/providers/index.ts +3 -0
- package/src/domains/providers/model-discovery.ts +9 -0
- package/src/domains/providers/runtime-resolution.ts +38 -1
- package/src/domains/providers/runtimes/common/probe-helpers.ts +97 -16
- package/src/domains/providers/types/context-window-slots.ts +18 -0
- package/src/domains/providers/types/runtime-descriptor.ts +3 -1
- package/src/domains/safety/call-target.ts +211 -14
- package/src/domains/safety/decision-presentation.ts +268 -0
- package/src/domains/safety/redaction.ts +73 -0
- package/src/domains/session/context-ledger.ts +10 -1
- package/src/domains/session/decision-board.ts +4 -0
- package/src/domains/session/entries.ts +3 -0
- package/src/domains/session/history.ts +68 -19
- package/src/domains/session/usage.ts +24 -7
- package/src/engine/acp/event-mapper.ts +7 -0
- package/src/engine/acp/server.ts +29 -2
- package/src/engine/apis/lmstudio.ts +25 -4
- package/src/engine/apis/openai-completions.ts +147 -22
- package/src/engine/claude/sdk-runtime.ts +8 -2
- package/src/engine/claude/tool-safety.ts +13 -0
- package/src/engine/loop-guard.ts +27 -3
- package/src/engine/worker-events.ts +4 -3
- package/src/engine/worker-runtime.ts +59 -54
- package/src/entry/orchestrator.ts +18 -1
- package/src/interactive/chat-loop-messages.ts +22 -0
- package/src/interactive/chat-loop.ts +13 -0
- package/src/interactive/chat-renderer.ts +19 -3
- package/src/interactive/clio-editor.ts +44 -7
- package/src/interactive/context-overlay.ts +43 -5
- package/src/interactive/cost-overlay.ts +39 -8
- package/src/interactive/dispatch-board.ts +212 -35
- package/src/interactive/footer/widgets.ts +13 -0
- package/src/interactive/interactive-application.ts +6 -1
- package/src/interactive/interactive-input-runtime.ts +11 -1
- package/src/interactive/interactive-presentation.ts +11 -1
- package/src/interactive/memory-overlay.ts +89 -4
- package/src/interactive/overlay-ask-user-lifecycle.ts +1 -1
- package/src/interactive/overlay-frame.ts +5 -2
- package/src/interactive/overlay-general-openers.ts +40 -1
- package/src/interactive/overlay-key-routing.ts +41 -1
- package/src/interactive/overlay-lifecycle.ts +11 -4
- package/src/interactive/overlay-permission-lifecycle.ts +23 -8
- package/src/interactive/overlay-transitions.ts +11 -0
- package/src/interactive/overlays/ask-user.ts +74 -30
- package/src/interactive/overlays/decisions.ts +3 -1
- package/src/interactive/permission-hint.ts +35 -0
- package/src/interactive/permission-overlay.ts +95 -45
- package/src/interactive/renderers/tool-execution.ts +19 -49
- package/src/interactive/session-last-turn.ts +8 -1
- package/src/interactive/session-usage-reseed.ts +36 -10
- package/src/interactive/slash-commands.ts +2 -2
- package/src/interactive/status/summary.ts +5 -0
- package/src/interactive/status/types.ts +5 -0
- package/src/interactive/terminal-lease.ts +1 -0
- package/src/interactive/turn-context.ts +96 -23
- package/src/interactive/turn-middleware.ts +1 -0
- package/src/interactive/turn-runtime.ts +37 -8
- package/src/interactive/turn-state.ts +3 -0
- package/src/interactive/worker-progress.ts +440 -0
- package/src/interactive/worker-stream.ts +51 -110
- package/src/tools/agent-tools.ts +28 -3
- package/src/tools/ask-user.ts +21 -1
- package/src/tools/context/index.ts +2 -2
- package/src/tools/dispatch-arguments.ts +8 -0
- package/src/tools/dispatch-event-text.ts +19 -0
- package/src/tools/dispatch.ts +24 -1
- package/src/tools/monitor.ts +15 -0
- package/src/tools/registry.ts +15 -5
- package/src/tools/result-disposition.ts +156 -0
- package/src/tools/result-shaping.ts +59 -1
- package/src/tools/verify/authoring.ts +55 -54
- package/src/tools/worker-evidence.ts +19 -0
- package/src/worker/spec-contract.ts +43 -3
- package/dist/chunk-EFADSJET.js +0 -18
- package/dist/memory-4ALKDJ4Q.js +0 -246
- package/src/domains/eval/metrics/chaos-stream.ts +0 -93
|
@@ -9,6 +9,14 @@ import {
|
|
|
9
9
|
import type { SafeEventBus } from "../core/event-bus.js";
|
|
10
10
|
import { rawDurationMs } from "../core/timers.js";
|
|
11
11
|
import type { AgentAudience } from "../domains/agents/spec.js";
|
|
12
|
+
import {
|
|
13
|
+
cloneRunToolBudgetEnvelope,
|
|
14
|
+
formatBudgetPolicy,
|
|
15
|
+
formatBudgetReasons,
|
|
16
|
+
formatBudgetRequest,
|
|
17
|
+
formatEffectiveBudget,
|
|
18
|
+
type RunToolBudgetEnvelope,
|
|
19
|
+
} from "../domains/dispatch/budget-envelope.js";
|
|
12
20
|
import type { DispatchSnapshot } from "../domains/dispatch/contract.js";
|
|
13
21
|
import {
|
|
14
22
|
type DispatchRequestOrigin,
|
|
@@ -25,7 +33,7 @@ import {
|
|
|
25
33
|
} from "../domains/observability/index.js";
|
|
26
34
|
import type { CostProvenance } from "../domains/providers/index.js";
|
|
27
35
|
import { sanitizeCallTargetText } from "../domains/safety/call-target.js";
|
|
28
|
-
import { type Component, truncateToWidth, visibleWidth } from "../engine/tui.js";
|
|
36
|
+
import { type Component, truncateToWidth, visibleWidth, wrapTextWithAnsi } from "../engine/tui.js";
|
|
29
37
|
import { formatWorkerContextMeter } from "./context-meter.js";
|
|
30
38
|
import { formatFooterTokens } from "./footer-panel.js";
|
|
31
39
|
import {
|
|
@@ -42,6 +50,13 @@ import {
|
|
|
42
50
|
screenTitle,
|
|
43
51
|
spinnerFrame,
|
|
44
52
|
} from "./theme/index.js";
|
|
53
|
+
import {
|
|
54
|
+
createWorkerProgressFold,
|
|
55
|
+
type WorkerAction,
|
|
56
|
+
type WorkerProgressFold,
|
|
57
|
+
type WorkerProgressSnapshot,
|
|
58
|
+
} from "./worker-progress.js";
|
|
59
|
+
import type { WorkerReceiptReader } from "./worker-stream.js";
|
|
45
60
|
|
|
46
61
|
export type DispatchBoardStatus =
|
|
47
62
|
| Extract<RunStatus, "running" | "completed" | "failed" | "stale" | "dead">
|
|
@@ -72,6 +87,8 @@ export interface DispatchBoardRow {
|
|
|
72
87
|
wireModelId: string;
|
|
73
88
|
/** Sanitized, one-line summary of the task assigned to this run. */
|
|
74
89
|
taskSummary?: string;
|
|
90
|
+
/** Immutable budget admission provenance for this run. */
|
|
91
|
+
budget?: RunToolBudgetEnvelope;
|
|
75
92
|
status: DispatchBoardStatus;
|
|
76
93
|
elapsedMs: number;
|
|
77
94
|
tokenCount: number;
|
|
@@ -94,10 +111,17 @@ export interface DispatchBoardRow {
|
|
|
94
111
|
contextWindow?: number;
|
|
95
112
|
/** Last assistant message's input+cacheRead+output: current context occupancy. */
|
|
96
113
|
lastContextTokens?: number;
|
|
97
|
-
/** Tool currently executing in the worker; null between calls. */
|
|
114
|
+
/** Tool currently executing in the worker; null between calls. Projected from `progress`. */
|
|
98
115
|
currentTool?: string | null;
|
|
99
|
-
/** Recently finished tools, newest first, bounded. */
|
|
116
|
+
/** Recently finished tools, newest first, bounded. Projected from `progress`. */
|
|
100
117
|
recentTools?: ReadonlyArray<string>;
|
|
118
|
+
/**
|
|
119
|
+
* The canonical worker-progress projection for this run: the bounded answer
|
|
120
|
+
* tail, the phase, and the redacted action descriptors. The same projection
|
|
121
|
+
* the transcript's worker block reads, so opening a run here shows what the
|
|
122
|
+
* worker is doing rather than a richer spinner.
|
|
123
|
+
*/
|
|
124
|
+
progress?: WorkerProgressSnapshot;
|
|
101
125
|
/**
|
|
102
126
|
* Id of the receipt sealed for this run (`receipts/<runId>.json`, the id
|
|
103
127
|
* `clio-coder trace` takes). Set only once a terminal dispatch event has
|
|
@@ -126,7 +150,10 @@ export function isDispatchBoardRowCancellable(row: DispatchBoardRow): boolean {
|
|
|
126
150
|
}
|
|
127
151
|
|
|
128
152
|
interface DispatchBoardEntry
|
|
129
|
-
extends Omit<
|
|
153
|
+
extends Omit<
|
|
154
|
+
DispatchBoardRow,
|
|
155
|
+
"elapsedMs" | "recentTools" | "lastContextTokens" | "currentTool" | "retry" | "progress"
|
|
156
|
+
> {
|
|
130
157
|
sequence: number;
|
|
131
158
|
enqueuedAtMs: number;
|
|
132
159
|
startedAtMs: number | null;
|
|
@@ -139,15 +166,19 @@ interface DispatchBoardEntry
|
|
|
139
166
|
finishedAtMs: number | null;
|
|
140
167
|
durationMs: number | null;
|
|
141
168
|
lastContextTokens: number;
|
|
142
|
-
|
|
143
|
-
|
|
169
|
+
/**
|
|
170
|
+
* The one fold that reads this run's worker events. Tool activity and the
|
|
171
|
+
* answer tail are its output, so the board no longer interprets the stream
|
|
172
|
+
* alongside the transcript.
|
|
173
|
+
*/
|
|
174
|
+
progress: WorkerProgressFold;
|
|
144
175
|
retry?: DispatchRetryPresentation;
|
|
145
176
|
}
|
|
146
177
|
|
|
147
|
-
/** Bound on the recent-tool trail rendered on a card. */
|
|
148
|
-
const RECENT_TOOL_TRAIL_LIMIT = 4;
|
|
149
178
|
/** Keep raw task text out of long-lived TUI rows and bound hostile/user-sized input. */
|
|
150
179
|
const TASK_SUMMARY_MAX_WIDTH = 240;
|
|
180
|
+
/** Rows of worker prose an expanded card shows before it defers to `/view`. */
|
|
181
|
+
const WORKER_PROGRESS_CARD_ROWS = 6;
|
|
151
182
|
|
|
152
183
|
interface WorkerEventShape {
|
|
153
184
|
type?: unknown;
|
|
@@ -433,11 +464,92 @@ function retryCountdown(retry: DispatchRetryPresentation, now = Date.now()): str
|
|
|
433
464
|
return remainingMs === 0 ? "now" : `in ${formatCompactMs(remainingMs)}`;
|
|
434
465
|
}
|
|
435
466
|
|
|
467
|
+
/**
|
|
468
|
+
* One action as a phrase: the tool name, then the verb and object of its
|
|
469
|
+
* redacted descriptor. The descriptor was bounded and scrubbed at the worker
|
|
470
|
+
* seam, so this composes text and never inspects an argument.
|
|
471
|
+
*/
|
|
472
|
+
function actionPhrase(action: WorkerAction): string {
|
|
473
|
+
const descriptor = action.descriptor;
|
|
474
|
+
if (descriptor === undefined) return action.tool;
|
|
475
|
+
const object = descriptor.object === undefined ? "" : ` ${descriptor.object}${descriptor.truncated ? "…" : ""}`;
|
|
476
|
+
return `${action.tool} ${descriptor.verb}${object}`;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
/** The phase word an expanded card names, or null for a phase that says nothing new. */
|
|
480
|
+
function progressPhaseUnit(theme: ClioTheme, progress: WorkerProgressSnapshot): string | null {
|
|
481
|
+
switch (progress.phase) {
|
|
482
|
+
case "thinking":
|
|
483
|
+
// The phase only. Reasoning content never reaches an operator surface.
|
|
484
|
+
return theme.fg("reason", `${GLYPH.phaseThinking} thinking`);
|
|
485
|
+
case "writing":
|
|
486
|
+
return theme.fg("accent", `${GLYPH.phaseWriting} writing`);
|
|
487
|
+
case "tool":
|
|
488
|
+
return theme.fg("action", `${GLYPH.phaseTool} tool`);
|
|
489
|
+
case "waiting":
|
|
490
|
+
return theme.fg("info", `${GLYPH.phaseWaiting} waiting`);
|
|
491
|
+
case "settled":
|
|
492
|
+
case "starting":
|
|
493
|
+
return null;
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
/** The `doing` row: the phase, then the running call or the most recent one, with its object. */
|
|
498
|
+
function progressActionLine(theme: ClioTheme, progress: WorkerProgressSnapshot, contentWidth: number): string | null {
|
|
499
|
+
const phase = progressPhaseUnit(theme, progress);
|
|
500
|
+
const current = progress.currentAction;
|
|
501
|
+
const recent = progress.recentActions[0];
|
|
502
|
+
const units: string[] = [];
|
|
503
|
+
if (phase !== null) units.push(phase);
|
|
504
|
+
if (current !== null) units.push(theme.fg("muted", actionPhrase(current)));
|
|
505
|
+
else if (recent !== undefined) units.push(theme.fg("dim", `last ${actionPhrase(recent)}`));
|
|
506
|
+
if (units.length === 0) return null;
|
|
507
|
+
return cardUnitsLine(theme, "doing", units, contentWidth);
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
/**
|
|
511
|
+
* The `answer` block: the newest rows of the worker's bounded prose on a rail,
|
|
512
|
+
* then what is not shown and where to read it. Wrapping happens before the row
|
|
513
|
+
* cap, so the block is at most {@link WORKER_PROGRESS_CARD_ROWS} rows tall
|
|
514
|
+
* whatever the terminal width, and a streaming answer cannot make the card grow
|
|
515
|
+
* under the operator.
|
|
516
|
+
*/
|
|
517
|
+
function progressAnswerLines(
|
|
518
|
+
theme: ClioTheme,
|
|
519
|
+
progress: WorkerProgressSnapshot,
|
|
520
|
+
runId: string,
|
|
521
|
+
contentWidth: number,
|
|
522
|
+
): string[] {
|
|
523
|
+
if (progress.tailText.length === 0) return [];
|
|
524
|
+
const gutter = CARD_KV_KEY_WIDTH + 1;
|
|
525
|
+
const railWidth = Math.max(1, contentWidth - gutter - 2);
|
|
526
|
+
const wrapped: string[] = [];
|
|
527
|
+
for (const line of progress.tailText.split("\n")) {
|
|
528
|
+
for (const row of wrapTextWithAnsi(sanitizeCallTargetText(line), railWidth)) wrapped.push(row);
|
|
529
|
+
}
|
|
530
|
+
const shown = wrapped.slice(Math.max(0, wrapped.length - WORKER_PROGRESS_CARD_ROWS));
|
|
531
|
+
const hiddenRows = wrapped.length - shown.length;
|
|
532
|
+
const rail = theme.fg("dim", `${GLYPH.rail} `);
|
|
533
|
+
const body = shown.map((row) => `${rail}${theme.fg("muted", row)}`);
|
|
534
|
+
const hiddenLines = progress.droppedLines + hiddenRows;
|
|
535
|
+
if (hiddenLines > 0 || progress.droppedBytes > 0) {
|
|
536
|
+
const facts = [
|
|
537
|
+
...(hiddenLines > 0 ? [`${hiddenLines} more line${hiddenLines === 1 ? "" : "s"}`] : []),
|
|
538
|
+
...(progress.droppedBytes > 0 ? [`${progress.droppedBytes} bytes outran the view`] : []),
|
|
539
|
+
`/view dispatch:${runId}`,
|
|
540
|
+
];
|
|
541
|
+
body.push(`${rail}${theme.fg("dim", truncateToWidth(facts.join(" · "), railWidth, "…", false))}`);
|
|
542
|
+
}
|
|
543
|
+
// The key labels the block once and the rest hangs under it, which is the
|
|
544
|
+
// card's key-value grammar applied to a body rather than to one value.
|
|
545
|
+
return body.map((row, index) => `${index === 0 ? cardKvKey(theme, "answer") : " ".repeat(gutter)}${row}`);
|
|
546
|
+
}
|
|
547
|
+
|
|
436
548
|
export function renderDispatchCard(
|
|
437
549
|
row: DispatchBoardRow,
|
|
438
550
|
width: number,
|
|
439
551
|
evidence?: RunEvidencePresentation,
|
|
440
|
-
options: { selected?: boolean } = {},
|
|
552
|
+
options: { selected?: boolean; expanded?: boolean } = {},
|
|
441
553
|
): string[] {
|
|
442
554
|
const theme = clioTheme();
|
|
443
555
|
const contentWidth = Math.max(0, width - 4);
|
|
@@ -506,6 +618,22 @@ export function renderDispatchCard(
|
|
|
506
618
|
? [truncateToWidth(`${cardKvKey(theme, "task")}${theme.fg("muted", row.taskSummary)}`, contentWidth, "…", false)]
|
|
507
619
|
: []),
|
|
508
620
|
cardUnitsLine(theme, "status", statusUnits, contentWidth),
|
|
621
|
+
...(row.budget !== undefined
|
|
622
|
+
? [
|
|
623
|
+
truncateToWidth(
|
|
624
|
+
`${cardKvKey(theme, "policy")}${theme.fg("muted", `${formatBudgetPolicy(row.budget)}; requested ${formatBudgetRequest(row.budget)}`)}`,
|
|
625
|
+
contentWidth,
|
|
626
|
+
"…",
|
|
627
|
+
false,
|
|
628
|
+
),
|
|
629
|
+
truncateToWidth(
|
|
630
|
+
`${cardKvKey(theme, "budget")}${theme.fg("muted", `${formatEffectiveBudget(row.budget)}; reason ${formatBudgetReasons(row.budget)}`)}`,
|
|
631
|
+
contentWidth,
|
|
632
|
+
"…",
|
|
633
|
+
false,
|
|
634
|
+
),
|
|
635
|
+
]
|
|
636
|
+
: []),
|
|
509
637
|
cardUnitsLine(theme, "telemetry", [up, down, total, ...(contextUnit !== null ? [contextUnit] : [])], contentWidth),
|
|
510
638
|
];
|
|
511
639
|
if (row.retry) {
|
|
@@ -533,6 +661,13 @@ export function renderDispatchCard(
|
|
|
533
661
|
];
|
|
534
662
|
bodyLines.push(cardUnitsLine(theme, "tools", toolUnits, contentWidth));
|
|
535
663
|
}
|
|
664
|
+
// Live worker progress is expanded detail, never a default row: five running
|
|
665
|
+
// scouts must stay five compact cards until the operator opens one.
|
|
666
|
+
if (options.expanded === true && row.progress) {
|
|
667
|
+
const doing = progressActionLine(theme, row.progress, contentWidth);
|
|
668
|
+
if (doing !== null) bodyLines.push(doing);
|
|
669
|
+
bodyLines.push(...progressAnswerLines(theme, row.progress, row.runId, contentWidth));
|
|
670
|
+
}
|
|
536
671
|
if (row.steerAcknowledgement) {
|
|
537
672
|
bodyLines.push(
|
|
538
673
|
cardUnitsLine(
|
|
@@ -610,6 +745,8 @@ export function formatDispatchBoardLines(
|
|
|
610
745
|
width = 76,
|
|
611
746
|
observability?: ObservabilitySnapshot,
|
|
612
747
|
selectedRunId?: string | null,
|
|
748
|
+
/** Whether the selected row renders its worker-progress detail. Off by default. */
|
|
749
|
+
detailExpanded = false,
|
|
613
750
|
): string[] {
|
|
614
751
|
if (rows.length === 0) {
|
|
615
752
|
const theme = clioTheme();
|
|
@@ -623,6 +760,7 @@ export function formatDispatchBoardLines(
|
|
|
623
760
|
const cards = rows.map((row) =>
|
|
624
761
|
renderDispatchCard(row, width, deriveRunEvidenceState(observability, row.runId), {
|
|
625
762
|
selected: row.runId === selectedRunId,
|
|
763
|
+
expanded: detailExpanded && row.runId === selectedRunId,
|
|
626
764
|
}),
|
|
627
765
|
);
|
|
628
766
|
const body: string[] = [];
|
|
@@ -648,6 +786,9 @@ export interface DispatchBoardView extends Component {
|
|
|
648
786
|
selectPrevious(): void;
|
|
649
787
|
selectNext(): void;
|
|
650
788
|
resetSelection(): void;
|
|
789
|
+
/** Open or close the selected row's worker-progress detail. */
|
|
790
|
+
toggleDetail(): void;
|
|
791
|
+
detailExpanded(): boolean;
|
|
651
792
|
}
|
|
652
793
|
|
|
653
794
|
export function createDispatchBoardView(
|
|
@@ -655,6 +796,9 @@ export function createDispatchBoardView(
|
|
|
655
796
|
observability: () => ObservabilitySnapshot | undefined,
|
|
656
797
|
): DispatchBoardView {
|
|
657
798
|
let selectedRunId: string | null = null;
|
|
799
|
+
// Detail is a property of the operator's attention, not of a run, so it
|
|
800
|
+
// follows the cursor rather than pinning to the row it was opened on.
|
|
801
|
+
let expanded = false;
|
|
658
802
|
|
|
659
803
|
const normalizeSelection = (currentRows: ReadonlyArray<DispatchBoardRow>): DispatchBoardRow | null => {
|
|
660
804
|
if (currentRows.length === 0) {
|
|
@@ -679,7 +823,7 @@ export function createDispatchBoardView(
|
|
|
679
823
|
render(width: number): string[] {
|
|
680
824
|
const currentRows = rows();
|
|
681
825
|
normalizeSelection(currentRows);
|
|
682
|
-
return formatDispatchBoardLines(currentRows, Math.max(1, width), observability(), selectedRunId);
|
|
826
|
+
return formatDispatchBoardLines(currentRows, Math.max(1, width), observability(), selectedRunId, expanded);
|
|
683
827
|
},
|
|
684
828
|
selectedRow(): DispatchBoardRow | null {
|
|
685
829
|
return normalizeSelection(rows());
|
|
@@ -692,8 +836,15 @@ export function createDispatchBoardView(
|
|
|
692
836
|
},
|
|
693
837
|
resetSelection(): void {
|
|
694
838
|
selectedRunId = null;
|
|
839
|
+
expanded = false;
|
|
695
840
|
normalizeSelection(rows());
|
|
696
841
|
},
|
|
842
|
+
toggleDetail(): void {
|
|
843
|
+
expanded = !expanded;
|
|
844
|
+
},
|
|
845
|
+
detailExpanded(): boolean {
|
|
846
|
+
return expanded;
|
|
847
|
+
},
|
|
697
848
|
invalidate(): void {},
|
|
698
849
|
};
|
|
699
850
|
}
|
|
@@ -879,6 +1030,7 @@ function readRunningSnapshot(snapshot: DispatchSnapshot): Map<
|
|
|
879
1030
|
costUsd: number;
|
|
880
1031
|
costProvenance: CostProvenance;
|
|
881
1032
|
outcomePhase: string;
|
|
1033
|
+
budget?: RunToolBudgetEnvelope;
|
|
882
1034
|
}
|
|
883
1035
|
> {
|
|
884
1036
|
const running = new Map<
|
|
@@ -890,12 +1042,14 @@ function readRunningSnapshot(snapshot: DispatchSnapshot): Map<
|
|
|
890
1042
|
costUsd: number;
|
|
891
1043
|
costProvenance: CostProvenance;
|
|
892
1044
|
outcomePhase: string;
|
|
1045
|
+
budget?: RunToolBudgetEnvelope;
|
|
893
1046
|
}
|
|
894
1047
|
>();
|
|
895
1048
|
try {
|
|
896
1049
|
for (const value of snapshot.running) {
|
|
897
1050
|
const runId = parseRunId(value.runId);
|
|
898
1051
|
if (!runId) continue;
|
|
1052
|
+
const budget = cloneRunToolBudgetEnvelope(value.budget);
|
|
899
1053
|
running.set(runId, {
|
|
900
1054
|
inputTokens: parseFiniteNumberOrZero(value.tokens.input),
|
|
901
1055
|
outputTokens: parseFiniteNumberOrZero(value.tokens.output),
|
|
@@ -903,6 +1057,7 @@ function readRunningSnapshot(snapshot: DispatchSnapshot): Map<
|
|
|
903
1057
|
costUsd: parseFiniteNumberOrZero(value.costUsd),
|
|
904
1058
|
costProvenance: value.costProvenance ?? "unknown",
|
|
905
1059
|
outcomePhase: value.outcomePhase,
|
|
1060
|
+
...(budget !== undefined ? { budget } : {}),
|
|
906
1061
|
});
|
|
907
1062
|
}
|
|
908
1063
|
} catch {
|
|
@@ -920,7 +1075,9 @@ function resolveElapsedMs(entry: DispatchBoardEntry, now: number): number {
|
|
|
920
1075
|
|
|
921
1076
|
function toRow(entry: DispatchBoardEntry, now: number): DispatchBoardRow {
|
|
922
1077
|
const retry = entry.retry;
|
|
1078
|
+
const progress = entry.progress.snapshot();
|
|
923
1079
|
return {
|
|
1080
|
+
progress,
|
|
924
1081
|
runId: entry.runId,
|
|
925
1082
|
agentId: entry.agentId,
|
|
926
1083
|
...(entry.agentAudience !== undefined ? { agentAudience: entry.agentAudience } : {}),
|
|
@@ -930,6 +1087,7 @@ function toRow(entry: DispatchBoardEntry, now: number): DispatchBoardRow {
|
|
|
930
1087
|
targetId: entry.targetId,
|
|
931
1088
|
wireModelId: entry.wireModelId,
|
|
932
1089
|
...(entry.taskSummary !== undefined ? { taskSummary: entry.taskSummary } : {}),
|
|
1090
|
+
...(entry.budget !== undefined ? { budget: entry.budget } : {}),
|
|
933
1091
|
status: retry ? "retrying" : entry.status,
|
|
934
1092
|
elapsedMs: resolveElapsedMs(entry, now),
|
|
935
1093
|
tokenCount: entry.tokenCount,
|
|
@@ -946,8 +1104,10 @@ function toRow(entry: DispatchBoardEntry, now: number): DispatchBoardRow {
|
|
|
946
1104
|
...(entry.contextWindow !== undefined ? { contextWindow: entry.contextWindow } : {}),
|
|
947
1105
|
lastContextTokens: entry.lastContextTokens,
|
|
948
1106
|
...(entry.receiptId !== undefined ? { receiptId: entry.receiptId } : {}),
|
|
949
|
-
|
|
950
|
-
|
|
1107
|
+
// Both tool fields are views of the one projection, so the compact card and
|
|
1108
|
+
// the expanded detail can never disagree about what is running.
|
|
1109
|
+
currentTool: retry ? null : (progress.currentAction?.tool ?? null),
|
|
1110
|
+
recentTools: progress.recentActions.map((action) => action.tool),
|
|
951
1111
|
...(retry ? { retry: { ...retry } } : {}),
|
|
952
1112
|
...(entry.steerAcknowledgement ? { steerAcknowledgement: { ...entry.steerAcknowledgement } } : {}),
|
|
953
1113
|
};
|
|
@@ -980,6 +1140,12 @@ function pruneEntries(entries: Map<string, DispatchBoardEntry>): void {
|
|
|
980
1140
|
export function createDispatchBoardStore(
|
|
981
1141
|
bus: SafeEventBus,
|
|
982
1142
|
snapshot?: () => DispatchSnapshot,
|
|
1143
|
+
/**
|
|
1144
|
+
* Sealed terminal facts for a finished run. Settlement replaces the
|
|
1145
|
+
* provisional live tail with the receipt's answer where one can be read; the
|
|
1146
|
+
* run's own durable message stands in where it cannot.
|
|
1147
|
+
*/
|
|
1148
|
+
readReceipt?: WorkerReceiptReader,
|
|
983
1149
|
): {
|
|
984
1150
|
rows(): ReadonlyArray<DispatchBoardRow>;
|
|
985
1151
|
activeRows(): ReadonlyArray<DispatchBoardRow>;
|
|
@@ -990,6 +1156,16 @@ export function createDispatchBoardStore(
|
|
|
990
1156
|
let nextSequence = 0;
|
|
991
1157
|
let reconciledAtMs = Date.now();
|
|
992
1158
|
|
|
1159
|
+
/**
|
|
1160
|
+
* Seal a run's projection on the sealed receipt's answer. Only for the
|
|
1161
|
+
* terminal bus events: the dispatch domain writes `receipts/<runId>.json`
|
|
1162
|
+
* before it publishes one, so this is the first moment the file exists.
|
|
1163
|
+
*/
|
|
1164
|
+
const settleFromReceipt = (entry: DispatchBoardEntry): void => {
|
|
1165
|
+
const text = readReceipt?.(entry.runId)?.text;
|
|
1166
|
+
entry.progress.settle(typeof text === "string" && text.trim().length > 0 ? text : undefined);
|
|
1167
|
+
};
|
|
1168
|
+
|
|
993
1169
|
// Payloads arrive typed off the bus, but the board keeps its runtime
|
|
994
1170
|
// parsing (parse* helpers) because events are not validated at runtime.
|
|
995
1171
|
const upsertBase = (
|
|
@@ -1007,6 +1183,7 @@ export function createDispatchBoardStore(
|
|
|
1007
1183
|
const rerouteCount = parsePositiveInt(raw.rerouteCount) ?? previous?.rerouteCount;
|
|
1008
1184
|
const contextWindow = parsePositiveInt(raw.contextWindow) ?? previous?.contextWindow;
|
|
1009
1185
|
const taskSummary = parseTaskSummary(raw, previous?.taskSummary);
|
|
1186
|
+
const budget = cloneRunToolBudgetEnvelope(raw.budget) ?? previous?.budget;
|
|
1010
1187
|
const entry: DispatchBoardEntry = {
|
|
1011
1188
|
runId,
|
|
1012
1189
|
agentId: parseText(raw.agentId, previous?.agentId ?? "-"),
|
|
@@ -1017,6 +1194,7 @@ export function createDispatchBoardStore(
|
|
|
1017
1194
|
targetId: parseText(raw.targetId, previous?.targetId ?? "-"),
|
|
1018
1195
|
wireModelId: parseText(raw.wireModelId, previous?.wireModelId ?? "-"),
|
|
1019
1196
|
...(taskSummary !== undefined ? { taskSummary } : {}),
|
|
1197
|
+
...(budget !== undefined ? { budget } : {}),
|
|
1020
1198
|
status,
|
|
1021
1199
|
tokenCount: previous?.tokenCount ?? 0,
|
|
1022
1200
|
costUsd: previous?.costUsd ?? 0,
|
|
@@ -1037,8 +1215,7 @@ export function createDispatchBoardStore(
|
|
|
1037
1215
|
...(contextWindow !== undefined ? { contextWindow } : {}),
|
|
1038
1216
|
lastContextTokens: previous?.lastContextTokens ?? 0,
|
|
1039
1217
|
...(previous?.receiptId !== undefined ? { receiptId: previous.receiptId } : {}),
|
|
1040
|
-
|
|
1041
|
-
recentTools: previous?.recentTools ?? [],
|
|
1218
|
+
progress: previous?.progress ?? createWorkerProgressFold(),
|
|
1042
1219
|
...(previous?.retry ? { retry: { ...previous.retry } } : {}),
|
|
1043
1220
|
...(previous?.steerAcknowledgement ? { steerAcknowledgement: { ...previous.steerAcknowledgement } } : {}),
|
|
1044
1221
|
};
|
|
@@ -1074,7 +1251,7 @@ export function createDispatchBoardStore(
|
|
|
1074
1251
|
entry.costUsd = parseFiniteNumber(payload.costUsd, entry.costUsd);
|
|
1075
1252
|
entry.costProvenance = payload.costProvenance ?? "unknown";
|
|
1076
1253
|
entry.outcomeDetail = null;
|
|
1077
|
-
entry
|
|
1254
|
+
settleFromReceipt(entry);
|
|
1078
1255
|
// A terminal dispatch event is published only after the run's receipt is
|
|
1079
1256
|
// sealed at receipts/<runId>.json, so the run id is the receipt id here.
|
|
1080
1257
|
entry.receiptId = entry.runId;
|
|
@@ -1102,7 +1279,7 @@ export function createDispatchBoardStore(
|
|
|
1102
1279
|
entry.costUsd = parseFiniteNumber(payload.costUsd, entry.costUsd);
|
|
1103
1280
|
entry.costProvenance = payload.costProvenance ?? "unknown";
|
|
1104
1281
|
entry.outcomeDetail = resolveFailureDetail(payload, entry.outcomeDetail);
|
|
1105
|
-
entry
|
|
1282
|
+
settleFromReceipt(entry);
|
|
1106
1283
|
// A denied retry never reached a run, so no receipt was sealed for it;
|
|
1107
1284
|
// every other failure finalized through recordReceipt like a success.
|
|
1108
1285
|
if (payload.reason !== "retry_denied") entry.receiptId = entry.runId;
|
|
@@ -1126,7 +1303,7 @@ export function createDispatchBoardStore(
|
|
|
1126
1303
|
// wind down, so the history row can become terminal immediately.
|
|
1127
1304
|
entry.status = "aborted";
|
|
1128
1305
|
entry.finishedAtMs = Date.now();
|
|
1129
|
-
entry.
|
|
1306
|
+
entry.progress.settle();
|
|
1130
1307
|
} else {
|
|
1131
1308
|
if (isTerminalStatus(entry.status) && !wasRetrying) return;
|
|
1132
1309
|
entry.status = "cancelling";
|
|
@@ -1148,7 +1325,10 @@ export function createDispatchBoardStore(
|
|
|
1148
1325
|
entry.status = status;
|
|
1149
1326
|
if (status === "dead") {
|
|
1150
1327
|
entry.finishedAtMs ??= Date.now();
|
|
1151
|
-
|
|
1328
|
+
// A worker-side terminal signal arrives before any receipt is
|
|
1329
|
+
// sealed, so the run's own last durable message is the best truth
|
|
1330
|
+
// there is; the terminal bus event replaces it with the sealed one.
|
|
1331
|
+
entry.progress.settle();
|
|
1152
1332
|
delete entry.retry;
|
|
1153
1333
|
}
|
|
1154
1334
|
return;
|
|
@@ -1159,7 +1339,9 @@ export function createDispatchBoardStore(
|
|
|
1159
1339
|
entry.failoverHops = (entry.failoverHops ?? 0) + 1;
|
|
1160
1340
|
entry.status = "running";
|
|
1161
1341
|
entry.finishedAtMs = null;
|
|
1162
|
-
|
|
1342
|
+
// The projection keeps the tail the operator is reading and drops the
|
|
1343
|
+
// finished attempt's live state, exactly as the transcript block does.
|
|
1344
|
+
entry.progress.restart();
|
|
1163
1345
|
delete entry.retry;
|
|
1164
1346
|
return;
|
|
1165
1347
|
}
|
|
@@ -1183,20 +1365,10 @@ export function createDispatchBoardStore(
|
|
|
1183
1365
|
entry.ttftMs = Math.round(performance.now() - entry.startedAtClockMs);
|
|
1184
1366
|
}
|
|
1185
1367
|
}
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
}
|
|
1191
|
-
if (type === "clio_tool_finish") {
|
|
1192
|
-
const payload = (workerEvent as { payload?: { tool?: unknown } }).payload;
|
|
1193
|
-
const tool = parseNonEmptyString(payload?.tool);
|
|
1194
|
-
entry.currentTool = null;
|
|
1195
|
-
if (tool !== undefined) {
|
|
1196
|
-
entry.recentTools.unshift(tool);
|
|
1197
|
-
if (entry.recentTools.length > RECENT_TOOL_TRAIL_LIMIT) entry.recentTools.length = RECENT_TOOL_TRAIL_LIMIT;
|
|
1198
|
-
}
|
|
1199
|
-
}
|
|
1368
|
+
// The one fold that reads worker prose and tool activity. Everything the
|
|
1369
|
+
// board says about what a worker is saying or touching comes from here,
|
|
1370
|
+
// so the board and the transcript block cannot tell two stories.
|
|
1371
|
+
entry.progress.observe(payload.event);
|
|
1200
1372
|
if (type === "clio_steer_received") {
|
|
1201
1373
|
const steerPayload = (workerEvent as { payload?: { chars?: unknown } }).payload;
|
|
1202
1374
|
entry.steerAcknowledgement = {
|
|
@@ -1221,7 +1393,10 @@ export function createDispatchBoardStore(
|
|
|
1221
1393
|
if (!status) return;
|
|
1222
1394
|
entry.status = status;
|
|
1223
1395
|
entry.finishedAtMs ??= Date.now();
|
|
1224
|
-
|
|
1396
|
+
// The worker says it is done before the domain seals its receipt. Settle
|
|
1397
|
+
// on the durable message now so the card stops reading as live; the
|
|
1398
|
+
// terminal bus event seals it on the receipt shortly after.
|
|
1399
|
+
entry.progress.settle();
|
|
1225
1400
|
delete entry.retry;
|
|
1226
1401
|
}
|
|
1227
1402
|
}),
|
|
@@ -1270,7 +1445,8 @@ export function createDispatchBoardStore(
|
|
|
1270
1445
|
now,
|
|
1271
1446
|
);
|
|
1272
1447
|
if (entry) {
|
|
1273
|
-
|
|
1448
|
+
// A waiting retry has no live worker; the row projection already
|
|
1449
|
+
// renders a retrying row with no current tool.
|
|
1274
1450
|
entry.retry = { ...projection.retry };
|
|
1275
1451
|
if (entry.taskSummary === undefined && projection.taskSummary !== undefined) {
|
|
1276
1452
|
entry.taskSummary = projection.taskSummary;
|
|
@@ -1286,6 +1462,7 @@ export function createDispatchBoardStore(
|
|
|
1286
1462
|
entry.tokenCount = live.tokenCount;
|
|
1287
1463
|
entry.costUsd = live.costUsd;
|
|
1288
1464
|
entry.costProvenance = live.costProvenance;
|
|
1465
|
+
if (live.budget !== undefined) entry.budget = live.budget;
|
|
1289
1466
|
if (live.outcomePhase === "aborting") {
|
|
1290
1467
|
if (entry.status !== "completed" && entry.status !== "aborted") {
|
|
1291
1468
|
entry.status = "cancelling";
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { OutputVerbosity } from "../../core/defaults.js";
|
|
2
|
+
import { responseModelIdObservationLabel } from "../../core/response-model-id.js";
|
|
2
3
|
import { ToolNames } from "../../core/tool-names.js";
|
|
3
4
|
import {
|
|
4
5
|
type CostAggregate,
|
|
@@ -27,6 +28,7 @@ import {
|
|
|
27
28
|
type TurnSummary,
|
|
28
29
|
} from "../status/index.js";
|
|
29
30
|
import {
|
|
31
|
+
abbreviateModelId,
|
|
30
32
|
type ClioTheme,
|
|
31
33
|
type ClioToken,
|
|
32
34
|
clioTheme,
|
|
@@ -724,6 +726,17 @@ export function formatLastTurn(theme: ClioTheme, summary: TurnSummary): string {
|
|
|
724
726
|
}
|
|
725
727
|
if (summary.watchdogPeak >= 2) parts.push(theme.fg("warning", "slow"));
|
|
726
728
|
if (summary.truncated) parts.push(theme.fg("warning", "trunc"));
|
|
729
|
+
const observation = summary.responseModelIdObservation;
|
|
730
|
+
const observedId =
|
|
731
|
+
observation.state === "reported"
|
|
732
|
+
? observation.reportedModelId
|
|
733
|
+
: observation.state === "legacy-difference-only"
|
|
734
|
+
? observation.differingModelId
|
|
735
|
+
: null;
|
|
736
|
+
const observationText = `response model id observation ${responseModelIdObservationLabel(observation)}${
|
|
737
|
+
observedId === null ? "" : ` ${abbreviateModelId(observedId)}`
|
|
738
|
+
}`;
|
|
739
|
+
parts.push(theme.fg("warning", observationText));
|
|
727
740
|
return parts.join(theme.fg("dim", " · "));
|
|
728
741
|
}
|
|
729
742
|
|
|
@@ -482,10 +482,16 @@ export async function createInteractiveApplication(deps: InteractiveDeps): Promi
|
|
|
482
482
|
* here for the same reason as the leader flag above.
|
|
483
483
|
*/
|
|
484
484
|
let shutdownArmed = false;
|
|
485
|
+
/**
|
|
486
|
+
* Assigned after the presentation because it renders into it. The composer
|
|
487
|
+
* rail reads it through the optional chain before then as "no prompt".
|
|
488
|
+
*/
|
|
489
|
+
let overlayLifecycle: OverlayLifecycleController;
|
|
485
490
|
const presentation = createInteractivePresentation({
|
|
486
491
|
bus: deps.bus,
|
|
487
492
|
getLeaderArmed: () => leaderArmed,
|
|
488
493
|
getShutdownArmed: () => shutdownArmed,
|
|
494
|
+
isAwaitingApproval: () => overlayLifecycle?.getState() === "permission-confirm",
|
|
489
495
|
resolveVisibleEventSequence: (event) => visibleEventIngress.get(event)?.traceSequence ?? null,
|
|
490
496
|
resolveStreamIngress: (event) => visibleEventIngress.get(event) ?? null,
|
|
491
497
|
commitFrame: (reason) => shell.commitCurrentFrame(reason === "teardown" ? 300 : 30_000),
|
|
@@ -668,7 +674,6 @@ export async function createInteractiveApplication(deps: InteractiveDeps): Promi
|
|
|
668
674
|
});
|
|
669
675
|
editor.onSubmit = editorSubmit.submitEditorText;
|
|
670
676
|
|
|
671
|
-
let overlayLifecycle: OverlayLifecycleController;
|
|
672
677
|
const interactiveTickers = createInteractiveTickers({
|
|
673
678
|
tui,
|
|
674
679
|
dispatchBoardStore,
|
|
@@ -76,13 +76,14 @@ export interface InteractiveInputRuntimeDeps {
|
|
|
76
76
|
dispatchBoard: {
|
|
77
77
|
selectPrevious(): void;
|
|
78
78
|
selectNext(): void;
|
|
79
|
+
toggleDetail(): void;
|
|
79
80
|
};
|
|
80
81
|
steerSelectedDispatch: () => void;
|
|
81
82
|
cancelSelectedDispatch: () => void;
|
|
82
83
|
cancelActiveEditorBash: () => boolean;
|
|
83
84
|
isStreaming: () => boolean;
|
|
84
85
|
cancelActiveRun: () => void;
|
|
85
|
-
editor: { getText(): string; setText(text: string): void };
|
|
86
|
+
editor: { getText(): string; setText(text: string): void; handleInput?(data: string): void };
|
|
86
87
|
editorSubmit: {
|
|
87
88
|
openExternalEditorForInput(): void;
|
|
88
89
|
queueFollowUpFromEditor(): void;
|
|
@@ -211,6 +212,11 @@ export function createInteractiveInputRuntime(deps: InteractiveInputRuntimeDeps)
|
|
|
211
212
|
cancelPermission: () => deps.overlay.closeOverlay(),
|
|
212
213
|
confirmPermission: () => deps.overlay.confirmPermission(),
|
|
213
214
|
stopTurnFromPermission: () => deps.overlay.stopTurnFromPermission(),
|
|
215
|
+
composerHasDraft: () => deps.editor.getText().length > 0,
|
|
216
|
+
editDraft: (data) => {
|
|
217
|
+
deps.editor.handleInput?.(data);
|
|
218
|
+
deps.requestRender();
|
|
219
|
+
},
|
|
214
220
|
closeOverlay: () => deps.overlay.closeOverlay(),
|
|
215
221
|
selectPreviousDispatch: () => {
|
|
216
222
|
deps.dispatchBoard.selectPrevious();
|
|
@@ -220,6 +226,10 @@ export function createInteractiveInputRuntime(deps: InteractiveInputRuntimeDeps)
|
|
|
220
226
|
deps.dispatchBoard.selectNext();
|
|
221
227
|
deps.requestRender();
|
|
222
228
|
},
|
|
229
|
+
toggleSelectedDispatchDetail: () => {
|
|
230
|
+
deps.dispatchBoard.toggleDetail();
|
|
231
|
+
deps.requestRender();
|
|
232
|
+
},
|
|
223
233
|
steerSelectedDispatch: deps.steerSelectedDispatch,
|
|
224
234
|
cancelSelectedDispatch: deps.cancelSelectedDispatch,
|
|
225
235
|
cancelAskUser: () => deps.overlay.cancelAskUser(),
|
|
@@ -39,6 +39,7 @@ import type { SmoothStreamingMode } from "./stream-pacer.js";
|
|
|
39
39
|
import { processAutoPacingAllowed, resolveSmoothStreamingMode } from "./stream-pacing-policy.js";
|
|
40
40
|
import { formatTargetLabel } from "./theme/index.js";
|
|
41
41
|
import { createWelcomeDashboard, type WelcomeDashboardComponent } from "./welcome-dashboard.js";
|
|
42
|
+
import { readWorkerReceiptFacts } from "./worker-receipts.js";
|
|
42
43
|
import type { WorkspaceFacts } from "./workspace-facts.js";
|
|
43
44
|
|
|
44
45
|
export interface PresentationTickerHandle {
|
|
@@ -96,6 +97,8 @@ export interface InteractivePresentationDeps {
|
|
|
96
97
|
getLeaderArmed?: () => boolean;
|
|
97
98
|
/** Whether a Ctrl+C armed the double tap and its window is still open. */
|
|
98
99
|
getShutdownArmed?: () => boolean;
|
|
100
|
+
/** Whether a permission prompt owns the keyboard, for the composer's CONFIRM rail. */
|
|
101
|
+
isAwaitingApproval?: () => boolean;
|
|
99
102
|
getCwd?: () => string;
|
|
100
103
|
resolveVisibleEventSequence?: (event: ChatLoopEvent) => number | null;
|
|
101
104
|
resolveStreamIngress?: (
|
|
@@ -242,7 +245,13 @@ export function createInteractivePresentation(deps: InteractivePresentationDeps)
|
|
|
242
245
|
bus: deps.bus,
|
|
243
246
|
...(deps.getSettings ? { getSettings: deps.getSettings } : {}),
|
|
244
247
|
});
|
|
245
|
-
|
|
248
|
+
// The board settles a finished run on the same sealed receipt the transcript
|
|
249
|
+
// block reads, so the two surfaces agree on a worker's terminal answer.
|
|
250
|
+
const dispatchBoardStore = factories.createDispatchBoardStore(
|
|
251
|
+
deps.bus,
|
|
252
|
+
() => deps.dispatch.snapshot(),
|
|
253
|
+
(runId) => readWorkerReceiptFacts(runId),
|
|
254
|
+
);
|
|
246
255
|
const contextActivityStore = factories.createContextActivityStore(deps.bus);
|
|
247
256
|
|
|
248
257
|
const footerToolCounts = new Map<string, number>();
|
|
@@ -408,6 +417,7 @@ export function createInteractivePresentation(deps: InteractivePresentationDeps)
|
|
|
408
417
|
);
|
|
409
418
|
},
|
|
410
419
|
isStreaming: () => deps.chat.isStreaming(),
|
|
420
|
+
...(deps.isAwaitingApproval ? { isAwaitingApproval: deps.isAwaitingApproval } : {}),
|
|
411
421
|
willEnterSteer: (text) => willEnterSteerActiveWork(deps, text),
|
|
412
422
|
getSubmitKeyLabel: () => formatKeyLabel(keybindings.getKeys("tui.input.submit")[0], "Enter"),
|
|
413
423
|
getNewlineKeyLabel: () => formatKeyLabel(keybindings.getKeys("tui.input.newLine")[0], "Shift+Enter"),
|