@ai-sdk/harness-claude-code 1.0.41 → 1.0.42
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +9 -0
- package/dist/bridge/index.mjs +284 -236
- package/dist/bridge/index.mjs.map +1 -1
- package/dist/index.js +1 -1
- package/package.json +2 -2
- package/src/bridge/create-emit-stream-event.ts +448 -0
- package/src/bridge/index.ts +35 -386
package/src/bridge/index.ts
CHANGED
|
@@ -34,6 +34,15 @@ import { argv, stdout } from 'node:process';
|
|
|
34
34
|
import * as claudeAgentSdk from '@anthropic-ai/claude-agent-sdk';
|
|
35
35
|
import * as mcpServerModule from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
36
36
|
import { toClaudeSkillsOption } from './claude-skills-option';
|
|
37
|
+
import {
|
|
38
|
+
createClaudeStreamEventState,
|
|
39
|
+
createEmitStreamEvent,
|
|
40
|
+
defaultUsage,
|
|
41
|
+
emitFinishStep,
|
|
42
|
+
finishApprovalStep,
|
|
43
|
+
mapUsage,
|
|
44
|
+
type ClaudeMessage,
|
|
45
|
+
} from './create-emit-stream-event';
|
|
37
46
|
import { jsonSchemaToZodShape } from './json-schema-to-zod';
|
|
38
47
|
|
|
39
48
|
/*
|
|
@@ -90,7 +99,6 @@ const PUBLIC_TO_NATIVE: Readonly<Record<string, string>> = {
|
|
|
90
99
|
};
|
|
91
100
|
|
|
92
101
|
const PUBLIC_TOOL_NAMES = Object.keys(PUBLIC_TO_NATIVE);
|
|
93
|
-
const UNRECOVERABLE_API_RETRY_STATUSES = new Set([401, 403, 404]);
|
|
94
102
|
|
|
95
103
|
const NATIVE_TOOL_KINDS: Readonly<
|
|
96
104
|
Record<string, 'readonly' | 'edit' | 'bash'>
|
|
@@ -305,50 +313,7 @@ async function runTurn(start: StartMessage, turn: BridgeTurn): Promise<void> {
|
|
|
305
313
|
});
|
|
306
314
|
}
|
|
307
315
|
|
|
308
|
-
|
|
309
|
-
* Map of native tool-use id → tool name. Claude assistant messages emit
|
|
310
|
-
* `tool_use` blocks with both `id` and `name`; the matching `tool_result`
|
|
311
|
-
* block on a later user message carries only `tool_use_id`, so without this
|
|
312
|
-
* map the tool-result event would have to emit `toolName: 'unknown'`.
|
|
313
|
-
*/
|
|
314
|
-
const nativeToolCallNames = new Map<string, string>();
|
|
315
|
-
const approvalRequestedToolUseIds = new Set<string>();
|
|
316
|
-
const partialBlocks = new Map<
|
|
317
|
-
number,
|
|
318
|
-
{ id: string; kind: 'text' | 'thinking' }
|
|
319
|
-
>();
|
|
320
|
-
let stepUsage: Record<string, unknown> | undefined;
|
|
321
|
-
let pendingStepToolUseIds = new Set<string>();
|
|
322
|
-
let pendingStepUsage: Record<string, unknown> | undefined;
|
|
323
|
-
let stepOpen = false;
|
|
324
|
-
|
|
325
|
-
const emitFinishStep = (usage: Record<string, unknown> | undefined): void => {
|
|
326
|
-
emit({
|
|
327
|
-
type: 'finish-step',
|
|
328
|
-
finishReason: { unified: 'stop', raw: 'stop' },
|
|
329
|
-
usage: usage ?? defaultUsage(),
|
|
330
|
-
});
|
|
331
|
-
stepUsage = usage ?? stepUsage;
|
|
332
|
-
pendingStepUsage = undefined;
|
|
333
|
-
pendingStepToolUseIds = new Set();
|
|
334
|
-
stepOpen = false;
|
|
335
|
-
};
|
|
336
|
-
|
|
337
|
-
const closeStepIfReady = (): void => {
|
|
338
|
-
if (!stepOpen || pendingStepToolUseIds.size > 0 || partialBlocks.size > 0) {
|
|
339
|
-
return;
|
|
340
|
-
}
|
|
341
|
-
emitFinishStep(pendingStepUsage);
|
|
342
|
-
};
|
|
343
|
-
|
|
344
|
-
/*
|
|
345
|
-
* Tool-use ids that originated from the MCP server hosting user-supplied
|
|
346
|
-
* tools. The MCP handler emits its own `tool-call`/`tool-result` pair with
|
|
347
|
-
* the user-facing tool name and a synthetic id, so the duplicate
|
|
348
|
-
* `tool_result` block Claude reports for the underlying native id must be
|
|
349
|
-
* suppressed.
|
|
350
|
-
*/
|
|
351
|
-
const mcpToolUseIds = new Set<string>();
|
|
316
|
+
const streamEventState = createClaudeStreamEventState();
|
|
352
317
|
|
|
353
318
|
const mcpServers: Record<string, unknown> = {};
|
|
354
319
|
if (start.tools && start.tools.length > 0) {
|
|
@@ -415,12 +380,10 @@ async function runTurn(start: StartMessage, turn: BridgeTurn): Promise<void> {
|
|
|
415
380
|
turn,
|
|
416
381
|
emit,
|
|
417
382
|
finishApprovalStep: approvalId => {
|
|
418
|
-
|
|
419
|
-
pendingStepToolUseIds.delete(approvalId);
|
|
420
|
-
closeStepIfReady();
|
|
383
|
+
finishApprovalStep({ state: streamEventState, emit, approvalId });
|
|
421
384
|
},
|
|
422
|
-
nativeToolCallNames,
|
|
423
|
-
approvalRequestedToolUseIds,
|
|
385
|
+
nativeToolCallNames: streamEventState.nativeToolCallNames,
|
|
386
|
+
approvalRequestedToolUseIds: streamEventState.approvalRequestedToolUseIds,
|
|
424
387
|
});
|
|
425
388
|
|
|
426
389
|
const q = claudeSdk.query({
|
|
@@ -467,15 +430,13 @@ async function runTurn(start: StartMessage, turn: BridgeTurn): Promise<void> {
|
|
|
467
430
|
|
|
468
431
|
let turnUsage: Record<string, unknown> | undefined;
|
|
469
432
|
let totalCostUsd: number | undefined;
|
|
470
|
-
let observedTerminalError: string | undefined;
|
|
471
433
|
let emittedTerminalError = false;
|
|
472
434
|
let emittedTerminalFinish = false;
|
|
473
|
-
let streamStarted = false;
|
|
474
435
|
|
|
475
436
|
const emitTerminalError = (message: string | undefined): void => {
|
|
476
437
|
const normalized = message?.trim();
|
|
477
438
|
if (!normalized || emittedTerminalError || emittedTerminalFinish) return;
|
|
478
|
-
observedTerminalError = normalized;
|
|
439
|
+
streamEventState.observedTerminalError = normalized;
|
|
479
440
|
emittedTerminalError = true;
|
|
480
441
|
turn.emitError({
|
|
481
442
|
error: normalized,
|
|
@@ -485,182 +446,28 @@ async function runTurn(start: StartMessage, turn: BridgeTurn): Promise<void> {
|
|
|
485
446
|
abortCtl.abort();
|
|
486
447
|
};
|
|
487
448
|
|
|
449
|
+
const emitStreamEvent = createEmitStreamEvent({
|
|
450
|
+
state: streamEventState,
|
|
451
|
+
emit,
|
|
452
|
+
emitWarning: turn.emitWarning,
|
|
453
|
+
emitTerminalError,
|
|
454
|
+
onCompactionBoundary: boundary => compaction.onBoundary(boundary),
|
|
455
|
+
toCommonName,
|
|
456
|
+
});
|
|
457
|
+
|
|
488
458
|
try {
|
|
489
459
|
for await (const msg of q as AsyncIterable<ClaudeMessage>) {
|
|
490
460
|
if (abortCtl.signal.aborted) break;
|
|
491
461
|
|
|
492
462
|
const type = msg.type;
|
|
493
463
|
|
|
494
|
-
|
|
495
|
-
// CLI resolved to (the `system`/`init` message reports it — this is the
|
|
496
|
-
// default model when none was configured).
|
|
497
|
-
if (!streamStarted) {
|
|
498
|
-
const initModel =
|
|
499
|
-
type === 'system' &&
|
|
500
|
-
msg.subtype === 'init' &&
|
|
501
|
-
typeof (msg as { model?: unknown }).model === 'string'
|
|
502
|
-
? (msg as { model: string }).model
|
|
503
|
-
: undefined;
|
|
504
|
-
emit({
|
|
505
|
-
type: 'stream-start',
|
|
506
|
-
...(initModel ? { modelId: initModel } : {}),
|
|
507
|
-
});
|
|
508
|
-
streamStarted = true;
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
if (type === 'system' && msg.subtype === 'api_retry') {
|
|
512
|
-
if (
|
|
513
|
-
typeof msg.error_status === 'number' &&
|
|
514
|
-
UNRECOVERABLE_API_RETRY_STATUSES.has(msg.error_status)
|
|
515
|
-
) {
|
|
516
|
-
emitTerminalError(
|
|
517
|
-
`HTTP ${msg.error_status}: ${
|
|
518
|
-
msg.error ?? 'provider request failed'
|
|
519
|
-
}`,
|
|
520
|
-
);
|
|
521
|
-
continue;
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
turn.emitWarning({ message: formatApiRetryWarning(msg) });
|
|
525
|
-
continue;
|
|
526
|
-
}
|
|
527
|
-
|
|
528
|
-
if (typeof msg.error === 'string' && msg.error.trim()) {
|
|
529
|
-
observedTerminalError = msg.error.trim();
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
if (
|
|
533
|
-
type === 'auth_status' &&
|
|
534
|
-
typeof msg.error === 'string' &&
|
|
535
|
-
msg.error.trim()
|
|
536
|
-
) {
|
|
537
|
-
emitTerminalError(msg.error);
|
|
538
|
-
continue;
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
if (
|
|
542
|
-
type === 'system' &&
|
|
543
|
-
msg.subtype === 'task_updated' &&
|
|
544
|
-
msg.patch?.status === 'failed' &&
|
|
545
|
-
typeof msg.patch.error === 'string'
|
|
546
|
-
) {
|
|
547
|
-
emitTerminalError(msg.patch.error);
|
|
548
|
-
continue;
|
|
549
|
-
}
|
|
550
|
-
|
|
551
|
-
if (type === 'system' && msg.subtype === 'compact_boundary') {
|
|
552
|
-
const meta = msg.compact_metadata;
|
|
553
|
-
if (meta) {
|
|
554
|
-
compaction.onBoundary({
|
|
555
|
-
trigger: meta.trigger,
|
|
556
|
-
...(typeof meta.pre_tokens === 'number'
|
|
557
|
-
? { tokensBefore: meta.pre_tokens }
|
|
558
|
-
: {}),
|
|
559
|
-
...(typeof meta.post_tokens === 'number'
|
|
560
|
-
? { tokensAfter: meta.post_tokens }
|
|
561
|
-
: {}),
|
|
562
|
-
});
|
|
563
|
-
}
|
|
564
|
-
continue;
|
|
565
|
-
}
|
|
566
|
-
|
|
567
|
-
if (type === 'stream_event') {
|
|
568
|
-
handleStreamEvent(msg.event, partialBlocks, emit);
|
|
569
|
-
continue;
|
|
570
|
-
}
|
|
571
|
-
|
|
572
|
-
if (type === 'assistant' && msg.message?.content) {
|
|
573
|
-
const usage = mapUsage(msg.message.usage);
|
|
574
|
-
const toolUseIds: string[] = [];
|
|
575
|
-
let opensStep = false;
|
|
576
|
-
for (const block of msg.message.content) {
|
|
577
|
-
if (
|
|
578
|
-
block.type === 'tool_use' &&
|
|
579
|
-
typeof block.id === 'string' &&
|
|
580
|
-
typeof block.name === 'string'
|
|
581
|
-
) {
|
|
582
|
-
toolUseIds.push(block.id);
|
|
583
|
-
const mcpPrefix = 'mcp__harness-tools__';
|
|
584
|
-
if (block.name.startsWith(mcpPrefix)) {
|
|
585
|
-
pendingStepToolUseIds.add(block.id);
|
|
586
|
-
mcpToolUseIds.add(block.id);
|
|
587
|
-
opensStep = true;
|
|
588
|
-
continue;
|
|
589
|
-
}
|
|
590
|
-
nativeToolCallNames.set(block.id, block.name);
|
|
591
|
-
if (approvalRequestedToolUseIds.has(block.id)) {
|
|
592
|
-
continue;
|
|
593
|
-
}
|
|
594
|
-
pendingStepToolUseIds.add(block.id);
|
|
595
|
-
opensStep = true;
|
|
596
|
-
emit({
|
|
597
|
-
type: 'tool-call',
|
|
598
|
-
toolCallId: block.id,
|
|
599
|
-
toolName: toCommonName(block.name),
|
|
600
|
-
nativeName: block.name,
|
|
601
|
-
input: JSON.stringify(block.input ?? {}),
|
|
602
|
-
providerExecuted: true,
|
|
603
|
-
});
|
|
604
|
-
}
|
|
605
|
-
}
|
|
606
|
-
if (opensStep || toolUseIds.length === 0) {
|
|
607
|
-
stepOpen = true;
|
|
608
|
-
if (usage) pendingStepUsage = usage;
|
|
609
|
-
}
|
|
610
|
-
continue;
|
|
611
|
-
}
|
|
612
|
-
|
|
613
|
-
if (type === 'user' && msg.message?.content) {
|
|
614
|
-
for (const block of msg.message.content) {
|
|
615
|
-
if (
|
|
616
|
-
block.type === 'tool_result' &&
|
|
617
|
-
typeof block.tool_use_id === 'string'
|
|
618
|
-
) {
|
|
619
|
-
if (mcpToolUseIds.has(block.tool_use_id)) {
|
|
620
|
-
mcpToolUseIds.delete(block.tool_use_id);
|
|
621
|
-
pendingStepToolUseIds.delete(block.tool_use_id);
|
|
622
|
-
continue;
|
|
623
|
-
}
|
|
624
|
-
approvalRequestedToolUseIds.delete(block.tool_use_id);
|
|
625
|
-
const nativeName =
|
|
626
|
-
nativeToolCallNames.get(block.tool_use_id) ?? 'unknown';
|
|
627
|
-
nativeToolCallNames.delete(block.tool_use_id);
|
|
628
|
-
const toolName = toCommonName(nativeName);
|
|
629
|
-
const isError = !!block.is_error;
|
|
630
|
-
const content = stringifyContent(block.content);
|
|
631
|
-
/*
|
|
632
|
-
* Claude Code's Bash tool does not report the command's real
|
|
633
|
-
* numeric exit code — the SDK exposes only stdout/stderr text and
|
|
634
|
-
* an is_error flag. Consumers (and the example UI) render bash
|
|
635
|
-
* failures from an `exitCode` field on a structured result, the
|
|
636
|
-
* shape Codex's shell tool provides natively. To match it, derive
|
|
637
|
-
* a binary code from is_error: 1 on failure, 0 on success. This is
|
|
638
|
-
* a stand-in for failed/succeeded, not the process's true exit
|
|
639
|
-
* status.
|
|
640
|
-
*/
|
|
641
|
-
const result =
|
|
642
|
-
toolName === 'bash'
|
|
643
|
-
? { exitCode: isError ? 1 : 0, stdout: content }
|
|
644
|
-
: content;
|
|
645
|
-
emit({
|
|
646
|
-
type: 'tool-result',
|
|
647
|
-
toolCallId: block.tool_use_id,
|
|
648
|
-
toolName,
|
|
649
|
-
result,
|
|
650
|
-
isError,
|
|
651
|
-
});
|
|
652
|
-
pendingStepToolUseIds.delete(block.tool_use_id);
|
|
653
|
-
}
|
|
654
|
-
}
|
|
655
|
-
closeStepIfReady();
|
|
656
|
-
continue;
|
|
657
|
-
}
|
|
464
|
+
emitStreamEvent(msg);
|
|
658
465
|
|
|
659
466
|
if (type === 'result') {
|
|
660
467
|
if (msg.subtype === 'success') {
|
|
661
468
|
const emptyResult = !msg.result?.trim?.();
|
|
662
|
-
if (emptyResult && observedTerminalError) {
|
|
663
|
-
emitTerminalError(observedTerminalError);
|
|
469
|
+
if (emptyResult && streamEventState.observedTerminalError) {
|
|
470
|
+
emitTerminalError(streamEventState.observedTerminalError);
|
|
664
471
|
continue;
|
|
665
472
|
}
|
|
666
473
|
const usage = msg.usage ?? msg.message?.usage;
|
|
@@ -669,13 +476,19 @@ async function runTurn(start: StartMessage, turn: BridgeTurn): Promise<void> {
|
|
|
669
476
|
if (typeof msg.total_cost_usd === 'number') {
|
|
670
477
|
totalCostUsd = (totalCostUsd ?? 0) + msg.total_cost_usd;
|
|
671
478
|
}
|
|
672
|
-
if (stepOpen)
|
|
479
|
+
if (streamEventState.stepOpen) {
|
|
480
|
+
emitFinishStep({
|
|
481
|
+
state: streamEventState,
|
|
482
|
+
emit,
|
|
483
|
+
usage: harnessUsage ?? streamEventState.pendingStepUsage,
|
|
484
|
+
});
|
|
485
|
+
}
|
|
673
486
|
queryInput.close();
|
|
674
487
|
break;
|
|
675
488
|
} else {
|
|
676
489
|
emitTerminalError(
|
|
677
490
|
(Array.isArray(msg.errors) ? msg.errors.join('\n') : undefined) ||
|
|
678
|
-
observedTerminalError ||
|
|
491
|
+
streamEventState.observedTerminalError ||
|
|
679
492
|
msg.result ||
|
|
680
493
|
'Unknown error',
|
|
681
494
|
);
|
|
@@ -698,119 +511,13 @@ async function runTurn(start: StartMessage, turn: BridgeTurn): Promise<void> {
|
|
|
698
511
|
emit({
|
|
699
512
|
type: 'finish',
|
|
700
513
|
finishReason: { unified: 'stop', raw: 'stop' },
|
|
701
|
-
totalUsage: turnUsage ?? stepUsage ?? defaultUsage(),
|
|
514
|
+
totalUsage: turnUsage ?? streamEventState.stepUsage ?? defaultUsage(),
|
|
702
515
|
...(totalCostUsd !== undefined
|
|
703
516
|
? { harnessMetadata: { 'claude-code': { costUsd: totalCostUsd } } }
|
|
704
517
|
: {}),
|
|
705
518
|
});
|
|
706
519
|
}
|
|
707
520
|
|
|
708
|
-
type ClaudeMessage = {
|
|
709
|
-
type?: string;
|
|
710
|
-
subtype?: string;
|
|
711
|
-
error?: string;
|
|
712
|
-
error_status?: number | null;
|
|
713
|
-
attempt?: number;
|
|
714
|
-
max_retries?: number;
|
|
715
|
-
retry_delay_ms?: number;
|
|
716
|
-
patch?: { status?: string; error?: string };
|
|
717
|
-
compact_metadata?: {
|
|
718
|
-
trigger: 'manual' | 'auto';
|
|
719
|
-
pre_tokens?: number;
|
|
720
|
-
post_tokens?: number;
|
|
721
|
-
};
|
|
722
|
-
event?: {
|
|
723
|
-
type?: string;
|
|
724
|
-
index?: number;
|
|
725
|
-
content_block?: { type?: string };
|
|
726
|
-
delta?: { type?: string; text?: string; thinking?: string };
|
|
727
|
-
};
|
|
728
|
-
message?: {
|
|
729
|
-
content?: ReadonlyArray<MessageBlock>;
|
|
730
|
-
usage?: Record<string, unknown>;
|
|
731
|
-
};
|
|
732
|
-
result?: string;
|
|
733
|
-
errors?: ReadonlyArray<string>;
|
|
734
|
-
usage?: Record<string, unknown>;
|
|
735
|
-
total_cost_usd?: number;
|
|
736
|
-
};
|
|
737
|
-
|
|
738
|
-
function formatApiRetryWarning(msg: ClaudeMessage): string {
|
|
739
|
-
const details: string[] = [];
|
|
740
|
-
if (typeof msg.attempt === 'number') {
|
|
741
|
-
const maxRetries =
|
|
742
|
-
typeof msg.max_retries === 'number' ? `/${msg.max_retries}` : '';
|
|
743
|
-
details.push(`attempt ${msg.attempt}${maxRetries}`);
|
|
744
|
-
}
|
|
745
|
-
if (typeof msg.error_status === 'number') {
|
|
746
|
-
details.push(`HTTP ${msg.error_status}`);
|
|
747
|
-
}
|
|
748
|
-
if (typeof msg.retry_delay_ms === 'number') {
|
|
749
|
-
details.push(`retrying in ${msg.retry_delay_ms}ms`);
|
|
750
|
-
}
|
|
751
|
-
if (msg.error) details.push(msg.error);
|
|
752
|
-
return details.length > 0
|
|
753
|
-
? `Claude Code API retry: ${details.join('; ')}`
|
|
754
|
-
: 'Claude Code API retry';
|
|
755
|
-
}
|
|
756
|
-
|
|
757
|
-
function handleStreamEvent(
|
|
758
|
-
event: ClaudeMessage['event'] | undefined,
|
|
759
|
-
partialBlocks: Map<number, { id: string; kind: 'text' | 'thinking' }>,
|
|
760
|
-
send: Emit,
|
|
761
|
-
): void {
|
|
762
|
-
if (!event || typeof event.index !== 'number') return;
|
|
763
|
-
const index = event.index;
|
|
764
|
-
|
|
765
|
-
if (event.type === 'content_block_start') {
|
|
766
|
-
const blockType = event.content_block?.type;
|
|
767
|
-
if (blockType === 'text') {
|
|
768
|
-
const id = randomUUID();
|
|
769
|
-
partialBlocks.set(index, { id, kind: 'text' });
|
|
770
|
-
send({ type: 'text-start', id });
|
|
771
|
-
} else if (blockType === 'thinking') {
|
|
772
|
-
const id = randomUUID();
|
|
773
|
-
partialBlocks.set(index, { id, kind: 'thinking' });
|
|
774
|
-
send({ type: 'reasoning-start', id });
|
|
775
|
-
}
|
|
776
|
-
return;
|
|
777
|
-
}
|
|
778
|
-
|
|
779
|
-
if (event.type === 'content_block_delta') {
|
|
780
|
-
const block = partialBlocks.get(index);
|
|
781
|
-
if (!block) return;
|
|
782
|
-
if (
|
|
783
|
-
block.kind === 'text' &&
|
|
784
|
-
event.delta?.type === 'text_delta' &&
|
|
785
|
-
typeof event.delta.text === 'string'
|
|
786
|
-
) {
|
|
787
|
-
send({ type: 'text-delta', id: block.id, delta: event.delta.text });
|
|
788
|
-
} else if (
|
|
789
|
-
block.kind === 'thinking' &&
|
|
790
|
-
event.delta?.type === 'thinking_delta' &&
|
|
791
|
-
typeof event.delta.thinking === 'string'
|
|
792
|
-
) {
|
|
793
|
-
send({
|
|
794
|
-
type: 'reasoning-delta',
|
|
795
|
-
id: block.id,
|
|
796
|
-
delta: event.delta.thinking,
|
|
797
|
-
});
|
|
798
|
-
}
|
|
799
|
-
return;
|
|
800
|
-
}
|
|
801
|
-
|
|
802
|
-
if (event.type === 'content_block_stop') {
|
|
803
|
-
const block = partialBlocks.get(index);
|
|
804
|
-
if (!block) return;
|
|
805
|
-
partialBlocks.delete(index);
|
|
806
|
-
if (block.kind === 'text') {
|
|
807
|
-
send({ type: 'text-end', id: block.id });
|
|
808
|
-
} else {
|
|
809
|
-
send({ type: 'reasoning-end', id: block.id });
|
|
810
|
-
}
|
|
811
|
-
}
|
|
812
|
-
}
|
|
813
|
-
|
|
814
521
|
function createQueryInput({
|
|
815
522
|
initialUserMessage,
|
|
816
523
|
pendingUserMessages,
|
|
@@ -873,64 +580,6 @@ function createQueryInput({
|
|
|
873
580
|
};
|
|
874
581
|
}
|
|
875
582
|
|
|
876
|
-
type MessageBlock = {
|
|
877
|
-
type: string;
|
|
878
|
-
text?: string;
|
|
879
|
-
thinking?: string;
|
|
880
|
-
id?: string;
|
|
881
|
-
name?: string;
|
|
882
|
-
input?: unknown;
|
|
883
|
-
tool_use_id?: string;
|
|
884
|
-
content?: unknown;
|
|
885
|
-
is_error?: boolean;
|
|
886
|
-
};
|
|
887
|
-
|
|
888
|
-
function stringifyContent(content: unknown): string {
|
|
889
|
-
if (typeof content === 'string') return content;
|
|
890
|
-
if (Array.isArray(content)) {
|
|
891
|
-
return content
|
|
892
|
-
.map(entry =>
|
|
893
|
-
entry && typeof entry === 'object' && 'text' in entry
|
|
894
|
-
? String((entry as { text?: unknown }).text ?? '')
|
|
895
|
-
: JSON.stringify(entry),
|
|
896
|
-
)
|
|
897
|
-
.join('');
|
|
898
|
-
}
|
|
899
|
-
return JSON.stringify(content);
|
|
900
|
-
}
|
|
901
|
-
|
|
902
|
-
function mapUsage(usage: unknown): Record<string, unknown> | undefined {
|
|
903
|
-
if (!usage || typeof usage !== 'object') return undefined;
|
|
904
|
-
const u = usage as {
|
|
905
|
-
input_tokens?: number;
|
|
906
|
-
cache_creation_input_tokens?: number;
|
|
907
|
-
cache_read_input_tokens?: number;
|
|
908
|
-
output_tokens?: number;
|
|
909
|
-
};
|
|
910
|
-
return {
|
|
911
|
-
inputTokens: {
|
|
912
|
-
total:
|
|
913
|
-
(u.input_tokens ?? 0) +
|
|
914
|
-
(u.cache_creation_input_tokens ?? 0) +
|
|
915
|
-
(u.cache_read_input_tokens ?? 0),
|
|
916
|
-
noCache: u.input_tokens ?? 0,
|
|
917
|
-
cacheRead: u.cache_read_input_tokens ?? 0,
|
|
918
|
-
cacheWrite: u.cache_creation_input_tokens ?? 0,
|
|
919
|
-
},
|
|
920
|
-
outputTokens: {
|
|
921
|
-
total: u.output_tokens ?? 0,
|
|
922
|
-
text: u.output_tokens ?? 0,
|
|
923
|
-
},
|
|
924
|
-
};
|
|
925
|
-
}
|
|
926
|
-
|
|
927
|
-
function defaultUsage(): Record<string, unknown> {
|
|
928
|
-
return {
|
|
929
|
-
inputTokens: { total: 0, noCache: 0, cacheRead: 0, cacheWrite: 0 },
|
|
930
|
-
outputTokens: { total: 0, text: 0 },
|
|
931
|
-
};
|
|
932
|
-
}
|
|
933
|
-
|
|
934
583
|
function parseArgs(args: string[]): {
|
|
935
584
|
workdir?: string;
|
|
936
585
|
bridgeStateDir?: string;
|