openhorse 0.2.13 → 0.2.17

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 (103) hide show
  1. package/dist/commands/index.d.ts.map +1 -1
  2. package/dist/commands/index.js +217 -49
  3. package/dist/commands/index.js.map +1 -1
  4. package/dist/commands/types.d.ts +4 -1
  5. package/dist/commands/types.d.ts.map +1 -1
  6. package/dist/commands/types.js.map +1 -1
  7. package/dist/core/checkpoint.d.ts +7 -0
  8. package/dist/core/checkpoint.d.ts.map +1 -1
  9. package/dist/core/checkpoint.js +10 -0
  10. package/dist/core/checkpoint.js.map +1 -1
  11. package/dist/core/cost-tracker.d.ts.map +1 -1
  12. package/dist/core/cost-tracker.js +11 -0
  13. package/dist/core/cost-tracker.js.map +1 -1
  14. package/dist/ink-ui/components/PromptInput.d.ts.map +1 -1
  15. package/dist/ink-ui/components/PromptInput.js +5 -3
  16. package/dist/ink-ui/components/PromptInput.js.map +1 -1
  17. package/dist/ink-ui/components/SelectList.d.ts.map +1 -1
  18. package/dist/ink-ui/components/SelectList.js +10 -4
  19. package/dist/ink-ui/components/SelectList.js.map +1 -1
  20. package/dist/ink-ui/components/StatusLine.d.ts +3 -1
  21. package/dist/ink-ui/components/StatusLine.d.ts.map +1 -1
  22. package/dist/ink-ui/components/StatusLine.js +5 -3
  23. package/dist/ink-ui/components/StatusLine.js.map +1 -1
  24. package/dist/ink-ui/components/ToolActivity.d.ts +3 -1
  25. package/dist/ink-ui/components/ToolActivity.d.ts.map +1 -1
  26. package/dist/ink-ui/components/ToolActivity.js +35 -9
  27. package/dist/ink-ui/components/ToolActivity.js.map +1 -1
  28. package/dist/ink-ui/launch.d.ts.map +1 -1
  29. package/dist/ink-ui/launch.js +39 -10
  30. package/dist/ink-ui/launch.js.map +1 -1
  31. package/dist/ink-ui/screens/ReplScreen.d.ts +3 -1
  32. package/dist/ink-ui/screens/ReplScreen.d.ts.map +1 -1
  33. package/dist/ink-ui/screens/ReplScreen.js +63 -93
  34. package/dist/ink-ui/screens/ReplScreen.js.map +1 -1
  35. package/dist/print-ui/launch.d.ts.map +1 -1
  36. package/dist/print-ui/launch.js +1 -0
  37. package/dist/print-ui/launch.js.map +1 -1
  38. package/dist/runtime/agent-runtime-controller.d.ts +5 -0
  39. package/dist/runtime/agent-runtime-controller.d.ts.map +1 -1
  40. package/dist/runtime/agent-runtime-controller.js +15 -3
  41. package/dist/runtime/agent-runtime-controller.js.map +1 -1
  42. package/dist/runtime/agent-status.d.ts +5 -0
  43. package/dist/runtime/agent-status.d.ts.map +1 -1
  44. package/dist/runtime/agent-status.js +28 -2
  45. package/dist/runtime/agent-status.js.map +1 -1
  46. package/dist/runtime/chat-controller.d.ts +4 -0
  47. package/dist/runtime/chat-controller.d.ts.map +1 -1
  48. package/dist/runtime/chat-controller.js +199 -67
  49. package/dist/runtime/chat-controller.js.map +1 -1
  50. package/dist/runtime/turn-controller.d.ts +4 -0
  51. package/dist/runtime/turn-controller.d.ts.map +1 -1
  52. package/dist/runtime/turn-controller.js +8 -0
  53. package/dist/runtime/turn-controller.js.map +1 -1
  54. package/dist/runtime/ui-events.d.ts +7 -0
  55. package/dist/runtime/ui-events.d.ts.map +1 -1
  56. package/dist/runtime/ui-events.js.map +1 -1
  57. package/dist/runtime/ui-view-model.d.ts +287 -0
  58. package/dist/runtime/ui-view-model.d.ts.map +1 -0
  59. package/dist/runtime/ui-view-model.js +642 -0
  60. package/dist/runtime/ui-view-model.js.map +1 -0
  61. package/dist/services/file-glob.d.ts +6 -2
  62. package/dist/services/file-glob.d.ts.map +1 -1
  63. package/dist/services/file-glob.js +10 -4
  64. package/dist/services/file-glob.js.map +1 -1
  65. package/dist/services/model-catalog.d.ts +14 -0
  66. package/dist/services/model-catalog.d.ts.map +1 -0
  67. package/dist/services/model-catalog.js +73 -0
  68. package/dist/services/model-catalog.js.map +1 -0
  69. package/dist/services/model-context.d.ts.map +1 -1
  70. package/dist/services/model-context.js +3 -0
  71. package/dist/services/model-context.js.map +1 -1
  72. package/dist/services/session-storage.d.ts +1 -0
  73. package/dist/services/session-storage.d.ts.map +1 -1
  74. package/dist/services/session-storage.js.map +1 -1
  75. package/dist/services/verification-profile.d.ts +15 -0
  76. package/dist/services/verification-profile.d.ts.map +1 -1
  77. package/dist/services/verification-profile.js +67 -0
  78. package/dist/services/verification-profile.js.map +1 -1
  79. package/dist/terminal-ui/completion.d.ts.map +1 -1
  80. package/dist/terminal-ui/completion.js +9 -6
  81. package/dist/terminal-ui/completion.js.map +1 -1
  82. package/dist/terminal-ui/launch.d.ts +15 -1
  83. package/dist/terminal-ui/launch.d.ts.map +1 -1
  84. package/dist/terminal-ui/launch.js +300 -56
  85. package/dist/terminal-ui/launch.js.map +1 -1
  86. package/dist/terminal-ui/raw-editor.d.ts +4 -0
  87. package/dist/terminal-ui/raw-editor.d.ts.map +1 -1
  88. package/dist/terminal-ui/raw-editor.js +29 -0
  89. package/dist/terminal-ui/raw-editor.js.map +1 -1
  90. package/dist/tui-ui/launch.d.ts.map +1 -1
  91. package/dist/tui-ui/launch.js +1 -0
  92. package/dist/tui-ui/launch.js.map +1 -1
  93. package/dist/tui-ui/layout.d.ts.map +1 -1
  94. package/dist/tui-ui/layout.js +21 -28
  95. package/dist/tui-ui/layout.js.map +1 -1
  96. package/dist/tui-ui/pickers.d.ts.map +1 -1
  97. package/dist/tui-ui/pickers.js +21 -62
  98. package/dist/tui-ui/pickers.js.map +1 -1
  99. package/dist/tui-ui/state.d.ts +2 -4
  100. package/dist/tui-ui/state.d.ts.map +1 -1
  101. package/dist/tui-ui/state.js +13 -11
  102. package/dist/tui-ui/state.js.map +1 -1
  103. package/package.json +1 -1
@@ -2,12 +2,38 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.agentStepStatus = agentStepStatus;
4
4
  exports.runningToolsStatus = runningToolsStatus;
5
+ exports.verifyingStatus = verifyingStatus;
6
+ exports.verificationGateStatus = verificationGateStatus;
7
+ exports.batchingSuggestion = batchingSuggestion;
8
+ exports.compactStatus = compactStatus;
9
+ exports.permissionPendingStatus = permissionPendingStatus;
5
10
  exports.isLegacyTurnStatus = isLegacyTurnStatus;
6
11
  function agentStepStatus(turn) {
7
- return turn <= 1 ? 'Thinking...' : 'Reading tool results...';
12
+ // request_start on turn 1 is fresh reasoning; later turns continue from
13
+ // tool results, so label them "reading tool results" to distinguish the phase.
14
+ // Tool execution, verification, compact, and permission states are emitted separately.
15
+ return turn <= 1 ? 'Working: thinking' : 'Working: reading tool results';
8
16
  }
9
17
  function runningToolsStatus(count) {
10
- return count === 1 ? 'Running tool...' : `Running ${count} tools...`;
18
+ return count === 1 ? 'Working: running tool' : `Working: running ${count} tools`;
19
+ }
20
+ function verifyingStatus(profile) {
21
+ return `Verifying: running ${profile} checks...`;
22
+ }
23
+ function verificationGateStatus(reason) {
24
+ return `Verification required: ${reason}`;
25
+ }
26
+ function batchingSuggestion(readOnlyCount) {
27
+ if (readOnlyCount >= 3) {
28
+ return `${readOnlyCount} independent read-only tool calls detected. Use the batch_read tool to group related reads into a single turn and reduce model-tool roundtrips.`;
29
+ }
30
+ return '';
31
+ }
32
+ function compactStatus() {
33
+ return 'Compacting: summarizing conversation history...';
34
+ }
35
+ function permissionPendingStatus(toolName) {
36
+ return `Waiting: permission required for ${toolName}`;
11
37
  }
12
38
  function isLegacyTurnStatus(message) {
13
39
  return /^Turn\s+\d+\.\.\.$/.test(message.trim());
@@ -1 +1 @@
1
- {"version":3,"file":"agent-status.js","sourceRoot":"","sources":["../../src/runtime/agent-status.ts"],"names":[],"mappings":";;AAAA,0CAEC;AAED,gDAEC;AAED,gDAEC;AAVD,SAAgB,eAAe,CAAC,IAAY;IAC1C,OAAO,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,yBAAyB,CAAC;AAC/D,CAAC;AAED,SAAgB,kBAAkB,CAAC,KAAa;IAC9C,OAAO,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,WAAW,KAAK,WAAW,CAAC;AACvE,CAAC;AAED,SAAgB,kBAAkB,CAAC,OAAe;IAChD,OAAO,oBAAoB,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;AACnD,CAAC"}
1
+ {"version":3,"file":"agent-status.js","sourceRoot":"","sources":["../../src/runtime/agent-status.ts"],"names":[],"mappings":";;AAAA,0CAKC;AAED,gDAEC;AAED,0CAEC;AAED,wDAEC;AAED,gDAKC;AAED,sCAEC;AAED,0DAEC;AAED,gDAEC;AApCD,SAAgB,eAAe,CAAC,IAAY;IAC1C,wEAAwE;IACxE,+EAA+E;IAC/E,uFAAuF;IACvF,OAAO,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC,+BAA+B,CAAC;AAC3E,CAAC;AAED,SAAgB,kBAAkB,CAAC,KAAa;IAC9C,OAAO,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,uBAAuB,CAAC,CAAC,CAAC,oBAAoB,KAAK,QAAQ,CAAC;AACnF,CAAC;AAED,SAAgB,eAAe,CAAC,OAAe;IAC7C,OAAO,sBAAsB,OAAO,YAAY,CAAC;AACnD,CAAC;AAED,SAAgB,sBAAsB,CAAC,MAAc;IACnD,OAAO,0BAA0B,MAAM,EAAE,CAAC;AAC5C,CAAC;AAED,SAAgB,kBAAkB,CAAC,aAAqB;IACtD,IAAI,aAAa,IAAI,CAAC,EAAE,CAAC;QACvB,OAAO,GAAG,aAAa,iJAAiJ,CAAC;IAC3K,CAAC;IACD,OAAO,EAAE,CAAC;AACZ,CAAC;AAED,SAAgB,aAAa;IAC3B,OAAO,iDAAiD,CAAC;AAC3D,CAAC;AAED,SAAgB,uBAAuB,CAAC,QAAgB;IACtD,OAAO,oCAAoC,QAAQ,EAAE,CAAC;AACxD,CAAC;AAED,SAAgB,kBAAkB,CAAC,OAAe;IAChD,OAAO,oBAAoB,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;AACnD,CAAC"}
@@ -1,3 +1,4 @@
1
+ import type { CommandUiRenderer } from '../commands/types';
1
2
  import { query, type QueryEvent } from '../framework';
2
3
  import type { OpenHorseUiRuntime, TranscriptEntry, UiEventSink, UiRendererCapabilities } from './ui-events';
3
4
  export declare function sessionMessagesToTranscriptEntries(sessionId: string): TranscriptEntry[];
@@ -18,6 +19,7 @@ type ToolResultEvent = Extract<QueryEvent, {
18
19
  export interface ToolEventPresenter {
19
20
  start(event: ToolCallEvent): void;
20
21
  finish(event: ToolResultEvent): void;
22
+ finalizePendingAsSkipped(reason?: string): void;
21
23
  }
22
24
  export declare function createToolEventPresenter(events: UiEventSink): ToolEventPresenter;
23
25
  export interface RunInputOptions {
@@ -27,6 +29,8 @@ export interface RunInputOptions {
27
29
  export interface AgentChatControllerOptions {
28
30
  confirmToolUse?: Parameters<typeof query>[0]['confirmToolUse'];
29
31
  uiCapabilities?: UiRendererCapabilities;
32
+ uiRenderer?: CommandUiRenderer;
33
+ onVerificationStateChange?: (state: 'pending' | 'running' | 'passed' | 'failed' | 'gated') => void;
30
34
  }
31
35
  /** @deprecated Use AgentChatControllerOptions. Chat execution is renderer-independent. */
32
36
  export type InkChatControllerOptions = AgentChatControllerOptions;
@@ -1 +1 @@
1
- {"version":3,"file":"chat-controller.d.ts","sourceRoot":"","sources":["../../src/runtime/chat-controller.ts"],"names":[],"mappings":"AAsBA,OAAO,EAAE,KAAK,EAAqJ,KAAK,UAAU,EAAE,MAAM,cAAc,CAAC;AAqBzM,OAAO,KAAK,EACV,kBAAkB,EAElB,eAAe,EACf,WAAW,EACX,sBAAsB,EACvB,MAAM,aAAa,CAAC;AA8jBrB,wBAAgB,kCAAkC,CAAC,SAAS,EAAE,MAAM,GAAG,eAAe,EAAE,CAwDvF;AAED,MAAM,WAAW,wBAAwB;IACvC,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,YAAY,IAAI,IAAI,CAAC;IACrB,cAAc,IAAI,IAAI,CAAC;IACvB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACvC;AAED,wBAAgB,8BAA8B,CAAC,MAAM,EAAE,WAAW,EAAE,WAAW,CAAC,EAAE,WAAW,GAAG,wBAAwB,CA+DvH;AAED,KAAK,aAAa,GAAG,OAAO,CAAC,UAAU,EAAE;IAAE,IAAI,EAAE,WAAW,CAAA;CAAE,CAAC,CAAC;AAChE,KAAK,eAAe,GAAG,OAAO,CAAC,UAAU,EAAE;IAAE,IAAI,EAAE,aAAa,CAAA;CAAE,CAAC,CAAC;AAoFpE,MAAM,WAAW,kBAAkB;IACjC,KAAK,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI,CAAC;IAClC,MAAM,CAAC,KAAK,EAAE,eAAe,GAAG,IAAI,CAAC;CACtC;AAED,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,WAAW,GAAG,kBAAkB,CAmDhF;AAwBD,MAAM,WAAW,eAAe;IAC9B,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,0BAA0B;IACzC,cAAc,CAAC,EAAE,UAAU,CAAC,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC;IAC/D,cAAc,CAAC,EAAE,sBAAsB,CAAC;CACzC;AAED,0FAA0F;AAC1F,MAAM,MAAM,wBAAwB,GAAG,0BAA0B,CAAC;AAElE,qBAAa,mBAAmB;IAE5B,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,iBAAiB;gBAFjB,OAAO,EAAE,kBAAkB,EAC3B,MAAM,EAAE,WAAW,EACnB,iBAAiB,GAAE,0BAA+B;IAGrE,OAAO,CAAC,YAAY;IAKd,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,GAAE,eAAoB,GAAG,OAAO,CAAC,IAAI,CAAC;YAsF7D,gBAAgB;IAwL9B,OAAO,CAAC,oBAAoB;YAiCd,OAAO;CA4jBtB;AAED,mFAAmF;AACnF,OAAO,EAAE,mBAAmB,IAAI,iBAAiB,EAAE,CAAC;AAEpD,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,kBAAkB,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAK7F;AAED,wBAAgB,YAAY,CAAC,OAAO,EAAE,kBAAkB,GAAG,IAAI,CAQ9D"}
1
+ {"version":3,"file":"chat-controller.d.ts","sourceRoot":"","sources":["../../src/runtime/chat-controller.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAiC,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAmB1F,OAAO,EAAE,KAAK,EAAqJ,KAAK,UAAU,EAAE,MAAM,cAAc,CAAC;AAuBzM,OAAO,KAAK,EACV,kBAAkB,EAElB,eAAe,EACf,WAAW,EACX,sBAAsB,EACvB,MAAM,aAAa,CAAC;AAgkBrB,wBAAgB,kCAAkC,CAAC,SAAS,EAAE,MAAM,GAAG,eAAe,EAAE,CAwDvF;AAED,MAAM,WAAW,wBAAwB;IACvC,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,YAAY,IAAI,IAAI,CAAC;IACrB,cAAc,IAAI,IAAI,CAAC;IACvB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACvC;AAED,wBAAgB,8BAA8B,CAAC,MAAM,EAAE,WAAW,EAAE,WAAW,CAAC,EAAE,WAAW,GAAG,wBAAwB,CA+DvH;AAED,KAAK,aAAa,GAAG,OAAO,CAAC,UAAU,EAAE;IAAE,IAAI,EAAE,WAAW,CAAA;CAAE,CAAC,CAAC;AAChE,KAAK,eAAe,GAAG,OAAO,CAAC,UAAU,EAAE;IAAE,IAAI,EAAE,aAAa,CAAA;CAAE,CAAC,CAAC;AAqFpE,MAAM,WAAW,kBAAkB;IACjC,KAAK,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI,CAAC;IAClC,MAAM,CAAC,KAAK,EAAE,eAAe,GAAG,IAAI,CAAC;IACrC,wBAAwB,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACjD;AAED,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,WAAW,GAAG,kBAAkB,CAmFhF;AAwBD,MAAM,WAAW,eAAe;IAC9B,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,0BAA0B;IACzC,cAAc,CAAC,EAAE,UAAU,CAAC,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC;IAC/D,cAAc,CAAC,EAAE,sBAAsB,CAAC;IACxC,UAAU,CAAC,EAAE,iBAAiB,CAAC;IAC/B,yBAAyB,CAAC,EAAE,CAAC,KAAK,EAAE,SAAS,GAAG,SAAS,GAAG,QAAQ,GAAG,QAAQ,GAAG,OAAO,KAAK,IAAI,CAAC;CACpG;AAED,0FAA0F;AAC1F,MAAM,MAAM,wBAAwB,GAAG,0BAA0B,CAAC;AAElE,qBAAa,mBAAmB;IAE5B,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,iBAAiB;gBAFjB,OAAO,EAAE,kBAAkB,EAC3B,MAAM,EAAE,WAAW,EACnB,iBAAiB,GAAE,0BAA+B;IAGrE,OAAO,CAAC,YAAY;IAKd,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,GAAE,eAAoB,GAAG,OAAO,CAAC,IAAI,CAAC;YAwF7D,gBAAgB;IA2L9B,OAAO,CAAC,oBAAoB;YAqCd,OAAO;CAqpBtB;AAED,mFAAmF;AACnF,OAAO,EAAE,mBAAmB,IAAI,iBAAiB,EAAE,CAAC;AAEpD,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,kBAAkB,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAK7F;AAED,wBAAgB,YAAY,CAAC,OAAO,EAAE,kBAAkB,GAAG,IAAI,CAQ9D"}
@@ -53,14 +53,17 @@ const checkpoint_1 = require("../core/checkpoint");
53
53
  const skills_1 = require("../skills");
54
54
  const file_context_1 = require("../services/file-context");
55
55
  const prompt_context_1 = require("../services/prompt-context");
56
+ const format_1 = require("../services/format");
56
57
  const workspace_state_1 = require("../services/workspace-state");
57
58
  const verification_profile_1 = require("../services/verification-profile");
58
59
  const ui_events_1 = require("./ui-events");
60
+ const ui_view_model_1 = require("./ui-view-model");
59
61
  const agent_status_1 = require("./agent-status");
60
62
  const loop_budget_1 = require("./loop-budget");
61
63
  const ANSI_PATTERN = /\x1b\[[0-9;?]*[A-Za-z]/g;
62
64
  const LOCAL_FAST_PATH_INLINE_OUTPUT_BYTES = 2048;
63
65
  const TRACE_ARGS_ARTIFACT_THRESHOLD_BYTES = 160;
66
+ const TOOL_TRANSCRIPT_ARG_BUDGET = 512;
64
67
  function stripAnsi(text) {
65
68
  return text.replace(ANSI_PATTERN, '');
66
69
  }
@@ -72,6 +75,29 @@ function isAbortError(error, abortSignal) {
72
75
  }
73
76
  return false;
74
77
  }
78
+ function errorLayerForChatError(error) {
79
+ const message = error instanceof Error ? error.message : String(error);
80
+ const lower = message.toLowerCase();
81
+ if (/NotEnoughCvError|code:\s*11210|provider|quota|rate.?limit|timeout|connection/i.test(message)) {
82
+ return 'provider';
83
+ }
84
+ if (/tool|command|exec_command|write_file|edit_file/i.test(message)) {
85
+ return 'tool';
86
+ }
87
+ if (/\bmcp\b/i.test(message))
88
+ return 'mcp';
89
+ if (/\b(session|resume|compact|harness)\b/i.test(message))
90
+ return 'session';
91
+ if (/\b(skill|skills)\b/i.test(message))
92
+ return 'skills';
93
+ if (/\b(memory|vector store|recall|forget)\b/i.test(message))
94
+ return 'memory';
95
+ if (/\b(renderer|terminal|tty|prompt|resize|scrollback)\b/i.test(message))
96
+ return 'renderer';
97
+ if (lower.includes('abort') || lower.includes('interrupted'))
98
+ return 'runtime';
99
+ return 'unknown';
100
+ }
75
101
  function formatChatError(error) {
76
102
  const message = error instanceof Error ? error.message : String(error);
77
103
  if (/NotEnoughCvError|code:\s*11210/i.test(message)) {
@@ -170,10 +196,11 @@ function checkpointTargetsFromToolCalls(cwd, toolCalls) {
170
196
  function createPreToolCheckpoint(events, sessionId, turnId, checkpointId, cwd, toolCalls) {
171
197
  const targets = checkpointTargetsFromToolCalls(cwd, toolCalls);
172
198
  if (targets.length === 0)
173
- return false;
199
+ return { created: false, targetCount: 0, risky: false };
200
+ const risky = (0, checkpoint_1.shouldCreateMultiFileCheckpoint)(targets.length);
174
201
  const checkpoint = (0, checkpoint_1.createCheckpoint)(cwd, checkpointId, targets);
175
202
  if (!sessionId)
176
- return true;
203
+ return { created: true, targetCount: targets.length, risky };
177
204
  const relativeTargets = targets.map(target => path.relative(cwd, target));
178
205
  recordTraceEvent(events, sessionId, {
179
206
  turnId,
@@ -183,10 +210,10 @@ function createPreToolCheckpoint(events, sessionId, turnId, checkpointId, cwd, t
183
210
  checkpointFiles: checkpoint?.files.map(file => file.path) ?? [],
184
211
  workspaceFiles: relativeTargets,
185
212
  note: checkpoint
186
- ? 'pre_edit_checkpoint'
213
+ ? (risky ? 'risky_multi_file_checkpoint' : 'pre_edit_checkpoint')
187
214
  : 'pre_edit_checkpoint_skipped',
188
215
  });
189
- return true;
216
+ return { created: true, targetCount: targets.length, risky };
190
217
  }
191
218
  function byteLength(text) {
192
219
  return Buffer.byteLength(text, 'utf8');
@@ -273,6 +300,7 @@ function appendVerificationProfileTrace(events, sessionId, turnId, profile) {
273
300
  type: 'verification_profile',
274
301
  verificationProfile: profile.profile,
275
302
  verificationRequired: profile.required,
303
+ verificationRisky: (0, verification_profile_1.isRiskyEdit)(profile.changedFiles),
276
304
  verificationCommands: compactPathList(profile.commands, 8),
277
305
  verificationChangedFiles: compactPathList(profile.changedFiles),
278
306
  note: profile.reason,
@@ -416,34 +444,11 @@ function toHarnessDiagnostics(state) {
416
444
  function emitHarnessDiagnostics(events, state) {
417
445
  events.harnessDiagnosticsUpdated?.(toHarnessDiagnostics(state));
418
446
  }
419
- function toolStartContent(name, args) {
420
- if (name === 'exec_command' && typeof args.command === 'string') {
421
- return `Running ${name}\n $ ${args.command}`;
422
- }
423
- const detail = compactToolArgs(args);
424
- return `Running ${name}${detail ? ` ${detail}` : ''}`;
425
- }
426
- function toolSummary(name, args, success, duration) {
427
- const details = compactToolArgs(args);
428
- const suffix = details ? ` ${details}` : '';
429
- return `${success ? '✓' : '✗'} ${name}${suffix} (${duration}ms)`;
447
+ function toolStartContent(event) {
448
+ return (0, ui_view_model_1.formatToolActivityTranscript)((0, ui_view_model_1.toolActivityFromStarted)(event, compactToolArgs(event.args, TOOL_TRANSCRIPT_ARG_BUDGET)));
430
449
  }
431
450
  function toolFinishContent(event) {
432
- const summary = event.summary || toolSummary(event.name, event.args, event.success, event.duration);
433
- const lines = [summary];
434
- if (event.name === 'exec_command' && typeof event.args.command === 'string') {
435
- lines.push(` $ ${event.args.command}`);
436
- }
437
- if (event.artifactRef) {
438
- lines.push(` artifact ${event.artifactRef.id} (${event.artifactRef.outputBytes}B full output)`);
439
- }
440
- else if (typeof event.outputBytes === 'number') {
441
- lines.push(` output ${event.outputBytes}B`);
442
- }
443
- if (event.error) {
444
- lines.push(`Error: ${event.error}`);
445
- }
446
- return lines.filter(Boolean).join('\n');
451
+ return (0, ui_view_model_1.formatToolActivityTranscript)((0, ui_view_model_1.toolActivityFromFinished)(event, compactToolArgs(event.args, TOOL_TRANSCRIPT_ARG_BUDGET)));
447
452
  }
448
453
  function isSyntheticCompactContext(content) {
449
454
  return content.startsWith('[OpenHorse Context State v2]')
@@ -645,7 +650,8 @@ function parseLocalFastPath(input) {
645
650
  }
646
651
  function formatLocalFastPathAssistantContent(action, rawResult, projectPath) {
647
652
  const envelope = (0, tool_serializer_1.parseToolResultEnvelope)(rawResult);
648
- const output = typeof envelope.output === 'string' ? envelope.output.trim() : '';
653
+ const rawOutput = typeof envelope.output === 'string' ? envelope.output : '';
654
+ const output = rawOutput.trim();
649
655
  const summary = envelope.summary || `${action.tool} ${envelope.success ? 'completed' : 'failed'}`;
650
656
  const lines = [
651
657
  envelope.success
@@ -658,41 +664,67 @@ function formatLocalFastPathAssistantContent(action, rawResult, projectPath) {
658
664
  lines.push(`Error: ${envelope.error}`);
659
665
  }
660
666
  if (!output) {
661
- return lines.join('\n');
667
+ return { content: lines.join('\n'), artifactRef: envelope.artifactRef };
662
668
  }
663
669
  let artifactRef = envelope.artifactRef;
664
670
  let preview = output;
665
- const outputBytes = envelope.outputBytes ?? byteLength(output);
666
- if (byteLength(output) > LOCAL_FAST_PATH_INLINE_OUTPUT_BYTES) {
671
+ const outputBytes = envelope.outputBytes ?? byteLength(rawOutput);
672
+ if (byteLength(rawOutput) > LOCAL_FAST_PATH_INLINE_OUTPUT_BYTES) {
667
673
  if (!artifactRef) {
668
- const artifact = (0, tool_artifacts_1.storeArtifact)(projectPath, action.tool, output, outputBytes);
674
+ const artifact = (0, tool_artifacts_1.storeArtifact)(projectPath, action.tool, rawOutput, outputBytes);
669
675
  artifactRef = artifact ? { id: artifact.id, outputBytes: artifact.outputBytes } : undefined;
670
676
  }
671
677
  preview = (0, tool_artifacts_1.truncateForContext)(output, LOCAL_FAST_PATH_INLINE_OUTPUT_BYTES);
672
678
  }
673
679
  if (artifactRef) {
674
- lines.push('', `Full output: /artifacts show ${artifactRef.id} --full (${artifactRef.outputBytes}B)`);
680
+ lines.push('', `Full output: /artifacts show ${artifactRef.id} --full (${(0, format_1.formatBytes)(artifactRef.outputBytes)})`);
675
681
  }
676
682
  lines.push('', 'Preview:', preview);
677
- return lines.join('\n');
683
+ return { content: lines.join('\n'), artifactRef };
678
684
  }
679
685
  function createToolEventPresenter(events) {
680
686
  const runningToolEntries = new Map();
681
687
  return {
682
688
  start(event) {
689
+ const entryId = events.append({
690
+ role: 'tool',
691
+ title: 'tool',
692
+ content: toolStartContent(event),
693
+ });
694
+ runningToolEntries.set(event.callId, {
695
+ entryId,
696
+ name: event.name,
697
+ args: event.args,
698
+ batchCount: event.batchCount,
699
+ batchIndex: event.batchIndex,
700
+ });
683
701
  events.toolStarted?.({
684
702
  callId: event.callId,
685
703
  name: event.name,
686
704
  args: event.args,
705
+ batchCount: event.batchCount,
706
+ batchIndex: event.batchIndex,
687
707
  });
688
- const entryId = events.append({
689
- role: 'tool',
690
- title: 'tool',
691
- content: toolStartContent(event.name, event.args),
692
- });
693
- runningToolEntries.set(event.callId, entryId);
694
708
  },
695
709
  finish(event) {
710
+ const content = toolFinishContent(event);
711
+ const stored = runningToolEntries.get(event.callId);
712
+ if (stored) {
713
+ events.finalize(stored.entryId, {
714
+ role: event.success ? 'tool' : 'error',
715
+ title: 'tool',
716
+ content,
717
+ });
718
+ runningToolEntries.delete(event.callId);
719
+ }
720
+ else {
721
+ const entryId = events.append({
722
+ role: event.success ? 'tool' : 'error',
723
+ title: 'tool',
724
+ content,
725
+ });
726
+ events.finalize(entryId);
727
+ }
696
728
  events.toolFinished?.({
697
729
  callId: event.callId,
698
730
  name: event.name,
@@ -703,24 +735,30 @@ function createToolEventPresenter(events) {
703
735
  error: event.error,
704
736
  outputBytes: event.outputBytes,
705
737
  artifactRef: event.artifactRef,
738
+ batchCount: event.batchCount,
739
+ batchIndex: event.batchIndex,
706
740
  });
707
- const content = toolFinishContent(event);
708
- const existingEntryId = runningToolEntries.get(event.callId);
709
- if (existingEntryId) {
710
- events.finalize(existingEntryId, {
711
- role: event.success ? 'tool' : 'error',
741
+ },
742
+ finalizePendingAsSkipped(reason = 'permission denied') {
743
+ for (const [callId, entry] of runningToolEntries) {
744
+ events.finalize(entry.entryId, {
745
+ role: 'tool',
712
746
  title: 'tool',
713
- content,
747
+ content: `Skipped · ${reason}`,
748
+ });
749
+ events.toolFinished?.({
750
+ callId,
751
+ name: entry.name,
752
+ args: entry.args,
753
+ success: false,
754
+ skipped: true,
755
+ duration: 0,
756
+ error: reason,
757
+ batchCount: entry.batchCount,
758
+ batchIndex: entry.batchIndex,
714
759
  });
715
- runningToolEntries.delete(event.callId);
716
- return;
717
760
  }
718
- const entryId = events.append({
719
- role: event.success ? 'tool' : 'error',
720
- title: 'tool',
721
- content,
722
- });
723
- events.finalize(entryId);
761
+ runningToolEntries.clear();
724
762
  },
725
763
  };
726
764
  }
@@ -791,6 +829,7 @@ class AgentChatController {
791
829
  content: suggestions.length > 0
792
830
  ? `Unknown command: /${parsed.name}\nDid you mean: ${suggestions.map(item => `/${item}`).join(', ')}?`
793
831
  : `Unknown command: /${parsed.name}`,
832
+ errorLayer: 'runtime',
794
833
  });
795
834
  return;
796
835
  }
@@ -815,6 +854,7 @@ class AgentChatController {
815
854
  role: 'error',
816
855
  title: `/${command.name}`,
817
856
  content: result.error,
857
+ errorLayer: 'runtime',
818
858
  });
819
859
  }
820
860
  if (result.sessionPicker) {
@@ -898,7 +938,8 @@ class AgentChatController {
898
938
  const outputBytes = typeof envelope.outputBytes === 'number'
899
939
  ? envelope.outputBytes
900
940
  : Buffer.byteLength(result, 'utf8');
901
- const assistantContent = formatLocalFastPathAssistantContent(action, result, this.runtime.cwd);
941
+ const formattedLocalResult = formatLocalFastPathAssistantContent(action, result, this.runtime.cwd);
942
+ const assistantContent = formattedLocalResult.content;
902
943
  const stats = (0, framework_1.createLocalFastPathLoopStats)({
903
944
  finishReason: envelope.success ? 'completed' : 'failed',
904
945
  toolCalls: 1,
@@ -923,6 +964,7 @@ class AgentChatController {
923
964
  error: envelope.error,
924
965
  summary: envelope.summary,
925
966
  outputBytes,
967
+ artifactRef: formattedLocalResult.artifactRef,
926
968
  }),
927
969
  });
928
970
  this.runtime.store.addMessage({ role: 'assistant', content: assistantContent });
@@ -940,6 +982,7 @@ class AgentChatController {
940
982
  duration,
941
983
  outputBytes,
942
984
  modelVisibleBytes: 0,
985
+ artifactId: formattedLocalResult.artifactRef?.id,
943
986
  error: envelope.error ? compactMiddle(envelope.error, 240) : undefined,
944
987
  });
945
988
  appendPostWorkspaceTrace(this.events, sessionId, turnId, this.runtime.cwd, preWorkspace);
@@ -967,7 +1010,7 @@ class AgentChatController {
967
1010
  }
968
1011
  catch (error) {
969
1012
  const message = error instanceof Error ? error.message : String(error);
970
- this.events.append({ role: 'error', title: 'local', content: message });
1013
+ this.events.append({ role: 'error', title: 'local', content: message, errorLayer: 'tool' });
971
1014
  this.events.setStatus('Local command failed. Ready for the next input.');
972
1015
  const assistantContent = `Local fast path failed for ${action.label}.\n\n${message}`;
973
1016
  this.runtime.store.addMessage({ role: 'assistant', content: assistantContent });
@@ -1031,18 +1074,20 @@ class AgentChatController {
1031
1074
  this.events.append({ role: 'system', content: text });
1032
1075
  }
1033
1076
  },
1034
- uiCapabilities: (0, ui_events_1.resolveUiRendererCapabilities)(this.controllerOptions.uiCapabilities),
1077
+ uiRenderer: this.controllerOptions.uiRenderer ?? this.runtime.config.ui?.renderer ?? 'terminal',
1078
+ uiCapabilities: (0, ui_events_1.resolveUiRendererCapabilities)(this.controllerOptions.uiCapabilities, this.controllerOptions.uiRenderer ?? this.runtime.config.ui?.renderer),
1035
1079
  };
1036
1080
  }
1037
1081
  async runChat(input, options = {}) {
1038
1082
  if (!input) {
1039
- this.events.append({ role: 'error', content: 'Usage: /chat <message>' });
1083
+ this.events.append({ role: 'error', content: 'Usage: /chat <message>', errorLayer: 'runtime' });
1040
1084
  return;
1041
1085
  }
1042
1086
  if (!this.runtime.llm || !(0, config_1.isConfigured)(this.runtime.config)) {
1043
1087
  this.events.append({
1044
1088
  role: 'error',
1045
1089
  content: 'LLM is not configured. Set OPENHORSE_API_KEY in ~/.openhorse/openhorse.json or environment.',
1090
+ errorLayer: 'provider',
1046
1091
  });
1047
1092
  return;
1048
1093
  }
@@ -1090,6 +1135,10 @@ class AgentChatController {
1090
1135
  });
1091
1136
  const intent = harness.updateContractFromUserInput(input);
1092
1137
  harness.recordAppliedSkills(skillResolution.skills);
1138
+ // Reconcile diagnostic: when harness state is present but objective may be incomplete
1139
+ if (snapshot.harnessState && !snapshot.harnessState.rootObjective && !snapshot.harnessState.contract?.objective) {
1140
+ this.events.setStatus('Resume diagnostic: harness state restored but objective may be incomplete. Run /harness explain to review.');
1141
+ }
1093
1142
  const promptCtx = {
1094
1143
  cwd: this.runtime.cwd,
1095
1144
  platform: process.platform,
@@ -1212,16 +1261,40 @@ class AgentChatController {
1212
1261
  case 'assistant_tool_calls':
1213
1262
  assistantStream.ensureMessage(event.content || '');
1214
1263
  assistantStream.closeSegment();
1215
- if (event.toolCalls.length > 1) {
1216
- this.events.setStatus((0, agent_status_1.runningToolsStatus)(event.toolCalls.length));
1264
+ this.events.setStatus((0, agent_status_1.runningToolsStatus)(event.toolCalls.length));
1265
+ {
1266
+ const batchReadOnlyCount = event.toolCalls.filter(tc => {
1267
+ const def = skillResolution.tools.find(t => t.name === tc.function.name);
1268
+ const args = parseToolCallArgsForRuntime(tc);
1269
+ return args && def?.isReadOnly?.(args) === true;
1270
+ }).length;
1271
+ const suggestion = (0, agent_status_1.batchingSuggestion)(batchReadOnlyCount);
1272
+ if (suggestion) {
1273
+ this.events.append({ role: 'status', content: suggestion });
1274
+ }
1217
1275
  }
1218
1276
  const checkpointId = checkpointSequence === 0
1219
1277
  ? turnId
1220
1278
  : `${turnId}-checkpoint-${checkpointSequence + 1}`;
1221
- if (createPreToolCheckpoint(this.events, sessionId, turnId, checkpointId, this.runtime.cwd, event.toolCalls)) {
1279
+ const checkpointResult = createPreToolCheckpoint(this.events, sessionId, turnId, checkpointId, this.runtime.cwd, event.toolCalls);
1280
+ if (checkpointResult.created) {
1222
1281
  checkpointIds.push(checkpointId);
1223
1282
  checkpointSequence++;
1224
1283
  }
1284
+ if (checkpointResult.created && checkpointResult.risky) {
1285
+ this.events.append({
1286
+ role: 'status',
1287
+ title: 'checkpoint',
1288
+ content: `Risky edit: ${checkpointResult.targetCount} files in one turn. Checkpoint ${checkpointId} created for rollback (/checkpoints restore ${checkpointId}).`,
1289
+ });
1290
+ }
1291
+ else if (checkpointResult.risky) {
1292
+ this.events.append({
1293
+ role: 'status',
1294
+ title: 'checkpoint',
1295
+ content: `Risky edit: ${checkpointResult.targetCount} files in one turn, but checkpoint creation failed. Restore any pre-existing checkpoint manually or revert via git.`,
1296
+ });
1297
+ }
1225
1298
  if (sessionId) {
1226
1299
  recordTraceEvent(this.events, sessionId, {
1227
1300
  turnId,
@@ -1353,6 +1426,9 @@ class AgentChatController {
1353
1426
  }
1354
1427
  break;
1355
1428
  case 'complete':
1429
+ if (event.stats?.finishReason === 'blocked') {
1430
+ toolEvents.finalizePendingAsSkipped('permission denied');
1431
+ }
1356
1432
  if (event.content && !finalContent) {
1357
1433
  if (event.stats?.finishReason === 'budget_exceeded') {
1358
1434
  assistantStream.replaceMessage(event.content);
@@ -1412,12 +1488,22 @@ class AgentChatController {
1412
1488
  this.events.setStatus('Interrupted.');
1413
1489
  removeTrailingUserMessage(this.runtime);
1414
1490
  if (sessionId) {
1415
- appendPostWorkspaceTrace(this.events, sessionId, turnId, this.runtime.cwd, preWorkspace, verificationResults);
1491
+ const { delta } = appendPostWorkspaceTrace(this.events, sessionId, turnId, this.runtime.cwd, preWorkspace, verificationResults);
1416
1492
  recordTraceEvent(this.events, sessionId, {
1417
1493
  turnId,
1418
1494
  type: 'aborted',
1419
1495
  note: 'aborted_after_query',
1420
1496
  });
1497
+ const recoveryNotice = workspaceDeltaHasTurnChanges(delta)
1498
+ ? formatFailureRecoveryNotice(turnId, delta, checkpointIds)
1499
+ : undefined;
1500
+ if (recoveryNotice) {
1501
+ this.events.append({
1502
+ role: 'status',
1503
+ title: 'recovery',
1504
+ content: recoveryNotice,
1505
+ });
1506
+ }
1421
1507
  (0, session_storage_1.removeLastIncompleteAssistantMessage)(sessionId);
1422
1508
  }
1423
1509
  return;
@@ -1425,6 +1511,10 @@ class AgentChatController {
1425
1511
  assistantStream.closeSegment();
1426
1512
  if (sessionId) {
1427
1513
  const { profile, summary } = appendPostWorkspaceTrace(this.events, sessionId, turnId, this.runtime.cwd, preWorkspace, verificationResults);
1514
+ if (profile.changedFiles.length > 0 && profile.required) {
1515
+ this.events.setStatus((0, agent_status_1.verifyingStatus)(profile.profile));
1516
+ this.controllerOptions.onVerificationStateChange?.('running');
1517
+ }
1428
1518
  if (shouldRecordVerificationLoopStats(profile, summary)) {
1429
1519
  const stats = pendingCompleteStats ?? this.runtime.store.getSnapshot().lastLoopStats;
1430
1520
  if (stats) {
@@ -1432,6 +1522,8 @@ class AgentChatController {
1432
1522
  }
1433
1523
  }
1434
1524
  if ((0, verification_profile_1.shouldGateCompletion)(summary)) {
1525
+ this.events.setStatus((0, agent_status_1.verificationGateStatus)(summary.skippedReason ?? 'verification checks not run'));
1526
+ this.controllerOptions.onVerificationStateChange?.('gated');
1435
1527
  const notice = (0, verification_profile_1.formatVerificationGateNotice)(summary);
1436
1528
  this.events.append({
1437
1529
  role: 'status',
@@ -1453,6 +1545,36 @@ class AgentChatController {
1453
1545
  };
1454
1546
  }
1455
1547
  }
1548
+ else if (profile.changedFiles.length > 0 && profile.required) {
1549
+ this.controllerOptions.onVerificationStateChange?.('passed');
1550
+ }
1551
+ }
1552
+ if (pendingCompleteStats?.finishReason === 'budget_exceeded') {
1553
+ const stats = pendingCompleteStats;
1554
+ const lines = ['Loop budget reached — stopping this turn.'];
1555
+ if (stats.budgetExceededReason) {
1556
+ lines.push(`Reason: ${stats.budgetExceededReason}`);
1557
+ }
1558
+ const progressParts = [];
1559
+ if (typeof stats.loopBudgetMaxLlmRequests === 'number') {
1560
+ progressParts.push(`${stats.llmRequests ?? 0}/${stats.loopBudgetMaxLlmRequests} LLM requests`);
1561
+ }
1562
+ if (typeof stats.loopBudgetMaxToolCalls === 'number') {
1563
+ progressParts.push(`${stats.toolCalls ?? 0}/${stats.loopBudgetMaxToolCalls} tool calls`);
1564
+ }
1565
+ if (progressParts.length) {
1566
+ lines.push(`Progress: ${progressParts.join(', ')}`);
1567
+ }
1568
+ if (stats.continuationActions?.length) {
1569
+ lines.push(`Next: ${stats.continuationActions.join('; ')}`);
1570
+ }
1571
+ else if (stats.continuationHint) {
1572
+ lines.push(`Next: ${stats.continuationHint}`);
1573
+ }
1574
+ const notice = lines.join('\n');
1575
+ this.events.append({ role: 'status', title: 'budget', content: notice });
1576
+ finalContent = finalContent ? `${finalContent}\n\n${notice}` : notice;
1577
+ appendAssistantNotice(sessionMessagesToRecord, notice);
1456
1578
  }
1457
1579
  if (pendingCompleteStats) {
1458
1580
  this.setLoopStats(pendingCompleteStats);
@@ -1494,18 +1616,28 @@ class AgentChatController {
1494
1616
  this.events.setStatus('Interrupted.');
1495
1617
  removeTrailingUserMessage(this.runtime);
1496
1618
  if (sessionId) {
1497
- appendPostWorkspaceTrace(this.events, sessionId, turnId, this.runtime.cwd, preWorkspace, verificationResults);
1619
+ const { delta } = appendPostWorkspaceTrace(this.events, sessionId, turnId, this.runtime.cwd, preWorkspace, verificationResults);
1498
1620
  recordTraceEvent(this.events, sessionId, {
1499
1621
  turnId,
1500
1622
  type: 'aborted',
1501
1623
  note: 'abort_error',
1502
1624
  });
1625
+ const recoveryNotice = workspaceDeltaHasTurnChanges(delta)
1626
+ ? formatFailureRecoveryNotice(turnId, delta, checkpointIds)
1627
+ : undefined;
1628
+ if (recoveryNotice) {
1629
+ this.events.append({
1630
+ role: 'status',
1631
+ title: 'recovery',
1632
+ content: recoveryNotice,
1633
+ });
1634
+ }
1503
1635
  (0, session_storage_1.removeLastIncompleteAssistantMessage)(sessionId);
1504
1636
  }
1505
1637
  return;
1506
1638
  }
1507
1639
  assistantStream.discardSegment();
1508
- this.events.append({ role: 'error', content: formatChatError(error) });
1640
+ this.events.append({ role: 'error', content: formatChatError(error), errorLayer: errorLayerForChatError(error) });
1509
1641
  this.events.setStatus('Turn failed. Ready for the next input.');
1510
1642
  const failedStats = error instanceof framework_1.QueryLoopError
1511
1643
  ? error.stats