@bitkyc08/opencodex 2.69.0 → 2.70.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/gui/dist/assets/App-_MrbTrBe.js +51 -0
- package/gui/dist/assets/{Tray-uqKRqfvw.js → Tray-5PuUalET.js} +1 -1
- package/gui/dist/assets/index-DKrO2eF3.js +86 -0
- package/gui/dist/assets/quota-summary-BCLivc0Q.js +1 -0
- package/gui/dist/index.html +1 -1
- package/package.json +1 -1
- package/src/adapters/anthropic-model-contract.ts +119 -0
- package/src/adapters/anthropic.ts +14 -83
- package/src/adapters/base.ts +2 -0
- package/src/adapters/command-code-project-context.ts +542 -0
- package/src/adapters/command-code-tool-text.ts +99 -67
- package/src/adapters/command-code.ts +5 -1
- package/src/adapters/cursor/catalog.ts +11 -0
- package/src/adapters/cursor/effort-map.ts +3 -0
- package/src/adapters/devin/cloud-direct/catalog.ts +91 -0
- package/src/adapters/devin/cloud-direct/chat.ts +78 -48
- package/src/adapters/devin/cloud-direct/tool-schema.ts +121 -0
- package/src/adapters/devin/context-overflow.ts +75 -0
- package/src/adapters/devin/live-models.ts +174 -13
- package/src/adapters/devin/reasoning-signature.ts +100 -0
- package/src/adapters/devin.ts +310 -117
- package/src/adapters/kiro/payload.ts +10 -10
- package/src/adapters/kiro-images.ts +58 -7
- package/src/adapters/openai-chat/passthrough.ts +3 -1
- package/src/adapters/openai-chat.ts +2 -1
- package/src/adapters/openai-responses/passthrough.ts +2 -1
- package/src/bridge/response-json.ts +15 -1
- package/src/bridge/sse.ts +3 -1
- package/src/claude/intercept/model-bindings.ts +1 -0
- package/src/cli/account-extended.ts +31 -1
- package/src/cli/account.ts +3 -0
- package/src/cli/capabilities.ts +13 -0
- package/src/cli/client-start-fence.ts +36 -0
- package/src/cli/dispatch.ts +3 -2
- package/src/cli/index.ts +98 -117
- package/src/cli/provider-runtime.ts +23 -2
- package/src/cli/provider.ts +3 -0
- package/src/cli/resolve.ts +18 -0
- package/src/cli/start-owner-fence.ts +30 -0
- package/src/cli/stop-approval.ts +13 -1
- package/src/cli/stop-restore.ts +73 -0
- package/src/cli/tray-proxy.ts +221 -18
- package/src/cli/version-skew.ts +25 -23
- package/src/client/runtime.ts +8 -4
- package/src/codex/auth-api/account-list.ts +11 -5
- package/src/codex/auth-api/login-flow.ts +5 -3
- package/src/codex/auth-api/main-account-probe.ts +186 -110
- package/src/codex/auth-api/pool-mode-gate.ts +61 -7
- package/src/codex/auth-api/pool-quota-probe.ts +142 -90
- package/src/codex/auth-api/reset-credit-service.ts +1 -1
- package/src/codex/catalog/gather-capture.ts +1 -0
- package/src/codex/catalog/model-hints.ts +3 -3
- package/src/codex/catalog/provider-models.ts +2 -0
- package/src/codex/low-quota-events.ts +28 -0
- package/src/codex/low-quota-observer.ts +23 -0
- package/src/codex/low-quota-protection.ts +272 -0
- package/src/codex/quota-query-backoff.ts +148 -0
- package/src/codex/quota.ts +7 -2
- package/src/config/diagnostics.ts +10 -1
- package/src/config/load-degrade.ts +17 -0
- package/src/config/mutation-lock.ts +3 -4
- package/src/config/process-state.ts +1 -1
- package/src/config/provider-validation.ts +22 -0
- package/src/config/schema/blocked-model-redirects.ts +17 -0
- package/src/config/schema/config-schema.ts +4 -1
- package/src/config/schema/leaf-validators.ts +33 -2
- package/src/config/serving-runtimes.ts +412 -0
- package/src/generated/compatibility-version.json +211 -115
- package/src/generated/model-metadata.ts +3 -3
- package/src/images/loop.ts +3 -0
- package/src/lib/errors.ts +2 -1
- package/src/lib/handled-signal-exit.ts +23 -0
- package/src/lib/request-failure-model.ts +1 -0
- package/src/lib/self-launch-argv.ts +9 -0
- package/src/lib/shadow-call.ts +5 -2
- package/src/lib/strict-semver.ts +26 -0
- package/src/lib/transient-service-path.ts +16 -0
- package/src/lib/windows-service-wrappers.ts +10 -6
- package/src/lib/winsw.ts +3 -1
- package/src/oauth/anthropic-model-routes.ts +105 -0
- package/src/oauth/anthropic-routing.ts +85 -48
- package/src/oauth/devin.ts +135 -5
- package/src/oauth/index.ts +20 -3
- package/src/oauth/kiro-terminal-failover.ts +23 -9
- package/src/oauth/pool-settings-capability.ts +10 -2
- package/src/oauth/store.ts +1 -1
- package/src/protocols/encoders/adapter-events.ts +3 -1
- package/src/providers/github-copilot-context.ts +27 -0
- package/src/providers/kiro-models.ts +4 -0
- package/src/providers/model-rename-fields.ts +3 -0
- package/src/providers/quota/account-cache.ts +8 -3
- package/src/providers/quota/devin.ts +286 -0
- package/src/providers/quota/vendor-probes-oauth.ts +6 -2
- package/src/providers/quota.ts +1 -1
- package/src/providers/registry/entries-core.ts +1 -1
- package/src/providers/registry/model-seeds.ts +5 -2
- package/src/responses/compaction-images.ts +27 -0
- package/src/router.ts +104 -16
- package/src/routing/analytics.ts +1 -0
- package/src/server/auth-cors.ts +9 -1
- package/src/server/background-lifecycle.ts +21 -10
- package/src/server/chat-completions.ts +2 -1
- package/src/server/chat-native.ts +1 -0
- package/src/server/index.ts +4 -8
- package/src/server/inference/client-encoder-delivery.ts +3 -0
- package/src/server/management/context.ts +3 -0
- package/src/server/management/low-quota-routes.ts +14 -0
- package/src/server/management/oauth-account-routes.ts +21 -1
- package/src/server/management/provider-overwrite-carry.ts +79 -6
- package/src/server/management/provider-routes.ts +41 -28
- package/src/server/management/route-registry.ts +1 -0
- package/src/server/management-api.ts +7 -0
- package/src/server/responses/adapter-continuation.ts +3 -0
- package/src/server/responses/adapter-delivery.ts +3 -0
- package/src/server/responses/adapter-dispatch.ts +20 -4
- package/src/server/responses/antigravity-validation-refusal.ts +20 -0
- package/src/server/responses/core-auth.ts +1 -0
- package/src/server/responses/core-codex-account.ts +1 -0
- package/src/server/responses/core-normalize.ts +4 -0
- package/src/server/responses/passthrough-dispatch.ts +8 -1
- package/src/server/responses/request-sidecar-auth.ts +2 -0
- package/src/server/responses/request-transport.ts +27 -10
- package/src/server/responses/response-effects.ts +4 -1
- package/src/server/responses/run-turn-execution.ts +126 -42
- package/src/server/responses/sidecar-execution.ts +4 -1
- package/src/service/diagnostics.ts +3 -1
- package/src/service/guarded-manager-target.ts +39 -12
- package/src/service/launchd.ts +14 -6
- package/src/service/repair.ts +6 -1
- package/src/service/service-child-ownership.ts +168 -0
- package/src/service/state.ts +22 -4
- package/src/service/systemd.ts +2 -2
- package/src/service/windows-guarded-manager.ts +530 -0
- package/src/service/windows-taskxml.ts +2 -2
- package/src/service.ts +2 -0
- package/src/types/config.ts +26 -0
- package/src/types/provider.ts +12 -0
- package/src/types/request.ts +2 -0
- package/src/types.ts +1 -0
- package/src/usage/expected-prices.ts +13 -0
- package/src/usage/telemetry-contract.ts +1 -0
- package/src/vision/anthropic-describe.ts +2 -1
- package/src/web-search/anthropic-executor.ts +2 -1
- package/src/web-search/loop.ts +1 -0
- package/gui/dist/assets/App-DDbgOTh6.js +0 -51
- package/gui/dist/assets/index-CuaBJkG4.js +0 -86
- package/gui/dist/assets/quota-summary-COppQSVq.js +0 -1
|
@@ -23,9 +23,8 @@ import {
|
|
|
23
23
|
import { EMPTY_EXEC_OUTPUT_MESSAGE, annotateCodeModeHostFailure, normalizeEmptyExecToolResultText } from "../exec-tool-result-normalize";
|
|
24
24
|
import { identifyRoutedModel } from "../identity";
|
|
25
25
|
import {
|
|
26
|
-
countKiroUninlinableImages,
|
|
27
26
|
extractKiroImages,
|
|
28
|
-
|
|
27
|
+
kiroImageOmissionMarker,
|
|
29
28
|
type KiroImage,
|
|
30
29
|
} from "../kiro-images";
|
|
31
30
|
import { convertKiroToolContext } from "../kiro-tools";
|
|
@@ -105,6 +104,7 @@ export function kiroCompletionTool(): Record<string, unknown> {
|
|
|
105
104
|
};
|
|
106
105
|
}
|
|
107
106
|
|
|
107
|
+
/** Build the Kiro wire request, retaining markers for images that cannot be inlined. */
|
|
108
108
|
export function buildKiroPayload(
|
|
109
109
|
parsed: OcxParsedRequest,
|
|
110
110
|
profileArn: string | undefined,
|
|
@@ -245,9 +245,9 @@ export function buildKiroPayload(
|
|
|
245
245
|
if (msg.role === "user" || msg.role === "developer") {
|
|
246
246
|
const content = (msg as { content: string | OcxContentPart[] }).content;
|
|
247
247
|
const images = extractKiroImages(content);
|
|
248
|
-
// Kiro inlines base64 bytes only.
|
|
249
|
-
//
|
|
250
|
-
const marker =
|
|
248
|
+
// Kiro inlines base64 bytes only. Neither remote references nor malformed
|
|
249
|
+
// inline data URLs can be forwarded; leave a bounded, URL-free marker.
|
|
250
|
+
const marker = kiroImageOmissionMarker(content);
|
|
251
251
|
const text = userContentText(content);
|
|
252
252
|
pushUser(marker ? (text ? text + "\n" + marker : marker) : text, images);
|
|
253
253
|
} else if (msg.role === "assistant") {
|
|
@@ -295,14 +295,14 @@ export function buildKiroPayload(
|
|
|
295
295
|
const annotatedExecText = normalizedExecText === undefined && codeModeExecName !== undefined
|
|
296
296
|
? annotateCodeModeHostFailure(text, execOptions)
|
|
297
297
|
: undefined;
|
|
298
|
-
const
|
|
298
|
+
const omittedImageMarker = kiroImageOmissionMarker(tr.content);
|
|
299
299
|
// Appended to the SELECTED result text, not to `text`: when an exec normalization
|
|
300
300
|
// fires, resultText below takes normalizedExecText/annotatedExecText instead, and
|
|
301
301
|
// a marker attached to `text` would be dropped — reinstating the silent loss this
|
|
302
302
|
// exists to remove.
|
|
303
303
|
const chosenText = normalizedExecText ?? annotatedExecText ?? (text.trim() ? text : KIRO_EMPTY_TOOL_RESULT_MESSAGE);
|
|
304
|
-
const resultText =
|
|
305
|
-
? (chosenText ? chosenText + "\n" +
|
|
304
|
+
const resultText = omittedImageMarker
|
|
305
|
+
? (chosenText ? chosenText + "\n" + omittedImageMarker : omittedImageMarker)
|
|
306
306
|
: chosenText;
|
|
307
307
|
const images = extractKiroImages(tr.content);
|
|
308
308
|
const toolUseId = normalizeToolId(tr.toolCallId);
|
|
@@ -315,8 +315,8 @@ export function buildKiroPayload(
|
|
|
315
315
|
? (annotatedExecText ?? text) : undefined;
|
|
316
316
|
// The grouping path rebuilds a collapsed turn's content from these texts, so the
|
|
317
317
|
// marker has to ride along here too or an adjacent-result turn loses it.
|
|
318
|
-
const rawGroupText =
|
|
319
|
-
? (rawGroupBase ? rawGroupBase + "\n" +
|
|
318
|
+
const rawGroupText = omittedImageMarker
|
|
319
|
+
? (rawGroupBase ? rawGroupBase + "\n" + omittedImageMarker : omittedImageMarker)
|
|
320
320
|
: rawGroupBase;
|
|
321
321
|
const last = turns.at(-1);
|
|
322
322
|
if (
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { OcxContentPart } from "../types";
|
|
2
2
|
import { normalizeImageTargets, type NormalizeOptions, type NormalizeTarget } from "./anthropic-image-normalize";
|
|
3
|
+
import { MAX_INPUT_BASE64_LENGTH, MAX_INPUT_PIXELS } from "./anthropic-image-codec";
|
|
4
|
+
import { sniffImageDimensions } from "./anthropic-image-guard";
|
|
3
5
|
|
|
4
6
|
// CodeWhisperer native image part (matches Kiro IDE wire format): the base64 bytes live directly in
|
|
5
7
|
// userInputMessage.images, NOT in userInputMessageContext. Verified against kiro-gateway.
|
|
@@ -8,8 +10,7 @@ export interface KiroImage {
|
|
|
8
10
|
source: { bytes: string }; // pure base64, no "data:...;base64," prefix
|
|
9
11
|
}
|
|
10
12
|
|
|
11
|
-
|
|
12
|
-
// inlined as bytes here; remote URLs are not fetchable at request-build time.
|
|
13
|
+
/** Parse inline image bytes; remote URLs are not fetched at request-build time. */
|
|
13
14
|
function parseDataUrlImage(imageUrl: string): KiroImage | undefined {
|
|
14
15
|
if (!imageUrl.startsWith("data:")) return undefined;
|
|
15
16
|
const comma = imageUrl.indexOf(",");
|
|
@@ -65,17 +66,36 @@ export function kiroUninlinableImageMarker(count: number): string {
|
|
|
65
66
|
return "[" + String(count) + " images omitted: remote image references are not supported by this provider]";
|
|
66
67
|
}
|
|
67
68
|
|
|
69
|
+
/** Report malformed inline references separately from remote URLs. */
|
|
70
|
+
export function kiroImageOmissionMarker(content: string | OcxContentPart[]): string {
|
|
71
|
+
if (typeof content === "string") return "";
|
|
72
|
+
const remote = kiroUninlinableImageMarker(countKiroUninlinableImages(content));
|
|
73
|
+
let malformed = 0;
|
|
74
|
+
for (const part of content) {
|
|
75
|
+
if (part.type === "image" && part.imageUrl.startsWith("data:") && !parseDataUrlImage(part.imageUrl)) malformed++;
|
|
76
|
+
}
|
|
77
|
+
const inline = malformed === 1
|
|
78
|
+
? "[image omitted: malformed inline image data URL]"
|
|
79
|
+
: malformed > 1 ? `[${malformed} images omitted: malformed inline image data URLs]` : "";
|
|
80
|
+
return [remote, inline].filter(Boolean).join("\n");
|
|
81
|
+
}
|
|
82
|
+
|
|
68
83
|
/**
|
|
69
|
-
* Conservative POLICY caps for the CodeWhisperer GenerateAssistantResponse payload
|
|
70
|
-
*
|
|
84
|
+
* Conservative POLICY caps for the CodeWhisperer GenerateAssistantResponse payload.
|
|
85
|
+
* The 100-image request cap comes from Kiro's IMAGE_COUNT_EXCEEDED error
|
|
86
|
+
* ("101 exceeds limit 100"); the per-message and byte limits are undocumented
|
|
87
|
+
* and derived from adjacent AWS surfaces
|
|
71
88
|
* (devlog/260714_image_normalization_pipeline/050): Bedrock `Message` allows 20 images
|
|
72
89
|
* per message (Converse), and `InvokeModel` caps requests at 25,000,000 bytes — 18MiB
|
|
73
90
|
* bounds the IMAGE share of the body with headroom for text/tools.
|
|
74
91
|
*/
|
|
75
92
|
export const KIRO_IMAGE_BASE64_BUDGET = 18 * 1024 * 1024;
|
|
76
93
|
export const KIRO_MAX_IMAGES_PER_MESSAGE = 20;
|
|
94
|
+
export const KIRO_MAX_IMAGES_PER_REQUEST = 100;
|
|
77
95
|
|
|
78
96
|
const COUNT_CAP_NOTE = "[image omitted: exceeded the 20-image per-message cap; oldest images in this message were dropped]";
|
|
97
|
+
const REQUEST_CAP_NOTE = "[images omitted: exceeded the 100-image request cap; oldest images in this message were dropped]";
|
|
98
|
+
const REQUEST_CAP_EMPTY_NOTE = "[images omitted: exceeded the 100-image request cap; no images remain in this message]";
|
|
79
99
|
|
|
80
100
|
/** A kiro wire message that can carry images (history userInputMessage or currentMessage). */
|
|
81
101
|
interface KiroImageCarrier {
|
|
@@ -105,11 +125,20 @@ function appendNote(carrier: KiroImageCarrier, note: string): void {
|
|
|
105
125
|
carrier.content = carrier.content ? `${carrier.content}\n${note}` : note;
|
|
106
126
|
}
|
|
107
127
|
|
|
128
|
+
/** Cheap structural check (no decode or encode) for the request-wide count. */
|
|
129
|
+
function countsTowardRequestCap(image: KiroImage): boolean {
|
|
130
|
+
const b64 = typeof image.source?.bytes === "string" ? image.source.bytes : "";
|
|
131
|
+
if (b64.length === 0 || b64.length > MAX_INPUT_BASE64_LENGTH) return false;
|
|
132
|
+
const dims = sniffImageDimensions(b64);
|
|
133
|
+
return dims !== null && dims.width * dims.height <= MAX_INPUT_PIXELS;
|
|
134
|
+
}
|
|
135
|
+
|
|
108
136
|
/**
|
|
109
137
|
* Apply the generous image pipeline to a built CodeWhisperer payload (mutates in
|
|
110
|
-
* place): per-message 20-image cap
|
|
111
|
-
*
|
|
112
|
-
*
|
|
138
|
+
* place): per-message 20-image cap, then the 100-image request cap over
|
|
139
|
+
* structurally usable images (oldest dropped), then the shared tier machinery
|
|
140
|
+
* with the kiro budget and terminal-overflow DROP (kiro has no downstream guard).
|
|
141
|
+
* Test seams (encode/validate) forward into the core.
|
|
113
142
|
*/
|
|
114
143
|
export async function normalizeKiroImages(
|
|
115
144
|
payload: unknown,
|
|
@@ -126,6 +155,28 @@ export async function normalizeKiroImages(
|
|
|
126
155
|
appendNote(carrier, COUNT_CAP_NOTE);
|
|
127
156
|
}
|
|
128
157
|
|
|
158
|
+
// Request count cap BEFORE the byte budget: a surplus image must not push survivors to
|
|
159
|
+
// lower tiers, which #4532 then pins across turns. Only structurally usable images count
|
|
160
|
+
// (bytes present, within the bomb limits, dimensions sniffable), so a corrupt image cannot
|
|
161
|
+
// evict valid history; the normalizer below still drops it with its own marker. This stays
|
|
162
|
+
// a cheap header check: truncated data that still sniffs as an image does count.
|
|
163
|
+
let excess = carriers.reduce((count, carrier) => count + (carrier.images ?? []).filter(countsTowardRequestCap).length, 0)
|
|
164
|
+
- KIRO_MAX_IMAGES_PER_REQUEST;
|
|
165
|
+
for (const carrier of carriers) {
|
|
166
|
+
if (excess <= 0) break;
|
|
167
|
+
const images = carrier.images;
|
|
168
|
+
if (!images?.length) continue;
|
|
169
|
+
const kept: KiroImage[] = [];
|
|
170
|
+
for (const image of images) {
|
|
171
|
+
if (excess > 0 && countsTowardRequestCap(image)) excess--;
|
|
172
|
+
else kept.push(image);
|
|
173
|
+
}
|
|
174
|
+
if (kept.length === images.length) continue;
|
|
175
|
+
if (kept.length === 0) delete carrier.images;
|
|
176
|
+
else carrier.images = kept;
|
|
177
|
+
appendNote(carrier, kept.length === 0 ? REQUEST_CAP_EMPTY_NOTE : REQUEST_CAP_NOTE);
|
|
178
|
+
}
|
|
179
|
+
|
|
129
180
|
// Targets over the survivors, oldest→newest across carriers. Drops resolve the image
|
|
130
181
|
// by OBJECT IDENTITY at execution time (indices go stale after earlier splices) and
|
|
131
182
|
// delete an emptied images field per the builder's omission contract.
|
|
@@ -6,6 +6,7 @@ import { applyExplicitChatDeveloperRole } from "./developer-role";
|
|
|
6
6
|
import { openRouterProviderPayload, resolveOpenRouterRouting } from "../../providers/openrouter-routing";
|
|
7
7
|
import { resolveVercelGatewayRouting, vercelGatewayProviderPayload } from "../../providers/vercel-gateway-routing";
|
|
8
8
|
import { fastPolicyForModel } from "../../providers/service-tier";
|
|
9
|
+
import { applyGithubCopilotContextTier } from "../../providers/github-copilot-context";
|
|
9
10
|
import { canonicalFastTierMarker, decideTier, type ResolvedFastPolicy } from "../../providers/fastwire";
|
|
10
11
|
import { debugProviderDiagnostic } from "../../lib/debug";
|
|
11
12
|
import { isDebugEnabled } from "../../lib/debug-settings";
|
|
@@ -53,6 +54,7 @@ export function buildOpenAIChatPassthroughRequest(
|
|
|
53
54
|
stream: boolean,
|
|
54
55
|
fastPolicy: ResolvedFastPolicy = fastPolicyForModel(provider, modelId, undefined, "chat"),
|
|
55
56
|
fastMode?: boolean,
|
|
57
|
+
providerName?: string,
|
|
56
58
|
): AdapterRequest {
|
|
57
59
|
const { url, headers, hasCredential } = openAIChatTransport(provider);
|
|
58
60
|
|
|
@@ -153,7 +155,7 @@ export function buildOpenAIChatPassthroughRequest(
|
|
|
153
155
|
body.stream_options = rawBody.stream_options;
|
|
154
156
|
}
|
|
155
157
|
|
|
156
|
-
const bodyJson = JSON.stringify(body);
|
|
158
|
+
const bodyJson = JSON.stringify(applyGithubCopilotContextTier(body, provider, modelId, providerName));
|
|
157
159
|
|
|
158
160
|
if (isDebugEnabled()) {
|
|
159
161
|
let host = "upstream";
|
|
@@ -13,6 +13,7 @@ import { isDebugEnabled } from "../lib/debug-settings";
|
|
|
13
13
|
import { openRouterProviderPayload, resolveOpenRouterRouting } from "../providers/openrouter-routing";
|
|
14
14
|
import { resolveVercelGatewayRouting, vercelGatewayProviderPayload } from "../providers/vercel-gateway-routing";
|
|
15
15
|
import { fastPolicyForModel } from "../providers/service-tier";
|
|
16
|
+
import { applyGithubCopilotContextTier } from "../providers/github-copilot-context";
|
|
16
17
|
import { createAdapterTierMetadata, decideTier, type AdapterTierMetadata } from "../providers/fastwire";
|
|
17
18
|
import {
|
|
18
19
|
isTranslatorBudgetExceededError,
|
|
@@ -229,7 +230,7 @@ export function createOpenAIChatAdapter(provider: OcxProviderConfig): ProviderAd
|
|
|
229
230
|
}
|
|
230
231
|
if (parsed.stream) body.stream_options = { include_usage: true };
|
|
231
232
|
|
|
232
|
-
const bodyJson = JSON.stringify(body);
|
|
233
|
+
const bodyJson = JSON.stringify(applyGithubCopilotContextTier(body, provider, parsed.modelId, incoming?.providerName));
|
|
233
234
|
const actualServiceTier = typeof body.service_tier === "string" ? body.service_tier : null;
|
|
234
235
|
const tierLog = createAdapterTierMetadata(
|
|
235
236
|
parsed.options.tierObservation,
|
|
@@ -3,6 +3,7 @@ import { nameRoutedIdentity, repairIdentityInResponsesBody, stripRoutedIdentity
|
|
|
3
3
|
import { stripBracketedModelSuffix } from "../openai-chat";
|
|
4
4
|
import { normalizeOpenCodeGoAdditionalTools } from "../opencode-go-additional-tools";
|
|
5
5
|
import { isXaiResponsesDestination } from "../../providers/xai-transport";
|
|
6
|
+
import { applyGithubCopilotContextTier } from "../../providers/github-copilot-context";
|
|
6
7
|
import { Buffer } from "node:buffer";
|
|
7
8
|
import type { IncomingMeta, ProviderAdapter } from "../base";
|
|
8
9
|
import { namespacedToolName, type AdapterEvent, type OcxParsedRequest, type OcxProviderConfig, type OcxUsage, type TierDecision } from "../../types";
|
|
@@ -538,7 +539,7 @@ export function createResponsesPassthroughAdapter(provider: OcxProviderConfig):
|
|
|
538
539
|
if (!isCanonicalOpenAiForwardProvider(provider)) {
|
|
539
540
|
validateFinalCustomToolCompatibility(finalBody, provider.supportsResponsesCustomTools);
|
|
540
541
|
}
|
|
541
|
-
const body = JSON.stringify(finalBody);
|
|
542
|
+
const body = JSON.stringify(applyGithubCopilotContextTier(finalBody, provider, parsed.modelId, incoming.providerName));
|
|
542
543
|
const releaseBodyObservation = translatorBudget.observeExternallyCapped(
|
|
543
544
|
"passthrough_serialization",
|
|
544
545
|
Buffer.byteLength(body, "utf8"),
|
|
@@ -83,6 +83,8 @@ function buildResponseJSONWithBudget(
|
|
|
83
83
|
modelId: string,
|
|
84
84
|
options?: {
|
|
85
85
|
hideThinkingSummary?: boolean;
|
|
86
|
+
/** Provider policy: suppress raw content-channel reasoning, keep provider-authored summaries. */
|
|
87
|
+
hideRawReasoning?: boolean;
|
|
86
88
|
toolNsMap?: Map<string, { namespace: string; name: string; freeform?: true }>;
|
|
87
89
|
/** Request-visible tool names. Required for client calls when enforcement is explicitly enabled. */
|
|
88
90
|
declaredToolNames?: ReadonlySet<string>;
|
|
@@ -102,6 +104,13 @@ function buildResponseJSONWithBudget(
|
|
|
102
104
|
translatorBudget?: TranslatorBudget;
|
|
103
105
|
/** Conversation identity for the reasoning replay cache (issue #950). */
|
|
104
106
|
replayCacheScope?: OcxReasoningReplayScopeRef;
|
|
107
|
+
/**
|
|
108
|
+
* Fold-only callers whose body never reaches the client (direct client encoders): hidden raw
|
|
109
|
+
* reasoning is still handed to the replay cache, but no client-bound `ocxr1` envelope is
|
|
110
|
+
* materialized. Encoding reserves roughly ten times the text against the translator budget,
|
|
111
|
+
* so a block that fit live delivery could otherwise overflow here and lose the cache write.
|
|
112
|
+
*/
|
|
113
|
+
omitHiddenReasoningEnvelope?: boolean;
|
|
105
114
|
},
|
|
106
115
|
): Record<string, unknown> {
|
|
107
116
|
const responseId = `resp_${uuid()}`;
|
|
@@ -284,7 +293,12 @@ function buildResponseJSONWithBudget(
|
|
|
284
293
|
const rawText = joinChunks(currentRawReasoning);
|
|
285
294
|
if (!rawText) return;
|
|
286
295
|
rawReasoningForNextToolCall = rawText;
|
|
287
|
-
if (options?.hideThinkingSummary === true) {
|
|
296
|
+
if (options?.hideThinkingSummary === true || options?.hideRawReasoning === true) {
|
|
297
|
+
if (options?.omitHiddenReasoningEnvelope === true) {
|
|
298
|
+
budget?.releaseRetained(currentRawReasoning.bytes, { kind: "reasoning" });
|
|
299
|
+
currentRawReasoning = emptyChunks();
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
288
302
|
// Same contract as the streaming path: no visible reasoning, txt-only envelope round-trip.
|
|
289
303
|
pushOutput({
|
|
290
304
|
type: "reasoning", id: `rs_${uuid()}`, summary: [],
|
package/src/bridge/sse.ts
CHANGED
|
@@ -80,6 +80,8 @@ export function bridgeToResponsesSSE(
|
|
|
80
80
|
*/
|
|
81
81
|
localUpstream?: boolean;
|
|
82
82
|
hideThinkingSummary?: boolean;
|
|
83
|
+
/** Provider policy: suppress raw content-channel reasoning, keep provider-authored summaries. */
|
|
84
|
+
hideRawReasoning?: boolean;
|
|
83
85
|
/**
|
|
84
86
|
* Remote compaction v2 turn: accumulate all assistant text and, on done, emit ONE synthetic
|
|
85
87
|
* `{type:"compaction", encrypted_content:"ocx1:"+base64(text)}` output item before
|
|
@@ -975,7 +977,7 @@ export function bridgeToResponsesSSE(
|
|
|
975
977
|
break;
|
|
976
978
|
}
|
|
977
979
|
case "reasoning_raw_delta": {
|
|
978
|
-
if (options?.hideThinkingSummary) {
|
|
980
|
+
if (options?.hideThinkingSummary || options?.hideRawReasoning) {
|
|
979
981
|
hiddenRawReasoning = appendString(
|
|
980
982
|
hiddenRawReasoning,
|
|
981
983
|
event.text,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { loadConfig } from "../config";
|
|
2
2
|
import { isReservedCodexAccountWord, reportCodexAccountTargetError, resolveCodexAccountTarget } from "./account-target";
|
|
3
3
|
import { hasPassiveAccountQuota } from "../providers/quota";
|
|
4
|
-
import { closeSync, openSync, readSync } from "node:fs";
|
|
4
|
+
import { closeSync, openSync, readSync, readFileSync, statSync } from "node:fs";
|
|
5
5
|
import {
|
|
6
6
|
MAX_ACCOUNT_PRIORITY,
|
|
7
7
|
MIN_ACCOUNT_PRIORITY,
|
|
@@ -47,6 +47,7 @@ const EXTENDED_USAGE = `Usage:
|
|
|
47
47
|
ocx account pause-exhausted <provider> [--json]
|
|
48
48
|
ocx account strategy <provider> [<quota|round-robin|fill-first|least-loaded|reset-first>] [--json]
|
|
49
49
|
ocx account sticky <provider> [<1-100>] [--json]
|
|
50
|
+
ocx account routes anthropic [--file <json-file>|--clear] [--json]
|
|
50
51
|
ocx account remove <provider> <id|alias|main> --yes [--json]
|
|
51
52
|
ocx account clear-cooldown <provider> <id|alias|main> [--json]
|
|
52
53
|
ocx account add-key <provider> [--label <label>] [--json]
|
|
@@ -1090,3 +1091,32 @@ export async function cmdAlias(args: string[], deps: AccountDeps): Promise<numbe
|
|
|
1090
1091
|
else console.log(alias ? `${name}: ${requestedId} is now “${alias}”` : `${name}: cleared alias for ${requestedId}`);
|
|
1091
1092
|
return 0;
|
|
1092
1093
|
}
|
|
1094
|
+
|
|
1095
|
+
/** Read or replace the ordered Anthropic model routes through the unified settings API. */
|
|
1096
|
+
export async function cmdRoutes(args: string[], deps: AccountDeps): Promise<number> {
|
|
1097
|
+
const wantsJson = flag(args, "--json");
|
|
1098
|
+
const file = flagValue(args, "--file");
|
|
1099
|
+
const clear = flag(args, "--clear");
|
|
1100
|
+
if (args.shift() !== "anthropic" || args.length || (file.found && (!file.value || clear))) return usage();
|
|
1101
|
+
const baseUrl = await resolveBaseUrl(deps);
|
|
1102
|
+
if (!baseUrl) return proxyUnreachable();
|
|
1103
|
+
let routes: unknown;
|
|
1104
|
+
if (file.found) {
|
|
1105
|
+
try {
|
|
1106
|
+
const info = statSync(file.value!);
|
|
1107
|
+
if (!info.isFile() || info.size > 64 * 1024) return usage("Error: route file must be a regular JSON file of at most 64 KiB");
|
|
1108
|
+
routes = JSON.parse(readFileSync(file.value!, "utf8"));
|
|
1109
|
+
} catch {
|
|
1110
|
+
return usage("Error: could not read a valid JSON route file");
|
|
1111
|
+
}
|
|
1112
|
+
}
|
|
1113
|
+
const writing = file.found || clear;
|
|
1114
|
+
const response = await apiJson(deps, baseUrl, writing ? "PUT" : "GET",
|
|
1115
|
+
writing ? "/api/pool/settings" : "/api/pool/settings?provider=anthropic",
|
|
1116
|
+
writing ? { provider: "anthropic", routes: clear ? null : routes } : undefined);
|
|
1117
|
+
if (response.status === 0) return proxyUnreachable(response.transportError);
|
|
1118
|
+
if (response.status !== 200) return apiError(response.json, "failed to manage Anthropic routes", response.status);
|
|
1119
|
+
if (wantsJson) console.log(JSON.stringify(response.json, null, 2));
|
|
1120
|
+
else console.log(JSON.stringify(response.json.routes ?? [], null, 2));
|
|
1121
|
+
return 0;
|
|
1122
|
+
}
|
package/src/cli/account.ts
CHANGED
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
cmdRefresh,
|
|
16
16
|
cmdRemove,
|
|
17
17
|
cmdSticky,
|
|
18
|
+
cmdRoutes,
|
|
18
19
|
cmdStrategy,
|
|
19
20
|
} from "./account-extended";
|
|
20
21
|
import { apiError, apiJson, classifyAccount, fetchRows, proxyUnreachable, resolveBaseUrl, type AccountDeps, type AccountRow, type AccountType, type ApiResult }
|
|
@@ -55,6 +56,7 @@ const ACCOUNT_USAGE = `Usage:
|
|
|
55
56
|
ocx account pause-exhausted <provider> [--json]
|
|
56
57
|
ocx account strategy <provider> [<quota|round-robin|fill-first|least-loaded|reset-first>] [--json]
|
|
57
58
|
ocx account sticky <provider> [<1-100>] [--json]
|
|
59
|
+
ocx account routes anthropic [--file <json-file>|--clear] [--json]
|
|
58
60
|
ocx account remove <provider> <account-or-key-id|alias|main> --yes [--json]
|
|
59
61
|
ocx account clear-cooldown <provider> <account-id|alias|main> [--json]
|
|
60
62
|
ocx account add-key <provider> [--label <label>] [--json]
|
|
@@ -417,6 +419,7 @@ export async function cmdAccount(args: string[], deps: AccountDeps = {}): Promis
|
|
|
417
419
|
if (sub === "pause-exhausted") return await cmdPauseExhausted(rest, deps);
|
|
418
420
|
if (sub === "strategy") return await cmdStrategy(rest, deps);
|
|
419
421
|
if (sub === "sticky") return await cmdSticky(rest, deps);
|
|
422
|
+
if (sub === "routes") return await cmdRoutes(rest, deps);
|
|
420
423
|
if (sub === "remove") return await cmdRemove(rest, deps);
|
|
421
424
|
if (sub === "clear-cooldown") return await cmdClearCooldown(rest, deps);
|
|
422
425
|
if (sub === "add-key") return await cmdAddKey(rest, deps);
|
package/src/cli/capabilities.ts
CHANGED
|
@@ -611,6 +611,19 @@ export const CAPABILITIES: readonly Capability[] = [
|
|
|
611
611
|
json: "envelope",
|
|
612
612
|
details: ["Only meaningful under the sticky-capable strategies; the pool strategy is the other half of this setting."],
|
|
613
613
|
},
|
|
614
|
+
{
|
|
615
|
+
command: ["account", "routes"],
|
|
616
|
+
summary: "Read, replace, or clear Anthropic OAuth model account routes.",
|
|
617
|
+
routes: [{ method: "GET", path: "/api/pool/settings" }, { method: "PUT", path: "/api/pool/settings" }],
|
|
618
|
+
flags: [
|
|
619
|
+
{ name: "--file", value: "string", summary: "Read a bounded JSON route array from a local file." },
|
|
620
|
+
{ name: "--clear", value: "boolean", summary: "Remove the stored routes." },
|
|
621
|
+
{ name: "--json", value: "boolean", summary: "Emit the unified settings response as JSON." },
|
|
622
|
+
],
|
|
623
|
+
mutates: true,
|
|
624
|
+
json: "envelope",
|
|
625
|
+
details: ["Only anthropic is supported. The server validates route names, patterns, and account IDs."],
|
|
626
|
+
},
|
|
614
627
|
{
|
|
615
628
|
command: ["account", "auto-switch"],
|
|
616
629
|
summary: "Show or set the usage percentage at which a pool moves to another account.",
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { ServiceChildOwnershipDecision } from "../service/service-child-ownership";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Connected-client starts publish PID and runtime records too, so a supervised service child
|
|
5
|
+
* must not reach them past a foreign owner any more than the server path may. The server path
|
|
6
|
+
* rechecks inside `bindAndPublishStartOwnership`; this is the same fence for
|
|
7
|
+
* `startClientRuntime`, which binds, publishes and then blocks for the process lifetime.
|
|
8
|
+
*
|
|
9
|
+
* The lease is held from acquisition until the runtime reports its records published (or the
|
|
10
|
+
* start fails), and the ownership decision is re-read under it before anything binds. A refusal
|
|
11
|
+
* releases the lease and hands the refusal to `stayOut`, which exits.
|
|
12
|
+
*/
|
|
13
|
+
export async function startClientRuntimeUnderOwnershipLease(deps: {
|
|
14
|
+
acquireLease: () => { release(): void };
|
|
15
|
+
decide: () => ServiceChildOwnershipDecision;
|
|
16
|
+
stayOut: (refusal: string) => never;
|
|
17
|
+
start: (afterPublish: () => void) => Promise<void>;
|
|
18
|
+
}): Promise<void> {
|
|
19
|
+
const lease = deps.acquireLease();
|
|
20
|
+
let released = false;
|
|
21
|
+
const release = () => {
|
|
22
|
+
if (released) return;
|
|
23
|
+
released = true;
|
|
24
|
+
lease.release();
|
|
25
|
+
};
|
|
26
|
+
try {
|
|
27
|
+
const decision = deps.decide();
|
|
28
|
+
if (decision.kind === "stay-out") {
|
|
29
|
+
release();
|
|
30
|
+
deps.stayOut(decision.refusal);
|
|
31
|
+
}
|
|
32
|
+
await deps.start(release);
|
|
33
|
+
} finally {
|
|
34
|
+
release();
|
|
35
|
+
}
|
|
36
|
+
}
|
package/src/cli/dispatch.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ProxyRestartStartOutcome } from "./tray-proxy";
|
|
1
2
|
/**
|
|
2
3
|
* Registry-driven command dispatch (Phase 3 of the CLI deepening).
|
|
3
4
|
*
|
|
@@ -54,8 +55,8 @@ export interface CliDispatchDeps {
|
|
|
54
55
|
handleResolve: (args: ResolveArgs) => Promise<number>;
|
|
55
56
|
handleTrayProxyStart: (existingIsSuccess?: boolean) => Promise<boolean>;
|
|
56
57
|
handleTrayProxyRestart: () => Promise<void>;
|
|
57
|
-
handleRestartStartWhenStopped: () => Promise<
|
|
58
|
-
handleProxyRestart: (startWhenStopped: () => Promise<
|
|
58
|
+
handleRestartStartWhenStopped: (recoveringLiveRestart?: boolean) => Promise<ProxyRestartStartOutcome>;
|
|
59
|
+
handleProxyRestart: (startWhenStopped: (recoveringLiveRestart: boolean) => Promise<ProxyRestartStartOutcome>) => Promise<boolean>;
|
|
59
60
|
handleUninstall: () => Promise<void>;
|
|
60
61
|
handleStatus: () => Promise<void>;
|
|
61
62
|
handleRecoverHistory: () => Promise<void>;
|