@openclaw/ai 2026.9.33 → 2026.9.34

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/dist/{anthropic-a0uD7NUz.mjs → anthropic-BL3z3SiB.mjs} +2 -2
  2. package/dist/{anthropic-payload-policy-BVcxWtQD.d.mts → anthropic-payload-policy-CFbalbbU.d.mts} +3 -3
  3. package/dist/{anthropic-stream-reducer-Cis7c1sm.mjs → anthropic-stream-reducer-XCL_L8KJ.mjs} +1 -1
  4. package/dist/{api-registry-pGjnPg1z.d.mts → api-registry-BLbq15N5.d.mts} +2 -2
  5. package/dist/{azure-openai-responses-CqWvI8x6.mjs → azure-openai-responses-DqXyjyTC.mjs} +2 -2
  6. package/dist/diagnostics.d.mts +4 -4
  7. package/dist/env-api-keys-BR0MUYYD.d.mts +17 -0
  8. package/dist/event-stream-B0vjWzHd.d.mts +1 -0
  9. package/dist/{event-stream-DOPFYVqs.d.mts → event-stream-CiaHxSK0.d.mts} +1 -1
  10. package/dist/event-stream.d.mts +2 -2
  11. package/dist/{google-BfYNxKpG.mjs → google-D9zpPelZ.mjs} +1 -1
  12. package/dist/{google-interactions-BJNj0ugu.mjs → google-interactions-Br_n4tZB.mjs} +14 -11
  13. package/dist/{google-messages-C4ZiLG1P.mjs → google-messages-pvt0rKfN.mjs} +3 -1
  14. package/dist/{google-shared-6u4WCDyl.mjs → google-shared-DPeR7LLA.mjs} +9 -14
  15. package/dist/{google-vertex-CX1QaTs3.mjs → google-vertex-D4Qde9Qq.mjs} +1 -1
  16. package/dist/{host-COwfRiSL.d.mts → host-BhpDO5Sy.d.mts} +3 -3
  17. package/dist/{index-DHeFfpcz.d.mts → index-Iw8l620S.d.mts} +3 -3
  18. package/dist/index.d.mts +6 -6
  19. package/dist/internal/anthropic.d.mts +5 -5
  20. package/dist/internal/anthropic.mjs +2 -2
  21. package/dist/internal/env-api-keys.d.mts +2 -0
  22. package/dist/internal/env-api-keys.mjs +2 -0
  23. package/dist/internal/google-model-family.d.mts +2 -0
  24. package/dist/internal/google-model-family.mjs +6 -2
  25. package/dist/internal/openai-completions-compat.d.mts +1 -1
  26. package/dist/internal/openai-responses-payload-policy.d.mts +1 -1
  27. package/dist/internal/openai.d.mts +9 -9
  28. package/dist/internal/openai.mjs +5 -5
  29. package/dist/internal/runtime.d.mts +8 -23
  30. package/dist/internal/shared.d.mts +3 -3
  31. package/dist/internal/tool-schema.mjs +1 -1
  32. package/dist/{openai-chatgpt-responses-L0Jc-0DF.mjs → openai-chatgpt-responses-CHluxZ0P.mjs} +58 -49
  33. package/dist/{openai-completions-gyTpI3xG.mjs → openai-completions-D0f28olp.mjs} +3 -3
  34. package/dist/{openai-completions-compat-CqWP1fn5.d.mts → openai-completions-compat-CQbGsPt0.d.mts} +3 -3
  35. package/dist/{openai-completions-stream-BrsoGafc.mjs → openai-completions-stream-iagLwGz6.mjs} +9 -8
  36. package/dist/{openai-prompt-cache-YTmJ_iMS.d.mts → openai-prompt-cache-CuY5QDIc.d.mts} +2 -2
  37. package/dist/{openai-provider-client-D1gd0dfR.mjs → openai-provider-client-Bcjj6_fK.mjs} +1 -1
  38. package/dist/{openai-responses-DluFZzn_.mjs → openai-responses-D2ElJYAb.mjs} +3 -3
  39. package/dist/{openai-responses-compaction-replay-CQHR2isp.d.mts → openai-responses-compaction-replay-DiGjwK4d.d.mts} +4 -4
  40. package/dist/{openai-responses-contracts-C2H8pffb.d.mts → openai-responses-contracts-CYScOS7J.d.mts} +4 -4
  41. package/dist/{openai-responses-prompt-observer-internal-C5WB2Kxf.mjs → openai-responses-prompt-observer-internal-CWhVpkYl.mjs} +4 -4
  42. package/dist/{openai-responses-shared-BpGnmnV8.mjs → openai-responses-shared-BUaK4Pb3.mjs} +503 -11
  43. package/dist/{openai-responses-terminal-usage-D0WZQlIF.d.mts → openai-responses-terminal-usage-CLzeNWqW.d.mts} +2 -2
  44. package/dist/{openai-tool-schema-vsl-cE-n.mjs → openai-tool-schema-DSAxYnHW.mjs} +18 -1
  45. package/dist/{openai-transport-params-C4eZAXf2.mjs → openai-transport-params-CqLxK93q.mjs} +15 -3
  46. package/dist/{provider-options-yDbWMoEr.d.mts → provider-options-CFiaIG4K.d.mts} +2 -2
  47. package/dist/{provider-types-CHsKHBBD.d.mts → provider-types-Bx_6hBmK.d.mts} +3 -3
  48. package/dist/provider-types.d.mts +5 -5
  49. package/dist/providers.d.mts +2 -2
  50. package/dist/providers.mjs +8 -8
  51. package/dist/{transport-stream-shared-CYLM2HEK.d.mts → transport-stream-shared-B_REuIea.d.mts} +4 -4
  52. package/dist/{transport-utils-CnxX8hPj.mjs → transport-utils-B11GlmDO.mjs} +5 -1
  53. package/dist/transports.d.mts +14 -13
  54. package/dist/transports.mjs +17 -502
  55. package/dist/{types-CLLR3eB4.d.mts → types-B89w6ly2.d.mts} +11 -0
  56. package/dist/types-DbmNRKvK.d.mts +1 -0
  57. package/dist/types.d.mts +5 -5
  58. package/dist/{validation-Cat2wQsc.d.mts → validation-DblQWSRA.d.mts} +1 -1
  59. package/dist/validation.d.mts +1 -1
  60. package/package.json +6 -1
  61. package/dist/event-stream-CMQfX_yr.d.mts +0 -1
  62. package/dist/types-CoL-LJ7j.d.mts +0 -1
@@ -6,20 +6,21 @@ import { a as normalizeOptionalString, o as normalizeStringifiedOptionalString,
6
6
  import { d as supportsOpenAITemperature } from "./openai-reasoning-effort-C_0p3psG.mjs";
7
7
  import { o as truncateUtf16Safe, s as stableStringify } from "./provider-error-CwT_j2v0.mjs";
8
8
  import { a as resolveModelSseDebugMode, d as isResponsesProviderTool, g as resolveResponsesTerminalStopReason, h as readResponsesReasoningTokens, i as resolveModelPayloadDebugMode, l as ResponsesOutputIdentityError, m as mapResponsesTerminalUsage, r as emitModelTransportDebug, u as hasOnlyResponsesFunctionTools } from "./diagnostics-CJaB6g8J.mjs";
9
- import { o as redactSensitiveText } from "./transport-utils-CnxX8hPj.mjs";
9
+ import { o as redactSensitiveText } from "./transport-utils-B11GlmDO.mjs";
10
10
  import { i as resolveOpenAICompletionsCompat } from "./openai-completions-compat-D1q02qdx.mjs";
11
11
  import { A as shortHash, C as parseJsonObjectPreservingUnsafeIntegers, E as createToolArgumentPreviewSchedule, O as parseStreamingJson, S as withProviderResponseHook, _ as sanitizeNonEmptyTransportPayloadText, b as transportAbortError, c as failTransportStream, d as iterateModelStream, g as parseTerminalToolCallArguments, t as IncompleteToolCallError, u as finalizeTransportStream, v as sanitizeTransportPayloadText } from "./transport-stream-shared-Dt7VrJFd.mjs";
12
- import { C as stripSystemPromptCacheBoundary, D as sortPromptCacheToolsByName, d as describeToolResultMediaPlaceholder, m as extractToolResultText, s as prepareModelRequestBody } from "./simple-options-B9u6-CF3.mjs";
13
- import { A as redactIdentifier, C as log, d as prepareOpenAITools, h as resolveOpenAIRequestReasoning, j as sha256Hex, u as resolveOpenAIStrictToolFlagWithDiagnostics, y as createOpenAIProviderAcceptanceHook } from "./openai-transport-params-C4eZAXf2.mjs";
12
+ import { C as stripSystemPromptCacheBoundary, D as sortPromptCacheToolsByName, d as describeToolResultMediaPlaceholder, m as extractToolResultText, r as clampMaxTokensToModel, s as prepareModelRequestBody } from "./simple-options-B9u6-CF3.mjs";
13
+ import { M as sha256Hex, S as detachPartialSnapshot, d as prepareOpenAITools, h as resolveOpenAIRequestReasoning, j as redactIdentifier, u as resolveOpenAIStrictToolFlagWithDiagnostics, w as log, y as createOpenAIProviderAcceptanceHook } from "./openai-transport-params-CqLxK93q.mjs";
14
14
  import { n as providerReplayContextMatches, t as buildProviderReplayContext } from "./provider-replay-context-BwVRJUQZ.mjs";
15
15
  import { t as notifyLlmRequestActivity } from "./llm-request-activity-BjtkplhG.mjs";
16
16
  import { t as transformProviderMessages } from "./provider-transcript-transform-Cg-36VSt.mjs";
17
17
  import { a as withFirstStreamEventTimeout, i as getFirstStreamEventTimeoutMs, r as getFirstStreamEventTimeoutHandler, t as createFirstStreamEventAbortController } from "./stream-first-event-timeout-B9o0S4SZ.mjs";
18
- import { a as resolveOpenAIProjectedToolsStrictToolFlag, f as withPreparedToolSchemaNormalization, r as normalizeOpenAIStrictToolParameters } from "./openai-tool-schema-vsl-cE-n.mjs";
19
- import { a as OPENAI_RESPONSES_COMPACTION_REPLAY_TYPE, c as OPENAI_RESPONSES_RETAINED_COMPACTION_REPLAY_TYPE, f as RESPONSE_FAILED_NO_DETAILS_MESSAGE, o as OPENAI_RESPONSES_REASONING_REPLAY_BLOCK_META_KEY, p as isPreviousResponseRejection, s as OPENAI_RESPONSES_REASONING_REPLAY_META_KEY } from "./openai-responses-contracts-CLxnqS9p.mjs";
18
+ import { a as resolveOpenAIProjectedToolsStrictToolFlag, f as withPreparedToolSchemaNormalization, r as normalizeOpenAIStrictToolParameters } from "./openai-tool-schema-DSAxYnHW.mjs";
19
+ import { a as OPENAI_RESPONSES_COMPACTION_REPLAY_TYPE, c as OPENAI_RESPONSES_RETAINED_COMPACTION_REPLAY_TYPE, d as OpenAIResponsesWebSocketSafeRetryError, f as RESPONSE_FAILED_NO_DETAILS_MESSAGE, l as OpenAIResponsesWebSocketPostDispatchError, m as parseOpenAIResponsesWebSocketServerError, o as OPENAI_RESPONSES_REASONING_REPLAY_BLOCK_META_KEY, p as isPreviousResponseRejection, s as OPENAI_RESPONSES_REASONING_REPLAY_META_KEY, u as OpenAIResponsesWebSocketPreDispatchError } from "./openai-responses-contracts-CLxnqS9p.mjs";
20
20
  import { n as isOpenAIResponsesCompactionOutput, r as readOpenAIResponsesCompactionWindow } from "./openai-responses-compaction-window-CYGCjAQz.mjs";
21
21
  import { n as registerSessionResourceCleanup } from "./session-resources-CkR4WWy1.mjs";
22
22
  import { randomUUID } from "node:crypto";
23
+ import { ResponsesWS } from "openai/resources/responses/ws.js";
23
24
  //#region packages/ai/src/transports/openai-responses-replay.ts
24
25
  /** Resolves the assistant message id that can be replayed to OpenAI Responses. */
25
26
  function resolveReplayableResponsesMessageId(params) {
@@ -649,6 +650,492 @@ function createResponsesInputReplay(model) {
649
650
  };
650
651
  }
651
652
  //#endregion
653
+ //#region packages/ai/src/transports/openai-responses-steering.ts
654
+ function cloneWireRequest(request) {
655
+ const serialized = JSON.stringify(request);
656
+ return JSON.parse(serialized);
657
+ }
658
+ async function projectResponsesSteeringInput(request, project) {
659
+ const { input: activeInput, ...activeSettings } = cloneWireRequest(request);
660
+ if (!Array.isArray(activeInput)) return [];
661
+ const { input, ...settings } = cloneWireRequest(await project());
662
+ if (!Array.isArray(input) || stableStringify(settings) !== stableStringify(activeSettings) || stableStringify(input.slice(0, activeInput.length)) !== stableStringify(activeInput)) return [];
663
+ const projected = input.slice(activeInput.length);
664
+ return projected.every((item) => isRecord(item) && item.role === "user") ? projected : [];
665
+ }
666
+ /** One response owns admission; accepted input stays on this connection until continuation. */
667
+ function createResponsesSteering(params) {
668
+ let responseId;
669
+ let sealed = false;
670
+ let unsubscribe;
671
+ const pending = [];
672
+ const accepted = /* @__PURE__ */ new Map();
673
+ const acknowledged = /* @__PURE__ */ new Set();
674
+ const seal = () => {
675
+ sealed = true;
676
+ const cleanup = unsubscribe;
677
+ unsubscribe = void 0;
678
+ cleanup?.();
679
+ };
680
+ return {
681
+ get responseId() {
682
+ return responseId;
683
+ },
684
+ get pending() {
685
+ return pending.length > 0;
686
+ },
687
+ get acceptedInput() {
688
+ return [...accepted.values()].flat();
689
+ },
690
+ seal,
691
+ close(error) {
692
+ seal();
693
+ for (const submission of pending.splice(0)) submission.reject(error);
694
+ },
695
+ handle(event) {
696
+ if (!isRecord(event)) return false;
697
+ if (event.type === "response.created" && !responseId && !sealed) {
698
+ const response = isRecord(event.response) ? event.response : void 0;
699
+ if (typeof response?.id !== "string" || !response.id.trim()) throw new Error("Responses steering requires a response identity");
700
+ const activeResponseId = response.id;
701
+ responseId = activeResponseId;
702
+ const cleanup = params.onActiveResponse({
703
+ needsContinuation: params.needsContinuation,
704
+ steer(messages) {
705
+ if (sealed || messages.length === 0) return Promise.resolve(false);
706
+ params.assertActive();
707
+ const converted = params.toInput(messages);
708
+ const submit = (input) => {
709
+ if (sealed || input.length === 0) return Promise.resolve(false);
710
+ params.assertActive();
711
+ return new Promise((resolve, reject) => {
712
+ pending.push({
713
+ input,
714
+ resolve,
715
+ reject
716
+ });
717
+ params.send({
718
+ type: "response.steer",
719
+ previous_response_id: activeResponseId,
720
+ input
721
+ });
722
+ });
723
+ };
724
+ return Array.isArray(converted) ? submit(converted) : converted.then(submit);
725
+ }
726
+ });
727
+ if (sealed) cleanup?.();
728
+ else unsubscribe = cleanup;
729
+ return false;
730
+ }
731
+ if (event.type !== "response.steer.accepted" && event.type !== "response.steer.failed" && event.type !== "response.steer.pending") return false;
732
+ const steer = isRecord(event.steer) ? event.steer : void 0;
733
+ if (!responseId || steer?.previous_response_id !== responseId) throw new Error("Responses steering acknowledgement has an unexpected identity");
734
+ if (event.type === "response.steer.failed" && steer.id === void 0) {
735
+ const index = pending.findIndex((submission) => stableStringify(submission.input) === stableStringify(steer.input));
736
+ const submission = pending[index];
737
+ if (!submission) throw new Error("Responses steering acknowledgement has no pending submission");
738
+ pending.splice(index, 1);
739
+ submission.resolve(false);
740
+ return true;
741
+ }
742
+ if (typeof steer.id !== "string" || !steer.id.trim()) throw new Error("Responses steering acknowledgement has an unexpected identity");
743
+ if (event.type === "response.steer.pending") {
744
+ if (!accepted.has(steer.id)) throw new Error("Responses steering pending event has no accepted submission");
745
+ return true;
746
+ }
747
+ if (event.type === "response.steer.failed" && accepted.has(steer.id)) throw new Error("OpenAI could not apply accepted steering; the queued message remains in the conversation");
748
+ if (acknowledged.has(steer.id)) throw new Error("Responses steering acknowledgement was already applied");
749
+ const submission = pending.shift();
750
+ if (!submission) throw new Error("Responses steering acknowledgement has no pending submission");
751
+ acknowledged.add(steer.id);
752
+ if (event.type === "response.steer.accepted") {
753
+ accepted.set(steer.id, submission.input);
754
+ submission.resolve(true);
755
+ } else submission.resolve(false);
756
+ return true;
757
+ }
758
+ };
759
+ }
760
+ /** Accepted steering is prepended by the server, so never repeat it in response.create. */
761
+ function omitAcceptedSteering(input, accepted) {
762
+ const remaining = [...input];
763
+ for (const message of accepted) {
764
+ const index = remaining.findIndex((item) => stableStringify(item) === stableStringify(message));
765
+ if (index < 0) throw new Error("Responses steering continuation no longer contains the accepted user input");
766
+ remaining.splice(index, 1);
767
+ }
768
+ return remaining;
769
+ }
770
+ //#endregion
771
+ //#region packages/ai/src/transports/openai-responses-websocket.ts
772
+ const SESSION_WEBSOCKET_CACHE_TTL_MS = 3e5;
773
+ const SESSION_WEBSOCKET_MAX_AGE_MS = 33e5;
774
+ const WEBSOCKET_OPEN_STATE = 1;
775
+ const websocketSessionCache = /* @__PURE__ */ new Map();
776
+ const degradedWebSocketConnections = /* @__PURE__ */ new Map();
777
+ function isOfficialOpenAIResponsesBaseUrl(baseUrl) {
778
+ if (!baseUrl) return false;
779
+ try {
780
+ const url = new URL(baseUrl);
781
+ return url.origin === "https://api.openai.com" && url.username === "" && url.password === "" && url.search === "" && url.hash === "" && url.pathname.replace(/\/+$/, "") === "/v1";
782
+ } catch {
783
+ return false;
784
+ }
785
+ }
786
+ function supportsNativeOpenAIResponsesEndpoint(params) {
787
+ return params.provider.trim().toLowerCase() === "openai" && params.api === "openai-responses" && isOfficialOpenAIResponsesBaseUrl(params.baseUrl);
788
+ }
789
+ function closeWebSocketSilently(socket, reason = "done") {
790
+ try {
791
+ socket.close({
792
+ code: 1e3,
793
+ reason
794
+ });
795
+ } catch {}
796
+ }
797
+ function invalidateOwnedWebSocketSession(cacheKey, entry, reason = "done") {
798
+ if (entry.idleTimer) {
799
+ clearTimeout(entry.idleTimer);
800
+ entry.idleTimer = void 0;
801
+ }
802
+ closeWebSocketSilently(entry.socket, reason);
803
+ entry.steeringContinuation?.steering.close(/* @__PURE__ */ new Error("Responses steering connection closed"));
804
+ entry.steeringContinuation?.iterator.return?.().catch(() => void 0);
805
+ entry.steeringContinuation = void 0;
806
+ if (websocketSessionCache.get(cacheKey) === entry) websocketSessionCache.delete(cacheKey);
807
+ }
808
+ function scheduleSessionWebSocketExpiry(cacheKey, entry) {
809
+ if (entry.idleTimer) clearTimeout(entry.idleTimer);
810
+ entry.idleTimer = setTimeout(() => {
811
+ if (entry.busy) return;
812
+ invalidateOwnedWebSocketSession(cacheKey, entry, "idle_timeout");
813
+ }, SESSION_WEBSOCKET_CACHE_TTL_MS);
814
+ entry.idleTimer.unref?.();
815
+ }
816
+ function prepareWebSocketConnection(client, headers) {
817
+ if (!isOfficialOpenAIResponsesBaseUrl(client.baseURL)) throw new Error("OpenAI Responses WebSocket requires the official API endpoint");
818
+ if (typeof client.apiKey !== "string" || client.apiKey.length === 0) throw new Error("OpenAI Responses WebSocket requires an API key");
819
+ const resolvedApiKey = getAiTransportHost().resolveSecretSentinel(client.apiKey);
820
+ const resolvedHeaders = { ...resolveAiTransportHeaderSentinels(headers) };
821
+ for (const key of Object.keys(resolvedHeaders)) {
822
+ const normalizedKey = key.toLowerCase();
823
+ if (normalizedKey === "authorization" || normalizedKey === "traceparent") delete resolvedHeaders[key];
824
+ }
825
+ if (!resolvedApiKey) throw new Error("OpenAI Responses WebSocket requires a resolved API key");
826
+ return {
827
+ client: client.withOptions({ apiKey: resolvedApiKey }),
828
+ headers: resolvedHeaders,
829
+ identity: sha256Hex(JSON.stringify([
830
+ resolvedApiKey,
831
+ client.baseURL,
832
+ Object.entries(resolvedHeaders).toSorted(([a], [b]) => a.localeCompare(b))
833
+ ]))
834
+ };
835
+ }
836
+ function createWebSocket(connection, onError) {
837
+ const socket = new ResponsesWS(connection.client, {
838
+ headers: connection.headers,
839
+ maxQueueSize: 1
840
+ });
841
+ socket.on("error", () => onError(socket));
842
+ return socket;
843
+ }
844
+ function createTransientWebSocketLease(connection) {
845
+ const socket = createWebSocket(connection, (failedSocket) => closeWebSocketSilently(failedSocket, "transport_error"));
846
+ return {
847
+ socket,
848
+ iterator: socket.stream(),
849
+ reusedConnection: false,
850
+ release: () => closeWebSocketSilently(socket)
851
+ };
852
+ }
853
+ function createCachedWebSocketLease(cacheKey, entry, reusedConnection) {
854
+ entry.busy = true;
855
+ const steeringContinuation = entry.steeringContinuation;
856
+ entry.steeringContinuation = void 0;
857
+ return {
858
+ socket: entry.socket,
859
+ iterator: steeringContinuation?.iterator ?? entry.socket.stream(),
860
+ entry,
861
+ reusedConnection,
862
+ steeringContinuation,
863
+ release: ({ keep } = {}) => {
864
+ if (!keep || entry.socket.socket.readyState !== WEBSOCKET_OPEN_STATE) {
865
+ invalidateOwnedWebSocketSession(cacheKey, entry);
866
+ return;
867
+ }
868
+ entry.busy = false;
869
+ scheduleSessionWebSocketExpiry(cacheKey, entry);
870
+ }
871
+ };
872
+ }
873
+ function acquireWebSocket(params, connection) {
874
+ if (!(params.mode !== "websocket" && Boolean(params.sessionId)) || !params.sessionId) return createTransientWebSocketLease(connection);
875
+ const cacheKey = `${params.sessionId}\0${connection.identity}`;
876
+ const cached = websocketSessionCache.get(cacheKey);
877
+ if (cached) {
878
+ if (cached.idleTimer) {
879
+ clearTimeout(cached.idleTimer);
880
+ cached.idleTimer = void 0;
881
+ }
882
+ if (cached.busy) return createTransientWebSocketLease(connection);
883
+ const expired = Date.now() - cached.createdAt >= SESSION_WEBSOCKET_MAX_AGE_MS;
884
+ if (!expired && cached.socket.socket.readyState === WEBSOCKET_OPEN_STATE) return createCachedWebSocketLease(cacheKey, cached, true);
885
+ invalidateOwnedWebSocketSession(cacheKey, cached, expired ? "connection_age_limit" : "done");
886
+ }
887
+ const entry = {
888
+ socket: createWebSocket(connection, (failedSocket) => {
889
+ const failedEntry = websocketSessionCache.get(cacheKey);
890
+ if (failedEntry?.socket === failedSocket) invalidateOwnedWebSocketSession(cacheKey, failedEntry, "transport_error");
891
+ else closeWebSocketSilently(failedSocket, "transport_error");
892
+ }),
893
+ sessionId: params.sessionId,
894
+ busy: true,
895
+ createdAt: Date.now()
896
+ };
897
+ websocketSessionCache.set(cacheKey, entry);
898
+ return createCachedWebSocketLease(cacheKey, entry, false);
899
+ }
900
+ function sanitizeWebSocketRequest(request) {
901
+ const { stream: _stream, background: _background, ...websocketRequest } = request;
902
+ return websocketRequest;
903
+ }
904
+ async function nextWebSocketMessage(iterator, signal) {
905
+ if (!signal) return iterator.next();
906
+ if (signal.aborted) throw transportAbortError(signal);
907
+ let onAbort;
908
+ try {
909
+ return await Promise.race([iterator.next(), new Promise((_resolve, reject) => {
910
+ onAbort = () => reject(transportAbortError(signal));
911
+ signal.addEventListener("abort", onAbort, { once: true });
912
+ })]);
913
+ } finally {
914
+ if (onAbort) signal.removeEventListener("abort", onAbort);
915
+ }
916
+ }
917
+ function readServerEvent(message) {
918
+ if (message.type === "message") return message.message;
919
+ if (message.type === "error") throw parseOpenAIResponsesWebSocketServerError(message.error) ?? new Error("OpenAI Responses WebSocket transport failed", { cause: message.error });
920
+ if (message.type === "close") throw new Error(`OpenAI Responses WebSocket closed before completion (code ${message.code})`);
921
+ }
922
+ function createOpenAIResponsesWebSocketStream(params) {
923
+ const connection = prepareWebSocketConnection(params.client, params.headers);
924
+ let fullRequest = sanitizeWebSocketRequest(params.request);
925
+ const requestModel = typeof fullRequest.model === "string" ? fullRequest.model : "";
926
+ const degradationKey = `${params.sessionId ?? ""}\0${connection.identity}\0${requestModel}`;
927
+ const degraded = degradedWebSocketConnections.get(degradationKey);
928
+ if (degraded && degraded.retryAt > Date.now()) throw new OpenAIResponsesWebSocketPreDispatchError(/* @__PURE__ */ new Error("OpenAI Responses WebSocket is cooling down after a transport failure"));
929
+ degradedWebSocketConnections.delete(degradationKey);
930
+ const markDegraded = () => {
931
+ const cooldownMs = params.degradeCooldownMs;
932
+ if (cooldownMs === void 0 || !Number.isFinite(cooldownMs) || cooldownMs <= 0) return;
933
+ degradedWebSocketConnections.set(degradationKey, {
934
+ sessionId: params.sessionId,
935
+ retryAt: Date.now() + cooldownMs
936
+ });
937
+ };
938
+ let lease;
939
+ try {
940
+ lease = acquireWebSocket(params, connection);
941
+ } catch (error) {
942
+ markDegraded();
943
+ throw new OpenAIResponsesWebSocketPreDispatchError(error);
944
+ }
945
+ let prepared;
946
+ let previousContinuation;
947
+ const resumedSteering = lease.steeringContinuation;
948
+ const steeringMode = resumedSteering ? resumedSteering.requiresInput ? "required-input" : "automatic" : void 0;
949
+ try {
950
+ const continuation = previousContinuation = lease.entry?.continuation;
951
+ if (continuation && lease.entry) {
952
+ lease.entry.continuation = void 0;
953
+ prepared = resolveResponsesContinuationRequest(continuation, fullRequest, steeringMode);
954
+ } else {
955
+ fullRequest = params.restoreRequest?.(fullRequest) ?? fullRequest;
956
+ prepared = {
957
+ request: fullRequest,
958
+ continuationStatus: lease.entry ? "no_previous_response" : "socket_not_cached"
959
+ };
960
+ }
961
+ } catch (error) {
962
+ lease.iterator.return?.().catch(() => void 0);
963
+ lease.release({ keep: false });
964
+ throw error;
965
+ }
966
+ let streamStarted = false;
967
+ let terminalResponse;
968
+ let terminalReceived = false;
969
+ let released = false;
970
+ let retainIterator = false;
971
+ let deferredInput = false;
972
+ let inputReplay;
973
+ if (resumedSteering) try {
974
+ if (prepared.continuationStatus !== "continued" || !prepared.request.previous_response_id) throw new Error("Responses steering continuation changed its request or history");
975
+ const input = omitAcceptedSteering(prepared.request.input ?? [], resumedSteering.acceptedInput);
976
+ if (!resumedSteering.requiresInput && (stableStringify(fullRequest.instructions) !== stableStringify(resumedSteering.instructions) || stableStringify(fullRequest.tools) !== stableStringify(resumedSteering.tools))) throw new Error("Responses automatic steering continuation cannot change instructions or tools");
977
+ const baseline = prepared.fullRequest ?? fullRequest;
978
+ const priorLength = (baseline.input?.length ?? 0) - (prepared.request.input?.length ?? 0);
979
+ const fingerprints = input.map(responsesInputFingerprint);
980
+ if (fingerprints.length > 0) inputReplay = {
981
+ afterResponseId: prepared.request.previous_response_id,
982
+ before: resumedSteering.requiresInput ? fingerprints : [],
983
+ after: resumedSteering.requiresInput ? [] : fingerprints
984
+ };
985
+ const delivered = resumedSteering.requiresInput ? input : [];
986
+ prepared.fullRequest = {
987
+ ...baseline,
988
+ input: [
989
+ ...(baseline.input ?? []).slice(0, priorLength),
990
+ ...resumedSteering.acceptedInput,
991
+ ...delivered
992
+ ]
993
+ };
994
+ prepared.request = {
995
+ ...prepared.request,
996
+ input: delivered
997
+ };
998
+ deferredInput = !resumedSteering.requiresInput && input.length > 0;
999
+ } catch (error) {
1000
+ lease.iterator.return?.().catch(() => void 0);
1001
+ lease.release({ keep: false });
1002
+ throw new OpenAIResponsesWebSocketPostDispatchError(error);
1003
+ }
1004
+ const steering = lease.entry && params.onActiveResponse && params.steeringInput ? createResponsesSteering({
1005
+ onActiveResponse: params.onActiveResponse,
1006
+ toInput: params.steeringInput,
1007
+ send: (event) => lease.socket.sendRaw(JSON.stringify(event)),
1008
+ needsContinuation: () => deferredInput,
1009
+ assertActive: () => {
1010
+ if (released || terminalReceived || params.signal?.aborted) throw new Error("Responses steering is no longer active");
1011
+ }
1012
+ }) : void 0;
1013
+ const finish = ({ keep = true } = {}) => {
1014
+ if (released) return;
1015
+ released = true;
1016
+ if (keep && lease.entry && terminalResponse) lease.entry.continuation = recordResponsesContinuationState(previousContinuation, prepared.fullRequest ?? fullRequest, terminalResponse, previousContinuation?.lastResponseId === prepared.request.previous_response_id);
1017
+ lease.release({ keep });
1018
+ };
1019
+ return {
1020
+ stream: { async *[Symbol.asyncIterator]() {
1021
+ if (streamStarted) throw new Error("OpenAI Responses WebSocket stream can only be consumed once");
1022
+ streamStarted = true;
1023
+ const iterator = lease.iterator;
1024
+ let requestDispatched = false;
1025
+ try {
1026
+ if (params.signal?.aborted) throw transportAbortError(params.signal);
1027
+ if (resumedSteering) {
1028
+ requestDispatched = true;
1029
+ if (resumedSteering.requiresInput) lease.socket.send({
1030
+ ...prepared.request,
1031
+ type: "response.create"
1032
+ });
1033
+ }
1034
+ for (;;) {
1035
+ const buffered = resumedSteering?.buffered.shift();
1036
+ const next = buffered ? {
1037
+ value: buffered,
1038
+ done: false
1039
+ } : await nextWebSocketMessage(iterator, params.signal);
1040
+ if (next.done) throw new Error("OpenAI Responses WebSocket closed before a terminal response event");
1041
+ if (next.value.type === "open") {
1042
+ if (!requestDispatched) {
1043
+ requestDispatched = true;
1044
+ lease.socket.send({
1045
+ ...prepared.request,
1046
+ type: "response.create"
1047
+ });
1048
+ }
1049
+ continue;
1050
+ }
1051
+ const event = readServerEvent(next.value);
1052
+ if (!event) continue;
1053
+ if (isRecord(event) && typeof event.type === "string" && event.type.startsWith("response.steer.")) {
1054
+ if ((isRecord(event.steer) && event.steer.previous_response_id === steering?.responseId ? steering : resumedSteering?.steering ?? steering)?.handle(event)) continue;
1055
+ }
1056
+ steering?.handle(event);
1057
+ if (event.type === "response.completed") terminalResponse = event.response;
1058
+ terminalReceived = event.type === "response.completed" || event.type === "response.incomplete" || event.type === "response.failed";
1059
+ if (terminalReceived) {
1060
+ steering?.seal();
1061
+ if (event.type === "response.failed") steering?.close(/* @__PURE__ */ new Error("Responses failed before steering could be applied"));
1062
+ const continuationBuffer = [];
1063
+ for (;;) {
1064
+ if (!steering?.pending) break;
1065
+ const acknowledgement = await nextWebSocketMessage(iterator, params.signal);
1066
+ if (acknowledgement.done) throw new Error("Responses closed before acknowledging steering");
1067
+ const acknowledgedEvent = readServerEvent(acknowledgement.value);
1068
+ if (!steering.handle(acknowledgedEvent)) continuationBuffer.push(acknowledgement.value);
1069
+ }
1070
+ const acceptedInput = steering?.acceptedInput ?? [];
1071
+ const isSteered = event.type === "response.incomplete" && isRecord(event.response.incomplete_details) && event.response.incomplete_details.reason === "steered";
1072
+ if (lease.entry && steering && acceptedInput.length > 0 && (event.type === "response.completed" || isSteered)) {
1073
+ if (event.type === "response.incomplete") terminalResponse = event.response;
1074
+ lease.entry.steeringContinuation = {
1075
+ iterator,
1076
+ buffered: continuationBuffer,
1077
+ acceptedInput,
1078
+ requiresInput: (terminalResponse?.output ?? []).some((item) => (item.type === "function_call" || item.type === "custom_tool_call") && !(isRecord(item) && item.async === true) || item.type === "mcp_approval_request"),
1079
+ steering,
1080
+ instructions: fullRequest.instructions,
1081
+ tools: fullRequest.tools
1082
+ };
1083
+ retainIterator = true;
1084
+ if (isSteered) {
1085
+ yield {
1086
+ ...event,
1087
+ type: "response.completed",
1088
+ response: {
1089
+ ...event.response,
1090
+ status: "completed",
1091
+ incomplete_details: null
1092
+ }
1093
+ };
1094
+ return;
1095
+ }
1096
+ }
1097
+ }
1098
+ yield event;
1099
+ if (terminalReceived) {
1100
+ degradedWebSocketConnections.delete(degradationKey);
1101
+ return;
1102
+ }
1103
+ }
1104
+ } catch (error) {
1105
+ const steeringDispatched = Boolean(steering?.pending || steering?.acceptedInput.length || resumedSteering);
1106
+ steering?.close(error instanceof Error ? error : /* @__PURE__ */ new Error("Responses steering failed"));
1107
+ if (lease.entry) lease.entry.continuation = void 0;
1108
+ const safeRetry = error instanceof OpenAIResponsesWebSocketSafeRetryError && !steeringDispatched;
1109
+ if (!params.callerSignal?.aborted && !safeRetry) markDegraded();
1110
+ if (!requestDispatched && !params.signal?.aborted) throw new OpenAIResponsesWebSocketPreDispatchError(error);
1111
+ if (!requestDispatched || params.callerSignal?.aborted || safeRetry) throw error;
1112
+ throw new OpenAIResponsesWebSocketPostDispatchError(error);
1113
+ } finally {
1114
+ steering?.seal();
1115
+ if (!retainIterator) {
1116
+ steering?.close(/* @__PURE__ */ new Error("Responses stream ended before steering was confirmed"));
1117
+ await iterator.return?.().catch(() => void 0);
1118
+ }
1119
+ if (!terminalReceived) finish({ keep: false });
1120
+ }
1121
+ } },
1122
+ request: prepared.request,
1123
+ fullRequest: prepared.fullRequest ?? fullRequest,
1124
+ reusedConnection: lease.reusedConnection,
1125
+ continuationStatus: prepared.continuationStatus,
1126
+ inputReplay,
1127
+ finish
1128
+ };
1129
+ }
1130
+ function closeOpenAIResponsesWebSocketSessions(sessionId) {
1131
+ for (const [cacheKey, entry] of websocketSessionCache) {
1132
+ if (sessionId && entry.sessionId !== sessionId) continue;
1133
+ invalidateOwnedWebSocketSession(cacheKey, entry, "session_cleanup");
1134
+ }
1135
+ for (const [key, entry] of degradedWebSocketConnections) if (!sessionId || entry.sessionId === sessionId) degradedWebSocketConnections.delete(key);
1136
+ }
1137
+ registerSessionResourceCleanup(closeOpenAIResponsesWebSocketSessions);
1138
+ //#endregion
652
1139
  //#region packages/ai/src/transports/openai-responses-replay-messages-internal.ts
653
1140
  function stripEncryptedReasoningContentFields(value) {
654
1141
  if (!value || typeof value !== "object") return {
@@ -803,6 +1290,9 @@ function buildResponsesInputMessage(role, content, source) {
803
1290
  ...identity ? { [responsesInputSource]: identity } : {}
804
1291
  };
805
1292
  }
1293
+ function resolveResponsesInstructionRole(model) {
1294
+ return model.reasoning && model.compat?.supportsDeveloperRole !== false ? "developer" : "system";
1295
+ }
806
1296
  function convertResponsesMessagesWithStyle(model, context, allowedToolCallProviders, options, conversionStyle) {
807
1297
  const messages = [];
808
1298
  const providerStyle = conversionStyle === "provider";
@@ -851,7 +1341,7 @@ function convertResponsesMessagesWithStyle(model, context, allowedToolCallProvid
851
1341
  preserveUnframedToolResults: replayPlan.preserveUnframedToolResults
852
1342
  });
853
1343
  const transformedMessages = orderResponsesAsyncToolResults(transformMessages(replayPlan.messages));
854
- if ((options?.includeSystemPrompt ?? true) && context.systemPrompt) messages.push(buildResponsesInputMessage(model.reasoning && model.compat?.supportsDeveloperRole !== false ? "developer" : "system", [{
1344
+ if ((options?.includeSystemPrompt ?? true) && context.systemPrompt) messages.push(buildResponsesInputMessage(resolveResponsesInstructionRole(model), [{
855
1345
  type: "input_text",
856
1346
  text: sanitizeTransportPayloadText(stripSystemPromptCacheBoundary(context.systemPrompt))
857
1347
  }]));
@@ -873,13 +1363,15 @@ function convertResponsesMessagesWithStyle(model, context, allowedToolCallProvid
873
1363
  }
874
1364
  let msgIndex = 0;
875
1365
  const appendAssistant = createResponsesInputReplay(model);
1366
+ const inHistorySystemUpdates = supportsNativeOpenAIResponsesEndpoint(model);
876
1367
  for (const msg of replayMessages) {
877
1368
  if (!("role" in msg)) {
878
1369
  messages.push(msg);
879
1370
  continue;
880
1371
  }
881
1372
  if (msg.role === "user") {
882
- if (typeof msg.content === "string") messages.push(buildResponsesInputMessage("user", [{
1373
+ const role = msg.runtimeContextCarrier === true && inHistorySystemUpdates ? resolveResponsesInstructionRole(model) : "user";
1374
+ if (typeof msg.content === "string") messages.push(buildResponsesInputMessage(role, [{
883
1375
  type: "input_text",
884
1376
  text: sanitizeTransportPayloadText(msg.content)
885
1377
  }], msg));
@@ -892,7 +1384,7 @@ function convertResponsesMessagesWithStyle(model, context, allowedToolCallProvid
892
1384
  detail: "auto",
893
1385
  image_url: `data:${item.mimeType};base64,${item.data}`
894
1386
  }).filter((item) => providerStyle || model.input.includes("image") || item.type !== "input_image");
895
- if (content.length > 0) messages.push(buildResponsesInputMessage("user", content, msg));
1387
+ if (content.length > 0) messages.push(buildResponsesInputMessage(role, content, msg));
896
1388
  else if (providerStyle) continue;
897
1389
  }
898
1390
  } else if (msg.role === "assistant") {
@@ -1704,7 +2196,7 @@ function createResponsesOutputTracker(params) {
1704
2196
  trackStream(sink) {
1705
2197
  return { push(event) {
1706
2198
  visibleTextEmitted ||= event.type === "text_delta" && event.delta.length > 0 || event.type === "text_end" && event.content.length > 0;
1707
- sink.push(event);
2199
+ sink.push(detachPartialSnapshot(event));
1708
2200
  } };
1709
2201
  },
1710
2202
  observeEvent(event) {
@@ -2565,7 +3057,7 @@ function applyResponsesServiceTierPricing(usage, serviceTier, model) {
2565
3057
  usage.cost.total = usage.cost.input + usage.cost.output + usage.cost.cacheRead + usage.cost.cacheWrite;
2566
3058
  }
2567
3059
  function applyCommonResponsesParams(params, model, context, options, config) {
2568
- if (options?.maxTokens) params.max_output_tokens = Math.max(options.maxTokens, 16);
3060
+ if (options?.maxTokens) params.max_output_tokens = clampMaxTokensToModel(model, Math.max(options.maxTokens, 16));
2569
3061
  if (options?.temperature !== void 0 && supportsOpenAITemperature(model)) params.temperature = options.temperature;
2570
3062
  if (context.tools) {
2571
3063
  const tools = convertResponsesToolPayload(context.tools, { model });
@@ -2686,4 +3178,4 @@ async function runResponsesStreamLifecycle(params) {
2686
3178
  }
2687
3179
  }
2688
3180
  //#endregion
2689
- export { OPENAI_RESPONSES_FUNCTION_CALL_ITEM_ID_RE as $, recordResponsesContextUsage as A, resolveResponsesContinuationRequest as B, resolveAzureOpenAIApiVersion as C, safeDebugValue as D, logResponsesFailedNoDetails as E, convertResponsesMessages as F, supportsResponsesReasoningUpdate as G, responsesContinuationRequestFingerprint as H, recordResponsesInputReplay as I, captureOpenAIResponsesCompaction as J, CompactionReplayRefreshRequiredError as K, responsesInputFingerprint as L, bindResponsesInputMessage as M, buildResponsesInputMessage as N, summarizeOpenAITransportError as O, convertProviderResponsesMessages as P, OPENAI_RESPONSES_CALL_ID_RE as Q, claimOpenAIResponsesHttpContinuation as R, isInvalidEncryptedContentError as S, ResponsesStreamFailure as T, isConfigurationUpdate as U, responsesContinuationPrefixFingerprint as V, replayResponsesReasoningUpdates as W, resolveNewestOpenAIResponsesCompactionReplay as X, isOpenAIResponsesReplayContext as Y, suppressOpenAIResponsesCompaction as Z, isResponsesTextContentPartType as _, prepareResponsesTools as a, commitResponsesEncryptedContentAttempt as b, observeResponsesStream as c, AZURE_RESPONSES_TEXT_CONTENT_PART_TYPE as d, OPENAI_RESPONSES_ID_MAX_LENGTH as et, AZURE_RESPONSES_TEXT_DELTA_EVENT_TYPE as f, isAzureResponsesTextDeltaEventType as g, isAzureResponsesTextDeltaEvent as h, convertResponsesToolPayload as i, resolveReplayableResponsesMessageId as it, resolveResponsesContextUsageBoundary as j, summarizeResponsesPayload as k, createResponsesToolCallTracker as l, OPENAI_RESPONSES_OUTPUT_TEXT_DELTA_EVENT_TYPE as m, applyResponsesServiceTierPricing as n, shouldNormalizeOpenAIResponsesToolCallId as nt, readOpenAIMisalignmentReview as o, OPENAI_RESPONSES_OUTPUT_TEXT_CONTENT_PART_TYPE as p, buildOpenAIResponsesReasoningReplayMetadata as q, runResponsesStreamLifecycle as r, splitOpenAIFunctionCallPairing as rt, processResponsesStream as s, applyCommonResponsesParams as t, normalizeOpenAIResponsesFunctionCallId as tt, readResponsesToolCallItemIdentity as u, isResponsesTextDeltaEventType as v, resolveNextResponsesEncryptedContentAttempt as w, createResponsesStreamWithEncryptedContentRetry as x, resolveResponsesMessageSnapshotCollapse as y, recordResponsesContinuationState as z };
3181
+ export { OPENAI_RESPONSES_FUNCTION_CALL_ITEM_ID_RE as $, recordResponsesContextUsage as A, claimOpenAIResponsesHttpContinuation as B, resolveAzureOpenAIApiVersion as C, safeDebugValue as D, logResponsesFailedNoDetails as E, convertResponsesMessages as F, supportsResponsesReasoningUpdate as G, responsesContinuationRequestFingerprint as H, createOpenAIResponsesWebSocketStream as I, captureOpenAIResponsesCompaction as J, CompactionReplayRefreshRequiredError as K, supportsNativeOpenAIResponsesEndpoint as L, bindResponsesInputMessage as M, buildResponsesInputMessage as N, summarizeOpenAITransportError as O, convertProviderResponsesMessages as P, OPENAI_RESPONSES_CALL_ID_RE as Q, projectResponsesSteeringInput as R, isInvalidEncryptedContentError as S, ResponsesStreamFailure as T, isConfigurationUpdate as U, responsesContinuationPrefixFingerprint as V, replayResponsesReasoningUpdates as W, resolveNewestOpenAIResponsesCompactionReplay as X, isOpenAIResponsesReplayContext as Y, suppressOpenAIResponsesCompaction as Z, isResponsesTextContentPartType as _, prepareResponsesTools as a, commitResponsesEncryptedContentAttempt as b, observeResponsesStream as c, AZURE_RESPONSES_TEXT_CONTENT_PART_TYPE as d, OPENAI_RESPONSES_ID_MAX_LENGTH as et, AZURE_RESPONSES_TEXT_DELTA_EVENT_TYPE as f, isAzureResponsesTextDeltaEventType as g, isAzureResponsesTextDeltaEvent as h, convertResponsesToolPayload as i, resolveReplayableResponsesMessageId as it, resolveResponsesContextUsageBoundary as j, summarizeResponsesPayload as k, createResponsesToolCallTracker as l, OPENAI_RESPONSES_OUTPUT_TEXT_DELTA_EVENT_TYPE as m, applyResponsesServiceTierPricing as n, shouldNormalizeOpenAIResponsesToolCallId as nt, readOpenAIMisalignmentReview as o, OPENAI_RESPONSES_OUTPUT_TEXT_CONTENT_PART_TYPE as p, buildOpenAIResponsesReasoningReplayMetadata as q, runResponsesStreamLifecycle as r, splitOpenAIFunctionCallPairing as rt, processResponsesStream as s, applyCommonResponsesParams as t, normalizeOpenAIResponsesFunctionCallId as tt, readResponsesToolCallItemIdentity as u, isResponsesTextDeltaEventType as v, resolveNextResponsesEncryptedContentAttempt as w, createResponsesStreamWithEncryptedContentRetry as x, resolveResponsesMessageSnapshotCollapse as y, recordResponsesInputReplay as z };
@@ -1,5 +1,5 @@
1
- import { K as StopReason, a as AssistantMessage, ot as Usage } from "./types-CLLR3eB4.mjs";
2
- import "./types-CoL-LJ7j.mjs";
1
+ import { K as StopReason, a as AssistantMessage, ot as Usage } from "./types-B89w6ly2.mjs";
2
+ import "./types-DbmNRKvK.mjs";
3
3
  import OpenAI from "openai";
4
4
  //#region packages/ai/src/providers/openai-responses-terminal-usage.d.ts
5
5
  /** A known output ceiling can resume sampling after admitted tools have settled. */
@@ -982,6 +982,21 @@ function normalizeOpenAIStrictCompatSchemaRecursive(schema, promoteEmptyObject =
982
982
  function normalizeOpenAIStrictCompatSchema(schema) {
983
983
  return normalizeOpenAIStrictCompatSchemaRecursive(schema, true);
984
984
  }
985
+ /**
986
+ * Returns whether a regex pattern contains a lookahead or lookbehind group.
987
+ * Escaped parentheses, character classes, and named groups are not lookarounds.
988
+ */
989
+ function hasRegexLookaround(pattern) {
990
+ let inCharacterClass = false;
991
+ for (let index = 0; index < pattern.length; index += 1) {
992
+ const char = pattern[index];
993
+ if (char === "\\") index += 1;
994
+ else if (inCharacterClass) inCharacterClass = char !== "]";
995
+ else if (char === "[") inCharacterClass = true;
996
+ else if (char === "(" && /^\?<?[=!]/.test(pattern.slice(index + 1, index + 4))) return true;
997
+ }
998
+ return false;
999
+ }
985
1000
  /** Finds schema paths that violate OpenAI strict tool-schema requirements. */
986
1001
  function findOpenAIStrictSchemaViolations(schema, path, options) {
987
1002
  if (Array.isArray(schema)) {
@@ -997,6 +1012,7 @@ function findOpenAIStrictSchemaViolations(schema, path, options) {
997
1012
  "allOf"
998
1013
  ]) if (key in record) violations.push(`${path}.${key}`);
999
1014
  if (Array.isArray(record.type)) violations.push(`${path}.type`);
1015
+ if (typeof record.pattern === "string" && hasRegexLookaround(record.pattern)) violations.push(`${path}.pattern`);
1000
1016
  const properties = isRecord(record.properties) ? record.properties : void 0;
1001
1017
  if (record.type === "object") {
1002
1018
  if (record.additionalProperties !== false) violations.push(`${path}.additionalProperties`);
@@ -1082,7 +1098,8 @@ function normalizeOpenAIStrictToolParameters(schema, strict, modelCompat) {
1082
1098
  }
1083
1099
  /** Returns whether a schema already satisfies OpenAI strict tool-schema constraints. */
1084
1100
  function isStrictOpenAIJsonSchemaCompatible(schema) {
1085
- return isStrictOpenAIJsonSchemaCompatibleRecursive(normalizeStrictOpenAIJsonSchema(schema));
1101
+ const normalized = normalizeStrictOpenAIJsonSchema(schema);
1102
+ return isStrictOpenAIJsonSchemaCompatibleRecursive(normalized) && findOpenAIStrictSchemaViolations(normalized, "parameters").length === 0;
1086
1103
  }
1087
1104
  /** Returns strict-schema diagnostics for an already materialized OpenAI tool projection. */
1088
1105
  function findOpenAIStrictToolProjectionDiagnostics(projection) {