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,77 +0,0 @@
1
- import { z } from 'zod'
2
- import path from 'node:path'
3
- import {
4
- continuityVaultRef,
5
- } from '../identity/continuity/storage.js'
6
- import { listSkillFiles } from '../identity/continuity/skills/loadSkills.js'
7
- import type { Tool } from './contracts.js'
8
-
9
- const schema = z.object({
10
- name: z.string().min(1),
11
- })
12
-
13
- export const listSkillFilesTool: Tool<typeof schema> = {
14
- name: 'list_private_skill_files',
15
- kind: 'private-continuity-read',
16
- readOnly: true,
17
- description: [
18
- 'List every file inside a private skill folder for the active identity.',
19
- 'Returns each file as `- <relativePath> (<bytes>) — <absolutePath>`, including SKILL.md and any supporting files (references, examples, scripts).',
20
- 'Use this after list_private_skills shows a skill with `(+N supporting files)` to discover what is available, then call read_private_skill with `file` to load text content.',
21
- 'Pass the absolute path directly to run_bash to execute supporting scripts (e.g. `python "<absolutePath>" <args>`).',
22
- ].join(' '),
23
- inputSchema: schema,
24
- inputSchemaJson: {
25
- type: 'object',
26
- properties: {
27
- name: { type: 'string', description: 'Skill folder name from the private skills index.' },
28
- },
29
- required: ['name'],
30
- additionalProperties: false,
31
- },
32
- parse(input) {
33
- return schema.parse(input)
34
- },
35
- async buildPermissionRequest(input, context) {
36
- const identity = context.config?.identity
37
- if (!identity) throw new Error('No active identity; create or load an identity before listing private skill files')
38
- const ref = continuityVaultRef(identity)
39
- const folder = input.name.replace(/^.*:/, '')
40
- const skillDir = path.join(ref.skillsDir, folder)
41
- return {
42
- kind: 'private-skill-read',
43
- path: skillDir,
44
- relativePath: `identity-vault/skills/${folder}`,
45
- directoryPath: skillDir,
46
- title: 'Allow private skill folder list?',
47
- subtitle: `List files in ${folder}/`,
48
- skillName: input.name,
49
- mode: 'list',
50
- }
51
- },
52
- async execute(input, context) {
53
- const identity = context.config?.identity
54
- if (!identity) {
55
- return { ok: false, summary: 'no active identity', content: 'No active identity; cannot list private skill files.' }
56
- }
57
- try {
58
- const folder = input.name.replace(/^.*:/, '')
59
- const files = await listSkillFiles(identity, folder)
60
- if (files.length === 0) {
61
- return { ok: true, summary: `no files in ${folder}`, content: `Skill folder ${folder}/ is empty or does not exist.` }
62
- }
63
- const lines = files.map(f => `- ${f.relativePath} (${f.sizeBytes} bytes) — ${f.absolutePath}`)
64
- return {
65
- ok: true,
66
- summary: `listed ${files.length} file${files.length === 1 ? '' : 's'} in ${folder}`,
67
- content: lines.join('\n'),
68
- }
69
- } catch (err: unknown) {
70
- return {
71
- ok: false,
72
- summary: 'list failed',
73
- content: (err as Error).message,
74
- }
75
- }
76
- },
77
- }
@@ -1,68 +0,0 @@
1
- import { z } from 'zod'
2
- import {
3
- continuityVaultRef,
4
- } from '../identity/continuity/storage.js'
5
- import { listSkillsTree } from '../identity/continuity/skills/loadSkills.js'
6
- import type { Tool } from './contracts.js'
7
-
8
- const schema = z.object({})
9
-
10
- export const listSkillsTool: Tool<typeof schema> = {
11
- name: 'list_private_skills',
12
- kind: 'private-continuity-read',
13
- readOnly: true,
14
- description: [
15
- 'List private skills available in the owner-authored skills tree for the active identity.',
16
- 'Returns each skill folder name with its one-line description; bodies are loaded separately via read_private_skill.',
17
- 'When a skill has supporting files beyond SKILL.md, the entry is annotated with (+N supporting files) so you know to call list_private_skill_files.',
18
- 'Use this when the user mentions a skill not in the injected skill index, or to discover what skills exist.',
19
- ].join(' '),
20
- inputSchema: schema,
21
- inputSchemaJson: {
22
- type: 'object',
23
- properties: {},
24
- additionalProperties: false,
25
- },
26
- parse(input) {
27
- return schema.parse(input ?? {})
28
- },
29
- async buildPermissionRequest(_input, context) {
30
- const identity = context.config?.identity
31
- if (!identity) throw new Error('No active identity; create or load an identity before listing private skills')
32
- const ref = continuityVaultRef(identity)
33
- return {
34
- kind: 'private-skill-read',
35
- path: ref.skillsDir,
36
- relativePath: 'identity-vault/skills',
37
- directoryPath: ref.skillsDir,
38
- title: 'Allow private skills index read?',
39
- subtitle: 'List skill names and descriptions from the private skills tree',
40
- skillName: '*',
41
- mode: 'list',
42
- }
43
- },
44
- async execute(_input, context) {
45
- const identity = context.config?.identity
46
- if (!identity) {
47
- return { ok: false, summary: 'no active identity', content: 'No active identity; cannot list private skills.' }
48
- }
49
- const { skills, supportingCounts } = await listSkillsTree(identity)
50
- if (skills.length === 0) {
51
- return { ok: true, summary: 'no private skills', content: 'The private skills tree is empty.' }
52
- }
53
- const lines = skills.map(entry => {
54
- const display = entry.displayName ?? entry.name
55
- const desc = entry.description ? ` — ${entry.description}` : ''
56
- const when = entry.whenToUse ? ` (when: ${entry.whenToUse})` : ''
57
- const vis = entry.visibility !== 'private' ? ` [visibility: ${entry.visibility}]` : ''
58
- const supporting = supportingCounts[entry.name] ?? 0
59
- const trailer = supporting > 0 ? ` (+${supporting} supporting file${supporting === 1 ? '' : 's'})` : ''
60
- return `- ${display}${desc}${when}${vis}${trailer}`
61
- })
62
- return {
63
- ok: true,
64
- summary: `listed ${skills.length} private skill${skills.length === 1 ? '' : 's'}`,
65
- content: lines.join('\n'),
66
- }
67
- },
68
- }
@@ -1,95 +0,0 @@
1
- import { z } from 'zod'
2
- import type { Tool } from './contracts.js'
3
- import { normalizeNameForMcp } from '../mcp/names.js'
4
-
5
- const ListMcpResourcesInput = z.object({
6
- server: z.string().min(1).optional(),
7
- })
8
-
9
- const ReadMcpResourceInput = z.object({
10
- server: z.string().min(1),
11
- uri: z.string().min(1),
12
- })
13
-
14
- export const listMcpResourcesTool: Tool<typeof ListMcpResourcesInput> = {
15
- name: 'list_mcp_resources',
16
- kind: 'mcp',
17
- readOnly: true,
18
- description: 'List resources exposed by connected MCP servers.',
19
- inputSchema: ListMcpResourcesInput,
20
- inputSchemaJson: {
21
- type: 'object',
22
- properties: {
23
- server: { type: 'string', description: 'Optional MCP server name. Omit to list resources from every connected server.' },
24
- },
25
- },
26
- parse(input) {
27
- return ListMcpResourcesInput.parse(input)
28
- },
29
- async buildPermissionRequest(input) {
30
- const serverName = input.server ?? '*'
31
- return {
32
- kind: 'mcp',
33
- title: 'Allow MCP resource listing?',
34
- subtitle: input.server ? `list resources from ${input.server}` : 'list resources from all connected MCP servers',
35
- serverName,
36
- normalizedServerName: normalizeNameForMcp(serverName),
37
- toolName: 'list_mcp_resources',
38
- toolKey: 'list_mcp_resources',
39
- readOnly: true,
40
- destructive: false,
41
- openWorld: false,
42
- canPersistServer: Boolean(input.server),
43
- }
44
- },
45
- async execute(input, context) {
46
- if (!context.mcp) return { ok: false, summary: 'MCP unavailable', content: 'MCP runtime is not available.' }
47
- return {
48
- ok: true,
49
- summary: input.server ? `listed MCP resources from ${input.server}` : 'listed MCP resources',
50
- content: await context.mcp.listResources(input.server),
51
- }
52
- },
53
- }
54
-
55
- export const readMcpResourceTool: Tool<typeof ReadMcpResourceInput> = {
56
- name: 'read_mcp_resource',
57
- kind: 'mcp',
58
- readOnly: true,
59
- description: 'Read a specific resource from a connected MCP server.',
60
- inputSchema: ReadMcpResourceInput,
61
- inputSchemaJson: {
62
- type: 'object',
63
- properties: {
64
- server: { type: 'string', description: 'The connected MCP server name.' },
65
- uri: { type: 'string', description: 'The resource URI to read.' },
66
- },
67
- required: ['server', 'uri'],
68
- },
69
- parse(input) {
70
- return ReadMcpResourceInput.parse(input)
71
- },
72
- async buildPermissionRequest(input) {
73
- return {
74
- kind: 'mcp',
75
- title: 'Allow MCP resource read?',
76
- subtitle: `${input.server} / ${input.uri}`,
77
- serverName: input.server,
78
- normalizedServerName: normalizeNameForMcp(input.server),
79
- toolName: 'read_mcp_resource',
80
- toolKey: `read_mcp_resource:${normalizeNameForMcp(input.server)}`,
81
- readOnly: true,
82
- destructive: false,
83
- openWorld: false,
84
- canPersistServer: true,
85
- }
86
- },
87
- async execute(input, context) {
88
- if (!context.mcp) return { ok: false, summary: 'MCP unavailable', content: 'MCP runtime is not available.' }
89
- return {
90
- ok: true,
91
- summary: `read MCP resource ${input.uri}`,
92
- content: await context.mcp.readResource(input.server, input.uri, context.abortSignal),
93
- }
94
- },
95
- }
@@ -1,85 +0,0 @@
1
- import path from 'node:path'
2
- import type { PermissionDecision, PermissionRequest, SessionPermissionRule } from './contracts.js'
3
-
4
- export function buildPermissionRule(
5
- decision: PermissionDecision,
6
- request: PermissionRequest,
7
- ): SessionPermissionRule | undefined {
8
- switch (decision) {
9
- case 'allow-kind-project':
10
- if (request.kind === 'read' || request.kind === 'edit' || request.kind === 'write' || request.kind === 'cd') return { kind: request.kind, scope: 'kind' }
11
- return undefined
12
- case 'allow-path-project':
13
- if (request.kind === 'read' || request.kind === 'edit' || request.kind === 'write' || request.kind === 'cd') {
14
- return { kind: request.kind, scope: 'path', path: request.path }
15
- }
16
- return undefined
17
- case 'allow-directory-project':
18
- if (request.kind === 'read' || request.kind === 'edit' || request.kind === 'write' || request.kind === 'cd') {
19
- return { kind: request.kind, scope: 'directory', path: request.directoryPath }
20
- }
21
- return undefined
22
- case 'allow-command-project':
23
- if (request.kind === 'bash' && request.canPersistExact) {
24
- return { kind: 'bash', scope: 'command', command: request.command, cwd: request.cwd }
25
- }
26
- return undefined
27
- case 'allow-command-prefix-project':
28
- if (request.kind === 'bash' && request.canPersistPrefix && request.commandPrefix) {
29
- return { kind: 'bash', scope: 'prefix', commandPrefix: request.commandPrefix, cwd: request.cwd }
30
- }
31
- return undefined
32
- case 'allow-mcp-tool-project':
33
- if (request.kind === 'mcp') return { kind: 'mcp', scope: 'tool', toolKey: request.toolKey }
34
- return undefined
35
- case 'allow-mcp-server-project':
36
- if (request.kind === 'mcp' && request.canPersistServer) {
37
- return { kind: 'mcp', scope: 'server', normalizedServerName: request.normalizedServerName }
38
- }
39
- return undefined
40
- default:
41
- return undefined
42
- }
43
- }
44
-
45
- export function matchPermissionRule(
46
- rules: SessionPermissionRule[],
47
- request: PermissionRequest,
48
- ): SessionPermissionRule | undefined {
49
- return rules.find(rule => matchesPermissionRule(rule, request))
50
- }
51
-
52
- export function shouldPersistPermissionDecision(decision: PermissionDecision): boolean {
53
- return decision !== 'allow-once' && decision !== 'deny'
54
- }
55
-
56
- function matchesPermissionRule(rule: SessionPermissionRule, request: PermissionRequest): boolean {
57
- if (rule.kind !== request.kind) return false
58
-
59
- if (request.kind === 'read' || request.kind === 'edit' || request.kind === 'write' || request.kind === 'delete' || request.kind === 'cd') {
60
- if (request.kind === 'delete') return false
61
- if (rule.scope === 'kind') return true
62
- if (rule.scope === 'path') return rule.path === request.path
63
- if (rule.scope === 'directory') {
64
- return request.path === rule.path || request.path.startsWith(`${rule.path}${path.sep}`)
65
- }
66
- return false
67
- }
68
-
69
- if (request.kind === 'bash') {
70
- if (rule.scope === 'command') return rule.command === request.command && rule.cwd === request.cwd
71
- if (rule.scope === 'prefix') {
72
- const normalizedCommand = request.command.trim()
73
- return (
74
- rule.cwd === request.cwd &&
75
- (normalizedCommand === rule.commandPrefix || normalizedCommand.startsWith(`${rule.commandPrefix} `))
76
- )
77
- }
78
- }
79
-
80
- if (request.kind === 'mcp') {
81
- if (rule.scope === 'tool') return rule.toolKey === request.toolKey
82
- if (rule.scope === 'server') return rule.normalizedServerName === request.normalizedServerName
83
- }
84
- return false
85
- }
@@ -1,187 +0,0 @@
1
- import { z } from 'zod'
2
- import {
3
- preparePrivateContinuityEdit,
4
- writePreparedPrivateContinuityEdit,
5
- } from '../identity/continuity/privateEdit.js'
6
- import { recordPrivateContinuityHistorySnapshot } from '../identity/continuity/history.js'
7
- import { readContinuityFiles, readAgentCardFile } from '../identity/continuity/storage.js'
8
- import type { Tool } from './contracts.js'
9
- import { formatFileChangeResult } from './fileDiff.js'
10
-
11
- const schema = z.object({
12
- file: z.preprocess(normalizePrivateContinuityFile, z.enum(['SOUL.md', 'MEMORY.md'])),
13
- oldText: z.string().optional(),
14
- newText: z.string().optional(),
15
- appendToSection: z.string().optional(),
16
- appendText: z.string().optional(),
17
- replaceAll: z.boolean().optional(),
18
- replaceWholeFile: z.boolean().optional(),
19
- }).superRefine((input, ctx) => {
20
- const hasOldText = input.oldText !== undefined
21
- const hasNewText = input.newText !== undefined
22
- const hasAppendToSection = input.appendToSection !== undefined
23
- const hasAppendText = input.appendText !== undefined
24
- const targeted = hasOldText || hasNewText
25
- const append = hasAppendToSection || hasAppendText
26
- if (input.replaceWholeFile) {
27
- ctx.addIssue({
28
- code: z.ZodIssueCode.custom,
29
- message: 'private continuity files must be edited in place; whole-file replacement is disabled',
30
- path: ['replaceWholeFile'],
31
- })
32
- }
33
- if (!targeted && !append) {
34
- ctx.addIssue({
35
- code: z.ZodIssueCode.custom,
36
- message: 'file alone is not enough; provide either oldText+newText for a targeted edit or appendToSection+appendText for an in-place append',
37
- })
38
- }
39
- if (targeted && append) {
40
- ctx.addIssue({
41
- code: z.ZodIssueCode.custom,
42
- message: 'provide only one edit mode: oldText+newText or appendToSection+appendText',
43
- })
44
- }
45
- if (targeted && !input.oldText?.trim()) {
46
- ctx.addIssue({
47
- code: z.ZodIssueCode.custom,
48
- message: 'oldText is required for targeted private continuity edits',
49
- path: ['oldText'],
50
- })
51
- }
52
- if (targeted && !hasNewText) {
53
- ctx.addIssue({
54
- code: z.ZodIssueCode.custom,
55
- message: 'newText is required for targeted private continuity edits',
56
- path: ['newText'],
57
- })
58
- }
59
- if (append && !input.appendToSection?.trim()) {
60
- ctx.addIssue({
61
- code: z.ZodIssueCode.custom,
62
- message: 'appendToSection is required for private continuity appends',
63
- path: ['appendToSection'],
64
- })
65
- }
66
- if (append && !input.appendText?.trim()) {
67
- ctx.addIssue({
68
- code: z.ZodIssueCode.custom,
69
- message: 'appendText is required for private continuity appends',
70
- path: ['appendText'],
71
- })
72
- }
73
- })
74
-
75
- export const privateContinuityEditTool: Tool<typeof schema> = {
76
- name: 'propose_private_continuity_edit',
77
- kind: 'private-continuity-edit',
78
- description: [
79
- 'Propose an explicit user-approved in-place edit to an existing private identity continuity scaffold.',
80
- 'Only the identity-vault SOUL.md and MEMORY.md are valid targets; do not create workspace files with these names.',
81
- 'Do not call read_file, list_directory, or run_bash to locate these files; pass file as SOUL.md or MEMORY.md and this tool resolves the vault path.',
82
- 'For new memories or preferences call exactly: {"file":"MEMORY.md","appendToSection":"Durable User Preferences","appendText":"- User preference or memory note."}.',
83
- 'For persona or standing behavior call exactly: {"file":"SOUL.md","appendToSection":"Persona","appendText":"- Persona or standing behavior note."}.',
84
- 'Prefer appendToSection+appendText to build on an existing scaffold section; use oldText+newText only for targeted replacement after exact text is known.',
85
- 'Whole-file replacement is disabled for private continuity.',
86
- 'Approved private continuity edits are not managed by /rewind.',
87
- ].join(' '),
88
- inputSchema: schema,
89
- inputSchemaJson: {
90
- type: 'object',
91
- properties: {
92
- file: { type: 'string', enum: ['SOUL.md', 'MEMORY.md'], description: 'Private continuity file to edit. Use only the file name; do not pass a workspace path.' },
93
- oldText: { type: 'string', description: 'Exact existing scaffold text to replace. Required for targeted replacement edits.' },
94
- newText: { type: 'string', description: 'Replacement text for oldText. Do not use this as whole-file content.' },
95
- appendToSection: { type: 'string', description: 'Existing markdown section heading to append under, for example "Durable User Preferences" or "Persona". Prefer this for new notes.' },
96
- appendText: { type: 'string', description: 'New non-empty markdown bullet to append under appendToSection. This builds on the existing scaffold instead of overwriting it.' },
97
- replaceAll: { type: 'boolean', description: 'Replace every exact oldText match. Prefer false unless certain.' },
98
- },
99
- required: ['file'],
100
- additionalProperties: false,
101
- },
102
- parse(input) {
103
- return schema.parse(normalizePrivateContinuityInput(input))
104
- },
105
- async buildPermissionRequest(input, context) {
106
- const prepared = await preparePrivateContinuityEdit(input, context.config)
107
- return {
108
- kind: 'private-continuity-edit',
109
- path: prepared.fullPath,
110
- relativePath: prepared.relativePath,
111
- directoryPath: prepared.directoryPath,
112
- title: 'Approve Private Continuity Edit?',
113
- subtitle: prepared.fullPath,
114
- file: prepared.file,
115
- before: prepared.previewBefore,
116
- after: prepared.previewAfter,
117
- diff: prepared.diff,
118
- changeSummary: prepared.changeSummary,
119
- }
120
- },
121
- async execute(input, context) {
122
- const prepared = await preparePrivateContinuityEdit(input, context.config)
123
- const [previousFiles, previousAgentCard] = await Promise.all([
124
- readContinuityFiles(prepared.identity),
125
- readAgentCardFile(prepared.identity),
126
- ])
127
- await recordPrivateContinuityHistorySnapshot({
128
- identity: prepared.identity,
129
- file: prepared.file,
130
- filePath: prepared.fullPath,
131
- existedBefore: prepared.existedBefore,
132
- previousContent: prepared.previousContent,
133
- previousFiles,
134
- previousAgentCard,
135
- changeSummary: prepared.changeSummary,
136
- sessionId: context.checkpoint?.sessionId,
137
- turnId: context.checkpoint?.turnId,
138
- promptSnippet: context.checkpoint?.promptSnippet,
139
- checkpointLabel: context.checkpoint?.checkpointLabel,
140
- })
141
- await writePreparedPrivateContinuityEdit(prepared)
142
- return {
143
- ok: true,
144
- summary: prepared.changeSummary,
145
- content: formatFileChangeResult(
146
- formatPrivateContinuityEditResult(prepared.file, prepared.fullPath),
147
- prepared.diff,
148
- ),
149
- }
150
- },
151
- }
152
-
153
- function formatPrivateContinuityEditResult(file: 'SOUL.md' | 'MEMORY.md', fullPath: string): string {
154
- return [
155
- '## Saved private continuity',
156
- '',
157
- `- File: \`identity-vault/${file}\``,
158
- `- Review file: \`${fullPath}\``,
159
- '- History: previous version saved to private identity history; `/rewind` does not restore identity continuity',
160
- '- Open: Identity Hub > Soul, Memory, and Skills',
161
- '- Save: Identity Hub > Recovery > Save Snapshot Now',
162
- ].join('\n')
163
- }
164
-
165
- function normalizePrivateContinuityInput(input: Record<string, unknown>): Record<string, unknown> {
166
- const normalized = { ...input }
167
- if (normalized.appendToSection === undefined) {
168
- normalized.appendToSection = normalized.section ?? normalized.heading
169
- }
170
- if (normalized.appendText === undefined) {
171
- normalized.appendText = normalized.note ?? normalized.text ?? normalized.content
172
- }
173
- for (const key of ['oldText', 'newText', 'appendToSection', 'appendText'] as const) {
174
- const value = normalized[key]
175
- if (typeof value === 'string' && value.trim() === '') {
176
- normalized[key] = undefined
177
- }
178
- }
179
- return normalized
180
- }
181
-
182
- function normalizePrivateContinuityFile(value: unknown): unknown {
183
- if (typeof value !== 'string') return value
184
- if (/^soul\.md$/i.test(value.trim())) return 'SOUL.md'
185
- if (/^memory\.md$/i.test(value.trim())) return 'MEMORY.md'
186
- return value
187
- }
@@ -1,106 +0,0 @@
1
- import path from 'node:path'
2
- import { z } from 'zod'
3
- import {
4
- continuityVaultRef,
5
- ensureContinuityFiles,
6
- } from '../identity/continuity/storage.js'
7
- import type { Tool } from './contracts.js'
8
-
9
- const schema = z.object({
10
- file: z.preprocess(normalizePrivateContinuityFile, z.enum(['SOUL.md', 'MEMORY.md'])),
11
- startLine: z.number().int().positive().optional(),
12
- endLine: z.number().int().positive().optional(),
13
- })
14
-
15
- export const privateContinuityReadTool: Tool<typeof schema> = {
16
- name: 'read_private_continuity_file',
17
- kind: 'private-continuity-read',
18
- description: [
19
- 'Read an explicitly user-approved private identity continuity file from the identity vault.',
20
- 'Only SOUL.md and MEMORY.md are valid targets; do not use workspace read_file for these files.',
21
- 'Use this before surgical removals or targeted replacements that need exact oldText.',
22
- 'Pass file as SOUL.md or MEMORY.md; this tool resolves the vault path.',
23
- 'Use startLine and endLine to limit the returned range when possible.',
24
- ].join(' '),
25
- inputSchema: schema,
26
- inputSchemaJson: {
27
- type: 'object',
28
- properties: {
29
- file: { type: 'string', enum: ['SOUL.md', 'MEMORY.md'], description: 'Private continuity file to read. Use only the file name; do not pass a workspace path.' },
30
- startLine: { type: 'number', description: 'Optional 1-based starting line.' },
31
- endLine: { type: 'number', description: 'Optional 1-based ending line.' },
32
- },
33
- required: ['file'],
34
- additionalProperties: false,
35
- },
36
- parse(input) {
37
- return schema.parse(normalizePrivateContinuityReadInput(input))
38
- },
39
- async buildPermissionRequest(input, context) {
40
- const prepared = preparePrivateContinuityRead(input, context.config)
41
- return {
42
- kind: 'private-continuity-read',
43
- path: prepared.fullPath,
44
- relativePath: prepared.relativePath,
45
- directoryPath: prepared.directoryPath,
46
- title: 'Allow private continuity read?',
47
- subtitle: input.startLine || input.endLine
48
- ? `${prepared.fullPath} · lines ${input.startLine ?? 1}-${input.endLine ?? 'end'}`
49
- : prepared.fullPath,
50
- file: input.file,
51
- range: input.startLine || input.endLine
52
- ? `lines ${input.startLine ?? 1}-${input.endLine ?? 'end'}`
53
- : 'entire file',
54
- }
55
- },
56
- async execute(input, context) {
57
- const prepared = preparePrivateContinuityRead(input, context.config)
58
- const files = await ensureContinuityFiles(prepared.identity)
59
- return {
60
- ok: true,
61
- summary: `read private ${input.file}`,
62
- content: numberedLineSlice(files[input.file], input.startLine, input.endLine),
63
- }
64
- },
65
- }
66
-
67
- function preparePrivateContinuityRead(
68
- input: z.infer<typeof schema>,
69
- config: Parameters<Tool['buildPermissionRequest']>[1]['config'],
70
- ) {
71
- const identity = config?.identity
72
- if (!identity) {
73
- throw new Error('No active identity; create or load an identity before reading private continuity files')
74
- }
75
- const ref = continuityVaultRef(identity)
76
- const fullPath = input.file === 'SOUL.md' ? ref.soulPath : ref.memoryPath
77
- return {
78
- identity,
79
- fullPath,
80
- relativePath: `identity-vault/${input.file}`,
81
- directoryPath: path.dirname(fullPath),
82
- }
83
- }
84
-
85
- function numberedLineSlice(content: string, startLine?: number, endLine?: number): string {
86
- const lines = content.replace(/\r\n/g, '\n').replace(/\r/g, '\n').split('\n')
87
- const start = Math.max(1, startLine ?? 1)
88
- const end = Math.max(start, endLine ?? lines.length)
89
- return lines.slice(start - 1, end).map((line, index) => `${start + index}: ${line}`).join('\n')
90
- }
91
-
92
- function normalizePrivateContinuityReadInput(input: Record<string, unknown>): Record<string, unknown> {
93
- const normalized = { ...input }
94
- if (normalized.file === undefined) {
95
- normalized.file = normalized.path ?? normalized.name
96
- }
97
- return normalized
98
- }
99
-
100
- function normalizePrivateContinuityFile(value: unknown): unknown {
101
- if (typeof value !== 'string') return value
102
- const basename = path.basename(value.replaceAll('\\', '/')).trim()
103
- if (/^soul\.md$/i.test(basename)) return 'SOUL.md'
104
- if (/^memory\.md$/i.test(basename)) return 'MEMORY.md'
105
- return value
106
- }