Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
01aa7eba21 | ||
|
|
9479fded34 |
No files matched your search
@@ -55,3 +55,5 @@ apps/ios/*.mobileprovision
|
||||
# Local untracked files
|
||||
.local/
|
||||
.vscode/
|
||||
IDENTITY.md
|
||||
USER.md
|
||||
+1
-1
@@ -16,6 +16,7 @@
|
||||
- CLI: set process titles to `clawdbot-<command>` for clearer process listings.
|
||||
- CLI/macOS: sync remote SSH target/identity to config and let `gateway status` auto-infer SSH targets (ssh-config aware).
|
||||
- Heartbeat: tighten prompt guidance + suppress duplicate alerts for 24h. (#980) — thanks @voidserf.
|
||||
- Repo: ignore local identity files to avoid accidental commits. (#1001) — thanks @gerardward2007.
|
||||
- Sessions/Security: add `session.dmScope` for multi-user DM isolation and audit warnings. (#948) — thanks @Alphonse-arianee.
|
||||
- Plugins: add provider auth registry + `clawdbot models auth login` for plugin-driven OAuth/API key flows.
|
||||
- Onboarding: switch channels setup to a single-select loop with per-channel actions and disabled hints in the picker.
|
||||
@@ -66,7 +67,6 @@
|
||||
- Fix: normalize pairing CLI aliases, allow extension channels, and harden Zalo webhook payload parsing. (#991) — thanks @longmaba.
|
||||
- Fix: allow local Tailscale Serve hostnames without treating tailnet clients as direct. (#885) — thanks @oswalpalash.
|
||||
- Fix: reset sessions after role-ordering conflicts to recover from consecutive user turns. (#998)
|
||||
- Fix: keep background exec aborts from killing backgrounded sessions while honoring timeouts. (#1000) — thanks @roshanasingh4.
|
||||
|
||||
## 2026.1.14-1
|
||||
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
import { afterEach, expect, test } from "vitest";
|
||||
|
||||
import { getFinishedSession, getSession, resetProcessRegistryForTests } from "./bash-process-registry.js";
|
||||
import { createExecTool } from "./bash-tools.exec.js";
|
||||
import { killProcessTree } from "./shell-utils.js";
|
||||
|
||||
const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
|
||||
|
||||
afterEach(() => {
|
||||
resetProcessRegistryForTests();
|
||||
});
|
||||
|
||||
test("background exec is not killed when tool signal aborts", async () => {
|
||||
const tool = createExecTool({ allowBackground: true, backgroundMs: 0 });
|
||||
const abortController = new AbortController();
|
||||
|
||||
const result = await tool.execute(
|
||||
"toolcall",
|
||||
{ command: "node -e \"setTimeout(() => {}, 5000)\"", background: true },
|
||||
abortController.signal,
|
||||
);
|
||||
|
||||
expect(result.details.status).toBe("running");
|
||||
const sessionId = (result.details as { sessionId: string }).sessionId;
|
||||
|
||||
abortController.abort();
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 150));
|
||||
|
||||
const running = getSession(sessionId);
|
||||
const finished = getFinishedSession(sessionId);
|
||||
|
||||
try {
|
||||
expect(finished).toBeUndefined();
|
||||
expect(running?.exited).toBe(false);
|
||||
} finally {
|
||||
const pid = running?.pid;
|
||||
if (pid) killProcessTree(pid);
|
||||
}
|
||||
});
|
||||
|
||||
test("background exec still times out after tool signal abort", async () => {
|
||||
const tool = createExecTool({ allowBackground: true, backgroundMs: 0 });
|
||||
const abortController = new AbortController();
|
||||
|
||||
const result = await tool.execute(
|
||||
"toolcall",
|
||||
{
|
||||
command: "node -e \"setTimeout(() => {}, 5000)\"",
|
||||
background: true,
|
||||
timeout: 0.2,
|
||||
},
|
||||
abortController.signal,
|
||||
);
|
||||
|
||||
expect(result.details.status).toBe("running");
|
||||
const sessionId = (result.details as { sessionId: string }).sessionId;
|
||||
|
||||
abortController.abort();
|
||||
|
||||
let finished = getFinishedSession(sessionId);
|
||||
const deadline = Date.now() + 2000;
|
||||
while (!finished && Date.now() < deadline) {
|
||||
await sleep(20);
|
||||
finished = getFinishedSession(sessionId);
|
||||
}
|
||||
|
||||
const running = getSession(sessionId);
|
||||
|
||||
try {
|
||||
expect(finished?.status).toBe("failed");
|
||||
} finally {
|
||||
const pid = running?.pid;
|
||||
if (pid) killProcessTree(pid);
|
||||
}
|
||||
});
|
||||
@@ -262,28 +262,21 @@ export function createExecTool(
|
||||
fn();
|
||||
};
|
||||
|
||||
// Tool-call abort should not kill backgrounded sessions; timeouts still must.
|
||||
const onAbortSignal = () => {
|
||||
if (yielded || session.backgrounded) return;
|
||||
const onAbort = () => {
|
||||
killSession(session);
|
||||
};
|
||||
|
||||
// Timeouts always terminate, even for backgrounded sessions.
|
||||
const onTimeout = () => {
|
||||
timedOut = true;
|
||||
killSession(session);
|
||||
};
|
||||
|
||||
if (signal?.aborted) onAbortSignal();
|
||||
if (signal?.aborted) onAbort();
|
||||
else if (signal) {
|
||||
signal.addEventListener("abort", onAbortSignal, { once: true });
|
||||
signal.addEventListener("abort", onAbort, { once: true });
|
||||
}
|
||||
|
||||
const effectiveTimeout =
|
||||
typeof params.timeout === "number" ? params.timeout : defaultTimeoutSec;
|
||||
if (effectiveTimeout > 0) {
|
||||
timeoutTimer = setTimeout(() => {
|
||||
onTimeout();
|
||||
timedOut = true;
|
||||
onAbort();
|
||||
}, effectiveTimeout * 1000);
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user