devez-vibe 1.9.39 → 1.9.40

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
@@ -1857,11 +1857,15 @@ function prepareTaskPlanForCreate(tasks, subject) {
1857
1857
  // 한 단계에 걸린 시간은 진행 중으로 바뀐 순간과 완료된 순간의 차이다. 기록에는
1858
1858
  // 항목마다 시각이 남으므로, 다시 읽을 때도 같은 방식으로 되살릴 수 있다.
1859
1859
  function markTaskStatus(task, status, at) {
1860
- if (task.status !== status && at != null) {
1861
- if (status === "in_progress") {
1862
- task.startedAt = task.startedAt ?? at;
1863
- } else if (status === "completed" && task.startedAt != null) {
1864
- task.elapsedMs = Math.max(0, at - task.startedAt);
1860
+ if (task.status !== status) {
1861
+ if (status === "in_progress") {
1862
+ task.startedAt = at;
1863
+ delete task.elapsedMs;
1864
+ } else if (status === "completed" && task.startedAt != null && at != null) {
1865
+ task.elapsedMs = Math.max(0, at - task.startedAt);
1866
+ } else if (status === "pending") {
1867
+ delete task.startedAt;
1868
+ delete task.elapsedMs;
1865
1869
  }
1866
1870
  }
1867
1871
  task.status = status;
@@ -1983,15 +1987,19 @@ function updatePlanFromToolResult(session, pending, message) {
1983
1987
  const turnId = session.turn?.id;
1984
1988
  const previous = session.tasks;
1985
1989
  session.tasks = new Map();
1986
- for (const task of value.tasks || value) {
1987
- const id = String(task.id);
1988
- session.tasks.set(id, {
1989
- id,
1990
- subject: task.subject || task.description || "작업",
1991
- status: task.status || "pending",
1990
+ for (const task of value.tasks || value) {
1991
+ const id = String(task.id);
1992
+ const restored = {
1993
+ id,
1994
+ subject: task.subject || task.description || "작업",
1995
+ status: previous.get(id)?.status || "pending",
1992
1996
  // 이 턴에서 다룬 적 없는 작업은 이전 턴의 것이므로 원래 turnId를 지켜 준다.
1993
- turnId: previous.get(id)?.turnId ?? turnId,
1994
- });
1997
+ turnId: previous.get(id)?.turnId ?? turnId,
1998
+ startedAt: previous.get(id)?.startedAt,
1999
+ elapsedMs: previous.get(id)?.elapsedMs,
2000
+ };
2001
+ markTaskStatus(restored, task.status || "pending", Date.now());
2002
+ session.tasks.set(id, restored);
1995
2003
  }
1996
2004
  session.tasks = latestTaskPlan(session.tasks);
1997
2005
  emitPlan(session);
@@ -2019,9 +2027,11 @@ function emitPlan(session) {
2019
2027
  notify("turn/plan/updated", {
2020
2028
  threadId: session.id,
2021
2029
  turnId: session.turn?.id,
2022
- plan: [...session.tasks.values()].map((task, index) => ({
2023
- step: numberedTaskSubject(task.subject, index),
2024
- status: planStatus(task.status),
2030
+ plan: [...session.tasks.values()].map((task, index) => ({
2031
+ id: task.id,
2032
+ step: numberedTaskSubject(task.subject, index),
2033
+ status: planStatus(task.status),
2034
+ elapsedMs: task.elapsedMs ?? null,
2025
2035
  })),
2026
2036
  });
2027
2037
  }
@@ -2965,8 +2975,8 @@ function beginUntrackedTurn(session, message) {
2965
2975
  return true;
2966
2976
  }
2967
2977
 
2968
- async function consumeMessage(session, message) {
2969
- adoptSessionId(session, message.session_id);
2978
+ async function consumeMessage(session, message) {
2979
+ adoptSessionId(session, message.session_id);
2970
2980
  beginUntrackedTurn(session, message);
2971
2981
  // A queued SDK response may beat the timer. It already resumes the work.
2972
2982
  if (!message.parent_tool_use_id && (message.type === "assistant" || message.type === "stream_event")) {
@@ -3346,18 +3356,20 @@ function historyState(messages) {
3346
3356
  } else if (pending.name === "TaskList" && Array.isArray(message.tool_use_result?.tasks)) {
3347
3357
  const known = new Map(tasks);
3348
3358
  tasks.clear();
3349
- for (const task of message.tool_use_result.tasks) {
3350
- const previous = known.get(String(task.id));
3351
- tasks.set(String(task.id), {
3359
+ for (const task of message.tool_use_result.tasks) {
3360
+ const previous = known.get(String(task.id));
3361
+ const restored = {
3352
3362
  id: String(task.id),
3353
3363
  subject: task.subject || "작업",
3354
- status: task.status || "pending",
3364
+ status: previous?.status || "pending",
3355
3365
  turnId: previous?.turnId ?? turn.id,
3356
3366
  // A listing restates the plan; it does not re-run it, so the
3357
3367
  // timings already measured for these steps have to survive it.
3358
3368
  startedAt: previous?.startedAt,
3359
3369
  elapsedMs: previous?.elapsedMs,
3360
- });
3370
+ };
3371
+ markTaskStatus(restored, task.status || "pending", messageTime(message));
3372
+ tasks.set(String(task.id), restored);
3361
3373
  }
3362
3374
  const current = latestTaskPlan(tasks);
3363
3375
  tasks.clear();
@@ -3380,8 +3392,9 @@ function historyState(messages) {
3380
3392
  const text = [...tasks.values()].map((task, index) => `${task.status === "completed" ? "✓" : task.status === "in_progress" ? "▸" : "□"} ${numberedTaskSubject(task.subject, index)}`).join("\n");
3381
3393
  // The text alone cannot say how long a step took, so the measured times ride
3382
3394
  // alongside it and the restored plan shows its total instead of zero.
3383
- const steps = [...tasks.values()].map((task, index) => ({
3384
- step: numberedTaskSubject(task.subject, index),
3395
+ const steps = [...tasks.values()].map((task, index) => ({
3396
+ id: task.id,
3397
+ step: numberedTaskSubject(task.subject, index),
3385
3398
  status: task.status || "pending",
3386
3399
  elapsedMs: task.elapsedMs ?? null,
3387
3400
  }));
@@ -4447,10 +4460,35 @@ async function runSelfTest() {
4447
4460
  at(taskUse("timed-done", "timed-done", "TaskUpdate", { taskId: "t1", status: "completed" }), "2026-08-07T01:00:09.000Z"),
4448
4461
  ];
4449
4462
  const timedPlan = historyState(timedMessages).turns.at(-1)?.items.find((item) => item.type === "plan");
4450
- if (timedPlan?.steps?.[0]?.elapsedMs !== 6000 || timedPlan.steps[0].status !== "completed") {
4451
- throw new Error(`Claude plan step timing self-test failed: ${JSON.stringify(timedPlan)}`);
4452
- }
4453
- const skippedTasks = new Map([
4463
+ if (timedPlan?.steps?.[0]?.elapsedMs !== 6000 || timedPlan.steps[0].status !== "completed") {
4464
+ throw new Error(`Claude plan step timing self-test failed: ${JSON.stringify(timedPlan)}`);
4465
+ }
4466
+ const timedLiveSession = {
4467
+ id: "timed-live-plan",
4468
+ turn: { id: "timed-live-turn" },
4469
+ tasks: historyState(timedMessages).tasks,
4470
+ };
4471
+ const timedLiveEvents = [];
4472
+ const timedLiveWrite = process.stdout.write;
4473
+ process.stdout.write = (chunk) => { timedLiveEvents.push(JSON.parse(String(chunk))); return true; };
4474
+ try {
4475
+ updatePlanFromToolResult(timedLiveSession, { name: "TaskList" }, {
4476
+ tool_use_result: { tasks: [{ id: "t1", subject: "1. 확인한 설정", status: "completed" }] },
4477
+ });
4478
+ } finally {
4479
+ process.stdout.write = timedLiveWrite;
4480
+ }
4481
+ const liveMeasured = timedLiveEvents.at(-1)?.params?.plan?.[0];
4482
+ if (liveMeasured?.elapsedMs !== 6000 || timedLiveSession.tasks.get("t1")?.elapsedMs !== 6000) {
4483
+ throw new Error(`Claude live plan timing self-test failed: ${JSON.stringify(liveMeasured)}`);
4484
+ }
4485
+ const restartedTask = { status: "pending" };
4486
+ markTaskStatus(restartedTask, "in_progress", 1000);
4487
+ markTaskStatus(restartedTask, "completed", 7000);
4488
+ markTaskStatus(restartedTask, "in_progress", 9000);
4489
+ markTaskStatus(restartedTask, "completed", 10000);
4490
+ if (restartedTask.elapsedMs !== 1000) throw new Error("Claude restarted task timing self-test failed");
4491
+ const skippedTasks = new Map([
4454
4492
  ["1", { id: "1", subject: "1. 조사", status: "pending" }],
4455
4493
  ["2", { id: "2", subject: "2. 분석", status: "pending" }],
4456
4494
  ["3", { id: "3", subject: "3. 검증", status: "pending" }],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "devez-vibe",
3
- "version": "1.9.39",
3
+ "version": "1.9.40",
4
4
  "type": "module",
5
5
  "description": "Stable terminal UI for Codex and Claude Agent SDK",
6
6
  "keywords": [