@testchimp/cli 0.1.59 → 0.1.61
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/dist/chimphands/run.js +90 -19
- package/package.json +1 -1
package/dist/chimphands/run.js
CHANGED
|
@@ -1081,6 +1081,34 @@ function runOpencode(prompt, model, childEnv, opencodeSessionId, callbacks, atta
|
|
|
1081
1081
|
const textByPartId = new Map();
|
|
1082
1082
|
const partTypeById = new Map();
|
|
1083
1083
|
let sawStdout = false;
|
|
1084
|
+
let cancelEscalationTimer = null;
|
|
1085
|
+
let cancelWatcher = null;
|
|
1086
|
+
const clearCancelWatcher = () => {
|
|
1087
|
+
if (cancelWatcher) {
|
|
1088
|
+
clearInterval(cancelWatcher);
|
|
1089
|
+
cancelWatcher = null;
|
|
1090
|
+
}
|
|
1091
|
+
if (cancelEscalationTimer) {
|
|
1092
|
+
clearTimeout(cancelEscalationTimer);
|
|
1093
|
+
cancelEscalationTimer = null;
|
|
1094
|
+
}
|
|
1095
|
+
};
|
|
1096
|
+
const killActiveChild = (signal) => {
|
|
1097
|
+
try {
|
|
1098
|
+
if (!child.killed)
|
|
1099
|
+
child.kill(signal);
|
|
1100
|
+
}
|
|
1101
|
+
catch {
|
|
1102
|
+
/* ignore */
|
|
1103
|
+
}
|
|
1104
|
+
};
|
|
1105
|
+
cancelWatcher = setInterval(() => {
|
|
1106
|
+
if (!callbacks.getCancelRequested?.())
|
|
1107
|
+
return;
|
|
1108
|
+
clearCancelWatcher();
|
|
1109
|
+
killActiveChild("SIGTERM");
|
|
1110
|
+
cancelEscalationTimer = setTimeout(() => killActiveChild("SIGKILL"), 2000);
|
|
1111
|
+
}, 500);
|
|
1084
1112
|
let progressTicker = setInterval(() => {
|
|
1085
1113
|
if (sawStdout) {
|
|
1086
1114
|
if (progressTicker) {
|
|
@@ -1236,6 +1264,7 @@ function runOpencode(prompt, model, childEnv, opencodeSessionId, callbacks, atta
|
|
|
1236
1264
|
});
|
|
1237
1265
|
child.on("close", (code) => {
|
|
1238
1266
|
callbacks.onActiveChild?.(null);
|
|
1267
|
+
clearCancelWatcher();
|
|
1239
1268
|
if (progressTicker) {
|
|
1240
1269
|
clearInterval(progressTicker);
|
|
1241
1270
|
progressTicker = null;
|
|
@@ -1546,9 +1575,7 @@ export async function runChimphands(opts) {
|
|
|
1546
1575
|
syncLiveSse(attached);
|
|
1547
1576
|
})
|
|
1548
1577
|
: () => { };
|
|
1549
|
-
|
|
1550
|
-
? startTunnelWorker(backend, apiKey, runtimeId, attachUrl)
|
|
1551
|
-
: () => { };
|
|
1578
|
+
let stopTunnel = () => { };
|
|
1552
1579
|
const userId = bootStr(boot, "chimphands_service_account_user_id", "chimphandsServiceAccountUserId");
|
|
1553
1580
|
if (userId) {
|
|
1554
1581
|
process.env.TESTCHIMP_USER_ID = userId;
|
|
@@ -1634,6 +1661,13 @@ export async function runChimphands(opts) {
|
|
|
1634
1661
|
// Polling is best-effort when inbound SSE misses an event.
|
|
1635
1662
|
}
|
|
1636
1663
|
};
|
|
1664
|
+
const seenWorktreeWriteRequestIds = new Set();
|
|
1665
|
+
const handleUserFileEdit = (edit) => {
|
|
1666
|
+
if (seenWorktreeWriteRequestIds.has(edit.requestId))
|
|
1667
|
+
return;
|
|
1668
|
+
seenWorktreeWriteRequestIds.add(edit.requestId);
|
|
1669
|
+
void applyUserFileEdit(backend, apiKey, sessionId, edit, () => agentTurnInProgress);
|
|
1670
|
+
};
|
|
1637
1671
|
const postEvent = (role, content, opts) => {
|
|
1638
1672
|
const bodyOpts = { ...opts };
|
|
1639
1673
|
if (opencodeSessionId && !bodyOpts.opencodeSessionId) {
|
|
@@ -1669,28 +1703,42 @@ export async function runChimphands(opts) {
|
|
|
1669
1703
|
};
|
|
1670
1704
|
if (userId)
|
|
1671
1705
|
childEnv.TESTCHIMP_USER_ID = userId;
|
|
1706
|
+
const handleCancelTurn = () => {
|
|
1707
|
+
cancelTurnRequested = true;
|
|
1708
|
+
const ch = activeOpencodeChild;
|
|
1709
|
+
if (ch && !ch.killed) {
|
|
1710
|
+
try {
|
|
1711
|
+
ch.kill("SIGTERM");
|
|
1712
|
+
setTimeout(() => {
|
|
1713
|
+
try {
|
|
1714
|
+
if (!ch.killed)
|
|
1715
|
+
ch.kill("SIGKILL");
|
|
1716
|
+
}
|
|
1717
|
+
catch {
|
|
1718
|
+
/* ignore */
|
|
1719
|
+
}
|
|
1720
|
+
}, 2000);
|
|
1721
|
+
}
|
|
1722
|
+
catch {
|
|
1723
|
+
/* ignore */
|
|
1724
|
+
}
|
|
1725
|
+
}
|
|
1726
|
+
};
|
|
1672
1727
|
const stopInbound = connectInboundStream(backend, apiKey, sessionId, {
|
|
1673
1728
|
onUserMessage: enqueueUserMessage,
|
|
1674
1729
|
onIdle: () => {
|
|
1675
1730
|
idle = true;
|
|
1676
1731
|
},
|
|
1677
|
-
onCancelTurn:
|
|
1678
|
-
|
|
1679
|
-
const ch = activeOpencodeChild;
|
|
1680
|
-
if (ch && !ch.killed) {
|
|
1681
|
-
try {
|
|
1682
|
-
ch.kill("SIGTERM");
|
|
1683
|
-
}
|
|
1684
|
-
catch {
|
|
1685
|
-
/* ignore */
|
|
1686
|
-
}
|
|
1687
|
-
}
|
|
1688
|
-
},
|
|
1689
|
-
onUserFileEdit: (edit) => {
|
|
1690
|
-
void applyUserFileEdit(backend, apiKey, sessionId, edit, () => agentTurnInProgress);
|
|
1691
|
-
},
|
|
1732
|
+
onCancelTurn: handleCancelTurn,
|
|
1733
|
+
onUserFileEdit: handleUserFileEdit,
|
|
1692
1734
|
shouldRun: () => sessionActive,
|
|
1693
1735
|
});
|
|
1736
|
+
if (runtimeId && attachUrl) {
|
|
1737
|
+
stopTunnel = startTunnelWorker(backend, apiKey, runtimeId, attachUrl, sessionId, {
|
|
1738
|
+
onUserFileEdit: handleUserFileEdit,
|
|
1739
|
+
onCancelTurn: handleCancelTurn,
|
|
1740
|
+
});
|
|
1741
|
+
}
|
|
1694
1742
|
const shutdownRuntime = async () => {
|
|
1695
1743
|
sessionActive = false;
|
|
1696
1744
|
stopInbound();
|
|
@@ -1970,7 +2018,7 @@ async function commitAndPushDirtyWorktree(message) {
|
|
|
1970
2018
|
console.error(`ChimpHands commit-before-idle failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
1971
2019
|
}
|
|
1972
2020
|
}
|
|
1973
|
-
function startTunnelWorker(backend, apiKey, runtimeId, attachUrl) {
|
|
2021
|
+
function startTunnelWorker(backend, apiKey, runtimeId, attachUrl, sessionId, handlers) {
|
|
1974
2022
|
let stopped = false;
|
|
1975
2023
|
const base = attachUrl.replace(/\/$/, "");
|
|
1976
2024
|
let ws = null;
|
|
@@ -2101,6 +2149,29 @@ function startTunnelWorker(backend, apiKey, runtimeId, attachUrl) {
|
|
|
2101
2149
|
socket.send(JSON.stringify({ type: "pong" }));
|
|
2102
2150
|
return;
|
|
2103
2151
|
}
|
|
2152
|
+
if (frame.type === "inbound_event" && frame.event === "cancel_turn") {
|
|
2153
|
+
const payload = frame.data;
|
|
2154
|
+
if (payload?.sessionId && payload.sessionId !== sessionId) {
|
|
2155
|
+
return;
|
|
2156
|
+
}
|
|
2157
|
+
handlers?.onCancelTurn?.();
|
|
2158
|
+
return;
|
|
2159
|
+
}
|
|
2160
|
+
if (frame.type === "inbound_event" && frame.event === "user_file_edit" && frame.data) {
|
|
2161
|
+
const edit = frame.data;
|
|
2162
|
+
if (edit.sessionId && edit.sessionId !== sessionId) {
|
|
2163
|
+
return;
|
|
2164
|
+
}
|
|
2165
|
+
const requestId = edit.requestId || edit.request_id;
|
|
2166
|
+
if (requestId && edit.path) {
|
|
2167
|
+
handlers?.onUserFileEdit?.({
|
|
2168
|
+
requestId,
|
|
2169
|
+
path: edit.path,
|
|
2170
|
+
content: edit.content ?? "",
|
|
2171
|
+
});
|
|
2172
|
+
}
|
|
2173
|
+
return;
|
|
2174
|
+
}
|
|
2104
2175
|
if (frame.type === "http_request" || frame.requestId) {
|
|
2105
2176
|
void handleHttpRequest(socket, frame);
|
|
2106
2177
|
}
|
package/package.json
CHANGED