@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",
|
|
@@ -217,11 +217,11 @@ async function probeImage(model, apiKey, timeoutMs) {
|
|
|
217
217
|
}
|
|
218
218
|
}
|
|
219
219
|
function ensureImageInput(model) {
|
|
220
|
-
if (model.input
|
|
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
|
|
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) {
|
package/dist/build-info.json
CHANGED
|
@@ -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)
|