refactor: extract media and ACP core packages (#88534)

* refactor: extract media and acp core packages

* refactor: remove relocated media and acp sources

* build: wire new core packages into dependency checks

* test: alias new core packages in vitest

* build: keep media sniffer runtime dependency

* docs: refresh plugin sdk api baseline

* fix: keep normalized proposal queries non-empty

* test: keep channel timer tests isolated

* fix: keep rebased plugin checks green

* fix: preserve sms numeric allowlist entries

* test: harden exec foreground timeout failure

* test: remove duplicate skill workshop assertion

* fix: remove channel config lint suppression

* test: refresh lint suppression allowlist
This commit is contained in:
Peter Steinberger
2026-05-31 11:30:33 +01:00
committed by GitHub
parent 4b1e5b7943
commit 77f1359612
160 changed files with 1152 additions and 218 deletions

View File

@@ -144,6 +144,7 @@ const config = {
entry: rootEntries,
ignoreDependencies: [
"@openclaw/*",
"file-type",
"playwright-core",
"sqlite-vec",
"tree-sitter-bash",
@@ -184,6 +185,14 @@ const config = {
entry: ["src/*.ts!"],
project: ["src/**/*.ts!"],
},
"packages/media-core": {
entry: ["src/*.ts!"],
project: ["src/**/*.ts!"],
},
"packages/acp-core": {
entry: ["src/*.ts!"],
project: ["src/**/*.ts!"],
},
"packages/terminal-core": {
entry: ["src/*.ts!"],
project: ["src/**/*.ts!"],

View File

@@ -1,2 +1,2 @@
71e7b7d92cba7f971a8f8f9ee14045320120df0f137f7c5cf295b333d46ecf8c plugin-sdk-api-baseline.json
c550d232c205924eb9e0df9995c205a68659febf0450d02d79bd3349d25a323c plugin-sdk-api-baseline.jsonl
8b0cb667fc676d9cf9e47ec8b3889aaa1cd75d493cc78428844931ca0ac415ff plugin-sdk-api-baseline.json
b97fddcfae489bb4496e5cba26de388e4fe7eb52584d6a6b8e9cb4e539b258c5 plugin-sdk-api-baseline.jsonl

View File

@@ -81,6 +81,24 @@ describe("SMS account config", () => {
});
});
it("normalizes numeric allowFrom entries accepted by config schema", () => {
const cfg = {
channels: {
sms: {
accountSid: "AC-parent",
authToken: "parent-token",
fromNumber: "+15550000000",
allowFrom: [1_555_333_4444],
},
},
};
expect(SmsConfigSchema.parse(cfg.channels.sms).allowFrom).toEqual([1_555_333_4444]);
expect(resolveSmsAccount(cfg)).toMatchObject({
allowFrom: ["+15553334444"],
});
});
it("uses the configured default account when accountId is omitted", () => {
const cfg = {
channels: {

View File

@@ -72,11 +72,11 @@ function parseTwilioSuccessPayload(text: string): TwilioMessagePayload {
from: typeof record.from === "string" ? record.from : undefined,
status: typeof record.status === "string" ? record.status : undefined,
};
} catch (error) {
if (error instanceof Error && error.message === "Twilio SMS send returned malformed JSON.") {
throw error;
} catch (cause) {
if (cause instanceof Error && cause.message === "Twilio SMS send returned malformed JSON.") {
throw cause;
}
throw new Error("Twilio SMS send returned malformed JSON.", { cause: error });
throw new Error("Twilio SMS send returned malformed JSON.", { cause });
}
}

View File

@@ -11,7 +11,7 @@ export type SmsChannelConfigFields = {
publicWebhookUrl?: string;
dangerouslyDisableSignatureValidation?: boolean;
dmPolicy?: "pairing" | "open" | "allowlist" | "disabled";
allowFrom?: string | string[];
allowFrom?: string | Array<string | number>;
textChunkLimit?: number;
};

View File

@@ -203,6 +203,66 @@
"@openclaw/media-generation-core/*": [
"../dist/plugin-sdk/packages/media-generation-core/src/*.d.ts"
],
"@openclaw/media-core": [
"../dist/plugin-sdk/packages/media-core/src/index.d.ts"
],
"@openclaw/media-core/base64": [
"../dist/plugin-sdk/packages/media-core/src/base64.d.ts"
],
"@openclaw/media-core/constants": [
"../dist/plugin-sdk/packages/media-core/src/constants.d.ts"
],
"@openclaw/media-core/content-length": [
"../dist/plugin-sdk/packages/media-core/src/content-length.d.ts"
],
"@openclaw/media-core/file-name": [
"../dist/plugin-sdk/packages/media-core/src/file-name.d.ts"
],
"@openclaw/media-core/inbound-path-policy": [
"../dist/plugin-sdk/packages/media-core/src/inbound-path-policy.d.ts"
],
"@openclaw/media-core/inline-image-data-url": [
"../dist/plugin-sdk/packages/media-core/src/inline-image-data-url.d.ts"
],
"@openclaw/media-core/media-source-url": [
"../dist/plugin-sdk/packages/media-core/src/media-source-url.d.ts"
],
"@openclaw/media-core/mime": [
"../dist/plugin-sdk/packages/media-core/src/mime.d.ts"
],
"@openclaw/media-core/read-byte-stream-with-limit": [
"../dist/plugin-sdk/packages/media-core/src/read-byte-stream-with-limit.d.ts"
],
"@openclaw/media-core/read-response-with-limit": [
"../dist/plugin-sdk/packages/media-core/src/read-response-with-limit.d.ts"
],
"@openclaw/media-core/*": [
"../dist/plugin-sdk/packages/media-core/src/*.d.ts"
],
"@openclaw/normalization-core/record-coerce": [
"../dist/plugin-sdk/packages/normalization-core/src/record-coerce.d.ts"
],
"@openclaw/normalization-core/string-coerce": [
"../dist/plugin-sdk/packages/normalization-core/src/string-coerce.d.ts"
],
"@openclaw/normalization-core/*": [
"../dist/plugin-sdk/packages/normalization-core/src/*.d.ts"
],
"@openclaw/acp-core": [
"../dist/plugin-sdk/packages/acp-core/src/index.d.ts"
],
"@openclaw/acp-core/normalize-text": [
"../dist/plugin-sdk/packages/acp-core/src/normalize-text.d.ts"
],
"@openclaw/acp-core/record-shared": [
"../dist/plugin-sdk/packages/acp-core/src/record-shared.d.ts"
],
"@openclaw/acp-core/runtime/types": [
"../dist/plugin-sdk/packages/acp-core/src/runtime/types.d.ts"
],
"@openclaw/acp-core/*": [
"../dist/plugin-sdk/packages/acp-core/src/*.d.ts"
],
"@openclaw/terminal-core": [
"../dist/plugin-sdk/packages/terminal-core/src/index.d.ts"
],

View File

@@ -189,6 +189,66 @@
"@openclaw/media-generation-core/*": [
"../../dist/plugin-sdk/packages/media-generation-core/src/*.d.ts"
],
"@openclaw/media-core": [
"../../dist/plugin-sdk/packages/media-core/src/index.d.ts"
],
"@openclaw/media-core/base64": [
"../../dist/plugin-sdk/packages/media-core/src/base64.d.ts"
],
"@openclaw/media-core/constants": [
"../../dist/plugin-sdk/packages/media-core/src/constants.d.ts"
],
"@openclaw/media-core/content-length": [
"../../dist/plugin-sdk/packages/media-core/src/content-length.d.ts"
],
"@openclaw/media-core/file-name": [
"../../dist/plugin-sdk/packages/media-core/src/file-name.d.ts"
],
"@openclaw/media-core/inbound-path-policy": [
"../../dist/plugin-sdk/packages/media-core/src/inbound-path-policy.d.ts"
],
"@openclaw/media-core/inline-image-data-url": [
"../../dist/plugin-sdk/packages/media-core/src/inline-image-data-url.d.ts"
],
"@openclaw/media-core/media-source-url": [
"../../dist/plugin-sdk/packages/media-core/src/media-source-url.d.ts"
],
"@openclaw/media-core/mime": [
"../../dist/plugin-sdk/packages/media-core/src/mime.d.ts"
],
"@openclaw/media-core/read-byte-stream-with-limit": [
"../../dist/plugin-sdk/packages/media-core/src/read-byte-stream-with-limit.d.ts"
],
"@openclaw/media-core/read-response-with-limit": [
"../../dist/plugin-sdk/packages/media-core/src/read-response-with-limit.d.ts"
],
"@openclaw/media-core/*": [
"../../dist/plugin-sdk/packages/media-core/src/*.d.ts"
],
"@openclaw/normalization-core/record-coerce": [
"../../dist/plugin-sdk/packages/normalization-core/src/record-coerce.d.ts"
],
"@openclaw/normalization-core/string-coerce": [
"../../dist/plugin-sdk/packages/normalization-core/src/string-coerce.d.ts"
],
"@openclaw/normalization-core/*": [
"../../dist/plugin-sdk/packages/normalization-core/src/*.d.ts"
],
"@openclaw/acp-core": [
"../../dist/plugin-sdk/packages/acp-core/src/index.d.ts"
],
"@openclaw/acp-core/normalize-text": [
"../../dist/plugin-sdk/packages/acp-core/src/normalize-text.d.ts"
],
"@openclaw/acp-core/record-shared": [
"../../dist/plugin-sdk/packages/acp-core/src/record-shared.d.ts"
],
"@openclaw/acp-core/runtime/types": [
"../../dist/plugin-sdk/packages/acp-core/src/runtime/types.d.ts"
],
"@openclaw/acp-core/*": [
"../../dist/plugin-sdk/packages/acp-core/src/*.d.ts"
],
"@openclaw/terminal-core": [
"../../dist/plugin-sdk/packages/terminal-core/src/index.d.ts"
],

View File

@@ -0,0 +1,39 @@
{
"name": "@openclaw/acp-core",
"version": "0.0.0-private",
"private": true,
"files": [
"dist"
],
"type": "module",
"main": "./dist/index.mjs",
"types": "./dist/index.d.mts",
"exports": {
".": {
"types": "./dist/index.d.mts",
"import": "./dist/index.mjs",
"default": "./dist/index.mjs"
},
"./normalize-text": {
"types": "./dist/normalize-text.d.mts",
"import": "./dist/normalize-text.mjs",
"default": "./dist/normalize-text.mjs"
},
"./record-shared": {
"types": "./dist/record-shared.d.mts",
"import": "./dist/record-shared.mjs",
"default": "./dist/record-shared.mjs"
},
"./runtime/types": {
"types": "./dist/runtime/types.d.mts",
"import": "./dist/runtime/types.mjs",
"default": "./dist/runtime/types.mjs"
}
},
"dependencies": {
"@openclaw/normalization-core": "workspace:*"
},
"scripts": {
"build": "tsdown src/index.ts src/normalize-text.ts src/record-shared.ts src/runtime/types.ts --no-config --platform node --format esm --dts --out-dir dist --clean"
}
}

View File

@@ -0,0 +1,3 @@
export * from "./normalize-text.js";
export * from "./record-shared.js";
export * from "./runtime/types.js";

View File

@@ -0,0 +1 @@
export { normalizeOptionalString as normalizeText } from "@openclaw/normalization-core/string-coerce";

View File

@@ -0,0 +1,75 @@
{
"name": "@openclaw/media-core",
"version": "0.0.0-private",
"private": true,
"files": [
"dist"
],
"type": "module",
"main": "./dist/index.mjs",
"types": "./dist/index.d.mts",
"exports": {
".": {
"types": "./dist/index.d.mts",
"import": "./dist/index.mjs",
"default": "./dist/index.mjs"
},
"./base64": {
"types": "./dist/base64.d.mts",
"import": "./dist/base64.mjs",
"default": "./dist/base64.mjs"
},
"./constants": {
"types": "./dist/constants.d.mts",
"import": "./dist/constants.mjs",
"default": "./dist/constants.mjs"
},
"./content-length": {
"types": "./dist/content-length.d.mts",
"import": "./dist/content-length.mjs",
"default": "./dist/content-length.mjs"
},
"./file-name": {
"types": "./dist/file-name.d.mts",
"import": "./dist/file-name.mjs",
"default": "./dist/file-name.mjs"
},
"./inbound-path-policy": {
"types": "./dist/inbound-path-policy.d.mts",
"import": "./dist/inbound-path-policy.mjs",
"default": "./dist/inbound-path-policy.mjs"
},
"./inline-image-data-url": {
"types": "./dist/inline-image-data-url.d.mts",
"import": "./dist/inline-image-data-url.mjs",
"default": "./dist/inline-image-data-url.mjs"
},
"./media-source-url": {
"types": "./dist/media-source-url.d.mts",
"import": "./dist/media-source-url.mjs",
"default": "./dist/media-source-url.mjs"
},
"./mime": {
"types": "./dist/mime.d.mts",
"import": "./dist/mime.mjs",
"default": "./dist/mime.mjs"
},
"./read-byte-stream-with-limit": {
"types": "./dist/read-byte-stream-with-limit.d.mts",
"import": "./dist/read-byte-stream-with-limit.mjs",
"default": "./dist/read-byte-stream-with-limit.mjs"
},
"./read-response-with-limit": {
"types": "./dist/read-response-with-limit.d.mts",
"import": "./dist/read-response-with-limit.mjs",
"default": "./dist/read-response-with-limit.mjs"
}
},
"dependencies": {
"@openclaw/normalization-core": "workspace:*",
"file-type": "22.0.1"
},
"scripts": {
"build": "tsdown src/index.ts src/base64.ts src/constants.ts src/content-length.ts src/file-name.ts src/inbound-path-policy.ts src/inline-image-data-url.ts src/media-source-url.ts src/mime.ts src/read-byte-stream-with-limit.ts src/read-response-with-limit.ts --no-config --platform node --format esm --dts --out-dir dist --clean"
}
}

View File

@@ -0,0 +1,10 @@
export * from "./base64.js";
export * from "./constants.js";
export * from "./content-length.js";
export * from "./file-name.js";
export * from "./inbound-path-policy.js";
export * from "./inline-image-data-url.js";
export * from "./media-source-url.js";
export * from "./mime.js";
export * from "./read-byte-stream-with-limit.js";
export * from "./read-response-with-limit.js";

View File

@@ -0,0 +1,37 @@
export type LazyPromiseLoader<T> = {
load(): Promise<T>;
clear(): void;
};
export type LazyPromiseLoaderOptions = {
cacheRejections?: boolean;
};
export function createLazyImportLoader<T>(
load: () => Promise<T>,
options: LazyPromiseLoaderOptions = {},
): LazyPromiseLoader<T> {
let promise: Promise<T> | undefined;
const createPromise = (): Promise<T> => {
const loaded = Promise.resolve().then(load);
if (options.cacheRejections !== true) {
void loaded.catch(() => {
if (promise === loaded) {
promise = undefined;
}
});
}
return loaded;
};
return {
async load(): Promise<T> {
promise ??= createPromise();
return await promise;
},
clear(): void {
promise = undefined;
},
};
}

View File

@@ -1,6 +1,6 @@
import path from "node:path";
import { createLazyImportLoader } from "../shared/lazy-promise.js";
import { type MediaKind, mediaKindFromMime } from "./constants.js";
import { createLazyImportLoader } from "./lazy-import.js";
/** @internal */
export const FILE_TYPE_SNIFF_MAX_BYTES = 1024 * 1024;

View File

@@ -1,5 +1,5 @@
import { beforeEach, describe, expect, it, vi } from "vitest";
import { MAX_TIMER_TIMEOUT_MS } from "../shared/number-coercion.js";
import { MAX_TIMER_TIMEOUT_MS } from "@openclaw/normalization-core/number-coercion";
import { readResponseTextSnippet, readResponseWithLimit } from "./read-response-with-limit.js";
function makeStream(chunks: Uint8Array[], delayMs?: number) {

View File

@@ -1,4 +1,4 @@
import { resolveTimerTimeoutMs } from "../shared/number-coercion.js";
import { resolveTimerTimeoutMs } from "@openclaw/normalization-core/number-coercion";
async function readChunkWithIdleTimeout(
reader: ReadableStreamDefaultReader<Uint8Array>,

View File

@@ -94,7 +94,7 @@ export {
export type { ProcessWarning } from "../../../../src/infra/warning-filter.js";
export { redactSensitiveText } from "../../../../src/logging/redact.js";
export { createSubsystemLogger } from "../../../../src/logging/subsystem.js";
export { detectMime } from "../../../../src/media/mime.js";
export { detectMime } from "@openclaw/media-core/mime";
// Memory plugin helpers.
export {

View File

@@ -14,9 +14,11 @@
},
"include": [
"../../packages/markdown-core/src/**/*.ts",
"../../packages/media-core/src/**/*.ts",
"../../packages/media-generation-core/src/**/*.ts",
"../../packages/model-catalog-core/src/**/*.ts",
"../../packages/normalization-core/src/**/*.ts",
"../../packages/acp-core/src/**/*.ts",
"../../packages/terminal-core/src/**/*.ts",
"../../src/plugin-sdk/**/*.ts",
"../../src/video-generation/dashscope-compatible.ts",

18
pnpm-lock.yaml generated
View File

@@ -1770,6 +1770,12 @@ importers:
specifier: 2026.5.28
version: 2026.5.28
packages/acp-core:
dependencies:
'@openclaw/normalization-core':
specifier: workspace:*
version: link:../normalization-core
packages/agent-core:
dependencies:
'@openclaw/llm-core':
@@ -1824,6 +1830,15 @@ importers:
specifier: 2.9.0
version: 2.9.0
packages/media-core:
dependencies:
'@openclaw/normalization-core':
specifier: workspace:*
version: link:../normalization-core
file-type:
specifier: 22.0.1
version: 22.0.1
packages/media-generation-core: {}
packages/media-understanding-common: {}
@@ -1877,6 +1892,9 @@ importers:
'@noble/ed25519':
specifier: 3.1.0
version: 3.1.0
'@openclaw/media-core':
specifier: workspace:*
version: link:../packages/media-core
'@openclaw/normalization-core':
specifier: workspace:*
version: link:../packages/normalization-core

View File

@@ -48,8 +48,10 @@ export const BUILD_ALL_STEPS = [
"packages/plugin-sdk/package.json",
"packages/llm-core/package.json",
"packages/markdown-core/package.json",
"packages/media-core/package.json",
"packages/media-understanding-common/package.json",
"packages/terminal-core/package.json",
"packages/acp-core/package.json",
"packages/model-catalog-core/package.json",
"packages/normalization-core/package.json",
"packages/web-content-core/package.json",
@@ -59,10 +61,12 @@ export const BUILD_ALL_STEPS = [
"src/plugin-sdk",
"packages/llm-core/src",
"packages/markdown-core/src",
"packages/media-core/src",
"packages/model-catalog-core/src",
"packages/memory-host-sdk/src",
"packages/media-generation-core/src",
"packages/normalization-core/src",
"packages/acp-core/src",
"packages/media-understanding-common/src",
"packages/terminal-core/src",
"packages/web-content-core/src",

View File

@@ -116,6 +116,48 @@ export const EXTENSION_PACKAGE_BOUNDARY_BASE_PATHS = {
"@openclaw/media-generation-core/*": [
"../dist/plugin-sdk/packages/media-generation-core/src/*.d.ts",
],
"@openclaw/media-core": ["../dist/plugin-sdk/packages/media-core/src/index.d.ts"],
"@openclaw/media-core/base64": ["../dist/plugin-sdk/packages/media-core/src/base64.d.ts"],
"@openclaw/media-core/constants": ["../dist/plugin-sdk/packages/media-core/src/constants.d.ts"],
"@openclaw/media-core/content-length": [
"../dist/plugin-sdk/packages/media-core/src/content-length.d.ts",
],
"@openclaw/media-core/file-name": ["../dist/plugin-sdk/packages/media-core/src/file-name.d.ts"],
"@openclaw/media-core/inbound-path-policy": [
"../dist/plugin-sdk/packages/media-core/src/inbound-path-policy.d.ts",
],
"@openclaw/media-core/inline-image-data-url": [
"../dist/plugin-sdk/packages/media-core/src/inline-image-data-url.d.ts",
],
"@openclaw/media-core/media-source-url": [
"../dist/plugin-sdk/packages/media-core/src/media-source-url.d.ts",
],
"@openclaw/media-core/mime": ["../dist/plugin-sdk/packages/media-core/src/mime.d.ts"],
"@openclaw/media-core/read-byte-stream-with-limit": [
"../dist/plugin-sdk/packages/media-core/src/read-byte-stream-with-limit.d.ts",
],
"@openclaw/media-core/read-response-with-limit": [
"../dist/plugin-sdk/packages/media-core/src/read-response-with-limit.d.ts",
],
"@openclaw/media-core/*": ["../dist/plugin-sdk/packages/media-core/src/*.d.ts"],
"@openclaw/normalization-core/record-coerce": [
"../dist/plugin-sdk/packages/normalization-core/src/record-coerce.d.ts",
],
"@openclaw/normalization-core/string-coerce": [
"../dist/plugin-sdk/packages/normalization-core/src/string-coerce.d.ts",
],
"@openclaw/normalization-core/*": ["../dist/plugin-sdk/packages/normalization-core/src/*.d.ts"],
"@openclaw/acp-core": ["../dist/plugin-sdk/packages/acp-core/src/index.d.ts"],
"@openclaw/acp-core/normalize-text": [
"../dist/plugin-sdk/packages/acp-core/src/normalize-text.d.ts",
],
"@openclaw/acp-core/record-shared": [
"../dist/plugin-sdk/packages/acp-core/src/record-shared.d.ts",
],
"@openclaw/acp-core/runtime/types": [
"../dist/plugin-sdk/packages/acp-core/src/runtime/types.d.ts",
],
"@openclaw/acp-core/*": ["../dist/plugin-sdk/packages/acp-core/src/*.d.ts"],
"@openclaw/terminal-core": ["../dist/plugin-sdk/packages/terminal-core/src/index.d.ts"],
"@openclaw/terminal-core/ansi": ["../dist/plugin-sdk/packages/terminal-core/src/ansi.d.ts"],
"@openclaw/terminal-core/decorative-emoji": [

View File

@@ -5,6 +5,7 @@ const TSDOWN_PACKAGE_NAMES = [
"llm-core",
"llm-runtime",
"markdown-core",
"media-core",
"media-generation-core",
"media-understanding-common",
"model-catalog-core",
@@ -12,6 +13,7 @@ const TSDOWN_PACKAGE_NAMES = [
"normalization-core",
"speech-core",
"terminal-core",
"acp-core",
];
export const TSDOWN_PACKAGE_OUTPUT_ROOTS = TSDOWN_PACKAGE_NAMES.map(packageOutputRoot);

View File

@@ -17,11 +17,13 @@ const PLUGIN_SDK_TYPE_INPUTS = [
"src/auto-reply",
"packages/llm-core/src",
"packages/markdown-core/src",
"packages/media-core/src",
"packages/model-catalog-core/src",
"packages/memory-host-sdk/src",
"packages/media-generation-core/src",
"packages/media-understanding-common/src",
"packages/normalization-core/src",
"packages/acp-core/src",
"packages/terminal-core/src",
"src/video-generation/dashscope-compatible.ts",
"src/video-generation/types.ts",
@@ -52,6 +54,17 @@ const ROOT_DTS_REQUIRED_OUTPUTS = [
"dist/plugin-sdk/packages/media-generation-core/src/index.d.ts",
"dist/plugin-sdk/packages/media-generation-core/src/model-ref.d.ts",
"dist/plugin-sdk/packages/media-generation-core/src/normalization.d.ts",
"dist/plugin-sdk/packages/media-core/src/base64.d.ts",
"dist/plugin-sdk/packages/media-core/src/constants.d.ts",
"dist/plugin-sdk/packages/media-core/src/content-length.d.ts",
"dist/plugin-sdk/packages/media-core/src/file-name.d.ts",
"dist/plugin-sdk/packages/media-core/src/inbound-path-policy.d.ts",
"dist/plugin-sdk/packages/media-core/src/inline-image-data-url.d.ts",
"dist/plugin-sdk/packages/media-core/src/media-source-url.d.ts",
"dist/plugin-sdk/packages/media-core/src/mime.d.ts",
"dist/plugin-sdk/packages/media-core/src/read-byte-stream-with-limit.d.ts",
"dist/plugin-sdk/packages/media-core/src/read-response-with-limit.d.ts",
"dist/plugin-sdk/packages/acp-core/src/runtime/types.d.ts",
"dist/plugin-sdk/packages/terminal-core/src/ansi.d.ts",
"dist/plugin-sdk/packages/terminal-core/src/decorative-emoji.d.ts",
"dist/plugin-sdk/packages/terminal-core/src/health-style.d.ts",
@@ -99,6 +112,17 @@ const PACKAGE_DTS_REQUIRED_OUTPUTS = [
"packages/plugin-sdk/dist/packages/media-generation-core/src/index.d.ts",
"packages/plugin-sdk/dist/packages/media-generation-core/src/model-ref.d.ts",
"packages/plugin-sdk/dist/packages/media-generation-core/src/normalization.d.ts",
"packages/plugin-sdk/dist/packages/media-core/src/base64.d.ts",
"packages/plugin-sdk/dist/packages/media-core/src/constants.d.ts",
"packages/plugin-sdk/dist/packages/media-core/src/content-length.d.ts",
"packages/plugin-sdk/dist/packages/media-core/src/file-name.d.ts",
"packages/plugin-sdk/dist/packages/media-core/src/inbound-path-policy.d.ts",
"packages/plugin-sdk/dist/packages/media-core/src/inline-image-data-url.d.ts",
"packages/plugin-sdk/dist/packages/media-core/src/media-source-url.d.ts",
"packages/plugin-sdk/dist/packages/media-core/src/mime.d.ts",
"packages/plugin-sdk/dist/packages/media-core/src/read-byte-stream-with-limit.d.ts",
"packages/plugin-sdk/dist/packages/media-core/src/read-response-with-limit.d.ts",
"packages/plugin-sdk/dist/packages/acp-core/src/runtime/types.d.ts",
"packages/plugin-sdk/dist/packages/model-catalog-core/src/configured-model-refs.d.ts",
"packages/plugin-sdk/dist/packages/model-catalog-core/src/model-catalog-normalize.d.ts",
"packages/plugin-sdk/dist/packages/model-catalog-core/src/model-catalog-refs.d.ts",

View File

@@ -11,9 +11,11 @@ const RUN_NODE_PACKAGE_SOURCE_ROOTS = [
"packages/gateway-client/src",
"packages/gateway-protocol/src",
"packages/markdown-core/src",
"packages/media-core/src",
"packages/media-generation-core/src",
"packages/media-understanding-common/src",
"packages/normalization-core/src",
"packages/acp-core/src",
"packages/terminal-core/src",
"packages/web-content-core/src",
"packages/net-policy/src",

View File

@@ -46,7 +46,9 @@ const RUNTIME_SHIMS: Partial<Record<string, string>> = {
function isBareImportSpecifier(id: string): boolean {
if (
id === "@openclaw/model-catalog-core/model-catalog-types" ||
id.startsWith("@openclaw/normalization-core/")
id.startsWith("@openclaw/normalization-core/") ||
id.startsWith("@openclaw/media-core/") ||
id.startsWith("@openclaw/acp-core/")
) {
return false;
}

View File

@@ -1,5 +1,6 @@
import { homedir } from "node:os";
import path from "node:path";
import { asRecord } from "@openclaw/acp-core/record-shared";
import {
normalizeLowercaseStringOrEmpty,
normalizeOptionalString,
@@ -7,7 +8,6 @@ import {
import { isKnownCoreToolId } from "../agents/tool-catalog.js";
import { isMutatingToolCall } from "../agents/tool-mutation.js";
import { isPathInside } from "../infra/path-guards.js";
import { asRecord } from "./record-shared.js";
const SAFE_SEARCH_TOOL_IDS = new Set(["search", "web_search", "memory_search"]);
const TRUSTED_SAFE_TOOL_ALIASES = new Set(["search"]);

View File

@@ -1,3 +1,10 @@
import type {
AcpRuntime,
AcpRuntimeCapabilities,
AcpRuntimeHandle,
AcpRuntimeSessionMode,
AcpRuntimeStatus,
} from "@openclaw/acp-core/runtime/types";
import { clampTimerTimeoutMs } from "@openclaw/normalization-core/number-coercion";
import { normalizeLowercaseStringOrEmpty } from "@openclaw/normalization-core/string-coerce";
import { resolveAgentTimeoutMs } from "../../agents/timeout.js";
@@ -32,13 +39,6 @@ import {
resolveRuntimeHandleIdentifiersFromIdentity,
resolveSessionIdentityFromMeta,
} from "../runtime/session-identity.js";
import type {
AcpRuntime,
AcpRuntimeCapabilities,
AcpRuntimeHandle,
AcpRuntimeSessionMode,
AcpRuntimeStatus,
} from "../runtime/types.js";
import { reconcileManagerRuntimeSessionIdentifiers } from "./manager.identity-reconcile.js";
import {
applyManagerRuntimeControls,

View File

@@ -1,3 +1,8 @@
import type {
AcpRuntime,
AcpRuntimeHandle,
AcpRuntimeStatus,
} from "@openclaw/acp-core/runtime/types";
import type { OpenClawConfig } from "../../config/types.openclaw.js";
import { logVerbose } from "../../globals.js";
import { withAcpRuntimeErrorBoundary } from "../runtime/errors.js";
@@ -9,7 +14,6 @@ import {
resolveRuntimeHandleIdentifiersFromIdentity,
resolveSessionIdentityFromMeta,
} from "../runtime/session-identity.js";
import type { AcpRuntime, AcpRuntimeHandle, AcpRuntimeStatus } from "../runtime/types.js";
import type { SessionAcpMeta, SessionEntry } from "./manager.types.js";
import { hasLegacyAcpIdentityProjection } from "./manager.utils.js";

View File

@@ -1,12 +1,12 @@
import { asNullableRecord } from "@openclaw/normalization-core/record-coerce";
import { normalizeLowercaseStringOrEmpty } from "@openclaw/normalization-core/string-coerce";
import { AcpRuntimeError, withAcpRuntimeErrorBoundary } from "../runtime/errors.js";
import type {
AcpRuntime,
AcpRuntimeCapabilities,
AcpRuntimeHandle,
AcpRuntimeStatus,
} from "../runtime/types.js";
} from "@openclaw/acp-core/runtime/types";
import { asNullableRecord } from "@openclaw/normalization-core/record-coerce";
import { normalizeLowercaseStringOrEmpty } from "@openclaw/normalization-core/string-coerce";
import { AcpRuntimeError, withAcpRuntimeErrorBoundary } from "../runtime/errors.js";
import type { SessionAcpMeta } from "./manager.types.js";
import { createUnsupportedControlError } from "./manager.utils.js";
import type { CachedRuntimeState } from "./runtime-cache.js";

View File

@@ -1,12 +1,12 @@
import { setTimeout as scheduleNativeTimeout } from "node:timers";
import { setTimeout as sleep } from "node:timers/promises";
import type { AcpRuntime, AcpRuntimeCapabilities } from "@openclaw/acp-core/runtime/types";
import { MAX_TIMER_TIMEOUT_MS } from "@openclaw/normalization-core/number-coercion";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import type { OpenClawConfig } from "../../config/config.js";
import type { AcpSessionRuntimeOptions, SessionAcpMeta } from "../../config/sessions/types.js";
import { resetHeartbeatWakeStateForTests } from "../../infra/heartbeat-wake.js";
import { withTempDir } from "../../test-helpers/temp-dir.js";
import type { AcpRuntime, AcpRuntimeCapabilities } from "../runtime/types.js";
const hoisted = vi.hoisted(() => {
const listAcpSessionEntriesMock = vi.fn();

View File

@@ -1,10 +1,10 @@
import { AcpRuntimeError } from "../runtime/errors.js";
import type {
AcpRuntime,
AcpRuntimeEvent,
AcpRuntimeTurnInput,
AcpRuntimeTurnResult,
} from "../runtime/types.js";
} from "@openclaw/acp-core/runtime/types";
import { AcpRuntimeError } from "../runtime/errors.js";
import { normalizeAcpErrorCode } from "./manager.utils.js";
import { normalizeText } from "./runtime-options.js";

View File

@@ -1,3 +1,12 @@
import type {
AcpRuntime,
AcpRuntimeCapabilities,
AcpRuntimeEvent,
AcpRuntimeHandle,
AcpRuntimePromptMode,
AcpRuntimeSessionMode,
AcpRuntimeStatus,
} from "@openclaw/acp-core/runtime/types";
import type {
SessionAcpIdentity,
AcpSessionRuntimeOptions,
@@ -12,15 +21,6 @@ import {
readAcpSessionEntry,
upsertAcpSessionMeta,
} from "../runtime/session-meta.js";
import type {
AcpRuntime,
AcpRuntimeCapabilities,
AcpRuntimeEvent,
AcpRuntimeHandle,
AcpRuntimePromptMode,
AcpRuntimeSessionMode,
AcpRuntimeStatus,
} from "../runtime/types.js";
export type AcpSessionResolution =
| {

View File

@@ -1,6 +1,6 @@
import type { AcpRuntime } from "@openclaw/acp-core/runtime/types";
import type { AcpRuntimeHandle } from "@openclaw/acp-core/runtime/types";
import { describe, expect, it } from "vitest";
import type { AcpRuntime } from "../runtime/types.js";
import type { AcpRuntimeHandle } from "../runtime/types.js";
import type { CachedRuntimeState } from "./runtime-cache.js";
import { RuntimeCache } from "./runtime-cache.js";

View File

@@ -1,4 +1,8 @@
import type { AcpRuntime, AcpRuntimeHandle, AcpRuntimeSessionMode } from "../runtime/types.js";
import type {
AcpRuntime,
AcpRuntimeHandle,
AcpRuntimeSessionMode,
} from "@openclaw/acp-core/runtime/types";
export type CachedRuntimeState = {
runtime: AcpRuntime;

View File

@@ -1,11 +1,11 @@
import { isAbsolute } from "node:path";
import { normalizeText } from "@openclaw/acp-core/normalize-text";
import { normalizeLowercaseStringOrEmpty } from "@openclaw/normalization-core/string-coerce";
import type { AcpSessionRuntimeOptions, SessionAcpMeta } from "../../config/sessions/types.js";
import { parseStrictPositiveInteger } from "../../infra/parse-finite-number.js";
import { normalizeText } from "../normalize-text.js";
import { AcpRuntimeError } from "../runtime/errors.js";
export { normalizeText } from "../normalize-text.js";
export { normalizeText } from "@openclaw/acp-core/normalize-text";
const MAX_RUNTIME_MODE_LENGTH = 64;
const MAX_MODEL_LENGTH = 200;

View File

@@ -5,13 +5,13 @@ import type {
ToolCallLocation,
ToolKind,
} from "@agentclientprotocol/sdk";
import { asRecord } from "@openclaw/acp-core/record-shared";
import {
hasNonEmptyString,
normalizeLowercaseStringOrEmpty,
normalizeOptionalString,
readStringValue,
} from "@openclaw/normalization-core/string-coerce";
import { asRecord } from "./record-shared.js";
type GatewayAttachment = {
type: string;

View File

@@ -1 +0,0 @@
export { normalizeOptionalString as normalizeText } from "../../packages/normalization-core/src/string-coerce.js";

View File

@@ -1,13 +1,13 @@
import { createHash } from "node:crypto";
import { normalizeText } from "@openclaw/acp-core/normalize-text";
import type { AcpRuntimeSessionMode } from "@openclaw/acp-core/runtime/types";
import { normalizeOptionalLowercaseString } from "@openclaw/normalization-core/string-coerce";
import type { ChannelId } from "../channels/plugins/types.public.js";
import type { SessionBindingRecord } from "../infra/outbound/session-binding-service.js";
import { normalizeAccountId, resolveAgentIdFromSessionKey } from "../routing/session-key.js";
import { sanitizeAgentId } from "../routing/session-key.js";
import { normalizeText } from "./normalize-text.js";
import type { AcpRuntimeSessionMode } from "./runtime/types.js";
export { normalizeText } from "./normalize-text.js";
export { normalizeText } from "@openclaw/acp-core/normalize-text";
export type ConfiguredAcpBindingChannel = ChannelId;

View File

@@ -1,8 +1,8 @@
import { randomUUID } from "node:crypto";
import type { AcpRuntime, AcpRuntimeEvent } from "@openclaw/acp-core/runtime/types";
import { normalizeOptionalString } from "@openclaw/normalization-core/string-coerce";
import { expect } from "vitest";
import { toAcpRuntimeError } from "./errors.js";
import type { AcpRuntime, AcpRuntimeEvent } from "./types.js";
export type AcpRuntimeAdapterContractParams = {
createRuntime: () => Promise<AcpRuntime> | AcpRuntime;

View File

@@ -1,3 +1,4 @@
import type { AcpRuntime } from "@openclaw/acp-core/runtime/types";
import { afterEach, beforeEach, describe, expect, it } from "vitest";
import { AcpRuntimeError } from "./errors.js";
import {
@@ -7,7 +8,6 @@ import {
requireAcpRuntimeBackend,
unregisterAcpRuntimeBackend,
} from "./registry.js";
import type { AcpRuntime } from "./types.js";
function createRuntimeStub(): AcpRuntime {
return {

View File

@@ -1,7 +1,7 @@
import type { AcpRuntime } from "@openclaw/acp-core/runtime/types";
import { normalizeOptionalLowercaseString } from "@openclaw/normalization-core/string-coerce";
import { resolveGlobalSingleton } from "../../shared/global-singleton.js";
import { AcpRuntimeError } from "./errors.js";
import type { AcpRuntime } from "./types.js";
export type AcpRuntimeBackend = {
id: string;

View File

@@ -1,6 +1,6 @@
import { normalizeText } from "@openclaw/acp-core/normalize-text";
import { normalizeLowercaseStringOrEmpty } from "@openclaw/normalization-core/string-coerce";
import type { SessionAcpIdentity, SessionAcpMeta } from "../../config/sessions/types.js";
import { normalizeText } from "../normalize-text.js";
import { isSessionIdentityPending, resolveSessionIdentityFromMeta } from "./session-identity.js";
export const ACP_SESSION_IDENTITY_RENDERER_VERSION = "v1";

View File

@@ -1,10 +1,10 @@
import { normalizeText } from "@openclaw/acp-core/normalize-text";
import type { AcpRuntimeHandle, AcpRuntimeStatus } from "@openclaw/acp-core/runtime/types";
import type {
SessionAcpIdentity,
SessionAcpIdentitySource,
SessionAcpMeta,
} from "../../config/sessions/types.js";
import { normalizeText } from "../normalize-text.js";
import type { AcpRuntimeHandle, AcpRuntimeStatus } from "./types.js";
function normalizeIdentityState(value: unknown): SessionAcpIdentity["state"] | undefined {
if (value !== "pending" && value !== "resolved") {

View File

@@ -1,5 +1,6 @@
import crypto from "node:crypto";
import fs from "node:fs/promises";
import type { AcpRuntimeSessionMode } from "@openclaw/acp-core/runtime/types";
import {
normalizeOptionalLowercaseString,
normalizeOptionalString,
@@ -15,7 +16,6 @@ import {
resolveAcpSessionCwd,
resolveAcpThreadSessionDetailLines,
} from "../acp/runtime/session-identifiers.js";
import type { AcpRuntimeSessionMode } from "../acp/runtime/types.js";
import { DEFAULT_HEARTBEAT_EVERY } from "../auto-reply/heartbeat.js";
import {
resolveChannelDefaultBindingPlacement,

View File

@@ -1,6 +1,7 @@
import fs from "node:fs/promises";
import path from "node:path";
import { URL } from "node:url";
import { detectMime } from "@openclaw/media-core/mime";
import { isWindowsDrivePath } from "../infra/archive-path.js";
import {
canonicalPathFromExistingAncestor,
@@ -9,7 +10,6 @@ import {
} from "../infra/fs-safe.js";
import { expandHomePrefix, resolveOsHomeDir } from "../infra/home-dir.js";
import { hasEncodedFileUrlSeparator, trySafeFileURLToPath } from "../infra/local-file-access.js";
import { detectMime } from "../media/mime.js";
import { sniffMimeFromBase64 } from "../media/sniff-mime-from-base64.js";
import {
REQUIRED_PARAM_GROUPS,

View File

@@ -22,6 +22,27 @@ const defaultShell = isWin
? undefined
: process.env.OPENCLAW_TEST_SHELL || resolveShellFromPath("bash") || process.env.SHELL || "sh";
function requireTextContent(
result: Awaited<ReturnType<ReturnType<typeof createExecTool>["execute"]>>,
) {
const content = result.content[0];
expect(content?.type).toBe("text");
if (content?.type !== "text") {
throw new Error(`expected text content, got ${content?.type ?? "missing"}`);
}
return content.text;
}
function requireFailedDetails(
details: Awaited<ReturnType<ReturnType<typeof createExecTool>["execute"]>>["details"],
) {
expect(details.status).toBe("failed");
if (details.status !== "failed") {
throw new Error(`expected failed details, got ${details.status}`);
}
return details;
}
describe("exec foreground failures", () => {
let envSnapshot: ReturnType<typeof captureEnv> | undefined;
@@ -49,7 +70,7 @@ describe("exec foreground failures", () => {
const tool = createExecTool({
security: "full",
ask: "off",
timeoutSec: 0.05,
timeoutSec: 1,
backgroundMs: 10,
allowBackground: false,
});
@@ -81,16 +102,11 @@ describe("exec foreground failures", () => {
});
expect(supervisorMock.spawn).toHaveBeenCalledOnce();
expect((supervisorMock.spawn.mock.calls[0]?.[0] as SpawnInput | undefined)?.timeoutMs).toBe(50);
expect(result.content[0]?.type).toBe("text");
const details = result.details as {
status?: string;
exitCode?: number | null;
aggregated?: string;
durationMs?: number;
timedOut?: boolean;
};
expect(details.status).toBe("failed");
expect(supervisorMock.spawn.mock.calls[0]?.[0]?.timeoutMs).toBe(1_000);
const text = requireTextContent(result);
expect(text).toMatch(/timed out/i);
expect(text).toMatch(/re-run with a higher timeout/i);
const details = requireFailedDetails(result.details);
expect(details.exitCode).toBeNull();
expect(details.timedOut).toBe(true);
expect(details.aggregated).toBe("");

View File

@@ -1,10 +1,10 @@
import fs from "node:fs/promises";
import path from "node:path";
import { MAX_IMAGE_BYTES } from "@openclaw/media-core/constants";
import type { ImageContent } from "openclaw/plugin-sdk/llm";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { createSolidPngBuffer } from "../../test/helpers/image-fixtures.js";
import { resolvePreferredOpenClawTmpDir } from "../infra/tmp-openclaw-dir.js";
import { MAX_IMAGE_BYTES } from "../media/constants.js";
import { escapeRegExp } from "../shared/regexp.js";
import {
buildCliArgs,

View File

@@ -2,6 +2,8 @@ import crypto from "node:crypto";
import fs from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import { MAX_IMAGE_BYTES } from "@openclaw/media-core/constants";
import { extensionForMime } from "@openclaw/media-core/mime";
import {
normalizeLowercaseStringOrEmpty,
normalizeOptionalLowercaseString,
@@ -16,8 +18,6 @@ import { privateFileStore } from "../../infra/private-file-store.js";
import { tempWorkspace } from "../../infra/private-temp-workspace.js";
import { resolvePreferredOpenClawTmpDir } from "../../infra/tmp-openclaw-dir.js";
import type { ImageContent } from "../../llm/types.js";
import { MAX_IMAGE_BYTES } from "../../media/constants.js";
import { extensionForMime } from "../../media/mime.js";
import { listRegisteredPluginAgentPromptGuidance } from "../../plugins/command-registry-state.js";
import type { EmbeddedContextFile } from "../embedded-agent-helpers.js";
import { detectImageReferences, loadImageFromRef } from "../embedded-agent-runner/run/images.js";

View File

@@ -1,6 +1,6 @@
import type { AcpRuntimeEvent } from "@openclaw/acp-core/runtime/types";
import { sanitizeForLog } from "../../../packages/terminal-core/src/ansi.js";
import { formatAcpErrorChain } from "../../acp/runtime/errors.js";
import type { AcpRuntimeEvent } from "../../acp/runtime/types.js";
import { normalizeReplyPayload } from "../../auto-reply/reply/normalize-reply.js";
import type { ThinkLevel, VerboseLevel } from "../../auto-reply/thinking.js";
import { appendSessionTranscriptMessage } from "../../config/sessions/transcript-append.js";

View File

@@ -1,5 +1,6 @@
import fs from "node:fs/promises";
import os from "node:os";
import { MAX_IMAGE_BYTES } from "@openclaw/media-core/constants";
import { normalizeOptionalString } from "@openclaw/normalization-core/string-coerce";
import { isAcpRuntimeSpawnAvailable } from "../../../acp/runtime/availability.js";
import { buildHierarchyReinforcementMessage } from "../../../auto-reply/handoff-summarizer.js";
@@ -34,7 +35,6 @@ import { resolveHeartbeatSummaryForAgent } from "../../../infra/heartbeat-summar
import { getMachineDisplayName } from "../../../infra/machine-name.js";
import { createCodexNativeWebSearchWrapper } from "../../../llm/providers/stream-wrappers/openai.js";
import type { AssistantMessage } from "../../../llm/types.js";
import { MAX_IMAGE_BYTES } from "../../../media/constants.js";
import { listRegisteredPluginAgentPromptGuidance } from "../../../plugins/command-registry-state.js";
import { getCurrentPluginMetadataSnapshot } from "../../../plugins/current-plugin-metadata-snapshot.js";
import { buildAgentHookContextChannelFields } from "../../../plugins/hook-agent-context.js";

View File

@@ -1,6 +1,6 @@
import { basenameFromAnyPath } from "@openclaw/media-core/file-name";
import { normalizeOptionalString } from "@openclaw/normalization-core/string-coerce";
import { uniqueStrings } from "@openclaw/normalization-core/string-normalization";
import { basenameFromAnyPath } from "../media/file-name.js";
export type AgentGeneratedAttachment = {
type?: "image" | "audio" | "video" | "file";

View File

@@ -1,6 +1,6 @@
import crypto from "node:crypto";
import { estimateBase64DecodedBytes } from "@openclaw/media-core/base64";
import { normalizeLowercaseStringOrEmpty } from "@openclaw/normalization-core/string-coerce";
import { estimateBase64DecodedBytes } from "../media/base64.js";
const REDACTED_IMAGE_DATA = "<redacted>";

View File

@@ -1,7 +1,7 @@
export { asFiniteNumber } from "../../packages/normalization-core/src/number-coercion.js";
import { readResponseWithLimit } from "@openclaw/media-core/read-response-with-limit";
import { normalizeOptionalString as trimToUndefined } from "../../packages/normalization-core/src/string-coerce.js";
import { redactSensitiveText } from "../logging/redact.js";
import { readResponseWithLimit } from "../media/read-response-with-limit.js";
export { asBoolean } from "../utils/boolean.js";
export { normalizeOptionalString as trimToUndefined } from "../../packages/normalization-core/src/string-coerce.js";

View File

@@ -1,5 +1,5 @@
import { sanitizeInlineImageDataUrl as sanitizeSharedInlineImageDataUrl } from "@openclaw/media-core/inline-image-data-url";
import { isRecord } from "@openclaw/normalization-core/record-coerce";
import { sanitizeInlineImageDataUrl as sanitizeSharedInlineImageDataUrl } from "../media/inline-image-data-url.js";
const IMAGE_OMITTED_TEXT = "omitted image payload: invalid inline image data";

View File

@@ -1,6 +1,7 @@
import os from "node:os";
import path from "node:path";
import { URL } from "node:url";
import { isPassThroughRemoteMediaSource } from "@openclaw/media-core/media-source-url";
import { isWindowsDrivePath } from "../infra/archive-path.js";
import {
assertNoWindowsNetworkPath,
@@ -10,7 +11,6 @@ import {
import { assertNoPathAliasEscape, type PathAliasPolicy } from "../infra/path-alias-guards.js";
import { isPathInside } from "../infra/path-guards.js";
import { resolvePreferredOpenClawTmpDir } from "../infra/tmp-openclaw-dir.js";
import { isPassThroughRemoteMediaSource } from "../media/media-source-url.js";
import { resolveConfigDir } from "../utils.js";
const UNICODE_SPACES = /[\u00A0\u2000-\u200A\u202F\u205F\u3000]/g;

View File

@@ -1,7 +1,7 @@
import { canonicalizeBase64 } from "@openclaw/media-core/base64";
import { resolveIntegerOption } from "@openclaw/normalization-core/number-coercion";
import type { ImageContent } from "../llm/types.js";
import { createSubsystemLogger } from "../logging/subsystem.js";
import { canonicalizeBase64 } from "../media/base64.js";
import {
buildImageResizeSideGrid,
getImageMetadata,

View File

@@ -1,3 +1,4 @@
import { detectMime } from "@openclaw/media-core/mime";
import {
asPositiveSafeInteger,
asSafeIntegerInRange,
@@ -6,7 +7,6 @@ import {
import { normalizeStringEntries } from "@openclaw/normalization-core/string-normalization";
import type { TSchema } from "typebox";
import { readLocalFileSafely } from "../../infra/fs-safe.js";
import { detectMime } from "../../media/mime.js";
import { readSnakeCaseParamRaw } from "../../param-key.js";
import type { ImageSanitizationLimits } from "../image-sanitization.js";
import type {

View File

@@ -1,7 +1,7 @@
import { estimateBase64DecodedBytes } from "@openclaw/media-core/base64";
import { normalizeLowercaseStringOrEmpty } from "@openclaw/normalization-core/string-coerce";
import type { OpenClawConfig } from "../../config/types.openclaw.js";
import type { AssistantMessage } from "../../llm/types.js";
import { estimateBase64DecodedBytes } from "../../media/base64.js";
import { extractAssistantText } from "../embedded-agent-utils.js";
import { isMinimaxVlmProvider } from "../minimax-vlm.js";
import { findNormalizedProviderValue, normalizeProviderId } from "../model-selection.js";

View File

@@ -2,10 +2,10 @@ import fsSync from "node:fs";
import fs from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import { isInboundPathAllowed } from "@openclaw/media-core/inbound-path-policy";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import type { OpenClawConfig } from "../../config/config.js";
import type { ModelDefinitionConfig } from "../../config/types.models.js";
import { isInboundPathAllowed } from "../../media/inbound-path-policy.js";
import { encodePngRgba, fillPixel } from "../../media/png-encode.js";
import type {
ImageDescriptionRequest,

View File

@@ -1,3 +1,4 @@
import { normalizeInboundPathRoots } from "@openclaw/media-core/inbound-path-policy";
import { normalizeProviderId } from "@openclaw/model-catalog-core/provider-id";
import {
normalizeOptionalLowercaseString,
@@ -13,7 +14,6 @@ import type { OpenClawConfig } from "../../config/types.openclaw.js";
import type { SsrFPolicy } from "../../infra/net/ssrf.js";
import type { Model } from "../../llm/types.js";
import { resolveChannelInboundAttachmentRootsForChannel } from "../../media/channel-inbound-roots.js";
import { normalizeInboundPathRoots } from "../../media/inbound-path-policy.js";
import { getDefaultLocalRoots } from "../../media/local-media-access.js";
import { readSnakeCaseParamRaw } from "../../param-key.js";
import { loadCapabilityManifestSnapshot } from "../../plugins/capability-provider-runtime.js";

View File

@@ -1,4 +1,5 @@
import crypto from "node:crypto";
import { imageMimeFromFormat } from "@openclaw/media-core/mime";
import { normalizeLowercaseStringOrEmpty } from "@openclaw/normalization-core/string-coerce";
import {
type CameraFacing,
@@ -14,7 +15,6 @@ import {
writeScreenRecordToFile,
} from "../../cli/nodes-screen.js";
import { parseDurationMs } from "../../cli/parse-duration.js";
import { imageMimeFromFormat } from "../../media/mime.js";
import type { ImageSanitizationLimits } from "../image-sanitization.js";
import type { AgentToolResult } from "../runtime/index.js";
import { sanitizeToolResultImages } from "../tool-images.js";

View File

@@ -175,6 +175,9 @@ describe("skill_workshop tool", () => {
status: "pending",
query: "!!!",
});
expect((punctuationOnly.content[0] as { text: string }).text).toBe(
"No skill proposals matched.",
);
expect((punctuationOnly.details as { proposals: unknown[] }).proposals).toEqual([]);
const inspected = await tool.execute("call-4", {

View File

@@ -1,6 +1,6 @@
import { MAX_VIDEO_BYTES } from "@openclaw/media-core/constants";
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
import type { OpenClawConfig } from "../../config/config.js";
import { MAX_VIDEO_BYTES } from "../../media/constants.js";
import * as mediaStore from "../../media/store.js";
import * as webMedia from "../../media/web-media.js";
import {

View File

@@ -1,8 +1,8 @@
import type { AcpRuntimeEvent, AcpSessionUpdateTag } from "@openclaw/acp-core/runtime/types";
import {
normalizeOptionalLowercaseString,
normalizeOptionalString,
} from "@openclaw/normalization-core/string-coerce";
import type { AcpRuntimeEvent, AcpSessionUpdateTag } from "../../acp/runtime/types.js";
import { EmbeddedBlockChunker } from "../../agents/embedded-agent-block-chunker.js";
import { formatToolSummary, resolveToolDisplay } from "../../agents/tool-display.js";
import type { OpenClawConfig } from "../../config/types.openclaw.js";

View File

@@ -1,4 +1,4 @@
import type { AcpSessionUpdateTag } from "../../acp/runtime/types.js";
import type { AcpSessionUpdateTag } from "@openclaw/acp-core/runtime/types";
import type { OpenClawConfig } from "../../config/types.openclaw.js";
import { clampPositiveInteger, resolveEffectiveBlockStreamingConfig } from "./block-streaming.js";

View File

@@ -1,9 +1,9 @@
import { isAudioFileName } from "@openclaw/media-core/mime";
import {
hasOutboundReplyContent,
resolveSendableOutboundReplyParts,
} from "openclaw/plugin-sdk/reply-payload";
import { loadSessionStore } from "../../config/sessions.js";
import { isAudioFileName } from "../../media/mime.js";
import { normalizeVerboseLevel, type VerboseLevel } from "../thinking.js";
import type { ReplyPayload } from "../types.js";
import type { TypingSignaler } from "./typing-mode.js";

View File

@@ -1,11 +1,11 @@
import { randomUUID } from "node:crypto";
import type { AcpRuntimeSessionMode } from "@openclaw/acp-core/runtime/types";
import {
normalizeOptionalLowercaseString,
normalizeOptionalString,
} from "@openclaw/normalization-core/string-coerce";
import { toAcpRuntimeErrorText } from "../../../acp/runtime/error-text.js";
import type { AcpRuntimeError } from "../../../acp/runtime/errors.js";
import type { AcpRuntimeSessionMode } from "../../../acp/runtime/types.js";
import { supportsAutomaticThreadBindingSpawn } from "../../../channels/thread-bindings-policy.js";
import type { AcpSessionRuntimeOptions } from "../../../config/sessions/types.js";
import { normalizeAgentId } from "../../../routing/session-key.js";

View File

@@ -1,9 +1,9 @@
import { mimeTypeFromFilePath } from "@openclaw/media-core/mime";
import { normalizeOptionalString } from "@openclaw/normalization-core/string-coerce";
import type { OpenClawConfig } from "../../config/types.openclaw.js";
import { logVerbose } from "../../globals.js";
import { formatErrorMessage } from "../../infra/errors.js";
import type { ImageContent } from "../../llm/types.js";
import { mimeTypeFromFilePath } from "../../media/mime.js";
import type { PromptImageOrderEntry } from "../../media/prompt-image-order.js";
import type { MsgContext } from "../templating.js";
import { resolveAgentTurnAttachments } from "./agent-turn-attachments.js";

View File

@@ -1,6 +1,6 @@
import { mimeTypeFromFilePath } from "@openclaw/media-core/mime";
import { asFiniteNumber } from "@openclaw/normalization-core/number-coercion";
import { normalizeOptionalString } from "@openclaw/normalization-core/string-coerce";
import { mimeTypeFromFilePath } from "../../media/mime.js";
import type { MsgContext } from "../templating.js";
import type { HistoryEntry, HistoryMediaEntry } from "./history.types.js";

View File

@@ -1,4 +1,5 @@
import path from "node:path";
import { isPassThroughRemoteMediaSource } from "@openclaw/media-core/media-source-url";
import { resolveSendableOutboundReplyParts } from "openclaw/plugin-sdk/reply-payload";
import { resolveSessionAgentId } from "../../agents/agent-scope.js";
import { resolvePathFromInput, toRelativeWorkspacePath } from "../../agents/path-policy.js";
@@ -11,7 +12,6 @@ import { ensureSandboxWorkspaceForSession } from "../../agents/sandbox.js";
import type { OpenClawConfig } from "../../config/types.openclaw.js";
import { logVerbose } from "../../globals.js";
import { resolveChannelAccountMediaMaxMb } from "../../media/configured-max-bytes.js";
import { isPassThroughRemoteMediaSource } from "../../media/media-source-url.js";
import { resolveOutboundAttachmentFromUrl } from "../../media/outbound-attachment.js";
import { resolveAgentScopedOutboundMediaAccess } from "../../media/read-capability.js";
import { MEDIA_MAX_BYTES } from "../../media/store.js";

View File

@@ -2,6 +2,7 @@ import { spawn } from "node:child_process";
import fs from "node:fs/promises";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { isInboundPathAllowed } from "@openclaw/media-core/inbound-path-policy";
import { normalizeOptionalString } from "@openclaw/normalization-core/string-coerce";
import { assertSandboxPath } from "../../agents/sandbox-paths.js";
import { ensureSandboxWorkspaceForSession } from "../../agents/sandbox.js";
@@ -12,7 +13,6 @@ import { root as fsRoot, FsSafeError } from "../../infra/fs-safe.js";
import { normalizeScpRemoteHost, normalizeScpRemotePath } from "../../infra/scp-host.js";
import { resolvePreferredOpenClawTmpDir } from "../../infra/tmp-openclaw-dir.js";
import { resolveChannelRemoteInboundAttachmentRoots } from "../../media/channel-inbound-roots.js";
import { isInboundPathAllowed } from "../../media/inbound-path-policy.js";
import { resolveInboundMediaReference } from "../../media/media-reference.js";
import { getMediaDir, MEDIA_MAX_BYTES } from "../../media/store.js";
import { CONFIG_DIR } from "../../utils.js";

View File

@@ -1,4 +1,4 @@
import { setImmediate as realSetImmediate } from "node:timers";
import { setImmediate as nextMacrotask } from "node:timers/promises";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { MAX_TIMER_TIMEOUT_MS } from "../shared/number-coercion.js";
import { createDraftStreamLoop } from "./draft-stream-loop.js";
@@ -9,7 +9,7 @@ const flushMicrotasks = async () => {
};
const flushMacrotask = async () => {
await new Promise<void>((resolve) => realSetImmediate(resolve));
await nextMacrotask();
};
async function waitForBackgroundFlushError(

View File

@@ -32,6 +32,10 @@ function isMentionPatternsPolicyConfig(value: unknown): value is MentionPatterns
return value != null && typeof value === "object" && !Array.isArray(value);
}
function isRecord(value: unknown): value is Record<string, unknown> {
return value != null && typeof value === "object" && !Array.isArray(value);
}
function resolveProviderMentionPatternsPolicy(
cfg: OpenClawConfig | undefined,
provider: string | undefined,
@@ -39,7 +43,8 @@ function resolveProviderMentionPatternsPolicy(
if (!cfg || !provider) {
return undefined;
}
const policy = cfg.channels?.[provider]?.mentionPatterns;
const channelConfig = cfg.channels?.[provider];
const policy = isRecord(channelConfig) ? channelConfig.mentionPatterns : undefined;
return isMentionPatternsPolicyConfig(policy) ? policy : undefined;
}

View File

@@ -11,7 +11,7 @@ type ChannelConfigWithAccounts = {
};
type ConfigWritePolicyConfig = {
channels?: Record<string, ChannelConfigWithAccounts>;
channels?: Record<string, unknown>;
};
export type ConfigWriteScopeLike<TChannelId extends string = string> = {
@@ -55,7 +55,10 @@ function resolveChannelConfig(
if (!channelId) {
return undefined;
}
return cfg.channels?.[channelId];
const channelConfig = cfg.channels?.[channelId];
return channelConfig != null && typeof channelConfig === "object" && !Array.isArray(channelConfig)
? (channelConfig as ChannelConfigWithAccounts)
: undefined;
}
function resolveChannelAccountConfig(

View File

@@ -25,6 +25,18 @@ type DirectSendFn<TOpts extends Record<string, unknown>, TResult extends DirectS
text: string,
opts: TOpts,
) => Promise<TResult>;
function asRecord(value: unknown): Record<string, unknown> | undefined {
return value != null && typeof value === "object" && !Array.isArray(value)
? (value as Record<string, unknown>)
: undefined;
}
function readNumberField(record: Record<string, unknown> | undefined, key: string) {
const value = record?.[key];
return typeof value === "number" ? value : undefined;
}
export {
resolvePayloadMediaUrls,
sendPayloadMediaSequence,
@@ -50,9 +62,14 @@ export function createScopedChannelMediaMaxBytesResolver(channel: string) {
resolveScopedChannelMediaMaxBytes({
cfg: params.cfg,
accountId: params.accountId,
resolveChannelLimitMb: ({ cfg, accountId }) =>
(cfg.channels?.[channel]?.accounts?.[accountId] as { mediaMaxMb?: number } | undefined)
?.mediaMaxMb ?? cfg.channels?.[channel]?.mediaMaxMb,
resolveChannelLimitMb: ({ cfg, accountId }) => {
const channelConfig = asRecord(cfg.channels?.[channel]);
const accountConfig = asRecord(asRecord(channelConfig?.accounts)?.[accountId]);
return (
readNumberField(accountConfig, "mediaMaxMb") ??
readNumberField(channelConfig, "mediaMaxMb")
);
},
});
}

View File

@@ -299,7 +299,7 @@ function rebindChannelConfig(
...cfg,
channels: {
...cfg.channels,
[sourceChannelId]: (cfg.channels as Record<string, unknown>)[targetChannelId],
[sourceChannelId]: cfg.channels[targetChannelId],
},
};
}

View File

@@ -32,6 +32,20 @@ function loadProviderAuthInput() {
return providerAuthInputPromise;
}
function asRecord(value: unknown): Record<string, unknown> | undefined {
return value != null && typeof value === "object" && !Array.isArray(value)
? (value as Record<string, unknown>)
: undefined;
}
function asAllowFromList(value: unknown): ReadonlyArray<string | number> | undefined {
return Array.isArray(value)
? value.filter(
(entry): entry is string | number => typeof entry === "string" || typeof entry === "number",
)
: undefined;
}
export const promptAccountId: PromptAccountId = async (params: PromptAccountIdParams) => {
const existingIds = params.listAccountIds(params.cfg);
const initial = params.currentId?.trim() || params.defaultAccountId || DEFAULT_ACCOUNT_ID;
@@ -537,14 +551,17 @@ export function setChannelDmPolicyWithAllowFrom(params: {
dmPolicy: DmPolicy;
}): OpenClawConfig {
const { cfg, channel, dmPolicy } = params;
const channelConfig = asRecord(cfg.channels?.[channel]);
const allowFrom =
dmPolicy === "open" ? addWildcardAllowFrom(cfg.channels?.[channel]?.allowFrom) : undefined;
dmPolicy === "open"
? addWildcardAllowFrom(asAllowFromList(channelConfig?.allowFrom))
: undefined;
return {
...cfg,
channels: {
...cfg.channels,
[channel]: {
...cfg.channels?.[channel],
...channelConfig,
dmPolicy,
...(allowFrom ? { allowFrom } : {}),
},

View File

@@ -80,14 +80,6 @@ describe("createTypingCallbacks", () => {
vi.useRealTimers();
});
afterEach(() => {
if (vi.isFakeTimers()) {
vi.clearAllTimers();
}
vi.useRealTimers();
vi.restoreAllMocks();
});
it("invokes start on reply start", async () => {
const { start, onStartError, callbacks } = createTypingHarness();

View File

@@ -4,6 +4,7 @@ import fs from "node:fs/promises";
import path from "node:path";
import { Readable } from "node:stream";
import { pipeline } from "node:stream/promises";
import { detectMime, extensionForMime, normalizeMimeType } from "@openclaw/media-core/mime";
import {
normalizeLowercaseStringOrEmpty,
normalizeOptionalString,
@@ -61,7 +62,6 @@ import {
transcribeAudioFile,
} from "../media-understanding/runtime.js";
import { convertHeicToJpeg, getImageMetadata } from "../media/media-services.js";
import { detectMime, extensionForMime, normalizeMimeType } from "../media/mime.js";
import { saveMediaBuffer } from "../media/store.js";
import {
createEmbeddingProvider,

View File

@@ -1,4 +1,4 @@
import type { AcpSessionUpdateTag } from "../acp/runtime/types.js";
import type { AcpSessionUpdateTag } from "@openclaw/acp-core/runtime/types";
export type AcpDispatchConfig = {
/** Master switch for ACP turn dispatch in the reply pipeline. */

View File

@@ -38,6 +38,16 @@ export type ExtensionNestedPolicyConfig = {
[key: string]: unknown;
};
export type ExtensionAccountConfig = ExtensionNestedPolicyConfig & {
defaultTo?: string | number;
dmPolicy?: string;
dm?: ExtensionNestedPolicyConfig;
mediaMaxMb?: number;
configWrites?: boolean;
};
type OpenWorldChannelConfig = ReturnType<typeof JSON.parse>;
/**
* Base type for extension channel config sections.
* Extensions can use this as a starting point for their channel config.
@@ -51,7 +61,7 @@ export type ExtensionChannelConfig = {
defaultAccount?: string;
dmPolicy?: string;
groupPolicy?: GroupPolicy;
mentionPatterns?: MentionPatternsPolicyConfig;
mentionPatterns?: MentionPatternsPolicyConfig | string[];
contextVisibility?: ContextVisibilityMode;
healthMonitor?: ChannelHealthMonitorConfig;
dm?: ExtensionNestedPolicyConfig;
@@ -74,7 +84,7 @@ export type ExtensionChannelConfig = {
botLoopProtection?: ChannelBotLoopProtectionConfig;
spawnSubagentSessions?: boolean;
dangerouslyAllowPrivateNetwork?: boolean;
accounts?: Record<string, unknown>;
accounts?: Record<string, ExtensionAccountConfig>;
[key: string]: unknown;
};
@@ -93,10 +103,7 @@ export interface ChannelsConfig {
whatsapp?: WhatsAppConfig;
/**
* Channel sections are plugin-owned and keyed by arbitrary channel ids.
* Keep the lookup permissive so augmented channel configs remain ergonomic at call sites.
* Open-world config keeps SDK/plugin-owned sections ergonomic for dynamic ids.
*/
// Plugin-owned channel sections are open-world config; narrowing this breaks
// SDK config-write helpers that accept account-shaped channel records.
// oxlint-disable-next-line typescript/no-explicit-any
[key: string]: any;
[key: string]: OpenWorldChannelConfig;
}

View File

@@ -1,7 +1,7 @@
import { isValidInboundPathRootPattern } from "@openclaw/media-core/inbound-path-policy";
import { normalizeOptionalString } from "@openclaw/normalization-core/string-coerce";
import { z } from "zod";
import { isSafeScpRemoteHost } from "../infra/scp-host.js";
import { isValidInboundPathRootPattern } from "../media/inbound-path-policy.js";
import {
normalizeCommandDescription,
normalizeSlashCommandName,

View File

@@ -1,3 +1,4 @@
import { isAudioFileName } from "@openclaw/media-core/mime";
import { normalizeOptionalString } from "@openclaw/normalization-core/string-coerce";
import { retireSessionMcpRuntime } from "../../agents/agent-bundle-mcp-tools.js";
import type { ReplyPayload } from "../../auto-reply/reply-payload.js";
@@ -31,7 +32,6 @@ import {
import type { SourceDeliveryOutcome } from "../../infra/outbound/source-delivery-plan.js";
import { normalizeTargetForProvider } from "../../infra/outbound/target-normalization.js";
import { hasReplyPayloadContent } from "../../interactive/payload.js";
import { isAudioFileName } from "../../media/mime.js";
import { stringifyRouteThreadId } from "../../plugin-sdk/channel-route.js";
import {
isCronSessionKey,

View File

@@ -21,8 +21,8 @@ vi.mock("../media/store.js", async (importOriginal) => {
};
});
import { MAX_IMAGE_BYTES } from "@openclaw/media-core/constants";
import type { OpenClawConfig } from "../config/types.openclaw.js";
import { MAX_IMAGE_BYTES } from "../media/constants.js";
import {
buildMessageWithAttachments,
type ChatAttachment,

View File

@@ -1,9 +1,9 @@
import { estimateBase64DecodedBytes } from "@openclaw/media-core/base64";
import { MAX_IMAGE_BYTES } from "@openclaw/media-core/constants";
import { extensionForMime, mimeTypeFromFilePath } from "@openclaw/media-core/mime";
import { normalizeOptionalLowercaseString } from "@openclaw/normalization-core/string-coerce";
import type { OpenClawConfig } from "../config/types.openclaw.js";
import { formatErrorMessage } from "../infra/errors.js";
import { estimateBase64DecodedBytes } from "../media/base64.js";
import { MAX_IMAGE_BYTES } from "../media/constants.js";
import { extensionForMime, mimeTypeFromFilePath } from "../media/mime.js";
import type { PromptImageOrderEntry } from "../media/prompt-image-order.js";
import { sniffMimeFromBase64 } from "../media/sniff-mime-from-base64.js";
import { deleteMediaBuffer, saveMediaBuffer } from "../media/store.js";

Some files were not shown because too many files have changed in this diff Show More