Skip to content

Commit aed5da7

Browse files
authored
Merge branch 'main' into fix/bound-msteams-sso-api
2 parents 6604f36 + a674ce5 commit aed5da7

79 files changed

Lines changed: 1285 additions & 1037 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/ci.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1613,6 +1613,12 @@ jobs:
16131613
has_package_script "deadcode:unused-files"; then
16141614
pnpm deadcode:dependencies
16151615
pnpm deadcode:unused-files
1616+
if has_package_script "deadcode:exports"; then
1617+
pnpm deadcode:exports
1618+
elif [[ "$HISTORICAL_TARGET" != "true" ]]; then
1619+
echo "Current CI targets must provide the deadcode:exports package script." >&2
1620+
exit 1
1621+
fi
16161622
elif [[ "$HISTORICAL_TARGET" == "true" ]] && has_package_script "deadcode:ci"; then
16171623
pnpm deadcode:ci
16181624
else

config/knip.config.ts

Lines changed: 53 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -79,62 +79,69 @@ const rootBundledPluginRuntimeDependencies = [
7979
"tokenjuice",
8080
] as const;
8181

82+
// These files are test infrastructure, so their exports are intentionally
83+
// available to tests without becoming part of the production dead-code scan.
84+
const ignoredTestSupportFiles = [
85+
"**/__tests__/**",
86+
"src/test-utils/**",
87+
"**/test-helpers/**",
88+
"**/test-fixtures/**",
89+
"**/test-support/**",
90+
"**/test-*.ts",
91+
"**/vitest*.{ts,mjs}",
92+
"**/*test-helpers.ts",
93+
"**/*test-fixtures.ts",
94+
"**/*test-harness.ts",
95+
"**/*test-utils.ts",
96+
"**/*test-support.ts",
97+
"**/*test-shared.ts",
98+
"**/*mocks.ts",
99+
"**/*.e2e-mocks.ts",
100+
"**/*.e2e-*.ts",
101+
"**/*.fixture-test-support.ts",
102+
"**/*.harness.ts",
103+
"**/*.job-fixtures.ts",
104+
"**/*.mock-harness.ts",
105+
"**/*.menu-test-support.ts",
106+
"**/*.suite-helpers.ts",
107+
"**/*.test-setup.ts",
108+
"**/job-fixtures.ts",
109+
"**/*test-mocks.ts",
110+
"**/*test-runtime*.ts",
111+
"**/*.mock-setup.ts",
112+
"**/*.cases.ts",
113+
"**/*.e2e-harness.ts",
114+
"**/*.fixture.ts",
115+
"**/*.fixtures.ts",
116+
"**/*.mocks.ts",
117+
"**/*.mocks.shared.ts",
118+
"**/*.route-test-support.ts",
119+
"**/*.shared-test.ts",
120+
"**/*.suite.ts",
121+
"**/*.test-runtime.ts",
122+
"**/*.testkit.ts",
123+
"**/*.test-fixtures.ts",
124+
"**/*.test-harness.ts",
125+
"**/*.test-helper.ts",
126+
"**/*.test-helpers.ts",
127+
"**/*.test-mocks.ts",
128+
"**/*.test-utils.ts",
129+
"test/helpers/live-image-probe.ts",
130+
] as const;
131+
82132
const config = {
83133
ignoreFiles: [
84134
"scripts/**",
85135
"packages/*/dist/**",
86-
"**/__tests__/**",
87-
"src/test-utils/**",
88-
"**/test-helpers/**",
89-
"**/test-fixtures/**",
90-
"**/test-support/**",
91136
"**/live-*.ts",
92-
"**/test-*.ts",
93-
"**/vitest*.{ts,mjs}",
94-
"**/*test-helpers.ts",
95-
"**/*test-fixtures.ts",
96-
"**/*test-harness.ts",
97-
"**/*test-utils.ts",
98-
"**/*test-support.ts",
99-
"**/*test-shared.ts",
100-
"**/*mocks.ts",
101-
"**/*.e2e-mocks.ts",
102-
"**/*.e2e-*.ts",
103-
"**/*.fixture-test-support.ts",
104-
"**/*.harness.ts",
105-
"**/*.job-fixtures.ts",
106-
"**/*.mock-harness.ts",
107-
"**/*.menu-test-support.ts",
108-
"**/*.suite-helpers.ts",
109-
"**/*.test-setup.ts",
110-
"**/job-fixtures.ts",
111-
"**/*test-mocks.ts",
112-
"**/*test-runtime*.ts",
113-
"**/*.mock-setup.ts",
114-
"**/*.cases.ts",
115-
"**/*.e2e-harness.ts",
116-
"**/*.fixture.ts",
117-
"**/*.fixtures.ts",
118-
"**/*.mocks.ts",
119-
"**/*.mocks.shared.ts",
120-
"**/*.route-test-support.ts",
121-
"**/*.shared-test.ts",
122-
"**/*.suite.ts",
123-
"**/*.test-runtime.ts",
124-
"**/*.testkit.ts",
125-
"**/*.test-fixtures.ts",
126-
"**/*.test-harness.ts",
127-
"**/*.test-helper.ts",
128-
"**/*.test-helpers.ts",
129-
"**/*.test-mocks.ts",
130-
"**/*.test-utils.ts",
131-
"test/helpers/live-image-probe.ts",
132137
"src/secrets/credential-matrix.ts",
133138
"src/shared/text/assistant-visible-text.ts",
134139
bundledPluginFile("telegram", "src/bot/reply-threading.ts"),
135140
bundledPluginFile("telegram", "src/draft-chunking.ts"),
136141
],
137-
ignore: ["packages/*/dist/**"],
142+
// Knip's `ignoreFiles` only suppresses unused-file findings. Test helpers
143+
// belong in `ignore` so they do not inflate unused-export/type findings.
144+
ignore: ["packages/*/dist/**", ...ignoredTestSupportFiles],
138145
workspaces: {
139146
".": {
140147
entry: rootEntries,
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
75edbe496d57c19d3ea53e51908c6c7aa37ed8a5c7311234c7ce17eea0775db8 plugin-sdk-api-baseline.json
2-
f21d330f28d93248bd6a7d96780452d6a969f5d1fcca1b9246ceb5a68e589b9f plugin-sdk-api-baseline.jsonl
1+
e07c029f6f47cc853bbdcb4cdb8e042e81fb6a3ba82ecbacf9ce94b508038204 plugin-sdk-api-baseline.json
2+
05a9890c8ee43b0c65ec24e820b16f89f2355e24372280198114ad0d30fcca24 plugin-sdk-api-baseline.jsonl

docs/ci.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ job budget.
115115

116116
Android CI runs both `testPlayDebugUnitTest` and `testThirdPartyDebugUnitTest` and then builds the Play debug APK. The third-party flavor has no separate source set or manifest; its unit-test lane still compiles the flavor with the SMS/call-log BuildConfig flags, while avoiding a duplicate debug APK packaging job on every Android-relevant push.
117117

118-
The `check-dependencies` shard runs a production Knip dependency-only pass and the unused-file allowlist check. The unused-file guard fails when a PR adds a new unreviewed unused file or leaves a stale allowlist entry, while preserving intentional dynamic plugin, generated, build, live-test, and package bridge surfaces that Knip cannot resolve statically. The unused-exports baseline remains available as an advisory maintenance scan through `pnpm deadcode:exports`; after deleting dead code, regenerate it with `pnpm deadcode:exports:update`.
118+
The `check-dependencies` shard runs production Knip dependency, unused-file, and unused-export checks. The unused-file guard fails when a PR adds a new unreviewed unused file or leaves a stale allowlist entry, while preserving intentional dynamic plugin, generated, build, live-test, and package bridge surfaces that Knip cannot resolve statically. The unused-export guard excludes test-support files, then fails on new findings or stale required baseline entries; after deleting dead exports, regenerate the shrink-only baseline with `pnpm deadcode:exports:update`. Historical targets run the export guard when they provide it and retain their older dead-code fallback otherwise.
119119

120120
## ClawSweeper activity forwarding
121121

docs/cli/backup.md

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,21 @@ Auth profiles and other per-agent runtime state live in SQLite under the state d
8989

9090
OpenClaw canonicalizes paths before building the archive: if config, the credentials directory, or a workspace already live inside the state directory, they are not duplicated as separate top-level backup sources. Missing paths are skipped.
9191

92-
During archive creation, OpenClaw skips known live-mutation files with no restoration value: active agent session transcripts, cron run logs, rolling logs, delivery queues, socket/pid/temp files under the state directory, and related durable-queue temp files. The JSON result's `skippedVolatileCount` reports how many files were intentionally omitted. SQLite databases under the state directory are compacted with `VACUUM INTO` so deleted-page remnants do not enter the archive, and live WAL/SHM files are not copied. A plugin-owned database that requires unavailable owner-defined SQLite capabilities fails closed rather than falling back to a raw page copy. SQLite files included through workspace backups are copied as workspace files and are not covered by the compaction guarantee.
92+
During archive creation, OpenClaw excludes known live-mutation paths before `tar` reads them. This avoids races between a file's recorded size and concurrent writes. The filter applies these state-relative rules under each backed-up state directory:
93+
94+
| State-relative scope | Skipped file suffixes |
95+
| -------------------------------------------- | ----------------------------- |
96+
| `sessions/**` | `.jsonl`, `.log` |
97+
| `agents/<agentId>/sessions/**` | `.jsonl`, `.log` |
98+
| `cron/runs/**` | `.jsonl`, `.log` |
99+
| `logs/**` | `.jsonl`, `.log` |
100+
| `delivery-queue/**` | `.json`, `.delivered`, `.tmp` |
101+
| `session-delivery-queue/**` | `.json`, `.delivered`, `.tmp` |
102+
| Any path under the backed-up state directory | `.sock`, `.pid`, `.tmp` |
103+
104+
These rules do not filter workspace files outside the state directory. They also omit completed transcript and log files that match the table, so retain those records separately when needed. The JSON result's `skippedVolatileCount` reports how many files were intentionally omitted.
105+
106+
SQLite databases under the state directory are compacted with `VACUUM INTO` so deleted-page remnants do not enter the archive, and live WAL/SHM files are not copied. A plugin-owned database that requires unavailable owner-defined SQLite capabilities fails closed rather than falling back to a raw page copy. SQLite files included through workspace backups are copied as workspace files and are not covered by the compaction guarantee.
93107

94108
Installed plugin source and manifest files under the state directory's `extensions/` tree are included, but their nested `node_modules/` dependency trees are skipped as rebuildable install artifacts. After restoring an archive, use `openclaw plugins update <id>` or reinstall with `openclaw plugins install <spec> --force` if a restored plugin reports missing dependencies.
95109

extensions/discord/src/monitor/model-picker.state.ts

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,7 @@ import type { ComponentData } from "../internal/discord.js";
1212
export const DISCORD_MODEL_PICKER_CUSTOM_ID_KEY = "mdlpk";
1313
export const DISCORD_CUSTOM_ID_MAX_CHARS = 100;
1414

15-
export const DISCORD_COMPONENT_MAX_ROWS = 5;
16-
export const DISCORD_COMPONENT_MAX_BUTTONS_PER_ROW = 5;
17-
export const DISCORD_COMPONENT_MAX_SELECT_OPTIONS = 25;
15+
const DISCORD_COMPONENT_MAX_SELECT_OPTIONS = 25;
1816

1917
export const DISCORD_MODEL_PICKER_PROVIDER_PAGE_SIZE = DISCORD_COMPONENT_MAX_SELECT_OPTIONS;
2018
export const DISCORD_MODEL_PICKER_PROVIDER_SINGLE_PAGE_MAX = DISCORD_COMPONENT_MAX_SELECT_OPTIONS;
@@ -43,8 +41,8 @@ const PICKER_ACTIONS = [
4341
const PICKER_VIEWS = ["providers", "models", "recents"] as const;
4442

4543
export type DiscordModelPickerCommandContext = (typeof COMMAND_CONTEXTS)[number];
46-
export type DiscordModelPickerAction = (typeof PICKER_ACTIONS)[number];
47-
export type DiscordModelPickerView = (typeof PICKER_VIEWS)[number];
44+
type DiscordModelPickerAction = (typeof PICKER_ACTIONS)[number];
45+
type DiscordModelPickerView = (typeof PICKER_VIEWS)[number];
4846
export type DiscordModelPickerLayout = "v2" | "classic";
4947

5048
export type DiscordModelPickerState = {
@@ -550,7 +548,7 @@ export function findModelBucketId(
550548
return containing && containing.id !== "all" ? containing.id : undefined;
551549
}
552550

553-
export function buildDiscordModelPickerProviderItems(
551+
function buildDiscordModelPickerProviderItems(
554552
data: ModelsProviderData,
555553
): DiscordModelPickerProviderItem[] {
556554
// Sort lexicographically so the alpha-bucket boundaries are deterministic

extensions/discord/src/monitor/model-picker.test.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,14 @@ import {
1919
loadDiscordModelPickerData,
2020
parseDiscordModelPickerCustomId,
2121
parseDiscordModelPickerData,
22+
} from "./model-picker.state.js";
23+
import { createModelsProviderData } from "./model-picker.test-utils.js";
24+
import {
2225
renderDiscordModelPickerModelsView,
2326
renderDiscordModelPickerProvidersView,
2427
renderDiscordModelPickerRecentsView,
2528
toDiscordModelPickerMessagePayload,
26-
} from "./model-picker.js";
27-
import { createModelsProviderData } from "./model-picker.test-utils.js";
29+
} from "./model-picker.view.js";
2830

2931
const buildModelsProviderDataMock = vi.hoisted(() => vi.fn());
3032

extensions/discord/src/monitor/model-picker.ts

Lines changed: 0 additions & 48 deletions
This file was deleted.

extensions/discord/src/monitor/native-command-model-picker-interaction.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,14 @@ import {
2525
findProviderBucketId,
2626
loadDiscordModelPickerData,
2727
parseDiscordModelPickerData,
28+
type DiscordModelPickerState,
29+
} from "./model-picker.state.js";
30+
import {
2831
renderDiscordModelPickerModelsView,
2932
renderDiscordModelPickerProvidersView,
3033
renderDiscordModelPickerRecentsView,
3134
toDiscordModelPickerMessagePayload,
32-
type DiscordModelPickerState,
33-
} from "./model-picker.js";
35+
} from "./model-picker.view.js";
3436
import type { DispatchDiscordCommandInteraction } from "./native-command-dispatch.js";
3537
import { applyDiscordModelPickerSelection } from "./native-command-model-picker-apply.js";
3638
import {

extensions/discord/src/monitor/native-command-model-picker-ui.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,13 @@ import {
2929
import {
3030
findProviderBucketLocation,
3131
loadDiscordModelPickerData,
32-
renderDiscordModelPickerModelsView,
3332
resolveDiscordModelPickerPageForModel,
34-
toDiscordModelPickerMessagePayload,
3533
type DiscordModelPickerCommandContext,
36-
} from "./model-picker.js";
34+
} from "./model-picker.state.js";
35+
import {
36+
renderDiscordModelPickerModelsView,
37+
toDiscordModelPickerMessagePayload,
38+
} from "./model-picker.view.js";
3739
import { resolveDiscordNativeInteractionRouteState } from "./native-command-route.js";
3840
import type { SafeDiscordInteractionCall } from "./native-command-ui.types.js";
3941
import { resolveDiscordNativeInteractionChannelContext } from "./native-interaction-channel-context.js";

0 commit comments

Comments
 (0)