@yansigit/opencodex 2.31.3 → 2.33.0
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/README.md +2 -2
- package/bin/ocx.mjs +99 -70
- package/gui/dist/assets/index-DKLr4LTE.js +102 -0
- package/gui/dist/assets/index-DrSQdTRd.css +1 -0
- package/gui/dist/index.html +2 -2
- package/package.json +6 -5
- package/src/adapters/anthropic.ts +25 -13
- package/src/adapters/azure.ts +20 -4
- package/src/adapters/base.ts +5 -1
- package/src/adapters/command-code.ts +42 -10
- package/src/adapters/cursor/live-models.ts +8 -0
- package/src/adapters/cursor/live-transport.ts +1 -1
- package/src/adapters/cursor/native-exec-desktop.ts +16 -0
- package/src/adapters/cursor/protobuf-events.ts +158 -7
- package/src/adapters/cursor/protobuf-request.ts +33 -15
- package/src/adapters/cursor/request-builder.ts +4 -3
- package/src/adapters/cursor/tool-definitions.ts +27 -1
- package/src/adapters/cursor/types.ts +4 -3
- package/src/adapters/cursor.ts +9 -0
- package/src/adapters/google-antigravity-replay.ts +2 -2
- package/src/adapters/google-antigravity-wire.ts +7 -0
- package/src/adapters/google-errors.ts +6 -2
- package/src/adapters/google-http.ts +30 -7
- package/src/adapters/google-truncation.ts +5 -0
- package/src/adapters/google-wire-compiler.ts +38 -6
- package/src/adapters/google.ts +154 -33
- package/src/adapters/kiro-tools.ts +20 -9
- package/src/adapters/kiro.ts +0 -3
- package/src/adapters/openai-chat.ts +9 -0
- package/src/adapters/openai-responses.ts +4 -1
- package/src/adapters/tool-catalog-nudge.ts +1 -1
- package/src/adapters/xai-web-search.ts +7 -2
- package/src/bridge.ts +133 -24
- package/src/claude/context-windows.ts +16 -9
- package/src/cli/dispatch.ts +50 -2
- package/src/cli/doctor.ts +26 -13
- package/src/cli/help.ts +4 -3
- package/src/cli/index.ts +20 -6
- package/src/cli/models.ts +13 -3
- package/src/cli/observe.ts +20 -5
- package/src/cli/provider.ts +8 -1
- package/src/cli/registry.ts +7 -5
- package/src/cli/status.ts +25 -1
- package/src/cli/system-restart-client.ts +1 -1
- package/src/cli/usage-report.ts +134 -0
- package/src/codex/app-server-processes.ts +3 -1
- package/src/codex/auth-api.ts +4 -2
- package/src/codex/autostart-health.ts +16 -0
- package/src/codex/catalog/aggregation.ts +12 -0
- package/src/codex/catalog/effort.ts +42 -12
- package/src/codex/catalog/metadata.ts +27 -1
- package/src/codex/catalog/model-metadata.ts +593 -0
- package/src/codex/catalog/parsing.ts +71 -27
- package/src/codex/catalog/provider-fetch.ts +189 -33
- package/src/codex/catalog/sync.ts +6 -5
- package/src/codex/convergence.ts +5 -0
- package/src/codex/desktop-app-restart.ts +342 -0
- package/src/codex/history-job.ts +32 -3
- package/src/codex/history-manifest.ts +112 -0
- package/src/codex/history-migration-guardian.ts +5 -5
- package/src/codex/history-provider.ts +825 -247
- package/src/codex/history-worker.ts +8 -5
- package/src/codex/inject.ts +49 -21
- package/src/codex/injected-marker.ts +1 -1
- package/src/codex/internal/history-writer.ts +4 -3
- package/src/codex/native-profile-startup.ts +157 -27
- package/src/codex/native-residue.ts +26 -33
- package/src/codex/shim.ts +56 -3
- package/src/combos/failover.ts +27 -0
- package/src/compatibility/index.ts +26 -0
- package/src/compatibility/manifest.ts +253 -0
- package/src/compatibility/openai-responses.ts +81 -0
- package/src/config/atomic-write.ts +219 -0
- package/src/config/paths.ts +40 -0
- package/src/config/process-state.ts +308 -0
- package/src/config/provider-validation.ts +214 -0
- package/src/config.ts +153 -814
- package/src/generated/compatibility-version.json +232 -148
- package/src/images/loop.ts +37 -6
- package/src/images/plan.ts +5 -4
- package/src/integrations/ownership-policy.ts +141 -0
- package/src/integrations/ownership.ts +10 -0
- package/src/integrations/state.ts +44 -5
- package/src/integrations/writer.ts +6 -0
- package/src/lib/azure-identity.ts +154 -0
- package/src/lib/bounded-body.ts +14 -2
- package/src/lib/debug.ts +42 -0
- package/src/lib/errors.ts +14 -0
- package/src/lib/process-control.ts +2 -1
- package/src/lib/provider-outbound.ts +45 -33
- package/src/lib/provider-tls-profile.ts +309 -0
- package/src/lib/proxy-env.ts +49 -0
- package/src/lib/redact.ts +10 -1
- package/src/lib/state-store-registrations.ts +2 -0
- package/src/lib/tool-argument-integers.ts +56 -5
- package/src/oauth/antigravity-routing.ts +282 -236
- package/src/oauth/callback-server.ts +22 -2
- package/src/oauth/command-code.ts +5 -16
- package/src/oauth/google-antigravity.ts +42 -5
- package/src/oauth/health.ts +1 -1
- package/src/oauth/index.ts +15 -3
- package/src/oauth/kimi.ts +9 -1
- package/src/oauth/open-browser-choice.ts +26 -0
- package/src/oauth/store.ts +6 -0
- package/src/providers/antigravity-quota.ts +3 -1
- package/src/providers/api-keys.ts +2 -1
- package/src/providers/auto-compact-budget.ts +65 -0
- package/src/providers/derive.ts +4 -0
- package/src/providers/key-failover.ts +5 -1
- package/src/providers/openai-tiers.ts +5 -0
- package/src/providers/provider-id-rewrite.ts +1 -0
- package/src/providers/quota.ts +59 -13
- package/src/providers/registry.ts +4 -2
- package/src/providers/request-pacing.ts +33 -6
- package/src/providers/xai-transport.ts +21 -0
- package/src/reasoning-effort.ts +19 -2
- package/src/responses/apply-patch-envelope.ts +63 -0
- package/src/responses/custom-tool-compat.ts +132 -38
- package/src/responses/google-provider-options.ts +36 -0
- package/src/responses/namespace-tool-compat.ts +84 -4
- package/src/responses/parser.ts +14 -2
- package/src/responses/provider-opaque-metadata.ts +3 -3
- package/src/responses/reasoning-replay-cache.ts +81 -3
- package/src/responses/schema.ts +37 -0
- package/src/responses/state.ts +94 -4
- package/src/router.ts +8 -2
- package/src/server/auth-cors.ts +37 -7
- package/src/server/images.ts +19 -35
- package/src/server/index.ts +102 -21
- package/src/server/local-management-read-client.ts +1 -1
- package/src/server/local-provider-reload-client.ts +1 -1
- package/src/server/management/agent-settings-routes.ts +206 -16
- package/src/server/management/combo-routes.ts +6 -0
- package/src/server/management/config-routes.ts +35 -6
- package/src/server/management/context.ts +1 -1
- package/src/server/management/logs-usage-routes.ts +27 -6
- package/src/server/management/model-routes.ts +8 -4
- package/src/server/management/model-rows.ts +4 -0
- package/src/server/management/native-integration-routes.ts +2 -1
- package/src/server/management/oauth-account-routes.ts +25 -4
- package/src/server/management/provider-capability-config.ts +1 -1
- package/src/server/management/provider-routes.ts +113 -15
- package/src/server/management/routing-profile-routes.ts +3 -0
- package/src/server/management/system-restart.ts +1 -1
- package/src/server/port-reclaim.ts +1 -1
- package/src/server/proxy-liveness.ts +2 -1
- package/src/server/request-log-conversation.ts +30 -0
- package/src/server/request-log.ts +21 -0
- package/src/server/responses/agent-task-recovery.ts +1 -1
- package/src/server/responses/codex-auth-error.ts +55 -0
- package/src/server/responses/combo-stream-preflight.ts +171 -0
- package/src/server/responses/compact.ts +36 -22
- package/src/server/responses/core.ts +584 -247
- package/src/server/responses/empty-completion-guard.ts +35 -6
- package/src/server/responses/fetch-helpers.ts +20 -102
- package/src/server/responses/v2-native-parent-override.ts +59 -0
- package/src/server/responses/ws-upstream.ts +75 -2
- package/src/server/responses-custom-tool-repair.ts +41 -5
- package/src/server/responses-undeclared-tool-guard.ts +241 -18
- package/src/service.ts +9 -5
- package/src/types/config.ts +16 -1
- package/src/types/provider.ts +16 -0
- package/src/types/request.ts +34 -1
- package/src/types/tools.ts +114 -11
- package/src/types.ts +7 -1
- package/src/update/index.ts +5 -4
- package/src/update/job.ts +3 -1
- package/src/update/transactional-install.mjs +8 -1
- package/src/usage/log.ts +16 -8
- package/src/usage/summary.ts +201 -8
- package/src/vision/describe.ts +18 -13
- package/src/web-search/executor.ts +10 -3
- package/src/web-search/gemini-executor.ts +6 -4
- package/src/web-search/loop.ts +42 -6
- package/gui/dist/assets/index-CGoDO3uO.css +0 -1
- package/gui/dist/assets/index-Cxt5fZMP.js +0 -102
|
@@ -7,12 +7,14 @@ import {
|
|
|
7
7
|
resolvePublicAddresses,
|
|
8
8
|
} from "./destination-policy";
|
|
9
9
|
import { pinnedHttpGet, pinnedHttpPost } from "./pinned-http";
|
|
10
|
-
import { outboundProxyConfigured } from "./proxy-env";
|
|
10
|
+
import { noProxyMatches, outboundProxyConfigured, proxyForUrl } from "./proxy-env";
|
|
11
11
|
import { publicProviderBaseUrl } from "./provider-url";
|
|
12
|
+
import { antigravityOAuthDestinationConfigError, isCanonicalAntigravityUrl, providerTlsFetch } from "./provider-tls-profile";
|
|
13
|
+
import { waitForProviderRequestSlot } from "../providers/request-pacing";
|
|
12
14
|
|
|
13
15
|
type ProviderGetInit = Omit<RequestInit, "body" | "method" | "redirect">;
|
|
14
16
|
type ProviderPostInit = ProviderGetInit & { body: string };
|
|
15
|
-
type ProviderOutboundConfig = Pick<OcxProviderConfig, "baseUrl" | "allowPrivateNetwork"> & {
|
|
17
|
+
type ProviderOutboundConfig = Pick<OcxProviderConfig, "baseUrl" | "allowPrivateNetwork"> & Partial<Pick<OcxProviderConfig, "adapter" | "authMode" | "googleMode" | "tlsProfile">> & {
|
|
16
18
|
fetch?: typeof globalThis.fetch;
|
|
17
19
|
};
|
|
18
20
|
export interface ProviderOutboundDependencies {
|
|
@@ -40,36 +42,26 @@ function normalizeProxyHostname(hostname: string): string {
|
|
|
40
42
|
: normalized;
|
|
41
43
|
}
|
|
42
44
|
|
|
43
|
-
function
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
entryHost = bracketed[1]!;
|
|
58
|
-
entryPort = bracketed[2] ?? "";
|
|
59
|
-
} else if ((entry.match(/:/g)?.length ?? 0) === 1) {
|
|
60
|
-
const separator = entry.lastIndexOf(":");
|
|
61
|
-
const possiblePort = entry.slice(separator + 1);
|
|
62
|
-
if (/^\d+$/.test(possiblePort)) {
|
|
63
|
-
entryHost = entry.slice(0, separator);
|
|
64
|
-
entryPort = possiblePort;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
if (entryPort && entryPort !== port) continue;
|
|
68
|
-
entryHost = normalizeProxyHostname(entryHost.replace(/^\*?\./, ""));
|
|
69
|
-
if (!entryHost) continue;
|
|
70
|
-
if (hostname === entryHost || hostname.endsWith(`.${entryHost}`)) return true;
|
|
45
|
+
function antigravityProfileFetch(
|
|
46
|
+
name: string,
|
|
47
|
+
provider: ProviderOutboundConfig,
|
|
48
|
+
url: string,
|
|
49
|
+
): typeof globalThis.fetch | undefined {
|
|
50
|
+
if (name !== "google-antigravity"
|
|
51
|
+
|| provider.fetch
|
|
52
|
+
|| provider.tlsProfile !== "antigravity-browser"
|
|
53
|
+
|| provider.adapter !== "google"
|
|
54
|
+
|| provider.authMode !== "oauth"
|
|
55
|
+
|| provider.googleMode !== "cloud-code-assist"
|
|
56
|
+
|| !isCanonicalAntigravityUrl(provider.baseUrl)
|
|
57
|
+
|| !isCanonicalAntigravityUrl(url)) {
|
|
58
|
+
return undefined;
|
|
71
59
|
}
|
|
72
|
-
|
|
60
|
+
const nativeFetch = providerTlsFetch(name, provider as Parameters<typeof providerTlsFetch>[1], globalThis.fetch);
|
|
61
|
+
return (async (input: Parameters<typeof globalThis.fetch>[0], init?: RequestInit): Promise<Response> => {
|
|
62
|
+
await waitForProviderRequestSlot(name, provider as OcxProviderConfig, undefined, init?.signal ?? undefined);
|
|
63
|
+
return nativeFetch(input, init);
|
|
64
|
+
}) as typeof globalThis.fetch;
|
|
73
65
|
}
|
|
74
66
|
|
|
75
67
|
let proxyBoundaryWarned = false;
|
|
@@ -112,11 +104,31 @@ async function providerOutboundRequest(
|
|
|
112
104
|
init: ProviderGetInit | ProviderPostInit,
|
|
113
105
|
dependencies: ProviderOutboundDependencies = {},
|
|
114
106
|
): Promise<Response> {
|
|
107
|
+
const antigravityBaseError = antigravityOAuthDestinationConfigError(name, provider);
|
|
108
|
+
if (antigravityBaseError) throw new ProviderOutboundPolicyError(`provider ${name} ${antigravityBaseError}`);
|
|
109
|
+
if (name === "google-antigravity" && !isCanonicalAntigravityUrl(url)) {
|
|
110
|
+
throw new ProviderOutboundPolicyError("provider google-antigravity requires a canonical Antigravity HTTPS destination for OAuth");
|
|
111
|
+
}
|
|
115
112
|
const postUrl = method === "POST" ? new URL(url) : undefined;
|
|
116
113
|
if (postUrl?.protocol !== undefined && postUrl.protocol !== "https:") {
|
|
117
114
|
throw new ProviderOutboundPolicyError("provider POST URL must use HTTPS");
|
|
118
115
|
}
|
|
119
|
-
|
|
116
|
+
const profiledFetch = antigravityProfileFetch(name, provider, url);
|
|
117
|
+
const fetchOverride = provider.fetch ?? profiledFetch;
|
|
118
|
+
if (profiledFetch) {
|
|
119
|
+
// Keep the profiled executor behind the same pre-dispatch DNS boundary as
|
|
120
|
+
// the pinned path. A proxy owns peer selection, so match the existing
|
|
121
|
+
// providerOutbound proxy boundary and deliberately skip local resolution.
|
|
122
|
+
if (!proxyForUrl(url)) {
|
|
123
|
+
const resolveAddresses = dependencies.resolveAddresses ?? resolvePublicAddresses;
|
|
124
|
+
await resolveAddresses(url, {
|
|
125
|
+
context: "provider URL",
|
|
126
|
+
allowPrivateNetwork: false,
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
return fetchOverride!(url, { ...init, method, redirect: "manual" });
|
|
130
|
+
}
|
|
131
|
+
if (fetchOverride) {
|
|
120
132
|
// A caller-owned executor cannot be peer-pinned here. This branch keeps literal/config
|
|
121
133
|
// checks and redirect blocking, but does not provide the resolved-address guarantees of
|
|
122
134
|
// the built-in transport. Main-request migration must define that executor contract first.
|
|
@@ -135,7 +147,7 @@ async function providerOutboundRequest(
|
|
|
135
147
|
});
|
|
136
148
|
if (destinationError) throw new ProviderOutboundPolicyError(destinationError);
|
|
137
149
|
}
|
|
138
|
-
return
|
|
150
|
+
return fetchOverride(url, { ...init, method, redirect: "manual" });
|
|
139
151
|
}
|
|
140
152
|
const parsed = postUrl ?? new URL(url);
|
|
141
153
|
const proxyConfigured = configuredProxyFor();
|
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import type { OcxProviderConfig, ProviderTlsProfile } from "../types";
|
|
3
|
+
import { resolvePublicAddresses } from "./destination-policy";
|
|
4
|
+
import { registerOptionalShutdownHook } from "./optional-shutdown-hooks";
|
|
5
|
+
import { proxyForUrl } from "./proxy-env";
|
|
6
|
+
import { redactErrorMessage } from "./redact";
|
|
7
|
+
|
|
8
|
+
export const ANTIGRAVITY_TLS_HOSTS = new Set([
|
|
9
|
+
"daily-cloudcode-pa.googleapis.com",
|
|
10
|
+
"cloudcode-pa.googleapis.com",
|
|
11
|
+
]);
|
|
12
|
+
|
|
13
|
+
export type ProviderTlsProfileStatus = "disabled" | "active" | "fallback";
|
|
14
|
+
|
|
15
|
+
interface WreqTransport {
|
|
16
|
+
close(): Promise<void>;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
interface WreqModule {
|
|
20
|
+
createTransport(options: {
|
|
21
|
+
browser: "chrome_142";
|
|
22
|
+
os: "windows" | "macos" | "linux";
|
|
23
|
+
proxy?: string;
|
|
24
|
+
}): Promise<WreqTransport>;
|
|
25
|
+
fetch(input: string | URL | Request, init?: Record<string, unknown>): Promise<unknown>;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
interface InitializedTransport {
|
|
29
|
+
module: WreqModule;
|
|
30
|
+
transport: WreqTransport;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
type ProviderTlsContract = Pick<OcxProviderConfig, "adapter" | "authMode" | "googleMode" | "baseUrl" | "tlsProfile">;
|
|
34
|
+
|
|
35
|
+
type AntigravityOAuthDestinationContract = Pick<OcxProviderConfig, "baseUrl"> & Partial<Pick<OcxProviderConfig, "adapter" | "authMode" | "googleMode">>;
|
|
36
|
+
|
|
37
|
+
export interface ProviderTlsRuntimeForTest {
|
|
38
|
+
importWreq: () => Promise<WreqModule>;
|
|
39
|
+
resolveDestination?: typeof resolvePublicAddresses;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const defaultRuntime: ProviderTlsRuntimeForTest = {
|
|
43
|
+
importWreq: async () => await import("wreq-js") as unknown as WreqModule,
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
let runtime = defaultRuntime;
|
|
47
|
+
const statusByProvider = new Map<string, { status: ProviderTlsProfileStatus; fingerprint: string }>();
|
|
48
|
+
const transports = new Map<string, Promise<InitializedTransport | undefined>>();
|
|
49
|
+
let shutdownDetach: (() => void) | undefined;
|
|
50
|
+
let fallbackWarned = false;
|
|
51
|
+
|
|
52
|
+
function providerTlsContractFingerprint(providerName: string, provider: ProviderTlsContract): string {
|
|
53
|
+
return createHash("sha256").update(JSON.stringify([
|
|
54
|
+
providerName,
|
|
55
|
+
provider.adapter,
|
|
56
|
+
provider.authMode,
|
|
57
|
+
provider.googleMode,
|
|
58
|
+
provider.baseUrl,
|
|
59
|
+
provider.tlsProfile,
|
|
60
|
+
])).digest("hex");
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function setStatus(providerName: string, provider: ProviderTlsContract, status: ProviderTlsProfileStatus): void {
|
|
64
|
+
statusByProvider.set(providerName, { status, fingerprint: providerTlsContractFingerprint(providerName, provider) });
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const SAFE_NATIVE_ERROR_NAMES = new Set(["AbortError", "TimeoutError"]);
|
|
68
|
+
const SAFE_NATIVE_ERROR_CODES = new Set([
|
|
69
|
+
"ABORT_ERR", "ECONNREFUSED", "ECONNRESET", "EAI_AGAIN", "ENETUNREACH", "ENOTFOUND",
|
|
70
|
+
"ETIMEDOUT", "UND_ERR_CONNECT_TIMEOUT", "UND_ERR_SOCKET",
|
|
71
|
+
]);
|
|
72
|
+
|
|
73
|
+
function safeProviderTlsError(error: unknown): Error {
|
|
74
|
+
const raw = error instanceof Error ? error.message : String(error);
|
|
75
|
+
const safe = new Error(redactErrorMessage(raw));
|
|
76
|
+
if (!error || typeof error !== "object") return safe;
|
|
77
|
+
const native = error as { name?: unknown; code?: unknown };
|
|
78
|
+
if (typeof native.name === "string" && SAFE_NATIVE_ERROR_NAMES.has(native.name)) {
|
|
79
|
+
safe.name = native.name;
|
|
80
|
+
}
|
|
81
|
+
if (typeof native.code === "string" && SAFE_NATIVE_ERROR_CODES.has(native.code)) {
|
|
82
|
+
Object.defineProperty(safe, "code", {
|
|
83
|
+
configurable: true,
|
|
84
|
+
enumerable: false,
|
|
85
|
+
value: native.code,
|
|
86
|
+
writable: false,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
return safe;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function warnFallbackOnce(): void {
|
|
93
|
+
if (fallbackWarned) return;
|
|
94
|
+
fallbackWarned = true;
|
|
95
|
+
console.warn("[opencodex] Antigravity TLS profile requested → fallback to Bun fetch; native transport initialization failed");
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function emulationOs(): "windows" | "macos" | "linux" {
|
|
99
|
+
if (process.platform === "win32") return "windows";
|
|
100
|
+
if (process.platform === "darwin") return "macos";
|
|
101
|
+
if (process.platform === "linux") return "linux";
|
|
102
|
+
throw new Error("unsupported host operating system for Antigravity TLS profile");
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function profileIsSupported(providerName: string, provider: Pick<OcxProviderConfig, "adapter" | "authMode" | "googleMode" | "baseUrl" | "tlsProfile">): boolean {
|
|
106
|
+
return providerName === "google-antigravity"
|
|
107
|
+
&& provider.adapter === "google"
|
|
108
|
+
&& provider.authMode === "oauth"
|
|
109
|
+
&& provider.googleMode === "cloud-code-assist"
|
|
110
|
+
&& isCanonicalAntigravityUrl(provider.baseUrl)
|
|
111
|
+
&& provider.tlsProfile === "antigravity-browser";
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export function providerTlsProfileConfigError(
|
|
115
|
+
providerName: string,
|
|
116
|
+
provider: Pick<OcxProviderConfig, "adapter" | "authMode" | "googleMode" | "baseUrl" | "tlsProfile">,
|
|
117
|
+
): string | null {
|
|
118
|
+
if (provider.tlsProfile === undefined) return null;
|
|
119
|
+
if (provider.tlsProfile !== "antigravity-browser") {
|
|
120
|
+
return "tlsProfile must be antigravity-browser";
|
|
121
|
+
}
|
|
122
|
+
if (providerName !== "google-antigravity") return "tlsProfile antigravity-browser is valid only for google-antigravity";
|
|
123
|
+
if (provider.adapter !== "google" || provider.authMode !== "oauth") {
|
|
124
|
+
return "tlsProfile antigravity-browser requires Google OAuth authentication";
|
|
125
|
+
}
|
|
126
|
+
if (provider.googleMode !== "cloud-code-assist") {
|
|
127
|
+
return "tlsProfile antigravity-browser requires Google Cloud Code Assist mode";
|
|
128
|
+
}
|
|
129
|
+
if (!isCanonicalAntigravityUrl(provider.baseUrl)) {
|
|
130
|
+
return "tlsProfile antigravity-browser requires a canonical Antigravity HTTPS destination";
|
|
131
|
+
}
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export function isCanonicalAntigravityUrl(input: string | URL): boolean {
|
|
136
|
+
try {
|
|
137
|
+
const url = new URL(input);
|
|
138
|
+
return url.protocol === "https:"
|
|
139
|
+
&& (url.port === "" || url.port === "443")
|
|
140
|
+
&& url.username === ""
|
|
141
|
+
&& url.password === ""
|
|
142
|
+
&& ANTIGRAVITY_TLS_HOSTS.has(url.hostname.toLowerCase());
|
|
143
|
+
} catch {
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export function isAntigravityOAuthProvider(
|
|
149
|
+
providerName: string,
|
|
150
|
+
provider: Pick<OcxProviderConfig, "authMode">,
|
|
151
|
+
): boolean {
|
|
152
|
+
return providerName === "google-antigravity"
|
|
153
|
+
&& (provider.authMode === undefined || provider.authMode === "oauth");
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/** Same-name Antigravity rows are OAuth destinations even when legacy config omitted authMode. */
|
|
157
|
+
export function antigravityOAuthDestinationConfigError(
|
|
158
|
+
providerName: string,
|
|
159
|
+
provider: AntigravityOAuthDestinationContract,
|
|
160
|
+
): string | null {
|
|
161
|
+
if (providerName !== "google-antigravity") return null;
|
|
162
|
+
if (provider.googleMode !== undefined && provider.googleMode !== "cloud-code-assist") {
|
|
163
|
+
return "requires Google Cloud Code Assist mode for OAuth";
|
|
164
|
+
}
|
|
165
|
+
if (!isCanonicalAntigravityUrl(provider.baseUrl)) {
|
|
166
|
+
return "requires a canonical Antigravity HTTPS destination for OAuth";
|
|
167
|
+
}
|
|
168
|
+
return null;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export function getProviderTlsProfileStatus(providerName: string, provider: ProviderTlsContract): ProviderTlsProfileStatus {
|
|
172
|
+
const current = statusByProvider.get(providerName);
|
|
173
|
+
return current?.fingerprint === providerTlsContractFingerprint(providerName, provider)
|
|
174
|
+
? current.status
|
|
175
|
+
: "disabled";
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export function clearProviderTlsProfileStatus(providerName: string): void {
|
|
179
|
+
statusByProvider.delete(providerName);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function registerShutdownHook(): void {
|
|
183
|
+
if (shutdownDetach) return;
|
|
184
|
+
shutdownDetach = registerOptionalShutdownHook("provider-tls-profile", () => {
|
|
185
|
+
const pending = [...transports.values()];
|
|
186
|
+
transports.clear();
|
|
187
|
+
shutdownDetach = undefined;
|
|
188
|
+
for (const transport of pending) {
|
|
189
|
+
void transport.then(value => value?.transport.close()).catch(() => undefined);
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function transportKey(proxy: string | undefined): string {
|
|
195
|
+
return `${process.platform}:${proxy ?? "direct"}`;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
async function getTransport(proxy: string | undefined): Promise<InitializedTransport | undefined> {
|
|
199
|
+
const key = transportKey(proxy);
|
|
200
|
+
const existing = transports.get(key);
|
|
201
|
+
if (existing) return existing;
|
|
202
|
+
const pending = (async () => {
|
|
203
|
+
let transport: WreqTransport | undefined;
|
|
204
|
+
try {
|
|
205
|
+
const module = await runtime.importWreq();
|
|
206
|
+
transport = await module.createTransport({
|
|
207
|
+
browser: "chrome_142",
|
|
208
|
+
os: emulationOs(),
|
|
209
|
+
...(proxy ? { proxy } : {}),
|
|
210
|
+
});
|
|
211
|
+
registerShutdownHook();
|
|
212
|
+
return { module, transport };
|
|
213
|
+
} catch {
|
|
214
|
+
if (transport) await transport.close().catch(() => undefined);
|
|
215
|
+
return undefined;
|
|
216
|
+
}
|
|
217
|
+
})();
|
|
218
|
+
transports.set(key, pending);
|
|
219
|
+
return pending;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
export function providerTlsFetch(
|
|
223
|
+
providerName: string,
|
|
224
|
+
provider: Pick<OcxProviderConfig, "adapter" | "authMode" | "googleMode" | "baseUrl" | "tlsProfile">,
|
|
225
|
+
bunFetch: typeof globalThis.fetch,
|
|
226
|
+
): typeof globalThis.fetch {
|
|
227
|
+
const antigravityError = antigravityOAuthDestinationConfigError(providerName, provider);
|
|
228
|
+
if (antigravityError) {
|
|
229
|
+
return (async () => {
|
|
230
|
+
throw new Error(`provider ${providerName} ${antigravityError}`);
|
|
231
|
+
}) as unknown as typeof globalThis.fetch;
|
|
232
|
+
}
|
|
233
|
+
if (provider.tlsProfile === undefined) {
|
|
234
|
+
setStatus(providerName, provider, "disabled");
|
|
235
|
+
return bunFetch;
|
|
236
|
+
}
|
|
237
|
+
if (!profileIsSupported(providerName, provider)) {
|
|
238
|
+
setStatus(providerName, provider, "fallback");
|
|
239
|
+
warnFallbackOnce();
|
|
240
|
+
return bunFetch;
|
|
241
|
+
}
|
|
242
|
+
return (async (input: string | URL | Request, init?: RequestInit): Promise<Response> => {
|
|
243
|
+
const url = typeof input === "string" || input instanceof URL ? input : input.url;
|
|
244
|
+
if (!isCanonicalAntigravityUrl(url)) return bunFetch(input, init);
|
|
245
|
+
const proxy = proxyForUrl(url);
|
|
246
|
+
// A direct profiled request must pass the same public-destination gate as the
|
|
247
|
+
// ordinary provider executor before wreq can send its bearer-bearing request.
|
|
248
|
+
// Proxied requests deliberately leave peer selection to the configured proxy,
|
|
249
|
+
// matching providerOutbound's proxy boundary (and its inability to pin a peer).
|
|
250
|
+
if (!proxy) {
|
|
251
|
+
await (runtime.resolveDestination ?? resolvePublicAddresses)(String(url), {
|
|
252
|
+
context: "Antigravity profile URL",
|
|
253
|
+
allowPrivateNetwork: false,
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
const initialized = await getTransport(proxy);
|
|
257
|
+
if (!initialized) {
|
|
258
|
+
setStatus(providerName, provider, "fallback");
|
|
259
|
+
warnFallbackOnce();
|
|
260
|
+
return bunFetch(input, { ...init, redirect: "manual" });
|
|
261
|
+
}
|
|
262
|
+
setStatus(providerName, provider, "active");
|
|
263
|
+
const wreqInit = {
|
|
264
|
+
...init,
|
|
265
|
+
transport: initialized.transport,
|
|
266
|
+
disableDefaultHeaders: true,
|
|
267
|
+
cookieMode: "ephemeral" as const,
|
|
268
|
+
redirect: "manual" as const,
|
|
269
|
+
};
|
|
270
|
+
try {
|
|
271
|
+
return await initialized.module.fetch(input, wreqInit) as Response;
|
|
272
|
+
} catch (error) {
|
|
273
|
+
// Native errors are post-dispatch failures. Redact at this boundary and
|
|
274
|
+
// never replay through Bun, since generation may already have started.
|
|
275
|
+
throw safeProviderTlsError(error);
|
|
276
|
+
}
|
|
277
|
+
}) as typeof globalThis.fetch;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
export function setProviderTlsRuntimeForTest(next: ProviderTlsRuntimeForTest | undefined): void {
|
|
281
|
+
if (next === undefined) {
|
|
282
|
+
runtime = defaultRuntime;
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
// Test callers normally replace only the native module. Keep those tests
|
|
286
|
+
// deterministic in offline sandboxes while allowing explicit DNS policy
|
|
287
|
+
// regressions to inject a rejecting resolver.
|
|
288
|
+
runtime = {
|
|
289
|
+
...defaultRuntime,
|
|
290
|
+
resolveDestination: async (url: string) => ({
|
|
291
|
+
hostname: new URL(url).hostname,
|
|
292
|
+
addresses: [{ address: "142.250.1.1", family: 4 }],
|
|
293
|
+
privateNetwork: false,
|
|
294
|
+
}),
|
|
295
|
+
...next,
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
export function resetProviderTlsProfileForTests(): void {
|
|
300
|
+
for (const pending of transports.values()) void pending.then(value => value?.transport.close()).catch(() => undefined);
|
|
301
|
+
transports.clear();
|
|
302
|
+
shutdownDetach?.();
|
|
303
|
+
shutdownDetach = undefined;
|
|
304
|
+
statusByProvider.clear();
|
|
305
|
+
fallbackWarned = false;
|
|
306
|
+
runtime = defaultRuntime;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
export type { ProviderTlsProfile };
|
package/src/lib/proxy-env.ts
CHANGED
|
@@ -16,3 +16,52 @@ export function outboundProxyConfigured(
|
|
|
16
16
|
): boolean {
|
|
17
17
|
return OUTBOUND_PROXY_ENV_KEYS.some(key => proxyEnvPresent(key, env));
|
|
18
18
|
}
|
|
19
|
+
|
|
20
|
+
function proxyValue(key: ProxyEnvKey, env: ProxyEnvMap): string | undefined {
|
|
21
|
+
const value = env[key]?.trim() || env[key.toLowerCase()]?.trim();
|
|
22
|
+
return value || undefined;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function noProxyMatches(url: URL, env: ProxyEnvMap = process.env): boolean {
|
|
26
|
+
const raw = (env.NO_PROXY ?? env.no_proxy ?? "").trim();
|
|
27
|
+
const hostname = url.hostname.trim().toLowerCase().replace(/^\[|\]$/g, "").replace(/\.+$/, "");
|
|
28
|
+
const port = url.port || (url.protocol === "https:" ? "443" : "80");
|
|
29
|
+
for (const value of raw.split(",")) {
|
|
30
|
+
let entry = value.trim().toLowerCase();
|
|
31
|
+
if (!entry) continue;
|
|
32
|
+
if (entry === "*") return true;
|
|
33
|
+
entry = entry.replace(/^https?:\/\//, "").split("/", 1)[0]!;
|
|
34
|
+
let entryHost = entry;
|
|
35
|
+
let entryPort = "";
|
|
36
|
+
const bracketed = /^\[([^\]]+)](?::(\d+))?$/.exec(entry);
|
|
37
|
+
if (bracketed) {
|
|
38
|
+
entryHost = bracketed[1]!;
|
|
39
|
+
entryPort = bracketed[2] ?? "";
|
|
40
|
+
} else if ((entry.match(/:/g)?.length ?? 0) === 1) {
|
|
41
|
+
const separator = entry.lastIndexOf(":");
|
|
42
|
+
const possiblePort = entry.slice(separator + 1);
|
|
43
|
+
if (/^\d+$/.test(possiblePort)) {
|
|
44
|
+
entryHost = entry.slice(0, separator);
|
|
45
|
+
entryPort = possiblePort;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
if (entryPort && entryPort !== port) continue;
|
|
49
|
+
entryHost = entryHost.replace(/^\*?\./, "").replace(/^\[|\]$/g, "").replace(/\.+$/, "");
|
|
50
|
+
if (entryHost && (hostname === entryHost || hostname.endsWith(`.${entryHost}`))) return true;
|
|
51
|
+
}
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Select the standard proxy for a URL without exposing its credentials. */
|
|
56
|
+
export function proxyForUrl(url: string | URL, env: ProxyEnvMap = process.env): string | undefined {
|
|
57
|
+
let parsed: URL;
|
|
58
|
+
try {
|
|
59
|
+
parsed = new URL(url);
|
|
60
|
+
} catch {
|
|
61
|
+
return undefined;
|
|
62
|
+
}
|
|
63
|
+
if (noProxyMatches(parsed, env)) return undefined;
|
|
64
|
+
if (parsed.protocol === "https:") return proxyValue("HTTPS_PROXY", env) ?? proxyValue("ALL_PROXY", env);
|
|
65
|
+
if (parsed.protocol === "http:") return proxyValue("HTTP_PROXY", env) ?? proxyValue("ALL_PROXY", env);
|
|
66
|
+
return undefined;
|
|
67
|
+
}
|
package/src/lib/redact.ts
CHANGED
|
@@ -490,10 +490,19 @@ export function redactUrlForLog(url: string): string {
|
|
|
490
490
|
parsed.hash = "";
|
|
491
491
|
return parsed.toString();
|
|
492
492
|
} catch {
|
|
493
|
-
|
|
493
|
+
const stripped = (url.split(/[?#]/)[0] ?? url).replace(/:\/\/[^@\s]+@/g, "://");
|
|
494
|
+
return redactSecretString(stripped);
|
|
494
495
|
}
|
|
495
496
|
}
|
|
496
497
|
|
|
498
|
+
/** Redact credentials and userinfo from arbitrary diagnostic text. */
|
|
499
|
+
export function redactErrorMessage(value: string): string {
|
|
500
|
+
return redactSecretString(value).replace(
|
|
501
|
+
/[a-z][a-z0-9+.-]*:\/\/[^\s"'<>]+/gi,
|
|
502
|
+
match => redactUrlForLog(match),
|
|
503
|
+
);
|
|
504
|
+
}
|
|
505
|
+
|
|
497
506
|
const USER_HOME_PATH_PATTERNS: Array<[RegExp, string]> = [
|
|
498
507
|
// Windows: C:\Users\<name>\... -> C:\Users\[USER]\...
|
|
499
508
|
[/([A-Za-z]:\\Users\\)[^\\/]+/gi, "$1[USER]"],
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
import { reconcileCodexReauthState } from "../codex/account-runtime-state";
|
|
7
7
|
import { reconcileCatalogWarningMemos } from "../codex/catalog/aggregation";
|
|
8
8
|
import { reconcileProviderFetchWarnings } from "../codex/catalog/provider-fetch";
|
|
9
|
+
import { reconcileModelMetadataSnapshot } from "../codex/catalog/model-metadata";
|
|
9
10
|
import { reconcileModelCacheGeneration } from "../codex/model-cache";
|
|
10
11
|
import { reconcilePoolRotationState } from "../codex/pool-rotation";
|
|
11
12
|
import { reconcileCodexQuotaAccounts } from "../codex/quota";
|
|
@@ -103,6 +104,7 @@ export const STATE_STORE_REGISTRATIONS = [
|
|
|
103
104
|
{ name: "provider-quota-history", reconcileGeneration: reconcileProviderAccountQuotaRows },
|
|
104
105
|
{ name: "codex-routing-health", reconcileGeneration: reconcileCodexRoutingHealth },
|
|
105
106
|
{ name: "model-cache-history", reconcileGeneration: reconcileModelCacheGeneration },
|
|
107
|
+
{ name: "model-metadata-snapshot", reconcileGeneration: reconcileModelMetadataSnapshot },
|
|
106
108
|
{ name: "pool-rotation", reconcileGeneration: reconcilePoolRotationState },
|
|
107
109
|
{ name: "combo-rotation", reconcileGeneration: reconcileComboRotationState },
|
|
108
110
|
{ name: "guardian-backoff", reconcileGeneration: reconcileGuardianBackoff },
|
|
@@ -59,6 +59,35 @@ function declaresString(schema: SchemaNode): boolean {
|
|
|
59
59
|
return Array.isArray(type) && type.includes("string");
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
+
// Issue #2316: Codex advertises `multi_agent_v1__wait_agent`'s `timeout_ms` as a JSON
|
|
63
|
+
// Schema `number`, but its Rust runtime deserializes the field as `u64` and rejects
|
|
64
|
+
// `120000.0` with "invalid type: floating point `120000.0`, expected u64" before the
|
|
65
|
+
// tool ever runs. The schema lookup is not the problem — the error text comes from
|
|
66
|
+
// Codex's own deserializer, so the call reached it — the problem is that `number`
|
|
67
|
+
// alone never authorizes the integral-float repair below.
|
|
68
|
+
//
|
|
69
|
+
// This allowlist is deliberately one field wide. It names only what has a live
|
|
70
|
+
// reproduction against a real `u64`, because the repair is only unambiguous for a
|
|
71
|
+
// field that cannot legitimately hold a fraction. Generic names (`start`, `end`,
|
|
72
|
+
// `priority`, `port`) would silently rewrite a third-party tool's fractional value,
|
|
73
|
+
// so a new entry needs its own evidence, not a plausible-sounding name.
|
|
74
|
+
//
|
|
75
|
+
// Cursor's sibling `yield_time_ms` (src/adapters/cursor/tool-definitions.ts) is also
|
|
76
|
+
// declared `number`; it is NOT included here because no rejection has been captured
|
|
77
|
+
// against it. It gets its own change when it gets its own reproduction.
|
|
78
|
+
const U64_NUMBER_FIELDS = new Set(["timeout_ms"]);
|
|
79
|
+
|
|
80
|
+
// Issue #2443 / #2451: Codex Desktop's bare `wait` tool has the same schema/runtime
|
|
81
|
+
// split for `yield_time_ms` and `max_tokens`. Scope these names to that bare tool so a
|
|
82
|
+
// third-party or namespaced tool can still use fractional values legitimately.
|
|
83
|
+
// #2448 allowlisted the hyphenated `yield-time_ms`; live Grok 4.6 calls and the
|
|
84
|
+
// advertised wait schema both use the underscore form, so that name is the one
|
|
85
|
+
// with a captured u64 rejection. Cursor still uses the same underscore name on a
|
|
86
|
+
// namespaced tool; the wait-only map keeps that path byte-identical.
|
|
87
|
+
const U64_NUMBER_FIELDS_BY_TOOL = new Map<string, ReadonlySet<string>>([
|
|
88
|
+
["wait", new Set(["yield_time_ms", "max_tokens"])],
|
|
89
|
+
]);
|
|
90
|
+
|
|
62
91
|
/** True when the node accepts a JSON number (`integer` or `number`), so a numeric
|
|
63
92
|
* value is already schema-valid and must not be rewritten into a string. */
|
|
64
93
|
function declaresNumeric(schema: SchemaNode): boolean {
|
|
@@ -118,7 +147,14 @@ interface CoerceResult {
|
|
|
118
147
|
changed: boolean;
|
|
119
148
|
}
|
|
120
149
|
|
|
121
|
-
function coerceValue(
|
|
150
|
+
function coerceValue(
|
|
151
|
+
value: unknown,
|
|
152
|
+
schema: SchemaNode | undefined,
|
|
153
|
+
root: SchemaNode,
|
|
154
|
+
depth: number,
|
|
155
|
+
toolName?: string,
|
|
156
|
+
propertyName?: string,
|
|
157
|
+
): CoerceResult {
|
|
122
158
|
// A hostile or deeply nested schema must not blow the stack.
|
|
123
159
|
if (depth > 64) return { value, changed: false };
|
|
124
160
|
const resolved = schema ? resolveRef(schema, root, new Set()) : undefined;
|
|
@@ -126,7 +162,19 @@ function coerceValue(value: unknown, schema: SchemaNode | undefined, root: Schem
|
|
|
126
162
|
if (typeof value === "number") {
|
|
127
163
|
if (!resolved) return { value, changed: false };
|
|
128
164
|
const branches = compositionBranches(resolved);
|
|
129
|
-
|
|
165
|
+
// #2316: a known Codex-native u64 field counts as integer-declared even when the
|
|
166
|
+
// advertised schema says `number`, but only when the field really is numeric —
|
|
167
|
+
// an allowlisted name over a string-typed field is a disagreement, not a repair.
|
|
168
|
+
const nativeU64Field = propertyName !== undefined
|
|
169
|
+
&& (
|
|
170
|
+
U64_NUMBER_FIELDS.has(propertyName)
|
|
171
|
+
|| U64_NUMBER_FIELDS_BY_TOOL.get(toolName ?? "")?.has(propertyName) === true
|
|
172
|
+
);
|
|
173
|
+
const nativeU64Declared = nativeU64Field
|
|
174
|
+
&& (declaresNumeric(resolved) || branches.some(declaresNumeric));
|
|
175
|
+
const integerDeclared = declaresInteger(resolved)
|
|
176
|
+
|| branches.some(declaresInteger)
|
|
177
|
+
|| nativeU64Declared;
|
|
130
178
|
if (!integerDeclared && safelyIntegral(value)) {
|
|
131
179
|
// Issue #1938: a bare integer in a string-only field has exactly one faithful
|
|
132
180
|
// string reading. A field that also accepts a numeric type keeps the number.
|
|
@@ -148,7 +196,9 @@ function coerceValue(value: unknown, schema: SchemaNode | undefined, root: Schem
|
|
|
148
196
|
const itemSchema = resolved ? asSchema(resolved.items) : undefined;
|
|
149
197
|
let changed = false;
|
|
150
198
|
const next = value.map(entry => {
|
|
151
|
-
|
|
199
|
+
// Array items have no property name of their own; passing the array's key would
|
|
200
|
+
// let `timeout_ms: [1.5]` inherit the allowlist. Items are judged by schema only.
|
|
201
|
+
const result = coerceValue(entry, itemSchema, root, depth + 1, toolName);
|
|
152
202
|
if (result.changed) changed = true;
|
|
153
203
|
return result.value;
|
|
154
204
|
});
|
|
@@ -164,7 +214,7 @@ function coerceValue(value: unknown, schema: SchemaNode | undefined, root: Schem
|
|
|
164
214
|
const next: Record<string, unknown> = {};
|
|
165
215
|
for (const [key, entry] of Object.entries(object)) {
|
|
166
216
|
const childSchema = asSchema(properties?.[key]) ?? additional;
|
|
167
|
-
const result = coerceValue(entry, childSchema, root, depth + 1);
|
|
217
|
+
const result = coerceValue(entry, childSchema, root, depth + 1, toolName, key);
|
|
168
218
|
if (result.changed) changed = true;
|
|
169
219
|
next[key] = result.value;
|
|
170
220
|
}
|
|
@@ -182,6 +232,7 @@ function coerceValue(value: unknown, schema: SchemaNode | undefined, root: Schem
|
|
|
182
232
|
export function coerceIntegerToolArguments(
|
|
183
233
|
args: string,
|
|
184
234
|
parameters: Record<string, unknown> | undefined,
|
|
235
|
+
toolName?: string,
|
|
185
236
|
): string {
|
|
186
237
|
if (!parameters || !args) return args;
|
|
187
238
|
// Cheap reject: a payload with no digit cannot need either repair (integral-float
|
|
@@ -196,7 +247,7 @@ export function coerceIntegerToolArguments(
|
|
|
196
247
|
return args;
|
|
197
248
|
}
|
|
198
249
|
const root = parameters as SchemaNode;
|
|
199
|
-
const result = coerceValue(parsed, root, root, 0);
|
|
250
|
+
const result = coerceValue(parsed, root, root, 0, toolName);
|
|
200
251
|
if (!result.changed) return args;
|
|
201
252
|
return JSON.stringify(result.value);
|
|
202
253
|
}
|