devez-vibe 1.6.3 → 1.6.5

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/LICENSE CHANGED
@@ -1,33 +1,33 @@
1
- DevezCode source code license
2
-
3
- The MIT License below applies only to the DevezCode source code and to files
4
- that are not listed in the exception below.
5
-
6
- Exceptions:
7
-
8
- * `Resources/Images/FileTypes/` and `Resources/Licenses/` are Microsoft Visual
9
- Studio Image Library materials, subject to the included Microsoft terms.
10
- * `Resources/Images/ShellPresets/` contains third-party brand assets, subject
11
- to their respective owners' terms.
12
-
13
- MIT License
14
-
15
- Copyright (c) 2026 DevezCode contributors
16
-
17
- Permission is hereby granted, free of charge, to any person obtaining a copy
18
- of this software and associated documentation files (the "Software"), to deal
19
- in the Software without restriction, including without limitation the rights
20
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
21
- copies of the Software, and to permit persons to whom the Software is
22
- furnished to do so, subject to the following conditions:
23
-
24
- The above copyright notice and this permission notice shall be included in all
25
- copies or substantial portions of the Software.
26
-
27
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
28
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
29
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
30
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
31
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
32
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
33
- SOFTWARE.
1
+ DevezCode source code license
2
+
3
+ The MIT License below applies only to the DevezCode source code and to files
4
+ that are not listed in the exception below.
5
+
6
+ Exceptions:
7
+
8
+ * `Resources/Images/FileTypes/` and `Resources/Licenses/` are Microsoft Visual
9
+ Studio Image Library materials, subject to the included Microsoft terms.
10
+ * `Resources/Images/ShellPresets/` contains third-party brand assets, subject
11
+ to their respective owners' terms.
12
+
13
+ MIT License
14
+
15
+ Copyright (c) 2026 DevezCode contributors
16
+
17
+ Permission is hereby granted, free of charge, to any person obtaining a copy
18
+ of this software and associated documentation files (the "Software"), to deal
19
+ in the Software without restriction, including without limitation the rights
20
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
21
+ copies of the Software, and to permit persons to whom the Software is
22
+ furnished to do so, subject to the following conditions:
23
+
24
+ The above copyright notice and this permission notice shall be included in all
25
+ copies or substantial portions of the Software.
26
+
27
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
28
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
29
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
30
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
31
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
32
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
33
+ SOFTWARE.
package/bin/dvz.exe CHANGED
Binary file
@@ -1061,22 +1061,27 @@ async function createSession(params, resumeId) {
1061
1061
  planCreatePending: false,
1062
1062
  subagents: new Map(),
1063
1063
  knownSubagents: new Map(),
1064
+ hiddenSubagentTasks: new Set(),
1065
+ subagentPulse: null,
1064
1066
  lastContextUsage: null,
1065
1067
  lastContextWindow: 0,
1066
1068
  };
1067
1069
  const agentQuery = await startAgentQuery(queue, makeOptions(params, id, resumeId));
1068
1070
  session.query = agentQuery;
1069
1071
  sessions.set(id, session);
1070
- const consumer = consume(session).catch((error) => {
1071
- notify("error", {
1072
- threadId: id,
1073
- provider: "Claude",
1074
- error: { message: error instanceof Error ? error.message : String(error) },
1075
- willRetry: false,
1072
+ const consumer = consume(session)
1073
+ .catch((error) => {
1074
+ notify("error", {
1075
+ threadId: id,
1076
+ provider: "Claude",
1077
+ error: { message: error instanceof Error ? error.message : String(error) },
1078
+ willRetry: false,
1079
+ });
1080
+ if (session.turn) finishTurn(session, error);
1081
+ })
1082
+ .finally(() => {
1083
+ clearSubagents(session);
1076
1084
  });
1077
- clearSubagents(session);
1078
- if (session.turn) finishTurn(session, error);
1079
- });
1080
1085
  session.consumer = consumer;
1081
1086
  let initialization;
1082
1087
  try {
@@ -1583,17 +1588,33 @@ function numberedTaskSubject(subject, index) {
1583
1588
  // 서브에이전트는 자기 메시지를 부모 Task 툴콜의 `parent_tool_use_id`와 함께 흘려보낸다.
1584
1589
  // 그 ID로 묶어 두면 지금 어떤 에이전트가 무슨 도구를 돌리는지 그대로 복원할 수 있다.
1585
1590
  const SUBAGENT_TOOLS = ["Agent", "Task"];
1591
+ const SUBAGENT_PULSE_MS = 5000;
1592
+ const BACKGROUND_SUBAGENT_LEASE_MS = 60_000;
1586
1593
 
1587
1594
  function startSubagent(session, block) {
1588
1595
  const input = block.input || {};
1596
+ const existing = findSubagent(session, block.id);
1597
+ if (existing) {
1598
+ existing.toolUseId = block.id;
1599
+ existing.name = firstLine(input.subagent_type || input.agentType || existing.name || "agent", 40);
1600
+ existing.description = firstLine(
1601
+ input.description || input.prompt || existing.description || "",
1602
+ 120,
1603
+ );
1604
+ existing.lastSeenAt = Date.now();
1605
+ emitSubagents(session);
1606
+ return;
1607
+ }
1589
1608
  session.subagents.set(block.id, {
1590
1609
  id: block.id,
1610
+ toolUseId: block.id,
1591
1611
  taskId: "",
1592
1612
  background: false,
1593
1613
  name: firstLine(input.subagent_type || input.agentType || "agent", 40),
1594
1614
  description: firstLine(input.description || input.prompt || "", 120),
1595
1615
  tool: "",
1596
1616
  startedAt: Date.now(),
1617
+ lastSeenAt: Date.now(),
1597
1618
  });
1598
1619
  emitSubagents(session);
1599
1620
  }
@@ -1606,10 +1627,11 @@ function isBackgroundSubagentResult(result) {
1606
1627
  // live until its later task-notification names the same task or tool-use id.
1607
1628
  function keepBackgroundSubagent(session, toolUseId, result) {
1608
1629
  if (!isBackgroundSubagentResult(result)) return false;
1609
- const running = session.subagents.get(toolUseId);
1630
+ const running = findSubagent(session, toolUseId);
1610
1631
  if (!running) return false;
1611
1632
  running.background = true;
1612
1633
  running.taskId = firstLine(result?.agentId || result?.taskId || "", 80);
1634
+ running.lastSeenAt = Date.now();
1613
1635
  if (running.taskId) {
1614
1636
  session.knownSubagents.set(running.taskId, {
1615
1637
  name: running.name,
@@ -1631,12 +1653,14 @@ function resumeBackgroundSubagent(session, toolUseId, pending, result) {
1631
1653
  const input = pending?.input || {};
1632
1654
  const running = {
1633
1655
  id: toolUseId,
1656
+ toolUseId,
1634
1657
  taskId,
1635
1658
  background: true,
1636
1659
  name: known?.name || "agent",
1637
1660
  description: known?.description || firstLine(input.summary || input.message || "", 120),
1638
1661
  tool: "",
1639
1662
  startedAt: Date.now(),
1663
+ lastSeenAt: Date.now(),
1640
1664
  };
1641
1665
  session.subagents.set(toolUseId, running);
1642
1666
  session.knownSubagents.set(taskId, {
@@ -1649,7 +1673,199 @@ function resumeBackgroundSubagent(session, toolUseId, pending, result) {
1649
1673
 
1650
1674
  function findSubagent(session, id) {
1651
1675
  return session.subagents.get(id)
1652
- || [...session.subagents.values()].find((agent) => agent.taskId === id);
1676
+ || [...session.subagents.values()].find(
1677
+ (agent) => agent.taskId === id || agent.toolUseId === id,
1678
+ );
1679
+ }
1680
+
1681
+ function isSubagentTaskType(taskType) {
1682
+ return String(taskType || "").toLowerCase().includes("agent");
1683
+ }
1684
+
1685
+ // Claude's SDK exposes task_started/task_progress/task_updated/task_notification
1686
+ // as structured lifecycle edges. Prefer those fields over parsing the injected
1687
+ // XML fallback so a formatting change cannot leave a stale running row.
1688
+ function upsertStructuredSubagent(session, message) {
1689
+ const taskId = firstLine(message.task_id || "", 80);
1690
+ const toolUseId = firstLine(message.tool_use_id || "", 80);
1691
+ const subagentType = firstLine(message.subagent_type || "", 40);
1692
+ if (taskId && session.hiddenSubagentTasks?.has(taskId)) return null;
1693
+ const byTool = toolUseId && findSubagent(session, toolUseId);
1694
+ const byTask = taskId && findSubagent(session, taskId);
1695
+ let running = byTool || byTask;
1696
+ // The SDK does not order the background level snapshot against task_started.
1697
+ // If both paths created a row, keep the tool-use row and fold the placeholder
1698
+ // into it before publishing the next snapshot.
1699
+ if (byTool && byTask && byTool !== byTask) {
1700
+ byTool.background ||= byTask.background;
1701
+ byTool.taskId ||= byTask.taskId;
1702
+ byTool.toolUseId ||= byTask.toolUseId;
1703
+ if ((!byTool.name || byTool.name === "agent") && byTask.name) byTool.name = byTask.name;
1704
+ if (!byTool.description) byTool.description = byTask.description;
1705
+ if (!byTool.tool) byTool.tool = byTask.tool;
1706
+ byTool.startedAt = Math.min(byTool.startedAt, byTask.startedAt);
1707
+ byTool.lastSeenAt = Math.max(byTool.lastSeenAt || 0, byTask.lastSeenAt || 0);
1708
+ session.subagents.delete(byTask.id);
1709
+ running = byTool;
1710
+ }
1711
+ if (!running && !subagentType && !isSubagentTaskType(message.task_type)) return null;
1712
+ if (!running) {
1713
+ const known = taskId ? session.knownSubagents.get(taskId) : null;
1714
+ const id = toolUseId || `task:${taskId}`;
1715
+ running = {
1716
+ id,
1717
+ toolUseId,
1718
+ taskId,
1719
+ background: false,
1720
+ name: subagentType || known?.name || "agent",
1721
+ description: firstLine(message.description || known?.description || "", 120),
1722
+ tool: "",
1723
+ startedAt: Date.now(),
1724
+ lastSeenAt: Date.now(),
1725
+ };
1726
+ session.subagents.set(id, running);
1727
+ }
1728
+ if (toolUseId) running.toolUseId = toolUseId;
1729
+ if (taskId) running.taskId = taskId;
1730
+ if (subagentType) running.name = subagentType;
1731
+ const description = firstLine(message.description || "", 120);
1732
+ if (description) running.description = description;
1733
+ running.lastSeenAt = Date.now();
1734
+ if (running.taskId) {
1735
+ session.knownSubagents.set(running.taskId, {
1736
+ name: running.name,
1737
+ description: running.description,
1738
+ });
1739
+ }
1740
+ return running;
1741
+ }
1742
+
1743
+ function finishStructuredSubagent(session, message, kind, text) {
1744
+ const taskId = firstLine(message.task_id || "", 80);
1745
+ const toolUseId = firstLine(message.tool_use_id || "", 80);
1746
+ const wasHidden = taskId ? session.hiddenSubagentTasks?.delete(taskId) === true : false;
1747
+ const running = (toolUseId && findSubagent(session, toolUseId))
1748
+ || (taskId && findSubagent(session, taskId));
1749
+ if (!running) return wasHidden;
1750
+ emitSubagentLine(session, running.id, { kind, text });
1751
+ session.subagents.delete(running.id);
1752
+ emitSubagents(session);
1753
+ return true;
1754
+ }
1755
+
1756
+ function syncBackgroundSubagents(session, tasks) {
1757
+ const live = (Array.isArray(tasks) ? tasks : []).filter((task) => {
1758
+ const taskId = firstLine(task?.task_id || "", 80);
1759
+ return !taskId || !session.hiddenSubagentTasks?.has(taskId);
1760
+ });
1761
+ const liveIds = new Set(live.map((task) => firstLine(task?.task_id || "", 80)).filter(Boolean));
1762
+ let changed = false;
1763
+ for (const [id, running] of session.subagents) {
1764
+ if (!running.background || !running.taskId || liveIds.has(running.taskId)) continue;
1765
+ session.subagents.delete(id);
1766
+ changed = true;
1767
+ }
1768
+ for (const task of live) {
1769
+ const taskId = firstLine(task?.task_id || "", 80);
1770
+ if (!taskId) continue;
1771
+ let running = findSubagent(session, taskId);
1772
+ const known = session.knownSubagents.get(taskId);
1773
+ if (!running && !known && !isSubagentTaskType(task?.task_type)) continue;
1774
+ if (!running) {
1775
+ running = {
1776
+ id: `task:${taskId}`,
1777
+ toolUseId: "",
1778
+ taskId,
1779
+ background: true,
1780
+ name: known?.name || "agent",
1781
+ description: firstLine(task?.description || known?.description || "", 120),
1782
+ tool: "",
1783
+ startedAt: Date.now(),
1784
+ lastSeenAt: Date.now(),
1785
+ };
1786
+ session.subagents.set(running.id, running);
1787
+ changed = true;
1788
+ }
1789
+ running.background = true;
1790
+ running.lastSeenAt = Date.now();
1791
+ const description = firstLine(task?.description || "", 120);
1792
+ if (description && description !== running.description) {
1793
+ running.description = description;
1794
+ changed = true;
1795
+ }
1796
+ }
1797
+ if (changed || liveIds.size) emitSubagents(session);
1798
+ }
1799
+
1800
+ function processSubagentSystemMessage(session, message) {
1801
+ if (message.subtype === "task_started") {
1802
+ if (message.skip_transcript === true) {
1803
+ const taskId = firstLine(message.task_id || "", 80);
1804
+ const toolUseId = firstLine(message.tool_use_id || "", 80);
1805
+ if (taskId) {
1806
+ session.hiddenSubagentTasks ||= new Set();
1807
+ session.hiddenSubagentTasks.add(taskId);
1808
+ }
1809
+ const running = (toolUseId && findSubagent(session, toolUseId))
1810
+ || (taskId && findSubagent(session, taskId));
1811
+ if (running) {
1812
+ session.subagents.delete(running.id);
1813
+ emitSubagents(session);
1814
+ }
1815
+ return true;
1816
+ }
1817
+ const running = upsertStructuredSubagent(session, message);
1818
+ if (!running) return false;
1819
+ emitSubagents(session);
1820
+ return true;
1821
+ }
1822
+ if (message.subtype === "task_progress") {
1823
+ const running = upsertStructuredSubagent(session, message);
1824
+ if (!running) return false;
1825
+ const tool = firstLine(message.last_tool_name || "", 80);
1826
+ if (tool) running.tool = tool;
1827
+ const summary = firstLine(message.summary || "", 200);
1828
+ if (summary && summary !== running.lastSummary) {
1829
+ running.lastSummary = summary;
1830
+ emitSubagentLine(session, running.id, { kind: "result", text: summary });
1831
+ }
1832
+ emitSubagents(session);
1833
+ return true;
1834
+ }
1835
+ if (message.subtype === "task_updated") {
1836
+ const taskId = firstLine(message.task_id || "", 80);
1837
+ const patch = message.patch || {};
1838
+ const status = patch.status;
1839
+ if (["completed", "failed", "killed"].includes(status)) {
1840
+ return finishStructuredSubagent(
1841
+ session,
1842
+ message,
1843
+ status === "completed" ? "result" : "error",
1844
+ firstLine(patch.error || status, 200),
1845
+ );
1846
+ }
1847
+ const running = taskId && findSubagent(session, taskId);
1848
+ if (!running) return false;
1849
+ if (patch.description) running.description = firstLine(patch.description, 120);
1850
+ if (patch.is_backgrounded === true) running.background = true;
1851
+ running.lastSeenAt = Date.now();
1852
+ emitSubagents(session);
1853
+ return true;
1854
+ }
1855
+ if (message.subtype === "task_notification") {
1856
+ const status = firstLine(message.status || "completed", 40);
1857
+ return finishStructuredSubagent(
1858
+ session,
1859
+ message,
1860
+ status === "completed" ? "result" : "error",
1861
+ firstLine(message.summary || status, 200),
1862
+ );
1863
+ }
1864
+ if (message.subtype === "background_tasks_changed") {
1865
+ syncBackgroundSubagents(session, message.tasks);
1866
+ return true;
1867
+ }
1868
+ return false;
1653
1869
  }
1654
1870
 
1655
1871
  // 서브에이전트가 실제로 무엇을 했는지는 자식 메시지에만 남는다. 열람용 기록은 여기서
@@ -1657,6 +1873,7 @@ function findSubagent(session, id) {
1657
1873
  function recordSubagentMessage(session, message) {
1658
1874
  const running = findSubagent(session, message.parent_tool_use_id);
1659
1875
  if (!running) return;
1876
+ running.lastSeenAt = Date.now();
1660
1877
  const content = Array.isArray(message.message?.content) ? message.message.content : [];
1661
1878
  let toolChanged = false;
1662
1879
  for (const block of content) {
@@ -1679,6 +1896,7 @@ function recordSubagentMessage(session, message) {
1679
1896
  function recordSubagentResult(session, message) {
1680
1897
  const running = findSubagent(session, message.parent_tool_use_id);
1681
1898
  if (!running) return;
1899
+ running.lastSeenAt = Date.now();
1682
1900
  const content = Array.isArray(message.message?.content) ? message.message.content : [];
1683
1901
  for (const block of content) {
1684
1902
  if (block.type !== "tool_result") continue;
@@ -1714,7 +1932,9 @@ function subagentToolLabel(block) {
1714
1932
  }
1715
1933
 
1716
1934
  function finishSubagent(session, toolUseId) {
1717
- if (!session.subagents.delete(toolUseId)) return;
1935
+ const running = findSubagent(session, toolUseId);
1936
+ if (!running) return;
1937
+ session.subagents.delete(running.id);
1718
1938
  emitSubagents(session);
1719
1939
  }
1720
1940
 
@@ -1738,20 +1958,35 @@ function clearForegroundSubagents(session) {
1738
1958
  }
1739
1959
 
1740
1960
  function clearSubagents(session) {
1741
- if (!session.subagents.size) return;
1961
+ const changed = session.subagents.size > 0;
1742
1962
  session.subagents.clear();
1743
- emitSubagents(session);
1963
+ session.hiddenSubagentTasks?.clear();
1964
+ if (session.subagentPulse) {
1965
+ clearInterval(session.subagentPulse);
1966
+ session.subagentPulse = null;
1967
+ }
1968
+ if (changed) emitSubagents(session);
1744
1969
  }
1745
1970
 
1746
1971
  function firstLine(value, limit) {
1747
1972
  return String(value ?? "").split("\n")[0].trim().slice(0, limit);
1748
1973
  }
1749
1974
 
1750
- function emitSubagents(session) {
1751
- // 하위 에이전트가 도는 동안 주기적으로 목록을 다시 알린다. 화면이
1752
- // 생존 신호로 살아 있는 행과 종료 신호를 놓친 행을 구분한다.
1975
+ function emitSubagents(session, pulse = false) {
1976
+ // 구조화된 진행 신호가 끊긴 백그라운드 행은 pulse로 영구 연장하지 않는다.
1977
+ // 정상 작업은 task_progress/background_tasks_changed가 lease를 갱신하고,
1978
+ // 종료 edge와 level 신호를 모두 놓친 행만 유한 시간 뒤 제거된다.
1979
+ if (pulse) {
1980
+ const now = Date.now();
1981
+ for (const [id, agent] of session.subagents) {
1982
+ const lastSeenAt = agent.lastSeenAt || agent.startedAt || now;
1983
+ if (agent.background && now - lastSeenAt >= BACKGROUND_SUBAGENT_LEASE_MS) {
1984
+ session.subagents.delete(id);
1985
+ }
1986
+ }
1987
+ }
1753
1988
  if (session.subagents.size && !session.subagentPulse) {
1754
- session.subagentPulse = setInterval(() => emitSubagents(session), 5000);
1989
+ session.subagentPulse = setInterval(() => emitSubagents(session, true), SUBAGENT_PULSE_MS);
1755
1990
  session.subagentPulse.unref?.();
1756
1991
  } else if (!session.subagents.size && session.subagentPulse) {
1757
1992
  clearInterval(session.subagentPulse);
@@ -2050,6 +2285,9 @@ async function consume(session) {
2050
2285
  } else if (message.type === "assistant") processAssistant(session, message);
2051
2286
  else if (message.type === "user") processUser(session, message);
2052
2287
  else if (message.type === "result") await processResult(session, message);
2288
+ else if (message.type === "system" && processSubagentSystemMessage(session, message)) {
2289
+ // Structured SDK task lifecycle handled above.
2290
+ }
2053
2291
  else if (message.type === "system" && message.subtype === "compact_boundary") {
2054
2292
  noteCompactBoundary(session, message.compact_metadata);
2055
2293
  notify("thread/compacted", { threadId: session.id });
@@ -2671,6 +2909,7 @@ async function dispatch(method, params = {}) {
2671
2909
  if (method === "session/close") {
2672
2910
  const session = lookupSession(params.sessionId);
2673
2911
  if (session) {
2912
+ clearSubagents(session);
2674
2913
  session.queue.close();
2675
2914
  session.query.close();
2676
2915
  sessions.delete(session.id);
@@ -2688,6 +2927,7 @@ async function dispatch(method, params = {}) {
2688
2927
  }
2689
2928
  if (method === "shutdown") {
2690
2929
  for (const session of sessions.values()) {
2930
+ clearSubagents(session);
2691
2931
  session.queue.close();
2692
2932
  session.query.close();
2693
2933
  }
@@ -3136,6 +3376,168 @@ async function runSelfTest() {
3136
3376
  throw new Error("Claude failed task notification did not finish the resumed agent");
3137
3377
  }
3138
3378
  finishTurn(lifecycleSession, null, 1);
3379
+
3380
+ const structuredSession = {
3381
+ id: "structured-self-test",
3382
+ turn: null,
3383
+ subagents: new Map(),
3384
+ knownSubagents: new Map(),
3385
+ hiddenSubagentTasks: new Set(),
3386
+ subagentPulse: null,
3387
+ };
3388
+ processSubagentSystemMessage(structuredSession, {
3389
+ type: "system",
3390
+ subtype: "task_started",
3391
+ task_id: "agent-structured",
3392
+ tool_use_id: "toolu_structured",
3393
+ task_type: "agent",
3394
+ subagent_type: "Explore",
3395
+ description: "Inspect structured events",
3396
+ });
3397
+ processSubagentSystemMessage(structuredSession, {
3398
+ type: "system",
3399
+ subtype: "background_tasks_changed",
3400
+ tasks: [{
3401
+ task_id: "agent-structured",
3402
+ task_type: "agent",
3403
+ description: "Inspect structured events",
3404
+ }],
3405
+ });
3406
+ processSubagentSystemMessage(structuredSession, {
3407
+ type: "system",
3408
+ subtype: "task_progress",
3409
+ task_id: "agent-structured",
3410
+ tool_use_id: "toolu_structured",
3411
+ subagent_type: "Explore",
3412
+ description: "Inspect structured events",
3413
+ last_tool_name: "Read",
3414
+ summary: "Reading lifecycle source",
3415
+ });
3416
+ const structured = structuredSession.subagents.get("toolu_structured");
3417
+ if (!structured?.background || structured.tool !== "Read") {
3418
+ throw new Error(`Claude structured task progress self-test failed: ${JSON.stringify(structured)}`);
3419
+ }
3420
+ processSubagentSystemMessage(structuredSession, {
3421
+ type: "system",
3422
+ subtype: "background_tasks_changed",
3423
+ tasks: [],
3424
+ });
3425
+ if (structuredSession.subagents.size !== 0) {
3426
+ throw new Error("Claude background task snapshot did not remove a finished subagent");
3427
+ }
3428
+
3429
+ processSubagentSystemMessage(structuredSession, {
3430
+ type: "system",
3431
+ subtype: "background_tasks_changed",
3432
+ tasks: [{ task_id: "agent-reordered", task_type: "agent", description: "Race" }],
3433
+ });
3434
+ startSubagent(structuredSession, {
3435
+ id: "toolu_reordered",
3436
+ input: { subagent_type: "Explore", description: "Race" },
3437
+ });
3438
+ processSubagentSystemMessage(structuredSession, {
3439
+ type: "system",
3440
+ subtype: "task_started",
3441
+ task_id: "agent-reordered",
3442
+ tool_use_id: "toolu_reordered",
3443
+ task_type: "agent",
3444
+ subagent_type: "Explore",
3445
+ });
3446
+ const reordered = findSubagent(structuredSession, "toolu_reordered");
3447
+ if (structuredSession.subagents.size !== 1
3448
+ || !reordered?.background
3449
+ || reordered.taskId !== "agent-reordered") {
3450
+ throw new Error(`Claude reordered lifecycle self-test failed: ${JSON.stringify([...structuredSession.subagents])}`);
3451
+ }
3452
+ processSubagentSystemMessage(structuredSession, {
3453
+ type: "system",
3454
+ subtype: "task_notification",
3455
+ task_id: "agent-reordered",
3456
+ tool_use_id: "toolu_reordered",
3457
+ status: "completed",
3458
+ summary: "Agent finished",
3459
+ });
3460
+
3461
+ processSubagentSystemMessage(structuredSession, {
3462
+ type: "system",
3463
+ subtype: "task_started",
3464
+ task_id: "agent-edge-first",
3465
+ tool_use_id: "toolu_edge_first",
3466
+ task_type: "agent",
3467
+ subagent_type: "Explore",
3468
+ description: "Edge first",
3469
+ });
3470
+ startSubagent(structuredSession, {
3471
+ id: "toolu_edge_first",
3472
+ input: { subagent_type: "Explore", description: "Edge first" },
3473
+ });
3474
+ const edgeFirst = findSubagent(structuredSession, "toolu_edge_first");
3475
+ if (structuredSession.subagents.size !== 1 || edgeFirst?.taskId !== "agent-edge-first") {
3476
+ throw new Error(`Claude edge-first lifecycle self-test failed: ${JSON.stringify([...structuredSession.subagents])}`);
3477
+ }
3478
+ finishSubagent(structuredSession, "toolu_edge_first");
3479
+ if (structuredSession.subagents.size !== 0) {
3480
+ throw new Error("Claude aliased tool completion did not remove the subagent row");
3481
+ }
3482
+
3483
+ processSubagentSystemMessage(structuredSession, {
3484
+ type: "system",
3485
+ subtype: "task_started",
3486
+ task_id: "agent-hidden",
3487
+ task_type: "agent",
3488
+ subagent_type: "Explore",
3489
+ skip_transcript: true,
3490
+ });
3491
+ processSubagentSystemMessage(structuredSession, {
3492
+ type: "system",
3493
+ subtype: "background_tasks_changed",
3494
+ tasks: [{ task_id: "agent-hidden", task_type: "agent", description: "Housekeeping" }],
3495
+ });
3496
+ if (structuredSession.subagents.size !== 0) {
3497
+ throw new Error("Claude skip_transcript task was shown as a subagent row");
3498
+ }
3499
+ processSubagentSystemMessage(structuredSession, {
3500
+ type: "system",
3501
+ subtype: "task_notification",
3502
+ task_id: "agent-hidden",
3503
+ status: "completed",
3504
+ summary: "Hidden task finished",
3505
+ });
3506
+
3507
+ processSubagentSystemMessage(structuredSession, {
3508
+ type: "system",
3509
+ subtype: "task_started",
3510
+ task_id: "agent-notified",
3511
+ tool_use_id: "toolu_notified",
3512
+ task_type: "agent",
3513
+ subagent_type: "Explore",
3514
+ });
3515
+ processSubagentSystemMessage(structuredSession, {
3516
+ type: "system",
3517
+ subtype: "task_notification",
3518
+ task_id: "agent-notified",
3519
+ tool_use_id: "toolu_notified",
3520
+ status: "failed",
3521
+ summary: "Agent failed",
3522
+ });
3523
+ if (structuredSession.subagents.size !== 0) {
3524
+ throw new Error("Claude structured task notification did not remove a failed subagent");
3525
+ }
3526
+
3527
+ structuredSession.subagents.set("expired", {
3528
+ id: "expired",
3529
+ taskId: "agent-expired",
3530
+ background: true,
3531
+ name: "Explore",
3532
+ description: "",
3533
+ tool: "",
3534
+ startedAt: Date.now() - BACKGROUND_SUBAGENT_LEASE_MS - 1,
3535
+ lastSeenAt: Date.now() - BACKGROUND_SUBAGENT_LEASE_MS - 1,
3536
+ });
3537
+ emitSubagents(structuredSession, true);
3538
+ if (structuredSession.subagents.size !== 0) {
3539
+ throw new Error("Claude expired background subagent lease self-test failed");
3540
+ }
3139
3541
  } finally {
3140
3542
  process.stdout.write = stdoutWrite;
3141
3543
  }
@@ -3300,5 +3702,8 @@ lines.on("line", async (line) => {
3300
3702
  });
3301
3703
 
3302
3704
  lines.on("close", () => {
3303
- for (const session of sessions.values()) session.query.close();
3705
+ for (const session of sessions.values()) {
3706
+ clearSubagents(session);
3707
+ session.query.close();
3708
+ }
3304
3709
  });
package/package.json CHANGED
@@ -1,41 +1,41 @@
1
- {
2
- "name": "devez-vibe",
3
- "version": "1.6.3",
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.231"
40
- }
41
- }
1
+ {
2
+ "name": "devez-vibe",
3
+ "version": "1.6.5",
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.231"
40
+ }
41
+ }