refactor(packages): DM-policy contract suite, dead routes, package folds (#114776)

* test(channels): share DM policy contract suite

* refactor(memory): remove dead host CLI routes

* refactor(acp): use canonical normalization helpers

* refactor(web): fold provider runtime into core

* refactor(packages): remove dead runtime exports

* chore(knip): drop retired ACP entry roots
This commit is contained in:
Peter Steinberger
2026-07-27 20:20:13 -04:00
committed by GitHub
parent 83fa3ce993
commit a1e0c9ea09
49 changed files with 288 additions and 853 deletions
@@ -20,7 +20,7 @@ paths:
- src/agents/tools/web-shared.ts
- src/plugin-sdk/ssrf-policy.ts
- src/web-fetch
- packages/web-content-core/src/provider-runtime-shared.ts
- src/web/provider-runtime-shared.ts
- packages/memory-host-sdk/src/host/ssrf-policy.ts
- packages/net-policy/src
@@ -16,7 +16,7 @@ query-filters:
paths:
- src/web-fetch
- src/web-search
- packages/web-content-core/src/provider-runtime-shared.ts
- src/web/provider-runtime-shared.ts
- src/media
- src/media-understanding
- src/image-generation
-1
View File
@@ -243,7 +243,6 @@
"packages/terminal-core/**/*.ts",
"packages/normalization-core/**/*.ts",
"packages/model-catalog-core/**/*.ts",
"packages/web-content-core/**/*.ts",
"packages/agent-core/**/*.ts",
"packages/acp-core/**/*.ts",
"packages/ai/**/*.ts",
-3
View File
@@ -562,10 +562,7 @@ const config = {
"packages/acp-core": {
entry: [
"src/index.ts!",
"src/normalize-text.ts!",
"src/meta.ts!",
"src/numeric-options.ts!",
"src/record-shared.ts!",
"src/session.ts!",
"src/session-interaction-mode.ts!",
"src/session-lineage-meta.ts!",
+11 -48
View File
@@ -1,4 +1,5 @@
// Discord tests cover setup surface plugin behavior.
import { installChannelDmPolicyContractSuite } from "openclaw/plugin-sdk/channel-test-helpers";
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-contracts";
import { describe, expect, it } from "vitest";
import { createDiscordSetupWizardBase } from "./setup-core.js";
@@ -19,55 +20,17 @@ const discordSetupWizard = createDiscordSetupWizardBase({
});
describe("discordSetupWizard.dmPolicy", () => {
it("reads the named-account DM policy instead of the channel root", () => {
expect(
discordSetupWizard.dmPolicy?.getCurrent(
{
channels: {
discord: {
dmPolicy: "disabled",
accounts: {
alerts: {
dmPolicy: "allowlist",
token: "discord-token",
},
},
},
},
} as OpenClawConfig,
"alerts",
),
).toBe("allowlist");
});
it("reports account-scoped config keys for named accounts", () => {
expect(discordSetupWizard.dmPolicy?.resolveConfigKeys?.({}, "alerts")).toEqual({
policyKey: "channels.discord.accounts.alerts.dmPolicy",
allowFromKey: "channels.discord.accounts.alerts.allowFrom",
});
});
it('writes open policy state to the named account and preserves inherited allowFrom with "*"', () => {
const next = discordSetupWizard.dmPolicy?.setPolicy(
installChannelDmPolicyContractSuite({
dmPolicy: discordSetupWizard.dmPolicy!,
cases: [
{
channels: {
discord: {
allowFrom: ["123"],
accounts: {
alerts: {
token: "discord-token",
},
},
},
},
} as OpenClawConfig,
"open",
"alerts",
);
expect(next?.channels?.discord?.dmPolicy).toBeUndefined();
expect(next?.channels?.discord?.accounts?.alerts?.dmPolicy).toBe("open");
expect(next?.channels?.discord?.accounts?.alerts?.allowFrom).toEqual(["123", "*"]);
name: "Discord named accounts",
channel: "discord",
accountId: "alerts",
accountConfig: { token: "discord-token" },
inheritedAllowFrom: ["123"],
},
],
});
});
+13 -76
View File
@@ -6,6 +6,7 @@ import {
createStartAccountContext,
expectLifecyclePatch,
expectPendingUntilAbort,
installChannelDmPolicyContractSuite,
startAccountAndTrackLifecycle,
} from "openclaw/plugin-sdk/channel-test-helpers";
import {
@@ -252,25 +253,18 @@ describe("googlechat setup", () => {
expect(result.cfg.channels?.googlechat?.audience).toBe("https://example.com/googlechat");
});
it("reads the named-account DM policy instead of the channel root", () => {
expect(
googlechatSetupWizard.dmPolicy?.getCurrent(
{
channels: {
googlechat: {
dmPolicy: "disabled",
accounts: {
alerts: {
serviceAccount: { client_email: "bot@example.com" },
dmPolicy: "allowlist",
},
},
},
},
} as OpenClawConfig,
"alerts",
),
).toBe("allowlist");
installChannelDmPolicyContractSuite({
dmPolicy: googlechatSetupWizard.dmPolicy!,
cases: [
{
name: "Google Chat named accounts",
channel: "googlechat",
accountId: "alerts",
accountConfig: { serviceAccount: { client_email: "bot@example.com" } },
inheritedAllowFrom: ["users/123"],
defaultAccount: {},
},
],
});
it("reports configured state for the selected account instead of any account", async () => {
@@ -318,40 +312,6 @@ describe("googlechat setup", () => {
expect(status.configured).toBe(false);
});
it("reports account-scoped config keys for named accounts", () => {
expect(googlechatSetupWizard.dmPolicy?.resolveConfigKeys?.({}, "alerts")).toEqual({
policyKey: "channels.googlechat.accounts.alerts.dmPolicy",
allowFromKey: "channels.googlechat.accounts.alerts.allowFrom",
});
});
it("uses configured defaultAccount for omitted DM policy account context", () => {
const cfg = {
channels: {
googlechat: {
defaultAccount: "alerts",
dmPolicy: "disabled",
accounts: {
alerts: {
serviceAccount: { client_email: "bot@example.com" },
dmPolicy: "allowlist",
},
},
},
},
} as OpenClawConfig;
expect(googlechatSetupWizard.dmPolicy?.getCurrent(cfg)).toBe("allowlist");
expect(googlechatSetupWizard.dmPolicy?.resolveConfigKeys?.(cfg)).toEqual({
policyKey: "channels.googlechat.accounts.alerts.dmPolicy",
allowFromKey: "channels.googlechat.accounts.alerts.allowFrom",
});
const next = googlechatSetupWizard.dmPolicy?.setPolicy(cfg, "open");
expect(next?.channels?.googlechat?.dmPolicy).toBe("disabled");
expect(next?.channels?.googlechat?.accounts?.alerts?.dmPolicy).toBe("open");
});
it("uses configured defaultAccount for omitted allowFrom prompt context", async () => {
const prompter = createTestWizardPrompter({
note: vi.fn(async () => {}),
@@ -380,29 +340,6 @@ describe("googlechat setup", () => {
expect(next?.channels?.googlechat?.accounts?.alerts?.allowFrom).toEqual(["users/123456789"]);
});
it('writes open DM policy to the named account and preserves inherited allowFrom with "*"', () => {
const next = googlechatSetupWizard.dmPolicy?.setPolicy(
{
channels: {
googlechat: {
allowFrom: ["users/123"],
accounts: {
alerts: {
serviceAccount: { client_email: "bot@example.com" },
},
},
},
},
} as OpenClawConfig,
"open",
"alerts",
);
expect(next?.channels?.googlechat?.dmPolicy).toBeUndefined();
expect(next?.channels?.googlechat?.accounts?.alerts?.dmPolicy).toBe("open");
expect(next?.channels?.googlechat?.accounts?.alerts?.allowFrom).toEqual(["users/123", "*"]);
});
it("keeps startAccount pending until abort, then unregisters", async () => {
const unregister = vi.fn();
hoisted.startGoogleChatMonitor.mockResolvedValue(unregister);
+14 -79
View File
@@ -1,5 +1,6 @@
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-contracts";
// Imessage tests cover targets plugin behavior.
import { installChannelDmPolicyContractSuite } from "openclaw/plugin-sdk/channel-test-helpers";
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-contracts";
import { describe, expect, it } from "vitest";
import {
resolveIMessageGroupRequireMention,
@@ -254,86 +255,20 @@ describe("parseIMessageAllowFromEntries", () => {
});
});
it("reads the named-account DM policy instead of the channel root", () => {
expect(
imessageDmPolicy.getCurrent(
{
channels: {
imessage: {
dmPolicy: "disabled",
accounts: {
work: {
cliPath: "imsg",
dmPolicy: "allowlist",
},
},
},
},
},
"work",
),
).toBe("allowlist");
});
it("reports account-scoped config keys for named accounts", () => {
expect(imessageDmPolicy.resolveConfigKeys?.({ channels: { imessage: {} } }, "work")).toEqual({
policyKey: "channels.imessage.accounts.work.dmPolicy",
allowFromKey: "channels.imessage.accounts.work.allowFrom",
});
});
it('writes open policy state to the named account and stores inherited allowFrom with "*"', () => {
const next = imessageDmPolicy.setPolicy(
installChannelDmPolicyContractSuite({
dmPolicy: imessageDmPolicy,
cases: [
{
channels: {
imessage: {
allowFrom: ["+15555550123"],
accounts: {
work: {
cliPath: "imsg",
},
},
},
name: "iMessage named accounts",
channel: "imessage",
accountId: "work",
accountConfig: { cliPath: "imsg" },
inheritedAllowFrom: ["+15555550123"],
defaultAccount: {
rootAllowFrom: ["+15555550123"],
accountAllowFrom: ["chat_id:123"],
},
},
"open",
"work",
);
expect(next.channels?.imessage?.dmPolicy).toBeUndefined();
expect(next.channels?.imessage?.allowFrom).toEqual(["+15555550123"]);
expect(next.channels?.imessage?.accounts?.work?.dmPolicy).toBe("open");
expect(next.channels?.imessage?.accounts?.work?.allowFrom).toEqual(["+15555550123", "*"]);
});
it("uses the configured default account for omitted-account DM policy reads, keys, and writes", () => {
const cfg = {
channels: {
imessage: {
allowFrom: ["+15555550123"],
defaultAccount: "work",
accounts: {
work: {
cliPath: "imsg",
dmPolicy: "allowlist" as const,
allowFrom: ["chat_id:123"],
},
},
},
},
};
expect(imessageDmPolicy.getCurrent(cfg)).toBe("allowlist");
expect(imessageDmPolicy.resolveConfigKeys?.(cfg)).toEqual({
policyKey: "channels.imessage.accounts.work.dmPolicy",
allowFromKey: "channels.imessage.accounts.work.allowFrom",
});
const next = imessageDmPolicy.setPolicy(cfg, "open");
expect(next.channels?.imessage?.dmPolicy).toBeUndefined();
expect(next.channels?.imessage?.allowFrom).toEqual(["+15555550123"]);
expect(next.channels?.imessage?.accounts?.work?.dmPolicy).toBe("open");
expect(next.channels?.imessage?.accounts?.work?.allowFrom).toEqual(["chat_id:123", "*"]);
],
});
});
+15 -92
View File
@@ -1,7 +1,10 @@
// Line tests cover setup surface plugin behavior.
import { readFileSync } from "node:fs";
import path from "node:path";
import { createStartAccountContext } from "openclaw/plugin-sdk/channel-test-helpers";
import {
createStartAccountContext,
installChannelDmPolicyContractSuite,
} from "openclaw/plugin-sdk/channel-test-helpers";
import {
createPluginSetupWizardConfigure,
createTestWizardPrompter,
@@ -186,98 +189,18 @@ describe("line setup wizard", () => {
expect(result.cfg.channels?.line?.channelSecret).toBe("line-secret");
});
it("reads the named-account DM policy instead of the channel root", () => {
expect(
lineSetupWizard.dmPolicy?.getCurrent(
{
channels: {
line: {
dmPolicy: "disabled",
accounts: {
work: {
channelAccessToken: "token",
channelSecret: "secret",
dmPolicy: "allowlist",
},
},
},
},
} as OpenClawConfig,
"work",
),
).toBe("allowlist");
});
it("reports account-scoped config keys for named accounts", () => {
expect(lineSetupWizard.dmPolicy?.resolveConfigKeys?.({} as OpenClawConfig, "work")).toEqual({
policyKey: "channels.line.accounts.work.dmPolicy",
allowFromKey: "channels.line.accounts.work.allowFrom",
});
});
it("uses configured defaultAccount for omitted DM policy account context", () => {
const cfg = {
channels: {
line: {
defaultAccount: "work",
dmPolicy: "disabled",
allowFrom: ["Uroot"],
accounts: {
work: {
channelAccessToken: "token",
channelSecret: "secret",
dmPolicy: "allowlist",
},
},
},
},
} as OpenClawConfig;
expect(lineSetupWizard.dmPolicy?.getCurrent(cfg)).toBe("allowlist");
expect(lineSetupWizard.dmPolicy?.resolveConfigKeys?.(cfg)).toEqual({
policyKey: "channels.line.accounts.work.dmPolicy",
allowFromKey: "channels.line.accounts.work.allowFrom",
});
const next = lineSetupWizard.dmPolicy?.setPolicy(cfg, "open");
const workAccount = next?.channels?.line?.accounts?.work as
| {
dmPolicy?: string;
}
| undefined;
expect(next?.channels?.line?.dmPolicy).toBe("disabled");
expect(workAccount?.dmPolicy).toBe("open");
});
it('writes open policy state to the named account and preserves inherited allowFrom with "*"', () => {
const next = lineSetupWizard.dmPolicy?.setPolicy(
installChannelDmPolicyContractSuite({
dmPolicy: lineSetupWizard.dmPolicy!,
cases: [
{
channels: {
line: {
allowFrom: ["Uroot"],
accounts: {
work: {
channelAccessToken: "token",
channelSecret: "secret",
},
},
},
},
} as OpenClawConfig,
"open",
"work",
);
const workAccount = next?.channels?.line?.accounts?.work as
| {
dmPolicy?: string;
allowFrom?: string[];
}
| undefined;
expect(next?.channels?.line?.dmPolicy).toBeUndefined();
expect(next?.channels?.line?.allowFrom).toEqual(["Uroot"]);
expect(workAccount?.dmPolicy).toBe("open");
expect(workAccount?.allowFrom).toEqual(["Uroot", "*"]);
name: "LINE named accounts",
channel: "line",
accountId: "work",
accountConfig: { channelAccessToken: "token", channelSecret: "secret" },
inheritedAllowFrom: ["Uroot"],
defaultAccount: { rootAllowFrom: ["Uroot"] },
},
],
});
it("uses configured defaultAccount for omitted setup configured state", async () => {
+12 -77
View File
@@ -4,6 +4,7 @@ import {
createMessageReceiptFromOutboundResults,
verifyChannelMessageAdapterCapabilityProofs,
} from "openclaw/plugin-sdk/channel-outbound";
import { installChannelDmPolicyContractSuite } from "openclaw/plugin-sdk/channel-test-helpers";
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-contracts";
import {
createPluginSetupWizardStatus,
@@ -1270,84 +1271,18 @@ describe("signal setup parsing", () => {
]);
});
it("reads the named-account DM policy instead of the channel root", () => {
expect(
signalDmPolicy.getCurrent(
{
channels: {
signal: {
dmPolicy: "disabled",
accounts: {
work: {
account: "+15555550123",
dmPolicy: "allowlist",
},
},
},
},
},
"work",
),
).toBe("allowlist");
});
it("reports account-scoped config keys for named accounts", () => {
expect(signalDmPolicy.resolveConfigKeys?.({ channels: { signal: {} } }, "work")).toEqual({
policyKey: "channels.signal.accounts.work.dmPolicy",
allowFromKey: "channels.signal.accounts.work.allowFrom",
});
});
it("uses configured defaultAccount for omitted DM policy account context", () => {
const cfg: OpenClawConfig = {
channels: {
signal: {
defaultAccount: "work",
dmPolicy: "disabled",
allowFrom: ["+15555550123"],
accounts: {
work: {
account: "+15555550999",
dmPolicy: "allowlist",
},
},
},
installChannelDmPolicyContractSuite({
dmPolicy: signalDmPolicy,
cases: [
{
name: "Signal named accounts",
channel: "signal",
accountId: "work",
accountConfig: { account: "+15555550999" },
inheritedAllowFrom: ["+15555550123"],
defaultAccount: { rootAllowFrom: ["+15555550123"] },
},
};
expect(signalDmPolicy.getCurrent(cfg)).toBe("allowlist");
expect(signalDmPolicy.resolveConfigKeys?.(cfg)).toEqual({
policyKey: "channels.signal.accounts.work.dmPolicy",
allowFromKey: "channels.signal.accounts.work.allowFrom",
});
const next = signalDmPolicy.setPolicy(cfg, "open");
expect(next.channels?.signal?.dmPolicy).toBe("disabled");
expect(next.channels?.signal?.allowFrom).toEqual(["+15555550123"]);
expect(next.channels?.signal?.accounts?.work?.dmPolicy).toBe("open");
expect(next.channels?.signal?.accounts?.work?.allowFrom).toEqual(["+15555550123", "*"]);
});
it('writes open policy state to the named account and stores inherited allowFrom with "*"', () => {
const cfg: OpenClawConfig = {
channels: {
signal: {
allowFrom: ["+15555550123"],
accounts: {
work: {
account: "+15555550999",
},
},
},
},
};
const next = signalDmPolicy.setPolicy(cfg, "open", "work");
expect(next.channels?.signal?.dmPolicy).toBeUndefined();
expect(next.channels?.signal?.allowFrom).toEqual(["+15555550123"]);
expect(next.channels?.signal?.accounts?.work?.dmPolicy).toBe("open");
expect(next.channels?.signal?.accounts?.work?.allowFrom).toEqual(["+15555550123", "*"]);
],
});
});
/* oxlint-disable max-lines -- TODO: split this grandfathered oversized file. */
+11 -50
View File
@@ -1,3 +1,4 @@
import { installChannelDmPolicyContractSuite } from "openclaw/plugin-sdk/channel-test-helpers";
// Slack tests cover setup surface plugin behavior.
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-contracts";
import {
@@ -427,57 +428,17 @@ describe("slackSetupWizard.prepare", () => {
});
describe("slackSetupWizard.dmPolicy", () => {
it("reads the named-account DM policy instead of the channel root", () => {
expect(
slackSetupWizard.dmPolicy?.getCurrent(
{
channels: {
slack: {
dmPolicy: "disabled",
accounts: {
alerts: {
dmPolicy: "allowlist",
botToken: "xoxb-alerts",
appToken: "xapp-alerts",
},
},
},
},
} as OpenClawConfig,
"alerts",
),
).toBe("allowlist");
});
it("reports account-scoped config keys for named accounts", () => {
expect(slackSetupWizard.dmPolicy?.resolveConfigKeys?.({}, "alerts")).toEqual({
policyKey: "channels.slack.accounts.alerts.dmPolicy",
allowFromKey: "channels.slack.accounts.alerts.allowFrom",
});
});
it('writes open policy state to the named account and preserves inherited allowFrom with "*"', () => {
const next = slackSetupWizard.dmPolicy?.setPolicy(
installChannelDmPolicyContractSuite({
dmPolicy: slackSetupWizard.dmPolicy!,
cases: [
{
channels: {
slack: {
allowFrom: ["U123"],
accounts: {
alerts: {
botToken: "xoxb-alerts",
appToken: "xapp-alerts",
},
},
},
},
} as OpenClawConfig,
"open",
"alerts",
);
expect(next?.channels?.slack?.dmPolicy).toBeUndefined();
expect(next?.channels?.slack?.accounts?.alerts?.dmPolicy).toBe("open");
expect(next?.channels?.slack?.accounts?.alerts?.allowFrom).toEqual(["U123", "*"]);
name: "Slack named accounts",
channel: "slack",
accountId: "alerts",
accountConfig: { botToken: "xoxb-alerts", appToken: "xapp-alerts" },
inheritedAllowFrom: ["U123"],
},
],
});
});
+12 -75
View File
@@ -1,5 +1,5 @@
// Telegram tests cover setup surface plugin behavior.
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-contracts";
import { installChannelDmPolicyContractSuite } from "openclaw/plugin-sdk/channel-test-helpers";
import { DEFAULT_ACCOUNT_ID } from "openclaw/plugin-sdk/setup";
import { describe, expect, it, vi } from "vitest";
import { promptTelegramAllowFromForAccount, telegramSetupAdapter } from "./setup-core.js";
@@ -98,81 +98,18 @@ describe("telegram DM access warning helpers", () => {
});
describe("telegramSetupDmPolicy", () => {
it("reads the named-account DM policy instead of the channel root", () => {
expect(
telegramSetupDmPolicy.getCurrent?.(
{
channels: {
telegram: {
dmPolicy: "disabled",
accounts: {
alerts: {
dmPolicy: "allowlist",
botToken: "tok",
},
},
},
},
},
"alerts",
),
).toBe("allowlist");
});
it("reports account-scoped config keys for named accounts", () => {
expect(telegramSetupDmPolicy.resolveConfigKeys?.({}, "alerts")).toEqual({
policyKey: "channels.telegram.accounts.alerts.dmPolicy",
allowFromKey: "channels.telegram.accounts.alerts.allowFrom",
});
});
it("uses configured defaultAccount for omitted DM policy account context", () => {
const cfg: OpenClawConfig = {
channels: {
telegram: {
defaultAccount: "alerts",
dmPolicy: "disabled",
allowFrom: ["123"],
accounts: {
alerts: {
dmPolicy: "allowlist",
botToken: "tok",
},
},
},
installChannelDmPolicyContractSuite({
dmPolicy: telegramSetupDmPolicy,
cases: [
{
name: "Telegram named accounts",
channel: "telegram",
accountId: "alerts",
accountConfig: { botToken: "tok" },
inheritedAllowFrom: ["123"],
defaultAccount: { rootAllowFrom: ["123"] },
},
};
expect(telegramSetupDmPolicy.getCurrent?.(cfg)).toBe("allowlist");
expect(telegramSetupDmPolicy.resolveConfigKeys?.(cfg)).toEqual({
policyKey: "channels.telegram.accounts.alerts.dmPolicy",
allowFromKey: "channels.telegram.accounts.alerts.allowFrom",
});
const next = telegramSetupDmPolicy.setPolicy?.(cfg, "open");
expect(next?.channels?.telegram?.dmPolicy).toBe("disabled");
expect(next?.channels?.telegram?.accounts?.alerts?.dmPolicy).toBe("open");
});
it('writes open policy state to the named account and preserves inherited allowFrom with "*"', () => {
const cfg: OpenClawConfig = {
channels: {
telegram: {
allowFrom: ["123"],
accounts: {
alerts: {
botToken: "tok",
},
},
},
},
};
const next = telegramSetupDmPolicy.setPolicy?.(cfg, "open", "alerts");
expect(next?.channels?.telegram?.dmPolicy).toBeUndefined();
expect(next?.channels?.telegram?.accounts?.alerts?.dmPolicy).toBe("open");
expect(next?.channels?.telegram?.accounts?.alerts?.allowFrom).toEqual(["123", "*"]);
],
});
});
@@ -752,18 +752,9 @@
"@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/meta": [
"../dist/plugin-sdk/packages/acp-core/src/meta.d.ts"
],
"@openclaw/acp-core/numeric-options": [
"../dist/plugin-sdk/packages/acp-core/src/numeric-options.d.ts"
],
"@openclaw/acp-core/record-shared": [
"../dist/plugin-sdk/packages/acp-core/src/record-shared.d.ts"
],
"@openclaw/acp-core/session": [
"../dist/plugin-sdk/packages/acp-core/src/session.d.ts"
],
-9
View File
@@ -732,18 +732,9 @@
"@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/meta": [
"../../dist/plugin-sdk/packages/acp-core/src/meta.d.ts"
],
"@openclaw/acp-core/numeric-options": [
"../../dist/plugin-sdk/packages/acp-core/src/numeric-options.d.ts"
],
"@openclaw/acp-core/record-shared": [
"../../dist/plugin-sdk/packages/acp-core/src/record-shared.d.ts"
],
"@openclaw/acp-core/session": [
"../../dist/plugin-sdk/packages/acp-core/src/session.d.ts"
],
+12 -96
View File
@@ -1,5 +1,6 @@
// Zalo tests cover setup surface plugin behavior.
import { adaptScopedAccountAccessor } from "openclaw/plugin-sdk/channel-config-helpers";
import { installChannelDmPolicyContractSuite } from "openclaw/plugin-sdk/channel-test-helpers";
import {
createPluginSetupWizardConfigure,
createTestWizardPrompter,
@@ -70,103 +71,18 @@ describe("zalo setup wizard", () => {
expect(zaloConfig.webhookUrl).toBeUndefined();
});
it("reads the named-account DM policy instead of the channel root", () => {
expect(
zaloDmPolicy.getCurrent(
{
channels: {
zalo: {
dmPolicy: "disabled",
accounts: {
work: {
botToken: "12345689:abc-xyz",
dmPolicy: "allowlist",
},
},
},
},
} as OpenClawConfig,
"work",
),
).toBe("allowlist");
});
it("reports account-scoped config keys for named accounts", () => {
expect(zaloDmPolicy.resolveConfigKeys?.({} as OpenClawConfig, "work")).toEqual({
policyKey: "channels.zalo.accounts.work.dmPolicy",
allowFromKey: "channels.zalo.accounts.work.allowFrom",
});
});
it("uses configured defaultAccount for omitted DM policy account context", () => {
const cfg = {
channels: {
zalo: {
defaultAccount: "work",
dmPolicy: "disabled",
allowFrom: ["123456789"],
accounts: {
work: {
botToken: "12345689:abc-xyz",
dmPolicy: "allowlist",
},
},
},
},
} as OpenClawConfig;
expect(zaloDmPolicy.getCurrent(cfg)).toBe("allowlist");
expect(zaloDmPolicy.resolveConfigKeys?.(cfg)).toEqual({
policyKey: "channels.zalo.accounts.work.dmPolicy",
allowFromKey: "channels.zalo.accounts.work.allowFrom",
});
const next = zaloDmPolicy.setPolicy(cfg, "open");
const zaloConfig = next.channels?.zalo;
if (!zaloConfig) {
throw new Error("expected Zalo config");
}
expect(zaloConfig.dmPolicy).toBe("disabled");
const workAccount = next.channels?.zalo?.accounts?.work as
| { dmPolicy?: string; allowFrom?: Array<string | number> }
| undefined;
if (!workAccount) {
throw new Error("expected Zalo work account");
}
expect(workAccount.dmPolicy).toBe("open");
});
it('writes open policy state to the named account and preserves inherited allowFrom with "*"', () => {
const next = zaloDmPolicy.setPolicy(
installChannelDmPolicyContractSuite({
dmPolicy: zaloDmPolicy,
cases: [
{
channels: {
zalo: {
allowFrom: ["123456789"],
accounts: {
work: {
botToken: "12345689:abc-xyz",
},
},
},
},
} as OpenClawConfig,
"open",
"work",
);
const zaloConfig = next.channels?.zalo;
if (!zaloConfig) {
throw new Error("expected Zalo config");
}
expect(zaloConfig.dmPolicy).toBeUndefined();
const workAccount = next.channels?.zalo?.accounts?.work as
| { dmPolicy?: string; allowFrom?: Array<string | number> }
| undefined;
if (!workAccount) {
throw new Error("expected Zalo work account");
}
expect(workAccount.dmPolicy).toBe("open");
expect(workAccount.allowFrom).toEqual(["123456789", "*"]);
name: "Zalo named accounts",
channel: "zalo",
accountId: "work",
accountConfig: { botToken: "12345689:abc-xyz" },
inheritedAllowFrom: ["123456789"],
defaultAccount: { rootAllowFrom: ["123456789"] },
},
],
});
it("uses configured defaultAccount for omitted setup configured state", async () => {
+11 -83
View File
@@ -1,4 +1,5 @@
// Zalouser tests cover setup surface plugin behavior.
import { installChannelDmPolicyContractSuite } from "openclaw/plugin-sdk/channel-test-helpers";
import {
createPluginSetupWizardConfigure,
createTestWizardPrompter,
@@ -374,91 +375,18 @@ describe("zalouser setup wizard", () => {
expect(result.cfg.plugins?.allow).toEqual(["telegram", "zalouser"]);
});
it("reads the named-account DM policy instead of the channel root", () => {
expect(
zalouserSetupWizard.dmPolicy?.getCurrent(
{
channels: {
zalouser: {
dmPolicy: "disabled",
accounts: {
work: {
profile: "work",
dmPolicy: "allowlist",
},
},
},
},
} as OpenClawConfig,
"work",
),
).toBe("allowlist");
});
it("reports account-scoped config keys for named accounts", () => {
expect(zalouserSetupWizard.dmPolicy?.resolveConfigKeys?.({} as OpenClawConfig, "work")).toEqual(
installChannelDmPolicyContractSuite({
dmPolicy: zalouserSetupWizard.dmPolicy!,
cases: [
{
policyKey: "channels.zalouser.accounts.work.dmPolicy",
allowFromKey: "channels.zalouser.accounts.work.allowFrom",
name: "Zalo Personal named accounts",
channel: "zalouser",
accountId: "work",
accountConfig: { profile: "work" },
inheritedAllowFrom: ["123456789"],
defaultAccount: { rootAllowFrom: ["123456789"] },
},
);
});
it("uses configured defaultAccount for omitted DM policy account context", () => {
const cfg = {
channels: {
zalouser: {
defaultAccount: "work",
dmPolicy: "disabled",
allowFrom: ["123456789"],
accounts: {
work: {
dmPolicy: "allowlist",
profile: "work-profile",
},
},
},
},
} as OpenClawConfig;
expect(zalouserSetupWizard.dmPolicy?.getCurrent(cfg)).toBe("allowlist");
expect(zalouserSetupWizard.dmPolicy?.resolveConfigKeys?.(cfg)).toEqual({
policyKey: "channels.zalouser.accounts.work.dmPolicy",
allowFromKey: "channels.zalouser.accounts.work.allowFrom",
});
const next = zalouserSetupWizard.dmPolicy?.setPolicy(cfg, "open");
expect(next?.channels?.zalouser?.dmPolicy).toBe("disabled");
const workAccount = next?.channels?.zalouser?.accounts?.work as
| { dmPolicy?: string; allowFrom?: Array<string | number> }
| undefined;
expect(workAccount?.dmPolicy).toBe("open");
});
it('writes open policy state to the named account and preserves inherited allowFrom with "*"', () => {
const next = zalouserSetupWizard.dmPolicy?.setPolicy(
{
channels: {
zalouser: {
allowFrom: ["123456789"],
accounts: {
work: {
profile: "work",
},
},
},
},
} as OpenClawConfig,
"open",
"work",
);
expect(next?.channels?.zalouser?.dmPolicy).toBeUndefined();
const workAccount = next?.channels?.zalouser?.accounts?.work as
| { dmPolicy?: string; allowFrom?: Array<string | number> }
| undefined;
expect(workAccount?.dmPolicy).toBe("open");
expect(workAccount?.allowFrom).toEqual(["123456789", "*"]);
],
});
it("shows the account-scoped current DM policy in quickstart notes", async () => {
+1 -16
View File
@@ -14,26 +14,11 @@
"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"
},
"./meta": {
"types": "./dist/meta.d.mts",
"import": "./dist/meta.mjs",
"default": "./dist/meta.mjs"
},
"./numeric-options": {
"types": "./dist/numeric-options.d.mts",
"import": "./dist/numeric-options.mjs",
"default": "./dist/numeric-options.mjs"
},
"./record-shared": {
"types": "./dist/record-shared.d.mts",
"import": "./dist/record-shared.mjs",
"default": "./dist/record-shared.mjs"
},
"./session": {
"types": "./dist/session.d.mts",
"import": "./dist/session.mjs",
@@ -84,6 +69,6 @@
"@openclaw/normalization-core": "workspace:*"
},
"scripts": {
"build": "tsdown src/index.ts src/error-format.ts src/meta.ts src/normalize-text.ts src/numeric-options.ts src/record-shared.ts src/session.ts src/session-interaction-mode.ts src/session-lineage-meta.ts src/types.ts src/runtime/error-text.ts src/runtime/errors.ts src/runtime/session-identifiers.ts src/runtime/session-identity.ts src/runtime/types.ts --no-config --platform node --format esm --dts --out-dir dist --clean"
"build": "tsdown src/index.ts src/error-format.ts src/meta.ts src/session.ts src/session-interaction-mode.ts src/session-lineage-meta.ts src/types.ts src/runtime/error-text.ts src/runtime/errors.ts src/runtime/session-identifiers.ts src/runtime/session-identity.ts src/runtime/types.ts --no-config --platform node --format esm --dts --out-dir dist --clean"
}
}
-3
View File
@@ -2,9 +2,6 @@
export * from "./error-format.js";
export * from "./meta.js";
export * from "./normalize-text.js";
export * from "./numeric-options.js";
export * from "./record-shared.js";
export * from "./session-interaction-mode.js";
export * from "./session-lineage-meta.js";
export * from "./session.js";
-3
View File
@@ -1,3 +0,0 @@
// ACP text normalization facade shared with older imports.
export { normalizeOptionalString as normalizeText } from "@openclaw/normalization-core/string-coerce";
-11
View File
@@ -1,11 +0,0 @@
// ACP Core module implements numeric options behavior.
import { resolveIntegerOption as resolveSharedIntegerOption } from "@openclaw/normalization-core/number-coercion";
/** Resolves ACP integer options through the shared normalization contract. */
export function resolveIntegerOption(
value: number | undefined,
fallback: number,
params: { min: number },
): number {
return resolveSharedIntegerOption(value, fallback, params);
}
-3
View File
@@ -1,3 +0,0 @@
// ACP record normalization facade shared with older imports.
export { asOptionalRecord as asRecord } from "@openclaw/normalization-core/record-coerce";
@@ -1,6 +1,8 @@
// ACP Core module implements session identifiers behavior.
import { normalizeLowercaseStringOrEmpty } from "@openclaw/normalization-core/string-coerce";
import { normalizeText } from "../normalize-text.js";
import {
normalizeLowercaseStringOrEmpty,
normalizeOptionalString as normalizeText,
} from "@openclaw/normalization-core/string-coerce";
import type { SessionAcpIdentity, SessionAcpMeta } from "../types.js";
import { isSessionIdentityPending, resolveSessionIdentityFromMeta } from "./session-identity.js";
@@ -1,5 +1,5 @@
// ACP Core module implements session identity behavior.
import { normalizeText } from "../normalize-text.js";
import { normalizeOptionalString as normalizeText } from "@openclaw/normalization-core/string-coerce";
import type { SessionAcpIdentity, SessionAcpIdentitySource, SessionAcpMeta } from "../types.js";
import type { AcpRuntimeHandle, AcpRuntimeStatus } from "./types.js";
+1 -1
View File
@@ -1,6 +1,6 @@
// ACP Core module implements session behavior.
import { randomUUID } from "node:crypto";
import { resolveIntegerOption } from "./numeric-options.js";
import { resolveIntegerOption } from "@openclaw/normalization-core/number-coercion";
import type { AcpSession } from "./types.js";
export type AcpSessionStore = {
-2
View File
@@ -13,9 +13,7 @@
},
"exports": {
"./runtime-core": "./src/runtime-core.ts",
"./runtime-cli": "./src/runtime-cli.ts",
"./runtime-files": "./src/runtime-files.ts",
"./engine": "./src/engine.ts",
"./engine-foundation": "./src/engine-foundation.ts",
"./engine-storage": "./src/engine-storage.ts",
"./engine-embeddings": "./src/engine-embeddings.ts",
-7
View File
@@ -1,7 +0,0 @@
// Aggregate workspace contract for the memory engine surface.
// Keep focused subpaths preferred for new code.
export * from "./engine-foundation.js";
export * from "./engine-storage.js";
export * from "./engine-embeddings.js";
export * from "./engine-qmd.js";
@@ -1,19 +0,0 @@
// Narrow CLI/runtime facade re-exported for memory host helpers.
export {
colorize,
defaultRuntime,
formatDocsLink,
formatErrorMessage,
formatHelpExamples,
isRich,
isVerbose,
resolveCommandSecretRefsViaGateway,
setVerbose,
shortenHomeInString,
shortenHomePath,
theme,
withManager,
withProgress,
withProgressTotals,
} from "./openclaw-runtime.js";
@@ -36,11 +36,7 @@ export {
} from "../../../../src/auto-reply/tokens.js";
// CLI/runtime/config helpers.
export { formatErrorMessage, withManager } from "../../../../src/cli/cli-utils.js";
export { resolveCommandSecretRefsViaGateway } from "../../../../src/cli/command-secret-gateway.js";
export { formatHelpExamples } from "../../../../src/cli/help-format.js";
export { parseDurationMs } from "../../../../src/cli/parse-duration.js";
export { withProgress, withProgressTotals } from "../../../../src/cli/progress.js";
export { parseNonNegativeByteSize } from "../../../../src/config/byte-size.js";
export {
getRuntimeConfig,
@@ -102,7 +98,6 @@ export {
} from "../../../../src/config/types.secrets.js";
export type { SecretInput } from "../../../../src/config/types.secrets.js";
export type { MemorySearchConfig } from "../../../../src/config/types.tools.js";
export { isVerbose, setVerbose } from "../../../../src/globals.js";
// IO, network, and logging helpers.
export { isExecCompletionEvent } from "../../../../src/infra/heartbeat-events-filter.js";
@@ -160,13 +155,10 @@ export type {
export type { OpenClawPluginApi } from "../../../../src/plugins/types.js";
// Shared session/text utilities.
export { defaultRuntime } from "../../../../src/runtime.js";
export { parseAgentSessionKey } from "../../../../src/routing/session-key.js";
export { hasInterSessionUserProvenance } from "../../../../src/sessions/input-provenance.js";
export { isCronRunSessionKey } from "../../../../src/sessions/session-key-utils.js";
export { onSessionTranscriptUpdate } from "../../../../src/sessions/transcript-events.js";
export { formatDocsLink } from "../../../terminal-core/src/links.js";
export { colorize, isRich, theme } from "../../../terminal-core/src/theme.js";
export { CHARS_PER_TOKEN_ESTIMATE, estimateStringChars } from "../../../../src/utils/cjk-chars.js";
export { runTasksWithConcurrency } from "../../../../src/utils/run-with-concurrency.js";
export { splitShellArgs } from "../../../../src/utils/shell-argv.js";
@@ -1,11 +0,0 @@
// Focused runtime contract for memory CLI/UI helpers.
export { formatErrorMessage, withManager } from "./host/openclaw-runtime-cli.js";
export { formatHelpExamples } from "./host/openclaw-runtime-cli.js";
export { resolveCommandSecretRefsViaGateway } from "./host/openclaw-runtime-cli.js";
export { withProgress, withProgressTotals } from "./host/openclaw-runtime-cli.js";
export { defaultRuntime } from "./host/openclaw-runtime-cli.js";
export { formatDocsLink } from "./host/openclaw-runtime-cli.js";
export { colorize, isRich, theme } from "./host/openclaw-runtime-cli.js";
export { isVerbose, setVerbose } from "./host/openclaw-runtime-cli.js";
export { shortenHomeInString, shortenHomePath } from "./host/openclaw-runtime-cli.js";
-19
View File
@@ -1,19 +0,0 @@
{
"name": "@openclaw/web-content-core",
"version": "0.0.0-private",
"private": true,
"files": [
"dist"
],
"type": "module",
"exports": {
"./provider-runtime-shared": {
"types": "./dist/provider-runtime-shared.d.mts",
"import": "./dist/provider-runtime-shared.mjs",
"default": "./dist/provider-runtime-shared.mjs"
}
},
"scripts": {
"build": "tsdown src/provider-runtime-shared.ts --no-config --platform node --format esm --dts --out-dir dist --clean"
}
}
-2
View File
@@ -2311,8 +2311,6 @@ importers:
specifier: workspace:*
version: link:../normalization-core
packages/web-content-core: {}
packages/workboard-contract: {}
ui:
-1
View File
@@ -20,7 +20,6 @@ const RUN_NODE_PACKAGE_SOURCE_ROOTS = [
"packages/retry/src",
"packages/acp-core/src",
"packages/terminal-core/src",
"packages/web-content-core/src",
"packages/net-policy/src",
];
+1 -1
View File
@@ -1,7 +1,7 @@
/** Classifies ACP tool permission requests into auto-approved and prompt-required risk buckets. */
import { homedir } from "node:os";
import path from "node:path";
import { asRecord } from "@openclaw/acp-core/record-shared";
import { asOptionalRecord as asRecord } from "@openclaw/normalization-core/record-coerce";
import {
normalizeLowercaseStringOrEmpty,
normalizeOptionalString,
+5 -3
View File
@@ -1,12 +1,14 @@
/** Validation and normalization for ACP session runtime options and config controls. */
import { isAbsolute } from "node:path";
import { normalizeText } from "@openclaw/acp-core/normalize-text";
import { normalizeLowercaseStringOrEmpty } from "@openclaw/normalization-core/string-coerce";
import {
normalizeLowercaseStringOrEmpty,
normalizeOptionalString as normalizeText,
} from "@openclaw/normalization-core/string-coerce";
import type { AcpSessionRuntimeOptions, SessionAcpMeta } from "../../config/sessions/types.js";
import { parseStrictPositiveInteger } from "../../infra/parse-finite-number.js";
import { AcpRuntimeError } from "../runtime/errors.js";
export { normalizeText } from "@openclaw/acp-core/normalize-text";
export { normalizeOptionalString as normalizeText } from "@openclaw/normalization-core/string-coerce";
const MAX_RUNTIME_MODE_LENGTH = 64;
const MAX_MODEL_LENGTH = 200;
+1 -1
View File
@@ -1,7 +1,7 @@
/** Persistent/replayable ACP event ledger implementations for session rehydration. */
import type { DatabaseSync } from "node:sqlite";
import type { ContentBlock, SessionUpdate } from "@agentclientprotocol/sdk";
import { resolveIntegerOption } from "@openclaw/acp-core/numeric-options";
import { resolveIntegerOption } from "@openclaw/normalization-core/number-coercion";
import {
openOpenClawStateDatabase,
type OpenClawStateDatabaseOptions,
+1 -1
View File
@@ -5,8 +5,8 @@ import type {
ToolCallLocation,
ToolKind,
} from "@agentclientprotocol/sdk";
import { asRecord } from "@openclaw/acp-core/record-shared";
import { hasHttpUrlPrefix } from "@openclaw/net-policy/url-protocol";
import { asOptionalRecord as asRecord } from "@openclaw/normalization-core/record-coerce";
import {
hasNonEmptyString,
normalizeLowercaseStringOrEmpty,
+5 -3
View File
@@ -1,14 +1,16 @@
/** Types and normalization helpers for configured channel-to-ACP persistent bindings. */
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 {
normalizeOptionalLowercaseString,
normalizeOptionalString as normalizeText,
} from "@openclaw/normalization-core/string-coerce";
import type { ChannelId } from "../channels/plugins/types.public.js";
import { sha256HexPrefix } from "../infra/crypto-digest.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";
export { normalizeText } from "@openclaw/acp-core/normalize-text";
export { normalizeOptionalString as normalizeText } from "@openclaw/normalization-core/string-coerce";
export type ConfiguredAcpBindingChannel = ChannelId;
+1 -1
View File
@@ -11,7 +11,6 @@ import {
} from "@openclaw/normalization-core/string-coerce";
import { truncateUtf16Safe } from "@openclaw/normalization-core/utf16-slice";
import { Type } from "typebox";
import { resolveWebProviderConfig } from "../../../packages/web-content-core/src/provider-runtime-shared.js";
import type { OpenClawConfig } from "../../config/types.openclaw.js";
import { SsrFBlockedError, type LookupFn, type SsrFPolicy } from "../../infra/net/ssrf.js";
import { logDebug } from "../../logger.js";
@@ -22,6 +21,7 @@ import { wrapExternalContent, wrapWebContent } from "../../security/external-con
import { createLazyImportLoader } from "../../shared/lazy-promise.js";
import { isRecord } from "../../utils.js";
import { extractReadableContent } from "../../web-fetch/content-extractors.runtime.js";
import { resolveWebProviderConfig } from "../../web/provider-runtime-shared.js";
import { stringEnum } from "../schema/string-enum.js";
import { writePrivateTempFile } from "../sessions/tools/private-temp-file.js";
import { formatFullOutputFooter } from "../sessions/tools/tool-contracts.js";
+1
View File
@@ -4,6 +4,7 @@ export { expectDirectoryIds, type DirectoryListFn } from "./test-helpers/directo
export {
expectChannelPluginContract,
installChannelActionsContractSuite,
installChannelDmPolicyContractSuite,
installChannelPluginContractSuite,
installChannelSetupContractSuite,
installChannelStatusContractSuite,
@@ -1,5 +1,6 @@
// Channel contract suites provide reusable expectations for channel plugin test coverage.
import { expect, it } from "vitest";
import type { ChannelSetupDmPolicy } from "../../channels/plugins/setup-wizard-types.js";
import type {
ChannelAccountSnapshot,
ChannelAccountState,
@@ -172,6 +173,142 @@ export function installChannelSetupContractSuite<
}
}
type ChannelDmPolicyConfig = {
dmPolicy?: unknown;
allowFrom?: unknown;
accounts?: Record<string, ChannelDmPolicyConfig | undefined>;
};
type ChannelDmPolicyContractCase = {
name: string;
channel: string;
accountId: string;
accountConfig: Record<string, unknown>;
inheritedAllowFrom: ReadonlyArray<string | number>;
defaultAccount?: {
rootAllowFrom?: ReadonlyArray<string | number>;
accountAllowFrom?: ReadonlyArray<string | number>;
};
};
function createDmPolicyContractConfig(params: {
testCase: ChannelDmPolicyContractCase;
mode: "read" | "write" | "default";
}): OpenClawConfig {
const { testCase } = params;
const defaultAccount = params.mode === "default" ? testCase.defaultAccount : undefined;
const account = {
...testCase.accountConfig,
...(params.mode === "write" ? {} : { dmPolicy: "allowlist" }),
...(defaultAccount?.accountAllowFrom
? { allowFrom: [...defaultAccount.accountAllowFrom] }
: {}),
};
const rootAllowFrom =
params.mode === "write" ? testCase.inheritedAllowFrom : defaultAccount?.rootAllowFrom;
return {
channels: {
[testCase.channel]: {
...(params.mode === "write" ? {} : { dmPolicy: "disabled" }),
...(params.mode === "default" ? { defaultAccount: testCase.accountId } : {}),
...(rootAllowFrom ? { allowFrom: [...rootAllowFrom] } : {}),
accounts: { [testCase.accountId]: account },
},
},
} as OpenClawConfig;
}
function addExpectedWildcard(values: ReadonlyArray<string | number> | undefined) {
return values?.includes("*") ? [...values] : [...(values ?? []), "*"];
}
function resolveDmPolicyConfig(
cfg: OpenClawConfig,
channel: string,
accountId: string,
): { channel: ChannelDmPolicyConfig; account: ChannelDmPolicyConfig } {
const channels = cfg.channels as Record<string, ChannelDmPolicyConfig | undefined> | undefined;
const channelConfig = channels?.[channel];
const accountConfig = channelConfig?.accounts?.[accountId];
expect(channelConfig).toBeDefined();
expect(accountConfig).toBeDefined();
return { channel: channelConfig!, account: accountConfig! };
}
function expectOpenDmPolicyPatch(params: {
dmPolicy: ChannelSetupDmPolicy;
cfg: OpenClawConfig;
channel: string;
accountId?: string;
resolvedAccountId: string;
expectedAllowFrom: readonly unknown[];
}) {
const before = resolveDmPolicyConfig(params.cfg, params.channel, params.resolvedAccountId);
const beforeRootPolicy = before.channel.dmPolicy;
const beforeRootAllowFrom = Array.isArray(before.channel.allowFrom)
? [...before.channel.allowFrom]
: before.channel.allowFrom;
const next = params.dmPolicy.setPolicy(params.cfg, "open", params.accountId);
const after = resolveDmPolicyConfig(next, params.channel, params.resolvedAccountId);
expect(after.channel.dmPolicy).toBe(beforeRootPolicy);
expect(after.channel.allowFrom).toEqual(beforeRootAllowFrom);
expect(after.account.dmPolicy).toBe("open");
expect(after.account.allowFrom).toEqual(params.expectedAllowFrom);
}
export function installChannelDmPolicyContractSuite(params: {
dmPolicy: ChannelSetupDmPolicy;
cases: readonly ChannelDmPolicyContractCase[];
}) {
for (const testCase of params.cases) {
it(`dm policy contract: ${testCase.name} reads the named-account policy`, () => {
expect(params.dmPolicy.channel).toBe(testCase.channel);
const cfg = createDmPolicyContractConfig({ testCase, mode: "read" });
expect(params.dmPolicy.getCurrent(cfg, testCase.accountId)).toBe("allowlist");
});
it(`dm policy contract: ${testCase.name} reports account-scoped config keys`, () => {
expect(params.dmPolicy.resolveConfigKeys?.({}, testCase.accountId)).toEqual({
policyKey: `channels.${testCase.channel}.accounts.${testCase.accountId}.dmPolicy`,
allowFromKey: `channels.${testCase.channel}.accounts.${testCase.accountId}.allowFrom`,
});
});
it(`dm policy contract: ${testCase.name} writes open policy with inherited allowFrom`, () => {
expectOpenDmPolicyPatch({
dmPolicy: params.dmPolicy,
cfg: createDmPolicyContractConfig({ testCase, mode: "write" }),
channel: testCase.channel,
accountId: testCase.accountId,
resolvedAccountId: testCase.accountId,
expectedAllowFrom: addExpectedWildcard(testCase.inheritedAllowFrom),
});
});
const defaultAccount = testCase.defaultAccount;
if (defaultAccount) {
it(`dm policy contract: ${testCase.name} uses defaultAccount when accountId is omitted`, () => {
const cfg = createDmPolicyContractConfig({ testCase, mode: "default" });
expect(params.dmPolicy.getCurrent(cfg)).toBe("allowlist");
expect(params.dmPolicy.resolveConfigKeys?.(cfg)).toEqual({
policyKey: `channels.${testCase.channel}.accounts.${testCase.accountId}.dmPolicy`,
allowFromKey: `channels.${testCase.channel}.accounts.${testCase.accountId}.allowFrom`,
});
expectOpenDmPolicyPatch({
dmPolicy: params.dmPolicy,
cfg,
channel: testCase.channel,
resolvedAccountId: testCase.accountId,
expectedAllowFrom: addExpectedWildcard(
defaultAccount.accountAllowFrom ?? defaultAccount.rootAllowFrom,
),
});
});
}
}
}
type ChannelStatusContractCase<Probe> = {
name: string;
cfg: OpenClawConfig;
@@ -45,14 +45,12 @@ type PackageJson = {
devDependencies?: Record<string, string>;
};
const MEMORY_HOST_SDK_EXPORTS = [
"./engine",
"./engine-embeddings",
"./engine-foundation",
"./engine-qmd",
"./engine-storage",
"./multimodal",
"./query",
"./runtime-cli",
"./runtime-core",
"./runtime-files",
"./secret",
@@ -66,7 +64,6 @@ const MEMORY_HOST_SDK_ALLOWED_CORE_BRIDGE_FILES = [
] as const;
const MEMORY_HOST_SDK_RUNTIME_ADAPTER_FILES = [
"packages/memory-host-sdk/src/host/openclaw-runtime-agent.ts",
"packages/memory-host-sdk/src/host/openclaw-runtime-cli.ts",
"packages/memory-host-sdk/src/host/openclaw-runtime-config.ts",
"packages/memory-host-sdk/src/host/openclaw-runtime-io.ts",
"packages/memory-host-sdk/src/host/openclaw-runtime-memory.ts",
@@ -764,7 +764,6 @@ describe("plugin-sdk subpath exports", () => {
});
expectSourceContract("memory-core-host-runtime-cli", {
mentions: ["defaultRuntime", "withManager", "withProgressTotals"],
omits: ['export * from "../../packages/memory-host-sdk/src/runtime-cli.js";'],
});
expectSourceContract("memory-core-host-runtime-files", {
mentions: ["listMemoryFiles", "normalizeExtraMemoryPaths", "MemorySearchResult"],
-3
View File
@@ -1528,9 +1528,6 @@ describe("plugin sdk alias helpers", () => {
expect(fs.realpathSync(aliases["@openclaw/media-generation-core/catalog"] ?? "")).toBe(
fs.realpathSync(mediaGenerationCore.distFile),
);
expect(fs.realpathSync(aliases["@openclaw/acp-core/normalize-text"] ?? "")).toBe(
fs.realpathSync(acpCoreRootDistFile),
);
expect(fs.realpathSync(aliases["@openclaw/normalization-core/record-coerce"] ?? "")).toBe(
fs.realpathSync(normalizationCoreRootDistFile),
);
+7 -7
View File
@@ -1,13 +1,6 @@
/** Runtime provider selection and tool construction for the `web_fetch` tool. */
import { createHash } from "node:crypto";
import { normalizeLowercaseStringOrEmpty } from "@openclaw/normalization-core/string-coerce";
import {
hasWebProviderEntryCredential,
providerRequiresCredential,
readWebProviderEnvValue,
resolveWebProviderConfig,
resolveWebProviderDefinition,
} from "../../packages/web-content-core/src/provider-runtime-shared.js";
import type { OpenClawConfig } from "../config/types.js";
import { logVerbose } from "../globals.js";
import { getActivePluginRegistryVersion } from "../plugins/runtime.js";
@@ -22,6 +15,13 @@ import {
import { sortWebFetchProvidersForAutoDetect } from "../plugins/web-fetch-providers.shared.js";
import { getActiveRuntimeWebToolsMetadata } from "../secrets/runtime-web-tools-state.js";
import type { RuntimeWebFetchMetadata } from "../secrets/runtime-web-tools.types.js";
import {
hasWebProviderEntryCredential,
providerRequiresCredential,
readWebProviderEnvValue,
resolveWebProviderConfig,
resolveWebProviderDefinition,
} from "../web/provider-runtime-shared.js";
// Runtime provider selection for the web_fetch tool. It resolves config,
// credentials, runtime metadata, and sandbox-safe bundled provider scopes.
+6 -6
View File
@@ -4,12 +4,6 @@ import {
normalizeOptionalLowercaseString,
} from "@openclaw/normalization-core/string-coerce";
import { uniqueStrings } from "@openclaw/normalization-core/string-normalization";
import {
hasWebProviderEntryCredential,
providerRequiresCredential,
readWebProviderEnvValue,
resolveWebProviderConfig,
} from "../../packages/web-content-core/src/provider-runtime-shared.js";
import { resolveDefaultAgentDir } from "../agents/agent-scope-config.js";
import { hasAuthProfileForProvider } from "../agents/tools/model-config.helpers.js";
import {
@@ -28,6 +22,12 @@ import {
import { sortWebSearchProvidersForAutoDetect } from "../plugins/web-search-providers.shared.js";
import { getActiveRuntimeWebToolsMetadata } from "../secrets/runtime-web-tools-state.js";
import type { RuntimeWebSearchMetadata } from "../secrets/runtime-web-tools.types.js";
import {
hasWebProviderEntryCredential,
providerRequiresCredential,
readWebProviderEnvValue,
resolveWebProviderConfig,
} from "../web/provider-runtime-shared.js";
import { executeWebSearchCandidates } from "./runtime-execution.js";
import type {
ResolveWebSearchDefinitionParams,
@@ -1,4 +1,4 @@
// Web Content Core tests cover provider runtime shared behavior.
// Web provider runtime tests cover shared config, credential, and definition resolution.
import { describe, expect, it } from "vitest";
import {
hasWebProviderEntryCredential,
@@ -1,5 +1,5 @@
// Web Content Core module implements provider runtime shared behavior.
export type WebProviderConfigSource = {
// Shared web provider config, credential, and definition resolution.
type WebProviderConfigSource = {
tools?: {
web?: {
search?: unknown;
-1
View File
@@ -183,7 +183,6 @@ describe("oxlint config", () => {
"packages/terminal-core/**/*.ts",
"packages/normalization-core/**/*.ts",
"packages/model-catalog-core/**/*.ts",
"packages/web-content-core/**/*.ts",
"packages/agent-core/**/*.ts",
"packages/acp-core/**/*.ts",
"packages/ai/**/*.ts",
-7
View File
@@ -180,9 +180,6 @@
"@openclaw/workboard-contract": ["./packages/workboard-contract/src/index.ts"],
"@openclaw/acp-core": ["./packages/acp-core/src/index.ts"],
"@openclaw/acp-core/meta": ["./packages/acp-core/src/meta.ts"],
"@openclaw/acp-core/numeric-options": ["./packages/acp-core/src/numeric-options.ts"],
"@openclaw/acp-core/normalize-text": ["./packages/acp-core/src/normalize-text.ts"],
"@openclaw/acp-core/record-shared": ["./packages/acp-core/src/record-shared.ts"],
"@openclaw/acp-core/session": ["./packages/acp-core/src/session.ts"],
"@openclaw/acp-core/session-interaction-mode": [
"./packages/acp-core/src/session-interaction-mode.ts"
@@ -247,10 +244,6 @@
"@openclaw/net-policy/url-protocol": ["./packages/net-policy/src/url-protocol.ts"],
"@openclaw/net-policy/url-userinfo": ["./packages/net-policy/src/url-userinfo.ts"],
"@openclaw/net-policy/*": ["./packages/net-policy/src/*"],
"@openclaw/web-content-core/provider-runtime-shared": [
"./packages/web-content-core/src/provider-runtime-shared.ts"
],
"@openclaw/web-content-core/*": ["./packages/web-content-core/src/*"],
"@openclaw/speech-core": ["./packages/speech-core/runtime-api.ts"],
"@openclaw/speech-core/runtime-api": ["./packages/speech-core/runtime-api.ts"],
"@openclaw/speech-core/speaker": ["./packages/speech-core/speaker.ts"],
-3
View File
@@ -565,9 +565,6 @@ const configs = [
neverBundle: shouldExternalizeTerminalCoreDependency,
},
}),
nodeWorkspacePackageBuildConfig("web-content-core", {
outDir: "packages/web-content-core/dist",
}),
nodeWorkspacePackageBuildConfig("speech-core", {
entry: buildSpeechCoreDistEntries(),
deps: {