@sider-ai/chrome-openclaw-sider 1.0.31

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/README.md +75 -0
  2. package/README.zh_CN.md +108 -0
  3. package/dist/account-cW9SLuNu.d.ts +75 -0
  4. package/dist/index.d.ts +12 -0
  5. package/dist/index.js +87 -0
  6. package/dist/setup-entry.d.ts +6 -0
  7. package/dist/setup-entry.js +11 -0
  8. package/dist/setup-plugin-api.d.ts +7 -0
  9. package/dist/setup-plugin-api.js +4 -0
  10. package/dist/src/account.d.ts +6 -0
  11. package/dist/src/account.js +260 -0
  12. package/dist/src/auth.d.ts +30 -0
  13. package/dist/src/auth.js +225 -0
  14. package/dist/src/channel-auto-title.d.ts +6 -0
  15. package/dist/src/channel-auto-title.js +102 -0
  16. package/dist/src/channel-builders.d.ts +105 -0
  17. package/dist/src/channel-builders.js +238 -0
  18. package/dist/src/channel-hooks.d.ts +30 -0
  19. package/dist/src/channel-hooks.js +380 -0
  20. package/dist/src/channel-monitor.d.ts +34 -0
  21. package/dist/src/channel-monitor.js +335 -0
  22. package/dist/src/channel-parts.d.ts +26 -0
  23. package/dist/src/channel-parts.js +32 -0
  24. package/dist/src/channel-relay.d.ts +117 -0
  25. package/dist/src/channel-relay.js +574 -0
  26. package/dist/src/channel-runtime.d.ts +33 -0
  27. package/dist/src/channel-runtime.js +138 -0
  28. package/dist/src/channel-send-result.d.ts +19 -0
  29. package/dist/src/channel-send-result.js +126 -0
  30. package/dist/src/channel-send.d.ts +73 -0
  31. package/dist/src/channel-send.js +291 -0
  32. package/dist/src/channel-session-model.d.ts +19 -0
  33. package/dist/src/channel-session-model.js +244 -0
  34. package/dist/src/channel-shared.d.ts +153 -0
  35. package/dist/src/channel-shared.js +96 -0
  36. package/dist/src/channel-state.d.ts +93 -0
  37. package/dist/src/channel-state.js +475 -0
  38. package/dist/src/channel-streaming.d.ts +117 -0
  39. package/dist/src/channel-streaming.js +681 -0
  40. package/dist/src/channel-types.d.ts +209 -0
  41. package/dist/src/channel-types.js +40 -0
  42. package/dist/src/channel-typing.d.ts +17 -0
  43. package/dist/src/channel-typing.js +79 -0
  44. package/dist/src/channel-util.d.ts +78 -0
  45. package/dist/src/channel-util.js +604 -0
  46. package/dist/src/channel.d.ts +14 -0
  47. package/dist/src/channel.js +834 -0
  48. package/dist/src/channel.setup.d.ts +10 -0
  49. package/dist/src/channel.setup.js +9 -0
  50. package/dist/src/config.d.ts +18 -0
  51. package/dist/src/config.js +38 -0
  52. package/dist/src/inbound-media.d.ts +37 -0
  53. package/dist/src/inbound-media.js +148 -0
  54. package/dist/src/media-upload.d.ts +87 -0
  55. package/dist/src/media-upload.js +1308 -0
  56. package/dist/src/setup-core.d.ts +72 -0
  57. package/dist/src/setup-core.js +343 -0
  58. package/dist/src/user-agent.d.ts +4 -0
  59. package/dist/src/user-agent.js +6 -0
  60. package/openclaw.plugin.json +96 -0
  61. package/package.json +47 -0
@@ -0,0 +1,138 @@
1
+ import { createSubsystemLogger } from "openclaw/plugin-sdk/core";
2
+ import { WebSocket } from "ws";
3
+ import { SIDER_CHANNEL_ID } from "./config.js";
4
+ import { SIDER_USER_AGENT } from "./user-agent.js";
5
+ let runtimeRef = null;
6
+ const siderSessionBindings = /* @__PURE__ */ new Map();
7
+ const siderRunStates = /* @__PURE__ */ new Map();
8
+ const siderRelaySockets = /* @__PURE__ */ new Map();
9
+ const siderRelayMonitors = /* @__PURE__ */ new Map();
10
+ const subsystemLog = createSubsystemLogger(`gateway/channels/${SIDER_CHANNEL_ID}`);
11
+ function createSiderWebSocket(url) {
12
+ return new WebSocket(url, {
13
+ headers: {
14
+ "User-Agent": SIDER_USER_AGENT
15
+ }
16
+ });
17
+ }
18
+ function setSiderRuntime(runtime) {
19
+ runtimeRef = runtime;
20
+ }
21
+ function getSiderRuntime() {
22
+ if (!runtimeRef) {
23
+ throw new Error("sider runtime not initialized");
24
+ }
25
+ return runtimeRef;
26
+ }
27
+ function isLogRecord(value) {
28
+ return typeof value === "object" && value !== null && !Array.isArray(value) && !(value instanceof Error);
29
+ }
30
+ function normalizeStructuredLogValue(value) {
31
+ if (value instanceof Error) {
32
+ return {
33
+ name: value.name,
34
+ message: value.message,
35
+ stack: value.stack
36
+ };
37
+ }
38
+ return value;
39
+ }
40
+ function formatLogPart(value) {
41
+ if (typeof value === "string") {
42
+ return value;
43
+ }
44
+ if (value instanceof Error) {
45
+ return value.stack ?? `${value.name}: ${value.message}`;
46
+ }
47
+ try {
48
+ const serialized = JSON.stringify(value);
49
+ return serialized ?? String(value);
50
+ } catch {
51
+ return String(value);
52
+ }
53
+ }
54
+ function formatLogMessage(...args) {
55
+ if (args.length === 0) {
56
+ return "";
57
+ }
58
+ return args.map((arg) => formatLogPart(arg)).filter((part) => part.length > 0).join(" ");
59
+ }
60
+ function buildStructuredLogData(args) {
61
+ if (args.length === 0) {
62
+ return void 0;
63
+ }
64
+ if (args.length === 1 && isLogRecord(args[0])) {
65
+ return args[0];
66
+ }
67
+ return {
68
+ args: args.map((arg) => normalizeStructuredLogValue(arg))
69
+ };
70
+ }
71
+ function normalizeLogEntry(args) {
72
+ if (args.length === 0) {
73
+ return { message: "", consoleMessage: "" };
74
+ }
75
+ const [first, ...rest] = args;
76
+ const message = typeof first === "string" ? first : formatLogPart(first);
77
+ const data = buildStructuredLogData(rest);
78
+ const consoleMessage = formatLogMessage(...args);
79
+ return { message, data, consoleMessage };
80
+ }
81
+ function emitLog(level, ...args) {
82
+ const { message, data, consoleMessage } = normalizeLogEntry(args);
83
+ if (data && Object.keys(data).length > 0) {
84
+ subsystemLog[level](message, { ...data, consoleMessage });
85
+ return;
86
+ }
87
+ subsystemLog[level](message);
88
+ }
89
+ function logDebug(...args) {
90
+ emitLog("debug", ...args);
91
+ }
92
+ function logInfo(...args) {
93
+ emitLog("info", ...args);
94
+ }
95
+ function logWarn(...args) {
96
+ emitLog("warn", ...args);
97
+ }
98
+ function logError(...args) {
99
+ emitLog("error", ...args);
100
+ }
101
+ function createMonitorLogger(log) {
102
+ const emit = (level, ...args) => {
103
+ if (log) {
104
+ log[level](formatLogMessage(...args));
105
+ return;
106
+ }
107
+ if (level === "info") {
108
+ logInfo(...args);
109
+ return;
110
+ }
111
+ logWarn(...args);
112
+ };
113
+ return {
114
+ info: (...args) => emit("info", ...args),
115
+ warn: (...args) => emit("warn", ...args)
116
+ };
117
+ }
118
+ function sleep(ms) {
119
+ return new Promise((resolve) => setTimeout(resolve, ms));
120
+ }
121
+ const siderOutboundLogger = { debug: logDebug, warn: logWarn };
122
+ export {
123
+ createMonitorLogger,
124
+ createSiderWebSocket,
125
+ getSiderRuntime,
126
+ logDebug,
127
+ logError,
128
+ logInfo,
129
+ logWarn,
130
+ runtimeRef,
131
+ setSiderRuntime,
132
+ siderOutboundLogger,
133
+ siderRelayMonitors,
134
+ siderRelaySockets,
135
+ siderRunStates,
136
+ siderSessionBindings,
137
+ sleep
138
+ };
@@ -0,0 +1,19 @@
1
+ import { R as ResolvedSiderAccount } from '../account-cW9SLuNu.js';
2
+ import { SiderPart } from './channel-types.js';
3
+ import 'openclaw/plugin-sdk/setup';
4
+ import './auth.js';
5
+ import 'openclaw/plugin-sdk';
6
+ import 'openclaw/plugin-sdk/plugin-entry';
7
+ import 'openclaw/plugin-sdk/config-runtime';
8
+ import 'ws';
9
+
10
+ declare function buildSendMediaResult(params: {
11
+ account: ResolvedSiderAccount;
12
+ sessionId: string;
13
+ part: SiderPart;
14
+ source?: string;
15
+ path?: string;
16
+ resolveDownloadUrls?: boolean;
17
+ }): Promise<Record<string, unknown>>;
18
+
19
+ export { buildSendMediaResult };
@@ -0,0 +1,126 @@
1
+ import { buildSourceFields } from "./channel-builders.js";
2
+ import { logWarn } from "./channel-runtime.js";
3
+ import { resolveSiderDownloadUrl } from "./inbound-media.js";
4
+ async function resolveDownloadUrlBestEffort(params) {
5
+ try {
6
+ return await resolveSiderDownloadUrl({
7
+ gatewayUrl: params.account.gatewayUrl,
8
+ objectKey: params.objectKey,
9
+ token: params.account.token,
10
+ timeoutMs: params.account.sendTimeoutMs
11
+ });
12
+ } catch (err) {
13
+ logWarn("sider failed to resolve download url for outbound file", {
14
+ accountId: params.account.accountId,
15
+ sessionId: params.sessionId,
16
+ objectKey: params.objectKey,
17
+ error: String(err)
18
+ });
19
+ return void 0;
20
+ }
21
+ }
22
+ function readStringField(record, key) {
23
+ const value = record[key];
24
+ if (typeof value !== "string") {
25
+ return void 0;
26
+ }
27
+ const trimmed = value.trim();
28
+ return trimmed || void 0;
29
+ }
30
+ function buildResultSourceFields(source) {
31
+ const fields = buildSourceFields(source);
32
+ const sourcePath = typeof fields.source_path === "string" ? fields.source_path : void 0;
33
+ const sourceUrl = typeof fields.source_url === "string" ? fields.source_url : void 0;
34
+ return {
35
+ ...fields,
36
+ ...sourcePath ? { sourcePath } : {},
37
+ ...sourceUrl ? { sourceUrl } : {}
38
+ };
39
+ }
40
+ function summarizeSiderFilePart(part) {
41
+ if (part.type !== "core.file" && part.type !== "core.media") {
42
+ return void 0;
43
+ }
44
+ const payload = part.payload && typeof part.payload === "object" && !Array.isArray(part.payload) ? part.payload : void 0;
45
+ const summary = {};
46
+ if (payload) {
47
+ summary.fileId = readStringField(payload, "file_id") ?? readStringField(payload, "fileId");
48
+ summary.objectKey = readStringField(payload, "object_key") ?? readStringField(payload, "objectKey");
49
+ summary.filename = readStringField(payload, "name") ?? readStringField(payload, "file_name") ?? readStringField(payload, "fileName");
50
+ summary.contentType = readStringField(payload, "mime") ?? readStringField(payload, "mime_type") ?? readStringField(payload, "mimeType") ?? readStringField(payload, "content_type") ?? readStringField(payload, "contentType");
51
+ const sizeValue = payload.size;
52
+ if (typeof sizeValue === "number" && Number.isFinite(sizeValue)) {
53
+ summary.size = sizeValue;
54
+ }
55
+ summary.sha256 = readStringField(payload, "sha256");
56
+ summary.uploadStatus = readStringField(payload, "upload_status") ?? readStringField(payload, "uploadStatus");
57
+ summary.downloadUrl = readStringField(payload, "download_url") ?? readStringField(payload, "downloadUrl");
58
+ }
59
+ const meta = part.meta && typeof part.meta === "object" && !Array.isArray(part.meta) ? part.meta : void 0;
60
+ if (meta) {
61
+ summary.mediaKind = readStringField(meta, "media_kind") ?? summary.mediaKind;
62
+ }
63
+ return summary;
64
+ }
65
+ async function buildSendMediaResult(params) {
66
+ const summary = summarizeSiderFilePart(params.part);
67
+ const result = {};
68
+ if (!summary) {
69
+ if (params.path) {
70
+ result.path = params.path;
71
+ }
72
+ if (params.source) {
73
+ Object.assign(result, buildResultSourceFields(params.source));
74
+ }
75
+ return result;
76
+ }
77
+ if (params.resolveDownloadUrls !== false && !summary.downloadUrl && summary.objectKey) {
78
+ const downloadUrl = await resolveDownloadUrlBestEffort({
79
+ account: params.account,
80
+ sessionId: params.sessionId,
81
+ objectKey: summary.objectKey
82
+ });
83
+ if (downloadUrl) {
84
+ summary.downloadUrl = downloadUrl;
85
+ }
86
+ }
87
+ if (params.path) {
88
+ result.path = params.path;
89
+ }
90
+ if (params.source) {
91
+ Object.assign(result, buildResultSourceFields(params.source));
92
+ }
93
+ if (summary.contentType) {
94
+ result.contentType = summary.contentType;
95
+ }
96
+ if (summary.filename) {
97
+ result.filename = summary.filename;
98
+ }
99
+ if (summary.fileId) {
100
+ result.fileId = summary.fileId;
101
+ result.file_id = summary.fileId;
102
+ }
103
+ if (summary.objectKey) {
104
+ result.objectKey = summary.objectKey;
105
+ result.object_key = summary.objectKey;
106
+ }
107
+ if (summary.downloadUrl) {
108
+ result.downloadUrl = summary.downloadUrl;
109
+ result.download_url = summary.downloadUrl;
110
+ }
111
+ if (typeof summary.size === "number") {
112
+ result.size = summary.size;
113
+ }
114
+ if (summary.sha256) {
115
+ result.sha256 = summary.sha256;
116
+ }
117
+ if (summary.uploadStatus) {
118
+ }
119
+ if (summary.mediaKind) {
120
+ result.mediaKind = summary.mediaKind;
121
+ }
122
+ return result;
123
+ }
124
+ export {
125
+ buildSendMediaResult
126
+ };
@@ -0,0 +1,73 @@
1
+ import { OpenClawConfig } from 'openclaw/plugin-sdk';
2
+ import { R as ResolvedSiderAccount } from '../account-cW9SLuNu.js';
3
+ import { SiderPart } from './channel-types.js';
4
+ import { DeferredSiderUpload } from './media-upload.js';
5
+ import 'openclaw/plugin-sdk/setup';
6
+ import './auth.js';
7
+ import 'openclaw/plugin-sdk/plugin-entry';
8
+ import 'openclaw/plugin-sdk/config-runtime';
9
+ import 'ws';
10
+
11
+ type PreparedSiderSend = {
12
+ account: ResolvedSiderAccount;
13
+ sessionId: string;
14
+ parts: SiderPart[];
15
+ deferredUploads: DeferredSiderUpload[];
16
+ attachments: string[];
17
+ };
18
+ type NormalizedSiderAttachment = {
19
+ source: string;
20
+ meta: Record<string, unknown>;
21
+ };
22
+ type DispatchedSiderParts = {
23
+ kind: "merged";
24
+ runId: string;
25
+ sessionKey: string;
26
+ } | {
27
+ kind: "sent";
28
+ messageId: string;
29
+ conversationId: string;
30
+ };
31
+ declare function readSiderSendText(params: Record<string, unknown>): string | undefined;
32
+ declare function collectSiderSendAttachments(params: Record<string, unknown>): NormalizedSiderAttachment[];
33
+ declare function prepareSiderSendParts(params: {
34
+ cfg: OpenClawConfig;
35
+ accountId?: string | null;
36
+ to: string;
37
+ text?: string;
38
+ mediaUrl?: string;
39
+ mediaUrls?: string[];
40
+ attachments?: NormalizedSiderAttachment[];
41
+ mediaLocalRoots?: readonly string[];
42
+ errorHint?: string;
43
+ }): Promise<PreparedSiderSend>;
44
+ declare function dispatchSiderParts(params: {
45
+ account: ResolvedSiderAccount;
46
+ sessionId: string;
47
+ parts: SiderPart[];
48
+ deferredUploads?: readonly DeferredSiderUpload[];
49
+ allowMergeActiveRun?: boolean;
50
+ context: string;
51
+ }): Promise<DispatchedSiderParts>;
52
+ declare function handleSiderSendAction(params: {
53
+ cfg: OpenClawConfig;
54
+ accountId?: string | null;
55
+ params: Record<string, unknown>;
56
+ mediaLocalRoots?: readonly string[];
57
+ }): Promise<Record<string, unknown>>;
58
+ declare function sendToSider(params: {
59
+ cfg: OpenClawConfig;
60
+ accountId?: string | null;
61
+ to: string;
62
+ text?: string;
63
+ mediaUrl?: string;
64
+ mediaUrls?: string[];
65
+ mediaLocalRoots?: readonly string[];
66
+ errorHint?: string;
67
+ }): Promise<{
68
+ account: ResolvedSiderAccount;
69
+ messageId: string;
70
+ conversationId: string;
71
+ }>;
72
+
73
+ export { type DispatchedSiderParts, type PreparedSiderSend, collectSiderSendAttachments, dispatchSiderParts, handleSiderSendAction, prepareSiderSendParts, readSiderSendText, sendToSider };
@@ -0,0 +1,291 @@
1
+ import { readStringParam } from "openclaw/plugin-sdk/agent-runtime";
2
+ import {
3
+ describeSiderAccountConfigurationError,
4
+ resolveManagedSiderAccount,
5
+ resolveOutboundSessionId
6
+ } from "./account.js";
7
+ import { resolveOutboundMediaLoadUrl, uploadMediaToSider } from "./media-upload.js";
8
+ import { logDebug } from "./channel-runtime.js";
9
+ import { sendMessageToSider } from "./channel-relay.js";
10
+ import {
11
+ enqueueCompletedParts,
12
+ getOrCreateSiderRunState,
13
+ resolveSiderBindingByTarget,
14
+ resolveSiderRunState,
15
+ scheduleQueuedPartFlush
16
+ } from "./channel-state.js";
17
+ import { buildSendMediaResult } from "./channel-send-result.js";
18
+ import { toRecord } from "./channel-util.js";
19
+ import {
20
+ startDeferredSiderUploads
21
+ } from "./media-upload.js";
22
+ import { buildTextPart } from "./channel-builders.js";
23
+ const SIDER_ATTACHMENT_SOURCE_KEYS = [
24
+ "path",
25
+ "filePath",
26
+ "file_path",
27
+ "fileUrl",
28
+ "file_url",
29
+ "url",
30
+ "media",
31
+ "mediaUrl",
32
+ "media_url",
33
+ "imageUrl",
34
+ "image_url"
35
+ ];
36
+ const SIDER_ATTACHMENT_SOURCE_ARRAY_KEYS = [
37
+ "mediaUrls",
38
+ "media_urls",
39
+ "imageUrls",
40
+ "image_urls"
41
+ ];
42
+ function readSiderSendText(params) {
43
+ return readStringParam(params, "message", { allowEmpty: true }) ?? readStringParam(params, "text", { allowEmpty: true }) ?? readStringParam(params, "content", { allowEmpty: true });
44
+ }
45
+ function collectSiderSendAttachments(params) {
46
+ const attachments = [];
47
+ const seen = /* @__PURE__ */ new Set();
48
+ const pushSource = (value, metaKey) => {
49
+ if (typeof value !== "string") {
50
+ return;
51
+ }
52
+ const trimmed = value.trim();
53
+ if (!trimmed || seen.has(trimmed)) {
54
+ return;
55
+ }
56
+ seen.add(trimmed);
57
+ attachments.push({
58
+ source: trimmed,
59
+ meta: { [metaKey]: trimmed }
60
+ });
61
+ };
62
+ for (const key of SIDER_ATTACHMENT_SOURCE_KEYS) {
63
+ pushSource(params[key], key);
64
+ }
65
+ for (const key of SIDER_ATTACHMENT_SOURCE_ARRAY_KEYS) {
66
+ const values = params[key];
67
+ if (!Array.isArray(values)) {
68
+ continue;
69
+ }
70
+ for (const value of values) {
71
+ pushSource(value, key);
72
+ }
73
+ }
74
+ const structuredAttachments = params.attachments;
75
+ if (!Array.isArray(structuredAttachments)) {
76
+ return attachments;
77
+ }
78
+ for (const item of structuredAttachments) {
79
+ const record = toRecord(item);
80
+ if (!record) {
81
+ continue;
82
+ }
83
+ for (const key of SIDER_ATTACHMENT_SOURCE_KEYS) {
84
+ if (typeof record[key] === "string") {
85
+ pushSource(record[key], key);
86
+ continue;
87
+ }
88
+ }
89
+ for (const key of SIDER_ATTACHMENT_SOURCE_ARRAY_KEYS) {
90
+ const values = record[key];
91
+ if (!Array.isArray(values)) {
92
+ continue;
93
+ }
94
+ for (const value of values) {
95
+ pushSource(value, key);
96
+ }
97
+ }
98
+ }
99
+ return attachments;
100
+ }
101
+ async function prepareSiderSendParts(params) {
102
+ const account = await resolveManagedSiderAccount({
103
+ cfg: params.cfg,
104
+ accountId: params.accountId ?? void 0
105
+ });
106
+ if (!account.configured) {
107
+ throw new Error(describeSiderAccountConfigurationError(account));
108
+ }
109
+ const sessionId = resolveOutboundSessionId({ account, to: params.to });
110
+ const attachmentItems = params.attachments?.length ? params.attachments : params.mediaUrls?.length ? params.mediaUrls.map((source) => ({ source, meta: { mediaUrls: [source] } })) : params.mediaUrl?.trim() ? [{ source: params.mediaUrl.trim(), meta: { mediaUrl: params.mediaUrl.trim() } }] : [];
111
+ const parts = [];
112
+ const deferredUploads = [];
113
+ if (params.text) {
114
+ parts.push(buildTextPart(params.text));
115
+ }
116
+ for (const attachment of attachmentItems) {
117
+ const loadUrl = await resolveOutboundMediaLoadUrl({
118
+ source: attachment.source,
119
+ mediaLocalRoots: params.mediaLocalRoots
120
+ });
121
+ if (!loadUrl) {
122
+ continue;
123
+ }
124
+ const prepared = await uploadMediaToSider({
125
+ account,
126
+ sessionId,
127
+ loadUrl,
128
+ source: attachment.source,
129
+ meta: attachment.meta,
130
+ mediaLocalRoots: params.mediaLocalRoots,
131
+ deferUpload: true
132
+ });
133
+ parts.push(prepared.part);
134
+ if (prepared.deferredUpload) {
135
+ deferredUploads.push(prepared.deferredUpload);
136
+ }
137
+ }
138
+ if (parts.length === 0) {
139
+ throw new Error(params.errorHint ?? "sider send requires text or media");
140
+ }
141
+ return {
142
+ account,
143
+ sessionId,
144
+ parts,
145
+ deferredUploads,
146
+ attachments: attachmentItems.map((item) => item.source)
147
+ };
148
+ }
149
+ function resolveActiveSiderRunTarget(params) {
150
+ const target = resolveSiderBindingByTarget({
151
+ accountId: params.account.accountId,
152
+ sessionId: params.sessionId
153
+ });
154
+ const runId = target?.binding.currentRunId;
155
+ if (!target || !runId) {
156
+ return void 0;
157
+ }
158
+ const runState = resolveSiderRunState(runId) ?? getOrCreateSiderRunState(runId);
159
+ runState.sessionKey = target.sessionKey;
160
+ runState.sessionId = params.sessionId;
161
+ runState.accountId = params.account.accountId;
162
+ return { sessionKey: target.sessionKey, runId };
163
+ }
164
+ async function dispatchSiderParts(params) {
165
+ if (params.allowMergeActiveRun && params.parts.length > 0) {
166
+ const activeRun = resolveActiveSiderRunTarget({
167
+ account: params.account,
168
+ sessionId: params.sessionId
169
+ });
170
+ if (activeRun) {
171
+ enqueueCompletedParts({
172
+ runId: activeRun.runId,
173
+ parts: params.parts,
174
+ sessionKey: activeRun.sessionKey,
175
+ sessionId: params.sessionId,
176
+ accountId: params.account.accountId
177
+ });
178
+ void scheduleQueuedPartFlush({
179
+ runId: activeRun.runId,
180
+ context: `${params.context}.merge_active_run`
181
+ });
182
+ logDebug("sider send merged parts into active run", {
183
+ accountId: params.account.accountId,
184
+ sessionId: params.sessionId,
185
+ runId: activeRun.runId,
186
+ mergedPartTypes: params.parts.map((part) => part.type),
187
+ context: params.context
188
+ });
189
+ if ((params.deferredUploads?.length ?? 0) > 0) {
190
+ startDeferredSiderUploads({
191
+ account: params.account,
192
+ sessionId: params.sessionId,
193
+ uploads: params.deferredUploads ?? [],
194
+ context: `${params.context}.merge_active_run`
195
+ });
196
+ }
197
+ return {
198
+ kind: "merged",
199
+ runId: activeRun.runId,
200
+ sessionKey: activeRun.sessionKey
201
+ };
202
+ }
203
+ }
204
+ const result = await sendMessageToSider({
205
+ account: params.account,
206
+ sessionId: params.sessionId,
207
+ parts: params.parts
208
+ });
209
+ if ((params.deferredUploads?.length ?? 0) > 0) {
210
+ startDeferredSiderUploads({
211
+ account: params.account,
212
+ sessionId: params.sessionId,
213
+ uploads: params.deferredUploads ?? [],
214
+ context: params.context
215
+ });
216
+ }
217
+ return {
218
+ kind: "sent",
219
+ messageId: result.messageId,
220
+ conversationId: result.conversationId
221
+ };
222
+ }
223
+ async function handleSiderSendAction(params) {
224
+ const to = readStringParam(params.params, "to", { required: true });
225
+ const text = readSiderSendText(params.params);
226
+ const attachments = collectSiderSendAttachments(params.params);
227
+ const prepared = await prepareSiderSendParts({
228
+ cfg: params.cfg,
229
+ accountId: params.accountId,
230
+ to,
231
+ text,
232
+ attachments,
233
+ mediaLocalRoots: params.mediaLocalRoots,
234
+ errorHint: "sider send requires text or attachments"
235
+ });
236
+ const mediaParts = prepared.parts.filter(
237
+ (part) => part.type === "core.file" || part.type === "core.media"
238
+ );
239
+ const fileResults = await Promise.all(
240
+ mediaParts.map(
241
+ async (part, index) => await buildSendMediaResult({
242
+ account: prepared.account,
243
+ sessionId: prepared.sessionId,
244
+ part,
245
+ source: prepared.attachments[index]
246
+ })
247
+ )
248
+ );
249
+ await dispatchSiderParts({
250
+ account: prepared.account,
251
+ sessionId: prepared.sessionId,
252
+ parts: prepared.parts,
253
+ deferredUploads: prepared.deferredUploads,
254
+ allowMergeActiveRun: true,
255
+ context: "action.send"
256
+ });
257
+ if (fileResults.length === 0) {
258
+ return { ok: true };
259
+ }
260
+ return {
261
+ ...fileResults[0],
262
+ files: fileResults,
263
+ fileCount: fileResults.length
264
+ };
265
+ }
266
+ async function sendToSider(params) {
267
+ const prepared = await prepareSiderSendParts(params);
268
+ const result = await dispatchSiderParts({
269
+ account: prepared.account,
270
+ sessionId: prepared.sessionId,
271
+ parts: prepared.parts,
272
+ deferredUploads: prepared.deferredUploads,
273
+ context: "outbound.send"
274
+ });
275
+ if (result.kind !== "sent") {
276
+ throw new Error("sider outbound.send unexpectedly merged into active run");
277
+ }
278
+ return {
279
+ account: prepared.account,
280
+ messageId: result.messageId,
281
+ conversationId: result.conversationId
282
+ };
283
+ }
284
+ export {
285
+ collectSiderSendAttachments,
286
+ dispatchSiderParts,
287
+ handleSiderSendAction,
288
+ prepareSiderSendParts,
289
+ readSiderSendText,
290
+ sendToSider
291
+ };
@@ -0,0 +1,19 @@
1
+ import { PluginRuntime, OpenClawConfig } from 'openclaw/plugin-sdk';
2
+
3
+ type SiderSessionModelResetResult = {
4
+ changed: false;
5
+ reason: string;
6
+ } | {
7
+ changed: true;
8
+ reason: "auto-override" | "runtime-model";
9
+ previousModel: string;
10
+ defaultModel: string;
11
+ };
12
+ declare function maybeResetSiderSessionToCurrentDefaultModel(params: {
13
+ runtime: PluginRuntime;
14
+ cfg: OpenClawConfig;
15
+ agentId: string;
16
+ sessionKey?: string;
17
+ }): Promise<SiderSessionModelResetResult>;
18
+
19
+ export { type SiderSessionModelResetResult, maybeResetSiderSessionToCurrentDefaultModel };