@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.
Files changed (188) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/CONTRIBUTING.md +14 -9
  3. package/NOTICE +16 -0
  4. package/README.md +312 -685
  5. package/dist/{acp-H3CU2HBQ.js → acp-UVEVWSDL.js} +6 -6
  6. package/dist/{agents-IXQQZ7BD.js → agents-VWY37DTK.js} +31 -30
  7. package/dist/assets/codewiki.json +1 -1
  8. package/dist/{auth-R7KYF7N3.js → auth-PCV2XQDV.js} +6 -6
  9. package/dist/{builtins-CRFDQUVP.js → builtins-PD652MTK.js} +3 -3
  10. package/dist/{chunk-SDWNDICN.js → chunk-24T2YZ7A.js} +58 -56
  11. package/dist/{chunk-FSTIBAGM.js → chunk-2AKYFREM.js} +7 -7
  12. package/dist/{chunk-SE4ZKILO.js → chunk-3BJGMT2D.js} +30 -9
  13. package/dist/{chunk-GVYWZNRK.js → chunk-3HQVBIKD.js} +2 -2
  14. package/dist/{chunk-EWD5P3GK.js → chunk-3JLFK5MW.js} +4 -4
  15. package/dist/{chunk-O2RQ7LCK.js → chunk-3O2ZAUGQ.js} +2 -2
  16. package/dist/{chunk-GXCCHKN6.js → chunk-3QKXHCGS.js} +4 -4
  17. package/dist/{chunk-YEJIG7LN.js → chunk-3WBALRQH.js} +7 -7
  18. package/dist/{chunk-I7VLWRK2.js → chunk-4DU2TVI4.js} +5 -5
  19. package/dist/{chunk-TEG3RYVZ.js → chunk-4U4TKZFR.js} +3 -3
  20. package/dist/{chunk-EHCPSZJF.js → chunk-4VMV6OS6.js} +2 -2
  21. package/dist/{chunk-T7FDXNRU.js → chunk-536HXRFE.js} +4 -4
  22. package/dist/{chunk-ZMZQEBKI.js → chunk-5QRX7SLS.js} +8 -8
  23. package/dist/{chunk-RXIOCVBZ.js → chunk-6HAO6X5B.js} +4 -4
  24. package/dist/{chunk-HGSLRG33.js → chunk-6OII3LOK.js} +2 -2
  25. package/dist/{chunk-U2JOLB7O.js → chunk-6RYDAVQT.js} +32 -32
  26. package/dist/{chunk-T53XI6I7.js → chunk-6VRRJT7L.js} +2 -2
  27. package/dist/{chunk-RLTT5GVY.js → chunk-7JSSKXG2.js} +4 -4
  28. package/dist/{chunk-633EXGZA.js → chunk-ACCKVSHU.js} +3 -3
  29. package/dist/{chunk-6SYEGZI3.js → chunk-B3MOMLJ4.js} +2 -2
  30. package/dist/{chunk-UXAC2FX4.js → chunk-BLTEAUCB.js} +3 -3
  31. package/dist/{chunk-DRD2A54W.js → chunk-BT7HGDOX.js} +15 -15
  32. package/dist/{chunk-F34AR6O3.js → chunk-DXLUVUD4.js} +5 -5
  33. package/dist/{chunk-HR3HPQ76.js → chunk-E7RF4KBO.js} +2 -2
  34. package/dist/chunk-EM2WNQS2.js +93 -0
  35. package/dist/{chunk-KYKNBD4Y.js → chunk-F5Z7EWMN.js} +953 -233
  36. package/dist/{chunk-VLPTKCKT.js → chunk-GLYBJ4RQ.js} +3 -3
  37. package/dist/{chunk-KDWLXVMN.js → chunk-HPTJF6DG.js} +16 -39
  38. package/dist/{chunk-6CDNRHWH.js → chunk-HZ2T2YED.js} +2 -2
  39. package/dist/{chunk-UTUNT5QL.js → chunk-HZFBVU5X.js} +7 -7
  40. package/dist/{chunk-KF6GFW6O.js → chunk-I2K2NCZQ.js} +7 -7
  41. package/dist/{chunk-5DDOTTG4.js → chunk-IGLVVNEG.js} +2 -2
  42. package/dist/{chunk-4SVIVOMC.js → chunk-IKRWLEDA.js} +5 -5
  43. package/dist/{chunk-R6MAYTMS.js → chunk-JMPL7NUB.js} +3 -3
  44. package/dist/{chunk-5ZMXGW7P.js → chunk-KAFMW7WM.js} +4 -85
  45. package/dist/{chunk-R6MICQYU.js → chunk-LIN2K3EL.js} +4 -4
  46. package/dist/{chunk-QEQISF75.js → chunk-LK2FN5JY.js} +1 -1
  47. package/dist/{chunk-YYB3MC65.js → chunk-N2OKBREN.js} +2 -2
  48. package/dist/{chunk-XXBQKW32.js → chunk-NNWJYUNO.js} +4 -4
  49. package/dist/{chunk-TZ7MFRG4.js → chunk-P5JMBPDJ.js} +22 -20
  50. package/dist/{chunk-WFKDPU7U.js → chunk-P6M7RKMI.js} +2 -2
  51. package/dist/{chunk-MDU3C27C.js → chunk-PZN2LQY6.js} +5 -5
  52. package/dist/{chunk-S32UQ5GL.js → chunk-Q7NNUHQV.js} +15 -15
  53. package/dist/{chunk-U7VQ4NI4.js → chunk-QCOV5DFY.js} +5 -5
  54. package/dist/{chunk-MC3JSVR6.js → chunk-QJMRYTCX.js} +7 -7
  55. package/dist/{chunk-DIMRLNIC.js → chunk-QNWRHOMF.js} +3 -3
  56. package/dist/{chunk-ILJ7DWGE.js → chunk-QXFBABFJ.js} +4 -4
  57. package/dist/chunk-R2CT5YDO.js +66 -0
  58. package/dist/{chunk-E3BQTQUP.js → chunk-RGZNQ5CT.js} +3 -3
  59. package/dist/{chunk-SBZX6ARC.js → chunk-SXQKNKRA.js} +7 -7
  60. package/dist/{chunk-WLMSOT73.js → chunk-TJFDL26W.js} +3 -3
  61. package/dist/{chunk-EQEJJ53P.js → chunk-VAHLPXPY.js} +2 -2
  62. package/dist/{chunk-7TKWWPD7.js → chunk-VANN3GDP.js} +3 -23
  63. package/dist/{chunk-GNQRWTMY.js → chunk-VHMX7SXF.js} +2 -2
  64. package/dist/{chunk-UL36QJX6.js → chunk-WKUYQBCG.js} +6 -6
  65. package/dist/{chunk-FKIOX6DS.js → chunk-WNGF5AJX.js} +9 -4
  66. package/dist/{chunk-ICO4TQ2F.js → chunk-WV6TDAPN.js} +36 -20
  67. package/dist/{chunk-K6RDB7IA.js → chunk-X3BI7HTV.js} +9 -9
  68. package/dist/{chunk-NY6IWDVU.js → chunk-XWMSPJYN.js} +2 -2
  69. package/dist/{chunk-7HBHHVCK.js → chunk-YPRSNUTC.js} +2 -2
  70. package/dist/{chunk-ONRTXOZY.js → chunk-ZS43Y2FT.js} +2 -2
  71. package/dist/cli/index.js +27 -27
  72. package/dist/{clio-5I2IBFYL.js → clio-QQCMTUZE.js} +6 -6
  73. package/dist/{code-nav-YXIAEZVS.js → code-nav-GUPM6766.js} +8 -8
  74. package/dist/{config-Y2JDJ3JJ.js → config-IIM7YOD3.js} +42 -41
  75. package/dist/{configure-SML6UBZ5.js → configure-4ZM5HWJX.js} +28 -10
  76. package/dist/{context-S2EEC3EJ.js → context-44BZLJ3J.js} +12 -12
  77. package/dist/{context-P5YSEO55.js → context-AIDVQKMW.js} +34 -33
  78. package/dist/{context-QQ53HSGJ.js → context-OL5P3HEP.js} +25 -24
  79. package/dist/{context-clear-TMEQCOTH.js → context-clear-OQV76MYP.js} +34 -33
  80. package/dist/{context-working-set-CNLSQN5K.js → context-working-set-ZUW3EGLS.js} +10 -9
  81. package/dist/{detail-5572VLBO.js → detail-UXPWRANG.js} +35 -34
  82. package/dist/{dispatch-runner-FRHD6P2O.js → dispatch-runner-P3ZW6G77.js} +38 -37
  83. package/dist/{doctor-N3LCM2LG.js → doctor-JJVGPZWT.js} +18 -17
  84. package/dist/{eval-BUU3ZOUZ.js → eval-V2RTN3UH.js} +26 -25
  85. package/dist/{evidence-DTGEN456.js → evidence-35PYWGSO.js} +34 -33
  86. package/dist/{evidence-SQ5DNFEN.js → evidence-CIGGKI6J.js} +36 -35
  87. package/dist/{evolve-IJNUFZDS.js → evolve-33SM3NJP.js} +34 -33
  88. package/dist/{fleet-ZNNVSYFO.js → fleet-LFO7SIPC.js} +62 -61
  89. package/dist/{fleet-commands-5YE7NPJL.js → fleet-commands-RLK72GU3.js} +9 -9
  90. package/dist/fleet-decisions-3SBSCTCW.js +1 -1
  91. package/dist/{fleet-graph-6HHPWF5T.js → fleet-graph-JZIBCGMW.js} +11 -11
  92. package/dist/{fleet-inspect-JEYMGCGC.js → fleet-inspect-2L5YGGZJ.js} +35 -34
  93. package/dist/{fleet-validate-4PM4GDDL.js → fleet-validate-LIAVKDUU.js} +12 -12
  94. package/dist/{fleet-verify-NJDTHTO3.js → fleet-verify-JIK3OQZR.js} +34 -33
  95. package/dist/{fleet-view-UP2BWQQY.js → fleet-view-IWHYGQKK.js} +35 -34
  96. package/dist/{init-S3GZQ5ZF.js → init-NJEDOTQW.js} +46 -45
  97. package/dist/{interop-YOTGXFJM.js → interop-RVCAYECG.js} +4 -4
  98. package/dist/{inventory-K2OZ36YJ.js → inventory-74UL7UHU.js} +35 -34
  99. package/dist/{library-GJI6W6OK.js → library-CTKZA7K3.js} +10 -10
  100. package/dist/{memory-OQOXQGYD.js → memory-I76I5VUH.js} +34 -33
  101. package/dist/{models-3IK2Y5WE.js → models-D4YDIQZQ.js} +19 -18
  102. package/dist/{monitor-FTJLTKCQ.js → monitor-NKVD55HX.js} +40 -39
  103. package/dist/{orchestrator-N47O7SFD.js → orchestrator-I6OATVPK.js} +658 -1092
  104. package/dist/{preload-65U34NG6.js → preload-EJAWJEIO.js} +34 -33
  105. package/dist/{reset-MUVQDLNL.js → reset-4XMQKDNZ.js} +5 -3
  106. package/dist/{resources-LM76COAO.js → resources-TVYVXK3Q.js} +10 -10
  107. package/dist/{run-3VFZQ33N.js → run-OWQNNSJU.js} +61 -60
  108. package/dist/{share-4JUB4BL7.js → share-NPZZMUXG.js} +11 -11
  109. package/dist/{skills-S36AAJHU.js → skills-OXFEAWMT.js} +12 -12
  110. package/dist/{skills-eval-G6T57L7M.js → skills-eval-J5TCKSJA.js} +38 -37
  111. package/dist/{skills-inventory-WLM46PJH.js → skills-inventory-7II4DM57.js} +10 -10
  112. package/dist/{slash-commands-GBARG3Y7.js → slash-commands-MDSDSWYF.js} +26 -25
  113. package/dist/{targets-N5REUETU.js → targets-KILZEOCE.js} +36 -20
  114. package/dist/{tasks-6BVY5YZF.js → tasks-R6P6OV22.js} +7 -7
  115. package/dist/{terminal-lease-57UEOJKS.js → terminal-lease-LZCG2KN3.js} +2 -2
  116. package/dist/{upgrade-4HD5HAIV.js → upgrade-D2OZH6IQ.js} +9 -9
  117. package/dist/{usage-4GWM2WWP.js → usage-PIASNUS4.js} +40 -39
  118. package/dist/{verifiers-GSKWKWVG.js → verifiers-LTHUY2PX.js} +9 -9
  119. package/dist/{verify-3AS3ULCY.js → verify-HYF7KBVE.js} +7 -7
  120. package/dist/{wiki-generate-7BV7XWIY.js → wiki-generate-RK7JQ5NP.js} +51 -50
  121. package/dist/worker/entry.js +41 -39
  122. package/docs/architecture/context-engine.md +1 -1
  123. package/docs/architecture/tui-design.md +45 -30
  124. package/docs/guide/commands-and-modes.md +26 -25
  125. package/docs/guide/configuration-and-targets.md +122 -27
  126. package/docs/guide/configuration-reference.md +9 -4
  127. package/docs/guide/fleet-dispatch.md +1 -6
  128. package/docs/guide/glossary.md +1 -1
  129. package/docs/guide/installation-and-lifecycle.md +44 -11
  130. package/docs/process/development-pipeline.md +1 -1
  131. package/docs/process/documentation-coverage.md +1 -1
  132. package/docs/process/release-cut-checklist.md +18 -6
  133. package/package.json +1 -1
  134. package/src/cli/ask.ts +10 -10
  135. package/src/cli/configure-editor.ts +51 -0
  136. package/src/cli/configure-interop.ts +1 -1
  137. package/src/cli/configure-oauth.ts +1 -1
  138. package/src/cli/configure-onboarding.ts +232 -96
  139. package/src/cli/configure-prompts.ts +76 -0
  140. package/src/cli/configure-quick.ts +274 -0
  141. package/src/cli/configure-target.ts +14 -2
  142. package/src/cli/configure.ts +413 -174
  143. package/src/cli/oauth-manual-input.ts +1 -1
  144. package/src/cli/reset.ts +2 -0
  145. package/src/cli/select.ts +35 -4
  146. package/src/cli/upgrade.ts +2 -2
  147. package/src/core/config.ts +6 -1
  148. package/src/core/defaults.ts +33 -11
  149. package/src/{interactive → core}/external-editor.ts +4 -4
  150. package/src/domains/config/classify.ts +1 -1
  151. package/src/domains/config/keybindings.ts +3 -31
  152. package/src/domains/providers/probe/fingerprint.ts +13 -6
  153. package/src/domains/providers/probe/reasoning.ts +2 -0
  154. package/src/domains/providers/runtimes/common/probe-helpers.ts +23 -7
  155. package/src/domains/providers/runtimes/protocol/anthropic-compat.ts +7 -2
  156. package/src/domains/providers/runtimes/protocol/openai-compat.ts +10 -4
  157. package/src/interactive/application-controller.ts +1 -31
  158. package/src/interactive/chat-panel.ts +163 -637
  159. package/src/interactive/chat-renderer.ts +13 -30
  160. package/src/interactive/dispatch-board.ts +1 -3
  161. package/src/interactive/editor-submit.ts +8 -31
  162. package/src/interactive/footer/dashboard.ts +8 -3
  163. package/src/interactive/footer/widgets.ts +50 -91
  164. package/src/interactive/interactive-application.ts +16 -42
  165. package/src/interactive/interactive-event-projection.ts +2 -44
  166. package/src/interactive/interactive-input-runtime.ts +3 -28
  167. package/src/interactive/interactive-presentation.ts +30 -9
  168. package/src/interactive/interactive-slash-runtime.ts +3 -40
  169. package/src/interactive/layout.ts +29 -4
  170. package/src/interactive/overlay-general-openers.ts +2 -0
  171. package/src/interactive/overlay-lifecycle.ts +1 -0
  172. package/src/interactive/overlay-session-lifecycle.ts +1 -1
  173. package/src/interactive/overlays/settings.ts +9 -8
  174. package/src/interactive/renderers/preview.ts +16 -0
  175. package/src/interactive/renderers/tool-execution.ts +67 -160
  176. package/src/interactive/renderers/worker-entry.ts +57 -41
  177. package/src/interactive/slash-autocomplete.ts +1 -6
  178. package/src/interactive/slash-commands.ts +7 -39
  179. package/src/interactive/status/controller.ts +0 -5
  180. package/src/interactive/status/state-machine.ts +44 -26
  181. package/src/interactive/status/types.ts +3 -0
  182. package/src/interactive/status/verbs.ts +13 -12
  183. package/src/interactive/transcript-detail.ts +50 -112
  184. package/src/interactive/view/artifacts.ts +4 -0
  185. package/src/interactive/view/view-overlay.ts +44 -34
  186. package/src/tools/presentation.ts +1 -1
  187. package/src/tools/registry.ts +1 -1
  188. package/src/tools/verify/numeric.ts +4 -4
@@ -19,6 +19,8 @@ export interface RetryOverlay {
19
19
  }
20
20
 
21
21
  export interface ToolOverlay {
22
+ toolCallId?: string;
23
+ startedAt?: number;
22
24
  toolName: string;
23
25
  toolPreview: string;
24
26
  }
@@ -104,6 +106,7 @@ export interface AgentStatus {
104
106
  * phase leaves `tool_running`.
105
107
  */
106
108
  toolStartedAt?: number | undefined;
109
+ activeTools?: readonly ToolOverlay[];
107
110
  retry?: RetryOverlay | undefined;
108
111
  dispatch?: DispatchOverlay | undefined;
109
112
  /**
@@ -44,30 +44,31 @@ function coreVerb(status: AgentStatus): { text: string; toneHint: VerbRender["to
44
44
  case "idle":
45
45
  return null;
46
46
  case "preparing":
47
- return { text: tier >= 2 ? "still preparing harness" : "preparing harness", toneHint: "normal" };
47
+ return { text: "Preparing", toneHint: "normal" };
48
48
  case "waiting_model":
49
49
  return {
50
- text: tier >= 2 ? "still waiting on model" : status.localRuntime ? "waiting on local model" : "waiting on model",
50
+ text: tier >= 2 ? "Waiting for model" : status.localRuntime ? "Waiting for local model" : "Waiting for model",
51
51
  toneHint: "normal",
52
52
  };
53
53
  case "thinking":
54
- return { text: tier >= 2 ? "still receiving thinking" : "receiving thinking", toneHint: "normal" };
54
+ return { text: "Thinking", toneHint: "normal" };
55
55
  case "writing":
56
- return { text: "streaming response", toneHint: "normal" };
56
+ return { text: "Writing", toneHint: "normal" };
57
57
  case "tool_running": {
58
58
  const name = status.tool?.toolName ?? "tool";
59
- if (name === ToolNames.AskUser) return { text: "waiting for user input", toneHint: "normal" };
60
- return { text: tier >= 2 ? `still running tool: ${name}` : `running tool: ${name}`, toneHint: "normal" };
59
+ if (name === ToolNames.Dispatch) return { text: "Waiting for worker", toneHint: "normal" };
60
+ if (name === ToolNames.AskUser) return { text: "Needs input", toneHint: "normal" };
61
+ return { text: `Running ${name}`, toneHint: "normal" };
61
62
  }
62
63
  case "tool_blocked":
63
- return { text: "awaiting confirmation", toneHint: "warn" };
64
+ return { text: "Needs approval", toneHint: "warn" };
64
65
  case "retrying": {
65
66
  const retry = status.retry;
66
67
  const wait = retry && retry.waitMs > 0 ? ` · ${formatStatusElapsed(retry.waitMs)}` : "";
67
68
  return { text: `retrying ${retry?.attempt ?? 0}/${retry?.maxAttempts ?? 0}${wait}`, toneHint: "warn" };
68
69
  }
69
70
  case "compacting":
70
- return { text: "compacting context", toneHint: "normal" };
71
+ return { text: "Compacting context", toneHint: "normal" };
71
72
  case "dispatching": {
72
73
  const agent = status.dispatch?.agentName;
73
74
  if (tier >= 2)
@@ -75,7 +76,7 @@ function coreVerb(status: AgentStatus): { text: string; toneHint: VerbRender["to
75
76
  return { text: agent ? `dispatching agent: ${agent}` : "dispatching agent", toneHint: "normal" };
76
77
  }
77
78
  case "stuck":
78
- return { text: "stuck", toneHint: "error" };
79
+ return { text: "No output", toneHint: "error" };
79
80
  case "ended": {
80
81
  const stop = status.summary?.stopReason ?? "stop";
81
82
  const elapsed = status.summary ? ` · ${formatStatusElapsed(status.summary.elapsedMs)}` : "";
@@ -97,7 +98,7 @@ export function resolveFooterVerb(status: AgentStatus, now: number, terminalCols
97
98
  if (status.phase === "stuck") {
98
99
  const elapsed = elapsedSince(status, now);
99
100
  return {
100
- text: terminalCols < 60 ? "stuck" : `stuck · ${elapsed} · Esc to cancel`,
101
+ text: terminalCols < 60 ? "No output" : `No output · ${elapsed} · Esc to cancel`,
101
102
  toneHint: "error",
102
103
  };
103
104
  }
@@ -117,12 +118,12 @@ function inlineParts(
117
118
  terminalCols: number,
118
119
  ): { text: string; hint: string | null; toneHint: VerbRender["toneHint"] } | null {
119
120
  if (status.phase === "stuck")
120
- return { text: `Stuck for ${elapsedSince(status, now)}.`, hint: "Press Esc to cancel.", toneHint: "error" };
121
+ return { text: `No output for ${noProgressSince(status, now)}.`, hint: "Press Esc to cancel.", toneHint: "error" };
121
122
  const core = coreVerb(status);
122
123
  if (!core) return null;
123
124
  const text = core.text.replace(/^[a-z]/, (c) => c.toUpperCase());
124
125
  if (status.watchdogTier >= 3) {
125
- const hint = terminalCols < 50 ? null : `(no progress for ${noProgressSince(status, now)}; press Esc to cancel)`;
126
+ const hint = terminalCols < 50 ? null : `(no output for ${noProgressSince(status, now)}; press Esc to cancel)`;
126
127
  return { text, hint, toneHint: core.toneHint };
127
128
  }
128
129
  if (terminalCols < 50 || status.phase === "tool_blocked") return { text, hint: null, toneHint: core.toneHint };
@@ -1,120 +1,58 @@
1
- /**
2
- * Transcript detail policy: the one place `/output minimal|default|verbose`
3
- * becomes a decision about how much of each foldable transcript block is open
4
- * before the operator touches it.
5
- *
6
- * The chat panel used to consult the verbosity string in four places, each
7
- * spelling the rule a little differently, and nothing else (running rows, live
8
- * tool output, worker cards in replay) consulted it at all. The panel now reads
9
- * this policy once per frame and threads it down; the verbosity string never
10
- * reaches a renderer.
11
- *
12
- * Operator toggles (`Alt+O`, `Alt+R`, expand-all, collapse-all) are overrides
13
- * layered on top of this policy, not fights with it: the effective state of a
14
- * block is `override ?? policy`, and `resolveFold` is that one rule.
15
- *
16
- * Pure module: no I/O, no UI imports.
17
- */
18
-
19
- import type { OutputVerbosity } from "../core/defaults.js";
20
- import type { ToolFoldDefault, ToolPresentationPolicy } from "../tools/presentation.js";
21
-
22
- /** A block's fold state. The same vocabulary serves tools, workers, thinking, and local bash. */
23
- export type Fold = ToolFoldDefault;
24
-
25
- /** An operator's explicit choice for one block, or none. */
26
- export type FoldOverride = Fold | undefined;
1
+ /** Presentation budgets for the same captured session. Detailed is still bounded. */
2
+ import type { OutputStyle } from "../core/defaults.js";
27
3
 
28
4
  export interface TranscriptDetailPolicy {
29
- /** Finished tool call body: folded subline, the tool's own default, or open. */
30
- toolBody: "folded" | "per-tool" | "expanded";
31
- /** In-flight tool call: one-line row with elapsed, or the header plus streaming body. */
32
- runningTool: "row" | "body";
33
- /** Reasoning stretch: bare marker, folded marker with live progress, or the open rail. */
34
- thinking: "marker" | "folded" | "rail";
35
- /** Worker block: folded card, the origin default, or open. */
36
- worker: "folded" | "origin" | "expanded";
37
- /** Settled turn receipt: none, `turn · in N · out M`, or the full receipt. */
5
+ style: OutputStyle;
6
+ reasoningRows: number;
7
+ resultRows: number;
8
+ bashRows: number;
9
+ operatorBashRows: number;
10
+ diffRows: number;
11
+ workerRows: number;
12
+ workerActivity: boolean;
13
+ errorRows: number;
38
14
  receipt: "none" | "compact" | "full";
39
- /** Failed tool under its folded row: the bounded excerpt, or the full body. */
40
- errors: "excerpt" | "body";
41
15
  }
42
16
 
43
- const MINIMAL: TranscriptDetailPolicy = {
44
- toolBody: "folded",
45
- runningTool: "row",
46
- thinking: "marker",
47
- worker: "folded",
48
- receipt: "none",
49
- errors: "excerpt",
50
- };
51
-
52
- const DEFAULT: TranscriptDetailPolicy = {
53
- toolBody: "per-tool",
54
- runningTool: "row",
55
- thinking: "folded",
56
- worker: "origin",
57
- receipt: "compact",
58
- errors: "excerpt",
17
+ const POLICIES: Record<OutputStyle, TranscriptDetailPolicy> = {
18
+ compact: {
19
+ style: "compact",
20
+ reasoningRows: 0,
21
+ resultRows: 0,
22
+ bashRows: 0,
23
+ operatorBashRows: 3,
24
+ diffRows: 0,
25
+ workerRows: 0,
26
+ workerActivity: false,
27
+ errorRows: 4,
28
+ receipt: "none",
29
+ },
30
+ standard: {
31
+ style: "standard",
32
+ reasoningRows: 3,
33
+ resultRows: 0,
34
+ bashRows: 0,
35
+ operatorBashRows: 6,
36
+ diffRows: 8,
37
+ workerRows: 3,
38
+ workerActivity: false,
39
+ errorRows: 4,
40
+ receipt: "compact",
41
+ },
42
+ detailed: {
43
+ style: "detailed",
44
+ reasoningRows: 12,
45
+ resultRows: 8,
46
+ bashRows: 12,
47
+ operatorBashRows: 12,
48
+ diffRows: 20,
49
+ workerRows: 8,
50
+ workerActivity: true,
51
+ errorRows: 12,
52
+ receipt: "full",
53
+ },
59
54
  };
60
55
 
61
- const VERBOSE: TranscriptDetailPolicy = {
62
- toolBody: "expanded",
63
- runningTool: "body",
64
- thinking: "rail",
65
- worker: "expanded",
66
- receipt: "full",
67
- errors: "body",
68
- };
69
-
70
- /**
71
- * Map a verbosity to the full policy table. An absent verbosity (a panel built
72
- * without settings) is the balanced default, so the literal never has to be
73
- * spelled by the caller.
74
- */
75
- export function transcriptDetail(verbosity: OutputVerbosity | undefined): TranscriptDetailPolicy {
76
- switch (verbosity) {
77
- case "minimal":
78
- return MINIMAL;
79
- case "verbose":
80
- return VERBOSE;
81
- default:
82
- return DEFAULT;
83
- }
84
- }
85
-
86
- /** Effective state of one block: the operator's override when set, else the policy's answer. */
87
- export function resolveFold(override: FoldOverride, policyFold: Fold): Fold {
88
- return override ?? policyFold;
89
- }
90
-
91
- /** The fold the policy gives a finished tool call, through the tool's own presentation when asked to. */
92
- export function policyToolFold(detail: TranscriptDetailPolicy, presentation: ToolPresentationPolicy): Fold {
93
- if (detail.toolBody === "per-tool") return presentation.foldDefault;
94
- return detail.toolBody;
95
- }
96
-
97
- /** The fold the policy gives an in-flight tool call. */
98
- export function policyRunningToolFold(detail: TranscriptDetailPolicy): Fold {
99
- return detail.runningTool === "body" ? "expanded" : "folded";
100
- }
101
-
102
- /** The fold the policy gives a reasoning stretch. */
103
- export function policyThinkingFold(detail: TranscriptDetailPolicy): Fold {
104
- return detail.thinking === "rail" ? "expanded" : "folded";
105
- }
106
-
107
- /**
108
- * The fold the policy gives a worker block. `askedByModel` is the origin rule
109
- * the caller already knows: a run the model asked for folds, the operator's own
110
- * run opens.
111
- */
112
- export function policyWorkerFold(detail: TranscriptDetailPolicy, askedByModel: boolean): Fold {
113
- if (detail.worker === "origin") return askedByModel ? "folded" : "expanded";
114
- return detail.worker;
115
- }
116
-
117
- /** The override that flips a block away from its current effective state. */
118
- export function toggledFold(effective: Fold): Fold {
119
- return effective === "expanded" ? "folded" : "expanded";
56
+ export function transcriptDetail(style: OutputStyle = "standard"): TranscriptDetailPolicy {
57
+ return POLICIES[style];
120
58
  }
@@ -44,6 +44,7 @@ import { clockLocal } from "../format-time.js";
44
44
  import { abbreviateModelId } from "../theme/index.js";
45
45
 
46
46
  export type ViewArtifactCategory =
47
+ | "transcript"
47
48
  | "accountability"
48
49
  | "evidence"
49
50
  | "receipt"
@@ -86,6 +87,7 @@ export interface ArtifactProvider {
86
87
  }
87
88
 
88
89
  export interface ArtifactProviderDeps {
90
+ readTranscript?: (() => ViewArtifact[]) | undefined;
89
91
  stateDir: string;
90
92
  dataDir?: string | undefined;
91
93
  dispatch?: Pick<DispatchContract, "listRuns" | "getRun"> | undefined;
@@ -94,6 +96,7 @@ export interface ArtifactProviderDeps {
94
96
  }
95
97
 
96
98
  export const VIEW_ARTIFACT_CATEGORIES: readonly ViewArtifactCategory[] = [
99
+ "transcript",
97
100
  "accountability",
98
101
  "evidence",
99
102
  "receipt",
@@ -1560,6 +1563,7 @@ export class SafetyAuditArtifactProvider implements ArtifactProvider {
1560
1563
 
1561
1564
  export function createDefaultArtifactProviders(deps: ArtifactProviderDeps): ArtifactProvider[] {
1562
1565
  return [
1566
+ { category: "transcript", list: async () => deps.readTranscript?.() ?? [] },
1563
1567
  new AccountabilityArtifactProvider(deps),
1564
1568
  new EvidenceArtifactProvider(deps),
1565
1569
  new ReceiptArtifactProvider(deps),
@@ -102,6 +102,8 @@ function artifactKey(artifact: ViewArtifact): string {
102
102
 
103
103
  function categoryLabel(category: ViewArtifactCategory): string {
104
104
  switch (category) {
105
+ case "transcript":
106
+ return "Transcript details";
105
107
  case "accountability":
106
108
  return "Accountability";
107
109
  case "evidence":
@@ -129,6 +131,7 @@ function categoryLabel(category: ViewArtifactCategory): string {
129
131
 
130
132
  const VIEW_ARTIFACT_CATEGORY_SET = new Set<ViewArtifactCategory>(VIEW_ARTIFACT_CATEGORIES);
131
133
  const BARE_CATEGORY_FILTERS = new Set<ViewArtifactCategory>([
134
+ "transcript",
132
135
  "task-ledger",
133
136
  "workspace",
134
137
  "protected-artifact",
@@ -275,11 +278,8 @@ function groupedViewRows(artifacts: ReadonlyArray<ViewArtifact>): RenderedViewRo
275
278
  const rows: RenderedViewRow[] = [];
276
279
  for (const category of VIEW_ARTIFACT_CATEGORIES) {
277
280
  const items = artifacts.filter((artifact) => artifact.category === category);
281
+ if (items.length === 0) continue;
278
282
  rows.push({ type: "group", category });
279
- if (items.length === 0) {
280
- rows.push({ type: "empty", category });
281
- continue;
282
- }
283
283
  for (const item of items) {
284
284
  rows.push({ type: "item", category, item, itemIndex: artifacts.indexOf(item) });
285
285
  }
@@ -393,11 +393,11 @@ function viewFooterHint(focus: ViewPaneFocus, canVerify: boolean, innerWidth?: n
393
393
  const tiers =
394
394
  focus === "list"
395
395
  ? [
396
- "[↑↓] select · [type] filter · [Tab] detail · [Esc] close",
397
- "[↑↓] select · [Tab] detail · [Esc] close",
398
- "[↑↓] select · [Tab] detail",
396
+ "[↑↓] select · [type] filter · [Enter] detail · [Esc] close",
397
+ "[↑↓] select · [Enter] detail · [Esc] close",
398
+ "[↑↓] select · [Enter] detail",
399
399
  ]
400
- : ["[↑↓] scroll · [Tab] list · [Esc] close", "[↑↓] scroll · [Tab] list"];
400
+ : ["[↑↓] scroll · [Esc] back", "[↑↓] scroll · [Tab] list"];
401
401
  return tiers.find((tier) => visibleWidth(tier) <= budget) ?? (tiers.at(-1) as string);
402
402
  }
403
403
  if (focus === "list") {
@@ -405,20 +405,21 @@ function viewFooterHint(focus: ViewPaneFocus, canVerify: boolean, innerWidth?: n
405
405
  { key: "↑↓", verb: "select" },
406
406
  { key: "←→", verb: "category" },
407
407
  { key: "type", verb: "filter" },
408
- { key: "Tab", verb: "content" },
409
- ...(canVerify ? [{ key: "v", verb: "verify" }] : []),
410
- { key: "o", verb: "path" },
408
+ { key: "Enter", verb: "content" },
409
+ { key: "Ctrl+U", verb: "clear filter" },
411
410
  ]);
412
411
  }
413
- return buildHint([
414
- { key: "↑↓", verb: "scroll" },
415
- { key: "←→", verb: "category" },
416
- { key: "PgUp/PgDn", verb: "page" },
417
- { key: "g/G", verb: "top/bottom" },
418
- { key: "Tab", verb: "list" },
419
- ...(canVerify ? [{ key: "v", verb: "verify" }] : []),
420
- { key: "o", verb: "path" },
421
- ]);
412
+ return buildHint(
413
+ [
414
+ { key: "↑↓", verb: "scroll" },
415
+ { key: "←→", verb: "category" },
416
+ { key: "PgUp/PgDn", verb: "page" },
417
+ { key: "g/G", verb: "top/bottom" },
418
+ ...(canVerify ? [{ key: "v", verb: "verify" }] : []),
419
+ { key: "o", verb: "path" },
420
+ ],
421
+ "back",
422
+ );
422
423
  }
423
424
 
424
425
  export class ViewOverlayView implements Component {
@@ -528,10 +529,11 @@ export class ViewOverlayView implements Component {
528
529
  const content = this.content;
529
530
  if (!content) return [];
530
531
  if (content.status === "error") return content.lines.flatMap((line) => wrapTextWithAnsi(line, Math.max(1, width)));
531
- if (content.format !== "markdown") return content.lines;
532
532
  if (content.renderedLines && content.renderWidth === width) return content.renderedLines;
533
- const md = new Markdown(content.lines.join("\n"), 0, 0, markdownTheme(clioTheme()));
534
- const rendered = md.render(width);
533
+ const rendered =
534
+ content.format === "markdown"
535
+ ? new Markdown(content.lines.join("\n"), 0, 0, markdownTheme(clioTheme())).render(width)
536
+ : content.lines.flatMap((line) => wrapTextWithAnsi(line, Math.max(1, width)));
535
537
  content.renderWidth = width;
536
538
  content.renderedLines = rendered;
537
539
  return rendered;
@@ -556,6 +558,7 @@ export class ViewOverlayView implements Component {
556
558
  }
557
559
 
558
560
  const filtered = this.filteredArtifacts();
561
+ if (filtered.length === 0) return this.fixedLines([...lines, theme.fg("dim", "No matching details.")], width, height);
559
562
  const rows = groupedViewRows(filtered);
560
563
  const selectedRow = rows.findIndex((row) => row.type === "item" && row.itemIndex === this.selectedIndex);
561
564
  const rowHeight = Math.max(1, height - 1);
@@ -634,7 +637,11 @@ export class ViewOverlayView implements Component {
634
637
  }
635
638
 
636
639
  handleInput(data: string): void {
637
- if (matchesKey(data, "tab") || matchesKey(data, "shift+tab")) {
640
+ if (
641
+ matchesKey(data, "tab") ||
642
+ matchesKey(data, "shift+tab") ||
643
+ (this.focus === "list" && matchesKey(data, "enter"))
644
+ ) {
638
645
  this.focus = this.focus === "list" ? "content" : "list";
639
646
  this.options.requestRender?.();
640
647
  return;
@@ -647,21 +654,19 @@ export class ViewOverlayView implements Component {
647
654
  this.selectCategory(1);
648
655
  return;
649
656
  }
650
- if (data === "v") {
657
+ if (this.focus === "content" && data === "v") {
651
658
  this.verifySelected();
652
659
  return;
653
660
  }
654
- if (data === "o") {
661
+ if (this.focus === "content" && data === "o") {
655
662
  this.openPathNotice();
656
663
  return;
657
664
  }
658
665
  if (matchesKey(data, "esc")) {
659
- if (this.focus === "list" && this.filterText.length > 0) {
660
- this.filterText = "";
661
- this.selectInitialFilterMatch();
662
- return;
663
- }
664
- this.options.onClose();
666
+ if (this.focus === "content") {
667
+ this.focus = "list";
668
+ this.options.requestRender?.();
669
+ } else this.options.onClose();
665
670
  return;
666
671
  }
667
672
  if (this.focus === "content") {
@@ -675,14 +680,19 @@ export class ViewOverlayView implements Component {
675
680
 
676
681
  private handleListInput(data: string): boolean {
677
682
  const filtered = this.filteredArtifacts();
678
- if (matchesKey(data, "up") || data === "k") {
683
+ if (matchesKey(data, "up")) {
679
684
  if (filtered.length > 0) this.selectIndex(this.selectedIndex - 1);
680
685
  return true;
681
686
  }
682
- if (matchesKey(data, "down") || data === "j") {
687
+ if (matchesKey(data, "down")) {
683
688
  if (filtered.length > 0) this.selectIndex(this.selectedIndex + 1);
684
689
  return true;
685
690
  }
691
+ if (matchesKey(data, "ctrl+u")) {
692
+ this.filterText = "";
693
+ this.selectInitialFilterMatch();
694
+ return true;
695
+ }
686
696
  if (matchesKey(data, "backspace")) {
687
697
  if (this.filterText.length > 0) {
688
698
  this.filterText = this.filterText.slice(0, -1);
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Typed tool presentation policy. Answers, per tool, what the transcript's
3
- * balanced (`/output default`) view needs to know: does the block open folded
3
+ * Standard view needs to know: does the block open folded
4
4
  * or expanded, does the folded row keep a mutation diff visible, and does a
5
5
  * failed folded row carry an output excerpt.
6
6
  *
@@ -107,7 +107,7 @@ export interface ToolMetadata {
107
107
  */
108
108
  promptHint?: ToolPromptHintMetadata;
109
109
  /**
110
- * How transcript surfaces present this tool's block under `/output default`.
110
+ * How transcript surfaces present this tool's block under Standard output style.
111
111
  * Optional: tools that declare nothing fold like every other tool.
112
112
  */
113
113
  presentation?: ToolPresentationPolicy;
@@ -133,15 +133,15 @@ export function parseNumericPayload(text: string, label: string): NumericPayload
133
133
  const keys = Object.keys(record);
134
134
  if (keys.length > NUMERIC_PAYLOAD_CAPS.keys)
135
135
  return new Error(`${label} exceeds the ${NUMERIC_PAYLOAD_CAPS.keys}-key cap`);
136
- const payload: NumericPayload = {};
136
+ const entries: Array<[string, number | number[]]> = [];
137
137
  let elements = 0;
138
138
  for (const key of keys) {
139
139
  const value = record[key];
140
140
  if (typeof value === "number") {
141
- payload[key] = value;
141
+ entries.push([key, value]);
142
142
  elements += 1;
143
143
  } else if (Array.isArray(value) && value.every((entry) => typeof entry === "number")) {
144
- payload[key] = [...(value as number[])];
144
+ entries.push([key, [...(value as number[])]]);
145
145
  elements += value.length;
146
146
  } else {
147
147
  return new Error(`${label}.${key} must be a number or an array of numbers`);
@@ -150,7 +150,7 @@ export function parseNumericPayload(text: string, label: string): NumericPayload
150
150
  return new Error(`${label} exceeds the ${NUMERIC_PAYLOAD_CAPS.elements}-element cap`);
151
151
  }
152
152
  }
153
- return payload;
153
+ return Object.fromEntries(entries);
154
154
  }
155
155
 
156
156
  function deviation(actual: number, expected: number, index?: number): NumericDeviation {