devez-vibe 1.3.26 → 1.3.28
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 +62 -31
- package/package.json +2 -2
package/bin/dvz.exe
CHANGED
|
Binary file
|
|
@@ -1011,6 +1011,14 @@ function emitOpeningNotice(session) {
|
|
|
1011
1011
|
const SMOOTH_TEXT_INTERVAL_MS = 10;
|
|
1012
1012
|
const SMOOTH_TEXT_TARGET_FRAMES = 10;
|
|
1013
1013
|
const SMOOTH_TEXT_MAX_GRAPHEMES = 24;
|
|
1014
|
+
const SMOOTH_TEXT_MIN_RATE = 1;
|
|
1015
|
+
// Ease the per-frame rate toward the backlog's demand instead of adopting it at
|
|
1016
|
+
// once: a phrase-sized event would otherwise jump straight to a wide chunk and
|
|
1017
|
+
// read as a lurch, and the next tiny event would snap back to one grapheme.
|
|
1018
|
+
const SMOOTH_TEXT_RATE_EASING = 0.25;
|
|
1019
|
+
// Cap how far the eased rate may move in one frame so a sudden 400-grapheme
|
|
1020
|
+
// backlog ramps up over frames instead of widening the very first chunk.
|
|
1021
|
+
const SMOOTH_TEXT_RATE_STEP = 1;
|
|
1014
1022
|
const graphemeSegmenter = typeof Intl.Segmenter === "function"
|
|
1015
1023
|
? new Intl.Segmenter(undefined, { granularity: "grapheme" })
|
|
1016
1024
|
: null;
|
|
@@ -1021,14 +1029,11 @@ function splitGraphemes(text) {
|
|
|
1021
1029
|
: Array.from(text);
|
|
1022
1030
|
}
|
|
1023
1031
|
|
|
1024
|
-
// Claude can deliver a whole phrase in one SDK event. Drain
|
|
1025
|
-
// frame
|
|
1026
|
-
|
|
1032
|
+
// Claude can deliver a whole phrase in one SDK event. Drain a steady share per
|
|
1033
|
+
// visual frame, where the rate eases toward the backlog instead of tracking it
|
|
1034
|
+
// event by event.
|
|
1035
|
+
function takeSmoothTextChunk(text, size) {
|
|
1027
1036
|
const graphemes = splitGraphemes(text);
|
|
1028
|
-
const size = Math.min(
|
|
1029
|
-
SMOOTH_TEXT_MAX_GRAPHEMES,
|
|
1030
|
-
Math.max(1, Math.ceil(graphemes.length / SMOOTH_TEXT_TARGET_FRAMES)),
|
|
1031
|
-
);
|
|
1032
1037
|
return {
|
|
1033
1038
|
chunk: graphemes.slice(0, size).join(""),
|
|
1034
1039
|
rest: graphemes.slice(size).join(""),
|
|
@@ -1042,6 +1047,24 @@ class SmoothTextStream {
|
|
|
1042
1047
|
this.pending = "";
|
|
1043
1048
|
this.timer = null;
|
|
1044
1049
|
this.waiters = [];
|
|
1050
|
+
this.rate = SMOOTH_TEXT_MIN_RATE;
|
|
1051
|
+
// Fractional rates only stay smooth if the leftover carries to the next
|
|
1052
|
+
// frame; truncating every frame would quantize the pace back to integers.
|
|
1053
|
+
this.carry = 0;
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1056
|
+
nextChunkSize(pendingLength) {
|
|
1057
|
+
const demand = pendingLength / SMOOTH_TEXT_TARGET_FRAMES;
|
|
1058
|
+
const target = Math.min(
|
|
1059
|
+
SMOOTH_TEXT_MAX_GRAPHEMES,
|
|
1060
|
+
Math.max(SMOOTH_TEXT_MIN_RATE, demand),
|
|
1061
|
+
);
|
|
1062
|
+
const eased = (target - this.rate) * SMOOTH_TEXT_RATE_EASING;
|
|
1063
|
+
this.rate += Math.max(-SMOOTH_TEXT_RATE_STEP, Math.min(SMOOTH_TEXT_RATE_STEP, eased));
|
|
1064
|
+
const budget = this.rate + this.carry;
|
|
1065
|
+
const size = Math.max(SMOOTH_TEXT_MIN_RATE, Math.floor(budget));
|
|
1066
|
+
this.carry = Math.max(0, budget - size);
|
|
1067
|
+
return size;
|
|
1045
1068
|
}
|
|
1046
1069
|
|
|
1047
1070
|
push(text) {
|
|
@@ -1066,7 +1089,8 @@ class SmoothTextStream {
|
|
|
1066
1089
|
for (const resolve of this.waiters.splice(0)) resolve();
|
|
1067
1090
|
return;
|
|
1068
1091
|
}
|
|
1069
|
-
const
|
|
1092
|
+
const size = this.nextChunkSize(splitGraphemes(this.pending).length);
|
|
1093
|
+
const { chunk, rest } = takeSmoothTextChunk(this.pending, size);
|
|
1070
1094
|
this.pending = rest;
|
|
1071
1095
|
this.emit(chunk);
|
|
1072
1096
|
if (this.pending) {
|
|
@@ -1160,9 +1184,12 @@ async function processStreamEvent(session, message) {
|
|
|
1160
1184
|
const item = block.type === "text"
|
|
1161
1185
|
? { id, type: "agentMessage", text: "", provider: "Claude" }
|
|
1162
1186
|
: { id, type: "reasoning", summary: [] };
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1187
|
+
// Thinking arrives in the same phrase-sized events as text, so it needs the
|
|
1188
|
+
// same paced drain — raw thinking deltas were the last visible stutter.
|
|
1189
|
+
const method = block.type === "text"
|
|
1190
|
+
? "item/agentMessage/delta"
|
|
1191
|
+
: "item/reasoning/summaryTextDelta";
|
|
1192
|
+
const smooth = new SmoothTextStream((delta) => emitDelta(session, method, id, delta));
|
|
1166
1193
|
// A held English line can end up dropped entirely, and an item announced
|
|
1167
1194
|
// before that decision would stay on screen as an empty bubble. Hold the
|
|
1168
1195
|
// start too, and emit it with the first text that survives.
|
|
@@ -1188,10 +1215,6 @@ async function processStreamEvent(session, message) {
|
|
|
1188
1215
|
// Held text may still be dropped, and counting it as visible would suppress
|
|
1189
1216
|
// the opening notice in its place — leaving the turn with nothing to show.
|
|
1190
1217
|
if (current.type === "text" && current.languagePending == null) session.turn.sawVisibleText = true;
|
|
1191
|
-
if (!current.smooth) {
|
|
1192
|
-
emitDelta(session, "item/reasoning/summaryTextDelta", current.id, delta);
|
|
1193
|
-
return;
|
|
1194
|
-
}
|
|
1195
1218
|
if (current.languagePending != null) {
|
|
1196
1219
|
current.languagePending += delta;
|
|
1197
1220
|
const probe = current.languagePending.trimStart();
|
|
@@ -2122,22 +2145,22 @@ function historyState(messages) {
|
|
|
2122
2145
|
const userText = message.type === "user"
|
|
2123
2146
|
? stripInternalTags(stripHandoff(blocks.filter((block) => block.type === "text").map((block) => block.text || "").join("\n")))
|
|
2124
2147
|
: "";
|
|
2125
|
-
if (userText
|
|
2126
|
-
&& !blocks.some((block) => block.type === "tool_result")
|
|
2127
|
-
&& !isInternalHistoryText(message, userText)) {
|
|
2128
|
-
turn = {
|
|
2129
|
-
id: `claude-turn-${message.uuid}`,
|
|
2130
|
-
status: "completed",
|
|
2131
|
-
items: [{ id: `claude-user-${message.uuid}`, type: "userMessage", content: [{ type: "text", text: userText }] }],
|
|
2132
|
-
};
|
|
2133
|
-
const startedAt = messageTime(message);
|
|
2134
|
-
if (startedAt != null) turn.startedAt = Math.floor(startedAt / 1000);
|
|
2135
|
-
turns.push(turn);
|
|
2136
|
-
}
|
|
2137
|
-
if (!turn) continue;
|
|
2138
|
-
const completedAt = messageTime(message);
|
|
2139
|
-
if (completedAt != null) turn.completedAt = Math.floor(completedAt / 1000);
|
|
2140
|
-
if (message.type === "assistant") {
|
|
2148
|
+
if (userText
|
|
2149
|
+
&& !blocks.some((block) => block.type === "tool_result")
|
|
2150
|
+
&& !isInternalHistoryText(message, userText)) {
|
|
2151
|
+
turn = {
|
|
2152
|
+
id: `claude-turn-${message.uuid}`,
|
|
2153
|
+
status: "completed",
|
|
2154
|
+
items: [{ id: `claude-user-${message.uuid}`, type: "userMessage", content: [{ type: "text", text: userText }] }],
|
|
2155
|
+
};
|
|
2156
|
+
const startedAt = messageTime(message);
|
|
2157
|
+
if (startedAt != null) turn.startedAt = Math.floor(startedAt / 1000);
|
|
2158
|
+
turns.push(turn);
|
|
2159
|
+
}
|
|
2160
|
+
if (!turn) continue;
|
|
2161
|
+
const completedAt = messageTime(message);
|
|
2162
|
+
if (completedAt != null) turn.completedAt = Math.floor(completedAt / 1000);
|
|
2163
|
+
if (message.type === "assistant") {
|
|
2141
2164
|
if (message.message?.model === "<synthetic>") {
|
|
2142
2165
|
turn.synthetic = true;
|
|
2143
2166
|
continue;
|
|
@@ -3030,6 +3053,14 @@ async function runSelfTest() {
|
|
|
3030
3053
|
if (emitted.length < 2 || emitted.join("") !== smoothText) {
|
|
3031
3054
|
throw new Error(`Claude smooth stream self-test failed: ${JSON.stringify(emitted)}`);
|
|
3032
3055
|
}
|
|
3056
|
+
const ramped = [];
|
|
3057
|
+
const rampStream = new SmoothTextStream((chunk) => ramped.push(chunk), 0);
|
|
3058
|
+
rampStream.push("가".repeat(400));
|
|
3059
|
+
await rampStream.finish();
|
|
3060
|
+
const rampSizes = ramped.map((chunk) => splitGraphemes(chunk).length);
|
|
3061
|
+
if (rampSizes[0] > 2 || rampSizes.some((size, index) => index > 0 && size > rampSizes[index - 1] + 2)) {
|
|
3062
|
+
throw new Error(`Claude smooth stream rate jumped: ${JSON.stringify(rampSizes.slice(0, 8))}`);
|
|
3063
|
+
}
|
|
3033
3064
|
const flushed = [];
|
|
3034
3065
|
const interrupted = new SmoothTextStream((chunk) => flushed.push(chunk), 1000);
|
|
3035
3066
|
interrupted.push(smoothText);
|
package/package.json
CHANGED