dsh-lcx-codex 0.4.2 → 0.4.3-pre.2

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 (47) hide show
  1. package/README.md +116 -202
  2. package/cordis.patch.yml +3 -20
  3. package/lib/client.js +273 -146
  4. package/lib/compact-v2.js +218 -199
  5. package/lib/dsh-compat.js +227 -100
  6. package/lib/dsh-responses.js +445 -277
  7. package/lib/index.js +851 -757
  8. package/lib/json-store.js +50 -31
  9. package/lib/native-checkpoint.js +520 -194
  10. package/lib/responses-request.js +106 -121
  11. package/lib/responses-stream.js +972 -443
  12. package/lib/route.js +229 -355
  13. package/lib/service-mutex.js +73 -64
  14. package/lib/token-budget.js +176 -108
  15. package/lib/transport.js +277 -67
  16. package/lib/types/client/index.d.ts +6 -0
  17. package/lib/types/compact-v2.d.ts +104 -0
  18. package/lib/types/dsh-compat.d.ts +78 -0
  19. package/lib/types/dsh-responses.d.ts +82 -0
  20. package/lib/types/index.d.ts +83 -0
  21. package/lib/types/json-store.d.ts +10 -0
  22. package/lib/types/native-checkpoint.d.ts +213 -0
  23. package/lib/types/responses-request.d.ts +58 -0
  24. package/lib/types/responses-stream.d.ts +51 -0
  25. package/lib/types/route.d.ts +132 -0
  26. package/lib/types/service-mutex.d.ts +14 -0
  27. package/lib/types/token-budget.d.ts +50 -0
  28. package/lib/types/transport.d.ts +20 -0
  29. package/lib/types/web-run-output.d.ts +29 -0
  30. package/lib/types/web-search-alpha.d.ts +286 -0
  31. package/lib/types/web-search-capability.d.ts +26 -0
  32. package/lib/types/web-search-hosted.d.ts +246 -0
  33. package/lib/types/web-search-ref-store.d.ts +22 -0
  34. package/lib/web-run-output.js +167 -18
  35. package/lib/web-search-alpha.js +865 -163
  36. package/lib/web-search-capability.js +55 -65
  37. package/lib/web-search-hosted.js +210 -32
  38. package/lib/web-search-ref-store.js +63 -59
  39. package/package.json +79 -27
  40. package/ARCHITECTURE.md +0 -117
  41. package/CHANGELOG.md +0 -224
  42. package/README_EN.md +0 -277
  43. package/assets/dsh-lcx-codex-banner.jpg +0 -0
  44. package/lib/legacy-v3.js +0 -20
  45. package/lib/responses-replay.js +0 -68
  46. package/scripts/probe-alpha.mjs +0 -43
  47. package/scripts/validate-dsh-schema.mjs +0 -31
package/lib/transport.js CHANGED
@@ -1,81 +1,291 @@
1
- const DEFAULT_TIMEOUT_MS = 300_000
2
- const DEFAULT_MAX_BYTES = 8 * 1024 * 1024
3
-
1
+ const DEFAULT_TIMEOUT_MS = 300_000;
2
+ const DEFAULT_MAX_BYTES = 8 * 1024 * 1024;
3
+ const MAX_ERROR_BYTES = 512 * 1024;
4
+ function errorFacts(value) {
5
+ return value instanceof Error ? value : new Error(String(value));
6
+ }
4
7
  export function abortIfNeeded(signal) {
5
- if (signal?.aborted) throw signal.reason ?? Object.assign(new Error('request aborted'), { code: 'LCX_ABORTED' })
8
+ if (signal?.aborted)
9
+ throw (signal.reason ??
10
+ Object.assign(new Error("request aborted"), { code: "LCX_ABORTED" }));
11
+ }
12
+ function makeError(message, code, extra = {}) {
13
+ const error = new Error(message);
14
+ error.code = code;
15
+ Object.assign(error, extra);
16
+ return error;
17
+ }
18
+ function retryableStatus(status) {
19
+ return (status === 408 ||
20
+ status === 409 ||
21
+ status === 425 ||
22
+ status === 429 ||
23
+ (status >= 500 && status <= 599));
6
24
  }
7
- function makeError(message, code, extra = {}) { const error = new Error(message); error.code = code; Object.assign(error, extra); return error }
8
- function retryableStatus(status) { return status === 408 || status === 409 || status === 425 || status === 429 || (status >= 500 && status <= 599) }
9
25
  function retryAfterFromHeaders(headers) {
10
- const rawMillis = headers?.get?.('retry-after-ms'); const millis = Number(rawMillis); if (rawMillis !== null && rawMillis !== undefined && rawMillis !== '' && Number.isFinite(millis) && millis >= 0) return Math.min(millis, 30_000)
11
- const retryAfter = headers?.get?.('retry-after'); if (retryAfter) { const seconds = Number(retryAfter); if (Number.isFinite(seconds) && seconds >= 0) return Math.min(seconds * 1000, 30_000); const date = Date.parse(retryAfter); if (!Number.isNaN(date)) return Math.min(Math.max(0, date - Date.now()), 30_000) }
12
- return undefined
26
+ const rawMillis = headers?.get?.("retry-after-ms");
27
+ const millis = Number(rawMillis);
28
+ if (rawMillis !== null &&
29
+ rawMillis !== undefined &&
30
+ rawMillis !== "" &&
31
+ Number.isFinite(millis) &&
32
+ millis >= 0)
33
+ return Math.min(millis, 30_000);
34
+ const retryAfter = headers?.get?.("retry-after");
35
+ if (retryAfter) {
36
+ const seconds = Number(retryAfter);
37
+ if (Number.isFinite(seconds) && seconds >= 0)
38
+ return Math.min(seconds * 1000, 30_000);
39
+ const date = Date.parse(retryAfter);
40
+ if (!Number.isNaN(date))
41
+ return Math.min(Math.max(0, date - Date.now()), 30_000);
42
+ }
43
+ return undefined;
13
44
  }
14
45
  function delayFromHeaders(headers, attempt) {
15
- const retryAfter = retryAfterFromHeaders(headers)
16
- if (retryAfter !== undefined) return retryAfter
17
- return Math.min(10_000, 500 * 2 ** Math.max(0, attempt - 1))
46
+ const retryAfter = retryAfterFromHeaders(headers);
47
+ if (retryAfter !== undefined)
48
+ return retryAfter;
49
+ return Math.min(10_000, 500 * 2 ** Math.max(0, attempt - 1));
18
50
  }
19
- async function sleep(ms, signal) { if (ms <= 0) return; await new Promise((resolve, reject) => { if (signal?.aborted) return reject(signal.reason); const timer = setTimeout(resolve, ms); signal?.addEventListener('abort', () => { clearTimeout(timer); reject(signal.reason) }, { once: true }) }) }
20
- function combinedSignal(signal, timeoutMs) { if (!(Number.isFinite(timeoutMs) && timeoutMs > 0)) return signal; const timeout = AbortSignal.timeout(timeoutMs); return signal ? AbortSignal.any([signal, timeout]) : timeout }
21
- async function readLimited(response, maxBytes = DEFAULT_MAX_BYTES) { const buffer = new Uint8Array(await response.arrayBuffer()); if (buffer.byteLength > maxBytes) throw makeError(`response exceeds ${maxBytes} bytes`, 'LCX_RESPONSE_TOO_LARGE', { status: response.status }); return new TextDecoder().decode(buffer) }
22
-
23
- export async function fetchJsonWithRetry(url, body, headers = {}, signal, timeoutMs = DEFAULT_TIMEOUT_MS, options = {}) {
24
- const maxAttempts = Math.max(1, Math.min(6, Number(options.maxAttempts ?? 3))); const maxResponseBytes = options.maxResponseBytes ?? DEFAULT_MAX_BYTES; let last
25
- for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
26
- abortIfNeeded(signal)
51
+ async function sleep(ms, signal) {
52
+ if (ms === undefined || ms <= 0)
53
+ return;
54
+ await new Promise((resolve, reject) => {
55
+ if (signal?.aborted)
56
+ return reject(signal.reason);
57
+ const timer = setTimeout(resolve, ms);
58
+ signal?.addEventListener("abort", () => {
59
+ clearTimeout(timer);
60
+ reject(signal.reason);
61
+ }, { once: true });
62
+ });
63
+ }
64
+ function combinedSignal(signal, timeoutMs) {
65
+ if (!(typeof timeoutMs === "number" && Number.isFinite(timeoutMs) && timeoutMs > 0))
66
+ return signal;
67
+ const timeout = AbortSignal.timeout(timeoutMs);
68
+ return signal ? AbortSignal.any([signal, timeout]) : timeout;
69
+ }
70
+ async function readLimited(response, maxBytes = DEFAULT_MAX_BYTES) {
71
+ if (!response.body)
72
+ return "";
73
+ const reader = response.body.getReader();
74
+ const decoder = new TextDecoder();
75
+ let bytes = 0;
76
+ let text = "";
27
77
  try {
28
- const requestSignal = combinedSignal(signal, timeoutMs)
29
- const response = await fetch(url, { method: 'POST', headers: { 'content-type': 'application/json', accept: 'application/json', ...headers }, body: JSON.stringify(body), signal: requestSignal, redirect: 'error' })
30
- const text = await readLimited(response, maxResponseBytes)
31
- if (!response.ok) {
32
- const retryable = retryableStatus(response.status)
33
- const providerRetryAfterMs = retryAfterFromHeaders(response.headers)
34
- const error = makeError(`HTTP ${response.status}`, retryable ? 'LCX_HTTP_RETRYABLE' : 'LCX_HTTP_ERROR', { status: response.status, retryable, requestId: response.headers.get('x-request-id') ?? response.headers.get('request-id') ?? undefined, ...(providerRetryAfterMs === undefined ? {} : { providerRetryAfterMs }) })
35
- if (!error.retryable || attempt >= maxAttempts) throw error; last = error; await sleep(delayFromHeaders(response.headers, attempt), signal); continue
36
- }
37
- let parsed
38
- try { parsed = text ? JSON.parse(text) : {} } catch (cause) { throw makeError(`invalid JSON response from ${url}`, 'LCX_INVALID_JSON', { status: response.status, cause }) }
39
- return parsed
40
- } catch (error) {
41
- if (signal?.aborted) throw signal.reason ?? error
42
- const retryable = error?.retryable === true || error?.code === 'LCX_HTTP_RETRYABLE' || error instanceof TypeError || ['ECONNRESET','ECONNREFUSED','ETIMEDOUT','EAI_AGAIN','UND_ERR_CONNECT_TIMEOUT'].includes(error?.code)
43
- if (!retryable || attempt >= maxAttempts) { if (attempt >= maxAttempts && retryable) throw makeError(`request failed after ${maxAttempts} attempts`, 'LCX_RETRY_EXHAUSTED', { cause: error, retryable: true }); throw error }
44
- last = error; await sleep(Math.min(10_000, 500 * 2 ** (attempt - 1)), signal)
78
+ while (true) {
79
+ const { done, value } = await reader.read();
80
+ if (done)
81
+ return text + decoder.decode();
82
+ bytes += value.byteLength;
83
+ if (bytes > maxBytes)
84
+ throw makeError(`response exceeds ${maxBytes} bytes`, "LCX_RESPONSE_TOO_LARGE", { status: response.status });
85
+ text += decoder.decode(value, { stream: true });
86
+ }
87
+ }
88
+ finally {
89
+ try {
90
+ await reader.cancel();
91
+ }
92
+ catch {
93
+ // Cleanup must not replace the read/size error.
94
+ }
95
+ reader.releaseLock();
45
96
  }
46
- }
47
- throw last ?? makeError('request failed', 'LCX_REQUEST_FAILED')
48
97
  }
49
-
98
+ export async function fetchJsonWithRetry(url, body, headers = {}, signal, timeoutMs = DEFAULT_TIMEOUT_MS, options = {}) {
99
+ const maxAttempts = Math.max(1, Math.min(6, Number(options.maxAttempts ?? 3)));
100
+ const maxResponseBytes = options.maxResponseBytes ?? DEFAULT_MAX_BYTES;
101
+ let last;
102
+ for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
103
+ abortIfNeeded(signal);
104
+ try {
105
+ const requestSignal = combinedSignal(signal, timeoutMs);
106
+ const response = await fetch(url, {
107
+ method: "POST",
108
+ headers: {
109
+ "content-type": "application/json",
110
+ accept: "application/json",
111
+ ...headers,
112
+ },
113
+ body: JSON.stringify(body),
114
+ signal: requestSignal,
115
+ redirect: "error",
116
+ });
117
+ const text = await readLimited(response, response.ok ? maxResponseBytes : Math.min(maxResponseBytes, MAX_ERROR_BYTES));
118
+ if (!response.ok) {
119
+ const retryable = retryableStatus(response.status);
120
+ const providerRetryAfterMs = retryAfterFromHeaders(response.headers);
121
+ const error = makeError(`HTTP ${response.status}`, retryable ? "LCX_HTTP_RETRYABLE" : "LCX_HTTP_ERROR", {
122
+ status: response.status,
123
+ retryable,
124
+ requestId: response.headers.get("x-request-id") ??
125
+ response.headers.get("request-id") ??
126
+ undefined,
127
+ ...(providerRetryAfterMs === undefined
128
+ ? {}
129
+ : { providerRetryAfterMs }),
130
+ });
131
+ if (!error.retryable || attempt >= maxAttempts)
132
+ throw error;
133
+ last = error;
134
+ await sleep(delayFromHeaders(response.headers, attempt), signal);
135
+ continue;
136
+ }
137
+ let parsed;
138
+ try {
139
+ parsed = text ? JSON.parse(text) : {};
140
+ }
141
+ catch (cause) {
142
+ throw makeError(`invalid JSON response from ${url}`, "LCX_INVALID_JSON", { status: response.status, cause });
143
+ }
144
+ return parsed;
145
+ }
146
+ catch (error) {
147
+ if (signal?.aborted)
148
+ throw signal.reason ?? error;
149
+ const details = errorFacts(error);
150
+ const retryable = details.retryable === true ||
151
+ details.code === "LCX_HTTP_RETRYABLE" ||
152
+ error instanceof TypeError ||
153
+ [
154
+ "ECONNRESET",
155
+ "ECONNREFUSED",
156
+ "ETIMEDOUT",
157
+ "EAI_AGAIN",
158
+ "UND_ERR_CONNECT_TIMEOUT",
159
+ ].includes(details.code ?? "");
160
+ if (!retryable || attempt >= maxAttempts) {
161
+ if (attempt >= maxAttempts && retryable)
162
+ throw makeError(`request failed after ${maxAttempts} attempts`, "LCX_RETRY_EXHAUSTED", { cause: error, retryable: true });
163
+ throw error;
164
+ }
165
+ last = error;
166
+ await sleep(Math.min(10_000, 500 * 2 ** (attempt - 1)), signal);
167
+ }
168
+ }
169
+ throw last ?? makeError("request failed", "LCX_REQUEST_FAILED");
170
+ }
50
171
  export async function fetchSseWithRetry(url, body, headers = {}, signal, timeoutMs = DEFAULT_TIMEOUT_MS, options = {}) {
51
- const maxAttempts = Math.max(1, Math.min(6, Number(options.maxAttempts ?? 3))); let last
52
- for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
53
- abortIfNeeded(signal)
54
- try {
55
- const requestSignal = combinedSignal(signal, timeoutMs)
56
- const response = await fetch(url, { method: 'POST', headers: { 'content-type': 'application/json', accept: 'text/event-stream', ...headers }, body: JSON.stringify(body), signal: requestSignal, redirect: 'error' })
57
- if (!response.ok) {
58
- await readLimited(response, Math.min(options.maxResponseBytes ?? DEFAULT_MAX_BYTES, 512 * 1024))
59
- const retryable = retryableStatus(response.status); const providerRetryAfterMs = retryAfterFromHeaders(response.headers); const error = makeError(`HTTP ${response.status}`, retryable ? 'LCX_HTTP_RETRYABLE' : 'LCX_HTTP_ERROR', { status: response.status, retryable, requestId: response.headers.get('x-request-id') ?? response.headers.get('request-id') ?? undefined, ...(providerRetryAfterMs === undefined ? {} : { providerRetryAfterMs }) })
60
- if (!retryable || attempt >= maxAttempts) throw error; last = error; await sleep(delayFromHeaders(response.headers, attempt), signal); continue
61
- }
62
- if (typeof options.consume === 'function') return await options.consume(response, { requestSignal }); return response
63
- } catch (error) {
64
- if (signal?.aborted) throw signal.reason ?? error
65
- const retryable = error?.retryable === true || error?.code === 'LCX_HTTP_RETRYABLE' || error instanceof TypeError || ['ECONNRESET','ECONNREFUSED','ETIMEDOUT','EAI_AGAIN','UND_ERR_CONNECT_TIMEOUT'].includes(error?.code)
66
- if (!retryable || attempt >= maxAttempts) { if (attempt >= maxAttempts && retryable) throw makeError(`request failed after ${maxAttempts} attempts`, 'LCX_RETRY_EXHAUSTED', { cause: error, retryable: true }); throw error }
67
- last = error; await sleep(Math.min(10_000, 500 * 2 ** (attempt - 1)), signal)
172
+ const maxAttempts = Math.max(1, Math.min(6, Number(options.maxAttempts ?? 3)));
173
+ let last;
174
+ for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
175
+ abortIfNeeded(signal);
176
+ try {
177
+ const requestSignal = combinedSignal(signal, timeoutMs);
178
+ const response = await fetch(url, {
179
+ method: "POST",
180
+ headers: {
181
+ "content-type": "application/json",
182
+ accept: "text/event-stream",
183
+ ...headers,
184
+ },
185
+ body: JSON.stringify(body),
186
+ signal: requestSignal,
187
+ redirect: "error",
188
+ });
189
+ if (!response.ok) {
190
+ await readLimited(response, Math.min(options.maxResponseBytes ?? DEFAULT_MAX_BYTES, MAX_ERROR_BYTES));
191
+ const retryable = retryableStatus(response.status);
192
+ const providerRetryAfterMs = retryAfterFromHeaders(response.headers);
193
+ const error = makeError(`HTTP ${response.status}`, retryable ? "LCX_HTTP_RETRYABLE" : "LCX_HTTP_ERROR", {
194
+ status: response.status,
195
+ retryable,
196
+ requestId: response.headers.get("x-request-id") ??
197
+ response.headers.get("request-id") ??
198
+ undefined,
199
+ ...(providerRetryAfterMs === undefined
200
+ ? {}
201
+ : { providerRetryAfterMs }),
202
+ });
203
+ if (!retryable || attempt >= maxAttempts)
204
+ throw error;
205
+ last = error;
206
+ await sleep(delayFromHeaders(response.headers, attempt), signal);
207
+ continue;
208
+ }
209
+ if (typeof options.consume === "function")
210
+ return await options.consume(response, { requestSignal });
211
+ return response;
212
+ }
213
+ catch (error) {
214
+ if (signal?.aborted)
215
+ throw signal.reason ?? error;
216
+ const details = errorFacts(error);
217
+ const retryable = details.retryable === true ||
218
+ details.code === "LCX_HTTP_RETRYABLE" ||
219
+ error instanceof TypeError ||
220
+ [
221
+ "ECONNRESET",
222
+ "ECONNREFUSED",
223
+ "ETIMEDOUT",
224
+ "EAI_AGAIN",
225
+ "UND_ERR_CONNECT_TIMEOUT",
226
+ ].includes(details.code ?? "");
227
+ if (!retryable || attempt >= maxAttempts) {
228
+ if (attempt >= maxAttempts && retryable)
229
+ throw makeError(`request failed after ${maxAttempts} attempts`, "LCX_RETRY_EXHAUSTED", { cause: error, retryable: true });
230
+ throw error;
231
+ }
232
+ last = error;
233
+ await sleep(Math.min(10_000, 500 * 2 ** (attempt - 1)), signal);
234
+ }
68
235
  }
69
- }
70
- throw last ?? makeError('request failed', 'LCX_REQUEST_FAILED')
236
+ throw last ?? makeError("request failed", "LCX_REQUEST_FAILED");
71
237
  }
72
-
73
238
  export async function consumeSse(response, onEvent, options = {}) {
74
- if (!response?.body) throw makeError('SSE response has no body', 'LCX_INVALID_SSE')
75
- const maxBytes = options.maxResponseBytes ?? DEFAULT_MAX_BYTES; const reader = response.body.getReader(); const decoder = new TextDecoder(); let pending = ''; let dataLines = []; let bytes = 0
76
- const dispatch = () => { if (dataLines.length === 0) return; const data = dataLines.join('\n'); dataLines = []; if (data === '[DONE]') return; let event; try { event = JSON.parse(data) } catch (cause) { throw makeError('malformed SSE JSON', 'LCX_INVALID_SSE', { cause }) } onEvent(event) }
77
- try {
78
- while (true) { abortIfNeeded(options.signal); const { done, value } = await reader.read(); if (done) break; bytes += value.byteLength; if (bytes > maxBytes) throw makeError(`SSE response exceeds ${maxBytes} bytes`, 'LCX_RESPONSE_TOO_LARGE'); pending += decoder.decode(value, { stream: true }); let newline; while ((newline = pending.indexOf('\n')) >= 0) { const line = pending.slice(0, newline).replace(/\r$/u, ''); pending = pending.slice(newline + 1); if (line === '') dispatch(); else if (!line.startsWith(':') && line.startsWith('data:')) dataLines.push(line.slice(5).replace(/^ /u, '')) } }
79
- pending += decoder.decode(); if (pending.startsWith('data:')) dataLines.push(pending.slice(5).replace(/^ /u, '')); dispatch()
80
- } finally { await reader.cancel().catch(() => undefined) }
239
+ if (!response.body)
240
+ throw makeError("SSE response has no body", "LCX_INVALID_SSE");
241
+ const maxBytes = options.maxResponseBytes ?? DEFAULT_MAX_BYTES;
242
+ const reader = response.body.getReader();
243
+ const decoder = new TextDecoder();
244
+ let pending = "";
245
+ let dataLines = [];
246
+ let bytes = 0;
247
+ const dispatch = () => {
248
+ if (dataLines.length === 0)
249
+ return;
250
+ const data = dataLines.join("\n");
251
+ dataLines = [];
252
+ if (data === "[DONE]")
253
+ return;
254
+ let event;
255
+ try {
256
+ event = JSON.parse(data);
257
+ }
258
+ catch (cause) {
259
+ throw makeError("malformed SSE JSON", "LCX_INVALID_SSE", { cause });
260
+ }
261
+ onEvent(event);
262
+ };
263
+ try {
264
+ while (true) {
265
+ abortIfNeeded(options.signal);
266
+ const { done, value } = await reader.read();
267
+ if (done)
268
+ break;
269
+ bytes += value.byteLength;
270
+ if (bytes > maxBytes)
271
+ throw makeError(`SSE response exceeds ${maxBytes} bytes`, "LCX_RESPONSE_TOO_LARGE");
272
+ pending += decoder.decode(value, { stream: true });
273
+ let newline;
274
+ while ((newline = pending.indexOf("\n")) >= 0) {
275
+ const line = pending.slice(0, newline).replace(/\r$/u, "");
276
+ pending = pending.slice(newline + 1);
277
+ if (line === "")
278
+ dispatch();
279
+ else if (!line.startsWith(":") && line.startsWith("data:"))
280
+ dataLines.push(line.slice(5).replace(/^ /u, ""));
281
+ }
282
+ }
283
+ pending += decoder.decode();
284
+ if (pending.startsWith("data:"))
285
+ dataLines.push(pending.slice(5).replace(/^ /u, ""));
286
+ dispatch();
287
+ }
288
+ finally {
289
+ await reader.cancel().catch(() => undefined);
290
+ }
81
291
  }
@@ -0,0 +1,6 @@
1
+ import type { ClientModuleLoaderTarget } from "@deepseek-ai/dsh-client-modules/client";
2
+ declare global {
3
+ interface Window {
4
+ __ModuleLoader__: Pick<ClientModuleLoaderTarget, "load">;
5
+ }
6
+ }
@@ -0,0 +1,104 @@
1
+ import { buildCompactionResponsesBody } from "./responses-request.js";
2
+ type HeaderMap = Record<string, string>;
3
+ type CompactionItem = {
4
+ type: "compaction";
5
+ encrypted_content: string;
6
+ };
7
+ type FunctionCallItem = {
8
+ type: "function_call";
9
+ call_id: string;
10
+ };
11
+ type FunctionCallOutputItem = {
12
+ type: "function_call_output";
13
+ call_id: string;
14
+ };
15
+ type ValidatedOutputItem = CompactionItem | FunctionCallItem | FunctionCallOutputItem | {
16
+ type: string;
17
+ };
18
+ type CanonicalUsage = {
19
+ inputTokens: number;
20
+ outputTokens: number;
21
+ cacheReadTokens?: number;
22
+ cacheWriteTokens?: number;
23
+ reasoningTokens?: number;
24
+ };
25
+ type GenerationControls = {
26
+ reasoningEffort?: unknown;
27
+ temperature?: unknown;
28
+ maxTokens?: unknown;
29
+ };
30
+ type GenerationEnvelope = {
31
+ reasoning?: {
32
+ effort: string;
33
+ summary: "auto";
34
+ };
35
+ include?: string[];
36
+ temperature?: number;
37
+ max_output_tokens?: number;
38
+ };
39
+ type NativeCompactionBodyOptions = GenerationControls & {
40
+ model: string;
41
+ modelDescriptor?: Exclude<Parameters<typeof buildCompactionResponsesBody>[0]["model"], string>;
42
+ input: unknown[];
43
+ tools?: unknown;
44
+ promptCacheKey?: string;
45
+ promptCacheRetention?: string;
46
+ cacheRetention?: "none" | "short" | "long";
47
+ };
48
+ type NativeCompactionRequestOptions = NativeCompactionBodyOptions & {
49
+ baseURL: string;
50
+ idempotencyKey?: string;
51
+ headers?: HeaderMap;
52
+ signal?: AbortSignal;
53
+ timeoutMs?: number;
54
+ maxAttempts?: number;
55
+ maxResponseBytes?: number;
56
+ };
57
+ type SseParseOptions = {
58
+ signal?: AbortSignal;
59
+ maxResponseBytes?: number;
60
+ };
61
+ type NativeCompactionResult = {
62
+ object: unknown;
63
+ id?: string;
64
+ output: ValidatedOutputItem[];
65
+ compaction: CompactionItem;
66
+ usage?: CanonicalUsage;
67
+ };
68
+ export declare const REMOTE_COMPACTION_V2_FEATURE = "remote_compaction_v2";
69
+ /**
70
+ * @param {HeaderMap} [headers]
71
+ * @returns {HeaderMap}
72
+ */
73
+ export declare function mergeFeatureHeader(headers?: HeaderMap): HeaderMap;
74
+ /**
75
+ * @param {GenerationControls} [controls]
76
+ * @returns {GenerationEnvelope}
77
+ */
78
+ export declare function responsesGenerationEnvelope(controls?: GenerationControls): GenerationEnvelope;
79
+ /**
80
+ * @param {NativeCompactionBodyOptions} options
81
+ * @returns {NativeCompactionBody}
82
+ */
83
+ export declare function buildNativeCompactionBody({ model, modelDescriptor, input, tools, promptCacheKey, promptCacheRetention, cacheRetention, reasoningEffort, temperature, maxTokens, }: NativeCompactionBodyOptions): {
84
+ [x: string]: unknown;
85
+ } & {
86
+ model: string;
87
+ input: unknown[];
88
+ stream: boolean;
89
+ store: boolean;
90
+ tool_choice?: string;
91
+ parallel_tool_calls?: boolean;
92
+ };
93
+ /**
94
+ * Provider SSE events enter as unknown; response.completed remains authoritative.
95
+ * @param {Response} response
96
+ * @param {SseParseOptions} [options]
97
+ * @returns {Promise<NativeCompactionResult>}
98
+ */
99
+ export declare function parseNativeCompactionSse(response: Response, options?: SseParseOptions): Promise<NativeCompactionResult>;
100
+ /**
101
+ * @param {NativeCompactionRequestOptions} options
102
+ */
103
+ export declare function requestNativeCompaction({ baseURL, model, modelDescriptor, input, tools, promptCacheKey, promptCacheRetention, cacheRetention, reasoningEffort, temperature, maxTokens, idempotencyKey, headers, signal, timeoutMs, maxAttempts, maxResponseBytes, }: NativeCompactionRequestOptions): Promise<NativeCompactionResult>;
104
+ export {};
@@ -0,0 +1,78 @@
1
+ import { Session, type SessionStore } from "@deepseek-ai/dsh-session";
2
+ import type { ToolRuntime } from "@deepseek-ai/dsh-tools";
3
+ import { ServiceMutex } from "./service-mutex.js";
4
+ type CompatMethod = (this: object, agent: unknown, trigger: string, signal: AbortSignal) => Promise<unknown>;
5
+ type PrunerMethod = (...args: unknown[]) => unknown;
6
+ type MutableService = Record<PropertyKey, unknown>;
7
+ interface CompactionService extends MutableService {
8
+ compactIfNeeded: CompatMethod;
9
+ }
10
+ export interface CompactionPatchRecord {
11
+ compaction: CompactionService;
12
+ original: CompatMethod;
13
+ wrapper: CompatMethod;
14
+ mutex: ServiceMutex;
15
+ lifecycle: AbortController;
16
+ }
17
+ export type CompactionPatchRecords = Map<object, CompactionPatchRecord>;
18
+ export type CompactionPatchBehavior = (agent: unknown, trigger: string, signal: AbortSignal, callOriginal: (signal: AbortSignal) => Promise<unknown>) => Promise<unknown>;
19
+ interface PrunerService extends MutableService {
20
+ pruneSession: unknown;
21
+ }
22
+ export interface PrunerPatchRecord {
23
+ pruner: PrunerService;
24
+ original: unknown;
25
+ replacement: PrunerMethod;
26
+ }
27
+ interface ConfigService extends MutableService {
28
+ config: unknown;
29
+ }
30
+ export interface ConfigPatchRecord {
31
+ service: ConfigService;
32
+ original: unknown;
33
+ installed: unknown;
34
+ }
35
+ export declare function agentSessionId(agent: unknown): string;
36
+ export declare function agentUsesSession(agent: unknown, session: Session): boolean;
37
+ export declare function sessionFromAgent(agent: unknown): Session | undefined;
38
+ export declare function readAgentRouteState(agent: unknown): {
39
+ requestConfig: unknown;
40
+ options: unknown;
41
+ sessionId: string;
42
+ };
43
+ export declare function scopedToolRuntime(agent: unknown): Pick<ToolRuntime, "register"> | undefined;
44
+ export declare function tokenMeterTotal(value: unknown, session: Session): number | undefined;
45
+ export declare function sessionsService(ctx: unknown): Pick<SessionStore, "get"> | undefined;
46
+ export declare function sessionFor(ctx: unknown, sessionId: string): Session | undefined;
47
+ export declare function readWebSearchProvider(ctx: unknown): unknown;
48
+ export declare function writeWebSearchProvider(ctx: unknown, providerId: unknown): boolean;
49
+ export declare function contextService(ctx: unknown, name: string): unknown;
50
+ export declare function resolveContextService(ctx: unknown, name: string): unknown;
51
+ export declare function resolveScopedService(agent: unknown, name: string): unknown;
52
+ export declare function resolveAgentService(ctx: unknown, agent: unknown, name: string): unknown;
53
+ export declare function concreteService(value: unknown): unknown;
54
+ export declare function compactionPatchCandidate(value: unknown, records: ReadonlyMap<object, CompactionPatchRecord>): Pick<CompactionPatchRecord, "compaction" | "original"> | undefined;
55
+ export declare function installCompactionPatch(records: CompactionPatchRecords, candidate: Pick<CompactionPatchRecord, "compaction" | "original">, mutex: ServiceMutex, lifecycle: AbortController, behavior: CompactionPatchBehavior): boolean;
56
+ export declare function restoreCompactionPatches(records: Map<object, CompactionPatchRecord>, entries?: Iterable<CompactionPatchRecord>): void;
57
+ export declare function toolResultPrunerState(value: unknown): {
58
+ pruner: PrunerService | undefined;
59
+ original: unknown;
60
+ };
61
+ export declare function patchToolResultPruner(state: {
62
+ pruner: PrunerService | undefined;
63
+ original: unknown;
64
+ }, replacement: PrunerMethod): PrunerPatchRecord | undefined;
65
+ export declare function restoreToolResultPruner(record: PrunerPatchRecord | undefined): void;
66
+ export declare function compactionConfigState(service: unknown): {
67
+ service: ConfigService | undefined;
68
+ original: unknown;
69
+ };
70
+ export declare function patchCompactionConfig(state: {
71
+ service: ConfigService | undefined;
72
+ original: unknown;
73
+ }, createConfig: (originalConfig: unknown) => unknown): ConfigPatchRecord | undefined;
74
+ export declare function restoreCompactionConfig(record: ConfigPatchRecord | undefined): void;
75
+ export declare function patchVisibleWebSearchTimeout(agent: unknown, getTimeoutMs: () => number | undefined, patchedDefinitions: Map<MutableService, unknown>): void;
76
+ export declare function refreshVisibleWebSearchTimeouts(patchedDefinitions: ReadonlyMap<MutableService, unknown>, timeoutMs: number | undefined): void;
77
+ export declare function restoreVisibleWebSearchTimeouts(patchedDefinitions: Map<MutableService, unknown>): void;
78
+ export {};
@@ -0,0 +1,82 @@
1
+ import { requestImageHandleText, type Message } from "@deepseek-ai/dsh-llm";
2
+ import type { Context } from "@deepseek-ai/cordis";
3
+ import type { ResponseInputItem } from "openai/resources/responses/responses.js";
4
+ type ImageAttachmentRef = Parameters<typeof requestImageHandleText>[0];
5
+ import type { Model as PiModel, Tool as PiToolValue } from "@earendil-works/pi-ai";
6
+ declare const DEFAULT_MAX_REQUEST_IMAGE_BYTES: number;
7
+ declare const DEFAULT_REQUEST_IMAGE_PIXEL_BUDGET: number;
8
+ declare const DEFAULT_REQUEST_IMAGE_MAX_BYTES: number;
9
+ export { DEFAULT_MAX_REQUEST_IMAGE_BYTES, DEFAULT_REQUEST_IMAGE_PIXEL_BUDGET, DEFAULT_REQUEST_IMAGE_MAX_BYTES, };
10
+ type WireRecord = Record<string, unknown>;
11
+ type ImageSupport = "supported" | "unsupported" | "unknown";
12
+ type PiTool = PiToolValue;
13
+ type DshContext = Pick<Context, "attachments" | "llm" | "fs">;
14
+ type ImageOptions = {
15
+ imageSupport: ImageSupport;
16
+ signal?: AbortSignal;
17
+ maxRequestImageBytes: number;
18
+ requestImagePixelBudget: number;
19
+ requestImageMaxBytes: number;
20
+ };
21
+ type SerializeOptions = Partial<ImageOptions> & {
22
+ route?: unknown;
23
+ model?: unknown;
24
+ responsesCompat?: unknown;
25
+ systemPrompt?: unknown;
26
+ includeSystemPrompt?: boolean;
27
+ onReplayDegrade?: unknown;
28
+ tools?: PiTool[];
29
+ };
30
+ type ReplayBlock = WireRecord & {
31
+ type: "text" | "reasoning" | "tool-call";
32
+ };
33
+ type ReplayResponse = WireRecord & {
34
+ kind: "pi-ai";
35
+ version: 2;
36
+ api: string;
37
+ provider: string;
38
+ model: string;
39
+ stopReason: "stop" | "length" | "toolUse" | "error" | "aborted";
40
+ };
41
+ export declare function responseInputItems(value: unknown): ResponseInputItem[];
42
+ export declare function resolveModelImageSupport(ctx: DshContext | undefined, route: {
43
+ provider?: unknown;
44
+ model?: unknown;
45
+ }, signal?: AbortSignal): Promise<ImageSupport>;
46
+ export declare function readDshPiReplayState(value: unknown): {
47
+ response: ReplayResponse;
48
+ blocks: ReplayBlock[];
49
+ };
50
+ export declare function resolvePiResponsesModel(options: {
51
+ imageSupport: unknown;
52
+ route?: unknown;
53
+ model?: unknown;
54
+ responsesCompat?: unknown;
55
+ }): {
56
+ id: string;
57
+ name: string;
58
+ api: string;
59
+ provider: string;
60
+ baseUrl: string;
61
+ reasoning: boolean;
62
+ input: any[];
63
+ cost: {};
64
+ contextWindow: number;
65
+ maxTokens: number;
66
+ compat?: {
67
+ [x: string]: unknown;
68
+ } | undefined;
69
+ };
70
+ export declare function serializeDshMessages(messages: readonly Message[], ctx: DshContext | undefined, options?: SerializeOptions): Promise<{
71
+ input: import("openai/resources/responses/responses.js").ResponseInput;
72
+ imageMap: Map<string, import("@deepseek-ai/dsh-attachment").ImageAttachmentRef>;
73
+ tools: import("openai/resources/responses/responses.js").Tool[] | undefined;
74
+ model: PiModel<"openai-responses">;
75
+ grammarToolInputProperties: ReadonlyMap<string, string>;
76
+ deferredToolsMode: string | undefined;
77
+ }>;
78
+ export declare function responsesTools(tools: readonly PiTool[] | undefined): any[] | undefined;
79
+ export declare function persistNativeImageReferences(output: readonly unknown[], imageMap: ReadonlyMap<string, ImageAttachmentRef>): unknown[];
80
+ export declare function hydrateNativeImageReferences(output: unknown, ctx: DshContext | undefined, options?: Partial<ImageOptions> & {
81
+ imageMap?: unknown;
82
+ }): Promise<unknown[]>;