refactor: extract shared sandbox and gateway plumbing

This commit is contained in:
Peter Steinberger
2026-03-02 23:16:02 +00:00
parent 350d041eaf
commit 7066d5e192
21 changed files with 870 additions and 675 deletions

View File

@@ -0,0 +1,25 @@
import { describe, expect, it } from "vitest";
import { resolveReactionMessageId } from "./reaction-message-id.js";
describe("resolveReactionMessageId", () => {
it("uses explicit messageId when present", () => {
const result = resolveReactionMessageId({
args: { messageId: "456" },
toolContext: { currentMessageId: "123" },
});
expect(result).toBe("456");
});
it("accepts snake_case message_id alias", () => {
const result = resolveReactionMessageId({ args: { message_id: "789" } });
expect(result).toBe("789");
});
it("falls back to toolContext.currentMessageId", () => {
const result = resolveReactionMessageId({
args: {},
toolContext: { currentMessageId: "9001" },
});
expect(result).toBe("9001");
});
});

View File

@@ -0,0 +1,12 @@
import { readStringOrNumberParam } from "../../../agents/tools/common.js";
type ReactionToolContext = {
currentMessageId?: string | number;
};
export function resolveReactionMessageId(params: {
args: Record<string, unknown>;
toolContext?: ReactionToolContext;
}): string | number | undefined {
return readStringOrNumberParam(params.args, "messageId") ?? params.toolContext?.currentMessageId;
}

View File

@@ -3,6 +3,7 @@ import { listEnabledSignalAccounts, resolveSignalAccount } from "../../../signal
import { resolveSignalReactionLevel } from "../../../signal/reaction-level.js";
import { sendReactionSignal, removeReactionSignal } from "../../../signal/send-reactions.js";
import type { ChannelMessageActionAdapter, ChannelMessageActionName } from "../types.js";
import { resolveReactionMessageId } from "./reaction-message-id.js";
const providerId = "signal";
const GROUP_PREFIX = "group:";
@@ -126,9 +127,8 @@ export const signalMessageActions: ChannelMessageActionAdapter = {
throw new Error("recipient or group required");
}
const messageId =
readStringParam(params, "messageId") ??
(toolContext?.currentMessageId != null ? String(toolContext.currentMessageId) : undefined);
const messageIdRaw = resolveReactionMessageId({ args: params, toolContext });
const messageId = messageIdRaw != null ? String(messageIdRaw) : undefined;
if (!messageId) {
throw new Error(
"messageId (timestamp) required. Provide messageId explicitly or react to the current inbound message.",

View File

@@ -13,6 +13,7 @@ import {
} from "../../../telegram/accounts.js";
import { isTelegramInlineButtonsEnabled } from "../../../telegram/inline-buttons.js";
import type { ChannelMessageActionAdapter, ChannelMessageActionName } from "../types.js";
import { resolveReactionMessageId } from "./reaction-message-id.js";
import { createUnionActionGate, listTokenSourcedAccounts } from "./shared.js";
const providerId = "telegram";
@@ -122,8 +123,7 @@ export const telegramMessageActions: ChannelMessageActionAdapter = {
}
if (action === "react") {
const messageId =
readStringOrNumberParam(params, "messageId") ?? toolContext?.currentMessageId;
const messageId = resolveReactionMessageId({ args: params, toolContext });
const emoji = readStringParam(params, "emoji", { allowEmpty: true });
const remove = typeof params.remove === "boolean" ? params.remove : undefined;
return await handleTelegramAction(