@lcv-ideas-software/cross-review 4.0.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/CHANGELOG.md +2568 -0
- package/LICENSE +201 -0
- package/NOTICE +26 -0
- package/README.md +208 -0
- package/SECURITY.md +52 -0
- package/dist/scripts/api-streaming-smoke.d.ts +1 -0
- package/dist/scripts/api-streaming-smoke.js +78 -0
- package/dist/scripts/api-streaming-smoke.js.map +1 -0
- package/dist/scripts/runtime-default-smoke.d.ts +1 -0
- package/dist/scripts/runtime-default-smoke.js +88 -0
- package/dist/scripts/runtime-default-smoke.js.map +1 -0
- package/dist/scripts/runtime-smoke.d.ts +1 -0
- package/dist/scripts/runtime-smoke.js +148 -0
- package/dist/scripts/runtime-smoke.js.map +1 -0
- package/dist/scripts/smoke.d.ts +1 -0
- package/dist/scripts/smoke.js +6156 -0
- package/dist/scripts/smoke.js.map +1 -0
- package/dist/src/core/cache-manifest.d.ts +22 -0
- package/dist/src/core/cache-manifest.js +133 -0
- package/dist/src/core/cache-manifest.js.map +1 -0
- package/dist/src/core/caller-tokens.d.ts +32 -0
- package/dist/src/core/caller-tokens.js +240 -0
- package/dist/src/core/caller-tokens.js.map +1 -0
- package/dist/src/core/config.d.ts +9 -0
- package/dist/src/core/config.js +643 -0
- package/dist/src/core/config.js.map +1 -0
- package/dist/src/core/convergence.d.ts +5 -0
- package/dist/src/core/convergence.js +186 -0
- package/dist/src/core/convergence.js.map +1 -0
- package/dist/src/core/cost.d.ts +59 -0
- package/dist/src/core/cost.js +359 -0
- package/dist/src/core/cost.js.map +1 -0
- package/dist/src/core/file-config.d.ts +316 -0
- package/dist/src/core/file-config.js +490 -0
- package/dist/src/core/file-config.js.map +1 -0
- package/dist/src/core/orchestrator.d.ts +199 -0
- package/dist/src/core/orchestrator.js +3430 -0
- package/dist/src/core/orchestrator.js.map +1 -0
- package/dist/src/core/prompt-parts.d.ts +58 -0
- package/dist/src/core/prompt-parts.js +122 -0
- package/dist/src/core/prompt-parts.js.map +1 -0
- package/dist/src/core/relator-lottery.d.ts +23 -0
- package/dist/src/core/relator-lottery.js +112 -0
- package/dist/src/core/relator-lottery.js.map +1 -0
- package/dist/src/core/reports.d.ts +2 -0
- package/dist/src/core/reports.js +82 -0
- package/dist/src/core/reports.js.map +1 -0
- package/dist/src/core/session-store.d.ts +149 -0
- package/dist/src/core/session-store.js +1923 -0
- package/dist/src/core/session-store.js.map +1 -0
- package/dist/src/core/status.d.ts +61 -0
- package/dist/src/core/status.js +249 -0
- package/dist/src/core/status.js.map +1 -0
- package/dist/src/core/timeouts.d.ts +2 -0
- package/dist/src/core/timeouts.js +3 -0
- package/dist/src/core/timeouts.js.map +1 -0
- package/dist/src/core/types.d.ts +604 -0
- package/dist/src/core/types.js +36 -0
- package/dist/src/core/types.js.map +1 -0
- package/dist/src/dashboard/server.d.ts +2 -0
- package/dist/src/dashboard/server.js +339 -0
- package/dist/src/dashboard/server.js.map +1 -0
- package/dist/src/mcp/server.d.ts +54 -0
- package/dist/src/mcp/server.js +1584 -0
- package/dist/src/mcp/server.js.map +1 -0
- package/dist/src/observability/logger.d.ts +9 -0
- package/dist/src/observability/logger.js +24 -0
- package/dist/src/observability/logger.js.map +1 -0
- package/dist/src/peers/anthropic.d.ts +14 -0
- package/dist/src/peers/anthropic.js +290 -0
- package/dist/src/peers/anthropic.js.map +1 -0
- package/dist/src/peers/base.d.ts +72 -0
- package/dist/src/peers/base.js +416 -0
- package/dist/src/peers/base.js.map +1 -0
- package/dist/src/peers/deepseek.d.ts +12 -0
- package/dist/src/peers/deepseek.js +246 -0
- package/dist/src/peers/deepseek.js.map +1 -0
- package/dist/src/peers/errors.d.ts +2 -0
- package/dist/src/peers/errors.js +185 -0
- package/dist/src/peers/errors.js.map +1 -0
- package/dist/src/peers/gemini.d.ts +13 -0
- package/dist/src/peers/gemini.js +215 -0
- package/dist/src/peers/gemini.js.map +1 -0
- package/dist/src/peers/grok.d.ts +17 -0
- package/dist/src/peers/grok.js +346 -0
- package/dist/src/peers/grok.js.map +1 -0
- package/dist/src/peers/model-selection.d.ts +4 -0
- package/dist/src/peers/model-selection.js +260 -0
- package/dist/src/peers/model-selection.js.map +1 -0
- package/dist/src/peers/openai.d.ts +14 -0
- package/dist/src/peers/openai.js +299 -0
- package/dist/src/peers/openai.js.map +1 -0
- package/dist/src/peers/perplexity.d.ts +18 -0
- package/dist/src/peers/perplexity.js +375 -0
- package/dist/src/peers/perplexity.js.map +1 -0
- package/dist/src/peers/registry.d.ts +3 -0
- package/dist/src/peers/registry.js +77 -0
- package/dist/src/peers/registry.js.map +1 -0
- package/dist/src/peers/retry.d.ts +2 -0
- package/dist/src/peers/retry.js +36 -0
- package/dist/src/peers/retry.js.map +1 -0
- package/dist/src/peers/stub.d.ts +13 -0
- package/dist/src/peers/stub.js +344 -0
- package/dist/src/peers/stub.js.map +1 -0
- package/dist/src/peers/text.d.ts +18 -0
- package/dist/src/peers/text.js +39 -0
- package/dist/src/peers/text.js.map +1 -0
- package/dist/src/security/redact.d.ts +2 -0
- package/dist/src/security/redact.js +128 -0
- package/dist/src/security/redact.js.map +1 -0
- package/docs/api-keys.md +34 -0
- package/docs/architecture.md +118 -0
- package/docs/caching.md +135 -0
- package/docs/costs.md +40 -0
- package/docs/evidence-preflight.md +88 -0
- package/docs/github-security-baseline.md +32 -0
- package/docs/model-selection.md +105 -0
- package/docs/reports/cross-review-v2-api-capability-smoke-2026-04-30.md +354 -0
- package/docs/reports/cross-review-v2-format-recovery-findings-2026-04-28.md +223 -0
- package/docs/reports/cross-review-v2-official-provider-docs-refresh-2026-05-05.md +60 -0
- package/docs/reports/cross-review-v2-token-streaming-smoke-2026-04-30.md +119 -0
- package/package.json +88 -0
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
import { statusInstruction } from "../core/status.js";
|
|
2
|
+
import { BasePeerAdapter, StreamBuffer } from "./base.js";
|
|
3
|
+
import { classifyProviderError } from "./errors.js";
|
|
4
|
+
import { loadOpenAICtor } from "./openai.js";
|
|
5
|
+
import { withRetry } from "./retry.js";
|
|
6
|
+
import { userPrompt } from "./text.js";
|
|
7
|
+
function usageFromChat(usage) {
|
|
8
|
+
if (!usage)
|
|
9
|
+
return undefined;
|
|
10
|
+
const cacheHit = usage.prompt_cache_hit_tokens ?? 0;
|
|
11
|
+
const cacheMiss = usage.prompt_cache_miss_tokens ?? 0;
|
|
12
|
+
const result = {
|
|
13
|
+
input_tokens: usage.prompt_tokens,
|
|
14
|
+
output_tokens: usage.completion_tokens,
|
|
15
|
+
total_tokens: usage.total_tokens,
|
|
16
|
+
reasoning_tokens: usage.completion_tokens_details?.reasoning_tokens,
|
|
17
|
+
};
|
|
18
|
+
if (cacheHit > 0 || cacheMiss > 0) {
|
|
19
|
+
if (cacheHit > 0)
|
|
20
|
+
result.cache_read_tokens = cacheHit;
|
|
21
|
+
if (cacheMiss > 0)
|
|
22
|
+
result.cache_write_tokens = cacheMiss;
|
|
23
|
+
result.cache_provider_mode = "auto";
|
|
24
|
+
}
|
|
25
|
+
return result;
|
|
26
|
+
}
|
|
27
|
+
function chatText(response) {
|
|
28
|
+
return response.choices?.[0]?.message?.content?.trim() || JSON.stringify(response);
|
|
29
|
+
}
|
|
30
|
+
function deepSeekReasoningEffort(value) {
|
|
31
|
+
return value === "max" || value === "xhigh" ? "max" : "high";
|
|
32
|
+
}
|
|
33
|
+
function deepSeekThinking(config, override) {
|
|
34
|
+
return {
|
|
35
|
+
thinking: {
|
|
36
|
+
type: "enabled",
|
|
37
|
+
reasoning_effort: deepSeekReasoningEffort(override ?? config.reasoning_effort.deepseek),
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
export class DeepSeekAdapter extends BasePeerAdapter {
|
|
42
|
+
id = "deepseek";
|
|
43
|
+
provider = "deepseek";
|
|
44
|
+
model;
|
|
45
|
+
constructor(config, modelOverride) {
|
|
46
|
+
super(config);
|
|
47
|
+
this.model = modelOverride ?? config.models.deepseek;
|
|
48
|
+
}
|
|
49
|
+
async client() {
|
|
50
|
+
const apiKey = this.config.api_keys.deepseek;
|
|
51
|
+
if (!apiKey)
|
|
52
|
+
throw new Error("DEEPSEEK_API_KEY was not found in environment variables.");
|
|
53
|
+
const Ctor = await loadOpenAICtor();
|
|
54
|
+
return new Ctor({ apiKey, baseURL: "https://api.deepseek.com" });
|
|
55
|
+
}
|
|
56
|
+
async probe() {
|
|
57
|
+
const started = Date.now();
|
|
58
|
+
const authPresent = Boolean(this.config.api_keys.deepseek);
|
|
59
|
+
if (!authPresent) {
|
|
60
|
+
return {
|
|
61
|
+
peer: this.id,
|
|
62
|
+
provider: this.provider,
|
|
63
|
+
model: this.model,
|
|
64
|
+
available: false,
|
|
65
|
+
auth_present: false,
|
|
66
|
+
latency_ms: Date.now() - started,
|
|
67
|
+
model_selection: this.config.model_selection.deepseek,
|
|
68
|
+
message: "DEEPSEEK_API_KEY is missing.",
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
try {
|
|
72
|
+
const probeClient = await this.client();
|
|
73
|
+
await probeClient.models.list({ timeout: this.config.retry.timeout_ms });
|
|
74
|
+
return {
|
|
75
|
+
peer: this.id,
|
|
76
|
+
provider: this.provider,
|
|
77
|
+
model: this.model,
|
|
78
|
+
available: true,
|
|
79
|
+
auth_present: true,
|
|
80
|
+
latency_ms: Date.now() - started,
|
|
81
|
+
model_selection: this.config.model_selection.deepseek,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
catch (error) {
|
|
85
|
+
const failure = classifyProviderError(this.id, this.provider, this.model, error, 1, started);
|
|
86
|
+
return {
|
|
87
|
+
peer: this.id,
|
|
88
|
+
provider: this.provider,
|
|
89
|
+
model: this.model,
|
|
90
|
+
available: false,
|
|
91
|
+
auth_present: true,
|
|
92
|
+
latency_ms: Date.now() - started,
|
|
93
|
+
model_selection: this.config.model_selection.deepseek,
|
|
94
|
+
message: failure.message,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
async call(prompt, context) {
|
|
99
|
+
const started = Date.now();
|
|
100
|
+
return withRetry(this.config, async (attempt) => {
|
|
101
|
+
context.emit({
|
|
102
|
+
type: "peer.call.started",
|
|
103
|
+
session_id: context.session_id,
|
|
104
|
+
round: context.round,
|
|
105
|
+
peer: this.id,
|
|
106
|
+
message: `DeepSeek review attempt ${attempt}`,
|
|
107
|
+
});
|
|
108
|
+
const payload = {
|
|
109
|
+
...deepSeekThinking(this.config, context.reasoning_effort_override),
|
|
110
|
+
model: this.model,
|
|
111
|
+
messages: [
|
|
112
|
+
{ role: "system", content: this.systemPrompt(context) },
|
|
113
|
+
{ role: "user", content: `${userPrompt(prompt)}\n\n${statusInstruction()}` },
|
|
114
|
+
],
|
|
115
|
+
response_format: { type: "json_object" },
|
|
116
|
+
max_tokens: this.config.max_output_tokens,
|
|
117
|
+
};
|
|
118
|
+
// DeepSeek's OpenAI-compatible API accepts the non-OpenAI `thinking` body field;
|
|
119
|
+
// the OpenAI JS client forwards unknown body keys, and the real API smoke verifies it.
|
|
120
|
+
if (this.shouldStreamTokens(context)) {
|
|
121
|
+
const streamPayload = {
|
|
122
|
+
...payload,
|
|
123
|
+
stream: true,
|
|
124
|
+
stream_options: { include_usage: true },
|
|
125
|
+
};
|
|
126
|
+
const reviewClient = await this.client();
|
|
127
|
+
const stream = await reviewClient.chat.completions.create(streamPayload, {
|
|
128
|
+
signal: context.signal,
|
|
129
|
+
timeout: this.config.retry.timeout_ms,
|
|
130
|
+
});
|
|
131
|
+
const stream_buffer = new StreamBuffer(this.id);
|
|
132
|
+
const tokenStream = this.createTokenEventBuffer(context, "review", "chat.completion.chunk.delta");
|
|
133
|
+
let usage;
|
|
134
|
+
let modelReported;
|
|
135
|
+
let chunks = 0;
|
|
136
|
+
for await (const chunk of stream) {
|
|
137
|
+
chunks += 1;
|
|
138
|
+
modelReported = chunk.model ?? modelReported;
|
|
139
|
+
usage = usageFromChat(chunk.usage) ?? usage;
|
|
140
|
+
for (const choice of chunk.choices ?? []) {
|
|
141
|
+
const delta = choice.delta?.content ?? "";
|
|
142
|
+
stream_buffer.append(delta);
|
|
143
|
+
tokenStream.append(delta);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
const text = stream_buffer.text();
|
|
147
|
+
tokenStream.complete(text.length);
|
|
148
|
+
return this.resultFromText({
|
|
149
|
+
text,
|
|
150
|
+
raw: { streamed: true, provider: this.provider, chunks, model: modelReported },
|
|
151
|
+
usage,
|
|
152
|
+
started,
|
|
153
|
+
attempts: attempt,
|
|
154
|
+
modelReported,
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
const reviewClient = await this.client();
|
|
158
|
+
const response = await reviewClient.chat.completions.create(payload, {
|
|
159
|
+
signal: context.signal,
|
|
160
|
+
timeout: this.config.retry.timeout_ms,
|
|
161
|
+
});
|
|
162
|
+
return this.resultFromText({
|
|
163
|
+
text: chatText(response),
|
|
164
|
+
raw: response,
|
|
165
|
+
usage: usageFromChat(response.usage),
|
|
166
|
+
started,
|
|
167
|
+
attempts: attempt,
|
|
168
|
+
modelReported: response.model,
|
|
169
|
+
});
|
|
170
|
+
}, (error, attempt) => classifyProviderError(this.id, this.provider, this.model, error, attempt, started));
|
|
171
|
+
}
|
|
172
|
+
async generate(prompt, context) {
|
|
173
|
+
const started = Date.now();
|
|
174
|
+
return withRetry(this.config, async (attempt) => {
|
|
175
|
+
context.emit({
|
|
176
|
+
type: "peer.generate.started",
|
|
177
|
+
session_id: context.session_id,
|
|
178
|
+
round: context.round,
|
|
179
|
+
peer: this.id,
|
|
180
|
+
message: `DeepSeek generation attempt ${attempt}`,
|
|
181
|
+
});
|
|
182
|
+
const payload = {
|
|
183
|
+
...deepSeekThinking(this.config, context.reasoning_effort_override),
|
|
184
|
+
model: this.model,
|
|
185
|
+
messages: [
|
|
186
|
+
{ role: "system", content: this.systemPrompt(context) },
|
|
187
|
+
{ role: "user", content: userPrompt(prompt) },
|
|
188
|
+
],
|
|
189
|
+
max_tokens: this.config.max_output_tokens,
|
|
190
|
+
};
|
|
191
|
+
// DeepSeek's OpenAI-compatible API accepts the non-OpenAI `thinking` body field;
|
|
192
|
+
// the OpenAI JS client forwards unknown body keys, and the real API smoke verifies it.
|
|
193
|
+
if (this.shouldStreamTokens(context)) {
|
|
194
|
+
const streamPayload = {
|
|
195
|
+
...payload,
|
|
196
|
+
stream: true,
|
|
197
|
+
stream_options: { include_usage: true },
|
|
198
|
+
};
|
|
199
|
+
const generateClient = await this.client();
|
|
200
|
+
const stream = await generateClient.chat.completions.create(streamPayload, {
|
|
201
|
+
signal: context.signal,
|
|
202
|
+
timeout: this.config.retry.timeout_ms,
|
|
203
|
+
});
|
|
204
|
+
const stream_buffer = new StreamBuffer(this.id);
|
|
205
|
+
const tokenStream = this.createTokenEventBuffer(context, "generation", "chat.completion.chunk.delta");
|
|
206
|
+
let usage;
|
|
207
|
+
let modelReported;
|
|
208
|
+
let chunks = 0;
|
|
209
|
+
for await (const chunk of stream) {
|
|
210
|
+
chunks += 1;
|
|
211
|
+
modelReported = chunk.model ?? modelReported;
|
|
212
|
+
usage = usageFromChat(chunk.usage) ?? usage;
|
|
213
|
+
for (const choice of chunk.choices ?? []) {
|
|
214
|
+
const delta = choice.delta?.content ?? "";
|
|
215
|
+
stream_buffer.append(delta);
|
|
216
|
+
tokenStream.append(delta);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
const text = stream_buffer.text();
|
|
220
|
+
tokenStream.complete(text.length);
|
|
221
|
+
return this.generationFromText({
|
|
222
|
+
text,
|
|
223
|
+
raw: { streamed: true, provider: this.provider, chunks, model: modelReported },
|
|
224
|
+
usage,
|
|
225
|
+
started,
|
|
226
|
+
attempts: attempt,
|
|
227
|
+
modelReported,
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
const generateClient = await this.client();
|
|
231
|
+
const response = await generateClient.chat.completions.create(payload, {
|
|
232
|
+
signal: context.signal,
|
|
233
|
+
timeout: this.config.retry.timeout_ms,
|
|
234
|
+
});
|
|
235
|
+
return this.generationFromText({
|
|
236
|
+
text: chatText(response),
|
|
237
|
+
raw: response,
|
|
238
|
+
usage: usageFromChat(response.usage),
|
|
239
|
+
started,
|
|
240
|
+
attempts: attempt,
|
|
241
|
+
modelReported: response.model,
|
|
242
|
+
});
|
|
243
|
+
}, (error, attempt) => classifyProviderError(this.id, this.provider, this.model, error, attempt, started));
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
//# sourceMappingURL=deepseek.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deepseek.js","sourceRoot":"","sources":["../../../src/peers/deepseek.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { safeErrorMessage } from "../security/redact.js";
|
|
2
|
+
// v2.4.0 / audit closure (P2.7): extract `Retry-After` from provider
|
|
3
|
+
// SDK error objects. Anthropic, OpenAI, Google GenAI and the OpenAI-
|
|
4
|
+
// compatible DeepSeek client all surface this header through `error.headers`
|
|
5
|
+
// (fetch-style) or `error.response.headers` (legacy axios-style). The
|
|
6
|
+
// retry loop already consumes `failure.retry_after_ms`, so honoring the
|
|
7
|
+
// server-authoritative hint is a one-place fix that helps every provider
|
|
8
|
+
// at once. Returns ms or undefined.
|
|
9
|
+
function extractRetryAfterMs(error) {
|
|
10
|
+
if (!error || typeof error !== "object")
|
|
11
|
+
return undefined;
|
|
12
|
+
const candidates = [];
|
|
13
|
+
const errorObj = error;
|
|
14
|
+
if (errorObj.headers)
|
|
15
|
+
candidates.push(errorObj.headers);
|
|
16
|
+
const response = errorObj.response;
|
|
17
|
+
if (response && typeof response === "object") {
|
|
18
|
+
const respHeaders = response.headers;
|
|
19
|
+
if (respHeaders)
|
|
20
|
+
candidates.push(respHeaders);
|
|
21
|
+
}
|
|
22
|
+
for (const headers of candidates) {
|
|
23
|
+
let value;
|
|
24
|
+
if (headers && typeof headers.get === "function") {
|
|
25
|
+
try {
|
|
26
|
+
value =
|
|
27
|
+
headers.get("retry-after") ?? undefined;
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
// some Headers implementations throw on missing key — ignore.
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
else if (headers && typeof headers === "object") {
|
|
34
|
+
const h = headers;
|
|
35
|
+
const raw = h["retry-after"] ?? h["Retry-After"];
|
|
36
|
+
if (typeof raw === "string")
|
|
37
|
+
value = raw;
|
|
38
|
+
else if (typeof raw === "number" && Number.isFinite(raw))
|
|
39
|
+
value = String(raw);
|
|
40
|
+
}
|
|
41
|
+
if (!value)
|
|
42
|
+
continue;
|
|
43
|
+
const trimmed = value.trim();
|
|
44
|
+
if (!trimmed)
|
|
45
|
+
continue;
|
|
46
|
+
// Numeric (delta-seconds).
|
|
47
|
+
const seconds = Number.parseFloat(trimmed);
|
|
48
|
+
if (Number.isFinite(seconds) && seconds >= 0)
|
|
49
|
+
return Math.round(seconds * 1000);
|
|
50
|
+
// HTTP-date.
|
|
51
|
+
const date = Date.parse(trimmed);
|
|
52
|
+
if (Number.isFinite(date)) {
|
|
53
|
+
const delta = date - Date.now();
|
|
54
|
+
if (delta > 0)
|
|
55
|
+
return delta;
|
|
56
|
+
return 0;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return undefined;
|
|
60
|
+
}
|
|
61
|
+
// v2.4.0 / audit closure (P4.17): treat upstream gateway errors (502 Bad
|
|
62
|
+
// Gateway, 503 Service Unavailable, 504 Gateway Timeout) as retryable.
|
|
63
|
+
// Pre-v2.4.0 these collapsed into the generic `provider_error` class and
|
|
64
|
+
// the retry loop never re-tried them, even though they are textbook
|
|
65
|
+
// transient failures. Retain `provider_error` as the default class so
|
|
66
|
+
// upstream observability semantics don't change.
|
|
67
|
+
const GATEWAY_5XX_RE = /\b(?:5(?:0[234]|9\d))\b|\b(?:bad\s+gateway|service\s+unavailable|gateway\s+timeout)\b/i;
|
|
68
|
+
// v2.15.0 (item 5, operator directive 2026-05-04 — feedback_consult_docs_before_amputating.md):
|
|
69
|
+
// detect 4xx errors that cite a named provider parameter so the operator
|
|
70
|
+
// (and the agent reading the failure) gets a docs URL pointer FIRST,
|
|
71
|
+
// before considering the amputation reflex (rip the offending field out
|
|
72
|
+
// to silence the 400). The xAI grok-4-latest case is the canonical
|
|
73
|
+
// example: `reasoning.effort` is rejected on non-multi-agent models;
|
|
74
|
+
// the docs page lists exactly which models accept it. Surfacing the
|
|
75
|
+
// docs URL on the failure object makes the resolution path obvious and
|
|
76
|
+
// pushes the agent toward the correct fix (allowlist gate or model
|
|
77
|
+
// switch) rather than removing the feature.
|
|
78
|
+
//
|
|
79
|
+
// Pattern matches: "parameter X", "X is not supported", "Argument not
|
|
80
|
+
// supported on this model: X", "Invalid parameter: X", "Unrecognized
|
|
81
|
+
// request argument: X", "field X". Captures the parameter name (alphanum,
|
|
82
|
+
// underscore, dot for nested) for inclusion on `docs_hint.parameter`.
|
|
83
|
+
// Prefix form: "<keyword>: <param>" — captures the parameter name
|
|
84
|
+
// after a known prefix (Argument not supported on this model:, Invalid
|
|
85
|
+
// parameter:, Unrecognized request argument:, Unknown parameter:).
|
|
86
|
+
const PARAM_REJECTION_PREFIX_RE = /(?:argument\s+not\s+supported(?:\s+on\s+this\s+model)?\s*:|invalid\s+(?:request\s+)?(?:parameter|argument)\s*:|unrecognized\s+(?:request\s+)?(?:parameter|argument)\s*:|unknown\s+parameter\s*:)\s*["'`]?([a-z_][a-z0-9_]*(?:\.[a-z_][a-z0-9_]*)*)/i;
|
|
87
|
+
// Suffix form: "parameter <param> is not supported" — captures when the
|
|
88
|
+
// parameter precedes an explicit rejection clause.
|
|
89
|
+
const PARAM_REJECTION_SUFFIX_RE = /\b(?:parameter|field|argument)s?\s+["'`]?([a-z_][a-z0-9_]*(?:\.[a-z_][a-z0-9_]*)*)["'`]?\s+(?:is\s+(?:not\s+supported|invalid|unknown|deprecated)|not\s+supported|cannot\s+be\s+used|is\s+only\s+(?:supported|available)|requires)/i;
|
|
90
|
+
const STATUS_4XX_RE = /\b(?:400|404|405|409|413|415|422)\b/i;
|
|
91
|
+
const PROVIDER_DOCS_URLS = {
|
|
92
|
+
openai: "https://platform.openai.com/docs/api-reference",
|
|
93
|
+
anthropic: "https://docs.anthropic.com/en/api/messages",
|
|
94
|
+
google: "https://ai.google.dev/api/generate-content",
|
|
95
|
+
deepseek: "https://api-docs.deepseek.com/api/create-chat-completion",
|
|
96
|
+
xai: "https://docs.x.ai/docs/api-reference",
|
|
97
|
+
};
|
|
98
|
+
// Provider-specific deep links for known sticky parameters. Looked up
|
|
99
|
+
// after the generic provider docs URL when a parameter rename is known.
|
|
100
|
+
const PROVIDER_PARAM_DOCS = {
|
|
101
|
+
xai: {
|
|
102
|
+
"reasoning.effort": "https://docs.x.ai/developers/model-capabilities/text/reasoning",
|
|
103
|
+
},
|
|
104
|
+
openai: {
|
|
105
|
+
"reasoning.effort": "https://platform.openai.com/docs/api-reference/responses/create#responses-create-reasoning",
|
|
106
|
+
},
|
|
107
|
+
anthropic: {
|
|
108
|
+
thinking: "https://docs.anthropic.com/en/docs/build-with-claude/extended-thinking",
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
export function classifyProviderError(peer, provider, model, error, attempts, started) {
|
|
112
|
+
const message = safeErrorMessage(error);
|
|
113
|
+
const contextual429 = /\b(?:http|status|statuscode|code|error)\s*[:=]?\s*["'(]?\s*429\b/i.test(message) ||
|
|
114
|
+
/\b429\s+(?:too many requests|rate[-_\s]?limit|quota|retry-after)\b/i.test(message);
|
|
115
|
+
const rateLimited = contextual429 ||
|
|
116
|
+
/\b(?:too many requests|rate[-_\s]?limit(?:ed|ing)?|quota exceeded|resource_exhausted|retry-after)\b/i.test(message);
|
|
117
|
+
const auth = /\b(?:401|403|unauthorized|forbidden|invalid api key|missing api key|expired api key|authentication failed|authentication required)\b/i.test(message);
|
|
118
|
+
const cancelled = /\b(?:aborterror|operation was aborted|call cancelled|session_cancelled)\b/i.test(message);
|
|
119
|
+
const moderation = /\b(?:invalid_prompt|prompt[_\s-]?flagged|moderation|moderated|safety policy|safety system|usage policy|responsibleaipolicyviolation|content[_\s-]?filter|blocked by policy|policy violation|could not be processed|input was rejected)\b/i.test(message);
|
|
120
|
+
const timeout = /\b(?:timeout|aborted|aborterror)\b/i.test(message);
|
|
121
|
+
const network = /\b(?:econnreset|enotfound|etimedout|network|fetch failed)\b/i.test(message);
|
|
122
|
+
const gateway5xx = GATEWAY_5XX_RE.test(message);
|
|
123
|
+
const failureClass = auth
|
|
124
|
+
? "auth"
|
|
125
|
+
: cancelled
|
|
126
|
+
? "cancelled"
|
|
127
|
+
: moderation
|
|
128
|
+
? "prompt_flagged_by_moderation"
|
|
129
|
+
: rateLimited
|
|
130
|
+
? "rate_limit"
|
|
131
|
+
: timeout
|
|
132
|
+
? "timeout"
|
|
133
|
+
: network
|
|
134
|
+
? "network"
|
|
135
|
+
: "provider_error";
|
|
136
|
+
// v2.15.0 (item 5): docs hint for 4xx parameter rejections. Only
|
|
137
|
+
// applies when the failure class is `provider_error` (avoid stomping
|
|
138
|
+
// on rate_limit/auth/network advice). The 4xx status check is a soft
|
|
139
|
+
// gate — many SDKs surface the parameter-rejection message without an
|
|
140
|
+
// explicit status code in the .message field, so we run the pattern
|
|
141
|
+
// even when STATUS_4XX_RE doesn't match, but only set docs_hint when
|
|
142
|
+
// both the regex matches AND the failure isn't already a known class.
|
|
143
|
+
let docsHint;
|
|
144
|
+
let docsAdvice;
|
|
145
|
+
if (failureClass === "provider_error") {
|
|
146
|
+
const prefixMatch = PARAM_REJECTION_PREFIX_RE.exec(message);
|
|
147
|
+
const suffixMatch = prefixMatch ? null : PARAM_REJECTION_SUFFIX_RE.exec(message);
|
|
148
|
+
const paramMatch = prefixMatch ?? suffixMatch;
|
|
149
|
+
if (paramMatch && (STATUS_4XX_RE.test(message) || /\bnot\s+supported\b/i.test(message))) {
|
|
150
|
+
const parameter = paramMatch[1];
|
|
151
|
+
const providerKey = provider.toLowerCase();
|
|
152
|
+
const deepLink = PROVIDER_PARAM_DOCS[providerKey]?.[parameter];
|
|
153
|
+
const fallbackLink = PROVIDER_DOCS_URLS[providerKey];
|
|
154
|
+
const docsUrl = deepLink ?? fallbackLink;
|
|
155
|
+
docsHint = { parameter, docs_url: docsUrl };
|
|
156
|
+
docsAdvice =
|
|
157
|
+
`Provider rejected parameter "${parameter}". HARD RULE (workspace memory feedback_consult_docs_before_amputating): consult official docs FIRST` +
|
|
158
|
+
(docsUrl ? ` at ${docsUrl}` : "") +
|
|
159
|
+
", do NOT amputate the field to silence the 400. Likely fix: gate the field on a model-capability allowlist (see peers/grok.ts GROK_REASONING_EFFORT_MODELS for precedent), or switch to a model that accepts it.";
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return {
|
|
163
|
+
peer,
|
|
164
|
+
provider,
|
|
165
|
+
model,
|
|
166
|
+
failure_class: failureClass,
|
|
167
|
+
message,
|
|
168
|
+
retryable: !cancelled && !auth && (rateLimited || timeout || network || gateway5xx),
|
|
169
|
+
recovery_hint: rateLimited
|
|
170
|
+
? "wait_and_retry"
|
|
171
|
+
: moderation
|
|
172
|
+
? "reformulate_and_retry"
|
|
173
|
+
: docsHint
|
|
174
|
+
? "consult_docs_then_revise"
|
|
175
|
+
: undefined,
|
|
176
|
+
reformulation_advice: moderation
|
|
177
|
+
? "Rephrase the request in neutral technical language, compact prior peer discussion, avoid quoting flagged text, and keep the same engineering intent."
|
|
178
|
+
: docsAdvice,
|
|
179
|
+
retry_after_ms: extractRetryAfterMs(error),
|
|
180
|
+
attempts,
|
|
181
|
+
latency_ms: Date.now() - started,
|
|
182
|
+
docs_hint: docsHint,
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../../src/peers/errors.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { AppConfig, GenerationResult, PeerAdapter, PeerCallContext, PeerId, PeerProbeResult, PeerResult } from "../core/types.js";
|
|
2
|
+
import { BasePeerAdapter } from "./base.js";
|
|
3
|
+
export declare function loadGenaiModule(): Promise<typeof import("@google/genai")>;
|
|
4
|
+
export declare class GeminiAdapter extends BasePeerAdapter implements PeerAdapter {
|
|
5
|
+
id: PeerId;
|
|
6
|
+
provider: string;
|
|
7
|
+
model: string;
|
|
8
|
+
constructor(config: AppConfig, modelOverride?: string);
|
|
9
|
+
private client;
|
|
10
|
+
probe(): Promise<PeerProbeResult>;
|
|
11
|
+
call(prompt: string, context: PeerCallContext): Promise<PeerResult>;
|
|
12
|
+
generate(prompt: string, context: PeerCallContext): Promise<GenerationResult>;
|
|
13
|
+
}
|