devez-vibe 1.2.34 → 1.2.36

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 CHANGED
Binary file
@@ -55,9 +55,9 @@ class AsyncQueue {
55
55
  }
56
56
  }
57
57
 
58
- function write(message) {
59
- process.stdout.write(`${JSON.stringify(message)}\n`);
60
- }
58
+ function write(message) {
59
+ process.stdout.write(`${JSON.stringify(message)}\n`, "utf8");
60
+ }
61
61
 
62
62
  function notify(method, params) {
63
63
  write({ method, params });
@@ -70,24 +70,34 @@ function rpcError(error) {
70
70
  };
71
71
  }
72
72
 
73
- function hostRequest(method, params, signal) {
74
- const id = `claude-host-${nextHostRequest++}`;
75
- return new Promise((resolve, reject) => {
73
+ function hostRequest(method, params, signal) {
74
+ const id = `claude-host-${nextHostRequest++}`;
75
+ return new Promise((resolve, reject) => {
76
76
  const abort = () => {
77
77
  pendingHostRequests.delete(id);
78
78
  reject(new Error("사용자 입력 요청이 취소되었습니다."));
79
79
  };
80
80
  if (signal?.aborted) return abort();
81
81
  signal?.addEventListener("abort", abort, { once: true });
82
- pendingHostRequests.set(id, {
83
- resolve: (value) => {
84
- signal?.removeEventListener("abort", abort);
85
- resolve(value);
86
- },
87
- });
88
- write({ id, method, params });
89
- });
90
- }
82
+ pendingHostRequests.set(id, {
83
+ resolve: (value) => {
84
+ signal?.removeEventListener("abort", abort);
85
+ resolve(value);
86
+ },
87
+ reject: (error) => {
88
+ signal?.removeEventListener("abort", abort);
89
+ reject(error);
90
+ },
91
+ });
92
+ try {
93
+ write({ id, method, params });
94
+ } catch (error) {
95
+ if (!pendingHostRequests.delete(id)) return;
96
+ signal?.removeEventListener("abort", abort);
97
+ reject(error);
98
+ }
99
+ });
100
+ }
91
101
 
92
102
  function sanitizedEnvironment() {
93
103
  const env = { ...process.env };
@@ -356,11 +366,29 @@ async function requestToolPermission(toolName, input, permission) {
356
366
  isOther: true,
357
367
  multiSelect: Boolean(question.multiSelect),
358
368
  }));
359
- const response = await hostRequest(
360
- "item/tool/requestUserInput",
361
- { questions },
362
- permission.signal,
363
- );
369
+ const requestQuestions = () => hostRequest(
370
+ "item/tool/requestUserInput",
371
+ // Keep model-provided text out of the NDJSON envelope. This protects
372
+ // Korean text, backslashes, and malformed Unicode from a partial escape
373
+ // corrupting the request line before the host can open its dialog.
374
+ {
375
+ encoding: "base64-json",
376
+ payload: Buffer.from(JSON.stringify({ questions }), "utf8").toString("base64"),
377
+ },
378
+ permission.signal,
379
+ );
380
+ let response;
381
+ try {
382
+ response = await requestQuestions();
383
+ } catch (error) {
384
+ if (!isQuestionDeliveryError(error)) throw error;
385
+ try {
386
+ response = await requestQuestions();
387
+ } catch (retryError) {
388
+ if (!isQuestionDeliveryError(retryError)) throw retryError;
389
+ return { behavior: "deny", message: questionFallbackMessage(questions) };
390
+ }
391
+ }
364
392
  const answers = {};
365
393
  for (let index = 0; index < questions.length; index += 1) {
366
394
  const selected = response?.answers?.[`q${index}`]?.answers;
@@ -418,9 +446,28 @@ async function requestToolPermission(toolName, input, permission) {
418
446
  ? { updatedPermissions: permission.suggestions }
419
447
  : {}),
420
448
  };
421
- }
422
-
423
- async function createSession(params, resumeId) {
449
+ }
450
+
451
+ function isQuestionDeliveryError(error) {
452
+ return error?.code === -32700
453
+ || String(error?.message || error).includes("사용자 입력 화면에 전달하지 못했습니다");
454
+ }
455
+
456
+ function questionFallbackMessage(questions) {
457
+ const text = questions.map((question, index) => {
458
+ const options = question.options
459
+ .map((option, optionIndex) => `${optionIndex + 1}. ${option.label}`)
460
+ .join(" / ");
461
+ return `${index + 1}. ${question.question}${options ? ` (${options})` : ""}`;
462
+ }).join("\n");
463
+ return [
464
+ "사용자 입력 창을 두 번 표시하지 못했습니다.",
465
+ "도구를 다시 호출하지 말고 다음 질문을 일반 텍스트로 사용자에게 제시한 뒤 답변을 기다리세요:",
466
+ text,
467
+ ].join("\n");
468
+ }
469
+
470
+ async function createSession(params, resumeId) {
424
471
  const id = resumeId || randomUUID();
425
472
  const queue = new AsyncQueue();
426
473
  const session = {
@@ -2420,14 +2467,20 @@ lines.on("line", async (line) => {
2420
2467
  write({ method: "warning", params: { provider: "Claude", message: `Claude 브리지 JSON 해석 실패: ${error.message}` } });
2421
2468
  return;
2422
2469
  }
2423
- if (typeof message.id === "string" && ("result" in message || "error" in message)) {
2424
- const pending = pendingHostRequests.get(message.id);
2425
- if (pending) {
2426
- pendingHostRequests.delete(message.id);
2427
- pending.resolve(message.result ?? { decision: "decline" });
2428
- }
2429
- return;
2430
- }
2470
+ if (typeof message.id === "string" && ("result" in message || "error" in message)) {
2471
+ const pending = pendingHostRequests.get(message.id);
2472
+ if (pending) {
2473
+ pendingHostRequests.delete(message.id);
2474
+ if (message.error) {
2475
+ const error = new Error(message.error.message || "호스트 요청이 거부되었습니다.");
2476
+ error.code = message.error.code;
2477
+ pending.reject(error);
2478
+ } else {
2479
+ pending.resolve(message.result ?? { decision: "decline" });
2480
+ }
2481
+ }
2482
+ return;
2483
+ }
2431
2484
  if (typeof message.id !== "number" || typeof message.method !== "string") return;
2432
2485
  try { write({ id: message.id, result: await dispatch(message.method, message.params) }); }
2433
2486
  catch (error) { write({ id: message.id, error: rpcError(error) }); }
package/package.json CHANGED
@@ -1,41 +1,41 @@
1
- {
2
- "name": "devez-vibe",
3
- "version": "1.2.34",
4
- "description": "Stable terminal UI for Codex and Claude Agent SDK",
5
- "keywords": [
6
- "codex",
7
- "cli",
8
- "tui",
9
- "terminal",
10
- "app-server",
11
- "claude-agent-sdk"
12
- ],
13
- "homepage": "https://github.com/MrHoje/Devez-vibe#readme",
14
- "bugs": "https://github.com/MrHoje/Devez-vibe/issues",
15
- "repository": {
16
- "type": "git",
17
- "url": "git+https://github.com/MrHoje/Devez-vibe.git"
18
- },
19
- "license": "MIT",
20
- "bin": {
21
- "dvz": "bin/dvz.exe"
22
- },
23
- "files": [
24
- "bin/dvz.exe",
25
- "bridge/claude-agent-sdk-bridge.mjs",
26
- "README.md",
27
- "LICENSE"
28
- ],
29
- "os": [
30
- "win32"
31
- ],
32
- "cpu": [
33
- "x64"
34
- ],
35
- "engines": {
36
- "node": ">=18"
37
- },
38
- "dependencies": {
39
- "@anthropic-ai/claude-agent-sdk": "0.3.223"
40
- }
41
- }
1
+ {
2
+ "name": "devez-vibe",
3
+ "version": "1.2.36",
4
+ "description": "Stable terminal UI for Codex and Claude Agent SDK",
5
+ "keywords": [
6
+ "codex",
7
+ "cli",
8
+ "tui",
9
+ "terminal",
10
+ "app-server",
11
+ "claude-agent-sdk"
12
+ ],
13
+ "homepage": "https://github.com/MrHoje/Devez-vibe#readme",
14
+ "bugs": "https://github.com/MrHoje/Devez-vibe/issues",
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "git+https://github.com/MrHoje/Devez-vibe.git"
18
+ },
19
+ "license": "MIT",
20
+ "bin": {
21
+ "dvz": "bin/dvz.exe"
22
+ },
23
+ "files": [
24
+ "bin/dvz.exe",
25
+ "bridge/claude-agent-sdk-bridge.mjs",
26
+ "README.md",
27
+ "LICENSE"
28
+ ],
29
+ "os": [
30
+ "win32"
31
+ ],
32
+ "cpu": [
33
+ "x64"
34
+ ],
35
+ "engines": {
36
+ "node": ">=18"
37
+ },
38
+ "dependencies": {
39
+ "@anthropic-ai/claude-agent-sdk": "0.3.223"
40
+ }
41
+ }