@clawling/clawchat-plugin-openclaw 2026.7.14-1 → 2026.7.20-1
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.
- package/dist/src/clawchat-metadata.js +16 -2
- package/dist/src/reply-dispatcher.js +9 -1
- package/dist/src/runtime.js +8 -1
- package/dist/src/storage.js +65 -0
- package/dist/src/tools.js +4 -1
- package/package.json +1 -1
- package/src/clawchat-metadata.ts +23 -2
- package/src/reply-dispatcher.ts +11 -1
- package/src/runtime.ts +9 -0
- package/src/storage.ts +102 -0
- package/src/tools.ts +4 -0
|
@@ -123,6 +123,16 @@ function pickUpdatePatch(patchInput, keys) {
|
|
|
123
123
|
}
|
|
124
124
|
return patch;
|
|
125
125
|
}
|
|
126
|
+
function persistOwnerProfileSafe(persist, owner) {
|
|
127
|
+
if (!persist)
|
|
128
|
+
return;
|
|
129
|
+
try {
|
|
130
|
+
persist(owner);
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
// The sqlite cache is advisory — a failed write must not fail the pull.
|
|
134
|
+
}
|
|
135
|
+
}
|
|
126
136
|
export async function pullOwnerMetadata(params) {
|
|
127
137
|
const getAgent = params.api.getAgentDetail ?? params.api.getAgentProfile;
|
|
128
138
|
if (!getAgent)
|
|
@@ -132,7 +142,9 @@ export async function pullOwnerMetadata(params) {
|
|
|
132
142
|
const data = await getAgent(params.agentId);
|
|
133
143
|
const metadata = ownerMetadataFromAgent(asRecord(data.agent) ?? {}, params);
|
|
134
144
|
if (params.api.getAgentOwner) {
|
|
135
|
-
|
|
145
|
+
const owner = asRecord(await params.api.getAgentOwner()) ?? {};
|
|
146
|
+
persistOwnerProfileSafe(params.persistOwnerProfile, owner);
|
|
147
|
+
addOwnerProfileMetadata(metadata, owner);
|
|
136
148
|
}
|
|
137
149
|
await writeClawChatMetadata(params.memoryRoot, { targetType: "owner", targetId: "owner" }, metadata);
|
|
138
150
|
return { ok: true, writes: [{ targetType: "owner", targetId: "owner" }], failures: [] };
|
|
@@ -275,7 +287,9 @@ export async function updateMetadata(params) {
|
|
|
275
287
|
accountUserId: params.accountUserId,
|
|
276
288
|
});
|
|
277
289
|
if (params.api.getAgentOwner) {
|
|
278
|
-
|
|
290
|
+
const owner = asRecord(await params.api.getAgentOwner()) ?? {};
|
|
291
|
+
persistOwnerProfileSafe(params.persistOwnerProfile, owner);
|
|
292
|
+
addOwnerProfileMetadata(metadata, owner);
|
|
279
293
|
}
|
|
280
294
|
await writeClawChatMetadata(params.memoryRoot, { targetType: "owner", targetId: "owner" }, metadata);
|
|
281
295
|
return { ok: true, writes: [{ targetType: "owner", targetId: "owner" }], failures: [], metadata };
|
|
@@ -284,7 +284,15 @@ export function createOpenclawClawlingReplyDispatcher(options) {
|
|
|
284
284
|
log?.info?.(`[${account.accountId}] clawchat-plugin-openclaw outbound media skipped: baseUrl not configured`);
|
|
285
285
|
return [];
|
|
286
286
|
}
|
|
287
|
-
|
|
287
|
+
const fragments = await uploadOutboundMedia(urls, { apiClient, runtime, log });
|
|
288
|
+
if (fragments.length < urls.length) {
|
|
289
|
+
// uploadOutboundMedia drops a failed item with only a log line. A shortfall
|
|
290
|
+
// means an attachment never reached the media service; surfacing it (parity
|
|
291
|
+
// with the adapter path in outbound.ts and with the Hermes plugin) stops a
|
|
292
|
+
// text-only frame from going out and reading as a successful send.
|
|
293
|
+
throw new Error(`clawchat-plugin-openclaw failed to upload ${urls.length - fragments.length}/${urls.length} outbound media attachment(s); message not sent`);
|
|
294
|
+
}
|
|
295
|
+
return fragments;
|
|
288
296
|
}
|
|
289
297
|
// ----- Reply state ------------------------------------------------------
|
|
290
298
|
let reasoningText = "";
|
package/dist/src/runtime.js
CHANGED
|
@@ -21,7 +21,7 @@ import { runWithTerminalClawChatSendScope } from "./terminal-send.js";
|
|
|
21
21
|
import { flushAlignedOutboundQueue, getAlignedOutboundQueueSize, sendOpenclawClawlingText, setAlignedOutboundLogContext, } from "./outbound.js";
|
|
22
22
|
import { formatWsLog } from "./ws-log.js";
|
|
23
23
|
import { createNotifySignalObserver, createProtocolControlHandler, createReconnectTracker } from "./ws-alignment.js";
|
|
24
|
-
import { clawChatDbPathForStateDir, getClawChatStore, } from "./storage.js";
|
|
24
|
+
import { clawChatDbPathForStateDir, getClawChatStore, makeOwnerProfilePersister, } from "./storage.js";
|
|
25
25
|
import { getClawChatGroupPrompt, getClawChatUserPrompt } from "./plugin-prompts.js";
|
|
26
26
|
import { loadClawChatPromptMetadata, renderClawChatProfilePrompt, resolveSenderRelation, } from "./profile-prompt.js";
|
|
27
27
|
import { refreshGroupProfile, syncFirstSeenClawChatProfiles } from "./profile-sync.js";
|
|
@@ -1094,6 +1094,10 @@ export async function startOpenclawClawlingGateway(params) {
|
|
|
1094
1094
|
log?.error?.(`[${accountId}] clawchat-plugin-openclaw metadata refresh failed source=${options.source} conversation=${conversationId}: ${err instanceof Error ? err.message : String(err)}`);
|
|
1095
1095
|
}
|
|
1096
1096
|
};
|
|
1097
|
+
const upsertOwnerProfile = store?.upsertOwnerProfile?.bind(store);
|
|
1098
|
+
const persistOwnerProfile = upsertOwnerProfile
|
|
1099
|
+
? makeOwnerProfilePersister({ upsertOwnerProfile }, { accountId })
|
|
1100
|
+
: undefined;
|
|
1097
1101
|
const refreshAgentBehavior = async (options) => {
|
|
1098
1102
|
try {
|
|
1099
1103
|
await pullOwnerMetadata({
|
|
@@ -1102,6 +1106,7 @@ export async function startOpenclawClawlingGateway(params) {
|
|
|
1102
1106
|
accountUserId: account.userId,
|
|
1103
1107
|
accountOwnerUserId: account.ownerUserId,
|
|
1104
1108
|
api: getConversationApiClient(),
|
|
1109
|
+
persistOwnerProfile,
|
|
1105
1110
|
});
|
|
1106
1111
|
}
|
|
1107
1112
|
catch (err) {
|
|
@@ -1173,6 +1178,7 @@ export async function startOpenclawClawlingGateway(params) {
|
|
|
1173
1178
|
accountUserId: account.userId,
|
|
1174
1179
|
accountOwnerUserId: account.ownerUserId,
|
|
1175
1180
|
api: getConversationApiClient(),
|
|
1181
|
+
persistOwnerProfile,
|
|
1176
1182
|
});
|
|
1177
1183
|
return;
|
|
1178
1184
|
}
|
|
@@ -1183,6 +1189,7 @@ export async function startOpenclawClawlingGateway(params) {
|
|
|
1183
1189
|
accountUserId: account.userId,
|
|
1184
1190
|
accountOwnerUserId: account.ownerUserId,
|
|
1185
1191
|
api: getConversationApiClient(),
|
|
1192
|
+
persistOwnerProfile,
|
|
1186
1193
|
});
|
|
1187
1194
|
return;
|
|
1188
1195
|
}
|
package/dist/src/storage.js
CHANGED
|
@@ -154,6 +154,23 @@ CREATE TABLE IF NOT EXISTS liveware_sample (
|
|
|
154
154
|
updated_at INTEGER NOT NULL,
|
|
155
155
|
PRIMARY KEY (platform, account_id)
|
|
156
156
|
);
|
|
157
|
+
`,
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
version: 9,
|
|
161
|
+
name: "owner_profile",
|
|
162
|
+
sql: `
|
|
163
|
+
CREATE TABLE IF NOT EXISTS owner_profile (
|
|
164
|
+
platform TEXT NOT NULL,
|
|
165
|
+
account_id TEXT NOT NULL,
|
|
166
|
+
owner_user_id TEXT,
|
|
167
|
+
nickname TEXT,
|
|
168
|
+
avatar_url TEXT,
|
|
169
|
+
bio TEXT,
|
|
170
|
+
locale TEXT,
|
|
171
|
+
updated_at INTEGER NOT NULL,
|
|
172
|
+
PRIMARY KEY (platform, account_id)
|
|
173
|
+
);
|
|
157
174
|
`,
|
|
158
175
|
},
|
|
159
176
|
];
|
|
@@ -495,6 +512,30 @@ export class ClawChatStore {
|
|
|
495
512
|
: null;
|
|
496
513
|
}) ?? null;
|
|
497
514
|
}
|
|
515
|
+
getOwnerProfile(input) {
|
|
516
|
+
return this.read(() => {
|
|
517
|
+
const row = this.requireDb()
|
|
518
|
+
.prepare(`SELECT * FROM owner_profile WHERE platform = ? AND account_id = ?`)
|
|
519
|
+
.get(input.platform, input.accountId);
|
|
520
|
+
return row ?? null;
|
|
521
|
+
}) ?? null;
|
|
522
|
+
}
|
|
523
|
+
upsertOwnerProfile(input) {
|
|
524
|
+
this.write(() => {
|
|
525
|
+
this.requireDb()
|
|
526
|
+
.prepare(`INSERT INTO owner_profile
|
|
527
|
+
(platform, account_id, owner_user_id, nickname, avatar_url, bio, locale, updated_at)
|
|
528
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
529
|
+
ON CONFLICT(platform, account_id) DO UPDATE SET
|
|
530
|
+
owner_user_id = excluded.owner_user_id,
|
|
531
|
+
nickname = excluded.nickname,
|
|
532
|
+
avatar_url = excluded.avatar_url,
|
|
533
|
+
bio = excluded.bio,
|
|
534
|
+
locale = excluded.locale,
|
|
535
|
+
updated_at = excluded.updated_at`)
|
|
536
|
+
.run(input.platform, input.accountId, input.ownerUserId ?? null, input.nickname ?? null, input.avatarUrl ?? null, input.bio ?? null, input.locale ?? null, Date.now());
|
|
537
|
+
});
|
|
538
|
+
}
|
|
498
539
|
getLivewareSample(input) {
|
|
499
540
|
return this.read(() => {
|
|
500
541
|
const row = this.requireDb()
|
|
@@ -701,3 +742,27 @@ export function resetClawChatStoreForTest() {
|
|
|
701
742
|
singleton?.close();
|
|
702
743
|
singleton = null;
|
|
703
744
|
}
|
|
745
|
+
/**
|
|
746
|
+
* Bind a persist callback for pullOwnerMetadata/updateMetadata.
|
|
747
|
+
*
|
|
748
|
+
* Maps the flat GET /v1/agents/me/owner payload onto the owner_profile
|
|
749
|
+
* cache row. Missing fields are stored as NULL (never empty strings).
|
|
750
|
+
*/
|
|
751
|
+
export function makeOwnerProfilePersister(store, input) {
|
|
752
|
+
const text = (value) => {
|
|
753
|
+
if (value == null)
|
|
754
|
+
return null;
|
|
755
|
+
return typeof value === "string" ? value : String(value);
|
|
756
|
+
};
|
|
757
|
+
return (owner) => {
|
|
758
|
+
store.upsertOwnerProfile({
|
|
759
|
+
platform: input.platform ?? "openclaw",
|
|
760
|
+
accountId: input.accountId,
|
|
761
|
+
ownerUserId: text(owner.id),
|
|
762
|
+
nickname: text(owner.nickname),
|
|
763
|
+
avatarUrl: text(owner.avatar_url),
|
|
764
|
+
bio: text(owner.bio),
|
|
765
|
+
locale: text(owner.locale),
|
|
766
|
+
});
|
|
767
|
+
};
|
|
768
|
+
}
|
package/dist/src/tools.js
CHANGED
|
@@ -6,7 +6,7 @@ import { resolveLivewarePath } from "./liveware-cli.js";
|
|
|
6
6
|
import { ClawlingApiError, } from "./api-types.js";
|
|
7
7
|
import { mapGateOutcome } from "./gate-outcome.js";
|
|
8
8
|
import { resolveOpenclawClawlingAccount } from "./config.js";
|
|
9
|
-
import { clawChatDbPathForStateDir, getClawChatStore, } from "./storage.js";
|
|
9
|
+
import { clawChatDbPathForStateDir, getClawChatStore, makeOwnerProfilePersister, } from "./storage.js";
|
|
10
10
|
import { sendOpenclawClawlingMentionMessage } from "./outbound.js";
|
|
11
11
|
import { getOpenclawClawlingClient, } from "./runtime.js";
|
|
12
12
|
import { markTerminalClawChatSend } from "./terminal-send.js";
|
|
@@ -612,6 +612,9 @@ export function registerOpenclawClawlingTools(api, options = {}) {
|
|
|
612
612
|
accountUserId: account.userId,
|
|
613
613
|
patch: p.patch,
|
|
614
614
|
api: built.client,
|
|
615
|
+
persistOwnerProfile: makeOwnerProfilePersister(getClawChatStore(), {
|
|
616
|
+
accountId: account.accountId,
|
|
617
|
+
}),
|
|
615
618
|
}));
|
|
616
619
|
}
|
|
617
620
|
catch (err) {
|
package/package.json
CHANGED
package/src/clawchat-metadata.ts
CHANGED
|
@@ -190,12 +190,28 @@ function pickUpdatePatch<T extends Record<string, string>>(
|
|
|
190
190
|
return patch as T;
|
|
191
191
|
}
|
|
192
192
|
|
|
193
|
+
/** Best-effort last-known-owner-profile cache write; must never break the pull. */
|
|
194
|
+
export type PersistOwnerProfile = (owner: Record<string, unknown>) => void;
|
|
195
|
+
|
|
196
|
+
function persistOwnerProfileSafe(
|
|
197
|
+
persist: PersistOwnerProfile | undefined,
|
|
198
|
+
owner: Record<string, unknown>,
|
|
199
|
+
): void {
|
|
200
|
+
if (!persist) return;
|
|
201
|
+
try {
|
|
202
|
+
persist(owner);
|
|
203
|
+
} catch {
|
|
204
|
+
// The sqlite cache is advisory — a failed write must not fail the pull.
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
193
208
|
export async function pullOwnerMetadata(params: {
|
|
194
209
|
memoryRoot: string;
|
|
195
210
|
agentId: string;
|
|
196
211
|
accountUserId?: string;
|
|
197
212
|
accountOwnerUserId?: string;
|
|
198
213
|
api: MetadataApi;
|
|
214
|
+
persistOwnerProfile?: PersistOwnerProfile;
|
|
199
215
|
}): Promise<MetadataPullResult> {
|
|
200
216
|
const getAgent = params.api.getAgentDetail ?? params.api.getAgentProfile;
|
|
201
217
|
if (!getAgent) throw new Error("ClawChat metadata pull requires getAgentDetail");
|
|
@@ -203,7 +219,9 @@ export async function pullOwnerMetadata(params: {
|
|
|
203
219
|
const data = await getAgent(params.agentId);
|
|
204
220
|
const metadata = ownerMetadataFromAgent(asRecord(data.agent) ?? {}, params);
|
|
205
221
|
if (params.api.getAgentOwner) {
|
|
206
|
-
|
|
222
|
+
const owner = asRecord(await params.api.getAgentOwner()) ?? {};
|
|
223
|
+
persistOwnerProfileSafe(params.persistOwnerProfile, owner);
|
|
224
|
+
addOwnerProfileMetadata(metadata, owner);
|
|
207
225
|
}
|
|
208
226
|
await writeClawChatMetadata(params.memoryRoot, { targetType: "owner", targetId: "owner" }, metadata);
|
|
209
227
|
return { ok: true, writes: [{ targetType: "owner", targetId: "owner" }], failures: [] };
|
|
@@ -375,6 +393,7 @@ export async function updateMetadata(params: {
|
|
|
375
393
|
accountUserId?: string;
|
|
376
394
|
patch: Record<string, unknown>;
|
|
377
395
|
api: MetadataApi;
|
|
396
|
+
persistOwnerProfile?: PersistOwnerProfile;
|
|
378
397
|
}): Promise<MetadataPushResult> {
|
|
379
398
|
if (params.targetType === "owner") {
|
|
380
399
|
if (!params.agentId) throw new Error("ClawChat owner metadata update requires agentId");
|
|
@@ -390,7 +409,9 @@ export async function updateMetadata(params: {
|
|
|
390
409
|
accountUserId: params.accountUserId,
|
|
391
410
|
});
|
|
392
411
|
if (params.api.getAgentOwner) {
|
|
393
|
-
|
|
412
|
+
const owner = asRecord(await params.api.getAgentOwner()) ?? {};
|
|
413
|
+
persistOwnerProfileSafe(params.persistOwnerProfile, owner);
|
|
414
|
+
addOwnerProfileMetadata(metadata, owner);
|
|
394
415
|
}
|
|
395
416
|
await writeClawChatMetadata(params.memoryRoot, { targetType: "owner", targetId: "owner" }, metadata);
|
|
396
417
|
return { ok: true, writes: [{ targetType: "owner", targetId: "owner" }], failures: [], metadata };
|
package/src/reply-dispatcher.ts
CHANGED
|
@@ -418,7 +418,17 @@ export function createOpenclawClawlingReplyDispatcher(options: ReplyDispatcherOp
|
|
|
418
418
|
);
|
|
419
419
|
return [];
|
|
420
420
|
}
|
|
421
|
-
|
|
421
|
+
const fragments = await uploadOutboundMedia(urls, { apiClient, runtime, log });
|
|
422
|
+
if (fragments.length < urls.length) {
|
|
423
|
+
// uploadOutboundMedia drops a failed item with only a log line. A shortfall
|
|
424
|
+
// means an attachment never reached the media service; surfacing it (parity
|
|
425
|
+
// with the adapter path in outbound.ts and with the Hermes plugin) stops a
|
|
426
|
+
// text-only frame from going out and reading as a successful send.
|
|
427
|
+
throw new Error(
|
|
428
|
+
`clawchat-plugin-openclaw failed to upload ${urls.length - fragments.length}/${urls.length} outbound media attachment(s); message not sent`,
|
|
429
|
+
);
|
|
430
|
+
}
|
|
431
|
+
return fragments;
|
|
422
432
|
}
|
|
423
433
|
|
|
424
434
|
// ----- Reply state ------------------------------------------------------
|
package/src/runtime.ts
CHANGED
|
@@ -62,6 +62,7 @@ import { createNotifySignalObserver, createProtocolControlHandler, createReconne
|
|
|
62
62
|
import {
|
|
63
63
|
clawChatDbPathForStateDir,
|
|
64
64
|
getClawChatStore,
|
|
65
|
+
makeOwnerProfilePersister,
|
|
65
66
|
type ClawChatStore,
|
|
66
67
|
type MessageRow,
|
|
67
68
|
} from "./storage.ts";
|
|
@@ -118,6 +119,7 @@ type RuntimeConnectionStore = Pick<
|
|
|
118
119
|
| "getActivationConversation"
|
|
119
120
|
| "getLastResolvedDeviceId"
|
|
120
121
|
| "listRecentGroupMessages"
|
|
122
|
+
| "upsertOwnerProfile"
|
|
121
123
|
>
|
|
122
124
|
>;
|
|
123
125
|
|
|
@@ -1447,6 +1449,10 @@ export async function startOpenclawClawlingGateway(params: StartGatewayParams):
|
|
|
1447
1449
|
);
|
|
1448
1450
|
}
|
|
1449
1451
|
};
|
|
1452
|
+
const upsertOwnerProfile = store?.upsertOwnerProfile?.bind(store);
|
|
1453
|
+
const persistOwnerProfile = upsertOwnerProfile
|
|
1454
|
+
? makeOwnerProfilePersister({ upsertOwnerProfile }, { accountId })
|
|
1455
|
+
: undefined;
|
|
1450
1456
|
const refreshAgentBehavior = async (options: {
|
|
1451
1457
|
metadataVersion?: number;
|
|
1452
1458
|
source: string;
|
|
@@ -1459,6 +1465,7 @@ export async function startOpenclawClawlingGateway(params: StartGatewayParams):
|
|
|
1459
1465
|
accountUserId: account.userId,
|
|
1460
1466
|
accountOwnerUserId: account.ownerUserId,
|
|
1461
1467
|
api: getConversationApiClient(),
|
|
1468
|
+
persistOwnerProfile,
|
|
1462
1469
|
});
|
|
1463
1470
|
} catch (err) {
|
|
1464
1471
|
log?.error?.(
|
|
@@ -1533,6 +1540,7 @@ export async function startOpenclawClawlingGateway(params: StartGatewayParams):
|
|
|
1533
1540
|
accountUserId: account.userId,
|
|
1534
1541
|
accountOwnerUserId: account.ownerUserId,
|
|
1535
1542
|
api: getConversationApiClient(),
|
|
1543
|
+
persistOwnerProfile,
|
|
1536
1544
|
});
|
|
1537
1545
|
return;
|
|
1538
1546
|
}
|
|
@@ -1543,6 +1551,7 @@ export async function startOpenclawClawlingGateway(params: StartGatewayParams):
|
|
|
1543
1551
|
accountUserId: account.userId,
|
|
1544
1552
|
accountOwnerUserId: account.ownerUserId,
|
|
1545
1553
|
api: getConversationApiClient(),
|
|
1554
|
+
persistOwnerProfile,
|
|
1546
1555
|
});
|
|
1547
1556
|
return;
|
|
1548
1557
|
}
|
package/src/storage.ts
CHANGED
|
@@ -191,6 +191,27 @@ export type UpdateLivewareSampleStatusInput = {
|
|
|
191
191
|
lastError?: string | null;
|
|
192
192
|
};
|
|
193
193
|
|
|
194
|
+
export type OwnerProfileRow = {
|
|
195
|
+
platform: string;
|
|
196
|
+
account_id: string;
|
|
197
|
+
owner_user_id: string | null;
|
|
198
|
+
nickname: string | null;
|
|
199
|
+
avatar_url: string | null;
|
|
200
|
+
bio: string | null;
|
|
201
|
+
locale: string | null;
|
|
202
|
+
updated_at: number;
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
export type OwnerProfileUpsert = {
|
|
206
|
+
platform: string;
|
|
207
|
+
accountId: string;
|
|
208
|
+
ownerUserId?: string | null;
|
|
209
|
+
nickname?: string | null;
|
|
210
|
+
avatarUrl?: string | null;
|
|
211
|
+
bio?: string | null;
|
|
212
|
+
locale?: string | null;
|
|
213
|
+
};
|
|
214
|
+
|
|
194
215
|
type Migration = { version: number; name: string; sql: string };
|
|
195
216
|
|
|
196
217
|
const DB_FILENAME = "clawchat.sqlite";
|
|
@@ -345,6 +366,23 @@ CREATE TABLE IF NOT EXISTS liveware_sample (
|
|
|
345
366
|
updated_at INTEGER NOT NULL,
|
|
346
367
|
PRIMARY KEY (platform, account_id)
|
|
347
368
|
);
|
|
369
|
+
`,
|
|
370
|
+
},
|
|
371
|
+
{
|
|
372
|
+
version: 9,
|
|
373
|
+
name: "owner_profile",
|
|
374
|
+
sql: `
|
|
375
|
+
CREATE TABLE IF NOT EXISTS owner_profile (
|
|
376
|
+
platform TEXT NOT NULL,
|
|
377
|
+
account_id TEXT NOT NULL,
|
|
378
|
+
owner_user_id TEXT,
|
|
379
|
+
nickname TEXT,
|
|
380
|
+
avatar_url TEXT,
|
|
381
|
+
bio TEXT,
|
|
382
|
+
locale TEXT,
|
|
383
|
+
updated_at INTEGER NOT NULL,
|
|
384
|
+
PRIMARY KEY (platform, account_id)
|
|
385
|
+
);
|
|
348
386
|
`,
|
|
349
387
|
},
|
|
350
388
|
];
|
|
@@ -865,6 +903,43 @@ export class ClawChatStore {
|
|
|
865
903
|
}) ?? null;
|
|
866
904
|
}
|
|
867
905
|
|
|
906
|
+
getOwnerProfile(input: ConversationAccountInput): OwnerProfileRow | null {
|
|
907
|
+
return this.read(() => {
|
|
908
|
+
const row = this.requireDb()
|
|
909
|
+
.prepare(`SELECT * FROM owner_profile WHERE platform = ? AND account_id = ?`)
|
|
910
|
+
.get(input.platform, input.accountId);
|
|
911
|
+
return (row as OwnerProfileRow | undefined) ?? null;
|
|
912
|
+
}) ?? null;
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
upsertOwnerProfile(input: OwnerProfileUpsert): void {
|
|
916
|
+
this.write(() => {
|
|
917
|
+
this.requireDb()
|
|
918
|
+
.prepare(
|
|
919
|
+
`INSERT INTO owner_profile
|
|
920
|
+
(platform, account_id, owner_user_id, nickname, avatar_url, bio, locale, updated_at)
|
|
921
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
922
|
+
ON CONFLICT(platform, account_id) DO UPDATE SET
|
|
923
|
+
owner_user_id = excluded.owner_user_id,
|
|
924
|
+
nickname = excluded.nickname,
|
|
925
|
+
avatar_url = excluded.avatar_url,
|
|
926
|
+
bio = excluded.bio,
|
|
927
|
+
locale = excluded.locale,
|
|
928
|
+
updated_at = excluded.updated_at`,
|
|
929
|
+
)
|
|
930
|
+
.run(
|
|
931
|
+
input.platform,
|
|
932
|
+
input.accountId,
|
|
933
|
+
input.ownerUserId ?? null,
|
|
934
|
+
input.nickname ?? null,
|
|
935
|
+
input.avatarUrl ?? null,
|
|
936
|
+
input.bio ?? null,
|
|
937
|
+
input.locale ?? null,
|
|
938
|
+
Date.now(),
|
|
939
|
+
);
|
|
940
|
+
});
|
|
941
|
+
}
|
|
942
|
+
|
|
868
943
|
getLivewareSample(input: ConversationAccountInput): LivewareSampleRow | null {
|
|
869
944
|
return this.read(() => {
|
|
870
945
|
const row = this.requireDb()
|
|
@@ -1130,3 +1205,30 @@ export function resetClawChatStoreForTest(): void {
|
|
|
1130
1205
|
singleton?.close();
|
|
1131
1206
|
singleton = null;
|
|
1132
1207
|
}
|
|
1208
|
+
|
|
1209
|
+
/**
|
|
1210
|
+
* Bind a persist callback for pullOwnerMetadata/updateMetadata.
|
|
1211
|
+
*
|
|
1212
|
+
* Maps the flat GET /v1/agents/me/owner payload onto the owner_profile
|
|
1213
|
+
* cache row. Missing fields are stored as NULL (never empty strings).
|
|
1214
|
+
*/
|
|
1215
|
+
export function makeOwnerProfilePersister(
|
|
1216
|
+
store: { upsertOwnerProfile(input: OwnerProfileUpsert): void },
|
|
1217
|
+
input: { accountId: string; platform?: string },
|
|
1218
|
+
): (owner: Record<string, unknown>) => void {
|
|
1219
|
+
const text = (value: unknown): string | null => {
|
|
1220
|
+
if (value == null) return null;
|
|
1221
|
+
return typeof value === "string" ? value : String(value);
|
|
1222
|
+
};
|
|
1223
|
+
return (owner) => {
|
|
1224
|
+
store.upsertOwnerProfile({
|
|
1225
|
+
platform: input.platform ?? "openclaw",
|
|
1226
|
+
accountId: input.accountId,
|
|
1227
|
+
ownerUserId: text(owner.id),
|
|
1228
|
+
nickname: text(owner.nickname),
|
|
1229
|
+
avatarUrl: text(owner.avatar_url),
|
|
1230
|
+
bio: text(owner.bio),
|
|
1231
|
+
locale: text(owner.locale),
|
|
1232
|
+
});
|
|
1233
|
+
};
|
|
1234
|
+
}
|
package/src/tools.ts
CHANGED
|
@@ -14,6 +14,7 @@ import { resolveOpenclawClawlingAccount } from "./config.ts";
|
|
|
14
14
|
import {
|
|
15
15
|
clawChatDbPathForStateDir,
|
|
16
16
|
getClawChatStore,
|
|
17
|
+
makeOwnerProfilePersister,
|
|
17
18
|
type ClawChatStore,
|
|
18
19
|
} from "./storage.ts";
|
|
19
20
|
import { sendOpenclawClawlingMentionMessage } from "./outbound.ts";
|
|
@@ -773,6 +774,9 @@ export function registerOpenclawClawlingTools(
|
|
|
773
774
|
accountUserId: account.userId,
|
|
774
775
|
patch: p.patch,
|
|
775
776
|
api: built.client,
|
|
777
|
+
persistOwnerProfile: makeOwnerProfilePersister(getClawChatStore(), {
|
|
778
|
+
accountId: account.accountId,
|
|
779
|
+
}),
|
|
776
780
|
}),
|
|
777
781
|
);
|
|
778
782
|
} catch (err) {
|