devez-vibe 1.2.13 → 1.2.15
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/bin/dvz.exe +0 -0
- package/bridge/claude-agent-sdk-bridge.mjs +180 -117
- package/package.json +1 -1
package/bin/dvz.exe
CHANGED
|
Binary file
|
|
@@ -438,10 +438,10 @@ async function createSession(params, resumeId) {
|
|
|
438
438
|
turnSequence: 1,
|
|
439
439
|
itemSequence: 1,
|
|
440
440
|
streamBlocks: new Map(),
|
|
441
|
-
tools: new Map(),
|
|
442
|
-
tasks: new Map(),
|
|
443
|
-
planCreatePending: false,
|
|
444
|
-
subagents: new Map(),
|
|
441
|
+
tools: new Map(),
|
|
442
|
+
tasks: new Map(),
|
|
443
|
+
planCreatePending: false,
|
|
444
|
+
subagents: new Map(),
|
|
445
445
|
knownSubagents: new Map(),
|
|
446
446
|
lastContextUsage: null,
|
|
447
447
|
lastContextWindow: 0,
|
|
@@ -519,6 +519,10 @@ function openingNotice(input) {
|
|
|
519
519
|
: "I’ll review the request and proceed with the necessary work.";
|
|
520
520
|
}
|
|
521
521
|
|
|
522
|
+
// `Now the tile view logic.` carries nothing a Korean reader needs, and the
|
|
523
|
+
// stand-in that used to replace it carried even less — the same sentence before
|
|
524
|
+
// every tool call, however many calls the turn made. Drop the line instead; the
|
|
525
|
+
// tool item that follows already names what is being read.
|
|
522
526
|
function normalizeProgressText(turn, text) {
|
|
523
527
|
const value = String(text || "");
|
|
524
528
|
const trimmed = value.trim();
|
|
@@ -526,7 +530,7 @@ function normalizeProgressText(turn, text) {
|
|
|
526
530
|
&& trimmed.length <= 160
|
|
527
531
|
&& !/[\uac00-\ud7a3]/.test(trimmed)
|
|
528
532
|
&& /^Now\b[^\r\n]*[.!?]?$/i.test(trimmed)) {
|
|
529
|
-
return "
|
|
533
|
+
return "";
|
|
530
534
|
}
|
|
531
535
|
return value;
|
|
532
536
|
}
|
|
@@ -547,7 +551,7 @@ function emitOpeningNotice(session) {
|
|
|
547
551
|
// Windows rounds larger timer delays up to the next scheduler slice. Ten
|
|
548
552
|
// milliseconds lands near one terminal frame instead of visibly stepping at ~30ms.
|
|
549
553
|
const SMOOTH_TEXT_INTERVAL_MS = 10;
|
|
550
|
-
const SMOOTH_TEXT_TARGET_FRAMES = 10;
|
|
554
|
+
const SMOOTH_TEXT_TARGET_FRAMES = 10;
|
|
551
555
|
const SMOOTH_TEXT_MAX_GRAPHEMES = 24;
|
|
552
556
|
const graphemeSegmenter = typeof Intl.Segmenter === "function"
|
|
553
557
|
? new Intl.Segmenter(undefined, { granularity: "grapheme" })
|
|
@@ -582,21 +586,21 @@ class SmoothTextStream {
|
|
|
582
586
|
this.waiters = [];
|
|
583
587
|
}
|
|
584
588
|
|
|
585
|
-
push(text) {
|
|
586
|
-
this.pending += text;
|
|
587
|
-
this.schedule();
|
|
588
|
-
}
|
|
589
|
-
|
|
590
|
-
schedule() {
|
|
591
|
-
if (this.timer != null) return;
|
|
592
|
-
// Wait one visual frame before the first drain. Claude often sends several
|
|
593
|
-
// tiny deltas back-to-back; batching them removes the uneven one-character
|
|
594
|
-
// jumps while keeping added latency below one frame.
|
|
595
|
-
this.timer = setTimeout(() => {
|
|
596
|
-
this.timer = null;
|
|
597
|
-
this.drain();
|
|
598
|
-
}, this.intervalMs);
|
|
599
|
-
}
|
|
589
|
+
push(text) {
|
|
590
|
+
this.pending += text;
|
|
591
|
+
this.schedule();
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
schedule() {
|
|
595
|
+
if (this.timer != null) return;
|
|
596
|
+
// Wait one visual frame before the first drain. Claude often sends several
|
|
597
|
+
// tiny deltas back-to-back; batching them removes the uneven one-character
|
|
598
|
+
// jumps while keeping added latency below one frame.
|
|
599
|
+
this.timer = setTimeout(() => {
|
|
600
|
+
this.timer = null;
|
|
601
|
+
this.drain();
|
|
602
|
+
}, this.intervalMs);
|
|
603
|
+
}
|
|
600
604
|
|
|
601
605
|
drain() {
|
|
602
606
|
if (!this.pending) {
|
|
@@ -605,13 +609,13 @@ class SmoothTextStream {
|
|
|
605
609
|
return;
|
|
606
610
|
}
|
|
607
611
|
const { chunk, rest } = takeSmoothTextChunk(this.pending);
|
|
608
|
-
this.pending = rest;
|
|
609
|
-
this.emit(chunk);
|
|
610
|
-
if (this.pending) {
|
|
611
|
-
this.schedule();
|
|
612
|
-
} else {
|
|
613
|
-
for (const resolve of this.waiters.splice(0)) resolve();
|
|
614
|
-
}
|
|
612
|
+
this.pending = rest;
|
|
613
|
+
this.emit(chunk);
|
|
614
|
+
if (this.pending) {
|
|
615
|
+
this.schedule();
|
|
616
|
+
} else {
|
|
617
|
+
for (const resolve of this.waiters.splice(0)) resolve();
|
|
618
|
+
}
|
|
615
619
|
}
|
|
616
620
|
|
|
617
621
|
finish() {
|
|
@@ -678,6 +682,12 @@ function historyTokenUsage(messages, models, model) {
|
|
|
678
682
|
};
|
|
679
683
|
}
|
|
680
684
|
|
|
685
|
+
function emitHeldStart(session, current) {
|
|
686
|
+
if (!current?.pendingStart) return;
|
|
687
|
+
emitItem(session, "started", current.pendingStart);
|
|
688
|
+
current.pendingStart = null;
|
|
689
|
+
}
|
|
690
|
+
|
|
681
691
|
async function processStreamEvent(session, message) {
|
|
682
692
|
if (!session.turn || message.parent_tool_use_id) return;
|
|
683
693
|
const event = message.event || {};
|
|
@@ -695,15 +705,20 @@ async function processStreamEvent(session, message) {
|
|
|
695
705
|
const smooth = block.type === "text"
|
|
696
706
|
? new SmoothTextStream((delta) => emitDelta(session, "item/agentMessage/delta", id, delta))
|
|
697
707
|
: null;
|
|
708
|
+
// A held English line can end up dropped entirely, and an item announced
|
|
709
|
+
// before that decision would stay on screen as an empty bubble. Hold the
|
|
710
|
+
// start too, and emit it with the first text that survives.
|
|
711
|
+
const held = block.type === "text" && session.turn.koreanRequest;
|
|
698
712
|
session.streamBlocks.set(event.index, {
|
|
699
713
|
id,
|
|
700
714
|
type: block.type,
|
|
701
715
|
text: "",
|
|
702
716
|
smooth,
|
|
703
|
-
languagePending:
|
|
717
|
+
languagePending: held ? "" : null,
|
|
704
718
|
holdEnglishProgress: false,
|
|
719
|
+
pendingStart: held ? item : null,
|
|
705
720
|
});
|
|
706
|
-
emitItem(session, "started", item);
|
|
721
|
+
if (!held) emitItem(session, "started", item);
|
|
707
722
|
return;
|
|
708
723
|
}
|
|
709
724
|
if (event.type === "content_block_delta") {
|
|
@@ -712,7 +727,9 @@ async function processStreamEvent(session, message) {
|
|
|
712
727
|
const delta = event.delta?.text || event.delta?.thinking || "";
|
|
713
728
|
if (!delta) return;
|
|
714
729
|
current.text += delta;
|
|
715
|
-
|
|
730
|
+
// Held text may still be dropped, and counting it as visible would suppress
|
|
731
|
+
// the opening notice in its place — leaving the turn with nothing to show.
|
|
732
|
+
if (current.type === "text" && current.languagePending == null) session.turn.sawVisibleText = true;
|
|
716
733
|
if (!current.smooth) {
|
|
717
734
|
emitDelta(session, "item/reasoning/summaryTextDelta", current.id, delta);
|
|
718
735
|
return;
|
|
@@ -726,6 +743,8 @@ async function processStreamEvent(session, message) {
|
|
|
726
743
|
current.holdEnglishProgress = true;
|
|
727
744
|
return;
|
|
728
745
|
}
|
|
746
|
+
emitHeldStart(session, current);
|
|
747
|
+
session.turn.sawVisibleText = true;
|
|
729
748
|
current.smooth.push(current.languagePending);
|
|
730
749
|
current.languagePending = null;
|
|
731
750
|
return;
|
|
@@ -739,9 +758,16 @@ async function processStreamEvent(session, message) {
|
|
|
739
758
|
if (current.languagePending != null) {
|
|
740
759
|
const visible = normalizeProgressText(session.turn, current.languagePending);
|
|
741
760
|
current.text = visible;
|
|
742
|
-
current.smooth?.push(visible);
|
|
743
761
|
current.languagePending = null;
|
|
762
|
+
if (!visible.trim() && current.pendingStart) {
|
|
763
|
+
session.streamBlocks.delete(event.index);
|
|
764
|
+
return;
|
|
765
|
+
}
|
|
766
|
+
emitHeldStart(session, current);
|
|
767
|
+
if (visible.trim()) session.turn.sawVisibleText = true;
|
|
768
|
+
current.smooth?.push(visible);
|
|
744
769
|
}
|
|
770
|
+
emitHeldStart(session, current);
|
|
745
771
|
await current.smooth?.finish();
|
|
746
772
|
const item = current.type === "text"
|
|
747
773
|
? { id: current.id, type: "agentMessage", text: current.text, provider: "Claude" }
|
|
@@ -777,9 +803,13 @@ function processAssistant(session, message) {
|
|
|
777
803
|
if (!session.streamBlocks.size && !session.turn.sawStreamText) {
|
|
778
804
|
for (const block of content) {
|
|
779
805
|
if (block.type !== "text" && block.type !== "thinking") continue;
|
|
806
|
+
const visible = block.type === "text"
|
|
807
|
+
? normalizeProgressText(session.turn, block.text)
|
|
808
|
+
: "";
|
|
809
|
+
if (block.type === "text" && !visible.trim() && String(block.text || "").trim()) continue;
|
|
780
810
|
const id = nextItemId(session, block.type);
|
|
781
811
|
const item = block.type === "text"
|
|
782
|
-
? { id, type: "agentMessage", text:
|
|
812
|
+
? { id, type: "agentMessage", text: visible, provider: "Claude" }
|
|
783
813
|
: { id, type: "reasoning", summary: [block.thinking || ""] };
|
|
784
814
|
emitItem(session, "started", item);
|
|
785
815
|
emitItem(session, "completed", item);
|
|
@@ -791,7 +821,7 @@ function processAssistant(session, message) {
|
|
|
791
821
|
}
|
|
792
822
|
}
|
|
793
823
|
|
|
794
|
-
function processToolUse(session, block) {
|
|
824
|
+
function processToolUse(session, block) {
|
|
795
825
|
const name = block.name || "Tool";
|
|
796
826
|
const input = block.input || {};
|
|
797
827
|
if (name === "TaskCreate" || name === "TaskUpdate" || name === "TaskList") {
|
|
@@ -799,13 +829,13 @@ function processToolUse(session, block) {
|
|
|
799
829
|
session.tools.set(block.id, { name, input, suppressed: true });
|
|
800
830
|
return;
|
|
801
831
|
}
|
|
802
|
-
if (name === "AskUserQuestion") {
|
|
803
|
-
flushPendingPlan(session);
|
|
804
|
-
session.tools.set(block.id, { name, input, suppressed: true });
|
|
805
|
-
return;
|
|
806
|
-
}
|
|
807
|
-
flushPendingPlan(session);
|
|
808
|
-
const item = toolItem(session, block.id, name, input);
|
|
832
|
+
if (name === "AskUserQuestion") {
|
|
833
|
+
flushPendingPlan(session);
|
|
834
|
+
session.tools.set(block.id, { name, input, suppressed: true });
|
|
835
|
+
return;
|
|
836
|
+
}
|
|
837
|
+
flushPendingPlan(session);
|
|
838
|
+
const item = toolItem(session, block.id, name, input);
|
|
809
839
|
session.tools.set(block.id, { name, input, item });
|
|
810
840
|
emitItem(session, "started", item);
|
|
811
841
|
if (SUBAGENT_TOOLS.includes(name)) startSubagent(session, block);
|
|
@@ -911,30 +941,30 @@ function latestTaskPlan(tasks) {
|
|
|
911
941
|
return new Map(entries.slice(start));
|
|
912
942
|
}
|
|
913
943
|
|
|
914
|
-
function updatePlanFromToolUse(session, name, toolUseId, input) {
|
|
944
|
+
function updatePlanFromToolUse(session, name, toolUseId, input) {
|
|
915
945
|
const turnId = session.turn?.id;
|
|
916
946
|
if (name === "TaskCreate") {
|
|
917
947
|
prepareTaskPlanForCreate(session.tasks, input.subject);
|
|
918
|
-
session.tasks.set(`pending:${toolUseId}`, {
|
|
948
|
+
session.tasks.set(`pending:${toolUseId}`, {
|
|
919
949
|
id: `pending:${toolUseId}`,
|
|
920
950
|
subject: input.subject || input.description || "작업",
|
|
921
951
|
status: "pending",
|
|
922
|
-
turnId,
|
|
923
|
-
});
|
|
924
|
-
session.planCreatePending = true;
|
|
925
|
-
return;
|
|
926
|
-
} else if (name === "TaskUpdate") {
|
|
927
|
-
session.planCreatePending = false;
|
|
928
|
-
applyTaskUpdate(session.tasks, input, turnId, () => emitPlan(session));
|
|
929
|
-
}
|
|
930
|
-
if (name === "TaskUpdate") emitPlan(session);
|
|
931
|
-
}
|
|
932
|
-
|
|
933
|
-
function flushPendingPlan(session) {
|
|
934
|
-
if (!session.planCreatePending) return;
|
|
935
|
-
session.planCreatePending = false;
|
|
936
|
-
emitPlan(session);
|
|
937
|
-
}
|
|
952
|
+
turnId,
|
|
953
|
+
});
|
|
954
|
+
session.planCreatePending = true;
|
|
955
|
+
return;
|
|
956
|
+
} else if (name === "TaskUpdate") {
|
|
957
|
+
session.planCreatePending = false;
|
|
958
|
+
applyTaskUpdate(session.tasks, input, turnId, () => emitPlan(session));
|
|
959
|
+
}
|
|
960
|
+
if (name === "TaskUpdate") emitPlan(session);
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
function flushPendingPlan(session) {
|
|
964
|
+
if (!session.planCreatePending) return;
|
|
965
|
+
session.planCreatePending = false;
|
|
966
|
+
emitPlan(session);
|
|
967
|
+
}
|
|
938
968
|
|
|
939
969
|
function updatePlanFromToolResult(session, pending, message) {
|
|
940
970
|
const value = message.tool_use_result;
|
|
@@ -1347,9 +1377,9 @@ async function runPendingPrompt(session) {
|
|
|
1347
1377
|
}
|
|
1348
1378
|
}
|
|
1349
1379
|
|
|
1350
|
-
function finishTurn(session, error, durationMs) {
|
|
1351
|
-
if (!session.turn) return;
|
|
1352
|
-
flushPendingPlan(session);
|
|
1380
|
+
function finishTurn(session, error, durationMs) {
|
|
1381
|
+
if (!session.turn) return;
|
|
1382
|
+
flushPendingPlan(session);
|
|
1353
1383
|
flushSmoothStreams(session);
|
|
1354
1384
|
clearForegroundSubagents(session);
|
|
1355
1385
|
const turn = { id: session.turn.id, status: error ? "failed" : "completed" };
|
|
@@ -1936,53 +1966,53 @@ async function runSelfTest() {
|
|
|
1936
1966
|
}
|
|
1937
1967
|
prepareTaskPlanForCreate(restoredTasks, "7. 추가 작업");
|
|
1938
1968
|
if (restoredTasks.size !== 6) throw new Error("Claude appended task unexpectedly reset the plan");
|
|
1939
|
-
prepareTaskPlanForCreate(restoredTasks, "1. 새 작업");
|
|
1940
|
-
if (restoredTasks.size !== 0) throw new Error("Claude new task plan did not reset the previous plan");
|
|
1941
|
-
const batchedPlanSession = {
|
|
1942
|
-
id: "batched-plan-self-test",
|
|
1943
|
-
turn: { id: "batched-plan-turn" },
|
|
1944
|
-
tasks: new Map(),
|
|
1945
|
-
planCreatePending: false,
|
|
1946
|
-
};
|
|
1947
|
-
const batchedPlanCaptured = [];
|
|
1948
|
-
const batchedPlanWrite = process.stdout.write;
|
|
1949
|
-
process.stdout.write = (chunk) => {
|
|
1950
|
-
batchedPlanCaptured.push(String(chunk));
|
|
1951
|
-
return true;
|
|
1952
|
-
};
|
|
1953
|
-
try {
|
|
1954
|
-
for (let index = 1; index <= 3; index++) {
|
|
1955
|
-
const toolUseId = `batched-create-${index}`;
|
|
1956
|
-
const subject = `${index}. 작업 ${index}`;
|
|
1957
|
-
updatePlanFromToolUse(batchedPlanSession, "TaskCreate", toolUseId, { subject });
|
|
1958
|
-
updatePlanFromToolResult(
|
|
1959
|
-
batchedPlanSession,
|
|
1960
|
-
{ name: "TaskCreate", toolUseId },
|
|
1961
|
-
{ tool_use_result: { task: { id: String(index), subject } } },
|
|
1962
|
-
);
|
|
1963
|
-
}
|
|
1964
|
-
updatePlanFromToolUse(
|
|
1965
|
-
batchedPlanSession,
|
|
1966
|
-
"TaskUpdate",
|
|
1967
|
-
"batched-update-1",
|
|
1968
|
-
{ taskId: "1", status: "in_progress" },
|
|
1969
|
-
);
|
|
1970
|
-
} finally {
|
|
1971
|
-
process.stdout.write = batchedPlanWrite;
|
|
1972
|
-
}
|
|
1973
|
-
const batchedPlanEvents = batchedPlanCaptured
|
|
1974
|
-
.join("")
|
|
1975
|
-
.trim()
|
|
1976
|
-
.split("\n")
|
|
1977
|
-
.filter(Boolean)
|
|
1978
|
-
.map((line) => JSON.parse(line))
|
|
1979
|
-
.filter((event) => event.method === "turn/plan/updated");
|
|
1980
|
-
if (batchedPlanEvents.length !== 1
|
|
1981
|
-
|| batchedPlanEvents[0].params?.plan?.length !== 3
|
|
1982
|
-
|| batchedPlanEvents[0].params.plan[0]?.status !== "inProgress") {
|
|
1983
|
-
throw new Error(`Claude batched plan self-test failed: ${JSON.stringify(batchedPlanEvents)}`);
|
|
1984
|
-
}
|
|
1985
|
-
const usage = tokenBreakdown({
|
|
1969
|
+
prepareTaskPlanForCreate(restoredTasks, "1. 새 작업");
|
|
1970
|
+
if (restoredTasks.size !== 0) throw new Error("Claude new task plan did not reset the previous plan");
|
|
1971
|
+
const batchedPlanSession = {
|
|
1972
|
+
id: "batched-plan-self-test",
|
|
1973
|
+
turn: { id: "batched-plan-turn" },
|
|
1974
|
+
tasks: new Map(),
|
|
1975
|
+
planCreatePending: false,
|
|
1976
|
+
};
|
|
1977
|
+
const batchedPlanCaptured = [];
|
|
1978
|
+
const batchedPlanWrite = process.stdout.write;
|
|
1979
|
+
process.stdout.write = (chunk) => {
|
|
1980
|
+
batchedPlanCaptured.push(String(chunk));
|
|
1981
|
+
return true;
|
|
1982
|
+
};
|
|
1983
|
+
try {
|
|
1984
|
+
for (let index = 1; index <= 3; index++) {
|
|
1985
|
+
const toolUseId = `batched-create-${index}`;
|
|
1986
|
+
const subject = `${index}. 작업 ${index}`;
|
|
1987
|
+
updatePlanFromToolUse(batchedPlanSession, "TaskCreate", toolUseId, { subject });
|
|
1988
|
+
updatePlanFromToolResult(
|
|
1989
|
+
batchedPlanSession,
|
|
1990
|
+
{ name: "TaskCreate", toolUseId },
|
|
1991
|
+
{ tool_use_result: { task: { id: String(index), subject } } },
|
|
1992
|
+
);
|
|
1993
|
+
}
|
|
1994
|
+
updatePlanFromToolUse(
|
|
1995
|
+
batchedPlanSession,
|
|
1996
|
+
"TaskUpdate",
|
|
1997
|
+
"batched-update-1",
|
|
1998
|
+
{ taskId: "1", status: "in_progress" },
|
|
1999
|
+
);
|
|
2000
|
+
} finally {
|
|
2001
|
+
process.stdout.write = batchedPlanWrite;
|
|
2002
|
+
}
|
|
2003
|
+
const batchedPlanEvents = batchedPlanCaptured
|
|
2004
|
+
.join("")
|
|
2005
|
+
.trim()
|
|
2006
|
+
.split("\n")
|
|
2007
|
+
.filter(Boolean)
|
|
2008
|
+
.map((line) => JSON.parse(line))
|
|
2009
|
+
.filter((event) => event.method === "turn/plan/updated");
|
|
2010
|
+
if (batchedPlanEvents.length !== 1
|
|
2011
|
+
|| batchedPlanEvents[0].params?.plan?.length !== 3
|
|
2012
|
+
|| batchedPlanEvents[0].params.plan[0]?.status !== "inProgress") {
|
|
2013
|
+
throw new Error(`Claude batched plan self-test failed: ${JSON.stringify(batchedPlanEvents)}`);
|
|
2014
|
+
}
|
|
2015
|
+
const usage = tokenBreakdown({
|
|
1986
2016
|
input_tokens: 2,
|
|
1987
2017
|
cache_read_input_tokens: 68_000,
|
|
1988
2018
|
cache_creation_input_tokens: 500,
|
|
@@ -2189,18 +2219,51 @@ async function runSelfTest() {
|
|
|
2189
2219
|
.filter((event) => event.method === "item/agentMessage/delta")
|
|
2190
2220
|
.map((event) => event.params?.delta || "")
|
|
2191
2221
|
.join("");
|
|
2192
|
-
if (visibleLanguage !== "
|
|
2222
|
+
if (visibleLanguage !== ""
|
|
2223
|
+
|| languageEvents.length
|
|
2193
2224
|
|| languageEvents.some((event) => JSON.stringify(event).includes("Now the tile view logic."))) {
|
|
2194
2225
|
throw new Error(`Claude Korean progress normalization self-test failed: ${JSON.stringify(languageEvents)}`);
|
|
2195
2226
|
}
|
|
2227
|
+
const keptCaptured = [];
|
|
2228
|
+
process.stdout.write = (chunk) => {
|
|
2229
|
+
keptCaptured.push(String(chunk));
|
|
2230
|
+
return true;
|
|
2231
|
+
};
|
|
2232
|
+
try {
|
|
2233
|
+
openingSession.streamBlocks.clear();
|
|
2234
|
+
await processStreamEvent(openingSession, {
|
|
2235
|
+
event: { type: "content_block_start", index: 0, content_block: { type: "text" } },
|
|
2236
|
+
});
|
|
2237
|
+
await processStreamEvent(openingSession, {
|
|
2238
|
+
event: { type: "content_block_delta", index: 0, delta: { text: "타일 보기 로직을 고쳤습니다." } },
|
|
2239
|
+
});
|
|
2240
|
+
await processStreamEvent(openingSession, {
|
|
2241
|
+
event: { type: "content_block_stop", index: 0 },
|
|
2242
|
+
});
|
|
2243
|
+
} finally {
|
|
2244
|
+
process.stdout.write = stdoutWrite;
|
|
2245
|
+
}
|
|
2246
|
+
const keptEvents = keptCaptured
|
|
2247
|
+
.join("")
|
|
2248
|
+
.trim()
|
|
2249
|
+
.split("\n")
|
|
2250
|
+
.filter(Boolean)
|
|
2251
|
+
.map((line) => JSON.parse(line));
|
|
2252
|
+
const keptStarted = keptEvents.findIndex((event) =>
|
|
2253
|
+
event.method === "item/started" && event.params?.item?.type === "agentMessage");
|
|
2254
|
+
const keptCompleted = keptEvents.find((event) =>
|
|
2255
|
+
event.method === "item/completed" && event.params?.item?.type === "agentMessage");
|
|
2256
|
+
if (keptStarted !== 0 || keptCompleted?.params?.item?.text !== "타일 보기 로직을 고쳤습니다.") {
|
|
2257
|
+
throw new Error(`Claude held Korean text self-test failed: ${JSON.stringify(keptEvents)}`);
|
|
2258
|
+
}
|
|
2196
2259
|
const smoothText = "Claude가 👨👩👧👦 한 문장을 한꺼번에 보내도 부드럽게 표시합니다.";
|
|
2197
|
-
const emitted = [];
|
|
2198
|
-
const smooth = new SmoothTextStream((chunk) => emitted.push(chunk), 0);
|
|
2199
|
-
smooth.push(smoothText);
|
|
2200
|
-
if (emitted.length !== 0) {
|
|
2201
|
-
throw new Error(`Claude smooth stream did not batch its first frame: ${JSON.stringify(emitted)}`);
|
|
2202
|
-
}
|
|
2203
|
-
await smooth.finish();
|
|
2260
|
+
const emitted = [];
|
|
2261
|
+
const smooth = new SmoothTextStream((chunk) => emitted.push(chunk), 0);
|
|
2262
|
+
smooth.push(smoothText);
|
|
2263
|
+
if (emitted.length !== 0) {
|
|
2264
|
+
throw new Error(`Claude smooth stream did not batch its first frame: ${JSON.stringify(emitted)}`);
|
|
2265
|
+
}
|
|
2266
|
+
await smooth.finish();
|
|
2204
2267
|
if (emitted.length < 2 || emitted.join("") !== smoothText) {
|
|
2205
2268
|
throw new Error(`Claude smooth stream self-test failed: ${JSON.stringify(emitted)}`);
|
|
2206
2269
|
}
|