dsh-lcx-codex 0.3.3 → 0.4.0-rc.13
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.
- package/ARCHITECTURE.md +93 -0
- package/CHANGELOG.md +143 -41
- package/README.md +208 -100
- package/README_EN.md +133 -101
- package/assets/dsh-lcx-codex-banner.jpg +0 -0
- package/cordis.patch.yml +10 -4
- package/lib/client.js +117 -194
- package/lib/compact-v2.js +124 -273
- package/lib/dsh-responses.js +299 -0
- package/lib/index.js +548 -1593
- package/lib/json-store.js +34 -0
- package/lib/legacy-v3.js +20 -0
- package/lib/native-checkpoint.js +93 -0
- package/lib/responses-replay.js +281 -0
- package/lib/route.js +222 -0
- package/lib/service-mutex.js +72 -0
- package/lib/transport.js +59 -380
- package/lib/web-run-output.js +13 -152
- package/lib/web-search-alpha.js +26 -448
- package/lib/web-search-capability.js +8 -10
- package/lib/web-search-hosted.js +22 -372
- package/lib/web-search-ref-store.js +7 -7
- package/package.json +20 -14
- package/scripts/probe-alpha.mjs +28 -76
- package/scripts/validate-dsh-schema.mjs +31 -0
- package/lib/checkpoint-store-v3.js +0 -459
- package/lib/compact.js +0 -752
- package/lib/dsh-pi-responses.js +0 -339
- package/lib/private-file.js +0 -18
- package/lib/session-lease.js +0 -70
- package/lib/web-search-store.js +0 -181
package/lib/index.js
CHANGED
|
@@ -1,31 +1,46 @@
|
|
|
1
1
|
import z from '@deepseek-ai/schemastery'
|
|
2
|
-
import {
|
|
3
|
-
import { settingsNamespace } from '@deepseek-ai/dsh-settings'
|
|
4
|
-
import { clampOpenAIPromptCacheKey } from '@earendil-works/pi-ai/api/openai-prompt-cache'
|
|
2
|
+
import { installSettingsSection, settingsNamespace } from '@deepseek-ai/dsh-settings'
|
|
5
3
|
import { randomUUID } from 'node:crypto'
|
|
6
4
|
import { homedir } from 'node:os'
|
|
7
5
|
import { join } from 'node:path'
|
|
8
|
-
import {
|
|
6
|
+
import { AsyncLocalStorage } from 'node:async_hooks'
|
|
7
|
+
import { symbols as cordisSymbols } from '@deepseek-ai/cordis'
|
|
8
|
+
import { fetchJsonWithRetry } from './transport.js'
|
|
9
9
|
import {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
inputImageCount,
|
|
19
|
-
checkpointNativeReplacementHistory,
|
|
20
|
-
latestPortableMarker,
|
|
21
|
-
normalizeCompactionResponse,
|
|
22
|
-
persistNativeImageReferences,
|
|
10
|
+
authenticatedHeaders,
|
|
11
|
+
currentRoute,
|
|
12
|
+
generationControlsFromHeader,
|
|
13
|
+
generationControlsFromSession,
|
|
14
|
+
promptCacheKey,
|
|
15
|
+
promptCacheRetention,
|
|
16
|
+
promptCacheSessionId,
|
|
17
|
+
resolveResponsesRouteConfig,
|
|
23
18
|
routeFingerprint,
|
|
24
|
-
|
|
25
|
-
} from './
|
|
26
|
-
import {
|
|
27
|
-
|
|
28
|
-
|
|
19
|
+
updateRequestHeaderCache,
|
|
20
|
+
} from './route.js'
|
|
21
|
+
import {
|
|
22
|
+
DEFAULT_MAX_REQUEST_IMAGE_BYTES,
|
|
23
|
+
DEFAULT_REQUEST_IMAGE_PIXEL_BUDGET,
|
|
24
|
+
DEFAULT_REQUEST_IMAGE_MAX_BYTES,
|
|
25
|
+
hydrateNativeImageReferences,
|
|
26
|
+
resolveModelImageSupport,
|
|
27
|
+
serializeDshMessages,
|
|
28
|
+
} from './dsh-responses.js'
|
|
29
|
+
import { requestNativeCompaction } from './compact-v2.js'
|
|
30
|
+
import { requestNativeReplay } from './responses-replay.js'
|
|
31
|
+
import {
|
|
32
|
+
checkpointStateForMessage,
|
|
33
|
+
compactCheckpointId,
|
|
34
|
+
createNativeCheckpointBlock,
|
|
35
|
+
legacyCheckpointId,
|
|
36
|
+
nativeCheckpointChunks,
|
|
37
|
+
portableMessagesForCheckpoint,
|
|
38
|
+
retainedConversationInput,
|
|
39
|
+
shadowedMessagesForCheckpoint,
|
|
40
|
+
rewriteCheckpointsPortable,
|
|
41
|
+
stateRouteCompatible,
|
|
42
|
+
} from './native-checkpoint.js'
|
|
43
|
+
import { legacyRouteCompatible, legacyV3Id, loadLegacyRecord } from './legacy-v3.js'
|
|
29
44
|
import {
|
|
30
45
|
HOSTED_SEARCH_OUTPUT,
|
|
31
46
|
HOSTED_SEARCH_PARAMETERS,
|
|
@@ -45,28 +60,28 @@ import {
|
|
|
45
60
|
} from './web-search-alpha.js'
|
|
46
61
|
import { AlphaCapabilityStore, alphaCapabilityFingerprint, alphaCapabilityUsable } from './web-search-capability.js'
|
|
47
62
|
import { AlphaRefStore } from './web-search-ref-store.js'
|
|
48
|
-
import {
|
|
63
|
+
import { ServiceMutex } from './service-mutex.js'
|
|
49
64
|
|
|
50
|
-
const name = 'lcx-codex'
|
|
51
|
-
const inject = ['llm', 'web', '
|
|
52
|
-
const
|
|
53
|
-
const
|
|
65
|
+
export const name = 'lcx-codex'
|
|
66
|
+
export const inject = ['llm', 'web', 'sessions']
|
|
67
|
+
const SETTINGS_NS = settingsNamespace('lcx-codex')
|
|
68
|
+
const ADVANCED_HOSTED_TOOL = 'websearch_gpt_advanced'
|
|
69
|
+
const ALPHA_TOOL = 'websearch_alpha'
|
|
54
70
|
const COMPACTION_DIRECTIVE = 'You are now acting as a compaction engine'
|
|
55
|
-
const
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
const UNSUPPORTED_IMAGE_PLACEHOLDER = '[image omitted because the target model does not support image input]'
|
|
71
|
+
const bypassReplayOptions = new WeakSet()
|
|
72
|
+
const hostedSearchRouteContext = new AsyncLocalStorage()
|
|
73
|
+
|
|
74
|
+
function dshHome() { return process.env.DSH_HOME ?? join(homedir(), '.dsh') }
|
|
75
|
+
function defaultLegacyCheckpointPath() { return join(dshHome(), 'storages', 'lcx-codex', 'checkpoints-v3.json') }
|
|
76
|
+
function defaultAlphaCapabilityPath() { return join(dshHome(), 'storages', 'lcx-codex', 'web-alpha-capabilities.json') }
|
|
77
|
+
function defaultAlphaRefPath() { return join(dshHome(), 'storages', 'lcx-codex', 'web-alpha-refs.json') }
|
|
63
78
|
|
|
64
|
-
const Config = z.object({
|
|
79
|
+
export const Config = z.object({
|
|
65
80
|
provider: z.string().default('lcx'),
|
|
66
81
|
baseURL: z.string().default('https://api.lcxbot.com/v1'),
|
|
67
82
|
apiKeyEnv: z.string().default('LCX_API_KEY'),
|
|
68
83
|
model: z.string().default('gpt-5.6-sol'),
|
|
69
|
-
|
|
84
|
+
legacyCheckpointPath: z.string().default(''),
|
|
70
85
|
checkpointPath: z.string().default(''),
|
|
71
86
|
alphaCapabilityPath: z.string().default(''),
|
|
72
87
|
alphaRefPath: z.string().default(''),
|
|
@@ -76,80 +91,46 @@ const Config = z.object({
|
|
|
76
91
|
webSearchProvider: z.string().default('lcx-responses'),
|
|
77
92
|
webMaxResults: z.number().default(8),
|
|
78
93
|
timeoutMs: z.number().default(300000),
|
|
79
|
-
maxResponseBytes: z.number().default(
|
|
94
|
+
maxResponseBytes: z.number().default(8 * 1024 * 1024),
|
|
80
95
|
maxAttempts: z.number().default(3),
|
|
81
96
|
maxRequestImageBytes: z.number().default(DEFAULT_MAX_REQUEST_IMAGE_BYTES),
|
|
97
|
+
requestImagePixelBudget: z.number().default(DEFAULT_REQUEST_IMAGE_PIXEL_BUDGET),
|
|
98
|
+
requestImageMaxBytes: z.number().default(DEFAULT_REQUEST_IMAGE_MAX_BYTES),
|
|
99
|
+
portableReplayMaxChars: z.number().default(80_000),
|
|
100
|
+
nativeRetentionTokenBudget: z.number().default(64_000),
|
|
101
|
+
assistantRetentionTokenReserve: z.number().default(24_000),
|
|
102
|
+
assistantRetentionPerMessageTokenCap: z.number().default(3_000),
|
|
103
|
+
webSearchTimeoutMs: z.number().default(240_000),
|
|
104
|
+
autoCompactionThresholdPercent: z.number().default(90),
|
|
105
|
+
emergencyPruneThresholdPercent: z.number().default(95),
|
|
82
106
|
})
|
|
83
107
|
|
|
84
108
|
const SettingsSchema = z.object({
|
|
85
109
|
enabled: z.boolean().default(false),
|
|
86
110
|
webSearch: z.boolean().default(false),
|
|
111
|
+
advancedHostedSearch: z.boolean().default(false),
|
|
87
112
|
alphaSearch: z.boolean().default(false),
|
|
88
113
|
remoteCompaction: z.boolean().default(false),
|
|
89
114
|
fallbackToBasicCompaction: z.boolean().default(true),
|
|
115
|
+
autoCompaction: z.boolean().default(true),
|
|
116
|
+
webSearchTimeoutSeconds: z.number().default(240),
|
|
117
|
+
autoCompactionThresholdPercent: z.number().default(90),
|
|
118
|
+
emergencyPruneThresholdPercent: z.number().default(95),
|
|
90
119
|
provider: z.string().default('lcx'),
|
|
91
120
|
baseURL: z.string().default('https://api.lcxbot.com/v1'),
|
|
92
121
|
apiKeyEnv: z.string().default('LCX_API_KEY'),
|
|
93
122
|
model: z.string().default('gpt-5.6-sol'),
|
|
94
|
-
compactTransport: z.string().default('native-v2'),
|
|
95
123
|
})
|
|
96
124
|
|
|
97
|
-
function webError(message, code, cause) {
|
|
98
|
-
const error = new Error(message, cause === undefined ? undefined : { cause })
|
|
99
|
-
error.name = 'WebError'
|
|
100
|
-
error.code = code
|
|
101
|
-
return error
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
function defaultCheckpointPath() {
|
|
105
|
-
return join(process.env.DSH_HOME ?? join(homedir(), '.dsh'), 'storages', 'lcx-codex', 'checkpoints-v3.json')
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
function defaultAlphaCapabilityPath() {
|
|
109
|
-
return join(process.env.DSH_HOME ?? join(homedir(), '.dsh'), 'storages', 'lcx-codex', 'web-alpha-capabilities.json')
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
function defaultAlphaRefPath() {
|
|
113
|
-
return join(process.env.DSH_HOME ?? join(homedir(), '.dsh'), 'storages', 'lcx-codex', 'web-alpha-refs.json')
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
function portableCheckpointPath(checkpointPath) {
|
|
117
|
-
const value = String(checkpointPath)
|
|
118
|
-
if (/-v3\.json$/iu.test(value)) return value
|
|
119
|
-
return /\.json$/iu.test(value) ? value.replace(/\.json$/iu, '-v3.json') : `${value}-v3.json`
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
const COMPACT_TRANSPORT_UNSUPPORTED_CODE = 'LCX_COMPACT_TRANSPORT_UNSUPPORTED'
|
|
123
|
-
const COMPACT_TRANSPORT_INVALID_CODE = 'LCX_COMPACT_TRANSPORT_INVALID'
|
|
124
|
-
|
|
125
|
-
function normalizeCompactTransport(value) {
|
|
126
|
-
const normalized = String(value ?? 'native-v2').trim().toLowerCase()
|
|
127
|
-
if (normalized === 'legacy') {
|
|
128
|
-
const error = new Error('Legacy compact transport is unsupported; only native-v2 is available')
|
|
129
|
-
error.code = COMPACT_TRANSPORT_UNSUPPORTED_CODE
|
|
130
|
-
throw error
|
|
131
|
-
}
|
|
132
|
-
if (normalized !== 'native-v2') {
|
|
133
|
-
const error = new Error(`Unsupported compact transport: ${String(value)}`)
|
|
134
|
-
error.code = COMPACT_TRANSPORT_INVALID_CODE
|
|
135
|
-
throw error
|
|
136
|
-
}
|
|
137
|
-
return normalized
|
|
138
|
-
}
|
|
139
|
-
|
|
140
125
|
function normalizeConfig(input = {}) {
|
|
141
|
-
const
|
|
126
|
+
const legacy = input.legacyCheckpointPath || input.checkpointPath || defaultLegacyCheckpointPath()
|
|
142
127
|
return {
|
|
143
128
|
provider: input.provider || 'lcx',
|
|
144
129
|
baseURL: String(input.baseURL || 'https://api.lcxbot.com/v1').replace(/\/+$/u, ''),
|
|
145
130
|
apiKeyEnv: input.apiKeyEnv || 'LCX_API_KEY',
|
|
146
131
|
model: input.model || 'gpt-5.6-sol',
|
|
147
|
-
compactTransport: normalizeCompactTransport(input.compactTransport),
|
|
148
132
|
headers: input.headers && typeof input.headers === 'object' ? { ...input.headers } : {},
|
|
149
|
-
|
|
150
|
-
supportsLongCacheRetention: input.supportsLongCacheRetention !== false,
|
|
151
|
-
checkpointPath: input.checkpointPath || defaultCheckpointPath(),
|
|
152
|
-
portableCheckpointPath: input.portableCheckpointPath || portableCheckpointPath(input.checkpointPath || defaultCheckpointPath()),
|
|
133
|
+
legacyCheckpointPath: legacy,
|
|
153
134
|
alphaCapabilityPath: input.alphaCapabilityPath || defaultAlphaCapabilityPath(),
|
|
154
135
|
alphaRefPath: input.alphaRefPath || defaultAlphaRefPath(),
|
|
155
136
|
alphaProfile: String(input.alphaProfile ?? ''),
|
|
@@ -158,1626 +139,600 @@ function normalizeConfig(input = {}) {
|
|
|
158
139
|
webSearchProvider: input.webSearchProvider || 'lcx-responses',
|
|
159
140
|
webMaxResults: Number.isInteger(input.webMaxResults) && input.webMaxResults > 0 ? input.webMaxResults : 8,
|
|
160
141
|
timeoutMs: Number.isInteger(input.timeoutMs) && input.timeoutMs > 0 ? input.timeoutMs : 300000,
|
|
161
|
-
maxResponseBytes: Number.isInteger(input.maxResponseBytes) && input.maxResponseBytes > 0 ? input.maxResponseBytes :
|
|
142
|
+
maxResponseBytes: Number.isInteger(input.maxResponseBytes) && input.maxResponseBytes > 0 ? input.maxResponseBytes : 8 * 1024 * 1024,
|
|
162
143
|
maxAttempts: Number.isInteger(input.maxAttempts) && input.maxAttempts > 0 ? Math.min(input.maxAttempts, 6) : 3,
|
|
163
144
|
maxRequestImageBytes: Number.isSafeInteger(input.maxRequestImageBytes) && input.maxRequestImageBytes > 0 ? input.maxRequestImageBytes : DEFAULT_MAX_REQUEST_IMAGE_BYTES,
|
|
145
|
+
requestImagePixelBudget: Number.isSafeInteger(input.requestImagePixelBudget) && input.requestImagePixelBudget > 0 ? input.requestImagePixelBudget : DEFAULT_REQUEST_IMAGE_PIXEL_BUDGET,
|
|
146
|
+
requestImageMaxBytes: Number.isSafeInteger(input.requestImageMaxBytes) && input.requestImageMaxBytes > 0 ? input.requestImageMaxBytes : DEFAULT_REQUEST_IMAGE_MAX_BYTES,
|
|
147
|
+
portableReplayMaxChars: Number.isSafeInteger(input.portableReplayMaxChars) && input.portableReplayMaxChars > 0 ? input.portableReplayMaxChars : 80_000,
|
|
148
|
+
nativeRetentionTokenBudget: Number.isSafeInteger(input.nativeRetentionTokenBudget) && input.nativeRetentionTokenBudget > 0 ? input.nativeRetentionTokenBudget : 64_000,
|
|
149
|
+
assistantRetentionTokenReserve: Number.isSafeInteger(input.assistantRetentionTokenReserve) && input.assistantRetentionTokenReserve >= 0 ? input.assistantRetentionTokenReserve : 24_000,
|
|
150
|
+
assistantRetentionPerMessageTokenCap: Number.isSafeInteger(input.assistantRetentionPerMessageTokenCap) && input.assistantRetentionPerMessageTokenCap > 0 ? input.assistantRetentionPerMessageTokenCap : 3_000,
|
|
151
|
+
webSearchTimeoutMs: Number.isSafeInteger(input.webSearchTimeoutMs) && input.webSearchTimeoutMs >= 30_000 ? Math.min(input.webSearchTimeoutMs, 600_000) : 240_000,
|
|
152
|
+
autoCompactionThresholdPercent: Number.isFinite(input.autoCompactionThresholdPercent) ? Math.min(95, Math.max(85, Number(input.autoCompactionThresholdPercent))) : 90,
|
|
153
|
+
emergencyPruneThresholdPercent: Number.isFinite(input.emergencyPruneThresholdPercent) ? Math.min(99, Math.max(90, Number(input.emergencyPruneThresholdPercent))) : 95,
|
|
164
154
|
}
|
|
165
155
|
}
|
|
166
156
|
|
|
167
|
-
function
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
function settingsValue(ctx, namespace) {
|
|
172
|
-
const settings = ctx?.get?.('settings') ?? ctx?.settings
|
|
173
|
-
return settings?.get?.(settingsNamespace(namespace))
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
function resolveResponsesRouteConfig(ctx, options, fallbackConfig) {
|
|
177
|
-
if (!isGptModel(options.model)) return undefined
|
|
178
|
-
const provider = String(options.provider ?? '')
|
|
179
|
-
const section = settingsValue(ctx, 'llm-pi-ai')
|
|
180
|
-
const profile = section?.providers?.[provider]
|
|
181
|
-
if (profile === undefined) {
|
|
182
|
-
if (provider !== fallbackConfig.provider) return undefined
|
|
183
|
-
return normalizeConfig({ ...fallbackConfig, provider, model: options.model })
|
|
184
|
-
}
|
|
185
|
-
if (profile.api !== 'openai-responses') return undefined
|
|
186
|
-
const baseURL = profile.baseURL ?? (provider === fallbackConfig.provider ? fallbackConfig.baseURL : undefined)
|
|
187
|
-
const apiKeyEnv = profile.apiKeyEnv ?? (provider === fallbackConfig.provider ? fallbackConfig.apiKeyEnv : undefined)
|
|
188
|
-
if (!baseURL || !apiKeyEnv) return undefined
|
|
189
|
-
return normalizeConfig({
|
|
190
|
-
...fallbackConfig,
|
|
191
|
-
provider,
|
|
192
|
-
baseURL,
|
|
193
|
-
apiKeyEnv,
|
|
194
|
-
headers: profile.headers ?? fallbackConfig.headers,
|
|
195
|
-
cacheRetention: profile.cacheRetention ?? fallbackConfig.cacheRetention,
|
|
196
|
-
supportsLongCacheRetention: profile.compat?.supportsLongCacheRetention ?? fallbackConfig.supportsLongCacheRetention,
|
|
197
|
-
model: options.model,
|
|
198
|
-
compactTransport: profile.compactTransport ?? fallbackConfig.compactTransport,
|
|
199
|
-
timeoutMs: profile.timeoutMs ?? fallbackConfig.timeoutMs,
|
|
200
|
-
maxRequestImageBytes: profile.maxRequestImageBytes ?? fallbackConfig.maxRequestImageBytes,
|
|
201
|
-
maxAttempts: retryAttempts(profile.retryPolicy, fallbackConfig.maxAttempts),
|
|
202
|
-
})
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
function retryAttempts(policy, fallback) {
|
|
206
|
-
if (!policy || typeof policy !== 'object') return fallback
|
|
207
|
-
try {
|
|
208
|
-
const resolved = resolveRetryPolicy(policy, 'llm-pi-ai provider retryPolicy')
|
|
209
|
-
if (resolved.mode === 'normal' && Number.isSafeInteger(resolved.maxRetries)) return Math.min(resolved.maxRetries + 1, 6)
|
|
210
|
-
} catch {
|
|
211
|
-
return fallback
|
|
212
|
-
}
|
|
213
|
-
return fallback
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
function nativeRouteError(options) {
|
|
217
|
-
const error = new Error(`【GPT 专属原生远程压缩】当前路由不满足 GPT + openai-responses 条件:${String(options.provider)}/${String(options.model)}`)
|
|
218
|
-
error.code = 'LCX_CHECKPOINT_ROUTE_MISMATCH'
|
|
219
|
-
return error
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
function ambientApiKey(config) {
|
|
223
|
-
const key = String(process.env[config.apiKeyEnv] ?? '').trim()
|
|
224
|
-
if (!key) throw new Error(`DSH provider credential is unavailable: ${config.apiKeyEnv}`)
|
|
225
|
-
return key
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
async function resolveApiKey(ctx, config) {
|
|
229
|
-
const credentials = ctx?.get?.('credentials') ?? ctx?.credentials
|
|
230
|
-
if (credentials?.resolve) {
|
|
231
|
-
const resolved = await credentials.resolve(config.apiKeyEnv)
|
|
232
|
-
if (typeof resolved?.value === 'string' && resolved.value.length > 0) return resolved.value
|
|
233
|
-
}
|
|
234
|
-
return ambientApiKey(config)
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
function attachmentImageResolver(ctx) {
|
|
238
|
-
const attachments = ctx?.get?.('attachments') ?? ctx?.attachments
|
|
239
|
-
return async (block, signal) => {
|
|
240
|
-
if (!attachments?.readImage) {
|
|
241
|
-
const error = new Error('LCX Compact image input requires the DSH attachment service')
|
|
242
|
-
error.code = 'LCX_COMPACT_IMAGE_UNAVAILABLE'
|
|
243
|
-
throw error
|
|
244
|
-
}
|
|
245
|
-
try {
|
|
246
|
-
const stored = await attachments.readImage(block?.attachment, signal)
|
|
247
|
-
return { data: stored?.data, mediaType: stored?.ref?.mediaType ?? block?.attachment?.mediaType }
|
|
248
|
-
} catch (error) {
|
|
249
|
-
const wrapped = new Error('LCX Compact failed to read image attachment', { cause: error })
|
|
250
|
-
wrapped.code = 'LCX_COMPACT_IMAGE_UNAVAILABLE'
|
|
251
|
-
throw wrapped
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
function usageFrom(value, seen = new Set()) {
|
|
257
|
-
if (!value || typeof value !== 'object' || seen.has(value)) return undefined
|
|
258
|
-
seen.add(value)
|
|
259
|
-
if (Number.isFinite(value.input_tokens) || Number.isFinite(value.output_tokens)) {
|
|
260
|
-
const totalInputTokens = Number(value.input_tokens ?? 0)
|
|
261
|
-
const outputTokens = Number(value.output_tokens ?? 0)
|
|
262
|
-
const cachedValue = Number(value.input_tokens_details?.cached_tokens ?? value.input_token_details?.cached_tokens ?? value.prompt_tokens_details?.cached_tokens ?? 0)
|
|
263
|
-
const cacheWriteValue = Number(value.input_tokens_details?.cache_write_tokens ?? value.input_token_details?.cache_write_tokens ?? value.prompt_tokens_details?.cache_write_tokens ?? 0)
|
|
264
|
-
const cacheReadTokens = Number.isFinite(cachedValue) && cachedValue > 0 ? cachedValue : 0
|
|
265
|
-
const cacheWriteTokens = Number.isFinite(cacheWriteValue) && cacheWriteValue > 0 ? cacheWriteValue : 0
|
|
266
|
-
const inputTokens = Number.isFinite(totalInputTokens)
|
|
267
|
-
? Math.max(0, totalInputTokens - cacheReadTokens - cacheWriteTokens)
|
|
268
|
-
: 0
|
|
269
|
-
const normalizedOutputTokens = Number.isFinite(outputTokens) && outputTokens > 0 ? outputTokens : 0
|
|
270
|
-
const reasoningValue = Number(value.output_token_details?.reasoning_tokens ?? 0)
|
|
271
|
-
if (inputTokens <= 0 && cacheReadTokens <= 0 && cacheWriteTokens <= 0 && normalizedOutputTokens <= 0) return undefined
|
|
272
|
-
const usage = { inputTokens, outputTokens: normalizedOutputTokens }
|
|
273
|
-
if (cacheReadTokens > 0) usage.cacheReadTokens = cacheReadTokens
|
|
274
|
-
if (cacheWriteTokens > 0) usage.cacheWriteTokens = cacheWriteTokens
|
|
275
|
-
if (Number.isFinite(reasoningValue) && reasoningValue > 0) usage.reasoningTokens = reasoningValue
|
|
276
|
-
return usage
|
|
277
|
-
}
|
|
278
|
-
if (Array.isArray(value)) {
|
|
279
|
-
for (const item of value) {
|
|
280
|
-
const found = usageFrom(item, seen)
|
|
281
|
-
if (found) return found
|
|
282
|
-
}
|
|
283
|
-
} else {
|
|
284
|
-
for (const item of Object.values(value)) {
|
|
285
|
-
const found = usageFrom(item, seen)
|
|
286
|
-
if (found) return found
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
return undefined
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
function responseTextParts(item) {
|
|
293
|
-
return (Array.isArray(item?.content) ? item.content : [])
|
|
294
|
-
.filter((part) => part?.type === 'output_text' && typeof part.text === 'string')
|
|
295
|
-
.map((part) => part.text)
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
function responseReasoningText(item) {
|
|
299
|
-
const textOf = (parts) => (Array.isArray(parts) ? parts : [])
|
|
300
|
-
.filter((part) => typeof part?.text === 'string')
|
|
301
|
-
.map((part) => part.text)
|
|
302
|
-
.filter((text) => text.length > 0)
|
|
303
|
-
.join('\n\n')
|
|
304
|
-
return textOf(item?.summary) || textOf(item?.content)
|
|
157
|
+
function webError(message, code, cause) {
|
|
158
|
+
const error = new Error(message, cause === undefined ? undefined : { cause })
|
|
159
|
+
error.name = 'WebError'; error.code = code; return error
|
|
305
160
|
}
|
|
306
161
|
|
|
307
|
-
|
|
162
|
+
function activeAgentRoute(exec, fallback) {
|
|
163
|
+
const agent = exec?.agent
|
|
164
|
+
const latest = agent?.session?.requestHeader?.()?.config
|
|
308
165
|
return {
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
...(clientRequestId === null ? {} : { 'x-client-request-id': clientRequestId ?? (sessionId ? String(sessionId) : randomUUID()) }),
|
|
313
|
-
...(sessionId ? { 'session-id': String(sessionId) } : {}),
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
const REMOTE_COMPACTION_ERROR_CODES = new Set([
|
|
318
|
-
'LCX_HTTP_RETRYABLE',
|
|
319
|
-
'LCX_TIMEOUT',
|
|
320
|
-
'LCX_RETRY_EXHAUSTED',
|
|
321
|
-
])
|
|
322
|
-
const TRANSIENT_NETWORK_ERROR_CODES = new Set(['ECONNRESET', 'ECONNREFUSED', 'ETIMEDOUT', 'EAI_AGAIN', 'UND_ERR_CONNECT_TIMEOUT'])
|
|
323
|
-
|
|
324
|
-
function hasRemoteCompactionErrorCode(error) {
|
|
325
|
-
const seen = new Set()
|
|
326
|
-
let current = error
|
|
327
|
-
while (current && !seen.has(current)) {
|
|
328
|
-
seen.add(current)
|
|
329
|
-
const statusRetryable = Number.isInteger(current.status) && (current.status === 429 || (current.status >= 500 && current.status <= 599))
|
|
330
|
-
if (current instanceof TypeError || current.name === 'TypeError' || statusRetryable || current.retryable === true || REMOTE_COMPACTION_ERROR_CODES.has(current.code) || TRANSIENT_NETWORK_ERROR_CODES.has(current.code)) return true
|
|
331
|
-
current = current.cause
|
|
166
|
+
provider: latest?.provider ?? agent?.options?.provider ?? fallback.provider,
|
|
167
|
+
model: latest?.model ?? agent?.options?.model ?? fallback.model,
|
|
168
|
+
sessionId: String(agent?.session?.id ?? ''),
|
|
332
169
|
}
|
|
333
|
-
return false
|
|
334
170
|
}
|
|
335
171
|
|
|
336
|
-
function
|
|
337
|
-
|
|
338
|
-
failure.remoteCompactionRequest = hasRemoteCompactionErrorCode(failure)
|
|
339
|
-
return failure
|
|
340
|
-
}
|
|
172
|
+
function sessionsService(ctx) { return ctx?.get?.('sessions') ?? ctx?.sessions }
|
|
173
|
+
function sessionFor(ctx, sessionId) { return sessionId ? sessionsService(ctx)?.get?.(sessionId) : undefined }
|
|
341
174
|
|
|
342
|
-
function
|
|
175
|
+
function requestImageOptions(routeConfig, imageSupport, signal, imageMap, extra = {}) {
|
|
343
176
|
return {
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
function sessionAncestry(ctx, sessionId) {
|
|
352
|
-
const id = typeof sessionId === 'string' ? sessionId : ''
|
|
353
|
-
const sessions = ctx?.get?.('sessions') ?? ctx?.sessions
|
|
354
|
-
if (!id || !sessions?.get) return []
|
|
355
|
-
const ancestors = []
|
|
356
|
-
const seen = new Set([id])
|
|
357
|
-
let current = sessions.get(id)
|
|
358
|
-
while (current && ancestors.length < 32) {
|
|
359
|
-
const parent = current.header?.parentSession
|
|
360
|
-
if (typeof parent !== 'string' || parent.length === 0 || seen.has(parent)) break
|
|
361
|
-
ancestors.push(parent)
|
|
362
|
-
seen.add(parent)
|
|
363
|
-
current = sessions.get(parent)
|
|
177
|
+
imageSupport, signal, imageMap,
|
|
178
|
+
maxRequestImageBytes: routeConfig.maxRequestImageBytes,
|
|
179
|
+
requestImagePixelBudget: routeConfig.requestImagePixelBudget,
|
|
180
|
+
requestImageMaxBytes: routeConfig.requestImageMaxBytes,
|
|
181
|
+
...extra,
|
|
364
182
|
}
|
|
365
|
-
return ancestors
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
function routeWithSessionAncestry(ctx, route) {
|
|
369
|
-
return { ...route, ancestorSessionIds: sessionAncestry(ctx, route.sessionId) }
|
|
370
183
|
}
|
|
371
184
|
|
|
372
|
-
function
|
|
373
|
-
|
|
374
|
-
return route?.sessionId ? String(route.sessionId) : undefined
|
|
185
|
+
function readWebSearchProvider(ctx) {
|
|
186
|
+
try { return Reflect.get(ctx?.web, 'searchProviderId') } catch { return undefined }
|
|
375
187
|
}
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
const sessionId = promptCacheSessionId(route, config)
|
|
379
|
-
return sessionId ? clampOpenAIPromptCacheKey(sessionId) : undefined
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
function promptCacheRetention(config) {
|
|
383
|
-
return config?.cacheRetention === 'long' && config.supportsLongCacheRetention !== false ? '24h' : undefined
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
function promptCacheHeaders(ctx, config, route) {
|
|
387
|
-
const sessionId = promptCacheSessionId(route, config)
|
|
388
|
-
return authenticatedHeaders(ctx, config, sessionId, sessionId === undefined ? null : undefined)
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
async function requestRemoteCompaction(options, input, config, signal, ctx, route, preflightHeaders) {
|
|
188
|
+
function writeWebSearchProvider(ctx, providerId) {
|
|
189
|
+
if (!ctx?.web) return false
|
|
392
190
|
try {
|
|
393
|
-
|
|
394
|
-
return
|
|
395
|
-
|
|
396
|
-
model: options.model,
|
|
397
|
-
input,
|
|
398
|
-
instructions: options.system,
|
|
399
|
-
promptCacheKey: promptCacheKey(route, config),
|
|
400
|
-
promptCacheRetention: promptCacheRetention(config),
|
|
401
|
-
idempotencyKey: randomUUID(),
|
|
402
|
-
tools: options.tools,
|
|
403
|
-
headers,
|
|
404
|
-
signal,
|
|
405
|
-
timeoutMs: config.timeoutMs,
|
|
406
|
-
maxAttempts: config.maxAttempts,
|
|
407
|
-
maxResponseBytes: config.maxResponseBytes,
|
|
408
|
-
})
|
|
409
|
-
} catch (error) {
|
|
410
|
-
throw markRemoteCompactionRequestError(error)
|
|
411
|
-
}
|
|
191
|
+
if (!Reflect.set(ctx.web, 'searchProviderId', providerId)) return false
|
|
192
|
+
return Reflect.get(ctx.web, 'searchProviderId') === providerId
|
|
193
|
+
} catch { return false }
|
|
412
194
|
}
|
|
413
195
|
|
|
414
|
-
function
|
|
415
|
-
const
|
|
416
|
-
const
|
|
417
|
-
const
|
|
418
|
-
|
|
196
|
+
async function executeHostedSearch(ctx, routeConfig, args, signal, sessionId = '') {
|
|
197
|
+
const normalized = normalizeHostedSearchArgs(args)
|
|
198
|
+
const requestId = randomUUID()
|
|
199
|
+
const route = currentRoute({ provider: routeConfig.provider, model: routeConfig.model, sessionId }, routeConfig)
|
|
200
|
+
const headers = await authenticatedHeaders(ctx, routeConfig, sessionId, requestId)
|
|
201
|
+
const searchCacheKey = sessionId ? `dsh-lcx-search:${routeFingerprint(route)}` : undefined
|
|
202
|
+
const body = buildHostedSearchBody(normalized, routeConfig.model, { promptCacheKey: searchCacheKey })
|
|
203
|
+
const response = await fetchJsonWithRetry(`${routeConfig.baseURL}/responses`, body, headers, signal, routeConfig.timeoutMs, { maxAttempts: routeConfig.maxAttempts, maxResponseBytes: routeConfig.maxResponseBytes })
|
|
204
|
+
return parseHostedSearchResponse(response, requestId, routeConfig.webMaxResults)
|
|
419
205
|
}
|
|
420
206
|
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
207
|
+
class LcxResponsesSearchProvider {
|
|
208
|
+
constructor(ctx, getConfig, enabled) { this.ctx = ctx; this.getConfig = getConfig; this.enabled = enabled; this.id = getConfig().webSearchProvider }
|
|
209
|
+
available() {
|
|
210
|
+
const config = this.getConfig()
|
|
211
|
+
const route = resolveResponsesRouteConfig(this.ctx, { provider: config.provider, model: config.model }, config)
|
|
212
|
+
return this.enabled() && Boolean(route)
|
|
213
|
+
}
|
|
214
|
+
async search(request, signal) {
|
|
215
|
+
const config = this.getConfig()
|
|
216
|
+
const active = hostedSearchRouteContext.getStore()
|
|
217
|
+
const requested = active ?? { provider: config.provider, model: config.model, sessionId: '' }
|
|
218
|
+
let route = resolveResponsesRouteConfig(this.ctx, requested, config)
|
|
219
|
+
let source = active ? 'active-agent' : 'fallback'
|
|
220
|
+
if (!route && active) {
|
|
221
|
+
route = resolveResponsesRouteConfig(this.ctx, { provider: config.provider, model: config.model }, config)
|
|
222
|
+
source = 'fallback'
|
|
223
|
+
}
|
|
224
|
+
if (!route) throw webError('LCX Hosted Search requires a configured GPT openai-responses route', 'LCX_WEB_ROUTE_UNAVAILABLE')
|
|
225
|
+
const sessionId = String((source === 'active-agent' ? active?.sessionId : '') ?? '')
|
|
226
|
+
this.ctx.logger?.info?.(`[lcx-codex] web_search route: ${route.provider}/${route.model} (${source})`)
|
|
227
|
+
const result = await executeHostedSearch(this.ctx, route, { query: request.query }, signal, sessionId)
|
|
228
|
+
const max = Number.isInteger(request.maxResults) && request.maxResults > 0 ? request.maxResults : route.webMaxResults
|
|
229
|
+
return { content: result.content || undefined, sources: result.sources.slice(0, max), truncated: result.truncated || result.sources.length > max }
|
|
427
230
|
}
|
|
428
231
|
}
|
|
429
232
|
|
|
430
|
-
function
|
|
431
|
-
return String(text).replace(V3_CHECKPOINT_MARKER_PATTERN, '').trim()
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
function replayBudget() {
|
|
233
|
+
function createAdvancedHostedTool(ctx, state, getConfig) {
|
|
435
234
|
return {
|
|
436
|
-
|
|
437
|
-
|
|
235
|
+
name: ADVANCED_HOSTED_TOOL,
|
|
236
|
+
description: 'Advanced GPT Responses Hosted Search. Use DSH web_search for ordinary lookup. Call this only when you need domain allow/block filters, approximate user location, search-context size, image search, external-web-access or return-token-budget controls. It is one-shot and has no open/find/click state.',
|
|
237
|
+
parameters: HOSTED_SEARCH_PARAMETERS,
|
|
238
|
+
output: { schema: HOSTED_SEARCH_OUTPUT, render: (_args, value) => renderHostedSearchResult(value) },
|
|
239
|
+
async execute(args, exec) {
|
|
240
|
+
if (!state.enabled || !state.webSearch || !state.advancedHostedSearch) throw webError(`${ADVANCED_HOSTED_TOOL} is disabled`, 'LCX_WEB_DISABLED')
|
|
241
|
+
const config = getConfig(); const active = activeAgentRoute(exec, config)
|
|
242
|
+
const route = resolveResponsesRouteConfig(ctx, active, config)
|
|
243
|
+
if (!route) throw webError('Advanced Hosted Search requires the active GPT openai-responses route', 'LCX_WEB_ROUTE_UNAVAILABLE')
|
|
244
|
+
return executeHostedSearch(ctx, route, args, exec?.signal, active.sessionId)
|
|
245
|
+
},
|
|
438
246
|
}
|
|
439
247
|
}
|
|
440
248
|
|
|
441
|
-
function
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
if (!normalized || budget.chars <= 0 || budget.bytes <= 0) return undefined
|
|
445
|
-
let text = normalized.slice(0, Math.min(PORTABLE_REPLAY_TEXT_MAX_CHARS, budget.chars))
|
|
446
|
-
while (text.length > 0 && Buffer.byteLength(text, 'utf8') > budget.bytes) text = text.slice(0, -1)
|
|
447
|
-
if (!text) return undefined
|
|
448
|
-
budget.chars -= text.length
|
|
449
|
-
budget.bytes -= Buffer.byteLength(text, 'utf8')
|
|
450
|
-
return text
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
function exactReplayText(value, budget) {
|
|
454
|
-
if (typeof value !== 'string' || value.length === 0 || value.length > PORTABLE_REPLAY_TEXT_MAX_CHARS) return undefined
|
|
455
|
-
const byteLength = Buffer.byteLength(value, 'utf8')
|
|
456
|
-
if (value.length > budget.chars || byteLength > budget.bytes) return undefined
|
|
457
|
-
budget.chars -= value.length
|
|
458
|
-
budget.bytes -= byteLength
|
|
459
|
-
return value
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
function isPortableReplayItem(item) {
|
|
463
|
-
return item !== null && typeof item === 'object' && !Array.isArray(item) &&
|
|
464
|
-
typeof item.type === 'string' && !item.type.startsWith('response.') &&
|
|
465
|
-
item.type !== 'compaction' && item.type !== 'context_compaction' && item.type !== 'compaction_trigger' &&
|
|
466
|
-
!Object.prototype.hasOwnProperty.call(item, 'encrypted_content')
|
|
249
|
+
function alphaCapabilityFor(config, store) {
|
|
250
|
+
const fingerprint = alphaCapabilityFingerprint({ baseURL: config.baseURL, provider: config.provider, model: config.model, profile: config.alphaProfile, group: config.alphaGroup, schemaFingerprint: ALPHA_SCHEMA_FINGERPRINT })
|
|
251
|
+
return { fingerprint, record: store.get(fingerprint) }
|
|
467
252
|
}
|
|
468
253
|
|
|
469
|
-
function
|
|
470
|
-
const
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
if (typeof item.call_id !== 'string' || item.call_id.length === 0 || item.call_id.length > 256) return undefined
|
|
478
|
-
if (typeof item.name !== 'string' || item.name.trim().length === 0 || item.name.length > 256) return undefined
|
|
479
|
-
if (typeof item.arguments !== 'string' || item.arguments.length === 0 || item.arguments.length > PORTABLE_REPLAY_TEXT_MAX_CHARS) return undefined
|
|
254
|
+
async function executeAlpha(ctx, routeConfig, capability, refStore, args, exec) {
|
|
255
|
+
const normalized = normalizeAlphaSearchArgs(args)
|
|
256
|
+
const sessionId = String(exec?.agent?.session?.id ?? '')
|
|
257
|
+
if (!sessionId) throw webError('Alpha Search requires a DSH session', 'LCX_ALPHA_SESSION_REQUIRED')
|
|
258
|
+
const routeFp = routeFingerprint({ provider: routeConfig.provider, model: routeConfig.model, baseURL: routeConfig.baseURL, sessionId })
|
|
259
|
+
if (['open','find','click','screenshot'].includes(normalized.action)) refStore.assertUsable(sessionId, routeFp, normalized.refId)
|
|
260
|
+
const requestId = randomUUID(); const headers = await authenticatedHeaders(ctx, routeConfig, sessionId, requestId)
|
|
261
|
+
let response
|
|
480
262
|
try {
|
|
481
|
-
|
|
482
|
-
} catch {
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
return { callId: item.call_id, name: item.name, arguments: item.arguments }
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
function safePortableOutput(item) {
|
|
489
|
-
if (!isPortableReplayItem(item) || item.type !== 'function_call_output') return undefined
|
|
490
|
-
if (typeof item.call_id !== 'string' || item.call_id.length === 0 || item.call_id.length > 256) return undefined
|
|
491
|
-
let outputParts
|
|
492
|
-
if (typeof item.output === 'string') {
|
|
493
|
-
outputParts = [{ type: 'input_text', text: item.output }]
|
|
494
|
-
} else if (Array.isArray(item.output)) {
|
|
495
|
-
for (const part of item.output) {
|
|
496
|
-
if (part === null || typeof part !== 'object' || Array.isArray(part) ||
|
|
497
|
-
(part.type !== 'dsh_image_attachment' &&
|
|
498
|
-
((part.type !== 'input_text' && part.type !== 'output_text') || typeof part.text !== 'string'))) {
|
|
499
|
-
throw portableReplayUnsupportedContentError(part?.type)
|
|
500
|
-
}
|
|
501
|
-
}
|
|
502
|
-
outputParts = item.output
|
|
503
|
-
} else {
|
|
504
|
-
return undefined
|
|
263
|
+
response = await fetchJsonWithRetry(`${routeConfig.baseURL}/alpha/search`, buildAlphaSearchBody(normalized, routeConfig.model, sessionId, true, routeConfig.alphaMaxOutputTokens), headers, exec?.signal, routeConfig.timeoutMs, { maxAttempts: routeConfig.maxAttempts, maxResponseBytes: routeConfig.maxResponseBytes })
|
|
264
|
+
} catch (error) {
|
|
265
|
+
if ([404,405].includes(error?.status) || /channel does not support/iu.test(String(error?.message ?? ''))) throw webError('Alpha Search is not supported by this route', 'LCX_ALPHA_UNAVAILABLE', error)
|
|
266
|
+
throw webError('Alpha Search provider request failed', 'LCX_ALPHA_PROVIDER_ERROR', error)
|
|
505
267
|
}
|
|
506
|
-
const
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
function portableImageCapabilityError() {
|
|
512
|
-
const error = new Error('LCX Compact cannot determine whether the target model accepts checkpoint images')
|
|
513
|
-
error.code = 'LCX_COMPACT_IMAGE_CAPABILITY_UNKNOWN'
|
|
514
|
-
return error
|
|
268
|
+
const result = parseAlphaSearchResponse(response, { action: normalized.action, capability: capability.classification, requestId })
|
|
269
|
+
refStore.record(sessionId, routeFp, result.refRecords ?? result.refs.map((refId) => ({ refId })))
|
|
270
|
+
delete result.refRecords
|
|
271
|
+
return result
|
|
515
272
|
}
|
|
516
273
|
|
|
517
|
-
function
|
|
518
|
-
|
|
519
|
-
|
|
274
|
+
function createAlphaTool(ctx, state, getConfig, capabilityStore, refStore) {
|
|
275
|
+
return {
|
|
276
|
+
name: ALPHA_TOOL,
|
|
277
|
+
description: 'Stateful Codex/Alpha web command tool. Use it for search/open/find/click/PDF screenshot and the structured image/finance/weather/sports/time actions. Use DSH web_search for ordinary search, and websearch_gpt_advanced only for Hosted Search controls.',
|
|
278
|
+
parameters: ALPHA_SEARCH_PARAMETERS,
|
|
279
|
+
output: { schema: ALPHA_SEARCH_OUTPUT, render: (_args, value) => renderAlphaSearchResult(value) },
|
|
280
|
+
async execute(args, exec) {
|
|
281
|
+
if (!state.enabled || !state.alphaSearch) throw webError(`${ALPHA_TOOL} is disabled`, 'LCX_ALPHA_DISABLED')
|
|
282
|
+
const config = getConfig(); const active = activeAgentRoute(exec, config); const route = resolveResponsesRouteConfig(ctx, active, config)
|
|
283
|
+
if (!route) throw webError('Alpha Search requires the active GPT openai-responses route', 'LCX_ALPHA_ROUTE_UNAVAILABLE')
|
|
284
|
+
const fingerprint = alphaCapabilityFingerprint({ baseURL: route.baseURL, provider: route.provider, model: route.model, profile: route.alphaProfile, group: route.alphaGroup, schemaFingerprint: ALPHA_SCHEMA_FINGERPRINT })
|
|
285
|
+
const capability = capabilityStore.get(fingerprint)
|
|
286
|
+
if (!alphaCapabilityUsable(capability) || capability?.schemaFingerprint !== ALPHA_SCHEMA_FINGERPRINT) throw webError('Alpha Search capability has not been verified for this exact route/schema', 'LCX_ALPHA_CAPABILITY_UNVERIFIED')
|
|
287
|
+
return executeAlpha(ctx, route, capability, refStore, args, exec)
|
|
288
|
+
},
|
|
520
289
|
}
|
|
521
|
-
if (imageSupport === 'unknown') throw portableImageCapabilityError()
|
|
522
|
-
return imageSupport === 'supported'
|
|
523
|
-
? { type: 'image', attachment: structuredClone(part.attachment) }
|
|
524
|
-
: { type: 'text', text: UNSUPPORTED_IMAGE_PLACEHOLDER }
|
|
525
290
|
}
|
|
526
291
|
|
|
527
|
-
function
|
|
528
|
-
|
|
529
|
-
|
|
292
|
+
function isDshCompactionDirective(message) {
|
|
293
|
+
if (message?.role !== 'user') return false
|
|
294
|
+
if (message?.source?.kind === 'plugin' && message?.source?.plugin === 'dsh-compaction-basic') return true
|
|
295
|
+
const text = (message?.content ?? []).filter((b) => b?.type === 'text').map((b) => b.text).join('')
|
|
296
|
+
return text.includes(COMPACTION_DIRECTIVE)
|
|
530
297
|
}
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
return (messages
|
|
298
|
+
function stripCompactionDirective(messages) {
|
|
299
|
+
if (!Array.isArray(messages) || messages.length === 0) return []
|
|
300
|
+
return isDshCompactionDirective(messages.at(-1)) ? messages.slice(0, -1) : messages
|
|
534
301
|
}
|
|
535
302
|
|
|
536
|
-
function
|
|
537
|
-
const content = []
|
|
538
|
-
let pendingText = ''
|
|
539
|
-
const flushText = () => {
|
|
540
|
-
if (!pendingText) return
|
|
541
|
-
const text = boundedReplayText(pendingText, budget)
|
|
542
|
-
if (text !== undefined) content.push({ type: 'text', text })
|
|
543
|
-
pendingText = ''
|
|
544
|
-
}
|
|
545
|
-
for (const part of parts ?? []) {
|
|
546
|
-
if (expectedTypes.has(part?.type) && typeof part.text === 'string') {
|
|
547
|
-
pendingText += part.text
|
|
548
|
-
continue
|
|
549
|
-
}
|
|
550
|
-
if (part?.type === 'dsh_image_attachment' && part.attachment) {
|
|
551
|
-
flushText()
|
|
552
|
-
content.push(portableImageBlock(part, imageSupport))
|
|
553
|
-
continue
|
|
554
|
-
}
|
|
555
|
-
throw portableReplayUnsupportedContentError(part?.type)
|
|
556
|
-
}
|
|
557
|
-
flushText()
|
|
558
|
-
return content
|
|
559
|
-
}
|
|
560
|
-
|
|
561
|
-
function portableTextMessage(item, budget, checkpointId, imageSupport) {
|
|
562
|
-
if (!isPortableReplayItem(item) || item.type !== 'message' || (item.role !== 'user' && item.role !== 'assistant')) return undefined
|
|
563
|
-
const expectedType = item.role === 'assistant' ? 'output_text' : 'input_text'
|
|
564
|
-
const parts = Array.isArray(item.content) ? item.content : []
|
|
565
|
-
const content = portableContentBlocks(parts, new Set([expectedType]), budget, imageSupport)
|
|
566
|
-
if (content.length === 0) return undefined
|
|
567
|
-
return {
|
|
568
|
-
id: randomUUID(),
|
|
569
|
-
role: item.role,
|
|
570
|
-
content,
|
|
571
|
-
source: checkpointReplaySource(checkpointId),
|
|
572
|
-
}
|
|
573
|
-
}
|
|
303
|
+
function mergeMap(target, source) { for (const [key, value] of source ?? []) target.set(key, value) }
|
|
574
304
|
|
|
575
|
-
function
|
|
576
|
-
const
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
305
|
+
async function serializeNativeAware(messages, route, routeConfig, ctx, options = {}) {
|
|
306
|
+
const session = sessionFor(ctx, route.sessionId)
|
|
307
|
+
const imageSupport = await resolveModelImageSupport(ctx, route, options.signal)
|
|
308
|
+
const input = []; const imageMap = new Map(); let nativeTools
|
|
309
|
+
let normal = []
|
|
310
|
+
const serializeOptions = (imageMapOverride, extra = {}) => requestImageOptions(routeConfig, imageSupport, options.signal, imageMapOverride, { route, tools: options.tools, responsesCompat: routeConfig.responsesCompat, ...extra })
|
|
311
|
+
const prelude = await serializeDshMessages([], ctx, serializeOptions(undefined, { systemPrompt: options.system, includeSystemPrompt: true }))
|
|
312
|
+
input.push(...prelude.input); nativeTools = prelude.tools
|
|
313
|
+
const flush = async () => {
|
|
314
|
+
if (!normal.length) return
|
|
315
|
+
const serialized = await serializeDshMessages(normal, ctx, serializeOptions())
|
|
316
|
+
nativeTools = serialized.tools ?? nativeTools
|
|
317
|
+
input.push(...serialized.input); mergeMap(imageMap, serialized.imageMap); normal = []
|
|
585
318
|
}
|
|
586
|
-
return structuredClone(block)
|
|
587
|
-
}
|
|
588
|
-
|
|
589
|
-
function cloneReplayTail(messages, imageSupport = 'unknown') {
|
|
590
|
-
const projected = []
|
|
591
319
|
for (const message of messages ?? []) {
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
320
|
+
const state = session ? checkpointStateForMessage(session, message) : undefined
|
|
321
|
+
if (state) {
|
|
322
|
+
await flush()
|
|
323
|
+
if (stateRouteCompatible(state, route, ctx)) {
|
|
324
|
+
let nativeOutput = state.nativeOutput
|
|
325
|
+
if (state.version === 4) {
|
|
326
|
+
const checkpointId = compactCheckpointId(message)
|
|
327
|
+
const shadowed = shadowedMessagesForCheckpoint(session, checkpointId)
|
|
328
|
+
if (shadowed.length > 0) {
|
|
329
|
+
const serializedShadowed = await serializeDshMessages(shadowed, ctx, serializeOptions())
|
|
330
|
+
const repairedRetained = retainedConversationInput(serializedShadowed.input, {
|
|
331
|
+
tokenBudget: routeConfig.nativeRetentionTokenBudget,
|
|
332
|
+
assistantTokenReserve: routeConfig.assistantRetentionTokenReserve,
|
|
333
|
+
assistantPerMessageTokenCap: routeConfig.assistantRetentionPerMessageTokenCap,
|
|
334
|
+
})
|
|
335
|
+
const opaque = state.nativeCompaction ?? nativeOutput.find((item) => item?.type === 'compaction')
|
|
336
|
+
if (repairedRetained.length > 0 && opaque) {
|
|
337
|
+
nativeOutput = [...repairedRetained, structuredClone(opaque)]
|
|
338
|
+
mergeMap(imageMap, serializedShadowed.imageMap)
|
|
339
|
+
} else if (state.retainedInputCount === undefined) {
|
|
340
|
+
const portable = portableMessagesForCheckpoint(session, checkpointId, { maxChars: routeConfig.portableReplayMaxChars })
|
|
341
|
+
const serializedPortable = await serializeDshMessages(portable, ctx, serializeOptions())
|
|
342
|
+
input.push(...serializedPortable.input); mergeMap(imageMap, serializedPortable.imageMap)
|
|
343
|
+
continue
|
|
344
|
+
}
|
|
345
|
+
} else if (state.retainedInputCount === undefined) {
|
|
346
|
+
const portable = portableMessagesForCheckpoint(session, checkpointId, { maxChars: routeConfig.portableReplayMaxChars })
|
|
347
|
+
const serializedPortable = await serializeDshMessages(portable, ctx, serializeOptions())
|
|
348
|
+
input.push(...serializedPortable.input); mergeMap(imageMap, serializedPortable.imageMap)
|
|
349
|
+
continue
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
const hydratedMap = new Map()
|
|
353
|
+
const hydrated = await hydrateNativeImageReferences(nativeOutput, ctx, requestImageOptions(routeConfig, imageSupport, options.signal, hydratedMap))
|
|
354
|
+
input.push(...hydrated); mergeMap(imageMap, hydratedMap)
|
|
355
|
+
} else {
|
|
356
|
+
const portable = portableMessagesForCheckpoint(session, compactCheckpointId(message), { maxChars: routeConfig.portableReplayMaxChars })
|
|
357
|
+
const serialized = await serializeDshMessages(portable, ctx, serializeOptions())
|
|
358
|
+
input.push(...serialized.input); mergeMap(imageMap, serialized.imageMap)
|
|
617
359
|
}
|
|
618
|
-
} else if (item.type !== 'function_call' && item.type !== 'function_call_output') {
|
|
619
|
-
throw portableReplayUnsupportedContentError(item.type)
|
|
620
|
-
}
|
|
621
|
-
}
|
|
622
|
-
const calls = new Map()
|
|
623
|
-
const outputs = new Map()
|
|
624
|
-
for (let index = 0; index < items.length; index += 1) {
|
|
625
|
-
const call = safePortableCall(items[index])
|
|
626
|
-
const output = safePortableOutput(items[index])
|
|
627
|
-
if (call) calls.set(call.callId, [...(calls.get(call.callId) ?? []), { ...call, index }])
|
|
628
|
-
if (output) outputs.set(output.callId, [...(outputs.get(output.callId) ?? []), { ...output, index }])
|
|
629
|
-
}
|
|
630
|
-
const pairs = new Map()
|
|
631
|
-
for (const [callId, callItems] of calls) {
|
|
632
|
-
const outputItems = outputs.get(callId)
|
|
633
|
-
if (callItems.length === 1 && outputItems?.length === 1 && callItems[0].index < outputItems[0].index) {
|
|
634
|
-
pairs.set(callId, { call: callItems[0], output: outputItems[0] })
|
|
635
|
-
}
|
|
636
|
-
}
|
|
637
|
-
|
|
638
|
-
const messages = []
|
|
639
|
-
const reservedPairs = new Map()
|
|
640
|
-
for (const item of items) {
|
|
641
|
-
const textMessage = portableTextMessage(item, budget, checkpointId, imageSupport)
|
|
642
|
-
if (textMessage) {
|
|
643
|
-
messages.push(textMessage)
|
|
644
360
|
continue
|
|
645
361
|
}
|
|
646
|
-
const
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
imageSupport,
|
|
656
|
-
)
|
|
657
|
-
if (outputContent.length === 0) {
|
|
658
|
-
const emptyOutput = exactReplayText('(no output)', budget)
|
|
659
|
-
outputContent = emptyOutput ? [{ type: 'text', text: emptyOutput }] : []
|
|
660
|
-
}
|
|
661
|
-
if (!argumentsText || outputContent.length === 0) {
|
|
662
|
-
budget.chars = before.chars
|
|
663
|
-
budget.bytes = before.bytes
|
|
362
|
+
const legacyId = legacyV3Id(message)
|
|
363
|
+
if (legacyId) {
|
|
364
|
+
const legacy = loadLegacyRecord(routeConfig.legacyCheckpointPath, legacyId)
|
|
365
|
+
if (legacy) {
|
|
366
|
+
await flush()
|
|
367
|
+
const legacyItems = legacyRouteCompatible(legacy, route, ctx) ? legacy.nativeOutput : (legacy.portableHistory ?? [])
|
|
368
|
+
const hydratedMap = new Map()
|
|
369
|
+
const hydrated = await hydrateNativeImageReferences(legacyItems, ctx, requestImageOptions(routeConfig, imageSupport, options.signal, hydratedMap))
|
|
370
|
+
input.push(...hydrated); mergeMap(imageMap, hydratedMap)
|
|
664
371
|
continue
|
|
665
372
|
}
|
|
666
|
-
reservedPairs.set(call.callId, outputContent)
|
|
667
|
-
messages.push({
|
|
668
|
-
id: randomUUID(),
|
|
669
|
-
role: 'assistant',
|
|
670
|
-
content: [{ type: 'tool-call', id: call.callId, name: call.name, arguments: argumentsText }],
|
|
671
|
-
source: checkpointReplaySource(checkpointId),
|
|
672
|
-
})
|
|
673
|
-
continue
|
|
674
373
|
}
|
|
675
|
-
|
|
676
|
-
const outputContent = output ? reservedPairs.get(output.callId) : undefined
|
|
677
|
-
if (output && outputContent !== undefined) {
|
|
678
|
-
messages.push({
|
|
679
|
-
id: randomUUID(),
|
|
680
|
-
role: 'user',
|
|
681
|
-
content: [{ type: 'tool-result', toolCallId: output.callId, content: outputContent }],
|
|
682
|
-
source: checkpointReplaySource(checkpointId),
|
|
683
|
-
})
|
|
684
|
-
reservedPairs.delete(output.callId)
|
|
685
|
-
}
|
|
686
|
-
}
|
|
687
|
-
return messages
|
|
688
|
-
}
|
|
689
|
-
|
|
690
|
-
function portableReplayRouteError(record, route) {
|
|
691
|
-
const dimensions = []
|
|
692
|
-
if (record.provider !== String(route.provider ?? '')) dimensions.push('provider')
|
|
693
|
-
if (record.baseURLFingerprint !== baseURLFingerprint(route.baseURL)) dimensions.push('endpoint')
|
|
694
|
-
const sessionId = String(route.sessionId ?? '')
|
|
695
|
-
const sameSessionLineage = !sessionId || record.lineageId === sessionId ||
|
|
696
|
-
(Array.isArray(route.ancestorSessionIds) && route.ancestorSessionIds.includes(record.lineageId))
|
|
697
|
-
if (!sameSessionLineage) dimensions.push('session')
|
|
698
|
-
const error = new Error(`LCX v3 checkpoint cannot migrate; route mismatch dimensions: ${(dimensions.length > 0 ? dimensions : ['route']).join(', ')}`)
|
|
699
|
-
error.code = 'LCX_CHECKPOINT_ROUTE_MISMATCH'
|
|
700
|
-
return error
|
|
701
|
-
}
|
|
702
|
-
|
|
703
|
-
function buildPortableReplayMessages(messages, store, route, options = {}) {
|
|
704
|
-
const marker = latestPortableMarker(messages)
|
|
705
|
-
if (!marker) return cloneReplayTail(messages)
|
|
706
|
-
const record = store?.get?.(marker.id)
|
|
707
|
-
if (!record) {
|
|
708
|
-
const error = new Error(`LCX v3 checkpoint ${marker.id} is missing from ${store?.file ?? 'checkpoint store'}`)
|
|
709
|
-
error.code = 'LCX_CHECKPOINT_V3_CORRUPT'
|
|
710
|
-
throw error
|
|
711
|
-
}
|
|
712
|
-
const exactRoute = Boolean(route.sessionId) && record.routeFingerprint === routeFingerprint(route)
|
|
713
|
-
const sessionId = String(route.sessionId ?? '')
|
|
714
|
-
const sameSessionLineage = !sessionId || record.lineageId === sessionId ||
|
|
715
|
-
(Array.isArray(route.ancestorSessionIds) && route.ancestorSessionIds.includes(record.lineageId))
|
|
716
|
-
const portableRoute = record.provider === String(route.provider ?? '') &&
|
|
717
|
-
record.baseURLFingerprint === baseURLFingerprint(route.baseURL) &&
|
|
718
|
-
sameSessionLineage
|
|
719
|
-
if (!exactRoute && !portableRoute) throw portableReplayRouteError(record, route)
|
|
720
|
-
const portableImages = Number(record.portableImageCount ?? 0)
|
|
721
|
-
const durableImages = inputImageCount(record.portableHistory)
|
|
722
|
-
if (portableImages > durableImages) {
|
|
723
|
-
throw portableReplayUnsupportedContentError('image')
|
|
374
|
+
normal.push(message)
|
|
724
375
|
}
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
const summary = boundedReplayText(record.portableSummary, budget)
|
|
728
|
-
const recalled = []
|
|
729
|
-
if (summary) recalled.push({
|
|
730
|
-
id: randomUUID(),
|
|
731
|
-
role: 'assistant',
|
|
732
|
-
content: [{ type: 'text', text: summary }],
|
|
733
|
-
source: checkpointReplaySource(marker.id),
|
|
734
|
-
})
|
|
735
|
-
recalled.push(...portableResponsesToMessages(record.portableHistory, { budget, checkpointId: marker.id, imageSupport }))
|
|
736
|
-
recalled.push(...cloneReplayTail((messages ?? []).slice(marker.index + 1), imageSupport))
|
|
737
|
-
return recalled
|
|
738
|
-
}
|
|
739
|
-
|
|
740
|
-
function checkpointReplayUnavailableError() {
|
|
741
|
-
const error = new Error('LCX checkpoint replay requires the public DSH ctx.llm.stream API')
|
|
742
|
-
error.code = CHECKPOINT_REPLAY_UNAVAILABLE_CODE
|
|
743
|
-
return error
|
|
376
|
+
await flush()
|
|
377
|
+
return { input, imageMap, imageSupport, tools: nativeTools }
|
|
744
378
|
}
|
|
745
379
|
|
|
746
|
-
function
|
|
747
|
-
|
|
748
|
-
if (
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
const error = new Error(`LCX v3 checkpoint ${marker.id} is missing from ${store?.file ?? 'checkpoint store'}`)
|
|
752
|
-
error.code = 'LCX_CHECKPOINT_V3_CORRUPT'
|
|
753
|
-
throw error
|
|
754
|
-
}
|
|
755
|
-
return { marker, record }
|
|
380
|
+
function fallbackEligible(error, signal) {
|
|
381
|
+
if (signal?.aborted) return false
|
|
382
|
+
if (error?.name === 'AbortError' || error?.code === 'LCX_ABORTED') return false
|
|
383
|
+
if (error?.name === 'TimeoutError') return true
|
|
384
|
+
return ['LCX_HTTP_RETRYABLE', 'LCX_RETRY_EXHAUSTED'].includes(error?.code)
|
|
756
385
|
}
|
|
757
386
|
|
|
758
|
-
function
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
input: [...structuredClone(nativeOutput), ...structuredClone(tailInput)],
|
|
762
|
-
stream: true,
|
|
763
|
-
store: false,
|
|
764
|
-
...(options.tools !== undefined ? { tools: responsesTools(options.tools) } : {}),
|
|
765
|
-
...(options.system !== undefined ? { instructions: options.system } : {}),
|
|
766
|
-
...(promptCacheKey(route, config) ? { prompt_cache_key: promptCacheKey(route, config) } : {}),
|
|
767
|
-
...(promptCacheRetention(config) ? { prompt_cache_retention: promptCacheRetention(config) } : {}),
|
|
768
|
-
}
|
|
769
|
-
}
|
|
770
|
-
|
|
771
|
-
async function* nativeCheckpointReplayStream(options, config, portableStore, ctx, record, marker) {
|
|
772
|
-
const route = routeWithSessionAncestry(ctx, currentRoute(options, config))
|
|
773
|
-
assertCheckpointRoute(record, route)
|
|
774
|
-
const llm = ctx?.llm ?? ctx?.get?.('llm')
|
|
775
|
-
const imageSupport = typeof llm?.resolveModelInfo === 'function'
|
|
776
|
-
? await resolveModelImageSupport(llm, route, options.signal)
|
|
777
|
-
: 'supported'
|
|
778
|
-
const imageOptions = { resolveImage: attachmentImageResolver(ctx), imageSupport, signal: options.signal, maxRequestImageBytes: config.maxRequestImageBytes }
|
|
779
|
-
const nativeOutput = await hydrateNativeImageReferences(checkpointNativeReplacementHistory(record), imageOptions)
|
|
780
|
-
const tailInput = await buildPortableResponsesInputWithImages(
|
|
781
|
-
(options.messages ?? []).slice(marker.index + 1),
|
|
782
|
-
portableStore,
|
|
783
|
-
route,
|
|
784
|
-
imageOptions,
|
|
785
|
-
)
|
|
786
|
-
const replaySignals = replayEffectiveSignal(options.signal, config.timeoutMs)
|
|
387
|
+
async function* remoteCompactionStream(options, routeConfig, state, ctx, next, requestHeaders) {
|
|
388
|
+
const history = stripCompactionDirective(options.messages)
|
|
389
|
+
const route = currentRoute(options, routeConfig)
|
|
787
390
|
try {
|
|
788
|
-
const
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
391
|
+
const prepared = await serializeNativeAware(history, route, routeConfig, ctx, { signal: options.signal, system: options.system, tools: options.tools })
|
|
392
|
+
const cacheSessionId = promptCacheSessionId(route, routeConfig)
|
|
393
|
+
const headers = await authenticatedHeaders(ctx, routeConfig, cacheSessionId, cacheSessionId === undefined ? null : undefined)
|
|
394
|
+
const cachedGeneration = generationControlsFromHeader(requestHeaders?.get(route.sessionId), route)
|
|
395
|
+
const generation = Object.keys(cachedGeneration).length > 0 ? cachedGeneration : generationControlsFromSession(sessionFor(ctx, route.sessionId), route)
|
|
396
|
+
const result = await requestNativeCompaction({
|
|
397
|
+
baseURL: routeConfig.baseURL,
|
|
398
|
+
model: route.model,
|
|
399
|
+
input: prepared.input,
|
|
400
|
+
tools: prepared.tools ?? options.tools,
|
|
401
|
+
promptCacheKey: promptCacheKey(route, routeConfig),
|
|
402
|
+
promptCacheRetention: promptCacheRetention(routeConfig),
|
|
403
|
+
reasoningEffort: generation.reasoningEffort,
|
|
404
|
+
temperature: generation.temperature,
|
|
405
|
+
maxTokens: generation.maxTokens,
|
|
406
|
+
idempotencyKey: randomUUID(),
|
|
407
|
+
headers,
|
|
408
|
+
signal: options.signal,
|
|
409
|
+
timeoutMs: routeConfig.timeoutMs,
|
|
410
|
+
maxAttempts: routeConfig.maxAttempts,
|
|
411
|
+
maxResponseBytes: routeConfig.maxResponseBytes,
|
|
802
412
|
})
|
|
413
|
+
const session = sessionFor(ctx, route.sessionId)
|
|
414
|
+
const block = createNativeCheckpointBlock({
|
|
415
|
+
session, route, result, input: prepared.input, imageMap: prepared.imageMap,
|
|
416
|
+
retentionOptions: {
|
|
417
|
+
tokenBudget: routeConfig.nativeRetentionTokenBudget,
|
|
418
|
+
assistantTokenReserve: routeConfig.assistantRetentionTokenReserve,
|
|
419
|
+
assistantPerMessageTokenCap: routeConfig.assistantRetentionPerMessageTokenCap,
|
|
420
|
+
},
|
|
421
|
+
})
|
|
422
|
+
ctx.logger?.info?.(`[lcx-codex] native V2 compaction succeeded; retained ${block.retainedClientCount ?? 0} client + ${block.retainedAssistantCount ?? 0} assistant-visible item(s) (~${block.retainedEstimatedTokens ?? 0} tokens) with the opaque checkpoint`)
|
|
423
|
+
for (const chunk of nativeCheckpointChunks(block, result.usage)) yield chunk
|
|
803
424
|
} catch (error) {
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
}
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
}
|
|
818
|
-
|
|
819
|
-
function isCompactionDirective(message) {
|
|
820
|
-
return textOfMessage(message).includes(COMPACTION_DIRECTIVE)
|
|
821
|
-
}
|
|
822
|
-
|
|
823
|
-
function diagnosticEndpoint(baseURL) {
|
|
824
|
-
try {
|
|
825
|
-
const url = new URL(`${String(baseURL).replace(/\/+$/u, '')}/responses`)
|
|
826
|
-
return `${url.origin}${url.pathname}`
|
|
827
|
-
} catch {
|
|
828
|
-
return 'invalid'
|
|
829
|
-
}
|
|
830
|
-
}
|
|
831
|
-
|
|
832
|
-
function logCompactionDiagnostic(ctx, options, config, error, phase) {
|
|
833
|
-
const failure = failureOf(error)
|
|
834
|
-
const status = failure.status === undefined ? '' : ` status=${failure.status}`
|
|
835
|
-
const detail = `dsh-lcx-codex compaction ${phase}: code=${failure.code}${status} provider=${String(options.provider ?? config.provider)} model=${String(options.model ?? config.model)} transport=${config.compactTransport} endpoint=${diagnosticEndpoint(config.baseURL)} inputItems=${Array.isArray(options.messages) ? options.messages.length : 0}`
|
|
836
|
-
try {
|
|
837
|
-
if (typeof ctx?.logger?.error === 'function') ctx.logger.error(detail)
|
|
838
|
-
else if (typeof console?.error === 'function') console.error(detail)
|
|
839
|
-
} catch {
|
|
840
|
-
// Diagnostics must never change compaction behavior.
|
|
841
|
-
}
|
|
842
|
-
}
|
|
843
|
-
|
|
844
|
-
async function prepareRemoteCompaction(options, config, portableStore, ctx, sessionTracker) {
|
|
845
|
-
const last = options.messages?.[options.messages.length - 1]
|
|
846
|
-
const history = last && isCompactionDirective(last) ? options.messages.slice(0, -1) : options.messages
|
|
847
|
-
const route = currentRoute(options, config)
|
|
848
|
-
const lease = sessionTracker?.capture(route.sessionId)
|
|
849
|
-
lease?.assert('request-start')
|
|
850
|
-
const headers = await promptCacheHeaders(ctx, config, route)
|
|
851
|
-
responsesTools(options.tools)
|
|
852
|
-
const llm = ctx?.llm ?? ctx?.get?.('llm')
|
|
853
|
-
const imageSupport = typeof llm?.resolveModelInfo === 'function'
|
|
854
|
-
? await resolveModelImageSupport(llm, route, options.signal)
|
|
855
|
-
: 'supported'
|
|
856
|
-
const imageReferences = new Map()
|
|
857
|
-
const input = await buildPortableResponsesInputWithImages(history, portableStore, route, {
|
|
858
|
-
resolveImage: attachmentImageResolver(ctx),
|
|
859
|
-
imageSupport,
|
|
860
|
-
signal: options.signal,
|
|
861
|
-
maxRequestImageBytes: config.maxRequestImageBytes,
|
|
862
|
-
onImageResolved: ({ imageUrl, attachment }) => imageReferences.set(imageUrl, attachment),
|
|
863
|
-
})
|
|
864
|
-
lease?.assert('preflight')
|
|
865
|
-
return { history, route, lease, input, imageReferences, headers }
|
|
866
|
-
}
|
|
867
|
-
|
|
868
|
-
function mergeUsage(...values) {
|
|
869
|
-
const keys = ['inputTokens', 'outputTokens', 'cacheReadTokens', 'cacheWriteTokens', 'reasoningTokens']
|
|
870
|
-
const merged = {}
|
|
871
|
-
for (const key of keys) {
|
|
872
|
-
const total = values.reduce((sum, value) => sum + (Number.isFinite(value?.[key]) ? value[key] : 0), 0)
|
|
873
|
-
if (total > 0) merged[key] = total
|
|
874
|
-
}
|
|
875
|
-
return Object.keys(merged).length > 0 ? merged : undefined
|
|
876
|
-
}
|
|
877
|
-
|
|
878
|
-
function readableSummaryText(text, model, usage) {
|
|
879
|
-
const summary = stripCheckpointMarker(String(text ?? '')).trim().slice(0, 16 * 1024)
|
|
880
|
-
return summary || portableSummaryText(model, usage)
|
|
881
|
-
}
|
|
882
|
-
|
|
883
|
-
function portableMarkerTextWithSummary(id, model, usage, summary) {
|
|
884
|
-
const summaryText = stripCheckpointMarker(String(summary ?? '')).trim().slice(0, 16 * 1024)
|
|
885
|
-
const status = portableSummaryText(model, usage)
|
|
886
|
-
return `${summaryText ? `${summaryText}\n` : ''}${status}\n[dsh-lcx-codex-v3-checkpoint:${id}]`
|
|
887
|
-
}
|
|
888
|
-
|
|
889
|
-
async function commitRemoteCompaction(prepared, result, portableStore, config, summary, usage) {
|
|
890
|
-
const { history, route, lease, input, imageReferences } = prepared
|
|
891
|
-
lease?.assert('commit')
|
|
892
|
-
const replacementHistory = buildNativeReplacementHistory(input, result.compaction)
|
|
893
|
-
const persistedNativeOutput = persistNativeImageReferences(replacementHistory, imageReferences)
|
|
894
|
-
const persistedPortableInput = persistNativeImageReferences(input, imageReferences)
|
|
895
|
-
const id = randomUUID()
|
|
896
|
-
const parent = latestPortableMarker(history)
|
|
897
|
-
const portableImageCount = inputImageCount(input)
|
|
898
|
-
const portableSummary = readableSummaryText(summary, route.model, usage)
|
|
899
|
-
portableStore.put(id, {
|
|
900
|
-
version: CHECKPOINT_V3_VERSION,
|
|
901
|
-
checkpointId: id,
|
|
902
|
-
...(parent ? { parentCheckpointId: parent.id } : {}),
|
|
903
|
-
lineageId: route.sessionId || `${route.provider}:${route.baseURL}`,
|
|
904
|
-
sourceSessionId: route.sessionId || `${route.provider}:${route.baseURL}`,
|
|
905
|
-
provider: route.provider,
|
|
906
|
-
model: route.model,
|
|
907
|
-
modelKey: `${route.provider}:${route.model}`,
|
|
908
|
-
transport: config.compactTransport,
|
|
909
|
-
baseURLFingerprint: baseURLFingerprint(route.baseURL),
|
|
910
|
-
routeFingerprint: routeFingerprint(route),
|
|
911
|
-
nativeOutput: persistedNativeOutput,
|
|
912
|
-
nativeCompaction: result.compaction,
|
|
913
|
-
portableHistory: buildPortableHistory(persistedPortableInput),
|
|
914
|
-
...(portableImageCount > 0 ? { portableImageCount } : {}),
|
|
915
|
-
portableSummary,
|
|
916
|
-
createdAt: Date.now(),
|
|
917
|
-
usage,
|
|
918
|
-
})
|
|
919
|
-
return { id, route, usage, text: portableMarkerTextWithSummary(id, route.model, usage, summary) }
|
|
920
|
-
}
|
|
921
|
-
|
|
922
|
-
async function* remoteCompactionStream(options, config, portableStore, ctx, sessionTracker) {
|
|
923
|
-
try {
|
|
924
|
-
const prepared = await prepareRemoteCompaction(options, config, portableStore, ctx, sessionTracker)
|
|
925
|
-
const result = await requestRemoteCompaction(options, prepared.input, config, options.signal, ctx, prepared.route, prepared.headers)
|
|
926
|
-
const committed = await commitRemoteCompaction(prepared, result, portableStore, config, undefined, result.usage)
|
|
927
|
-
yield* textChunks(committed.text, committed.usage)
|
|
928
|
-
} catch (error) {
|
|
929
|
-
logCompactionDiagnostic(ctx, options, config, error, 'failed')
|
|
930
|
-
throw error
|
|
425
|
+
const session = sessionFor(ctx, route.sessionId)
|
|
426
|
+
const hasExistingCheckpoint = messagesContainNativeCheckpoint(history, session) || messagesContainLegacyCheckpoint(history)
|
|
427
|
+
const code = error?.code ?? error?.name ?? 'ERROR'
|
|
428
|
+
const status = Number.isInteger(error?.status) ? ` status=${error.status}` : ''
|
|
429
|
+
const requestId = error?.requestId ? ` requestId=${String(error.requestId)}` : ''
|
|
430
|
+
const providerCode = error?.providerCode ? ` providerCode=${error.providerCode}` : ''
|
|
431
|
+
const providerType = error?.providerType ? ` providerType=${error.providerType}` : ''
|
|
432
|
+
const providerParam = error?.providerParam ? ` providerParam=${error.providerParam}` : ''
|
|
433
|
+
ctx.logger?.warn?.(`[lcx-codex] native V2 compaction failed: code=${code}${status}${requestId}${providerCode}${providerType}${providerParam}`)
|
|
434
|
+
if (!state.fallbackToBasicCompaction || hasExistingCheckpoint || !fallbackEligible(error, options.signal)) throw error
|
|
435
|
+
ctx.logger?.info?.('[lcx-codex] falling back to DSH basic compaction after allowlisted first-checkpoint native failure')
|
|
436
|
+
const stream = await next()
|
|
437
|
+
for await (const chunk of stream) yield chunk
|
|
931
438
|
}
|
|
932
439
|
}
|
|
933
440
|
|
|
934
|
-
|
|
935
|
-
const
|
|
936
|
-
const
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
const usage = chunks.find((chunk) => chunk?.type === 'usage')?.usage
|
|
940
|
-
return { chunks, text, usage }
|
|
441
|
+
function routedTargetForAgent(agent, fallback) {
|
|
442
|
+
const latest = agent?.session?.requestHeader?.()?.config
|
|
443
|
+
const provider = latest?.provider ?? agent?.options?.provider ?? fallback.provider
|
|
444
|
+
const model = latest?.model ?? agent?.options?.model ?? fallback.model
|
|
445
|
+
return provider && model ? { provider, model } : undefined
|
|
941
446
|
}
|
|
942
447
|
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
prepared = await prepareRemoteCompaction(options, config, portableStore, ctx, sessionTracker)
|
|
947
|
-
} catch (error) {
|
|
948
|
-
logCompactionDiagnostic(ctx, options, config, error, 'failed')
|
|
949
|
-
throw error
|
|
950
|
-
}
|
|
951
|
-
const localPromise = collectLocalCompaction(next)
|
|
952
|
-
const remotePromise = requestRemoteCompaction(options, prepared.input, config, options.signal, ctx, prepared.route, prepared.headers)
|
|
953
|
-
.then((result) => ({ result }), (error) => ({ error }))
|
|
954
|
-
const [localOutcome, remoteOutcome] = await Promise.allSettled([localPromise, remotePromise])
|
|
955
|
-
if (remoteOutcome.status === 'fulfilled' && remoteOutcome.value.result) {
|
|
956
|
-
const local = localOutcome.status === 'fulfilled' ? localOutcome.value : { text: '', usage: undefined }
|
|
957
|
-
const usage = mergeUsage(remoteOutcome.value.result.usage, local.usage)
|
|
958
|
-
try {
|
|
959
|
-
const committed = await commitRemoteCompaction(prepared, remoteOutcome.value.result, portableStore, config, local.text, usage)
|
|
960
|
-
yield* textChunks(committed.text, committed.usage)
|
|
961
|
-
return
|
|
962
|
-
} catch (error) {
|
|
963
|
-
logCompactionDiagnostic(ctx, options, config, error, 'failed')
|
|
964
|
-
throw error
|
|
965
|
-
}
|
|
966
|
-
}
|
|
967
|
-
const remoteError = remoteOutcome.status === 'fulfilled' ? remoteOutcome.value.error : remoteOutcome.reason
|
|
968
|
-
if (options.signal?.aborted || remoteError?.remoteCompactionRequest !== true) throw remoteError
|
|
969
|
-
diagnostic?.(remoteError, 'failed')
|
|
970
|
-
diagnostic?.(remoteError, 'fallback')
|
|
971
|
-
if (localOutcome.status !== 'fulfilled') throw localOutcome.reason
|
|
972
|
-
for (const chunk of localOutcome.value.chunks) yield chunk
|
|
448
|
+
function clampPercent(value, fallback, min, max) {
|
|
449
|
+
const number = Number(value)
|
|
450
|
+
return Number.isFinite(number) ? Math.min(max, Math.max(min, number)) : fallback
|
|
973
451
|
}
|
|
974
452
|
|
|
975
|
-
function
|
|
976
|
-
const
|
|
977
|
-
|
|
978
|
-
|
|
453
|
+
function pressurePolicy(state, config) {
|
|
454
|
+
const auto = clampPercent(state.autoCompactionThresholdPercent, config.autoCompactionThresholdPercent, 85, 95)
|
|
455
|
+
const emergencyRaw = clampPercent(state.emergencyPruneThresholdPercent, config.emergencyPruneThresholdPercent, 90, 99)
|
|
456
|
+
const emergency = Math.max(auto + 1, emergencyRaw)
|
|
457
|
+
return { auto, emergency: Math.min(99, emergency) }
|
|
979
458
|
}
|
|
980
459
|
|
|
981
|
-
function
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
460
|
+
function adjustedCompactionConfig(config, target, thresholdRatio) {
|
|
461
|
+
if (!config || typeof config !== 'object') return config
|
|
462
|
+
const modelPolicies = Array.isArray(config.modelPolicies)
|
|
463
|
+
? config.modelPolicies.map((policy) => policy?.provider === target.provider && policy?.model === target.model ? { ...policy, thresholdRatio } : policy)
|
|
464
|
+
: config.modelPolicies
|
|
465
|
+
return { ...config, thresholdRatio, ...(modelPolicies === undefined ? {} : { modelPolicies }) }
|
|
985
466
|
}
|
|
986
467
|
|
|
987
|
-
function
|
|
988
|
-
|
|
989
|
-
return replayTimeoutError(options.timeoutMs, cause)
|
|
990
|
-
}
|
|
991
|
-
return cause ?? options.signal?.reason ?? new Error('request aborted')
|
|
468
|
+
function resolveContextService(ctx, name) {
|
|
469
|
+
try { return ctx?.get?.(name) ?? ctx?.[name] } catch { return undefined }
|
|
992
470
|
}
|
|
993
471
|
|
|
994
|
-
function
|
|
995
|
-
const timeoutSignal = AbortSignal.timeout(timeoutMs)
|
|
996
|
-
return {
|
|
997
|
-
timeoutSignal,
|
|
998
|
-
signal: signal ? AbortSignal.any([signal, timeoutSignal]) : timeoutSignal,
|
|
999
|
-
}
|
|
1000
|
-
}
|
|
472
|
+
function resolveScopedService(agent, name) { return resolveContextService(agent?.ctx, name) }
|
|
1001
473
|
|
|
1002
|
-
function
|
|
1003
|
-
|
|
1004
|
-
const data = dataLines.join('\n')
|
|
1005
|
-
if (data === '[DONE]') return undefined
|
|
474
|
+
function resolveAgentService(ctx, agent, name) {
|
|
475
|
+
const agentPresets = resolveContextService(ctx, 'agentPresets')
|
|
1006
476
|
try {
|
|
1007
|
-
const
|
|
1008
|
-
if (
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
throw replaySseError(`LCX Responses replay returned malformed SSE JSON: ${String(error)}`, 'LCX_INVALID_SSE')
|
|
1012
|
-
}
|
|
1013
|
-
}
|
|
1014
|
-
|
|
1015
|
-
function replayOutputIndex(event, item) {
|
|
1016
|
-
const value = event?.output_index ?? item?.output_index
|
|
1017
|
-
return Number.isInteger(value) ? value : undefined
|
|
1018
|
-
}
|
|
1019
|
-
|
|
1020
|
-
function replayItemId(event, item) {
|
|
1021
|
-
const value = event?.item_id ?? item?.id
|
|
1022
|
-
return typeof value === 'string' && value.length > 0 ? value : undefined
|
|
1023
|
-
}
|
|
1024
|
-
|
|
1025
|
-
function replayItemKey(event, item) {
|
|
1026
|
-
const outputIndex = replayOutputIndex(event, item)
|
|
1027
|
-
if (outputIndex !== undefined) return `output:${outputIndex}`
|
|
1028
|
-
const itemId = replayItemId(event, item)
|
|
1029
|
-
return itemId ? `item:${itemId}` : undefined
|
|
1030
|
-
}
|
|
1031
|
-
|
|
1032
|
-
function replayKind(type) {
|
|
1033
|
-
if (type === 'function_call' || type === 'response.function_call' || type === 'tool-call') return 'tool-call'
|
|
1034
|
-
if (type === 'reasoning') return 'reasoning'
|
|
1035
|
-
return 'text'
|
|
1036
|
-
}
|
|
1037
|
-
|
|
1038
|
-
function replayItemText(item, kind) {
|
|
1039
|
-
if (kind === 'reasoning') return responseReasoningText(item)
|
|
1040
|
-
return responseTextParts(item).join('')
|
|
477
|
+
const service = agentPresets?.serviceFor?.(agent, name)
|
|
478
|
+
if (service !== undefined) return service
|
|
479
|
+
} catch {}
|
|
480
|
+
return resolveScopedService(agent, name)
|
|
1041
481
|
}
|
|
1042
482
|
|
|
1043
|
-
function
|
|
1044
|
-
|
|
1045
|
-
state.open = true
|
|
1046
|
-
state.started = true
|
|
1047
|
-
return [{ type: 'block-start', index: state.index, blockType: state.kind }]
|
|
483
|
+
function concreteService(value) {
|
|
484
|
+
try { return value?.[cordisSymbols.original] ?? value } catch { return value }
|
|
1048
485
|
}
|
|
1049
486
|
|
|
1050
|
-
function
|
|
1051
|
-
|
|
1052
|
-
if (!
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
}
|
|
1056
|
-
if (typeof finalText === 'string' && (state.text.length === 0 || finalText.length >= state.text.length)) state.text = finalText
|
|
1057
|
-
state.open = false
|
|
1058
|
-
state.closed = true
|
|
1059
|
-
return [...chunks, {
|
|
1060
|
-
type: 'block-end',
|
|
1061
|
-
index: state.index,
|
|
1062
|
-
block: state.kind === 'tool-call'
|
|
1063
|
-
? { type: 'tool-call', id: state.id, name: state.name, arguments: state.arguments }
|
|
1064
|
-
: { type: state.kind, text: state.text },
|
|
1065
|
-
}]
|
|
487
|
+
function combinedAbortSignal(primary, lifecycle) {
|
|
488
|
+
if (!primary) return lifecycle
|
|
489
|
+
if (!lifecycle) return primary
|
|
490
|
+
if (primary === lifecycle) return primary
|
|
491
|
+
return AbortSignal.any([primary, lifecycle])
|
|
1066
492
|
}
|
|
1067
493
|
|
|
1068
|
-
function
|
|
1069
|
-
const
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
const
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
chunks.push(...replayEnsureOpen(state))
|
|
1079
|
-
if (finalArguments) {
|
|
1080
|
-
state.arguments = finalArguments
|
|
1081
|
-
chunks.push({ type: 'tool-call-delta', index: state.index, id: state.id, name: state.name, argumentsDelta: finalArguments })
|
|
1082
|
-
}
|
|
1083
|
-
} else if (finalArguments && state.arguments.length === 0) {
|
|
1084
|
-
state.arguments = finalArguments
|
|
1085
|
-
}
|
|
1086
|
-
chunks.push(...replayClose(state))
|
|
1087
|
-
return chunks
|
|
1088
|
-
}
|
|
1089
|
-
const finalText = replayItemText(item, kind)
|
|
1090
|
-
const chunks = []
|
|
1091
|
-
if (!state.started && finalText) {
|
|
1092
|
-
chunks.push(...replayEnsureOpen(state))
|
|
1093
|
-
state.text = finalText
|
|
1094
|
-
chunks.push({ type: kind === 'reasoning' ? 'reasoning-delta' : 'text-delta', index: state.index, text: finalText })
|
|
1095
|
-
}
|
|
1096
|
-
chunks.push(...replayClose(state, finalText))
|
|
1097
|
-
return chunks
|
|
1098
|
-
}
|
|
1099
|
-
|
|
1100
|
-
async function* responsesSseChunks(response, options = {}) {
|
|
1101
|
-
if (!response?.body) throw replaySseError('LCX Responses replay response did not include an SSE body', 'LCX_INVALID_SSE')
|
|
1102
|
-
const maxBytes = options.maxResponseBytes ?? 8 * 1024 * 1024
|
|
1103
|
-
const reader = response.body.getReader()
|
|
1104
|
-
const decoder = new TextDecoder()
|
|
1105
|
-
const states = new Map()
|
|
1106
|
-
let nextIndex = 0
|
|
1107
|
-
let pending = ''
|
|
1108
|
-
let dataLines = []
|
|
1109
|
-
let bytes = 0
|
|
1110
|
-
let sawCompleted = false
|
|
1111
|
-
let hasToolCall = false
|
|
1112
|
-
let completedResponse
|
|
1113
|
-
const cancel = () => { reader.cancel(options.signal?.reason).catch(() => undefined) }
|
|
1114
|
-
options.signal?.addEventListener('abort', cancel, { once: true })
|
|
1115
|
-
|
|
1116
|
-
const stateFor = (event, item, kindHint) => {
|
|
1117
|
-
const outputIndex = replayOutputIndex(event, item)
|
|
1118
|
-
const itemId = replayItemId(event, item)
|
|
1119
|
-
const kind = kindHint ?? replayKind(item?.type)
|
|
1120
|
-
const key = replayItemKey(event, item)
|
|
1121
|
-
let state = key ? states.get(key) : undefined
|
|
1122
|
-
if (!state) {
|
|
1123
|
-
state = [...states.values()].find((candidate) =>
|
|
1124
|
-
!candidate.closed &&
|
|
1125
|
-
((outputIndex !== undefined && candidate.outputIndex === outputIndex) ||
|
|
1126
|
-
(itemId !== undefined && candidate.itemId === itemId) ||
|
|
1127
|
-
(outputIndex === undefined && itemId === undefined && candidate.kind === kind)))
|
|
1128
|
-
}
|
|
1129
|
-
if (!state) {
|
|
1130
|
-
state = {
|
|
1131
|
-
index: nextIndex++,
|
|
1132
|
-
kind,
|
|
1133
|
-
open: false,
|
|
1134
|
-
closed: false,
|
|
1135
|
-
started: false,
|
|
1136
|
-
text: '',
|
|
1137
|
-
arguments: '',
|
|
1138
|
-
id: undefined,
|
|
1139
|
-
itemId: undefined,
|
|
1140
|
-
outputIndex: undefined,
|
|
1141
|
-
name: '',
|
|
1142
|
-
}
|
|
1143
|
-
states.set(key ?? `anonymous:${state.index}`, state)
|
|
1144
|
-
}
|
|
1145
|
-
if (outputIndex !== undefined) state.outputIndex = outputIndex
|
|
1146
|
-
if (itemId !== undefined) state.itemId = itemId
|
|
1147
|
-
if (item?.type) state.kind = replayKind(item.type)
|
|
1148
|
-
if (typeof event?.call_id === 'string' && event.call_id.length > 0) state.id = event.call_id
|
|
1149
|
-
if (typeof item?.call_id === 'string' && item.call_id.length > 0) state.id = item.call_id
|
|
1150
|
-
if (item?.name) state.name = String(item.name)
|
|
1151
|
-
return state
|
|
1152
|
-
}
|
|
1153
|
-
|
|
1154
|
-
const stateForCompleted = (item, outputIndex, consumed) => {
|
|
1155
|
-
const kind = replayKind(item?.type)
|
|
1156
|
-
const itemId = replayItemId(undefined, item)
|
|
1157
|
-
let state = [...states.values()].find((candidate) =>
|
|
1158
|
-
!consumed.has(candidate) &&
|
|
1159
|
-
((outputIndex !== undefined && candidate.outputIndex === outputIndex) ||
|
|
1160
|
-
(itemId !== undefined && candidate.itemId === itemId)))
|
|
1161
|
-
if (!state && itemId === undefined) {
|
|
1162
|
-
state = [...states.values()].find((candidate) => !consumed.has(candidate) && candidate.kind === kind)
|
|
1163
|
-
}
|
|
1164
|
-
if (!state) state = stateFor({ output_index: outputIndex }, item, kind)
|
|
1165
|
-
consumed.add(state)
|
|
1166
|
-
return state
|
|
1167
|
-
}
|
|
1168
|
-
|
|
1169
|
-
const eventChunks = (event) => {
|
|
1170
|
-
if (event?.type === 'error' || event?.type === 'response.failed' || event?.type === 'response.incomplete' || event?.type === 'response.error') {
|
|
1171
|
-
const message = event.error?.message ?? event.response?.error?.message ?? `LCX Responses replay upstream error (${String(event.type)})`
|
|
1172
|
-
throw replaySseError(message)
|
|
1173
|
-
}
|
|
1174
|
-
if (event?.type === 'response.output_item.added') {
|
|
1175
|
-
const item = event.item
|
|
1176
|
-
const state = stateFor(event, item)
|
|
1177
|
-
if (item?.type === 'function_call') hasToolCall = true
|
|
1178
|
-
return []
|
|
1179
|
-
}
|
|
1180
|
-
if (event?.type === 'response.output_text.delta') {
|
|
1181
|
-
const state = stateFor(event, undefined, 'text')
|
|
1182
|
-
const delta = typeof event.delta === 'string' ? event.delta : ''
|
|
1183
|
-
if (!delta) return []
|
|
1184
|
-
const chunks = replayEnsureOpen(state)
|
|
1185
|
-
state.text += delta
|
|
1186
|
-
chunks.push({ type: 'text-delta', index: state.index, text: delta })
|
|
1187
|
-
return chunks
|
|
1188
|
-
}
|
|
1189
|
-
if (event?.type === 'response.output_text.done') {
|
|
1190
|
-
const state = stateFor(event, undefined, 'text')
|
|
1191
|
-
return replayClose(state, typeof event.text === 'string' ? event.text : undefined)
|
|
1192
|
-
}
|
|
1193
|
-
if (event?.type === 'response.function_call_arguments.delta') {
|
|
1194
|
-
const state = stateFor(event, undefined, 'tool-call')
|
|
1195
|
-
state.kind = 'tool-call'
|
|
1196
|
-
if (typeof event.call_id === 'string' && event.call_id.length > 0) state.id = event.call_id
|
|
1197
|
-
if (!state.id) state.id = randomUUID()
|
|
1198
|
-
if (typeof event.name === 'string') state.name = event.name
|
|
1199
|
-
const delta = typeof event.delta === 'string' ? event.delta : ''
|
|
1200
|
-
if (!delta) return []
|
|
1201
|
-
hasToolCall = true
|
|
1202
|
-
const chunks = replayEnsureOpen(state)
|
|
1203
|
-
state.arguments += delta
|
|
1204
|
-
chunks.push({ type: 'tool-call-delta', index: state.index, id: state.id, name: state.name, argumentsDelta: delta })
|
|
1205
|
-
return chunks
|
|
1206
|
-
}
|
|
1207
|
-
if (event?.type === 'response.function_call_arguments.done') {
|
|
1208
|
-
const state = stateFor(event, undefined, 'tool-call')
|
|
1209
|
-
state.kind = 'tool-call'
|
|
1210
|
-
hasToolCall = true
|
|
1211
|
-
if (typeof event.call_id === 'string' && event.call_id.length > 0) state.id = event.call_id
|
|
1212
|
-
if (!state.id) state.id = randomUUID()
|
|
1213
|
-
if (typeof event.name === 'string') state.name = event.name
|
|
1214
|
-
const chunks = []
|
|
1215
|
-
const finalArguments = typeof event.arguments === 'string' ? event.arguments : ''
|
|
1216
|
-
if (!state.started && (state.name || finalArguments)) {
|
|
1217
|
-
chunks.push(...replayEnsureOpen(state))
|
|
1218
|
-
if (finalArguments) {
|
|
1219
|
-
state.arguments = finalArguments
|
|
1220
|
-
chunks.push({ type: 'tool-call-delta', index: state.index, id: state.id, name: state.name, argumentsDelta: finalArguments })
|
|
1221
|
-
}
|
|
1222
|
-
} else if (finalArguments && state.arguments.length === 0) {
|
|
1223
|
-
state.arguments = finalArguments
|
|
1224
|
-
}
|
|
1225
|
-
chunks.push(...replayClose(state))
|
|
1226
|
-
return chunks
|
|
1227
|
-
}
|
|
1228
|
-
if (event?.type === 'response.output_item.done') {
|
|
1229
|
-
const state = stateFor(event, event.item)
|
|
1230
|
-
if (event.item?.type === 'function_call' || state.kind === 'tool-call') hasToolCall = true
|
|
1231
|
-
if (state.kind === 'text') {
|
|
1232
|
-
const finalText = replayItemText(event.item, 'text')
|
|
1233
|
-
if (finalText && [...states.values()].some((candidate) =>
|
|
1234
|
-
candidate !== state && candidate.closed && candidate.kind === 'text' && candidate.text === finalText)) return []
|
|
1235
|
-
}
|
|
1236
|
-
return replayCloseItem(state, event.item)
|
|
494
|
+
function patchCompactionPressureService(compactionValue, state, getConfig, ctx, records, requestHeaders) {
|
|
495
|
+
const compaction = concreteService(compactionValue)
|
|
496
|
+
if (!compaction || typeof compaction.compactIfNeeded !== 'function' || records.has(compaction)) return false
|
|
497
|
+
const original = compaction.compactIfNeeded
|
|
498
|
+
const record = { compaction, original, wrapper: undefined, mutex: new ServiceMutex(), lifecycle: new AbortController() }
|
|
499
|
+
const wrapper = async function(agentArg, trigger, signal) {
|
|
500
|
+
const activeSignal = combinedAbortSignal(signal, record.lifecycle.signal)
|
|
501
|
+
const callOriginal = () => {
|
|
502
|
+
updateRequestHeaderCache(requestHeaders, agentArg?.session, { type: 'compaction/start' })
|
|
503
|
+
return original.call(this, agentArg, trigger, activeSignal)
|
|
1237
504
|
}
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
const
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
chunks.push(...replayCloseItem(state, item))
|
|
1263
|
-
}
|
|
505
|
+
return record.mutex.run(activeSignal, async () => {
|
|
506
|
+
if (trigger !== 'pressure' || !state.enabled || !state.remoteCompaction || !state.autoCompaction) return callOriginal()
|
|
507
|
+
const config = getConfig()
|
|
508
|
+
const target = routedTargetForAgent(agentArg, config)
|
|
509
|
+
if (!target || !resolveResponsesRouteConfig(ctx, target, config)) return callOriginal()
|
|
510
|
+
const tokenMeter = resolveScopedService(agentArg, 'tokenMeter') ?? resolveContextService(this?.ctx, 'tokenMeter')
|
|
511
|
+
const llm = resolveScopedService(agentArg, 'llm') ?? resolveContextService(ctx, 'llm')
|
|
512
|
+
if (!tokenMeter?.measure || !llm?.resolveModelInfo) return callOriginal()
|
|
513
|
+
let contextWindow
|
|
514
|
+
try { contextWindow = (await llm.resolveModelInfo(target.provider, target.model, activeSignal))?.context?.contextWindow } catch { return callOriginal() }
|
|
515
|
+
if (!Number.isFinite(contextWindow) || contextWindow <= 0) return callOriginal()
|
|
516
|
+
const totalTokens = Number(tokenMeter.measure(agentArg.session)?.totalTokens ?? 0)
|
|
517
|
+
const { auto, emergency } = pressurePolicy(state, config)
|
|
518
|
+
const ratioPercent = totalTokens / contextWindow * 100
|
|
519
|
+
if (ratioPercent < auto) return null
|
|
520
|
+
const pruner = concreteService(resolveAgentService(ctx, agentArg, 'toolResultPruner') ?? resolveContextService(this?.ctx, 'toolResultPruner'))
|
|
521
|
+
const originalPrune = pruner?.pruneSession
|
|
522
|
+
const originalConfig = this?.config
|
|
523
|
+
const nativeFirst = ratioPercent < emergency
|
|
524
|
+
const noOpPrune = () => ({ pruned: [], charsRemoved: 0 })
|
|
525
|
+
let installedConfig
|
|
526
|
+
if (nativeFirst && pruner && typeof originalPrune === 'function') pruner.pruneSession = noOpPrune
|
|
527
|
+
if (originalConfig && this && Object.prototype.hasOwnProperty.call(this, 'config')) {
|
|
528
|
+
try { installedConfig = adjustedCompactionConfig(originalConfig, target, auto / 100); this.config = installedConfig } catch {}
|
|
1264
529
|
}
|
|
1265
|
-
|
|
1266
|
-
|
|
530
|
+
ctx.logger?.info?.(`[lcx-codex] auto pressure ${ratioPercent.toFixed(1)}%: ${nativeFirst ? 'Native V2 first' : 'emergency DSH prune allowed'} (native ${auto}%, emergency ${emergency}%)`)
|
|
531
|
+
try { return await callOriginal() }
|
|
532
|
+
finally {
|
|
533
|
+
if (installedConfig && this?.config === installedConfig) { try { this.config = originalConfig } catch {} }
|
|
534
|
+
if (nativeFirst && pruner?.pruneSession === noOpPrune) { try { pruner.pruneSession = originalPrune } catch {} }
|
|
1267
535
|
}
|
|
1268
|
-
|
|
1269
|
-
if (usage) chunks.push({ type: 'usage', usage })
|
|
1270
|
-
chunks.push({ type: 'finish', reason: { kind: hasToolCall ? 'tool-calls' : 'stop' } })
|
|
1271
|
-
return chunks
|
|
1272
|
-
}
|
|
1273
|
-
if (event?.type === 'response.reasoning_summary_text.delta' || event?.type === 'response.reasoning_text.delta' || event?.type === 'response.reasoning.delta') {
|
|
1274
|
-
const state = stateFor(event, undefined, 'reasoning')
|
|
1275
|
-
state.kind = 'reasoning'
|
|
1276
|
-
const delta = typeof event.delta === 'string' ? event.delta : ''
|
|
1277
|
-
if (!delta) return []
|
|
1278
|
-
const chunks = replayEnsureOpen(state)
|
|
1279
|
-
state.text += delta
|
|
1280
|
-
chunks.push({ type: 'reasoning-delta', index: state.index, text: delta })
|
|
1281
|
-
return chunks
|
|
1282
|
-
}
|
|
1283
|
-
if (event?.type === 'response.reasoning_summary_text.done' || event?.type === 'response.reasoning_text.done' || event?.type === 'response.reasoning.done') {
|
|
1284
|
-
const state = stateFor(event, undefined, 'reasoning')
|
|
1285
|
-
state.kind = 'reasoning'
|
|
1286
|
-
return replayClose(state, typeof event.text === 'string' ? event.text : undefined)
|
|
1287
|
-
}
|
|
1288
|
-
return []
|
|
1289
|
-
}
|
|
1290
|
-
|
|
1291
|
-
const dispatch = () => {
|
|
1292
|
-
const event = replaySseEvent(dataLines)
|
|
1293
|
-
dataLines = []
|
|
1294
|
-
return event ? eventChunks(event) : []
|
|
1295
|
-
}
|
|
1296
|
-
|
|
1297
|
-
try {
|
|
1298
|
-
while (true) {
|
|
1299
|
-
if (options.signal?.aborted) throw replayAbortError(options)
|
|
1300
|
-
const result = await reader.read()
|
|
1301
|
-
if (result.done) break
|
|
1302
|
-
bytes += result.value.byteLength
|
|
1303
|
-
if (bytes > maxBytes) throw replaySseError(`LCX Responses replay SSE response exceeds ${maxBytes} bytes`, 'LCX_RESPONSE_TOO_LARGE')
|
|
1304
|
-
pending += decoder.decode(result.value, { stream: true })
|
|
1305
|
-
let newline
|
|
1306
|
-
while ((newline = pending.indexOf('\n')) >= 0) {
|
|
1307
|
-
const line = pending.slice(0, newline).replace(/\r$/u, '')
|
|
1308
|
-
pending = pending.slice(newline + 1)
|
|
1309
|
-
if (line === '') {
|
|
1310
|
-
for (const chunk of dispatch()) yield chunk
|
|
1311
|
-
} else if (!line.startsWith(':')) {
|
|
1312
|
-
if (line.startsWith('data:')) dataLines.push(line.slice(5).replace(/^ /u, ''))
|
|
1313
|
-
}
|
|
1314
|
-
}
|
|
1315
|
-
}
|
|
1316
|
-
pending += decoder.decode()
|
|
1317
|
-
if (pending.length > 0) {
|
|
1318
|
-
const line = pending.replace(/\r$/u, '')
|
|
1319
|
-
if (line.startsWith('data:')) dataLines.push(line.slice(5).replace(/^ /u, ''))
|
|
1320
|
-
}
|
|
1321
|
-
for (const chunk of dispatch()) yield chunk
|
|
1322
|
-
} catch (error) {
|
|
1323
|
-
throw replayAbortError(options, error)
|
|
1324
|
-
} finally {
|
|
1325
|
-
options.signal?.removeEventListener('abort', cancel)
|
|
1326
|
-
await reader.cancel().catch(() => undefined)
|
|
1327
|
-
}
|
|
1328
|
-
if (!sawCompleted && options.timeoutSignal?.aborted && !options.requestSignal?.aborted) {
|
|
1329
|
-
throw replayTimeoutError(options.timeoutMs)
|
|
536
|
+
})
|
|
1330
537
|
}
|
|
1331
|
-
|
|
1332
|
-
|
|
538
|
+
record.wrapper = wrapper
|
|
539
|
+
try { compaction.compactIfNeeded = wrapper } catch { return false }
|
|
540
|
+
records.set(compaction, record)
|
|
541
|
+
return true
|
|
1333
542
|
}
|
|
1334
543
|
|
|
1335
|
-
function
|
|
1336
|
-
return
|
|
1337
|
-
message: error instanceof Error ? error.message : String(error),
|
|
1338
|
-
code: typeof error?.code === 'string' ? error.code : 'LCX_CODEX_ERROR',
|
|
1339
|
-
...(Number.isInteger(error?.status) ? { status: error.status } : {}),
|
|
1340
|
-
}
|
|
544
|
+
function patchCompactionPressureForAgent(agent, state, getConfig, ctx, records, requestHeaders) {
|
|
545
|
+
return patchCompactionPressureService(resolveAgentService(ctx, agent, 'compaction'), state, getConfig, ctx, records, requestHeaders)
|
|
1341
546
|
}
|
|
1342
547
|
|
|
1343
|
-
function
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
const failure = failureOf(error)
|
|
1349
|
-
yield {
|
|
1350
|
-
type: 'finish',
|
|
1351
|
-
reason: signal?.aborted ? { kind: 'aborted', failure } : { kind: 'error', failure },
|
|
1352
|
-
}
|
|
1353
|
-
}
|
|
1354
|
-
})()
|
|
1355
|
-
}
|
|
1356
|
-
|
|
1357
|
-
async function executeHostedSearch(ctx, config, args, signal) {
|
|
1358
|
-
abortIfNeeded(signal)
|
|
1359
|
-
const normalized = normalizeHostedSearchArgs(args)
|
|
1360
|
-
const requestId = randomUUID()
|
|
1361
|
-
try {
|
|
1362
|
-
const response = await fetchJsonWithRetry(
|
|
1363
|
-
`${config.baseURL}/responses`,
|
|
1364
|
-
buildHostedSearchBody(normalized, config.model),
|
|
1365
|
-
await authenticatedHeaders(ctx, config, undefined, requestId),
|
|
1366
|
-
signal,
|
|
1367
|
-
config.timeoutMs,
|
|
1368
|
-
{ maxAttempts: config.maxAttempts, maxResponseBytes: config.maxResponseBytes },
|
|
1369
|
-
)
|
|
1370
|
-
return parseHostedSearchResponse(response, requestId, config.webMaxResults)
|
|
1371
|
-
} catch (error) {
|
|
1372
|
-
if (signal?.aborted) throw webError('LCX hosted Web Search aborted', 'LCX_WEB_ABORTED', error)
|
|
1373
|
-
if (error?.code === 'LCX_TIMEOUT') throw webError(String(error), 'LCX_WEB_TIMEOUT', error)
|
|
1374
|
-
if (error?.code?.startsWith?.('WEB_')) throw error
|
|
1375
|
-
throw webError(String(error), 'LCX_WEB_PROVIDER_ERROR', error)
|
|
548
|
+
async function restoreCompactionPressure(records) {
|
|
549
|
+
const reason = new Error('lcx-codex pressure coordination is shutting down')
|
|
550
|
+
const entries = [...records.values()]
|
|
551
|
+
for (const record of entries) {
|
|
552
|
+
if (!record.lifecycle.signal.aborted) record.lifecycle.abort(reason)
|
|
1376
553
|
}
|
|
554
|
+
await Promise.allSettled(entries.map((record) => record.mutex.close(reason)))
|
|
555
|
+
for (const record of entries) if (record.compaction?.compactIfNeeded === record.wrapper) { try { record.compaction.compactIfNeeded = record.original } catch {} }
|
|
556
|
+
records.clear()
|
|
1377
557
|
}
|
|
1378
558
|
|
|
1379
|
-
function
|
|
1380
|
-
const
|
|
1381
|
-
|
|
559
|
+
function patchVisibleWebSearchTimeout(agent, state, getConfig, patchedDefinitions) {
|
|
560
|
+
const tools = resolveScopedService(agent, 'tools')
|
|
561
|
+
const definition = tools?.get?.('web_search', agent)
|
|
562
|
+
if (!definition || typeof definition !== 'object') return
|
|
563
|
+
if (!patchedDefinitions.has(definition)) patchedDefinitions.set(definition, definition.timeoutMs)
|
|
564
|
+
const config = getConfig()
|
|
565
|
+
const timeoutMs = Number.isFinite(state.webSearchTimeoutSeconds) ? Math.min(600_000, Math.max(30_000, Math.round(state.webSearchTimeoutSeconds * 1000))) : config.webSearchTimeoutMs
|
|
566
|
+
try { definition.timeoutMs = state.enabled && state.webSearch ? timeoutMs : patchedDefinitions.get(definition) } catch {}
|
|
1382
567
|
}
|
|
1383
568
|
|
|
1384
|
-
function
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
model,
|
|
1389
|
-
profile: config.alphaProfile,
|
|
1390
|
-
group: config.alphaGroup,
|
|
1391
|
-
schemaFingerprint: ALPHA_SCHEMA_FINGERPRINT,
|
|
1392
|
-
})
|
|
569
|
+
function refreshPatchedWebSearchTimeouts(state, getConfig, patchedDefinitions) {
|
|
570
|
+
const config = getConfig()
|
|
571
|
+
const timeoutMs = Number.isFinite(state.webSearchTimeoutSeconds) ? Math.min(600_000, Math.max(30_000, Math.round(state.webSearchTimeoutSeconds * 1000))) : config.webSearchTimeoutMs
|
|
572
|
+
for (const [definition, original] of patchedDefinitions.entries()) { try { definition.timeoutMs = state.enabled && state.webSearch ? timeoutMs : original } catch {} }
|
|
1393
573
|
}
|
|
1394
574
|
|
|
1395
|
-
function
|
|
1396
|
-
try {
|
|
1397
|
-
|
|
1398
|
-
} catch {
|
|
1399
|
-
return false
|
|
1400
|
-
}
|
|
575
|
+
function restoreWebSearchTimeouts(patchedDefinitions) {
|
|
576
|
+
for (const [definition, original] of patchedDefinitions.entries()) { try { if (original === undefined) delete definition.timeoutMs; else definition.timeoutMs = original } catch {} }
|
|
577
|
+
patchedDefinitions.clear()
|
|
1401
578
|
}
|
|
1402
579
|
|
|
1403
|
-
function
|
|
1404
|
-
|
|
1405
|
-
}
|
|
580
|
+
function messagesContainNativeCheckpoint(messages, session) { return Boolean(session && (messages ?? []).some((message) => checkpointStateForMessage(session, message))) }
|
|
581
|
+
function messagesContainLegacyCheckpoint(messages) { return (messages ?? []).some((message) => Boolean(legacyCheckpointId(message))) }
|
|
1406
582
|
|
|
1407
|
-
function
|
|
1408
|
-
const
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
583
|
+
async function* recursiveLlmStream(ctx, options, messages) {
|
|
584
|
+
const llm = ctx?.get?.('llm') ?? ctx?.llm
|
|
585
|
+
if (!llm?.stream) throw Object.assign(new Error('LCX portable replay requires ctx.llm.stream'), { code: 'LCX_CHECKPOINT_REPLAY_UNAVAILABLE' })
|
|
586
|
+
const rewritten = { ...options, messages }
|
|
587
|
+
bypassReplayOptions.add(rewritten)
|
|
588
|
+
const stream = await llm.stream(rewritten)
|
|
589
|
+
for await (const chunk of stream) yield chunk
|
|
1414
590
|
}
|
|
1415
591
|
|
|
1416
|
-
async function
|
|
1417
|
-
|
|
1418
|
-
const
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
}, config)
|
|
1426
|
-
if (!routeConfig) throw alphaUnavailable('Alpha Web Search requires an active DSH GPT openai-responses model route')
|
|
1427
|
-
const fingerprint = currentAlphaCapabilityFingerprint(routeConfig)
|
|
1428
|
-
const capability = capabilityStore.get(fingerprint)
|
|
1429
|
-
if (!alphaCapabilityUsable(capability) || capability.schemaFingerprint !== ALPHA_SCHEMA_FINGERPRINT || capability.actions?.[normalized.action] !== 'supported') {
|
|
1430
|
-
throw alphaUnavailable()
|
|
1431
|
-
}
|
|
1432
|
-
if (normalized.refId && !isHttpReference(normalized.refId)) refStore.assertUsable(sessionId, fingerprint, normalized.refId)
|
|
1433
|
-
const requestId = randomUUID()
|
|
1434
|
-
try {
|
|
1435
|
-
const response = await fetchJsonWithRetry(
|
|
1436
|
-
`${routeConfig.baseURL}/alpha/search`,
|
|
1437
|
-
buildAlphaSearchBody(normalized, routeConfig.model, sessionId, true, routeConfig.alphaMaxOutputTokens),
|
|
1438
|
-
await authenticatedHeaders(ctx, routeConfig, sessionId, requestId),
|
|
1439
|
-
exec?.signal,
|
|
1440
|
-
routeConfig.timeoutMs,
|
|
1441
|
-
{ maxAttempts: routeConfig.maxAttempts, maxResponseBytes: routeConfig.maxResponseBytes },
|
|
1442
|
-
)
|
|
1443
|
-
const result = parseAlphaSearchResponse(response, {
|
|
1444
|
-
action: normalized.action,
|
|
1445
|
-
capability: capability.classification,
|
|
1446
|
-
requestId,
|
|
1447
|
-
})
|
|
1448
|
-
if (result.refs.length > 0) {
|
|
1449
|
-
refStore.record(sessionId, fingerprint, result.refs.map((refId) => ({
|
|
1450
|
-
refId,
|
|
1451
|
-
...(result.sources.find((source) => source.refId === refId)?.url ? { url: result.sources.find((source) => source.refId === refId).url } : {}),
|
|
1452
|
-
})))
|
|
1453
|
-
}
|
|
1454
|
-
return result
|
|
1455
|
-
} catch (error) {
|
|
1456
|
-
if (exec?.signal?.aborted) throw webError('LCX Alpha Web Search aborted', 'LCX_ALPHA_ABORTED', error)
|
|
1457
|
-
if (error?.code === 'LCX_TIMEOUT') throw webError('LCX Alpha Web Search timed out', 'LCX_ALPHA_TIMEOUT', error)
|
|
1458
|
-
if (error?.code?.startsWith?.('WEB_') || error?.code?.startsWith?.('LCX_ALPHA_')) throw error
|
|
1459
|
-
if ([404, 405].includes(error?.status) || /channel does not support/iu.test(String(error?.message ?? ''))) throw alphaUnavailable()
|
|
1460
|
-
throw webError('LCX Alpha Web Search provider request failed', 'LCX_ALPHA_PROVIDER_ERROR', error)
|
|
592
|
+
async function* replayCheckpointStream(options, routeConfig, ctx) {
|
|
593
|
+
const route = currentRoute(options, routeConfig)
|
|
594
|
+
const session = sessionFor(ctx, route.sessionId)
|
|
595
|
+
if (!session) throw Object.assign(new Error('Native checkpoint replay requires the live DSH session'), { code: 'LCX_SESSION_UNAVAILABLE' })
|
|
596
|
+
const incompatibleV4 = (options.messages ?? []).some((message) => { const state = checkpointStateForMessage(session, message); return state && !stateRouteCompatible(state, route, ctx) })
|
|
597
|
+
if (incompatibleV4) {
|
|
598
|
+
const portable = rewriteCheckpointsPortable(options.messages, session, { maxChars: routeConfig.portableReplayMaxChars })
|
|
599
|
+
yield* recursiveLlmStream(ctx, options, portable)
|
|
600
|
+
return
|
|
1461
601
|
}
|
|
602
|
+
const prepared = await serializeNativeAware(options.messages, route, routeConfig, ctx, { signal: options.signal, system: options.system, tools: options.tools })
|
|
603
|
+
const cacheSessionId = promptCacheSessionId(route, routeConfig)
|
|
604
|
+
const headers = await authenticatedHeaders(ctx, routeConfig, cacheSessionId, cacheSessionId === undefined ? null : undefined)
|
|
605
|
+
yield* requestNativeReplay({ baseURL: routeConfig.baseURL, provider: route.provider, model: route.model, input: prepared.input, tools: prepared.tools ?? options.tools, promptCacheKey: promptCacheKey(route, routeConfig), promptCacheRetention: promptCacheRetention(routeConfig), reasoningEffort: options.reasoningEffort, temperature: options.temperature, maxTokens: options.maxTokens, headers, signal: options.signal, timeoutMs: routeConfig.timeoutMs, maxAttempts: routeConfig.maxAttempts, maxResponseBytes: routeConfig.maxResponseBytes })
|
|
1462
606
|
}
|
|
1463
607
|
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
608
|
+
function installInjected(ctx, configInput = {}) {
|
|
609
|
+
const baseConfig = normalizeConfig(configInput)
|
|
610
|
+
let runtimeConfig = baseConfig
|
|
611
|
+
const state = { enabled: false, webSearch: false, advancedHostedSearch: false, alphaSearch: false, remoteCompaction: false, fallbackToBasicCompaction: true, autoCompaction: true, webSearchTimeoutSeconds: baseConfig.webSearchTimeoutMs / 1000, autoCompactionThresholdPercent: baseConfig.autoCompactionThresholdPercent, emergencyPruneThresholdPercent: baseConfig.emergencyPruneThresholdPercent }
|
|
612
|
+
const originalSearchProvider = readWebSearchProvider(ctx)
|
|
613
|
+
let warnedWebSelection = false
|
|
614
|
+
const provider = new LcxResponsesSearchProvider(ctx, () => runtimeConfig, () => state.enabled && state.webSearch)
|
|
615
|
+
ctx.web.registerSearchProvider(provider)
|
|
616
|
+
const tools = ctx?.get?.('tools') ?? ctx?.tools
|
|
617
|
+
let disposeAdvanced, disposeAlpha
|
|
618
|
+
const capabilityStore = new AlphaCapabilityStore(baseConfig.alphaCapabilityPath)
|
|
619
|
+
const refStore = new AlphaRefStore(baseConfig.alphaRefPath)
|
|
620
|
+
const compactionPatchRecords = new Map()
|
|
621
|
+
const patchedWebSearchDefinitions = new Map()
|
|
622
|
+
const requestHeaders = new Map()
|
|
623
|
+
const seedRequestHeader = (session) => updateRequestHeaderCache(requestHeaders, session, { type: 'compaction/start' })
|
|
624
|
+
for (const session of sessionsService(ctx)?.list?.() ?? []) seedRequestHeader(session)
|
|
625
|
+
ctx.on('session/created', (session) => {
|
|
626
|
+
seedRequestHeader(session)
|
|
627
|
+
}, { global: true })
|
|
628
|
+
ctx.on('session/disposed', (session) => {
|
|
629
|
+
requestHeaders.delete(String(session?.id ?? ''))
|
|
630
|
+
}, { global: true })
|
|
631
|
+
ctx.on('session/event', (session, event) => {
|
|
632
|
+
updateRequestHeaderCache(requestHeaders, session, event)
|
|
633
|
+
}, { global: true })
|
|
634
|
+
|
|
635
|
+
const refreshTools = () => {
|
|
636
|
+
if (state.enabled && state.webSearch && state.advancedHostedSearch && !disposeAdvanced && tools?.register) disposeAdvanced = tools.register(createAdvancedHostedTool(ctx, state, () => runtimeConfig))
|
|
637
|
+
if ((!state.enabled || !state.webSearch || !state.advancedHostedSearch) && disposeAdvanced) { disposeAdvanced(); disposeAdvanced = undefined }
|
|
638
|
+
let alphaUsable = false
|
|
639
|
+
if (state.enabled && state.alphaSearch) {
|
|
640
|
+
try { const { record } = alphaCapabilityFor(runtimeConfig, capabilityStore); alphaUsable = alphaCapabilityUsable(record) && record?.schemaFingerprint === ALPHA_SCHEMA_FINGERPRINT } catch (error) { ctx.logger?.warn?.(`[lcx-codex] Alpha capability store unavailable: ${error?.message ?? error}`) }
|
|
641
|
+
}
|
|
642
|
+
if (alphaUsable && !disposeAlpha && tools?.register) disposeAlpha = tools.register(createAlphaTool(ctx, state, () => runtimeConfig, capabilityStore, refStore))
|
|
643
|
+
if (!alphaUsable && disposeAlpha) { disposeAlpha(); disposeAlpha = undefined }
|
|
644
|
+
if (ctx.web) {
|
|
645
|
+
const target = state.enabled && state.webSearch ? runtimeConfig.webSearchProvider : originalSearchProvider
|
|
646
|
+
const selected = writeWebSearchProvider(ctx, target)
|
|
647
|
+
if (!selected && state.enabled && state.webSearch && !warnedWebSelection) {
|
|
648
|
+
warnedWebSelection = true
|
|
649
|
+
ctx.logger?.warn?.('[lcx-codex] DSH web provider selection could not be changed at runtime; websearch_gpt_advanced still works, but DSH web_search may remain on its configured provider. Pin web.searchProvider=lcx-responses in a profile overlay if this DSH version removes the runtime compatibility field.')
|
|
650
|
+
}
|
|
1487
651
|
}
|
|
1488
652
|
}
|
|
1489
|
-
}
|
|
1490
|
-
|
|
1491
|
-
function createWebSearchGptTool(ctx, state, getConfig) {
|
|
1492
|
-
return {
|
|
1493
|
-
name: WEB_SEARCH_TOOL_NAME,
|
|
1494
|
-
description: 'GPT 专属一次性 Responses Hosted Web Search。支持 query、域名过滤、用户位置、搜索上下文、外部 Web 访问、返回预算和图片搜索控制,并返回来源、引用和检索时间;不提供基于当前会话 ref_id 的 open、find、click 或 screenshot 页面操作。',
|
|
1495
|
-
parameters: HOSTED_SEARCH_PARAMETERS,
|
|
1496
|
-
output: {
|
|
1497
|
-
schema: HOSTED_SEARCH_OUTPUT,
|
|
1498
|
-
render: (_args, value) => renderHostedSearchResult(value),
|
|
1499
|
-
presentationMeta: (_args, value) => ({
|
|
1500
|
-
mode: value.mode,
|
|
1501
|
-
action: value.action,
|
|
1502
|
-
emulation: value.emulation,
|
|
1503
|
-
answer: value.content,
|
|
1504
|
-
sources: value.sources,
|
|
1505
|
-
citations: value.citations,
|
|
1506
|
-
images: value.images,
|
|
1507
|
-
warnings: value.warnings,
|
|
1508
|
-
...(value.outputBlocks ? { outputBlocks: value.outputBlocks } : {}),
|
|
1509
|
-
...(value.domains ? { domains: value.domains } : {}),
|
|
1510
|
-
...(value.lineRange ? { lineRange: value.lineRange } : {}),
|
|
1511
|
-
...(value.requestId ? { requestId: value.requestId } : {}),
|
|
1512
|
-
...(value.responseId ? { responseId: value.responseId } : {}),
|
|
1513
|
-
retrievedAt: value.retrievedAt,
|
|
1514
|
-
truncated: value.truncated,
|
|
1515
|
-
}),
|
|
1516
|
-
},
|
|
1517
|
-
presentCall: (args) => ({
|
|
1518
|
-
card: 'generic',
|
|
1519
|
-
title: `Responses Hosted 搜索:${typeof args?.query === 'string' ? args.query : ''}`,
|
|
1520
|
-
kind: 'search',
|
|
1521
|
-
rawInput: JSON.stringify(args ?? {}),
|
|
1522
|
-
}),
|
|
1523
|
-
async execute(args, exec) {
|
|
1524
|
-
if (!state.enabled || !state.webSearch) throw new Error(`${WEB_SEARCH_TOOL_NAME} 未启用,请先在设置中开启 GPT 原生 Web Search`)
|
|
1525
|
-
const config = getConfig()
|
|
1526
|
-
const { provider, model } = activeAgentRoute(exec?.agent)
|
|
1527
|
-
const routeConfig = resolveResponsesRouteConfig(ctx, {
|
|
1528
|
-
provider: provider ?? config.provider,
|
|
1529
|
-
model: model ?? config.model,
|
|
1530
|
-
}, config)
|
|
1531
|
-
if (!routeConfig) throw webError('Hosted Web Search requires an active DSH GPT openai-responses model route', 'LCX_WEB_ROUTE_UNAVAILABLE')
|
|
1532
|
-
return executeHostedSearch(ctx, routeConfig, args, exec?.signal)
|
|
1533
|
-
},
|
|
1534
|
-
}
|
|
1535
|
-
}
|
|
1536
653
|
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
pdfRefs: value.pdfRefs,
|
|
1556
|
-
...(value.requestId ? { requestId: value.requestId } : {}),
|
|
1557
|
-
...(value.responseId ? { responseId: value.responseId } : {}),
|
|
1558
|
-
retrievedAt: value.retrievedAt,
|
|
1559
|
-
warnings: value.warnings,
|
|
1560
|
-
}),
|
|
1561
|
-
},
|
|
1562
|
-
presentCall: (args) => ({
|
|
1563
|
-
card: 'generic',
|
|
1564
|
-
title: `Alpha Web Search:${typeof args?.action === 'string' ? args.action : ''}`,
|
|
1565
|
-
kind: 'search',
|
|
1566
|
-
rawInput: JSON.stringify(args ?? {}),
|
|
1567
|
-
}),
|
|
1568
|
-
execute(args, exec) {
|
|
1569
|
-
return executeAlphaSearch(ctx, getConfig(), capabilityStore, refStore, args, exec)
|
|
654
|
+
let source = () => ({ ...baseConfig, ...state })
|
|
655
|
+
installSettingsSection(ctx, SETTINGS_NS, SettingsSchema, {
|
|
656
|
+
enabled: false, webSearch: false, advancedHostedSearch: false, alphaSearch: false, remoteCompaction: false, fallbackToBasicCompaction: true, autoCompaction: true,
|
|
657
|
+
webSearchTimeoutSeconds: baseConfig.webSearchTimeoutMs / 1000,
|
|
658
|
+
autoCompactionThresholdPercent: baseConfig.autoCompactionThresholdPercent,
|
|
659
|
+
emergencyPruneThresholdPercent: baseConfig.emergencyPruneThresholdPercent,
|
|
660
|
+
provider: baseConfig.provider, baseURL: baseConfig.baseURL, apiKeyEnv: baseConfig.apiKeyEnv, model: baseConfig.model,
|
|
661
|
+
}, {
|
|
662
|
+
setSource: (current) => { source = current },
|
|
663
|
+
onChange: () => {
|
|
664
|
+
const value = source()
|
|
665
|
+
state.enabled = Boolean(value.enabled); state.webSearch = Boolean(value.webSearch); state.advancedHostedSearch = Boolean(value.advancedHostedSearch); state.alphaSearch = Boolean(value.alphaSearch); state.remoteCompaction = Boolean(value.remoteCompaction)
|
|
666
|
+
state.fallbackToBasicCompaction = value.fallbackToBasicCompaction !== false; state.autoCompaction = value.autoCompaction !== false
|
|
667
|
+
state.webSearchTimeoutSeconds = Number.isFinite(value.webSearchTimeoutSeconds) ? value.webSearchTimeoutSeconds : baseConfig.webSearchTimeoutMs / 1000
|
|
668
|
+
state.autoCompactionThresholdPercent = clampPercent(value.autoCompactionThresholdPercent, baseConfig.autoCompactionThresholdPercent, 85, 95)
|
|
669
|
+
state.emergencyPruneThresholdPercent = clampPercent(value.emergencyPruneThresholdPercent, baseConfig.emergencyPruneThresholdPercent, 90, 99)
|
|
670
|
+
runtimeConfig = normalizeConfig({ ...baseConfig, provider: value.provider ?? baseConfig.provider, baseURL: value.baseURL ?? baseConfig.baseURL, apiKeyEnv: value.apiKeyEnv ?? baseConfig.apiKeyEnv, model: value.model ?? baseConfig.model })
|
|
671
|
+
refreshTools(); refreshPatchedWebSearchTimeouts(state, () => runtimeConfig, patchedWebSearchDefinitions)
|
|
1570
672
|
},
|
|
1571
|
-
}
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
alphaSearch: false,
|
|
1582
|
-
remoteCompaction: false,
|
|
1583
|
-
fallbackToBasicCompaction: true,
|
|
1584
|
-
}
|
|
1585
|
-
let runtimeConfig = config
|
|
1586
|
-
const provider = new LcxResponsesSearchProvider(ctx, runtimeConfig, () => state.enabled && state.webSearch)
|
|
1587
|
-
ctx.web.registerSearchProvider(provider)
|
|
1588
|
-
const tools = ctx.get?.('tools') ?? ctx.tools
|
|
1589
|
-
const systemPrompt = ctx.get?.('systemPrompt') ?? ctx.systemPrompt
|
|
1590
|
-
const sessionTracker = createSessionGenerationTracker(ctx)
|
|
1591
|
-
let disposeHostedTool
|
|
1592
|
-
let disposeAlphaTool
|
|
1593
|
-
let disposeHostedPrompt
|
|
1594
|
-
let disposeAlphaPrompt
|
|
1595
|
-
let alphaCapabilityStore
|
|
1596
|
-
let alphaRefStore
|
|
1597
|
-
let lastAlphaStoreErrorCode
|
|
673
|
+
})
|
|
674
|
+
try {
|
|
675
|
+
const value = source()
|
|
676
|
+
Object.assign(state, { enabled: Boolean(value.enabled), webSearch: Boolean(value.webSearch), advancedHostedSearch: Boolean(value.advancedHostedSearch), alphaSearch: Boolean(value.alphaSearch), remoteCompaction: Boolean(value.remoteCompaction), fallbackToBasicCompaction: value.fallbackToBasicCompaction !== false, autoCompaction: value.autoCompaction !== false, webSearchTimeoutSeconds: Number.isFinite(value.webSearchTimeoutSeconds) ? value.webSearchTimeoutSeconds : baseConfig.webSearchTimeoutMs / 1000, autoCompactionThresholdPercent: clampPercent(value.autoCompactionThresholdPercent, baseConfig.autoCompactionThresholdPercent, 85, 95), emergencyPruneThresholdPercent: clampPercent(value.emergencyPruneThresholdPercent, baseConfig.emergencyPruneThresholdPercent, 90, 99) })
|
|
677
|
+
runtimeConfig = normalizeConfig({ ...baseConfig, provider: value.provider ?? baseConfig.provider, baseURL: value.baseURL ?? baseConfig.baseURL, apiKeyEnv: value.apiKeyEnv ?? baseConfig.apiKeyEnv, model: value.model ?? baseConfig.model })
|
|
678
|
+
} catch {}
|
|
679
|
+
refreshTools()
|
|
680
|
+
ctx.inject(['compaction'], compactionCtx => {
|
|
681
|
+
patchCompactionPressureService(resolveContextService(compactionCtx, 'compaction'), state, () => runtimeConfig, ctx, compactionPatchRecords, requestHeaders)
|
|
682
|
+
})
|
|
1598
683
|
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
} catch (error) {
|
|
1605
|
-
const code = String(error?.code ?? 'LCX_ALPHA_STORE_UNAVAILABLE')
|
|
1606
|
-
if (code !== lastAlphaStoreErrorCode) ctx.logger?.error?.(`[lcx-codex] alpha-store unavailable code=${code}`)
|
|
1607
|
-
lastAlphaStoreErrorCode = code
|
|
1608
|
-
alphaCapabilityStore = undefined
|
|
1609
|
-
alphaRefStore = undefined
|
|
1610
|
-
return undefined
|
|
1611
|
-
}
|
|
1612
|
-
}
|
|
684
|
+
ctx.on('tools/execute', async (exec, next) => {
|
|
685
|
+
if (!state.enabled || !state.webSearch || exec?.name !== 'web_search') return next()
|
|
686
|
+
const active = activeAgentRoute(exec, runtimeConfig)
|
|
687
|
+
return hostedSearchRouteContext.run(active, () => next())
|
|
688
|
+
})
|
|
1613
689
|
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
const
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
let alphaCapability
|
|
1620
|
-
try {
|
|
1621
|
-
alphaCapability = fingerprint ? stores?.capability.get(fingerprint) : undefined
|
|
1622
|
-
if (stores) lastAlphaStoreErrorCode = undefined
|
|
1623
|
-
} catch (error) {
|
|
1624
|
-
const code = String(error?.code ?? 'LCX_ALPHA_STORE_UNAVAILABLE')
|
|
1625
|
-
if (code !== lastAlphaStoreErrorCode) ctx.logger?.error?.(`[lcx-codex] alpha-store unavailable code=${code}`)
|
|
1626
|
-
lastAlphaStoreErrorCode = code
|
|
1627
|
-
}
|
|
1628
|
-
const alphaEnabled = Boolean(stores && alphaCapabilityUsable(alphaCapability) && alphaCapability?.schemaFingerprint === ALPHA_SCHEMA_FINGERPRINT)
|
|
1629
|
-
if (hostedEnabled && !disposeHostedTool && tools?.register) disposeHostedTool = tools.register(createWebSearchGptTool(ctx, state, () => runtimeConfig))
|
|
1630
|
-
if (!hostedEnabled && disposeHostedTool) {
|
|
1631
|
-
disposeHostedTool()
|
|
1632
|
-
disposeHostedTool = undefined
|
|
1633
|
-
}
|
|
1634
|
-
if (alphaEnabled && !disposeAlphaTool && tools?.register) disposeAlphaTool = tools.register(createWebSearchAlphaTool(ctx, () => runtimeConfig, stores.capability, stores.refs))
|
|
1635
|
-
if (!alphaEnabled && disposeAlphaTool) {
|
|
1636
|
-
disposeAlphaTool()
|
|
1637
|
-
disposeAlphaTool = undefined
|
|
1638
|
-
}
|
|
1639
|
-
if (hostedEnabled && !disposeHostedPrompt && systemPrompt?.section) {
|
|
1640
|
-
disposeHostedPrompt = systemPrompt.section({
|
|
1641
|
-
name: 'tool:websearch_gpt',
|
|
1642
|
-
order: 111,
|
|
1643
|
-
text: '使用 websearch_gpt 查询当前或需要来源的问题。该工具走 Responses hosted web_search;最终回答必须引用直接 URL,并区分来源发布时间与检索时间。',
|
|
1644
|
-
}) ?? (() => {})
|
|
1645
|
-
}
|
|
1646
|
-
if (!hostedEnabled && disposeHostedPrompt) {
|
|
1647
|
-
disposeHostedPrompt()
|
|
1648
|
-
disposeHostedPrompt = undefined
|
|
1649
|
-
}
|
|
1650
|
-
if (alphaEnabled && !disposeAlphaPrompt && systemPrompt?.section) {
|
|
1651
|
-
disposeAlphaPrompt = systemPrompt.section({
|
|
1652
|
-
name: 'tool:websearch_alpha',
|
|
1653
|
-
order: 112,
|
|
1654
|
-
text: 'websearch_alpha 是独立 Alpha command API。一次只调用一个 action;只能对当前 session 已返回的 ref_id 执行 open/find/click/screenshot,最终回答引用直接 URL,不暴露内部 ref_id。',
|
|
1655
|
-
}) ?? (() => {})
|
|
1656
|
-
}
|
|
1657
|
-
if (!alphaEnabled && disposeAlphaPrompt) {
|
|
1658
|
-
disposeAlphaPrompt()
|
|
1659
|
-
disposeAlphaPrompt = undefined
|
|
1660
|
-
}
|
|
1661
|
-
}
|
|
690
|
+
ctx.on('agent/created', ({ agent }) => {
|
|
691
|
+
if (!agent) return
|
|
692
|
+
const installed = patchCompactionPressureForAgent(agent, state, () => runtimeConfig, ctx, compactionPatchRecords, requestHeaders)
|
|
693
|
+
if (installed) ctx.logger?.info?.('[lcx-codex] pressure coordination installed through AgentPresets service resolver')
|
|
694
|
+
}, { global: true })
|
|
1662
695
|
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
}
|
|
1670
|
-
const settingsBase = {
|
|
1671
|
-
enabled: false,
|
|
1672
|
-
webSearch: false,
|
|
1673
|
-
alphaSearch: false,
|
|
1674
|
-
remoteCompaction: false,
|
|
1675
|
-
fallbackToBasicCompaction: true,
|
|
1676
|
-
provider: config.provider,
|
|
1677
|
-
baseURL: config.baseURL,
|
|
1678
|
-
apiKeyEnv: config.apiKeyEnv,
|
|
1679
|
-
model: config.model,
|
|
1680
|
-
compactTransport: config.compactTransport,
|
|
1681
|
-
}
|
|
1682
|
-
const attachSettings = (settingsContext) => {
|
|
1683
|
-
const settings = settingsContext.settings ?? settingsContext
|
|
1684
|
-
if (!settings?.register) return
|
|
1685
|
-
const scope = settings.register(settingsNamespace(SETTINGS_NAMESPACE), SettingsSchema, { base: settingsBase })
|
|
1686
|
-
sync(scope.get())
|
|
1687
|
-
scope.watch(() => sync(scope.get()))
|
|
1688
|
-
}
|
|
1689
|
-
if (typeof ctx.inject === 'function') ctx.inject(['settings'], attachSettings)
|
|
1690
|
-
else attachSettings(ctx.get?.('settings') ?? {})
|
|
696
|
+
ctx.on('agent/status', ({ agent, status }) => {
|
|
697
|
+
if (status !== 'running' || !agent) return
|
|
698
|
+
const installed = patchCompactionPressureForAgent(agent, state, () => runtimeConfig, ctx, compactionPatchRecords, requestHeaders)
|
|
699
|
+
if (installed) ctx.logger?.info?.('[lcx-codex] pressure coordination installed through AgentPresets service resolver')
|
|
700
|
+
patchVisibleWebSearchTimeout(agent, state, () => runtimeConfig, patchedWebSearchDefinitions)
|
|
701
|
+
}, { global: true })
|
|
1691
702
|
|
|
1692
|
-
ctx.effect?.(() => () => {
|
|
1693
|
-
disposeHostedTool?.()
|
|
1694
|
-
disposeAlphaTool?.()
|
|
1695
|
-
disposeHostedPrompt?.()
|
|
1696
|
-
disposeAlphaPrompt?.()
|
|
1697
|
-
sessionTracker.dispose()
|
|
1698
|
-
ctx.web.searchProviderId = originalSearchProvider
|
|
1699
|
-
}, 'lcx-codex: restore web provider selection')
|
|
1700
703
|
ctx.on('llm/stream', (options, next) => {
|
|
1701
|
-
if (
|
|
1702
|
-
if (!state.enabled || !state.remoteCompaction) return next()
|
|
1703
|
-
if (options.purpose === 'session-title') return next()
|
|
704
|
+
if (bypassReplayOptions.delete(options)) return next()
|
|
705
|
+
if (!state.enabled || !state.remoteCompaction || options.purpose === 'session-title') return next()
|
|
1704
706
|
const routeConfig = resolveResponsesRouteConfig(ctx, options, runtimeConfig)
|
|
1705
|
-
let portableCheckpoint
|
|
1706
|
-
try {
|
|
1707
|
-
portableCheckpoint = hasPortableCheckpoint(options.messages)
|
|
1708
|
-
} catch (error) {
|
|
1709
|
-
return guardedStream(() => { throw error }, options.signal)
|
|
1710
|
-
}
|
|
1711
|
-
if (portableCheckpoint) {
|
|
1712
|
-
if (!routeConfig) return guardedStream(() => { throw nativeRouteError(options) }, options.signal)
|
|
1713
|
-
if (options.purpose === 'compaction') {
|
|
1714
|
-
const factory = () => remoteCompactionStream(options, routeConfig, portableStore, ctx, sessionTracker)
|
|
1715
|
-
return guardedStream(factory, options.signal)
|
|
1716
|
-
}
|
|
1717
|
-
let replay
|
|
1718
|
-
try {
|
|
1719
|
-
replay = checkpointReplayRecord(options.messages, portableStore)
|
|
1720
|
-
} catch (error) {
|
|
1721
|
-
return guardedStream(() => { throw error }, options.signal)
|
|
1722
|
-
}
|
|
1723
|
-
const route = routeWithSessionAncestry(ctx, currentRoute(options, routeConfig))
|
|
1724
|
-
if (options.sessionId && replay?.record.routeFingerprint === routeFingerprint(route)) {
|
|
1725
|
-
return guardedStream(
|
|
1726
|
-
() => nativeCheckpointReplayStream(options, routeConfig, portableStore, ctx, replay.record, replay.marker),
|
|
1727
|
-
options.signal,
|
|
1728
|
-
)
|
|
1729
|
-
}
|
|
1730
|
-
const llm = ctx?.llm ?? ctx?.get?.('llm')
|
|
1731
|
-
if (!llm || typeof llm.stream !== 'function') {
|
|
1732
|
-
return guardedStream(() => { throw checkpointReplayUnavailableError() }, options.signal)
|
|
1733
|
-
}
|
|
1734
|
-
return guardedStream(async function* () {
|
|
1735
|
-
const markerTail = replay?.marker ? options.messages.slice(replay.marker.index + 1) : []
|
|
1736
|
-
const needsImageCapability = Number(replay?.record.portableImageCount ?? 0) > 0 || dshMessagesContainImage(markerTail)
|
|
1737
|
-
const imageSupport = needsImageCapability
|
|
1738
|
-
? await resolveModelImageSupport(llm, route, options.signal)
|
|
1739
|
-
: 'unknown'
|
|
1740
|
-
const rewrittenOptions = {
|
|
1741
|
-
...options,
|
|
1742
|
-
messages: buildPortableReplayMessages(options.messages, portableStore, route, { imageSupport }),
|
|
1743
|
-
}
|
|
1744
|
-
checkpointReplayOptions.add(rewrittenOptions)
|
|
1745
|
-
const stream = await llm.stream(rewrittenOptions)
|
|
1746
|
-
for await (const chunk of stream) yield chunk
|
|
1747
|
-
}, options.signal)
|
|
1748
|
-
}
|
|
1749
707
|
if (options.purpose === 'compaction') {
|
|
1750
708
|
if (!routeConfig) return next()
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
709
|
+
return remoteCompactionStream(options, routeConfig, state, ctx, next, requestHeaders)
|
|
710
|
+
}
|
|
711
|
+
const session = sessionFor(ctx, String(options.sessionId ?? ''))
|
|
712
|
+
const hasNative = messagesContainNativeCheckpoint(options.messages, session)
|
|
713
|
+
const hasLegacy = messagesContainLegacyCheckpoint(options.messages)
|
|
714
|
+
if (!hasNative && !hasLegacy) return next()
|
|
715
|
+
if (!routeConfig) {
|
|
716
|
+
if (hasNative && session) return recursiveLlmStream(ctx, options, rewriteCheckpointsPortable(options.messages, session, { maxChars: runtimeConfig.portableReplayMaxChars }))
|
|
717
|
+
return next()
|
|
1759
718
|
}
|
|
1760
|
-
return
|
|
1761
|
-
}
|
|
719
|
+
return replayCheckpointStream(options, routeConfig, ctx)
|
|
720
|
+
})
|
|
721
|
+
|
|
722
|
+
ctx.effect?.(() => async () => {
|
|
723
|
+
disposeAdvanced?.(); disposeAlpha?.()
|
|
724
|
+
await restoreCompactionPressure(compactionPatchRecords)
|
|
725
|
+
restoreWebSearchTimeouts(patchedWebSearchDefinitions)
|
|
726
|
+
writeWebSearchProvider(ctx, originalSearchProvider)
|
|
727
|
+
requestHeaders.clear()
|
|
728
|
+
}, 'lcx-codex cleanup')
|
|
1762
729
|
}
|
|
1763
730
|
|
|
1764
|
-
export {
|
|
1765
|
-
|
|
1766
|
-
SettingsSchema,
|
|
1767
|
-
CheckpointV3Store,
|
|
1768
|
-
LcxResponsesSearchProvider,
|
|
1769
|
-
apply,
|
|
1770
|
-
baseURLFingerprint,
|
|
1771
|
-
buildPortableHistory,
|
|
1772
|
-
buildPortableReplayMessages,
|
|
1773
|
-
buildPortableResponsesInput,
|
|
1774
|
-
hasPortableCheckpoint,
|
|
1775
|
-
inject,
|
|
1776
|
-
name,
|
|
1777
|
-
normalizeConfig,
|
|
1778
|
-
normalizeCompactionResponse,
|
|
1779
|
-
normalizeHostedSearchArgs,
|
|
1780
|
-
normalizeAlphaSearchArgs,
|
|
1781
|
-
portableResponsesToMessages,
|
|
1782
|
-
routeFingerprint,
|
|
731
|
+
export function apply(ctx, configInput = {}) {
|
|
732
|
+
return ctx.inject(['llm', 'web', 'sessions'], injectedCtx => installInjected(injectedCtx, configInput))
|
|
1783
733
|
}
|
|
734
|
+
|
|
735
|
+
apply.inject = inject
|
|
736
|
+
apply.Config = Config
|
|
737
|
+
|
|
738
|
+
export default apply
|