Skip to content

Commit aa15de8

Browse files
hxy91819steipete
authored andcommitted
plugin-sdk: split command status surface
1 parent 691e2aa commit aa15de8

13 files changed

Lines changed: 120 additions & 75 deletions

File tree

docs/plugins/sdk-migration.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,7 @@ Current bundled provider examples:
245245
| `plugin-sdk/allow-from` | Allowlist formatting | `formatAllowFromLowercase` |
246246
| `plugin-sdk/allowlist-resolution` | Allowlist input mapping | `mapAllowlistResolutionInputs` |
247247
| `plugin-sdk/command-auth` | Command gating and command-surface helpers | `resolveControlCommandGate`, sender-authorization helpers, command registry helpers |
248+
| `plugin-sdk/command-status` | Command status/help renderers | `buildCommandsMessage`, `buildCommandsMessagePaginated`, `buildHelpMessage` |
248249
| `plugin-sdk/secret-input` | Secret input parsing | Secret input helpers |
249250
| `plugin-sdk/webhook-ingress` | Webhook request helpers | Webhook target utilities |
250251
| `plugin-sdk/webhook-request-guards` | Webhook body guard helpers | Request body read/limit helpers |

docs/plugins/sdk-overview.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ explicitly promotes one as public.
149149
| Subpath | Key exports |
150150
| --- | --- |
151151
| `plugin-sdk/command-auth` | `resolveControlCommandGate`, command registry helpers, sender-authorization helpers |
152+
| `plugin-sdk/command-status` | Command/help message builders such as `buildCommandsMessagePaginated` and `buildHelpMessage` |
152153
| `plugin-sdk/approval-auth-runtime` | Approver resolution and same-chat action-auth helpers |
153154
| `plugin-sdk/approval-client-runtime` | Native exec approval profile/filter helpers |
154155
| `plugin-sdk/approval-delivery-runtime` | Native approval capability/delivery adapters |

extensions/telegram/src/bot-handlers.runtime.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,8 @@ import {
55
createInboundDebouncer,
66
resolveInboundDebounceMs,
77
} from "openclaw/plugin-sdk/channel-inbound";
8-
import {
9-
buildCommandsMessagePaginated,
10-
resolveStoredModelOverride,
11-
} from "openclaw/plugin-sdk/command-auth";
8+
import { resolveStoredModelOverride } from "openclaw/plugin-sdk/command-auth";
9+
import { buildCommandsMessagePaginated } from "openclaw/plugin-sdk/command-status";
1210
import { writeConfigFile } from "openclaw/plugin-sdk/config-runtime";
1311
import {
1412
loadSessionStore,

package.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -453,6 +453,10 @@
453453
"types": "./dist/plugin-sdk/command-auth-native.d.ts",
454454
"default": "./dist/plugin-sdk/command-auth-native.js"
455455
},
456+
"./plugin-sdk/command-status": {
457+
"types": "./dist/plugin-sdk/command-status.d.ts",
458+
"default": "./dist/plugin-sdk/command-status.js"
459+
},
456460
"./plugin-sdk/command-detection": {
457461
"types": "./dist/plugin-sdk/command-detection.d.ts",
458462
"default": "./dist/plugin-sdk/command-detection.js"

scripts/lib/plugin-sdk-doc-metadata.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,9 @@ export const pluginSdkDocMetadata = {
6262
"command-auth": {
6363
category: "channel",
6464
},
65+
"command-status": {
66+
category: "channel",
67+
},
6568
"secret-input": {
6669
category: "channel",
6770
},

scripts/lib/plugin-sdk-entrypoints.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@
102102
"dangerous-name-runtime",
103103
"command-auth",
104104
"command-auth-native",
105+
"command-status",
105106
"command-detection",
106107
"command-surface",
107108
"collection-runtime",

src/agents/context.eager-warmup.test.ts

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,13 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
22

33
const loadConfigMock = vi.hoisted(() => vi.fn());
44

5-
vi.mock("../config/config.js", () => ({
6-
loadConfig: loadConfigMock,
7-
}));
5+
vi.mock("../config/config.js", async (importOriginal) => {
6+
const actual = await importOriginal<typeof import("../config/config.js")>();
7+
return {
8+
...actual,
9+
loadConfig: loadConfigMock,
10+
};
11+
});
812

913
describe("agents/context eager warmup", () => {
1014
const originalArgv = process.argv.slice();
@@ -27,4 +31,28 @@ describe("agents/context eager warmup", () => {
2731

2832
expect(loadConfigMock).not.toHaveBeenCalled();
2933
});
34+
35+
it("does not eager-load config when onboard imports command-auth through plugin-sdk", async () => {
36+
process.argv = ["node", "openclaw", "onboard"];
37+
38+
await import("../plugin-sdk/command-auth.js");
39+
40+
expect(loadConfigMock).not.toHaveBeenCalled();
41+
});
42+
43+
it("does not eager-load config when pairing approve imports command-auth through plugin-sdk", async () => {
44+
process.argv = ["node", "openclaw", "pairing", "approve", "feishu", "BAH8YVB3"];
45+
46+
await import("../plugin-sdk/command-auth.js");
47+
48+
expect(loadConfigMock).not.toHaveBeenCalled();
49+
});
50+
51+
it("does not eager-load config when channels login imports command-auth through plugin-sdk", async () => {
52+
process.argv = ["node", "openclaw", "channels", "login", "--channel", "openclaw-weixin"];
53+
54+
await import("../plugin-sdk/command-auth.js");
55+
56+
expect(loadConfigMock).not.toHaveBeenCalled();
57+
});
3058
});

src/auto-reply/reply/model-selection.ts

Lines changed: 2 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,12 @@ import {
1515
resolveReasoningDefault,
1616
resolveThinkingDefault,
1717
} from "../../agents/model-selection.js";
18-
import { resolveSessionParentSessionKey } from "../../channels/plugins/session-conversation.js";
1918
import type { OpenClawConfig } from "../../config/config.js";
2019
import type { SessionEntry } from "../../config/sessions/types.js";
2120
import { applyModelOverrideToSessionEntry } from "../../sessions/model-overrides.js";
22-
import {
23-
normalizeLowercaseStringOrEmpty,
24-
normalizeOptionalString,
25-
} from "../../shared/string-coerce.js";
21+
import { normalizeLowercaseStringOrEmpty } from "../../shared/string-coerce.js";
2622
import type { ThinkLevel } from "./directives.js";
23+
import { resolveStoredModelOverride } from "./stored-model-override.js";
2724

2825
export type ModelDirectiveSelection = {
2926
provider: string;
@@ -142,61 +139,6 @@ function boundedLevenshteinDistance(a: string, b: string, maxDistance: number):
142139
return dist;
143140
}
144141

145-
export type StoredModelOverride = {
146-
provider?: string;
147-
model: string;
148-
source: "session" | "parent";
149-
};
150-
151-
function resolveParentSessionKeyCandidate(params: {
152-
sessionKey?: string;
153-
parentSessionKey?: string;
154-
}): string | null {
155-
const explicit = normalizeOptionalString(params.parentSessionKey);
156-
if (explicit && explicit !== params.sessionKey) {
157-
return explicit;
158-
}
159-
const derived = resolveSessionParentSessionKey(params.sessionKey);
160-
if (derived && derived !== params.sessionKey) {
161-
return derived;
162-
}
163-
return null;
164-
}
165-
166-
export function resolveStoredModelOverride(params: {
167-
sessionEntry?: SessionEntry;
168-
sessionStore?: Record<string, SessionEntry>;
169-
sessionKey?: string;
170-
parentSessionKey?: string;
171-
defaultProvider: string;
172-
}): StoredModelOverride | null {
173-
const direct = resolvePersistedOverrideModelRef({
174-
defaultProvider: params.defaultProvider,
175-
overrideProvider: params.sessionEntry?.providerOverride,
176-
overrideModel: params.sessionEntry?.modelOverride,
177-
});
178-
if (direct) {
179-
return { ...direct, source: "session" };
180-
}
181-
const parentKey = resolveParentSessionKeyCandidate({
182-
sessionKey: params.sessionKey,
183-
parentSessionKey: params.parentSessionKey,
184-
});
185-
if (!parentKey || !params.sessionStore) {
186-
return null;
187-
}
188-
const parentEntry = params.sessionStore[parentKey];
189-
const parentOverride = resolvePersistedOverrideModelRef({
190-
defaultProvider: params.defaultProvider,
191-
overrideProvider: parentEntry?.providerOverride,
192-
overrideModel: parentEntry?.modelOverride,
193-
});
194-
if (!parentOverride) {
195-
return null;
196-
}
197-
return { ...parentOverride, source: "parent" };
198-
}
199-
200142
function scoreFuzzyMatch(params: {
201143
provider: string;
202144
model: string;
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import { resolvePersistedOverrideModelRef } from "../../agents/model-selection.js";
2+
import { resolveSessionParentSessionKey } from "../../channels/plugins/session-conversation.js";
3+
import type { SessionEntry } from "../../config/sessions/types.js";
4+
import { normalizeOptionalString } from "../../shared/string-coerce.js";
5+
6+
export type StoredModelOverride = {
7+
provider?: string;
8+
model: string;
9+
source: "session" | "parent";
10+
};
11+
12+
function resolveParentSessionKeyCandidate(params: {
13+
sessionKey?: string;
14+
parentSessionKey?: string;
15+
}): string | null {
16+
const explicit = normalizeOptionalString(params.parentSessionKey);
17+
if (explicit && explicit !== params.sessionKey) {
18+
return explicit;
19+
}
20+
const derived = resolveSessionParentSessionKey(params.sessionKey);
21+
if (derived && derived !== params.sessionKey) {
22+
return derived;
23+
}
24+
return null;
25+
}
26+
27+
export function resolveStoredModelOverride(params: {
28+
sessionEntry?: SessionEntry;
29+
sessionStore?: Record<string, SessionEntry>;
30+
sessionKey?: string;
31+
parentSessionKey?: string;
32+
defaultProvider: string;
33+
}): StoredModelOverride | null {
34+
const direct = resolvePersistedOverrideModelRef({
35+
defaultProvider: params.defaultProvider,
36+
overrideProvider: params.sessionEntry?.providerOverride,
37+
overrideModel: params.sessionEntry?.modelOverride,
38+
});
39+
if (direct) {
40+
return { ...direct, source: "session" };
41+
}
42+
const parentKey = resolveParentSessionKeyCandidate({
43+
sessionKey: params.sessionKey,
44+
parentSessionKey: params.parentSessionKey,
45+
});
46+
if (!parentKey || !params.sessionStore) {
47+
return null;
48+
}
49+
const parentEntry = params.sessionStore[parentKey];
50+
const parentOverride = resolvePersistedOverrideModelRef({
51+
defaultProvider: params.defaultProvider,
52+
overrideProvider: parentEntry?.providerOverride,
53+
overrideModel: parentEntry?.modelOverride,
54+
});
55+
if (!parentOverride) {
56+
return null;
57+
}
58+
return { ...parentOverride, source: "parent" };
59+
}

src/plugin-sdk/command-auth.ts

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -78,13 +78,8 @@ export {
7878
resolveModelsCommandReply,
7979
} from "../auto-reply/reply/commands-models.js";
8080
export type { ModelsProviderData } from "../auto-reply/reply/commands-models.js";
81-
export { resolveStoredModelOverride } from "../auto-reply/reply/model-selection.js";
82-
export type { StoredModelOverride } from "../auto-reply/reply/model-selection.js";
83-
export {
84-
buildCommandsMessage,
85-
buildCommandsMessagePaginated,
86-
buildHelpMessage,
87-
} from "../auto-reply/status.js";
81+
export { resolveStoredModelOverride } from "../auto-reply/reply/stored-model-override.js";
82+
export type { StoredModelOverride } from "../auto-reply/reply/stored-model-override.js";
8883

8984
export type ResolveSenderCommandAuthorizationParams = {
9085
cfg: OpenClawConfig;

0 commit comments

Comments
 (0)