@parall/parall 1.32.1 → 1.34.0

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/gateway.d.ts CHANGED
@@ -1,7 +1,16 @@
1
+ import type { PluginRuntime } from 'openclaw/plugin-sdk';
1
2
  import type { ChannelPlugin } from 'openclaw/plugin-sdk/core';
2
3
  /** Extract ChannelGatewayAdapter from ChannelPlugin (removed from public SDK exports in 2026.3.24). */
3
4
  type ChannelGatewayAdapter<T = unknown> = NonNullable<ChannelPlugin<T>['gateway']>;
5
+ import { type DispatchAdapter } from '@parall/agent-core';
4
6
  import type { ResolvedParallAccount } from './types.js';
7
+ export declare function createOpenClawDispatchAdapter(opts: {
8
+ core: PluginRuntime;
9
+ cfg: Record<string, unknown>;
10
+ accountId: string;
11
+ sessionsDir: string;
12
+ workspaceDir: string;
13
+ }): DispatchAdapter;
5
14
  export declare const parallGateway: ChannelGatewayAdapter<ResolvedParallAccount>;
6
15
  export {};
7
16
  //# sourceMappingURL=gateway.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"gateway.d.ts","sourceRoot":"","sources":["../src/gateway.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAE9D,uGAAuG;AACvG,KAAK,qBAAqB,CAAC,CAAC,GAAG,OAAO,IAAI,WAAW,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC;AAgCnF,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AA8SxD,eAAO,MAAM,aAAa,EAAE,qBAAqB,CAAC,qBAAqB,CAmJtE,CAAC"}
1
+ {"version":3,"file":"gateway.d.ts","sourceRoot":"","sources":["../src/gateway.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAE9D,uGAAuG;AACvG,KAAK,qBAAqB,CAAC,CAAC,GAAG,OAAO,IAAI,WAAW,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC;AACnF,OAAO,EAOL,KAAK,eAAe,EAGrB,MAAM,oBAAoB,CAAC;AAqB5B,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAiKxD,wBAAgB,6BAA6B,CAAC,IAAI,EAAE;IAClD,IAAI,EAAE,aAAa,CAAC;IACpB,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;CACtB,GAAG,eAAe,CA0KlB;AAED,eAAO,MAAM,aAAa,EAAE,qBAAqB,CAAC,qBAAqB,CAmJtE,CAAC"}
package/dist/gateway.js CHANGED
@@ -145,8 +145,41 @@ async function waitForOpenClawSessionId(sessionsDir, sessionKey, timeoutMs = 10_
145
145
  }
146
146
  return null;
147
147
  }
148
- function createOpenClawDispatchAdapter(opts) {
148
+ export function createOpenClawDispatchAdapter(opts) {
149
149
  const activeStreams = new Map();
150
+ let configFallbackWarned = false;
151
+ /**
152
+ * Resolve the config for one dispatch. OpenClaw hot-reloads
153
+ * `agents.defaults.model` / `models.providers` WITHOUT restarting channel
154
+ * plugins, so the `ctx.cfg` snapshot captured at startAccount goes stale the
155
+ * moment the operator switches the agent's model — every run would keep the
156
+ * old model until the whole gateway restarts. `core.config.current()` is the
157
+ * host's live (hot-reload-refreshed) config; fall back to the startup
158
+ * snapshot on hosts that predate the API. The fallback warns once per
159
+ * degradation streak (latch resets on success) — silently dispatching with
160
+ * the stale snapshot would recreate the invisible model-switch mismatch this
161
+ * function exists to fix.
162
+ */
163
+ function resolveDispatchConfig(log) {
164
+ try {
165
+ const current = opts.core.config?.current?.();
166
+ if (current && typeof current === 'object') {
167
+ configFallbackWarned = false;
168
+ return current;
169
+ }
170
+ if (!configFallbackWarned) {
171
+ configFallbackWarned = true;
172
+ log?.warn?.(`parall[${opts.accountId}]: config.current() unavailable on this host — dispatching with the startup config snapshot; model switches need a gateway restart until then`);
173
+ }
174
+ }
175
+ catch (err) {
176
+ if (!configFallbackWarned) {
177
+ configFallbackWarned = true;
178
+ log?.warn?.(`parall[${opts.accountId}]: config.current() failed (${String(err)}) — dispatching with the startup config snapshot; model switches need a gateway restart until then`);
179
+ }
180
+ }
181
+ return opts.cfg;
182
+ }
150
183
  return {
151
184
  abortDispatch(sessionKey) {
152
185
  activeStreams.get(sessionKey)?.fail(new Error('dispatch deadline exceeded'));
@@ -172,7 +205,7 @@ function createOpenClawDispatchAdapter(opts) {
172
205
  let turnGroupKey = '';
173
206
  const run = opts.core.channel.reply.dispatchReplyWithBufferedBlockDispatcher({
174
207
  ctx: buildInboundCtx(opts.core, opts.accountId, event, sessionKey, promptBody, earlierEvents),
175
- cfg: opts.cfg,
208
+ cfg: resolveDispatchConfig(context.log),
176
209
  dispatcherOptions: {
177
210
  deliver: async (payload) => {
178
211
  const replyText = payload.text?.trim();
@@ -27092,6 +27092,7 @@ var ENDPOINTS = {
27092
27092
  MACHINE_AGENT_DAEMON_CONFIG: (orgId, machineId, agentId) => `${API_BASE}/orgs/${orgId}/machines/${machineId}/agents/${agentId}/daemon-config`,
27093
27093
  MACHINE_AGENT_WORKSPACE_SETUP: (orgId, machineId, agentId) => `${API_BASE}/orgs/${orgId}/machines/${machineId}/agents/${agentId}/workspace/setup`,
27094
27094
  MACHINE_LLM_SOURCE: (orgId, machineId) => `${API_BASE}/orgs/${orgId}/machines/${machineId}/llm-source`,
27095
+ MACHINE_PROVIDER_ENABLED: (orgId, machineId) => `${API_BASE}/orgs/${orgId}/machines/${machineId}/provider-enabled`,
27095
27096
  MACHINE_RUNTIME_AUTH: (orgId, machineId) => `${API_BASE}/orgs/${orgId}/machines/${machineId}/runtime-auth`,
27096
27097
  MACHINE_KEYS: (orgId, machineId) => `${API_BASE}/orgs/${orgId}/machines/${machineId}/keys`,
27097
27098
  MACHINE_KEY: (orgId, machineId, keyId) => `${API_BASE}/orgs/${orgId}/machines/${machineId}/keys/${keyId}`,
@@ -27105,8 +27106,9 @@ var ENDPOINTS = {
27105
27106
  MACHINES_ME: `${API_BASE}/machines/me`,
27106
27107
  MACHINES_ME_AGENTS: `${API_BASE}/machines/me/agents`,
27107
27108
  MACHINES_ME_HEALTH: `${API_BASE}/machines/me/health`,
27108
- MACHINES_ME_CLIP_INSTALLS: `${API_BASE}/machines/me/clip-installs`,
27109
- MACHINES_ME_CLIP_INSTALL_DONE: (clipId) => `${API_BASE}/machines/me/clip-installs/${clipId}/installed`,
27109
+ MACHINES_ME_CLIPS: `${API_BASE}/machines/me/clips`,
27110
+ MACHINES_ME_BROWSER_PROFILES: `${API_BASE}/machines/me/browser-profiles`,
27111
+ MACHINES_ME_BROWSER_PROFILE_STATUS: (profileId) => `${API_BASE}/machines/me/browser-profiles/${profileId}/status`,
27110
27112
  MACHINES_ME_AGENT_LAUNCH_CREDENTIAL: (agentId) => `${API_BASE}/machines/me/agents/${agentId}/launch-credential`,
27111
27113
  MACHINES_ME_AGENT_WORKSPACE_STATE: (agentId) => `${API_BASE}/machines/me/agents/${agentId}/workspace-state`,
27112
27114
  MACHINES_ME_WS_TICKET: `${API_BASE}/machines/me/ws/ticket`,
@@ -27205,6 +27207,7 @@ var ENDPOINTS = {
27205
27207
  DISPATCH_RECEIVED: (orgId) => `${API_BASE}/orgs/${orgId}/dispatch/received`,
27206
27208
  DISPATCH_ACK: (orgId) => `${API_BASE}/orgs/${orgId}/dispatch/ack`,
27207
27209
  DISPATCH_ACK_BY_ID: (orgId, id) => `${API_BASE}/orgs/${orgId}/dispatch/${id}/ack`,
27210
+ DISPATCH_EXPIRE: (orgId) => `${API_BASE}/orgs/${orgId}/dispatch/expire`,
27208
27211
  DISPATCH_BY_MESSAGES: (orgId) => `${API_BASE}/orgs/${orgId}/dispatch/by-messages`,
27209
27212
  // Unread
27210
27213
  UNREAD: `${API_BASE}/me/unread`,
@@ -27223,6 +27226,8 @@ var ENDPOINTS = {
27223
27226
  PUSH_VAPID_KEY: `${API_BASE}/push/vapid-key`,
27224
27227
  // Notification preferences
27225
27228
  NOTIFICATION_PREFERENCES: `${API_BASE}/notification-preferences`,
27229
+ // Unified search (messages + tasks + wiki, org-scoped)
27230
+ SEARCH: (orgId) => `${API_BASE}/orgs/${orgId}/search`,
27226
27231
  // Feature flags (org-scoped, server-evaluated)
27227
27232
  FEATURE_FLAGS: (orgId) => `${API_BASE}/orgs/${orgId}/feature-flags`,
27228
27233
  // Billing & Credits (org-scoped)
@@ -27243,6 +27248,13 @@ var ENDPOINTS = {
27243
27248
  AGENT_CLIP: (orgId, agentId, clipId) => `${CLIP_BASE}/orgs/${orgId}/agents/${agentId}/clips/${clipId}`,
27244
27249
  CLIP_INVOKE: (orgId) => `${CLIP_BASE}/orgs/${orgId}/invoke`,
27245
27250
  CLIP_ONLINE: (orgId) => `${CLIP_BASE}/orgs/${orgId}/online`,
27251
+ BROWSER_PROFILES: (orgId) => `${CLIP_BASE}/orgs/${orgId}/browser-profiles`,
27252
+ BROWSER_PROFILE: (orgId, profileId) => `${CLIP_BASE}/orgs/${orgId}/browser-profiles/${profileId}`,
27253
+ BROWSER_PROFILE_OPEN: (orgId, profileId) => `${CLIP_BASE}/orgs/${orgId}/browser-profiles/${profileId}/open`,
27254
+ BROWSER_PROFILE_STOP: (orgId, profileId) => `${CLIP_BASE}/orgs/${orgId}/browser-profiles/${profileId}/stop`,
27255
+ BROWSER_PROFILE_RESET: (orgId, profileId) => `${CLIP_BASE}/orgs/${orgId}/browser-profiles/${profileId}/reset`,
27256
+ BROWSER_PROFILE_CONSENTS: (orgId, profileId) => `${CLIP_BASE}/orgs/${orgId}/browser-profiles/${profileId}/consents`,
27257
+ BROWSER_PROFILE_CONSENT: (orgId, profileId, clipId) => `${CLIP_BASE}/orgs/${orgId}/browser-profiles/${profileId}/consents/${clipId}`,
27246
27258
  // Clip registry (global, served by clip-service → Pinix Hub proxy)
27247
27259
  CLIP_REGISTRY: () => `${CLIP_BASE}/registry/clips`
27248
27260
  };
@@ -27317,9 +27329,10 @@ var WS_EVENTS = {
27317
27329
  MACHINE_FILESYSTEM_BROWSE: "machine.filesystem.browse",
27318
27330
  MACHINE_UPDATE: "machine.update",
27319
27331
  MACHINE_CONFIG_UPDATED: "machine.config.updated",
27320
- MACHINE_CLIP_INSTALL: "machine.clip.install",
27332
+ MACHINE_CLIP_SYNC: "machine.clip.sync",
27333
+ MACHINE_BROWSER_PROFILE_LIFECYCLE: "machine.browser_profile.lifecycle",
27321
27334
  AGENT_NEW_SESSION: "agent.new_session",
27322
- CLIP_INSTALLED: "clip.installed",
27335
+ CLIP_CREATED: "clip.created",
27323
27336
  CLIP_REMOVED: "clip.removed",
27324
27337
  CLIP_UPDATED: "clip.updated"
27325
27338
  };
@@ -27348,8 +27361,13 @@ var ParallClient = class _ParallClient {
27348
27361
  /** Proactive refresh when token expires within this window (seconds). */
27349
27362
  static REFRESH_THRESHOLD_S = 5 * 60;
27350
27363
  static normalizeFetchError(err) {
27351
- if (typeof DOMException !== "undefined" && err instanceof DOMException && (err.name === "TimeoutError" || err.name === "AbortError")) {
27352
- return new ApiError(0, "Request timed out", "REQUEST_TIMEOUT");
27364
+ if (typeof DOMException !== "undefined" && err instanceof DOMException) {
27365
+ if (err.name === "TimeoutError") {
27366
+ return new ApiError(0, "Request timed out", "REQUEST_TIMEOUT");
27367
+ }
27368
+ if (err.name === "AbortError") {
27369
+ return new ApiError(0, "Request aborted", "REQUEST_ABORTED");
27370
+ }
27353
27371
  }
27354
27372
  const apiError = new ApiError(0, "Network request failed", "NETWORK_ERROR");
27355
27373
  if (err instanceof Error && err.message && err.message !== "Failed to fetch") {
@@ -27456,13 +27474,15 @@ var ParallClient = class _ParallClient {
27456
27474
  url += `?${qs}`;
27457
27475
  }
27458
27476
  const headers = this.buildHeaders();
27477
+ const timeoutSignal = AbortSignal.timeout(opts?.timeoutMs ?? 15e3);
27478
+ const signal = opts?.signal ? AbortSignal.any([opts.signal, timeoutSignal]) : timeoutSignal;
27459
27479
  let res;
27460
27480
  try {
27461
27481
  res = await fetch(url, {
27462
27482
  method,
27463
27483
  headers,
27464
27484
  body: body ? JSON.stringify(body) : void 0,
27465
- signal: AbortSignal.timeout(opts?.timeoutMs ?? 15e3)
27485
+ signal
27466
27486
  });
27467
27487
  } catch (err) {
27468
27488
  throw _ParallClient.normalizeFetchError(err);
@@ -27772,8 +27792,8 @@ var ParallClient = class _ParallClient {
27772
27792
  const res = await this.request("GET", ENDPOINTS.CHAT_MEMBERS(orgId, chatId));
27773
27793
  return res.data;
27774
27794
  }
27775
- async addChatMember(orgId, chatId, userId, role) {
27776
- return this.request("POST", ENDPOINTS.CHAT_MEMBERS(orgId, chatId), { user_id: userId, role });
27795
+ async addChatMember(orgId, chatId, userId) {
27796
+ return this.request("POST", ENDPOINTS.CHAT_MEMBERS(orgId, chatId), { user_id: userId });
27777
27797
  }
27778
27798
  async removeChatMember(orgId, chatId, userId) {
27779
27799
  return this.request("DELETE", ENDPOINTS.CHAT_MEMBER(orgId, chatId, userId));
@@ -28032,6 +28052,16 @@ var ParallClient = class _ParallClient {
28032
28052
  llm_source: llmSource
28033
28053
  });
28034
28054
  }
28055
+ /**
28056
+ * Toggle whether the machine contributes its local clips as a hub provider
28057
+ * — org-admin-gated (RequireOrgAdmin), like all machine management. See
28058
+ * docs/engineering-design/clip-provider-sharing-design.md.
28059
+ */
28060
+ async patchMachineProviderEnabled(orgId, machineId, providerEnabled) {
28061
+ return this.request("PATCH", ENDPOINTS.MACHINE_PROVIDER_ENABLED(orgId, machineId), {
28062
+ provider_enabled: providerEnabled
28063
+ });
28064
+ }
28035
28065
  /** Get machine-level runtime auth state. */
28036
28066
  async getMachineRuntimeAuth(orgId, machineId) {
28037
28067
  return this.request("GET", ENDPOINTS.MACHINE_RUNTIME_AUTH(orgId, machineId));
@@ -28065,25 +28095,39 @@ var ParallClient = class _ParallClient {
28065
28095
  const res = await this.request("GET", ENDPOINTS.MACHINES_ME_AGENTS);
28066
28096
  return res.data;
28067
28097
  }
28068
- /** `GET /machines/me/clip-installs` — registry clips this machine should
28069
- * install but hasn't yet (durable reconcile, mck_-authed). */
28070
- async listClipInstalls() {
28071
- const res = await this.request("GET", ENDPOINTS.MACHINES_ME_CLIP_INSTALLS);
28098
+ /** `GET /machines/me/clips` — executable Clip definitions assigned to this machine. */
28099
+ async listMachineClips() {
28100
+ const res = await this.request("GET", ENDPOINTS.MACHINES_ME_CLIPS);
28072
28101
  return res.data;
28073
28102
  }
28074
- /** `POST /machines/me/clip-installs/{clipId}/installed` — report a finished install. */
28075
- async reportClipInstall(clipId, version) {
28076
- return this.request("POST", ENDPOINTS.MACHINES_ME_CLIP_INSTALL_DONE(clipId), version ? { version } : void 0);
28103
+ /** `GET /machines/me/browser-profiles` — browser profiles hosted by this machine. */
28104
+ async listMachineBrowserProfiles() {
28105
+ const res = await this.request("GET", ENDPOINTS.MACHINES_ME_BROWSER_PROFILES);
28106
+ return res.data;
28107
+ }
28108
+ /** `POST /machines/me/browser-profiles/{profileId}/status` — report runtime status. */
28109
+ async reportBrowserProfileStatus(profileId, status, errorMsg) {
28110
+ await this.request("POST", ENDPOINTS.MACHINES_ME_BROWSER_PROFILE_STATUS(profileId), {
28111
+ status,
28112
+ ...errorMsg ? { error_msg: errorMsg } : {}
28113
+ });
28077
28114
  }
28078
28115
  /**
28079
- * `POST /machines/me/health` — bump the Machine's `updated_at` to now.
28080
- * Body is intentionally empty; the server ignores any payload. The
28081
- * daemon should call this on a fixed cadence (e.g. every 30s) so an
28082
- * external observer can detect a wedged supervisor.
28116
+ * `POST /machines/me/health` — bump the Machine's `updated_at` to now and
28117
+ * report daemon state. The daemon should call this on a fixed cadence (e.g.
28118
+ * every 30s) so an external observer can detect a wedged supervisor. Both
28119
+ * fields are optional and only persisted when changed:
28120
+ * - `daemonVersion` — the running bundle/launcher version.
28121
+ * - `selfUpdateCapable` — whether the daemon can act on a machine.update
28122
+ * signal (true under a service manager, false for bare `npx` foreground).
28083
28123
  */
28084
- async postMachineHeartbeat(daemonVersion) {
28085
- const body = daemonVersion ? { daemon_version: daemonVersion } : void 0;
28086
- return this.request("POST", ENDPOINTS.MACHINES_ME_HEALTH, body);
28124
+ async postMachineHeartbeat(opts) {
28125
+ const body = {};
28126
+ if (opts?.daemonVersion)
28127
+ body.daemon_version = opts.daemonVersion;
28128
+ if (opts?.selfUpdateCapable !== void 0)
28129
+ body.self_update_capable = opts.selfUpdateCapable;
28130
+ return this.request("POST", ENDPOINTS.MACHINES_ME_HEALTH, Object.keys(body).length > 0 ? body : void 0);
28087
28131
  }
28088
28132
  async reportAgentWorkspaceState(agentId, state) {
28089
28133
  const res = await this.request("PUT", ENDPOINTS.MACHINES_ME_AGENT_WORKSPACE_STATE(agentId), state);
@@ -28185,6 +28229,10 @@ var ParallClient = class _ParallClient {
28185
28229
  async ackDispatchByID(orgId, id) {
28186
28230
  return this.request("POST", ENDPOINTS.DISPATCH_ACK_BY_ID(orgId, id));
28187
28231
  }
28232
+ async expireDispatch(orgId, maxAgeHours) {
28233
+ const params = maxAgeHours !== void 0 ? { max_age_hours: String(maxAgeHours) } : void 0;
28234
+ return this.request("POST", ENDPOINTS.DISPATCH_EXPIRE(orgId), void 0, params);
28235
+ }
28188
28236
  async getDispatchByMessages(orgId, chatId, messageIds) {
28189
28237
  const params = { chat_id: chatId, message_ids: messageIds.join(",") };
28190
28238
  return this.request("GET", ENDPOINTS.DISPATCH_BY_MESSAGES(orgId), void 0, params);
@@ -28382,6 +28430,9 @@ var ParallClient = class _ParallClient {
28382
28430
  async getWikiNodeSections(orgId, wikiId, params) {
28383
28431
  return this.request("GET", ENDPOINTS.WIKI_NODE_SECTIONS(orgId, wikiId), void 0, params);
28384
28432
  }
28433
+ async search(orgId, params, opts) {
28434
+ return this.request("GET", ENDPOINTS.SEARCH(orgId), void 0, params, false, opts);
28435
+ }
28385
28436
  async searchWiki(orgId, wikiId, params) {
28386
28437
  return this.request("GET", ENDPOINTS.WIKI_SEARCH(orgId, wikiId), void 0, params);
28387
28438
  }
@@ -28665,6 +28716,41 @@ var ParallClient = class _ParallClient {
28665
28716
  const resp = await this.request("GET", ENDPOINTS.CLIP_ONLINE(orgId));
28666
28717
  return resp.data;
28667
28718
  }
28719
+ async listBrowserProfiles(orgId) {
28720
+ const resp = await this.request("GET", ENDPOINTS.BROWSER_PROFILES(orgId));
28721
+ return resp.data;
28722
+ }
28723
+ async createBrowserProfile(orgId, req) {
28724
+ return this.request("POST", ENDPOINTS.BROWSER_PROFILES(orgId), req);
28725
+ }
28726
+ async getBrowserProfile(orgId, profileId) {
28727
+ return this.request("GET", ENDPOINTS.BROWSER_PROFILE(orgId, profileId));
28728
+ }
28729
+ async updateBrowserProfile(orgId, profileId, req) {
28730
+ return this.request("PATCH", ENDPOINTS.BROWSER_PROFILE(orgId, profileId), req);
28731
+ }
28732
+ async deleteBrowserProfile(orgId, profileId) {
28733
+ await this.request("DELETE", ENDPOINTS.BROWSER_PROFILE(orgId, profileId));
28734
+ }
28735
+ async openBrowserProfile(orgId, profileId, req = {}) {
28736
+ return this.request("POST", ENDPOINTS.BROWSER_PROFILE_OPEN(orgId, profileId), req);
28737
+ }
28738
+ async stopBrowserProfile(orgId, profileId) {
28739
+ return this.request("POST", ENDPOINTS.BROWSER_PROFILE_STOP(orgId, profileId), {});
28740
+ }
28741
+ async resetBrowserProfile(orgId, profileId) {
28742
+ return this.request("POST", ENDPOINTS.BROWSER_PROFILE_RESET(orgId, profileId));
28743
+ }
28744
+ async listBrowserProfileConsents(orgId, profileId) {
28745
+ const resp = await this.request("GET", ENDPOINTS.BROWSER_PROFILE_CONSENTS(orgId, profileId));
28746
+ return resp.data;
28747
+ }
28748
+ async grantBrowserProfileConsent(orgId, profileId, req) {
28749
+ return this.request("POST", ENDPOINTS.BROWSER_PROFILE_CONSENTS(orgId, profileId), req);
28750
+ }
28751
+ async revokeBrowserProfileConsent(orgId, profileId, clipId) {
28752
+ await this.request("DELETE", ENDPOINTS.BROWSER_PROFILE_CONSENT(orgId, profileId, clipId));
28753
+ }
28668
28754
  async listRegistryClips(q) {
28669
28755
  const url = ENDPOINTS.CLIP_REGISTRY() + (q ? `?q=${encodeURIComponent(q)}` : "");
28670
28756
  const resp = await this.request("GET", url);
@@ -30648,8 +30734,28 @@ var ParallAgentGateway = class {
30648
30734
  return dispatched;
30649
30735
  }
30650
30736
  async catchUpFromDispatch() {
30737
+ const OVERFLOW_THRESHOLD = 500;
30738
+ const EXPIRE_AGE_HOURS = 24;
30739
+ const CATCHUP_MAX = 10;
30740
+ let expireSummary = null;
30741
+ let overflowMode = false;
30742
+ try {
30743
+ const { count } = await this.opts.client.getDispatchPendingCount(this.opts.config.org_id);
30744
+ if (count > OVERFLOW_THRESHOLD) {
30745
+ overflowMode = true;
30746
+ const result = await this.opts.client.expireDispatch(this.opts.config.org_id, EXPIRE_AGE_HOURS);
30747
+ if (result.total > 0) {
30748
+ this.opts.log?.warn(`dispatch catch-up overflow: ${count} pending, expired ${result.total} events older than ${EXPIRE_AGE_HOURS}h`);
30749
+ expireSummary = this.buildOfflineSummary(result.groups);
30750
+ }
30751
+ }
30752
+ } catch (err) {
30753
+ this.opts.log?.warn(`dispatch catch-up overflow check failed: ${String(err)}`);
30754
+ }
30651
30755
  let cursor;
30652
30756
  let processed = 0;
30757
+ let skipped = 0;
30758
+ const skippedByType = /* @__PURE__ */ new Map();
30653
30759
  do {
30654
30760
  const page = await this.opts.client.getDispatch(this.opts.config.org_id, {
30655
30761
  limit: 50,
@@ -30658,6 +30764,17 @@ var ParallAgentGateway = class {
30658
30764
  for (const item of page.data ?? []) {
30659
30765
  if (this.shuttingDown)
30660
30766
  break;
30767
+ if (overflowMode && processed >= CATCHUP_MAX) {
30768
+ try {
30769
+ await this.opts.client.ackDispatchByID(this.opts.config.org_id, item.id);
30770
+ skipped++;
30771
+ const key = item.event_type;
30772
+ skippedByType.set(key, (skippedByType.get(key) ?? 0) + 1);
30773
+ } catch {
30774
+ this.opts.log?.warn(`catch-up skip ack failed for ${item.id}, leaving pending`);
30775
+ }
30776
+ continue;
30777
+ }
30661
30778
  processed++;
30662
30779
  try {
30663
30780
  let dispatched = false;
@@ -30732,9 +30849,52 @@ var ParallAgentGateway = class {
30732
30849
  }
30733
30850
  cursor = !this.shuttingDown && page.has_more ? page.next_cursor : void 0;
30734
30851
  } while (cursor);
30735
- if (processed > 0) {
30736
- this.opts.log?.info(`dispatch catch-up: processed ${processed}`);
30852
+ if (expireSummary || skipped > 0) {
30853
+ const parts = [];
30854
+ if (expireSummary)
30855
+ parts.push(expireSummary);
30856
+ if (skipped > 0) {
30857
+ const recentLines = [...skippedByType.entries()].map(([type, count]) => `- ${count} recent ${type.replace(".", " ").replace("_", " ")} events`).join("\n");
30858
+ parts.push(expireSummary ? `Additionally, ${skipped} recent events exceeded the catch-up limit and were skipped:
30859
+ ${recentLines}` : `[Offline Summary] You were offline. ${skipped} pending events exceeded the catch-up limit and were skipped:
30860
+ ${recentLines}
30861
+
30862
+ Use your tools (read messages, check tasks) to review anything still relevant.`);
30863
+ }
30864
+ const fullSummary = parts.join("\n\n");
30865
+ this.pendingRestartNotification = this.pendingRestartNotification ? `${this.pendingRestartNotification}
30866
+
30867
+ ${fullSummary}` : fullSummary;
30868
+ }
30869
+ if (processed > 0 || skipped > 0) {
30870
+ this.opts.log?.info(`dispatch catch-up: processed ${processed}, skipped ${skipped}`);
30871
+ }
30872
+ }
30873
+ buildOfflineSummary(groups) {
30874
+ const lines = [
30875
+ "[Offline Summary] You were offline and had a large backlog of pending events. The following old events were skipped:"
30876
+ ];
30877
+ for (const g of groups) {
30878
+ const age = this.formatAge(g.oldest);
30879
+ if (g.chat_id && g.event_type === "message") {
30880
+ const name = g.chat_name ?? g.chat_id;
30881
+ lines.push(`- ${g.count} messages in "${name}" (oldest: ${age})`);
30882
+ } else {
30883
+ const label = g.event_type.replace(".", " ").replace("_", " ");
30884
+ lines.push(`- ${g.count} ${label} events (oldest: ${age})`);
30885
+ }
30737
30886
  }
30887
+ lines.push("");
30888
+ lines.push("These events were too old to process individually. Use your tools (read messages, check tasks) to review anything still relevant.");
30889
+ return lines.join("\n");
30890
+ }
30891
+ formatAge(iso) {
30892
+ const ms = Date.now() - new Date(iso).getTime();
30893
+ const hours = Math.floor(ms / 36e5);
30894
+ if (hours < 24)
30895
+ return `${hours}h ago`;
30896
+ const days = Math.floor(hours / 24);
30897
+ return `${days}d ago`;
30738
30898
  }
30739
30899
  async handleHello(data) {
30740
30900
  const { client, config, log } = this.opts;
@@ -32259,6 +32419,26 @@ async function waitForOpenClawSessionId(sessionsDir, sessionKey, timeoutMs = 1e4
32259
32419
  }
32260
32420
  function createOpenClawDispatchAdapter(opts) {
32261
32421
  const activeStreams = /* @__PURE__ */ new Map();
32422
+ let configFallbackWarned = false;
32423
+ function resolveDispatchConfig(log) {
32424
+ try {
32425
+ const current = opts.core.config?.current?.();
32426
+ if (current && typeof current === "object") {
32427
+ configFallbackWarned = false;
32428
+ return current;
32429
+ }
32430
+ if (!configFallbackWarned) {
32431
+ configFallbackWarned = true;
32432
+ log?.warn?.(`parall[${opts.accountId}]: config.current() unavailable on this host \u2014 dispatching with the startup config snapshot; model switches need a gateway restart until then`);
32433
+ }
32434
+ } catch (err) {
32435
+ if (!configFallbackWarned) {
32436
+ configFallbackWarned = true;
32437
+ log?.warn?.(`parall[${opts.accountId}]: config.current() failed (${String(err)}) \u2014 dispatching with the startup config snapshot; model switches need a gateway restart until then`);
32438
+ }
32439
+ }
32440
+ return opts.cfg;
32441
+ }
32262
32442
  return {
32263
32443
  abortDispatch(sessionKey) {
32264
32444
  activeStreams.get(sessionKey)?.fail(new Error("dispatch deadline exceeded"));
@@ -32284,7 +32464,7 @@ function createOpenClawDispatchAdapter(opts) {
32284
32464
  let turnGroupKey = "";
32285
32465
  const run = opts.core.channel.reply.dispatchReplyWithBufferedBlockDispatcher({
32286
32466
  ctx: buildInboundCtx(opts.core, opts.accountId, event, sessionKey, promptBody, earlierEvents),
32287
- cfg: opts.cfg,
32467
+ cfg: resolveDispatchConfig(context2.log),
32288
32468
  dispatcherOptions: {
32289
32469
  deliver: async (payload) => {
32290
32470
  const replyText = payload.text?.trim();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@parall/parall",
3
- "version": "1.32.1",
3
+ "version": "1.34.0",
4
4
  "description": "OpenClaw channel plugin for Parall IM",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -16,8 +16,8 @@
16
16
  "openclaw.plugin.json"
17
17
  ],
18
18
  "dependencies": {
19
- "@parall/agent-core": "1.32.1",
20
- "@parall/sdk": "1.32.1"
19
+ "@parall/agent-core": "1.34.0",
20
+ "@parall/sdk": "1.34.0"
21
21
  },
22
22
  "devDependencies": {
23
23
  "@types/node": "^22.0.0",
package/src/gateway.ts CHANGED
@@ -195,7 +195,7 @@ async function waitForOpenClawSessionId(
195
195
  return null;
196
196
  }
197
197
 
198
- function createOpenClawDispatchAdapter(opts: {
198
+ export function createOpenClawDispatchAdapter(opts: {
199
199
  core: PluginRuntime;
200
200
  cfg: Record<string, unknown>;
201
201
  accountId: string;
@@ -203,6 +203,43 @@ function createOpenClawDispatchAdapter(opts: {
203
203
  workspaceDir: string;
204
204
  }): DispatchAdapter {
205
205
  const activeStreams = new Map<string, ReturnType<typeof createRuntimeEventStream>>();
206
+ let configFallbackWarned = false;
207
+
208
+ /**
209
+ * Resolve the config for one dispatch. OpenClaw hot-reloads
210
+ * `agents.defaults.model` / `models.providers` WITHOUT restarting channel
211
+ * plugins, so the `ctx.cfg` snapshot captured at startAccount goes stale the
212
+ * moment the operator switches the agent's model — every run would keep the
213
+ * old model until the whole gateway restarts. `core.config.current()` is the
214
+ * host's live (hot-reload-refreshed) config; fall back to the startup
215
+ * snapshot on hosts that predate the API. The fallback warns once per
216
+ * degradation streak (latch resets on success) — silently dispatching with
217
+ * the stale snapshot would recreate the invisible model-switch mismatch this
218
+ * function exists to fix.
219
+ */
220
+ function resolveDispatchConfig(log?: { warn?: (msg: string) => void }): Record<string, unknown> {
221
+ try {
222
+ const current = opts.core.config?.current?.();
223
+ if (current && typeof current === 'object') {
224
+ configFallbackWarned = false;
225
+ return current as unknown as Record<string, unknown>;
226
+ }
227
+ if (!configFallbackWarned) {
228
+ configFallbackWarned = true;
229
+ log?.warn?.(
230
+ `parall[${opts.accountId}]: config.current() unavailable on this host — dispatching with the startup config snapshot; model switches need a gateway restart until then`,
231
+ );
232
+ }
233
+ } catch (err) {
234
+ if (!configFallbackWarned) {
235
+ configFallbackWarned = true;
236
+ log?.warn?.(
237
+ `parall[${opts.accountId}]: config.current() failed (${String(err)}) — dispatching with the startup config snapshot; model switches need a gateway restart until then`,
238
+ );
239
+ }
240
+ }
241
+ return opts.cfg;
242
+ }
206
243
 
207
244
  return {
208
245
  abortDispatch(sessionKey: string): void {
@@ -238,7 +275,7 @@ function createOpenClawDispatchAdapter(opts: {
238
275
  promptBody,
239
276
  earlierEvents,
240
277
  ),
241
- cfg: opts.cfg,
278
+ cfg: resolveDispatchConfig(context.log),
242
279
  dispatcherOptions: {
243
280
  deliver: async (payload: { text?: string }) => {
244
281
  const replyText = payload.text?.trim();