@iowarp/clio-coder 0.4.5 → 0.4.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 +24 -0
- package/CONTRIBUTING.md +14 -9
- package/NOTICE +16 -0
- package/README.md +312 -685
- package/dist/{acp-H3CU2HBQ.js → acp-UVEVWSDL.js} +6 -6
- package/dist/{agents-IXQQZ7BD.js → agents-VWY37DTK.js} +31 -30
- package/dist/assets/codewiki.json +1 -1
- package/dist/{auth-R7KYF7N3.js → auth-PCV2XQDV.js} +6 -6
- package/dist/{builtins-CRFDQUVP.js → builtins-PD652MTK.js} +3 -3
- package/dist/{chunk-SDWNDICN.js → chunk-24T2YZ7A.js} +58 -56
- package/dist/{chunk-FSTIBAGM.js → chunk-2AKYFREM.js} +7 -7
- package/dist/{chunk-SE4ZKILO.js → chunk-3BJGMT2D.js} +30 -9
- package/dist/{chunk-GVYWZNRK.js → chunk-3HQVBIKD.js} +2 -2
- package/dist/{chunk-EWD5P3GK.js → chunk-3JLFK5MW.js} +4 -4
- package/dist/{chunk-O2RQ7LCK.js → chunk-3O2ZAUGQ.js} +2 -2
- package/dist/{chunk-GXCCHKN6.js → chunk-3QKXHCGS.js} +4 -4
- package/dist/{chunk-YEJIG7LN.js → chunk-3WBALRQH.js} +7 -7
- package/dist/{chunk-I7VLWRK2.js → chunk-4DU2TVI4.js} +5 -5
- package/dist/{chunk-TEG3RYVZ.js → chunk-4U4TKZFR.js} +3 -3
- package/dist/{chunk-EHCPSZJF.js → chunk-4VMV6OS6.js} +2 -2
- package/dist/{chunk-T7FDXNRU.js → chunk-536HXRFE.js} +4 -4
- package/dist/{chunk-ZMZQEBKI.js → chunk-5QRX7SLS.js} +8 -8
- package/dist/{chunk-RXIOCVBZ.js → chunk-6HAO6X5B.js} +4 -4
- package/dist/{chunk-HGSLRG33.js → chunk-6OII3LOK.js} +2 -2
- package/dist/{chunk-U2JOLB7O.js → chunk-6RYDAVQT.js} +32 -32
- package/dist/{chunk-T53XI6I7.js → chunk-6VRRJT7L.js} +2 -2
- package/dist/{chunk-RLTT5GVY.js → chunk-7JSSKXG2.js} +4 -4
- package/dist/{chunk-633EXGZA.js → chunk-ACCKVSHU.js} +3 -3
- package/dist/{chunk-6SYEGZI3.js → chunk-B3MOMLJ4.js} +2 -2
- package/dist/{chunk-UXAC2FX4.js → chunk-BLTEAUCB.js} +3 -3
- package/dist/{chunk-DRD2A54W.js → chunk-BT7HGDOX.js} +15 -15
- package/dist/{chunk-F34AR6O3.js → chunk-DXLUVUD4.js} +5 -5
- package/dist/{chunk-HR3HPQ76.js → chunk-E7RF4KBO.js} +2 -2
- package/dist/chunk-EM2WNQS2.js +93 -0
- package/dist/{chunk-KYKNBD4Y.js → chunk-F5Z7EWMN.js} +953 -233
- package/dist/{chunk-VLPTKCKT.js → chunk-GLYBJ4RQ.js} +3 -3
- package/dist/{chunk-KDWLXVMN.js → chunk-HPTJF6DG.js} +16 -39
- package/dist/{chunk-6CDNRHWH.js → chunk-HZ2T2YED.js} +2 -2
- package/dist/{chunk-UTUNT5QL.js → chunk-HZFBVU5X.js} +7 -7
- package/dist/{chunk-KF6GFW6O.js → chunk-I2K2NCZQ.js} +7 -7
- package/dist/{chunk-5DDOTTG4.js → chunk-IGLVVNEG.js} +2 -2
- package/dist/{chunk-4SVIVOMC.js → chunk-IKRWLEDA.js} +5 -5
- package/dist/{chunk-R6MAYTMS.js → chunk-JMPL7NUB.js} +3 -3
- package/dist/{chunk-5ZMXGW7P.js → chunk-KAFMW7WM.js} +4 -85
- package/dist/{chunk-R6MICQYU.js → chunk-LIN2K3EL.js} +4 -4
- package/dist/{chunk-QEQISF75.js → chunk-LK2FN5JY.js} +1 -1
- package/dist/{chunk-YYB3MC65.js → chunk-N2OKBREN.js} +2 -2
- package/dist/{chunk-XXBQKW32.js → chunk-NNWJYUNO.js} +4 -4
- package/dist/{chunk-TZ7MFRG4.js → chunk-P5JMBPDJ.js} +22 -20
- package/dist/{chunk-WFKDPU7U.js → chunk-P6M7RKMI.js} +2 -2
- package/dist/{chunk-MDU3C27C.js → chunk-PZN2LQY6.js} +5 -5
- package/dist/{chunk-S32UQ5GL.js → chunk-Q7NNUHQV.js} +15 -15
- package/dist/{chunk-U7VQ4NI4.js → chunk-QCOV5DFY.js} +5 -5
- package/dist/{chunk-MC3JSVR6.js → chunk-QJMRYTCX.js} +7 -7
- package/dist/{chunk-DIMRLNIC.js → chunk-QNWRHOMF.js} +3 -3
- package/dist/{chunk-ILJ7DWGE.js → chunk-QXFBABFJ.js} +4 -4
- package/dist/chunk-R2CT5YDO.js +66 -0
- package/dist/{chunk-E3BQTQUP.js → chunk-RGZNQ5CT.js} +3 -3
- package/dist/{chunk-SBZX6ARC.js → chunk-SXQKNKRA.js} +7 -7
- package/dist/{chunk-WLMSOT73.js → chunk-TJFDL26W.js} +3 -3
- package/dist/{chunk-EQEJJ53P.js → chunk-VAHLPXPY.js} +2 -2
- package/dist/{chunk-7TKWWPD7.js → chunk-VANN3GDP.js} +3 -23
- package/dist/{chunk-GNQRWTMY.js → chunk-VHMX7SXF.js} +2 -2
- package/dist/{chunk-UL36QJX6.js → chunk-WKUYQBCG.js} +6 -6
- package/dist/{chunk-FKIOX6DS.js → chunk-WNGF5AJX.js} +9 -4
- package/dist/{chunk-ICO4TQ2F.js → chunk-WV6TDAPN.js} +36 -20
- package/dist/{chunk-K6RDB7IA.js → chunk-X3BI7HTV.js} +9 -9
- package/dist/{chunk-NY6IWDVU.js → chunk-XWMSPJYN.js} +2 -2
- package/dist/{chunk-7HBHHVCK.js → chunk-YPRSNUTC.js} +2 -2
- package/dist/{chunk-ONRTXOZY.js → chunk-ZS43Y2FT.js} +2 -2
- package/dist/cli/index.js +27 -27
- package/dist/{clio-5I2IBFYL.js → clio-QQCMTUZE.js} +6 -6
- package/dist/{code-nav-YXIAEZVS.js → code-nav-GUPM6766.js} +8 -8
- package/dist/{config-Y2JDJ3JJ.js → config-IIM7YOD3.js} +42 -41
- package/dist/{configure-SML6UBZ5.js → configure-4ZM5HWJX.js} +28 -10
- package/dist/{context-S2EEC3EJ.js → context-44BZLJ3J.js} +12 -12
- package/dist/{context-P5YSEO55.js → context-AIDVQKMW.js} +34 -33
- package/dist/{context-QQ53HSGJ.js → context-OL5P3HEP.js} +25 -24
- package/dist/{context-clear-TMEQCOTH.js → context-clear-OQV76MYP.js} +34 -33
- package/dist/{context-working-set-CNLSQN5K.js → context-working-set-ZUW3EGLS.js} +10 -9
- package/dist/{detail-5572VLBO.js → detail-UXPWRANG.js} +35 -34
- package/dist/{dispatch-runner-FRHD6P2O.js → dispatch-runner-P3ZW6G77.js} +38 -37
- package/dist/{doctor-N3LCM2LG.js → doctor-JJVGPZWT.js} +18 -17
- package/dist/{eval-BUU3ZOUZ.js → eval-V2RTN3UH.js} +26 -25
- package/dist/{evidence-DTGEN456.js → evidence-35PYWGSO.js} +34 -33
- package/dist/{evidence-SQ5DNFEN.js → evidence-CIGGKI6J.js} +36 -35
- package/dist/{evolve-IJNUFZDS.js → evolve-33SM3NJP.js} +34 -33
- package/dist/{fleet-ZNNVSYFO.js → fleet-LFO7SIPC.js} +62 -61
- package/dist/{fleet-commands-5YE7NPJL.js → fleet-commands-RLK72GU3.js} +9 -9
- package/dist/fleet-decisions-3SBSCTCW.js +1 -1
- package/dist/{fleet-graph-6HHPWF5T.js → fleet-graph-JZIBCGMW.js} +11 -11
- package/dist/{fleet-inspect-JEYMGCGC.js → fleet-inspect-2L5YGGZJ.js} +35 -34
- package/dist/{fleet-validate-4PM4GDDL.js → fleet-validate-LIAVKDUU.js} +12 -12
- package/dist/{fleet-verify-NJDTHTO3.js → fleet-verify-JIK3OQZR.js} +34 -33
- package/dist/{fleet-view-UP2BWQQY.js → fleet-view-IWHYGQKK.js} +35 -34
- package/dist/{init-S3GZQ5ZF.js → init-NJEDOTQW.js} +46 -45
- package/dist/{interop-YOTGXFJM.js → interop-RVCAYECG.js} +4 -4
- package/dist/{inventory-K2OZ36YJ.js → inventory-74UL7UHU.js} +35 -34
- package/dist/{library-GJI6W6OK.js → library-CTKZA7K3.js} +10 -10
- package/dist/{memory-OQOXQGYD.js → memory-I76I5VUH.js} +34 -33
- package/dist/{models-3IK2Y5WE.js → models-D4YDIQZQ.js} +19 -18
- package/dist/{monitor-FTJLTKCQ.js → monitor-NKVD55HX.js} +40 -39
- package/dist/{orchestrator-N47O7SFD.js → orchestrator-I6OATVPK.js} +658 -1092
- package/dist/{preload-65U34NG6.js → preload-EJAWJEIO.js} +34 -33
- package/dist/{reset-MUVQDLNL.js → reset-4XMQKDNZ.js} +5 -3
- package/dist/{resources-LM76COAO.js → resources-TVYVXK3Q.js} +10 -10
- package/dist/{run-3VFZQ33N.js → run-OWQNNSJU.js} +61 -60
- package/dist/{share-4JUB4BL7.js → share-NPZZMUXG.js} +11 -11
- package/dist/{skills-S36AAJHU.js → skills-OXFEAWMT.js} +12 -12
- package/dist/{skills-eval-G6T57L7M.js → skills-eval-J5TCKSJA.js} +38 -37
- package/dist/{skills-inventory-WLM46PJH.js → skills-inventory-7II4DM57.js} +10 -10
- package/dist/{slash-commands-GBARG3Y7.js → slash-commands-MDSDSWYF.js} +26 -25
- package/dist/{targets-N5REUETU.js → targets-KILZEOCE.js} +36 -20
- package/dist/{tasks-6BVY5YZF.js → tasks-R6P6OV22.js} +7 -7
- package/dist/{terminal-lease-57UEOJKS.js → terminal-lease-LZCG2KN3.js} +2 -2
- package/dist/{upgrade-4HD5HAIV.js → upgrade-D2OZH6IQ.js} +9 -9
- package/dist/{usage-4GWM2WWP.js → usage-PIASNUS4.js} +40 -39
- package/dist/{verifiers-GSKWKWVG.js → verifiers-LTHUY2PX.js} +9 -9
- package/dist/{verify-3AS3ULCY.js → verify-HYF7KBVE.js} +7 -7
- package/dist/{wiki-generate-7BV7XWIY.js → wiki-generate-RK7JQ5NP.js} +51 -50
- package/dist/worker/entry.js +41 -39
- package/docs/architecture/context-engine.md +1 -1
- package/docs/architecture/tui-design.md +45 -30
- package/docs/guide/commands-and-modes.md +26 -25
- package/docs/guide/configuration-and-targets.md +122 -27
- package/docs/guide/configuration-reference.md +9 -4
- package/docs/guide/fleet-dispatch.md +1 -6
- package/docs/guide/glossary.md +1 -1
- package/docs/guide/installation-and-lifecycle.md +44 -11
- package/docs/process/development-pipeline.md +1 -1
- package/docs/process/documentation-coverage.md +1 -1
- package/docs/process/release-cut-checklist.md +18 -6
- package/package.json +1 -1
- package/src/cli/ask.ts +10 -10
- package/src/cli/configure-editor.ts +51 -0
- package/src/cli/configure-interop.ts +1 -1
- package/src/cli/configure-oauth.ts +1 -1
- package/src/cli/configure-onboarding.ts +232 -96
- package/src/cli/configure-prompts.ts +76 -0
- package/src/cli/configure-quick.ts +274 -0
- package/src/cli/configure-target.ts +14 -2
- package/src/cli/configure.ts +413 -174
- package/src/cli/oauth-manual-input.ts +1 -1
- package/src/cli/reset.ts +2 -0
- package/src/cli/select.ts +35 -4
- package/src/cli/upgrade.ts +2 -2
- package/src/core/config.ts +6 -1
- package/src/core/defaults.ts +33 -11
- package/src/{interactive → core}/external-editor.ts +4 -4
- package/src/domains/config/classify.ts +1 -1
- package/src/domains/config/keybindings.ts +3 -31
- package/src/domains/providers/probe/fingerprint.ts +13 -6
- package/src/domains/providers/probe/reasoning.ts +2 -0
- package/src/domains/providers/runtimes/common/probe-helpers.ts +23 -7
- package/src/domains/providers/runtimes/protocol/anthropic-compat.ts +7 -2
- package/src/domains/providers/runtimes/protocol/openai-compat.ts +10 -4
- package/src/interactive/application-controller.ts +1 -31
- package/src/interactive/chat-panel.ts +163 -637
- package/src/interactive/chat-renderer.ts +13 -30
- package/src/interactive/dispatch-board.ts +1 -3
- package/src/interactive/editor-submit.ts +8 -31
- package/src/interactive/footer/dashboard.ts +8 -3
- package/src/interactive/footer/widgets.ts +50 -91
- package/src/interactive/interactive-application.ts +16 -42
- package/src/interactive/interactive-event-projection.ts +2 -44
- package/src/interactive/interactive-input-runtime.ts +3 -28
- package/src/interactive/interactive-presentation.ts +30 -9
- package/src/interactive/interactive-slash-runtime.ts +3 -40
- package/src/interactive/layout.ts +29 -4
- package/src/interactive/overlay-general-openers.ts +2 -0
- package/src/interactive/overlay-lifecycle.ts +1 -0
- package/src/interactive/overlay-session-lifecycle.ts +1 -1
- package/src/interactive/overlays/settings.ts +9 -8
- package/src/interactive/renderers/preview.ts +16 -0
- package/src/interactive/renderers/tool-execution.ts +67 -160
- package/src/interactive/renderers/worker-entry.ts +57 -41
- package/src/interactive/slash-autocomplete.ts +1 -6
- package/src/interactive/slash-commands.ts +7 -39
- package/src/interactive/status/controller.ts +0 -5
- package/src/interactive/status/state-machine.ts +44 -26
- package/src/interactive/status/types.ts +3 -0
- package/src/interactive/status/verbs.ts +13 -12
- package/src/interactive/transcript-detail.ts +50 -112
- package/src/interactive/view/artifacts.ts +4 -0
- package/src/interactive/view/view-overlay.ts +44 -34
- package/src/tools/presentation.ts +1 -1
- package/src/tools/registry.ts +1 -1
- package/src/tools/verify/numeric.ts +4 -4
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import type { TranscriptDetailPolicy } from "../transcript-detail.js";
|
|
2
|
+
import { transcriptDetail } from "../transcript-detail.js";
|
|
3
|
+
import { previewBudget, previewRows } from "./preview.js";
|
|
1
4
|
/**
|
|
2
5
|
* Structured renderer for tool-execution chat segments (Slice A of the
|
|
3
6
|
* pi-coding-agent parity work). pi-coding-agent renders every tool call as
|
|
@@ -48,10 +51,7 @@ const ARG_PREVIEW_LIMIT = 60;
|
|
|
48
51
|
const WEB_FETCH_ARG_PREVIEW_LIMIT = 140;
|
|
49
52
|
const FULL_RESULT_PREVIEW_LIMIT = 60_000;
|
|
50
53
|
const FULL_RESULT_ROW_LIMIT = 120;
|
|
51
|
-
const STREAMING_RESULT_ROW_LIMIT = 20;
|
|
52
54
|
const ARGS_BODY_LINE_LIMIT = 24;
|
|
53
|
-
/** Rows of mutation diff a folded edit/write row keeps visible before it defers to the body. */
|
|
54
|
-
const FOLDED_DIFF_ROW_LIMIT = 24;
|
|
55
55
|
const STATUS_OK_GLYPH = GLYPH.ok;
|
|
56
56
|
const STATUS_ERROR_GLYPH = GLYPH.error;
|
|
57
57
|
|
|
@@ -111,6 +111,8 @@ export interface ToolBodyRenderOptions {
|
|
|
111
111
|
unbounded?: boolean;
|
|
112
112
|
/** Live rows color mutation diffs; replay and export deliberately use plain text. */
|
|
113
113
|
diffStyle?: "color" | "plain";
|
|
114
|
+
detail?: TranscriptDetailPolicy;
|
|
115
|
+
terminalRows?: number;
|
|
114
116
|
}
|
|
115
117
|
|
|
116
118
|
/** Row cap for a tool body: unbounded lifts both the row and char limits. */
|
|
@@ -1110,30 +1112,6 @@ function renderResultBlock(
|
|
|
1110
1112
|
return renderOutputRows(resultText(unwrapped, resultCharLimit(opts)), width, isError, resultRowLimit(opts));
|
|
1111
1113
|
}
|
|
1112
1114
|
|
|
1113
|
-
/**
|
|
1114
|
-
* Header-only render for a tool call that has not yet finished. Used by the
|
|
1115
|
-
* live chat panel from streamed argument formation through execution. The
|
|
1116
|
-
* lifecycle tail distinguishes a forming call, a ready call, and a running
|
|
1117
|
-
* call without inventing execution time before Pi starts the tool.
|
|
1118
|
-
*/
|
|
1119
|
-
export function renderToolCallHeader(call: ToolExecutionStart, width: number): string[] {
|
|
1120
|
-
return wrap(
|
|
1121
|
-
headerLine(call.toolName, call.args, call.phase ?? "running", {
|
|
1122
|
-
elapsedMs: call.elapsedMs,
|
|
1123
|
-
}),
|
|
1124
|
-
width,
|
|
1125
|
-
);
|
|
1126
|
-
}
|
|
1127
|
-
|
|
1128
|
-
/** Running-only footer used when an operator pauses live output; execution continues. */
|
|
1129
|
-
export function renderToolRunningStatus(call: ToolExecutionStart, width: number): string[] {
|
|
1130
|
-
const elapsed = optionalCompactMs(call.elapsedMs);
|
|
1131
|
-
return [
|
|
1132
|
-
...renderToolCallHeader(call, width),
|
|
1133
|
-
...indentAndWrap(dim(elapsed ? `live output paused · ${elapsed}` : "live output paused"), width, false),
|
|
1134
|
-
];
|
|
1135
|
-
}
|
|
1136
|
-
|
|
1137
1115
|
function sublineStatus(call: ToolExecutionStart | ToolExecutionFinished): HeaderStatus {
|
|
1138
1116
|
// Discriminate on `result` rather than `isError`: only `ToolExecutionFinished`
|
|
1139
1117
|
// carries a `result` field, so a `ToolExecutionStart` with a stray
|
|
@@ -1143,59 +1121,8 @@ function sublineStatus(call: ToolExecutionStart | ToolExecutionFinished): Header
|
|
|
1143
1121
|
return call.isError ? "error" : "ok";
|
|
1144
1122
|
}
|
|
1145
1123
|
|
|
1146
|
-
/**
|
|
1147
|
-
|
|
1148
|
-
* ▸ <body><status>
|
|
1149
|
-
* `status` is "" for in-flight, " ✓" green for success, " ✗" red for error.
|
|
1150
|
-
* Output is width-wrapped via wrapTextWithAnsi.
|
|
1151
|
-
*
|
|
1152
|
-
* When `expandKey` is supplied AND the call has finished, a dim ` (<key>)`
|
|
1153
|
-
* discoverability hint rides at the very end of the atomic status tail so users
|
|
1154
|
-
* see how to expand the collapsed block. Composing the full line (facts, status
|
|
1155
|
-
* glyph, duration, offload, and hint) before wrapping keeps the status glyph
|
|
1156
|
-
* and duration together on one row. The hint is suppressed for in-flight calls
|
|
1157
|
-
* (still running, no useful body to expand yet) and when `expandKey` is
|
|
1158
|
-
* empty/undefined (no key bound, hint would be misleading). The renderer never
|
|
1159
|
-
* imports the keybindings manager directly; the caller resolves the key string
|
|
1160
|
-
* and passes it in to keep this module pure.
|
|
1161
|
-
*/
|
|
1162
|
-
export interface ToolSublineRenderOptions {
|
|
1163
|
-
/** Live rows color the folded mutation diff; replay and export request plain rows. */
|
|
1164
|
-
diffStyle?: "color" | "plain";
|
|
1165
|
-
/**
|
|
1166
|
-
* Whether the folded row carries the extras its tool's presentation asks
|
|
1167
|
-
* for (today the bounded mutation diff). The bare subline level drops them;
|
|
1168
|
-
* the failure excerpt is not one of them and always rides the row.
|
|
1169
|
-
*/
|
|
1170
|
-
foldedExtras?: "per-tool" | "none";
|
|
1171
|
-
}
|
|
1172
|
-
|
|
1173
|
-
/**
|
|
1174
|
-
* The bounded diff a folded mutation row keeps under itself, when the tool's
|
|
1175
|
-
* presentation asks for one and the result carries one. A folded `edit` that
|
|
1176
|
-
* hid its change told the operator nothing they could act on.
|
|
1177
|
-
*/
|
|
1178
|
-
function foldedDiffRows(finished: ToolExecutionFinished, width: number, opts: ToolSublineRenderOptions): string[] {
|
|
1179
|
-
if (opts.foldedExtras === "none") return [];
|
|
1180
|
-
if (finished.isError || isNonExecutedOutcome(finished.outcome)) return [];
|
|
1181
|
-
const presentation =
|
|
1182
|
-
toolResultPresentationPolicy(finished.result) ?? toolPresentationPolicy(finished.toolName, finished.args);
|
|
1183
|
-
if (!presentation.showDiffWhenFolded) return [];
|
|
1184
|
-
const diff = resultDiff(finished.result);
|
|
1185
|
-
if (diff === null) return [];
|
|
1186
|
-
return truncateRowsMiddle(
|
|
1187
|
-
renderMutationDiffBlock(diff, width, opts.diffStyle !== "plain"),
|
|
1188
|
-
FOLDED_DIFF_ROW_LIMIT,
|
|
1189
|
-
false,
|
|
1190
|
-
);
|
|
1191
|
-
}
|
|
1192
|
-
|
|
1193
|
-
export function renderToolSubline(
|
|
1194
|
-
call: ToolExecutionStart | ToolExecutionFinished,
|
|
1195
|
-
width: number,
|
|
1196
|
-
expandKey?: string,
|
|
1197
|
-
opts: ToolSublineRenderOptions = {},
|
|
1198
|
-
): string[] {
|
|
1124
|
+
/** Stable action identity, outcome, and captured-result metadata. */
|
|
1125
|
+
export function renderToolSubline(call: ToolExecutionStart | ToolExecutionFinished, width: number): string[] {
|
|
1199
1126
|
const status = sublineStatus(call);
|
|
1200
1127
|
const meta: StatusMeta =
|
|
1201
1128
|
"result" in call
|
|
@@ -1208,11 +1135,7 @@ export function renderToolSubline(
|
|
|
1208
1135
|
: { elapsedMs: call.elapsedMs };
|
|
1209
1136
|
const parts = sublineParts(call, status, meta);
|
|
1210
1137
|
const excerpt = "result" in call ? failureExcerpt(call, width) : "";
|
|
1211
|
-
|
|
1212
|
-
const tail = showHint ? `${parts.tail}${dim(` (${expandKey})`)}` : parts.tail;
|
|
1213
|
-
const lines = wrapSublineWithTail(`${parts.lead}${excerpt}`, tail, width);
|
|
1214
|
-
if ("result" in call) lines.push(...foldedDiffRows(call, width, opts));
|
|
1215
|
-
return lines;
|
|
1138
|
+
return wrapSublineWithTail(`${parts.lead}${excerpt}`, parts.tail, width);
|
|
1216
1139
|
}
|
|
1217
1140
|
|
|
1218
1141
|
/**
|
|
@@ -1300,6 +1223,7 @@ export function renderToolResultOnly(
|
|
|
1300
1223
|
width: number,
|
|
1301
1224
|
opts: ToolBodyRenderOptions = {},
|
|
1302
1225
|
): string[] {
|
|
1226
|
+
if (!opts.unbounded && opts.detail) return renderToolPreview(finished, width, opts.detail, opts);
|
|
1303
1227
|
const status: HeaderStatus = finished.isError ? "error" : "ok";
|
|
1304
1228
|
const statusMeta: StatusMeta = {
|
|
1305
1229
|
durationMs: finished.durationMs,
|
|
@@ -1315,50 +1239,6 @@ export function renderToolResultOnly(
|
|
|
1315
1239
|
return out;
|
|
1316
1240
|
}
|
|
1317
1241
|
|
|
1318
|
-
/**
|
|
1319
|
-
* Streaming render for an in-flight tool call whose expanded block should
|
|
1320
|
-
* surface the latest partial output. Used by the chat panel between
|
|
1321
|
-
* `tool_execution_start` and `tool_execution_end` when the user has expanded
|
|
1322
|
-
* the tool segment. The header carries the running lifecycle tail, secondary
|
|
1323
|
-
* arguments remain visible, and the latest cumulative Pi result renders under
|
|
1324
|
-
* a labeled live-output rail capped at the streaming row limit. An empty result renders
|
|
1325
|
-
* `(no output yet)` so the user can distinguish a slow start from a stalled
|
|
1326
|
-
* call.
|
|
1327
|
-
*/
|
|
1328
|
-
export function renderToolStreamingExecution(
|
|
1329
|
-
call: ToolExecutionStart,
|
|
1330
|
-
width: number,
|
|
1331
|
-
partialResult: unknown,
|
|
1332
|
-
): string[] {
|
|
1333
|
-
const out: string[] = [];
|
|
1334
|
-
out.push(...renderToolCallHeader({ ...call, phase: "running" }, width));
|
|
1335
|
-
out.push(...renderArgsBody(call.toolName, call.args, width, false));
|
|
1336
|
-
const partial: ToolExecutionFinished = {
|
|
1337
|
-
toolCallId: call.toolCallId,
|
|
1338
|
-
toolName: call.toolName,
|
|
1339
|
-
args: call.args,
|
|
1340
|
-
result: partialResult,
|
|
1341
|
-
isError: false,
|
|
1342
|
-
};
|
|
1343
|
-
out.push(...renderOutputMeta(partial, width, false, "live output"));
|
|
1344
|
-
const partialOutput = unwrapResultEnvelope(partialResult);
|
|
1345
|
-
if (isEmptyResult(partialOutput)) {
|
|
1346
|
-
out.push(...indentAndWrap(dim("(no output yet)"), width, false));
|
|
1347
|
-
} else {
|
|
1348
|
-
if (call.toolName === "bash") {
|
|
1349
|
-
const bashArgs = asBashArgs(redactToolArgs(call.args));
|
|
1350
|
-
if (bashArgs !== null) {
|
|
1351
|
-
const commandLine = `${cyanBold("$")} ${highlightBashCommand(stripShellWrapperForDisplay(bashArgs.command))}`;
|
|
1352
|
-
out.push(...indentAndWrap(commandLine, width, false));
|
|
1353
|
-
}
|
|
1354
|
-
}
|
|
1355
|
-
out.push(
|
|
1356
|
-
...renderOutputRows(resultText(partialOutput, FULL_RESULT_PREVIEW_LIMIT), width, false, STREAMING_RESULT_ROW_LIMIT),
|
|
1357
|
-
);
|
|
1358
|
-
}
|
|
1359
|
-
return out;
|
|
1360
|
-
}
|
|
1361
|
-
|
|
1362
1242
|
/** Display-only lifecycle for operator-issued `!` and `!!` bash commands. */
|
|
1363
1243
|
export interface BashTranscriptExecution {
|
|
1364
1244
|
command: string;
|
|
@@ -1372,13 +1252,6 @@ export interface BashTranscriptExecution {
|
|
|
1372
1252
|
fullOutputPath?: string | undefined;
|
|
1373
1253
|
excludeFromContext?: boolean | undefined;
|
|
1374
1254
|
error?: string | undefined;
|
|
1375
|
-
/**
|
|
1376
|
-
* Whether the block draws its one-line row instead of the full body. The
|
|
1377
|
-
* caller resolves this from the transcript detail policy and the operator's
|
|
1378
|
-
* override, the same way the panel resolves a model bash call. Omitted means
|
|
1379
|
-
* folded.
|
|
1380
|
-
*/
|
|
1381
|
-
folded?: boolean | undefined;
|
|
1382
1255
|
}
|
|
1383
1256
|
|
|
1384
1257
|
/**
|
|
@@ -1389,7 +1262,7 @@ export interface BashTranscriptExecution {
|
|
|
1389
1262
|
export function renderBashTranscriptExecution(
|
|
1390
1263
|
execution: BashTranscriptExecution,
|
|
1391
1264
|
width: number,
|
|
1392
|
-
|
|
1265
|
+
_expandKey?: string,
|
|
1393
1266
|
bodyOptions: ToolBodyRenderOptions = {},
|
|
1394
1267
|
): string[] {
|
|
1395
1268
|
const shownBytes = Buffer.byteLength(execution.output, "utf8");
|
|
@@ -1409,30 +1282,12 @@ export function renderBashTranscriptExecution(
|
|
|
1409
1282
|
content: [{ type: "text", text: execution.output }],
|
|
1410
1283
|
details,
|
|
1411
1284
|
};
|
|
1412
|
-
const folded = execution.folded !== false;
|
|
1413
1285
|
if (execution.running) {
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
{
|
|
1417
|
-
toolCallId: "local-bash",
|
|
1418
|
-
toolName: "bash",
|
|
1419
|
-
args,
|
|
1420
|
-
elapsedMs: execution.elapsedMs,
|
|
1421
|
-
phase: "running",
|
|
1422
|
-
},
|
|
1423
|
-
width,
|
|
1424
|
-
);
|
|
1425
|
-
}
|
|
1426
|
-
return renderToolStreamingExecution(
|
|
1427
|
-
{
|
|
1428
|
-
toolCallId: "local-bash",
|
|
1429
|
-
toolName: "bash",
|
|
1430
|
-
args,
|
|
1431
|
-
elapsedMs: execution.elapsedMs,
|
|
1432
|
-
phase: "running",
|
|
1433
|
-
},
|
|
1286
|
+
return renderToolPreview(
|
|
1287
|
+
{ toolCallId: "local-bash", toolName: "bash", args, phase: "running", elapsedMs: execution.elapsedMs },
|
|
1434
1288
|
width,
|
|
1435
|
-
|
|
1289
|
+
bodyOptions.detail ?? transcriptDetail(),
|
|
1290
|
+
{ ...bodyOptions, operator: true, partialResult: result },
|
|
1436
1291
|
);
|
|
1437
1292
|
}
|
|
1438
1293
|
const message = execution.error?.trim();
|
|
@@ -1454,5 +1309,57 @@ export function renderBashTranscriptExecution(
|
|
|
1454
1309
|
...(execution.fullOutputPath !== undefined ? { offloadPath: execution.fullOutputPath } : {}),
|
|
1455
1310
|
},
|
|
1456
1311
|
};
|
|
1457
|
-
return
|
|
1312
|
+
return bodyOptions.unbounded
|
|
1313
|
+
? renderToolExecution(finished, width, bodyOptions)
|
|
1314
|
+
: renderToolPreview(finished, width, bodyOptions.detail ?? transcriptDetail(), { ...bodyOptions, operator: true });
|
|
1315
|
+
}
|
|
1316
|
+
|
|
1317
|
+
/** One action identity plus a bounded result; arguments and full output belong in /view. */
|
|
1318
|
+
export function renderToolPreview(
|
|
1319
|
+
call: ToolExecutionStart | ToolExecutionFinished,
|
|
1320
|
+
width: number,
|
|
1321
|
+
detail: TranscriptDetailPolicy,
|
|
1322
|
+
options: ToolBodyRenderOptions & { terminalRows?: number; partialResult?: unknown; operator?: boolean } = {},
|
|
1323
|
+
): string[] {
|
|
1324
|
+
const finished = "result" in call ? call : undefined;
|
|
1325
|
+
const failure = finished?.isError === true || finished?.outcome !== undefined;
|
|
1326
|
+
const limit = previewBudget(
|
|
1327
|
+
failure
|
|
1328
|
+
? detail.errorRows
|
|
1329
|
+
: options.operator
|
|
1330
|
+
? detail.operatorBashRows
|
|
1331
|
+
: call.toolName === "bash"
|
|
1332
|
+
? detail.bashRows
|
|
1333
|
+
: detail.resultRows,
|
|
1334
|
+
options.terminalRows,
|
|
1335
|
+
);
|
|
1336
|
+
const rows = renderToolSubline(call, width);
|
|
1337
|
+
const result = finished?.result ?? options.partialResult;
|
|
1338
|
+
const diff = finished && !failure ? resultDiff(result) : null;
|
|
1339
|
+
if (diff !== null && detail.diffRows > 0) {
|
|
1340
|
+
rows.push(
|
|
1341
|
+
...previewRows(
|
|
1342
|
+
renderMutationDiffBlock(redactSecretString(diff), width, options.diffStyle !== "plain"),
|
|
1343
|
+
previewBudget(detail.diffRows, options.terminalRows),
|
|
1344
|
+
width,
|
|
1345
|
+
),
|
|
1346
|
+
);
|
|
1347
|
+
} else if (limit > 0 && result !== undefined) {
|
|
1348
|
+
const text = resultText(unwrapResultEnvelope(result), Number.POSITIVE_INFINITY);
|
|
1349
|
+
const body = indentAndWrap(redactSecretString(text), width, failure);
|
|
1350
|
+
rows.push(...previewRows(body, limit, width, call.toolName === "bash" || !finished));
|
|
1351
|
+
}
|
|
1352
|
+
return rows;
|
|
1353
|
+
}
|
|
1354
|
+
|
|
1355
|
+
/** Only ordinary successful observations can lose their individual metadata rows. */
|
|
1356
|
+
export function canGroupObservation(call: ToolExecutionFinished): boolean {
|
|
1357
|
+
return (
|
|
1358
|
+
["read", "grep", "find", "ls"].includes(call.toolName) &&
|
|
1359
|
+
!call.isError &&
|
|
1360
|
+
!call.outcome &&
|
|
1361
|
+
!call.evictedReason &&
|
|
1362
|
+
!isTruncatedResult(call) &&
|
|
1363
|
+
offloadPathOf(call) === null
|
|
1364
|
+
);
|
|
1458
1365
|
}
|
|
@@ -1,27 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
* a delegated ACP peer differ only in what the header can name about their
|
|
7
|
-
* route, so the body, the tool line, and the receipt footer are identical and
|
|
8
|
-
* an operator learns one grammar. The origin glyph is the only thing that says
|
|
9
|
-
* who asked, which is the same rule the board and the footer chip follow.
|
|
10
|
-
*
|
|
11
|
-
* ◇ coder · node-a/example-coder-model · run 2mkas6s
|
|
12
|
-
* │ Hello! I'm the coder worker.
|
|
13
|
-
* │ ⚙ read · artifact
|
|
14
|
-
* └ ✓ execution ok · 4.8k tok · 9.6s · contract pass
|
|
15
|
-
* │ quality: grounded
|
|
16
|
-
*
|
|
17
|
-
* Folded (the default for a run the model asked for) keeps identity and
|
|
18
|
-
* execution on a tool-style subline, followed by the settled quality fact:
|
|
19
|
-
*
|
|
20
|
-
* ◆ scout · node-b/example-scout-model · run 3nc18jo ✓ execution ok · 41s (Ctrl+O)
|
|
21
|
-
* │ quality: validation failed
|
|
22
|
-
*
|
|
23
|
-
* Pure: no I/O, no module-level mutable state beyond the shared theme handle.
|
|
24
|
-
*/
|
|
1
|
+
import { redactSecretString } from "../../domains/safety/redaction.js";
|
|
2
|
+
import type { TranscriptDetailPolicy } from "../transcript-detail.js";
|
|
3
|
+
import { transcriptDetail } from "../transcript-detail.js";
|
|
4
|
+
import { previewBudget, previewRows } from "./preview.js";
|
|
5
|
+
/** Bounded worker summaries share the main transcript's output style. */
|
|
25
6
|
|
|
26
7
|
import { parseJsonObjectPayload } from "../../core/json-payload.js";
|
|
27
8
|
import { trustStateWord } from "../../domains/evidence/trust-projection.js";
|
|
@@ -48,10 +29,8 @@ const SEPARATOR = " · ";
|
|
|
48
29
|
const BODY_LINE_LIMIT = 80;
|
|
49
30
|
|
|
50
31
|
export interface WorkerEntryRenderOptions {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
/** Key hint appended to a folded header, e.g. "Ctrl+O". Omitted when unknown. */
|
|
54
|
-
expandKey?: string | undefined;
|
|
32
|
+
detail?: TranscriptDetailPolicy;
|
|
33
|
+
terminalRows?: number;
|
|
55
34
|
/** Render the full body without the line cap; `/export` sets this. */
|
|
56
35
|
unbounded?: boolean;
|
|
57
36
|
}
|
|
@@ -392,15 +371,14 @@ function footerLine(entry: WorkerEntryState, width: number): string {
|
|
|
392
371
|
* renders only when the caller resolved a key binding for it, so a rebound or
|
|
393
372
|
* unbound key never advertises a wrong chord.
|
|
394
373
|
*/
|
|
395
|
-
function
|
|
374
|
+
function actionLine(entry: WorkerEntryState, width: number): string {
|
|
396
375
|
const identity = `${originGlyph(entry)} ${identityUnits(entry).join(dim(SEPARATOR))}`;
|
|
397
376
|
const status =
|
|
398
377
|
isPending(entry) || entry.receipt === undefined ? pendingUnit(entry) : outcomeUnit(entry.receipt, false);
|
|
399
378
|
const elapsed =
|
|
400
379
|
entry.receipt?.durationMs === undefined ? "" : dim(`${SEPARATOR}${formatCompactMs(entry.receipt.durationMs)}`);
|
|
401
|
-
const
|
|
402
|
-
|
|
403
|
-
let tail = visibleWidth(identity) + visibleWidth(full) <= width ? full : ` ${status}${hint}`;
|
|
380
|
+
const full = ` ${status}${elapsed}`;
|
|
381
|
+
let tail = visibleWidth(identity) + visibleWidth(full) <= width ? full : ` ${status}`;
|
|
404
382
|
// Reserve one identity cell. The optional hint yields before execution
|
|
405
383
|
// status when the suffix alone would exhaust the header's width.
|
|
406
384
|
if (visibleWidth(tail) >= width) tail = ` ${status}`;
|
|
@@ -421,15 +399,53 @@ export function renderWorkerEntryLines(
|
|
|
421
399
|
"muted",
|
|
422
400
|
safeWidth,
|
|
423
401
|
);
|
|
424
|
-
|
|
425
|
-
|
|
402
|
+
const detail = options.detail ?? transcriptDetail();
|
|
403
|
+
if (options.unbounded) {
|
|
404
|
+
const tools = toolLine(entry, safeWidth);
|
|
405
|
+
return [
|
|
406
|
+
headerLine(entry, safeWidth),
|
|
407
|
+
...bodyLines(entry, safeWidth, true),
|
|
408
|
+
...attemptLines(entry, safeWidth),
|
|
409
|
+
...(tools ? [tools] : []),
|
|
410
|
+
...failureLines(entry, safeWidth),
|
|
411
|
+
footerLine(entry, safeWidth),
|
|
412
|
+
...quality,
|
|
413
|
+
].map(redactSecretString);
|
|
414
|
+
}
|
|
415
|
+
const budget = (limit: number) => previewBudget(limit, options.terminalRows);
|
|
416
|
+
const summary = bodySourceLines(entry).flatMap((line) => railLines(redactSecretString(line), "muted", safeWidth));
|
|
417
|
+
const actions = entry.progress
|
|
418
|
+
? [...entry.progress.recentActions]
|
|
419
|
+
.reverse()
|
|
420
|
+
.concat(entry.progress.currentAction ? [entry.progress.currentAction] : [])
|
|
421
|
+
: [];
|
|
422
|
+
const trail = actions.flatMap((action) =>
|
|
423
|
+
railLines(
|
|
424
|
+
`${GLYPH.phaseTool} ${action.descriptor ? `${action.descriptor.verb} ${action.descriptor.object}` : action.tool}`,
|
|
425
|
+
"muted",
|
|
426
|
+
safeWidth,
|
|
427
|
+
),
|
|
428
|
+
);
|
|
429
|
+
const tools = detail.workerActivity && trail.length === 0 ? toolLine(entry, safeWidth) : null;
|
|
430
|
+
const failure = previewRows(failureLines(entry, safeWidth), budget(detail.errorRows), safeWidth);
|
|
431
|
+
const presented = presentedContractAnswer(entry)?.footer;
|
|
426
432
|
return [
|
|
427
|
-
|
|
428
|
-
...
|
|
429
|
-
...attemptLines(entry, safeWidth),
|
|
430
|
-
...(tools
|
|
431
|
-
...
|
|
432
|
-
|
|
433
|
+
actionLine(entry, safeWidth),
|
|
434
|
+
...(detail.workerRows > 0 ? previewRows(summary, budget(detail.workerRows), safeWidth, entry.pending) : []),
|
|
435
|
+
...previewRows(attemptLines(entry, safeWidth), budget(detail.errorRows), safeWidth, true),
|
|
436
|
+
...(tools ? [tools] : []),
|
|
437
|
+
...(detail.workerActivity ? previewRows(trail, budget(4), safeWidth, true) : []),
|
|
438
|
+
...failure,
|
|
439
|
+
...(presented ? railLines(presented, "muted", safeWidth) : []),
|
|
440
|
+
...(entry.receipt?.abandonedDetail ? railLines(entry.receipt.abandonedDetail, "warning", safeWidth) : []),
|
|
441
|
+
...(entry.receipt?.receiptUnavailable ? railLines("receipt unavailable", "warning", safeWidth) : []),
|
|
442
|
+
...(entry.droppedLines
|
|
443
|
+
? railLines(
|
|
444
|
+
`… ${entry.droppedLines} earlier lines unavailable here · /view dispatch:${entry.runId}`,
|
|
445
|
+
"muted",
|
|
446
|
+
safeWidth,
|
|
447
|
+
)
|
|
448
|
+
: []),
|
|
433
449
|
...quality,
|
|
434
|
-
];
|
|
450
|
+
].map(redactSecretString);
|
|
435
451
|
}
|
|
@@ -154,12 +154,7 @@ function completionGrammar(command: string, args: CommandArgsSpec): CommandArgsS
|
|
|
154
154
|
? {
|
|
155
155
|
positionals: spec.positionals.map((pos) => {
|
|
156
156
|
const completionSlot = COMPLETION_SLOT_MANIFEST[`${path}:pos:${pos.name}`];
|
|
157
|
-
const values =
|
|
158
|
-
command === "output" && pos.name === "verbosity"
|
|
159
|
-
? (["minimal", "default", "verbose"] as const)
|
|
160
|
-
: command === "settings" && pos.name === "area"
|
|
161
|
-
? SETTINGS_AREA_IDS
|
|
162
|
-
: undefined;
|
|
157
|
+
const values = command === "settings" && pos.name === "area" ? SETTINGS_AREA_IDS : undefined;
|
|
163
158
|
return { ...pos, ...(completionSlot ? { completionSlot } : {}), ...(values ? { values } : {}) };
|
|
164
159
|
}),
|
|
165
160
|
}
|
|
@@ -174,8 +174,6 @@ type SlashCommandVariant =
|
|
|
174
174
|
| { kind: "files-usage"; reason: string }
|
|
175
175
|
| { kind: "thinking-set"; level: string }
|
|
176
176
|
| { kind: "thinking-picker" }
|
|
177
|
-
| { kind: "output-set"; verbosity: string }
|
|
178
|
-
| { kind: "output-picker" }
|
|
179
177
|
| { kind: "model" }
|
|
180
178
|
| { kind: "model-set"; pattern: string }
|
|
181
179
|
| { kind: "settings"; area?: SettingsAreaId; group?: string }
|
|
@@ -207,11 +205,6 @@ export type SetThinkingLevelResult =
|
|
|
207
205
|
| { status: "unsupported"; level: string; supported: ReadonlyArray<string> }
|
|
208
206
|
| { status: "unavailable" };
|
|
209
207
|
|
|
210
|
-
export type SetOutputVerbosityResult =
|
|
211
|
-
| { status: "applied"; verbosity: string }
|
|
212
|
-
| { status: "unsupported"; verbosity: string; supported: ReadonlyArray<string> }
|
|
213
|
-
| { status: "unavailable" };
|
|
214
|
-
|
|
215
208
|
/**
|
|
216
209
|
* What one admitted dispatch-tool call reports back to `/council`. The council
|
|
217
210
|
* itself is watched on the board and in the transcript, so success is silent;
|
|
@@ -728,8 +721,6 @@ export interface SlashCommandContext {
|
|
|
728
721
|
thinkingLevelChoices?: () => ReadonlyArray<{ value: string; label: string }>;
|
|
729
722
|
openThinkingPicker?: () => void;
|
|
730
723
|
/** Apply a transcript verbosity named on the command line; refused values are reported by the caller. */
|
|
731
|
-
setOutputVerbosity?: (verbosity: string) => SetOutputVerbosityResult;
|
|
732
|
-
openOutputPicker?: () => void;
|
|
733
724
|
openModel: () => void;
|
|
734
725
|
/** Live providers contract used by `/model <pattern>` to resolve directly. */
|
|
735
726
|
providers: ProvidersContract;
|
|
@@ -1917,35 +1908,6 @@ export const BUILTIN_SLASH_COMMANDS: ReadonlyArray<BuiltinSlashCommand> = [
|
|
|
1917
1908
|
ctx.render();
|
|
1918
1909
|
},
|
|
1919
1910
|
},
|
|
1920
|
-
{
|
|
1921
|
-
name: "output",
|
|
1922
|
-
description: "Set transcript detail to minimal, default, or verbose",
|
|
1923
|
-
group: "Configure",
|
|
1924
|
-
kinds: ["output-set", "output-picker"],
|
|
1925
|
-
args: { positionals: [{ name: "verbosity", required: false }] },
|
|
1926
|
-
fromArgs(parsed) {
|
|
1927
|
-
if (parsed.error) return { kind: "usage-error", command: "output", reason: parsed.error };
|
|
1928
|
-
const verbosity = parsed.positionals[0];
|
|
1929
|
-
return verbosity ? { kind: "output-set", verbosity } : { kind: "output-picker" };
|
|
1930
|
-
},
|
|
1931
|
-
handle(command, ctx) {
|
|
1932
|
-
if (command.kind === "output-picker") {
|
|
1933
|
-
if (ctx.openOutputPicker) ctx.openOutputPicker();
|
|
1934
|
-
else ctx.notice("error", "output picker is not wired in this session");
|
|
1935
|
-
return;
|
|
1936
|
-
}
|
|
1937
|
-
if (command.kind !== "output-set") return;
|
|
1938
|
-
const result = ctx.setOutputVerbosity?.(command.verbosity) ?? { status: "unavailable" as const };
|
|
1939
|
-
if (result.status === "applied") {
|
|
1940
|
-
ctx.notice("success", `output detail: ${result.verbosity}`);
|
|
1941
|
-
} else if (result.status === "unsupported") {
|
|
1942
|
-
ctx.notice("error", `output detail "${result.verbosity}" is not one of ${result.supported.join(", ")}`);
|
|
1943
|
-
} else {
|
|
1944
|
-
ctx.notice("error", "output detail cannot be set right now");
|
|
1945
|
-
}
|
|
1946
|
-
ctx.render();
|
|
1947
|
-
},
|
|
1948
|
-
},
|
|
1949
1911
|
{
|
|
1950
1912
|
name: "model",
|
|
1951
1913
|
description: "Open model selector or set a model",
|
|
@@ -2199,6 +2161,13 @@ function reloadExtensionsCommand(ctx: SlashCommandContext): void {
|
|
|
2199
2161
|
export function parseSlashCommand(input: string): SlashCommand {
|
|
2200
2162
|
const trimmed = input.trim();
|
|
2201
2163
|
if (trimmed.length === 0) return { kind: "empty" };
|
|
2164
|
+
if (/^\/output(?:\s|$)/u.test(trimmed)) {
|
|
2165
|
+
return {
|
|
2166
|
+
kind: "usage-error",
|
|
2167
|
+
command: "output",
|
|
2168
|
+
reason: "Use Alt+O to cycle Output style, or /settings interface to save a default. Use /view for full details",
|
|
2169
|
+
};
|
|
2170
|
+
}
|
|
2202
2171
|
if (trimmed.startsWith(COMMAND_ESCAPE)) return { kind: "unknown", text: trimmed.slice(1) };
|
|
2203
2172
|
for (const entry of BUILTIN_SLASH_COMMANDS) {
|
|
2204
2173
|
const match = entry.match ? entry.match(trimmed) : matchFromSpec(entry, trimmed);
|
|
@@ -2286,7 +2255,6 @@ const COMMAND_ORDER = [
|
|
|
2286
2255
|
"help",
|
|
2287
2256
|
"model",
|
|
2288
2257
|
"thinking",
|
|
2289
|
-
"output",
|
|
2290
2258
|
"settings",
|
|
2291
2259
|
"new",
|
|
2292
2260
|
"resume",
|
|
@@ -206,9 +206,6 @@ export function createStatusController(deps: StatusControllerDeps): StatusContro
|
|
|
206
206
|
deps.bus.on(BusChannels.PermissionResolved, () => apply({ type: "overlay_pop", overlay: "tool_blocked" }, true)),
|
|
207
207
|
deps.bus.on(BusChannels.CompactionBegin, () => apply({ type: "overlay_push", overlay: "compacting" }, true)),
|
|
208
208
|
deps.bus.on(BusChannels.CompactionEnd, () => apply({ type: "overlay_pop", overlay: "compacting" }, true)),
|
|
209
|
-
deps.bus.on(BusChannels.DispatchStarted, (payload) =>
|
|
210
|
-
apply({ type: "overlay_push", overlay: "dispatching", data: payload }, true),
|
|
211
|
-
),
|
|
212
209
|
deps.bus.on(BusChannels.DispatchProgress, (payload) => {
|
|
213
210
|
if (!isWorkerStreamEvidence(payload)) return;
|
|
214
211
|
const at = now();
|
|
@@ -216,8 +213,6 @@ export function createStatusController(deps: StatusControllerDeps): StatusContro
|
|
|
216
213
|
lastDispatchProgressAt = at;
|
|
217
214
|
apply({ type: "dispatch_progress" });
|
|
218
215
|
}),
|
|
219
|
-
deps.bus.on(BusChannels.DispatchCompleted, () => apply({ type: "overlay_pop", overlay: "dispatching" }, true)),
|
|
220
|
-
deps.bus.on(BusChannels.DispatchFailed, () => apply({ type: "overlay_pop", overlay: "dispatching" }, true)),
|
|
221
216
|
deps.bus.on(BusChannels.RunAborted, (payload) => {
|
|
222
217
|
scheduleAbortCeiling();
|
|
223
218
|
// Carry abort provenance into the summary; a dispatch drain and a
|
|
@@ -245,6 +245,7 @@ function activePhaseAfterStuck(prev: AgentStatus): StatusPhase {
|
|
|
245
245
|
}
|
|
246
246
|
|
|
247
247
|
function isAwaitingDispatch(prev: AgentStatus): boolean {
|
|
248
|
+
if (activePhaseAfterStuck(prev) === "tool_running" && prev.tool?.toolName === ToolNames.Dispatch) return true;
|
|
248
249
|
if (prev.phase === "dispatching") return true;
|
|
249
250
|
if (prev.activePhases?.has("dispatching")) return true;
|
|
250
251
|
return (prev.overlayStack ?? []).some((frame) => frame.phase === "dispatching");
|
|
@@ -360,26 +361,9 @@ export function reduceStatus(prev: AgentStatus, event: StatusInputEvent, ctx: Re
|
|
|
360
361
|
if (base === "preparing") return { ...next, phase: "waiting_model", resumePhase: undefined };
|
|
361
362
|
return next;
|
|
362
363
|
}
|
|
363
|
-
case "message_start":
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
if (role === "assistant" && prev.phase === "tool_running") return { ...next, phase: "writing" };
|
|
367
|
-
if (
|
|
368
|
-
role === "assistant" &&
|
|
369
|
-
(prev.phase === "preparing" || prev.phase === "waiting_model" || prev.phase === "thinking")
|
|
370
|
-
)
|
|
371
|
-
return { ...next, phase: "writing", resumePhase: undefined };
|
|
372
|
-
return next;
|
|
373
|
-
}
|
|
374
|
-
case "message_update": {
|
|
375
|
-
const base = activePhaseAfterStuck(prev);
|
|
376
|
-
const next = refreshMeaningful({ ...prev, phase: base }, ctx);
|
|
377
|
-
if (base === "preparing" || base === "waiting_model" || base === "thinking") {
|
|
378
|
-
return { ...next, phase: "writing", resumePhase: undefined };
|
|
379
|
-
}
|
|
380
|
-
if (CORE_ACTIVE_PHASES.has(base)) return { ...next, resumePhase: undefined };
|
|
381
|
-
return next;
|
|
382
|
-
}
|
|
364
|
+
case "message_start":
|
|
365
|
+
case "message_update":
|
|
366
|
+
return refreshMeaningful(prev, ctx);
|
|
383
367
|
case "tool_execution_update":
|
|
384
368
|
return refreshMeaningful(prev, ctx);
|
|
385
369
|
case "message_end":
|
|
@@ -390,10 +374,22 @@ export function reduceStatus(prev: AgentStatus, event: StatusInputEvent, ctx: Re
|
|
|
390
374
|
runTally: foldMessageIntoRunTally(prev.runTally ?? emptyRunTally(), event.message),
|
|
391
375
|
};
|
|
392
376
|
case "tool_execution_end": {
|
|
393
|
-
const
|
|
394
|
-
|
|
377
|
+
const activeTools = (prev.activeTools ?? []).filter((tool) => tool.toolCallId !== event.toolCallId);
|
|
378
|
+
const next = { ...refreshMeaningful(prev, ctx), activeTools };
|
|
379
|
+
const remaining = activeTools.at(-1);
|
|
380
|
+
if (remaining && prev.phase === "tool_running")
|
|
381
|
+
return { ...next, tool: remaining, toolStartedAt: remaining.startedAt };
|
|
382
|
+
if (prev.phase === "tool_running")
|
|
383
|
+
return { ...next, phase: "preparing", tool: undefined, toolStartedAt: undefined, activeTools: [] };
|
|
395
384
|
if (prev.phase === "stuck" && prev.resumePhase === "tool_running") {
|
|
396
|
-
return {
|
|
385
|
+
return {
|
|
386
|
+
...next,
|
|
387
|
+
phase: "preparing",
|
|
388
|
+
resumePhase: undefined,
|
|
389
|
+
tool: undefined,
|
|
390
|
+
toolStartedAt: undefined,
|
|
391
|
+
activeTools: [],
|
|
392
|
+
};
|
|
397
393
|
}
|
|
398
394
|
// A blocked call's end can land while an overlay is still visible
|
|
399
395
|
// (permission prompt not yet resolved, retry or dispatch in front).
|
|
@@ -422,7 +418,14 @@ export function reduceStatus(prev: AgentStatus, event: StatusInputEvent, ctx: Re
|
|
|
422
418
|
// tool is done (its end never landed or was an admission block): the
|
|
423
419
|
// model is generating, nothing is executing, so drop the tool display.
|
|
424
420
|
if (base === "preparing" || base === "waiting_model" || base === "writing" || base === "tool_running") {
|
|
425
|
-
return {
|
|
421
|
+
return {
|
|
422
|
+
...next,
|
|
423
|
+
phase: "thinking",
|
|
424
|
+
resumePhase: undefined,
|
|
425
|
+
tool: undefined,
|
|
426
|
+
toolStartedAt: undefined,
|
|
427
|
+
activeTools: [],
|
|
428
|
+
};
|
|
426
429
|
}
|
|
427
430
|
if (CORE_ACTIVE_PHASES.has(base)) return { ...next, resumePhase: undefined };
|
|
428
431
|
return next;
|
|
@@ -433,19 +436,34 @@ export function reduceStatus(prev: AgentStatus, event: StatusInputEvent, ctx: Re
|
|
|
433
436
|
// Streamed answer text while a tool still shows as running: same as
|
|
434
437
|
// above, the model is writing, so the running-tool spinner must clear.
|
|
435
438
|
if (base === "preparing" || base === "waiting_model" || base === "thinking" || base === "tool_running") {
|
|
436
|
-
return {
|
|
439
|
+
return {
|
|
440
|
+
...next,
|
|
441
|
+
phase: "writing",
|
|
442
|
+
resumePhase: undefined,
|
|
443
|
+
tool: undefined,
|
|
444
|
+
toolStartedAt: undefined,
|
|
445
|
+
activeTools: [],
|
|
446
|
+
};
|
|
437
447
|
}
|
|
438
448
|
if (CORE_ACTIVE_PHASES.has(base)) return { ...next, resumePhase: undefined };
|
|
439
449
|
return next;
|
|
440
450
|
}
|
|
441
451
|
case "tool_execution_start": {
|
|
442
452
|
const tool: ToolOverlay = {
|
|
453
|
+
toolCallId: event.toolCallId,
|
|
454
|
+
startedAt: ctx.now,
|
|
443
455
|
toolName: event.toolName,
|
|
444
456
|
toolPreview: compactPreview(event.args),
|
|
445
457
|
};
|
|
446
458
|
// Stamp this call's own start so the footer's running-tool timer counts
|
|
447
459
|
// from here, not from turn start.
|
|
448
|
-
return {
|
|
460
|
+
return {
|
|
461
|
+
...refreshMeaningful(prev, ctx),
|
|
462
|
+
phase: "tool_running",
|
|
463
|
+
tool,
|
|
464
|
+
toolStartedAt: ctx.now,
|
|
465
|
+
activeTools: [...(prev.activeTools ?? []).filter((active) => active.toolCallId !== event.toolCallId), tool],
|
|
466
|
+
};
|
|
449
467
|
}
|
|
450
468
|
case "retry_status": {
|
|
451
469
|
const phase = event.status.phase;
|