openhorse 0.2.21 → 0.2.23

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 (263) hide show
  1. package/dist/cli.js +25 -0
  2. package/dist/cli.js.map +1 -1
  3. package/dist/commands/index.d.ts.map +1 -1
  4. package/dist/commands/index.js +189 -49
  5. package/dist/commands/index.js.map +1 -1
  6. package/dist/commands/parser.d.ts.map +1 -1
  7. package/dist/commands/parser.js +8 -3
  8. package/dist/commands/parser.js.map +1 -1
  9. package/dist/commands/types.d.ts +2 -0
  10. package/dist/commands/types.d.ts.map +1 -1
  11. package/dist/commands/types.js.map +1 -1
  12. package/dist/core/cost-tracker.d.ts +45 -5
  13. package/dist/core/cost-tracker.d.ts.map +1 -1
  14. package/dist/core/cost-tracker.js +78 -13
  15. package/dist/core/cost-tracker.js.map +1 -1
  16. package/dist/framework/index.d.ts +1 -1
  17. package/dist/framework/index.d.ts.map +1 -1
  18. package/dist/framework/index.js.map +1 -1
  19. package/dist/framework/query.d.ts +25 -0
  20. package/dist/framework/query.d.ts.map +1 -1
  21. package/dist/framework/query.js +196 -49
  22. package/dist/framework/query.js.map +1 -1
  23. package/dist/framework/store.d.ts +6 -1
  24. package/dist/framework/store.d.ts.map +1 -1
  25. package/dist/framework/store.js +10 -1
  26. package/dist/framework/store.js.map +1 -1
  27. package/dist/harness/capsule.d.ts +1 -0
  28. package/dist/harness/capsule.d.ts.map +1 -1
  29. package/dist/harness/capsule.js +38 -9
  30. package/dist/harness/capsule.js.map +1 -1
  31. package/dist/harness/contract.d.ts +2 -0
  32. package/dist/harness/contract.d.ts.map +1 -1
  33. package/dist/harness/contract.js +78 -14
  34. package/dist/harness/contract.js.map +1 -1
  35. package/dist/harness/index.d.ts +2 -2
  36. package/dist/harness/index.d.ts.map +1 -1
  37. package/dist/harness/index.js +4 -1
  38. package/dist/harness/index.js.map +1 -1
  39. package/dist/harness/ledger.d.ts.map +1 -1
  40. package/dist/harness/ledger.js +6 -2
  41. package/dist/harness/ledger.js.map +1 -1
  42. package/dist/harness/state.d.ts.map +1 -1
  43. package/dist/harness/state.js +37 -7
  44. package/dist/harness/state.js.map +1 -1
  45. package/dist/index.d.ts +2 -1
  46. package/dist/index.d.ts.map +1 -1
  47. package/dist/ink-ui/components/StatusLine.d.ts.map +1 -1
  48. package/dist/ink-ui/components/StatusLine.js +0 -2
  49. package/dist/ink-ui/components/StatusLine.js.map +1 -1
  50. package/dist/ink-ui/screens/ReplScreen.d.ts.map +1 -1
  51. package/dist/ink-ui/screens/ReplScreen.js +12 -5
  52. package/dist/ink-ui/screens/ReplScreen.js.map +1 -1
  53. package/dist/runtime/agent-runtime-controller.d.ts.map +1 -1
  54. package/dist/runtime/agent-runtime-controller.js +11 -1
  55. package/dist/runtime/agent-runtime-controller.js.map +1 -1
  56. package/dist/runtime/aggregate-tool-presenter.d.ts +13 -0
  57. package/dist/runtime/aggregate-tool-presenter.d.ts.map +1 -0
  58. package/dist/runtime/aggregate-tool-presenter.js +44 -0
  59. package/dist/runtime/aggregate-tool-presenter.js.map +1 -0
  60. package/dist/runtime/chat-controller.d.ts +9 -2
  61. package/dist/runtime/chat-controller.d.ts.map +1 -1
  62. package/dist/runtime/chat-controller.js +345 -90
  63. package/dist/runtime/chat-controller.js.map +1 -1
  64. package/dist/runtime/rich-text/ansi-parser.d.ts +1 -1
  65. package/dist/runtime/rich-text/ansi-parser.d.ts.map +1 -1
  66. package/dist/runtime/rich-text/ansi-parser.js +93 -55
  67. package/dist/runtime/rich-text/ansi-parser.js.map +1 -1
  68. package/dist/runtime/rich-text/layout.d.ts +2 -11
  69. package/dist/runtime/rich-text/layout.d.ts.map +1 -1
  70. package/dist/runtime/rich-text/layout.js +280 -245
  71. package/dist/runtime/rich-text/layout.js.map +1 -1
  72. package/dist/runtime/rich-text/markdown-parser.js +31 -24
  73. package/dist/runtime/rich-text/markdown-parser.js.map +1 -1
  74. package/dist/runtime/rich-text/types.d.ts +1 -1
  75. package/dist/runtime/rich-text/types.d.ts.map +1 -1
  76. package/dist/runtime/subagents/production.d.ts.map +1 -1
  77. package/dist/runtime/subagents/production.js +28 -3
  78. package/dist/runtime/subagents/production.js.map +1 -1
  79. package/dist/runtime/subagents/result-parser.d.ts.map +1 -1
  80. package/dist/runtime/subagents/result-parser.js +54 -40
  81. package/dist/runtime/subagents/result-parser.js.map +1 -1
  82. package/dist/runtime/subagents/types.d.ts +2 -0
  83. package/dist/runtime/subagents/types.d.ts.map +1 -1
  84. package/dist/runtime/subagents/types.js +5 -1
  85. package/dist/runtime/subagents/types.js.map +1 -1
  86. package/dist/runtime/tool-detail-repository.d.ts +45 -0
  87. package/dist/runtime/tool-detail-repository.d.ts.map +1 -0
  88. package/dist/runtime/tool-detail-repository.js +131 -0
  89. package/dist/runtime/tool-detail-repository.js.map +1 -0
  90. package/dist/runtime/tool-output-presentation.d.ts +89 -0
  91. package/dist/runtime/tool-output-presentation.d.ts.map +1 -0
  92. package/dist/runtime/tool-output-presentation.js +329 -0
  93. package/dist/runtime/tool-output-presentation.js.map +1 -0
  94. package/dist/runtime/ui-events.d.ts +17 -0
  95. package/dist/runtime/ui-events.d.ts.map +1 -1
  96. package/dist/runtime/ui-events.js.map +1 -1
  97. package/dist/runtime/ui-view-model.d.ts +9 -0
  98. package/dist/runtime/ui-view-model.d.ts.map +1 -1
  99. package/dist/runtime/ui-view-model.js +59 -37
  100. package/dist/runtime/ui-view-model.js.map +1 -1
  101. package/dist/services/compact/auto-compact.d.ts +15 -2
  102. package/dist/services/compact/auto-compact.d.ts.map +1 -1
  103. package/dist/services/compact/auto-compact.js +56 -24
  104. package/dist/services/compact/auto-compact.js.map +1 -1
  105. package/dist/services/compact/compact.d.ts +2 -0
  106. package/dist/services/compact/compact.d.ts.map +1 -1
  107. package/dist/services/compact/compact.js +22 -5
  108. package/dist/services/compact/compact.js.map +1 -1
  109. package/dist/services/compact/coordinator.d.ts +26 -0
  110. package/dist/services/compact/coordinator.d.ts.map +1 -0
  111. package/dist/services/compact/coordinator.js +44 -0
  112. package/dist/services/compact/coordinator.js.map +1 -0
  113. package/dist/services/compact/index.d.ts +1 -0
  114. package/dist/services/compact/index.d.ts.map +1 -1
  115. package/dist/services/compact/index.js +3 -1
  116. package/dist/services/compact/index.js.map +1 -1
  117. package/dist/services/compact/summary-generator.d.ts +6 -2
  118. package/dist/services/compact/summary-generator.d.ts.map +1 -1
  119. package/dist/services/compact/summary-generator.js +73 -31
  120. package/dist/services/compact/summary-generator.js.map +1 -1
  121. package/dist/services/config-dir.d.ts +11 -1
  122. package/dist/services/config-dir.d.ts.map +1 -1
  123. package/dist/services/config-dir.js +19 -2
  124. package/dist/services/config-dir.js.map +1 -1
  125. package/dist/services/config.d.ts +4 -2
  126. package/dist/services/config.d.ts.map +1 -1
  127. package/dist/services/config.js +40 -0
  128. package/dist/services/config.js.map +1 -1
  129. package/dist/services/doctor.d.ts +2 -0
  130. package/dist/services/doctor.d.ts.map +1 -1
  131. package/dist/services/doctor.js +8 -1
  132. package/dist/services/doctor.js.map +1 -1
  133. package/dist/services/global-config.d.ts +10 -0
  134. package/dist/services/global-config.d.ts.map +1 -1
  135. package/dist/services/global-config.js.map +1 -1
  136. package/dist/services/llm.d.ts +21 -4
  137. package/dist/services/llm.d.ts.map +1 -1
  138. package/dist/services/llm.js +66 -8
  139. package/dist/services/llm.js.map +1 -1
  140. package/dist/services/model-context.d.ts +36 -0
  141. package/dist/services/model-context.d.ts.map +1 -1
  142. package/dist/services/model-context.js +245 -32
  143. package/dist/services/model-context.js.map +1 -1
  144. package/dist/services/redaction.js +1 -1
  145. package/dist/services/redaction.js.map +1 -1
  146. package/dist/services/session-storage.d.ts +42 -1
  147. package/dist/services/session-storage.d.ts.map +1 -1
  148. package/dist/services/session-storage.js +175 -54
  149. package/dist/services/session-storage.js.map +1 -1
  150. package/dist/services/storage-maintenance.d.ts.map +1 -1
  151. package/dist/services/storage-maintenance.js +3 -2
  152. package/dist/services/storage-maintenance.js.map +1 -1
  153. package/dist/services/usage-state.d.ts +55 -5
  154. package/dist/services/usage-state.d.ts.map +1 -1
  155. package/dist/services/usage-state.js +185 -59
  156. package/dist/services/usage-state.js.map +1 -1
  157. package/dist/services/verification-profile.d.ts.map +1 -1
  158. package/dist/services/verification-profile.js +3 -2
  159. package/dist/services/verification-profile.js.map +1 -1
  160. package/dist/services/workspace-diff.d.ts.map +1 -1
  161. package/dist/services/workspace-diff.js +34 -3
  162. package/dist/services/workspace-diff.js.map +1 -1
  163. package/dist/services/yaml-config.js +1 -1
  164. package/dist/skills/runtime.d.ts +9 -5
  165. package/dist/skills/runtime.d.ts.map +1 -1
  166. package/dist/skills/runtime.js +157 -12
  167. package/dist/skills/runtime.js.map +1 -1
  168. package/dist/terminal-ui/editor.d.ts.map +1 -1
  169. package/dist/terminal-ui/editor.js +14 -3
  170. package/dist/terminal-ui/editor.js.map +1 -1
  171. package/dist/terminal-ui/launch.d.ts +21 -0
  172. package/dist/terminal-ui/launch.d.ts.map +1 -1
  173. package/dist/terminal-ui/launch.js +203 -45
  174. package/dist/terminal-ui/launch.js.map +1 -1
  175. package/dist/terminal-ui/output-queue.d.ts +43 -0
  176. package/dist/terminal-ui/output-queue.d.ts.map +1 -0
  177. package/dist/terminal-ui/output-queue.js +139 -0
  178. package/dist/terminal-ui/output-queue.js.map +1 -0
  179. package/dist/terminal-ui/raw-editor.d.ts +42 -0
  180. package/dist/terminal-ui/raw-editor.d.ts.map +1 -1
  181. package/dist/terminal-ui/raw-editor.js +337 -41
  182. package/dist/terminal-ui/raw-editor.js.map +1 -1
  183. package/dist/tools/bash_security.d.ts.map +1 -1
  184. package/dist/tools/bash_security.js +14 -5
  185. package/dist/tools/bash_security.js.map +1 -1
  186. package/dist/tools/index.d.ts.map +1 -1
  187. package/dist/tools/index.js +176 -73
  188. package/dist/tools/index.js.map +1 -1
  189. package/dist/tools/plan.d.ts.map +1 -1
  190. package/dist/tools/plan.js +7 -0
  191. package/dist/tools/plan.js.map +1 -1
  192. package/dist/tools/web.d.ts +10 -0
  193. package/dist/tools/web.d.ts.map +1 -1
  194. package/dist/tools/web.js +81 -1
  195. package/dist/tools/web.js.map +1 -1
  196. package/dist/tui-core/input-parser.d.ts +9 -2
  197. package/dist/tui-core/input-parser.d.ts.map +1 -1
  198. package/dist/tui-core/input-parser.js +57 -3
  199. package/dist/tui-core/input-parser.js.map +1 -1
  200. package/dist/tui-core/style.d.ts +16 -0
  201. package/dist/tui-core/style.d.ts.map +1 -1
  202. package/dist/tui-core/style.js +22 -1
  203. package/dist/tui-core/style.js.map +1 -1
  204. package/dist/tui-ui/inline-surface.d.ts +69 -12
  205. package/dist/tui-ui/inline-surface.d.ts.map +1 -1
  206. package/dist/tui-ui/inline-surface.js +366 -94
  207. package/dist/tui-ui/inline-surface.js.map +1 -1
  208. package/dist/tui-ui/input-ownership.d.ts +46 -0
  209. package/dist/tui-ui/input-ownership.d.ts.map +1 -0
  210. package/dist/tui-ui/input-ownership.js +74 -0
  211. package/dist/tui-ui/input-ownership.js.map +1 -0
  212. package/dist/tui-ui/launch.d.ts +2 -0
  213. package/dist/tui-ui/launch.d.ts.map +1 -1
  214. package/dist/tui-ui/launch.js +289 -43
  215. package/dist/tui-ui/launch.js.map +1 -1
  216. package/dist/tui-ui/layout.d.ts +52 -2
  217. package/dist/tui-ui/layout.d.ts.map +1 -1
  218. package/dist/tui-ui/layout.js +230 -76
  219. package/dist/tui-ui/layout.js.map +1 -1
  220. package/dist/tui-ui/runner.d.ts +101 -8
  221. package/dist/tui-ui/runner.d.ts.map +1 -1
  222. package/dist/tui-ui/runner.js +843 -51
  223. package/dist/tui-ui/runner.js.map +1 -1
  224. package/dist/tui-ui/state.d.ts +70 -4
  225. package/dist/tui-ui/state.d.ts.map +1 -1
  226. package/dist/tui-ui/state.js +251 -20
  227. package/dist/tui-ui/state.js.map +1 -1
  228. package/dist/tui-ui/theme-profile.d.ts +25 -0
  229. package/dist/tui-ui/theme-profile.d.ts.map +1 -0
  230. package/dist/tui-ui/theme-profile.js +130 -0
  231. package/dist/tui-ui/theme-profile.js.map +1 -0
  232. package/dist/tui-ui/theme.d.ts +13 -0
  233. package/dist/tui-ui/theme.d.ts.map +1 -0
  234. package/dist/tui-ui/theme.js +20 -0
  235. package/dist/tui-ui/theme.js.map +1 -0
  236. package/dist/tui-ui/transcript-cache.d.ts +5 -3
  237. package/dist/tui-ui/transcript-cache.d.ts.map +1 -1
  238. package/dist/tui-ui/transcript-cache.js +16 -9
  239. package/dist/tui-ui/transcript-cache.js.map +1 -1
  240. package/dist/tui-ui/transcript-inspector-layout.d.ts +9 -0
  241. package/dist/tui-ui/transcript-inspector-layout.d.ts.map +1 -0
  242. package/dist/tui-ui/transcript-inspector-layout.js +97 -0
  243. package/dist/tui-ui/transcript-inspector-layout.js.map +1 -0
  244. package/dist/tui-ui/transcript-inspector-surface.d.ts +18 -0
  245. package/dist/tui-ui/transcript-inspector-surface.d.ts.map +1 -0
  246. package/dist/tui-ui/transcript-inspector-surface.js +88 -0
  247. package/dist/tui-ui/transcript-inspector-surface.js.map +1 -0
  248. package/dist/tui-ui/transcript-inspector.d.ts +35 -0
  249. package/dist/tui-ui/transcript-inspector.d.ts.map +1 -0
  250. package/dist/tui-ui/transcript-inspector.js +128 -0
  251. package/dist/tui-ui/transcript-inspector.js.map +1 -0
  252. package/dist/tui-ui/transcript-layout.d.ts +22 -0
  253. package/dist/tui-ui/transcript-layout.d.ts.map +1 -0
  254. package/dist/tui-ui/transcript-layout.js +439 -0
  255. package/dist/tui-ui/transcript-layout.js.map +1 -0
  256. package/dist/ui/status-bar.d.ts +1 -1
  257. package/dist/ui/status-bar.d.ts.map +1 -1
  258. package/dist/ui/status-bar.js +2 -15
  259. package/dist/ui/status-bar.js.map +1 -1
  260. package/dist/ui-v2/components/shell.d.ts.map +1 -1
  261. package/dist/ui-v2/components/shell.js +0 -8
  262. package/dist/ui-v2/components/shell.js.map +1 -1
  263. package/package.json +1 -1
@@ -61,6 +61,8 @@ const ui_events_1 = require("./ui-events");
61
61
  const ui_view_model_1 = require("./ui-view-model");
62
62
  const agent_status_1 = require("./agent-status");
63
63
  const loop_budget_1 = require("./loop-budget");
64
+ const tool_output_presentation_1 = require("./tool-output-presentation");
65
+ const aggregate_tool_presenter_1 = require("./aggregate-tool-presenter");
64
66
  const ANSI_PATTERN = /\x1b\[[0-9;?]*[A-Za-z]/g;
65
67
  const LOCAL_FAST_PATH_INLINE_OUTPUT_BYTES = 2048;
66
68
  const TRACE_ARGS_ARTIFACT_THRESHOLD_BYTES = 160;
@@ -121,7 +123,18 @@ function compactMiddle(text, maxLength) {
121
123
  return `${compact.slice(0, headLength)}...${compact.slice(-tailLength)}`;
122
124
  }
123
125
  function compactToolArgs(args, maxLength = 160) {
124
- for (const key of ['path', 'file_path', 'file', 'cwd', 'command', 'pattern', 'query', 'url', 'target', 'sessionId']) {
126
+ for (const key of [
127
+ 'path',
128
+ 'file_path',
129
+ 'file',
130
+ 'cwd',
131
+ 'command',
132
+ 'pattern',
133
+ 'query',
134
+ 'url',
135
+ 'target',
136
+ 'sessionId',
137
+ ]) {
125
138
  const value = args[key];
126
139
  if (typeof value === 'string') {
127
140
  return compactMiddle(value, maxLength);
@@ -148,16 +161,14 @@ function buildTraceArgsDetails(projectPath, name, args) {
148
161
  const argsSummary = compactToolArgs(args);
149
162
  const fullArgs = (0, session_storage_1.redactTraceText)(fullToolArgsForTrace(name, args)).trim();
150
163
  const argsBytes = byteLength(fullArgs);
151
- if (!projectPath
152
- || !fullArgs
153
- || fullArgs === (0, session_storage_1.redactTraceText)(argsSummary)
154
- || argsBytes <= TRACE_ARGS_ARTIFACT_THRESHOLD_BYTES) {
164
+ if (!projectPath ||
165
+ !fullArgs ||
166
+ fullArgs === (0, session_storage_1.redactTraceText)(argsSummary) ||
167
+ argsBytes <= TRACE_ARGS_ARTIFACT_THRESHOLD_BYTES) {
155
168
  return { argsSummary };
156
169
  }
157
170
  const artifact = (0, tool_artifacts_1.storeArtifact)(projectPath, `${name}-args`, fullArgs, argsBytes);
158
- return artifact
159
- ? { argsSummary, argsArtifactId: artifact.id, argsBytes }
160
- : { argsSummary };
171
+ return artifact ? { argsSummary, argsArtifactId: artifact.id, argsBytes } : { argsSummary };
161
172
  }
162
173
  function parseToolCallArgsForRuntime(toolCall) {
163
174
  try {
@@ -211,7 +222,9 @@ function createPreToolCheckpoint(events, sessionId, turnId, checkpointId, cwd, t
211
222
  checkpointFiles: checkpoint?.files.map(file => file.path) ?? [],
212
223
  workspaceFiles: relativeTargets,
213
224
  note: checkpoint
214
- ? (risky ? 'risky_multi_file_checkpoint' : 'pre_edit_checkpoint')
225
+ ? risky
226
+ ? 'risky_multi_file_checkpoint'
227
+ : 'pre_edit_checkpoint'
215
228
  : 'pre_edit_checkpoint_skipped',
216
229
  });
217
230
  return { created: true, targetCount: targets.length, risky };
@@ -239,7 +252,9 @@ function formatWorkspaceFileForTrace(file) {
239
252
  const metadata = [
240
253
  typeof file.sizeBytes === 'number' ? `${file.sizeBytes}B` : '',
241
254
  typeof file.mtimeMs === 'number' ? `mtime=${file.mtimeMs}` : '',
242
- ].filter(Boolean).join(' ');
255
+ ]
256
+ .filter(Boolean)
257
+ .join(' ');
243
258
  return `${file.status} ${file.path}${metadata ? ` (${metadata})` : ''}`;
244
259
  }
245
260
  function appendWorkspaceSnapshotTrace(events, sessionId, turnId, phase, snapshot) {
@@ -275,19 +290,13 @@ function appendWorkspaceDeltaTrace(events, sessionId, turnId, before, after) {
275
290
  return delta;
276
291
  }
277
292
  function workspaceDeltaHasTurnChanges(delta) {
278
- return delta.newFilesByTurn.length > 0
279
- || delta.changedByTurn.length > 0
280
- || delta.resolvedByTurn.length > 0;
293
+ return (delta.newFilesByTurn.length > 0 ||
294
+ delta.changedByTurn.length > 0 ||
295
+ delta.resolvedByTurn.length > 0);
281
296
  }
282
297
  function formatFailureRecoveryNotice(turnId, delta, checkpointIds) {
283
- const files = compactPathList([
284
- ...delta.newFilesByTurn,
285
- ...delta.changedByTurn,
286
- ...delta.resolvedByTurn,
287
- ], 8);
288
- const fileText = files.length > 0
289
- ? files.join(', ')
290
- : 'workspace changes recorded';
298
+ const files = compactPathList([...delta.newFilesByTurn, ...delta.changedByTurn, ...delta.resolvedByTurn], 8);
299
+ const fileText = files.length > 0 ? files.join(', ') : 'workspace changes recorded';
291
300
  const checkpointText = checkpointIds.length > 0
292
301
  ? ` Checkpoints: ${checkpointIds.join(', ')}. Preview rollback with /checkpoint restore <id>; restore each listed checkpoint if multiple.`
293
302
  : '';
@@ -348,15 +357,17 @@ function withVerificationLoopStats(stats, summary) {
348
357
  verificationPassedCommands: compactVerificationCommands(summary.passedCommands),
349
358
  verificationFailedCommands: compactVerificationCommands(summary.failedCommands),
350
359
  verificationMissingCommands: compactVerificationCommands(summary.missingCommands),
351
- verificationSkippedReason: summary.skippedReason ? (0, session_storage_1.redactTraceText)(summary.skippedReason) : undefined,
360
+ verificationSkippedReason: summary.skippedReason
361
+ ? (0, session_storage_1.redactTraceText)(summary.skippedReason)
362
+ : undefined,
352
363
  };
353
364
  }
354
365
  function shouldRecordVerificationLoopStats(profile, summary) {
355
- return profile.changedFiles.length > 0
356
- || summary.commandsRun.length > 0
357
- || summary.passedCommands.length > 0
358
- || summary.failedCommands.length > 0
359
- || summary.missingCommands.length > 0;
366
+ return (profile.changedFiles.length > 0 ||
367
+ summary.commandsRun.length > 0 ||
368
+ summary.passedCommands.length > 0 ||
369
+ summary.failedCommands.length > 0 ||
370
+ summary.missingCommands.length > 0);
360
371
  }
361
372
  function appendPostWorkspaceTrace(events, sessionId, turnId, cwd, before, verificationResults = []) {
362
373
  const postWorkspace = (0, workspace_state_1.captureWorkspaceSnapshot)(cwd);
@@ -451,11 +462,78 @@ function toolStartContent(event) {
451
462
  function toolFinishContent(event) {
452
463
  return (0, ui_view_model_1.formatToolActivityTranscript)((0, ui_view_model_1.toolActivityFromFinished)(event, compactToolArgs(event.args, TOOL_TRANSCRIPT_ARG_BUDGET)));
453
464
  }
465
+ function structuredToolStartActivity(event, seq) {
466
+ const command = event.name === 'exec_command' && typeof event.args.command === 'string'
467
+ ? event.args.command
468
+ : undefined;
469
+ const safeCommand = command ? (0, session_storage_1.redactTraceText)(command) : undefined;
470
+ return {
471
+ state: 'running',
472
+ name: event.name,
473
+ detail: command ? '' : (0, session_storage_1.redactTraceText)(compactToolArgs(event.args, TOOL_TRANSCRIPT_ARG_BUDGET)),
474
+ command: safeCommand,
475
+ body: '',
476
+ seq,
477
+ };
478
+ }
479
+ function structuredToolFinishActivity(event, seq, options = {}) {
480
+ const modelVisible = (0, tool_serializer_1.parseToolResultEnvelope)(event.modelVisibleResult);
481
+ const durable = (0, tool_serializer_1.parseToolResultEnvelope)(event.result);
482
+ const durableOutput = typeof durable.output === 'string' ? durable.output : '';
483
+ const displayOutput = typeof modelVisible.output === 'string' ? modelVisible.output : durableOutput;
484
+ const outputBytes = event.outputBytes ?? Buffer.byteLength(durableOutput, 'utf8');
485
+ const aggregatePresentation = (0, aggregate_tool_presenter_1.presentAggregateToolResult)(event.name, durableOutput, outputBytes);
486
+ const aggregate = aggregatePresentation?.view;
487
+ const storedArtifact = event.artifactRef ?? (options.projectPath && durableOutput && (outputBytes > tool_output_presentation_1.DEFAULT_TOOL_OUTPUT_POLICY.inlineMaxBytes || aggregate)
488
+ ? (0, tool_artifacts_1.storeArtifact)(options.projectPath, event.name, durableOutput, outputBytes) ?? undefined
489
+ : undefined);
490
+ const artifactRef = storedArtifact
491
+ ? { id: storedArtifact.id, outputBytes: storedArtifact.outputBytes }
492
+ : undefined;
493
+ const outputView = (0, tool_output_presentation_1.createToolOutputView)({
494
+ toolName: event.name,
495
+ success: event.success,
496
+ summary: event.summary,
497
+ rawOutput: durableOutput.length <= 64 * 1024 ? durableOutput : displayOutput,
498
+ outputBytes,
499
+ artifactRef,
500
+ callId: event.callId,
501
+ sequence: seq,
502
+ turnId: options.turnId,
503
+ policy: tool_output_presentation_1.DEFAULT_TOOL_OUTPUT_POLICY,
504
+ });
505
+ if (aggregate) {
506
+ outputView.aggregate = {
507
+ ...aggregate,
508
+ steps: aggregate.steps.map(step => ({ ...step, detailRef: outputView.detailRef })),
509
+ };
510
+ }
511
+ const command = event.name === 'exec_command' && typeof event.args.command === 'string'
512
+ ? event.args.command
513
+ : undefined;
514
+ const safeCommand = command ? (0, session_storage_1.redactTraceText)(command) : undefined;
515
+ return {
516
+ state: event.success ? 'success' : 'error',
517
+ name: event.name,
518
+ detail: command ? '' : (0, session_storage_1.redactTraceText)(compactToolArgs(event.args, TOOL_TRANSCRIPT_ARG_BUDGET)),
519
+ command: safeCommand,
520
+ duration: `${event.duration}ms`,
521
+ summary: event.summary ? (0, session_storage_1.redactTraceText)(event.summary.split(/\r?\n/u, 1)[0]) : undefined,
522
+ outputBytes,
523
+ body: (0, session_storage_1.redactTraceText)(modelVisible.output),
524
+ error: event.error ? (0, session_storage_1.redactTraceText)(event.error) : undefined,
525
+ seq,
526
+ artifactHint: artifactRef ? `/artifacts show ${artifactRef.id} --full` : undefined,
527
+ callId: event.callId,
528
+ turnId: options.turnId,
529
+ outputView,
530
+ };
531
+ }
454
532
  function isSyntheticCompactContext(content) {
455
- return content.startsWith('[OpenHorse Context State v2]')
456
- || content.startsWith('[Context Summary]')
457
- || content.startsWith('I will continue from this OpenHorse Context State')
458
- || content.startsWith('I understand the context. I will continue the conversation with this background information.');
533
+ return (content.startsWith('[OpenHorse Context State v2]') ||
534
+ content.startsWith('[Context Summary]') ||
535
+ content.startsWith('I will continue from this OpenHorse Context State') ||
536
+ content.startsWith('I understand the context. I will continue the conversation with this background information.'));
459
537
  }
460
538
  function sessionToolCallSummaries(message) {
461
539
  return (message.tool_calls ?? []).map(call => {
@@ -506,16 +584,25 @@ function sessionToolResultSummary(message, toolCallsById) {
506
584
  const args = parseToolCallArgs(call.function.arguments);
507
585
  const detail = compactToolArgs(args);
508
586
  const parsed = parseSessionToolResult(message.content);
509
- const firstLine = parsed.summary || `${parsed.success ? '✓' : '✗'} ${call.function.name}${detail ? ` ${detail}` : ''}`;
587
+ const firstLine = parsed.summary ||
588
+ `${parsed.success ? '✓' : '✗'} ${call.function.name}${detail ? ` ${detail}` : ''}`;
510
589
  return parsed.error ? `${firstLine}\nError: ${parsed.error}` : firstLine;
511
590
  }
512
- function sessionMessagesToTranscriptEntries(sessionId) {
513
- const session = (0, session_storage_1.loadSessionMeta)(sessionId);
514
- const displayStartTime = session?.transcriptDisplayStartTime;
515
- const messages = (0, session_storage_1.readSessionMessages)(sessionId).filter(message => typeof displayStartTime !== 'number' || (message.timestamp ?? 0) >= displayStartTime);
591
+ function sessionMessagesToTranscriptEntries(sessionId, options = {}) {
592
+ const messages = (0, session_storage_1.loadSessionTranscriptMessages)(sessionId);
593
+ const resultTraces = options.includeToolOutputViews
594
+ ? (0, session_storage_1.readSessionTraceEvents)(sessionId).filter(trace => trace.type === 'tool_result' && trace.callId)
595
+ : [];
596
+ const resultTraceByCallId = new Map(resultTraces.map(trace => [trace.callId, trace]));
597
+ const sequenceByCallId = new Map();
598
+ resultTraces.forEach((trace, index) => {
599
+ if (!sequenceByCallId.has(trace.callId))
600
+ sequenceByCallId.set(trace.callId, index + 1);
601
+ });
516
602
  const entries = [];
517
603
  const toolCallsById = new Map();
518
604
  const completedToolCallIds = new Set();
605
+ let fallbackToolSequence = 0;
519
606
  for (const message of messages) {
520
607
  for (const call of message.tool_calls ?? []) {
521
608
  toolCallsById.set(call.id, call);
@@ -538,17 +625,73 @@ function sessionMessagesToTranscriptEntries(sessionId) {
538
625
  }
539
626
  for (const summary of sessionToolCallSummaries(message)) {
540
627
  if (!completedToolCallIds.has(summary.id)) {
541
- entries.push({ id: `${idBase}-tool-call-${entries.length}`, role: 'tool', content: summary.content });
628
+ entries.push({
629
+ id: `${idBase}-tool-call-${entries.length}`,
630
+ role: 'tool',
631
+ content: summary.content,
632
+ });
542
633
  }
543
634
  }
544
635
  return;
545
636
  }
546
637
  if (message.role === 'tool') {
547
638
  const summary = sessionToolResultSummary(message, toolCallsById);
639
+ const call = message.toolCallId ? toolCallsById.get(message.toolCallId) : undefined;
640
+ const trace = message.toolCallId ? resultTraceByCallId.get(message.toolCallId) : undefined;
641
+ const durable = (0, tool_serializer_1.parseToolResultEnvelope)(message.content);
642
+ const modelVisible = message.modelVisibleContent
643
+ ? (0, tool_serializer_1.parseToolResultEnvelope)(message.modelVisibleContent)
644
+ : durable;
645
+ const durableOutput = typeof durable.output === 'string' ? durable.output : '';
646
+ const displayOutput = typeof modelVisible.output === 'string' ? modelVisible.output : durableOutput;
647
+ const outputBytes = trace?.outputBytes
648
+ ?? (durable.schemaVersion === 1 ? durable.outputBytes : undefined)
649
+ ?? Buffer.byteLength(durableOutput, 'utf8');
650
+ const artifactId = durable.artifactRef?.id ?? trace?.artifactId;
651
+ fallbackToolSequence += 1;
652
+ const sequence = message.toolCallId
653
+ ? sequenceByCallId.get(message.toolCallId) ?? fallbackToolSequence
654
+ : fallbackToolSequence;
655
+ const outputView = options.includeToolOutputViews && call && message.toolCallId
656
+ ? (0, tool_output_presentation_1.createToolOutputView)({
657
+ toolName: call.function.name,
658
+ success: durable.success,
659
+ summary: durable.summary,
660
+ rawOutput: durableOutput.length <= 64 * 1024 ? durableOutput : displayOutput,
661
+ outputBytes,
662
+ artifactRef: artifactId ? { id: artifactId, outputBytes } : undefined,
663
+ callId: message.toolCallId,
664
+ sequence,
665
+ turnId: trace?.turnId,
666
+ policy: tool_output_presentation_1.DEFAULT_TOOL_OUTPUT_POLICY,
667
+ })
668
+ : undefined;
548
669
  entries.push({
549
670
  id: `${idBase}-tool`,
550
671
  role: 'tool',
551
- content: summary ?? (message.toolCallId ? `Tool result ${message.toolCallId}\n${message.content}` : message.content),
672
+ content: summary ??
673
+ (message.toolCallId
674
+ ? `Tool result ${message.toolCallId}\n${message.content}`
675
+ : message.content),
676
+ toolActivity: call && message.toolCallId && outputView
677
+ ? {
678
+ state: durable.success ? 'success' : 'error',
679
+ name: call.function.name,
680
+ detail: (0, session_storage_1.redactTraceText)(compactToolArgs(parseToolCallArgs(call.function.arguments))),
681
+ summary: durable.summary
682
+ ? (0, session_storage_1.redactTraceText)(durable.summary.split(/\r?\n/u, 1)[0])
683
+ : undefined,
684
+ outputBytes,
685
+ body: (0, session_storage_1.redactTraceText)(durableOutput.length <= 64 * 1024 ? durableOutput : displayOutput),
686
+ error: durable.error ? (0, session_storage_1.redactTraceText)(durable.error) : undefined,
687
+ duration: typeof trace?.duration === 'number' ? `${trace.duration}ms` : undefined,
688
+ seq: sequence,
689
+ artifactHint: artifactId ? `/artifacts show ${artifactId} --full` : undefined,
690
+ callId: message.toolCallId,
691
+ turnId: trace?.turnId,
692
+ outputView,
693
+ }
694
+ : undefined,
552
695
  });
553
696
  return;
554
697
  }
@@ -633,19 +776,27 @@ function parseLocalFastPath(input) {
633
776
  }
634
777
  const readMatch = /^(?:read|读取)\s+(.+)$/i.exec(text);
635
778
  const readTarget = readMatch?.[1]?.trim();
636
- const looksLikePath = Boolean(readTarget)
637
- && !/\s/.test(readTarget)
638
- && (/[/\\.]/.test(readTarget) || readTarget.startsWith('~'));
779
+ const looksLikePath = Boolean(readTarget) &&
780
+ !/\s/.test(readTarget) &&
781
+ (/[/\\.]/.test(readTarget) || readTarget.startsWith('~'));
639
782
  if (readTarget && looksLikePath) {
640
783
  return { tool: 'read_file', args: { path: readTarget }, label: `read ${readTarget}` };
641
784
  }
642
785
  const grepMatch = /^(?:grep|搜索)\s+(.+)$/i.exec(text);
643
786
  if (grepMatch?.[1]?.trim()) {
644
- return { tool: 'grep', args: { pattern: grepMatch[1].trim() }, label: `grep ${grepMatch[1].trim()}` };
787
+ return {
788
+ tool: 'grep',
789
+ args: { pattern: grepMatch[1].trim() },
790
+ label: `grep ${grepMatch[1].trim()}`,
791
+ };
645
792
  }
646
793
  const runTestMatch = /^(?:run\s+test|运行测试)\s*[::]\s*(.+)$/i.exec(text);
647
794
  if (runTestMatch?.[1]?.trim()) {
648
- return { tool: 'exec_command', args: { command: runTestMatch[1].trim() }, label: `run test: ${runTestMatch[1].trim()}` };
795
+ return {
796
+ tool: 'exec_command',
797
+ args: { command: runTestMatch[1].trim() },
798
+ label: `run test: ${runTestMatch[1].trim()}`,
799
+ };
649
800
  }
650
801
  return null;
651
802
  }
@@ -683,7 +834,7 @@ function formatLocalFastPathAssistantContent(action, rawResult, projectPath) {
683
834
  lines.push('', 'Preview:', preview);
684
835
  return { content: lines.join('\n'), artifactRef };
685
836
  }
686
- function createToolEventPresenter(events) {
837
+ function createToolEventPresenter(events, options = {}) {
687
838
  const runningToolEntries = new Map();
688
839
  let toolSequenceCounter = 0;
689
840
  return {
@@ -693,12 +844,7 @@ function createToolEventPresenter(events) {
693
844
  role: 'tool',
694
845
  title: 'tool',
695
846
  content: toolStartContent(event),
696
- toolActivity: {
697
- state: 'running',
698
- name: event.name,
699
- detail: '',
700
- seq,
701
- },
847
+ toolActivity: structuredToolStartActivity(event, seq),
702
848
  });
703
849
  runningToolEntries.set(event.callId, {
704
850
  entryId,
@@ -721,15 +867,7 @@ function createToolEventPresenter(events) {
721
867
  const content = toolFinishContent(event);
722
868
  const stored = runningToolEntries.get(event.callId);
723
869
  const seq = stored?.sequence ?? ++toolSequenceCounter;
724
- const state = event.success ? 'success' : 'error';
725
- const toolActivity = {
726
- state,
727
- name: event.name,
728
- detail: '',
729
- duration: `${event.duration}ms`,
730
- error: event.error,
731
- seq,
732
- };
870
+ const toolActivity = structuredToolFinishActivity(event, seq, options);
733
871
  if (stored) {
734
872
  events.finalize(stored.entryId, {
735
873
  role: event.success ? 'tool' : 'error',
@@ -757,7 +895,12 @@ function createToolEventPresenter(events) {
757
895
  summary: event.summary,
758
896
  error: event.error,
759
897
  outputBytes: event.outputBytes,
760
- artifactRef: event.artifactRef,
898
+ artifactRef: toolActivity.outputView?.detailRef?.artifactId
899
+ ? {
900
+ id: toolActivity.outputView.detailRef.artifactId,
901
+ outputBytes: toolActivity.outputView.detailRef.outputBytes,
902
+ }
903
+ : event.artifactRef,
761
904
  sequence: seq,
762
905
  batchCount: event.batchCount,
763
906
  batchIndex: event.batchIndex,
@@ -773,6 +916,7 @@ function createToolEventPresenter(events) {
773
916
  state: 'skipped',
774
917
  name: entry.name,
775
918
  detail: '',
919
+ body: '',
776
920
  error: reason,
777
921
  seq: entry.sequence,
778
922
  },
@@ -800,7 +944,7 @@ async function captureConsoleOutput(fn) {
800
944
  const originalError = console.error;
801
945
  const originalWarn = console.warn;
802
946
  const capture = (...args) => {
803
- lines.push(stripAnsi(args.map(arg => typeof arg === 'string' ? arg : JSON.stringify(arg)).join(' ')));
947
+ lines.push(stripAnsi(args.map(arg => (typeof arg === 'string' ? arg : JSON.stringify(arg))).join(' ')));
804
948
  };
805
949
  console.log = capture;
806
950
  console.error = capture;
@@ -850,7 +994,7 @@ class AgentChatController {
850
994
  }
851
995
  const command = (0, commands_1.findCommand)(parsed.name);
852
996
  if (!command) {
853
- if ((0, skills_1.hasMatchingSkill)(text)) {
997
+ if ((0, skills_1.hasMatchingSkill)(text, this.runtime.cwd)) {
854
998
  await this.runChat(text, options);
855
999
  return;
856
1000
  }
@@ -902,7 +1046,9 @@ class AgentChatController {
902
1046
  }
903
1047
  }
904
1048
  async runLocalFastPath(input, action, options = {}) {
905
- const activeSession = this.runtime.getSession() ?? this.runtime.ensureSession() ?? (0, session_storage_1.loadSessionMeta)(this.runtime.getSession()?.id ?? '');
1049
+ const activeSession = this.runtime.getSession() ??
1050
+ this.runtime.ensureSession() ??
1051
+ (0, session_storage_1.loadSessionMeta)(this.runtime.getSession()?.id ?? '');
906
1052
  const sessionId = activeSession?.id;
907
1053
  const turnId = traceTurnId(options.turnId);
908
1054
  const localCallId = `local-${turnId}`;
@@ -981,22 +1127,27 @@ class AgentChatController {
981
1127
  modelVisibleToolBytes: 0,
982
1128
  summarizedBytes: outputBytes,
983
1129
  });
1130
+ const localToolResultEvent = {
1131
+ type: 'tool_result',
1132
+ name: action.tool,
1133
+ args: action.args,
1134
+ callId: localCallId,
1135
+ result,
1136
+ modelVisibleResult: result,
1137
+ duration,
1138
+ success: envelope.success,
1139
+ error: envelope.error,
1140
+ summary: envelope.summary,
1141
+ outputBytes,
1142
+ artifactRef: formattedLocalResult.artifactRef,
1143
+ };
984
1144
  this.events.append({
985
1145
  role: envelope.success ? 'tool' : 'error',
986
1146
  title: 'local',
987
- content: toolFinishContent({
988
- type: 'tool_result',
989
- name: action.tool,
990
- args: action.args,
991
- callId: localCallId,
992
- result,
993
- modelVisibleResult: result,
994
- duration,
995
- success: envelope.success,
996
- error: envelope.error,
997
- summary: envelope.summary,
998
- outputBytes,
999
- artifactRef: formattedLocalResult.artifactRef,
1147
+ content: toolFinishContent(localToolResultEvent),
1148
+ toolActivity: structuredToolFinishActivity(localToolResultEvent, 1, {
1149
+ projectPath: this.runtime.cwd,
1150
+ turnId,
1000
1151
  }),
1001
1152
  });
1002
1153
  this.runtime.store.addMessage({ role: 'assistant', content: assistantContent });
@@ -1083,13 +1234,19 @@ class AgentChatController {
1083
1234
  config: this.runtime.config,
1084
1235
  store: this.runtime.store,
1085
1236
  llm: this.runtime.llm,
1237
+ compactCoordinator: this.runtime.compactCoordinator,
1086
1238
  runtime: this.runtime.runtime,
1087
1239
  sessionId: this.runtime.getSession()?.id,
1088
1240
  turnId,
1089
1241
  ensureSession: this.runtime.ensureSession,
1090
1242
  setSession: session => {
1091
1243
  this.runtime.setSession(session);
1092
- this.events.replaceTranscript(sessionMessagesToTranscriptEntries(session.id));
1244
+ const renderer = this.controllerOptions.uiRenderer
1245
+ ?? this.runtime.config.ui?.renderer
1246
+ ?? 'terminal';
1247
+ this.events.replaceTranscript(sessionMessagesToTranscriptEntries(session.id, {
1248
+ includeToolOutputViews: renderer === 'tui',
1249
+ }));
1093
1250
  },
1094
1251
  sessionRestored: event => {
1095
1252
  this.events.sessionRestored?.(event);
@@ -1147,7 +1304,11 @@ class AgentChatController {
1147
1304
  content: `▸ subtask ${event.role} started: ${event.objective.slice(0, 120)}`,
1148
1305
  });
1149
1306
  }
1150
- else if (event.state === 'completed' || event.state === 'failed' || event.state === 'cancelled' || event.state === 'timed_out' || event.state === 'rejected') {
1307
+ else if (event.state === 'completed' ||
1308
+ event.state === 'failed' ||
1309
+ event.state === 'cancelled' ||
1310
+ event.state === 'timed_out' ||
1311
+ event.state === 'rejected') {
1151
1312
  const summary = event.summary ? ` — ${event.summary.slice(0, 200)}` : '';
1152
1313
  this.events.append({
1153
1314
  role: 'system',
@@ -1181,7 +1342,11 @@ class AgentChatController {
1181
1342
  }
1182
1343
  async runChat(input, options = {}) {
1183
1344
  if (!input) {
1184
- this.events.append({ role: 'error', content: 'Usage: /chat <message>', errorLayer: 'runtime' });
1345
+ this.events.append({
1346
+ role: 'error',
1347
+ content: 'Usage: /chat <message>',
1348
+ errorLayer: 'runtime',
1349
+ });
1185
1350
  return;
1186
1351
  }
1187
1352
  if (!this.runtime.llm || !(0, config_1.isConfigured)(this.runtime.config)) {
@@ -1194,7 +1359,9 @@ class AgentChatController {
1194
1359
  }
1195
1360
  const abortSignal = options.abortSignal;
1196
1361
  const turnId = traceTurnId(options.turnId);
1197
- const activeSession = this.runtime.getSession() ?? this.runtime.ensureSession() ?? (0, session_storage_1.loadSessionMeta)(this.runtime.getSession()?.id ?? '');
1362
+ const activeSession = this.runtime.getSession() ??
1363
+ this.runtime.ensureSession() ??
1364
+ (0, session_storage_1.loadSessionMeta)(this.runtime.getSession()?.id ?? '');
1198
1365
  const sessionId = activeSession?.id;
1199
1366
  const preWorkspace = (0, workspace_state_1.captureWorkspaceSnapshot)(this.runtime.cwd);
1200
1367
  const runtimeTools = (0, tools_1.getRuntimeTools)();
@@ -1237,7 +1404,9 @@ class AgentChatController {
1237
1404
  const intent = harness.updateContractFromUserInput(input);
1238
1405
  harness.recordAppliedSkills(skillResolution.skills);
1239
1406
  // Reconcile diagnostic: when harness state is present but objective may be incomplete
1240
- if (snapshot.harnessState && !snapshot.harnessState.rootObjective && !snapshot.harnessState.contract?.objective) {
1407
+ if (snapshot.harnessState &&
1408
+ !snapshot.harnessState.rootObjective &&
1409
+ !snapshot.harnessState.contract?.objective) {
1241
1410
  this.events.setStatus('Resume diagnostic: harness state restored but objective may be incomplete. Run /harness explain to review.');
1242
1411
  }
1243
1412
  const subagentConfig = this.runtime.config.subagents;
@@ -1245,7 +1414,10 @@ class AgentChatController {
1245
1414
  // turns, but respects an active skill scope: when a skill restricts the
1246
1415
  // tool set, subtask is not appended (the skill owns the scope).
1247
1416
  const projectPath = activeSession?.projectPath;
1248
- const subagentBundle = subagentConfig && subagentConfig.mode !== 'off' && this.runtime.llm && !skillResolution.toolScopeActive
1417
+ const subagentBundle = subagentConfig &&
1418
+ subagentConfig.mode !== 'off' &&
1419
+ this.runtime.llm &&
1420
+ !skillResolution.toolScopeActive
1249
1421
  ? (0, subagents_1.createSubagentBundleForTurn)({
1250
1422
  config: subagentConfig,
1251
1423
  cwd: this.runtime.cwd,
@@ -1253,7 +1425,7 @@ class AgentChatController {
1253
1425
  modelLabel: this.runtime.llm.getModel(),
1254
1426
  rootObjectiveSummary: harness.toJSON()?.rootObjective ?? input,
1255
1427
  abortSignal,
1256
- onSubtaskEvent: (event) => {
1428
+ onSubtaskEvent: event => {
1257
1429
  this.handleSubtaskEvent(event, sessionId, turnId);
1258
1430
  },
1259
1431
  onSubtaskResult: (result, _batchId) => {
@@ -1310,10 +1482,14 @@ class AgentChatController {
1310
1482
  let finalModel = '';
1311
1483
  let pendingCompleteTrace = null;
1312
1484
  let pendingCompleteStats;
1485
+ let pendingCompact;
1313
1486
  const verificationResults = [];
1314
1487
  const sessionMessagesToRecord = [];
1315
1488
  const assistantStream = createAssistantStreamPresenter(this.events, abortSignal);
1316
- const toolEvents = createToolEventPresenter(this.events);
1489
+ const toolEvents = createToolEventPresenter(this.events, {
1490
+ projectPath: this.runtime.cwd,
1491
+ turnId,
1492
+ });
1317
1493
  let checkpointSequence = 0;
1318
1494
  const checkpointIds = [];
1319
1495
  const streamCallbacks = {
@@ -1384,6 +1560,10 @@ class AgentChatController {
1384
1560
  harness,
1385
1561
  input,
1386
1562
  loopBudget,
1563
+ onContextUsage: usage => {
1564
+ this.runtime.store.setContextUsage(usage);
1565
+ },
1566
+ compactCoordinator: this.runtime.compactCoordinator,
1387
1567
  })) {
1388
1568
  switch (event.type) {
1389
1569
  case 'request_start':
@@ -1434,9 +1614,7 @@ class AgentChatController {
1434
1614
  this.events.append({ role: 'status', content: suggestion });
1435
1615
  }
1436
1616
  }
1437
- const checkpointId = checkpointSequence === 0
1438
- ? turnId
1439
- : `${turnId}-checkpoint-${checkpointSequence + 1}`;
1617
+ const checkpointId = checkpointSequence === 0 ? turnId : `${turnId}-checkpoint-${checkpointSequence + 1}`;
1440
1618
  const checkpointResult = createPreToolCheckpoint(this.events, sessionId, turnId, checkpointId, this.runtime.cwd, event.toolCalls);
1441
1619
  if (checkpointResult.created) {
1442
1620
  checkpointIds.push(checkpointId);
@@ -1446,6 +1624,7 @@ class AgentChatController {
1446
1624
  this.events.append({
1447
1625
  role: 'status',
1448
1626
  title: 'checkpoint',
1627
+ statusTone: 'warning',
1449
1628
  content: `Risky edit: ${checkpointResult.targetCount} files in one turn. Checkpoint ${checkpointId} created for rollback (/checkpoints restore ${checkpointId}).`,
1450
1629
  });
1451
1630
  }
@@ -1453,6 +1632,7 @@ class AgentChatController {
1453
1632
  this.events.append({
1454
1633
  role: 'status',
1455
1634
  title: 'checkpoint',
1635
+ statusTone: 'warning',
1456
1636
  content: `Risky edit: ${checkpointResult.targetCount} files in one turn, but checkpoint creation failed. Restore any pre-existing checkpoint manually or revert via git.`,
1457
1637
  });
1458
1638
  }
@@ -1606,6 +1786,7 @@ class AgentChatController {
1606
1786
  finalContent = event.content;
1607
1787
  finalUsage = event.usage;
1608
1788
  finalModel = event.model;
1789
+ pendingCompact = event.compact;
1609
1790
  if (event.stats) {
1610
1791
  pendingCompleteStats = event.stats;
1611
1792
  recordProviderTraceEvents(this.events, sessionId, turnId, event.stats);
@@ -1662,6 +1843,7 @@ class AgentChatController {
1662
1843
  this.events.append({
1663
1844
  role: 'status',
1664
1845
  title: 'recovery',
1846
+ statusTone: 'warning',
1665
1847
  content: recoveryNotice,
1666
1848
  });
1667
1849
  }
@@ -1694,6 +1876,7 @@ class AgentChatController {
1694
1876
  this.events.append({
1695
1877
  role: 'status',
1696
1878
  title: 'verification',
1879
+ statusTone: 'warning',
1697
1880
  content: notice,
1698
1881
  });
1699
1882
  finalContent = finalContent ? `${finalContent}\n\n${notice}` : notice;
@@ -1738,7 +1921,12 @@ class AgentChatController {
1738
1921
  lines.push(`Next: ${stats.continuationHint}`);
1739
1922
  }
1740
1923
  const notice = lines.join('\n');
1741
- this.events.append({ role: 'status', title: 'budget', content: notice });
1924
+ this.events.append({
1925
+ role: 'status',
1926
+ title: 'budget',
1927
+ statusTone: 'warning',
1928
+ content: notice,
1929
+ });
1742
1930
  finalContent = finalContent ? `${finalContent}\n\n${notice}` : notice;
1743
1931
  appendAssistantNotice(sessionMessagesToRecord, notice);
1744
1932
  }
@@ -1751,6 +1939,65 @@ class AgentChatController {
1751
1939
  if (sessionId && sessionMessagesToRecord.length > 0) {
1752
1940
  (0, session_storage_1.appendSessionMessages)(sessionId, sessionMessagesToRecord);
1753
1941
  }
1942
+ if (pendingCompact) {
1943
+ try {
1944
+ let committedCheckpointId;
1945
+ if (sessionId) {
1946
+ const sourceMessageCount = (0, session_storage_1.readSessionMessages)(sessionId).length;
1947
+ const checkpoint = (0, session_storage_1.commitSessionCompactCheckpoint)({
1948
+ sessionId,
1949
+ mode: pendingCompact.mode,
1950
+ modelId: finalModel || this.runtime.llm.getModel(),
1951
+ sourceMessageCount,
1952
+ transcriptStartMessageIndex: Math.max(0, sourceMessageCount - 20),
1953
+ modelHistory: pendingCompact.modelHistory,
1954
+ summary: pendingCompact.summary,
1955
+ beforeUsage: pendingCompact.before,
1956
+ afterUsage: pendingCompact.after,
1957
+ });
1958
+ committedCheckpointId = checkpoint.checkpointId;
1959
+ this.runtime.store.setState({ conversationHistory: checkpoint.modelHistory });
1960
+ }
1961
+ else {
1962
+ this.runtime.store.setState({
1963
+ conversationHistory: pendingCompact.modelHistory.filter(message => message.role !== 'system'),
1964
+ });
1965
+ }
1966
+ this.runtime.store.setContextUsage(pendingCompact.after);
1967
+ if (sessionId) {
1968
+ recordTraceEvent(this.events, sessionId, {
1969
+ turnId,
1970
+ type: 'compact_completed',
1971
+ checkpointId: committedCheckpointId,
1972
+ model: finalModel || this.runtime.llm.getModel(),
1973
+ note: pendingCompact.mode,
1974
+ });
1975
+ }
1976
+ this.events.append({
1977
+ role: 'status',
1978
+ title: 'auto-compact',
1979
+ statusTone: 'neutral',
1980
+ content: `Context reached ${pendingCompact.before.percent}% of the safe input budget. Agent core committed a ${pendingCompact.mode} compact checkpoint; current context is ${pendingCompact.after.percent}%.`,
1981
+ });
1982
+ }
1983
+ catch (error) {
1984
+ if (sessionId) {
1985
+ recordTraceEvent(this.events, sessionId, {
1986
+ turnId,
1987
+ type: 'compact_failed',
1988
+ model: finalModel || this.runtime.llm.getModel(),
1989
+ error: compactTraceError(error),
1990
+ note: pendingCompact.mode,
1991
+ });
1992
+ }
1993
+ this.events.append({
1994
+ role: 'error',
1995
+ title: 'compact-failed',
1996
+ content: `Compact checkpoint failed; the previous model context remains active. ${error instanceof Error ? error.message : String(error)}`,
1997
+ errorLayer: 'runtime',
1998
+ });
1999
+ }
2000
+ }
1754
2001
  if (finalUsage) {
1755
2002
  // Fold subagent token usage into /cost accounting.
1756
2003
  if (subagentBundle) {
@@ -1803,6 +2050,7 @@ class AgentChatController {
1803
2050
  this.events.append({
1804
2051
  role: 'status',
1805
2052
  title: 'recovery',
2053
+ statusTone: 'warning',
1806
2054
  content: recoveryNotice,
1807
2055
  });
1808
2056
  }
@@ -1811,7 +2059,11 @@ class AgentChatController {
1811
2059
  return;
1812
2060
  }
1813
2061
  assistantStream.discardSegment();
1814
- this.events.append({ role: 'error', content: formatChatError(error), errorLayer: errorLayerForChatError(error) });
2062
+ this.events.append({
2063
+ role: 'error',
2064
+ content: formatChatError(error),
2065
+ errorLayer: errorLayerForChatError(error),
2066
+ });
1815
2067
  this.events.setStatus('Turn failed. Ready for the next input.');
1816
2068
  const failedStats = error instanceof framework_1.QueryLoopError
1817
2069
  ? error.stats
@@ -1837,6 +2089,7 @@ class AgentChatController {
1837
2089
  this.events.append({
1838
2090
  role: 'status',
1839
2091
  title: 'recovery',
2092
+ statusTone: 'warning',
1840
2093
  content: recoveryNotice,
1841
2094
  });
1842
2095
  }
@@ -1879,7 +2132,9 @@ exports.InkChatController = AgentChatController;
1879
2132
  function loadSessionIntoRuntime(runtime, sessionId) {
1880
2133
  const history = (0, session_storage_1.loadSessionHistory)(sessionId);
1881
2134
  runtime.store.setState({ conversationHistory: history });
1882
- runtime.store.setState({ harnessState: (0, session_storage_1.loadSessionHarnessState)(sessionId) ?? (0, session_storage_1.loadSessionMeta)(sessionId)?.harnessState });
2135
+ runtime.store.setState({
2136
+ harnessState: (0, session_storage_1.loadSessionHarnessState)(sessionId) ?? (0, session_storage_1.loadSessionMeta)(sessionId)?.harnessState,
2137
+ });
1883
2138
  return `Restored ${history.length} messages`;
1884
2139
  }
1885
2140
  function closeSession(runtime) {