fix: abort runs between tool calls
This commit is contained in:
@@ -853,6 +853,7 @@ export async function compactEmbeddedPiSession(params: {
|
||||
sessionKey: params.sessionKey ?? params.sessionId,
|
||||
agentDir,
|
||||
config: params.config,
|
||||
abortSignal: runAbortController.signal,
|
||||
// No currentChannelId/currentThreadTs for compaction - not in message context
|
||||
});
|
||||
const machineName = await getMachineDisplayName();
|
||||
@@ -1045,6 +1046,7 @@ export async function runEmbeddedPiAgent(params: {
|
||||
const enqueueGlobal =
|
||||
params.enqueue ??
|
||||
((task, opts) => enqueueCommandInLane(globalLane, task, opts));
|
||||
const runAbortController = new AbortController();
|
||||
return enqueueCommandInLane(sessionLane, () =>
|
||||
enqueueGlobal(async () => {
|
||||
const started = Date.now();
|
||||
@@ -1223,6 +1225,7 @@ export async function runEmbeddedPiAgent(params: {
|
||||
sessionKey: params.sessionKey ?? params.sessionId,
|
||||
agentDir,
|
||||
config: params.config,
|
||||
abortSignal: runAbortController.signal,
|
||||
currentChannelId: params.currentChannelId,
|
||||
currentThreadTs: params.currentThreadTs,
|
||||
replyToMode: params.replyToMode,
|
||||
@@ -1326,6 +1329,7 @@ export async function runEmbeddedPiAgent(params: {
|
||||
const abortRun = (isTimeout = false) => {
|
||||
aborted = true;
|
||||
if (isTimeout) timedOut = true;
|
||||
runAbortController.abort();
|
||||
void session.abort();
|
||||
};
|
||||
let subscription: ReturnType<typeof subscribeEmbeddedPiSession>;
|
||||
|
||||
@@ -503,6 +503,48 @@ export const __testing = {
|
||||
cleanToolSchemaForGemini,
|
||||
} as const;
|
||||
|
||||
function throwAbortError(): never {
|
||||
const err = new Error("Aborted");
|
||||
err.name = "AbortError";
|
||||
throw err;
|
||||
}
|
||||
|
||||
function combineAbortSignals(
|
||||
a?: AbortSignal,
|
||||
b?: AbortSignal,
|
||||
): AbortSignal | undefined {
|
||||
if (!a && !b) return undefined;
|
||||
if (a && !b) return a;
|
||||
if (b && !a) return b;
|
||||
if (a?.aborted) return a;
|
||||
if (b?.aborted) return b;
|
||||
if (typeof AbortSignal.any === "function") {
|
||||
return AbortSignal.any([a as AbortSignal, b as AbortSignal]);
|
||||
}
|
||||
const controller = new AbortController();
|
||||
const onAbort = () => controller.abort();
|
||||
a?.addEventListener("abort", onAbort, { once: true });
|
||||
b?.addEventListener("abort", onAbort, { once: true });
|
||||
return controller.signal;
|
||||
}
|
||||
|
||||
function wrapToolWithAbortSignal(
|
||||
tool: AnyAgentTool,
|
||||
abortSignal?: AbortSignal,
|
||||
): AnyAgentTool {
|
||||
if (!abortSignal) return tool;
|
||||
const execute = tool.execute;
|
||||
if (!execute) return tool;
|
||||
return {
|
||||
...tool,
|
||||
execute: async (toolCallId, params, signal, onUpdate) => {
|
||||
const combined = combineAbortSignals(signal, abortSignal);
|
||||
if (combined?.aborted) throwAbortError();
|
||||
return await execute(toolCallId, params, combined, onUpdate);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function createClawdbotCodingTools(options?: {
|
||||
bash?: BashToolDefaults & ProcessToolDefaults;
|
||||
messageProvider?: string;
|
||||
@@ -511,6 +553,7 @@ export function createClawdbotCodingTools(options?: {
|
||||
sessionKey?: string;
|
||||
agentDir?: string;
|
||||
config?: ClawdbotConfig;
|
||||
abortSignal?: AbortSignal;
|
||||
/** Current channel ID for auto-threading (Slack). */
|
||||
currentChannelId?: string;
|
||||
/** Current thread timestamp for auto-threading (Slack). */
|
||||
@@ -607,8 +650,11 @@ export function createClawdbotCodingTools(options?: {
|
||||
// Always normalize tool JSON Schemas before handing them to pi-agent/pi-ai.
|
||||
// Without this, some providers (notably OpenAI) will reject root-level union schemas.
|
||||
const normalized = subagentFiltered.map(normalizeToolParameters);
|
||||
const withAbort = options?.abortSignal
|
||||
? normalized.map((tool) => wrapToolWithAbortSignal(tool, options.abortSignal))
|
||||
: normalized;
|
||||
|
||||
// Anthropic blocks specific lowercase tool names (bash, read, write, edit) with OAuth tokens.
|
||||
// Always use capitalized versions for compatibility with both OAuth and regular API keys.
|
||||
return renameBlockedToolsForOAuth(normalized);
|
||||
return renameBlockedToolsForOAuth(withAbort);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user