ethagent 3.3.4 → 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 -260
  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,395 +0,0 @@
1
- import type { Message, Provider } from '../providers/contracts.js'
2
- import { approximateTokens, messageTextContent } from '../utils/messages.js'
3
- import type { SessionMessage } from '../storage/sessions.js'
4
- import { getCachedLlamaCppContextSize } from '../models/llamacpp.js'
5
-
6
- const COMPACT_SYSTEM = `Create a continuation handoff for this coding-agent conversation.
7
- Keep it concise but complete. Preserve the current goal, user constraints, key decisions, relevant files, tool results, pending tasks, and known failures. Do not claim unverified work was completed. No preamble.`
8
-
9
- const LOCAL_COMPACTION_INPUT_TOKENS = 6_000
10
- const CLOUD_COMPACTION_INPUT_TOKENS = 24_000
11
- const LOCAL_COMPACTION_OUTPUT_TOKENS = 1_000
12
- const CLOUD_COMPACTION_OUTPUT_TOKENS = 1_600
13
- const LOCAL_RECENT_MESSAGE_COUNT = 28
14
- const CLOUD_RECENT_MESSAGE_COUNT = 80
15
- const LOCAL_MESSAGE_CHAR_LIMIT = 900
16
- const CLOUD_MESSAGE_CHAR_LIMIT = 2_000
17
-
18
- export type CompactionStage =
19
- | 'preparing transcript'
20
- | 'compressing long context'
21
- | 'summarizing transcript'
22
-
23
- export type CompactTranscriptOptions = {
24
- signal?: AbortSignal
25
- onStage?: (stage: CompactionStage) => void
26
- maxInputTokens?: number
27
- maxOutputTokens?: number
28
- }
29
-
30
- export type CompactTranscriptResult =
31
- | { ok: true; summary: string; inputTokens: number; compressed: boolean }
32
- | { ok: false; reason: string; cancelled?: boolean; inputTokens?: number; compressed?: boolean }
33
-
34
- export type CompactionSource = {
35
- text: string
36
- inputTokens: number
37
- compressed: boolean
38
- }
39
-
40
- export type ContextWindowConfidence = 'exact' | 'inferred' | 'fallback'
41
-
42
- export type ContextWindowInfo = {
43
- tokens: number
44
- confidence: ContextWindowConfidence
45
- source: string
46
- }
47
-
48
- export type ContextUsage = {
49
- usedTokens: number
50
- windowTokens: number
51
- percent: number
52
- confidence: ContextWindowConfidence
53
- source: string
54
- }
55
-
56
- export function contextWindow(model: string): number {
57
- return contextWindowInfo('', model).tokens
58
- }
59
-
60
- export function contextWindowInfo(provider: string, model: string): ContextWindowInfo {
61
- const lower = model.toLowerCase()
62
- const providerLower = provider.toLowerCase()
63
- if (providerLower === 'llamacpp') {
64
- const cached = getCachedLlamaCppContextSize()
65
- if (cached) {
66
- return { tokens: cached, confidence: 'exact', source: 'llama.cpp /props' }
67
- }
68
- }
69
- if (lower.startsWith('qwen3:4b') || lower.startsWith('qwen3:30b') || lower.startsWith('qwen3:235b')) {
70
- return { tokens: 256_000, confidence: 'inferred', source: 'qwen3 long-context tag' }
71
- }
72
- if (lower.includes('qwen3')) {
73
- return { tokens: 40_000, confidence: 'inferred', source: 'qwen3 default' }
74
- }
75
- if (lower.includes('qwen')) {
76
- return { tokens: 32_768, confidence: 'inferred', source: 'qwen default' }
77
- }
78
- if (lower.includes('llama3')) {
79
- return { tokens: 128_000, confidence: 'inferred', source: 'llama3 family default' }
80
- }
81
- if (providerLower === 'anthropic' || lower.includes('claude')) {
82
- return { tokens: 200_000, confidence: 'inferred', source: 'claude family default' }
83
- }
84
- if (providerLower === 'gemini' || lower.includes('gemini')) {
85
- return { tokens: 1_000_000, confidence: 'inferred', source: 'gemini family default' }
86
- }
87
- if (
88
- lower.includes('gpt-4.1')
89
- ) {
90
- return { tokens: 1_000_000, confidence: 'inferred', source: 'gpt-4.1 family default' }
91
- }
92
- if (
93
- providerLower === 'openai'
94
- || lower.includes('gpt-4o')
95
- || /^o[134](?:-|$)/.test(lower)
96
- ) {
97
- return { tokens: 128_000, confidence: 'inferred', source: 'openai chat default' }
98
- }
99
- return { tokens: 128_000, confidence: 'fallback', source: 'ethagent fallback' }
100
- }
101
-
102
- export function contextUsage(messages: Message[], provider: string, model: string): ContextUsage {
103
- return contextUsageFromTokens(approximateTokens(messages), provider, model)
104
- }
105
-
106
- export function contextUsageFromTokens(tokens: number, provider: string, model: string): ContextUsage {
107
- const info = contextWindowInfo(provider, model)
108
- const usedTokens = Math.max(0, Math.ceil(tokens))
109
- return {
110
- usedTokens,
111
- windowTokens: info.tokens,
112
- percent: info.tokens > 0 ? Math.round((usedTokens / info.tokens) * 100) : 0,
113
- confidence: info.confidence,
114
- source: info.source,
115
- }
116
- }
117
-
118
- export function shouldConfirmContextUsage(usage: Pick<ContextUsage, 'percent'>, thresholdPercent = 90): boolean {
119
- return usage.percent >= thresholdPercent
120
- }
121
-
122
- export async function compactTranscript(
123
- provider: Provider,
124
- transcript: Message[],
125
- options: CompactTranscriptOptions = {},
126
- ): Promise<CompactTranscriptResult> {
127
- const nonSystem = transcript.filter(m => m.role !== 'system')
128
- if (nonSystem.length < 2) {
129
- return { ok: false, reason: 'not enough turns to compact' }
130
- }
131
-
132
- options.onStage?.('preparing transcript')
133
- const source = buildCompactionSource(transcript, provider.id, {
134
- maxInputTokens: options.maxInputTokens,
135
- })
136
- if (source.compressed) options.onStage?.('compressing long context')
137
-
138
- const prompt: Message[] = [
139
- { role: 'system', content: COMPACT_SYSTEM },
140
- { role: 'user', content: `Create a continuation handoff from this conversation context:\n\n${source.text}` },
141
- ]
142
-
143
- const controller = options.signal ? null : new AbortController()
144
- const signal = options.signal ?? controller!.signal
145
- let summary = ''
146
- const local = isLocalProviderId(provider.id)
147
- options.onStage?.('summarizing transcript')
148
- try {
149
- for await (const ev of provider.complete(prompt, signal, {
150
- maxTokens: options.maxOutputTokens ?? (local ? LOCAL_COMPACTION_OUTPUT_TOKENS : CLOUD_COMPACTION_OUTPUT_TOKENS),
151
- })) {
152
- if (signal.aborted) return { ok: false, reason: 'cancelled', cancelled: true, inputTokens: source.inputTokens, compressed: source.compressed }
153
- if (ev.type === 'text') summary += ev.delta
154
- else if (ev.type === 'error') return { ok: false, reason: ev.message }
155
- else if (ev.type === 'done') break
156
- }
157
- } catch (err: unknown) {
158
- if (signal.aborted) return { ok: false, reason: 'cancelled', cancelled: true, inputTokens: source.inputTokens, compressed: source.compressed }
159
- return { ok: false, reason: (err as Error).message || 'compact stream error' }
160
- }
161
-
162
- if (signal.aborted) return { ok: false, reason: 'cancelled', cancelled: true, inputTokens: source.inputTokens, compressed: source.compressed }
163
- summary = summary.trim()
164
- if (summary.length < 40) return { ok: false, reason: 'summary too short', inputTokens: source.inputTokens, compressed: source.compressed }
165
-
166
- return { ok: true, summary, inputTokens: source.inputTokens, compressed: source.compressed }
167
- }
168
-
169
- export function buildCompactionSource(
170
- transcript: Message[],
171
- providerId: Provider['id'],
172
- options: { maxInputTokens?: number } = {},
173
- ): CompactionSource {
174
- const nonSystem = transcript.filter(m => m.role !== 'system')
175
- const local = isLocalProviderId(providerId)
176
- const tokenBudget = options.maxInputTokens ?? (local ? LOCAL_COMPACTION_INPUT_TOKENS : CLOUD_COMPACTION_INPUT_TOKENS)
177
- const charBudget = Math.max(1_000, tokenBudget * 3)
178
- const recentMessageCount = local ? LOCAL_RECENT_MESSAGE_COUNT : CLOUD_RECENT_MESSAGE_COUNT
179
- const messageCharLimit = local ? LOCAL_MESSAGE_CHAR_LIMIT : CLOUD_MESSAGE_CHAR_LIMIT
180
- const rawTokenEstimate = approximateTokens(nonSystem)
181
- const mustCompress = rawTokenEstimate > tokenBudget || nonSystem.length > recentMessageCount
182
-
183
- if (!mustCompress) {
184
- const text = nonSystem.map((message, index) =>
185
- formatCompactionMessage(message, index + 1, messageCharLimit),
186
- ).join('\n\n')
187
- return {
188
- text,
189
- inputTokens: approximateTextTokens(text),
190
- compressed: false,
191
- }
192
- }
193
-
194
- const recent = nonSystem.slice(-recentMessageCount)
195
- const earlier = nonSystem.slice(0, Math.max(0, nonSystem.length - recent.length))
196
- const parts: string[] = []
197
- parts.push('Deterministic pre-summary of earlier context:')
198
- parts.push(summarizeTranscriptLocally(earlier.length > 0 ? earlier : nonSystem, 'input was bounded before model summarization'))
199
- parts.push('')
200
- parts.push('Recent transcript excerpts:')
201
- parts.push(...recent.map((message, index) =>
202
- formatCompactionMessage(message, nonSystem.length - recent.length + index + 1, messageCharLimit),
203
- ))
204
-
205
- const bounded = limitCompactionText(parts.join('\n\n'), charBudget)
206
- return {
207
- text: bounded,
208
- inputTokens: approximateTextTokens(bounded),
209
- compressed: true,
210
- }
211
- }
212
-
213
- export function summarizeTranscriptLocally(
214
- transcript: Message[],
215
- reason?: string,
216
- ): string {
217
- const nonSystem = transcript.filter(m => m.role !== 'system')
218
- const userRequests: string[] = []
219
- const assistantReplies: string[] = []
220
- const toolNotes: string[] = []
221
-
222
- for (const message of nonSystem) {
223
- const text = oneLine(messageTextContent(message), 240)
224
- if (!text) continue
225
- if (message.role === 'user') userRequests.push(text)
226
- else if (message.role === 'assistant') assistantReplies.push(text)
227
- else toolNotes.push(`${message.role}: ${text}`)
228
- }
229
-
230
- const parts = [
231
- 'Local conversation summary for continuation.',
232
- reason ? `Provider summary was unavailable: ${oneLine(reason, 180)}.` : '',
233
- ].filter(Boolean)
234
-
235
- if (userRequests.length > 0) {
236
- parts.push('Recent user requests:')
237
- parts.push(...userRequests.slice(-8).map(item => `- ${item}`))
238
- }
239
- if (assistantReplies.length > 0) {
240
- parts.push('Recent assistant progress:')
241
- parts.push(...assistantReplies.slice(-6).map(item => `- ${item}`))
242
- }
243
- if (toolNotes.length > 0) {
244
- parts.push('Recent tool context:')
245
- parts.push(...toolNotes.slice(-6).map(item => `- ${item}`))
246
- }
247
- parts.push('Continue from this summary, and verify current files or external state before relying on stale details.')
248
-
249
- return parts.join('\n')
250
- }
251
-
252
- export type MicroCompactOptions = {
253
- activeTurnId?: string
254
- }
255
-
256
- export type MicroCompactResult = {
257
- messages: SessionMessage[]
258
- compactedTurns: number
259
- }
260
-
261
- const RECENT_TURN_BUDGET = 15
262
- const TRIGGER_MESSAGE_COUNT = 50
263
-
264
- export function shouldMicroCompact(messages: SessionMessage[]): boolean {
265
- return messages.length > TRIGGER_MESSAGE_COUNT
266
- }
267
-
268
- export function microCompactSessionMessages(
269
- messages: SessionMessage[],
270
- options: MicroCompactOptions = {},
271
- ): MicroCompactResult {
272
- if (!shouldMicroCompact(messages)) {
273
- return { messages, compactedTurns: 0 }
274
- }
275
-
276
- const turnOrder: string[] = []
277
- const turnsSeen = new Set<string>()
278
- for (const message of messages) {
279
- if (message.role !== 'user') continue
280
- const turnId = (message as { turnId?: string }).turnId
281
- if (!turnId || turnsSeen.has(turnId)) continue
282
- turnsSeen.add(turnId)
283
- turnOrder.push(turnId)
284
- }
285
-
286
- if (turnOrder.length <= RECENT_TURN_BUDGET) {
287
- return { messages, compactedTurns: 0 }
288
- }
289
-
290
- const keepTurnIds = new Set(turnOrder.slice(turnOrder.length - RECENT_TURN_BUDGET))
291
- if (options.activeTurnId) keepTurnIds.add(options.activeTurnId)
292
-
293
- const oldMessages: SessionMessage[] = []
294
- const kept: SessionMessage[] = []
295
- for (const message of messages) {
296
- const turnId = (message as { turnId?: string }).turnId
297
- if (!turnId) {
298
- kept.push(message)
299
- continue
300
- }
301
- if (keepTurnIds.has(turnId)) {
302
- kept.push(message)
303
- } else {
304
- oldMessages.push(message)
305
- }
306
- }
307
-
308
- if (oldMessages.length === 0) {
309
- return { messages, compactedTurns: 0 }
310
- }
311
-
312
- const compactedTurns = new Set(
313
- oldMessages
314
- .map(message => (message as { turnId?: string }).turnId)
315
- .filter((id): id is string => typeof id === 'string'),
316
- )
317
-
318
- const summary = summarizeCompactedTurns(oldMessages)
319
- const firstKeptIndex = messages.findIndex(m => kept.includes(m))
320
- const summaryMessage: SessionMessage = {
321
- role: 'assistant',
322
- content: summary,
323
- createdAt: oldMessages[oldMessages.length - 1]!.createdAt,
324
- }
325
-
326
- const out: SessionMessage[] = []
327
- if (firstKeptIndex > 0) {
328
- out.push(...messages.slice(0, firstKeptIndex).filter(m => !oldMessages.includes(m)))
329
- }
330
- out.push(summaryMessage)
331
- out.push(...kept)
332
- return { messages: out, compactedTurns: compactedTurns.size }
333
- }
334
-
335
- function summarizeCompactedTurns(messages: SessionMessage[]): string {
336
- const userRequests: string[] = []
337
- const assistantReplies: string[] = []
338
-
339
- for (const message of messages) {
340
- if (message.role === 'user') {
341
- const content = typeof message.content === 'string' ? message.content : ''
342
- if (content.trim()) userRequests.push(oneLine(content, 140))
343
- } else if (message.role === 'assistant') {
344
- const content = typeof message.content === 'string' ? message.content : ''
345
- if (content.trim()) assistantReplies.push(oneLine(content, 140))
346
- }
347
- }
348
-
349
- const parts = ['[Earlier conversation context was compacted to fit the model\'s context window.]']
350
- if (userRequests.length > 0) {
351
- const sample = userRequests.slice(-5)
352
- parts.push(`Most recent earlier user requests: ${sample.map(item => `"${item}"`).join('; ')}`)
353
- }
354
- if (assistantReplies.length > 0) {
355
- const last = assistantReplies[assistantReplies.length - 1]!
356
- parts.push(`Most recent earlier assistant reply: "${last}"`)
357
- }
358
- parts.push('Treat the above as background; respond to the current user message next.')
359
- return parts.join(' ')
360
- }
361
-
362
- function oneLine(text: string, limit: number): string {
363
- const normalized = text.replace(/\s+/g, ' ').trim()
364
- if (normalized.length <= limit) return normalized
365
- return `${normalized.slice(0, Math.max(0, limit - 3))}...`
366
- }
367
-
368
- function isLocalProviderId(providerId: Provider['id']): boolean {
369
- return providerId === 'llamacpp'
370
- }
371
-
372
- function formatCompactionMessage(message: Message, index: number, limit: number): string {
373
- const text = oneLine(messageTextContent(message), limit)
374
- return `${index}. ${message.role}: ${text || '[empty]'}`
375
- }
376
-
377
- function limitCompactionText(text: string, charBudget: number): string {
378
- if (text.length <= charBudget) return text
379
- const recentHeader = 'Recent transcript excerpts:'
380
- const recentIndex = text.indexOf(recentHeader)
381
- if (recentIndex !== -1) {
382
- const prefixEnd = recentIndex + recentHeader.length
383
- const prefix = text.slice(0, prefixEnd)
384
- const marker = '[Earlier recent transcript excerpts omitted to keep local summarization responsive.]'
385
- const tailBudget = Math.max(0, charBudget - prefix.length - marker.length - 4)
386
- return `${prefix}\n\n${marker}\n\n${text.slice(Math.max(prefixEnd, text.length - tailBudget))}`
387
- }
388
- const marker = '[Earlier bounded compaction text omitted to keep local summarization responsive.]'
389
- const tailBudget = Math.max(0, charBudget - marker.length - 2)
390
- return `${marker}\n\n${text.slice(Math.max(0, text.length - tailBudget))}`
391
- }
392
-
393
- function approximateTextTokens(text: string): number {
394
- return Math.ceil(text.length / 3)
395
- }
@@ -1,43 +0,0 @@
1
- import fs from 'node:fs'
2
- import os from 'node:os'
3
- import path from 'node:path'
4
-
5
- let cwdState = normalizeExistingPath(process.cwd())
6
-
7
- export function getCwd(): string {
8
- return cwdState
9
- }
10
-
11
- export function syncCwdFromProcess(): string {
12
- cwdState = normalizeExistingPath(process.cwd())
13
- return cwdState
14
- }
15
-
16
- export function setCwd(next: string, relativeTo = cwdState): string {
17
- const resolved = normalizeRequestedPath(next, relativeTo)
18
- process.chdir(resolved)
19
- cwdState = normalizeExistingPath(process.cwd())
20
- return cwdState
21
- }
22
-
23
- export function resolveUserPath(input: string, relativeTo = cwdState): string {
24
- return normalizeRequestedPath(input, relativeTo)
25
- }
26
-
27
- function normalizeRequestedPath(input: string, relativeTo: string): string {
28
- const expanded = input.startsWith('~')
29
- ? path.join(os.homedir(), input.slice(1))
30
- : input
31
- const resolved = path.isAbsolute(expanded)
32
- ? expanded
33
- : path.resolve(relativeTo, expanded)
34
- return normalizeExistingPath(resolved)
35
- }
36
-
37
- function normalizeExistingPath(input: string): string {
38
- try {
39
- return fs.realpathSync.native(input)
40
- } catch {
41
- return path.resolve(input)
42
- }
43
- }
@@ -1,38 +0,0 @@
1
- import type { Message, Provider, StreamEvent } from '../providers/contracts.js'
2
- import type { ProviderTurnEvent } from './turnTypes.js'
3
-
4
- export async function* runProviderTurn(
5
- provider: Provider,
6
- messages: Message[],
7
- signal: AbortSignal,
8
- ): AsyncIterable<ProviderTurnEvent> {
9
- if (signal.aborted) {
10
- yield { type: 'cancelled' }
11
- return
12
- }
13
- for await (const ev of provider.complete(messages, signal)) {
14
- if (signal.aborted) {
15
- yield { type: 'cancelled' }
16
- return
17
- }
18
- yield normalize(ev)
19
- if (ev.type === 'done' || ev.type === 'error') return
20
- }
21
- if (signal.aborted) {
22
- yield { type: 'cancelled' }
23
- }
24
- }
25
-
26
- function normalize(event: StreamEvent): ProviderTurnEvent {
27
- switch (event.type) {
28
- case 'text': return { type: 'text', delta: event.delta }
29
- case 'thinking': return { type: 'thinking', delta: event.delta }
30
- case 'thinking_end': return { type: 'thinking_end' }
31
- case 'retry': return event
32
- case 'tool_use_start': return event
33
- case 'tool_use_delta': return event
34
- case 'tool_use_stop': return event
35
- case 'done': return { type: 'done', stopReason: event.stopReason }
36
- case 'error': return { type: 'error', message: event.message }
37
- }
38
- }
@@ -1,55 +0,0 @@
1
- import type { ToolKind } from '../tools/contracts.js'
2
-
3
- export type SessionMode = 'chat' | 'plan' | 'accept-edits'
4
-
5
- export type PermissionMode = 'default' | 'plan' | 'accept-edits'
6
-
7
- type PolicyMode = SessionMode | 'default'
8
-
9
- export type ModePolicy = {
10
- mode: PolicyMode
11
- exposesToolKind: (kind: ToolKind) => boolean
12
- autoAllowToolKind: (kind: ToolKind) => boolean
13
- promptLabel: string
14
- }
15
-
16
- export function toPermissionMode(mode: SessionMode): PermissionMode {
17
- if (mode === 'plan') return 'plan'
18
- if (mode === 'accept-edits') return 'accept-edits'
19
- return 'default'
20
- }
21
-
22
- export function nextSessionMode(mode: SessionMode): SessionMode {
23
- return mode === 'chat' ? 'plan' : mode === 'plan' ? 'accept-edits' : 'chat'
24
- }
25
-
26
- export function sessionModeLabel(mode: SessionMode): string {
27
- return mode === 'plan' ? 'plan mode' : mode === 'accept-edits' ? 'accept edits on' : ''
28
- }
29
-
30
- export function modePolicy(mode: PolicyMode): ModePolicy {
31
- if (mode === 'plan') {
32
- return {
33
- mode,
34
- exposesToolKind: kind => kind === 'read' || kind === 'private-continuity-read' || kind === 'mcp',
35
- autoAllowToolKind: kind => kind === 'private-continuity-read',
36
- promptLabel: 'plan mode',
37
- }
38
- }
39
-
40
- if (mode === 'accept-edits') {
41
- return {
42
- mode,
43
- exposesToolKind: () => true,
44
- autoAllowToolKind: kind => kind === 'read' || kind === 'edit' || kind === 'write' || kind === 'private-continuity-read',
45
- promptLabel: 'accept edits',
46
- }
47
- }
48
-
49
- return {
50
- mode,
51
- exposesToolKind: () => true,
52
- autoAllowToolKind: kind => kind === 'private-continuity-read',
53
- promptLabel: 'default chat',
54
- }
55
- }