dsh-lcx-codex 0.4.3 → 0.5.0-pre.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.
package/lib/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import z from "@deepseek-ai/schemastery";
2
2
  import { recordHostedUsage, withAuxiliaryUsage } from "./auxiliary-usage.js";
3
3
  import { installSearchUsage } from "./search-usage.js";
4
+ import { projectToolUpdates } from "@deepseek-ai/dsh-llm";
4
5
  import { WEB_SEARCH_MAX_QUERIES, applyWebSearchTool, } from "@deepseek-ai/dsh-tool-web";
5
6
  import "@deepseek-ai/dsh-agent";
6
7
  import "@deepseek-ai/dsh-tools";
@@ -11,7 +12,7 @@ import { join } from "node:path";
11
12
  import { AsyncLocalStorage } from "node:async_hooks";
12
13
  import { getSupportedThinkingLevels } from "./pi-responses-runtime.js";
13
14
  import { fetchJsonWithRetry } from "./transport.js";
14
- import { agentSessionId, agentUsesSession, compactionConfigState, compactionPatchCandidate, installCompactionPatch, patchCompactionConfig, patchToolResultPruner, readAgentRouteState, resolveAgentService, resolveContextService, resolveScopedService, restoreCompactionConfig, restoreCompactionPatches, restoreToolResultPruner, sessionFor, sessionFromAgent, scopedToolRuntime, tokenMeterTotal, toolResultPrunerState, } from "./dsh-compat.js";
15
+ import { agentSessionId, agentUsesSession, compactionConfigState, compactionPatchCandidate, installCompactionPatch, patchCompactionConfig, patchToolResultPruner, readAgentRouteState, readSelectedAgentRouteState, recordAgentRouteEvent, resolveAgentService, resolveContextService, resolveScopedService, restoreCompactionConfig, restoreCompactionPatches, restoreToolResultPruner, sessionFor, sessionFromAgent, scopedToolRuntime, tokenMeterTotal, toolResultPrunerState, } from "./dsh-compat.js";
15
16
  import { InvocationPolicyScope } from "./invocation-policy-scope.js";
16
17
  import { authenticatedGrokHeaders, authenticatedHeaders, currentRoute, generationControlsFromSession, grokPromptCacheSessionId, promptCacheKey, promptCacheRetention, promptCacheSessionId, resolveGrokResponsesRouteConfig, resolveResponsesRouteConfig, routeFingerprint, } from "./route.js";
17
18
  import { DEFAULT_MAX_REQUEST_IMAGE_BYTES, DEFAULT_REQUEST_IMAGE_PIXEL_BUDGET, DEFAULT_REQUEST_IMAGE_MAX_BYTES, hydrateNativeImageReferences, resolveModelImageSupport, responseInputItems, serializeDshMessages, } from "./dsh-responses.js";
@@ -20,14 +21,14 @@ import { buildResponsesBody } from "./responses-request.js";
20
21
  import { managedFailureChunk, streamResponsesRequest, } from "./responses-stream.js";
21
22
  import { assertSupportedCheckpointMessage, checkpointStateForMessage, compactCheckpointId, createNativeCheckpointBlock, nativeCheckpointChunks, portableMessagesForCheckpoint, stateRouteCompatible, } from "./native-checkpoint.js";
22
23
  import { HOSTED_SEARCH_OUTPUT, HOSTED_SEARCH_PARAMETERS, buildHostedSearchBody, hostedMediaPresentationMeta, normalizeHostedSearchArgs, parseHostedSearchResponse, renderHostedSearchResult, } from "./web-search-hosted.js";
23
- import { ALPHA_SCHEMA_FINGERPRINT, alphaRefRequiresStore, isAlphaContinuationUrl, isAlphaHttpUrl, ALPHA_SEARCH_OUTPUT, ALPHA_SEARCH_PARAMETERS, alphaSearchRetryOptions, buildAlphaSearchBody, normalizeAlphaSearchArgs, parseAlphaSearchResponse, renderAlphaSearchResult, runWithAlphaSessionLock, } from "./web-search-alpha.js";
24
+ import { ALPHA_SCHEMA_FINGERPRINT, alphaRefRequiresStore, isAlphaContinuationUrl, isAlphaHttpUrl, ALPHA_SEARCH_OUTPUT, ALPHA_SEARCH_PARAMETERS, alphaSearchRetryOptions, buildAlphaSearchBody, normalizeAlphaSearchArgs, parseAlphaSearchResponse, remapAlphaResultRefs, renderAlphaSearchResult, runWithAlphaSessionLock, } from "./web-search-alpha.js";
25
+ import { recentAlphaInput } from "./web-search-alpha-history.js";
24
26
  import { AlphaCapabilityStore, alphaCapabilityFingerprint, alphaCapabilityUsable, alphaSearchParametersFor, assertAlphaActionAllowed, } from "./web-search-capability.js";
25
27
  import { AlphaRefStore } from "./web-search-ref-store.js";
26
28
  import { ServiceMutex } from "./service-mutex.js";
27
29
  import { GROK_NATIVE_SERVER_TOOL_TYPES, grokNativeSearchEnabled, grokVisibleFunctionTools, isGrokNativeServerToolItem, grokWireTools, restoreGrokNativeReplay, } from "./grok-native-search.js";
28
30
  export const name = "lcx-codex";
29
31
  export const inject = ["llm", "web", "sessions", "tools", "settings", "credentials", "attachments", "fs"];
30
- const SETTINGS_NS = "lcx-codex";
31
32
  const ADVANCED_HOSTED_TOOL = "websearch_gpt_advanced";
32
33
  const ALPHA_TOOL = "websearch_alpha";
33
34
  const hostedSearchRouteContext = new AsyncLocalStorage();
@@ -92,6 +93,13 @@ function defaultAlphaRefPath() {
92
93
  return join(dshHome(), "storages", "lcx-codex", "web-alpha-refs.json");
93
94
  }
94
95
  export const Config = z.object({
96
+ enabled: z.boolean().default(false).volatile(),
97
+ webSearch: z.boolean().default(false).volatile(),
98
+ advancedHostedSearch: z.boolean().default(false).volatile(),
99
+ alphaSearch: z.boolean().default(false).volatile(),
100
+ grokNativeWebSearch: z.boolean().default(false).volatile(),
101
+ grokNativeXSearch: z.boolean().default(false).volatile(),
102
+ searchMediaPreview: z.boolean().default(false).volatile(),
95
103
  supportsLongCacheRetention: z.boolean().default(false),
96
104
  supportsExplicitPromptCacheMode: z.boolean().default(false),
97
105
  alphaCapabilityPath: z.string().default(""),
@@ -114,15 +122,6 @@ export const Config = z.object({
114
122
  assistantRetentionTokenReserve: z.number().default(24_000),
115
123
  assistantRetentionPerMessageTokenCap: z.number().default(3_000),
116
124
  });
117
- const SettingsSchema = z.object({
118
- enabled: z.boolean().default(false),
119
- webSearch: z.boolean().default(false),
120
- advancedHostedSearch: z.boolean().default(false),
121
- alphaSearch: z.boolean().default(false),
122
- grokNativeWebSearch: z.boolean().default(false),
123
- grokNativeXSearch: z.boolean().default(false),
124
- searchMediaPreview: z.boolean().default(false),
125
- });
126
125
  function normalizeConfig(input = {}) {
127
126
  return {
128
127
  supportsLongCacheRetention: input.supportsLongCacheRetention === true,
@@ -170,7 +169,7 @@ function activeAgentRoute(exec) {
170
169
  };
171
170
  }
172
171
  function selectedAgentRoute(agent) {
173
- const route = readAgentRouteState(agent);
172
+ const route = readSelectedAgentRouteState(agent);
174
173
  return {
175
174
  provider: stringValue(routeConfigValue(route.requestConfig, "provider") ??
176
175
  routeConfigValue(route.options, "provider"), ""),
@@ -409,11 +408,13 @@ async function executeAlpha(ctx, routeConfig, capability, refStore, args, exec)
409
408
  const continuedRef = alphaRefRequiresStore(normalized.action, normalized.refId)
410
409
  ? refStore.assertUsable(sessionId, routeFp, normalized.refId)
411
410
  : undefined;
411
+ const rawRefId = continuedRef?.rawRefId ?? continuedRef?.refId;
412
+ const outbound = rawRefId ? { ...normalized, refId: rawRefId } : normalized;
412
413
  const requestId = randomUUID();
413
414
  const headers = await authenticatedHeaders(ctx, routeConfig, sessionId, requestId);
414
415
  let response;
415
416
  try {
416
- response = await fetchJsonWithRetry(`${routeConfig.baseURL}/alpha/search`, buildAlphaSearchBody(normalized, routeConfig.model, sessionId, true, routeConfig.alphaMaxOutputTokens), headers, exec?.signal, routeConfig.timeoutMs, alphaSearchRetryOptions(routeConfig.maxResponseBytes));
417
+ response = await fetchJsonWithRetry(`${routeConfig.baseURL}/alpha/search`, buildAlphaSearchBody(outbound, routeConfig.model, sessionId, true, routeConfig.alphaMaxOutputTokens, recentAlphaInput(sessionFromAgent(exec?.agent)?.deriveMessages() ?? [])), headers, exec?.signal, routeConfig.timeoutMs, alphaSearchRetryOptions(routeConfig.maxResponseBytes));
417
418
  }
418
419
  catch (error) {
419
420
  const details = errorDetails(error);
@@ -427,22 +428,21 @@ async function executeAlpha(ctx, routeConfig, capability, refStore, args, exec)
427
428
  capability: capability.classification,
428
429
  requestId,
429
430
  });
430
- const refRecords = observedRefs.map((observation) => {
431
- if (observation.refId !== normalized.refId || !continuedRef)
432
- return observation;
431
+ const echoed = {};
432
+ const refRecords = observedRefs.flatMap((observation) => {
433
+ if (observation.refId !== rawRefId || !continuedRef)
434
+ return [observation];
433
435
  const observedArtifact = observation.provenance.artifactFingerprint;
434
436
  const acceptedArtifact = continuedRef.provenance.artifactFingerprint;
435
- if (observedArtifact && observedArtifact !== acceptedArtifact)
436
- return observation;
437
- // An echoed input ref retains its accepted origin; an omitted URL makes no new claim.
438
- return {
439
- ...observation,
440
- ...(observation.url === undefined && continuedRef.url ? { url: continuedRef.url } : {}),
441
- provenance: { ...continuedRef.provenance },
442
- };
437
+ if ((observedArtifact && observedArtifact !== acceptedArtifact) ||
438
+ (observation.url && continuedRef.url && observation.url !== continuedRef.url))
439
+ throw webError("Alpha continuation changed its reference observation", "LCX_ALPHA_REF_COLLISION");
440
+ // Echoes retain their existing handle and cannot create a new observation.
441
+ echoed[observation.refId] = continuedRef.refId;
442
+ return [];
443
443
  });
444
- refStore.record(sessionId, routeFp, refRecords);
445
- return result;
444
+ const handles = refStore.record(sessionId, routeFp, refRecords, true);
445
+ return remapAlphaResultRefs(result, { ...handles, ...echoed });
446
446
  });
447
447
  }
448
448
  function createAlphaTool(ctx, state, getConfig, capabilityStore, refStore, advertisedRecord) {
@@ -521,10 +521,17 @@ function disposeAlphaToolForAgent(agent, registrations) {
521
521
  registration.dispose();
522
522
  registrations.delete(agent);
523
523
  }
524
+ function isPersistedMessage(message) {
525
+ return typeof message.id === "string";
526
+ }
524
527
  function isDshCompactionDirective(message) {
525
- return (message?.role === "user" &&
526
- message?.source?.kind === "plugin" &&
527
- message?.source?.plugin === "dsh-compaction-basic");
528
+ // DSH compaction-basic appends a request-only user input in its
529
+ // purpose="compaction" call. Durable conversation messages have an id.
530
+ return (message.role === "user" &&
531
+ !("id" in message) &&
532
+ !("source" in message) &&
533
+ message.content.length === 1 &&
534
+ message.content[0]?.type === "text");
528
535
  }
529
536
  function stripCompactionDirective(messages) {
530
537
  if (messages.length === 0)
@@ -578,13 +585,13 @@ async function serializeNativeAware(messages, route, routeConfig, ctx, options)
578
585
  nativeModel = serialized.model ?? nativeModel;
579
586
  grammarToolInputProperties =
580
587
  serialized.grammarToolInputProperties ?? grammarToolInputProperties;
581
- input.push(...restoreGrokNativeReplay(serialized.input, batch, options.grokNativeReplayRoute));
588
+ input.push(...restoreGrokNativeReplay(serialized.input, batch.filter(isPersistedMessage), options.grokNativeReplayRoute));
582
589
  mergeMap(imageMap, serialized.imageMap);
583
590
  };
584
591
  for (const message of surfaceMessages ?? []) {
585
592
  assertSupportedCheckpointMessage(message);
586
- const state = session
587
- ? checkpointStateForMessage(session, message)
593
+ const state = session && isPersistedMessage(message) && compactCheckpointId(message)
594
+ ? await checkpointStateForMessage(ctx, session, message)
588
595
  : undefined;
589
596
  if (state && session) {
590
597
  const checkpointId = compactCheckpointId(message);
@@ -601,7 +608,7 @@ async function serializeNativeAware(messages, route, routeConfig, ctx, options)
601
608
  mergeMap(imageMap, hydratedMap);
602
609
  }
603
610
  else {
604
- const portable = portableMessagesForCheckpoint(session, checkpointId, { maxChars: routeConfig.portableReplayMaxChars });
611
+ const portable = await portableMessagesForCheckpoint(ctx, session, checkpointId, { maxChars: routeConfig.portableReplayMaxChars });
605
612
  const serialized = await serializeDshMessages(portable, ctx, serializeOptions());
606
613
  input.push(...serialized.input);
607
614
  mergeMap(imageMap, serialized.imageMap);
@@ -634,10 +641,11 @@ function fallbackEligible(error, signal) {
634
641
  return ["LCX_HTTP_RETRYABLE", "LCX_RETRY_EXHAUSTED"].includes(String(details.code ?? ""));
635
642
  }
636
643
  async function* remoteCompactionStream(options, routeConfig, ctx, next) {
637
- const history = stripCompactionDirective(options.messages);
644
+ const projected = projectToolUpdates(options.messages, options.tools, undefined, options.toolHistory);
645
+ const history = stripCompactionDirective(projected.messages);
638
646
  const route = currentRoute(options, routeConfig);
639
647
  try {
640
- const prepared = await serializeNativeAware(history, route, routeConfig, ctx, { signal: options.signal, tools: options.tools });
648
+ const prepared = await serializeNativeAware(history, route, routeConfig, ctx, { signal: options.signal, tools: projected.tools && [...projected.tools] });
641
649
  const cacheSessionId = promptCacheSessionId(route, routeConfig, ctx);
642
650
  const headers = await authenticatedHeaders(ctx, routeConfig, cacheSessionId, cacheSessionId === undefined ? null : route.sessionId);
643
651
  const generation = generationControlsFromSession(sessionFor(ctx, route.sessionId), route);
@@ -646,7 +654,7 @@ async function* remoteCompactionStream(options, routeConfig, ctx, next) {
646
654
  model: route.model,
647
655
  modelDescriptor: prepared.model,
648
656
  input: prepared.input,
649
- tools: prepared.tools ?? options.tools,
657
+ tools: prepared.tools ?? (projected.tools && [...projected.tools]),
650
658
  promptCacheKey: promptCacheKey(route, routeConfig, ctx),
651
659
  promptCacheRetention: promptCacheRetention(routeConfig),
652
660
  cacheRetention: routeConfig.cacheRetention,
@@ -663,7 +671,8 @@ async function* remoteCompactionStream(options, routeConfig, ctx, next) {
663
671
  const session = sessionFor(ctx, route.sessionId);
664
672
  if (session === undefined)
665
673
  throw webError("Native compaction requires a live DSH session", "LCX_COMPACT_SESSION_UNAVAILABLE");
666
- const block = createNativeCheckpointBlock({
674
+ const block = await createNativeCheckpointBlock({
675
+ ctx,
667
676
  session,
668
677
  route,
669
678
  result,
@@ -681,8 +690,10 @@ async function* remoteCompactionStream(options, routeConfig, ctx, next) {
681
690
  yield chunk;
682
691
  }
683
692
  catch (error) {
693
+ if (isRecord(error) && error.code === "LCX_SESSION_QUERY_UNAVAILABLE")
694
+ throw error;
684
695
  const session = sessionFor(ctx, route.sessionId);
685
- const hasExistingCheckpoint = messagesContainNativeCheckpoint(history, session);
696
+ const hasExistingCheckpoint = await messagesContainNativeCheckpoint(ctx, history, session);
686
697
  const details = errorDetails(error);
687
698
  const code = details.code ?? details.name ?? "ERROR";
688
699
  const status = Number.isInteger(details.status)
@@ -718,16 +729,53 @@ function routedTargetForAgent(agent) {
718
729
  return provider && model ? { provider, model } : undefined;
719
730
  }
720
731
  export function compactionPressureBand(totalTokens, contextWindow, policy) {
721
- const ratioPercent = (totalTokens / contextWindow) * 100;
732
+ const pressureBudget = contextWindow - policy.outputReserve - policy.headroomTokens;
733
+ const ratioPercent = (totalTokens / pressureBudget) * 100;
734
+ const nativeThreshold = Math.floor(pressureBudget * policy.auto / 100);
735
+ const emergencyThreshold = Math.floor(pressureBudget * policy.emergency / 100);
722
736
  return {
737
+ pressureBudget,
738
+ nativeThreshold,
739
+ emergencyThreshold,
723
740
  ratioPercent,
724
- band: ratioPercent < policy.auto
741
+ band: totalTokens < nativeThreshold
725
742
  ? "below"
726
- : ratioPercent < policy.emergency
743
+ : totalTokens < emergencyThreshold
727
744
  ? "native"
728
745
  : "emergency",
729
746
  };
730
747
  }
748
+ function compactionHeadroomTokens(config, target) {
749
+ if (!isRecord(config))
750
+ return undefined;
751
+ const override = Array.isArray(config.modelPolicies)
752
+ ? config.modelPolicies.find((policy) => isRecord(policy) && policy.provider === target.provider && policy.model === target.model)
753
+ : undefined;
754
+ const headroom = isRecord(override)
755
+ ? override.headroomTokens ?? config.headroomTokens
756
+ : config.headroomTokens;
757
+ return Number.isSafeInteger(headroom) && headroom >= 0
758
+ ? headroom
759
+ : undefined;
760
+ }
761
+ function compactionRetainTokens(config, target, messageBudgetTokens) {
762
+ if (!isRecord(config))
763
+ return undefined;
764
+ const override = Array.isArray(config.modelPolicies)
765
+ ? config.modelPolicies.find((policy) => isRecord(policy) && policy.provider === target.provider && policy.model === target.model)
766
+ : undefined;
767
+ // Match compaction-basic's resolveRetention/resolveTargetPolicy precedence.
768
+ const retention = isRecord(override) &&
769
+ (override.retainTokens !== undefined || override.retainRatio !== undefined)
770
+ ? override
771
+ : config;
772
+ if (retention.retainTokens !== undefined)
773
+ return typeof retention.retainTokens === "number" ? retention.retainTokens : undefined;
774
+ const ratio = retention.retainRatio === undefined ? 0.16 : retention.retainRatio;
775
+ return typeof ratio === "number"
776
+ ? Math.floor(messageBudgetTokens * ratio)
777
+ : undefined;
778
+ }
731
779
  function adjustedCompactionConfig(config, target, thresholdRatio) {
732
780
  if (!isRecord(config))
733
781
  return config;
@@ -778,15 +826,25 @@ function patchCompactionPressureService(compactionValue, state, getConfig, ctx,
778
826
  if (!session || tokenMeter === undefined)
779
827
  return callOriginal(activeSignal);
780
828
  let contextWindow;
829
+ let defaultMaxTokens;
781
830
  try {
782
- contextWindow = (await ctx.llm.resolveModelInfo(target.provider, target.model, activeSignal)).context?.contextWindow;
831
+ const info = await ctx.llm.resolveModelInfo(target.provider, target.model, activeSignal);
832
+ contextWindow = info.context?.contextWindow;
833
+ defaultMaxTokens = info.defaultMaxTokens;
783
834
  }
784
835
  catch {
785
836
  return callOriginal(activeSignal);
786
837
  }
787
838
  const totalTokens = tokenMeterTotal(tokenMeter, session);
788
- if (contextWindow === undefined ||
839
+ const headroomTokens = compactionHeadroomTokens(compactionConfigState(candidate.compaction).original, target);
840
+ const outputReserve = session.requestHeader()?.config.maxTokens ?? defaultMaxTokens ?? 0;
841
+ if (typeof contextWindow !== "number" ||
842
+ !Number.isSafeInteger(contextWindow) ||
789
843
  contextWindow <= 0 ||
844
+ !Number.isSafeInteger(outputReserve) ||
845
+ outputReserve < 0 ||
846
+ headroomTokens === undefined ||
847
+ contextWindow - outputReserve - headroomTokens <= 0 ||
790
848
  totalTokens === undefined)
791
849
  return callOriginal(activeSignal);
792
850
  const auto = AUTO_COMPACTION_THRESHOLD_PERCENT;
@@ -794,14 +852,23 @@ function patchCompactionPressureService(compactionValue, state, getConfig, ctx,
794
852
  const pressure = compactionPressureBand(totalTokens, contextWindow, {
795
853
  auto,
796
854
  emergency,
855
+ outputReserve,
856
+ headroomTokens,
797
857
  });
798
858
  if (pressure.band === "below")
799
- return null;
859
+ return callOriginal(activeSignal);
860
+ const effectiveRetain = compactionRetainTokens(compactionConfigState(candidate.compaction).original, target, contextWindow - outputReserve);
861
+ const scopedRatio = pressure.nativeThreshold / contextWindow;
862
+ const scopedThreshold = Math.floor(Math.min(contextWindow * scopedRatio, pressure.pressureBudget));
863
+ if (effectiveRetain === undefined || effectiveRetain >= scopedThreshold) {
864
+ ctx.logger?.info?.("[lcx-codex] auto pressure: retaining upstream DSH threshold for the routed retention policy");
865
+ return callOriginal(activeSignal);
866
+ }
800
867
  const nativeFirst = pressure.band === "native";
801
868
  const prunerPatch = nativeFirst
802
869
  ? patchToolResultPruner(prunerState, () => ({ pruned: [], charsRemoved: 0 }))
803
870
  : undefined;
804
- const configPatch = patchCompactionConfig(compactionConfigState(candidate.compaction), (originalConfig) => adjustedCompactionConfig(originalConfig, target, auto / 100));
871
+ const configPatch = patchCompactionConfig(compactionConfigState(candidate.compaction), (originalConfig) => adjustedCompactionConfig(originalConfig, target, scopedRatio));
805
872
  ctx.logger?.info?.(`[lcx-codex] auto pressure ${pressure.ratioPercent.toFixed(1)}%: ${nativeFirst ? "Native V2 first" : "emergency DSH prune allowed"} (native ${auto}%, emergency ${emergency}%)`);
806
873
  try {
807
874
  return await callOriginal(activeSignal);
@@ -835,8 +902,15 @@ async function restoreCompactionPressure(records) {
835
902
  for (const record of entries)
836
903
  record.policyScope.restore();
837
904
  }
838
- function messagesContainNativeCheckpoint(messages, session) {
839
- return session !== undefined && messages.some((message) => checkpointStateForMessage(session, message));
905
+ async function messagesContainNativeCheckpoint(ctx, messages, session) {
906
+ if (!session)
907
+ return false;
908
+ for (const message of messages) {
909
+ if (isPersistedMessage(message) && compactCheckpointId(message) &&
910
+ await checkpointStateForMessage(ctx, session, message))
911
+ return true;
912
+ }
913
+ return false;
840
914
  }
841
915
  function inputHasNativeState(input) {
842
916
  return input.some((item) => isRecord(item) && item.type === "compaction");
@@ -846,13 +920,14 @@ async function* managedResponsesStream(options, routeConfig, ctx) {
846
920
  try {
847
921
  if (options.stop !== undefined)
848
922
  throw Object.assign(new Error("LCX Responses does not support GenerateOptions.stop"), { code: "LCX_RESPONSES_UNSUPPORTED_OPTION" });
849
- const prepared = await serializeNativeAware(options.messages, route, routeConfig, ctx, { signal: options.signal, tools: options.tools });
923
+ const projected = projectToolUpdates(options.messages, options.tools, undefined, options.toolHistory);
924
+ const prepared = await serializeNativeAware(projected.messages, route, routeConfig, ctx, { signal: options.signal, tools: projected.tools && [...projected.tools] });
850
925
  const cacheSessionId = promptCacheSessionId(route, routeConfig, ctx);
851
926
  const headers = await authenticatedHeaders(ctx, routeConfig, cacheSessionId, cacheSessionId === undefined ? null : route.sessionId);
852
927
  const body = buildResponsesBody({
853
928
  model: prepared.model,
854
929
  input: prepared.input,
855
- tools: prepared.tools ?? options.tools,
930
+ tools: prepared.tools ?? (projected.tools && [...projected.tools]),
856
931
  sessionId: cacheSessionId,
857
932
  promptCacheKey: promptCacheKey(route, routeConfig, ctx),
858
933
  promptCacheRetention: promptCacheRetention(routeConfig),
@@ -890,9 +965,10 @@ async function* managedGrokNativeSearchStream(options, routeConfig, nativeSearch
890
965
  try {
891
966
  if (options.stop !== undefined)
892
967
  throw Object.assign(new Error("LCX Responses does not support GenerateOptions.stop"), { code: "LCX_RESPONSES_UNSUPPORTED_OPTION" });
893
- const visibleTools = grokVisibleFunctionTools(options.tools, nativeSearch);
968
+ const projected = projectToolUpdates(options.messages, options.tools, undefined, options.toolHistory);
969
+ const visibleTools = grokVisibleFunctionTools(projected.tools, nativeSearch);
894
970
  const declaredToolNames = new Set(visibleTools?.map((tool) => tool.name) ?? []);
895
- const prepared = await serializeNativeAware(options.messages, route, routeConfig, ctx, {
971
+ const prepared = await serializeNativeAware(projected.messages, route, routeConfig, ctx, {
896
972
  signal: options.signal,
897
973
  tools: visibleTools,
898
974
  grokNativeReplayRoute: nativeReplayRoute,
@@ -961,7 +1037,7 @@ function isGptLifecycleTarget(options) {
961
1037
  async function* unavailableManagedRouteStream(options) {
962
1038
  yield managedFailureChunk(Object.assign(new Error("LCX is enabled but the selected DSH route cannot be resolved as an authenticated OpenAI Responses wire route"), { code: "LCX_RESPONSES_ROUTE_UNAVAILABLE" }), options.signal);
963
1039
  }
964
- function installInjected(ctx, configInput = {}) {
1040
+ function installInjected(ctx, configInput) {
965
1041
  installSearchUsage(ctx);
966
1042
  const baseConfig = normalizeConfig(configInput);
967
1043
  let runtimeConfig = baseConfig;
@@ -997,73 +1073,66 @@ function installInjected(ctx, configInput = {}) {
997
1073
  }
998
1074
  }, { global: true });
999
1075
  ctx.on("session/event", (session, event) => {
1000
- if (event.type === "request/header")
1076
+ recordAgentRouteEvent(session, event);
1077
+ // DSH 0.2 session/selectModel appends model/selection before the next
1078
+ // prompt assembly projects tools (dsh-api-session-controller/lib/index.js:320).
1079
+ if (event.type === "model/selection" || event.type === "request/header")
1001
1080
  for (const agent of managedAgents)
1002
1081
  if (agentUsesSession(agent, session))
1003
1082
  syncAgentTools(agent);
1004
1083
  }, { global: true });
1005
- const settingsEntry = {
1006
- enabled: false,
1007
- webSearch: false,
1008
- advancedHostedSearch: false,
1009
- alphaSearch: false,
1010
- grokNativeWebSearch: false,
1011
- grokNativeXSearch: false,
1012
- searchMediaPreview: false,
1013
- };
1014
- let source = () => settingsEntry;
1015
- ctx.settings.installSection(ctx, SETTINGS_NS, SettingsSchema, settingsEntry, {
1016
- setSource(current) {
1017
- source = current;
1018
- },
1019
- onChange() {
1020
- const value = source();
1021
- state.enabled = value.enabled;
1022
- state.webSearch = value.webSearch;
1023
- state.advancedHostedSearch = value.advancedHostedSearch;
1024
- state.alphaSearch = value.alphaSearch;
1025
- state.grokNativeWebSearch = value.grokNativeWebSearch;
1026
- state.grokNativeXSearch = value.grokNativeXSearch;
1027
- runtimeConfig = baseConfig;
1028
- refreshTools();
1029
- },
1030
- });
1031
- try {
1032
- const value = source();
1033
- Object.assign(state, {
1034
- enabled: Boolean(value.enabled),
1035
- webSearch: Boolean(value.webSearch),
1036
- advancedHostedSearch: Boolean(value.advancedHostedSearch),
1037
- alphaSearch: Boolean(value.alphaSearch),
1038
- grokNativeWebSearch: Boolean(value.grokNativeWebSearch),
1039
- grokNativeXSearch: Boolean(value.grokNativeXSearch),
1040
- });
1084
+ ctx.effect(() => ctx.settings.configure({ auto: false }));
1085
+ const syncSettings = () => {
1086
+ state.enabled = configInput.enabled.get();
1087
+ state.webSearch = configInput.webSearch.get();
1088
+ state.advancedHostedSearch = configInput.advancedHostedSearch.get();
1089
+ state.alphaSearch = configInput.alphaSearch.get();
1090
+ state.grokNativeWebSearch = configInput.grokNativeWebSearch.get();
1091
+ state.grokNativeXSearch = configInput.grokNativeXSearch.get();
1041
1092
  runtimeConfig = baseConfig;
1042
- }
1043
- catch { }
1044
- refreshTools();
1093
+ refreshTools();
1094
+ };
1095
+ syncSettings();
1096
+ ctx.on("loader/volatile-update", syncSettings);
1097
+ ctx.on("settings/document-updated", refreshTools);
1045
1098
  ctx.inject(["compaction"], (compactionCtx) => {
1046
1099
  patchCompactionPressureService(resolveContextService(compactionCtx, "compaction"), state, () => runtimeConfig, ctx, compactionPatchRecords);
1047
1100
  });
1048
- ctx.on("agent/created", ({ agent }) => {
1101
+ /**
1102
+ * LCX instrumentation is optional capability, never a creation precondition.
1103
+ * DSH 0.1.6 awaits `agent/created` serially, so an escaping failure would make
1104
+ * an otherwise valid Agent uncreatable; `agent/status` stays a contained emit.
1105
+ * Both edges therefore fail closed locally: the Agent runs uninstrumented and
1106
+ * the reason is logged, rather than propagating into the host lifecycle.
1107
+ */
1108
+ const instrumentAgent = (agent, edge) => {
1109
+ try {
1110
+ managedAgents.add(agent);
1111
+ syncAgentTools(agent);
1112
+ const installed = patchCompactionPressureForAgent(agent, state, () => runtimeConfig, ctx, compactionPatchRecords);
1113
+ if (installed)
1114
+ ctx.logger?.info?.("[lcx-codex] pressure coordination installed through AgentPresets service resolver");
1115
+ }
1116
+ catch (error) {
1117
+ ctx.logger?.warn?.(`[lcx-codex] agent instrumentation skipped on ${edge}: ${String(error)}`);
1118
+ }
1119
+ };
1120
+ /** DSH 0.1.6 lifecycle source, recorded for diagnostics only. */
1121
+ const startSource = (value) => typeof value === "string" && /^[a-z]+$/u.test(value) ? value : "unknown";
1122
+ ctx.on("agent/created", (payload) => {
1123
+ const agent = payload?.agent;
1049
1124
  if (agent === null || typeof agent !== "object")
1050
1125
  return;
1051
- managedAgents.add(agent);
1052
- syncAgentTools(agent);
1053
- const installed = patchCompactionPressureForAgent(agent, state, () => runtimeConfig, ctx, compactionPatchRecords);
1054
- if (installed)
1055
- ctx.logger?.info?.("[lcx-codex] pressure coordination installed through AgentPresets service resolver");
1126
+ // `source` is startup | resume | clear | compact. Recorded, not branched on:
1127
+ // no runtime evidence yet that a source needs different instrumentation.
1128
+ instrumentAgent(agent, `agent/created(${startSource(payload?.source)})`);
1056
1129
  }, { global: true });
1057
1130
  ctx.on("agent/status", ({ agent, status }) => {
1058
1131
  if (status !== "running" ||
1059
1132
  agent === null ||
1060
1133
  typeof agent !== "object")
1061
1134
  return;
1062
- managedAgents.add(agent);
1063
- syncAgentTools(agent);
1064
- const installed = patchCompactionPressureForAgent(agent, state, () => runtimeConfig, ctx, compactionPatchRecords);
1065
- if (installed)
1066
- ctx.logger?.info?.("[lcx-codex] pressure coordination installed through AgentPresets service resolver");
1135
+ instrumentAgent(agent, "agent/status(running)");
1067
1136
  }, { global: true });
1068
1137
  ctx.on("llm/stream", (options, next) => {
1069
1138
  if (options.purpose === "session-title")
@@ -1098,7 +1167,7 @@ function installInjected(ctx, configInput = {}) {
1098
1167
  await restoreCompactionPressure(compactionPatchRecords);
1099
1168
  }, "lcx-codex cleanup");
1100
1169
  }
1101
- export function apply(ctx, configInput = {}) {
1170
+ export function apply(ctx, configInput) {
1102
1171
  return installInjected(ctx, configInput);
1103
1172
  }
1104
1173
  apply.inject = inject;
@@ -1,4 +1,5 @@
1
1
  import { AsyncLocalStorage } from "node:async_hooks";
2
+ import { abortError } from "./abort-error.js";
2
3
  const activePolicy = new AsyncLocalStorage();
3
4
  const projections = new WeakMap();
4
5
  function scopedValue(values, target, key) {
@@ -118,11 +119,6 @@ function restoreProjection(record) {
118
119
  }
119
120
  catch { }
120
121
  }
121
- function operationError(signal) {
122
- if (signal?.reason instanceof Error)
123
- return signal.reason;
124
- return new DOMException("The operation was aborted", "AbortError");
125
- }
126
122
  export class InvocationPolicyScope {
127
123
  records = new Set();
128
124
  queue = [];
@@ -147,7 +143,7 @@ export class InvocationPolicyScope {
147
143
  if (this.closed)
148
144
  return Promise.reject(this.closeReason ?? new Error("invocation policy scope closed"));
149
145
  if (signal?.aborted)
150
- return Promise.reject(operationError(signal));
146
+ return Promise.reject(abortError(signal));
151
147
  if (this.queue.length === 0 &&
152
148
  !this.exclusiveActive &&
153
149
  (mode === "shared" || this.sharedActive === 0)) {
@@ -165,7 +161,7 @@ export class InvocationPolicyScope {
165
161
  if (index >= 0)
166
162
  this.queue.splice(index, 1);
167
163
  signal.removeEventListener("abort", onAbort);
168
- reject(operationError(signal));
164
+ reject(abortError(signal));
169
165
  this.flushQueue();
170
166
  };
171
167
  waiter.onAbort = onAbort;
@@ -193,7 +189,7 @@ export class InvocationPolicyScope {
193
189
  this.queue.shift();
194
190
  if (waiter.onAbort)
195
191
  waiter.signal.removeEventListener("abort", waiter.onAbort);
196
- waiter.reject(operationError(waiter.signal));
192
+ waiter.reject(abortError(waiter.signal));
197
193
  continue;
198
194
  }
199
195
  if (waiter.mode === "exclusive") {