claude-code-rust 0.6.0 → 0.7.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/README.md +5 -5
- package/agent-sdk/dist/bridge/agents.js +75 -0
- package/agent-sdk/dist/bridge/commands.js +42 -11
- package/agent-sdk/dist/bridge/error_classification.js +55 -0
- package/agent-sdk/dist/bridge/events.js +83 -0
- package/agent-sdk/dist/bridge/history.js +0 -17
- package/agent-sdk/dist/bridge/message_handlers.js +428 -0
- package/agent-sdk/dist/bridge/permissions.js +15 -3
- package/agent-sdk/dist/bridge/session_lifecycle.js +368 -0
- package/agent-sdk/dist/bridge/shared.js +49 -0
- package/agent-sdk/dist/bridge/state_parsing.js +66 -0
- package/agent-sdk/dist/bridge/tool_calls.js +168 -0
- package/agent-sdk/dist/bridge/user_interaction.js +175 -0
- package/agent-sdk/dist/bridge.js +21 -1323
- package/agent-sdk/dist/bridge.test.js +113 -55
- package/bin/claude-rs.js +1 -1
- package/package.json +5 -6
- package/scripts/postinstall.js +2 -2
- package/agent-sdk/README.md +0 -13
- package/agent-sdk/dist/bridge/usage.js +0 -95
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import test from "node:test";
|
|
2
2
|
import assert from "node:assert/strict";
|
|
3
|
-
import { CACHE_SPLIT_POLICY, buildRateLimitUpdate,
|
|
3
|
+
import { AsyncQueue, CACHE_SPLIT_POLICY, buildRateLimitUpdate, buildQueryOptions, buildToolResultFields, createToolCall, mapAvailableAgents, mapSessionMessagesToUpdates, mapSdkSessions, agentSdkVersionCompatibilityError, looksLikeAuthRequired, normalizeToolResultText, parseFastModeState, parseRateLimitStatus, normalizeToolKind, parseCommandEnvelope, permissionOptionsFromSuggestions, permissionResultFromOutcome, previewKilobyteLabel, resolveInstalledAgentSdkVersion, unwrapToolUseResult, } from "./bridge.js";
|
|
4
4
|
test("parseCommandEnvelope validates initialize command", () => {
|
|
5
5
|
const parsed = parseCommandEnvelope(JSON.stringify({
|
|
6
6
|
request_id: "req-1",
|
|
@@ -19,6 +19,13 @@ test("parseCommandEnvelope validates resume_session command without cwd", () =>
|
|
|
19
19
|
request_id: "req-2",
|
|
20
20
|
command: "resume_session",
|
|
21
21
|
session_id: "session-123",
|
|
22
|
+
launch_settings: {
|
|
23
|
+
model: "haiku",
|
|
24
|
+
language: "German",
|
|
25
|
+
permission_mode: "plan",
|
|
26
|
+
thinking_mode: "adaptive",
|
|
27
|
+
effort_level: "high",
|
|
28
|
+
},
|
|
22
29
|
}));
|
|
23
30
|
assert.equal(parsed.requestId, "req-2");
|
|
24
31
|
assert.equal(parsed.command.command, "resume_session");
|
|
@@ -26,6 +33,97 @@ test("parseCommandEnvelope validates resume_session command without cwd", () =>
|
|
|
26
33
|
throw new Error("unexpected command variant");
|
|
27
34
|
}
|
|
28
35
|
assert.equal(parsed.command.session_id, "session-123");
|
|
36
|
+
assert.equal(parsed.command.launch_settings.model, "haiku");
|
|
37
|
+
assert.equal(parsed.command.launch_settings.language, "German");
|
|
38
|
+
assert.equal(parsed.command.launch_settings.permission_mode, "plan");
|
|
39
|
+
assert.equal(parsed.command.launch_settings.thinking_mode, "adaptive");
|
|
40
|
+
assert.equal(parsed.command.launch_settings.effort_level, "high");
|
|
41
|
+
});
|
|
42
|
+
test("buildQueryOptions maps launch settings into sdk query options", () => {
|
|
43
|
+
const input = new AsyncQueue();
|
|
44
|
+
const options = buildQueryOptions({
|
|
45
|
+
cwd: "C:/work",
|
|
46
|
+
launchSettings: {
|
|
47
|
+
model: "haiku",
|
|
48
|
+
language: "German",
|
|
49
|
+
permission_mode: "plan",
|
|
50
|
+
thinking_mode: "adaptive",
|
|
51
|
+
effort_level: "medium",
|
|
52
|
+
},
|
|
53
|
+
provisionalSessionId: "session-1",
|
|
54
|
+
input,
|
|
55
|
+
canUseTool: async () => ({ behavior: "deny", message: "not used" }),
|
|
56
|
+
enableSdkDebug: false,
|
|
57
|
+
enableSpawnDebug: false,
|
|
58
|
+
sessionIdForLogs: () => "session-1",
|
|
59
|
+
});
|
|
60
|
+
assert.equal(options.model, "haiku");
|
|
61
|
+
assert.deepEqual(options.systemPrompt, {
|
|
62
|
+
type: "preset",
|
|
63
|
+
preset: "claude_code",
|
|
64
|
+
append: "Always respond to the user in German unless the user explicitly asks for a different language. " +
|
|
65
|
+
"Keep code, shell commands, file paths, API names, tool names, and raw error text unchanged unless the user explicitly asks for translation.",
|
|
66
|
+
});
|
|
67
|
+
assert.equal(options.permissionMode, "plan");
|
|
68
|
+
assert.deepEqual(options.thinking, { type: "adaptive" });
|
|
69
|
+
assert.equal(options.effort, "medium");
|
|
70
|
+
assert.equal(options.sessionId, "session-1");
|
|
71
|
+
assert.deepEqual(options.settingSources, ["user", "project", "local"]);
|
|
72
|
+
});
|
|
73
|
+
test("buildQueryOptions maps disabled thinking mode into sdk query options", () => {
|
|
74
|
+
const input = new AsyncQueue();
|
|
75
|
+
const options = buildQueryOptions({
|
|
76
|
+
cwd: "C:/work",
|
|
77
|
+
launchSettings: {
|
|
78
|
+
thinking_mode: "disabled",
|
|
79
|
+
effort_level: "high",
|
|
80
|
+
},
|
|
81
|
+
provisionalSessionId: "session-3",
|
|
82
|
+
input,
|
|
83
|
+
canUseTool: async () => ({ behavior: "deny", message: "not used" }),
|
|
84
|
+
enableSdkDebug: false,
|
|
85
|
+
enableSpawnDebug: false,
|
|
86
|
+
sessionIdForLogs: () => "session-3",
|
|
87
|
+
});
|
|
88
|
+
assert.deepEqual(options.thinking, { type: "disabled" });
|
|
89
|
+
assert.equal("effort" in options, false);
|
|
90
|
+
});
|
|
91
|
+
test("buildQueryOptions omits startup overrides for default logout path", () => {
|
|
92
|
+
const input = new AsyncQueue();
|
|
93
|
+
const options = buildQueryOptions({
|
|
94
|
+
cwd: "C:/work",
|
|
95
|
+
launchSettings: {},
|
|
96
|
+
provisionalSessionId: "session-2",
|
|
97
|
+
input,
|
|
98
|
+
canUseTool: async () => ({ behavior: "deny", message: "not used" }),
|
|
99
|
+
enableSdkDebug: false,
|
|
100
|
+
enableSpawnDebug: false,
|
|
101
|
+
sessionIdForLogs: () => "session-2",
|
|
102
|
+
});
|
|
103
|
+
assert.equal("model" in options, false);
|
|
104
|
+
assert.equal("permissionMode" in options, false);
|
|
105
|
+
assert.equal("systemPrompt" in options, false);
|
|
106
|
+
});
|
|
107
|
+
test("buildQueryOptions trims language before appending system prompt", () => {
|
|
108
|
+
const input = new AsyncQueue();
|
|
109
|
+
const options = buildQueryOptions({
|
|
110
|
+
cwd: "C:/work",
|
|
111
|
+
launchSettings: {
|
|
112
|
+
language: " German ",
|
|
113
|
+
},
|
|
114
|
+
provisionalSessionId: "session-4",
|
|
115
|
+
input,
|
|
116
|
+
canUseTool: async () => ({ behavior: "deny", message: "not used" }),
|
|
117
|
+
enableSdkDebug: false,
|
|
118
|
+
enableSpawnDebug: false,
|
|
119
|
+
sessionIdForLogs: () => "session-4",
|
|
120
|
+
});
|
|
121
|
+
assert.deepEqual(options.systemPrompt, {
|
|
122
|
+
type: "preset",
|
|
123
|
+
preset: "claude_code",
|
|
124
|
+
append: "Always respond to the user in German unless the user explicitly asks for a different language. " +
|
|
125
|
+
"Keep code, shell commands, file paths, API names, tool names, and raw error text unchanged unless the user explicitly asks for translation.",
|
|
126
|
+
});
|
|
29
127
|
});
|
|
30
128
|
test("parseCommandEnvelope rejects missing required fields", () => {
|
|
31
129
|
assert.throws(() => parseCommandEnvelope(JSON.stringify({ command: "set_model", session_id: "s1" })), /set_model\.model must be a string/);
|
|
@@ -297,7 +395,7 @@ test("permissionOptionsFromSuggestions uses persistent label when settings scope
|
|
|
297
395
|
type: "addRules",
|
|
298
396
|
behavior: "allow",
|
|
299
397
|
destination: "localSettings",
|
|
300
|
-
rules: [{ toolName: "Bash", ruleContent: "
|
|
398
|
+
rules: [{ toolName: "Bash", ruleContent: "pnpm install" }],
|
|
301
399
|
},
|
|
302
400
|
]);
|
|
303
401
|
assert.deepEqual(options, [
|
|
@@ -307,13 +405,13 @@ test("permissionOptionsFromSuggestions uses persistent label when settings scope
|
|
|
307
405
|
]);
|
|
308
406
|
});
|
|
309
407
|
test("permissionResultFromOutcome keeps Bash allow_always suggestions unchanged", () => {
|
|
310
|
-
const allow = permissionResultFromOutcome({ outcome: "selected", option_id: "allow_always" }, "tool-1", { command: "
|
|
408
|
+
const allow = permissionResultFromOutcome({ outcome: "selected", option_id: "allow_always" }, "tool-1", { command: "pnpm install" }, [
|
|
311
409
|
{
|
|
312
410
|
type: "addRules",
|
|
313
411
|
behavior: "allow",
|
|
314
|
-
destination: "
|
|
412
|
+
destination: "localSettings",
|
|
315
413
|
rules: [
|
|
316
|
-
{ toolName: "Bash", ruleContent: "
|
|
414
|
+
{ toolName: "Bash", ruleContent: "pnpm install" },
|
|
317
415
|
{ toolName: "WebFetch", ruleContent: "https://example.com" },
|
|
318
416
|
{ toolName: "Bash", ruleContent: "dir /B" },
|
|
319
417
|
],
|
|
@@ -327,9 +425,9 @@ test("permissionResultFromOutcome keeps Bash allow_always suggestions unchanged"
|
|
|
327
425
|
{
|
|
328
426
|
type: "addRules",
|
|
329
427
|
behavior: "allow",
|
|
330
|
-
destination: "
|
|
428
|
+
destination: "localSettings",
|
|
331
429
|
rules: [
|
|
332
|
-
{ toolName: "Bash", ruleContent: "
|
|
430
|
+
{ toolName: "Bash", ruleContent: "pnpm install" },
|
|
333
431
|
{ toolName: "WebFetch", ruleContent: "https://example.com" },
|
|
334
432
|
{ toolName: "Bash", ruleContent: "dir /B" },
|
|
335
433
|
],
|
|
@@ -367,7 +465,7 @@ test("permissionResultFromOutcome falls back to session tool rule for allow_sess
|
|
|
367
465
|
},
|
|
368
466
|
]);
|
|
369
467
|
});
|
|
370
|
-
test("permissionResultFromOutcome
|
|
468
|
+
test("permissionResultFromOutcome falls back to localSettings rule for allow_always when only session suggestions exist", () => {
|
|
371
469
|
const allow = permissionResultFromOutcome({ outcome: "selected", option_id: "allow_always" }, "tool-4", { file_path: "C:\\work\\baz.txt" }, [
|
|
372
470
|
{
|
|
373
471
|
type: "addRules",
|
|
@@ -380,45 +478,14 @@ test("permissionResultFromOutcome does not apply session suggestions to allow_al
|
|
|
380
478
|
if (allow.behavior !== "allow") {
|
|
381
479
|
throw new Error("expected allow permission result");
|
|
382
480
|
}
|
|
383
|
-
assert.
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
outputTokens: 34,
|
|
390
|
-
cacheReadInputTokens: 5,
|
|
391
|
-
cacheCreationInputTokens: 6,
|
|
392
|
-
},
|
|
393
|
-
});
|
|
394
|
-
assert.deepEqual(update, {
|
|
395
|
-
type: "usage_update",
|
|
396
|
-
usage: {
|
|
397
|
-
input_tokens: 12,
|
|
398
|
-
output_tokens: 34,
|
|
399
|
-
cache_read_tokens: 5,
|
|
400
|
-
cache_write_tokens: 6,
|
|
401
|
-
},
|
|
402
|
-
});
|
|
403
|
-
});
|
|
404
|
-
test("buildUsageUpdateFromResult includes cost and context window fields", () => {
|
|
405
|
-
const update = buildUsageUpdateFromResult({
|
|
406
|
-
total_cost_usd: 1.25,
|
|
407
|
-
modelUsage: {
|
|
408
|
-
"claude-sonnet-4-5": {
|
|
409
|
-
contextWindow: 200000,
|
|
410
|
-
maxOutputTokens: 64000,
|
|
411
|
-
},
|
|
412
|
-
},
|
|
413
|
-
});
|
|
414
|
-
assert.deepEqual(update, {
|
|
415
|
-
type: "usage_update",
|
|
416
|
-
usage: {
|
|
417
|
-
total_cost_usd: 1.25,
|
|
418
|
-
context_window: 200000,
|
|
419
|
-
max_output_tokens: 64000,
|
|
481
|
+
assert.deepEqual(allow.updatedPermissions, [
|
|
482
|
+
{
|
|
483
|
+
type: "addRules",
|
|
484
|
+
rules: [{ toolName: "Write" }],
|
|
485
|
+
behavior: "allow",
|
|
486
|
+
destination: "localSettings",
|
|
420
487
|
},
|
|
421
|
-
|
|
488
|
+
]);
|
|
422
489
|
});
|
|
423
490
|
test("looksLikeAuthRequired detects login hints", () => {
|
|
424
491
|
assert.equal(looksLikeAuthRequired("Please run /login to continue"), true);
|
|
@@ -486,15 +553,6 @@ test("mapSessionMessagesToUpdates maps message content blocks", () => {
|
|
|
486
553
|
assert.equal(variantCounts.get("agent_message_chunk"), 1);
|
|
487
554
|
assert.equal(variantCounts.get("tool_call"), 1);
|
|
488
555
|
assert.equal(variantCounts.get("tool_call_update"), 1);
|
|
489
|
-
assert.equal(variantCounts.get("usage_update"), 1);
|
|
490
|
-
const usage = updates.find((update) => update.type === "usage_update");
|
|
491
|
-
assert.ok(usage && usage.type === "usage_update");
|
|
492
|
-
assert.deepEqual(usage.usage, {
|
|
493
|
-
input_tokens: 11,
|
|
494
|
-
output_tokens: 7,
|
|
495
|
-
cache_read_tokens: 5,
|
|
496
|
-
cache_write_tokens: 3,
|
|
497
|
-
});
|
|
498
556
|
});
|
|
499
557
|
test("mapSessionMessagesToUpdates ignores unsupported records", () => {
|
|
500
558
|
const updates = mapSessionMessagesToUpdates([
|
package/bin/claude-rs.js
CHANGED
|
@@ -24,7 +24,7 @@ function resolveInstall() {
|
|
|
24
24
|
return {
|
|
25
25
|
error:
|
|
26
26
|
`Missing binary at ${binaryPath}\n` +
|
|
27
|
-
"Reinstall with `
|
|
27
|
+
"Reinstall with `pnpm add -g claude-code-rust` to fetch release artifacts."
|
|
28
28
|
};
|
|
29
29
|
}
|
|
30
30
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-code-rust",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Claude Code Rust - native Rust terminal interface for Claude Code",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cli",
|
|
@@ -31,14 +31,13 @@
|
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"@anthropic-ai/claude-agent-sdk": "0.2.63"
|
|
33
33
|
},
|
|
34
|
-
"scripts": {
|
|
35
|
-
"postinstall": "node ./scripts/postinstall.js",
|
|
36
|
-
"prepack": "pnpm -C agent-sdk run build"
|
|
37
|
-
},
|
|
38
34
|
"engines": {
|
|
39
35
|
"node": ">=18"
|
|
40
36
|
},
|
|
41
37
|
"publishConfig": {
|
|
42
38
|
"access": "public"
|
|
39
|
+
},
|
|
40
|
+
"scripts": {
|
|
41
|
+
"postinstall": "node ./scripts/postinstall.js"
|
|
43
42
|
}
|
|
44
|
-
}
|
|
43
|
+
}
|
package/scripts/postinstall.js
CHANGED
|
@@ -27,7 +27,7 @@ async function downloadFile(url, outPath, redirects = 0) {
|
|
|
27
27
|
await new Promise((resolve, reject) => {
|
|
28
28
|
const req = https.get(
|
|
29
29
|
url,
|
|
30
|
-
{ headers: { "User-Agent": "claude-code-rust-
|
|
30
|
+
{ headers: { "User-Agent": "claude-code-rust-pnpm-installer" } },
|
|
31
31
|
(res) => {
|
|
32
32
|
const status = res.statusCode ?? 0;
|
|
33
33
|
|
|
@@ -60,7 +60,7 @@ async function main() {
|
|
|
60
60
|
const info = getTargetInfo();
|
|
61
61
|
if (!info) {
|
|
62
62
|
const key = `${process.platform}:${process.arch}`;
|
|
63
|
-
throw new Error(`Unsupported platform/arch for claude-code-rust
|
|
63
|
+
throw new Error(`Unsupported platform/arch for claude-code-rust package install: ${key}`);
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
const pkgJsonPath = path.join(__dirname, "..", "package.json");
|
package/agent-sdk/README.md
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
# claude-rs agent-sdk bridge
|
|
2
|
-
|
|
3
|
-
Initial scaffold for the NDJSON stdio bridge that will connect Rust (`claude-code-rust`) with `@anthropic-ai/claude-agent-sdk`.
|
|
4
|
-
|
|
5
|
-
## Local build
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
npm install
|
|
9
|
-
npm run build
|
|
10
|
-
```
|
|
11
|
-
|
|
12
|
-
Build output is written to `dist/bridge.mjs`.
|
|
13
|
-
|
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
import { asRecordOrNull } from "./shared.js";
|
|
2
|
-
function numberField(record, ...keys) {
|
|
3
|
-
for (const key of keys) {
|
|
4
|
-
const value = record[key];
|
|
5
|
-
if (typeof value === "number" && Number.isFinite(value)) {
|
|
6
|
-
return value;
|
|
7
|
-
}
|
|
8
|
-
}
|
|
9
|
-
return undefined;
|
|
10
|
-
}
|
|
11
|
-
function selectModelUsageRecord(session, message) {
|
|
12
|
-
const modelUsageRaw = asRecordOrNull(message.modelUsage);
|
|
13
|
-
if (!modelUsageRaw) {
|
|
14
|
-
return null;
|
|
15
|
-
}
|
|
16
|
-
const sortedKeys = Object.keys(modelUsageRaw).sort();
|
|
17
|
-
if (sortedKeys.length === 0) {
|
|
18
|
-
return null;
|
|
19
|
-
}
|
|
20
|
-
const preferredKeys = new Set();
|
|
21
|
-
if (session?.model) {
|
|
22
|
-
preferredKeys.add(session.model);
|
|
23
|
-
}
|
|
24
|
-
if (typeof message.model === "string") {
|
|
25
|
-
preferredKeys.add(message.model);
|
|
26
|
-
}
|
|
27
|
-
for (const key of preferredKeys) {
|
|
28
|
-
const value = asRecordOrNull(modelUsageRaw[key]);
|
|
29
|
-
if (value) {
|
|
30
|
-
return value;
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
for (const key of sortedKeys) {
|
|
34
|
-
const value = asRecordOrNull(modelUsageRaw[key]);
|
|
35
|
-
if (value) {
|
|
36
|
-
return value;
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
return null;
|
|
40
|
-
}
|
|
41
|
-
export function buildUsageUpdateFromResultForSession(session, message) {
|
|
42
|
-
const usage = asRecordOrNull(message.usage);
|
|
43
|
-
const inputTokens = usage ? numberField(usage, "inputTokens", "input_tokens") : undefined;
|
|
44
|
-
const outputTokens = usage ? numberField(usage, "outputTokens", "output_tokens") : undefined;
|
|
45
|
-
const cacheReadTokens = usage
|
|
46
|
-
? numberField(usage, "cacheReadInputTokens", "cache_read_input_tokens", "cache_read_tokens")
|
|
47
|
-
: undefined;
|
|
48
|
-
const cacheWriteTokens = usage
|
|
49
|
-
? numberField(usage, "cacheCreationInputTokens", "cache_creation_input_tokens", "cache_write_tokens")
|
|
50
|
-
: undefined;
|
|
51
|
-
const totalCostUsd = numberField(message, "total_cost_usd", "totalCostUsd");
|
|
52
|
-
let turnCostUsd;
|
|
53
|
-
if (totalCostUsd !== undefined && session) {
|
|
54
|
-
if (session.lastTotalCostUsd === undefined) {
|
|
55
|
-
turnCostUsd = totalCostUsd;
|
|
56
|
-
}
|
|
57
|
-
else {
|
|
58
|
-
turnCostUsd = Math.max(0, totalCostUsd - session.lastTotalCostUsd);
|
|
59
|
-
}
|
|
60
|
-
session.lastTotalCostUsd = totalCostUsd;
|
|
61
|
-
}
|
|
62
|
-
const modelUsage = selectModelUsageRecord(session, message);
|
|
63
|
-
const contextWindow = modelUsage
|
|
64
|
-
? numberField(modelUsage, "contextWindow", "context_window")
|
|
65
|
-
: undefined;
|
|
66
|
-
const maxOutputTokens = modelUsage
|
|
67
|
-
? numberField(modelUsage, "maxOutputTokens", "max_output_tokens")
|
|
68
|
-
: undefined;
|
|
69
|
-
if (inputTokens === undefined &&
|
|
70
|
-
outputTokens === undefined &&
|
|
71
|
-
cacheReadTokens === undefined &&
|
|
72
|
-
cacheWriteTokens === undefined &&
|
|
73
|
-
totalCostUsd === undefined &&
|
|
74
|
-
turnCostUsd === undefined &&
|
|
75
|
-
contextWindow === undefined &&
|
|
76
|
-
maxOutputTokens === undefined) {
|
|
77
|
-
return null;
|
|
78
|
-
}
|
|
79
|
-
return {
|
|
80
|
-
type: "usage_update",
|
|
81
|
-
usage: {
|
|
82
|
-
...(inputTokens !== undefined ? { input_tokens: inputTokens } : {}),
|
|
83
|
-
...(outputTokens !== undefined ? { output_tokens: outputTokens } : {}),
|
|
84
|
-
...(cacheReadTokens !== undefined ? { cache_read_tokens: cacheReadTokens } : {}),
|
|
85
|
-
...(cacheWriteTokens !== undefined ? { cache_write_tokens: cacheWriteTokens } : {}),
|
|
86
|
-
...(totalCostUsd !== undefined ? { total_cost_usd: totalCostUsd } : {}),
|
|
87
|
-
...(turnCostUsd !== undefined ? { turn_cost_usd: turnCostUsd } : {}),
|
|
88
|
-
...(contextWindow !== undefined ? { context_window: contextWindow } : {}),
|
|
89
|
-
...(maxOutputTokens !== undefined ? { max_output_tokens: maxOutputTokens } : {}),
|
|
90
|
-
},
|
|
91
|
-
};
|
|
92
|
-
}
|
|
93
|
-
export function buildUsageUpdateFromResult(message) {
|
|
94
|
-
return buildUsageUpdateFromResultForSession(undefined, message);
|
|
95
|
-
}
|