From 80bc497c8aac72edba41d64c06719ab3ff830022 Mon Sep 17 00:00:00 2001 From: Junebugg1214 <82672745+Junebugg1214@users.noreply.github.com> Date: Fri, 20 Mar 2026 23:12:13 -0400 Subject: [PATCH] Fix plugin-sdk text boundary regressions --- extensions/matrix/src/matrix/format.ts | 2 +- extensions/slack/src/format.ts | 4 +- extensions/slack/src/monitor/allow-list.ts | 2 +- extensions/slack/src/monitor/provider.ts | 2 +- extensions/slack/src/monitor/slash.ts | 2 +- extensions/slack/src/probe.ts | 2 +- extensions/slack/src/scopes.ts | 2 +- extensions/slack/src/sent-thread-cache.ts | 2 +- .../telegram/src/audit-membership-runtime.ts | 3 +- extensions/telegram/src/draft-stream.ts | 2 +- extensions/telegram/src/format.ts | 4 +- extensions/telegram/src/probe.ts | 2 +- extensions/telegram/src/reaction-level.ts | 2 +- .../src/reasoning-lane-coordinator.ts | 7 +++- extensions/telegram/src/send.ts | 2 +- extensions/telegram/src/sent-message-cache.ts | 2 +- extensions/telegram/src/thread-bindings.ts | 2 +- extensions/whatsapp/src/test-helpers.ts | 40 ++++++++++++++++++- package.json | 4 ++ scripts/lib/plugin-sdk-entrypoints.json | 1 + src/plugin-sdk/text-core.ts | 32 +++++++++++++++ ...n-extension-import-boundary-inventory.json | 8 ---- 22 files changed, 99 insertions(+), 30 deletions(-) create mode 100644 src/plugin-sdk/text-core.ts diff --git a/extensions/matrix/src/matrix/format.ts b/extensions/matrix/src/matrix/format.ts index efb81ebff2a..50a662c1056 100644 --- a/extensions/matrix/src/matrix/format.ts +++ b/extensions/matrix/src/matrix/format.ts @@ -1,5 +1,5 @@ import MarkdownIt from "markdown-it"; -import { isAutoLinkedFileRef } from "openclaw/plugin-sdk/text-runtime"; +import { isAutoLinkedFileRef } from "openclaw/plugin-sdk/text-core"; const md = new MarkdownIt({ html: false, diff --git a/extensions/slack/src/format.ts b/extensions/slack/src/format.ts index e5ab385fc6b..530d8b60422 100644 --- a/extensions/slack/src/format.ts +++ b/extensions/slack/src/format.ts @@ -2,9 +2,9 @@ import type { MarkdownTableMode } from "openclaw/plugin-sdk/config-runtime"; import { chunkMarkdownIR, markdownToIR, + renderMarkdownWithMarkers, type MarkdownLinkSpan, -} from "openclaw/plugin-sdk/text-runtime"; -import { renderMarkdownWithMarkers } from "openclaw/plugin-sdk/text-runtime"; +} from "openclaw/plugin-sdk/text-core"; // Escape special characters for Slack mrkdwn format. // Preserve Slack's angle-bracket tokens so mentions and links stay intact. diff --git a/extensions/slack/src/monitor/allow-list.ts b/extensions/slack/src/monitor/allow-list.ts index 0ae6de23ec1..0955444931a 100644 --- a/extensions/slack/src/monitor/allow-list.ts +++ b/extensions/slack/src/monitor/allow-list.ts @@ -7,7 +7,7 @@ import { normalizeHyphenSlug, normalizeStringEntries, normalizeStringEntriesLower, -} from "openclaw/plugin-sdk/text-runtime"; +} from "openclaw/plugin-sdk/text-core"; const SLACK_SLUG_CACHE_MAX = 512; const slackSlugCache = new Map(); diff --git a/extensions/slack/src/monitor/provider.ts b/extensions/slack/src/monitor/provider.ts index 1af83676e93..1c4520dd777 100644 --- a/extensions/slack/src/monitor/provider.ts +++ b/extensions/slack/src/monitor/provider.ts @@ -24,7 +24,7 @@ import { normalizeMainKey } from "openclaw/plugin-sdk/routing"; import { warn } from "openclaw/plugin-sdk/runtime-env"; import { createNonExitingRuntime, type RuntimeEnv } from "openclaw/plugin-sdk/runtime-env"; import { normalizeResolvedSecretInputString } from "openclaw/plugin-sdk/secret-input"; -import { normalizeStringEntries } from "openclaw/plugin-sdk/text-runtime"; +import { normalizeStringEntries } from "openclaw/plugin-sdk/text-core"; import { resolveSlackAccount } from "../accounts.js"; import { resolveSlackWebClientOptions } from "../client.js"; import { normalizeSlackWebhookPath, registerSlackHttpHandler } from "../http/index.js"; diff --git a/extensions/slack/src/monitor/slash.ts b/extensions/slack/src/monitor/slash.ts index 6ff790e42b2..68ae882f1e6 100644 --- a/extensions/slack/src/monitor/slash.ts +++ b/extensions/slack/src/monitor/slash.ts @@ -11,7 +11,7 @@ import { } from "openclaw/plugin-sdk/config-runtime"; import type { ReplyPayload } from "openclaw/plugin-sdk/reply-runtime"; import { danger, logVerbose } from "openclaw/plugin-sdk/runtime-env"; -import { chunkItems } from "openclaw/plugin-sdk/text-runtime"; +import { chunkItems } from "openclaw/plugin-sdk/text-core"; import type { ResolvedSlackAccount } from "../accounts.js"; import { truncateSlackText } from "../truncate.js"; import { resolveSlackAllowListMatch, resolveSlackUserAllowed } from "./allow-list.js"; diff --git a/extensions/slack/src/probe.ts b/extensions/slack/src/probe.ts index a0d698e54b5..5da0db7413e 100644 --- a/extensions/slack/src/probe.ts +++ b/extensions/slack/src/probe.ts @@ -1,5 +1,5 @@ import type { BaseProbeResult } from "openclaw/plugin-sdk/channel-contract"; -import { withTimeout } from "openclaw/plugin-sdk/text-runtime"; +import { withTimeout } from "openclaw/plugin-sdk/text-core"; import { createSlackWebClient } from "./client.js"; export type SlackProbe = BaseProbeResult & { diff --git a/extensions/slack/src/scopes.ts b/extensions/slack/src/scopes.ts index fc7e14d741b..63cf77074d0 100644 --- a/extensions/slack/src/scopes.ts +++ b/extensions/slack/src/scopes.ts @@ -1,5 +1,5 @@ import type { WebClient } from "@slack/web-api"; -import { isRecord } from "openclaw/plugin-sdk/text-runtime"; +import { isRecord } from "openclaw/plugin-sdk/text-core"; import { createSlackWebClient } from "./client.js"; export type SlackScopesResult = { diff --git a/extensions/slack/src/sent-thread-cache.ts b/extensions/slack/src/sent-thread-cache.ts index f155571a1b4..1d857c86949 100644 --- a/extensions/slack/src/sent-thread-cache.ts +++ b/extensions/slack/src/sent-thread-cache.ts @@ -1,4 +1,4 @@ -import { resolveGlobalMap } from "openclaw/plugin-sdk/text-runtime"; +import { resolveGlobalMap } from "openclaw/plugin-sdk/text-core"; /** * In-memory cache of Slack threads the bot has participated in. diff --git a/extensions/telegram/src/audit-membership-runtime.ts b/extensions/telegram/src/audit-membership-runtime.ts index 930d768778e..3dd478ee537 100644 --- a/extensions/telegram/src/audit-membership-runtime.ts +++ b/extensions/telegram/src/audit-membership-runtime.ts @@ -1,5 +1,4 @@ -import { isRecord } from "openclaw/plugin-sdk/text-runtime"; -import { fetchWithTimeout } from "openclaw/plugin-sdk/text-runtime"; +import { fetchWithTimeout, isRecord } from "openclaw/plugin-sdk/text-core"; import type { AuditTelegramGroupMembershipParams, TelegramGroupMembershipAudit, diff --git a/extensions/telegram/src/draft-stream.ts b/extensions/telegram/src/draft-stream.ts index ae943f169d3..cff15ec1ad0 100644 --- a/extensions/telegram/src/draft-stream.ts +++ b/extensions/telegram/src/draft-stream.ts @@ -1,6 +1,6 @@ import type { Bot } from "grammy"; import { createFinalizableDraftLifecycle } from "openclaw/plugin-sdk/channel-lifecycle"; -import { resolveGlobalSingleton } from "openclaw/plugin-sdk/text-runtime"; +import { resolveGlobalSingleton } from "openclaw/plugin-sdk/text-core"; import { buildTelegramThreadParams, type TelegramThreadSpec } from "./bot/helpers.js"; import { isSafeToRetrySendError, isTelegramClientRejection } from "./network-errors.js"; diff --git a/extensions/telegram/src/format.ts b/extensions/telegram/src/format.ts index 4d14f179b2f..7be7b2164ee 100644 --- a/extensions/telegram/src/format.ts +++ b/extensions/telegram/src/format.ts @@ -4,10 +4,10 @@ import { FILE_REF_EXTENSIONS_WITH_TLD, isAutoLinkedFileRef, markdownToIR, + renderMarkdownWithMarkers, type MarkdownLinkSpan, type MarkdownIR, -} from "openclaw/plugin-sdk/text-runtime"; -import { renderMarkdownWithMarkers } from "openclaw/plugin-sdk/text-runtime"; +} from "openclaw/plugin-sdk/text-core"; export type TelegramFormattedChunk = { html: string; diff --git a/extensions/telegram/src/probe.ts b/extensions/telegram/src/probe.ts index d297635e4a1..554f0014ace 100644 --- a/extensions/telegram/src/probe.ts +++ b/extensions/telegram/src/probe.ts @@ -1,5 +1,5 @@ import type { BaseProbeResult } from "openclaw/plugin-sdk/channel-contract"; -import { fetchWithTimeout } from "openclaw/plugin-sdk/text-runtime"; +import { fetchWithTimeout } from "openclaw/plugin-sdk/text-core"; import type { TelegramNetworkConfig } from "../runtime-api.js"; import { resolveTelegramFetch } from "./fetch.js"; import { makeProxyFetch } from "./proxy.js"; diff --git a/extensions/telegram/src/reaction-level.ts b/extensions/telegram/src/reaction-level.ts index 3f33277d19a..4d00342f061 100644 --- a/extensions/telegram/src/reaction-level.ts +++ b/extensions/telegram/src/reaction-level.ts @@ -3,7 +3,7 @@ import { resolveReactionLevel, type ReactionLevel, type ResolvedReactionLevel as BaseResolvedReactionLevel, -} from "openclaw/plugin-sdk/text-runtime"; +} from "openclaw/plugin-sdk/text-core"; import { resolveTelegramAccount } from "./accounts.js"; export type TelegramReactionLevel = ReactionLevel; diff --git a/extensions/telegram/src/reasoning-lane-coordinator.ts b/extensions/telegram/src/reasoning-lane-coordinator.ts index a4e414a6727..a209f2c8475 100644 --- a/extensions/telegram/src/reasoning-lane-coordinator.ts +++ b/extensions/telegram/src/reasoning-lane-coordinator.ts @@ -1,7 +1,10 @@ import { formatReasoningMessage } from "openclaw/plugin-sdk/agent-runtime"; import type { ReplyPayload } from "openclaw/plugin-sdk/reply-runtime"; -import { findCodeRegions, isInsideCode } from "openclaw/plugin-sdk/text-runtime"; -import { stripReasoningTagsFromText } from "openclaw/plugin-sdk/text-runtime"; +import { + findCodeRegions, + isInsideCode, + stripReasoningTagsFromText, +} from "openclaw/plugin-sdk/text-core"; const REASONING_MESSAGE_PREFIX = "Reasoning:\n"; const REASONING_TAG_PREFIXES = [ diff --git a/extensions/telegram/src/send.ts b/extensions/telegram/src/send.ts index ec824d88ec7..ccde77a8625 100644 --- a/extensions/telegram/src/send.ts +++ b/extensions/telegram/src/send.ts @@ -18,7 +18,7 @@ import { isGifMedia, kindFromMime } from "openclaw/plugin-sdk/media-runtime"; import { normalizePollInput, type PollInput } from "openclaw/plugin-sdk/media-runtime"; import { logVerbose } from "openclaw/plugin-sdk/runtime-env"; import { createSubsystemLogger } from "openclaw/plugin-sdk/runtime-env"; -import { redactSensitiveText } from "openclaw/plugin-sdk/text-runtime"; +import { redactSensitiveText } from "openclaw/plugin-sdk/text-core"; import { loadWebMedia } from "openclaw/plugin-sdk/web-media"; import { type ResolvedTelegramAccount, resolveTelegramAccount } from "./accounts.js"; import { withTelegramApiErrorLogging } from "./api-logging.js"; diff --git a/extensions/telegram/src/sent-message-cache.ts b/extensions/telegram/src/sent-message-cache.ts index bb48bce3c0f..371731f6ebd 100644 --- a/extensions/telegram/src/sent-message-cache.ts +++ b/extensions/telegram/src/sent-message-cache.ts @@ -1,4 +1,4 @@ -import { resolveGlobalMap } from "openclaw/plugin-sdk/text-runtime"; +import { resolveGlobalMap } from "openclaw/plugin-sdk/text-core"; /** * In-memory cache of sent message IDs per chat. diff --git a/extensions/telegram/src/thread-bindings.ts b/extensions/telegram/src/thread-bindings.ts index 27dfc14e2f4..e93396aabd7 100644 --- a/extensions/telegram/src/thread-bindings.ts +++ b/extensions/telegram/src/thread-bindings.ts @@ -14,7 +14,7 @@ import { writeJsonAtomic } from "openclaw/plugin-sdk/infra-runtime"; import { normalizeAccountId } from "openclaw/plugin-sdk/routing"; import { logVerbose } from "openclaw/plugin-sdk/runtime-env"; import { resolveStateDir } from "openclaw/plugin-sdk/state-paths"; -import { resolveGlobalSingleton } from "openclaw/plugin-sdk/text-runtime"; +import { resolveGlobalSingleton } from "openclaw/plugin-sdk/text-core"; const DEFAULT_THREAD_BINDING_IDLE_TIMEOUT_MS = 24 * 60 * 60 * 1000; const DEFAULT_THREAD_BINDING_MAX_AGE_MS = 0; diff --git a/extensions/whatsapp/src/test-helpers.ts b/extensions/whatsapp/src/test-helpers.ts index b71f25f9d63..ed812a80b83 100644 --- a/extensions/whatsapp/src/test-helpers.ts +++ b/extensions/whatsapp/src/test-helpers.ts @@ -1,5 +1,7 @@ import fsSync from "node:fs"; import fs from "node:fs/promises"; +import os from "node:os"; +import path from "node:path"; import { vi } from "vitest"; import type { MockBaileysSocket } from "../../../test/mocks/baileys.js"; import { createMockBaileys } from "../../../test/mocks/baileys.js"; @@ -32,6 +34,42 @@ export function resetLoadConfigMock() { (globalThis as Record)[CONFIG_KEY] = () => DEFAULT_CONFIG; } +function normalizeAgentIdForStorePath(value: string | undefined): string { + const trimmed = (value ?? "").trim(); + if (!trimmed) { + return "main"; + } + return ( + trimmed + .toLowerCase() + .replace(/[^a-z0-9_-]+/g, "-") + .replace(/^-+/, "") + .replace(/-+$/, "") + .slice(0, 64) || "main" + ); +} + +function resolveStorePathFallback(store?: string, opts?: { agentId?: string }): string { + const agentId = normalizeAgentIdForStorePath(opts?.agentId); + if (!store) { + return path.resolve( + process.env.HOME ?? os.homedir(), + ".openclaw", + "agents", + agentId, + "sessions", + "sessions.json", + ); + } + if (store.includes("{agentId}")) { + return path.resolve(store.replaceAll("{agentId}", agentId)); + } + if (store.startsWith("~")) { + return path.resolve(path.join(process.env.HOME ?? os.homedir(), store.slice(1))); + } + return path.resolve(store); +} + vi.mock("openclaw/plugin-sdk/config-runtime", async (importOriginal) => { const actual = await importOriginal(); const mockModule = Object.create(null) as Record; @@ -92,7 +130,7 @@ vi.mock("openclaw/plugin-sdk/config-runtime", async (importOriginal) => { configurable: true, enumerable: true, writable: true, - value: actual.resolveStorePath, + value: actual.resolveStorePath ?? resolveStorePathFallback, }, }); return mockModule; diff --git a/package.json b/package.json index d4325e4016c..c0d6da40567 100644 --- a/package.json +++ b/package.json @@ -149,6 +149,10 @@ "types": "./dist/plugin-sdk/thread-bindings-runtime.d.ts", "default": "./dist/plugin-sdk/thread-bindings-runtime.js" }, + "./plugin-sdk/text-core": { + "types": "./dist/plugin-sdk/text-core.d.ts", + "default": "./dist/plugin-sdk/text-core.js" + }, "./plugin-sdk/text-runtime": { "types": "./dist/plugin-sdk/text-runtime.d.ts", "default": "./dist/plugin-sdk/text-runtime.js" diff --git a/scripts/lib/plugin-sdk-entrypoints.json b/scripts/lib/plugin-sdk-entrypoints.json index 656dd6a72bb..8bb1dd84b56 100644 --- a/scripts/lib/plugin-sdk-entrypoints.json +++ b/scripts/lib/plugin-sdk-entrypoints.json @@ -27,6 +27,7 @@ "matrix-runtime-heavy", "matrix-runtime-shared", "thread-bindings-runtime", + "text-core", "text-runtime", "agent-runtime", "speech-runtime", diff --git a/src/plugin-sdk/text-core.ts b/src/plugin-sdk/text-core.ts new file mode 100644 index 00000000000..5ed66d4c131 --- /dev/null +++ b/src/plugin-sdk/text-core.ts @@ -0,0 +1,32 @@ +// Narrow text/shared helpers for extensions that should not pull the full +// text-runtime aggregator into mixed runtime/Jiti loader paths. + +export { redactSensitiveText } from "../logging/redact.js"; +export { + chunkMarkdownIR, + markdownToIR, + type MarkdownIR, + type MarkdownLinkSpan, +} from "../markdown/ir.js"; +export { renderMarkdownWithMarkers } from "../markdown/render.js"; +export { resolveGlobalMap, resolveGlobalSingleton } from "../shared/global-singleton.js"; +export { + normalizeHyphenSlug, + normalizeStringEntries, + normalizeStringEntriesLower, +} from "../shared/string-normalization.js"; +export { + FILE_REF_EXTENSIONS_WITH_TLD, + isAutoLinkedFileRef, +} from "../shared/text/auto-linked-file-ref.js"; +export { findCodeRegions, isInsideCode } from "../shared/text/code-regions.js"; +export { stripReasoningTagsFromText } from "../shared/text/reasoning-tags.js"; +export { isRecord } from "../utils.js"; +export { chunkItems } from "../utils/chunk-items.js"; +export { fetchWithTimeout } from "../utils/fetch-timeout.js"; +export { + resolveReactionLevel, + type ReactionLevel, + type ResolvedReactionLevel, +} from "../utils/reaction-level.js"; +export { withTimeout } from "../utils/with-timeout.js"; diff --git a/test/fixtures/plugin-extension-import-boundary-inventory.json b/test/fixtures/plugin-extension-import-boundary-inventory.json index 0894fe0d5b5..ead171321f9 100644 --- a/test/fixtures/plugin-extension-import-boundary-inventory.json +++ b/test/fixtures/plugin-extension-import-boundary-inventory.json @@ -31,14 +31,6 @@ "resolvedPath": "extensions/imessage/runtime-api.js", "reason": "imports extension-owned file from src/plugins" }, - { - "file": "src/plugins/runtime/runtime-matrix.ts", - "line": 4, - "kind": "import", - "specifier": "../../../extensions/matrix/runtime-api.js", - "resolvedPath": "extensions/matrix/runtime-api.js", - "reason": "imports extension-owned file from src/plugins" - }, { "file": "src/plugins/runtime/runtime-slack-ops.runtime.ts", "line": 10,