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 +33 -33
- package/bin/dvz.exe +0 -0
- package/bridge/claude-agent-sdk-bridge.mjs +424 -19
- package/package.json +41 -41
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)
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
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
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
1961
|
+
const changed = session.subagents.size > 0;
|
|
1742
1962
|
session.subagents.clear();
|
|
1743
|
-
|
|
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),
|
|
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())
|
|
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.
|
|
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
|
+
}
|