spiracha 2.1.0 → 2.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 (125) hide show
  1. package/AGENTS.md +27 -12
  2. package/README.md +37 -19
  3. package/apps/ui/AGENTS.md +18 -10
  4. package/apps/ui/README.md +17 -10
  5. package/apps/ui/src/components/analytics-breakdowns.tsx +106 -0
  6. package/apps/ui/src/components/antigravity-conversations-table.tsx +1 -0
  7. package/apps/ui/src/components/app-shell.tsx +45 -3
  8. package/apps/ui/src/components/data-table.tsx +17 -1
  9. package/apps/ui/src/components/delete-confirm-dialog.tsx +4 -3
  10. package/apps/ui/src/components/export-dialog.tsx +77 -62
  11. package/apps/ui/src/components/projects-table.tsx +4 -1
  12. package/apps/ui/src/components/route-error-panel.tsx +13 -0
  13. package/apps/ui/src/components/thread-goals-panel.tsx +50 -0
  14. package/apps/ui/src/components/thread-tools-panel.tsx +142 -0
  15. package/apps/ui/src/components/threads-table.tsx +4 -1
  16. package/apps/ui/src/lib/antigravity-server.ts +70 -17
  17. package/apps/ui/src/lib/antigravity-transcript-events.ts +46 -70
  18. package/apps/ui/src/lib/claude-code-server.ts +14 -4
  19. package/apps/ui/src/lib/codex-queries.ts +3 -3
  20. package/apps/ui/src/lib/codex-server.ts +3 -3
  21. package/apps/ui/src/lib/cursor-server.ts +41 -69
  22. package/apps/ui/src/lib/cursor-transcript-events.ts +11 -86
  23. package/apps/ui/src/lib/delete-batch.ts +52 -0
  24. package/apps/ui/src/lib/error-presentation.ts +43 -0
  25. package/apps/ui/src/lib/export-mutation.ts +20 -0
  26. package/apps/ui/src/lib/export-options.ts +15 -0
  27. package/apps/ui/src/lib/grok-server.ts +2 -3
  28. package/apps/ui/src/lib/kiro-server.ts +14 -2
  29. package/apps/ui/src/lib/mutation-error.ts +15 -0
  30. package/apps/ui/src/lib/opencode-server.ts +13 -2
  31. package/apps/ui/src/lib/path-utils.ts +1 -1
  32. package/apps/ui/src/lib/qoder-queries.ts +15 -0
  33. package/apps/ui/src/lib/qoder-transcript-events.ts +1 -1
  34. package/apps/ui/src/lib/route-state-reset.tsx +10 -0
  35. package/apps/ui/src/lib/settings-server.ts +33 -0
  36. package/apps/ui/src/lib/settings-store.tsx +82 -38
  37. package/apps/ui/src/lib/settings.ts +65 -0
  38. package/apps/ui/src/lib/source-session-export-server.ts +1 -1
  39. package/apps/ui/src/lib/thread-metadata.ts +22 -0
  40. package/apps/ui/src/lib/thread-transcript-stats.ts +10 -1
  41. package/apps/ui/src/lib/workspace-delete-navigation.ts +8 -0
  42. package/apps/ui/src/routeTree.gen.ts +282 -282
  43. package/apps/ui/src/routes/__root.tsx +12 -15
  44. package/apps/ui/src/routes/analytics.tsx +10 -61
  45. package/apps/ui/src/routes/antigravity-conversations.$conversationId.tsx +73 -19
  46. package/apps/ui/src/routes/antigravity.$workspaceKey.tsx +42 -60
  47. package/apps/ui/src/routes/antigravity.index.tsx +2 -2
  48. package/apps/ui/src/routes/claude-code-sessions.$sessionId.tsx +25 -11
  49. package/apps/ui/src/routes/claude-code.$workspaceKey.tsx +27 -32
  50. package/apps/ui/src/routes/claude-code.index.tsx +2 -2
  51. package/apps/ui/src/routes/codex.$project.tsx +33 -31
  52. package/apps/ui/src/routes/codex.index.tsx +5 -10
  53. package/apps/ui/src/routes/cursor-threads.$composerId.tsx +29 -16
  54. package/apps/ui/src/routes/cursor.$workspaceKey.tsx +33 -27
  55. package/apps/ui/src/routes/cursor.index.tsx +5 -2
  56. package/apps/ui/src/routes/grok-sessions.$sessionId.tsx +25 -12
  57. package/apps/ui/src/routes/grok.$workspaceKey.tsx +20 -26
  58. package/apps/ui/src/routes/grok.index.tsx +2 -2
  59. package/apps/ui/src/routes/index.tsx +2 -18
  60. package/apps/ui/src/routes/kiro-sessions.$sessionId.tsx +25 -12
  61. package/apps/ui/src/routes/kiro.$workspaceKey.tsx +27 -32
  62. package/apps/ui/src/routes/kiro.index.tsx +2 -2
  63. package/apps/ui/src/routes/opencode-sessions.$sessionId.tsx +29 -36
  64. package/apps/ui/src/routes/opencode.$workspaceKey.tsx +45 -61
  65. package/apps/ui/src/routes/opencode.index.tsx +8 -17
  66. package/apps/ui/src/routes/qoder-sessions.$sessionId.tsx +12 -11
  67. package/apps/ui/src/routes/qoder.$workspaceKey.tsx +19 -25
  68. package/apps/ui/src/routes/qoder.index.tsx +2 -2
  69. package/apps/ui/src/routes/threads.$threadId.tsx +87 -56
  70. package/apps/ui/vite.config.ts +22 -12
  71. package/bin/spiracha.ts +28 -1
  72. package/package.json +26 -17
  73. package/src/client.ts +61 -16
  74. package/src/lib/antigravity-db.ts +236 -82
  75. package/src/lib/antigravity-keychain.ts +7 -8
  76. package/src/lib/antigravity-transcript-phase.ts +47 -0
  77. package/src/lib/claude-code-db.ts +45 -19
  78. package/src/lib/codex-analytics.ts +32 -5
  79. package/src/lib/codex-browser-db.ts +540 -137
  80. package/src/lib/codex-browser-export.ts +2 -7
  81. package/src/lib/codex-browser-types.ts +21 -1
  82. package/src/lib/codex-thread-cache.ts +88 -33
  83. package/src/lib/codex-thread-parser.ts +78 -35
  84. package/src/lib/codex-thread-recovery.ts +38 -11
  85. package/src/lib/codex-transcript-renderer.ts +49 -61
  86. package/src/lib/concurrency.ts +15 -3
  87. package/src/lib/conversation-api.ts +34 -31
  88. package/src/lib/conversation-data/adapter-helpers.ts +25 -3
  89. package/src/lib/conversation-data/antigravity-adapter.ts +54 -29
  90. package/src/lib/conversation-data/claude-code-adapter.ts +32 -6
  91. package/src/lib/conversation-data/codex-adapter.ts +91 -80
  92. package/src/lib/conversation-data/cursor-adapter.ts +48 -34
  93. package/src/lib/conversation-data/grok-adapter.ts +19 -6
  94. package/src/lib/conversation-data/index.ts +29 -10
  95. package/src/lib/conversation-data/kiro-adapter.ts +18 -5
  96. package/src/lib/conversation-data/message-selector.ts +2 -5
  97. package/src/lib/conversation-data/opencode-adapter.ts +43 -15
  98. package/src/lib/conversation-data/path-match.ts +1 -13
  99. package/src/lib/conversation-data/qoder-adapter.ts +23 -18
  100. package/src/lib/conversation-zip-export.ts +20 -2
  101. package/src/lib/cursor-db.ts +34 -23
  102. package/src/lib/cursor-id.ts +17 -0
  103. package/src/lib/cursor-recovery.ts +146 -35
  104. package/src/lib/cursor-transcript-phase.ts +40 -0
  105. package/src/lib/cursor-transcript.ts +21 -5
  106. package/src/lib/grok-db.ts +76 -34
  107. package/src/lib/grok-transcript-phase.ts +11 -2
  108. package/src/lib/grok-transcript.ts +15 -2
  109. package/src/lib/kiro-db.ts +161 -68
  110. package/src/lib/kiro-transcript.ts +0 -4
  111. package/src/lib/opencode-db.ts +4 -8
  112. package/src/lib/opencode-think-tags.ts +17 -3
  113. package/src/lib/portable-path.ts +9 -0
  114. package/src/lib/qoder-acp-client.ts +11 -2
  115. package/src/lib/qoder-db.ts +51 -17
  116. package/src/lib/qoder-transcript-phase.ts +64 -0
  117. package/src/lib/qoder-transcript.ts +30 -2
  118. package/src/lib/shared.ts +51 -15
  119. package/src/lib/sqlite-error.ts +31 -5
  120. package/src/lib/transcript-load-limiter.ts +8 -3
  121. package/src/lib/ui-cache.ts +152 -17
  122. package/src/lib/ui-export-archive.ts +14 -55
  123. package/src/lib/ui-export-files.ts +75 -15
  124. package/src/lib/ui-export-zip.ts +40 -0
  125. package/apps/ui/package.json +0 -65
@@ -1,12 +1,17 @@
1
1
  import { createReadStream } from 'node:fs';
2
- import { access, constants } from 'node:fs/promises';
3
2
  import path from 'node:path';
4
3
  import tailwindcss from '@tailwindcss/vite';
5
4
  import { devtools } from '@tanstack/devtools-vite';
6
5
  import { tanstackStart } from '@tanstack/react-start/plugin/vite';
7
6
  import viteReact from '@vitejs/plugin-react';
8
7
  import { defineConfig, type Plugin } from 'vite';
9
- import { buildUiExportContentDisposition, resolveUiExportFilePathFromRequestPath } from '../../src/lib/ui-export-files';
8
+ import {
9
+ buildUiExportContentDisposition,
10
+ resolveReadableUiExportFileFromRequestPath,
11
+ UI_EXPORT_URL_PREFIX,
12
+ } from '../../src/lib/ui-export-files';
13
+
14
+ const uiRoot = __dirname;
10
15
 
11
16
  const getExportContentType = (filePath: string) => {
12
17
  if (filePath.endsWith('.zip')) {
@@ -33,17 +38,14 @@ const spirachaExportFiles = (): Plugin => {
33
38
  return;
34
39
  }
35
40
 
36
- const exportFilePath = resolveUiExportFilePathFromRequestPath(
37
- new URL(req.url, 'http://spiracha.local').pathname,
38
- );
39
- if (!exportFilePath) {
41
+ const pathname = new URL(req.url, 'http://spiracha.local').pathname;
42
+ if (!pathname.startsWith(UI_EXPORT_URL_PREFIX)) {
40
43
  next();
41
44
  return;
42
45
  }
43
46
 
44
- try {
45
- await access(exportFilePath, constants.R_OK);
46
- } catch {
47
+ const exportFilePath = await resolveReadableUiExportFileFromRequestPath(pathname);
48
+ if (!exportFilePath) {
47
49
  res.statusCode = 404;
48
50
  res.end('Not Found');
49
51
  return;
@@ -59,7 +61,14 @@ const spirachaExportFiles = (): Plugin => {
59
61
  return;
60
62
  }
61
63
 
62
- createReadStream(exportFilePath).pipe(res);
64
+ const stream = createReadStream(exportFilePath);
65
+ stream.once('error', () => {
66
+ if (!res.headersSent) {
67
+ res.statusCode = 500;
68
+ }
69
+ res.end('Internal Server Error');
70
+ });
71
+ stream.pipe(res);
63
72
  });
64
73
  },
65
74
  name: 'spiracha-export-files',
@@ -70,13 +79,14 @@ const config = defineConfig({
70
79
  plugins: [spirachaExportFiles(), devtools(), tailwindcss(), tanstackStart(), viteReact()],
71
80
  resolve: {
72
81
  alias: {
73
- '@spiracha': path.resolve(__dirname, '..', '..', 'src'),
82
+ '@spiracha': path.resolve(uiRoot, '..', '..', 'src'),
74
83
  },
75
84
  tsconfigPaths: true,
76
85
  },
86
+ root: uiRoot,
77
87
  server: {
78
88
  fs: {
79
- allow: [path.resolve(__dirname, '..', '..')],
89
+ allow: [path.resolve(uiRoot, '..', '..')],
80
90
  },
81
91
  },
82
92
  });
package/bin/spiracha.ts CHANGED
@@ -14,6 +14,33 @@ export const buildSpirachaDevServerCommand = (packageRoot = resolveSpirachaPacka
14
14
  cwd: path.join(packageRoot, 'apps', 'ui'),
15
15
  });
16
16
 
17
+ type SpirachaChildProcess = {
18
+ exited: Promise<number>;
19
+ kill: (signal?: NodeJS.Signals | number) => void;
20
+ };
21
+
22
+ type SpirachaSignalEmitter = {
23
+ off: (event: NodeJS.Signals, listener: () => void) => unknown;
24
+ on: (event: NodeJS.Signals, listener: () => void) => unknown;
25
+ };
26
+
27
+ export const waitForSpirachaDevServer = async (
28
+ child: SpirachaChildProcess,
29
+ signalEmitter: SpirachaSignalEmitter = process,
30
+ ): Promise<number> => {
31
+ const forwardSigint = () => child.kill('SIGINT');
32
+ const forwardSigterm = () => child.kill('SIGTERM');
33
+ signalEmitter.on('SIGINT', forwardSigint);
34
+ signalEmitter.on('SIGTERM', forwardSigterm);
35
+
36
+ try {
37
+ return await child.exited;
38
+ } finally {
39
+ signalEmitter.off('SIGINT', forwardSigint);
40
+ signalEmitter.off('SIGTERM', forwardSigterm);
41
+ }
42
+ };
43
+
17
44
  export const runSpirachaDevServer = async (): Promise<number> => {
18
45
  const command = buildSpirachaDevServerCommand();
19
46
  const proc = Bun.spawn([process.execPath, ...command.args], {
@@ -24,7 +51,7 @@ export const runSpirachaDevServer = async (): Promise<number> => {
24
51
  stdout: 'inherit',
25
52
  });
26
53
 
27
- return proc.exited;
54
+ return waitForSpirachaDevServer(proc);
28
55
  };
29
56
 
30
57
  if (import.meta.main) {
package/package.json CHANGED
@@ -11,7 +11,7 @@
11
11
  },
12
12
  "dependencies": {
13
13
  "@tailwindcss/typography": "0.5.20",
14
- "@tailwindcss/vite": "4.3.2",
14
+ "@tailwindcss/vite": "4.3.3",
15
15
  "@tanstack/devtools-vite": "0.8.1",
16
16
  "@tanstack/match-sorter-utils": "8.19.4",
17
17
  "@tanstack/react-devtools": "0.10.8",
@@ -20,28 +20,37 @@
20
20
  "@tanstack/react-router": "1.170.18",
21
21
  "@tanstack/react-router-devtools": "1.167.0",
22
22
  "@tanstack/react-router-ssr-query": "1.167.1",
23
- "@tanstack/react-start": "1.168.28",
23
+ "@tanstack/react-start": "1.168.30",
24
24
  "@tanstack/react-table": "8.21.3",
25
25
  "@tanstack/react-virtual": "3.14.6",
26
- "@tanstack/router-plugin": "1.168.20",
26
+ "@tanstack/router-plugin": "1.168.22",
27
27
  "@vitejs/plugin-react": "6.0.3",
28
28
  "class-variance-authority": "0.7.1",
29
29
  "clsx": "2.1.1",
30
- "lucide-react": "1.24.0",
30
+ "fflate": "0.8.3",
31
+ "lucide-react": "1.25.0",
31
32
  "radix-ui": "1.6.2",
32
33
  "react": "19.2.7",
33
34
  "react-dom": "19.2.7",
34
35
  "tailwind-merge": "3.6.0",
35
- "tailwindcss": "4.3.2",
36
+ "tailwindcss": "4.3.3",
36
37
  "tw-animate-css": "1.4.0",
37
- "vite": "8.1.4",
38
+ "vite": "8.1.5",
38
39
  "zod": "4.4.3"
39
40
  },
40
41
  "description": "Browse and expose local Codex, Claude Code, Grok, Kiro, Qoder, Cursor, Antigravity, and OpenCode conversation history through a local UI and stable data API.",
41
42
  "devDependencies": {
43
+ "@biomejs/biome": "2.5.4",
44
+ "@testing-library/dom": "10.4.1",
45
+ "@testing-library/react": "16.3.2",
42
46
  "@types/bun": "^1.3.14",
43
47
  "@types/node": "^26.1.1",
44
- "typescript": "^7.0.2"
48
+ "@types/react": "19.2.17",
49
+ "@types/react-dom": "19.2.3",
50
+ "@vitest/coverage-v8": "4.1.10",
51
+ "jsdom": "29.1.1",
52
+ "typescript": "^7.0.2",
53
+ "vitest": "4.1.10"
45
54
  },
46
55
  "engines": {
47
56
  "bun": ">=1.3.14"
@@ -63,7 +72,6 @@
63
72
  "files": [
64
73
  "bin/spiracha.ts",
65
74
  "apps/ui/components.json",
66
- "apps/ui/package.json",
67
75
  "apps/ui/public/**/*",
68
76
  "apps/ui/src/**/*",
69
77
  "!apps/ui/src/**/*.vitest.ts",
@@ -78,6 +86,9 @@
78
86
  "AGENTS.md"
79
87
  ],
80
88
  "homepage": "https://github.com/ragaeeb/spiracha",
89
+ "imports": {
90
+ "#/*": "./apps/ui/src/*"
91
+ },
81
92
  "keywords": [
82
93
  "codex",
83
94
  "claude",
@@ -100,21 +111,19 @@
100
111
  "url": "git+https://github.com/ragaeeb/spiracha.git"
101
112
  },
102
113
  "scripts": {
103
- "build": "bun run typecheck && bun run --cwd apps/ui build",
114
+ "build": "bun run typecheck && bun --cwd apps/ui --bun vite build",
104
115
  "coverage": "bun run coverage:root && bun run coverage:ui",
105
116
  "coverage:root": "bun test --coverage --coverage-reporter=lcov --coverage-dir coverage && bun run ./src/coverage-check.ts root",
106
- "coverage:ui": "bun run --cwd apps/ui coverage && bun run ./src/coverage-check.ts ui",
117
+ "coverage:ui": "vitest run --coverage --config apps/ui/vitest.config.ts && bun run ./src/coverage-check.ts ui",
107
118
  "format": "biome check . --write && biome lint . --write",
108
119
  "lint": "biome check .",
109
- "start": "bun run --cwd apps/ui dev",
120
+ "start": "bun --cwd apps/ui --bun vite dev --host 127.0.0.1 --port 3000",
121
+ "test:ui": "vitest run --config apps/ui/vitest.config.ts",
110
122
  "typecheck": "bun run typecheck:root && bun run typecheck:ui",
111
123
  "typecheck:root": "bunx tsc --noEmit",
112
- "typecheck:ui": "bun run --cwd apps/ui typecheck",
113
- "ui:preview": "bun run --cwd apps/ui preview"
124
+ "typecheck:ui": "bunx tsc --noEmit -p apps/ui/tsconfig.json",
125
+ "ui:preview": "bun --cwd apps/ui --bun vite preview"
114
126
  },
115
127
  "type": "module",
116
- "version": "2.1.0",
117
- "workspaces": [
118
- "apps/*"
119
- ]
128
+ "version": "2.2.0"
120
129
  }
package/src/client.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { mapWithConcurrency } from './lib/concurrency';
1
2
  import {
2
3
  deleteConversation as deleteLocalConversation,
3
4
  deleteConversations as deleteLocalConversations,
@@ -51,9 +52,11 @@ export type {
51
52
  type HttpEnvelope<T> = {
52
53
  data?: T;
53
54
  error?: {
55
+ code?: string | null;
54
56
  message?: string | null;
55
57
  } | null;
56
58
  meta?: {
59
+ has_next?: boolean | null;
57
60
  hasNext?: boolean | null;
58
61
  nextCursor?: string | null;
59
62
  next_cursor?: string | null;
@@ -196,9 +199,22 @@ const fetchJson = async <T>(url: URL, init?: RequestInit): Promise<HttpEnvelope<
196
199
  return readJsonEnvelope(response);
197
200
  };
198
201
 
202
+ const isMissingConversationResponse = async (response: Response): Promise<boolean> => {
203
+ if (response.status !== 404) {
204
+ return false;
205
+ }
206
+
207
+ try {
208
+ const envelope = (await response.clone().json()) as HttpEnvelope<unknown>;
209
+ return envelope.error?.code === 'conversation_not_found';
210
+ } catch {
211
+ return false;
212
+ }
213
+ };
214
+
199
215
  const fetchJsonOrNull = async <T>(url: URL, init?: RequestInit): Promise<HttpEnvelope<T> | null> => {
200
216
  const response = await fetchResponse(url, init);
201
- if (response.status === 404) {
217
+ if (await isMissingConversationResponse(response)) {
202
218
  return null;
203
219
  }
204
220
 
@@ -206,9 +222,30 @@ const fetchJsonOrNull = async <T>(url: URL, init?: RequestInit): Promise<HttpEnv
206
222
  return readJsonEnvelope(response);
207
223
  };
208
224
 
225
+ const fetchDeleteJsonOrNull = async <T>(url: URL, init?: RequestInit): Promise<HttpEnvelope<T> | null> => {
226
+ const response = await fetchResponse(url, init);
227
+ if (await isMissingConversationResponse(response)) {
228
+ return null;
229
+ }
230
+
231
+ if (response.status === 405) {
232
+ try {
233
+ const envelope = (await response.clone().json()) as HttpEnvelope<T>;
234
+ if (envelope.error?.code === 'unsupported_operation') {
235
+ return null;
236
+ }
237
+ } catch {
238
+ // Let the normal error path report malformed error responses.
239
+ }
240
+ }
241
+
242
+ await assertOkResponse(response);
243
+ return readJsonEnvelope(response);
244
+ };
245
+
209
246
  const fetchTextOrNull = async (url: URL, init?: RequestInit): Promise<string | null> => {
210
247
  const response = await fetchResponse(url, init);
211
- if (response.status === 404) {
248
+ if (await isMissingConversationResponse(response)) {
212
249
  return null;
213
250
  }
214
251
 
@@ -232,7 +269,7 @@ const fileNameFromContentDisposition = (contentDisposition: string | null, fallb
232
269
 
233
270
  const fetchZipOrNull = async (url: URL, init?: RequestInit): Promise<ConversationZipDownload | null> => {
234
271
  const response = await fetchResponse(url, init);
235
- if (response.status === 404) {
272
+ if (await isMissingConversationResponse(response)) {
236
273
  return null;
237
274
  }
238
275
 
@@ -261,13 +298,20 @@ const normalizePage = (envelope: HttpEnvelope<ConversationDetail[]>): Conversati
261
298
  return {
262
299
  data,
263
300
  meta: {
264
- hasNext: envelope.meta?.hasNext === true,
301
+ hasNext: envelope.meta?.has_next === true || envelope.meta?.hasNext === true,
265
302
  nextCursor: envelope.meta?.nextCursor ?? envelope.meta?.next_cursor ?? null,
266
303
  },
267
304
  };
268
305
  };
269
306
 
270
- const makeHttpUrl = (baseUrl: URL, pathname: string): URL => new URL(pathname, baseUrl);
307
+ const makeHttpUrl = (baseUrl: URL, pathname: string): URL => {
308
+ const url = new URL(baseUrl);
309
+ const basePath = url.pathname.replace(/\/+$/u, '');
310
+ url.pathname = `${basePath}/${pathname.replace(/^\/+/, '')}`;
311
+ url.search = '';
312
+ url.hash = '';
313
+ return url;
314
+ };
271
315
 
272
316
  const rejectHttpLocations = (locations: ConversationDataLocations | undefined): void => {
273
317
  if (locations) {
@@ -287,15 +331,16 @@ const exportLocalConversationsZip = async (
287
331
  locations: ConversationDataLocations | undefined,
288
332
  ) => {
289
333
  const options = withDefaultLocations(exportOptions, locations);
290
- const conversations = await Promise.all(
291
- options.ids.map((id) =>
292
- getLocalConversation({
293
- id,
294
- locations: options.locations,
295
- messageSelector: options.messageSelector ?? 'all',
296
- source: options.source,
297
- }),
298
- ),
334
+ if (options.ids.length > 200) {
335
+ throw new SpirachaClientError('At most 200 conversation ids may be exported at once.');
336
+ }
337
+ const conversations = await mapWithConcurrency(options.ids, 4, (id) =>
338
+ getLocalConversation({
339
+ id,
340
+ locations: options.locations,
341
+ messageSelector: options.messageSelector ?? 'all',
342
+ source: options.source,
343
+ }),
299
344
  );
300
345
 
301
346
  if (conversations.some((conversation) => conversation === null)) {
@@ -346,7 +391,7 @@ const makeHttpClient = (options: HttpConversationClientOptions): ConversationCli
346
391
  rejectHttpLocations(deleteOptions.locations);
347
392
  const { id, source } = deleteOptions;
348
393
  const url = makeHttpUrl(baseUrl, `/api/v1/conversations/${source}/${encodeURIComponent(id)}`);
349
- const envelope = await fetchJsonOrNull<DeleteConversationResult>(url, { method: 'DELETE' });
394
+ const envelope = await fetchDeleteJsonOrNull<DeleteConversationResult>(url, { method: 'DELETE' });
350
395
  if (!envelope) {
351
396
  return null;
352
397
  }
@@ -354,7 +399,7 @@ const makeHttpClient = (options: HttpConversationClientOptions): ConversationCli
354
399
  },
355
400
  deleteConversations: async (deleteOptions) => {
356
401
  rejectHttpLocations(deleteOptions.locations);
357
- const envelope = await fetchJsonOrNull<DeleteConversationsResult>(
402
+ const envelope = await fetchDeleteJsonOrNull<DeleteConversationsResult>(
358
403
  makeHttpUrl(baseUrl, '/api/v1/conversations/delete'),
359
404
  {
360
405
  body: JSON.stringify({