cc-claw 0.18.1 → 0.18.2
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/cli.js +226 -29
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -33,7 +33,7 @@ var VERSION;
|
|
|
33
33
|
var init_version = __esm({
|
|
34
34
|
"src/version.ts"() {
|
|
35
35
|
"use strict";
|
|
36
|
-
VERSION = true ? "0.18.
|
|
36
|
+
VERSION = true ? "0.18.2" : (() => {
|
|
37
37
|
try {
|
|
38
38
|
return JSON.parse(readFileSync(join(process.cwd(), "package.json"), "utf-8")).version ?? "unknown";
|
|
39
39
|
} catch {
|
|
@@ -1342,7 +1342,8 @@ function initSchema(db3) {
|
|
|
1342
1342
|
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
1343
1343
|
last_run_at TEXT,
|
|
1344
1344
|
next_run_at TEXT,
|
|
1345
|
-
consecutive_failures INTEGER NOT NULL DEFAULT 0
|
|
1345
|
+
consecutive_failures INTEGER NOT NULL DEFAULT 0,
|
|
1346
|
+
allow_paid_slots INTEGER NOT NULL DEFAULT 0
|
|
1346
1347
|
);
|
|
1347
1348
|
`);
|
|
1348
1349
|
try {
|
|
@@ -1498,6 +1499,10 @@ function initSchema(db3) {
|
|
|
1498
1499
|
db3.exec("ALTER TABLE jobs ADD COLUMN job_type TEXT DEFAULT 'normal'");
|
|
1499
1500
|
} catch {
|
|
1500
1501
|
}
|
|
1502
|
+
try {
|
|
1503
|
+
db3.exec("ALTER TABLE jobs ADD COLUMN allow_paid_slots INTEGER NOT NULL DEFAULT 0");
|
|
1504
|
+
} catch {
|
|
1505
|
+
}
|
|
1501
1506
|
try {
|
|
1502
1507
|
db3.exec("UPDATE jobs SET job_type = 'reflection' WHERE description LIKE '%reflection analysis%' AND job_type = 'normal'");
|
|
1503
1508
|
} catch {
|
|
@@ -1781,6 +1786,13 @@ function initSchema(db3) {
|
|
|
1781
1786
|
PRIMARY KEY (chat_id, backend)
|
|
1782
1787
|
);
|
|
1783
1788
|
`);
|
|
1789
|
+
db3.exec(`
|
|
1790
|
+
CREATE TABLE IF NOT EXISTS chat_allow_paid_slots (
|
|
1791
|
+
chat_id TEXT NOT NULL,
|
|
1792
|
+
backend TEXT NOT NULL,
|
|
1793
|
+
PRIMARY KEY (chat_id, backend)
|
|
1794
|
+
);
|
|
1795
|
+
`);
|
|
1784
1796
|
try {
|
|
1785
1797
|
const deleted = db3.prepare(
|
|
1786
1798
|
"DELETE FROM usage_log WHERE created_at < datetime('now', '-90 days')"
|
|
@@ -2445,6 +2457,8 @@ var chat_settings_exports = {};
|
|
|
2445
2457
|
__export(chat_settings_exports, {
|
|
2446
2458
|
ALL_TOOLS: () => ALL_TOOLS,
|
|
2447
2459
|
clearAgentMode: () => clearAgentMode,
|
|
2460
|
+
clearAllPaidSlots: () => clearAllPaidSlots,
|
|
2461
|
+
clearChatPaidSlots: () => clearChatPaidSlots,
|
|
2448
2462
|
clearCwd: () => clearCwd,
|
|
2449
2463
|
clearExecMode: () => clearExecMode,
|
|
2450
2464
|
clearModel: () => clearModel,
|
|
@@ -2455,6 +2469,7 @@ __export(chat_settings_exports, {
|
|
|
2455
2469
|
findBookmarksByPrefix: () => findBookmarksByPrefix,
|
|
2456
2470
|
getAgentMode: () => getAgentMode,
|
|
2457
2471
|
getAllBookmarks: () => getAllBookmarks,
|
|
2472
|
+
getAllowPaidSlots: () => getAllowPaidSlots,
|
|
2458
2473
|
getBackend: () => getBackend,
|
|
2459
2474
|
getBookmark: () => getBookmark,
|
|
2460
2475
|
getCwd: () => getCwd,
|
|
@@ -2473,6 +2488,7 @@ __export(chat_settings_exports, {
|
|
|
2473
2488
|
removePendingEscalation: () => removePendingEscalation,
|
|
2474
2489
|
resetTools: () => resetTools,
|
|
2475
2490
|
setAgentMode: () => setAgentMode,
|
|
2491
|
+
setAllowPaidSlots: () => setAllowPaidSlots,
|
|
2476
2492
|
setBackend: () => setBackend,
|
|
2477
2493
|
setCwd: () => setCwd,
|
|
2478
2494
|
setExecMode: () => setExecMode,
|
|
@@ -2774,6 +2790,23 @@ function toggleSessionLogEnabled(chatId) {
|
|
|
2774
2790
|
setSessionLogEnabled(chatId, next);
|
|
2775
2791
|
return next;
|
|
2776
2792
|
}
|
|
2793
|
+
function getAllowPaidSlots(chatId, backend2) {
|
|
2794
|
+
const row = getDb().prepare(
|
|
2795
|
+
"SELECT 1 FROM chat_allow_paid_slots WHERE chat_id = ? AND backend = ?"
|
|
2796
|
+
).get(chatId, backend2);
|
|
2797
|
+
return !!row;
|
|
2798
|
+
}
|
|
2799
|
+
function setAllowPaidSlots(chatId, backend2) {
|
|
2800
|
+
getDb().prepare(`
|
|
2801
|
+
INSERT OR IGNORE INTO chat_allow_paid_slots (chat_id, backend) VALUES (?, ?)
|
|
2802
|
+
`).run(chatId, backend2);
|
|
2803
|
+
}
|
|
2804
|
+
function clearChatPaidSlots(chatId) {
|
|
2805
|
+
getDb().prepare("DELETE FROM chat_allow_paid_slots WHERE chat_id = ?").run(chatId);
|
|
2806
|
+
}
|
|
2807
|
+
function clearAllPaidSlots() {
|
|
2808
|
+
getDb().prepare("DELETE FROM chat_allow_paid_slots").run();
|
|
2809
|
+
}
|
|
2777
2810
|
var pendingEscalations, ESCALATION_TTL_MS, ALL_TOOLS;
|
|
2778
2811
|
var init_chat_settings = __esm({
|
|
2779
2812
|
"src/memory/chat-settings.ts"() {
|
|
@@ -3038,8 +3071,8 @@ function insertJob(params) {
|
|
|
3038
3071
|
const db3 = getDb();
|
|
3039
3072
|
const result = db3.prepare(`
|
|
3040
3073
|
INSERT INTO jobs (schedule_type, cron, at_time, every_ms, title, description, chat_id,
|
|
3041
|
-
backend, model, thinking, timeout, fallbacks, session_type, channel, target, delivery_mode, timezone, job_type)
|
|
3042
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
3074
|
+
backend, model, thinking, timeout, fallbacks, session_type, channel, target, delivery_mode, timezone, job_type, allow_paid_slots)
|
|
3075
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
3043
3076
|
`).run(
|
|
3044
3077
|
params.scheduleType,
|
|
3045
3078
|
params.cron ?? null,
|
|
@@ -3058,13 +3091,15 @@ function insertJob(params) {
|
|
|
3058
3091
|
params.target ?? null,
|
|
3059
3092
|
params.deliveryMode ?? "announce",
|
|
3060
3093
|
params.timezone ?? "UTC",
|
|
3061
|
-
params.jobType ?? "normal"
|
|
3094
|
+
params.jobType ?? "normal",
|
|
3095
|
+
params.allowPaidSlots ? 1 : 0
|
|
3062
3096
|
);
|
|
3063
3097
|
return getJobById(Number(result.lastInsertRowid));
|
|
3064
3098
|
}
|
|
3065
3099
|
function mapJobRow(row) {
|
|
3066
3100
|
if (!row) return void 0;
|
|
3067
3101
|
row.fallbacks = row.fallbacks ? JSON.parse(row.fallbacks) : [];
|
|
3102
|
+
row.allowPaidSlots = !!row.allowPaidSlots;
|
|
3068
3103
|
return row;
|
|
3069
3104
|
}
|
|
3070
3105
|
function getJobById(id) {
|
|
@@ -3118,7 +3153,8 @@ function updateJob(id, fields) {
|
|
|
3118
3153
|
target: "target",
|
|
3119
3154
|
deliveryMode: "delivery_mode",
|
|
3120
3155
|
timezone: "timezone",
|
|
3121
|
-
jobType: "job_type"
|
|
3156
|
+
jobType: "job_type",
|
|
3157
|
+
allowPaidSlots: "allow_paid_slots"
|
|
3122
3158
|
};
|
|
3123
3159
|
for (const [key, val] of Object.entries(fields)) {
|
|
3124
3160
|
const col = fieldMap[key];
|
|
@@ -3190,7 +3226,8 @@ var init_jobs = __esm({
|
|
|
3190
3226
|
title, description, chat_id as chatId, backend, model, thinking, timeout, fallbacks,
|
|
3191
3227
|
session_type as sessionType, channel, target, delivery_mode as deliveryMode,
|
|
3192
3228
|
timezone, job_type as jobType, enabled, active, created_at as createdAt, last_run_at as lastRunAt,
|
|
3193
|
-
next_run_at as nextRunAt, consecutive_failures as consecutiveFailures
|
|
3229
|
+
next_run_at as nextRunAt, consecutive_failures as consecutiveFailures,
|
|
3230
|
+
allow_paid_slots as allowPaidSlots
|
|
3194
3231
|
FROM jobs
|
|
3195
3232
|
`;
|
|
3196
3233
|
}
|
|
@@ -3370,9 +3407,13 @@ function getGeminiSlots() {
|
|
|
3370
3407
|
FROM gemini_credentials ORDER BY priority ASC, id ASC
|
|
3371
3408
|
`).all();
|
|
3372
3409
|
}
|
|
3373
|
-
function getEligibleGeminiSlots(mode) {
|
|
3410
|
+
function getEligibleGeminiSlots(mode, allowPaid) {
|
|
3374
3411
|
if (mode === "off") return [];
|
|
3375
|
-
const
|
|
3412
|
+
const effectiveAllowPaid = mode === "keys" ? true : allowPaid ?? false;
|
|
3413
|
+
const slotTypeFilter = mode === "accounts" ? "AND slot_type = 'oauth'" : mode === "keys" ? "AND slot_type = 'api_key'" : (
|
|
3414
|
+
// "all" or undefined: exclude paid slots unless allowed
|
|
3415
|
+
!effectiveAllowPaid ? "AND slot_type != 'api_key'" : ""
|
|
3416
|
+
);
|
|
3376
3417
|
return getDb().prepare(`
|
|
3377
3418
|
SELECT id, slot_type AS slotType, label, api_key AS apiKey, config_home AS configHome,
|
|
3378
3419
|
priority, enabled, cooldown_until AS cooldownUntil, last_used AS lastUsed,
|
|
@@ -3380,7 +3421,8 @@ function getEligibleGeminiSlots(mode) {
|
|
|
3380
3421
|
FROM gemini_credentials
|
|
3381
3422
|
WHERE enabled = 1 AND (cooldown_until IS NULL OR cooldown_until <= datetime('now'))
|
|
3382
3423
|
${slotTypeFilter}
|
|
3383
|
-
ORDER BY
|
|
3424
|
+
ORDER BY CASE WHEN slot_type = 'api_key' THEN 1 ELSE 0 END ASC,
|
|
3425
|
+
priority ASC, last_used ASC NULLS FIRST
|
|
3384
3426
|
`).all();
|
|
3385
3427
|
}
|
|
3386
3428
|
function getChatGeminiSlotId(chatId) {
|
|
@@ -3473,13 +3515,20 @@ function getBackendSlots(backend2) {
|
|
|
3473
3515
|
FROM backend_credentials bc WHERE bc.backend = ? ORDER BY bc.priority ASC, bc.id ASC
|
|
3474
3516
|
`).all(backend2);
|
|
3475
3517
|
}
|
|
3476
|
-
function getEligibleBackendSlots(backend2) {
|
|
3518
|
+
function getEligibleBackendSlots(backend2, mode, allowPaid) {
|
|
3519
|
+
if (mode === "off") return [];
|
|
3520
|
+
const slotTypeFilter = mode === "accounts" ? "AND bc.slot_type = 'oauth'" : mode === "keys" ? "AND bc.slot_type = 'api_key'" : (
|
|
3521
|
+
// "all" or undefined: exclude paid slots unless allowed
|
|
3522
|
+
!allowPaid ? "AND bc.slot_type != 'api_key'" : ""
|
|
3523
|
+
);
|
|
3477
3524
|
return getDb().prepare(`
|
|
3478
3525
|
SELECT ${SLOT_COLUMNS}
|
|
3479
3526
|
FROM backend_credentials bc
|
|
3480
3527
|
WHERE bc.backend = ? AND bc.enabled = 1
|
|
3481
3528
|
AND (bc.cooldown_until IS NULL OR bc.cooldown_until <= datetime('now'))
|
|
3482
|
-
|
|
3529
|
+
${slotTypeFilter}
|
|
3530
|
+
ORDER BY CASE WHEN bc.slot_type = 'api_key' THEN 1 ELSE 0 END ASC,
|
|
3531
|
+
bc.priority ASC, bc.last_used ASC NULLS FIRST
|
|
3483
3532
|
`).all(backend2);
|
|
3484
3533
|
}
|
|
3485
3534
|
function getChatBackendSlotId(chatId, backend2) {
|
|
@@ -3550,6 +3599,13 @@ function reenableBackendSlot(slotId) {
|
|
|
3550
3599
|
WHERE id = ?
|
|
3551
3600
|
`).run(slotId);
|
|
3552
3601
|
}
|
|
3602
|
+
function getBackendRotationMode(backend2) {
|
|
3603
|
+
const row = getDb().prepare("SELECT value FROM meta WHERE key = ?").get(`${backend2}_rotation_mode`);
|
|
3604
|
+
return row?.value ?? "all";
|
|
3605
|
+
}
|
|
3606
|
+
function setBackendRotationMode(backend2, mode) {
|
|
3607
|
+
getDb().prepare("INSERT OR REPLACE INTO meta (key, value) VALUES (?, ?)").run(`${backend2}_rotation_mode`, mode);
|
|
3608
|
+
}
|
|
3553
3609
|
var SLOT_COLUMNS;
|
|
3554
3610
|
var init_backend_slots = __esm({
|
|
3555
3611
|
"src/memory/backend-slots.ts"() {
|
|
@@ -3576,10 +3632,12 @@ __export(store_exports5, {
|
|
|
3576
3632
|
checkBackendLimits: () => checkBackendLimits,
|
|
3577
3633
|
cleanExpiredWatches: () => cleanExpiredWatches,
|
|
3578
3634
|
clearAgentMode: () => clearAgentMode,
|
|
3635
|
+
clearAllPaidSlots: () => clearAllPaidSlots,
|
|
3579
3636
|
clearAllSessions: () => clearAllSessions,
|
|
3580
3637
|
clearBackendLimit: () => clearBackendLimit,
|
|
3581
3638
|
clearChatBackendSlot: () => clearChatBackendSlot,
|
|
3582
3639
|
clearChatGeminiSlot: () => clearChatGeminiSlot,
|
|
3640
|
+
clearChatPaidSlots: () => clearChatPaidSlots,
|
|
3583
3641
|
clearCwd: () => clearCwd,
|
|
3584
3642
|
clearExecMode: () => clearExecMode,
|
|
3585
3643
|
clearMessageLog: () => clearMessageLog,
|
|
@@ -3605,8 +3663,10 @@ __export(store_exports5, {
|
|
|
3605
3663
|
getAllMemoriesWithEmbeddings: () => getAllMemoriesWithEmbeddings,
|
|
3606
3664
|
getAllSessionSummariesWithEmbeddings: () => getAllSessionSummariesWithEmbeddings,
|
|
3607
3665
|
getAllTimeUsage: () => getAllTimeUsage,
|
|
3666
|
+
getAllowPaidSlots: () => getAllowPaidSlots,
|
|
3608
3667
|
getBackend: () => getBackend,
|
|
3609
3668
|
getBackendLimit: () => getBackendLimit,
|
|
3669
|
+
getBackendRotationMode: () => getBackendRotationMode,
|
|
3610
3670
|
getBackendSlots: () => getBackendSlots,
|
|
3611
3671
|
getBackendUsageInWindow: () => getBackendUsageInWindow,
|
|
3612
3672
|
getBookmark: () => getBookmark,
|
|
@@ -3686,8 +3746,10 @@ __export(store_exports5, {
|
|
|
3686
3746
|
searchMessageLog: () => searchMessageLog,
|
|
3687
3747
|
searchSessionSummaries: () => searchSessionSummaries,
|
|
3688
3748
|
setAgentMode: () => setAgentMode,
|
|
3749
|
+
setAllowPaidSlots: () => setAllowPaidSlots,
|
|
3689
3750
|
setBackend: () => setBackend,
|
|
3690
3751
|
setBackendLimit: () => setBackendLimit,
|
|
3752
|
+
setBackendRotationMode: () => setBackendRotationMode,
|
|
3691
3753
|
setBackendSlotEnabled: () => setBackendSlotEnabled,
|
|
3692
3754
|
setBootTime: () => setBootTime,
|
|
3693
3755
|
setChatAlias: () => setChatAlias,
|
|
@@ -10886,6 +10948,7 @@ var init_detect = __esm({
|
|
|
10886
10948
|
var agent_exports = {};
|
|
10887
10949
|
__export(agent_exports, {
|
|
10888
10950
|
FIRST_RESPONSE_TIMEOUT_ERROR: () => FIRST_RESPONSE_TIMEOUT_ERROR,
|
|
10951
|
+
FREE_SLOTS_EXHAUSTED: () => FREE_SLOTS_EXHAUSTED,
|
|
10889
10952
|
askAgent: () => askAgent,
|
|
10890
10953
|
getInFlightMessage: () => getInFlightMessage,
|
|
10891
10954
|
isChatBusy: () => isChatBusy,
|
|
@@ -11200,9 +11263,15 @@ Partial output: ${accumulatedText.slice(-500)}`;
|
|
|
11200
11263
|
});
|
|
11201
11264
|
});
|
|
11202
11265
|
}
|
|
11203
|
-
async function spawnWithSlotRotation(chatId, adapter, baseConfig, configWithSession, model2, cancelState, thinkingLevel, timeoutMs, maxTurns, opts, onSlotRotation) {
|
|
11204
|
-
const
|
|
11266
|
+
async function spawnWithSlotRotation(chatId, adapter, baseConfig, configWithSession, model2, cancelState, thinkingLevel, timeoutMs, maxTurns, rotationMode, allowPaid, opts, onSlotRotation) {
|
|
11267
|
+
const effectiveAllowPaid = rotationMode === "keys" ? true : allowPaid;
|
|
11268
|
+
const slots = getEligibleBackendSlots(adapter.id, rotationMode, effectiveAllowPaid);
|
|
11205
11269
|
if (slots.length === 0) {
|
|
11270
|
+
const allSlots = getBackendSlots(adapter.id).filter((s) => s.enabled);
|
|
11271
|
+
const paidSlots = allSlots.filter((s) => s.slotType === "api_key");
|
|
11272
|
+
if (!effectiveAllowPaid && paidSlots.length > 0) {
|
|
11273
|
+
throw new Error(`${FREE_SLOTS_EXHAUSTED}|${adapter.id}|${paidSlots.length}`);
|
|
11274
|
+
}
|
|
11206
11275
|
return spawnQuery(adapter, configWithSession, model2, cancelState, thinkingLevel, timeoutMs, maxTurns, opts);
|
|
11207
11276
|
}
|
|
11208
11277
|
const maxAttempts = slots.length;
|
|
@@ -11234,7 +11303,7 @@ async function spawnWithSlotRotation(chatId, adapter, baseConfig, configWithSess
|
|
|
11234
11303
|
clearSession(chatId);
|
|
11235
11304
|
} catch {
|
|
11236
11305
|
}
|
|
11237
|
-
const nextSlot = getEligibleBackendSlots(adapter.id)[0];
|
|
11306
|
+
const nextSlot = getEligibleBackendSlots(adapter.id, rotationMode, effectiveAllowPaid)[0];
|
|
11238
11307
|
if (nextSlot && onSlotRotation) {
|
|
11239
11308
|
const nextLabel = nextSlot.label || `slot-${nextSlot.id}`;
|
|
11240
11309
|
onSlotRotation(slotLabel, nextLabel);
|
|
@@ -11245,11 +11314,18 @@ async function spawnWithSlotRotation(chatId, adapter, baseConfig, configWithSess
|
|
|
11245
11314
|
throw err;
|
|
11246
11315
|
}
|
|
11247
11316
|
}
|
|
11317
|
+
if (!effectiveAllowPaid) {
|
|
11318
|
+
const paidSlots = getBackendSlots(adapter.id).filter((s) => s.enabled && s.slotType === "api_key");
|
|
11319
|
+
if (paidSlots.length > 0) {
|
|
11320
|
+
throw new Error(`${FREE_SLOTS_EXHAUSTED}|${adapter.id}|${paidSlots.length}`);
|
|
11321
|
+
}
|
|
11322
|
+
}
|
|
11248
11323
|
throw lastError ?? new Error(`All ${adapter.id} credential slots exhausted`);
|
|
11249
11324
|
}
|
|
11250
11325
|
async function spawnGeminiWithRotation(chatId, adapter, baseConfig, configWithSession, model2, cancelState, thinkingLevel, timeoutMs, maxTurns, rotationMode, opts, onSlotRotation, parentChatId, onModelDowngrade) {
|
|
11251
11326
|
const geminiAdapter = adapter;
|
|
11252
|
-
const
|
|
11327
|
+
const effectiveAllowPaid = rotationMode === "keys" ? true : getAllowPaidSlots(chatId, "gemini");
|
|
11328
|
+
const slots = getEligibleGeminiSlots(rotationMode, effectiveAllowPaid);
|
|
11253
11329
|
if (slots.length === 0) {
|
|
11254
11330
|
return spawnQuery(adapter, configWithSession, model2, cancelState, thinkingLevel, timeoutMs, maxTurns, opts);
|
|
11255
11331
|
}
|
|
@@ -11328,7 +11404,7 @@ async function spawnGeminiWithRotation(chatId, adapter, baseConfig, configWithSe
|
|
|
11328
11404
|
clearSession(chatId);
|
|
11329
11405
|
} catch {
|
|
11330
11406
|
}
|
|
11331
|
-
const nextSlot = getEligibleGeminiSlots(rotationMode)[0];
|
|
11407
|
+
const nextSlot = getEligibleGeminiSlots(rotationMode, effectiveAllowPaid)[0];
|
|
11332
11408
|
if (nextSlot && onSlotRotation) {
|
|
11333
11409
|
const nextLabel = nextSlot.label || `slot-${nextSlot.id}`;
|
|
11334
11410
|
onSlotRotation(slotLabel, nextLabel);
|
|
@@ -11413,17 +11489,19 @@ async function askAgentImpl(chatId, userMessage, opts) {
|
|
|
11413
11489
|
};
|
|
11414
11490
|
const resolvedModel = model2 ?? adapter.defaultModel;
|
|
11415
11491
|
let result = { resultText: "", sessionId: void 0, input: 0, output: 0, cacheRead: 0, sawToolEvents: false, sawResultEvent: false };
|
|
11416
|
-
const
|
|
11417
|
-
const
|
|
11418
|
-
const
|
|
11492
|
+
const geminiRotationMode = adapter.id === "gemini" ? getGeminiRotationMode() : "off";
|
|
11493
|
+
const backendRotationMode = adapter.id !== "gemini" ? getBackendRotationMode(adapter.id) : "off";
|
|
11494
|
+
const allowPaid = getAllowPaidSlots(chatId, adapter.id);
|
|
11495
|
+
const useGeminiRotation = geminiRotationMode !== "off" && getEligibleGeminiSlots(geminiRotationMode, allowPaid).length > 0;
|
|
11496
|
+
const useBackendRotation = adapter.id !== "gemini" && (backendRotationMode !== "off" || getBackendSlots(adapter.id).filter((s) => s.enabled).length > 0);
|
|
11419
11497
|
try {
|
|
11420
11498
|
if (useGeminiRotation) {
|
|
11421
11499
|
const rotationCb = onSlotRotation ? (from, to) => onSlotRotation(chatId, from, to) : void 0;
|
|
11422
11500
|
const downgradeCb = onModelDowngrade ? (from, to, reason) => onModelDowngrade(chatId, from, to, reason) : void 0;
|
|
11423
|
-
result = await spawnGeminiWithRotation(chatId, adapter, baseConfig, configWithSession, resolvedModel, cancelState, thinkingLevel, timeoutMs, maxTurns,
|
|
11501
|
+
result = await spawnGeminiWithRotation(chatId, adapter, baseConfig, configWithSession, resolvedModel, cancelState, thinkingLevel, timeoutMs, maxTurns, geminiRotationMode, spawnOpts, rotationCb, settingsSourceChatId, downgradeCb);
|
|
11424
11502
|
} else if (useBackendRotation) {
|
|
11425
11503
|
const rotationCb = onSlotRotation ? (from, to) => onSlotRotation(chatId, from, to) : void 0;
|
|
11426
|
-
result = await spawnWithSlotRotation(chatId, adapter, baseConfig, configWithSession, resolvedModel, cancelState, thinkingLevel, timeoutMs, maxTurns, spawnOpts, rotationCb);
|
|
11504
|
+
result = await spawnWithSlotRotation(chatId, adapter, baseConfig, configWithSession, resolvedModel, cancelState, thinkingLevel, timeoutMs, maxTurns, backendRotationMode, allowPaid, spawnOpts, rotationCb);
|
|
11427
11505
|
} else {
|
|
11428
11506
|
const slotSpawnOpts = (() => {
|
|
11429
11507
|
if (adapter.id !== "gemini") return spawnOpts;
|
|
@@ -11474,10 +11552,10 @@ async function askAgentImpl(chatId, userMessage, opts) {
|
|
|
11474
11552
|
if (useGeminiRotation) {
|
|
11475
11553
|
const rotationCb = onSlotRotation ? (from, to) => onSlotRotation(chatId, from, to) : void 0;
|
|
11476
11554
|
const downgradeCb = onModelDowngrade ? (from, to, reason) => onModelDowngrade(chatId, from, to, reason) : void 0;
|
|
11477
|
-
result = await spawnGeminiWithRotation(chatId, adapter, baseConfig, baseConfig, resolvedModel, cancelState, thinkingLevel, timeoutMs, maxTurns,
|
|
11555
|
+
result = await spawnGeminiWithRotation(chatId, adapter, baseConfig, baseConfig, resolvedModel, cancelState, thinkingLevel, timeoutMs, maxTurns, geminiRotationMode, spawnOpts, rotationCb, settingsSourceChatId, downgradeCb);
|
|
11478
11556
|
} else if (useBackendRotation) {
|
|
11479
11557
|
const rotationCb = onSlotRotation ? (from, to) => onSlotRotation(chatId, from, to) : void 0;
|
|
11480
|
-
result = await spawnWithSlotRotation(chatId, adapter, baseConfig, baseConfig, resolvedModel, cancelState, thinkingLevel, timeoutMs, maxTurns, spawnOpts, rotationCb);
|
|
11558
|
+
result = await spawnWithSlotRotation(chatId, adapter, baseConfig, baseConfig, resolvedModel, cancelState, thinkingLevel, timeoutMs, maxTurns, backendRotationMode, allowPaid, spawnOpts, rotationCb);
|
|
11481
11559
|
} else {
|
|
11482
11560
|
const retryOpts = (() => {
|
|
11483
11561
|
if (adapter.id !== "gemini") return spawnOpts;
|
|
@@ -11497,10 +11575,10 @@ async function askAgentImpl(chatId, userMessage, opts) {
|
|
|
11497
11575
|
if (useGeminiRotation) {
|
|
11498
11576
|
const rotationCb = onSlotRotation ? (from, to) => onSlotRotation(chatId, from, to) : void 0;
|
|
11499
11577
|
const downgradeCb = onModelDowngrade ? (from, to, reason) => onModelDowngrade(chatId, from, to, reason) : void 0;
|
|
11500
|
-
result = await spawnGeminiWithRotation(chatId, adapter, baseConfig, baseConfig, resolvedModel, cancelState, thinkingLevel, timeoutMs, maxTurns,
|
|
11578
|
+
result = await spawnGeminiWithRotation(chatId, adapter, baseConfig, baseConfig, resolvedModel, cancelState, thinkingLevel, timeoutMs, maxTurns, geminiRotationMode, spawnOpts, rotationCb, settingsSourceChatId, downgradeCb);
|
|
11501
11579
|
} else if (useBackendRotation) {
|
|
11502
11580
|
const rotationCb = onSlotRotation ? (from, to) => onSlotRotation(chatId, from, to) : void 0;
|
|
11503
|
-
result = await spawnWithSlotRotation(chatId, adapter, baseConfig, baseConfig, resolvedModel, cancelState, thinkingLevel, timeoutMs, maxTurns, spawnOpts, rotationCb);
|
|
11581
|
+
result = await spawnWithSlotRotation(chatId, adapter, baseConfig, baseConfig, resolvedModel, cancelState, thinkingLevel, timeoutMs, maxTurns, backendRotationMode, allowPaid, spawnOpts, rotationCb);
|
|
11504
11582
|
} else {
|
|
11505
11583
|
const retryOpts = (() => {
|
|
11506
11584
|
if (adapter.id !== "gemini") return spawnOpts;
|
|
@@ -11615,7 +11693,7 @@ function injectMcpConfig(adapterId, args, mcpConfigPath) {
|
|
|
11615
11693
|
if (!flag) return args;
|
|
11616
11694
|
return [...args, ...flag, mcpConfigPath];
|
|
11617
11695
|
}
|
|
11618
|
-
var activeChats, chatLocks, SPAWN_TIMEOUT_MS, FIRST_RESPONSE_TIMEOUT_MS, FIRST_RESPONSE_TIMEOUT_ERROR, GEMINI_FALLBACK_CHAIN, GEMINI_DOWNGRADE_MODELS, MCP_CONFIG_FLAG;
|
|
11696
|
+
var activeChats, chatLocks, SPAWN_TIMEOUT_MS, FIRST_RESPONSE_TIMEOUT_MS, FIRST_RESPONSE_TIMEOUT_ERROR, FREE_SLOTS_EXHAUSTED, GEMINI_FALLBACK_CHAIN, GEMINI_DOWNGRADE_MODELS, MCP_CONFIG_FLAG;
|
|
11619
11697
|
var init_agent = __esm({
|
|
11620
11698
|
"src/agent.ts"() {
|
|
11621
11699
|
"use strict";
|
|
@@ -11641,6 +11719,7 @@ var init_agent = __esm({
|
|
|
11641
11719
|
SPAWN_TIMEOUT_MS = 10 * 60 * 1e3;
|
|
11642
11720
|
FIRST_RESPONSE_TIMEOUT_MS = parseInt(process.env.GEMINI_FIRST_RESPONSE_TIMEOUT_MS ?? "30000", 10);
|
|
11643
11721
|
FIRST_RESPONSE_TIMEOUT_ERROR = "FIRST_RESPONSE_TIMEOUT";
|
|
11722
|
+
FREE_SLOTS_EXHAUSTED = "FREE_SLOTS_EXHAUSTED";
|
|
11644
11723
|
GEMINI_FALLBACK_CHAIN = {
|
|
11645
11724
|
"gemini-3.1-pro-preview": "gemini-2.5-pro",
|
|
11646
11725
|
"gemini-2.5-pro": "gemini-3.1-flash-preview"
|
|
@@ -14004,6 +14083,17 @@ var init_image_gen = __esm({
|
|
|
14004
14083
|
});
|
|
14005
14084
|
|
|
14006
14085
|
// src/router/response.ts
|
|
14086
|
+
var response_exports = {};
|
|
14087
|
+
__export(response_exports, {
|
|
14088
|
+
ensureReaction: () => ensureReaction,
|
|
14089
|
+
handleResponseExhaustion: () => handleResponseExhaustion,
|
|
14090
|
+
makeToolActionCallback: () => makeToolActionCallback,
|
|
14091
|
+
pendingFallbackMessages: () => pendingFallbackMessages,
|
|
14092
|
+
processFileSends: () => processFileSends2,
|
|
14093
|
+
processImageGenerations: () => processImageGenerations,
|
|
14094
|
+
processReaction: () => processReaction,
|
|
14095
|
+
sendResponse: () => sendResponse
|
|
14096
|
+
});
|
|
14007
14097
|
import { readFile as readFile3 } from "fs/promises";
|
|
14008
14098
|
function makeToolActionCallback(chatId, channel, level) {
|
|
14009
14099
|
return async (toolName, input, result) => {
|
|
@@ -17921,6 +18011,7 @@ Tap to toggle:`,
|
|
|
17921
18011
|
const exchangeCount = getMessagePairCount(chatId);
|
|
17922
18012
|
const summarized = await summarizeSession(chatId);
|
|
17923
18013
|
clearSession(chatId);
|
|
18014
|
+
clearChatPaidSlots(chatId);
|
|
17924
18015
|
setSessionStartedAt(chatId);
|
|
17925
18016
|
logActivity(getDb(), { chatId, source: "telegram", eventType: "config_changed", summary: "New session started", detail: { field: "session", action: "reset", summarized } });
|
|
17926
18017
|
if (typeof channel.sendKeyboard === "function" && oldSessionId) {
|
|
@@ -18241,6 +18332,7 @@ Add with: <code>cc-claw ${slotBackend} add-key</code>`, { parseMode: "html" });
|
|
|
18241
18332
|
break;
|
|
18242
18333
|
}
|
|
18243
18334
|
if (typeof channel.sendKeyboard === "function") {
|
|
18335
|
+
const currentMode = getBackendRotationMode(slotBackend);
|
|
18244
18336
|
const pinnedSlotId = getChatBackendSlotId(chatId, slotBackend);
|
|
18245
18337
|
const slotButtons = slots.filter((s) => s.enabled).map((s) => {
|
|
18246
18338
|
const label2 = s.label || `slot-${s.id}`;
|
|
@@ -18253,8 +18345,15 @@ Add with: <code>cc-claw ${slotBackend} add-key</code>`, { parseMode: "html" });
|
|
|
18253
18345
|
rows.push(slotButtons.slice(i, i + 2));
|
|
18254
18346
|
}
|
|
18255
18347
|
rows.push([{ label: "\u{1F504} Auto (rotation)", data: `bslot:${slotBackend}:auto` }]);
|
|
18256
|
-
|
|
18348
|
+
const modeLabels = { off: "Off", all: "All", accounts: "\u{1F468}\u{1F3FD}\u200D\u{1F4BB} Only", keys: "\u{1F511} Only" };
|
|
18349
|
+
const modeButtons = ["off", "all", "accounts", "keys"].map((m) => ({
|
|
18350
|
+
label: `${m === currentMode ? "\u2713 " : ""}${modeLabels[m]}`,
|
|
18351
|
+
data: `brotation:${slotBackend}:${m}`
|
|
18352
|
+
}));
|
|
18353
|
+
rows.push(modeButtons);
|
|
18354
|
+
await channel.sendKeyboard(chatId, `${slotDisplayName} Accounts & Rotation:`, rows);
|
|
18257
18355
|
} else {
|
|
18356
|
+
const currentMode = getBackendRotationMode(slotBackend);
|
|
18258
18357
|
const list = slots.filter((s) => s.enabled).map((s) => {
|
|
18259
18358
|
const icon = s.slotType === "oauth" ? "\u{1F468}\u{1F3FD}\u200D\u{1F4BB}" : "\u{1F511}";
|
|
18260
18359
|
return `${icon} ${s.label || `slot-${s.id}`} (#${s.id})`;
|
|
@@ -18262,6 +18361,7 @@ Add with: <code>cc-claw ${slotBackend} add-key</code>`, { parseMode: "html" });
|
|
|
18262
18361
|
await channel.sendText(chatId, `${slotDisplayName} Slots:
|
|
18263
18362
|
${list}
|
|
18264
18363
|
|
|
18364
|
+
Rotation mode: ${currentMode}
|
|
18265
18365
|
Use: /${command} <name> to pin`, { parseMode: "plain" });
|
|
18266
18366
|
}
|
|
18267
18367
|
break;
|
|
@@ -19952,6 +20052,32 @@ Result: ${task.result.slice(0, 500)}` : ""
|
|
|
19952
20052
|
const modeLabels = { off: "Off", all: "All", accounts: "\u{1F468}\u{1F3FD}\u200D\u{1F4BB} Accounts only", keys: "\u{1F511} Keys only" };
|
|
19953
20053
|
await channel.sendText(chatId, `Rotation mode set to <b>${modeLabels[mode]}</b>.`, { parseMode: "html" });
|
|
19954
20054
|
return;
|
|
20055
|
+
} else if (data.startsWith("brotation:")) {
|
|
20056
|
+
const parts = data.split(":");
|
|
20057
|
+
const backend2 = parts[1];
|
|
20058
|
+
const mode = parts[2];
|
|
20059
|
+
const validModes = ["off", "all", "accounts", "keys"];
|
|
20060
|
+
if (!validModes.includes(mode)) return;
|
|
20061
|
+
const displayName = backend2.charAt(0).toUpperCase() + backend2.slice(1);
|
|
20062
|
+
const slots = getBackendSlots(backend2);
|
|
20063
|
+
if (mode === "accounts") {
|
|
20064
|
+
const oauthSlots = slots.filter((s) => s.enabled && s.slotType === "oauth");
|
|
20065
|
+
if (oauthSlots.length === 0) {
|
|
20066
|
+
await channel.sendText(chatId, `\u26A0\uFE0F No ${displayName} OAuth accounts configured. Add one with <code>cc-claw ${backend2} add-account</code> or choose a different mode.`, { parseMode: "html" });
|
|
20067
|
+
return;
|
|
20068
|
+
}
|
|
20069
|
+
} else if (mode === "keys") {
|
|
20070
|
+
const keySlots = slots.filter((s) => s.enabled && s.slotType === "api_key");
|
|
20071
|
+
if (keySlots.length === 0) {
|
|
20072
|
+
await channel.sendText(chatId, `\u26A0\uFE0F No ${displayName} API keys configured. Add one with <code>cc-claw ${backend2} add-key</code> or choose a different mode.`, { parseMode: "html" });
|
|
20073
|
+
return;
|
|
20074
|
+
}
|
|
20075
|
+
}
|
|
20076
|
+
setBackendRotationMode(backend2, mode);
|
|
20077
|
+
clearSession(chatId);
|
|
20078
|
+
const modeLabelMap = { off: "Off", all: "All", accounts: "\u{1F468}\u{1F3FD}\u200D\u{1F4BB} Accounts only", keys: "\u{1F511} Keys only" };
|
|
20079
|
+
await channel.sendText(chatId, `${displayName} rotation mode set to <b>${modeLabelMap[mode]}</b>.`, { parseMode: "html" });
|
|
20080
|
+
return;
|
|
19955
20081
|
} else if (data.startsWith("gslot:")) {
|
|
19956
20082
|
const val = data.split(":")[1];
|
|
19957
20083
|
if (val === "auto") {
|
|
@@ -20026,6 +20152,37 @@ ${rotationNote}`, { parseMode: "html" });
|
|
|
20026
20152
|
}
|
|
20027
20153
|
}
|
|
20028
20154
|
return;
|
|
20155
|
+
} else if (data.startsWith("paidslot:")) {
|
|
20156
|
+
const parts = data.split(":");
|
|
20157
|
+
const action = parts[1];
|
|
20158
|
+
const backend2 = parts[2];
|
|
20159
|
+
const displayName = backend2.charAt(0).toUpperCase() + backend2.slice(1);
|
|
20160
|
+
if (action === "approve") {
|
|
20161
|
+
setAllowPaidSlots(chatId, backend2);
|
|
20162
|
+
await channel.sendText(chatId, `\u2705 Paid API key usage approved for ${displayName} (this session). Retrying\u2026`, { parseMode: "html" });
|
|
20163
|
+
const { pendingFallbackMessages: pendingFallbackMessages3 } = await Promise.resolve().then(() => (init_response(), response_exports));
|
|
20164
|
+
const pending = pendingFallbackMessages3.get(chatId);
|
|
20165
|
+
if (pending) {
|
|
20166
|
+
pendingFallbackMessages3.delete(chatId);
|
|
20167
|
+
const { handleMessage: handleMessage2 } = await Promise.resolve().then(() => (init_router(), router_exports));
|
|
20168
|
+
await handleMessage2(pending.msg, pending.channel);
|
|
20169
|
+
}
|
|
20170
|
+
} else if (action === "deny") {
|
|
20171
|
+
const { pendingFallbackMessages: pendingFallbackMessages3 } = await Promise.resolve().then(() => (init_response(), response_exports));
|
|
20172
|
+
pendingFallbackMessages3.delete(chatId);
|
|
20173
|
+
const otherBackends = (await Promise.resolve().then(() => (init_backends(), backends_exports))).getAvailableAdapters().filter((a) => a.id !== backend2);
|
|
20174
|
+
if (typeof channel.sendKeyboard === "function" && otherBackends.length > 0) {
|
|
20175
|
+
const buttons = otherBackends.map((a) => ({
|
|
20176
|
+
label: `\u{1F504} ${a.displayName}`,
|
|
20177
|
+
data: `backend:${a.id}`,
|
|
20178
|
+
style: "primary"
|
|
20179
|
+
}));
|
|
20180
|
+
await channel.sendKeyboard(chatId, `Switch to another backend?`, [buttons]);
|
|
20181
|
+
} else {
|
|
20182
|
+
await channel.sendText(chatId, `Paid usage denied. Try again later or switch backend manually.`, { parseMode: "plain" });
|
|
20183
|
+
}
|
|
20184
|
+
}
|
|
20185
|
+
return;
|
|
20029
20186
|
} else if (data.startsWith("shell:")) {
|
|
20030
20187
|
const parts = data.split(":");
|
|
20031
20188
|
const action = parts[1];
|
|
@@ -21399,6 +21556,33 @@ You're still in discussion mode \u2014 try again or click a button to exit.`, {
|
|
|
21399
21556
|
}
|
|
21400
21557
|
return;
|
|
21401
21558
|
}
|
|
21559
|
+
if (errMsg.startsWith(FREE_SLOTS_EXHAUSTED)) {
|
|
21560
|
+
const parts = errMsg.split("|");
|
|
21561
|
+
const backend2 = parts[1] ?? "unknown";
|
|
21562
|
+
const paidCount = parts[2] ?? "?";
|
|
21563
|
+
const displayName = backend2.charAt(0).toUpperCase() + backend2.slice(1);
|
|
21564
|
+
const { pendingFallbackMessages: pendingFallbackMessages3 } = await Promise.resolve().then(() => (init_response(), response_exports));
|
|
21565
|
+
pendingFallbackMessages3.set(chatId, { msg, channel, agentMode: effectiveAgentMode });
|
|
21566
|
+
if (typeof channel.sendKeyboard === "function") {
|
|
21567
|
+
await channel.sendKeyboard(
|
|
21568
|
+
chatId,
|
|
21569
|
+
`\u26A0\uFE0F All free ${displayName} accounts are exhausted.
|
|
21570
|
+
|
|
21571
|
+
${paidCount} paid API key slot(s) available. Using them will incur API costs.
|
|
21572
|
+
|
|
21573
|
+
Approve paid usage for this session?`,
|
|
21574
|
+
[
|
|
21575
|
+
[
|
|
21576
|
+
{ label: "\u2705 Approve (this session)", data: `paidslot:approve:${backend2}`, style: "success" },
|
|
21577
|
+
{ label: "\u274C No, switch backend", data: `paidslot:deny:${backend2}`, style: "danger" }
|
|
21578
|
+
]
|
|
21579
|
+
]
|
|
21580
|
+
);
|
|
21581
|
+
} else {
|
|
21582
|
+
await channel.sendText(chatId, `\u26A0\uFE0F All free ${displayName} accounts exhausted. ${paidCount} paid API key slot(s) available but not approved. Switch backend or approve paid usage.`, { parseMode: "plain" });
|
|
21583
|
+
}
|
|
21584
|
+
return;
|
|
21585
|
+
}
|
|
21402
21586
|
const errorClass = classifyError(err);
|
|
21403
21587
|
if (errorClass === "exhausted") {
|
|
21404
21588
|
if (await handleResponseExhaustion(errMsg, chatId, msg, channel)) return;
|
|
@@ -21742,6 +21926,10 @@ async function runWithRetry(job, model2, runId, t0) {
|
|
|
21742
21926
|
onToolAction = makeToolActionCallback2(chatId, channel, vLevel);
|
|
21743
21927
|
}
|
|
21744
21928
|
}
|
|
21929
|
+
if (job.allowPaidSlots) {
|
|
21930
|
+
const cronBackend = currentBackend;
|
|
21931
|
+
setAllowPaidSlots(chatId, cronBackend);
|
|
21932
|
+
}
|
|
21745
21933
|
const response = await askAgent(chatId, job.description, {
|
|
21746
21934
|
model: currentModel,
|
|
21747
21935
|
backend: currentBackend,
|
|
@@ -21750,6 +21938,9 @@ async function runWithRetry(job, model2, runId, t0) {
|
|
|
21750
21938
|
permMode: job.sessionType === "main" ? getMode(job.chatId) : "yolo",
|
|
21751
21939
|
onToolAction
|
|
21752
21940
|
});
|
|
21941
|
+
if (job.allowPaidSlots) {
|
|
21942
|
+
clearChatPaidSlots(chatId);
|
|
21943
|
+
}
|
|
21753
21944
|
if (isFallback) {
|
|
21754
21945
|
response.text = `[Fallback: ran on ${currentBackend}:${currentModel}]
|
|
21755
21946
|
|
|
@@ -21759,8 +21950,13 @@ ${response.text}`;
|
|
|
21759
21950
|
} catch (err) {
|
|
21760
21951
|
lastError = err;
|
|
21761
21952
|
const errorClass = classifyError(err);
|
|
21953
|
+
const errMsg = errorMessage(err);
|
|
21954
|
+
if (typeof errMsg === "string" && errMsg.startsWith(FREE_SLOTS_EXHAUSTED)) {
|
|
21955
|
+
log(`[scheduler] Job #${job.id} backend ${currentBackend} free slots exhausted (paid not approved): ${errMsg}`);
|
|
21956
|
+
break;
|
|
21957
|
+
}
|
|
21762
21958
|
if (errorClass === "exhausted") {
|
|
21763
|
-
log(`[scheduler] Job #${job.id} backend ${currentBackend} exhausted: ${
|
|
21959
|
+
log(`[scheduler] Job #${job.id} backend ${currentBackend} exhausted: ${errMsg}`);
|
|
21764
21960
|
break;
|
|
21765
21961
|
}
|
|
21766
21962
|
if (errorClass === "permanent" || attempt >= MAX_RETRIES) {
|
|
@@ -23814,6 +24010,7 @@ async function main() {
|
|
|
23814
24010
|
}
|
|
23815
24011
|
log(`[cc-claw] Starting v${version}`);
|
|
23816
24012
|
initDatabase();
|
|
24013
|
+
clearAllPaidSlots();
|
|
23817
24014
|
pruneMessageLog(30, 2e3);
|
|
23818
24015
|
bootstrapBuiltinMcps(getDb());
|
|
23819
24016
|
try {
|
package/package.json
CHANGED