devez-vibe 1.2.2 → 1.2.4

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
@@ -465,7 +465,14 @@ async function createSession(params, resumeId) {
465
465
  throw error;
466
466
  }
467
467
  session.models = Array.isArray(initialization.models) ? initialization.models : [];
468
- session.effort = supportedEffort(modelCapabilities(session.models, params.model), params.effort);
468
+ const capabilities = modelCapabilities(session.models, params.model);
469
+ // A resumed session can only name its model as the resolved id the transcript
470
+ // recorded (`claude-opus-4-5-...`), which matches nothing in the host's model
471
+ // list. Report the catalog value instead so the picker lands on that model.
472
+ if (capabilities?.value && capabilities.value !== "default") {
473
+ session.model = visibleModel(capabilities.value);
474
+ }
475
+ session.effort = supportedEffort(capabilities, params.effort);
469
476
  const account = initialization.account || await safeAccount(agentQuery);
470
477
  const usage = await safeUsage(agentQuery);
471
478
  return { session, initialization, account, usage };
@@ -703,15 +710,24 @@ function updatePlanFromToolResult(session, pending, message) {
703
710
  session.tasks.set(temporary.id, temporary);
704
711
  }
705
712
  } else if (pending.name === "TaskList" && Array.isArray(value?.tasks || value)) {
706
- session.tasks.clear();
713
+ const turnId = session.turn?.id;
714
+ const previous = session.tasks;
715
+ session.tasks = new Map();
707
716
  for (const task of value.tasks || value) {
708
- session.tasks.set(String(task.id), {
709
- id: String(task.id),
717
+ const id = String(task.id);
718
+ session.tasks.set(id, {
719
+ id,
710
720
  subject: task.subject || task.description || "작업",
711
721
  status: task.status || "pending",
712
- turnId: session.turn?.id,
722
+ // 이 턴에서 다룬 적 없는 작업은 이전 턴의 것이므로 원래 turnId 지켜 준다.
723
+ turnId: previous.get(id)?.turnId ?? turnId,
713
724
  });
714
725
  }
726
+ // TaskList는 세션 전체 목록을 돌려주므로, 지난 턴에 끝난 작업까지 되살아나지 않게 걷어낸다.
727
+ // 다만 전부 걷어내 목록이 비면 계획 카드가 통째로 사라지므로, 그때는 정리를 다음 TaskCreate에 맡긴다.
728
+ const listed = new Map(session.tasks);
729
+ pruneFinishedTasks(session.tasks, turnId);
730
+ if (session.tasks.size === 0) session.tasks = listed;
715
731
  emitPlan(session);
716
732
  }
717
733
  }
@@ -732,6 +748,8 @@ function planStatus(status) {
732
748
  }
733
749
 
734
750
  function emitPlan(session) {
751
+ // 빈 계획을 보내면 화면의 계획 카드가 사라진다. 보여줄 작업이 없을 때는 마지막 계획을 그대로 둔다.
752
+ if (session.tasks.size === 0) return;
735
753
  notify("turn/plan/updated", {
736
754
  threadId: session.id,
737
755
  turnId: session.turn?.id,
@@ -742,9 +760,11 @@ function emitPlan(session) {
742
760
  });
743
761
  }
744
762
 
763
+ // Claude의 Task 번호는 세션 전체에서 누적되므로 모델이 붙인 `4. `를 그대로 쓰면
764
+ // 다음 계획이 4번부터 시작한다. 지금 보여줄 목록 기준으로 항상 1번부터 다시 매긴다.
745
765
  function numberedTaskSubject(subject, index) {
746
- const text = String(subject || "작업").trim();
747
- return /^\d+\.\s/.test(text) ? text : `${index + 1}. ${text}`;
766
+ const text = String(subject || "작업").trim().replace(/^\d+[.)]\s*/, "").trim();
767
+ return `${index + 1}. ${text || "작업"}`;
748
768
  }
749
769
 
750
770
  // 서브에이전트는 자기 메시지를 부모 Task 툴콜의 `parent_tool_use_id`와 함께 흘려보낸다.
@@ -1151,8 +1171,12 @@ function historyTurns(messages) {
1151
1171
  tasks.set(temporary.id, temporary);
1152
1172
  }
1153
1173
  } else if (pending.name === "TaskList" && Array.isArray(message.tool_use_result?.tasks)) {
1174
+ const known = new Map(tasks);
1154
1175
  tasks.clear();
1155
- for (const task of message.tool_use_result.tasks) tasks.set(String(task.id), { id: String(task.id), subject: task.subject || "작업", status: task.status || "pending", turnId: turn.id });
1176
+ for (const task of message.tool_use_result.tasks) tasks.set(String(task.id), { id: String(task.id), subject: task.subject || "작업", status: task.status || "pending", turnId: known.get(String(task.id))?.turnId ?? turn.id });
1177
+ const listed = new Map(tasks);
1178
+ pruneFinishedTasks(tasks, turn.id);
1179
+ if (tasks.size === 0) for (const [id, task] of listed) tasks.set(id, task);
1156
1180
  } else if (pending.item) {
1157
1181
  const output = toolOutput(block.content, message.tool_use_result);
1158
1182
  Object.assign(pending.item, pending.item.type === "commandExecution"
@@ -1215,7 +1239,14 @@ async function dispatch(method, params = {}) {
1215
1239
  if (!info) throw new Error(`Claude 세션을 찾을 수 없습니다: ${id}`);
1216
1240
  const messages = await getSessionMessages(id, { dir: params.cwd, includeSystemMessages: true });
1217
1241
  const lastModel = [...messages].reverse().find((message) => message.type === "assistant")?.message?.model;
1218
- const { session, account, usage } = await createSession({ ...params, cwd: info.cwd || params.cwd, model: params.model || lastModel }, id);
1242
+ // The transcript's own model outranks the host's fallback, which is only what
1243
+ // a new session would have opened on.
1244
+ const { session, account, usage } = await createSession({
1245
+ ...params,
1246
+ cwd: info.cwd || params.cwd,
1247
+ model: params.model || lastModel || params.fallbackModel,
1248
+ effort: params.effort || params.fallbackEffort,
1249
+ }, id);
1219
1250
  const tokenUsage = historyTokenUsage(messages, session.models, session.model);
1220
1251
  // Seed the live session so the next turn keeps reporting a full context.
1221
1252
  session.lastContextUsage = tokenUsage?.last || null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "devez-vibe",
3
- "version": "1.2.2",
3
+ "version": "1.2.4",
4
4
  "description": "Stable terminal UI for Codex and Claude Agent SDK",
5
5
  "keywords": [
6
6
  "codex",