@nfreeness/clawdbot 0.124.21703 → 0.124.21801

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.
@@ -2,14 +2,18 @@ import { normalizeProviderId } from "./model-selection.js";
2
2
  const CLAUDE_MODEL_ALIASES = {
3
3
  opus: "opus",
4
4
  "opus-4.5": "opus",
5
+ "opus-4.6": "opus",
5
6
  "opus-4": "opus",
6
7
  "claude-opus-4-5": "opus",
8
+ "claude-opus-4-6": "opus",
7
9
  "claude-opus-4": "opus",
8
10
  sonnet: "sonnet",
9
11
  "sonnet-4.5": "sonnet",
12
+ "sonnet-4.6": "sonnet",
10
13
  "sonnet-4.1": "sonnet",
11
14
  "sonnet-4.0": "sonnet",
12
15
  "claude-sonnet-4-5": "sonnet",
16
+ "claude-sonnet-4-6": "sonnet",
13
17
  "claude-sonnet-4-1": "sonnet",
14
18
  "claude-sonnet-4-0": "sonnet",
15
19
  haiku: "haiku",
@@ -1,6 +1,7 @@
1
1
  const ANTHROPIC_PREFIXES = [
2
2
  "claude-opus-4-6",
3
3
  "claude-opus-4-5",
4
+ "claude-sonnet-4-6",
4
5
  "claude-sonnet-4-5",
5
6
  "claude-haiku-4-5",
6
7
  ];
@@ -217,11 +217,11 @@ async function probeImage(model, apiKey, timeoutMs) {
217
217
  }
218
218
  }
219
219
  function ensureImageInput(model) {
220
- if (model.input.includes("image"))
220
+ if (model.input?.includes("image"))
221
221
  return model;
222
222
  return {
223
223
  ...model,
224
- input: Array.from(new Set([...model.input, "image"])),
224
+ input: Array.from(new Set([...(model.input ?? []), "image"])),
225
225
  };
226
226
  }
227
227
  async function mapWithConcurrency(items, concurrency, fn, opts) {
@@ -319,7 +319,7 @@ export async function scanOpenRouterModels(options = {}) {
319
319
  reasoning: baseModel.reasoning,
320
320
  };
321
321
  const toolResult = await probeTool(model, apiKey, timeoutMs);
322
- const imageResult = model.input.includes("image")
322
+ const imageResult = model.input?.includes("image")
323
323
  ? await probeImage(ensureImageInput(model), apiKey, timeoutMs)
324
324
  : { ok: false, latencyMs: null, skipped: true };
325
325
  return {
@@ -33,8 +33,12 @@ function normalizeAnthropicModelId(model) {
33
33
  const lower = trimmed.toLowerCase();
34
34
  if (lower === "opus-4.5")
35
35
  return "claude-opus-4-5";
36
+ if (lower === "opus-4.6")
37
+ return "claude-opus-4-6";
36
38
  if (lower === "sonnet-4.5")
37
39
  return "claude-sonnet-4-5";
40
+ if (lower === "sonnet-4.6")
41
+ return "claude-sonnet-4-6";
38
42
  return trimmed;
39
43
  }
40
44
  function normalizeProviderModelId(provider, model) {
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.124.21703",
3
- "commit": "7f515d663c475d027b92e4a9a4f91d31d5b8276d",
4
- "builtAt": "2026-02-17T12:50:00.229Z"
2
+ "version": "0.124.21801",
3
+ "commit": "f3fa348bed73e882c5e5065f8f59c0fad888503e",
4
+ "builtAt": "2026-02-18T15:38:22.460Z"
5
5
  }
@@ -3,11 +3,17 @@ import { getChannelPlugin, listChannelPlugins } from "../channels/plugins/index.
3
3
  import { formatErrorMessage } from "../infra/errors.js";
4
4
  import { resetDirectoryCache } from "../infra/outbound/target-resolver.js";
5
5
  import { DEFAULT_ACCOUNT_ID } from "../routing/session-key.js";
6
+ /** Auto-restart constants */
7
+ const CHANNEL_RESTART_BASE_DELAY_MS = 10_000;
8
+ const CHANNEL_RESTART_MAX_DELAY_MS = 5 * 60_000;
9
+ const CHANNEL_RESTART_MAX_RETRIES = 10;
6
10
  function createRuntimeStore() {
7
11
  return {
8
12
  aborts: new Map(),
9
13
  tasks: new Map(),
10
14
  runtimes: new Map(),
15
+ retryTimers: new Map(),
16
+ retryCounts: new Map(),
11
17
  };
12
18
  }
13
19
  function isAccountEnabled(account) {
@@ -26,6 +32,9 @@ function cloneDefaultRuntime(channelId, accountId) {
26
32
  // Channel docking: lifecycle hooks (`plugin.gateway`) flow through this manager.
27
33
  export function createChannelManager(opts) {
28
34
  const { loadConfig, channelLogs, channelRuntimeEnvs } = opts;
35
+ /** Set of channel+account keys that have been explicitly stopped (no auto-restart). */
36
+ const explicitlyStopped = new Set();
37
+ const stoppedKey = (channelId, accountId) => `${channelId}:${accountId}`;
29
38
  const channelStores = new Map();
30
39
  const getStore = (channelId) => {
31
40
  const existing = channelStores.get(channelId);
@@ -57,6 +66,10 @@ export function createChannelManager(opts) {
57
66
  const accountIds = accountId ? [accountId] : plugin.config.listAccountIds(cfg);
58
67
  if (accountIds.length === 0)
59
68
  return;
69
+ // Clear explicit-stop flag when starting a channel (allows auto-restart again).
70
+ for (const id of accountIds) {
71
+ explicitlyStopped.delete(stoppedKey(channelId, id));
72
+ }
60
73
  await Promise.all(accountIds.map(async (id) => {
61
74
  if (store.tasks.has(id))
62
75
  return;
@@ -112,11 +125,36 @@ export function createChannelManager(opts) {
112
125
  .finally(() => {
113
126
  store.aborts.delete(id);
114
127
  store.tasks.delete(id);
128
+ const wasRunning = getRuntime(channelId, id).running;
115
129
  setRuntime(channelId, id, {
116
130
  accountId: id,
117
131
  running: false,
118
132
  lastStopAt: Date.now(),
119
133
  });
134
+ // Auto-restart: if the channel was running (not explicitly stopped) and crashed,
135
+ // schedule a restart with exponential backoff.
136
+ const key = stoppedKey(channelId, id);
137
+ if (wasRunning && !explicitlyStopped.has(key) && !abort.signal.aborted) {
138
+ const retryCount = store.retryCounts.get(id) ?? 0;
139
+ if (retryCount < CHANNEL_RESTART_MAX_RETRIES) {
140
+ const delay = Math.min(CHANNEL_RESTART_BASE_DELAY_MS * 2 ** retryCount, CHANNEL_RESTART_MAX_DELAY_MS);
141
+ log.warn?.(`[${id}] channel crashed; scheduling auto-restart in ${Math.round(delay / 1000)}s (attempt ${retryCount + 1}/${CHANNEL_RESTART_MAX_RETRIES})`);
142
+ store.retryCounts.set(id, retryCount + 1);
143
+ const timer = setTimeout(() => {
144
+ store.retryTimers.delete(id);
145
+ if (explicitlyStopped.has(key))
146
+ return;
147
+ log.info?.(`[${id}] auto-restarting channel`);
148
+ void startChannel(channelId, id).catch((restartErr) => {
149
+ log.error?.(`[${id}] auto-restart failed: ${formatErrorMessage(restartErr)}`);
150
+ });
151
+ }, delay);
152
+ store.retryTimers.set(id, timer);
153
+ }
154
+ else {
155
+ log.error?.(`[${id}] channel crashed; max retries (${CHANNEL_RESTART_MAX_RETRIES}) exhausted, giving up auto-restart`);
156
+ }
157
+ }
120
158
  });
121
159
  store.tasks.set(id, tracked);
122
160
  }));
@@ -135,6 +173,15 @@ export function createChannelManager(opts) {
135
173
  knownIds.add(accountId);
136
174
  }
137
175
  await Promise.all(Array.from(knownIds.values()).map(async (id) => {
176
+ // Mark as explicitly stopped to prevent auto-restart.
177
+ explicitlyStopped.add(stoppedKey(channelId, id));
178
+ // Cancel any pending auto-restart timer.
179
+ const pendingTimer = store.retryTimers.get(id);
180
+ if (pendingTimer) {
181
+ clearTimeout(pendingTimer);
182
+ store.retryTimers.delete(id);
183
+ }
184
+ store.retryCounts.delete(id);
138
185
  const abort = store.aborts.get(id);
139
186
  const task = store.tasks.get(id);
140
187
  if (!abort && !task && !plugin?.gateway?.stopAccount)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nfreeness/clawdbot",
3
- "version": "0.124.21703",
3
+ "version": "0.124.21801",
4
4
  "description": "WhatsApp gateway CLI (Baileys web) with Pi RPC agent",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",