mirror of
https://github.com/moltbot/moltbot.git
synced 2026-04-20 21:23:23 +00:00
* test(memory): lock qmd status counts regression * feat: make /tools show what the agent can use right now * fix: sync web ui slash commands with the shared registry * feat: add profile and unavailable counts to /tools * refine: keep /tools focused on available tools * fix: resolve /tools review regressions * fix: honor model compat in /tools inventory * fix: sync generated protocol models for /tools * fix: restore canonical slash command names * fix: avoid ci lint drift in google helper exports * perf: stop computing unused /tools unavailable counts * docs: clarify /tools runtime behavior
73 lines
1.9 KiB
TypeScript
73 lines
1.9 KiB
TypeScript
import { copyPluginToolMeta } from "../plugins/tools.js";
|
|
import { bindAbortRelay } from "../utils/fetch-timeout.js";
|
|
import { copyChannelAgentToolMeta } from "./channel-tools.js";
|
|
import type { AnyAgentTool } from "./pi-tools.types.js";
|
|
|
|
function throwAbortError(): never {
|
|
const err = new Error("Aborted");
|
|
err.name = "AbortError";
|
|
throw err;
|
|
}
|
|
|
|
/**
|
|
* Checks if an object is a valid AbortSignal using structural typing.
|
|
* This is more reliable than `instanceof` across different realms (VM, iframe, etc.)
|
|
* where the AbortSignal constructor may differ.
|
|
*/
|
|
function isAbortSignal(obj: unknown): obj is AbortSignal {
|
|
return obj instanceof AbortSignal;
|
|
}
|
|
|
|
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" && isAbortSignal(a) && isAbortSignal(b)) {
|
|
return AbortSignal.any([a, b]);
|
|
}
|
|
|
|
const controller = new AbortController();
|
|
const onAbort = bindAbortRelay(controller);
|
|
a?.addEventListener("abort", onAbort, { once: true });
|
|
b?.addEventListener("abort", onAbort, { once: true });
|
|
return controller.signal;
|
|
}
|
|
|
|
export function wrapToolWithAbortSignal(
|
|
tool: AnyAgentTool,
|
|
abortSignal?: AbortSignal,
|
|
): AnyAgentTool {
|
|
if (!abortSignal) {
|
|
return tool;
|
|
}
|
|
const execute = tool.execute;
|
|
if (!execute) {
|
|
return tool;
|
|
}
|
|
const wrappedTool: AnyAgentTool = {
|
|
...tool,
|
|
execute: async (toolCallId, params, signal, onUpdate) => {
|
|
const combined = combineAbortSignals(signal, abortSignal);
|
|
if (combined?.aborted) {
|
|
throwAbortError();
|
|
}
|
|
return await execute(toolCallId, params, combined, onUpdate);
|
|
},
|
|
};
|
|
copyPluginToolMeta(tool, wrappedTool);
|
|
copyChannelAgentToolMeta(tool as never, wrappedTool as never);
|
|
return wrappedTool;
|
|
}
|