dsh-lcx-codex 0.4.0 → 0.4.1

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.
@@ -0,0 +1,124 @@
1
+ import { symbols as cordisSymbols } from '@deepseek-ai/cordis'
2
+
3
+ export function agentSessionId(agent) { return String(agent?.session?.id ?? '') }
4
+
5
+ export function readAgentRouteState(agent) {
6
+ return {
7
+ requestConfig: agent?.session?.requestHeader?.()?.config,
8
+ options: agent?.options,
9
+ sessionId: agentSessionId(agent),
10
+ }
11
+ }
12
+
13
+ export function sessionsService(ctx) { return ctx?.get?.('sessions') ?? ctx?.sessions }
14
+ export function sessionFor(ctx, sessionId) { return sessionId ? sessionsService(ctx)?.get?.(sessionId) : undefined }
15
+
16
+ export function readWebSearchProvider(ctx) {
17
+ try { return Reflect.get(ctx?.web, 'searchProviderId') } catch { return undefined }
18
+ }
19
+
20
+ export function writeWebSearchProvider(ctx, providerId) {
21
+ if (!ctx?.web) return false
22
+ try {
23
+ if (!Reflect.set(ctx.web, 'searchProviderId', providerId)) return false
24
+ return Reflect.get(ctx.web, 'searchProviderId') === providerId
25
+ } catch { return false }
26
+ }
27
+
28
+ export function contextService(ctx, name) { return ctx?.get?.(name) ?? ctx?.[name] }
29
+
30
+ export function resolveContextService(ctx, name) {
31
+ try { return contextService(ctx, name) } catch { return undefined }
32
+ }
33
+
34
+ export function resolveScopedService(agent, name) { return resolveContextService(agent?.ctx, name) }
35
+
36
+ export function resolveAgentService(ctx, agent, name) {
37
+ const agentPresets = resolveContextService(ctx, 'agentPresets')
38
+ try {
39
+ const service = agentPresets?.serviceFor?.(agent, name)
40
+ if (service !== undefined) return service
41
+ } catch {}
42
+ return resolveScopedService(agent, name)
43
+ }
44
+
45
+ export function concreteService(value) {
46
+ try { return value?.[cordisSymbols.original] ?? value } catch { return value }
47
+ }
48
+
49
+ export function compactionPatchCandidate(value, records) {
50
+ const compaction = concreteService(value)
51
+ if (!compaction || typeof compaction.compactIfNeeded !== 'function' || records.has(compaction)) return undefined
52
+ return { compaction, original: compaction.compactIfNeeded }
53
+ }
54
+
55
+ export function installCompactionPatch(records, record) {
56
+ try { record.compaction.compactIfNeeded = record.wrapper } catch { return false }
57
+ records.set(record.compaction, record)
58
+ return true
59
+ }
60
+
61
+ export function restoreCompactionPatches(records, entries = [...records.values()]) {
62
+ for (const record of entries) if (record.compaction?.compactIfNeeded === record.wrapper) {
63
+ try { record.compaction.compactIfNeeded = record.original } catch {}
64
+ }
65
+ records.clear()
66
+ }
67
+
68
+ export function toolResultPrunerState(value) {
69
+ const pruner = concreteService(value)
70
+ const original = pruner?.pruneSession
71
+ return { pruner, original }
72
+ }
73
+
74
+ export function patchToolResultPruner(state, replacement) {
75
+ if (!state?.pruner || typeof state.original !== 'function') return undefined
76
+ state.pruner.pruneSession = replacement
77
+ return { ...state, replacement }
78
+ }
79
+
80
+ export function restoreToolResultPruner(record) {
81
+ if (record?.pruner?.pruneSession === record.replacement) {
82
+ try { record.pruner.pruneSession = record.original } catch {}
83
+ }
84
+ }
85
+
86
+ export function compactionConfigState(service) { return { service, original: service?.config } }
87
+
88
+ export function patchCompactionConfig(state, createConfig) {
89
+ if (!state?.original || !state.service || !Object.prototype.hasOwnProperty.call(state.service, 'config')) return undefined
90
+ try {
91
+ const installed = createConfig(state.original)
92
+ state.service.config = installed
93
+ return { ...state, installed }
94
+ } catch { return undefined }
95
+ }
96
+
97
+ export function restoreCompactionConfig(record) {
98
+ if (record?.service?.config === record.installed) {
99
+ try { record.service.config = record.original } catch {}
100
+ }
101
+ }
102
+
103
+ export function patchVisibleWebSearchTimeout(agent, getTimeoutMs, patchedDefinitions) {
104
+ const tools = resolveScopedService(agent, 'tools')
105
+ const definition = tools?.get?.('web_search', agent)
106
+ if (!definition || typeof definition !== 'object') return
107
+ if (!patchedDefinitions.has(definition)) patchedDefinitions.set(definition, definition.timeoutMs)
108
+ const timeoutMs = getTimeoutMs()
109
+ const target = timeoutMs === undefined ? patchedDefinitions.get(definition) : timeoutMs
110
+ try { definition.timeoutMs = target } catch {}
111
+ }
112
+
113
+ export function refreshVisibleWebSearchTimeouts(patchedDefinitions, timeoutMs) {
114
+ for (const [definition, original] of patchedDefinitions.entries()) {
115
+ try { definition.timeoutMs = timeoutMs === undefined ? original : timeoutMs } catch {}
116
+ }
117
+ }
118
+
119
+ export function restoreVisibleWebSearchTimeouts(patchedDefinitions) {
120
+ for (const [definition, original] of patchedDefinitions.entries()) {
121
+ try { if (original === undefined) delete definition.timeoutMs; else definition.timeoutMs = original } catch {}
122
+ }
123
+ patchedDefinitions.clear()
124
+ }
package/lib/index.js CHANGED
@@ -4,8 +4,31 @@ import { randomUUID } from 'node:crypto'
4
4
  import { homedir } from 'node:os'
5
5
  import { join } from 'node:path'
6
6
  import { AsyncLocalStorage } from 'node:async_hooks'
7
- import { symbols as cordisSymbols } from '@deepseek-ai/cordis'
8
7
  import { fetchJsonWithRetry } from './transport.js'
8
+ import {
9
+ agentSessionId,
10
+ compactionConfigState,
11
+ compactionPatchCandidate,
12
+ contextService,
13
+ installCompactionPatch,
14
+ patchCompactionConfig,
15
+ patchToolResultPruner,
16
+ patchVisibleWebSearchTimeout,
17
+ readAgentRouteState,
18
+ readWebSearchProvider,
19
+ refreshVisibleWebSearchTimeouts,
20
+ resolveAgentService,
21
+ resolveContextService,
22
+ resolveScopedService,
23
+ restoreCompactionConfig,
24
+ restoreCompactionPatches,
25
+ restoreToolResultPruner,
26
+ restoreVisibleWebSearchTimeouts,
27
+ sessionFor,
28
+ sessionsService,
29
+ toolResultPrunerState,
30
+ writeWebSearchProvider,
31
+ } from './dsh-compat.js'
9
32
  import {
10
33
  authenticatedHeaders,
11
34
  currentRoute,
@@ -51,6 +74,9 @@ import {
51
74
  } from './web-search-hosted.js'
52
75
  import {
53
76
  ALPHA_SCHEMA_FINGERPRINT,
77
+ alphaRefRequiresStore,
78
+ isAlphaContinuationUrl,
79
+ isAlphaHttpUrl,
54
80
  ALPHA_SEARCH_OUTPUT,
55
81
  ALPHA_SEARCH_PARAMETERS,
56
82
  buildAlphaSearchBody,
@@ -160,17 +186,22 @@ function webError(message, code, cause) {
160
186
  }
161
187
 
162
188
  function activeAgentRoute(exec, fallback) {
163
- const agent = exec?.agent
164
- const latest = agent?.session?.requestHeader?.()?.config
189
+ const route = readAgentRouteState(exec?.agent)
165
190
  return {
166
- provider: latest?.provider ?? agent?.options?.provider ?? fallback.provider,
167
- model: latest?.model ?? agent?.options?.model ?? fallback.model,
168
- sessionId: String(agent?.session?.id ?? ''),
191
+ provider: route.requestConfig?.provider ?? route.options?.provider ?? fallback.provider,
192
+ model: route.requestConfig?.model ?? route.options?.model ?? fallback.model,
193
+ sessionId: route.sessionId,
169
194
  }
170
195
  }
171
196
 
172
- function sessionsService(ctx) { return ctx?.get?.('sessions') ?? ctx?.sessions }
173
- function sessionFor(ctx, sessionId) { return sessionId ? sessionsService(ctx)?.get?.(sessionId) : undefined }
197
+ function selectedAgentRoute(agent, fallback) {
198
+ const route = readAgentRouteState(agent)
199
+ return {
200
+ provider: route.options?.provider ?? route.requestConfig?.provider ?? fallback.provider,
201
+ model: route.options?.model ?? route.requestConfig?.model ?? fallback.model,
202
+ sessionId: route.sessionId,
203
+ }
204
+ }
174
205
 
175
206
  function requestImageOptions(routeConfig, imageSupport, signal, imageMap, extra = {}) {
176
207
  return {
@@ -182,17 +213,6 @@ function requestImageOptions(routeConfig, imageSupport, signal, imageMap, extra
182
213
  }
183
214
  }
184
215
 
185
- function readWebSearchProvider(ctx) {
186
- try { return Reflect.get(ctx?.web, 'searchProviderId') } catch { return undefined }
187
- }
188
- function writeWebSearchProvider(ctx, providerId) {
189
- if (!ctx?.web) return false
190
- try {
191
- if (!Reflect.set(ctx.web, 'searchProviderId', providerId)) return false
192
- return Reflect.get(ctx.web, 'searchProviderId') === providerId
193
- } catch { return false }
194
- }
195
-
196
216
  async function executeHostedSearch(ctx, routeConfig, args, signal, sessionId = '') {
197
217
  const normalized = normalizeHostedSearchArgs(args)
198
218
  const requestId = randomUUID()
@@ -251,12 +271,22 @@ function alphaCapabilityFor(config, store) {
251
271
  return { fingerprint, record: store.get(fingerprint) }
252
272
  }
253
273
 
274
+ function verifiedAlphaCapabilityForRoute(ctx, active, config, store) {
275
+ const route = resolveResponsesRouteConfig(ctx, active, config)
276
+ if (!route) return { route: undefined, usable: false }
277
+ const { fingerprint, record } = alphaCapabilityFor(route, store)
278
+ return { route, fingerprint, record, usable: alphaCapabilityUsable(record) && record?.schemaFingerprint === ALPHA_SCHEMA_FINGERPRINT }
279
+ }
280
+
254
281
  async function executeAlpha(ctx, routeConfig, capability, refStore, args, exec) {
255
282
  const normalized = normalizeAlphaSearchArgs(args)
256
- const sessionId = String(exec?.agent?.session?.id ?? '')
283
+ if (['open', 'find', 'screenshot'].includes(normalized.action) && isAlphaHttpUrl(normalized.refId) && !isAlphaContinuationUrl(normalized.refId)) {
284
+ throw webError('Alpha Search direct URLs must be public HTTP(S) targets', 'LCX_ALPHA_URL_UNAVAILABLE')
285
+ }
286
+ const sessionId = agentSessionId(exec?.agent)
257
287
  if (!sessionId) throw webError('Alpha Search requires a DSH session', 'LCX_ALPHA_SESSION_REQUIRED')
258
288
  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)
289
+ if (alphaRefRequiresStore(normalized.action, normalized.refId)) refStore.assertUsable(sessionId, routeFp, normalized.refId)
260
290
  const requestId = randomUUID(); const headers = await authenticatedHeaders(ctx, routeConfig, sessionId, requestId)
261
291
  let response
262
292
  try {
@@ -289,6 +319,41 @@ function createAlphaTool(ctx, state, getConfig, capabilityStore, refStore) {
289
319
  }
290
320
  }
291
321
 
322
+ function syncAlphaToolForAgent(ctx, agent, state, getConfig, capabilityStore, refStore, registrations) {
323
+ const previous = registrations.get(agent)
324
+ if (!state.enabled || !state.alphaSearch) {
325
+ disposeAlphaToolForAgent(agent, registrations)
326
+ return false
327
+ }
328
+ try {
329
+ const config = getConfig()
330
+ const active = selectedAgentRoute(agent, config)
331
+ const capability = verifiedAlphaCapabilityForRoute(ctx, active, config, capabilityStore)
332
+ if (!capability.usable) {
333
+ disposeAlphaToolForAgent(agent, registrations)
334
+ return false
335
+ }
336
+ if (previous?.fingerprint === capability.fingerprint) return true
337
+ disposeAlphaToolForAgent(agent, registrations)
338
+ const scopedTools = resolveScopedService(agent, 'tools')
339
+ if (!scopedTools?.register) return false
340
+ // Alpha is route-bound; global registration would advertise a static record to other routes.
341
+ registrations.set(agent, { fingerprint: capability.fingerprint, dispose: scopedTools.register(createAlphaTool(ctx, state, getConfig, capabilityStore, refStore)) })
342
+ return true
343
+ } catch (error) {
344
+ disposeAlphaToolForAgent(agent, registrations)
345
+ ctx.logger?.warn?.(`[lcx-codex] Alpha capability store unavailable: ${error?.message ?? error}`)
346
+ return false
347
+ }
348
+ }
349
+
350
+ function disposeAlphaToolForAgent(agent, registrations) {
351
+ const registration = registrations.get(agent)
352
+ if (!registration) return
353
+ registration.dispose()
354
+ registrations.delete(agent)
355
+ }
356
+
292
357
  function isDshCompactionDirective(message) {
293
358
  if (message?.role !== 'user') return false
294
359
  if (message?.source?.kind === 'plugin' && message?.source?.plugin === 'dsh-compaction-basic') return true
@@ -439,9 +504,9 @@ async function* remoteCompactionStream(options, routeConfig, state, ctx, next, r
439
504
  }
440
505
 
441
506
  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
507
+ const route = readAgentRouteState(agent)
508
+ const provider = route.requestConfig?.provider ?? route.options?.provider ?? fallback.provider
509
+ const model = route.requestConfig?.model ?? route.options?.model ?? fallback.model
445
510
  return provider && model ? { provider, model } : undefined
446
511
  }
447
512
 
@@ -465,25 +530,6 @@ function adjustedCompactionConfig(config, target, thresholdRatio) {
465
530
  return { ...config, thresholdRatio, ...(modelPolicies === undefined ? {} : { modelPolicies }) }
466
531
  }
467
532
 
468
- function resolveContextService(ctx, name) {
469
- try { return ctx?.get?.(name) ?? ctx?.[name] } catch { return undefined }
470
- }
471
-
472
- function resolveScopedService(agent, name) { return resolveContextService(agent?.ctx, name) }
473
-
474
- function resolveAgentService(ctx, agent, name) {
475
- const agentPresets = resolveContextService(ctx, 'agentPresets')
476
- try {
477
- const service = agentPresets?.serviceFor?.(agent, name)
478
- if (service !== undefined) return service
479
- } catch {}
480
- return resolveScopedService(agent, name)
481
- }
482
-
483
- function concreteService(value) {
484
- try { return value?.[cordisSymbols.original] ?? value } catch { return value }
485
- }
486
-
487
533
  function combinedAbortSignal(primary, lifecycle) {
488
534
  if (!primary) return lifecycle
489
535
  if (!lifecycle) return primary
@@ -492,9 +538,9 @@ function combinedAbortSignal(primary, lifecycle) {
492
538
  }
493
539
 
494
540
  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
541
+ const candidate = compactionPatchCandidate(compactionValue, records)
542
+ if (!candidate) return false
543
+ const { compaction, original } = candidate
498
544
  const record = { compaction, original, wrapper: undefined, mutex: new ServiceMutex(), lifecycle: new AbortController() }
499
545
  const wrapper = async function(agentArg, trigger, signal) {
500
546
  const activeSignal = combinedAbortSignal(signal, record.lifecycle.signal)
@@ -517,28 +563,22 @@ function patchCompactionPressureService(compactionValue, state, getConfig, ctx,
517
563
  const { auto, emergency } = pressurePolicy(state, config)
518
564
  const ratioPercent = totalTokens / contextWindow * 100
519
565
  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
566
+ const prunerState = toolResultPrunerState(resolveAgentService(ctx, agentArg, 'toolResultPruner') ?? resolveContextService(this?.ctx, 'toolResultPruner'))
567
+ const configState = compactionConfigState(this)
523
568
  const nativeFirst = ratioPercent < emergency
524
569
  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 {}
529
- }
570
+ const prunerPatch = nativeFirst ? patchToolResultPruner(prunerState, noOpPrune) : undefined
571
+ const configPatch = patchCompactionConfig(configState, originalConfig => adjustedCompactionConfig(originalConfig, target, auto / 100))
530
572
  ctx.logger?.info?.(`[lcx-codex] auto pressure ${ratioPercent.toFixed(1)}%: ${nativeFirst ? 'Native V2 first' : 'emergency DSH prune allowed'} (native ${auto}%, emergency ${emergency}%)`)
531
573
  try { return await callOriginal() }
532
574
  finally {
533
- if (installedConfig && this?.config === installedConfig) { try { this.config = originalConfig } catch {} }
534
- if (nativeFirst && pruner?.pruneSession === noOpPrune) { try { pruner.pruneSession = originalPrune } catch {} }
575
+ restoreCompactionConfig(configPatch)
576
+ restoreToolResultPruner(prunerPatch)
535
577
  }
536
578
  })
537
579
  }
538
580
  record.wrapper = wrapper
539
- try { compaction.compactIfNeeded = wrapper } catch { return false }
540
- records.set(compaction, record)
541
- return true
581
+ return installCompactionPatch(records, record)
542
582
  }
543
583
 
544
584
  function patchCompactionPressureForAgent(agent, state, getConfig, ctx, records, requestHeaders) {
@@ -552,36 +592,20 @@ async function restoreCompactionPressure(records) {
552
592
  if (!record.lifecycle.signal.aborted) record.lifecycle.abort(reason)
553
593
  }
554
594
  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()
595
+ restoreCompactionPatches(records, entries)
557
596
  }
558
597
 
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)
598
+ function visibleWebSearchTimeout(state, getConfig) {
564
599
  const config = getConfig()
565
600
  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 {}
567
- }
568
-
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 {} }
573
- }
574
-
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()
601
+ return state.enabled && state.webSearch ? timeoutMs : undefined
578
602
  }
579
603
 
580
604
  function messagesContainNativeCheckpoint(messages, session) { return Boolean(session && (messages ?? []).some((message) => checkpointStateForMessage(session, message))) }
581
605
  function messagesContainLegacyCheckpoint(messages) { return (messages ?? []).some((message) => Boolean(legacyCheckpointId(message))) }
582
606
 
583
607
  async function* recursiveLlmStream(ctx, options, messages) {
584
- const llm = ctx?.get?.('llm') ?? ctx?.llm
608
+ const llm = contextService(ctx, 'llm')
585
609
  if (!llm?.stream) throw Object.assign(new Error('LCX portable replay requires ctx.llm.stream'), { code: 'LCX_CHECKPOINT_REPLAY_UNAVAILABLE' })
586
610
  const rewritten = { ...options, messages }
587
611
  bypassReplayOptions.add(rewritten)
@@ -613,10 +637,12 @@ function installInjected(ctx, configInput = {}) {
613
637
  let warnedWebSelection = false
614
638
  const provider = new LcxResponsesSearchProvider(ctx, () => runtimeConfig, () => state.enabled && state.webSearch)
615
639
  ctx.web.registerSearchProvider(provider)
616
- const tools = ctx?.get?.('tools') ?? ctx?.tools
617
- let disposeAdvanced, disposeAlpha
640
+ const tools = contextService(ctx, 'tools')
641
+ let disposeAdvanced
618
642
  const capabilityStore = new AlphaCapabilityStore(baseConfig.alphaCapabilityPath)
619
643
  const refStore = new AlphaRefStore(baseConfig.alphaRefPath)
644
+ const alphaAgents = new Set()
645
+ const alphaToolRegistrations = new Map()
620
646
  const compactionPatchRecords = new Map()
621
647
  const patchedWebSearchDefinitions = new Map()
622
648
  const requestHeaders = new Map()
@@ -627,20 +653,22 @@ function installInjected(ctx, configInput = {}) {
627
653
  }, { global: true })
628
654
  ctx.on('session/disposed', (session) => {
629
655
  requestHeaders.delete(String(session?.id ?? ''))
656
+ for (const agent of alphaAgents) if (agent?.session === session) {
657
+ disposeAlphaToolForAgent(agent, alphaToolRegistrations)
658
+ alphaAgents.delete(agent)
659
+ }
630
660
  }, { global: true })
631
661
  ctx.on('session/event', (session, event) => {
632
662
  updateRequestHeaderCache(requestHeaders, session, event)
663
+ if (event?.type === 'request/header') for (const agent of alphaAgents) if (agent?.session === session) {
664
+ syncAlphaToolForAgent(ctx, agent, state, () => runtimeConfig, capabilityStore, refStore, alphaToolRegistrations)
665
+ }
633
666
  }, { global: true })
634
667
 
635
668
  const refreshTools = () => {
636
669
  if (state.enabled && state.webSearch && state.advancedHostedSearch && !disposeAdvanced && tools?.register) disposeAdvanced = tools.register(createAdvancedHostedTool(ctx, state, () => runtimeConfig))
637
670
  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 }
671
+ for (const agent of alphaAgents) syncAlphaToolForAgent(ctx, agent, state, () => runtimeConfig, capabilityStore, refStore, alphaToolRegistrations)
644
672
  if (ctx.web) {
645
673
  const target = state.enabled && state.webSearch ? runtimeConfig.webSearchProvider : originalSearchProvider
646
674
  const selected = writeWebSearchProvider(ctx, target)
@@ -668,7 +696,7 @@ function installInjected(ctx, configInput = {}) {
668
696
  state.autoCompactionThresholdPercent = clampPercent(value.autoCompactionThresholdPercent, baseConfig.autoCompactionThresholdPercent, 85, 95)
669
697
  state.emergencyPruneThresholdPercent = clampPercent(value.emergencyPruneThresholdPercent, baseConfig.emergencyPruneThresholdPercent, 90, 99)
670
698
  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)
699
+ refreshTools(); refreshVisibleWebSearchTimeouts(patchedWebSearchDefinitions, visibleWebSearchTimeout(state, () => runtimeConfig))
672
700
  },
673
701
  })
674
702
  try {
@@ -689,15 +717,19 @@ function installInjected(ctx, configInput = {}) {
689
717
 
690
718
  ctx.on('agent/created', ({ agent }) => {
691
719
  if (!agent) return
720
+ alphaAgents.add(agent)
721
+ syncAlphaToolForAgent(ctx, agent, state, () => runtimeConfig, capabilityStore, refStore, alphaToolRegistrations)
692
722
  const installed = patchCompactionPressureForAgent(agent, state, () => runtimeConfig, ctx, compactionPatchRecords, requestHeaders)
693
723
  if (installed) ctx.logger?.info?.('[lcx-codex] pressure coordination installed through AgentPresets service resolver')
694
724
  }, { global: true })
695
725
 
696
726
  ctx.on('agent/status', ({ agent, status }) => {
697
727
  if (status !== 'running' || !agent) return
728
+ alphaAgents.add(agent)
729
+ syncAlphaToolForAgent(ctx, agent, state, () => runtimeConfig, capabilityStore, refStore, alphaToolRegistrations)
698
730
  const installed = patchCompactionPressureForAgent(agent, state, () => runtimeConfig, ctx, compactionPatchRecords, requestHeaders)
699
731
  if (installed) ctx.logger?.info?.('[lcx-codex] pressure coordination installed through AgentPresets service resolver')
700
- patchVisibleWebSearchTimeout(agent, state, () => runtimeConfig, patchedWebSearchDefinitions)
732
+ patchVisibleWebSearchTimeout(agent, () => visibleWebSearchTimeout(state, () => runtimeConfig), patchedWebSearchDefinitions)
701
733
  }, { global: true })
702
734
 
703
735
  ctx.on('llm/stream', (options, next) => {
@@ -720,16 +752,18 @@ function installInjected(ctx, configInput = {}) {
720
752
  })
721
753
 
722
754
  ctx.effect?.(() => async () => {
723
- disposeAdvanced?.(); disposeAlpha?.()
755
+ disposeAdvanced?.()
756
+ for (const agent of alphaAgents) disposeAlphaToolForAgent(agent, alphaToolRegistrations)
757
+ alphaAgents.clear()
724
758
  await restoreCompactionPressure(compactionPatchRecords)
725
- restoreWebSearchTimeouts(patchedWebSearchDefinitions)
759
+ restoreVisibleWebSearchTimeouts(patchedWebSearchDefinitions)
726
760
  writeWebSearchProvider(ctx, originalSearchProvider)
727
761
  requestHeaders.clear()
728
762
  }, 'lcx-codex cleanup')
729
763
  }
730
764
 
731
765
  export function apply(ctx, configInput = {}) {
732
- return ctx.inject(['llm', 'web', 'sessions'], injectedCtx => installInjected(injectedCtx, configInput))
766
+ return installInjected(ctx, configInput)
733
767
  }
734
768
 
735
769
  apply.inject = inject