@ozaiya/openclaw-channel 0.10.39 → 0.10.41

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.
@@ -0,0 +1,20 @@
1
+ /**
2
+ * A bot's non-secret settings from the app — its AI model and its add-ons' configs — written where
3
+ * helpers running beside the gateway can read them: `<stateDir>/ozaiya-bots/<botId>.json`.
4
+ *
5
+ * A host-side helper (e.g. a 闲鱼 inbox watcher that wakes the bot) thereby follows the owner's
6
+ * choices in the app — which model, whether to auto-reply — without its own Ozaiya credentials.
7
+ * Rewritten whenever the gateway (re)loads the bot's config; skipped when nothing changed.
8
+ */
9
+ export interface BotLocalSettings {
10
+ botId: string;
11
+ /** The app's per-bot AI model ("provider/model"), or null for the gateway's default. */
12
+ model: string | null;
13
+ addons: Record<string, Record<string, unknown>>;
14
+ }
15
+ export declare function botLocalSettingsPath(stateDir: string, botId: string): string;
16
+ export declare function writeBotLocalSettings(stateDir: string, account: {
17
+ accountId: string;
18
+ model?: string;
19
+ addons?: Record<string, Record<string, unknown>>;
20
+ }): Promise<boolean>;
@@ -0,0 +1,20 @@
1
+ import fs from "node:fs/promises";
2
+ import path from "node:path";
3
+ const lastWritten = new Map();
4
+ export function botLocalSettingsPath(stateDir, botId) {
5
+ return path.join(stateDir, "ozaiya-bots", `${botId}.json`);
6
+ }
7
+ export async function writeBotLocalSettings(stateDir, account) {
8
+ const settings = { botId: account.accountId, model: account.model ?? null, addons: account.addons ?? {} };
9
+ const body = JSON.stringify(settings, null, 2) + "\n";
10
+ if (lastWritten.get(account.accountId) === body)
11
+ return false;
12
+ const file = botLocalSettingsPath(stateDir, account.accountId);
13
+ await fs.mkdir(path.dirname(file), { recursive: true });
14
+ // Write-then-rename: a reader never sees half a file.
15
+ await fs.writeFile(`${file}.tmp`, body);
16
+ await fs.rename(`${file}.tmp`, file);
17
+ lastWritten.set(account.accountId, body);
18
+ return true;
19
+ }
20
+ //# sourceMappingURL=botLocalSettings.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"botLocalSettings.js","sourceRoot":"","sources":["../../src/botLocalSettings.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAClC,OAAO,IAAI,MAAM,WAAW,CAAC;AAiB7B,MAAM,WAAW,GAAG,IAAI,GAAG,EAAkB,CAAC;AAE9C,MAAM,UAAU,oBAAoB,CAAC,QAAgB,EAAE,KAAa;IAClE,OAAO,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,aAAa,EAAE,GAAG,KAAK,OAAO,CAAC,CAAC;AAC7D,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,QAAgB,EAChB,OAAgG;IAEhG,MAAM,QAAQ,GAAqB,EAAE,KAAK,EAAE,OAAO,CAAC,SAAS,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,IAAI,EAAE,EAAE,CAAC;IAC5H,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC;IACtD,IAAI,WAAW,CAAC,GAAG,CAAC,OAAO,CAAC,SAAS,CAAC,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAC9D,MAAM,IAAI,GAAG,oBAAoB,CAAC,QAAQ,EAAE,OAAO,CAAC,SAAS,CAAC,CAAC;IAC/D,MAAM,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACxD,sDAAsD;IACtD,MAAM,EAAE,CAAC,SAAS,CAAC,GAAG,IAAI,MAAM,EAAE,IAAI,CAAC,CAAC;IACxC,MAAM,EAAE,CAAC,MAAM,CAAC,GAAG,IAAI,MAAM,EAAE,IAAI,CAAC,CAAC;IACrC,WAAW,CAAC,GAAG,CAAC,OAAO,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;IACzC,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -33,6 +33,8 @@ import { fetchSocialMediaPost, searchSocialMedia, extractSocialMediaContent } fr
33
33
  import { requestConfirmation, parseActionCallback, resolveConfirmation } from "./actionConfirmation.js";
34
34
  import { createBotEmailTool, emailAddonOf, newMailNotice, watchMailboxes } from "./botEmail.js";
35
35
  import { createBotMcpTool, mcpAddonOf } from "./botMcp.js";
36
+ import { lastChatFromSessionStore } from "./lastChat.js";
37
+ import { writeBotLocalSettings } from "./botLocalSettings.js";
36
38
  const DEFAULT_API_BASE_URL = "https://api.ozaiya.com";
37
39
  const DEFAULT_WEBHOOK_PATH = "/ozaiya/webhook";
38
40
  const DEFAULT_ACCOUNT_ID = "default";
@@ -1481,6 +1483,25 @@ async function buildNativeDocumentContext(params) {
1481
1483
  * Non-read-only tools are wrapped to report progress steps to the active
1482
1484
  * dispatch's task progress card (when one exists for this account).
1483
1485
  */
1486
+ /**
1487
+ * The chat a tool works for: the message being answered, else the last one this bot received, else —
1488
+ * a turn started from outside (gateway `agent` call, host watcher) or the first after a restart, when
1489
+ * no inbound message has set the in-memory origin — the bot's newest chat in the session store.
1490
+ */
1491
+ async function currentChatGroupId(account, cfg) {
1492
+ const live = activeDispatches.get(account.accountId)?.groupId ?? accountToOriginGroupId.get(account.accountId);
1493
+ if (live)
1494
+ return live;
1495
+ try {
1496
+ const ch = getOzaiyaRuntime().channel;
1497
+ const route = ch.routing.resolveAgentRoute({ cfg, channel: "ozaiya", accountId: account.accountId, peer: { kind: "group", id: "" } });
1498
+ const storePath = String(ch.session.resolveStorePath(undefined, { agentId: route.agentId }));
1499
+ return lastChatFromSessionStore(await fs.readFile(storePath, "utf8"), account.accountId);
1500
+ }
1501
+ catch {
1502
+ return undefined;
1503
+ }
1504
+ }
1484
1505
  function buildChannelTools(account, cfg) {
1485
1506
  const resolveForGroup = (groupId) => resolveAccountForGroup(cfg, groupId) ?? account;
1486
1507
  const tools = [
@@ -1606,7 +1627,7 @@ function buildChannelTools(account, cfg) {
1606
1627
  execute: async (_toolCallId, rawArgs) => {
1607
1628
  const args = (rawArgs ?? {});
1608
1629
  const dispatch = activeDispatches.get(account.accountId);
1609
- const groupId = dispatch?.groupId ?? accountToOriginGroupId.get(account.accountId);
1630
+ const groupId = await currentChatGroupId(account, cfg);
1610
1631
  if (!groupId) {
1611
1632
  return { content: [{ type: "text", text: "No active task to attach a deliverable to." }] };
1612
1633
  }
@@ -1679,8 +1700,7 @@ function buildChannelTools(account, cfg) {
1679
1700
  },
1680
1701
  execute: async (_toolCallId, rawArgs) => {
1681
1702
  const args = (rawArgs ?? {});
1682
- const dispatch = activeDispatches.get(account.accountId);
1683
- const groupId = dispatch?.groupId ?? accountToOriginGroupId.get(account.accountId);
1703
+ const groupId = await currentChatGroupId(account, cfg);
1684
1704
  if (!groupId) {
1685
1705
  return { content: [{ type: "text", text: "No active chat to send the login request to." }] };
1686
1706
  }
@@ -2113,6 +2133,10 @@ export const ozaiyaPlugin = {
2113
2133
  });
2114
2134
  ctx.log?.info(`[${botAccount.accountId}] registered webhook at ${botAccount.webhookPath}`);
2115
2135
  syncMailWatch(botAccount, ctx.cfg, stateDir, ctx.log);
2136
+ // The app's model + add-on settings, for helpers beside the gateway (e.g. a 闲鱼 inbox watcher).
2137
+ writeBotLocalSettings(stateDir, botAccount).catch((err) => {
2138
+ ctx.log?.warn?.(`[${botAccount.accountId}] could not write local settings: ${String(err)}`);
2139
+ });
2116
2140
  };
2117
2141
  const stopBotHandler = (botId) => {
2118
2142
  botUnregisters.get(botId)?.();