devez-vibe 1.6.4 → 1.6.6

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
@@ -1058,30 +1058,30 @@ async function createSession(params, resumeId) {
1058
1058
  tools: new Map(),
1059
1059
  permissionDenials: [],
1060
1060
  tasks: new Map(),
1061
- planCreatePending: false,
1062
- subagents: new Map(),
1063
- knownSubagents: new Map(),
1064
- hiddenSubagentTasks: new Set(),
1065
- subagentPulse: null,
1066
- lastContextUsage: null,
1061
+ planCreatePending: false,
1062
+ subagents: new Map(),
1063
+ knownSubagents: new Map(),
1064
+ hiddenSubagentTasks: new Set(),
1065
+ subagentPulse: null,
1066
+ lastContextUsage: null,
1067
1067
  lastContextWindow: 0,
1068
1068
  };
1069
1069
  const agentQuery = await startAgentQuery(queue, makeOptions(params, id, resumeId));
1070
1070
  session.query = agentQuery;
1071
1071
  sessions.set(id, session);
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);
1084
- });
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);
1084
+ });
1085
1085
  session.consumer = consumer;
1086
1086
  let initialization;
1087
1087
  try {
@@ -1587,37 +1587,37 @@ function numberedTaskSubject(subject, index) {
1587
1587
 
1588
1588
  // 서브에이전트는 자기 메시지를 부모 Task 툴콜의 `parent_tool_use_id`와 함께 흘려보낸다.
1589
1589
  // 그 ID로 묶어 두면 지금 어떤 에이전트가 무슨 도구를 돌리는지 그대로 복원할 수 있다.
1590
- const SUBAGENT_TOOLS = ["Agent", "Task"];
1591
- const SUBAGENT_PULSE_MS = 5000;
1592
- const BACKGROUND_SUBAGENT_LEASE_MS = 60_000;
1593
-
1594
- function startSubagent(session, block) {
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
- }
1608
- session.subagents.set(block.id, {
1609
- id: block.id,
1610
- toolUseId: block.id,
1611
- taskId: "",
1590
+ const SUBAGENT_TOOLS = ["Agent", "Task"];
1591
+ const SUBAGENT_PULSE_MS = 5000;
1592
+ const BACKGROUND_SUBAGENT_LEASE_MS = 60_000;
1593
+
1594
+ function startSubagent(session, block) {
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
+ }
1608
+ session.subagents.set(block.id, {
1609
+ id: block.id,
1610
+ toolUseId: block.id,
1611
+ taskId: "",
1612
1612
  background: false,
1613
1613
  name: firstLine(input.subagent_type || input.agentType || "agent", 40),
1614
- description: firstLine(input.description || input.prompt || "", 120),
1615
- tool: "",
1616
- startedAt: Date.now(),
1617
- lastSeenAt: Date.now(),
1618
- });
1619
- emitSubagents(session);
1620
- }
1614
+ description: firstLine(input.description || input.prompt || "", 120),
1615
+ tool: "",
1616
+ startedAt: Date.now(),
1617
+ lastSeenAt: Date.now(),
1618
+ });
1619
+ emitSubagents(session);
1620
+ }
1621
1621
 
1622
1622
  function isBackgroundSubagentResult(result) {
1623
1623
  return result?.isAsync === true || result?.status === "async_launched";
@@ -1625,13 +1625,13 @@ function isBackgroundSubagentResult(result) {
1625
1625
 
1626
1626
  // Claude Code treats an async Agent result as a launch receipt. The agent remains
1627
1627
  // live until its later task-notification names the same task or tool-use id.
1628
- function keepBackgroundSubagent(session, toolUseId, result) {
1629
- if (!isBackgroundSubagentResult(result)) return false;
1630
- const running = findSubagent(session, toolUseId);
1631
- if (!running) return false;
1632
- running.background = true;
1633
- running.taskId = firstLine(result?.agentId || result?.taskId || "", 80);
1634
- running.lastSeenAt = Date.now();
1628
+ function keepBackgroundSubagent(session, toolUseId, result) {
1629
+ if (!isBackgroundSubagentResult(result)) return false;
1630
+ const running = findSubagent(session, toolUseId);
1631
+ if (!running) return false;
1632
+ running.background = true;
1633
+ running.taskId = firstLine(result?.agentId || result?.taskId || "", 80);
1634
+ running.lastSeenAt = Date.now();
1635
1635
  if (running.taskId) {
1636
1636
  session.knownSubagents.set(running.taskId, {
1637
1637
  name: running.name,
@@ -1651,17 +1651,17 @@ function resumeBackgroundSubagent(session, toolUseId, pending, result) {
1651
1651
  if (existing) return true;
1652
1652
  const known = session.knownSubagents.get(taskId);
1653
1653
  const input = pending?.input || {};
1654
- const running = {
1655
- id: toolUseId,
1656
- toolUseId,
1657
- taskId,
1654
+ const running = {
1655
+ id: toolUseId,
1656
+ toolUseId,
1657
+ taskId,
1658
1658
  background: true,
1659
1659
  name: known?.name || "agent",
1660
- description: known?.description || firstLine(input.summary || input.message || "", 120),
1661
- tool: "",
1662
- startedAt: Date.now(),
1663
- lastSeenAt: Date.now(),
1664
- };
1660
+ description: known?.description || firstLine(input.summary || input.message || "", 120),
1661
+ tool: "",
1662
+ startedAt: Date.now(),
1663
+ lastSeenAt: Date.now(),
1664
+ };
1665
1665
  session.subagents.set(toolUseId, running);
1666
1666
  session.knownSubagents.set(taskId, {
1667
1667
  name: running.name,
@@ -1671,209 +1671,209 @@ function resumeBackgroundSubagent(session, toolUseId, pending, result) {
1671
1671
  return true;
1672
1672
  }
1673
1673
 
1674
- function findSubagent(session, id) {
1675
- return session.subagents.get(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;
1869
- }
1870
-
1871
- // 서브에이전트가 실제로 무엇을 했는지는 자식 메시지에만 남는다. 열람용 기록은 여기서
1674
+ function findSubagent(session, id) {
1675
+ return session.subagents.get(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;
1869
+ }
1870
+
1871
+ // 서브에이전트가 실제로 무엇을 했는지는 자식 메시지에만 남는다. 열람용 기록은 여기서
1872
1872
  // 한 줄씩 흘려보내고, 목록 행에 쓸 현재 도구만 따로 갱신한다.
1873
- function recordSubagentMessage(session, message) {
1874
- const running = findSubagent(session, message.parent_tool_use_id);
1875
- if (!running) return;
1876
- running.lastSeenAt = Date.now();
1873
+ function recordSubagentMessage(session, message) {
1874
+ const running = findSubagent(session, message.parent_tool_use_id);
1875
+ if (!running) return;
1876
+ running.lastSeenAt = Date.now();
1877
1877
  const content = Array.isArray(message.message?.content) ? message.message.content : [];
1878
1878
  let toolChanged = false;
1879
1879
  for (const block of content) {
@@ -1893,10 +1893,10 @@ function recordSubagentMessage(session, message) {
1893
1893
  if (toolChanged) emitSubagents(session);
1894
1894
  }
1895
1895
 
1896
- function recordSubagentResult(session, message) {
1897
- const running = findSubagent(session, message.parent_tool_use_id);
1898
- if (!running) return;
1899
- running.lastSeenAt = Date.now();
1896
+ function recordSubagentResult(session, message) {
1897
+ const running = findSubagent(session, message.parent_tool_use_id);
1898
+ if (!running) return;
1899
+ running.lastSeenAt = Date.now();
1900
1900
  const content = Array.isArray(message.message?.content) ? message.message.content : [];
1901
1901
  for (const block of content) {
1902
1902
  if (block.type !== "tool_result") continue;
@@ -1931,12 +1931,12 @@ function subagentToolLabel(block) {
1931
1931
  return text ? `${name}(${text})` : name;
1932
1932
  }
1933
1933
 
1934
- function finishSubagent(session, toolUseId) {
1935
- const running = findSubagent(session, toolUseId);
1936
- if (!running) return;
1937
- session.subagents.delete(running.id);
1938
- emitSubagents(session);
1939
- }
1934
+ function finishSubagent(session, toolUseId) {
1935
+ const running = findSubagent(session, toolUseId);
1936
+ if (!running) return;
1937
+ session.subagents.delete(running.id);
1938
+ emitSubagents(session);
1939
+ }
1940
1940
 
1941
1941
  function finishSubagentTask(session, taskId) {
1942
1942
  if (!taskId) return false;
@@ -1957,38 +1957,38 @@ function clearForegroundSubagents(session) {
1957
1957
  if (changed) emitSubagents(session);
1958
1958
  }
1959
1959
 
1960
- function clearSubagents(session) {
1961
- const changed = session.subagents.size > 0;
1962
- session.subagents.clear();
1963
- session.hiddenSubagentTasks?.clear();
1964
- if (session.subagentPulse) {
1965
- clearInterval(session.subagentPulse);
1966
- session.subagentPulse = null;
1967
- }
1968
- if (changed) emitSubagents(session);
1969
- }
1960
+ function clearSubagents(session) {
1961
+ const changed = session.subagents.size > 0;
1962
+ session.subagents.clear();
1963
+ session.hiddenSubagentTasks?.clear();
1964
+ if (session.subagentPulse) {
1965
+ clearInterval(session.subagentPulse);
1966
+ session.subagentPulse = null;
1967
+ }
1968
+ if (changed) emitSubagents(session);
1969
+ }
1970
1970
 
1971
1971
  function firstLine(value, limit) {
1972
1972
  return String(value ?? "").split("\n")[0].trim().slice(0, limit);
1973
1973
  }
1974
1974
 
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
- }
1988
- if (session.subagents.size && !session.subagentPulse) {
1989
- session.subagentPulse = setInterval(() => emitSubagents(session, true), SUBAGENT_PULSE_MS);
1990
- session.subagentPulse.unref?.();
1991
- } else if (!session.subagents.size && session.subagentPulse) {
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
+ }
1988
+ if (session.subagents.size && !session.subagentPulse) {
1989
+ session.subagentPulse = setInterval(() => emitSubagents(session, true), SUBAGENT_PULSE_MS);
1990
+ session.subagentPulse.unref?.();
1991
+ } else if (!session.subagents.size && session.subagentPulse) {
1992
1992
  clearInterval(session.subagentPulse);
1993
1993
  session.subagentPulse = null;
1994
1994
  }
@@ -2282,13 +2282,13 @@ async function consume(session) {
2282
2282
  if (session.turn) session.turn.sawStreamText = true;
2283
2283
  }
2284
2284
  await processStreamEvent(session, message);
2285
- } else if (message.type === "assistant") processAssistant(session, message);
2286
- else if (message.type === "user") processUser(session, message);
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
- }
2291
- else if (message.type === "system" && message.subtype === "compact_boundary") {
2285
+ } else if (message.type === "assistant") processAssistant(session, message);
2286
+ else if (message.type === "user") processUser(session, message);
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
+ }
2291
+ else if (message.type === "system" && message.subtype === "compact_boundary") {
2292
2292
  noteCompactBoundary(session, message.compact_metadata);
2293
2293
  notify("thread/compacted", { threadId: session.id });
2294
2294
  } else if (message.type === "system" && message.subtype === "permission_denied") {
@@ -2906,12 +2906,12 @@ async function dispatch(method, params = {}) {
2906
2906
  usage,
2907
2907
  };
2908
2908
  }
2909
- if (method === "session/close") {
2910
- const session = lookupSession(params.sessionId);
2911
- if (session) {
2912
- clearSubagents(session);
2913
- session.queue.close();
2914
- session.query.close();
2909
+ if (method === "session/close") {
2910
+ const session = lookupSession(params.sessionId);
2911
+ if (session) {
2912
+ clearSubagents(session);
2913
+ session.queue.close();
2914
+ session.query.close();
2915
2915
  sessions.delete(session.id);
2916
2916
  for (const [from, to] of sessionAliases) {
2917
2917
  if (to === session.id) sessionAliases.delete(from);
@@ -2925,11 +2925,11 @@ async function dispatch(method, params = {}) {
2925
2925
  if (!session) return { account: null, usage: null };
2926
2926
  return { account: await safeAccount(session.query), usage: await safeUsage(session.query) };
2927
2927
  }
2928
- if (method === "shutdown") {
2929
- for (const session of sessions.values()) {
2930
- clearSubagents(session);
2931
- session.queue.close();
2932
- session.query.close();
2928
+ if (method === "shutdown") {
2929
+ for (const session of sessions.values()) {
2930
+ clearSubagents(session);
2931
+ session.queue.close();
2932
+ session.query.close();
2933
2933
  }
2934
2934
  sessions.clear();
2935
2935
  sessionAliases.clear();
@@ -3372,173 +3372,173 @@ async function runSelfTest() {
3372
3372
  <status>failed</status><summary>Agent failed</summary>
3373
3373
  </task-notification>` },
3374
3374
  });
3375
- if (lifecycleSession.subagents.size !== 0 || lifecycleSession.turn === null) {
3376
- throw new Error("Claude failed task notification did not finish the resumed agent");
3377
- }
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
- }
3541
- } finally {
3375
+ if (lifecycleSession.subagents.size !== 0 || lifecycleSession.turn === null) {
3376
+ throw new Error("Claude failed task notification did not finish the resumed agent");
3377
+ }
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
+ }
3541
+ } finally {
3542
3542
  process.stdout.write = stdoutWrite;
3543
3543
  }
3544
3544
  const lifecycleEvents = captured
@@ -3701,9 +3701,9 @@ lines.on("line", async (line) => {
3701
3701
  catch (error) { write({ id: message.id, error: rpcError(error) }); }
3702
3702
  });
3703
3703
 
3704
- lines.on("close", () => {
3705
- for (const session of sessions.values()) {
3706
- clearSubagents(session);
3707
- session.query.close();
3708
- }
3709
- });
3704
+ lines.on("close", () => {
3705
+ for (const session of sessions.values()) {
3706
+ clearSubagents(session);
3707
+ session.query.close();
3708
+ }
3709
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "devez-vibe",
3
- "version": "1.6.4",
3
+ "version": "1.6.6",
4
4
  "description": "Stable terminal UI for Codex and Claude Agent SDK",
5
5
  "keywords": [
6
6
  "codex",