devez-vibe 1.9.23 → 1.9.25
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 +274 -55
- package/package.json +2 -2
package/bin/dvz.exe
CHANGED
|
Binary file
|
|
@@ -39,7 +39,10 @@ const CLAUDE_PROVIDER_SKILLS = new Set([
|
|
|
39
39
|
// Built-in commands Claude Code runs only from the user's own message, never
|
|
40
40
|
// from the model (disableModelInvocation), so they go in as typed `/name` text.
|
|
41
41
|
const CLAUDE_USER_ONLY_COMMANDS = new Set(["team-onboarding"]);
|
|
42
|
-
|
|
42
|
+
// Claude Code's own safeguard fallback (opus5). The host keeps it for that
|
|
43
|
+
// switch but leaves it out of /model; Opus 4.8 is no longer offered.
|
|
44
|
+
const PREVIOUS_OPUS_MODEL = "claude-opus-5";
|
|
45
|
+
const RETIRED_OPUS_MODEL = "claude-opus-4-8";
|
|
43
46
|
const CLAUDE_EFFORT_LEVELS = ["low", "medium", "high", "xhigh", "max"];
|
|
44
47
|
const CLAUDE_TASK_TOOLS = ["TaskCreate", "TaskGet", "TaskUpdate", "TaskList"];
|
|
45
48
|
let nextHostRequest = 1;
|
|
@@ -230,8 +233,8 @@ function modelCapabilities(models, model) {
|
|
|
230
233
|
const capabilities = models.find((candidate) =>
|
|
231
234
|
candidate.value === value || candidate.resolvedModel === value)
|
|
232
235
|
|| (!value ? models.find((candidate) => candidate.value === "default") : undefined);
|
|
233
|
-
return value ===
|
|
234
|
-
?
|
|
236
|
+
return value === PREVIOUS_OPUS_MODEL
|
|
237
|
+
? previousOpusCapabilities(models, capabilities)
|
|
235
238
|
: capabilities;
|
|
236
239
|
}
|
|
237
240
|
|
|
@@ -251,10 +254,10 @@ function familyCapabilities(models, model) {
|
|
|
251
254
|
.some((name) => String(name || "").toLowerCase().includes(family)));
|
|
252
255
|
}
|
|
253
256
|
|
|
254
|
-
function
|
|
257
|
+
function previousOpusCapabilities(models, existing = {}) {
|
|
255
258
|
// The CLI now exposes the Opus alias as `opus[1m]` (and only `default`
|
|
256
259
|
// resolves to it), so an exact `value === "opus"` match finds nothing and the
|
|
257
|
-
// synthesized Opus
|
|
260
|
+
// synthesized Opus 5 row loses supportsAutoMode/supportsFastMode. Match the
|
|
258
261
|
// Opus family instead, falling back to `default`.
|
|
259
262
|
const opus = models.find((model) => String(model.value || "").toLowerCase().startsWith("opus"))
|
|
260
263
|
|| models.find((model) => model.value === "default")
|
|
@@ -271,9 +274,9 @@ function opus48Capabilities(models, existing = {}) {
|
|
|
271
274
|
return {
|
|
272
275
|
...opus,
|
|
273
276
|
...existing,
|
|
274
|
-
value:
|
|
275
|
-
resolvedModel:
|
|
276
|
-
displayName: "Opus
|
|
277
|
+
value: PREVIOUS_OPUS_MODEL,
|
|
278
|
+
resolvedModel: PREVIOUS_OPUS_MODEL,
|
|
279
|
+
displayName: "Opus 5",
|
|
277
280
|
supportsEffort: true,
|
|
278
281
|
supportedEffortLevels,
|
|
279
282
|
};
|
|
@@ -328,22 +331,23 @@ function catalogEntry(model, defaultResolvedModel) {
|
|
|
328
331
|
}
|
|
329
332
|
|
|
330
333
|
function claudeCatalogEntries(models, defaultResolvedModel) {
|
|
331
|
-
const catalogModels = models.filter((model) => model.value && model.value !== "default"
|
|
334
|
+
const catalogModels = models.filter((model) => model.value && model.value !== "default"
|
|
335
|
+
&& ![model.value, model.resolvedModel]
|
|
336
|
+
.some((name) => stripClaudeModel(String(name || "")).startsWith(RETIRED_OPUS_MODEL)));
|
|
332
337
|
const entries = catalogModels.map((model) => catalogEntry(model, defaultResolvedModel));
|
|
333
338
|
const existingIndex = entries.findIndex((entry) =>
|
|
334
|
-
stripClaudeModel(entry.model) ===
|
|
335
|
-
|| stripClaudeModel(entry.id) ===
|
|
336
|
-
|| entry.displayName === "Opus 4.8");
|
|
339
|
+
stripClaudeModel(entry.model) === PREVIOUS_OPUS_MODEL
|
|
340
|
+
|| stripClaudeModel(entry.id) === PREVIOUS_OPUS_MODEL);
|
|
337
341
|
const existing = existingIndex >= 0 ? catalogModels[existingIndex] : {};
|
|
338
342
|
if (existingIndex >= 0) entries.splice(existingIndex, 1);
|
|
339
|
-
const
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
343
|
+
const previousOpus = {
|
|
344
|
+
...catalogEntry(previousOpusCapabilities(models, existing), defaultResolvedModel),
|
|
345
|
+
hidden: true,
|
|
346
|
+
};
|
|
347
|
+
const opusIndex = entries.findIndex((entry) => /\bopus\b/i.test(entry.displayName));
|
|
344
348
|
const fableIndex = entries.findIndex((entry) => /\bfable\b/i.test(entry.displayName));
|
|
345
349
|
const insertAfter = opusIndex >= 0 ? opusIndex : fableIndex;
|
|
346
|
-
entries.splice(insertAfter + 1, 0,
|
|
350
|
+
entries.splice(insertAfter + 1, 0, previousOpus);
|
|
347
351
|
return entries;
|
|
348
352
|
}
|
|
349
353
|
|
|
@@ -830,13 +834,13 @@ function permissionSuggestionLabel(suggestions) {
|
|
|
830
834
|
const values = [...new Set([...rules, ...directories].filter(Boolean))];
|
|
831
835
|
const destinations = new Set(suggestions.map((suggestion) => suggestion.destination));
|
|
832
836
|
const scope = destinations.has("userSettings")
|
|
833
|
-
? "
|
|
837
|
+
? "in all projects"
|
|
834
838
|
: destinations.has("projectSettings") || destinations.has("localSettings")
|
|
835
|
-
? "
|
|
836
|
-
: "
|
|
839
|
+
? "in this project"
|
|
840
|
+
: "for this session";
|
|
837
841
|
return values.length
|
|
838
|
-
?
|
|
839
|
-
:
|
|
842
|
+
? `Always allow ${scope}: ${values.join(", ")}`
|
|
843
|
+
: `Don't ask again ${scope}`;
|
|
840
844
|
}
|
|
841
845
|
|
|
842
846
|
async function requestToolPermission(toolName, input, permission) {
|
|
@@ -1486,25 +1490,66 @@ function isKoreanPrompt(input) {
|
|
|
1486
1490
|
.filter((item) => item?.type === "text")
|
|
1487
1491
|
.map((item) => String(item.text || ""))
|
|
1488
1492
|
.join("\n");
|
|
1489
|
-
|
|
1493
|
+
// Jamo too: a bare `\u3147\u314b` reply is still a Korean turn.
|
|
1494
|
+
return /[\uac00-\ud7a3\u3131-\u318e]/.test(prompt);
|
|
1490
1495
|
}
|
|
1491
1496
|
|
|
1492
1497
|
// `Now the tile view logic.` carries nothing a Korean reader needs, and the
|
|
1493
1498
|
// stand-in that used to replace it carried even less — the same sentence before
|
|
1494
1499
|
// every tool call, however many calls the turn made. Drop the line instead; the
|
|
1495
|
-
// tool item that follows already names what is being read.
|
|
1496
|
-
|
|
1500
|
+
// tool item that follows already names what is being read. Any other English
|
|
1501
|
+
// one-liner goes too once a tool call is known to follow it
|
|
1502
|
+
// (`Bundle is ready; running the upload script.`).
|
|
1503
|
+
function normalizeProgressText(turn, text, beforeTool = false) {
|
|
1497
1504
|
const value = String(text || "");
|
|
1498
1505
|
const trimmed = value.trim();
|
|
1499
|
-
if (turn
|
|
1500
|
-
&& trimmed
|
|
1501
|
-
&& !/[\uac00-\ud7a3]/.test(trimmed)
|
|
1502
|
-
&& /^Now\b[^\r\n]*[.!?]?$/i.test(trimmed)) {
|
|
1506
|
+
if (isEnglishProgressLine(turn, trimmed)
|
|
1507
|
+
&& (beforeTool || /^Now\b/i.test(trimmed))) {
|
|
1503
1508
|
return "";
|
|
1504
1509
|
}
|
|
1505
1510
|
return value;
|
|
1506
1511
|
}
|
|
1507
1512
|
|
|
1513
|
+
function isEnglishProgressLine(turn, text) {
|
|
1514
|
+
const trimmed = String(text || "").trim();
|
|
1515
|
+
return Boolean(turn?.koreanRequest)
|
|
1516
|
+
&& trimmed.length <= 160
|
|
1517
|
+
&& !/[\r\n]/.test(trimmed)
|
|
1518
|
+
&& !/[\uac00-\ud7a3]/.test(trimmed);
|
|
1519
|
+
}
|
|
1520
|
+
|
|
1521
|
+
// A finished English line waits for the next block: a tool call drops it,
|
|
1522
|
+
// anything else shows it as written.
|
|
1523
|
+
function releaseHeldProgress(session, dropped) {
|
|
1524
|
+
const held = session.heldProgress;
|
|
1525
|
+
session.heldProgress = null;
|
|
1526
|
+
if (!held || dropped) return;
|
|
1527
|
+
emitHeldStart(session, held);
|
|
1528
|
+
held.emit(held.text);
|
|
1529
|
+
emitItem(session, "completed", { id: held.id, type: "agentMessage", text: held.text, provider: "Claude" });
|
|
1530
|
+
}
|
|
1531
|
+
|
|
1532
|
+
// Claude Opus 5.5 and Fable 5.1 send the notes they write between tool calls as
|
|
1533
|
+
// progress-update thinking blocks, and the CLI asks for their short summaries.
|
|
1534
|
+
// They are the reply's own words rather than reasoning, so they go out as text:
|
|
1535
|
+
// shown as reasoning, Super Vibe dropped them and the turn went silent before a
|
|
1536
|
+
// question.
|
|
1537
|
+
const PROGRESS_UPDATE_MODEL = /claude-(?:opus-5-5|fable-5-1|mythos-5-1)/;
|
|
1538
|
+
|
|
1539
|
+
function isProgressUpdateModel(model) {
|
|
1540
|
+
return PROGRESS_UPDATE_MODEL.test(String(model || ""));
|
|
1541
|
+
}
|
|
1542
|
+
|
|
1543
|
+
function emitProgressUpdate(session, text) {
|
|
1544
|
+
const visible = normalizeProgressText(session.turn, text, true);
|
|
1545
|
+
if (!visible.trim()) return;
|
|
1546
|
+
const id = nextItemId(session, "text");
|
|
1547
|
+
emitItem(session, "started", { id, type: "agentMessage", text: "", provider: "Claude" });
|
|
1548
|
+
emitDelta(session, "item/agentMessage/delta", id, visible);
|
|
1549
|
+
emitItem(session, "completed", { id, type: "agentMessage", text: visible, provider: "Claude" });
|
|
1550
|
+
session.turn.sawVisibleText = true;
|
|
1551
|
+
}
|
|
1552
|
+
|
|
1508
1553
|
// Text pacing lives in the host renderer, not here. A timer in this process
|
|
1509
1554
|
// runs on Node's scheduler, which cannot line up with the terminal's redraw
|
|
1510
1555
|
// rhythm — the mismatch was itself the visible stutter. Forward each delta as
|
|
@@ -1565,11 +1610,14 @@ function emitHeldStart(session, current) {
|
|
|
1565
1610
|
async function processStreamEvent(session, message) {
|
|
1566
1611
|
if (!session.turn || message.parent_tool_use_id) return;
|
|
1567
1612
|
const event = message.event || {};
|
|
1613
|
+
if (event.type === "message_start" || event.type === "message_stop") releaseHeldProgress(session);
|
|
1568
1614
|
if (event.type === "message_start") {
|
|
1569
1615
|
session.streamBlocks.clear();
|
|
1616
|
+
session.streamModel = event.message?.model || session.model;
|
|
1570
1617
|
}
|
|
1571
1618
|
if (event.type === "content_block_start") {
|
|
1572
1619
|
const block = event.content_block || {};
|
|
1620
|
+
releaseHeldProgress(session, block.type === "tool_use" || block.type === "server_tool_use");
|
|
1573
1621
|
if (block.type !== "text" && block.type !== "thinking") return;
|
|
1574
1622
|
const id = nextItemId(session, block.type);
|
|
1575
1623
|
const item = block.type === "text"
|
|
@@ -1586,10 +1634,10 @@ async function processStreamEvent(session, message) {
|
|
|
1586
1634
|
session.streamBlocks.set(event.index, {
|
|
1587
1635
|
id,
|
|
1588
1636
|
type: block.type,
|
|
1637
|
+
progress: block.type === "thinking" && isProgressUpdateModel(session.streamModel),
|
|
1589
1638
|
text: "",
|
|
1590
1639
|
emit,
|
|
1591
1640
|
languagePending: held ? "" : null,
|
|
1592
|
-
holdEnglishProgress: false,
|
|
1593
1641
|
pendingStart: held ? item : null,
|
|
1594
1642
|
});
|
|
1595
1643
|
if (!held) emitItem(session, "started", item);
|
|
@@ -1606,20 +1654,14 @@ async function processStreamEvent(session, message) {
|
|
|
1606
1654
|
if (current.type === "text" && current.languagePending == null) session.turn.sawVisibleText = true;
|
|
1607
1655
|
if (current.languagePending != null) {
|
|
1608
1656
|
current.languagePending += delta;
|
|
1609
|
-
|
|
1610
|
-
const lower = probe.toLowerCase();
|
|
1611
|
-
if (!current.holdEnglishProgress && "now".startsWith(lower)) return;
|
|
1612
|
-
if (/^now(?:\s|$)/i.test(probe)) {
|
|
1613
|
-
current.holdEnglishProgress = true;
|
|
1614
|
-
return;
|
|
1615
|
-
}
|
|
1657
|
+
if (isEnglishProgressLine(session.turn, current.languagePending)) return;
|
|
1616
1658
|
emitHeldStart(session, current);
|
|
1617
1659
|
session.turn.sawVisibleText = true;
|
|
1618
1660
|
current.emit(current.languagePending);
|
|
1619
1661
|
current.languagePending = null;
|
|
1620
1662
|
return;
|
|
1621
1663
|
}
|
|
1622
|
-
current.emit(delta);
|
|
1664
|
+
if (!current.progress) current.emit(delta);
|
|
1623
1665
|
return;
|
|
1624
1666
|
}
|
|
1625
1667
|
if (event.type === "content_block_stop") {
|
|
@@ -1633,6 +1675,12 @@ async function processStreamEvent(session, message) {
|
|
|
1633
1675
|
session.streamBlocks.delete(event.index);
|
|
1634
1676
|
return;
|
|
1635
1677
|
}
|
|
1678
|
+
if (current.pendingStart && isEnglishProgressLine(session.turn, visible)) {
|
|
1679
|
+
releaseHeldProgress(session);
|
|
1680
|
+
session.heldProgress = current;
|
|
1681
|
+
session.streamBlocks.delete(event.index);
|
|
1682
|
+
return;
|
|
1683
|
+
}
|
|
1636
1684
|
emitHeldStart(session, current);
|
|
1637
1685
|
if (visible.trim()) session.turn.sawVisibleText = true;
|
|
1638
1686
|
current.emit(visible);
|
|
@@ -1640,9 +1688,10 @@ async function processStreamEvent(session, message) {
|
|
|
1640
1688
|
emitHeldStart(session, current);
|
|
1641
1689
|
const item = current.type === "text"
|
|
1642
1690
|
? { id: current.id, type: "agentMessage", text: current.text, provider: "Claude" }
|
|
1643
|
-
: { id: current.id, type: "reasoning", summary: [current.text] };
|
|
1691
|
+
: { id: current.id, type: "reasoning", summary: [current.progress ? "" : current.text] };
|
|
1644
1692
|
emitItem(session, "completed", item);
|
|
1645
1693
|
session.streamBlocks.delete(event.index);
|
|
1694
|
+
if (current.progress) emitProgressUpdate(session, current.text);
|
|
1646
1695
|
}
|
|
1647
1696
|
}
|
|
1648
1697
|
|
|
@@ -1687,10 +1736,14 @@ function processAssistant(session, message) {
|
|
|
1687
1736
|
// Without partial SDK events, replay completed text before tool items so the
|
|
1688
1737
|
// visible order still matches the assistant content order.
|
|
1689
1738
|
if (!session.streamBlocks.size && !session.turn.sawStreamText) {
|
|
1690
|
-
for (const block of content) {
|
|
1739
|
+
for (const [index, block] of content.entries()) {
|
|
1691
1740
|
if (block.type !== "text" && block.type !== "thinking") continue;
|
|
1741
|
+
if (block.type === "thinking" && isProgressUpdateModel(message.message?.model)) {
|
|
1742
|
+
emitProgressUpdate(session, block.thinking);
|
|
1743
|
+
continue;
|
|
1744
|
+
}
|
|
1692
1745
|
const visible = block.type === "text"
|
|
1693
|
-
? normalizeProgressText(session.turn, block.text)
|
|
1746
|
+
? normalizeProgressText(session.turn, block.text, content[index + 1]?.type === "tool_use")
|
|
1694
1747
|
: "";
|
|
1695
1748
|
if (block.type === "text" && !visible.trim() && String(block.text || "").trim()) continue;
|
|
1696
1749
|
const id = nextItemId(session, block.type);
|
|
@@ -2700,6 +2753,23 @@ const STARTUP_FAILURE_GUIDES = {
|
|
|
2700
2753
|
bypass_root: "관리자 계정에서는 권한 우회 모드로 시작할 수 없습니다. 일반 계정으로 실행하세요.",
|
|
2701
2754
|
};
|
|
2702
2755
|
|
|
2756
|
+
/**
|
|
2757
|
+
* A turn can end without the error flag yet still carry no answer: the model
|
|
2758
|
+
* refused, or the response was cut off. Those slip by silently unless surfaced.
|
|
2759
|
+
* Refusal is reported as an error so the host opens its model-switch card on the
|
|
2760
|
+
* `(refusal)` marker; anything else is a plain notice. Normal stops give null.
|
|
2761
|
+
*/
|
|
2762
|
+
function abnormalStopOutcome(stopReason) {
|
|
2763
|
+
if (!stopReason || ["end_turn", "tool_use", "stop_sequence"].includes(stopReason)) return null;
|
|
2764
|
+
if (stopReason === "refusal") {
|
|
2765
|
+
return { error: "Claude declined to answer this request. (refusal)" };
|
|
2766
|
+
}
|
|
2767
|
+
if (stopReason === "max_tokens") {
|
|
2768
|
+
return { warning: "The response was cut off at its maximum length. (max_tokens)" };
|
|
2769
|
+
}
|
|
2770
|
+
return { warning: `The response ended unexpectedly. (${stopReason})` };
|
|
2771
|
+
}
|
|
2772
|
+
|
|
2703
2773
|
async function processResult(session, message) {
|
|
2704
2774
|
if (!session.turn) return;
|
|
2705
2775
|
for (const denial of Array.isArray(message.permission_denials) ? message.permission_denials : []) {
|
|
@@ -2732,9 +2802,27 @@ async function processResult(session, message) {
|
|
|
2732
2802
|
});
|
|
2733
2803
|
const detail = message.errors?.join("\n") || message.result || message.stop_reason || "Claude 실행 실패";
|
|
2734
2804
|
const guide = STARTUP_FAILURE_GUIDES[message.startup_failure_reason];
|
|
2735
|
-
|
|
2805
|
+
let error = message.is_error && !interrupted
|
|
2736
2806
|
? { message: guide ? `${guide}\n${detail}` : detail }
|
|
2737
2807
|
: null;
|
|
2808
|
+
if (!error && !interrupted) {
|
|
2809
|
+
const outcome = abnormalStopOutcome(message.stop_reason);
|
|
2810
|
+
if (outcome?.error) error = { message: outcome.error };
|
|
2811
|
+
else if (outcome?.warning) {
|
|
2812
|
+
notify("warning", { threadId: session.id, provider: "Claude", message: outcome.warning });
|
|
2813
|
+
}
|
|
2814
|
+
}
|
|
2815
|
+
// An assistant-level error is otherwise kept only in turn state (for retry
|
|
2816
|
+
// decisions) and never shown. Surface it when the result itself carried no
|
|
2817
|
+
// error, so it cannot double with the result error above. Verification was
|
|
2818
|
+
// already announced when the assistant message arrived.
|
|
2819
|
+
const assistantError = session.turn?.assistantError;
|
|
2820
|
+
if (!error && assistantError && assistantError !== "verification_required") {
|
|
2821
|
+
const text = typeof assistantError === "string"
|
|
2822
|
+
? assistantError
|
|
2823
|
+
: assistantError.message || JSON.stringify(assistantError);
|
|
2824
|
+
notify("warning", { threadId: session.id, provider: "Claude", message: `Claude response error: ${text}` });
|
|
2825
|
+
}
|
|
2738
2826
|
if (error && waitForUsageLimit(session)) return;
|
|
2739
2827
|
finishTurn(session, error, message.duration_ms);
|
|
2740
2828
|
notify("claude/account/updated", {
|
|
@@ -2810,6 +2898,7 @@ async function runPendingPrompt(session) {
|
|
|
2810
2898
|
function finishTurn(session, error, durationMs) {
|
|
2811
2899
|
clearUsageLimitWait(session);
|
|
2812
2900
|
if (!session.turn) return;
|
|
2901
|
+
releaseHeldProgress(session);
|
|
2813
2902
|
flushPendingPlan(session);
|
|
2814
2903
|
clearForegroundSubagents(session);
|
|
2815
2904
|
const turn = { id: session.turn.id, status: error ? "failed" : session.turn.interruptRequested ? "interrupted" : "completed" };
|
|
@@ -2870,10 +2959,18 @@ async function consumeMessage(session, message) {
|
|
|
2870
2959
|
noteCompactBoundary(session, message.compact_metadata);
|
|
2871
2960
|
notify("thread/compacted", { threadId: session.id });
|
|
2872
2961
|
} else if (message.type === "system" && message.subtype === "permission_denied") {
|
|
2962
|
+
const reason = message.decision_reason || message.decision_reason_type;
|
|
2873
2963
|
rememberPermissionDenial(session, {
|
|
2874
2964
|
tool: message.tool_name,
|
|
2875
2965
|
toolUseId: message.tool_use_id,
|
|
2876
|
-
reason
|
|
2966
|
+
reason,
|
|
2967
|
+
});
|
|
2968
|
+
// The /permissions panel keeps the record, but a denial mid-turn otherwise
|
|
2969
|
+
// leaves the screen silent. Show it inline too.
|
|
2970
|
+
notify("warning", {
|
|
2971
|
+
threadId: session.id,
|
|
2972
|
+
provider: "Claude",
|
|
2973
|
+
message: `${message.tool_name || "Tool"} was denied${reason ? `: ${reason}` : "."}`,
|
|
2877
2974
|
});
|
|
2878
2975
|
} else if (message.type === "rate_limit_event") {
|
|
2879
2976
|
if (session.turn) session.turn.rateLimitInfo = message.rate_limit_info;
|
|
@@ -3042,13 +3139,17 @@ async function runPrompt(session, params) {
|
|
|
3042
3139
|
|
|
3043
3140
|
// A background task notification is an internal user message that starts its
|
|
3044
3141
|
// own Claude response even though the host did not submit a new prompt.
|
|
3045
|
-
function beginTurn(session, input
|
|
3142
|
+
function beginTurn(session, input) {
|
|
3046
3143
|
const turnId = `claude-turn-${session.turnSequence++}-${randomUUID()}`;
|
|
3144
|
+
const koreanRequest = input === undefined
|
|
3145
|
+
? Boolean(session.lastKoreanRequest)
|
|
3146
|
+
: isKoreanPrompt(input);
|
|
3147
|
+
session.lastKoreanRequest = koreanRequest;
|
|
3047
3148
|
session.turn = {
|
|
3048
3149
|
id: turnId,
|
|
3049
3150
|
sawStreamText: false,
|
|
3050
3151
|
sawVisibleText: false,
|
|
3051
|
-
koreanRequest
|
|
3152
|
+
koreanRequest,
|
|
3052
3153
|
};
|
|
3053
3154
|
session.lastContextUsage = null;
|
|
3054
3155
|
notify("turn/started", { threadId: session.id, turn: { id: turnId } });
|
|
@@ -3168,8 +3269,14 @@ function historyState(messages) {
|
|
|
3168
3269
|
if (!visible.trim() && String(block.text || "").trim()) continue;
|
|
3169
3270
|
turn.items.push({ id: `${message.uuid}-text`, type: "agentMessage", text: visible, provider: "Claude" });
|
|
3170
3271
|
}
|
|
3272
|
+
else if (block.type === "thinking" && isProgressUpdateModel(message.message?.model)) {
|
|
3273
|
+
const visible = normalizeProgressText(historyTurn, block.thinking, true);
|
|
3274
|
+
if (visible.trim()) turn.items.push({ id: `${message.uuid}-thinking`, type: "agentMessage", text: visible, provider: "Claude" });
|
|
3275
|
+
}
|
|
3171
3276
|
else if (block.type === "thinking") turn.items.push({ id: `${message.uuid}-thinking`, type: "reasoning", summary: [block.thinking || ""] });
|
|
3172
3277
|
else if (block.type === "tool_use") {
|
|
3278
|
+
const last = turn.items.at(-1);
|
|
3279
|
+
if (last?.type === "agentMessage" && !normalizeProgressText(historyTurn, last.text, true).trim()) turn.items.pop();
|
|
3173
3280
|
const pending = { name: block.name, input: block.input || {}, item: toolItem({}, block.id, block.name, block.input || {}) };
|
|
3174
3281
|
tools.set(block.id, pending);
|
|
3175
3282
|
if (block.name === "TaskCreate") {
|
|
@@ -3915,6 +4022,18 @@ async function runSelfTest() {
|
|
|
3915
4022
|
await runPermissionModeSelfTest();
|
|
3916
4023
|
runToolPolicySelfTest();
|
|
3917
4024
|
await runCommandTimeoutSelfTest();
|
|
4025
|
+
// A turn that ends with no answer must reach the user: refusal as an error
|
|
4026
|
+
// carrying the marker the host's switch card keys off, the rest as a notice.
|
|
4027
|
+
const refusalStop = abnormalStopOutcome("refusal");
|
|
4028
|
+
const truncatedStop = abnormalStopOutcome("max_tokens");
|
|
4029
|
+
const unknownStop = abnormalStopOutcome("model_context_window_exceeded");
|
|
4030
|
+
if (abnormalStopOutcome("end_turn") !== null
|
|
4031
|
+
|| abnormalStopOutcome(undefined) !== null
|
|
4032
|
+
|| !refusalStop?.error?.includes("(refusal)")
|
|
4033
|
+
|| !truncatedStop?.warning?.includes("max_tokens")
|
|
4034
|
+
|| !unknownStop?.warning?.includes("model_context_window_exceeded")) {
|
|
4035
|
+
throw new Error(`Claude abnormal stop notice self-test failed: ${JSON.stringify({ refusalStop, truncatedStop, unknownStop })}`);
|
|
4036
|
+
}
|
|
3918
4037
|
const planUsage = { rate_limits: { five_hour: { utilization: 25 } }, behaviors: null };
|
|
3919
4038
|
let usageOptions;
|
|
3920
4039
|
const fetchedUsage = await safeUsage({
|
|
@@ -4195,6 +4314,26 @@ async function runSelfTest() {
|
|
|
4195
4314
|
|| englishNowText !== "Now the answer starts.") {
|
|
4196
4315
|
throw new Error(`Claude resumed progress normalization self-test failed: ${JSON.stringify({ resumedProgressText, englishNowText })}`);
|
|
4197
4316
|
}
|
|
4317
|
+
const jamoProgress = historyTurns([
|
|
4318
|
+
user("jamo-user", "ㅇㅋ"),
|
|
4319
|
+
{
|
|
4320
|
+
type: "assistant",
|
|
4321
|
+
uuid: "jamo-assistant",
|
|
4322
|
+
message: {
|
|
4323
|
+
role: "assistant",
|
|
4324
|
+
model: "claude-opus-5",
|
|
4325
|
+
content: [
|
|
4326
|
+
{ type: "text", text: "Bundle is ready; running the upload script." },
|
|
4327
|
+
{ type: "tool_use", id: "jamo-run", name: "PowerShell", input: { command: "upload" } },
|
|
4328
|
+
],
|
|
4329
|
+
},
|
|
4330
|
+
},
|
|
4331
|
+
taskResult("jamo-result", "jamo-run", { content: "ok" }, "ok"),
|
|
4332
|
+
assistant("jamo-final", "claude-opus-5", "Uploaded v1.2.74."),
|
|
4333
|
+
])[0]?.items.filter((item) => item.type === "agentMessage").map((item) => item.text);
|
|
4334
|
+
if (JSON.stringify(jamoProgress) !== JSON.stringify(["Uploaded v1.2.74."])) {
|
|
4335
|
+
throw new Error(`Claude English line before tool self-test failed: ${JSON.stringify(jamoProgress)}`);
|
|
4336
|
+
}
|
|
4198
4337
|
const taskMessages = [user("plan", "작업을 진행해")];
|
|
4199
4338
|
for (let index = 1; index <= 6; index++) {
|
|
4200
4339
|
const id = String(24 + index);
|
|
@@ -4366,29 +4505,33 @@ async function runSelfTest() {
|
|
|
4366
4505
|
const catalogModels = [
|
|
4367
4506
|
{
|
|
4368
4507
|
value: "opus[1m]",
|
|
4369
|
-
resolvedModel: "claude-opus-5[1m]",
|
|
4370
|
-
displayName: "Opus 5",
|
|
4508
|
+
resolvedModel: "claude-opus-5-5[1m]",
|
|
4509
|
+
displayName: "Opus 5.5",
|
|
4371
4510
|
supportsEffort: true,
|
|
4372
4511
|
supportedEffortLevels: ["high", "max"],
|
|
4373
4512
|
supportsAutoMode: true,
|
|
4374
4513
|
},
|
|
4375
4514
|
{ value: "sonnet", resolvedModel: "claude-sonnet-5", displayName: "Sonnet 5" },
|
|
4376
4515
|
{
|
|
4377
|
-
value: "claude-opus-
|
|
4378
|
-
resolvedModel: "claude-opus-
|
|
4379
|
-
displayName: "Opus
|
|
4516
|
+
value: "claude-opus-5",
|
|
4517
|
+
resolvedModel: "claude-opus-5",
|
|
4518
|
+
displayName: "Opus 5",
|
|
4380
4519
|
supportsEffort: false,
|
|
4381
4520
|
supportedEffortLevels: [],
|
|
4382
4521
|
},
|
|
4522
|
+
{ value: "claude-opus-4-8", resolvedModel: "claude-opus-4-8", displayName: "Opus 4.8" },
|
|
4383
4523
|
];
|
|
4384
4524
|
const catalog = claudeCatalogEntries(catalogModels, "claude-sonnet-5");
|
|
4385
|
-
if (catalog
|
|
4386
|
-
|| catalog[
|
|
4387
|
-
|| catalog[1]?.
|
|
4525
|
+
if (catalog.length !== 3
|
|
4526
|
+
|| catalog[0]?.displayName !== "Opus 5.5"
|
|
4527
|
+
|| catalog[1]?.displayName !== "Opus 5"
|
|
4528
|
+
|| catalog[1]?.model !== "claude:claude-opus-5"
|
|
4529
|
+
|| catalog[1]?.hidden !== true
|
|
4530
|
+
|| catalog.some((entry) => entry.displayName === "Opus 4.8")
|
|
4388
4531
|
|| catalog[1]?.supportsAutoMode !== true
|
|
4389
4532
|
|| catalog[1]?.supportedReasoningEfforts?.[1]?.reasoningEffort !== "max"
|
|
4390
4533
|
|| supportedEffort(
|
|
4391
|
-
modelCapabilities(catalogModels, "claude:claude-opus-
|
|
4534
|
+
modelCapabilities(catalogModels, "claude:claude-opus-5"),
|
|
4392
4535
|
"max",
|
|
4393
4536
|
) !== "max") {
|
|
4394
4537
|
throw new Error(`Claude pinned model self-test failed: ${JSON.stringify(catalog)}`);
|
|
@@ -5282,6 +5425,30 @@ async function runSelfTest() {
|
|
|
5282
5425
|
if (openingMessageIndex >= 0 || openingToolIndex < 0) {
|
|
5283
5426
|
throw new Error(`Claude tool-first turn self-test failed: ${JSON.stringify(openingEvents)}`);
|
|
5284
5427
|
}
|
|
5428
|
+
const continuationSession = {
|
|
5429
|
+
...openingSession,
|
|
5430
|
+
turn: null,
|
|
5431
|
+
turnSequence: 1,
|
|
5432
|
+
automaticTurnsPending: 0,
|
|
5433
|
+
steerPending: 0,
|
|
5434
|
+
streamBlocks: new Map(),
|
|
5435
|
+
};
|
|
5436
|
+
beginTurn(continuationSession, [{ type: "text", text: "provider 메뉴를 수정해" }]);
|
|
5437
|
+
continuationSession.turn = null;
|
|
5438
|
+
continuationSession.automaticTurnsPending = 1;
|
|
5439
|
+
beginUntrackedTurn(continuationSession, { type: "assistant", parent_tool_use_id: null });
|
|
5440
|
+
if (!continuationSession.turn?.koreanRequest
|
|
5441
|
+
|| normalizeProgressText(continuationSession.turn, "Now continue the implementation.") !== "") {
|
|
5442
|
+
throw new Error("Claude automatic turn lost Korean progress filtering");
|
|
5443
|
+
}
|
|
5444
|
+
beginTurn(continuationSession, [{ type: "text", text: "Update the provider menu" }]);
|
|
5445
|
+
continuationSession.turn = null;
|
|
5446
|
+
continuationSession.automaticTurnsPending = 1;
|
|
5447
|
+
beginUntrackedTurn(continuationSession, { type: "assistant", parent_tool_use_id: null });
|
|
5448
|
+
if (continuationSession.turn?.koreanRequest
|
|
5449
|
+
|| normalizeProgressText(continuationSession.turn, "Now continue the implementation.") === "") {
|
|
5450
|
+
throw new Error("Claude automatic turn changed English progress filtering");
|
|
5451
|
+
}
|
|
5285
5452
|
const contextCaptured = [];
|
|
5286
5453
|
process.stdout.write = (chunk) => {
|
|
5287
5454
|
contextCaptured.push(String(chunk));
|
|
@@ -5406,6 +5573,58 @@ async function runSelfTest() {
|
|
|
5406
5573
|
if (keptStarted !== 0 || keptCompleted?.params?.item?.text !== "타일 보기 로직을 고쳤습니다.") {
|
|
5407
5574
|
throw new Error(`Claude held Korean text self-test failed: ${JSON.stringify(keptEvents)}`);
|
|
5408
5575
|
}
|
|
5576
|
+
const englishLineEvents = async (next) => {
|
|
5577
|
+
const captured = [];
|
|
5578
|
+
process.stdout.write = (chunk) => {
|
|
5579
|
+
captured.push(String(chunk));
|
|
5580
|
+
return true;
|
|
5581
|
+
};
|
|
5582
|
+
try {
|
|
5583
|
+
openingSession.streamBlocks.clear();
|
|
5584
|
+
await processStreamEvent(openingSession, {
|
|
5585
|
+
event: { type: "content_block_start", index: 0, content_block: { type: "text" } },
|
|
5586
|
+
});
|
|
5587
|
+
await processStreamEvent(openingSession, {
|
|
5588
|
+
event: { type: "content_block_delta", index: 0, delta: { text: "Bundle is ready; running the upload script." } },
|
|
5589
|
+
});
|
|
5590
|
+
await processStreamEvent(openingSession, { event: { type: "content_block_stop", index: 0 } });
|
|
5591
|
+
await processStreamEvent(openingSession, { event: next });
|
|
5592
|
+
} finally {
|
|
5593
|
+
process.stdout.write = stdoutWrite;
|
|
5594
|
+
}
|
|
5595
|
+
return captured.join("").trim().split("\n").filter(Boolean).map((line) => JSON.parse(line));
|
|
5596
|
+
};
|
|
5597
|
+
const beforeTool = await englishLineEvents({ type: "content_block_start", index: 1, content_block: { type: "tool_use", id: "t", name: "Read" } });
|
|
5598
|
+
const atEnd = await englishLineEvents({ type: "message_stop" });
|
|
5599
|
+
const endText = atEnd.find((event) => event.method === "item/completed")?.params?.item?.text;
|
|
5600
|
+
if (beforeTool.length || endText !== "Bundle is ready; running the upload script.") {
|
|
5601
|
+
throw new Error(`Claude English progress line self-test failed: ${JSON.stringify({ beforeTool, atEnd })}`);
|
|
5602
|
+
}
|
|
5603
|
+
const progressEvents = [];
|
|
5604
|
+
process.stdout.write = (chunk) => {
|
|
5605
|
+
progressEvents.push(String(chunk));
|
|
5606
|
+
return true;
|
|
5607
|
+
};
|
|
5608
|
+
try {
|
|
5609
|
+
openingSession.streamBlocks.clear();
|
|
5610
|
+
for (const event of [
|
|
5611
|
+
{ type: "message_start", message: { model: "claude-opus-5-5" } },
|
|
5612
|
+
{ type: "content_block_start", index: 0, content_block: { type: "thinking" } },
|
|
5613
|
+
{ type: "content_block_delta", index: 0, delta: { thinking: "노트 초안 6항목을 정리했습니다." } },
|
|
5614
|
+
{ type: "content_block_stop", index: 0 },
|
|
5615
|
+
{ type: "content_block_start", index: 1, content_block: { type: "tool_use", id: "q", name: "AskUserQuestion" } },
|
|
5616
|
+
]) await processStreamEvent(openingSession, { event });
|
|
5617
|
+
} finally {
|
|
5618
|
+
process.stdout.write = stdoutWrite;
|
|
5619
|
+
openingSession.streamModel = undefined;
|
|
5620
|
+
}
|
|
5621
|
+
const progressItems = progressEvents.join("").trim().split("\n").filter(Boolean).map((line) => JSON.parse(line))
|
|
5622
|
+
.filter((event) => event.method === "item/completed").map((event) => event.params.item);
|
|
5623
|
+
if (progressItems.find((item) => item.type === "reasoning")?.summary?.join("")
|
|
5624
|
+
|| progressItems.find((item) => item.type === "agentMessage")?.text !== "노트 초안 6항목을 정리했습니다.") {
|
|
5625
|
+
throw new Error(`Claude progress update self-test failed: ${JSON.stringify(progressItems)}`);
|
|
5626
|
+
}
|
|
5627
|
+
if (!isKoreanPrompt([{ type: "text", text: "ㅇㅋ" }])) throw new Error("Claude jamo prompt self-test failed");
|
|
5409
5628
|
const explicitSkillContent = await inputContent([
|
|
5410
5629
|
{ type: "text", text: "$debug investigate" },
|
|
5411
5630
|
{ type: "skill", name: "debug", path: "claude-command://debug" },
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "devez-vibe",
|
|
3
|
-
"version": "1.9.
|
|
3
|
+
"version": "1.9.25",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Stable terminal UI for Codex and Claude Agent SDK",
|
|
6
6
|
"keywords": [
|
|
@@ -45,6 +45,6 @@
|
|
|
45
45
|
"postinstall": "node install-skills.mjs && node prepare-search-deps.mjs"
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
|
-
"@anthropic-ai/claude-agent-sdk": "0.3.
|
|
48
|
+
"@anthropic-ai/claude-agent-sdk": "0.3.280"
|
|
49
49
|
}
|
|
50
50
|
}
|