@ggui-ai/ui-gen 0.20.0 → 0.22.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 +1078 -3464
- package/dist/adapters/generation-dispatch.js.map +1 -1
- package/dist/adapters/index.js +60 -3
- package/dist/adapters/index.js.map +1 -1
- package/dist/advanced/index.d.ts +4 -4
- package/dist/advanced/index.js +1346 -3557
- package/dist/advanced/index.js.map +1 -1
- package/dist/blueprint-validator.js +10 -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-BEkzEtNz.d.ts} +2 -2
- package/dist/{dispatch-N8wdNOX-.d.ts → dispatch-DJNpQ7DI.d.ts} +1 -1
- package/dist/evaluation/axis-checks/index.d.ts +3 -3
- package/dist/evaluation/axis-checks/index.js.map +1 -1
- package/dist/evaluation/axis-checks/registry.d.ts +1 -1
- package/dist/evaluation/index.d.ts +38 -6
- package/dist/evaluation/index.js +670 -3374
- 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 +10 -3
- package/dist/harness/check/runtime-render/index.js.map +1 -1
- package/dist/harness/check/runtime-render/render-check-worker.js +5 -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 +587 -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/prompts.js +1 -1
- package/dist/harness/prompts.js.map +1 -1
- package/dist/harness/result-types.d.ts +15 -2
- package/dist/harness/result-types.js.map +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 +23 -7
- package/dist/index.js +1346 -3557
- 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-mM_-1Yk3.d.ts} +59 -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 +128 -0
- package/src/adapters/claude/sdk.ts +9 -1
- package/src/adapters/generation-dispatch.ts +19 -2
- package/src/adapters/openai/tokens.ts +18 -0
- package/src/adapters/provider-router.ts +28 -0
- package/src/boilerplate/generate.ts +19 -4
- package/src/boilerplate/hard-sections.ts +1 -1
- package/src/boilerplate/templates/layouts/chat-mobile.tsx.tmpl +7 -3
- package/src/boilerplate/templates/layouts/chat-universal.tsx.tmpl +7 -3
- package/src/contract-context.ts +1 -1
- package/src/create-ui-generator.ts +49 -10
- package/src/evaluation/design-identity.ts +16 -5
- package/src/evaluation/index.ts +1 -0
- package/src/evaluation/llm-evaluator.ts +40 -13
- package/src/evaluation/types-public.ts +58 -0
- package/src/evaluation/visual-evaluator.ts +240 -30
- package/src/generator-build.ts +158 -0
- package/src/harness/check/runtime-render/adapter.ts +9 -0
- package/src/harness/check/runtime-render/render-check.ts +26 -5
- package/src/harness/claude-code-login-query.ts +529 -0
- package/src/harness/coding/assemble-result.ts +18 -2
- package/src/harness/coding/generate-task-runner.ts +75 -36
- package/src/harness/coding/run-coding-turn.ts +9 -0
- package/src/harness/coding/run-eval-round.ts +338 -38
- 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/result-types.ts +14 -0
- package/src/harness/runtime.ts +6 -0
- package/src/index.ts +2 -1
- 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,50 @@ function createAnthropicClient(rawKey) {
|
|
|
3227
3227
|
});
|
|
3228
3228
|
}
|
|
3229
3229
|
|
|
3230
|
+
// src/adapters/claude/claude-code-login.ts
|
|
3231
|
+
var LOGIN_CHILD_ENV_NAMES = [
|
|
3232
|
+
"PATH",
|
|
3233
|
+
"HOME",
|
|
3234
|
+
"USER",
|
|
3235
|
+
"LOGNAME",
|
|
3236
|
+
"SHELL",
|
|
3237
|
+
"TMPDIR",
|
|
3238
|
+
"TMP",
|
|
3239
|
+
"TEMP",
|
|
3240
|
+
"TERM",
|
|
3241
|
+
"TZ",
|
|
3242
|
+
"LANG",
|
|
3243
|
+
"CLAUDE_CONFIG_DIR",
|
|
3244
|
+
"CLAUDE_CODE_OAUTH_TOKEN",
|
|
3245
|
+
"XDG_CONFIG_HOME",
|
|
3246
|
+
"XDG_DATA_HOME",
|
|
3247
|
+
"XDG_CACHE_HOME",
|
|
3248
|
+
"XDG_STATE_HOME",
|
|
3249
|
+
"XDG_RUNTIME_DIR",
|
|
3250
|
+
"HTTPS_PROXY",
|
|
3251
|
+
"https_proxy",
|
|
3252
|
+
"HTTP_PROXY",
|
|
3253
|
+
"http_proxy",
|
|
3254
|
+
"NO_PROXY",
|
|
3255
|
+
"no_proxy",
|
|
3256
|
+
"ALL_PROXY",
|
|
3257
|
+
"all_proxy",
|
|
3258
|
+
"NODE_EXTRA_CA_CERTS",
|
|
3259
|
+
"SSL_CERT_FILE",
|
|
3260
|
+
"SSL_CERT_DIR"
|
|
3261
|
+
];
|
|
3262
|
+
var LOGIN_CHILD_ENV = new Set(LOGIN_CHILD_ENV_NAMES);
|
|
3263
|
+
function loginChildEnv(env) {
|
|
3264
|
+
const out = {};
|
|
3265
|
+
for (const [k, v] of Object.entries(env)) {
|
|
3266
|
+
if (v !== void 0 && (LOGIN_CHILD_ENV.has(k) || k.startsWith("LC_"))) out[k] = v;
|
|
3267
|
+
}
|
|
3268
|
+
return out;
|
|
3269
|
+
}
|
|
3270
|
+
function claudeCodeLoginQueryOptions() {
|
|
3271
|
+
return { tools: [], settingSources: [] };
|
|
3272
|
+
}
|
|
3273
|
+
|
|
3230
3274
|
// src/adapters/provider-router.ts
|
|
3231
3275
|
function getBedrockModelId(model) {
|
|
3232
3276
|
const BEDROCK_MAP = {
|
|
@@ -3277,6 +3321,26 @@ function toJsonValue(value, path) {
|
|
|
3277
3321
|
);
|
|
3278
3322
|
}
|
|
3279
3323
|
|
|
3324
|
+
// src/adapters/openai/tokens.ts
|
|
3325
|
+
function splitOpenAiUsage(inputTokensInclCached, outputTokens, cachedTokens) {
|
|
3326
|
+
return splitCacheInclusiveUsage(inputTokensInclCached, outputTokens, cachedTokens);
|
|
3327
|
+
}
|
|
3328
|
+
function splitCacheInclusiveUsage(inputTokensInclCached, outputTokens, cachedTokens) {
|
|
3329
|
+
const cacheRead = Math.min(
|
|
3330
|
+
Math.max(cachedTokens, 0),
|
|
3331
|
+
Math.max(inputTokensInclCached, 0)
|
|
3332
|
+
);
|
|
3333
|
+
const nonCachedInput = Math.max(inputTokensInclCached, 0) - cacheRead;
|
|
3334
|
+
return {
|
|
3335
|
+
tokens: {
|
|
3336
|
+
input: nonCachedInput,
|
|
3337
|
+
output: outputTokens,
|
|
3338
|
+
total: nonCachedInput + cacheRead + outputTokens
|
|
3339
|
+
},
|
|
3340
|
+
cacheReadTokens: cacheRead
|
|
3341
|
+
};
|
|
3342
|
+
}
|
|
3343
|
+
|
|
3280
3344
|
// src/harness/llm-trace-sink.ts
|
|
3281
3345
|
var activeSink2 = null;
|
|
3282
3346
|
function setLlmTraceSink(sink) {
|
|
@@ -3300,6 +3364,271 @@ function newLlmTraceId() {
|
|
|
3300
3364
|
return Date.now().toString(36) + Math.random().toString(36).slice(2, 10);
|
|
3301
3365
|
}
|
|
3302
3366
|
|
|
3367
|
+
// src/adapters/claude/tool-bridge.ts
|
|
3368
|
+
var MCP_SERVER_NAME = "ggui";
|
|
3369
|
+
|
|
3370
|
+
// src/harness/claude-code-login-query.ts
|
|
3371
|
+
var MCP_TOOL_PREFIX = `mcp__${MCP_SERVER_NAME}__`;
|
|
3372
|
+
function stripMcpToolPrefix(name) {
|
|
3373
|
+
return name.startsWith(MCP_TOOL_PREFIX) ? name.slice(MCP_TOOL_PREFIX.length) : name;
|
|
3374
|
+
}
|
|
3375
|
+
var LOGIN_API_KEY_SOURCE = "none";
|
|
3376
|
+
var ClaudeCodeLoginAuthError = class extends Error {
|
|
3377
|
+
apiKeySource;
|
|
3378
|
+
constructor(apiKeySource) {
|
|
3379
|
+
super(
|
|
3380
|
+
`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`
|
|
3381
|
+
);
|
|
3382
|
+
this.name = "ClaudeCodeLoginAuthError";
|
|
3383
|
+
this.apiKeySource = apiKeySource;
|
|
3384
|
+
}
|
|
3385
|
+
};
|
|
3386
|
+
var ClaudeCodeLoginResultError = class extends Error {
|
|
3387
|
+
subtype;
|
|
3388
|
+
errors;
|
|
3389
|
+
status;
|
|
3390
|
+
constructor(subtype, errors, status) {
|
|
3391
|
+
super(
|
|
3392
|
+
`claude-code-login: query() ended with ${subtype}` + (status !== void 0 ? ` (api status ${status})` : "") + `: ${errors.length > 0 ? errors.join("; ") : "no detail from the binary"}`
|
|
3393
|
+
);
|
|
3394
|
+
this.name = "ClaudeCodeLoginResultError";
|
|
3395
|
+
this.subtype = subtype;
|
|
3396
|
+
this.errors = errors;
|
|
3397
|
+
this.status = status;
|
|
3398
|
+
}
|
|
3399
|
+
};
|
|
3400
|
+
function createDenyingIntercept(intercepted) {
|
|
3401
|
+
return async (toolName, input) => {
|
|
3402
|
+
intercepted.push({ name: toolName, input });
|
|
3403
|
+
return {
|
|
3404
|
+
behavior: "deny",
|
|
3405
|
+
message: "ggui executes tools in its harness; the binary only proposes them."
|
|
3406
|
+
};
|
|
3407
|
+
};
|
|
3408
|
+
}
|
|
3409
|
+
async function* imageUserMessage(text, image) {
|
|
3410
|
+
const message = {
|
|
3411
|
+
role: "user",
|
|
3412
|
+
content: [
|
|
3413
|
+
{
|
|
3414
|
+
type: "image",
|
|
3415
|
+
source: { type: "base64", media_type: image.mediaType, data: image.base64 }
|
|
3416
|
+
},
|
|
3417
|
+
{ type: "text", text }
|
|
3418
|
+
]
|
|
3419
|
+
};
|
|
3420
|
+
yield { type: "user", message, parent_tool_use_id: null };
|
|
3421
|
+
}
|
|
3422
|
+
function buildLoginQueryOptions(params) {
|
|
3423
|
+
return {
|
|
3424
|
+
model: params.model,
|
|
3425
|
+
systemPrompt: params.systemPrompt,
|
|
3426
|
+
maxTurns: 1,
|
|
3427
|
+
thinking: { type: "disabled" },
|
|
3428
|
+
env: loginChildEnv(params.env ?? process.env),
|
|
3429
|
+
...claudeCodeLoginQueryOptions(),
|
|
3430
|
+
...params.tools !== void 0 ? {
|
|
3431
|
+
mcpServers: { [MCP_SERVER_NAME]: params.tools.server },
|
|
3432
|
+
canUseTool: params.tools.canUseTool
|
|
3433
|
+
} : {}
|
|
3434
|
+
};
|
|
3435
|
+
}
|
|
3436
|
+
var JSON_SCHEMA_TYPES = [
|
|
3437
|
+
"object",
|
|
3438
|
+
"array",
|
|
3439
|
+
"string",
|
|
3440
|
+
"number",
|
|
3441
|
+
"boolean",
|
|
3442
|
+
"null",
|
|
3443
|
+
"integer"
|
|
3444
|
+
];
|
|
3445
|
+
function isJsonSchemaType(value) {
|
|
3446
|
+
return typeof value === "string" && JSON_SCHEMA_TYPES.includes(value);
|
|
3447
|
+
}
|
|
3448
|
+
function isJsonObject(value) {
|
|
3449
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3450
|
+
}
|
|
3451
|
+
function schemaAt(value, path) {
|
|
3452
|
+
if (typeof value === "boolean") return value;
|
|
3453
|
+
if (!isJsonObject(value)) {
|
|
3454
|
+
throw new Error(
|
|
3455
|
+
`claude-code-login: ${path} must be a JSON-Schema object or boolean, got ${JSON.stringify(value)}`
|
|
3456
|
+
);
|
|
3457
|
+
}
|
|
3458
|
+
return toZodJsonSchema(value, path);
|
|
3459
|
+
}
|
|
3460
|
+
function toZodJsonSchema(parameters, path = "parameters") {
|
|
3461
|
+
const out = {};
|
|
3462
|
+
for (const [key, value] of Object.entries(parameters)) {
|
|
3463
|
+
if (value === void 0) continue;
|
|
3464
|
+
switch (key) {
|
|
3465
|
+
case "type": {
|
|
3466
|
+
if (!isJsonSchemaType(value)) {
|
|
3467
|
+
throw new Error(
|
|
3468
|
+
`claude-code-login: ${path}.type must be one of ${JSON_SCHEMA_TYPES.join("|")}, got ${JSON.stringify(value)}`
|
|
3469
|
+
);
|
|
3470
|
+
}
|
|
3471
|
+
out.type = value;
|
|
3472
|
+
break;
|
|
3473
|
+
}
|
|
3474
|
+
case "properties":
|
|
3475
|
+
case "patternProperties":
|
|
3476
|
+
case "$defs": {
|
|
3477
|
+
if (!isJsonObject(value))
|
|
3478
|
+
throw new Error(`claude-code-login: ${path}.${key} must be an object`);
|
|
3479
|
+
const record = {};
|
|
3480
|
+
for (const [name, sub] of Object.entries(value)) {
|
|
3481
|
+
if (sub === void 0) continue;
|
|
3482
|
+
record[name] = schemaAt(sub, `${path}.${key}.${name}`);
|
|
3483
|
+
}
|
|
3484
|
+
if (key === "$defs") {
|
|
3485
|
+
const defs = {};
|
|
3486
|
+
for (const [name, sub] of Object.entries(record)) {
|
|
3487
|
+
if (typeof sub === "boolean")
|
|
3488
|
+
throw new Error(`claude-code-login: ${path}.$defs.${name} must be an object`);
|
|
3489
|
+
defs[name] = sub;
|
|
3490
|
+
}
|
|
3491
|
+
out.$defs = defs;
|
|
3492
|
+
} else {
|
|
3493
|
+
out[key] = record;
|
|
3494
|
+
}
|
|
3495
|
+
break;
|
|
3496
|
+
}
|
|
3497
|
+
case "items": {
|
|
3498
|
+
out.items = Array.isArray(value) ? value.map((sub, i) => schemaAt(sub, `${path}.items[${i}]`)) : schemaAt(value, `${path}.items`);
|
|
3499
|
+
break;
|
|
3500
|
+
}
|
|
3501
|
+
case "additionalProperties":
|
|
3502
|
+
case "not": {
|
|
3503
|
+
out[key] = schemaAt(value, `${path}.${key}`);
|
|
3504
|
+
break;
|
|
3505
|
+
}
|
|
3506
|
+
case "anyOf":
|
|
3507
|
+
case "oneOf":
|
|
3508
|
+
case "allOf": {
|
|
3509
|
+
if (!Array.isArray(value))
|
|
3510
|
+
throw new Error(`claude-code-login: ${path}.${key} must be an array`);
|
|
3511
|
+
out[key] = value.map((sub, i) => {
|
|
3512
|
+
const s = schemaAt(sub, `${path}.${key}[${i}]`);
|
|
3513
|
+
if (typeof s === "boolean")
|
|
3514
|
+
throw new Error(`claude-code-login: ${path}.${key}[${i}] must be an object`);
|
|
3515
|
+
return s;
|
|
3516
|
+
});
|
|
3517
|
+
break;
|
|
3518
|
+
}
|
|
3519
|
+
case "required": {
|
|
3520
|
+
if (!Array.isArray(value) || !value.every((v) => typeof v === "string")) {
|
|
3521
|
+
throw new Error(`claude-code-login: ${path}.required must be an array of strings`);
|
|
3522
|
+
}
|
|
3523
|
+
out.required = value;
|
|
3524
|
+
break;
|
|
3525
|
+
}
|
|
3526
|
+
case "enum": {
|
|
3527
|
+
if (!Array.isArray(value) || !value.every(
|
|
3528
|
+
(v) => v === null || typeof v === "string" || typeof v === "number" || typeof v === "boolean"
|
|
3529
|
+
)) {
|
|
3530
|
+
throw new Error(`claude-code-login: ${path}.enum must be an array of primitives`);
|
|
3531
|
+
}
|
|
3532
|
+
out.enum = value;
|
|
3533
|
+
break;
|
|
3534
|
+
}
|
|
3535
|
+
default:
|
|
3536
|
+
out[key] = value;
|
|
3537
|
+
}
|
|
3538
|
+
}
|
|
3539
|
+
return out;
|
|
3540
|
+
}
|
|
3541
|
+
function createLoginToolServer(sdk, tools) {
|
|
3542
|
+
const sdkTools = tools.map((def) => {
|
|
3543
|
+
const schema = z.fromJSONSchema(toZodJsonSchema(def.parameters, `${def.name}.parameters`));
|
|
3544
|
+
if (!(schema instanceof z.ZodObject)) {
|
|
3545
|
+
throw new Error(
|
|
3546
|
+
`claude-code-login: tool ${def.name}'s parameters must be a JSON-Schema object`
|
|
3547
|
+
);
|
|
3548
|
+
}
|
|
3549
|
+
return sdk.tool(def.name, def.description, schema.shape, async () => {
|
|
3550
|
+
throw new Error(
|
|
3551
|
+
`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)`
|
|
3552
|
+
);
|
|
3553
|
+
});
|
|
3554
|
+
});
|
|
3555
|
+
return sdk.createSdkMcpServer({ name: MCP_SERVER_NAME, version: "1.0.0", tools: sdkTools });
|
|
3556
|
+
}
|
|
3557
|
+
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.";
|
|
3558
|
+
function describeCalls(calls) {
|
|
3559
|
+
return JSON.stringify(calls.map((c) => ({ name: stripMcpToolPrefix(c.name), input: c.input })));
|
|
3560
|
+
}
|
|
3561
|
+
async function collectLoginTurn(stream, opts) {
|
|
3562
|
+
let init;
|
|
3563
|
+
const toolCalls = [];
|
|
3564
|
+
let text = "";
|
|
3565
|
+
let thinkingBlocks = 0;
|
|
3566
|
+
let result;
|
|
3567
|
+
try {
|
|
3568
|
+
for await (const msg of stream) {
|
|
3569
|
+
if (msg.type === "system" && msg.subtype === "init") {
|
|
3570
|
+
const measured = msg;
|
|
3571
|
+
init = measured;
|
|
3572
|
+
opts.log(
|
|
3573
|
+
`[claude-code-login] apiKeySource=${measured.apiKeySource} claude_code_version=${measured.claude_code_version} model=${measured.model}`
|
|
3574
|
+
);
|
|
3575
|
+
if (measured.apiKeySource !== LOGIN_API_KEY_SOURCE) {
|
|
3576
|
+
throw new ClaudeCodeLoginAuthError(measured.apiKeySource);
|
|
3577
|
+
}
|
|
3578
|
+
} else if (msg.type === "assistant") {
|
|
3579
|
+
for (const block of msg.message.content) {
|
|
3580
|
+
if (block.type === "tool_use") {
|
|
3581
|
+
toolCalls.push({
|
|
3582
|
+
id: block.id,
|
|
3583
|
+
name: stripMcpToolPrefix(block.name),
|
|
3584
|
+
input: isRecord(block.input) ? toolArgsToJsonObject(block.input) : {}
|
|
3585
|
+
});
|
|
3586
|
+
} else if (block.type === "text") {
|
|
3587
|
+
text += block.text;
|
|
3588
|
+
} else if (block.type === "thinking" || block.type === "redacted_thinking") {
|
|
3589
|
+
thinkingBlocks += 1;
|
|
3590
|
+
}
|
|
3591
|
+
}
|
|
3592
|
+
} else if (msg.type === "result") {
|
|
3593
|
+
result = msg;
|
|
3594
|
+
}
|
|
3595
|
+
}
|
|
3596
|
+
} catch (e) {
|
|
3597
|
+
if (result === void 0) throw e;
|
|
3598
|
+
opts.log(
|
|
3599
|
+
`[claude-code-login] query() threw after its result message \u2014 the result decides (${e instanceof Error ? e.message : String(e)})`
|
|
3600
|
+
);
|
|
3601
|
+
}
|
|
3602
|
+
if (result === void 0) {
|
|
3603
|
+
throw new Error("claude-code-login: query() ended without a result message");
|
|
3604
|
+
}
|
|
3605
|
+
const usage = result.usage;
|
|
3606
|
+
const thinkingTokens = usage.output_tokens_details?.thinking_tokens ?? 0;
|
|
3607
|
+
opts.log(
|
|
3608
|
+
`[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}`
|
|
3609
|
+
);
|
|
3610
|
+
const streamSaw = describeCalls(toolCalls);
|
|
3611
|
+
const interceptSaw = describeCalls(opts.intercepted);
|
|
3612
|
+
if (streamSaw !== interceptSaw) {
|
|
3613
|
+
opts.warn(
|
|
3614
|
+
`[claude-code-login] intercept/stream disagreement \u2014 the stream's blocks win. intercepted=${interceptSaw} stream=${streamSaw}`
|
|
3615
|
+
);
|
|
3616
|
+
}
|
|
3617
|
+
if (result.subtype === "success") {
|
|
3618
|
+
if (result.is_error) {
|
|
3619
|
+
throw new ClaudeCodeLoginResultError(
|
|
3620
|
+
"success",
|
|
3621
|
+
[result.result],
|
|
3622
|
+
result.api_error_status ?? void 0
|
|
3623
|
+
);
|
|
3624
|
+
}
|
|
3625
|
+
} else if (result.subtype !== "error_max_turns") {
|
|
3626
|
+
const failed = result;
|
|
3627
|
+
throw new ClaudeCodeLoginResultError(failed.subtype, failed.errors, void 0);
|
|
3628
|
+
}
|
|
3629
|
+
return { init, toolCalls, text, result };
|
|
3630
|
+
}
|
|
3631
|
+
|
|
3303
3632
|
// src/harness/llm-router.ts
|
|
3304
3633
|
function addStrictSchemaConstraints(schema) {
|
|
3305
3634
|
const result = { ...schema };
|
|
@@ -3494,17 +3823,6 @@ var LLMAgent = class {
|
|
|
3494
3823
|
}
|
|
3495
3824
|
}
|
|
3496
3825
|
};
|
|
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
3826
|
function resolveAnthropicSampling(model, temperature) {
|
|
3509
3827
|
if (temperature === void 0) {
|
|
3510
3828
|
return { request: {}, applied: { temperature: "provider-default" } };
|
|
@@ -3880,6 +4198,200 @@ var AnthropicAgent = class extends LLMAgent {
|
|
|
3880
4198
|
}
|
|
3881
4199
|
}
|
|
3882
4200
|
};
|
|
4201
|
+
var ClaudeCodeLoginAgent = class extends LLMAgent {
|
|
4202
|
+
provider = "anthropic";
|
|
4203
|
+
resolveModel(model) {
|
|
4204
|
+
return model.startsWith("anthropic/") ? model.slice("anthropic/".length) : model;
|
|
4205
|
+
}
|
|
4206
|
+
async createClient() {
|
|
4207
|
+
const { query, tool, createSdkMcpServer } = await import('@anthropic-ai/claude-agent-sdk');
|
|
4208
|
+
return { query, tool, createSdkMcpServer };
|
|
4209
|
+
}
|
|
4210
|
+
async callText(model, systemPrompt, userPrompt, _maxTokens, _temperature) {
|
|
4211
|
+
const sdk = await this.getClient();
|
|
4212
|
+
const traceId = newLlmTraceId();
|
|
4213
|
+
const startedAt = Date.now();
|
|
4214
|
+
const resolvedModel = this.resolveModel(model);
|
|
4215
|
+
try {
|
|
4216
|
+
const turn = await this.apiCall(
|
|
4217
|
+
() => collectLoginTurn(
|
|
4218
|
+
sdk.query({
|
|
4219
|
+
prompt: userPrompt,
|
|
4220
|
+
options: buildLoginQueryOptions({ model: resolvedModel, systemPrompt })
|
|
4221
|
+
}),
|
|
4222
|
+
{ intercepted: [], kind: "callText", choice: "none", log: console.log, warn: console.warn }
|
|
4223
|
+
)
|
|
4224
|
+
);
|
|
4225
|
+
const usage = turn.result.usage;
|
|
4226
|
+
const endedAt = Date.now();
|
|
4227
|
+
emitLlmTraceEvent({
|
|
4228
|
+
id: traceId,
|
|
4229
|
+
at: startedAt,
|
|
4230
|
+
endedAt,
|
|
4231
|
+
durationMs: endedAt - startedAt,
|
|
4232
|
+
provider: "claude-code-login",
|
|
4233
|
+
model: resolvedModel,
|
|
4234
|
+
kind: "callText",
|
|
4235
|
+
systemPrompt,
|
|
4236
|
+
userPrompt,
|
|
4237
|
+
result: {
|
|
4238
|
+
inputTokens: usage.input_tokens,
|
|
4239
|
+
outputTokens: usage.output_tokens,
|
|
4240
|
+
cacheCreated: usage.cache_creation_input_tokens,
|
|
4241
|
+
cacheRead: usage.cache_read_input_tokens,
|
|
4242
|
+
text: turn.text
|
|
4243
|
+
}
|
|
4244
|
+
});
|
|
4245
|
+
return { text: turn.text, inputTokens: usage.input_tokens, outputTokens: usage.output_tokens };
|
|
4246
|
+
} catch (e) {
|
|
4247
|
+
const endedAt = Date.now();
|
|
4248
|
+
emitLlmTraceEvent({
|
|
4249
|
+
id: traceId,
|
|
4250
|
+
at: startedAt,
|
|
4251
|
+
endedAt,
|
|
4252
|
+
durationMs: endedAt - startedAt,
|
|
4253
|
+
provider: "claude-code-login",
|
|
4254
|
+
model: resolvedModel,
|
|
4255
|
+
kind: "callText",
|
|
4256
|
+
systemPrompt,
|
|
4257
|
+
userPrompt,
|
|
4258
|
+
error: { message: e instanceof Error ? e.message : String(e) }
|
|
4259
|
+
});
|
|
4260
|
+
throw e;
|
|
4261
|
+
}
|
|
4262
|
+
}
|
|
4263
|
+
async callVision(model, systemPrompt, userPrompt, image, _maxTokens) {
|
|
4264
|
+
const sdk = await this.getClient();
|
|
4265
|
+
const traceId = newLlmTraceId();
|
|
4266
|
+
const startedAt = Date.now();
|
|
4267
|
+
const resolvedModel = this.resolveModel(model);
|
|
4268
|
+
try {
|
|
4269
|
+
const turn = await this.apiCall(
|
|
4270
|
+
() => collectLoginTurn(
|
|
4271
|
+
sdk.query({
|
|
4272
|
+
prompt: imageUserMessage(userPrompt, image),
|
|
4273
|
+
options: buildLoginQueryOptions({ model: resolvedModel, systemPrompt })
|
|
4274
|
+
}),
|
|
4275
|
+
{ intercepted: [], kind: "callVision", choice: "none", log: console.log, warn: console.warn }
|
|
4276
|
+
)
|
|
4277
|
+
);
|
|
4278
|
+
const usage = turn.result.usage;
|
|
4279
|
+
const endedAt = Date.now();
|
|
4280
|
+
emitLlmTraceEvent({
|
|
4281
|
+
id: traceId,
|
|
4282
|
+
at: startedAt,
|
|
4283
|
+
endedAt,
|
|
4284
|
+
durationMs: endedAt - startedAt,
|
|
4285
|
+
provider: "claude-code-login",
|
|
4286
|
+
model: resolvedModel,
|
|
4287
|
+
kind: "callVision",
|
|
4288
|
+
systemPrompt,
|
|
4289
|
+
userPrompt,
|
|
4290
|
+
result: { inputTokens: usage.input_tokens, outputTokens: usage.output_tokens, text: turn.text }
|
|
4291
|
+
});
|
|
4292
|
+
return { text: turn.text, inputTokens: usage.input_tokens, outputTokens: usage.output_tokens };
|
|
4293
|
+
} catch (e) {
|
|
4294
|
+
const endedAt = Date.now();
|
|
4295
|
+
emitLlmTraceEvent({
|
|
4296
|
+
id: traceId,
|
|
4297
|
+
at: startedAt,
|
|
4298
|
+
endedAt,
|
|
4299
|
+
durationMs: endedAt - startedAt,
|
|
4300
|
+
provider: "claude-code-login",
|
|
4301
|
+
model: resolvedModel,
|
|
4302
|
+
kind: "callVision",
|
|
4303
|
+
systemPrompt,
|
|
4304
|
+
userPrompt,
|
|
4305
|
+
error: { message: e instanceof Error ? e.message : String(e) }
|
|
4306
|
+
});
|
|
4307
|
+
throw e;
|
|
4308
|
+
}
|
|
4309
|
+
}
|
|
4310
|
+
async callTools(model, systemPrompt, userPrompt, tools, toolChoice = "required", _scopedTools) {
|
|
4311
|
+
const sdk = await this.getClient();
|
|
4312
|
+
const traceId = newLlmTraceId();
|
|
4313
|
+
const startedAt = Date.now();
|
|
4314
|
+
const resolvedModel = this.resolveModel(model);
|
|
4315
|
+
const sentUserPrompt = toolChoice === "required" ? `${userPrompt}
|
|
4316
|
+
|
|
4317
|
+
${LOGIN_REQUIRED_TOOL_INSTRUCTION}` : userPrompt;
|
|
4318
|
+
try {
|
|
4319
|
+
const turn = await this.apiCall(() => {
|
|
4320
|
+
const intercepted = [];
|
|
4321
|
+
const server = createLoginToolServer(sdk, tools);
|
|
4322
|
+
return collectLoginTurn(
|
|
4323
|
+
sdk.query({
|
|
4324
|
+
prompt: sentUserPrompt,
|
|
4325
|
+
options: buildLoginQueryOptions({
|
|
4326
|
+
model: resolvedModel,
|
|
4327
|
+
systemPrompt,
|
|
4328
|
+
tools: { server, canUseTool: createDenyingIntercept(intercepted) }
|
|
4329
|
+
})
|
|
4330
|
+
}),
|
|
4331
|
+
{ intercepted, kind: "callTools", choice: toolChoice === "required" ? "required:instructed" : "auto", log: console.log, warn: console.warn }
|
|
4332
|
+
);
|
|
4333
|
+
});
|
|
4334
|
+
const usage = turn.result.usage;
|
|
4335
|
+
const cacheCreated = usage.cache_creation_input_tokens;
|
|
4336
|
+
const cacheRead = usage.cache_read_input_tokens;
|
|
4337
|
+
if (cacheCreated || cacheRead) {
|
|
4338
|
+
console.log(
|
|
4339
|
+
`[claude-code-login] callTools cache: created=${cacheCreated} read=${cacheRead} input=${usage.input_tokens} output=${usage.output_tokens}`
|
|
4340
|
+
);
|
|
4341
|
+
}
|
|
4342
|
+
const toolCalls = turn.toolCalls.map((c) => ({ id: c.id, name: c.name, input: c.input }));
|
|
4343
|
+
const endedAt = Date.now();
|
|
4344
|
+
emitLlmTraceEvent({
|
|
4345
|
+
id: traceId,
|
|
4346
|
+
at: startedAt,
|
|
4347
|
+
endedAt,
|
|
4348
|
+
durationMs: endedAt - startedAt,
|
|
4349
|
+
provider: "claude-code-login",
|
|
4350
|
+
model: resolvedModel,
|
|
4351
|
+
kind: "callTools",
|
|
4352
|
+
systemPrompt,
|
|
4353
|
+
userPrompt: sentUserPrompt,
|
|
4354
|
+
tools: summarizeTools(tools),
|
|
4355
|
+
result: {
|
|
4356
|
+
inputTokens: usage.input_tokens,
|
|
4357
|
+
outputTokens: usage.output_tokens,
|
|
4358
|
+
cacheCreated,
|
|
4359
|
+
cacheRead,
|
|
4360
|
+
toolCalls: toolCalls.map((c) => ({ name: c.name, input: c.input }))
|
|
4361
|
+
}
|
|
4362
|
+
});
|
|
4363
|
+
return {
|
|
4364
|
+
toolCalls,
|
|
4365
|
+
inputTokens: usage.input_tokens,
|
|
4366
|
+
outputTokens: usage.output_tokens,
|
|
4367
|
+
cacheReadTokens: cacheRead,
|
|
4368
|
+
cacheCreationTokens: cacheCreated,
|
|
4369
|
+
appliedToolChoice: "auto"
|
|
4370
|
+
};
|
|
4371
|
+
} catch (e) {
|
|
4372
|
+
const endedAt = Date.now();
|
|
4373
|
+
emitLlmTraceEvent({
|
|
4374
|
+
id: traceId,
|
|
4375
|
+
at: startedAt,
|
|
4376
|
+
endedAt,
|
|
4377
|
+
durationMs: endedAt - startedAt,
|
|
4378
|
+
provider: "claude-code-login",
|
|
4379
|
+
model: resolvedModel,
|
|
4380
|
+
kind: "callTools",
|
|
4381
|
+
systemPrompt,
|
|
4382
|
+
userPrompt: sentUserPrompt,
|
|
4383
|
+
tools: summarizeTools(tools),
|
|
4384
|
+
error: { message: e instanceof Error ? e.message : String(e) }
|
|
4385
|
+
});
|
|
4386
|
+
throw e;
|
|
4387
|
+
}
|
|
4388
|
+
}
|
|
4389
|
+
async callWithTools() {
|
|
4390
|
+
throw new Error(
|
|
4391
|
+
"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"
|
|
4392
|
+
);
|
|
4393
|
+
}
|
|
4394
|
+
};
|
|
3883
4395
|
var OpenAIAgent = class extends LLMAgent {
|
|
3884
4396
|
provider = "openai";
|
|
3885
4397
|
resolveModel(model) {
|
|
@@ -3908,10 +4420,15 @@ var OpenAIAgent = class extends LLMAgent {
|
|
|
3908
4420
|
}
|
|
3909
4421
|
}
|
|
3910
4422
|
}
|
|
4423
|
+
const textUsage = splitOpenAiUsage(
|
|
4424
|
+
response.usage?.input_tokens ?? 0,
|
|
4425
|
+
response.usage?.output_tokens ?? 0,
|
|
4426
|
+
response.usage?.input_tokens_details?.cached_tokens ?? 0
|
|
4427
|
+
);
|
|
3911
4428
|
return {
|
|
3912
4429
|
text,
|
|
3913
|
-
inputTokens:
|
|
3914
|
-
outputTokens:
|
|
4430
|
+
inputTokens: textUsage.tokens.input,
|
|
4431
|
+
outputTokens: textUsage.tokens.output,
|
|
3915
4432
|
sampling: { temperature: temperature ?? "provider-default" }
|
|
3916
4433
|
};
|
|
3917
4434
|
}
|
|
@@ -3950,14 +4467,20 @@ var OpenAIAgent = class extends LLMAgent {
|
|
|
3950
4467
|
const functionCalls = response.output.filter(
|
|
3951
4468
|
(o) => o.type === "function_call"
|
|
3952
4469
|
);
|
|
4470
|
+
const toolsUsage = splitOpenAiUsage(
|
|
4471
|
+
response.usage?.input_tokens ?? 0,
|
|
4472
|
+
response.usage?.output_tokens ?? 0,
|
|
4473
|
+
response.usage?.input_tokens_details?.cached_tokens ?? 0
|
|
4474
|
+
);
|
|
3953
4475
|
return {
|
|
3954
4476
|
toolCalls: functionCalls.map((fc) => ({
|
|
3955
4477
|
id: fc.call_id,
|
|
3956
4478
|
name: fc.name,
|
|
3957
4479
|
input: JSON.parse(fc.arguments ?? "{}")
|
|
3958
4480
|
})),
|
|
3959
|
-
inputTokens:
|
|
3960
|
-
outputTokens:
|
|
4481
|
+
inputTokens: toolsUsage.tokens.input,
|
|
4482
|
+
outputTokens: toolsUsage.tokens.output,
|
|
4483
|
+
cacheReadTokens: toolsUsage.cacheReadTokens
|
|
3961
4484
|
};
|
|
3962
4485
|
}
|
|
3963
4486
|
// Pending tool results — stored by sendToolResult, consumed by next callTools
|
|
@@ -3984,6 +4507,7 @@ var OpenAIAgent = class extends LLMAgent {
|
|
|
3984
4507
|
];
|
|
3985
4508
|
let totalIn = 0;
|
|
3986
4509
|
let totalOut = 0;
|
|
4510
|
+
let totalCacheRead = 0;
|
|
3987
4511
|
let allText = "";
|
|
3988
4512
|
for (let turn = 0; turn < maxTurns; turn++) {
|
|
3989
4513
|
const response = await this.apiCall(
|
|
@@ -3994,8 +4518,14 @@ var OpenAIAgent = class extends LLMAgent {
|
|
|
3994
4518
|
tools: openaiTools
|
|
3995
4519
|
})
|
|
3996
4520
|
);
|
|
3997
|
-
|
|
3998
|
-
|
|
4521
|
+
const turnUsage = splitOpenAiUsage(
|
|
4522
|
+
response.usage?.input_tokens ?? 0,
|
|
4523
|
+
response.usage?.output_tokens ?? 0,
|
|
4524
|
+
response.usage?.input_tokens_details?.cached_tokens ?? 0
|
|
4525
|
+
);
|
|
4526
|
+
totalIn += turnUsage.tokens.input;
|
|
4527
|
+
totalOut += turnUsage.tokens.output;
|
|
4528
|
+
totalCacheRead += turnUsage.cacheReadTokens;
|
|
3999
4529
|
const functionCalls = response.output.filter(
|
|
4000
4530
|
(o) => o.type === "function_call"
|
|
4001
4531
|
);
|
|
@@ -4026,6 +4556,7 @@ var OpenAIAgent = class extends LLMAgent {
|
|
|
4026
4556
|
text: allText,
|
|
4027
4557
|
inputTokens: totalIn,
|
|
4028
4558
|
outputTokens: totalOut,
|
|
4559
|
+
cacheReadTokens: totalCacheRead,
|
|
4029
4560
|
turnsUsed: input.length
|
|
4030
4561
|
};
|
|
4031
4562
|
}
|
|
@@ -4051,6 +4582,10 @@ function functionCallSteps(interaction) {
|
|
|
4051
4582
|
function toUserInputStep(text) {
|
|
4052
4583
|
return { type: "user_input", content: [{ type: "text", text }] };
|
|
4053
4584
|
}
|
|
4585
|
+
function googleInteractionUsage(usage) {
|
|
4586
|
+
const split = splitCacheInclusiveUsage(usage?.total_input_tokens ?? 0, usage?.total_output_tokens ?? 0, usage?.total_cached_tokens ?? 0);
|
|
4587
|
+
return { inputTokens: split.tokens.input, outputTokens: split.tokens.output, cacheReadTokens: split.cacheReadTokens };
|
|
4588
|
+
}
|
|
4054
4589
|
var GoogleAgent = class extends LLMAgent {
|
|
4055
4590
|
provider = "google";
|
|
4056
4591
|
resolveModel(model) {
|
|
@@ -4080,8 +4615,9 @@ var GoogleAgent = class extends LLMAgent {
|
|
|
4080
4615
|
);
|
|
4081
4616
|
return {
|
|
4082
4617
|
text: interactionText(interaction),
|
|
4083
|
-
|
|
4084
|
-
|
|
4618
|
+
// ggui#1186 (third site): `total_input_tokens` INCLUDES the cached part of the prompt — report the non-cached input.
|
|
4619
|
+
inputTokens: googleInteractionUsage(interaction.usage).inputTokens,
|
|
4620
|
+
outputTokens: googleInteractionUsage(interaction.usage).outputTokens,
|
|
4085
4621
|
sampling: { temperature: temperature ?? "provider-default" }
|
|
4086
4622
|
};
|
|
4087
4623
|
}
|
|
@@ -4113,7 +4649,8 @@ var GoogleAgent = class extends LLMAgent {
|
|
|
4113
4649
|
const usage = response.usageMetadata;
|
|
4114
4650
|
const result = {
|
|
4115
4651
|
text,
|
|
4116
|
-
|
|
4652
|
+
// ggui#1186 (third site): `promptTokenCount` includes `cachedContentTokenCount` — report the non-cached input.
|
|
4653
|
+
inputTokens: splitCacheInclusiveUsage(usage?.promptTokenCount ?? 0, usage?.candidatesTokenCount ?? 0, usage?.cachedContentTokenCount ?? 0).tokens.input,
|
|
4117
4654
|
outputTokens: usage?.candidatesTokenCount ?? 0
|
|
4118
4655
|
};
|
|
4119
4656
|
const endedAt = Date.now();
|
|
@@ -4257,8 +4794,8 @@ var GoogleAgent = class extends LLMAgent {
|
|
|
4257
4794
|
}));
|
|
4258
4795
|
return {
|
|
4259
4796
|
toolCalls,
|
|
4260
|
-
|
|
4261
|
-
|
|
4797
|
+
// ggui#1186 (third site): the coding loop reads this per-turn usage — the non-cached prompt as input, the cached subset reported.
|
|
4798
|
+
...googleInteractionUsage(interaction.usage)
|
|
4262
4799
|
};
|
|
4263
4800
|
}
|
|
4264
4801
|
// Pending tool results — stored by sendToolResult, consumed by next callTools
|
|
@@ -4282,6 +4819,7 @@ var GoogleAgent = class extends LLMAgent {
|
|
|
4282
4819
|
}));
|
|
4283
4820
|
let totalIn = 0;
|
|
4284
4821
|
let totalOut = 0;
|
|
4822
|
+
let totalCacheRead = 0;
|
|
4285
4823
|
let allText = "";
|
|
4286
4824
|
let turnsUsed = 0;
|
|
4287
4825
|
let interaction = await this.apiCall(
|
|
@@ -4295,8 +4833,10 @@ var GoogleAgent = class extends LLMAgent {
|
|
|
4295
4833
|
for (let turn = 0; turn < maxTurns; turn++) {
|
|
4296
4834
|
turnsUsed = turn + 1;
|
|
4297
4835
|
if (interaction.usage) {
|
|
4298
|
-
|
|
4299
|
-
|
|
4836
|
+
const turnUsage = googleInteractionUsage(interaction.usage);
|
|
4837
|
+
totalIn += turnUsage.inputTokens;
|
|
4838
|
+
totalOut += turnUsage.outputTokens;
|
|
4839
|
+
totalCacheRead += turnUsage.cacheReadTokens;
|
|
4300
4840
|
}
|
|
4301
4841
|
allText += interactionText(interaction);
|
|
4302
4842
|
const fnCalls = functionCallSteps(interaction);
|
|
@@ -4334,6 +4874,7 @@ var GoogleAgent = class extends LLMAgent {
|
|
|
4334
4874
|
text: allText,
|
|
4335
4875
|
inputTokens: totalIn,
|
|
4336
4876
|
outputTokens: totalOut,
|
|
4877
|
+
cacheReadTokens: totalCacheRead,
|
|
4337
4878
|
turnsUsed
|
|
4338
4879
|
};
|
|
4339
4880
|
}
|
|
@@ -4389,12 +4930,19 @@ var OpenRouterAgent = class extends LLMAgent {
|
|
|
4389
4930
|
this.sessionMessages.push({ role: "system", content: systemPrompt });
|
|
4390
4931
|
}
|
|
4391
4932
|
this.sessionMessages.push({ role: "user", content: userPrompt });
|
|
4933
|
+
const requested = toolChoice ?? "auto";
|
|
4934
|
+
const appliedToolChoice = requested === "required" && anthropicRejectsForcedToolChoice(resolved) ? "auto" : requested;
|
|
4935
|
+
if (appliedToolChoice !== requested) {
|
|
4936
|
+
console.warn(
|
|
4937
|
+
`[openrouter] callTools: ${resolved} rejects forced tool_choice (HTTP 400); caller's 'required' downgraded to 'auto'`
|
|
4938
|
+
);
|
|
4939
|
+
}
|
|
4392
4940
|
const response = await this.apiCall(
|
|
4393
4941
|
() => client.chatCompletion({
|
|
4394
4942
|
model: resolved,
|
|
4395
4943
|
messages: this.sessionMessages,
|
|
4396
4944
|
tools: orTools,
|
|
4397
|
-
tool_choice:
|
|
4945
|
+
tool_choice: appliedToolChoice
|
|
4398
4946
|
})
|
|
4399
4947
|
);
|
|
4400
4948
|
const choice = response.choices[0];
|
|
@@ -4413,7 +4961,8 @@ var OpenRouterAgent = class extends LLMAgent {
|
|
|
4413
4961
|
return {
|
|
4414
4962
|
toolCalls,
|
|
4415
4963
|
inputTokens: response.usage.prompt_tokens,
|
|
4416
|
-
outputTokens: response.usage.completion_tokens
|
|
4964
|
+
outputTokens: response.usage.completion_tokens,
|
|
4965
|
+
appliedToolChoice
|
|
4417
4966
|
};
|
|
4418
4967
|
}
|
|
4419
4968
|
async sendToolResult(results) {
|
|
@@ -4484,6 +5033,14 @@ function createAgent(providerOrConfig) {
|
|
|
4484
5033
|
const provider = typeof providerOrConfig === "string" ? providerOrConfig : providerOrConfig.provider;
|
|
4485
5034
|
const routeOverride = typeof providerOrConfig === "string" ? void 0 : providerOrConfig.routeOverride;
|
|
4486
5035
|
const onRetry = typeof providerOrConfig === "string" ? void 0 : providerOrConfig.onRetry;
|
|
5036
|
+
if (routeOverride?.claudeCodeLogin === true) {
|
|
5037
|
+
if (provider !== "anthropic") {
|
|
5038
|
+
throw new Error(
|
|
5039
|
+
`${provider}: the claude-code-login credential authenticates the Claude Code binary only \u2014 pick an anthropic route or supply a ${provider} API key`
|
|
5040
|
+
);
|
|
5041
|
+
}
|
|
5042
|
+
return new ClaudeCodeLoginAgent(routeOverride, onRetry);
|
|
5043
|
+
}
|
|
4487
5044
|
switch (provider) {
|
|
4488
5045
|
case "anthropic":
|
|
4489
5046
|
return new AnthropicAgent(routeOverride, onRetry);
|