kumo-cli 1.0.6 → 1.2.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 (241) hide show
  1. package/README.md +26 -13
  2. package/dist/auth/credentialsStore.js +20 -7
  3. package/dist/auth/credentialsStore.js.map +1 -1
  4. package/dist/claude/conversation.js +272 -0
  5. package/dist/claude/conversation.js.map +1 -0
  6. package/dist/claude/transcriptParser.js +140 -36
  7. package/dist/claude/transcriptParser.js.map +1 -1
  8. package/dist/commands/cliArgs.js +1 -0
  9. package/dist/commands/cliArgs.js.map +1 -1
  10. package/dist/core/config.js +1 -9
  11. package/dist/core/config.js.map +1 -1
  12. package/dist/core/logger.js +50 -21
  13. package/dist/core/logger.js.map +1 -1
  14. package/dist/core/projectFileWatcher.js +100 -36
  15. package/dist/core/projectFileWatcher.js.map +1 -1
  16. package/dist/engine/claudeArgs.js +106 -0
  17. package/dist/engine/claudeArgs.js.map +1 -0
  18. package/dist/engine/claudeBinary.js +94 -0
  19. package/dist/engine/claudeBinary.js.map +1 -0
  20. package/dist/engine/claudeConfig.js +127 -0
  21. package/dist/engine/claudeConfig.js.map +1 -0
  22. package/dist/engine/claudeLogin.js +76 -0
  23. package/dist/engine/claudeLogin.js.map +1 -0
  24. package/dist/engine/controlChannel.js +75 -0
  25. package/dist/engine/controlChannel.js.map +1 -0
  26. package/dist/engine/liveTranscript.js +150 -0
  27. package/dist/engine/liveTranscript.js.map +1 -0
  28. package/dist/engine/ndjson.js +48 -0
  29. package/dist/engine/ndjson.js.map +1 -0
  30. package/dist/engine/permissions.js +101 -0
  31. package/dist/engine/permissions.js.map +1 -0
  32. package/dist/engine/probePool.js +51 -0
  33. package/dist/engine/probePool.js.map +1 -0
  34. package/dist/engine/processTree.js +45 -0
  35. package/dist/engine/processTree.js.map +1 -0
  36. package/dist/engine/remoteConfig.js +17 -0
  37. package/dist/engine/remoteConfig.js.map +1 -0
  38. package/dist/engine/sessionManager.js +444 -0
  39. package/dist/engine/sessionManager.js.map +1 -0
  40. package/dist/engine/settingsCommands.js +260 -0
  41. package/dist/engine/settingsCommands.js.map +1 -0
  42. package/dist/engine/streamSession.js +502 -0
  43. package/dist/engine/streamSession.js.map +1 -0
  44. package/dist/engine/taskTracker.js +110 -0
  45. package/dist/engine/taskTracker.js.map +1 -0
  46. package/dist/engine/toolLabels.js +32 -0
  47. package/dist/engine/toolLabels.js.map +1 -0
  48. package/dist/handlers/controlRequest.js +17 -0
  49. package/dist/handlers/controlRequest.js.map +1 -0
  50. package/dist/handlers/fileHandlers.js +12 -0
  51. package/dist/handlers/fileHandlers.js.map +1 -1
  52. package/dist/handlers/messageRouter.js +8 -1
  53. package/dist/handlers/messageRouter.js.map +1 -1
  54. package/dist/handlers/promptImages.js +5 -91
  55. package/dist/handlers/promptImages.js.map +1 -1
  56. package/dist/handlers/runtimeHandlers.js +100 -32
  57. package/dist/handlers/runtimeHandlers.js.map +1 -1
  58. package/dist/handlers/sessionHandlers.js +85 -80
  59. package/dist/handlers/sessionHandlers.js.map +1 -1
  60. package/dist/handlers/terminalHandlers.js +9 -2
  61. package/dist/handlers/terminalHandlers.js.map +1 -1
  62. package/dist/http/httpClient.js +6 -0
  63. package/dist/http/httpClient.js.map +1 -1
  64. package/dist/index.js +87 -50
  65. package/dist/index.js.map +1 -1
  66. package/dist/pty/terminalManager.js +58 -11
  67. package/dist/pty/terminalManager.js.map +1 -1
  68. package/dist/services/bootstrapService.js +17 -1
  69. package/dist/services/bootstrapService.js.map +1 -1
  70. package/dist/services/claudeAdapterProxy.js +68 -21
  71. package/dist/services/claudeAdapterProxy.js.map +1 -1
  72. package/dist/services/cliRuntime.js +39 -32
  73. package/dist/services/cliRuntime.js.map +1 -1
  74. package/dist/services/effortService.js +13 -60
  75. package/dist/services/effortService.js.map +1 -1
  76. package/dist/services/fileService.js +50 -24
  77. package/dist/services/fileService.js.map +1 -1
  78. package/dist/services/forkService.js +120 -0
  79. package/dist/services/forkService.js.map +1 -0
  80. package/dist/services/modelService.js +86 -89
  81. package/dist/services/modelService.js.map +1 -1
  82. package/dist/services/pairingService.js +21 -15
  83. package/dist/services/pairingService.js.map +1 -1
  84. package/dist/services/sessionListService.js +7 -9
  85. package/dist/services/sessionListService.js.map +1 -1
  86. package/dist/services/sessionService.js +245 -106
  87. package/dist/services/sessionService.js.map +1 -1
  88. package/dist/services/tunnelHelpers.js +9 -5
  89. package/dist/services/tunnelHelpers.js.map +1 -1
  90. package/dist/services/tunnelService.js +21 -5
  91. package/dist/services/tunnelService.js.map +1 -1
  92. package/dist/transport/wsClient.js +60 -42
  93. package/dist/transport/wsClient.js.map +1 -1
  94. package/dist/utils/ignorePaths.js +3 -3
  95. package/dist/utils/ignorePaths.js.map +1 -1
  96. package/dist/utils/json.js +7 -0
  97. package/dist/utils/json.js.map +1 -0
  98. package/dist/utils/localHost.js +17 -0
  99. package/dist/utils/localHost.js.map +1 -0
  100. package/dist/utils/safePath.js +51 -20
  101. package/dist/utils/safePath.js.map +1 -1
  102. package/package.json +5 -12
  103. package/dist/ansiCodes.js +0 -107
  104. package/dist/ansiCodes.js.map +0 -1
  105. package/dist/auth/credentialsStore.test.js +0 -60
  106. package/dist/auth/credentialsStore.test.js.map +0 -1
  107. package/dist/claude/fileHistory.js +0 -79
  108. package/dist/claude/fileHistory.js.map +0 -1
  109. package/dist/claude/generateHookSettings.js +0 -23
  110. package/dist/claude/generateHookSettings.js.map +0 -1
  111. package/dist/claude/hookServer.js +0 -33
  112. package/dist/claude/hookServer.js.map +0 -1
  113. package/dist/claude/sessionScanner.js +0 -162
  114. package/dist/claude/sessionScanner.js.map +0 -1
  115. package/dist/claude/sessionScanner.test.js +0 -24
  116. package/dist/claude/sessionScanner.test.js.map +0 -1
  117. package/dist/claude/transcriptParser.test.js +0 -78
  118. package/dist/claude/transcriptParser.test.js.map +0 -1
  119. package/dist/commands/cliArgs.test.js +0 -25
  120. package/dist/commands/cliArgs.test.js.map +0 -1
  121. package/dist/config.js +0 -40
  122. package/dist/config.js.map +0 -1
  123. package/dist/credentialsStore.js +0 -57
  124. package/dist/credentialsStore.js.map +0 -1
  125. package/dist/generateHookSettings.js +0 -30
  126. package/dist/generateHookSettings.js.map +0 -1
  127. package/dist/handlers/fileHandlerPayloads.test.js +0 -55
  128. package/dist/handlers/fileHandlerPayloads.test.js.map +0 -1
  129. package/dist/handlers/promptImages.test.js +0 -52
  130. package/dist/handlers/promptImages.test.js.map +0 -1
  131. package/dist/hookServer.js +0 -32
  132. package/dist/hookServer.js.map +0 -1
  133. package/dist/ignorePaths.js +0 -68
  134. package/dist/ignorePaths.js.map +0 -1
  135. package/dist/loadDotEnv.js +0 -79
  136. package/dist/loadDotEnv.js.map +0 -1
  137. package/dist/logger.js +0 -29
  138. package/dist/logger.js.map +0 -1
  139. package/dist/portForwardManager.js +0 -97
  140. package/dist/portForwardManager.js.map +0 -1
  141. package/dist/projectHistoryReader.js +0 -85
  142. package/dist/projectHistoryReader.js.map +0 -1
  143. package/dist/pty/ansiCodes.js +0 -14
  144. package/dist/pty/ansiCodes.js.map +0 -1
  145. package/dist/pty/notificationDetector.js +0 -29
  146. package/dist/pty/notificationDetector.js.map +0 -1
  147. package/dist/pty/notificationDetector.test.js +0 -24
  148. package/dist/pty/notificationDetector.test.js.map +0 -1
  149. package/dist/pty/ptyMenuParser.js +0 -91
  150. package/dist/pty/ptyMenuParser.js.map +0 -1
  151. package/dist/pty/ptyMenuParser.test.js +0 -26
  152. package/dist/pty/ptyMenuParser.test.js.map +0 -1
  153. package/dist/pty/ptySnapshotRenderer.js +0 -71
  154. package/dist/pty/ptySnapshotRenderer.js.map +0 -1
  155. package/dist/pty/selectionMenuDetector.js +0 -49
  156. package/dist/pty/selectionMenuDetector.js.map +0 -1
  157. package/dist/pty/selectionMenuDetector.test.js +0 -62
  158. package/dist/pty/selectionMenuDetector.test.js.map +0 -1
  159. package/dist/pty/selectionMenuMeta.js +0 -54
  160. package/dist/pty/selectionMenuMeta.js.map +0 -1
  161. package/dist/pty/selectionMenuMeta.test.js +0 -26
  162. package/dist/pty/selectionMenuMeta.test.js.map +0 -1
  163. package/dist/pty/selectionMenuParser.js +0 -83
  164. package/dist/pty/selectionMenuParser.js.map +0 -1
  165. package/dist/pty/selectionMenuParser.test.js +0 -30
  166. package/dist/pty/selectionMenuParser.test.js.map +0 -1
  167. package/dist/pty/spawn.js +0 -104
  168. package/dist/pty/spawn.js.map +0 -1
  169. package/dist/pty/spawn.test.js +0 -17
  170. package/dist/pty/spawn.test.js.map +0 -1
  171. package/dist/pty/statusSessionDetector.js +0 -28
  172. package/dist/pty/statusSessionDetector.js.map +0 -1
  173. package/dist/pty/statusSessionDetector.test.js +0 -23
  174. package/dist/pty/statusSessionDetector.test.js.map +0 -1
  175. package/dist/pty/textInputDetector.js +0 -60
  176. package/dist/pty/textInputDetector.js.map +0 -1
  177. package/dist/pty/textInputDetector.test.js +0 -32
  178. package/dist/pty/textInputDetector.test.js.map +0 -1
  179. package/dist/ptyMenuParser.js +0 -470
  180. package/dist/ptyMenuParser.js.map +0 -1
  181. package/dist/ptySnapshotRenderer.js +0 -78
  182. package/dist/ptySnapshotRenderer.js.map +0 -1
  183. package/dist/services/checkpointService.js +0 -113
  184. package/dist/services/checkpointService.js.map +0 -1
  185. package/dist/services/checkpointService.test.js +0 -61
  186. package/dist/services/checkpointService.test.js.map +0 -1
  187. package/dist/services/claudeMenuBridge.js +0 -116
  188. package/dist/services/claudeMenuBridge.js.map +0 -1
  189. package/dist/services/claudeMenuBridge.test.js +0 -90
  190. package/dist/services/claudeMenuBridge.test.js.map +0 -1
  191. package/dist/services/claudeService.js +0 -146
  192. package/dist/services/claudeService.js.map +0 -1
  193. package/dist/services/claudeService.test.js +0 -39
  194. package/dist/services/claudeService.test.js.map +0 -1
  195. package/dist/services/cliRuntime.test.js +0 -55
  196. package/dist/services/cliRuntime.test.js.map +0 -1
  197. package/dist/services/modelAdapter.test.js +0 -47
  198. package/dist/services/modelAdapter.test.js.map +0 -1
  199. package/dist/services/modelArgs.test.js +0 -64
  200. package/dist/services/modelArgs.test.js.map +0 -1
  201. package/dist/services/modelBaseUrl.test.js +0 -32
  202. package/dist/services/modelBaseUrl.test.js.map +0 -1
  203. package/dist/services/modelService.test.js +0 -136
  204. package/dist/services/modelService.test.js.map +0 -1
  205. package/dist/services/notificationDedupe.js +0 -23
  206. package/dist/services/notificationDedupe.js.map +0 -1
  207. package/dist/services/notificationDedupe.test.js +0 -20
  208. package/dist/services/notificationDedupe.test.js.map +0 -1
  209. package/dist/services/sessionListService.test.js +0 -22
  210. package/dist/services/sessionListService.test.js.map +0 -1
  211. package/dist/services/sessionRollbackService.js +0 -195
  212. package/dist/services/sessionRollbackService.js.map +0 -1
  213. package/dist/services/sessionRollbackService.test.js +0 -142
  214. package/dist/services/sessionRollbackService.test.js.map +0 -1
  215. package/dist/services/tunnelHelpers.test.js +0 -42
  216. package/dist/services/tunnelHelpers.test.js.map +0 -1
  217. package/dist/services/tunnelService.test.js +0 -91
  218. package/dist/services/tunnelService.test.js.map +0 -1
  219. package/dist/sessionScanner.js +0 -271
  220. package/dist/sessionScanner.js.map +0 -1
  221. package/dist/snapshotScanner.js +0 -154
  222. package/dist/snapshotScanner.js.map +0 -1
  223. package/dist/spawn.js +0 -79
  224. package/dist/spawn.js.map +0 -1
  225. package/dist/terminalManager.js +0 -73
  226. package/dist/terminalManager.js.map +0 -1
  227. package/dist/transport/directWsServer.js +0 -135
  228. package/dist/transport/directWsServer.js.map +0 -1
  229. package/dist/transport/webrtcTransport.js +0 -145
  230. package/dist/transport/webrtcTransport.js.map +0 -1
  231. package/dist/transport/webrtcTransport.test.js +0 -104
  232. package/dist/transport/webrtcTransport.test.js.map +0 -1
  233. package/dist/transport/webrtcTypes.js +0 -29
  234. package/dist/transport/webrtcTypes.js.map +0 -1
  235. package/dist/transport/webrtcTypes.test.js +0 -20
  236. package/dist/transport/webrtcTypes.test.js.map +0 -1
  237. package/dist/utils/ptyBuffer.js +0 -46
  238. package/dist/utils/ptyBuffer.js.map +0 -1
  239. package/scripts/claude_launcher.cjs +0 -71
  240. package/scripts/run-tests.mjs +0 -46
  241. package/scripts/session_hook_forwarder.cjs +0 -44
@@ -1,55 +0,0 @@
1
- import test from 'node:test';
2
- import assert from 'node:assert/strict';
3
- import { createSessionStartHandler, createWsOpenHandler, ensureClaudeArgs } from './cliRuntime.js';
4
- test('ensureClaudeArgs appends the bypass flag only once', () => {
5
- assert.deepEqual(ensureClaudeArgs(['--debug']), ['--debug', '--dangerously-skip-permissions']);
6
- assert.deepEqual(ensureClaudeArgs(['--dangerously-skip-permissions']), ['--dangerously-skip-permissions']);
7
- });
8
- test('createSessionStartHandler replays pending prompt and announces assistant completion', async () => {
9
- const sent = [];
10
- const dispatched = [];
11
- const state = {
12
- currentSessionId: null,
13
- pendingPrompt: 'hello',
14
- startScannerAtEnd: false,
15
- scanner: null,
16
- };
17
- const handler = createSessionStartHandler({
18
- state,
19
- send: (data) => sent.push(data),
20
- resetSnapshot: () => { },
21
- dispatchPrompt: (text) => dispatched.push(text),
22
- scannerFactory: () => ({
23
- stop() { },
24
- on(_event, handler) {
25
- handler({ role: 'assistant', stopReason: 'end_turn' });
26
- return this;
27
- },
28
- start() { },
29
- }),
30
- });
31
- handler('session-1');
32
- assert.equal(state.currentSessionId, 'session-1');
33
- assert.equal(state.pendingPrompt, null);
34
- assert.equal(dispatched[0], 'hello');
35
- assert.equal(sent[0]?.type, 'session_start');
36
- });
37
- test('createWsOpenHandler registers the cli and sends a session list', async () => {
38
- const sent = [];
39
- let menuReset = false;
40
- let watcherRestarted = false;
41
- const handler = createWsOpenHandler({
42
- send: (data) => sent.push(data),
43
- credentials: () => ({ deviceUuid: 'device-1', deviceToken: 'token-1', serverUrl: 'http://localhost' }),
44
- currentSessionId: () => 'session-1',
45
- resetMenu: () => { menuReset = true; },
46
- restartWatcher: () => { watcherRestarted = true; },
47
- });
48
- await handler();
49
- assert.equal(sent[0]?.type, 'register');
50
- assert.equal(sent[0]?.deviceToken, 'token-1');
51
- assert.equal(sent.at(-1)?.type, 'session_list');
52
- assert.equal(menuReset, true);
53
- assert.equal(watcherRestarted, true);
54
- });
55
- //# sourceMappingURL=cliRuntime.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"cliRuntime.test.js","sourceRoot":"","sources":["../../src/services/cliRuntime.test.ts"],"names":[],"mappings":"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"}
@@ -1,47 +0,0 @@
1
- import test from 'node:test';
2
- import assert from 'node:assert/strict';
3
- import { convertRequestToOpenAI } from 'claude-adapter';
4
- test('xml tool format avoids tool role messages for strict providers', () => {
5
- const request = {
6
- model: 'claude-sonnet-4',
7
- max_tokens: 128,
8
- messages: [
9
- { role: 'user', content: 'list files' },
10
- {
11
- role: 'assistant',
12
- content: [
13
- {
14
- type: 'tool_use',
15
- id: 'toolu_1',
16
- name: 'ls',
17
- input: { path: '.' },
18
- },
19
- ],
20
- },
21
- {
22
- role: 'user',
23
- content: [
24
- {
25
- type: 'tool_result',
26
- tool_use_id: 'toolu_1',
27
- content: 'package.json',
28
- },
29
- ],
30
- },
31
- ],
32
- tools: [
33
- {
34
- name: 'ls',
35
- description: 'list files',
36
- input_schema: {
37
- type: 'object',
38
- properties: { path: { type: 'string' } },
39
- },
40
- },
41
- ],
42
- };
43
- const converted = convertRequestToOpenAI(request, 'deepseek-v4-flash-free', 'xml');
44
- assert.deepEqual([...new Set(converted.messages.map((message) => message.role))].sort(), ['assistant', 'system', 'user']);
45
- assert.equal(converted.messages.some((message) => message.role === 'tool'), false);
46
- });
47
- //# sourceMappingURL=modelAdapter.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"modelAdapter.test.js","sourceRoot":"","sources":["../../src/services/modelAdapter.test.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAC;AAGxD,IAAI,CAAC,gEAAgE,EAAE,GAAG,EAAE;IAC1E,MAAM,OAAO,GAA4B;QACvC,KAAK,EAAE,iBAAiB;QACxB,UAAU,EAAE,GAAG;QACf,QAAQ,EAAE;YACR,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,EAAE;YACvC;gBACE,IAAI,EAAE,WAAW;gBACjB,OAAO,EAAE;oBACP;wBACE,IAAI,EAAE,UAAU;wBAChB,EAAE,EAAE,SAAS;wBACb,IAAI,EAAE,IAAI;wBACV,KAAK,EAAE,EAAE,IAAI,EAAE,GAAG,EAAE;qBACrB;iBACF;aACF;YACD;gBACE,IAAI,EAAE,MAAM;gBACZ,OAAO,EAAE;oBACP;wBACE,IAAI,EAAE,aAAa;wBACnB,WAAW,EAAE,SAAS;wBACtB,OAAO,EAAE,cAAc;qBACxB;iBACF;aACF;SACF;QACD,KAAK,EAAE;YACL;gBACE,IAAI,EAAE,IAAI;gBACV,WAAW,EAAE,YAAY;gBACzB,YAAY,EAAE;oBACZ,IAAI,EAAE,QAAQ;oBACd,UAAU,EAAE,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE;iBACzC;aACF;SACF;KACF,CAAC;IACF,MAAM,SAAS,GAAG,sBAAsB,CAAC,OAAO,EAAE,wBAAwB,EAAE,KAAK,CAAC,CAAC;IACnF,MAAM,CAAC,SAAS,CACd,CAAC,GAAG,IAAI,GAAG,CAAC,SAAS,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,EACtE,CAAC,WAAW,EAAE,QAAQ,EAAE,MAAM,CAAC,CAChC,CAAC;IACF,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,KAAK,MAAM,CAAC,EAAE,KAAK,CAAC,CAAC;AACrF,CAAC,CAAC,CAAC"}
@@ -1,64 +0,0 @@
1
- import test from 'node:test';
2
- import assert from 'node:assert/strict';
3
- import { ModelService } from './modelService.js';
4
- test('apply replaces legacy model args with the selected model flag', async () => {
5
- const service = new ModelService();
6
- const args = service.apply({
7
- model: 'claude-sonnet-4',
8
- baseURL: 'https://provider.example.com/custom',
9
- apiKey: 'sk-test',
10
- }, ['--verbose', '--model', 'legacy-model']);
11
- const env = await service.buildEnv();
12
- assert.deepEqual(args, ['--verbose', '--model', 'claude-sonnet-4']);
13
- assert.equal(env.ANTHROPIC_BASE_URL, 'https://provider.example.com/custom');
14
- assert.equal(env.ANTHROPIC_API_KEY, 'sk-test');
15
- await service.dispose();
16
- });
17
- test('apply accepts payloads without legacy model name', async () => {
18
- const service = new ModelService();
19
- const args = service.apply({
20
- baseURL: 'https://api.example.com/root/v1/messages',
21
- apiKey: 'sk-test',
22
- }, ['-m', 'legacy-model', '--debug']);
23
- const env = await service.buildEnv();
24
- assert.deepEqual(args, ['--debug']);
25
- assert.equal(env.ANTHROPIC_BASE_URL, 'https://api.example.com/root');
26
- assert.equal(env.ANTHROPIC_API_KEY, 'sk-test');
27
- await service.dispose();
28
- });
29
- test('apply keeps no-set requests free of model flags', async () => {
30
- const service = new ModelService();
31
- const args = service.apply(null, ['--model', 'legacy-model', '--debug']);
32
- const env = await service.buildEnv();
33
- assert.deepEqual(args, ['--debug']);
34
- assert.equal(service.getCurrent(), null);
35
- assert.equal(env.ANTHROPIC_BASE_URL, undefined);
36
- assert.equal(env.ANTHROPIC_API_KEY, undefined);
37
- await service.dispose();
38
- });
39
- test('ensureBeforeSpawn does not fallback to default after no-set request', async () => {
40
- const service = new ModelService();
41
- service.apply(null, ['--debug']);
42
- service.fetchDefault = async () => ({
43
- model: 'claude-sonnet-4',
44
- baseURL: 'https://provider.example.com/custom',
45
- apiKey: 'sk-test',
46
- });
47
- const args = await service.ensureBeforeSpawn(['--debug'], 'device-token');
48
- assert.deepEqual(args, ['--debug']);
49
- assert.equal(service.getCurrent(), null);
50
- await service.dispose();
51
- });
52
- test('ensureBeforeSpawn still loads default when no selection was applied', async () => {
53
- const service = new ModelService();
54
- service.fetchDefault = async () => ({
55
- model: 'claude-sonnet-4',
56
- baseURL: 'https://provider.example.com/custom',
57
- apiKey: 'sk-test',
58
- });
59
- const args = await service.ensureBeforeSpawn(['--debug'], 'device-token');
60
- assert.deepEqual(args, ['--debug', '--model', 'claude-sonnet-4']);
61
- assert.equal(service.getCurrent()?.model, 'claude-sonnet-4');
62
- await service.dispose();
63
- });
64
- //# sourceMappingURL=modelArgs.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"modelArgs.test.js","sourceRoot":"","sources":["../../src/services/modelArgs.test.ts"],"names":[],"mappings":"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"}
@@ -1,32 +0,0 @@
1
- import test from 'node:test';
2
- import assert from 'node:assert/strict';
3
- import { ModelService } from './modelService.js';
4
- test('strips v1 messages suffix for Claude-compatible providers', async () => {
5
- const service = new ModelService();
6
- const url = await service.resolveClaudeBaseURL({
7
- model: 'claude-haiku-4.5',
8
- baseURL: 'https://api.example.com/root/v1/messages',
9
- apiKey: 'sk-test',
10
- });
11
- assert.equal(url, 'https://api.example.com/root');
12
- });
13
- test('uses local adapter proxy for OpenAI chat completions providers', async () => {
14
- const service = new ModelService();
15
- const url = await service.resolveClaudeBaseURL({
16
- model: 'deepseek-v4-flash-free',
17
- baseURL: 'https://opencode.ai/zen/v1/chat/completions',
18
- apiKey: 'public',
19
- });
20
- assert.match(url, /^http:\/\/127\.0\.0\.1:\d+$/);
21
- await service.dispose();
22
- });
23
- test('keeps unknown provider url unchanged', async () => {
24
- const service = new ModelService();
25
- const url = await service.resolveClaudeBaseURL({
26
- model: 'custom',
27
- baseURL: 'https://provider.example.com/custom',
28
- apiKey: 'sk-test',
29
- });
30
- assert.equal(url, 'https://provider.example.com/custom');
31
- });
32
- //# sourceMappingURL=modelBaseUrl.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"modelBaseUrl.test.js","sourceRoot":"","sources":["../../src/services/modelBaseUrl.test.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEjD,IAAI,CAAC,2DAA2D,EAAE,KAAK,IAAI,EAAE;IAC3E,MAAM,OAAO,GAAG,IAAI,YAAY,EAAE,CAAC;IACnC,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,oBAAoB,CAAC;QAC7C,KAAK,EAAE,kBAAkB;QACzB,OAAO,EAAE,0CAA0C;QACnD,MAAM,EAAE,SAAS;KAClB,CAAC,CAAC;IACH,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,8BAA8B,CAAC,CAAC;AACpD,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,gEAAgE,EAAE,KAAK,IAAI,EAAE;IAChF,MAAM,OAAO,GAAG,IAAI,YAAY,EAAE,CAAC;IACnC,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,oBAAoB,CAAC;QAC7C,KAAK,EAAE,wBAAwB;QAC/B,OAAO,EAAE,6CAA6C;QACtD,MAAM,EAAE,QAAQ;KACjB,CAAC,CAAC;IACH,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,6BAA6B,CAAC,CAAC;IACjD,MAAM,OAAO,CAAC,OAAO,EAAE,CAAC;AAC1B,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,sCAAsC,EAAE,KAAK,IAAI,EAAE;IACtD,MAAM,OAAO,GAAG,IAAI,YAAY,EAAE,CAAC;IACnC,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,oBAAoB,CAAC;QAC7C,KAAK,EAAE,QAAQ;QACf,OAAO,EAAE,qCAAqC;QAC9C,MAAM,EAAE,SAAS;KAClB,CAAC,CAAC;IACH,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,qCAAqC,CAAC,CAAC;AAC3D,CAAC,CAAC,CAAC"}
@@ -1,136 +0,0 @@
1
- import test from 'node:test';
2
- import assert from 'node:assert/strict';
3
- import { ModelService } from './modelService.js';
4
- import { convertRequestToOpenAI } from 'claude-adapter';
5
- test('strips v1 messages suffix for Claude-compatible providers', async () => {
6
- const service = new ModelService();
7
- const url = await service.resolveClaudeBaseURL({
8
- model: 'claude-haiku-4.5',
9
- baseURL: 'https://api.example.com/root/v1/messages',
10
- apiKey: 'sk-test',
11
- });
12
- assert.equal(url, 'https://api.example.com/root');
13
- });
14
- test('uses local adapter proxy for OpenAI chat completions providers', async () => {
15
- const service = new ModelService();
16
- const url = await service.resolveClaudeBaseURL({
17
- model: 'deepseek-v4-flash-free',
18
- baseURL: 'https://opencode.ai/zen/v1/chat/completions',
19
- apiKey: 'public',
20
- });
21
- assert.match(url, /^http:\/\/127\.0\.0\.1:\d+$/);
22
- await service.dispose();
23
- });
24
- test('keeps unknown provider url unchanged', async () => {
25
- const service = new ModelService();
26
- const url = await service.resolveClaudeBaseURL({
27
- model: 'custom',
28
- baseURL: 'https://provider.example.com/custom',
29
- apiKey: 'sk-test',
30
- });
31
- assert.equal(url, 'https://provider.example.com/custom');
32
- });
33
- test('apply replaces legacy model args with the selected model flag', async () => {
34
- const service = new ModelService();
35
- const args = service.apply({
36
- model: 'claude-sonnet-4',
37
- baseURL: 'https://provider.example.com/custom',
38
- apiKey: 'sk-test',
39
- }, ['--verbose', '--model', 'legacy-model']);
40
- const env = await service.buildEnv();
41
- assert.deepEqual(args, ['--verbose', '--model', 'claude-sonnet-4']);
42
- assert.equal(env.ANTHROPIC_BASE_URL, 'https://provider.example.com/custom');
43
- assert.equal(env.ANTHROPIC_API_KEY, 'sk-test');
44
- await service.dispose();
45
- });
46
- test('apply accepts payloads without legacy model name', async () => {
47
- const service = new ModelService();
48
- const args = service.apply({
49
- baseURL: 'https://api.example.com/root/v1/messages',
50
- apiKey: 'sk-test',
51
- }, ['-m', 'legacy-model', '--debug']);
52
- const env = await service.buildEnv();
53
- assert.deepEqual(args, ['--debug']);
54
- assert.equal(env.ANTHROPIC_BASE_URL, 'https://api.example.com/root');
55
- assert.equal(env.ANTHROPIC_API_KEY, 'sk-test');
56
- await service.dispose();
57
- });
58
- test('apply keeps no-set requests free of model flags', async () => {
59
- const service = new ModelService();
60
- const args = service.apply(null, ['--model', 'legacy-model', '--debug']);
61
- const env = await service.buildEnv();
62
- assert.deepEqual(args, ['--debug']);
63
- assert.equal(service.getCurrent(), null);
64
- assert.equal(env.ANTHROPIC_BASE_URL, undefined);
65
- assert.equal(env.ANTHROPIC_API_KEY, undefined);
66
- await service.dispose();
67
- });
68
- test('ensureBeforeSpawn does not fallback to default after no-set request', async () => {
69
- const service = new ModelService();
70
- service.apply(null, ['--debug']);
71
- service.fetchDefault = async () => ({
72
- model: 'claude-sonnet-4',
73
- baseURL: 'https://provider.example.com/custom',
74
- apiKey: 'sk-test',
75
- });
76
- const args = await service.ensureBeforeSpawn(['--debug'], 'device-token');
77
- assert.deepEqual(args, ['--debug']);
78
- assert.equal(service.getCurrent(), null);
79
- await service.dispose();
80
- });
81
- test('ensureBeforeSpawn still loads default when no selection was applied', async () => {
82
- const service = new ModelService();
83
- service.fetchDefault = async () => ({
84
- model: 'claude-sonnet-4',
85
- baseURL: 'https://provider.example.com/custom',
86
- apiKey: 'sk-test',
87
- });
88
- const args = await service.ensureBeforeSpawn(['--debug'], 'device-token');
89
- assert.deepEqual(args, ['--debug', '--model', 'claude-sonnet-4']);
90
- assert.equal(service.getCurrent()?.model, 'claude-sonnet-4');
91
- await service.dispose();
92
- });
93
- test('xml tool format avoids tool role messages for strict providers', () => {
94
- const request = {
95
- model: 'claude-sonnet-4',
96
- max_tokens: 128,
97
- messages: [
98
- { role: 'user', content: 'list files' },
99
- {
100
- role: 'assistant',
101
- content: [
102
- {
103
- type: 'tool_use',
104
- id: 'toolu_1',
105
- name: 'ls',
106
- input: { path: '.' },
107
- },
108
- ],
109
- },
110
- {
111
- role: 'user',
112
- content: [
113
- {
114
- type: 'tool_result',
115
- tool_use_id: 'toolu_1',
116
- content: 'package.json',
117
- },
118
- ],
119
- },
120
- ],
121
- tools: [
122
- {
123
- name: 'ls',
124
- description: 'list files',
125
- input_schema: {
126
- type: 'object',
127
- properties: { path: { type: 'string' } },
128
- },
129
- },
130
- ],
131
- };
132
- const converted = convertRequestToOpenAI(request, 'deepseek-v4-flash-free', 'xml');
133
- assert.deepEqual([...new Set(converted.messages.map((message) => message.role))].sort(), ['assistant', 'system', 'user']);
134
- assert.equal(converted.messages.some((message) => message.role === 'tool'), false);
135
- });
136
- //# sourceMappingURL=modelService.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"modelService.test.js","sourceRoot":"","sources":["../../src/services/modelService.test.ts"],"names":[],"mappings":"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"}
@@ -1,23 +0,0 @@
1
- const NOTIFICATION_DEDUPE_WINDOW_MS = 5 * 60 * 1000;
2
- // keep a short-lived cache of normalized notifications to avoid duplicate ws sends
3
- export class NotificationDedupe {
4
- dedupes = new Map();
5
- // return true only when the notification is outside the dedupe window
6
- shouldSend(level, message, now = Date.now()) {
7
- const key = this.buildKey(level, message);
8
- for (const [existingKey, expiresAt] of this.dedupes) {
9
- if (expiresAt <= now)
10
- this.dedupes.delete(existingKey);
11
- }
12
- const expiresAt = this.dedupes.get(key);
13
- if (expiresAt && expiresAt > now)
14
- return false;
15
- this.dedupes.set(key, now + NOTIFICATION_DEDUPE_WINDOW_MS);
16
- return true;
17
- }
18
- // normalize notification text so cosmetic whitespace changes do not bypass dedupe
19
- buildKey(level, message) {
20
- return `${level.trim().toLowerCase()}::${message.trim().replace(/\s+/g, ' ').toLowerCase()}`;
21
- }
22
- }
23
- //# sourceMappingURL=notificationDedupe.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"notificationDedupe.js","sourceRoot":"","sources":["../../src/services/notificationDedupe.ts"],"names":[],"mappings":"AAAA,MAAM,6BAA6B,GAAG,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC;AAEpD,mFAAmF;AACnF,MAAM,OAAO,kBAAkB;IACrB,OAAO,GAAG,IAAI,GAAG,EAAkB,CAAC;IAE5C,sEAAsE;IACtE,UAAU,CAAC,KAAa,EAAE,OAAe,EAAE,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE;QACzD,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;QAC1C,KAAK,MAAM,CAAC,WAAW,EAAE,SAAS,CAAC,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACpD,IAAI,SAAS,IAAI,GAAG;gBAAE,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;QACzD,CAAC;QACD,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACxC,IAAI,SAAS,IAAI,SAAS,GAAG,GAAG;YAAE,OAAO,KAAK,CAAC;QAC/C,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,GAAG,6BAA6B,CAAC,CAAC;QAC3D,OAAO,IAAI,CAAC;IACd,CAAC;IAED,kFAAkF;IAC1E,QAAQ,CAAC,KAAa,EAAE,OAAe;QAC7C,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,KAAK,OAAO,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC;IAC/F,CAAC;CACF"}
@@ -1,20 +0,0 @@
1
- import test from 'node:test';
2
- import assert from 'node:assert/strict';
3
- import { NotificationDedupe } from './notificationDedupe.js';
4
- test('dedupes same notification within 5 minutes', () => {
5
- const dedupe = new NotificationDedupe();
6
- const now = new Date('2026-05-29T10:09:17.389Z').getTime();
7
- const first = dedupe.shouldSend('error', 'IDE extension install failed (see /status for info)', now);
8
- const second = dedupe.shouldSend('error', ' IDE extension install failed (see /status for info) ', now);
9
- assert.equal(first, true);
10
- assert.equal(second, false);
11
- });
12
- test('allows same notification again after 5 minutes', () => {
13
- const dedupe = new NotificationDedupe();
14
- const base = new Date('2026-05-29T10:09:17.389Z').getTime();
15
- const first = dedupe.shouldSend('error', 'IDE extension install failed (see /status for info)', base);
16
- const second = dedupe.shouldSend('error', 'IDE extension install failed (see /status for info)', base + 5 * 60 * 1000 + 1);
17
- assert.equal(first, true);
18
- assert.equal(second, true);
19
- });
20
- //# sourceMappingURL=notificationDedupe.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"notificationDedupe.test.js","sourceRoot":"","sources":["../../src/services/notificationDedupe.test.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAE7D,IAAI,CAAC,4CAA4C,EAAE,GAAG,EAAE;IACtD,MAAM,MAAM,GAAG,IAAI,kBAAkB,EAAE,CAAC;IACxC,MAAM,GAAG,GAAG,IAAI,IAAI,CAAC,0BAA0B,CAAC,CAAC,OAAO,EAAE,CAAC;IAE3D,MAAM,KAAK,GAAG,MAAM,CAAC,UAAU,CAAC,OAAO,EAAE,qDAAqD,EAAE,GAAG,CAAC,CAAC;IACrG,MAAM,MAAM,GAAG,MAAM,CAAC,UAAU,CAAC,OAAO,EAAE,yDAAyD,EAAE,GAAG,CAAC,CAAC;IAE1G,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IAC1B,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;AAC9B,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,gDAAgD,EAAE,GAAG,EAAE;IAC1D,MAAM,MAAM,GAAG,IAAI,kBAAkB,EAAE,CAAC;IACxC,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,0BAA0B,CAAC,CAAC,OAAO,EAAE,CAAC;IAE5D,MAAM,KAAK,GAAG,MAAM,CAAC,UAAU,CAAC,OAAO,EAAE,qDAAqD,EAAE,IAAI,CAAC,CAAC;IACtG,MAAM,MAAM,GAAG,MAAM,CAAC,UAAU,CAAC,OAAO,EAAE,qDAAqD,EAAE,IAAI,GAAG,CAAC,GAAG,EAAE,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC;IAE3H,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IAC1B,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;AAC7B,CAAC,CAAC,CAAC"}
@@ -1,22 +0,0 @@
1
- import test from 'node:test';
2
- import assert from 'node:assert/strict';
3
- import { mergeSessionActivity } from './sessionListService.js';
4
- test('mergeSessionActivity marks persisted active sessions', () => {
5
- const sessions = [
6
- { sessionId: 's1', firstMessage: 'hello', lastTimestamp: '2026-06-22T10:00:00.000Z' },
7
- { sessionId: 's2', firstMessage: 'world', lastTimestamp: '2026-06-22T09:00:00.000Z' },
8
- ];
9
- const result = mergeSessionActivity(sessions, new Set(['s2']));
10
- assert.deepEqual(result, [
11
- { sessionId: 's1', firstMessage: 'hello', lastTimestamp: '2026-06-22T10:00:00.000Z', isActive: false },
12
- { sessionId: 's2', firstMessage: 'world', lastTimestamp: '2026-06-22T09:00:00.000Z', isActive: true },
13
- ]);
14
- });
15
- test('mergeSessionActivity includes the current runtime session id', () => {
16
- const sessions = [
17
- { sessionId: 's1', firstMessage: 'hello', lastTimestamp: '2026-06-22T10:00:00.000Z' },
18
- ];
19
- const result = mergeSessionActivity(sessions, new Set(), 's1');
20
- assert.equal(result[0]?.isActive, true);
21
- });
22
- //# sourceMappingURL=sessionListService.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"sessionListService.test.js","sourceRoot":"","sources":["../../src/services/sessionListService.test.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAE/D,IAAI,CAAC,sDAAsD,EAAE,GAAG,EAAE;IAChE,MAAM,QAAQ,GAAG;QACf,EAAE,SAAS,EAAE,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,aAAa,EAAE,0BAA0B,EAAE;QACrF,EAAE,SAAS,EAAE,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,aAAa,EAAE,0BAA0B,EAAE;KACtF,CAAC;IAEF,MAAM,MAAM,GAAG,oBAAoB,CAAC,QAAQ,EAAE,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAE/D,MAAM,CAAC,SAAS,CAAC,MAAM,EAAE;QACvB,EAAE,SAAS,EAAE,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,aAAa,EAAE,0BAA0B,EAAE,QAAQ,EAAE,KAAK,EAAE;QACtG,EAAE,SAAS,EAAE,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,aAAa,EAAE,0BAA0B,EAAE,QAAQ,EAAE,IAAI,EAAE;KACtG,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,8DAA8D,EAAE,GAAG,EAAE;IACxE,MAAM,QAAQ,GAAG;QACf,EAAE,SAAS,EAAE,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,aAAa,EAAE,0BAA0B,EAAE;KACtF,CAAC;IAEF,MAAM,MAAM,GAAG,oBAAoB,CAAC,QAAQ,EAAE,IAAI,GAAG,EAAU,EAAE,IAAI,CAAC,CAAC;IAEvE,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAC;AAC1C,CAAC,CAAC,CAAC"}
@@ -1,195 +0,0 @@
1
- import fs from 'node:fs';
2
- import path from 'node:path';
3
- import { listFileHistoryEntries, readFileHistoryVersion } from '../claude/fileHistory.js';
4
- import { backupSessionFile, getSessionFileInfo, readSessionHistory, readSessionJsonlLines, writeSessionJsonlLines } from './sessionService.js';
5
- const FILE_TOOL_NAMES = new Set(['Write', 'Edit', 'SearchReplace', 'DeleteFile']);
6
- function getTextContent(content) {
7
- if (typeof content === 'string')
8
- return content;
9
- if (!Array.isArray(content))
10
- return '';
11
- return content
12
- .filter((block) => block.type === 'text' && typeof block.text === 'string')
13
- .map((block) => block.text ?? '')
14
- .join('')
15
- .trim();
16
- }
17
- function extractToolPaths(message) {
18
- const content = message.message?.content;
19
- if (!Array.isArray(content))
20
- return [];
21
- const filePaths = new Set();
22
- for (const block of content) {
23
- if (block.type !== 'tool_use' || !block.name || !FILE_TOOL_NAMES.has(block.name))
24
- continue;
25
- const input = block.input ?? {};
26
- const paths = [input['file_path'], input['path']];
27
- if (Array.isArray(input['file_paths'])) {
28
- for (const value of input['file_paths'])
29
- paths.push(value);
30
- }
31
- for (const value of paths) {
32
- if (typeof value === 'string' && value.trim().length > 0) {
33
- filePaths.add(value);
34
- }
35
- }
36
- }
37
- return [...filePaths];
38
- }
39
- function parseTurns(sessionId, cwdOverride) {
40
- const lines = readSessionJsonlLines(sessionId, cwdOverride);
41
- const turns = [];
42
- const fileUsage = new Map();
43
- let currentTurn = null;
44
- let userMessageIndex = -1;
45
- for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {
46
- try {
47
- const message = JSON.parse(lines[lineIndex]);
48
- if (message.isMeta || message.sourceToolUseID || !message.message)
49
- continue;
50
- if (message.type === 'user' && message.message.role === 'user') {
51
- const prompt = getTextContent(message.message.content);
52
- if (!prompt || /<local-command-/i.test(prompt))
53
- continue;
54
- userMessageIndex += 1;
55
- currentTurn = {
56
- turnId: message.uuid ?? `turn-${userMessageIndex}`,
57
- prompt,
58
- messageIndex: userMessageIndex,
59
- lineStart: lineIndex,
60
- filePaths: new Set(),
61
- };
62
- turns.push(currentTurn);
63
- continue;
64
- }
65
- if (!currentTurn)
66
- continue;
67
- for (const filePath of extractToolPaths(message)) {
68
- currentTurn.filePaths.add(filePath);
69
- const usages = fileUsage.get(filePath) ?? [];
70
- usages.push({ turnIndex: currentTurn.messageIndex, lineIndex });
71
- fileUsage.set(filePath, usages);
72
- }
73
- }
74
- catch { }
75
- }
76
- return { lines, turns, fileUsage };
77
- }
78
- function resolveRollbackSnapshotVersion(sessionId, filePath, turnIndex, fileUsage) {
79
- const usages = fileUsage.get(filePath) ?? [];
80
- const firstUsageInTurn = usages.find((entry) => entry.turnIndex === turnIndex);
81
- const entries = listFileHistoryEntries(sessionId, filePath);
82
- if (entries.length === 0)
83
- return null;
84
- if (!firstUsageInTurn)
85
- return entries[entries.length - 1]?.version ?? null;
86
- const usageOrdinal = usages.filter((entry) => entry.lineIndex <= firstUsageInTurn.lineIndex).length;
87
- const targetIndex = entries.length - usageOrdinal - 1;
88
- if (targetIndex < 0)
89
- return entries[0]?.version ?? null;
90
- return entries[targetIndex]?.version ?? null;
91
- }
92
- function readCurrentFileContent(filePath) {
93
- try {
94
- const resolved = path.resolve(filePath);
95
- if (!fs.existsSync(resolved))
96
- return null;
97
- return fs.readFileSync(resolved, 'utf-8');
98
- }
99
- catch {
100
- return null;
101
- }
102
- }
103
- function resolveTurn(turns, turnId, messageIndex) {
104
- return turns.find((turn) => turn.turnId === turnId || (messageIndex != null && turn.messageIndex === messageIndex)) ?? null;
105
- }
106
- export function buildRollbackPreview(sessionId, turnId, cwdOverride, messageIndex) {
107
- const { turns, fileUsage } = parseTurns(sessionId, cwdOverride);
108
- const turn = resolveTurn(turns, turnId, messageIndex);
109
- if (turn == null)
110
- return null;
111
- const turnIndex = turns.indexOf(turn);
112
- if (turnIndex < 0)
113
- return null;
114
- const fileChanges = [...turn.filePaths].map((filePath) => {
115
- const snapshotVersion = resolveRollbackSnapshotVersion(sessionId, filePath, turn.messageIndex, fileUsage);
116
- const rollbackContent = snapshotVersion !== null ? readFileHistoryVersion(sessionId, filePath, snapshotVersion) : null;
117
- const currentContent = readCurrentFileContent(filePath);
118
- return {
119
- path: filePath,
120
- status: rollbackContent === null ? 'unsupported' : currentContent === null ? 'deleted' : 'modified',
121
- currentContent,
122
- rollbackContent,
123
- snapshotVersion,
124
- };
125
- });
126
- return {
127
- sessionId,
128
- turnId,
129
- prompt: turn.prompt,
130
- deleteCount: Math.max(0, turns.length - turnIndex),
131
- deleteFromMessageIndex: turn.messageIndex,
132
- startsNewSession: turnIndex === 0,
133
- fileChanges,
134
- };
135
- }
136
- export function applyRollback(sessionId, turnId, cwdOverride, messageIndex) {
137
- const preview = buildRollbackPreview(sessionId, turnId, cwdOverride, messageIndex);
138
- if (!preview)
139
- return null;
140
- const { lines, turns } = parseTurns(sessionId, cwdOverride);
141
- const turn = resolveTurn(turns, turnId, messageIndex);
142
- if (!turn)
143
- return null;
144
- const sessionBackup = backupSessionFile(sessionId, cwdOverride);
145
- const fileBackup = new Map();
146
- try {
147
- for (const fileChange of preview.fileChanges) {
148
- if (fileChange.rollbackContent === null)
149
- continue;
150
- const filePath = path.resolve(fileChange.path);
151
- fileBackup.set(filePath, fs.existsSync(filePath) ? fs.readFileSync(filePath, 'utf-8') : null);
152
- fs.mkdirSync(path.dirname(filePath), { recursive: true });
153
- fs.writeFileSync(filePath, fileChange.rollbackContent, 'utf-8');
154
- }
155
- writeSessionJsonlLines(sessionId, lines.slice(0, turn.lineStart), cwdOverride);
156
- return preview;
157
- }
158
- catch (error) {
159
- for (const [filePath, content] of fileBackup.entries()) {
160
- if (content === null) {
161
- try {
162
- if (fs.existsSync(filePath))
163
- fs.unlinkSync(filePath);
164
- }
165
- catch { }
166
- continue;
167
- }
168
- try {
169
- fs.mkdirSync(path.dirname(filePath), { recursive: true });
170
- fs.writeFileSync(filePath, content, 'utf-8');
171
- }
172
- catch { }
173
- }
174
- if (sessionBackup) {
175
- const { sessionFile } = getSessionFileInfo(sessionId, cwdOverride);
176
- try {
177
- fs.copyFileSync(sessionBackup, sessionFile);
178
- }
179
- catch { }
180
- }
181
- throw error;
182
- }
183
- }
184
- export function loadRolledBackHistory(sessionId, cwdOverride) {
185
- const { cwd } = getSessionFileInfo(sessionId, cwdOverride);
186
- const previousCwd = process.cwd();
187
- try {
188
- process.chdir(cwd);
189
- return readSessionHistory(sessionId);
190
- }
191
- finally {
192
- process.chdir(previousCwd);
193
- }
194
- }
195
- //# sourceMappingURL=sessionRollbackService.js.map