ethagent 3.3.3 → 4.0.0

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 (322) hide show
  1. package/.claude-plugin/marketplace.json +11 -0
  2. package/.claude-plugin/plugin.json +35 -0
  3. package/LICENSE +1 -1
  4. package/README.md +64 -104
  5. package/commands/ethagent.md +40 -0
  6. package/package.json +16 -16
  7. package/src/app/keybindings/KeybindingProvider.tsx +1 -6
  8. package/src/app/keybindings/types.ts +1 -6
  9. package/src/cli/ResetConfirmView.tsx +54 -53
  10. package/src/cli/demo.ts +86 -0
  11. package/src/cli/hookIo.ts +45 -0
  12. package/src/cli/main.tsx +94 -123
  13. package/src/cli/memoryGuard.ts +49 -0
  14. package/src/cli/reset.ts +28 -70
  15. package/src/cli/sessionStart.ts +33 -0
  16. package/src/cli/status.ts +46 -0
  17. package/src/cli/sync.ts +167 -0
  18. package/src/cli/syncAdapters/claude-code.ts +86 -0
  19. package/src/cli/syncAdapters/codex.ts +66 -0
  20. package/src/cli/syncAdapters/index.ts +45 -0
  21. package/src/cli/syncAdapters/managedBlock.ts +175 -0
  22. package/src/cli/syncAdapters/shared.ts +63 -0
  23. package/src/identity/continuity/envelopeParse.ts +20 -1
  24. package/src/identity/continuity/publicSkills.ts +3 -1
  25. package/src/identity/continuity/skills/publicSkillsSync.ts +2 -1
  26. package/src/identity/continuity/skills/scaffold.ts +5 -2
  27. package/src/identity/continuity/snapshots.ts +12 -5
  28. package/src/identity/continuity/storage/defaults.ts +20 -19
  29. package/src/identity/continuity/storage/status.ts +1 -1
  30. package/src/identity/ens/ensLookup/constants.ts +1 -1
  31. package/src/identity/manager/IdentityManager.tsx +33 -0
  32. package/src/identity/{hub → manager}/OperationalRoutes.tsx +37 -18
  33. package/src/identity/{hub → manager}/Routes.tsx +48 -34
  34. package/src/identity/{hub → manager}/continuity/ContinuityDashboardScreen.tsx +9 -19
  35. package/src/identity/{hub → manager}/continuity/RebackupStorageScreen.tsx +3 -3
  36. package/src/identity/manager/continuity/RecoveryConfirmScreen.tsx +102 -0
  37. package/src/identity/{hub → manager}/continuity/SavePromptScreen.tsx +2 -3
  38. package/src/identity/{hub → manager}/continuity/completion.ts +1 -1
  39. package/src/identity/{hub → manager}/continuity/effects.ts +1 -1
  40. package/src/identity/{hub → manager}/continuity/skills/DeleteSkillConfirmScreen.tsx +2 -2
  41. package/src/identity/{hub → manager}/continuity/skills/NewSkillScreen.tsx +0 -5
  42. package/src/identity/{hub → manager}/continuity/skills/NewSkillVisibilityScreen.tsx +4 -4
  43. package/src/identity/{hub → manager}/continuity/skills/SkillActionsScreen.tsx +6 -22
  44. package/src/identity/{hub → manager}/continuity/skills/SkillsTreeScreen.tsx +5 -17
  45. package/src/identity/{hub → manager}/continuity/snapshot.ts +1 -1
  46. package/src/identity/{hub → manager}/continuity/vault.ts +1 -1
  47. package/src/identity/{hub → manager}/create/CreateFlow.tsx +59 -32
  48. package/src/identity/{hub → manager}/create/effects.ts +19 -10
  49. package/src/identity/manager/create/importScan.ts +122 -0
  50. package/src/identity/{hub → manager}/custody/CustodyEditFlow.tsx +17 -61
  51. package/src/identity/{hub → manager}/custody/actions.ts +1 -15
  52. package/src/identity/{hub → manager}/custody/routes.tsx +20 -40
  53. package/src/identity/{hub → manager}/custody/transactions.ts +1 -0
  54. package/src/identity/{hub → manager}/custody/types.ts +1 -2
  55. package/src/identity/{hub → manager}/custody/useCustodyEffects.ts +1 -1
  56. package/src/identity/{hub → manager}/ens/EnsEditAdvancedScreens.tsx +2 -2
  57. package/src/identity/{hub → manager}/ens/EnsEditMaintenanceScreens.tsx +12 -23
  58. package/src/identity/{hub → manager}/ens/EnsEditReviewScreens.tsx +18 -42
  59. package/src/identity/{hub → manager}/ens/EnsEditRunners.tsx +1 -1
  60. package/src/identity/{hub → manager}/ens/EnsEditShared.tsx +0 -2
  61. package/src/identity/{hub → manager}/ens/EnsEditSimpleScreens.tsx +10 -19
  62. package/src/identity/{hub → manager}/ens/EnsFlow.tsx +133 -41
  63. package/src/identity/{hub → manager}/ens/EnsOperatorWalletsScreen.tsx +14 -19
  64. package/src/identity/{hub → manager}/ens/editCopy.ts +1 -14
  65. package/src/identity/{hub → manager}/profile/EditProfileFlow.tsx +99 -66
  66. package/src/identity/{hub → manager}/profile/effects.ts +1 -3
  67. package/src/identity/{hub → manager}/profile/operatorSave.ts +1 -1
  68. package/src/identity/{hub → manager}/profile/state.ts +1 -1
  69. package/src/identity/{hub/identityHubReducer.ts → manager/reducer.ts} +25 -26
  70. package/src/identity/{hub → manager}/restore/RestoreFlow.tsx +16 -24
  71. package/src/identity/{hub → manager}/restore/apply.ts +1 -1
  72. package/src/identity/{hub → manager}/restore/auth.ts +1 -1
  73. package/src/identity/{hub → manager}/restore/discover.ts +1 -1
  74. package/src/identity/{hub → manager}/restore/fetch.ts +1 -1
  75. package/src/identity/{hub → manager}/restore/restoreAdmin.ts +1 -1
  76. package/src/identity/{hub → manager}/restore/useRestoreEffects.ts +2 -9
  77. package/src/identity/{hub → manager}/settings/StorageCredentialScreen.tsx +10 -25
  78. package/src/identity/{hub → manager}/shared/components/DetailsScreen.tsx +5 -7
  79. package/src/identity/{hub → manager}/shared/components/ErrorScreen.tsx +6 -10
  80. package/src/identity/{hub → manager}/shared/components/FlowTimeline.tsx +4 -3
  81. package/src/identity/{hub → manager}/shared/components/IdentitySummary.tsx +19 -59
  82. package/src/identity/manager/shared/components/LazyMenu.tsx +147 -0
  83. package/src/identity/manager/shared/components/MenuScreen.tsx +220 -0
  84. package/src/identity/manager/shared/components/OperationCompleteScreen.tsx +28 -0
  85. package/src/identity/{hub → manager}/shared/components/UnlinkedIdentityScreen.tsx +9 -10
  86. package/src/identity/{hub → manager}/shared/components/WalletApprovalScreen.tsx +1 -2
  87. package/src/identity/manager/shared/components/Wordmark.tsx +54 -0
  88. package/src/identity/{hub → manager}/shared/components/menuFlagsFromReconciliation.ts +39 -15
  89. package/src/identity/{hub → manager}/shared/effects/profilePrep.ts +1 -1
  90. package/src/identity/manager/shared/effects/types.ts +30 -0
  91. package/src/identity/{hub → manager}/shared/model/copy.ts +0 -4
  92. package/src/identity/{hub → manager}/shared/model/errors.ts +32 -3
  93. package/src/identity/{hub → manager}/shared/model/network.ts +2 -2
  94. package/src/identity/{hub → manager}/shared/reconciliation/agentReconciliation/hook.ts +5 -0
  95. package/src/identity/{hub → manager}/shared/reconciliation/agentReconciliation/run.ts +1 -1
  96. package/src/identity/{hub/shared/reconciliation/useAgentReconciliation.ts → manager/shared/reconciliation/index.ts} +6 -0
  97. package/src/identity/{hub → manager}/shared/utils.ts +6 -10
  98. package/src/identity/{hub → manager}/transfer/TokenTransferFlow.tsx +3 -3
  99. package/src/identity/{hub → manager}/transfer/TokenTransferScreens.tsx +4 -10
  100. package/src/identity/{hub → manager}/transfer/effects.ts +1 -1
  101. package/src/identity/{hub → manager}/types.ts +5 -6
  102. package/src/identity/{hub/useIdentityHubContinuity.ts → manager/useContinuity.ts} +59 -27
  103. package/src/identity/{hub/useIdentityHubController.ts → manager/useController.ts} +38 -35
  104. package/src/identity/{hub/useIdentityHubSideEffects.ts → manager/useSideEffects.ts} +40 -4
  105. package/src/identity/registry/erc8004/discovery.ts +3 -17
  106. package/src/identity/registry/erc8004/utils.ts +1 -1
  107. package/src/identity/storage/ipfs.ts +21 -1
  108. package/src/identity/wallet/browserWallet/html.ts +10 -2
  109. package/src/identity/wallet/browserWallet/http.ts +18 -0
  110. package/src/identity/wallet/browserWallet/requestServer.ts +5 -1
  111. package/src/identity/wallet/browserWallet/requests.ts +10 -28
  112. package/src/identity/wallet/browserWallet/session.ts +26 -33
  113. package/src/identity/wallet/browserWallet/validation.ts +14 -0
  114. package/src/identity/wallet/browserWallet/walletPageSource.ts +22 -40
  115. package/src/identity/wallet/page/boot.ts +43 -0
  116. package/src/identity/wallet/page/config.ts +59 -0
  117. package/src/identity/wallet/page/constants.ts +12 -0
  118. package/src/identity/wallet/page/copy.ts +47 -68
  119. package/src/identity/wallet/page/css.ts +638 -0
  120. package/src/identity/wallet/page/{errorView.ts → errors.ts} +5 -14
  121. package/src/identity/wallet/page/{controller.ts → flow.ts} +4 -71
  122. package/src/identity/wallet/page/markup.ts +44 -34
  123. package/src/identity/wallet/page/{walletProvider.ts → provider.ts} +0 -3
  124. package/src/identity/wallet/page/resize.ts +95 -0
  125. package/src/identity/wallet/page/state.ts +135 -8
  126. package/src/identity/wallet/page/timeline.ts +161 -0
  127. package/src/identity/wallet/page/view.ts +22 -302
  128. package/src/storage/config.ts +30 -80
  129. package/src/storage/reset.ts +31 -0
  130. package/src/storage/secrets.ts +1 -16
  131. package/src/ui/Select.tsx +27 -5
  132. package/src/ui/Spinner.tsx +16 -15
  133. package/src/ui/Surface.tsx +21 -17
  134. package/src/ui/TextArea.tsx +173 -0
  135. package/src/ui/TextInput.tsx +31 -133
  136. package/src/ui/theme.ts +22 -13
  137. package/src/utils/clipboard.ts +0 -140
  138. package/src/app/FirstRun.tsx +0 -577
  139. package/src/app/FirstRunTimeline.tsx +0 -51
  140. package/src/app/firstRunConfig.ts +0 -26
  141. package/src/app/hooks/useCancelRequest.ts +0 -22
  142. package/src/app/hooks/useDoublePress.ts +0 -46
  143. package/src/app/hooks/useExitOnCtrlC.ts +0 -36
  144. package/src/auth/openaiOAuth/credentials.ts +0 -47
  145. package/src/auth/openaiOAuth/crypto.ts +0 -23
  146. package/src/auth/openaiOAuth/index.ts +0 -238
  147. package/src/auth/openaiOAuth/landingPage.ts +0 -116
  148. package/src/auth/openaiOAuth/listener.ts +0 -151
  149. package/src/auth/openaiOAuth/refresh.ts +0 -70
  150. package/src/auth/openaiOAuth/shared.ts +0 -115
  151. package/src/chat/ChatBottomPane.tsx +0 -296
  152. package/src/chat/ChatScreen.tsx +0 -1685
  153. package/src/chat/ConversationStack.tsx +0 -56
  154. package/src/chat/MessageList.tsx +0 -638
  155. package/src/chat/SessionStatus.tsx +0 -53
  156. package/src/chat/chatEnvironment.ts +0 -16
  157. package/src/chat/chatScreenUtils.ts +0 -194
  158. package/src/chat/chatSessionState.ts +0 -146
  159. package/src/chat/chatTurnContext.ts +0 -50
  160. package/src/chat/chatTurnOrchestrator.ts +0 -603
  161. package/src/chat/chatTurnRows.ts +0 -64
  162. package/src/chat/commands.ts +0 -494
  163. package/src/chat/continuityEditReview.ts +0 -42
  164. package/src/chat/display/DiffView.tsx +0 -193
  165. package/src/chat/display/SyntaxText.tsx +0 -192
  166. package/src/chat/display/toolCallDisplay.ts +0 -103
  167. package/src/chat/display/toolResultDisplay.ts +0 -19
  168. package/src/chat/input/ChatInput.tsx +0 -625
  169. package/src/chat/input/chatInputHelpers.ts +0 -62
  170. package/src/chat/input/chatInputState.ts +0 -247
  171. package/src/chat/input/chatPaste.ts +0 -49
  172. package/src/chat/input/imageRefs.ts +0 -30
  173. package/src/chat/input/inputRendering.tsx +0 -93
  174. package/src/chat/input/textCursor.ts +0 -212
  175. package/src/chat/messageMarkdown.ts +0 -220
  176. package/src/chat/messageRows.ts +0 -43
  177. package/src/chat/planImplementation.ts +0 -62
  178. package/src/chat/slashCommandHandlers.ts +0 -122
  179. package/src/chat/slashCommandViews.ts +0 -120
  180. package/src/chat/transcript/TranscriptView.tsx +0 -184
  181. package/src/chat/transcript/transcriptViewport.ts +0 -295
  182. package/src/chat/views/ContextLimitView.tsx +0 -95
  183. package/src/chat/views/ContinuityEditReviewView.tsx +0 -50
  184. package/src/chat/views/CopyPicker.tsx +0 -50
  185. package/src/chat/views/PermissionPrompt.tsx +0 -156
  186. package/src/chat/views/PermissionsView.tsx +0 -165
  187. package/src/chat/views/PlanApprovalView.tsx +0 -91
  188. package/src/chat/views/ResumeView.tsx +0 -273
  189. package/src/chat/views/RewindView.tsx +0 -412
  190. package/src/cli/preview.tsx +0 -14
  191. package/src/cli/updateNotice.ts +0 -54
  192. package/src/identity/continuity/privateEdit/apply.ts +0 -170
  193. package/src/identity/continuity/privateEdit/diff.ts +0 -6
  194. package/src/identity/continuity/privateEdit/files.ts +0 -23
  195. package/src/identity/continuity/privateEdit/types.ts +0 -28
  196. package/src/identity/continuity/privateEdit.ts +0 -46
  197. package/src/identity/hub/IdentityHub.tsx +0 -14
  198. package/src/identity/hub/continuity/RecoveryConfirmScreen.tsx +0 -104
  199. package/src/identity/hub/ens/effects.ts +0 -218
  200. package/src/identity/hub/shared/components/MenuScreen.tsx +0 -241
  201. package/src/identity/hub/shared/effects/types.ts +0 -53
  202. package/src/identity/hub/shared/reconciliation/index.ts +0 -14
  203. package/src/identity/wallet/page/grainient.ts +0 -278
  204. package/src/identity/wallet/page/html.ts +0 -28
  205. package/src/identity/wallet/page/styles/base.ts +0 -259
  206. package/src/identity/wallet/page/styles/components.ts +0 -262
  207. package/src/identity/wallet/page/styles/index.ts +0 -5
  208. package/src/identity/wallet/page/styles/responsive.ts +0 -247
  209. package/src/identity/wallet/page.tsx +0 -38
  210. package/src/mcp/approvals.ts +0 -113
  211. package/src/mcp/config.ts +0 -235
  212. package/src/mcp/manager.ts +0 -482
  213. package/src/mcp/managerHelpers.ts +0 -70
  214. package/src/mcp/names.ts +0 -19
  215. package/src/mcp/output.ts +0 -96
  216. package/src/models/ModelPicker.tsx +0 -1009
  217. package/src/models/catalog.ts +0 -327
  218. package/src/models/huggingface.ts +0 -712
  219. package/src/models/huggingfaceStorage.ts +0 -136
  220. package/src/models/llamacpp.ts +0 -848
  221. package/src/models/llamacppCommands.ts +0 -44
  222. package/src/models/llamacppConfig.ts +0 -34
  223. package/src/models/llamacppDiscovery.ts +0 -176
  224. package/src/models/llamacppOutput.ts +0 -65
  225. package/src/models/llamacppPreflight.ts +0 -158
  226. package/src/models/modelDisplay.ts +0 -180
  227. package/src/models/modelPickerCatalogFlow.ts +0 -56
  228. package/src/models/modelPickerCredentials.ts +0 -166
  229. package/src/models/modelPickerData.ts +0 -41
  230. package/src/models/modelPickerDisplay.tsx +0 -132
  231. package/src/models/modelPickerHfFlow.ts +0 -192
  232. package/src/models/modelPickerLocalRunnerFlow.ts +0 -115
  233. package/src/models/modelPickerOptions.ts +0 -457
  234. package/src/models/modelPickerTypes.ts +0 -69
  235. package/src/models/modelPickerUninstallFlow.ts +0 -48
  236. package/src/models/modelPickerViewHelpers.ts +0 -174
  237. package/src/models/modelRecommendation.ts +0 -139
  238. package/src/models/providerDisplay.ts +0 -16
  239. package/src/models/runtimeDetection.ts +0 -81
  240. package/src/models/uncensoredCatalog.ts +0 -86
  241. package/src/providers/anthropic.ts +0 -290
  242. package/src/providers/contracts.ts +0 -71
  243. package/src/providers/errors.ts +0 -80
  244. package/src/providers/gemini.ts +0 -391
  245. package/src/providers/openai-chat.ts +0 -474
  246. package/src/providers/openai-responses-format.ts +0 -177
  247. package/src/providers/openai-responses.ts +0 -306
  248. package/src/providers/openaiChatWire.ts +0 -124
  249. package/src/providers/registry.ts +0 -120
  250. package/src/providers/retry.ts +0 -58
  251. package/src/providers/sse.ts +0 -93
  252. package/src/runtime/compaction.ts +0 -395
  253. package/src/runtime/cwd.ts +0 -43
  254. package/src/runtime/providerTurn.ts +0 -38
  255. package/src/runtime/sessionMode.ts +0 -55
  256. package/src/runtime/systemPrompt.ts +0 -213
  257. package/src/runtime/textToolParser.ts +0 -161
  258. package/src/runtime/toolClaimGuards.ts +0 -143
  259. package/src/runtime/toolExecution.ts +0 -304
  260. package/src/runtime/toolIntent.ts +0 -143
  261. package/src/runtime/turn.ts +0 -369
  262. package/src/runtime/turnNudges.ts +0 -223
  263. package/src/runtime/turnTypes.ts +0 -86
  264. package/src/storage/factoryReset.ts +0 -127
  265. package/src/storage/history.ts +0 -58
  266. package/src/storage/permissions.ts +0 -76
  267. package/src/storage/rewind.ts +0 -266
  268. package/src/storage/sessionExport.ts +0 -49
  269. package/src/storage/sessions.ts +0 -495
  270. package/src/tools/bashSafety.ts +0 -186
  271. package/src/tools/bashTool.ts +0 -140
  272. package/src/tools/changeDirectoryTool.ts +0 -213
  273. package/src/tools/contracts.ts +0 -192
  274. package/src/tools/deleteFileTool.ts +0 -116
  275. package/src/tools/editTool.ts +0 -165
  276. package/src/tools/editUtils.ts +0 -170
  277. package/src/tools/fileDiff.ts +0 -261
  278. package/src/tools/listDirectoryTool.ts +0 -55
  279. package/src/tools/listSkillFilesTool.ts +0 -77
  280. package/src/tools/listSkillsTool.ts +0 -68
  281. package/src/tools/mcpResourceTools.ts +0 -95
  282. package/src/tools/permissionRules.ts +0 -85
  283. package/src/tools/privateContinuityEditTool.ts +0 -187
  284. package/src/tools/privateContinuityReadTool.ts +0 -106
  285. package/src/tools/readSkillTool.ts +0 -107
  286. package/src/tools/readTool.ts +0 -85
  287. package/src/tools/registry.ts +0 -103
  288. package/src/tools/writeFileTool.ts +0 -167
  289. package/src/ui/BrandSplash.tsx +0 -133
  290. package/src/ui/terminalTitle.ts +0 -30
  291. package/src/utils/images.ts +0 -140
  292. package/src/utils/markdownSegments.ts +0 -51
  293. package/src/utils/messages.ts +0 -37
  294. package/src/utils/withRetry.ts +0 -324
  295. /package/src/identity/{hub → manager}/continuity/state.ts +0 -0
  296. /package/src/identity/{hub → manager}/custody/helpers.ts +0 -0
  297. /package/src/identity/{hub → manager}/custody/preflight.ts +0 -0
  298. /package/src/identity/{hub → manager}/custody/state.ts +0 -0
  299. /package/src/identity/{hub → manager}/custody/useCustodyFlow.tsx +0 -0
  300. /package/src/identity/{hub → manager}/ens/EnsEditFlow.tsx +0 -0
  301. /package/src/identity/{hub → manager}/ens/advancedEnsValidation.ts +0 -0
  302. /package/src/identity/{hub → manager}/ens/state.ts +0 -0
  303. /package/src/identity/{hub → manager}/ens/transactions.ts +0 -0
  304. /package/src/identity/{hub → manager}/ens/types.ts +0 -0
  305. /package/src/identity/{hub → manager}/profile/identity.ts +0 -0
  306. /package/src/identity/{hub → manager}/restore/envelopes.ts +0 -0
  307. /package/src/identity/{hub → manager}/restore/helpers.ts +0 -0
  308. /package/src/identity/{hub → manager}/restore/recovery.ts +0 -0
  309. /package/src/identity/{hub → manager}/restore/resolve.ts +0 -0
  310. /package/src/identity/{hub → manager}/shared/components/BusyScreen.tsx +0 -0
  311. /package/src/identity/{hub → manager}/shared/components/NetworkScreen.tsx +0 -0
  312. /package/src/identity/{hub → manager}/shared/components/PinataJwtInput.tsx +0 -0
  313. /package/src/identity/{hub → manager}/shared/effects/receipts.ts +0 -0
  314. /package/src/identity/{hub → manager}/shared/effects/sync.ts +0 -0
  315. /package/src/identity/{hub → manager}/shared/model/format.ts +0 -0
  316. /package/src/identity/{hub → manager}/shared/operatorWallets.ts +0 -0
  317. /package/src/identity/{hub → manager}/shared/reconciliation/agentReconciliation/ownership.ts +0 -0
  318. /package/src/identity/{hub → manager}/shared/reconciliation/agentReconciliation/types.ts +0 -0
  319. /package/src/identity/{hub → manager}/shared/reconciliation/walletSetup.ts +0 -0
  320. /package/src/identity/{hub → manager}/shared/txGuard.ts +0 -0
  321. /package/src/identity/{hub → manager}/transfer/progress.ts +0 -0
  322. /package/src/identity/{hub → manager}/transfer/state.ts +0 -0
@@ -1,603 +0,0 @@
1
- import type { Message, Provider } from '../providers/contracts.js'
2
- import { directToolUsesForUserText } from '../runtime/toolIntent.js'
3
- import { toPermissionMode, type SessionMode } from '../runtime/sessionMode.js'
4
- import { runPendingToolUses } from '../runtime/toolExecution.js'
5
- import { runRuntimeTurn, type TurnEvent } from '../runtime/turn.js'
6
- import type { EthagentConfig } from '../storage/config.js'
7
- import type { SessionMessage } from '../storage/sessions.js'
8
- import type { SessionPermissionRule, ToolResult } from '../tools/contracts.js'
9
- import { readContinuityFiles } from '../identity/continuity/storage.js'
10
- import { listSkillsTree } from '../identity/continuity/skills/loadSkills.js'
11
- import { isDraftScaffold } from '../identity/continuity/skills/scaffold.js'
12
- import type { MessageRow } from './MessageList.js'
13
- import {
14
- buildBaseMessages,
15
- createTurnCheckpoint,
16
- type TurnCheckpoint,
17
- } from './chatScreenUtils.js'
18
- import { collapseImagePathsToRefs, userTextToContentBlocks } from '../utils/images.js'
19
- import { buildFileMentionContextMessages } from './chatTurnContext.js'
20
- import {
21
- finalizeStreamingRowsById,
22
- updateStreamingRows,
23
- } from './chatTurnRows.js'
24
-
25
- type MutableRef<T> = { current: T }
26
-
27
- type ExecuteToolResult = {
28
- result: ToolResult
29
- sessionRule?: SessionPermissionRule
30
- persistRule?: boolean
31
- }
32
-
33
- export type TurnOrchestratorContext = {
34
- provider: Provider
35
- mode: SessionMode
36
- sessionId: string
37
- userText: string
38
- streamFlushMs: number
39
- controller: AbortController
40
- nextRowId: () => string
41
- nowIso: () => string
42
- getConfig: () => EthagentConfig
43
- getCwd: () => string
44
- getDisplayCwd: () => string
45
- getSessionMessages: () => SessionMessage[]
46
- setActiveCheckpoint: (checkpoint: TurnCheckpoint | undefined) => void
47
- setStreaming: (streaming: boolean) => void
48
- updateRows: (updater: (prev: MessageRow[]) => MessageRow[]) => void
49
- pushNote: (text: string, kind?: 'info' | 'error' | 'dim') => void
50
- persistTurnMessage: (message: SessionMessage) => Promise<void>
51
- executeTool: (
52
- name: string,
53
- input: Record<string, unknown>,
54
- mode: ReturnType<typeof toPermissionMode>,
55
- ) => Promise<ExecuteToolResult>
56
- applySessionRule: (rule?: SessionPermissionRule, persistRule?: boolean) => Promise<void>
57
- preflightProvider?: () => Promise<{ ok: true } | { ok: false; message: string }>
58
- onPlanReady?: (plan: string) => void
59
- onContextExceeded?: (info: { contextLimit: number }) => void
60
- pendingAssistantTextRef: MutableRef<string | null>
61
- pendingThinkingTextRef: MutableRef<string | null>
62
- streamFlushTimerRef: MutableRef<ReturnType<typeof setTimeout> | null>
63
- }
64
-
65
- export type StreamingTurnResult = {
66
- finishedNormally: boolean
67
- cancelled: boolean
68
- }
69
-
70
- export async function runStreamingTurn(
71
- context: TurnOrchestratorContext,
72
- ): Promise<StreamingTurnResult> {
73
- const {
74
- provider,
75
- mode,
76
- sessionId,
77
- userText,
78
- streamFlushMs,
79
- controller,
80
- nextRowId,
81
- nowIso,
82
- getConfig,
83
- getCwd,
84
- setActiveCheckpoint,
85
- setStreaming,
86
- updateRows,
87
- pushNote,
88
- persistTurnMessage,
89
- executeTool,
90
- applySessionRule,
91
- preflightProvider,
92
- onPlanReady,
93
- onContextExceeded,
94
- pendingAssistantTextRef,
95
- pendingThinkingTextRef,
96
- streamFlushTimerRef,
97
- } = context
98
-
99
- if (mode === 'accept-edits') {
100
- pushNote(
101
- provider.supportsTools
102
- ? 'accept-edits mode: workspace reads/edits auto-allow. private continuity edits and bash still prompt.'
103
- : 'accept-edits mode selected, but the current provider does not support tools yet.',
104
- 'dim',
105
- )
106
- }
107
-
108
- setStreaming(true)
109
- const activeCheckpoint = createTurnCheckpoint(sessionId, userText)
110
- setActiveCheckpoint(activeCheckpoint)
111
-
112
- const userContent = userTextToContentBlocks(userText)
113
- const displayText = collapseImagePathsToRefs(userText)
114
- updateRows(prev => [...prev, { role: 'user', id: nextRowId(), content: displayText }])
115
- await persistTurnMessage({
116
- role: 'user',
117
- content: displayText,
118
- providerContent: typeof userContent === 'string' ? undefined : userContent,
119
- createdAt: nowIso(),
120
- turnId: activeCheckpoint.turnId,
121
- })
122
-
123
- const mentionContextMessages = await buildFileMentionContextMessages(userText, getCwd())
124
-
125
- const buildWorking = async (): Promise<Message[]> => {
126
- const baseMessages = buildWorkingMessages(context, activeCheckpoint.turnId)
127
- const [baseSystem, ...conversationMessages] = baseMessages
128
- return [
129
- ...(baseSystem ? [baseSystem] : []),
130
- ...await buildIdentityContinuityContextMessages(getConfig()),
131
- ...await buildSkillsIndexMessage(getConfig()),
132
- ...conversationMessages,
133
- ...mentionContextMessages,
134
- ]
135
- }
136
-
137
- let accumulated = ''
138
- let thinkingContent = ''
139
- let thinkingRowId: string | null = null
140
- let thinkingCursorActive = false
141
- let assistantId: string | null = null
142
-
143
- const resetIteration = () => {
144
- accumulated = ''
145
- thinkingContent = ''
146
- thinkingRowId = null
147
- thinkingCursorActive = false
148
- assistantId = null
149
- }
150
-
151
- const stopThinkingCursor = () => {
152
- if (!thinkingRowId || !thinkingCursorActive) return
153
- thinkingCursorActive = false
154
- updateRows(prev => prev.map(row =>
155
- row.id === thinkingRowId && row.role === 'thinking'
156
- ? { ...row, showCursor: false }
157
- : row,
158
- ))
159
- }
160
-
161
- const ensureAssistantRow = (): string => {
162
- if (assistantId) return assistantId
163
- assistantId = nextRowId()
164
- updateRows(prev => [
165
- ...prev,
166
- { role: 'assistant', id: assistantId!, content: '', liveTail: '', streaming: true },
167
- ])
168
- return assistantId
169
- }
170
-
171
- const flushStreamRows = (immediate = false) => {
172
- const commit = () => {
173
- streamFlushTimerRef.current = null
174
- const nextAssistant = pendingAssistantTextRef.current
175
- const nextThinking = pendingThinkingTextRef.current
176
- if (nextAssistant === null && nextThinking === null) return
177
- updateRows(prev => updateStreamingRows(
178
- prev,
179
- assistantId,
180
- thinkingRowId,
181
- nextAssistant,
182
- nextThinking,
183
- ))
184
- pendingAssistantTextRef.current = null
185
- pendingThinkingTextRef.current = null
186
- }
187
-
188
- if (immediate) {
189
- if (streamFlushTimerRef.current) {
190
- clearTimeout(streamFlushTimerRef.current)
191
- streamFlushTimerRef.current = null
192
- }
193
- commit()
194
- return
195
- }
196
-
197
- if (streamFlushTimerRef.current) return
198
- streamFlushTimerRef.current = setTimeout(commit, streamFlushMs)
199
- }
200
-
201
- const finalizeStreamingRows = () => {
202
- flushStreamRows(true)
203
- updateRows(prev => {
204
- let next = finalizeStreamingRowsById(prev, assistantId, thinkingRowId, accumulated, thinkingContent)
205
- if (assistantId && accumulated.length === 0) {
206
- next = next.filter(r => r.id !== assistantId)
207
- }
208
- return next
209
- })
210
- }
211
-
212
- const discardStreamingRows = () => {
213
- flushStreamRows(true)
214
- updateRows(prev => prev.filter(row =>
215
- !(assistantId && row.id === assistantId)
216
- && !(thinkingRowId && row.id === thinkingRowId),
217
- ))
218
- pendingAssistantTextRef.current = null
219
- pendingThinkingTextRef.current = null
220
- }
221
-
222
- let finishedNormally = false
223
- let cancelled = false
224
-
225
- const runToolBatch = async (pendingToolUses: Array<{
226
- id: string
227
- name: string
228
- input: Record<string, unknown>
229
- }>) => {
230
- const step = await runPendingToolUses({
231
- pendingToolUses,
232
- nextRowId,
233
- nowIso,
234
- mode,
235
- getCwd,
236
- getConfig,
237
- turnId: activeCheckpoint.turnId,
238
- controller,
239
- updateRows,
240
- pushNote,
241
- persistTurnMessage,
242
- executeTool,
243
- applySessionRule,
244
- })
245
- return step
246
- }
247
-
248
- const directToolUses = provider.supportsTools
249
- ? directToolUsesForUserText(userText)
250
- : []
251
- if (directToolUses.length > 0) {
252
- const step = await runToolBatch(directToolUses)
253
- const directCancelled = step.cancelled || controller.signal.aborted
254
- if (directCancelled) pushNote('(cancelled)', 'dim')
255
- setStreaming(false)
256
- setActiveCheckpoint(undefined)
257
- return {
258
- finishedNormally: !directCancelled,
259
- cancelled: directCancelled,
260
- }
261
- }
262
-
263
- if (preflightProvider) {
264
- let preflight: { ok: true } | { ok: false; message: string }
265
- try {
266
- preflight = await preflightProvider()
267
- } catch (err: unknown) {
268
- preflight = {
269
- ok: false,
270
- message: `provider preflight failed: ${(err as Error).message || 'unknown error'}`,
271
- }
272
- }
273
- if (!preflight.ok) {
274
- pushNote(preflight.message, 'error')
275
- setStreaming(false)
276
- setActiveCheckpoint(undefined)
277
- return {
278
- finishedNormally: false,
279
- cancelled: controller.signal.aborted,
280
- }
281
- }
282
- }
283
-
284
- try {
285
- for await (const ev of runRuntimeTurn({
286
- provider,
287
- signal: controller.signal,
288
- initialMessages: await buildWorking(),
289
- rebuildMessages: buildWorking,
290
- runToolBatch,
291
- })) {
292
- cancelled = cancelled || isCancelledEvent(ev)
293
- await handleEvent(ev, {
294
- ensureAssistantRow,
295
- flushStreamRows,
296
- finalizeStreamingRows,
297
- discardStreamingRows,
298
- resetIteration,
299
- stopThinkingCursor,
300
- setAccumulated: text => { accumulated = text },
301
- getAccumulated: () => accumulated,
302
- setThinkingContent: text => { thinkingContent = text },
303
- getThinkingContent: () => thinkingContent,
304
- setThinkingRowId: id => { thinkingRowId = id },
305
- markThinkingCursorActive: () => { thinkingCursorActive = true },
306
- getThinkingRowId: () => thinkingRowId,
307
- updateRows,
308
- pushNote,
309
- nextRowId,
310
- pendingAssistantTextRef,
311
- pendingThinkingTextRef,
312
- persistTurnMessage,
313
- nowIso,
314
- mode,
315
- onPlanReady,
316
- onContextExceeded,
317
- turnId: activeCheckpoint.turnId,
318
- model: getConfig().model,
319
- onFinishedNormally: () => { finishedNormally = true },
320
- })
321
- }
322
- } catch (err: unknown) {
323
- if (!controller.signal.aborted) {
324
- pushNote((err as Error).message || 'stream error', 'error')
325
- }
326
- finalizeStreamingRows()
327
- }
328
-
329
- if (cancelled || controller.signal.aborted) pushNote('(cancelled)', 'dim')
330
- setStreaming(false)
331
- setActiveCheckpoint(undefined)
332
-
333
- return {
334
- finishedNormally,
335
- cancelled,
336
- }
337
- }
338
-
339
- type EventHandlerContext = {
340
- ensureAssistantRow: () => string
341
- flushStreamRows: (immediate?: boolean) => void
342
- finalizeStreamingRows: () => void
343
- discardStreamingRows: () => void
344
- resetIteration: () => void
345
- stopThinkingCursor: () => void
346
- setAccumulated: (text: string) => void
347
- getAccumulated: () => string
348
- setThinkingContent: (text: string) => void
349
- getThinkingContent: () => string
350
- setThinkingRowId: (id: string | null) => void
351
- getThinkingRowId: () => string | null
352
- markThinkingCursorActive: () => void
353
- updateRows: (updater: (prev: MessageRow[]) => MessageRow[]) => void
354
- pushNote: (text: string, kind?: 'info' | 'error' | 'dim') => void
355
- nextRowId: () => string
356
- pendingAssistantTextRef: MutableRef<string | null>
357
- pendingThinkingTextRef: MutableRef<string | null>
358
- persistTurnMessage: (message: SessionMessage) => Promise<void>
359
- nowIso: () => string
360
- mode: SessionMode
361
- onPlanReady?: (plan: string) => void
362
- onContextExceeded?: (info: { contextLimit: number }) => void
363
- turnId: string
364
- model: string
365
- onFinishedNormally: () => void
366
- }
367
-
368
- function isCancelledEvent(ev: TurnEvent): boolean {
369
- return ev.type === 'cancelled'
370
- }
371
-
372
- export function parseContextExceededLimit(message: string): number | null {
373
- const match = /exceeds the available context size \((\d+)\s*tokens?\)/i.exec(message)
374
- if (!match) return null
375
- const limit = Number.parseInt(match[1]!, 10)
376
- return Number.isFinite(limit) && limit > 0 ? limit : null
377
- }
378
-
379
- async function handleEvent(ev: TurnEvent, ctx: EventHandlerContext): Promise<void> {
380
- switch (ev.type) {
381
- case 'iteration_start': {
382
- ctx.resetIteration()
383
- return
384
- }
385
- case 'text': {
386
- ctx.stopThinkingCursor()
387
- ctx.ensureAssistantRow()
388
- const next = ctx.getAccumulated() + ev.delta
389
- ctx.setAccumulated(next)
390
- ctx.pendingAssistantTextRef.current = next
391
- ctx.flushStreamRows()
392
- return
393
- }
394
- case 'thinking': {
395
- const current = ctx.getThinkingContent()
396
- const appended = current + ev.delta
397
- ctx.setThinkingContent(appended)
398
- if (ctx.getThinkingRowId() === null) {
399
- const id = ctx.nextRowId()
400
- ctx.setThinkingRowId(id)
401
- ctx.markThinkingCursorActive()
402
- ctx.updateRows(prev => [
403
- ...prev,
404
- {
405
- role: 'thinking',
406
- id,
407
- content: '',
408
- liveTail: appended,
409
- streaming: true,
410
- expanded: false,
411
- showCursor: true,
412
- },
413
- ])
414
- }
415
- ctx.pendingThinkingTextRef.current = appended
416
- ctx.flushStreamRows()
417
- return
418
- }
419
- case 'thinking_end': {
420
- ctx.flushStreamRows(true)
421
- ctx.stopThinkingCursor()
422
- return
423
- }
424
- case 'retry': {
425
- return
426
- }
427
- case 'tool_use_stop': {
428
- ctx.finalizeStreamingRows()
429
- return
430
- }
431
- case 'assistant_message_committed': {
432
- ctx.finalizeStreamingRows()
433
- if (ev.text) {
434
- await ctx.persistTurnMessage({
435
- role: 'assistant',
436
- content: ev.text,
437
- createdAt: ctx.nowIso(),
438
- model: ctx.model,
439
- turnId: ctx.turnId,
440
- })
441
- if (ctx.mode === 'plan') ctx.onPlanReady?.(ev.text)
442
- }
443
- return
444
- }
445
- case 'tool_executed': {
446
- return
447
- }
448
- case 'local_tool_recovery': {
449
- ctx.discardStreamingRows()
450
- return
451
- }
452
- case 'continuation_nudge': {
453
- if (
454
- ev.reason === 'tool_state_claim' ||
455
- ev.reason === 'tool_capability' ||
456
- ev.reason === 'tool_protocol_fake' ||
457
- ev.reason === 'tool_delegation'
458
- ) {
459
- ctx.discardStreamingRows()
460
- } else {
461
- ctx.finalizeStreamingRows()
462
- }
463
- ctx.resetIteration()
464
- return
465
- }
466
- case 'error': {
467
- const contextLimit = parseContextExceededLimit(ev.message)
468
- if (contextLimit !== null && ctx.onContextExceeded) {
469
- ctx.discardStreamingRows()
470
- ctx.onContextExceeded({ contextLimit })
471
- return
472
- }
473
- ctx.pushNote(ev.message, 'error')
474
- if (ev.discardAssistant) {
475
- ctx.discardStreamingRows()
476
- } else {
477
- ctx.finalizeStreamingRows()
478
- }
479
- return
480
- }
481
- case 'cancelled': {
482
- ctx.finalizeStreamingRows()
483
- return
484
- }
485
- case 'done': {
486
- ctx.finalizeStreamingRows()
487
- if (ev.finishedNormally) ctx.onFinishedNormally()
488
- return
489
- }
490
- case 'tool_use_start':
491
- case 'tool_use_delta':
492
- return
493
- }
494
- }
495
-
496
- const PRIVATE_SKILLS_INDEX_BUDGET = 2048
497
-
498
- export async function buildSkillsIndexMessage(
499
- config: EthagentConfig,
500
- ): Promise<Message[]> {
501
- const identity = config.identity
502
- if (!identity) return []
503
- try {
504
- const { skills, supportingCounts } = await listSkillsTree(identity)
505
- const entries = skills.filter(entry => !isDraftScaffold(entry))
506
- if (entries.length === 0) return []
507
- const header = [
508
- '<private_skills index="true" visibility="private">',
509
- 'Private skills are owner-authored content packs available for this active identity.',
510
- 'Each line shows skill_name — description. When an entry ends with (+N supporting files), call list_private_skill_files to see relative + absolute paths for each file. Read text content via read_private_skill with file:; run executable supporting scripts with run_bash using the absolute path returned by list_private_skill_files. Call list_private_skills for the full index.',
511
- ]
512
- const lines: string[] = []
513
- let charCount = header.reduce((sum, line) => sum + line.length + 1, 0)
514
- let included = 0
515
- let dropped = 0
516
- for (const entry of entries) {
517
- const displayName = entry.displayName ?? entry.name
518
- const desc = entry.description ? ` — ${entry.description}` : ''
519
- const hint = entry.whenToUse ? ` (when: ${entry.whenToUse})` : ''
520
- const supporting = supportingCounts[entry.name] ?? 0
521
- const trailer = supporting > 0
522
- ? ` (+${supporting} supporting file${supporting === 1 ? '' : 's'})`
523
- : ''
524
- const line = `- ${displayName}${desc}${hint}${trailer}`
525
- if (charCount + line.length + 1 > PRIVATE_SKILLS_INDEX_BUDGET && included > 0) {
526
- dropped = entries.length - included
527
- break
528
- }
529
- lines.push(line)
530
- charCount += line.length + 1
531
- included++
532
- }
533
- const footer: string[] = []
534
- if (dropped > 0) {
535
- footer.push(`(${dropped} more — call list_private_skills to enumerate)`)
536
- }
537
- footer.push('</private_skills>')
538
- return [{
539
- role: 'system',
540
- content: [...header, ...lines, ...footer].join('\n'),
541
- }]
542
- } catch {
543
- return []
544
- }
545
- }
546
-
547
- export async function buildIdentityContinuityContextMessages(
548
- config: EthagentConfig,
549
- ): Promise<Message[]> {
550
- const identity = config.identity
551
- if (!identity) return []
552
-
553
- try {
554
- const privateFiles = await readContinuityFiles(identity)
555
- const parts: string[] = [
556
- '<identity_continuity_files>',
557
- 'The active identity continuity files have been loaded automatically for this turn.',
558
- 'SOUL.md is private owner continuity and is the authoritative persona, voice, and standing-behavior layer for this active identity.',
559
- 'MEMORY.md is private owner continuity for durable preferences, facts, and project context.',
560
- 'Apply SOUL.md and MEMORY.md over generic ethagent identity/style unless they conflict with safety, tool correctness, developer instructions, or the user\'s latest explicit request. Do not quote private continuity unless necessary.',
561
- '<SOUL.md visibility="private">',
562
- privateFiles['SOUL.md'].trimEnd(),
563
- '</SOUL.md>',
564
- '',
565
- '<MEMORY.md visibility="private">',
566
- privateFiles['MEMORY.md'].trimEnd(),
567
- '</MEMORY.md>',
568
- '</identity_continuity_files>',
569
- ]
570
- return [{
571
- role: 'system',
572
- content: parts.join('\n'),
573
- }]
574
- } catch (err: unknown) {
575
- return [{
576
- role: 'system',
577
- content: [
578
- '<identity_continuity_files>',
579
- `Automatic identity continuity load failed: ${(err as Error).message}`,
580
- 'If the user asks about continuity, surface this failure and route them to the Identity Hub.',
581
- '</identity_continuity_files>',
582
- ].join('\n'),
583
- }]
584
- }
585
- }
586
-
587
- function buildWorkingMessages(
588
- context: Pick<
589
- TurnOrchestratorContext,
590
- 'getSessionMessages' | 'getConfig' | 'provider' | 'getCwd' | 'mode'
591
- >,
592
- preserveTurnId?: string,
593
- ): Message[] {
594
- const config = context.getConfig()
595
- return buildBaseMessages(
596
- [...context.getSessionMessages()],
597
- config,
598
- context.provider.supportsTools,
599
- context.getCwd(),
600
- context.mode,
601
- { preserveTurnId },
602
- )
603
- }
@@ -1,64 +0,0 @@
1
- import type { MessageRow } from './MessageList.js'
2
-
3
- export function updateStreamingRows(
4
- rows: MessageRow[],
5
- assistantId: string | null,
6
- thinkingRowId: string | null,
7
- assistantText: string | null,
8
- thinkingText: string | null,
9
- ): MessageRow[] {
10
- let next: MessageRow[] | null = null
11
- if (assistantId && assistantText !== null) {
12
- const index = findRowIndexById(rows, assistantId)
13
- const row = rows[index]
14
- if (row?.role === 'assistant') {
15
- next = next ?? rows.slice()
16
- next[index] = { ...row, content: assistantText, liveTail: '' }
17
- }
18
- }
19
- const source = next ?? rows
20
- if (thinkingRowId && thinkingText !== null) {
21
- const index = findRowIndexById(source, thinkingRowId)
22
- const row = source[index]
23
- if (row?.role === 'thinking') {
24
- next = next ?? rows.slice()
25
- next[index] = { ...row, content: thinkingText, liveTail: '' }
26
- }
27
- }
28
- return next ?? rows
29
- }
30
-
31
- export function finalizeStreamingRowsById(
32
- rows: MessageRow[],
33
- assistantId: string | null,
34
- thinkingRowId: string | null,
35
- assistantText: string,
36
- thinkingText: string,
37
- ): MessageRow[] {
38
- let next: MessageRow[] | null = null
39
- if (assistantId) {
40
- const index = findRowIndexById(rows, assistantId)
41
- const row = rows[index]
42
- if (row?.role === 'assistant') {
43
- next = next ?? rows.slice()
44
- next[index] = { ...row, content: assistantText || row.content, liveTail: undefined, streaming: false }
45
- }
46
- }
47
- const source = next ?? rows
48
- if (thinkingRowId) {
49
- const index = findRowIndexById(source, thinkingRowId)
50
- const row = source[index]
51
- if (row?.role === 'thinking') {
52
- next = next ?? rows.slice()
53
- next[index] = { ...row, content: thinkingText || row.content, liveTail: undefined, streaming: false, showCursor: false }
54
- }
55
- }
56
- return next ?? rows
57
- }
58
-
59
- function findRowIndexById(rows: MessageRow[], id: string): number {
60
- for (let index = rows.length - 1; index >= 0; index -= 1) {
61
- if (rows[index]?.id === id) return index
62
- }
63
- return -1
64
- }