devez-vibe 1.9.57 → 1.9.59

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
@@ -2667,6 +2667,7 @@ function keepBackgroundSubagent(session, toolUseId, result) {
2667
2667
  }
2668
2668
  if (running.taskId) {
2669
2669
  session.knownSubagents.set(running.taskId, {
2670
+ rowId: running.id,
2670
2671
  name: running.name,
2671
2672
  description: running.description,
2672
2673
  backgroundTask: running.backgroundTask === true,
@@ -2685,8 +2686,9 @@ function resumeBackgroundSubagent(session, toolUseId, pending, result) {
2685
2686
  if (existing) return true;
2686
2687
  const known = session.knownSubagents.get(taskId);
2687
2688
  const input = pending?.input || {};
2689
+ // 끝난 줄과 같은 id로 돌아와야 호스트가 같은 작업의 idle 줄과 실행 줄을 함께 세우지 않는다.
2688
2690
  const running = {
2689
- id: toolUseId,
2691
+ id: known?.rowId || toolUseId,
2690
2692
  toolUseId,
2691
2693
  taskId,
2692
2694
  background: true,
@@ -2696,8 +2698,9 @@ function resumeBackgroundSubagent(session, toolUseId, pending, result) {
2696
2698
  startedAt: Date.now(),
2697
2699
  lastSeenAt: Date.now(),
2698
2700
  };
2699
- session.subagents.set(toolUseId, running);
2701
+ session.subagents.set(running.id, running);
2700
2702
  session.knownSubagents.set(taskId, {
2703
+ rowId: running.id,
2701
2704
  name: running.name,
2702
2705
  description: running.description,
2703
2706
  backgroundTask: false,
@@ -2758,14 +2761,16 @@ function upsertStructuredSubagent(session, message) {
2758
2761
  if (!running && !subagentType && !isSubagentTaskType(message.task_type)) return null;
2759
2762
  if (!running) {
2760
2763
  const known = taskId ? session.knownSubagents.get(taskId) : null;
2761
- const id = toolUseId || `task:${taskId}`;
2764
+ // 백그라운드 명령이 끝나 SDK가 같은 작업을 다시 돌리면, 끝난 줄과 같은 id·이름·설명으로
2765
+ // 이어 붙여 설명 없는 새 줄이 따로 서지 않게 한다.
2766
+ const id = known?.rowId || toolUseId || `task:${taskId}`;
2762
2767
  running = {
2763
2768
  id,
2764
2769
  toolUseId,
2765
2770
  taskId,
2766
- background: false,
2767
- name: subagentType || known?.name || "agent",
2768
- description: "",
2771
+ background: message.is_backgrounded === true,
2772
+ name: known?.name || subagentType || "agent",
2773
+ description: known?.description || "",
2769
2774
  tool: "",
2770
2775
  startedAt: Date.now(),
2771
2776
  lastSeenAt: Date.now(),
@@ -2774,6 +2779,8 @@ function upsertStructuredSubagent(session, message) {
2774
2779
  }
2775
2780
  if (toolUseId) running.toolUseId = toolUseId;
2776
2781
  if (taskId) running.taskId = taskId;
2782
+ const runId = firstLine(message.run_id || "", 80);
2783
+ if (runId && !isLateRun(running, runId)) running.runId = runId;
2777
2784
  // 스크립트 Monitor는 local_bash 작업으로 와서 스냅숏 이름이 Bash가 된다.
2778
2785
  if (session.tools?.get(toolUseId)?.name === "Monitor") running.name = "Monitor";
2779
2786
  const parentTaskId = firstLine(message.parent_task_id || "", 80);
@@ -2783,9 +2790,12 @@ function upsertStructuredSubagent(session, message) {
2783
2790
  if (subagentType || isSubagentTaskType(message.task_type)) running.backgroundTask = false;
2784
2791
  running.lastSeenAt = Date.now();
2785
2792
  if (running.taskId) {
2793
+ const known = session.knownSubagents.get(running.taskId);
2786
2794
  session.knownSubagents.set(running.taskId, {
2795
+ rowId: running.id,
2787
2796
  name: running.name,
2788
- description: running.description,
2797
+ // 진행 요약으로 바뀐 설명이 아니라 호출 설명을 기억해, 다시 실행된 줄이 그 설명으로 돌아오게 한다.
2798
+ description: known?.description || running.description,
2789
2799
  backgroundTask: running.backgroundTask === true,
2790
2800
  });
2791
2801
  }
@@ -2796,6 +2806,12 @@ function hasLiveChildren(session, taskId) {
2796
2806
  return !!taskId && [...session.subagents.values()].some((agent) => agent.parentTaskId === taskId);
2797
2807
  }
2798
2808
 
2809
+ // 같은 작업의 실행 id는 열린 순서대로 문자열 정렬된다. 지금 행보다 앞선 실행의
2810
+ // 통지는 늦게 도착한 것이라 새 실행의 행을 지우면 안 된다.
2811
+ function isLateRun(running, runId) {
2812
+ return Boolean(runId && running.runId && runId < running.runId);
2813
+ }
2814
+
2799
2815
  function finishStructuredSubagent(session, message) {
2800
2816
  const taskId = firstLine(message.task_id || "", 80);
2801
2817
  const toolUseId = firstLine(message.tool_use_id || "", 80);
@@ -2810,7 +2826,8 @@ function finishStructuredSubagent(session, message) {
2810
2826
  || (taskId && findSubagent(session, taskId));
2811
2827
  if (!running) return wasHidden;
2812
2828
  // 자기 백그라운드 명령을 기다리며 멈춘 에이전트는 그 명령이 끝나면 다시 돌므로 행을 남긴다.
2813
- if (hasLiveChildren(session, running.taskId)) return true;
2829
+ if (hasLiveChildren(session, running.taskId)
2830
+ || isLateRun(running, firstLine(message.run_id || "", 80))) return true;
2814
2831
  session.subagents.delete(running.id);
2815
2832
  emitSubagents(session);
2816
2833
  return true;
@@ -2847,12 +2864,12 @@ function syncBackgroundSubagents(session, tasks) {
2847
2864
  const known = session.knownSubagents.get(taskId);
2848
2865
  if (!running) {
2849
2866
  running = {
2850
- id: `task:${taskId}`,
2867
+ id: known?.rowId || `task:${taskId}`,
2851
2868
  toolUseId: "",
2852
2869
  taskId,
2853
2870
  background: true,
2854
2871
  name: known?.name || backgroundTaskName(task?.task_type),
2855
- description: "",
2872
+ description: known?.description || "",
2856
2873
  tool: "",
2857
2874
  command: !isSubagentTaskType(task?.task_type),
2858
2875
  // 셸로 띄운 위임 에이전트도 있으므로 command와 별도로 표시 종류를 보존한다.
@@ -3125,6 +3142,7 @@ function taskNotifications(message) {
3125
3142
  toolUseId: notificationTag(body, "tool-use-id"),
3126
3143
  status: notificationTag(body, "status"),
3127
3144
  summary: notificationTag(body, "summary"),
3145
+ runId: firstLine(message.origin?.runId || "", 80),
3128
3146
  });
3129
3147
  }
3130
3148
  return notifications;
@@ -3152,7 +3170,8 @@ function finishNotifiedSubagents(session, notifications) {
3152
3170
  .find((agent) => agent.background && agent.name === name);
3153
3171
  }
3154
3172
  }
3155
- if (!running) continue;
3173
+ if (!running || isLateRun(running, notification.runId)
3174
+ || hasLiveChildren(session, running.taskId)) continue;
3156
3175
  session.subagents.delete(running.id);
3157
3176
  emitSubagents(session);
3158
3177
  }
@@ -6006,8 +6025,10 @@ async function runSelfTest() {
6006
6025
  message: { content: [{ type: "tool_result", tool_use_id: "toolu_2", content: "resumed" }] },
6007
6026
  tool_use_result: { success: true, resumedAgentId: "agent-1" },
6008
6027
  });
6009
- const resumed = lifecycleSession.subagents.get("toolu_2");
6010
- if (!resumed?.background || resumed.taskId !== "agent-1" || resumed.name !== "Explore") {
6028
+ // 재개된 줄은 끝난 줄과 같은 id(처음 Agent 호출 id)로 돌아오고, SendMessage 호출 id로도 찾힌다.
6029
+ const resumed = lifecycleSession.subagents.get("toolu_1");
6030
+ if (!resumed?.background || resumed.taskId !== "agent-1" || resumed.name !== "Explore"
6031
+ || findSubagent(lifecycleSession, "toolu_2") !== resumed) {
6011
6032
  throw new Error(`Claude resumed subagent self-test failed: ${JSON.stringify(resumed)}`);
6012
6033
  }
6013
6034
  finishTurn(lifecycleSession, null, 1);
@@ -6157,31 +6178,68 @@ async function runSelfTest() {
6157
6178
  }
6158
6179
  clearSubagents(nestedSession);
6159
6180
  // 자기 백그라운드 명령을 기다리며 멈춘 에이전트는 그 명령이 도는 동안 행을 지킨다.
6160
- processSubagentSystemMessage(nestedSession, {
6161
- type: "system",
6162
- subtype: "task_started",
6163
- task_id: "agent-wait",
6164
- tool_use_id: "toolu_wait",
6165
- task_type: "local_agent",
6166
- subagent_type: "general-purpose",
6167
- });
6168
- nestedSession.subagents.get("toolu_wait").background = true;
6181
+ // SDK 순서(접수 결과, task_started, 스냅숏, task_updated·task_notification, 스냅숏,
6182
+ // XML 통지)를 그대로 재생하고, 다시 실행된 뒤 늦게 온 첫 실행 통지도 무시한다.
6169
6183
  const waitSnapshot = (tasks) => processSubagentSystemMessage(nestedSession, {
6170
6184
  type: "system",
6171
6185
  subtype: "background_tasks_changed",
6172
6186
  tasks,
6173
6187
  });
6174
- waitSnapshot([{ task_id: "shell-wait", task_type: "local_bash", description: "sleep 300", parent_task_id: "agent-wait" }]);
6175
- processSubagentSystemMessage(nestedSession, {
6188
+ const waitStructured = (subtype, extra) => processSubagentSystemMessage(nestedSession, {
6176
6189
  type: "system",
6177
- subtype: "task_notification",
6190
+ subtype,
6178
6191
  task_id: "agent-wait",
6179
- status: "completed",
6192
+ ...extra,
6180
6193
  });
6194
+ const waitXml = (runId) => finishNotifiedSubagents(nestedSession, taskNotifications({
6195
+ origin: { kind: "task-notification", runId },
6196
+ message: { content: [{ type: "text", text: "<task-notification><task-id>agent-wait</task-id><status>completed</status><summary>Agent \"general-purpose\" finished</summary></task-notification>" }] },
6197
+ }));
6198
+ const agentRow = { task_id: "agent-wait", task_type: "local_agent", description: "wait" };
6199
+ const shellRow = { task_id: "shell-wait", task_type: "local_bash", description: "sleep 300", parent_task_id: "agent-wait" };
6200
+ startSubagent(nestedSession, { id: "toolu_wait", input: { description: "대기", subagent_type: "general-purpose" } });
6201
+ keepBackgroundSubagent(nestedSession, "toolu_wait", { isAsync: true, agentId: "agent-wait" });
6202
+ waitStructured("task_started", { tool_use_id: "toolu_wait", task_type: "local_agent", is_backgrounded: true, run_id: "r1" });
6203
+ waitSnapshot([agentRow, shellRow]);
6204
+ waitStructured("task_updated", { patch: { status: "completed" } });
6205
+ waitStructured("task_notification", { status: "completed", run_id: "r1" });
6206
+ waitSnapshot([shellRow]);
6207
+ waitXml("r1");
6181
6208
  const waitingKept = nestedSession.subagents.has("toolu_wait");
6182
6209
  waitSnapshot([]);
6183
- if (!waitingKept || nestedSession.subagents.size !== 0) {
6184
- throw new Error(`Claude waiting subagent self-test failed: ${waitingKept} ${nestedSession.subagents.size}`);
6210
+ const settledAfterChild = !nestedSession.subagents.has("toolu_wait");
6211
+ waitStructured("task_started", { task_type: "local_agent", is_backgrounded: true, run_id: "r2" });
6212
+ waitXml("r1");
6213
+ const keptAgainstLate = nestedSession.subagents.has("toolu_wait");
6214
+ waitStructured("task_notification", { status: "completed", run_id: "r2" });
6215
+ if (!waitingKept || !settledAfterChild || !keptAgainstLate || nestedSession.subagents.size !== 0) {
6216
+ throw new Error(`Claude waiting subagent self-test failed: ${waitingKept} ${settledAfterChild} ${keptAgainstLate} ${nestedSession.subagents.size}`);
6217
+ }
6218
+ clearSubagents(nestedSession);
6219
+ // 끝난 뒤 SDK가 같은 작업을 다시 돌리면 설명 없는 새 줄이 아니라 같은 줄로 돌아온다.
6220
+ startSubagent(nestedSession, { id: "toolu_again", input: { description: "3분 대기", subagent_type: "general-purpose" } });
6221
+ keepBackgroundSubagent(nestedSession, "toolu_again", { isAsync: true, agentId: "agent-again" });
6222
+ // 진행 요약이 설명을 바꿔도 다시 실행된 줄은 호출 설명으로 돌아온다.
6223
+ for (let tick = 0; tick < 2; tick += 1) {
6224
+ processSubagentSystemMessage(nestedSession, {
6225
+ type: "system",
6226
+ subtype: "task_progress",
6227
+ task_id: "agent-again",
6228
+ summary: "로그를 살피는 중",
6229
+ });
6230
+ }
6231
+ waitSnapshot([]);
6232
+ processSubagentSystemMessage(nestedSession, {
6233
+ type: "system",
6234
+ subtype: "task_started",
6235
+ task_id: "agent-again",
6236
+ task_type: "local_agent",
6237
+ subagent_type: "general-purpose",
6238
+ is_backgrounded: true,
6239
+ });
6240
+ const again = [...nestedSession.subagents.values()];
6241
+ if (again.length !== 1 || again[0].id !== "toolu_again" || again[0].description !== "3분 대기") {
6242
+ throw new Error(`Claude resumed subagent row self-test failed: ${JSON.stringify(again)}`);
6185
6243
  }
6186
6244
  clearSubagents(nestedSession);
6187
6245
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "devez-vibe",
3
- "version": "1.9.57",
3
+ "version": "1.9.59",
4
4
  "type": "module",
5
5
  "description": "Stable terminal UI for Codex and Claude Agent SDK",
6
6
  "keywords": [