@bitkyc08/opencodex 2.14.1 → 2.14.2
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/gui/dist/assets/{index-DWhX3yMp.css → index-DUCH59lJ.css} +1 -1
- package/gui/dist/assets/index-DUyQeU1j.js +76 -0
- package/gui/dist/index.html +2 -2
- package/package.json +1 -1
- package/src/adapters/command-code.ts +15 -4
- package/src/adapters/cursor/request-builder.ts +54 -10
- package/src/adapters/cursor/tool-definitions.ts +24 -0
- package/src/adapters/kiro.ts +10 -1
- package/src/adapters/openai-chat.ts +5 -3
- package/src/adapters/openai-responses.ts +109 -0
- package/src/adapters/tool-catalog-nudge.ts +26 -4
- package/src/bridge.ts +50 -3
- package/src/cli/init.ts +4 -17
- package/src/codex/catalog/effort.ts +2 -1
- package/src/codex/catalog/metadata.ts +62 -12
- package/src/codex/catalog/native-models.ts +27 -0
- package/src/codex/catalog/parsing.ts +17 -2
- package/src/codex/catalog/provider-fetch.ts +47 -5
- package/src/codex/catalog/sync.ts +21 -7
- package/src/codex/catalog.ts +1 -1
- package/src/config.ts +79 -4
- package/src/generated/compatibility-version.json +48 -36
- package/src/lib/app-owned-memory-stores.ts +22 -0
- package/src/lib/tool-argument-integers.ts +158 -0
- package/src/oauth/nous.ts +58 -9
- package/src/providers/base-url-choices.ts +10 -0
- package/src/providers/command-code-efforts.ts +18 -0
- package/src/providers/model-rename-migration.ts +202 -0
- package/src/providers/model-rename-startup.ts +28 -0
- package/src/providers/openai-tier-startup.ts +31 -2
- package/src/providers/quota.ts +9 -2
- package/src/providers/registry.ts +12 -5
- package/src/responses/spill-store.ts +5 -1
- package/src/responses/state.ts +50 -2
- package/src/server/index.ts +2 -1
- package/src/server/management/api-key-usage.ts +31 -5
- package/src/server/management/logs-usage-routes.ts +48 -10
- package/src/server/management/provider-routes.ts +2 -1
- package/src/server/management/usage-summary-cache.ts +7 -1
- package/src/server/responses/collaboration.ts +12 -2
- package/src/server/responses/core.ts +33 -16
- package/src/server/startup-health-cache.ts +12 -0
- package/src/usage/log.ts +430 -12
- package/gui/dist/assets/index-DuaUVm_d.js +0 -76
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
currentUsageLogRevision,
|
|
3
3
|
readUsageSnapshotForManagement,
|
|
4
|
-
|
|
4
|
+
usageLogIdentityKey,
|
|
5
5
|
type PersistedUsageEntry,
|
|
6
6
|
} from "../../usage/log";
|
|
7
7
|
|
|
@@ -110,7 +110,7 @@ export function rollupApiKeyUsage(
|
|
|
110
110
|
* create/rename/delete. The compact rollup is a handful of counters per key, so
|
|
111
111
|
* caching it costs nothing; a new row changes the revision and invalidates it.
|
|
112
112
|
*/
|
|
113
|
-
let rollupCache: { revisionKey: string; expiresAt: number; snapshot: ApiKeyUsageSnapshot } | null = null;
|
|
113
|
+
let rollupCache: { revisionKey: string; expiresAt: number; lastSeenSize?: number; snapshot: ApiKeyUsageSnapshot } | null = null;
|
|
114
114
|
|
|
115
115
|
/**
|
|
116
116
|
* The rollup is a function of the log AND of the clock: a request ages out of
|
|
@@ -136,6 +136,29 @@ export function clearApiKeyUsageCacheForTests(): void {
|
|
|
136
136
|
* `attributionSince`. Key management working matters more than usage numbers
|
|
137
137
|
* being present, and the GUI already treats an absent field as "no data".
|
|
138
138
|
*/
|
|
139
|
+
export function cacheApiKeyUsageFromSnapshot(
|
|
140
|
+
entries: PersistedUsageEntry[],
|
|
141
|
+
configuredIds: string[],
|
|
142
|
+
identityKey: string,
|
|
143
|
+
lastSeenSize: number,
|
|
144
|
+
truncated: boolean,
|
|
145
|
+
maxReadBytes: number | undefined,
|
|
146
|
+
now: number = Date.now(),
|
|
147
|
+
): ApiKeyUsageSnapshot {
|
|
148
|
+
const idsKey = JSON.stringify([configuredIds, maxReadBytes]);
|
|
149
|
+
const rolled = {
|
|
150
|
+
...rollupApiKeyUsage(entries, configuredIds, now),
|
|
151
|
+
...(truncated ? { historyTruncated: true as const } : {}),
|
|
152
|
+
};
|
|
153
|
+
rollupCache = {
|
|
154
|
+
revisionKey: `${identityKey}|${idsKey}`,
|
|
155
|
+
expiresAt: now + ROLLUP_CACHE_TTL_MS,
|
|
156
|
+
lastSeenSize,
|
|
157
|
+
snapshot: rolled,
|
|
158
|
+
};
|
|
159
|
+
return rolled;
|
|
160
|
+
}
|
|
161
|
+
|
|
139
162
|
export async function readApiKeyUsageRollup(configuredIds: string[], maxReadBytes?: number): Promise<ApiKeyUsageSnapshot> {
|
|
140
163
|
// JSON rather than a joined string: ids are only validated as non-empty
|
|
141
164
|
// strings, so `["a\0b","c"]` and `["a","b\0c"]` join to the same value and one
|
|
@@ -143,8 +166,10 @@ export async function readApiKeyUsageRollup(configuredIds: string[], maxReadByte
|
|
|
143
166
|
const idsKey = JSON.stringify([configuredIds, maxReadBytes]);
|
|
144
167
|
const now = Date.now();
|
|
145
168
|
try {
|
|
146
|
-
const
|
|
147
|
-
|
|
169
|
+
const observed = currentUsageLogRevision();
|
|
170
|
+
const observedKey = `${usageLogIdentityKey(observed)}|${idsKey}`;
|
|
171
|
+
const observedSize = observed?.size ?? 0;
|
|
172
|
+
if (rollupCache?.revisionKey === observedKey && now < rollupCache.expiresAt && observedSize >= (rollupCache.lastSeenSize ?? 0)) {
|
|
148
173
|
return rollupCache.snapshot;
|
|
149
174
|
}
|
|
150
175
|
|
|
@@ -154,8 +179,9 @@ export async function readApiKeyUsageRollup(configuredIds: string[], maxReadByte
|
|
|
154
179
|
...(snapshot.truncatedPrefixBytes > 0 || snapshot.entriesTruncated ? { historyTruncated: true as const } : {}),
|
|
155
180
|
};
|
|
156
181
|
rollupCache = {
|
|
157
|
-
revisionKey: `${
|
|
182
|
+
revisionKey: `${usageLogIdentityKey(snapshot.revision)}|${idsKey}`,
|
|
158
183
|
expiresAt: now + ROLLUP_CACHE_TTL_MS,
|
|
184
|
+
lastSeenSize: snapshot.revision?.size ?? 0,
|
|
159
185
|
snapshot: rolled,
|
|
160
186
|
};
|
|
161
187
|
return rolled;
|
|
@@ -50,6 +50,7 @@ import {
|
|
|
50
50
|
import {
|
|
51
51
|
currentUsageLogRevision,
|
|
52
52
|
readUsageSnapshotForManagement,
|
|
53
|
+
usageLogIdentityKey,
|
|
53
54
|
usageLogRevisionKey,
|
|
54
55
|
type PersistedUsageEntry,
|
|
55
56
|
} from "../../usage/log";
|
|
@@ -84,6 +85,7 @@ import {
|
|
|
84
85
|
getUsageSummaryCacheEntry,
|
|
85
86
|
setUsageSummaryCacheEntry,
|
|
86
87
|
} from "./usage-summary-cache";
|
|
88
|
+
import { cacheApiKeyUsageFromSnapshot } from "./api-key-usage";
|
|
87
89
|
|
|
88
90
|
const USAGE_DAY_MS = 86_400_000;
|
|
89
91
|
function usageEntryMatchesSurface(entry: PersistedUsageEntry, surface: UsageSurface): boolean {
|
|
@@ -215,12 +217,16 @@ export async function handleLogsUsageRoutes(ctx: ManagementContext): Promise<Res
|
|
|
215
217
|
try {
|
|
216
218
|
const cacheKey = `${range}:${surface}`;
|
|
217
219
|
const effectiveReadLimit = config.managementUsageMaxReadBytes ?? 64 * 1024 * 1024;
|
|
218
|
-
const
|
|
220
|
+
const observed = currentUsageLogRevision();
|
|
221
|
+
const identityKey = `${usageLogIdentityKey(observed)}\0${effectiveReadLimit}`;
|
|
222
|
+
const observedSize = observed?.size ?? 0;
|
|
219
223
|
const cached = getUsageSummaryCacheEntry(cacheKey);
|
|
220
224
|
if (cached
|
|
221
|
-
&& cached.
|
|
225
|
+
&& cached.identityKey === identityKey
|
|
226
|
+
&& cached.maxReadBytes === effectiveReadLimit
|
|
222
227
|
&& cached.overlayVersion === userCostOverlayVersion()
|
|
223
|
-
&& now < cached.
|
|
228
|
+
&& now < cached.freshUntil
|
|
229
|
+
&& observedSize >= cached.lastSeenSize) {
|
|
224
230
|
return jsonResponse(refreshedUsageSummary(cached.summary, range, now));
|
|
225
231
|
}
|
|
226
232
|
if (cached) discardUsageSummaryCacheEntry(cacheKey);
|
|
@@ -249,13 +255,45 @@ export async function handleLogsUsageRoutes(ctx: ManagementContext): Promise<Res
|
|
|
249
255
|
// mixed-price entry under either version.
|
|
250
256
|
return jsonResponse(summary);
|
|
251
257
|
}
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
258
|
+
const freshUntil = now + 60_000;
|
|
259
|
+
const snapshotIdentity = `${usageLogIdentityKey(snapshot.revision)}\0${effectiveReadLimit}`;
|
|
260
|
+
const revisionKey = `${usageLogRevisionKey(snapshot.revision)}\0${effectiveReadLimit}`;
|
|
261
|
+
const lastSeenSize = snapshot.revision?.size ?? 0;
|
|
262
|
+
const ranges: UsageRange[] = ["7d", "30d", "all"];
|
|
263
|
+
const surfaces: UsageSurface[] = ["all", "codex", "claude", "grok"];
|
|
264
|
+
for (const nextRange of ranges) {
|
|
265
|
+
for (const nextSurface of surfaces) {
|
|
266
|
+
const nextSummary = nextRange === range && nextSurface === surface ? summary : {
|
|
267
|
+
...summarizeUsage(snapshot.entries, nextRange, now, nextSurface),
|
|
268
|
+
historyTruncated: summary.historyTruncated,
|
|
269
|
+
truncatedPrefixBytes: summary.truncatedPrefixBytes,
|
|
270
|
+
entriesTruncated: summary.entriesTruncated,
|
|
271
|
+
entriesDropped: summary.entriesDropped,
|
|
272
|
+
snapshotWindowStart: summary.snapshotWindowStart,
|
|
273
|
+
snapshotWindowEnd: summary.snapshotWindowEnd,
|
|
274
|
+
};
|
|
275
|
+
setUsageSummaryCacheEntry(`${nextRange}:${nextSurface}`, {
|
|
276
|
+
revisionKey,
|
|
277
|
+
identityKey: snapshotIdentity,
|
|
278
|
+
maxReadBytes: effectiveReadLimit,
|
|
279
|
+
overlayVersion,
|
|
280
|
+
expiresAt: usageSummaryExpiresAt(snapshot.entries, nextRange, nextSurface, now),
|
|
281
|
+
freshUntil,
|
|
282
|
+
lastSeenSize,
|
|
283
|
+
revisionReadAt,
|
|
284
|
+
summary: nextSummary,
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
cacheApiKeyUsageFromSnapshot(
|
|
289
|
+
snapshot.entries,
|
|
290
|
+
(config.apiKeys ?? []).map(key => key.id),
|
|
291
|
+
usageLogIdentityKey(snapshot.revision),
|
|
292
|
+
snapshot.revision?.size ?? 0,
|
|
293
|
+
snapshot.truncatedPrefixBytes > 0 || snapshot.entriesTruncated,
|
|
294
|
+
effectiveReadLimit,
|
|
295
|
+
now,
|
|
296
|
+
);
|
|
259
297
|
return jsonResponse(summary);
|
|
260
298
|
} catch {
|
|
261
299
|
return jsonResponse({
|
|
@@ -27,7 +27,7 @@ import {
|
|
|
27
27
|
submitManualLoginCode,
|
|
28
28
|
upsertOAuthProvider,
|
|
29
29
|
} from "../../oauth";
|
|
30
|
-
import {
|
|
30
|
+
import { replaceProviderAccountSet } from "../../oauth/store";
|
|
31
31
|
import { providerDestinationResolvedError } from "../../lib/destination-policy";
|
|
32
32
|
import { reconcileLiveStateStores } from "../../lib/state-store-registrations";
|
|
33
33
|
import { ProviderOutboundPolicyError, providerOutboundGet, providerOutboundPost, providerRedirectError } from "../../lib/provider-outbound";
|
|
@@ -765,6 +765,7 @@ export async function handleProviderRoutes(ctx: ManagementContext): Promise<Resp
|
|
|
765
765
|
const droppedCustomModels = dropProviderCustomModels(config, name);
|
|
766
766
|
setProviderContextCap(config, name, false);
|
|
767
767
|
save(config);
|
|
768
|
+
await replaceProviderAccountSet(name, null);
|
|
768
769
|
reconcileLiveStateStores();
|
|
769
770
|
const { clearModelCache: clearCache } = await import("../../codex/model-cache");
|
|
770
771
|
clearCache(name);
|
|
@@ -8,11 +8,17 @@ export type CachedUsageSummary = UsageSummary & {
|
|
|
8
8
|
entriesDropped: number;
|
|
9
9
|
};
|
|
10
10
|
|
|
11
|
-
interface UsageSummaryCacheEntry {
|
|
11
|
+
export interface UsageSummaryCacheEntry {
|
|
12
12
|
revisionKey: string;
|
|
13
|
+
/** path/dev/ino/birthtime only; appends keep this stable. */
|
|
14
|
+
identityKey: string;
|
|
15
|
+
maxReadBytes: number;
|
|
13
16
|
/** userCostOverlayVersion() when the summary was computed; overlay edits invalidate the entry. */
|
|
14
17
|
overlayVersion: number;
|
|
15
18
|
expiresAt: number;
|
|
19
|
+
/** Generation freshness: ignore size/mtime until this instant. */
|
|
20
|
+
freshUntil: number;
|
|
21
|
+
lastSeenSize: number;
|
|
16
22
|
summary: CachedUsageSummary;
|
|
17
23
|
revisionReadAt: number;
|
|
18
24
|
sizeBytes: number;
|
|
@@ -102,18 +102,28 @@ import type { TranslatorBudget } from "../../lib/translator-budget";
|
|
|
102
102
|
|
|
103
103
|
export function buildToolBridgeMaps(parsed: OcxParsedRequest, budget?: TranslatorBudget): {
|
|
104
104
|
toolNsMap: Map<string, { namespace: string; name: string }>;
|
|
105
|
+
declaredToolNames: Set<string>;
|
|
106
|
+
/** Declared parameter schema per request-visible tool name (#1611 integer repair). */
|
|
107
|
+
toolParameterSchemas: Map<string, Record<string, unknown>>;
|
|
105
108
|
freeformToolNames: Set<string>;
|
|
106
109
|
toolSearchToolNames: Set<string>;
|
|
107
110
|
} {
|
|
108
111
|
const toolNsMap = new Map<string, { namespace: string; name: string }>();
|
|
112
|
+
const declaredToolNames = new Set<string>();
|
|
113
|
+
const toolParameterSchemas = new Map<string, Record<string, unknown>>();
|
|
109
114
|
const freeformToolNames = new Set<string>();
|
|
110
115
|
const toolSearchToolNames = new Set<string>();
|
|
111
116
|
const toolAllowed = toolChoiceToolPredicate(parsed.options.toolChoice);
|
|
112
117
|
for (const t of parsed.context.tools ?? []) {
|
|
113
118
|
// Upstream output is untrusted: only restore calls for tools the caller authorized.
|
|
114
119
|
if (!toolAllowed(t)) continue;
|
|
120
|
+
const wireName = namespacedToolName(t.namespace, t.name);
|
|
121
|
+
budget?.chargeRetained(new TextEncoder().encode(wireName).byteLength, { kind: "retained_collectors" });
|
|
122
|
+
declaredToolNames.add(wireName);
|
|
123
|
+
// Retained by reference (the schema is already resident in parsed.context.tools),
|
|
124
|
+
// so this adds a map entry rather than a copy of every tool's parameters.
|
|
125
|
+
if (t.parameters && typeof t.parameters === "object") toolParameterSchemas.set(wireName, t.parameters);
|
|
115
126
|
if (t.namespace) {
|
|
116
|
-
const wireName = namespacedToolName(t.namespace, t.name);
|
|
117
127
|
budget?.chargeRetained(new TextEncoder().encode(JSON.stringify([wireName, t.namespace, t.name])).byteLength, { kind: "retained_collectors" });
|
|
118
128
|
toolNsMap.set(wireName, { namespace: t.namespace, name: t.name });
|
|
119
129
|
}
|
|
@@ -126,7 +136,7 @@ export function buildToolBridgeMaps(parsed: OcxParsedRequest, budget?: Translato
|
|
|
126
136
|
toolSearchToolNames.add(t.name);
|
|
127
137
|
}
|
|
128
138
|
}
|
|
129
|
-
return { toolNsMap, freeformToolNames, toolSearchToolNames };
|
|
139
|
+
return { toolNsMap, declaredToolNames, toolParameterSchemas, freeformToolNames, toolSearchToolNames };
|
|
130
140
|
}
|
|
131
141
|
|
|
132
142
|
|
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
markBodyNonPersistable,
|
|
23
23
|
previousResponseProviderState,
|
|
24
24
|
previousResponseReplayFailure,
|
|
25
|
+
previousResponseScopeMismatch,
|
|
25
26
|
rememberResponseState,
|
|
26
27
|
} from "../../responses/state";
|
|
27
28
|
import {
|
|
@@ -1503,8 +1504,12 @@ async function handleResponsesInner(
|
|
|
1503
1504
|
let unreadableEncryptedAgentTask = hasUnreadableEncryptedAgentTask(
|
|
1504
1505
|
(body as { input?: unknown } | undefined)?.input,
|
|
1505
1506
|
);
|
|
1507
|
+
const inboundClientThreadId = req.headers.get("x-codex-parent-thread-id")?.trim() || undefined;
|
|
1506
1508
|
const originalBody = body;
|
|
1507
|
-
body = expandPreviousResponseInput(body);
|
|
1509
|
+
body = expandPreviousResponseInput(body, inboundClientThreadId);
|
|
1510
|
+
if (previousResponseScopeMismatch(body)) {
|
|
1511
|
+
console.warn("[opencodex] dropped a previous_response_id with a mismatched client task scope; continuing fresh");
|
|
1512
|
+
}
|
|
1508
1513
|
if (previousResponseReplayFailure(body)) {
|
|
1509
1514
|
return formatErrorResponse(
|
|
1510
1515
|
400,
|
|
@@ -1512,7 +1517,8 @@ async function handleResponsesInner(
|
|
|
1512
1517
|
"Continuation state is unavailable or corrupt; resend the full conversation without previous_response_id.",
|
|
1513
1518
|
);
|
|
1514
1519
|
}
|
|
1515
|
-
const previousResponseInputExpanded = body !== originalBody
|
|
1520
|
+
const previousResponseInputExpanded = body !== originalBody
|
|
1521
|
+
&& typeof (body as { previous_response_id?: unknown }).previous_response_id === "string";
|
|
1516
1522
|
|
|
1517
1523
|
// Spawn-message compatibility (both directions): agent_message task payloads ride in
|
|
1518
1524
|
// encrypted_content slots as plaintext. Rewrite them to input_text on the RAW body BEFORE
|
|
@@ -1529,7 +1535,7 @@ async function handleResponsesInner(
|
|
|
1529
1535
|
);
|
|
1530
1536
|
}
|
|
1531
1537
|
|
|
1532
|
-
let parsed;
|
|
1538
|
+
let parsed: OcxParsedRequest;
|
|
1533
1539
|
let toolBridgeMaps: ReturnType<typeof buildToolBridgeMaps>;
|
|
1534
1540
|
try {
|
|
1535
1541
|
parsed = parseRequest(body);
|
|
@@ -1537,10 +1543,9 @@ async function handleResponsesInner(
|
|
|
1537
1543
|
if (previousResponseInputExpanded) parsed._previousResponseInputExpanded = true;
|
|
1538
1544
|
parsed._providerContinuation = previousResponseProviderState(parsed.previousResponseId);
|
|
1539
1545
|
parsed._cursorConversationId = parsed._providerContinuation?.cursor?.conversationId;
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
parsed.
|
|
1543
|
-
parsed._reasoningReplayScope = { clientThreadId };
|
|
1546
|
+
if (inboundClientThreadId) {
|
|
1547
|
+
parsed._clientThreadId = inboundClientThreadId;
|
|
1548
|
+
parsed._reasoningReplayScope = { clientThreadId: inboundClientThreadId };
|
|
1544
1549
|
}
|
|
1545
1550
|
} catch (err) {
|
|
1546
1551
|
if (isTranslatorBudgetExceededError(err)) {
|
|
@@ -1550,6 +1555,10 @@ async function handleResponsesInner(
|
|
|
1550
1555
|
}
|
|
1551
1556
|
return formatErrorResponse(400, "invalid_request_error", err instanceof Error ? err.message : String(err));
|
|
1552
1557
|
}
|
|
1558
|
+
const responseStateOptions = (force = false): { force?: boolean; clientThreadId?: string } => ({
|
|
1559
|
+
...(force ? { force: true } : {}),
|
|
1560
|
+
...(parsed._clientThreadId ? { clientThreadId: parsed._clientThreadId } : {}),
|
|
1561
|
+
});
|
|
1553
1562
|
// Prefer a pre-populated id (routed Claude) over Responses headers that may be
|
|
1554
1563
|
// absent or synthetically injected (session_id from prompt_cache_key).
|
|
1555
1564
|
if (!logCtx.conversationId) {
|
|
@@ -2137,7 +2146,7 @@ async function handleResponsesInner(
|
|
|
2137
2146
|
&& (!parsed.previousResponseId || parsed._previousResponseInputExpanded === true);
|
|
2138
2147
|
const rememberPassthroughResponse = passthroughRecordEligible
|
|
2139
2148
|
? (response: { id?: unknown; output?: unknown; status?: unknown }) =>
|
|
2140
|
-
rememberResponseState(parsed._rawBody, response, undefined,
|
|
2149
|
+
rememberResponseState(parsed._rawBody, response, undefined, responseStateOptions(true))
|
|
2141
2150
|
: undefined;
|
|
2142
2151
|
if (parsed.previousResponseId && !parsed._previousResponseInputExpanded) {
|
|
2143
2152
|
console.warn(
|
|
@@ -2962,7 +2971,7 @@ async function handleResponsesInner(
|
|
|
2962
2971
|
parsed._rawBody,
|
|
2963
2972
|
response,
|
|
2964
2973
|
continuationStateForResponse(providerState),
|
|
2965
|
-
adapterNeedsForcedContinuation(adapter.name)
|
|
2974
|
+
responseStateOptions(adapterNeedsForcedContinuation(adapter.name)),
|
|
2966
2975
|
),
|
|
2967
2976
|
});
|
|
2968
2977
|
if (imgResponse.body) {
|
|
@@ -3075,7 +3084,7 @@ async function handleResponsesInner(
|
|
|
3075
3084
|
}
|
|
3076
3085
|
};
|
|
3077
3086
|
|
|
3078
|
-
const { toolNsMap, freeformToolNames, toolSearchToolNames } = toolBridgeMaps;
|
|
3087
|
+
const { toolNsMap, declaredToolNames, toolParameterSchemas, freeformToolNames, toolSearchToolNames } = toolBridgeMaps;
|
|
3079
3088
|
if (parsed.stream) {
|
|
3080
3089
|
void runTurn();
|
|
3081
3090
|
let eventSource: AsyncIterable<AdapterEvent> = queue.stream();
|
|
@@ -3101,6 +3110,8 @@ async function handleResponsesInner(
|
|
|
3101
3110
|
...(options.forceEmptyResponseId ? { responseId: "" } : {}),
|
|
3102
3111
|
stallTimeoutSec: config.stallTimeoutSec,
|
|
3103
3112
|
hideThinkingSummary: parsed.options.hideThinkingSummary,
|
|
3113
|
+
declaredToolNames,
|
|
3114
|
+
toolParameterSchemas,
|
|
3104
3115
|
...(options.onFirstOutput ? { onFirstOutput: options.onFirstOutput } : {}),
|
|
3105
3116
|
...(routedCompaction ? { compaction: true } : {}),
|
|
3106
3117
|
onUsage: usage => {
|
|
@@ -3118,7 +3129,7 @@ async function handleResponsesInner(
|
|
|
3118
3129
|
parsed._rawBody,
|
|
3119
3130
|
response,
|
|
3120
3131
|
continuationStateForResponse(providerState),
|
|
3121
|
-
adapterNeedsForcedContinuation(adapter.name)
|
|
3132
|
+
responseStateOptions(adapterNeedsForcedContinuation(adapter.name)),
|
|
3122
3133
|
),
|
|
3123
3134
|
}),
|
|
3124
3135
|
},
|
|
@@ -3147,6 +3158,8 @@ async function handleResponsesInner(
|
|
|
3147
3158
|
replayCacheScope: parsed._reasoningReplayScope,
|
|
3148
3159
|
hideThinkingSummary: parsed.options.hideThinkingSummary,
|
|
3149
3160
|
toolNsMap,
|
|
3161
|
+
declaredToolNames,
|
|
3162
|
+
toolParameterSchemas,
|
|
3150
3163
|
freeformToolNames,
|
|
3151
3164
|
toolSearchToolNames,
|
|
3152
3165
|
...(routedCompaction ? { compaction: true } : {}),
|
|
@@ -3164,7 +3177,7 @@ async function handleResponsesInner(
|
|
|
3164
3177
|
parsed._rawBody,
|
|
3165
3178
|
json,
|
|
3166
3179
|
continuationStateForResponse(providerState),
|
|
3167
|
-
adapterNeedsForcedContinuation(adapter.name)
|
|
3180
|
+
responseStateOptions(adapterNeedsForcedContinuation(adapter.name)),
|
|
3168
3181
|
);
|
|
3169
3182
|
}
|
|
3170
3183
|
return new Response(JSON.stringify(json), { headers: { "Content-Type": "application/json" } });
|
|
@@ -3835,7 +3848,7 @@ async function handleResponsesInner(
|
|
|
3835
3848
|
continuation: fetchTerminalGuardContinuation,
|
|
3836
3849
|
})
|
|
3837
3850
|
: initialEventStream;
|
|
3838
|
-
const { toolNsMap, freeformToolNames, toolSearchToolNames } = toolBridgeMaps;
|
|
3851
|
+
const { toolNsMap, declaredToolNames, toolParameterSchemas, freeformToolNames, toolSearchToolNames } = toolBridgeMaps;
|
|
3839
3852
|
const sseStream = bridgeToResponsesSSE(
|
|
3840
3853
|
eventStream, parsed._responseModelId ?? parsed.modelId, toolNsMap, freeformToolNames, toolSearchToolNames,
|
|
3841
3854
|
() => upstream.abort(), 2_000,
|
|
@@ -3845,6 +3858,8 @@ async function handleResponsesInner(
|
|
|
3845
3858
|
...(options.forceEmptyResponseId ? { responseId: "" } : {}),
|
|
3846
3859
|
stallTimeoutSec: config.stallTimeoutSec,
|
|
3847
3860
|
hideThinkingSummary: parsed.options.hideThinkingSummary,
|
|
3861
|
+
declaredToolNames,
|
|
3862
|
+
toolParameterSchemas,
|
|
3848
3863
|
...(options.onFirstOutput ? { onFirstOutput: options.onFirstOutput } : {}),
|
|
3849
3864
|
...(routedCompaction ? { compaction: true } : {}),
|
|
3850
3865
|
onUsage: usage => {
|
|
@@ -3864,7 +3879,7 @@ async function handleResponsesInner(
|
|
|
3864
3879
|
parsed._rawBody,
|
|
3865
3880
|
response,
|
|
3866
3881
|
continuationStateForResponse(providerState),
|
|
3867
|
-
activeAdapter.name === "kiro"
|
|
3882
|
+
responseStateOptions(activeAdapter.name === "kiro"),
|
|
3868
3883
|
),
|
|
3869
3884
|
}),
|
|
3870
3885
|
},
|
|
@@ -3895,13 +3910,15 @@ async function handleResponsesInner(
|
|
|
3895
3910
|
} finally {
|
|
3896
3911
|
cleanupUpstreamAbort();
|
|
3897
3912
|
}
|
|
3898
|
-
const { toolNsMap, freeformToolNames, toolSearchToolNames } = toolBridgeMaps;
|
|
3913
|
+
const { toolNsMap, declaredToolNames, toolParameterSchemas, freeformToolNames, toolSearchToolNames } = toolBridgeMaps;
|
|
3899
3914
|
let providerState: OcxProviderContinuationState | undefined;
|
|
3900
3915
|
const json = buildResponseJSON(events, parsed._responseModelId ?? parsed.modelId, {
|
|
3901
3916
|
translatorBudget,
|
|
3902
3917
|
replayCacheScope: parsed._reasoningReplayScope,
|
|
3903
3918
|
hideThinkingSummary: parsed.options.hideThinkingSummary,
|
|
3904
3919
|
toolNsMap,
|
|
3920
|
+
declaredToolNames,
|
|
3921
|
+
toolParameterSchemas,
|
|
3905
3922
|
freeformToolNames,
|
|
3906
3923
|
toolSearchToolNames,
|
|
3907
3924
|
...(routedCompaction ? { compaction: true } : {}),
|
|
@@ -3920,7 +3937,7 @@ async function handleResponsesInner(
|
|
|
3920
3937
|
parsed._rawBody,
|
|
3921
3938
|
json,
|
|
3922
3939
|
continuationStateForResponse(providerState),
|
|
3923
|
-
activeAdapter.name === "kiro"
|
|
3940
|
+
responseStateOptions(activeAdapter.name === "kiro"),
|
|
3924
3941
|
);
|
|
3925
3942
|
}
|
|
3926
3943
|
return new Response(JSON.stringify(json), { headers: { "Content-Type": "application/json" } });
|
|
@@ -10,6 +10,7 @@ import { truncateRetainedUtf8 } from "../lib/admission";
|
|
|
10
10
|
|
|
11
11
|
const CACHE_TTL_MS = 30_000;
|
|
12
12
|
const PROBE_TIMEOUT_MS = 5_000;
|
|
13
|
+
const INITIAL_PROBE_WAIT_MS = 5_500;
|
|
13
14
|
const MAX_DIAGNOSTIC_VALUE_BYTES = 8 * 1024;
|
|
14
15
|
let cached: { timestamp: number; value: StartupHealth } | null = null;
|
|
15
16
|
let inflight: Promise<StartupHealth> | null = null;
|
|
@@ -109,6 +110,17 @@ function refreshInBackground(config: Pick<OcxConfig, "codexAutoStart">): void {
|
|
|
109
110
|
export async function getCachedStartupHealth(config: Pick<OcxConfig, "codexAutoStart">): Promise<StartupHealth> {
|
|
110
111
|
if (cached && Date.now() - cached.timestamp < CACHE_TTL_MS) return cached.value;
|
|
111
112
|
refreshInBackground(config);
|
|
113
|
+
// An expired or empty read is an explicit protection check. Wait for the
|
|
114
|
+
// isolated probe instead of presenting a synthetic failure while that probe
|
|
115
|
+
// is still running. The probe remains child-process isolated and hard-capped
|
|
116
|
+
// at 5s; stale state is returned only if that bounded probe cannot settle.
|
|
117
|
+
if (inflight) {
|
|
118
|
+
const settled = await Promise.race([
|
|
119
|
+
inflight,
|
|
120
|
+
new Promise<null>(resolve => setTimeout(() => resolve(null), INITIAL_PROBE_WAIT_MS)),
|
|
121
|
+
]);
|
|
122
|
+
if (settled) return settled;
|
|
123
|
+
}
|
|
112
124
|
return cached ? markStartupHealthDiagnosticStale(cached.value) : conservativeFallback(config);
|
|
113
125
|
}
|
|
114
126
|
|