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 +33 -33
- package/bin/dvz.exe +0 -0
- package/bridge/claude-agent-sdk-bridge.mjs +494 -494
- package/package.json +1 -1
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
|
+
});
|