openhorse 0.2.21 → 0.2.22

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 (205) 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/ledger.d.ts.map +1 -1
  28. package/dist/harness/ledger.js +6 -2
  29. package/dist/harness/ledger.js.map +1 -1
  30. package/dist/index.d.ts +2 -1
  31. package/dist/index.d.ts.map +1 -1
  32. package/dist/ink-ui/components/StatusLine.d.ts.map +1 -1
  33. package/dist/ink-ui/components/StatusLine.js +0 -2
  34. package/dist/ink-ui/components/StatusLine.js.map +1 -1
  35. package/dist/ink-ui/screens/ReplScreen.d.ts.map +1 -1
  36. package/dist/ink-ui/screens/ReplScreen.js +12 -5
  37. package/dist/ink-ui/screens/ReplScreen.js.map +1 -1
  38. package/dist/runtime/agent-runtime-controller.d.ts.map +1 -1
  39. package/dist/runtime/agent-runtime-controller.js +11 -1
  40. package/dist/runtime/agent-runtime-controller.js.map +1 -1
  41. package/dist/runtime/chat-controller.d.ts.map +1 -1
  42. package/dist/runtime/chat-controller.js +229 -86
  43. package/dist/runtime/chat-controller.js.map +1 -1
  44. package/dist/runtime/rich-text/ansi-parser.d.ts +1 -1
  45. package/dist/runtime/rich-text/ansi-parser.d.ts.map +1 -1
  46. package/dist/runtime/rich-text/ansi-parser.js +93 -55
  47. package/dist/runtime/rich-text/ansi-parser.js.map +1 -1
  48. package/dist/runtime/rich-text/layout.d.ts +2 -11
  49. package/dist/runtime/rich-text/layout.d.ts.map +1 -1
  50. package/dist/runtime/rich-text/layout.js +280 -245
  51. package/dist/runtime/rich-text/layout.js.map +1 -1
  52. package/dist/runtime/rich-text/markdown-parser.js +31 -24
  53. package/dist/runtime/rich-text/markdown-parser.js.map +1 -1
  54. package/dist/runtime/rich-text/types.d.ts +1 -1
  55. package/dist/runtime/rich-text/types.d.ts.map +1 -1
  56. package/dist/runtime/subagents/production.d.ts.map +1 -1
  57. package/dist/runtime/subagents/production.js +28 -3
  58. package/dist/runtime/subagents/production.js.map +1 -1
  59. package/dist/runtime/subagents/result-parser.d.ts.map +1 -1
  60. package/dist/runtime/subagents/result-parser.js +54 -40
  61. package/dist/runtime/subagents/result-parser.js.map +1 -1
  62. package/dist/runtime/subagents/types.d.ts +2 -0
  63. package/dist/runtime/subagents/types.d.ts.map +1 -1
  64. package/dist/runtime/subagents/types.js +5 -1
  65. package/dist/runtime/subagents/types.js.map +1 -1
  66. package/dist/runtime/ui-events.d.ts +13 -0
  67. package/dist/runtime/ui-events.d.ts.map +1 -1
  68. package/dist/runtime/ui-events.js.map +1 -1
  69. package/dist/runtime/ui-view-model.d.ts +9 -0
  70. package/dist/runtime/ui-view-model.d.ts.map +1 -1
  71. package/dist/runtime/ui-view-model.js +59 -37
  72. package/dist/runtime/ui-view-model.js.map +1 -1
  73. package/dist/services/compact/auto-compact.d.ts +15 -2
  74. package/dist/services/compact/auto-compact.d.ts.map +1 -1
  75. package/dist/services/compact/auto-compact.js +56 -24
  76. package/dist/services/compact/auto-compact.js.map +1 -1
  77. package/dist/services/compact/compact.d.ts +2 -0
  78. package/dist/services/compact/compact.d.ts.map +1 -1
  79. package/dist/services/compact/compact.js +22 -5
  80. package/dist/services/compact/compact.js.map +1 -1
  81. package/dist/services/compact/coordinator.d.ts +26 -0
  82. package/dist/services/compact/coordinator.d.ts.map +1 -0
  83. package/dist/services/compact/coordinator.js +44 -0
  84. package/dist/services/compact/coordinator.js.map +1 -0
  85. package/dist/services/compact/index.d.ts +1 -0
  86. package/dist/services/compact/index.d.ts.map +1 -1
  87. package/dist/services/compact/index.js +3 -1
  88. package/dist/services/compact/index.js.map +1 -1
  89. package/dist/services/compact/summary-generator.d.ts +6 -2
  90. package/dist/services/compact/summary-generator.d.ts.map +1 -1
  91. package/dist/services/compact/summary-generator.js +73 -31
  92. package/dist/services/compact/summary-generator.js.map +1 -1
  93. package/dist/services/config-dir.d.ts +11 -1
  94. package/dist/services/config-dir.d.ts.map +1 -1
  95. package/dist/services/config-dir.js +19 -2
  96. package/dist/services/config-dir.js.map +1 -1
  97. package/dist/services/config.d.ts +4 -2
  98. package/dist/services/config.d.ts.map +1 -1
  99. package/dist/services/config.js +40 -0
  100. package/dist/services/config.js.map +1 -1
  101. package/dist/services/doctor.d.ts +2 -0
  102. package/dist/services/doctor.d.ts.map +1 -1
  103. package/dist/services/doctor.js +8 -1
  104. package/dist/services/doctor.js.map +1 -1
  105. package/dist/services/global-config.d.ts +10 -0
  106. package/dist/services/global-config.d.ts.map +1 -1
  107. package/dist/services/global-config.js.map +1 -1
  108. package/dist/services/llm.d.ts +21 -4
  109. package/dist/services/llm.d.ts.map +1 -1
  110. package/dist/services/llm.js +66 -8
  111. package/dist/services/llm.js.map +1 -1
  112. package/dist/services/model-context.d.ts +36 -0
  113. package/dist/services/model-context.d.ts.map +1 -1
  114. package/dist/services/model-context.js +238 -32
  115. package/dist/services/model-context.js.map +1 -1
  116. package/dist/services/redaction.js +1 -1
  117. package/dist/services/redaction.js.map +1 -1
  118. package/dist/services/session-storage.d.ts +39 -1
  119. package/dist/services/session-storage.d.ts.map +1 -1
  120. package/dist/services/session-storage.js +121 -27
  121. package/dist/services/session-storage.js.map +1 -1
  122. package/dist/services/usage-state.d.ts +55 -5
  123. package/dist/services/usage-state.d.ts.map +1 -1
  124. package/dist/services/usage-state.js +185 -59
  125. package/dist/services/usage-state.js.map +1 -1
  126. package/dist/services/verification-profile.d.ts.map +1 -1
  127. package/dist/services/verification-profile.js +3 -2
  128. package/dist/services/verification-profile.js.map +1 -1
  129. package/dist/services/workspace-diff.d.ts.map +1 -1
  130. package/dist/services/workspace-diff.js +34 -3
  131. package/dist/services/workspace-diff.js.map +1 -1
  132. package/dist/services/yaml-config.js +1 -1
  133. package/dist/skills/runtime.d.ts +9 -5
  134. package/dist/skills/runtime.d.ts.map +1 -1
  135. package/dist/skills/runtime.js +157 -12
  136. package/dist/skills/runtime.js.map +1 -1
  137. package/dist/terminal-ui/launch.d.ts +2 -0
  138. package/dist/terminal-ui/launch.d.ts.map +1 -1
  139. package/dist/terminal-ui/launch.js +65 -21
  140. package/dist/terminal-ui/launch.js.map +1 -1
  141. package/dist/terminal-ui/raw-editor.d.ts +9 -0
  142. package/dist/terminal-ui/raw-editor.d.ts.map +1 -1
  143. package/dist/terminal-ui/raw-editor.js +207 -30
  144. package/dist/terminal-ui/raw-editor.js.map +1 -1
  145. package/dist/tools/bash_security.d.ts.map +1 -1
  146. package/dist/tools/bash_security.js +14 -5
  147. package/dist/tools/bash_security.js.map +1 -1
  148. package/dist/tools/index.d.ts.map +1 -1
  149. package/dist/tools/index.js +48 -20
  150. package/dist/tools/index.js.map +1 -1
  151. package/dist/tools/plan.d.ts.map +1 -1
  152. package/dist/tools/plan.js +7 -0
  153. package/dist/tools/plan.js.map +1 -1
  154. package/dist/tools/web.d.ts +10 -0
  155. package/dist/tools/web.d.ts.map +1 -1
  156. package/dist/tools/web.js +81 -1
  157. package/dist/tools/web.js.map +1 -1
  158. package/dist/tui-core/input-parser.d.ts +1 -1
  159. package/dist/tui-core/input-parser.d.ts.map +1 -1
  160. package/dist/tui-core/input-parser.js +20 -1
  161. package/dist/tui-core/input-parser.js.map +1 -1
  162. package/dist/tui-core/style.d.ts +16 -0
  163. package/dist/tui-core/style.d.ts.map +1 -1
  164. package/dist/tui-core/style.js +22 -1
  165. package/dist/tui-core/style.js.map +1 -1
  166. package/dist/tui-ui/inline-surface.d.ts +62 -11
  167. package/dist/tui-ui/inline-surface.d.ts.map +1 -1
  168. package/dist/tui-ui/inline-surface.js +343 -92
  169. package/dist/tui-ui/inline-surface.js.map +1 -1
  170. package/dist/tui-ui/launch.d.ts +1 -0
  171. package/dist/tui-ui/launch.d.ts.map +1 -1
  172. package/dist/tui-ui/launch.js +185 -41
  173. package/dist/tui-ui/launch.js.map +1 -1
  174. package/dist/tui-ui/layout.d.ts +51 -2
  175. package/dist/tui-ui/layout.d.ts.map +1 -1
  176. package/dist/tui-ui/layout.js +226 -76
  177. package/dist/tui-ui/layout.js.map +1 -1
  178. package/dist/tui-ui/runner.d.ts +69 -8
  179. package/dist/tui-ui/runner.d.ts.map +1 -1
  180. package/dist/tui-ui/runner.js +304 -41
  181. package/dist/tui-ui/runner.js.map +1 -1
  182. package/dist/tui-ui/state.d.ts +5 -4
  183. package/dist/tui-ui/state.d.ts.map +1 -1
  184. package/dist/tui-ui/state.js +65 -19
  185. package/dist/tui-ui/state.js.map +1 -1
  186. package/dist/tui-ui/theme.d.ts +13 -0
  187. package/dist/tui-ui/theme.d.ts.map +1 -0
  188. package/dist/tui-ui/theme.js +20 -0
  189. package/dist/tui-ui/theme.js.map +1 -0
  190. package/dist/tui-ui/transcript-cache.d.ts +5 -3
  191. package/dist/tui-ui/transcript-cache.d.ts.map +1 -1
  192. package/dist/tui-ui/transcript-cache.js +16 -9
  193. package/dist/tui-ui/transcript-cache.js.map +1 -1
  194. package/dist/tui-ui/transcript-layout.d.ts +21 -0
  195. package/dist/tui-ui/transcript-layout.d.ts.map +1 -0
  196. package/dist/tui-ui/transcript-layout.js +398 -0
  197. package/dist/tui-ui/transcript-layout.js.map +1 -0
  198. package/dist/ui/status-bar.d.ts +1 -1
  199. package/dist/ui/status-bar.d.ts.map +1 -1
  200. package/dist/ui/status-bar.js +2 -15
  201. package/dist/ui/status-bar.js.map +1 -1
  202. package/dist/ui-v2/components/shell.d.ts.map +1 -1
  203. package/dist/ui-v2/components/shell.js +0 -8
  204. package/dist/ui-v2/components/shell.js.map +1 -1
  205. package/package.json +1 -1
@@ -121,7 +121,18 @@ function compactMiddle(text, maxLength) {
121
121
  return `${compact.slice(0, headLength)}...${compact.slice(-tailLength)}`;
122
122
  }
123
123
  function compactToolArgs(args, maxLength = 160) {
124
- for (const key of ['path', 'file_path', 'file', 'cwd', 'command', 'pattern', 'query', 'url', 'target', 'sessionId']) {
124
+ for (const key of [
125
+ 'path',
126
+ 'file_path',
127
+ 'file',
128
+ 'cwd',
129
+ 'command',
130
+ 'pattern',
131
+ 'query',
132
+ 'url',
133
+ 'target',
134
+ 'sessionId',
135
+ ]) {
125
136
  const value = args[key];
126
137
  if (typeof value === 'string') {
127
138
  return compactMiddle(value, maxLength);
@@ -148,16 +159,14 @@ function buildTraceArgsDetails(projectPath, name, args) {
148
159
  const argsSummary = compactToolArgs(args);
149
160
  const fullArgs = (0, session_storage_1.redactTraceText)(fullToolArgsForTrace(name, args)).trim();
150
161
  const argsBytes = byteLength(fullArgs);
151
- if (!projectPath
152
- || !fullArgs
153
- || fullArgs === (0, session_storage_1.redactTraceText)(argsSummary)
154
- || argsBytes <= TRACE_ARGS_ARTIFACT_THRESHOLD_BYTES) {
162
+ if (!projectPath ||
163
+ !fullArgs ||
164
+ fullArgs === (0, session_storage_1.redactTraceText)(argsSummary) ||
165
+ argsBytes <= TRACE_ARGS_ARTIFACT_THRESHOLD_BYTES) {
155
166
  return { argsSummary };
156
167
  }
157
168
  const artifact = (0, tool_artifacts_1.storeArtifact)(projectPath, `${name}-args`, fullArgs, argsBytes);
158
- return artifact
159
- ? { argsSummary, argsArtifactId: artifact.id, argsBytes }
160
- : { argsSummary };
169
+ return artifact ? { argsSummary, argsArtifactId: artifact.id, argsBytes } : { argsSummary };
161
170
  }
162
171
  function parseToolCallArgsForRuntime(toolCall) {
163
172
  try {
@@ -211,7 +220,9 @@ function createPreToolCheckpoint(events, sessionId, turnId, checkpointId, cwd, t
211
220
  checkpointFiles: checkpoint?.files.map(file => file.path) ?? [],
212
221
  workspaceFiles: relativeTargets,
213
222
  note: checkpoint
214
- ? (risky ? 'risky_multi_file_checkpoint' : 'pre_edit_checkpoint')
223
+ ? risky
224
+ ? 'risky_multi_file_checkpoint'
225
+ : 'pre_edit_checkpoint'
215
226
  : 'pre_edit_checkpoint_skipped',
216
227
  });
217
228
  return { created: true, targetCount: targets.length, risky };
@@ -239,7 +250,9 @@ function formatWorkspaceFileForTrace(file) {
239
250
  const metadata = [
240
251
  typeof file.sizeBytes === 'number' ? `${file.sizeBytes}B` : '',
241
252
  typeof file.mtimeMs === 'number' ? `mtime=${file.mtimeMs}` : '',
242
- ].filter(Boolean).join(' ');
253
+ ]
254
+ .filter(Boolean)
255
+ .join(' ');
243
256
  return `${file.status} ${file.path}${metadata ? ` (${metadata})` : ''}`;
244
257
  }
245
258
  function appendWorkspaceSnapshotTrace(events, sessionId, turnId, phase, snapshot) {
@@ -275,19 +288,13 @@ function appendWorkspaceDeltaTrace(events, sessionId, turnId, before, after) {
275
288
  return delta;
276
289
  }
277
290
  function workspaceDeltaHasTurnChanges(delta) {
278
- return delta.newFilesByTurn.length > 0
279
- || delta.changedByTurn.length > 0
280
- || delta.resolvedByTurn.length > 0;
291
+ return (delta.newFilesByTurn.length > 0 ||
292
+ delta.changedByTurn.length > 0 ||
293
+ delta.resolvedByTurn.length > 0);
281
294
  }
282
295
  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';
296
+ const files = compactPathList([...delta.newFilesByTurn, ...delta.changedByTurn, ...delta.resolvedByTurn], 8);
297
+ const fileText = files.length > 0 ? files.join(', ') : 'workspace changes recorded';
291
298
  const checkpointText = checkpointIds.length > 0
292
299
  ? ` Checkpoints: ${checkpointIds.join(', ')}. Preview rollback with /checkpoint restore <id>; restore each listed checkpoint if multiple.`
293
300
  : '';
@@ -348,15 +355,17 @@ function withVerificationLoopStats(stats, summary) {
348
355
  verificationPassedCommands: compactVerificationCommands(summary.passedCommands),
349
356
  verificationFailedCommands: compactVerificationCommands(summary.failedCommands),
350
357
  verificationMissingCommands: compactVerificationCommands(summary.missingCommands),
351
- verificationSkippedReason: summary.skippedReason ? (0, session_storage_1.redactTraceText)(summary.skippedReason) : undefined,
358
+ verificationSkippedReason: summary.skippedReason
359
+ ? (0, session_storage_1.redactTraceText)(summary.skippedReason)
360
+ : undefined,
352
361
  };
353
362
  }
354
363
  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;
364
+ return (profile.changedFiles.length > 0 ||
365
+ summary.commandsRun.length > 0 ||
366
+ summary.passedCommands.length > 0 ||
367
+ summary.failedCommands.length > 0 ||
368
+ summary.missingCommands.length > 0);
360
369
  }
361
370
  function appendPostWorkspaceTrace(events, sessionId, turnId, cwd, before, verificationResults = []) {
362
371
  const postWorkspace = (0, workspace_state_1.captureWorkspaceSnapshot)(cwd);
@@ -451,11 +460,43 @@ function toolStartContent(event) {
451
460
  function toolFinishContent(event) {
452
461
  return (0, ui_view_model_1.formatToolActivityTranscript)((0, ui_view_model_1.toolActivityFromFinished)(event, compactToolArgs(event.args, TOOL_TRANSCRIPT_ARG_BUDGET)));
453
462
  }
463
+ function structuredToolStartActivity(event, seq) {
464
+ const command = event.name === 'exec_command' && typeof event.args.command === 'string'
465
+ ? event.args.command
466
+ : undefined;
467
+ return {
468
+ state: 'running',
469
+ name: event.name,
470
+ detail: command ? '' : compactToolArgs(event.args, TOOL_TRANSCRIPT_ARG_BUDGET),
471
+ command,
472
+ body: '',
473
+ seq,
474
+ };
475
+ }
476
+ function structuredToolFinishActivity(event, seq) {
477
+ const modelVisible = (0, tool_serializer_1.parseToolResultEnvelope)(event.modelVisibleResult);
478
+ const command = event.name === 'exec_command' && typeof event.args.command === 'string'
479
+ ? event.args.command
480
+ : undefined;
481
+ return {
482
+ state: event.success ? 'success' : 'error',
483
+ name: event.name,
484
+ detail: command ? '' : compactToolArgs(event.args, TOOL_TRANSCRIPT_ARG_BUDGET),
485
+ command,
486
+ duration: `${event.duration}ms`,
487
+ summary: event.summary?.split(/\r?\n/u, 1)[0],
488
+ outputBytes: event.outputBytes,
489
+ body: modelVisible.output,
490
+ error: event.error,
491
+ seq,
492
+ artifactHint: event.artifactRef ? `/artifacts show ${event.artifactRef.id} --full` : undefined,
493
+ };
494
+ }
454
495
  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.');
496
+ return (content.startsWith('[OpenHorse Context State v2]') ||
497
+ content.startsWith('[Context Summary]') ||
498
+ content.startsWith('I will continue from this OpenHorse Context State') ||
499
+ content.startsWith('I understand the context. I will continue the conversation with this background information.'));
459
500
  }
460
501
  function sessionToolCallSummaries(message) {
461
502
  return (message.tool_calls ?? []).map(call => {
@@ -506,13 +547,12 @@ function sessionToolResultSummary(message, toolCallsById) {
506
547
  const args = parseToolCallArgs(call.function.arguments);
507
548
  const detail = compactToolArgs(args);
508
549
  const parsed = parseSessionToolResult(message.content);
509
- const firstLine = parsed.summary || `${parsed.success ? '✓' : '✗'} ${call.function.name}${detail ? ` ${detail}` : ''}`;
550
+ const firstLine = parsed.summary ||
551
+ `${parsed.success ? '✓' : '✗'} ${call.function.name}${detail ? ` ${detail}` : ''}`;
510
552
  return parsed.error ? `${firstLine}\nError: ${parsed.error}` : firstLine;
511
553
  }
512
554
  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);
555
+ const messages = (0, session_storage_1.loadSessionTranscriptMessages)(sessionId);
516
556
  const entries = [];
517
557
  const toolCallsById = new Map();
518
558
  const completedToolCallIds = new Set();
@@ -538,7 +578,11 @@ function sessionMessagesToTranscriptEntries(sessionId) {
538
578
  }
539
579
  for (const summary of sessionToolCallSummaries(message)) {
540
580
  if (!completedToolCallIds.has(summary.id)) {
541
- entries.push({ id: `${idBase}-tool-call-${entries.length}`, role: 'tool', content: summary.content });
581
+ entries.push({
582
+ id: `${idBase}-tool-call-${entries.length}`,
583
+ role: 'tool',
584
+ content: summary.content,
585
+ });
542
586
  }
543
587
  }
544
588
  return;
@@ -548,7 +592,10 @@ function sessionMessagesToTranscriptEntries(sessionId) {
548
592
  entries.push({
549
593
  id: `${idBase}-tool`,
550
594
  role: 'tool',
551
- content: summary ?? (message.toolCallId ? `Tool result ${message.toolCallId}\n${message.content}` : message.content),
595
+ content: summary ??
596
+ (message.toolCallId
597
+ ? `Tool result ${message.toolCallId}\n${message.content}`
598
+ : message.content),
552
599
  });
553
600
  return;
554
601
  }
@@ -633,19 +680,27 @@ function parseLocalFastPath(input) {
633
680
  }
634
681
  const readMatch = /^(?:read|读取)\s+(.+)$/i.exec(text);
635
682
  const readTarget = readMatch?.[1]?.trim();
636
- const looksLikePath = Boolean(readTarget)
637
- && !/\s/.test(readTarget)
638
- && (/[/\\.]/.test(readTarget) || readTarget.startsWith('~'));
683
+ const looksLikePath = Boolean(readTarget) &&
684
+ !/\s/.test(readTarget) &&
685
+ (/[/\\.]/.test(readTarget) || readTarget.startsWith('~'));
639
686
  if (readTarget && looksLikePath) {
640
687
  return { tool: 'read_file', args: { path: readTarget }, label: `read ${readTarget}` };
641
688
  }
642
689
  const grepMatch = /^(?:grep|搜索)\s+(.+)$/i.exec(text);
643
690
  if (grepMatch?.[1]?.trim()) {
644
- return { tool: 'grep', args: { pattern: grepMatch[1].trim() }, label: `grep ${grepMatch[1].trim()}` };
691
+ return {
692
+ tool: 'grep',
693
+ args: { pattern: grepMatch[1].trim() },
694
+ label: `grep ${grepMatch[1].trim()}`,
695
+ };
645
696
  }
646
697
  const runTestMatch = /^(?:run\s+test|运行测试)\s*[::]\s*(.+)$/i.exec(text);
647
698
  if (runTestMatch?.[1]?.trim()) {
648
- return { tool: 'exec_command', args: { command: runTestMatch[1].trim() }, label: `run test: ${runTestMatch[1].trim()}` };
699
+ return {
700
+ tool: 'exec_command',
701
+ args: { command: runTestMatch[1].trim() },
702
+ label: `run test: ${runTestMatch[1].trim()}`,
703
+ };
649
704
  }
650
705
  return null;
651
706
  }
@@ -693,12 +748,7 @@ function createToolEventPresenter(events) {
693
748
  role: 'tool',
694
749
  title: 'tool',
695
750
  content: toolStartContent(event),
696
- toolActivity: {
697
- state: 'running',
698
- name: event.name,
699
- detail: '',
700
- seq,
701
- },
751
+ toolActivity: structuredToolStartActivity(event, seq),
702
752
  });
703
753
  runningToolEntries.set(event.callId, {
704
754
  entryId,
@@ -721,15 +771,7 @@ function createToolEventPresenter(events) {
721
771
  const content = toolFinishContent(event);
722
772
  const stored = runningToolEntries.get(event.callId);
723
773
  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
- };
774
+ const toolActivity = structuredToolFinishActivity(event, seq);
733
775
  if (stored) {
734
776
  events.finalize(stored.entryId, {
735
777
  role: event.success ? 'tool' : 'error',
@@ -773,6 +815,7 @@ function createToolEventPresenter(events) {
773
815
  state: 'skipped',
774
816
  name: entry.name,
775
817
  detail: '',
818
+ body: '',
776
819
  error: reason,
777
820
  seq: entry.sequence,
778
821
  },
@@ -800,7 +843,7 @@ async function captureConsoleOutput(fn) {
800
843
  const originalError = console.error;
801
844
  const originalWarn = console.warn;
802
845
  const capture = (...args) => {
803
- lines.push(stripAnsi(args.map(arg => typeof arg === 'string' ? arg : JSON.stringify(arg)).join(' ')));
846
+ lines.push(stripAnsi(args.map(arg => (typeof arg === 'string' ? arg : JSON.stringify(arg))).join(' ')));
804
847
  };
805
848
  console.log = capture;
806
849
  console.error = capture;
@@ -850,7 +893,7 @@ class AgentChatController {
850
893
  }
851
894
  const command = (0, commands_1.findCommand)(parsed.name);
852
895
  if (!command) {
853
- if ((0, skills_1.hasMatchingSkill)(text)) {
896
+ if ((0, skills_1.hasMatchingSkill)(text, this.runtime.cwd)) {
854
897
  await this.runChat(text, options);
855
898
  return;
856
899
  }
@@ -902,7 +945,9 @@ class AgentChatController {
902
945
  }
903
946
  }
904
947
  async runLocalFastPath(input, action, options = {}) {
905
- const activeSession = this.runtime.getSession() ?? this.runtime.ensureSession() ?? (0, session_storage_1.loadSessionMeta)(this.runtime.getSession()?.id ?? '');
948
+ const activeSession = this.runtime.getSession() ??
949
+ this.runtime.ensureSession() ??
950
+ (0, session_storage_1.loadSessionMeta)(this.runtime.getSession()?.id ?? '');
906
951
  const sessionId = activeSession?.id;
907
952
  const turnId = traceTurnId(options.turnId);
908
953
  const localCallId = `local-${turnId}`;
@@ -981,23 +1026,25 @@ class AgentChatController {
981
1026
  modelVisibleToolBytes: 0,
982
1027
  summarizedBytes: outputBytes,
983
1028
  });
1029
+ const localToolResultEvent = {
1030
+ type: 'tool_result',
1031
+ name: action.tool,
1032
+ args: action.args,
1033
+ callId: localCallId,
1034
+ result,
1035
+ modelVisibleResult: result,
1036
+ duration,
1037
+ success: envelope.success,
1038
+ error: envelope.error,
1039
+ summary: envelope.summary,
1040
+ outputBytes,
1041
+ artifactRef: formattedLocalResult.artifactRef,
1042
+ };
984
1043
  this.events.append({
985
1044
  role: envelope.success ? 'tool' : 'error',
986
1045
  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,
1000
- }),
1046
+ content: toolFinishContent(localToolResultEvent),
1047
+ toolActivity: structuredToolFinishActivity(localToolResultEvent, 1),
1001
1048
  });
1002
1049
  this.runtime.store.addMessage({ role: 'assistant', content: assistantContent });
1003
1050
  this.setLoopStats(stats);
@@ -1083,6 +1130,7 @@ class AgentChatController {
1083
1130
  config: this.runtime.config,
1084
1131
  store: this.runtime.store,
1085
1132
  llm: this.runtime.llm,
1133
+ compactCoordinator: this.runtime.compactCoordinator,
1086
1134
  runtime: this.runtime.runtime,
1087
1135
  sessionId: this.runtime.getSession()?.id,
1088
1136
  turnId,
@@ -1147,7 +1195,11 @@ class AgentChatController {
1147
1195
  content: `▸ subtask ${event.role} started: ${event.objective.slice(0, 120)}`,
1148
1196
  });
1149
1197
  }
1150
- else if (event.state === 'completed' || event.state === 'failed' || event.state === 'cancelled' || event.state === 'timed_out' || event.state === 'rejected') {
1198
+ else if (event.state === 'completed' ||
1199
+ event.state === 'failed' ||
1200
+ event.state === 'cancelled' ||
1201
+ event.state === 'timed_out' ||
1202
+ event.state === 'rejected') {
1151
1203
  const summary = event.summary ? ` — ${event.summary.slice(0, 200)}` : '';
1152
1204
  this.events.append({
1153
1205
  role: 'system',
@@ -1181,7 +1233,11 @@ class AgentChatController {
1181
1233
  }
1182
1234
  async runChat(input, options = {}) {
1183
1235
  if (!input) {
1184
- this.events.append({ role: 'error', content: 'Usage: /chat <message>', errorLayer: 'runtime' });
1236
+ this.events.append({
1237
+ role: 'error',
1238
+ content: 'Usage: /chat <message>',
1239
+ errorLayer: 'runtime',
1240
+ });
1185
1241
  return;
1186
1242
  }
1187
1243
  if (!this.runtime.llm || !(0, config_1.isConfigured)(this.runtime.config)) {
@@ -1194,7 +1250,9 @@ class AgentChatController {
1194
1250
  }
1195
1251
  const abortSignal = options.abortSignal;
1196
1252
  const turnId = traceTurnId(options.turnId);
1197
- const activeSession = this.runtime.getSession() ?? this.runtime.ensureSession() ?? (0, session_storage_1.loadSessionMeta)(this.runtime.getSession()?.id ?? '');
1253
+ const activeSession = this.runtime.getSession() ??
1254
+ this.runtime.ensureSession() ??
1255
+ (0, session_storage_1.loadSessionMeta)(this.runtime.getSession()?.id ?? '');
1198
1256
  const sessionId = activeSession?.id;
1199
1257
  const preWorkspace = (0, workspace_state_1.captureWorkspaceSnapshot)(this.runtime.cwd);
1200
1258
  const runtimeTools = (0, tools_1.getRuntimeTools)();
@@ -1237,7 +1295,9 @@ class AgentChatController {
1237
1295
  const intent = harness.updateContractFromUserInput(input);
1238
1296
  harness.recordAppliedSkills(skillResolution.skills);
1239
1297
  // Reconcile diagnostic: when harness state is present but objective may be incomplete
1240
- if (snapshot.harnessState && !snapshot.harnessState.rootObjective && !snapshot.harnessState.contract?.objective) {
1298
+ if (snapshot.harnessState &&
1299
+ !snapshot.harnessState.rootObjective &&
1300
+ !snapshot.harnessState.contract?.objective) {
1241
1301
  this.events.setStatus('Resume diagnostic: harness state restored but objective may be incomplete. Run /harness explain to review.');
1242
1302
  }
1243
1303
  const subagentConfig = this.runtime.config.subagents;
@@ -1245,7 +1305,10 @@ class AgentChatController {
1245
1305
  // turns, but respects an active skill scope: when a skill restricts the
1246
1306
  // tool set, subtask is not appended (the skill owns the scope).
1247
1307
  const projectPath = activeSession?.projectPath;
1248
- const subagentBundle = subagentConfig && subagentConfig.mode !== 'off' && this.runtime.llm && !skillResolution.toolScopeActive
1308
+ const subagentBundle = subagentConfig &&
1309
+ subagentConfig.mode !== 'off' &&
1310
+ this.runtime.llm &&
1311
+ !skillResolution.toolScopeActive
1249
1312
  ? (0, subagents_1.createSubagentBundleForTurn)({
1250
1313
  config: subagentConfig,
1251
1314
  cwd: this.runtime.cwd,
@@ -1253,7 +1316,7 @@ class AgentChatController {
1253
1316
  modelLabel: this.runtime.llm.getModel(),
1254
1317
  rootObjectiveSummary: harness.toJSON()?.rootObjective ?? input,
1255
1318
  abortSignal,
1256
- onSubtaskEvent: (event) => {
1319
+ onSubtaskEvent: event => {
1257
1320
  this.handleSubtaskEvent(event, sessionId, turnId);
1258
1321
  },
1259
1322
  onSubtaskResult: (result, _batchId) => {
@@ -1310,6 +1373,7 @@ class AgentChatController {
1310
1373
  let finalModel = '';
1311
1374
  let pendingCompleteTrace = null;
1312
1375
  let pendingCompleteStats;
1376
+ let pendingCompact;
1313
1377
  const verificationResults = [];
1314
1378
  const sessionMessagesToRecord = [];
1315
1379
  const assistantStream = createAssistantStreamPresenter(this.events, abortSignal);
@@ -1384,6 +1448,10 @@ class AgentChatController {
1384
1448
  harness,
1385
1449
  input,
1386
1450
  loopBudget,
1451
+ onContextUsage: usage => {
1452
+ this.runtime.store.setContextUsage(usage);
1453
+ },
1454
+ compactCoordinator: this.runtime.compactCoordinator,
1387
1455
  })) {
1388
1456
  switch (event.type) {
1389
1457
  case 'request_start':
@@ -1434,9 +1502,7 @@ class AgentChatController {
1434
1502
  this.events.append({ role: 'status', content: suggestion });
1435
1503
  }
1436
1504
  }
1437
- const checkpointId = checkpointSequence === 0
1438
- ? turnId
1439
- : `${turnId}-checkpoint-${checkpointSequence + 1}`;
1505
+ const checkpointId = checkpointSequence === 0 ? turnId : `${turnId}-checkpoint-${checkpointSequence + 1}`;
1440
1506
  const checkpointResult = createPreToolCheckpoint(this.events, sessionId, turnId, checkpointId, this.runtime.cwd, event.toolCalls);
1441
1507
  if (checkpointResult.created) {
1442
1508
  checkpointIds.push(checkpointId);
@@ -1446,6 +1512,7 @@ class AgentChatController {
1446
1512
  this.events.append({
1447
1513
  role: 'status',
1448
1514
  title: 'checkpoint',
1515
+ statusTone: 'warning',
1449
1516
  content: `Risky edit: ${checkpointResult.targetCount} files in one turn. Checkpoint ${checkpointId} created for rollback (/checkpoints restore ${checkpointId}).`,
1450
1517
  });
1451
1518
  }
@@ -1453,6 +1520,7 @@ class AgentChatController {
1453
1520
  this.events.append({
1454
1521
  role: 'status',
1455
1522
  title: 'checkpoint',
1523
+ statusTone: 'warning',
1456
1524
  content: `Risky edit: ${checkpointResult.targetCount} files in one turn, but checkpoint creation failed. Restore any pre-existing checkpoint manually or revert via git.`,
1457
1525
  });
1458
1526
  }
@@ -1606,6 +1674,7 @@ class AgentChatController {
1606
1674
  finalContent = event.content;
1607
1675
  finalUsage = event.usage;
1608
1676
  finalModel = event.model;
1677
+ pendingCompact = event.compact;
1609
1678
  if (event.stats) {
1610
1679
  pendingCompleteStats = event.stats;
1611
1680
  recordProviderTraceEvents(this.events, sessionId, turnId, event.stats);
@@ -1662,6 +1731,7 @@ class AgentChatController {
1662
1731
  this.events.append({
1663
1732
  role: 'status',
1664
1733
  title: 'recovery',
1734
+ statusTone: 'warning',
1665
1735
  content: recoveryNotice,
1666
1736
  });
1667
1737
  }
@@ -1694,6 +1764,7 @@ class AgentChatController {
1694
1764
  this.events.append({
1695
1765
  role: 'status',
1696
1766
  title: 'verification',
1767
+ statusTone: 'warning',
1697
1768
  content: notice,
1698
1769
  });
1699
1770
  finalContent = finalContent ? `${finalContent}\n\n${notice}` : notice;
@@ -1738,7 +1809,12 @@ class AgentChatController {
1738
1809
  lines.push(`Next: ${stats.continuationHint}`);
1739
1810
  }
1740
1811
  const notice = lines.join('\n');
1741
- this.events.append({ role: 'status', title: 'budget', content: notice });
1812
+ this.events.append({
1813
+ role: 'status',
1814
+ title: 'budget',
1815
+ statusTone: 'warning',
1816
+ content: notice,
1817
+ });
1742
1818
  finalContent = finalContent ? `${finalContent}\n\n${notice}` : notice;
1743
1819
  appendAssistantNotice(sessionMessagesToRecord, notice);
1744
1820
  }
@@ -1751,6 +1827,65 @@ class AgentChatController {
1751
1827
  if (sessionId && sessionMessagesToRecord.length > 0) {
1752
1828
  (0, session_storage_1.appendSessionMessages)(sessionId, sessionMessagesToRecord);
1753
1829
  }
1830
+ if (pendingCompact) {
1831
+ try {
1832
+ let committedCheckpointId;
1833
+ if (sessionId) {
1834
+ const sourceMessageCount = (0, session_storage_1.readSessionMessages)(sessionId).length;
1835
+ const checkpoint = (0, session_storage_1.commitSessionCompactCheckpoint)({
1836
+ sessionId,
1837
+ mode: pendingCompact.mode,
1838
+ modelId: finalModel || this.runtime.llm.getModel(),
1839
+ sourceMessageCount,
1840
+ transcriptStartMessageIndex: Math.max(0, sourceMessageCount - 20),
1841
+ modelHistory: pendingCompact.modelHistory,
1842
+ summary: pendingCompact.summary,
1843
+ beforeUsage: pendingCompact.before,
1844
+ afterUsage: pendingCompact.after,
1845
+ });
1846
+ committedCheckpointId = checkpoint.checkpointId;
1847
+ this.runtime.store.setState({ conversationHistory: checkpoint.modelHistory });
1848
+ }
1849
+ else {
1850
+ this.runtime.store.setState({
1851
+ conversationHistory: pendingCompact.modelHistory.filter(message => message.role !== 'system'),
1852
+ });
1853
+ }
1854
+ this.runtime.store.setContextUsage(pendingCompact.after);
1855
+ if (sessionId) {
1856
+ recordTraceEvent(this.events, sessionId, {
1857
+ turnId,
1858
+ type: 'compact_completed',
1859
+ checkpointId: committedCheckpointId,
1860
+ model: finalModel || this.runtime.llm.getModel(),
1861
+ note: pendingCompact.mode,
1862
+ });
1863
+ }
1864
+ this.events.append({
1865
+ role: 'status',
1866
+ title: 'auto-compact',
1867
+ statusTone: 'neutral',
1868
+ 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}%.`,
1869
+ });
1870
+ }
1871
+ catch (error) {
1872
+ if (sessionId) {
1873
+ recordTraceEvent(this.events, sessionId, {
1874
+ turnId,
1875
+ type: 'compact_failed',
1876
+ model: finalModel || this.runtime.llm.getModel(),
1877
+ error: compactTraceError(error),
1878
+ note: pendingCompact.mode,
1879
+ });
1880
+ }
1881
+ this.events.append({
1882
+ role: 'error',
1883
+ title: 'compact-failed',
1884
+ content: `Compact checkpoint failed; the previous model context remains active. ${error instanceof Error ? error.message : String(error)}`,
1885
+ errorLayer: 'runtime',
1886
+ });
1887
+ }
1888
+ }
1754
1889
  if (finalUsage) {
1755
1890
  // Fold subagent token usage into /cost accounting.
1756
1891
  if (subagentBundle) {
@@ -1803,6 +1938,7 @@ class AgentChatController {
1803
1938
  this.events.append({
1804
1939
  role: 'status',
1805
1940
  title: 'recovery',
1941
+ statusTone: 'warning',
1806
1942
  content: recoveryNotice,
1807
1943
  });
1808
1944
  }
@@ -1811,7 +1947,11 @@ class AgentChatController {
1811
1947
  return;
1812
1948
  }
1813
1949
  assistantStream.discardSegment();
1814
- this.events.append({ role: 'error', content: formatChatError(error), errorLayer: errorLayerForChatError(error) });
1950
+ this.events.append({
1951
+ role: 'error',
1952
+ content: formatChatError(error),
1953
+ errorLayer: errorLayerForChatError(error),
1954
+ });
1815
1955
  this.events.setStatus('Turn failed. Ready for the next input.');
1816
1956
  const failedStats = error instanceof framework_1.QueryLoopError
1817
1957
  ? error.stats
@@ -1837,6 +1977,7 @@ class AgentChatController {
1837
1977
  this.events.append({
1838
1978
  role: 'status',
1839
1979
  title: 'recovery',
1980
+ statusTone: 'warning',
1840
1981
  content: recoveryNotice,
1841
1982
  });
1842
1983
  }
@@ -1879,7 +2020,9 @@ exports.InkChatController = AgentChatController;
1879
2020
  function loadSessionIntoRuntime(runtime, sessionId) {
1880
2021
  const history = (0, session_storage_1.loadSessionHistory)(sessionId);
1881
2022
  runtime.store.setState({ conversationHistory: history });
1882
- runtime.store.setState({ harnessState: (0, session_storage_1.loadSessionHarnessState)(sessionId) ?? (0, session_storage_1.loadSessionMeta)(sessionId)?.harnessState });
2023
+ runtime.store.setState({
2024
+ harnessState: (0, session_storage_1.loadSessionHarnessState)(sessionId) ?? (0, session_storage_1.loadSessionMeta)(sessionId)?.harnessState,
2025
+ });
1883
2026
  return `Restored ${history.length} messages`;
1884
2027
  }
1885
2028
  function closeSession(runtime) {