@roackb2/heddle 0.0.36 → 0.0.38

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 (245) hide show
  1. package/README.md +2 -0
  2. package/dist/examples/repo-investigator.js +1 -2
  3. package/dist/examples/repo-investigator.js.map +1 -1
  4. package/dist/src/cli/ask.d.ts.map +1 -1
  5. package/dist/src/cli/ask.js +11 -0
  6. package/dist/src/cli/ask.js.map +1 -1
  7. package/dist/src/cli/chat/App.d.ts.map +1 -1
  8. package/dist/src/cli/chat/App.js +123 -93
  9. package/dist/src/cli/chat/App.js.map +1 -1
  10. package/dist/src/cli/chat/components/ModelPickerPanel.d.ts +2 -1
  11. package/dist/src/cli/chat/components/ModelPickerPanel.d.ts.map +1 -1
  12. package/dist/src/cli/chat/components/ModelPickerPanel.js +8 -4
  13. package/dist/src/cli/chat/components/ModelPickerPanel.js.map +1 -1
  14. package/dist/src/cli/chat/components/PromptInput.d.ts +5 -0
  15. package/dist/src/cli/chat/components/PromptInput.d.ts.map +1 -1
  16. package/dist/src/cli/chat/components/PromptInput.js +131 -85
  17. package/dist/src/cli/chat/components/PromptInput.js.map +1 -1
  18. package/dist/src/cli/chat/debug/tui-debug-snapshot.d.ts +2 -1
  19. package/dist/src/cli/chat/debug/tui-debug-snapshot.d.ts.map +1 -1
  20. package/dist/src/cli/chat/debug/tui-debug-snapshot.js +14 -1
  21. package/dist/src/cli/chat/debug/tui-debug-snapshot.js.map +1 -1
  22. package/dist/src/cli/chat/hooks/tui-agent-turn-lifecycle.d.ts +6 -0
  23. package/dist/src/cli/chat/hooks/tui-agent-turn-lifecycle.d.ts.map +1 -0
  24. package/dist/src/cli/chat/hooks/tui-agent-turn-lifecycle.js +38 -0
  25. package/dist/src/cli/chat/hooks/tui-agent-turn-lifecycle.js.map +1 -0
  26. package/dist/src/cli/chat/hooks/tui-agent-turn-result.d.ts +48 -0
  27. package/dist/src/cli/chat/hooks/tui-agent-turn-result.d.ts.map +1 -0
  28. package/dist/src/cli/chat/hooks/tui-agent-turn-result.js +171 -0
  29. package/dist/src/cli/chat/hooks/tui-agent-turn-result.js.map +1 -0
  30. package/dist/src/cli/chat/hooks/tui-compaction-status.d.ts +24 -0
  31. package/dist/src/cli/chat/hooks/tui-compaction-status.d.ts.map +1 -0
  32. package/dist/src/cli/chat/hooks/tui-compaction-status.js +65 -0
  33. package/dist/src/cli/chat/hooks/tui-compaction-status.js.map +1 -0
  34. package/dist/src/cli/chat/hooks/tui-direct-shell-result.d.ts +28 -0
  35. package/dist/src/cli/chat/hooks/tui-direct-shell-result.d.ts.map +1 -0
  36. package/dist/src/cli/chat/hooks/tui-direct-shell-result.js +41 -0
  37. package/dist/src/cli/chat/hooks/tui-direct-shell-result.js.map +1 -0
  38. package/dist/src/cli/chat/hooks/tui-direct-shell.d.ts +22 -0
  39. package/dist/src/cli/chat/hooks/tui-direct-shell.d.ts.map +1 -0
  40. package/dist/src/cli/chat/hooks/tui-direct-shell.js +127 -0
  41. package/dist/src/cli/chat/hooks/tui-direct-shell.js.map +1 -0
  42. package/dist/src/cli/chat/hooks/tui-drift-observer.d.ts +13 -0
  43. package/dist/src/cli/chat/hooks/tui-drift-observer.d.ts.map +1 -0
  44. package/dist/src/cli/chat/hooks/tui-drift-observer.js +36 -0
  45. package/dist/src/cli/chat/hooks/tui-drift-observer.js.map +1 -0
  46. package/dist/src/cli/chat/hooks/tui-ordinary-turn.d.ts +31 -0
  47. package/dist/src/cli/chat/hooks/tui-ordinary-turn.d.ts.map +1 -0
  48. package/dist/src/cli/chat/hooks/tui-ordinary-turn.js +82 -0
  49. package/dist/src/cli/chat/hooks/tui-ordinary-turn.js.map +1 -0
  50. package/dist/src/cli/chat/hooks/tui-run-loop-events.d.ts +23 -0
  51. package/dist/src/cli/chat/hooks/tui-run-loop-events.d.ts.map +1 -0
  52. package/dist/src/cli/chat/hooks/tui-run-loop-events.js +74 -0
  53. package/dist/src/cli/chat/hooks/tui-run-loop-events.js.map +1 -0
  54. package/dist/src/cli/chat/hooks/tui-tool-approval.d.ts +8 -0
  55. package/dist/src/cli/chat/hooks/tui-tool-approval.d.ts.map +1 -0
  56. package/dist/src/cli/chat/hooks/tui-tool-approval.js +37 -0
  57. package/dist/src/cli/chat/hooks/tui-tool-approval.js.map +1 -0
  58. package/dist/src/cli/chat/hooks/useAgentRun.d.ts +1 -1
  59. package/dist/src/cli/chat/hooks/useAgentRun.d.ts.map +1 -1
  60. package/dist/src/cli/chat/hooks/useAgentRun.js +59 -551
  61. package/dist/src/cli/chat/hooks/useAgentRun.js.map +1 -1
  62. package/dist/src/cli/chat/hooks/useApprovalFlow.d.ts.map +1 -1
  63. package/dist/src/cli/chat/hooks/useApprovalFlow.js +3 -3
  64. package/dist/src/cli/chat/hooks/useApprovalFlow.js.map +1 -1
  65. package/dist/src/cli/chat/hooks/useChatPickers.d.ts +6 -3
  66. package/dist/src/cli/chat/hooks/useChatPickers.d.ts.map +1 -1
  67. package/dist/src/cli/chat/hooks/useChatPickers.js +17 -4
  68. package/dist/src/cli/chat/hooks/useChatPickers.js.map +1 -1
  69. package/dist/src/cli/chat/hooks/useChatSessions.d.ts +1 -1
  70. package/dist/src/cli/chat/hooks/useChatSessions.d.ts.map +1 -1
  71. package/dist/src/cli/chat/hooks/useChatSessions.js +23 -20
  72. package/dist/src/cli/chat/hooks/useChatSessions.js.map +1 -1
  73. package/dist/src/cli/chat/hooks/useChatStatusSummary.d.ts +58 -0
  74. package/dist/src/cli/chat/hooks/useChatStatusSummary.d.ts.map +1 -0
  75. package/dist/src/cli/chat/hooks/useChatStatusSummary.js +85 -0
  76. package/dist/src/cli/chat/hooks/useChatStatusSummary.js.map +1 -0
  77. package/dist/src/cli/chat/hooks/usePromptSubmission.d.ts +2 -1
  78. package/dist/src/cli/chat/hooks/usePromptSubmission.d.ts.map +1 -1
  79. package/dist/src/cli/chat/hooks/usePromptSubmission.js +5 -1
  80. package/dist/src/cli/chat/hooks/usePromptSubmission.js.map +1 -1
  81. package/dist/src/cli/chat/state/local-commands.d.ts +2 -0
  82. package/dist/src/cli/chat/state/local-commands.d.ts.map +1 -1
  83. package/dist/src/cli/chat/state/local-commands.js +17 -3
  84. package/dist/src/cli/chat/state/local-commands.js.map +1 -1
  85. package/dist/src/cli/chat/submit.d.ts.map +1 -1
  86. package/dist/src/cli/chat/submit.js +13 -1
  87. package/dist/src/cli/chat/submit.js.map +1 -1
  88. package/dist/src/cli/chat/utils/format.d.ts.map +1 -1
  89. package/dist/src/cli/chat/utils/format.js +21 -0
  90. package/dist/src/cli/chat/utils/format.js.map +1 -1
  91. package/dist/src/cli/chat/utils/runtime.d.ts.map +1 -1
  92. package/dist/src/cli/chat/utils/runtime.js +5 -3
  93. package/dist/src/cli/chat/utils/runtime.js.map +1 -1
  94. package/dist/src/cli/eval/index.d.ts +24 -0
  95. package/dist/src/cli/eval/index.d.ts.map +1 -0
  96. package/dist/src/cli/eval/index.js +232 -0
  97. package/dist/src/cli/eval/index.js.map +1 -0
  98. package/dist/src/cli/main.js +25 -3
  99. package/dist/src/cli/main.js.map +1 -1
  100. package/dist/src/cli/remote/control-plane-client.d.ts +5 -1
  101. package/dist/src/cli/remote/control-plane-client.d.ts.map +1 -1
  102. package/dist/src/core/agent/mutation-tracking.d.ts +0 -7
  103. package/dist/src/core/agent/mutation-tracking.d.ts.map +1 -1
  104. package/dist/src/core/agent/mutation-tracking.js +5 -63
  105. package/dist/src/core/agent/mutation-tracking.js.map +1 -1
  106. package/dist/src/core/agent/post-mutation.d.ts +2 -2
  107. package/dist/src/core/agent/post-mutation.d.ts.map +1 -1
  108. package/dist/src/core/agent/post-mutation.js +5 -20
  109. package/dist/src/core/agent/post-mutation.js.map +1 -1
  110. package/dist/src/core/agent/progress-reminders.d.ts +1 -4
  111. package/dist/src/core/agent/progress-reminders.d.ts.map +1 -1
  112. package/dist/src/core/agent/progress-reminders.js +4 -56
  113. package/dist/src/core/agent/progress-reminders.js.map +1 -1
  114. package/dist/src/core/agent/run-agent.d.ts.map +1 -1
  115. package/dist/src/core/agent/run-agent.js +3 -103
  116. package/dist/src/core/agent/run-agent.js.map +1 -1
  117. package/dist/src/core/agent/tool-dispatch.js +3 -2
  118. package/dist/src/core/agent/tool-dispatch.js.map +1 -1
  119. package/dist/src/core/chat/ordinary-turn.d.ts +34 -0
  120. package/dist/src/core/chat/ordinary-turn.d.ts.map +1 -0
  121. package/dist/src/core/chat/ordinary-turn.js +274 -0
  122. package/dist/src/core/chat/ordinary-turn.js.map +1 -0
  123. package/dist/src/core/chat/session-submit.d.ts +4 -4
  124. package/dist/src/core/chat/session-submit.d.ts.map +1 -1
  125. package/dist/src/core/chat/session-submit.js +22 -282
  126. package/dist/src/core/chat/session-submit.js.map +1 -1
  127. package/dist/src/core/chat/session-title.d.ts +15 -0
  128. package/dist/src/core/chat/session-title.d.ts.map +1 -0
  129. package/dist/src/core/chat/session-title.js +17 -0
  130. package/dist/src/core/chat/session-title.js.map +1 -0
  131. package/dist/src/core/chat/session-turn-preflight.d.ts +37 -0
  132. package/dist/src/core/chat/session-turn-preflight.d.ts.map +1 -0
  133. package/dist/src/core/chat/session-turn-preflight.js +43 -0
  134. package/dist/src/core/chat/session-turn-preflight.js.map +1 -0
  135. package/dist/src/core/chat/session-turn-result.d.ts +36 -0
  136. package/dist/src/core/chat/session-turn-result.d.ts.map +1 -0
  137. package/dist/src/core/chat/session-turn-result.js +60 -0
  138. package/dist/src/core/chat/session-turn-result.js.map +1 -0
  139. package/dist/src/core/chat/tool-approval-host.d.ts +22 -0
  140. package/dist/src/core/chat/tool-approval-host.d.ts.map +1 -0
  141. package/dist/src/core/chat/tool-approval-host.js +14 -0
  142. package/dist/src/core/chat/tool-approval-host.js.map +1 -0
  143. package/dist/src/core/chat/turn-host.d.ts +25 -0
  144. package/dist/src/core/chat/turn-host.d.ts.map +1 -0
  145. package/dist/src/core/chat/turn-host.js +2 -0
  146. package/dist/src/core/chat/turn-host.js.map +1 -0
  147. package/dist/src/core/chat/types.d.ts +1 -0
  148. package/dist/src/core/chat/types.d.ts.map +1 -1
  149. package/dist/src/core/config.d.ts +1 -1
  150. package/dist/src/core/config.d.ts.map +1 -1
  151. package/dist/src/core/config.js +1 -1
  152. package/dist/src/core/config.js.map +1 -1
  153. package/dist/src/core/eval/agent-runner.d.ts +24 -0
  154. package/dist/src/core/eval/agent-runner.d.ts.map +1 -0
  155. package/dist/src/core/eval/agent-runner.js +151 -0
  156. package/dist/src/core/eval/agent-runner.js.map +1 -0
  157. package/dist/src/core/eval/case-loader.d.ts +7 -0
  158. package/dist/src/core/eval/case-loader.d.ts.map +1 -0
  159. package/dist/src/core/eval/case-loader.js +34 -0
  160. package/dist/src/core/eval/case-loader.js.map +1 -0
  161. package/dist/src/core/eval/check-runner.d.ts +8 -0
  162. package/dist/src/core/eval/check-runner.d.ts.map +1 -0
  163. package/dist/src/core/eval/check-runner.js +33 -0
  164. package/dist/src/core/eval/check-runner.js.map +1 -0
  165. package/dist/src/core/eval/cleanup.d.ts +20 -0
  166. package/dist/src/core/eval/cleanup.d.ts.map +1 -0
  167. package/dist/src/core/eval/cleanup.js +42 -0
  168. package/dist/src/core/eval/cleanup.js.map +1 -0
  169. package/dist/src/core/eval/git-artifacts.d.ts +26 -0
  170. package/dist/src/core/eval/git-artifacts.d.ts.map +1 -0
  171. package/dist/src/core/eval/git-artifacts.js +211 -0
  172. package/dist/src/core/eval/git-artifacts.js.map +1 -0
  173. package/dist/src/core/eval/process.d.ts +22 -0
  174. package/dist/src/core/eval/process.d.ts.map +1 -0
  175. package/dist/src/core/eval/process.js +65 -0
  176. package/dist/src/core/eval/process.js.map +1 -0
  177. package/dist/src/core/eval/progress.d.ts +28 -0
  178. package/dist/src/core/eval/progress.d.ts.map +1 -0
  179. package/dist/src/core/eval/progress.js +94 -0
  180. package/dist/src/core/eval/progress.js.map +1 -0
  181. package/dist/src/core/eval/report-writer.d.ts +7 -0
  182. package/dist/src/core/eval/report-writer.d.ts.map +1 -0
  183. package/dist/src/core/eval/report-writer.js +159 -0
  184. package/dist/src/core/eval/report-writer.js.map +1 -0
  185. package/dist/src/core/eval/schema.d.ts +206 -0
  186. package/dist/src/core/eval/schema.d.ts.map +1 -0
  187. package/dist/src/core/eval/schema.js +104 -0
  188. package/dist/src/core/eval/schema.js.map +1 -0
  189. package/dist/src/core/eval/trace-analyzer.d.ts +6 -0
  190. package/dist/src/core/eval/trace-analyzer.d.ts.map +1 -0
  191. package/dist/src/core/eval/trace-analyzer.js +106 -0
  192. package/dist/src/core/eval/trace-analyzer.js.map +1 -0
  193. package/dist/src/core/eval/workspace-fixture.d.ts +14 -0
  194. package/dist/src/core/eval/workspace-fixture.d.ts.map +1 -0
  195. package/dist/src/core/eval/workspace-fixture.js +235 -0
  196. package/dist/src/core/eval/workspace-fixture.js.map +1 -0
  197. package/dist/src/core/llm/model-policy.d.ts +26 -0
  198. package/dist/src/core/llm/model-policy.d.ts.map +1 -1
  199. package/dist/src/core/llm/model-policy.js +47 -0
  200. package/dist/src/core/llm/model-policy.js.map +1 -1
  201. package/dist/src/core/memory/domain-prompt.d.ts.map +1 -1
  202. package/dist/src/core/memory/domain-prompt.js +48 -40
  203. package/dist/src/core/memory/domain-prompt.js.map +1 -1
  204. package/dist/src/core/prompts/system-prompt.d.ts +1 -1
  205. package/dist/src/core/prompts/system-prompt.d.ts.map +1 -1
  206. package/dist/src/core/prompts/system-prompt.js +19 -100
  207. package/dist/src/core/prompts/system-prompt.js.map +1 -1
  208. package/dist/src/core/runtime/agent-loop.d.ts.map +1 -1
  209. package/dist/src/core/runtime/agent-loop.js +9 -3
  210. package/dist/src/core/runtime/agent-loop.js.map +1 -1
  211. package/dist/src/core/runtime/default-tools.d.ts.map +1 -1
  212. package/dist/src/core/runtime/default-tools.js +1 -2
  213. package/dist/src/core/runtime/default-tools.js.map +1 -1
  214. package/dist/src/core/tools/file-edit-core.d.ts.map +1 -1
  215. package/dist/src/core/tools/file-edit-core.js +21 -2
  216. package/dist/src/core/tools/file-edit-core.js.map +1 -1
  217. package/dist/src/index.d.ts +0 -1
  218. package/dist/src/index.d.ts.map +1 -1
  219. package/dist/src/index.js +0 -1
  220. package/dist/src/index.js.map +1 -1
  221. package/dist/src/server/features/control-plane/router.d.ts +5 -1
  222. package/dist/src/server/features/control-plane/router.d.ts.map +1 -1
  223. package/dist/src/server/features/control-plane/router.js +16 -2
  224. package/dist/src/server/features/control-plane/router.js.map +1 -1
  225. package/dist/src/server/features/control-plane/services/chat-session-events.d.ts +26 -0
  226. package/dist/src/server/features/control-plane/services/chat-session-events.d.ts.map +1 -0
  227. package/dist/src/server/features/control-plane/services/chat-session-events.js +61 -0
  228. package/dist/src/server/features/control-plane/services/chat-session-events.js.map +1 -0
  229. package/dist/src/server/features/control-plane/services/chat-sessions.d.ts +2 -1
  230. package/dist/src/server/features/control-plane/services/chat-sessions.d.ts.map +1 -1
  231. package/dist/src/server/features/control-plane/services/chat-sessions.js +30 -42
  232. package/dist/src/server/features/control-plane/services/chat-sessions.js.map +1 -1
  233. package/dist/src/server/router.d.ts +5 -1
  234. package/dist/src/server/router.d.ts.map +1 -1
  235. package/dist/src/web/assets/{MonacoDiffViewer-DM8Cy5Xf.js → MonacoDiffViewer-DP7GeCEC.js} +1 -1
  236. package/dist/src/web/assets/index-CYd4sslC.css +2 -0
  237. package/dist/src/web/assets/index-PUxjg447.js +56 -0
  238. package/dist/src/web/index.html +2 -2
  239. package/package.json +7 -2
  240. package/dist/src/core/tools/report-state.d.ts +0 -3
  241. package/dist/src/core/tools/report-state.d.ts.map +0 -1
  242. package/dist/src/core/tools/report-state.js +0 -63
  243. package/dist/src/core/tools/report-state.js.map +0 -1
  244. package/dist/src/web/assets/index-BEeN-RT5.css +0 -2
  245. package/dist/src/web/assets/index-BKDg9H_-.js +0 -56
@@ -1,18 +1,16 @@
1
1
  import { useMemo } from 'react';
2
- import { readFileSync, writeFileSync } from 'node:fs';
3
- import { createCyberLoopKinematicsObserver, createLogger, createLlmAdapter, createDefaultAgentTools, runAgentLoop, } from '../../../index.js';
4
- import { runMaintenanceForRecordedCandidates } from '../../../core/memory/maintenance-integration.js';
5
- import { DEFAULT_INSPECT_RULES, DEFAULT_MUTATE_RULES, runShellCommand } from '../../../core/tools/run-shell.js';
6
- import { previewEditFileInput } from '../../../core/tools/edit-file.js';
7
- import { appendDirectShellHistory, buildConversationMessages, countAssistantSteps, formatChatFailureMessage, formatEditPreviewHistoryMessage, formatPlanHistoryMessage, formatDirectShellResponse, shouldFallbackToMutate, summarizeTrace, summarizeToolCall, toLiveEvent, } from '../utils/format.js';
8
- import { saveTrace } from '../utils/runtime.js';
2
+ import { createLogger, createLlmAdapter, createDefaultAgentTools, } from '../../../index.js';
9
3
  import { formatMissingProviderCredentialMessage, hasProviderCredentialForModel, resolveApiKeyForModel, resolveProviderCredentialSourceForModel, } from '../../../core/runtime/api-keys.js';
10
- import { acquireSessionLease, getSessionLeaseConflict, releaseSessionLease } from '../../../core/chat/session-lease.js';
11
- import { createProjectApprovalRuleForCall, describeProjectApprovalRule } from '../state/approval-rules.js';
12
- import { buildCompactionRunningContext, compactChatHistoryWithArchive, estimateChatHistoryTokens } from '../state/compaction.js';
13
- import { isGenericSessionName, readChatSession, touchSession } from '../state/storage.js';
4
+ import { releaseSessionLease } from '../../../core/chat/session-lease.js';
5
+ import { generateSessionTitle } from '../../../core/chat/session-title.js';
6
+ import { isGenericSessionName } from '../state/storage.js';
14
7
  import { normalizeSessionTitle } from '../utils/format.js';
15
8
  import { useProjectApprovals } from './useProjectApprovals.js';
9
+ import { beginTuiAgentTurn, finishTuiAgentTurn, } from './tui-agent-turn-lifecycle.js';
10
+ import { createTuiChatDriftObserver } from './tui-drift-observer.js';
11
+ import { executeTuiDirectShell } from './tui-direct-shell.js';
12
+ import { applyTuiAgentTurnFailure } from './tui-agent-turn-result.js';
13
+ import { executeTuiOrdinaryTurn } from './tui-ordinary-turn.js';
16
14
  const PLAN_ITEM_STATUSES = new Set(['pending', 'in_progress', 'completed']);
17
15
  export function useAgentRun(args) {
18
16
  const { runtime, activeModel, sessionTitleModel, activeSessionId, sessions, state, updateSessionById, updateActiveSession } = args;
@@ -34,7 +32,15 @@ export function useAgentRun(args) {
34
32
  searchIgnoreDirs: runtime.searchIgnoreDirs,
35
33
  includePlanTool: true,
36
34
  });
37
- }, [activeApiKey, activeCredentialSource, activeModel, runtime.memoryDir, runtime.searchIgnoreDirs, runtime.workspaceRoot]);
35
+ }, [
36
+ activeApiKey,
37
+ activeCredentialSource,
38
+ activeModel,
39
+ runtime.credentialStorePath,
40
+ runtime.memoryDir,
41
+ runtime.searchIgnoreDirs,
42
+ runtime.workspaceRoot,
43
+ ]);
38
44
  const logger = useMemo(() => createLogger({
39
45
  pretty: false,
40
46
  level: 'debug',
@@ -48,17 +54,12 @@ export function useAgentRun(args) {
48
54
  }
49
55
  void (async () => {
50
56
  try {
51
- const result = await titleLlm.chat([
52
- {
53
- role: 'system',
54
- content: 'You name terminal chat sessions. Return only a short 3 to 6 word title in plain text. No quotes, no punctuation, no prefix.',
55
- },
56
- {
57
- role: 'user',
58
- content: `User prompt:\n${prompt}\n\nAssistant or tool summary:\n${responseText}\n\nCreate a concise session title.`,
59
- },
60
- ], []);
61
- const title = normalizeSessionTitle(result.content);
57
+ const title = await generateSessionTitle({
58
+ llm: titleLlm,
59
+ prompt,
60
+ responseText,
61
+ normalize: normalizeSessionTitle,
62
+ });
62
63
  if (!title) {
63
64
  return;
64
65
  }
@@ -110,7 +111,7 @@ export function useAgentRun(args) {
110
111
  };
111
112
  }
112
113
  export async function executeAgentTurn(args) {
113
- const { prompt, displayText, sessionId, sessionHistory, runtime, llm, tools, logger, state, updateSessionById, referenceAssistantText, maybeAutoNameSession, isProjectApproved, rememberProjectApproval, drift, } = args;
114
+ const { prompt, displayText, sessionId, sessionHistory, runtime, llm, logger, state, updateSessionById, referenceAssistantText, maybeAutoNameSession, isProjectApproved, rememberProjectApproval, drift, } = args;
114
115
  if (!prompt || state.isRunning) {
115
116
  return undefined;
116
117
  }
@@ -119,96 +120,16 @@ export async function executeAgentTurn(args) {
119
120
  state.setStatus('Error');
120
121
  return undefined;
121
122
  }
122
- state.setError(undefined);
123
- state.setIsRunning(true);
124
- state.setStatus('Running');
125
- state.interruptRequestedRef.current = false;
126
- state.setInterruptRequested(false);
127
- state.abortControllerRef.current = new AbortController();
128
- state.setLiveEvents([]);
129
- state.setCurrentEditPreview(undefined);
130
- state.setCurrentPlan(undefined);
131
- state.setCurrentAssistantText(undefined);
123
+ const turnAbortController = beginTuiAgentTurn(state);
132
124
  updateSessionById(sessionId, (session) => ({ ...session, lastContinuePrompt: prompt }));
133
- const appendedEditPreviewIds = new Set();
134
- const appendedPlanSteps = new Set();
135
- const streamingBuffers = new Map();
136
125
  drift?.onRunStart?.();
137
- let historyForRun = sessionHistory;
138
126
  const leaseOwner = {
139
127
  ownerKind: 'tui',
140
128
  ownerId: `tui-${process.pid}`,
141
129
  clientLabel: 'terminal chat',
142
130
  };
143
- const persistedSession = readChatSession(runtime.sessionCatalogFile, sessionId, true);
144
- const leaseConflict = persistedSession ? getSessionLeaseConflict(persistedSession, leaseOwner) : undefined;
145
- if (leaseConflict) {
146
- state.setError(leaseConflict);
147
- state.setStatus('Blocked');
148
- state.setIsRunning(false);
149
- state.interruptRequestedRef.current = false;
150
- state.setInterruptRequested(false);
151
- state.abortControllerRef.current = undefined;
152
- updateSessionById(sessionId, (sessionToUpdate) => ({
153
- ...sessionToUpdate,
154
- lastContinuePrompt: undefined,
155
- messages: [
156
- ...sessionToUpdate.messages,
157
- { id: state.nextLocalId(), role: 'assistant', text: leaseConflict },
158
- ],
159
- }));
160
- return undefined;
161
- }
162
- if (persistedSession) {
163
- const leasedSession = touchSession(acquireSessionLease(persistedSession, leaseOwner));
164
- historyForRun = leasedSession.history;
165
- updateSessionById(sessionId, () => leasedSession);
166
- }
167
- const toolNames = tools.map((tool) => tool.name);
168
- const emitCompactionStatus = (event, sourceHistory) => {
169
- if (event.status === 'running') {
170
- state.setStatus('Compacting');
171
- state.setLiveEvents((current) => [...current, { id: state.nextLocalId(), text: 'Compacting earlier conversation history…' }].slice(-8));
172
- updateSessionById(sessionId, (sessionToUpdate) => ({
173
- ...sessionToUpdate,
174
- history: sourceHistory,
175
- context: buildCompactionRunningContext({
176
- history: sourceHistory,
177
- previous: sessionToUpdate.context,
178
- archiveCount: sessionToUpdate.archives?.length,
179
- currentSummaryPath: sessionToUpdate.context?.currentSummaryPath,
180
- lastArchivePath: event.archivePath,
181
- }),
182
- }));
183
- return;
184
- }
185
- if (event.status === 'failed') {
186
- state.setLiveEvents((current) => [...current, { id: state.nextLocalId(), text: `Compaction failed: ${event.error ?? 'unknown error'}` }].slice(-8));
187
- return;
188
- }
189
- state.setLiveEvents((current) => [...current, { id: state.nextLocalId(), text: 'Compaction finished.' }].slice(-8));
190
- };
191
- const preflightCompacted = await compactChatHistoryWithArchive({
192
- history: historyForRun,
193
- model: llm.info?.model ?? runtime.model,
194
- sessionId,
195
- stateRoot: runtime.stateRoot,
196
- systemContext: runtime.systemContext,
197
- toolNames,
198
- goal: prompt,
199
- summarizer: { credentialSource: runtime.providerCredentialSource },
200
- onStatusChange: (event) => emitCompactionStatus(event, historyForRun),
201
- });
202
- historyForRun = preflightCompacted.history;
203
- updateSessionById(sessionId, (sessionToUpdate) => ({
204
- ...sessionToUpdate,
205
- history: preflightCompacted.history,
206
- context: preflightCompacted.context,
207
- archives: preflightCompacted.archives,
208
- messages: buildConversationMessages(preflightCompacted.history),
209
- }));
210
131
  state.setStatus('Running');
211
- const driftObserver = await createChatDriftObserver({
132
+ const driftObserver = await createTuiChatDriftObserver({
212
133
  prompt,
213
134
  referenceAssistantText,
214
135
  llm,
@@ -216,247 +137,40 @@ export async function executeAgentTurn(args) {
216
137
  logger,
217
138
  options: drift,
218
139
  });
219
- if (displayText) {
220
- updateSessionById(sessionId, (session) => ({
221
- ...session,
222
- messages: [...session.messages, { id: state.nextLocalId(), role: 'user', text: displayText }],
223
- }));
224
- }
225
140
  try {
226
- const result = await runAgentLoop({
227
- goal: prompt,
228
- model: llm.info?.model ?? runtime.model,
229
- workspaceRoot: runtime.workspaceRoot,
230
- memoryDir: runtime.memoryDir,
231
- searchIgnoreDirs: runtime.searchIgnoreDirs,
232
- llm,
233
- tools,
234
- includeDefaultTools: false,
235
- maxSteps: runtime.maxSteps,
236
- logger,
237
- history: historyForRun,
238
- systemContext: runtime.systemContext,
239
- onAssistantStream: (update) => {
240
- streamingBuffers.set(update.step, update.text);
241
- state.setCurrentAssistantText(update.text || undefined);
242
- if (update.done) {
243
- streamingBuffers.delete(update.step);
244
- }
245
- },
246
- onTraceEvent: (event) => {
247
- if (event.type === 'assistant.turn' && event.content.trim()) {
248
- streamingBuffers.delete(event.step);
249
- state.setCurrentAssistantText(event.content);
250
- }
251
- if (event.type === 'tool.call' && event.call.tool === 'edit_file') {
252
- void previewEditFileInput(event.call.input).then((preview) => {
253
- if (!preview || appendedEditPreviewIds.has(event.call.id)) {
254
- return;
255
- }
256
- appendedEditPreviewIds.add(event.call.id);
257
- updateSessionById(sessionId, (session) => ({
258
- ...session,
259
- messages: [
260
- ...session.messages,
261
- {
262
- id: state.nextLocalId(),
263
- role: 'assistant',
264
- text: formatEditPreviewHistoryMessage(preview),
265
- },
266
- ],
267
- }));
268
- });
269
- }
270
- if (event.type === 'tool.result') {
271
- if (event.tool === 'update_plan') {
272
- state.setCurrentPlan(parsePlanStateFromToolResult(event.result.output));
273
- if (!appendedPlanSteps.has(event.step)) {
274
- const renderedPlan = formatPlanHistoryMessage(event.result.output);
275
- if (renderedPlan) {
276
- appendedPlanSteps.add(event.step);
277
- updateSessionById(sessionId, (session) => ({
278
- ...session,
279
- messages: [
280
- ...session.messages,
281
- {
282
- id: state.nextLocalId(),
283
- role: 'assistant',
284
- text: renderedPlan,
285
- },
286
- ],
287
- }));
288
- }
289
- }
290
- }
291
- }
292
- const next = toLiveEvent(event);
293
- if (!next) {
294
- return;
295
- }
296
- state.setLiveEvents((current) => {
297
- const previous = current[current.length - 1];
298
- if (previous?.text === next) {
299
- return current;
300
- }
301
- return [...current, { id: state.nextLocalId(), text: next }].slice(-8);
302
- });
303
- },
304
- onEvent: (event) => {
305
- driftObserver?.observer.handleEvent(event);
306
- },
307
- approveToolCall: async (call, tool) => {
308
- if (isProjectApproved(call)) {
309
- return {
310
- approved: true,
311
- reason: 'Approved by saved project rule',
312
- };
313
- }
314
- const editPreview = call.tool === 'edit_file' ? await previewEditFileInput(call.input) : undefined;
315
- return new Promise((resolve) => {
316
- const rememberedRule = createProjectApprovalRuleForCall(call);
317
- state.setPendingApproval({
318
- call,
319
- tool,
320
- editPreview,
321
- rememberForProject: () => rememberProjectApproval(call),
322
- rememberLabel: rememberedRule ? describeProjectApprovalRule(rememberedRule) : undefined,
323
- resolve,
324
- });
325
- });
326
- },
327
- shouldStop: () => state.interruptRequestedRef.current,
328
- abortSignal: state.abortControllerRef.current.signal,
329
- });
330
- await driftObserver?.observer.flush();
331
- if (driftObserver?.annotations.length) {
332
- result.trace.push(...driftObserver.annotations);
333
- }
334
- const compacted = await compactChatHistoryWithArchive({
335
- history: result.transcript,
336
- model: llm.info?.model ?? runtime.model,
337
- sessionId,
338
- stateRoot: runtime.stateRoot,
339
- usage: result.usage,
340
- systemContext: runtime.systemContext,
341
- toolNames,
342
- goal: prompt,
343
- summarizer: { credentialSource: runtime.providerCredentialSource },
344
- onStatusChange: (event) => emitCompactionStatus(event, result.transcript),
345
- });
346
- updateSessionById(sessionId, (sessionToUpdate) => ({
347
- ...sessionToUpdate,
348
- history: compacted.history,
349
- context: compacted.context,
350
- archives: compacted.archives,
351
- }));
352
- const traceFile = saveTrace(runtime.traceDir, result.trace);
353
- const nextTurn = {
354
- id: state.nextLocalId(),
141
+ const result = await executeTuiOrdinaryTurn({
355
142
  prompt,
356
- outcome: result.outcome,
357
- summary: result.summary,
358
- steps: countAssistantSteps(result.trace),
359
- traceFile,
360
- events: summarizeTrace(result.trace),
361
- };
362
- updateSessionById(sessionId, (sessionToUpdate) => ({
363
- ...sessionToUpdate,
364
- turns: [...sessionToUpdate.turns, nextTurn].slice(-8),
365
- }));
366
- const formattedSummary = result.outcome === 'error' ?
367
- formatChatFailureMessage(result.summary, {
368
- model: llm.info?.model ?? runtime.model,
369
- estimatedHistoryTokens: estimateChatHistoryTokens(historyForRun),
370
- })
371
- : result.summary;
372
- state.setCurrentAssistantText(undefined);
373
- updateSessionById(sessionId, (sessionToUpdate) => ({
374
- ...sessionToUpdate,
375
- messages: [
376
- ...sessionToUpdate.messages,
377
- {
378
- id: state.nextLocalId(),
379
- role: 'assistant',
380
- text: result.outcome === 'done' ? formattedSummary : `Run stopped: ${formattedSummary}`,
381
- },
382
- ],
383
- }));
384
- const assistantText = formattedSummary;
385
- maybeAutoNameSession(sessionId, prompt, assistantText);
386
- if (result.outcome === 'error') {
387
- state.setError(formattedSummary);
388
- }
389
- state.setStatus(result.outcome === 'done' ? 'Idle' : `Stopped: ${result.outcome}`);
390
- scheduleBackgroundMemoryMaintenance({
391
- runtime,
392
- llm,
143
+ displayText,
393
144
  sessionId,
394
- trace: result.trace,
395
- traceFile,
145
+ runtime,
146
+ state,
396
147
  updateSessionById,
397
- nextLocalId: state.nextLocalId,
398
- setLiveEvents: state.setLiveEvents,
399
- setIsMemoryUpdating: state.setIsMemoryUpdating,
148
+ parsePlanState: parsePlanStateFromToolResult,
149
+ maybeAutoNameSession,
150
+ isProjectApproved,
151
+ rememberProjectApproval,
152
+ driftObserver,
153
+ turnAbortSignal: turnAbortController.signal,
154
+ leaseOwner,
400
155
  });
156
+ await driftObserver?.observer.flush();
401
157
  return result;
402
158
  }
403
159
  catch (runError) {
404
160
  await driftObserver?.observer.flush();
405
- const message = runError instanceof Error ? runError.message : String(runError);
406
- const formattedMessage = formatChatFailureMessage(message, {
161
+ await applyTuiAgentTurnFailure({
162
+ error: runError,
163
+ promptHistory: sessionHistory,
407
164
  model: llm.info?.model ?? runtime.model,
408
- estimatedHistoryTokens: estimateChatHistoryTokens(historyForRun),
165
+ state,
166
+ sessionId,
167
+ updateSessionById,
409
168
  });
410
- state.setError(formattedMessage);
411
- state.setStatus('Error');
412
- updateSessionById(sessionId, (sessionToUpdate) => ({
413
- ...sessionToUpdate,
414
- messages: [
415
- ...sessionToUpdate.messages,
416
- { id: state.nextLocalId(), role: 'assistant', text: `Run failed before a final answer: ${formattedMessage}` },
417
- ],
418
- }));
419
169
  return undefined;
420
170
  }
421
171
  finally {
422
172
  updateSessionById(sessionId, (sessionToUpdate) => releaseSessionLease(sessionToUpdate, leaseOwner));
423
- state.setIsRunning(false);
424
- state.interruptRequestedRef.current = false;
425
- state.setInterruptRequested(false);
426
- state.abortControllerRef.current = undefined;
427
- }
428
- }
429
- async function createChatDriftObserver(args) {
430
- const { prompt, referenceAssistantText, llm, runtime, logger, options } = args;
431
- if (!options?.enabled) {
432
- return undefined;
433
- }
434
- const llmInfo = llm.info;
435
- const credentialSource = llmInfo?.provider === 'openai' ?
436
- resolveProviderCredentialSourceForModel(llmInfo.model, runtime)
437
- : undefined;
438
- if (credentialSource?.type === 'oauth') {
439
- const message = 'CyberLoop drift detection requires OpenAI Platform API-key mode for embeddings; active auth is OpenAI account sign-in.';
440
- logger.debug({ model: llmInfo?.model, credentialSource: credentialSource.type }, message);
441
- options.onError?.(new Error(message));
442
- return undefined;
443
- }
444
- try {
445
- return await createCyberLoopKinematicsObserver({
446
- goal: prompt,
447
- referenceText: referenceAssistantText,
448
- apiKey: llm.info?.provider === 'openai' ? resolveApiKeyForModel(llm.info.model, runtime) : undefined,
449
- onAnnotation: options.onAnnotation,
450
- onError: (error) => {
451
- logger.debug({ error: error instanceof Error ? error.message : String(error) }, 'CyberLoop drift observer failed');
452
- options.onError?.(error);
453
- },
454
- });
455
- }
456
- catch (error) {
457
- logger.debug({ error: error instanceof Error ? error.message : String(error) }, 'CyberLoop drift observer unavailable');
458
- options.onError?.(error);
459
- return undefined;
173
+ finishTuiAgentTurn(state);
460
174
  }
461
175
  }
462
176
  function previousAssistantOutput(session) {
@@ -517,225 +231,19 @@ async function runDirectShellAction(args) {
517
231
  if (!command || state.isRunning || !activeSession) {
518
232
  return;
519
233
  }
520
- const shellDisplay = `!${command}`;
521
- const leaseOwner = {
522
- ownerKind: 'tui',
523
- ownerId: `tui-${process.pid}`,
524
- clientLabel: 'terminal chat',
525
- };
526
- const persistedSession = readChatSession(runtime.sessionCatalogFile, activeSessionId, true) ?? activeSession;
527
- const leaseConflict = getSessionLeaseConflict(persistedSession, leaseOwner);
528
- if (leaseConflict) {
529
- state.setError(leaseConflict);
530
- state.setStatus('Blocked');
531
- updateActiveSession((session) => ({
532
- ...session,
533
- messages: [...session.messages, { id: state.nextLocalId(), role: 'assistant', text: leaseConflict }],
534
- }));
535
- return;
536
- }
537
- updateActiveSession(() => touchSession(acquireSessionLease(persistedSession, leaseOwner)));
538
- state.setError(undefined);
539
- state.setIsRunning(true);
540
- state.setStatus('Running');
541
- state.interruptRequestedRef.current = false;
542
- state.setInterruptRequested(false);
543
- state.abortControllerRef.current = new AbortController();
544
- state.setLiveEvents([{ id: state.nextLocalId(), text: `running direct shell (${command})` }]);
545
- updateActiveSession((session) => ({
546
- ...session,
547
- messages: [...session.messages, { id: state.nextLocalId(), role: 'user', text: shellDisplay }],
548
- lastContinuePrompt: undefined,
549
- }));
550
- try {
551
- const inspectCall = {
552
- id: `direct-shell-${Date.now()}-inspect`,
553
- tool: 'run_shell_inspect',
554
- input: { command },
555
- };
556
- const inspectResult = await runShellCommand(inspectCall.input, {
557
- toolName: inspectCall.tool,
558
- rules: DEFAULT_INSPECT_RULES,
559
- allowUnknown: false,
560
- }, state.abortControllerRef.current.signal);
561
- let chosenCall = inspectCall;
562
- let chosenResult = inspectResult;
563
- if (shouldFallbackToMutate(inspectResult.error)) {
564
- const mutateCall = {
565
- id: `direct-shell-${Date.now()}-mutate`,
566
- tool: 'run_shell_mutate',
567
- input: { command },
568
- };
569
- if (runtime.directShellApproval === 'always') {
570
- const directShellTool = tools.find((tool) => tool.name === 'run_shell_mutate');
571
- if (!directShellTool) {
572
- throw new Error('run_shell_mutate tool is not registered');
573
- }
574
- const approval = isProjectApproved(mutateCall) ?
575
- { approved: true, reason: 'Approved by saved project rule' }
576
- : await new Promise((resolve) => {
577
- const rememberedRule = createProjectApprovalRuleForCall(mutateCall);
578
- state.setPendingApproval({
579
- call: mutateCall,
580
- tool: directShellTool,
581
- rememberForProject: () => rememberProjectApproval(mutateCall),
582
- rememberLabel: rememberedRule ? describeProjectApprovalRule(rememberedRule) : undefined,
583
- resolve,
584
- });
585
- });
586
- if (!approval.approved) {
587
- const denialMessage = approval.reason ? `Command denied.\n${approval.reason}` : 'Command denied.';
588
- updateActiveSession((session) => ({
589
- ...session,
590
- messages: [...session.messages, { id: state.nextLocalId(), role: 'assistant', text: denialMessage }],
591
- }));
592
- state.setLiveEvents([
593
- {
594
- id: state.nextLocalId(),
595
- text: `approval denied for ${summarizeToolCall(mutateCall.tool, mutateCall.input)}`,
596
- },
597
- ]);
598
- state.setStatus('Idle');
599
- return;
600
- }
601
- }
602
- chosenCall = mutateCall;
603
- chosenResult = await runShellCommand(mutateCall.input, {
604
- toolName: mutateCall.tool,
605
- rules: DEFAULT_MUTATE_RULES,
606
- allowUnknown: true,
607
- }, state.abortControllerRef.current.signal);
608
- }
609
- const responseText = formatDirectShellResponse(chosenCall.tool, command, chosenResult);
610
- const directShellHistory = appendDirectShellHistory(activeSession.history, shellDisplay, chosenCall.tool, chosenResult);
611
- const compacted = await compactChatHistoryWithArchive({
612
- history: directShellHistory,
613
- model,
614
- sessionId: activeSessionId,
615
- stateRoot: runtime.stateRoot,
616
- systemContext: runtime.systemContext,
617
- toolNames: tools.map((tool) => tool.name),
618
- goal: shellDisplay,
619
- summarizer: { credentialSource: runtime.providerCredentialSource },
620
- onStatusChange: (event) => {
621
- if (event.status === 'running') {
622
- state.setStatus('Compacting');
623
- state.setLiveEvents((current) => [...current, { id: state.nextLocalId(), text: 'Compacting earlier conversation history…' }].slice(-8));
624
- updateActiveSession((session) => ({
625
- ...session,
626
- context: buildCompactionRunningContext({
627
- history: directShellHistory,
628
- previous: session.context,
629
- archiveCount: session.archives?.length,
630
- currentSummaryPath: session.context?.currentSummaryPath,
631
- lastArchivePath: event.archivePath,
632
- }),
633
- }));
634
- }
635
- else if (event.status === 'failed') {
636
- state.setLiveEvents((current) => [...current, { id: state.nextLocalId(), text: `Compaction failed: ${event.error ?? 'unknown error'}` }].slice(-8));
637
- }
638
- else {
639
- state.setLiveEvents((current) => [...current, { id: state.nextLocalId(), text: 'Compaction finished.' }].slice(-8));
640
- }
641
- },
642
- });
643
- updateActiveSession((session) => ({
644
- ...session,
645
- history: compacted.history,
646
- context: compacted.context,
647
- archives: compacted.archives,
648
- messages: buildConversationMessages(compacted.history),
649
- }));
650
- state.setLiveEvents([
651
- {
652
- id: state.nextLocalId(),
653
- text: chosenResult.ok ?
654
- `${summarizeToolCall(chosenCall.tool, chosenCall.input)} completed`
655
- : `${summarizeToolCall(chosenCall.tool, chosenCall.input)} failed`,
656
- },
657
- ]);
658
- state.setStatus(chosenResult.ok ? 'Idle' : 'Stopped: error');
659
- if (!chosenResult.ok && chosenResult.error) {
660
- state.setError(chosenResult.error);
661
- }
662
- maybeAutoNameSession(activeSessionId, shellDisplay, responseText);
663
- }
664
- catch (shellError) {
665
- const message = shellError instanceof Error ? shellError.message : String(shellError);
666
- state.setError(message);
667
- state.setStatus('Error');
668
- updateActiveSession((session) => ({
669
- ...session,
670
- messages: [
671
- ...session.messages,
672
- { id: state.nextLocalId(), role: 'assistant', text: `Direct shell execution failed:\n${message}` },
673
- ],
674
- }));
675
- }
676
- finally {
677
- updateActiveSession((session) => releaseSessionLease(session, leaseOwner));
678
- state.setPendingApproval(undefined);
679
- state.setApprovalChoice('approve');
680
- state.interruptRequestedRef.current = false;
681
- state.setInterruptRequested(false);
682
- state.abortControllerRef.current = undefined;
683
- state.setIsRunning(false);
684
- }
685
- }
686
- function scheduleBackgroundMemoryMaintenance(args) {
687
- void (async () => {
688
- const maintenance = await runMaintenanceForRecordedCandidates({
689
- memoryRoot: args.runtime.memoryDir,
690
- llm: args.llm,
691
- source: `terminal chat session ${args.sessionId}`,
692
- trace: args.trace,
693
- maxSteps: 20,
694
- onTraceEvent: (event) => {
695
- if (event.type === 'memory.maintenance_started') {
696
- args.setIsMemoryUpdating(true);
697
- }
698
- const next = toLiveEvent(event);
699
- if (!next) {
700
- return;
701
- }
702
- args.setLiveEvents((current) => [...current, { id: args.nextLocalId(), text: next }].slice(-8));
703
- },
704
- });
705
- if (maintenance.events.length === 0) {
706
- return;
707
- }
708
- const currentTrace = readTraceEvents(args.traceFile);
709
- const nextTrace = [...currentTrace, ...maintenance.events];
710
- writeFileSync(args.traceFile, `${JSON.stringify(nextTrace, null, 2)}\n`, 'utf8');
711
- args.updateSessionById(args.sessionId, (session) => touchSession({
712
- ...session,
713
- turns: session.turns.map((turn, index) => (index === session.turns.length - 1 ?
714
- {
715
- ...turn,
716
- events: summarizeTrace(nextTrace),
717
- }
718
- : turn)),
719
- }));
720
- args.setIsMemoryUpdating(false);
721
- })().catch((error) => {
722
- args.setIsMemoryUpdating(false);
723
- args.setLiveEvents((current) => [
724
- ...current,
725
- {
726
- id: args.nextLocalId(),
727
- text: `Memory maintenance failed: ${error instanceof Error ? error.message : String(error)}`,
728
- },
729
- ].slice(-8));
234
+ await executeTuiDirectShell({
235
+ command,
236
+ shellDisplay: `!${command}`,
237
+ model,
238
+ activeSessionId,
239
+ activeSession,
240
+ runtime,
241
+ tools,
242
+ state,
243
+ updateActiveSession,
244
+ maybeAutoNameSession,
245
+ isProjectApproved,
246
+ rememberProjectApproval,
730
247
  });
731
248
  }
732
- function readTraceEvents(path) {
733
- try {
734
- const parsed = JSON.parse(readFileSync(path, 'utf8'));
735
- return Array.isArray(parsed) ? parsed : [];
736
- }
737
- catch {
738
- return [];
739
- }
740
- }
741
249
  //# sourceMappingURL=useAgentRun.js.map