@ggui-ai/ui-gen 0.19.0 → 0.21.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/dist/adapters/base.d.ts +7 -0
- package/dist/adapters/base.js +1 -0
- package/dist/adapters/base.js.map +1 -1
- package/dist/adapters/generation-dispatch.d.ts +17 -3
- package/dist/adapters/generation-dispatch.js +992 -3468
- package/dist/adapters/generation-dispatch.js.map +1 -1
- package/dist/adapters/index.js +70 -13
- package/dist/adapters/index.js.map +1 -1
- package/dist/adapters/types.d.ts +1 -1
- package/dist/adapters/types.js.map +1 -1
- package/dist/advanced/index.d.ts +4 -4
- package/dist/advanced/index.js +1060 -3520
- package/dist/advanced/index.js.map +1 -1
- package/dist/blueprint-validator.js +13 -3
- package/dist/blueprint-validator.js.map +1 -1
- package/dist/boilerplate.js +2 -2
- package/dist/boilerplate.js.map +1 -1
- package/dist/check/index.d.ts +1 -1
- package/dist/check/index.js.map +1 -1
- package/dist/{create-ui-generator-CxPvudbs.d.ts → create-ui-generator-D7kOXNjJ.d.ts} +2 -2
- package/dist/{dispatch-N8wdNOX-.d.ts → dispatch-DGvhmARp.d.ts} +1 -1
- package/dist/evaluation/axis-checks/index.d.ts +3 -3
- package/dist/evaluation/axis-checks/index.js +46 -0
- package/dist/evaluation/axis-checks/index.js.map +1 -1
- package/dist/evaluation/axis-checks/registry.d.ts +1 -1
- package/dist/evaluation/axis-checks/registry.js +46 -0
- package/dist/evaluation/axis-checks/registry.js.map +1 -1
- package/dist/evaluation/index.d.ts +38 -6
- package/dist/evaluation/index.js +682 -3357
- package/dist/evaluation/index.js.map +1 -1
- package/dist/harness/check/runtime-render/index.d.ts +2 -2
- package/dist/harness/check/runtime-render/index.js +13 -3
- package/dist/harness/check/runtime-render/index.js.map +1 -1
- package/dist/harness/check/runtime-render/render-check-worker.js +8 -3
- package/dist/harness/check/runtime-render/render-check-worker.js.map +1 -1
- package/dist/harness/index.d.ts +2 -2
- package/dist/harness/index.js +562 -30
- package/dist/harness/index.js.map +1 -1
- package/dist/harness/llm-trace-sink.d.ts +7 -1
- package/dist/harness/llm-trace-sink.js.map +1 -1
- package/dist/harness/result-types.d.ts +1 -1
- package/dist/harness/runtime.d.ts +11 -2
- package/dist/harness/runtime.js +3 -3
- package/dist/harness/runtime.js.map +1 -1
- package/dist/harness/types-public.d.ts +1 -1
- package/dist/harness/types-public.js +2 -2
- package/dist/harness/types-public.js.map +1 -1
- package/dist/harness/validator-trace-sink.d.ts +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +1060 -3520
- package/dist/index.js.map +1 -1
- package/dist/{llm-router-D7fjBhtr.d.ts → llm-router-DwwUK1bz.d.ts} +16 -3
- package/dist/patch.d.ts +1 -1
- package/dist/providers/index.js +5 -4
- package/dist/providers/index.js.map +1 -1
- package/dist/{render-check-WfOVO9kU.d.ts → render-check-C0CqVceF.d.ts} +17 -0
- package/dist/{types-public-DKR_h70H.d.ts → types-public-CzYqZclY.d.ts} +44 -1
- package/dist/validation/index.d.ts +1 -1
- package/dist/validation/index.js +1 -1
- package/dist/validation/index.js.map +1 -1
- package/dist/workflows.d.ts +1 -1
- package/package.json +16 -12
- package/src/adapters/base.ts +8 -0
- package/src/adapters/claude/claude-code-login.ts +73 -0
- package/src/adapters/claude/sdk.ts +8 -1
- package/src/adapters/generation-dispatch.ts +19 -2
- package/src/adapters/openai/raw.ts +11 -5
- package/src/adapters/openai/sdk.ts +8 -5
- package/src/adapters/openai/tokens.ts +67 -0
- package/src/adapters/provider-router.ts +28 -0
- package/src/adapters/types.ts +1 -1
- package/src/boilerplate/generate.ts +19 -4
- package/src/boilerplate/hard-sections.ts +1 -1
- package/src/create-ui-generator.ts +19 -10
- package/src/evaluation/axis-checks/checks/universal.ts +92 -0
- package/src/evaluation/design-identity.ts +16 -5
- package/src/evaluation/types-public.ts +42 -0
- package/src/evaluation/visual-evaluator.ts +231 -24
- package/src/harness/check/runtime-render/adapter.ts +9 -0
- package/src/harness/check/runtime-render/render-check.ts +37 -1
- package/src/harness/claude-code-login-query.ts +528 -0
- package/src/harness/coding/assemble-result.ts +10 -1
- package/src/harness/coding/run-eval-round.ts +179 -34
- package/src/harness/enforced-coding.ts +20 -0
- package/src/harness/llm-router.ts +390 -55
- package/src/harness/llm-trace-sink.ts +7 -1
- package/src/harness/runtime.ts +6 -0
- package/src/providers/openai.ts +31 -19
- package/src/validation/primitives.ts +1 -1
package/dist/harness/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { LUCIDE_ICON_NAMES } from '@ggui-ai/design';
|
|
2
|
-
import { resolveAppGadgets, listContractGadgets, HOOK_NAME_RE, APP_GENERATION_PROFILE_BOUNDS } from '@ggui-ai/protocol';
|
|
2
|
+
import { resolveAppGadgets, listContractGadgets, HOOK_NAME_RE, anthropicRejectsForcedToolChoice, normalizeAnthropicModelId, isRecord, anthropicRejectsSamplingParams, APP_GENERATION_PROFILE_BOUNDS } from '@ggui-ai/protocol';
|
|
3
3
|
import { createHash } from 'crypto';
|
|
4
4
|
import { readFileSync } from 'fs';
|
|
5
5
|
import { dirname, resolve } from 'path';
|
|
@@ -7,7 +7,7 @@ import { fileURLToPath } from 'url';
|
|
|
7
7
|
import { consumedTokenManifest } from '@ggui-ai/design/themes';
|
|
8
8
|
import ts from 'typescript';
|
|
9
9
|
import Anthropic from '@anthropic-ai/sdk';
|
|
10
|
-
import 'zod';
|
|
10
|
+
import { z } from 'zod';
|
|
11
11
|
|
|
12
12
|
var __defProp = Object.defineProperty;
|
|
13
13
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
@@ -1478,7 +1478,7 @@ function generateBoilerplate(_userPrompt, contract, shellType, screen, composedS
|
|
|
1478
1478
|
}
|
|
1479
1479
|
const toolNote = tool ? ` (label "${label}", nextStep hint \u2192 ${tool})` : "";
|
|
1480
1480
|
actionTypeAliases.push(
|
|
1481
|
-
`/** Action payload: ${desc || label}${toolNote} */
|
|
1481
|
+
`/** Action payload: ${desc || label}${toolNote} \u2014 control copy: "${label}" VERBATIM */
|
|
1482
1482
|
type ${typeName} = ${tsType};`
|
|
1483
1483
|
);
|
|
1484
1484
|
const callSig = tsType === "void" ? "() => void \u2014 fire and forget" : `(data: ${tsType}) => void`;
|
|
@@ -1817,7 +1817,7 @@ Rules:
|
|
|
1817
1817
|
1. All request-specific data \u2192 props with defaults. City names, tickers, user names, dates, counts.
|
|
1818
1818
|
2. Layout and styling are universal. Colors, spacing, structure \u2014 these are the reusable part.
|
|
1819
1819
|
3. Default values come from the current request \u2014 so the component renders correctly standalone.
|
|
1820
|
-
4. Copy comes from the props. NEVER invent display text the contract does not carry \u2014 no eyebrow / kicker / overline (a \`<Text caps>\` label), no helper line, no status text, no tagline. **Do NOT add a caps label above the heading** \u2014 not \`<Text caps>LET'S BEGIN</Text>\`, not \`WELCOME\`, not a section kicker like \`GETTING STARTED\` or \`OVERVIEW\` \u2014 UNLESS a prop supplies that exact text (\`<Text caps>{props.section}</Text>\`). When the request asks for a heading and a line, render the heading and the line and NOTHING above the heading. A heading's fallback is the request's own words, never a slogan of your own. Text the agent cannot change is chatter on every render, and a caps eyebrow you wrote is the most common form of it.
|
|
1820
|
+
4. Copy comes from the props. NEVER invent display text the contract does not carry \u2014 no eyebrow / kicker / overline (a \`<Text caps>\` label), no helper line, no status text, no tagline. **Do NOT add a caps label above the heading** \u2014 not \`<Text caps>LET'S BEGIN</Text>\`, not \`WELCOME\`, not a section kicker like \`GETTING STARTED\` or \`OVERVIEW\` \u2014 UNLESS a prop supplies that exact text (\`<Text caps>{props.section}</Text>\`). When the request asks for a heading and a line, render the heading and the line and NOTHING above the heading. A heading's fallback is the request's own words, never a slogan of your own. Text the agent cannot change is chatter on every render, and a caps eyebrow you wrote is the most common form of it. An action's control shows the contract's \`label\` VERBATIM \u2014 every character, including \`&\`, \`/\`, \`\u2013\` and quotes \u2014 never paraphrased, shortened or re-cased (\`"Confirm & Schedule"\` is not \`"Confirm Schedule"\`).
|
|
1821
1821
|
5. Props interface must be typed and exported.`;
|
|
1822
1822
|
var TERMINAL_ACTIONS = `## Actions the user means once
|
|
1823
1823
|
|
|
@@ -3227,6 +3227,25 @@ function createAnthropicClient(rawKey) {
|
|
|
3227
3227
|
});
|
|
3228
3228
|
}
|
|
3229
3229
|
|
|
3230
|
+
// src/adapters/claude/claude-code-login.ts
|
|
3231
|
+
var PROVIDER_KEY_ENV_NAMES = [
|
|
3232
|
+
"ANTHROPIC_API_KEY",
|
|
3233
|
+
"CLAUDE_API_KEY",
|
|
3234
|
+
"ANTHROPIC_AUTH_TOKEN",
|
|
3235
|
+
"ANTHROPIC_BASE_URL"
|
|
3236
|
+
];
|
|
3237
|
+
function stripProviderKeyEnv(env) {
|
|
3238
|
+
const out = {};
|
|
3239
|
+
const drop = new Set(PROVIDER_KEY_ENV_NAMES);
|
|
3240
|
+
for (const [k, v] of Object.entries(env)) {
|
|
3241
|
+
if (v !== void 0 && !drop.has(k)) out[k] = v;
|
|
3242
|
+
}
|
|
3243
|
+
return out;
|
|
3244
|
+
}
|
|
3245
|
+
function claudeCodeLoginQueryOptions() {
|
|
3246
|
+
return { tools: [], settingSources: [] };
|
|
3247
|
+
}
|
|
3248
|
+
|
|
3230
3249
|
// src/adapters/provider-router.ts
|
|
3231
3250
|
function getBedrockModelId(model) {
|
|
3232
3251
|
const BEDROCK_MAP = {
|
|
@@ -3277,6 +3296,26 @@ function toJsonValue(value, path) {
|
|
|
3277
3296
|
);
|
|
3278
3297
|
}
|
|
3279
3298
|
|
|
3299
|
+
// src/adapters/openai/tokens.ts
|
|
3300
|
+
function splitOpenAiUsage(inputTokensInclCached, outputTokens, cachedTokens) {
|
|
3301
|
+
return splitCacheInclusiveUsage(inputTokensInclCached, outputTokens, cachedTokens);
|
|
3302
|
+
}
|
|
3303
|
+
function splitCacheInclusiveUsage(inputTokensInclCached, outputTokens, cachedTokens) {
|
|
3304
|
+
const cacheRead = Math.min(
|
|
3305
|
+
Math.max(cachedTokens, 0),
|
|
3306
|
+
Math.max(inputTokensInclCached, 0)
|
|
3307
|
+
);
|
|
3308
|
+
const nonCachedInput = Math.max(inputTokensInclCached, 0) - cacheRead;
|
|
3309
|
+
return {
|
|
3310
|
+
tokens: {
|
|
3311
|
+
input: nonCachedInput,
|
|
3312
|
+
output: outputTokens,
|
|
3313
|
+
total: nonCachedInput + cacheRead + outputTokens
|
|
3314
|
+
},
|
|
3315
|
+
cacheReadTokens: cacheRead
|
|
3316
|
+
};
|
|
3317
|
+
}
|
|
3318
|
+
|
|
3280
3319
|
// src/harness/llm-trace-sink.ts
|
|
3281
3320
|
var activeSink2 = null;
|
|
3282
3321
|
function setLlmTraceSink(sink) {
|
|
@@ -3300,6 +3339,271 @@ function newLlmTraceId() {
|
|
|
3300
3339
|
return Date.now().toString(36) + Math.random().toString(36).slice(2, 10);
|
|
3301
3340
|
}
|
|
3302
3341
|
|
|
3342
|
+
// src/adapters/claude/tool-bridge.ts
|
|
3343
|
+
var MCP_SERVER_NAME = "ggui";
|
|
3344
|
+
|
|
3345
|
+
// src/harness/claude-code-login-query.ts
|
|
3346
|
+
var MCP_TOOL_PREFIX = `mcp__${MCP_SERVER_NAME}__`;
|
|
3347
|
+
function stripMcpToolPrefix(name) {
|
|
3348
|
+
return name.startsWith(MCP_TOOL_PREFIX) ? name.slice(MCP_TOOL_PREFIX.length) : name;
|
|
3349
|
+
}
|
|
3350
|
+
var LOGIN_API_KEY_SOURCE = "none";
|
|
3351
|
+
var ClaudeCodeLoginAuthError = class extends Error {
|
|
3352
|
+
apiKeySource;
|
|
3353
|
+
constructor(apiKeySource) {
|
|
3354
|
+
super(
|
|
3355
|
+
`claude-code-login: the Claude Code binary authenticated from apiKeySource=${apiKeySource}, not from its login \u2014 a provider key reached the spawned process; the run was stopped before it spent a turn`
|
|
3356
|
+
);
|
|
3357
|
+
this.name = "ClaudeCodeLoginAuthError";
|
|
3358
|
+
this.apiKeySource = apiKeySource;
|
|
3359
|
+
}
|
|
3360
|
+
};
|
|
3361
|
+
var ClaudeCodeLoginResultError = class extends Error {
|
|
3362
|
+
subtype;
|
|
3363
|
+
errors;
|
|
3364
|
+
status;
|
|
3365
|
+
constructor(subtype, errors, status) {
|
|
3366
|
+
super(
|
|
3367
|
+
`claude-code-login: query() ended with ${subtype}` + (status !== void 0 ? ` (api status ${status})` : "") + `: ${errors.length > 0 ? errors.join("; ") : "no detail from the binary"}`
|
|
3368
|
+
);
|
|
3369
|
+
this.name = "ClaudeCodeLoginResultError";
|
|
3370
|
+
this.subtype = subtype;
|
|
3371
|
+
this.errors = errors;
|
|
3372
|
+
this.status = status;
|
|
3373
|
+
}
|
|
3374
|
+
};
|
|
3375
|
+
function createDenyingIntercept(intercepted) {
|
|
3376
|
+
return async (toolName, input) => {
|
|
3377
|
+
intercepted.push({ name: toolName, input });
|
|
3378
|
+
return {
|
|
3379
|
+
behavior: "deny",
|
|
3380
|
+
message: "ggui executes tools in its harness; the binary only proposes them."
|
|
3381
|
+
};
|
|
3382
|
+
};
|
|
3383
|
+
}
|
|
3384
|
+
async function* imageUserMessage(text, image) {
|
|
3385
|
+
const message = {
|
|
3386
|
+
role: "user",
|
|
3387
|
+
content: [
|
|
3388
|
+
{
|
|
3389
|
+
type: "image",
|
|
3390
|
+
source: { type: "base64", media_type: image.mediaType, data: image.base64 }
|
|
3391
|
+
},
|
|
3392
|
+
{ type: "text", text }
|
|
3393
|
+
]
|
|
3394
|
+
};
|
|
3395
|
+
yield { type: "user", message, parent_tool_use_id: null };
|
|
3396
|
+
}
|
|
3397
|
+
function buildLoginQueryOptions(params) {
|
|
3398
|
+
return {
|
|
3399
|
+
model: params.model,
|
|
3400
|
+
systemPrompt: params.systemPrompt,
|
|
3401
|
+
maxTurns: 1,
|
|
3402
|
+
thinking: { type: "disabled" },
|
|
3403
|
+
env: stripProviderKeyEnv(params.env ?? process.env),
|
|
3404
|
+
...claudeCodeLoginQueryOptions(),
|
|
3405
|
+
...params.tools !== void 0 ? {
|
|
3406
|
+
mcpServers: { [MCP_SERVER_NAME]: params.tools.server },
|
|
3407
|
+
canUseTool: params.tools.canUseTool
|
|
3408
|
+
} : {}
|
|
3409
|
+
};
|
|
3410
|
+
}
|
|
3411
|
+
var JSON_SCHEMA_TYPES = [
|
|
3412
|
+
"object",
|
|
3413
|
+
"array",
|
|
3414
|
+
"string",
|
|
3415
|
+
"number",
|
|
3416
|
+
"boolean",
|
|
3417
|
+
"null",
|
|
3418
|
+
"integer"
|
|
3419
|
+
];
|
|
3420
|
+
function isJsonSchemaType(value) {
|
|
3421
|
+
return typeof value === "string" && JSON_SCHEMA_TYPES.includes(value);
|
|
3422
|
+
}
|
|
3423
|
+
function isJsonObject(value) {
|
|
3424
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3425
|
+
}
|
|
3426
|
+
function schemaAt(value, path) {
|
|
3427
|
+
if (typeof value === "boolean") return value;
|
|
3428
|
+
if (!isJsonObject(value)) {
|
|
3429
|
+
throw new Error(
|
|
3430
|
+
`claude-code-login: ${path} must be a JSON-Schema object or boolean, got ${JSON.stringify(value)}`
|
|
3431
|
+
);
|
|
3432
|
+
}
|
|
3433
|
+
return toZodJsonSchema(value, path);
|
|
3434
|
+
}
|
|
3435
|
+
function toZodJsonSchema(parameters, path = "parameters") {
|
|
3436
|
+
const out = {};
|
|
3437
|
+
for (const [key, value] of Object.entries(parameters)) {
|
|
3438
|
+
if (value === void 0) continue;
|
|
3439
|
+
switch (key) {
|
|
3440
|
+
case "type": {
|
|
3441
|
+
if (!isJsonSchemaType(value)) {
|
|
3442
|
+
throw new Error(
|
|
3443
|
+
`claude-code-login: ${path}.type must be one of ${JSON_SCHEMA_TYPES.join("|")}, got ${JSON.stringify(value)}`
|
|
3444
|
+
);
|
|
3445
|
+
}
|
|
3446
|
+
out.type = value;
|
|
3447
|
+
break;
|
|
3448
|
+
}
|
|
3449
|
+
case "properties":
|
|
3450
|
+
case "patternProperties":
|
|
3451
|
+
case "$defs": {
|
|
3452
|
+
if (!isJsonObject(value))
|
|
3453
|
+
throw new Error(`claude-code-login: ${path}.${key} must be an object`);
|
|
3454
|
+
const record = {};
|
|
3455
|
+
for (const [name, sub] of Object.entries(value)) {
|
|
3456
|
+
if (sub === void 0) continue;
|
|
3457
|
+
record[name] = schemaAt(sub, `${path}.${key}.${name}`);
|
|
3458
|
+
}
|
|
3459
|
+
if (key === "$defs") {
|
|
3460
|
+
const defs = {};
|
|
3461
|
+
for (const [name, sub] of Object.entries(record)) {
|
|
3462
|
+
if (typeof sub === "boolean")
|
|
3463
|
+
throw new Error(`claude-code-login: ${path}.$defs.${name} must be an object`);
|
|
3464
|
+
defs[name] = sub;
|
|
3465
|
+
}
|
|
3466
|
+
out.$defs = defs;
|
|
3467
|
+
} else {
|
|
3468
|
+
out[key] = record;
|
|
3469
|
+
}
|
|
3470
|
+
break;
|
|
3471
|
+
}
|
|
3472
|
+
case "items": {
|
|
3473
|
+
out.items = Array.isArray(value) ? value.map((sub, i) => schemaAt(sub, `${path}.items[${i}]`)) : schemaAt(value, `${path}.items`);
|
|
3474
|
+
break;
|
|
3475
|
+
}
|
|
3476
|
+
case "additionalProperties":
|
|
3477
|
+
case "not": {
|
|
3478
|
+
out[key] = schemaAt(value, `${path}.${key}`);
|
|
3479
|
+
break;
|
|
3480
|
+
}
|
|
3481
|
+
case "anyOf":
|
|
3482
|
+
case "oneOf":
|
|
3483
|
+
case "allOf": {
|
|
3484
|
+
if (!Array.isArray(value))
|
|
3485
|
+
throw new Error(`claude-code-login: ${path}.${key} must be an array`);
|
|
3486
|
+
out[key] = value.map((sub, i) => {
|
|
3487
|
+
const s = schemaAt(sub, `${path}.${key}[${i}]`);
|
|
3488
|
+
if (typeof s === "boolean")
|
|
3489
|
+
throw new Error(`claude-code-login: ${path}.${key}[${i}] must be an object`);
|
|
3490
|
+
return s;
|
|
3491
|
+
});
|
|
3492
|
+
break;
|
|
3493
|
+
}
|
|
3494
|
+
case "required": {
|
|
3495
|
+
if (!Array.isArray(value) || !value.every((v) => typeof v === "string")) {
|
|
3496
|
+
throw new Error(`claude-code-login: ${path}.required must be an array of strings`);
|
|
3497
|
+
}
|
|
3498
|
+
out.required = value;
|
|
3499
|
+
break;
|
|
3500
|
+
}
|
|
3501
|
+
case "enum": {
|
|
3502
|
+
if (!Array.isArray(value) || !value.every(
|
|
3503
|
+
(v) => v === null || typeof v === "string" || typeof v === "number" || typeof v === "boolean"
|
|
3504
|
+
)) {
|
|
3505
|
+
throw new Error(`claude-code-login: ${path}.enum must be an array of primitives`);
|
|
3506
|
+
}
|
|
3507
|
+
out.enum = value;
|
|
3508
|
+
break;
|
|
3509
|
+
}
|
|
3510
|
+
default:
|
|
3511
|
+
out[key] = value;
|
|
3512
|
+
}
|
|
3513
|
+
}
|
|
3514
|
+
return out;
|
|
3515
|
+
}
|
|
3516
|
+
function createLoginToolServer(sdk, tools) {
|
|
3517
|
+
const sdkTools = tools.map((def) => {
|
|
3518
|
+
const schema = z.fromJSONSchema(toZodJsonSchema(def.parameters, `${def.name}.parameters`));
|
|
3519
|
+
if (!(schema instanceof z.ZodObject)) {
|
|
3520
|
+
throw new Error(
|
|
3521
|
+
`claude-code-login: tool ${def.name}'s parameters must be a JSON-Schema object`
|
|
3522
|
+
);
|
|
3523
|
+
}
|
|
3524
|
+
return sdk.tool(def.name, def.description, schema.shape, async () => {
|
|
3525
|
+
throw new Error(
|
|
3526
|
+
`claude-code-login: the binary tried to execute ${def.name} \u2014 ggui's tools are proposed to the harness and never run in the binary (the intercept denies every call)`
|
|
3527
|
+
);
|
|
3528
|
+
});
|
|
3529
|
+
});
|
|
3530
|
+
return sdk.createSdkMcpServer({ name: MCP_SERVER_NAME, version: "1.0.0", tools: sdkTools });
|
|
3531
|
+
}
|
|
3532
|
+
var LOGIN_REQUIRED_TOOL_INSTRUCTION = "Respond only with tool calls: do not write any text before, between or after them. Text outside a tool call is not read.";
|
|
3533
|
+
function describeCalls(calls) {
|
|
3534
|
+
return JSON.stringify(calls.map((c) => ({ name: stripMcpToolPrefix(c.name), input: c.input })));
|
|
3535
|
+
}
|
|
3536
|
+
async function collectLoginTurn(stream, opts) {
|
|
3537
|
+
let init;
|
|
3538
|
+
const toolCalls = [];
|
|
3539
|
+
let text = "";
|
|
3540
|
+
let thinkingBlocks = 0;
|
|
3541
|
+
let result;
|
|
3542
|
+
try {
|
|
3543
|
+
for await (const msg of stream) {
|
|
3544
|
+
if (msg.type === "system" && msg.subtype === "init") {
|
|
3545
|
+
const measured = msg;
|
|
3546
|
+
init = measured;
|
|
3547
|
+
opts.log(
|
|
3548
|
+
`[claude-code-login] apiKeySource=${measured.apiKeySource} claude_code_version=${measured.claude_code_version} model=${measured.model}`
|
|
3549
|
+
);
|
|
3550
|
+
if (measured.apiKeySource !== LOGIN_API_KEY_SOURCE) {
|
|
3551
|
+
throw new ClaudeCodeLoginAuthError(measured.apiKeySource);
|
|
3552
|
+
}
|
|
3553
|
+
} else if (msg.type === "assistant") {
|
|
3554
|
+
for (const block of msg.message.content) {
|
|
3555
|
+
if (block.type === "tool_use") {
|
|
3556
|
+
toolCalls.push({
|
|
3557
|
+
id: block.id,
|
|
3558
|
+
name: stripMcpToolPrefix(block.name),
|
|
3559
|
+
input: isRecord(block.input) ? toolArgsToJsonObject(block.input) : {}
|
|
3560
|
+
});
|
|
3561
|
+
} else if (block.type === "text") {
|
|
3562
|
+
text += block.text;
|
|
3563
|
+
} else if (block.type === "thinking" || block.type === "redacted_thinking") {
|
|
3564
|
+
thinkingBlocks += 1;
|
|
3565
|
+
}
|
|
3566
|
+
}
|
|
3567
|
+
} else if (msg.type === "result") {
|
|
3568
|
+
result = msg;
|
|
3569
|
+
}
|
|
3570
|
+
}
|
|
3571
|
+
} catch (e) {
|
|
3572
|
+
if (result === void 0) throw e;
|
|
3573
|
+
opts.log(
|
|
3574
|
+
`[claude-code-login] query() threw after its result message \u2014 the result decides (${e instanceof Error ? e.message : String(e)})`
|
|
3575
|
+
);
|
|
3576
|
+
}
|
|
3577
|
+
if (result === void 0) {
|
|
3578
|
+
throw new Error("claude-code-login: query() ended without a result message");
|
|
3579
|
+
}
|
|
3580
|
+
const usage = result.usage;
|
|
3581
|
+
const thinkingTokens = usage.output_tokens_details?.thinking_tokens ?? 0;
|
|
3582
|
+
opts.log(
|
|
3583
|
+
`[claude-code-login] usage kind=${opts.kind} input=${usage.input_tokens} output=${usage.output_tokens} cacheRead=${usage.cache_read_input_tokens} cacheCreated=${usage.cache_creation_input_tokens} thinking=${thinkingTokens} thinkingBlocks=${thinkingBlocks} textChars=${text.length} toolCalls=${toolCalls.length} stop=${result.stop_reason ?? "none"} choice=${opts.choice}`
|
|
3584
|
+
);
|
|
3585
|
+
const streamSaw = describeCalls(toolCalls);
|
|
3586
|
+
const interceptSaw = describeCalls(opts.intercepted);
|
|
3587
|
+
if (streamSaw !== interceptSaw) {
|
|
3588
|
+
opts.warn(
|
|
3589
|
+
`[claude-code-login] intercept/stream disagreement \u2014 the stream's blocks win. intercepted=${interceptSaw} stream=${streamSaw}`
|
|
3590
|
+
);
|
|
3591
|
+
}
|
|
3592
|
+
if (result.subtype === "success") {
|
|
3593
|
+
if (result.is_error) {
|
|
3594
|
+
throw new ClaudeCodeLoginResultError(
|
|
3595
|
+
"success",
|
|
3596
|
+
[result.result],
|
|
3597
|
+
result.api_error_status ?? void 0
|
|
3598
|
+
);
|
|
3599
|
+
}
|
|
3600
|
+
} else if (result.subtype !== "error_max_turns") {
|
|
3601
|
+
const failed = result;
|
|
3602
|
+
throw new ClaudeCodeLoginResultError(failed.subtype, failed.errors, void 0);
|
|
3603
|
+
}
|
|
3604
|
+
return { init, toolCalls, text, result };
|
|
3605
|
+
}
|
|
3606
|
+
|
|
3303
3607
|
// src/harness/llm-router.ts
|
|
3304
3608
|
function addStrictSchemaConstraints(schema) {
|
|
3305
3609
|
const result = { ...schema };
|
|
@@ -3494,17 +3798,6 @@ var LLMAgent = class {
|
|
|
3494
3798
|
}
|
|
3495
3799
|
}
|
|
3496
3800
|
};
|
|
3497
|
-
function normalizeAnthropicModelId(model) {
|
|
3498
|
-
return model.replace(/^anthropic\//, "").replace(/^(?:[a-z]{2}\.)?anthropic\./, "");
|
|
3499
|
-
}
|
|
3500
|
-
function anthropicRejectsSamplingParams(model) {
|
|
3501
|
-
return /^claude-(?:opus-4-7|opus-5|sonnet-5|fable-5)(?:-|$)/.test(
|
|
3502
|
-
normalizeAnthropicModelId(model)
|
|
3503
|
-
);
|
|
3504
|
-
}
|
|
3505
|
-
function anthropicRejectsForcedToolChoice(model) {
|
|
3506
|
-
return /^claude-fable-5-1(?:-|$)/.test(normalizeAnthropicModelId(model));
|
|
3507
|
-
}
|
|
3508
3801
|
function resolveAnthropicSampling(model, temperature) {
|
|
3509
3802
|
if (temperature === void 0) {
|
|
3510
3803
|
return { request: {}, applied: { temperature: "provider-default" } };
|
|
@@ -3880,6 +4173,200 @@ var AnthropicAgent = class extends LLMAgent {
|
|
|
3880
4173
|
}
|
|
3881
4174
|
}
|
|
3882
4175
|
};
|
|
4176
|
+
var ClaudeCodeLoginAgent = class extends LLMAgent {
|
|
4177
|
+
provider = "anthropic";
|
|
4178
|
+
resolveModel(model) {
|
|
4179
|
+
return model.startsWith("anthropic/") ? model.slice("anthropic/".length) : model;
|
|
4180
|
+
}
|
|
4181
|
+
async createClient() {
|
|
4182
|
+
const { query, tool, createSdkMcpServer } = await import('@anthropic-ai/claude-agent-sdk');
|
|
4183
|
+
return { query, tool, createSdkMcpServer };
|
|
4184
|
+
}
|
|
4185
|
+
async callText(model, systemPrompt, userPrompt, _maxTokens, _temperature) {
|
|
4186
|
+
const sdk = await this.getClient();
|
|
4187
|
+
const traceId = newLlmTraceId();
|
|
4188
|
+
const startedAt = Date.now();
|
|
4189
|
+
const resolvedModel = this.resolveModel(model);
|
|
4190
|
+
try {
|
|
4191
|
+
const turn = await this.apiCall(
|
|
4192
|
+
() => collectLoginTurn(
|
|
4193
|
+
sdk.query({
|
|
4194
|
+
prompt: userPrompt,
|
|
4195
|
+
options: buildLoginQueryOptions({ model: resolvedModel, systemPrompt })
|
|
4196
|
+
}),
|
|
4197
|
+
{ intercepted: [], kind: "callText", choice: "none", log: console.log, warn: console.warn }
|
|
4198
|
+
)
|
|
4199
|
+
);
|
|
4200
|
+
const usage = turn.result.usage;
|
|
4201
|
+
const endedAt = Date.now();
|
|
4202
|
+
emitLlmTraceEvent({
|
|
4203
|
+
id: traceId,
|
|
4204
|
+
at: startedAt,
|
|
4205
|
+
endedAt,
|
|
4206
|
+
durationMs: endedAt - startedAt,
|
|
4207
|
+
provider: "claude-code-login",
|
|
4208
|
+
model: resolvedModel,
|
|
4209
|
+
kind: "callText",
|
|
4210
|
+
systemPrompt,
|
|
4211
|
+
userPrompt,
|
|
4212
|
+
result: {
|
|
4213
|
+
inputTokens: usage.input_tokens,
|
|
4214
|
+
outputTokens: usage.output_tokens,
|
|
4215
|
+
cacheCreated: usage.cache_creation_input_tokens,
|
|
4216
|
+
cacheRead: usage.cache_read_input_tokens,
|
|
4217
|
+
text: turn.text
|
|
4218
|
+
}
|
|
4219
|
+
});
|
|
4220
|
+
return { text: turn.text, inputTokens: usage.input_tokens, outputTokens: usage.output_tokens };
|
|
4221
|
+
} catch (e) {
|
|
4222
|
+
const endedAt = Date.now();
|
|
4223
|
+
emitLlmTraceEvent({
|
|
4224
|
+
id: traceId,
|
|
4225
|
+
at: startedAt,
|
|
4226
|
+
endedAt,
|
|
4227
|
+
durationMs: endedAt - startedAt,
|
|
4228
|
+
provider: "claude-code-login",
|
|
4229
|
+
model: resolvedModel,
|
|
4230
|
+
kind: "callText",
|
|
4231
|
+
systemPrompt,
|
|
4232
|
+
userPrompt,
|
|
4233
|
+
error: { message: e instanceof Error ? e.message : String(e) }
|
|
4234
|
+
});
|
|
4235
|
+
throw e;
|
|
4236
|
+
}
|
|
4237
|
+
}
|
|
4238
|
+
async callVision(model, systemPrompt, userPrompt, image, _maxTokens) {
|
|
4239
|
+
const sdk = await this.getClient();
|
|
4240
|
+
const traceId = newLlmTraceId();
|
|
4241
|
+
const startedAt = Date.now();
|
|
4242
|
+
const resolvedModel = this.resolveModel(model);
|
|
4243
|
+
try {
|
|
4244
|
+
const turn = await this.apiCall(
|
|
4245
|
+
() => collectLoginTurn(
|
|
4246
|
+
sdk.query({
|
|
4247
|
+
prompt: imageUserMessage(userPrompt, image),
|
|
4248
|
+
options: buildLoginQueryOptions({ model: resolvedModel, systemPrompt })
|
|
4249
|
+
}),
|
|
4250
|
+
{ intercepted: [], kind: "callVision", choice: "none", log: console.log, warn: console.warn }
|
|
4251
|
+
)
|
|
4252
|
+
);
|
|
4253
|
+
const usage = turn.result.usage;
|
|
4254
|
+
const endedAt = Date.now();
|
|
4255
|
+
emitLlmTraceEvent({
|
|
4256
|
+
id: traceId,
|
|
4257
|
+
at: startedAt,
|
|
4258
|
+
endedAt,
|
|
4259
|
+
durationMs: endedAt - startedAt,
|
|
4260
|
+
provider: "claude-code-login",
|
|
4261
|
+
model: resolvedModel,
|
|
4262
|
+
kind: "callVision",
|
|
4263
|
+
systemPrompt,
|
|
4264
|
+
userPrompt,
|
|
4265
|
+
result: { inputTokens: usage.input_tokens, outputTokens: usage.output_tokens, text: turn.text }
|
|
4266
|
+
});
|
|
4267
|
+
return { text: turn.text, inputTokens: usage.input_tokens, outputTokens: usage.output_tokens };
|
|
4268
|
+
} catch (e) {
|
|
4269
|
+
const endedAt = Date.now();
|
|
4270
|
+
emitLlmTraceEvent({
|
|
4271
|
+
id: traceId,
|
|
4272
|
+
at: startedAt,
|
|
4273
|
+
endedAt,
|
|
4274
|
+
durationMs: endedAt - startedAt,
|
|
4275
|
+
provider: "claude-code-login",
|
|
4276
|
+
model: resolvedModel,
|
|
4277
|
+
kind: "callVision",
|
|
4278
|
+
systemPrompt,
|
|
4279
|
+
userPrompt,
|
|
4280
|
+
error: { message: e instanceof Error ? e.message : String(e) }
|
|
4281
|
+
});
|
|
4282
|
+
throw e;
|
|
4283
|
+
}
|
|
4284
|
+
}
|
|
4285
|
+
async callTools(model, systemPrompt, userPrompt, tools, toolChoice = "required", _scopedTools) {
|
|
4286
|
+
const sdk = await this.getClient();
|
|
4287
|
+
const traceId = newLlmTraceId();
|
|
4288
|
+
const startedAt = Date.now();
|
|
4289
|
+
const resolvedModel = this.resolveModel(model);
|
|
4290
|
+
const sentUserPrompt = toolChoice === "required" ? `${userPrompt}
|
|
4291
|
+
|
|
4292
|
+
${LOGIN_REQUIRED_TOOL_INSTRUCTION}` : userPrompt;
|
|
4293
|
+
try {
|
|
4294
|
+
const turn = await this.apiCall(() => {
|
|
4295
|
+
const intercepted = [];
|
|
4296
|
+
const server = createLoginToolServer(sdk, tools);
|
|
4297
|
+
return collectLoginTurn(
|
|
4298
|
+
sdk.query({
|
|
4299
|
+
prompt: sentUserPrompt,
|
|
4300
|
+
options: buildLoginQueryOptions({
|
|
4301
|
+
model: resolvedModel,
|
|
4302
|
+
systemPrompt,
|
|
4303
|
+
tools: { server, canUseTool: createDenyingIntercept(intercepted) }
|
|
4304
|
+
})
|
|
4305
|
+
}),
|
|
4306
|
+
{ intercepted, kind: "callTools", choice: toolChoice === "required" ? "required:instructed" : "auto", log: console.log, warn: console.warn }
|
|
4307
|
+
);
|
|
4308
|
+
});
|
|
4309
|
+
const usage = turn.result.usage;
|
|
4310
|
+
const cacheCreated = usage.cache_creation_input_tokens;
|
|
4311
|
+
const cacheRead = usage.cache_read_input_tokens;
|
|
4312
|
+
if (cacheCreated || cacheRead) {
|
|
4313
|
+
console.log(
|
|
4314
|
+
`[claude-code-login] callTools cache: created=${cacheCreated} read=${cacheRead} input=${usage.input_tokens} output=${usage.output_tokens}`
|
|
4315
|
+
);
|
|
4316
|
+
}
|
|
4317
|
+
const toolCalls = turn.toolCalls.map((c) => ({ id: c.id, name: c.name, input: c.input }));
|
|
4318
|
+
const endedAt = Date.now();
|
|
4319
|
+
emitLlmTraceEvent({
|
|
4320
|
+
id: traceId,
|
|
4321
|
+
at: startedAt,
|
|
4322
|
+
endedAt,
|
|
4323
|
+
durationMs: endedAt - startedAt,
|
|
4324
|
+
provider: "claude-code-login",
|
|
4325
|
+
model: resolvedModel,
|
|
4326
|
+
kind: "callTools",
|
|
4327
|
+
systemPrompt,
|
|
4328
|
+
userPrompt: sentUserPrompt,
|
|
4329
|
+
tools: summarizeTools(tools),
|
|
4330
|
+
result: {
|
|
4331
|
+
inputTokens: usage.input_tokens,
|
|
4332
|
+
outputTokens: usage.output_tokens,
|
|
4333
|
+
cacheCreated,
|
|
4334
|
+
cacheRead,
|
|
4335
|
+
toolCalls: toolCalls.map((c) => ({ name: c.name, input: c.input }))
|
|
4336
|
+
}
|
|
4337
|
+
});
|
|
4338
|
+
return {
|
|
4339
|
+
toolCalls,
|
|
4340
|
+
inputTokens: usage.input_tokens,
|
|
4341
|
+
outputTokens: usage.output_tokens,
|
|
4342
|
+
cacheReadTokens: cacheRead,
|
|
4343
|
+
cacheCreationTokens: cacheCreated,
|
|
4344
|
+
appliedToolChoice: "auto"
|
|
4345
|
+
};
|
|
4346
|
+
} catch (e) {
|
|
4347
|
+
const endedAt = Date.now();
|
|
4348
|
+
emitLlmTraceEvent({
|
|
4349
|
+
id: traceId,
|
|
4350
|
+
at: startedAt,
|
|
4351
|
+
endedAt,
|
|
4352
|
+
durationMs: endedAt - startedAt,
|
|
4353
|
+
provider: "claude-code-login",
|
|
4354
|
+
model: resolvedModel,
|
|
4355
|
+
kind: "callTools",
|
|
4356
|
+
systemPrompt,
|
|
4357
|
+
userPrompt: sentUserPrompt,
|
|
4358
|
+
tools: summarizeTools(tools),
|
|
4359
|
+
error: { message: e instanceof Error ? e.message : String(e) }
|
|
4360
|
+
});
|
|
4361
|
+
throw e;
|
|
4362
|
+
}
|
|
4363
|
+
}
|
|
4364
|
+
async callWithTools() {
|
|
4365
|
+
throw new Error(
|
|
4366
|
+
"claude-code-login: callWithTools (an in-client agentic loop) is not part of the login client \u2014 ggui's harness runs the loop through callTools"
|
|
4367
|
+
);
|
|
4368
|
+
}
|
|
4369
|
+
};
|
|
3883
4370
|
var OpenAIAgent = class extends LLMAgent {
|
|
3884
4371
|
provider = "openai";
|
|
3885
4372
|
resolveModel(model) {
|
|
@@ -3908,10 +4395,15 @@ var OpenAIAgent = class extends LLMAgent {
|
|
|
3908
4395
|
}
|
|
3909
4396
|
}
|
|
3910
4397
|
}
|
|
4398
|
+
const textUsage = splitOpenAiUsage(
|
|
4399
|
+
response.usage?.input_tokens ?? 0,
|
|
4400
|
+
response.usage?.output_tokens ?? 0,
|
|
4401
|
+
response.usage?.input_tokens_details?.cached_tokens ?? 0
|
|
4402
|
+
);
|
|
3911
4403
|
return {
|
|
3912
4404
|
text,
|
|
3913
|
-
inputTokens:
|
|
3914
|
-
outputTokens:
|
|
4405
|
+
inputTokens: textUsage.tokens.input,
|
|
4406
|
+
outputTokens: textUsage.tokens.output,
|
|
3915
4407
|
sampling: { temperature: temperature ?? "provider-default" }
|
|
3916
4408
|
};
|
|
3917
4409
|
}
|
|
@@ -3950,14 +4442,20 @@ var OpenAIAgent = class extends LLMAgent {
|
|
|
3950
4442
|
const functionCalls = response.output.filter(
|
|
3951
4443
|
(o) => o.type === "function_call"
|
|
3952
4444
|
);
|
|
4445
|
+
const toolsUsage = splitOpenAiUsage(
|
|
4446
|
+
response.usage?.input_tokens ?? 0,
|
|
4447
|
+
response.usage?.output_tokens ?? 0,
|
|
4448
|
+
response.usage?.input_tokens_details?.cached_tokens ?? 0
|
|
4449
|
+
);
|
|
3953
4450
|
return {
|
|
3954
4451
|
toolCalls: functionCalls.map((fc) => ({
|
|
3955
4452
|
id: fc.call_id,
|
|
3956
4453
|
name: fc.name,
|
|
3957
4454
|
input: JSON.parse(fc.arguments ?? "{}")
|
|
3958
4455
|
})),
|
|
3959
|
-
inputTokens:
|
|
3960
|
-
outputTokens:
|
|
4456
|
+
inputTokens: toolsUsage.tokens.input,
|
|
4457
|
+
outputTokens: toolsUsage.tokens.output,
|
|
4458
|
+
cacheReadTokens: toolsUsage.cacheReadTokens
|
|
3961
4459
|
};
|
|
3962
4460
|
}
|
|
3963
4461
|
// Pending tool results — stored by sendToolResult, consumed by next callTools
|
|
@@ -3984,6 +4482,7 @@ var OpenAIAgent = class extends LLMAgent {
|
|
|
3984
4482
|
];
|
|
3985
4483
|
let totalIn = 0;
|
|
3986
4484
|
let totalOut = 0;
|
|
4485
|
+
let totalCacheRead = 0;
|
|
3987
4486
|
let allText = "";
|
|
3988
4487
|
for (let turn = 0; turn < maxTurns; turn++) {
|
|
3989
4488
|
const response = await this.apiCall(
|
|
@@ -3994,8 +4493,14 @@ var OpenAIAgent = class extends LLMAgent {
|
|
|
3994
4493
|
tools: openaiTools
|
|
3995
4494
|
})
|
|
3996
4495
|
);
|
|
3997
|
-
|
|
3998
|
-
|
|
4496
|
+
const turnUsage = splitOpenAiUsage(
|
|
4497
|
+
response.usage?.input_tokens ?? 0,
|
|
4498
|
+
response.usage?.output_tokens ?? 0,
|
|
4499
|
+
response.usage?.input_tokens_details?.cached_tokens ?? 0
|
|
4500
|
+
);
|
|
4501
|
+
totalIn += turnUsage.tokens.input;
|
|
4502
|
+
totalOut += turnUsage.tokens.output;
|
|
4503
|
+
totalCacheRead += turnUsage.cacheReadTokens;
|
|
3999
4504
|
const functionCalls = response.output.filter(
|
|
4000
4505
|
(o) => o.type === "function_call"
|
|
4001
4506
|
);
|
|
@@ -4026,6 +4531,7 @@ var OpenAIAgent = class extends LLMAgent {
|
|
|
4026
4531
|
text: allText,
|
|
4027
4532
|
inputTokens: totalIn,
|
|
4028
4533
|
outputTokens: totalOut,
|
|
4534
|
+
cacheReadTokens: totalCacheRead,
|
|
4029
4535
|
turnsUsed: input.length
|
|
4030
4536
|
};
|
|
4031
4537
|
}
|
|
@@ -4051,6 +4557,10 @@ function functionCallSteps(interaction) {
|
|
|
4051
4557
|
function toUserInputStep(text) {
|
|
4052
4558
|
return { type: "user_input", content: [{ type: "text", text }] };
|
|
4053
4559
|
}
|
|
4560
|
+
function googleInteractionUsage(usage) {
|
|
4561
|
+
const split = splitCacheInclusiveUsage(usage?.total_input_tokens ?? 0, usage?.total_output_tokens ?? 0, usage?.total_cached_tokens ?? 0);
|
|
4562
|
+
return { inputTokens: split.tokens.input, outputTokens: split.tokens.output, cacheReadTokens: split.cacheReadTokens };
|
|
4563
|
+
}
|
|
4054
4564
|
var GoogleAgent = class extends LLMAgent {
|
|
4055
4565
|
provider = "google";
|
|
4056
4566
|
resolveModel(model) {
|
|
@@ -4080,8 +4590,9 @@ var GoogleAgent = class extends LLMAgent {
|
|
|
4080
4590
|
);
|
|
4081
4591
|
return {
|
|
4082
4592
|
text: interactionText(interaction),
|
|
4083
|
-
|
|
4084
|
-
|
|
4593
|
+
// ggui#1186 (third site): `total_input_tokens` INCLUDES the cached part of the prompt — report the non-cached input.
|
|
4594
|
+
inputTokens: googleInteractionUsage(interaction.usage).inputTokens,
|
|
4595
|
+
outputTokens: googleInteractionUsage(interaction.usage).outputTokens,
|
|
4085
4596
|
sampling: { temperature: temperature ?? "provider-default" }
|
|
4086
4597
|
};
|
|
4087
4598
|
}
|
|
@@ -4113,7 +4624,8 @@ var GoogleAgent = class extends LLMAgent {
|
|
|
4113
4624
|
const usage = response.usageMetadata;
|
|
4114
4625
|
const result = {
|
|
4115
4626
|
text,
|
|
4116
|
-
|
|
4627
|
+
// ggui#1186 (third site): `promptTokenCount` includes `cachedContentTokenCount` — report the non-cached input.
|
|
4628
|
+
inputTokens: splitCacheInclusiveUsage(usage?.promptTokenCount ?? 0, usage?.candidatesTokenCount ?? 0, usage?.cachedContentTokenCount ?? 0).tokens.input,
|
|
4117
4629
|
outputTokens: usage?.candidatesTokenCount ?? 0
|
|
4118
4630
|
};
|
|
4119
4631
|
const endedAt = Date.now();
|
|
@@ -4257,8 +4769,8 @@ var GoogleAgent = class extends LLMAgent {
|
|
|
4257
4769
|
}));
|
|
4258
4770
|
return {
|
|
4259
4771
|
toolCalls,
|
|
4260
|
-
|
|
4261
|
-
|
|
4772
|
+
// ggui#1186 (third site): the coding loop reads this per-turn usage — the non-cached prompt as input, the cached subset reported.
|
|
4773
|
+
...googleInteractionUsage(interaction.usage)
|
|
4262
4774
|
};
|
|
4263
4775
|
}
|
|
4264
4776
|
// Pending tool results — stored by sendToolResult, consumed by next callTools
|
|
@@ -4282,6 +4794,7 @@ var GoogleAgent = class extends LLMAgent {
|
|
|
4282
4794
|
}));
|
|
4283
4795
|
let totalIn = 0;
|
|
4284
4796
|
let totalOut = 0;
|
|
4797
|
+
let totalCacheRead = 0;
|
|
4285
4798
|
let allText = "";
|
|
4286
4799
|
let turnsUsed = 0;
|
|
4287
4800
|
let interaction = await this.apiCall(
|
|
@@ -4295,8 +4808,10 @@ var GoogleAgent = class extends LLMAgent {
|
|
|
4295
4808
|
for (let turn = 0; turn < maxTurns; turn++) {
|
|
4296
4809
|
turnsUsed = turn + 1;
|
|
4297
4810
|
if (interaction.usage) {
|
|
4298
|
-
|
|
4299
|
-
|
|
4811
|
+
const turnUsage = googleInteractionUsage(interaction.usage);
|
|
4812
|
+
totalIn += turnUsage.inputTokens;
|
|
4813
|
+
totalOut += turnUsage.outputTokens;
|
|
4814
|
+
totalCacheRead += turnUsage.cacheReadTokens;
|
|
4300
4815
|
}
|
|
4301
4816
|
allText += interactionText(interaction);
|
|
4302
4817
|
const fnCalls = functionCallSteps(interaction);
|
|
@@ -4334,6 +4849,7 @@ var GoogleAgent = class extends LLMAgent {
|
|
|
4334
4849
|
text: allText,
|
|
4335
4850
|
inputTokens: totalIn,
|
|
4336
4851
|
outputTokens: totalOut,
|
|
4852
|
+
cacheReadTokens: totalCacheRead,
|
|
4337
4853
|
turnsUsed
|
|
4338
4854
|
};
|
|
4339
4855
|
}
|
|
@@ -4389,12 +4905,19 @@ var OpenRouterAgent = class extends LLMAgent {
|
|
|
4389
4905
|
this.sessionMessages.push({ role: "system", content: systemPrompt });
|
|
4390
4906
|
}
|
|
4391
4907
|
this.sessionMessages.push({ role: "user", content: userPrompt });
|
|
4908
|
+
const requested = toolChoice ?? "auto";
|
|
4909
|
+
const appliedToolChoice = requested === "required" && anthropicRejectsForcedToolChoice(resolved) ? "auto" : requested;
|
|
4910
|
+
if (appliedToolChoice !== requested) {
|
|
4911
|
+
console.warn(
|
|
4912
|
+
`[openrouter] callTools: ${resolved} rejects forced tool_choice (HTTP 400); caller's 'required' downgraded to 'auto'`
|
|
4913
|
+
);
|
|
4914
|
+
}
|
|
4392
4915
|
const response = await this.apiCall(
|
|
4393
4916
|
() => client.chatCompletion({
|
|
4394
4917
|
model: resolved,
|
|
4395
4918
|
messages: this.sessionMessages,
|
|
4396
4919
|
tools: orTools,
|
|
4397
|
-
tool_choice:
|
|
4920
|
+
tool_choice: appliedToolChoice
|
|
4398
4921
|
})
|
|
4399
4922
|
);
|
|
4400
4923
|
const choice = response.choices[0];
|
|
@@ -4413,7 +4936,8 @@ var OpenRouterAgent = class extends LLMAgent {
|
|
|
4413
4936
|
return {
|
|
4414
4937
|
toolCalls,
|
|
4415
4938
|
inputTokens: response.usage.prompt_tokens,
|
|
4416
|
-
outputTokens: response.usage.completion_tokens
|
|
4939
|
+
outputTokens: response.usage.completion_tokens,
|
|
4940
|
+
appliedToolChoice
|
|
4417
4941
|
};
|
|
4418
4942
|
}
|
|
4419
4943
|
async sendToolResult(results) {
|
|
@@ -4484,6 +5008,14 @@ function createAgent(providerOrConfig) {
|
|
|
4484
5008
|
const provider = typeof providerOrConfig === "string" ? providerOrConfig : providerOrConfig.provider;
|
|
4485
5009
|
const routeOverride = typeof providerOrConfig === "string" ? void 0 : providerOrConfig.routeOverride;
|
|
4486
5010
|
const onRetry = typeof providerOrConfig === "string" ? void 0 : providerOrConfig.onRetry;
|
|
5011
|
+
if (routeOverride?.claudeCodeLogin === true) {
|
|
5012
|
+
if (provider !== "anthropic") {
|
|
5013
|
+
throw new Error(
|
|
5014
|
+
`${provider}: the claude-code-login credential authenticates the Claude Code binary only \u2014 pick an anthropic route or supply a ${provider} API key`
|
|
5015
|
+
);
|
|
5016
|
+
}
|
|
5017
|
+
return new ClaudeCodeLoginAgent(routeOverride, onRetry);
|
|
5018
|
+
}
|
|
4487
5019
|
switch (provider) {
|
|
4488
5020
|
case "anthropic":
|
|
4489
5021
|
return new AnthropicAgent(routeOverride, onRetry);
|