cc-claw 0.29.2 → 0.30.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/cli.js +1464 -454
- 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.
|
|
36
|
+
VERSION = true ? "0.30.0" : (() => {
|
|
37
37
|
try {
|
|
38
38
|
return JSON.parse(readFileSync(join(process.cwd(), "package.json"), "utf-8")).version ?? "unknown";
|
|
39
39
|
} catch {
|
|
@@ -806,6 +806,7 @@ __export(store_exports4, {
|
|
|
806
806
|
getRejectedInsights: () => getRejectedInsights,
|
|
807
807
|
getReviewSession: () => getReviewSession,
|
|
808
808
|
getSignalCountForChat: () => getSignalCountForChat,
|
|
809
|
+
getSkillCorrections: () => getSkillCorrections,
|
|
809
810
|
getUnprocessedSignalCount: () => getUnprocessedSignalCount,
|
|
810
811
|
getUnprocessedSignals: () => getUnprocessedSignals,
|
|
811
812
|
initReflectionTables: () => initReflectionTables,
|
|
@@ -872,11 +873,13 @@ function initReflectionTables(db3) {
|
|
|
872
873
|
backend TEXT,
|
|
873
874
|
model TEXT,
|
|
874
875
|
thinkingLevel TEXT,
|
|
876
|
+
skill_name TEXT,
|
|
875
877
|
processedAt TEXT,
|
|
876
878
|
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
877
879
|
);
|
|
878
880
|
CREATE INDEX IF NOT EXISTS idx_signals_chat_time ON feedback_signals (chatId, created_at DESC);
|
|
879
881
|
CREATE INDEX IF NOT EXISTS idx_signals_unprocessed ON feedback_signals (processedAt) WHERE processedAt IS NULL;
|
|
882
|
+
CREATE INDEX IF NOT EXISTS idx_signals_skill ON feedback_signals (skill_name) WHERE skill_name IS NOT NULL;
|
|
880
883
|
|
|
881
884
|
CREATE TABLE IF NOT EXISTS insights (
|
|
882
885
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
@@ -949,11 +952,17 @@ function initReflectionTables(db3) {
|
|
|
949
952
|
cleanupReflectionData(db3);
|
|
950
953
|
const settings = getReflectionSettings(db3, "global");
|
|
951
954
|
cleanupBackupFiles(CC_CLAW_HOME, settings.backupRetentionDays);
|
|
955
|
+
try {
|
|
956
|
+
db3.prepare("SELECT skill_name FROM feedback_signals LIMIT 0").get();
|
|
957
|
+
} catch {
|
|
958
|
+
db3.exec("ALTER TABLE feedback_signals ADD COLUMN skill_name TEXT");
|
|
959
|
+
db3.exec("CREATE INDEX IF NOT EXISTS idx_signals_skill ON feedback_signals (skill_name) WHERE skill_name IS NOT NULL");
|
|
960
|
+
}
|
|
952
961
|
}
|
|
953
962
|
function logSignal(db3, params) {
|
|
954
963
|
const result = db3.prepare(`
|
|
955
|
-
INSERT INTO feedback_signals (chatId, signalType, trigger, context, source, confidence, backend, model, thinkingLevel)
|
|
956
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
964
|
+
INSERT INTO feedback_signals (chatId, signalType, trigger, context, source, confidence, backend, model, thinkingLevel, skill_name)
|
|
965
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
957
966
|
`).run(
|
|
958
967
|
params.chatId,
|
|
959
968
|
params.signalType,
|
|
@@ -963,7 +972,8 @@ function logSignal(db3, params) {
|
|
|
963
972
|
params.confidence,
|
|
964
973
|
params.backend ?? null,
|
|
965
974
|
params.model ?? null,
|
|
966
|
-
params.thinkingLevel ?? null
|
|
975
|
+
params.thinkingLevel ?? null,
|
|
976
|
+
params.skillName ?? null
|
|
967
977
|
);
|
|
968
978
|
return Number(result.lastInsertRowid);
|
|
969
979
|
}
|
|
@@ -1004,6 +1014,56 @@ function getLastAnalysisTime(db3, chatId) {
|
|
|
1004
1014
|
`).get(chatId);
|
|
1005
1015
|
return row.lastTime;
|
|
1006
1016
|
}
|
|
1017
|
+
function getSkillCorrections(db3, windowDays = 30, minCorrections = 3) {
|
|
1018
|
+
const skillRows = db3.prepare(`
|
|
1019
|
+
SELECT
|
|
1020
|
+
skill_name,
|
|
1021
|
+
COUNT(*) AS cnt,
|
|
1022
|
+
GROUP_CONCAT(context, '|||') AS contexts_raw
|
|
1023
|
+
FROM feedback_signals
|
|
1024
|
+
WHERE
|
|
1025
|
+
signalType = 'correction'
|
|
1026
|
+
AND skill_name IS NOT NULL
|
|
1027
|
+
AND created_at >= datetime('now', '-' || ? || ' days')
|
|
1028
|
+
GROUP BY skill_name
|
|
1029
|
+
HAVING cnt >= ?
|
|
1030
|
+
ORDER BY cnt DESC
|
|
1031
|
+
`).all(windowDays, minCorrections);
|
|
1032
|
+
if (skillRows.length === 0) return [];
|
|
1033
|
+
const skillNames = skillRows.map((r) => r.skill_name);
|
|
1034
|
+
const placeholders = skillNames.map(() => "?").join(", ");
|
|
1035
|
+
const breakdownRows = db3.prepare(`
|
|
1036
|
+
SELECT
|
|
1037
|
+
skill_name,
|
|
1038
|
+
model,
|
|
1039
|
+
COUNT(*) AS cnt
|
|
1040
|
+
FROM feedback_signals
|
|
1041
|
+
WHERE
|
|
1042
|
+
signalType = 'correction'
|
|
1043
|
+
AND skill_name IN (${placeholders})
|
|
1044
|
+
AND model IS NOT NULL
|
|
1045
|
+
AND created_at >= datetime('now', '-' || ? || ' days')
|
|
1046
|
+
GROUP BY skill_name, model
|
|
1047
|
+
ORDER BY skill_name, cnt DESC
|
|
1048
|
+
`).all(...skillNames, windowDays);
|
|
1049
|
+
const breakdownMap = /* @__PURE__ */ new Map();
|
|
1050
|
+
for (const row of breakdownRows) {
|
|
1051
|
+
if (!breakdownMap.has(row.skill_name)) {
|
|
1052
|
+
breakdownMap.set(row.skill_name, []);
|
|
1053
|
+
}
|
|
1054
|
+
breakdownMap.get(row.skill_name).push({ model: row.model, count: row.cnt });
|
|
1055
|
+
}
|
|
1056
|
+
return skillRows.map((row) => {
|
|
1057
|
+
const rawContexts = row.contexts_raw ? row.contexts_raw.split("|||") : [];
|
|
1058
|
+
const contexts = rawContexts.filter((c) => c && c.trim().length > 0);
|
|
1059
|
+
return {
|
|
1060
|
+
skillName: row.skill_name,
|
|
1061
|
+
correctionCount: row.cnt,
|
|
1062
|
+
contexts,
|
|
1063
|
+
modelBreakdown: breakdownMap.get(row.skill_name) ?? []
|
|
1064
|
+
};
|
|
1065
|
+
});
|
|
1066
|
+
}
|
|
1007
1067
|
function getReflectionStatus(db3, chatId) {
|
|
1008
1068
|
const row = db3.prepare("SELECT status FROM chat_reflection WHERE chatId = ?").get(chatId);
|
|
1009
1069
|
return row?.status ?? "frozen";
|
|
@@ -2009,6 +2069,12 @@ function initSchema(db3) {
|
|
|
2009
2069
|
value INTEGER NOT NULL DEFAULT 0
|
|
2010
2070
|
);
|
|
2011
2071
|
`);
|
|
2072
|
+
db3.exec(`
|
|
2073
|
+
CREATE TABLE IF NOT EXISTS chat_pinned_skills (
|
|
2074
|
+
chat_id TEXT PRIMARY KEY,
|
|
2075
|
+
skills TEXT NOT NULL DEFAULT '[]'
|
|
2076
|
+
);
|
|
2077
|
+
`);
|
|
2012
2078
|
db3.exec(`
|
|
2013
2079
|
CREATE TABLE IF NOT EXISTS backend_credentials (
|
|
2014
2080
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
@@ -2091,17 +2157,48 @@ function applySalienceDecay(db3) {
|
|
|
2091
2157
|
db3.prepare("DELETE FROM session_summaries WHERE salience < 0.1").run();
|
|
2092
2158
|
db3.prepare("INSERT OR REPLACE INTO meta (key, value) VALUES ('last_salience_decay', ?)").run(now);
|
|
2093
2159
|
}
|
|
2160
|
+
function _setSchemaDbGetter(getter) {
|
|
2161
|
+
_getDb = getter;
|
|
2162
|
+
}
|
|
2094
2163
|
function getMetaValue(key) {
|
|
2095
|
-
|
|
2096
|
-
const db3 =
|
|
2164
|
+
if (!_getDb) throw new Error("[schema] DB getter not initialized \u2014 was _setSchemaDbGetter() called?");
|
|
2165
|
+
const db3 = _getDb();
|
|
2097
2166
|
const row = db3.prepare("SELECT value FROM meta WHERE key = ?").get(key);
|
|
2098
2167
|
return row?.value;
|
|
2099
2168
|
}
|
|
2100
2169
|
function setMetaValue(key, value) {
|
|
2101
|
-
|
|
2102
|
-
const db3 =
|
|
2170
|
+
if (!_getDb) throw new Error("[schema] DB getter not initialized \u2014 was _setSchemaDbGetter() called?");
|
|
2171
|
+
const db3 = _getDb();
|
|
2103
2172
|
db3.prepare("INSERT OR REPLACE INTO meta (key, value) VALUES (?, ?)").run(key, value);
|
|
2104
2173
|
}
|
|
2174
|
+
function getDisabledBackends() {
|
|
2175
|
+
if (_disabledCache) return _disabledCache;
|
|
2176
|
+
const json = getMetaValue(DISABLED_BACKENDS_KEY);
|
|
2177
|
+
if (!json) {
|
|
2178
|
+
_disabledCache = [];
|
|
2179
|
+
return _disabledCache;
|
|
2180
|
+
}
|
|
2181
|
+
try {
|
|
2182
|
+
_disabledCache = JSON.parse(json);
|
|
2183
|
+
return _disabledCache;
|
|
2184
|
+
} catch {
|
|
2185
|
+
_disabledCache = [];
|
|
2186
|
+
return _disabledCache;
|
|
2187
|
+
}
|
|
2188
|
+
}
|
|
2189
|
+
function setBackendDisabled(backendId, disabled) {
|
|
2190
|
+
const current = [...getDisabledBackends()];
|
|
2191
|
+
if (disabled) {
|
|
2192
|
+
if (!current.includes(backendId)) {
|
|
2193
|
+
current.push(backendId);
|
|
2194
|
+
}
|
|
2195
|
+
} else {
|
|
2196
|
+
const idx = current.indexOf(backendId);
|
|
2197
|
+
if (idx !== -1) current.splice(idx, 1);
|
|
2198
|
+
}
|
|
2199
|
+
setMetaValue(DISABLED_BACKENDS_KEY, JSON.stringify(current));
|
|
2200
|
+
_disabledCache = current;
|
|
2201
|
+
}
|
|
2105
2202
|
function backfillJobTitles(database) {
|
|
2106
2203
|
const rows = database.prepare(
|
|
2107
2204
|
"SELECT id, description FROM jobs WHERE active = 1 AND (title IS NULL OR title LIKE 'No thinking%' OR title LIKE 'You must%' OR title LIKE 'You MUST%')"
|
|
@@ -2125,6 +2222,7 @@ function backfillJobTitles(database) {
|
|
|
2125
2222
|
database.prepare("UPDATE jobs SET title = ? WHERE id = ?").run(title, row.id);
|
|
2126
2223
|
}
|
|
2127
2224
|
}
|
|
2225
|
+
var _getDb, DISABLED_BACKENDS_KEY, _disabledCache;
|
|
2128
2226
|
var init_schema = __esm({
|
|
2129
2227
|
"src/memory/schema.ts"() {
|
|
2130
2228
|
"use strict";
|
|
@@ -2135,6 +2233,9 @@ var init_schema = __esm({
|
|
|
2135
2233
|
init_store4();
|
|
2136
2234
|
init_log();
|
|
2137
2235
|
init_classify();
|
|
2236
|
+
_getDb = null;
|
|
2237
|
+
DISABLED_BACKENDS_KEY = "disabled_backends";
|
|
2238
|
+
_disabledCache = null;
|
|
2138
2239
|
}
|
|
2139
2240
|
});
|
|
2140
2241
|
|
|
@@ -2813,13 +2914,16 @@ __export(chat_settings_exports, {
|
|
|
2813
2914
|
GLOBAL_SUMMARIZER_SENTINEL: () => GLOBAL_SUMMARIZER_SENTINEL,
|
|
2814
2915
|
clearAgentMode: () => clearAgentMode,
|
|
2815
2916
|
clearAllPaidSlots: () => clearAllPaidSlots,
|
|
2917
|
+
clearAllSummarizerOverrides: () => clearAllSummarizerOverrides,
|
|
2816
2918
|
clearChatPaidSlots: () => clearChatPaidSlots,
|
|
2817
2919
|
clearCwd: () => clearCwd,
|
|
2818
2920
|
clearExecMode: () => clearExecMode,
|
|
2819
2921
|
clearModel: () => clearModel,
|
|
2820
2922
|
clearModelMap: () => clearModelMap,
|
|
2923
|
+
clearPinnedSkills: () => clearPinnedSkills,
|
|
2821
2924
|
clearSummarizer: () => clearSummarizer,
|
|
2822
2925
|
clearThinkingLevel: () => clearThinkingLevel,
|
|
2926
|
+
countSummarizerOverrides: () => countSummarizerOverrides,
|
|
2823
2927
|
deleteBookmark: () => deleteBookmark,
|
|
2824
2928
|
determineEscalationTarget: () => determineEscalationTarget,
|
|
2825
2929
|
findBookmarksByPrefix: () => findBookmarksByPrefix,
|
|
@@ -2835,11 +2939,13 @@ __export(chat_settings_exports, {
|
|
|
2835
2939
|
getMode: () => getMode,
|
|
2836
2940
|
getModel: () => getModel,
|
|
2837
2941
|
getPendingEscalation: () => getPendingEscalation,
|
|
2942
|
+
getPinnedSkills: () => getPinnedSkills,
|
|
2838
2943
|
getRecentBookmarks: () => getRecentBookmarks,
|
|
2839
2944
|
getSessionLogEnabled: () => getSessionLogEnabled,
|
|
2840
2945
|
getShowThinkingUi: () => getShowThinkingUi,
|
|
2841
2946
|
getSkillSuggestionsEnabled: () => getSkillSuggestionsEnabled,
|
|
2842
2947
|
getSummarizer: () => getSummarizer,
|
|
2948
|
+
getSummarizerWithSource: () => getSummarizerWithSource,
|
|
2843
2949
|
getThinkingLevel: () => getThinkingLevel,
|
|
2844
2950
|
getToolsMap: () => getToolsMap,
|
|
2845
2951
|
getVerboseLevel: () => getVerboseLevel,
|
|
@@ -2853,6 +2959,7 @@ __export(chat_settings_exports, {
|
|
|
2853
2959
|
setGlobalSummarizer: () => setGlobalSummarizer,
|
|
2854
2960
|
setMode: () => setMode,
|
|
2855
2961
|
setModel: () => setModel,
|
|
2962
|
+
setPinnedSkills: () => setPinnedSkills,
|
|
2856
2963
|
setSessionLogEnabled: () => setSessionLogEnabled,
|
|
2857
2964
|
setShowThinkingUi: () => setShowThinkingUi,
|
|
2858
2965
|
setSkillSuggestionsEnabled: () => setSkillSuggestionsEnabled,
|
|
@@ -3184,6 +3291,35 @@ function setSummarizer(chatId, backend2, model2) {
|
|
|
3184
3291
|
function clearSummarizer(chatId) {
|
|
3185
3292
|
getDb().prepare("DELETE FROM chat_summarizer WHERE chat_id = ?").run(chatId);
|
|
3186
3293
|
}
|
|
3294
|
+
function clearAllSummarizerOverrides() {
|
|
3295
|
+
const result = getDb().prepare(
|
|
3296
|
+
"DELETE FROM chat_summarizer WHERE chat_id != ?"
|
|
3297
|
+
).run(GLOBAL_SUMMARIZER_SENTINEL);
|
|
3298
|
+
return result.changes;
|
|
3299
|
+
}
|
|
3300
|
+
function getSummarizerWithSource(chatId) {
|
|
3301
|
+
const perChat = getDb().prepare(
|
|
3302
|
+
"SELECT backend, model FROM chat_summarizer WHERE chat_id = ?"
|
|
3303
|
+
).get(chatId);
|
|
3304
|
+
const globalRow = getDb().prepare(
|
|
3305
|
+
"SELECT backend, model FROM chat_summarizer WHERE chat_id = ?"
|
|
3306
|
+
).get(GLOBAL_SUMMARIZER_SENTINEL);
|
|
3307
|
+
const hasPerChat = perChat && (perChat.backend || perChat.model);
|
|
3308
|
+
const globalConfig = globalRow && (globalRow.backend || globalRow.model) ? globalRow : { backend: null, model: null };
|
|
3309
|
+
if (hasPerChat) {
|
|
3310
|
+
return { config: perChat, source: "per-chat", globalConfig };
|
|
3311
|
+
}
|
|
3312
|
+
if (globalConfig.backend || globalConfig.model) {
|
|
3313
|
+
return { config: globalConfig, source: "global", globalConfig };
|
|
3314
|
+
}
|
|
3315
|
+
return { config: { backend: null, model: null }, source: "auto", globalConfig };
|
|
3316
|
+
}
|
|
3317
|
+
function countSummarizerOverrides() {
|
|
3318
|
+
const row = getDb().prepare(
|
|
3319
|
+
"SELECT COUNT(*) as cnt FROM chat_summarizer WHERE chat_id != ?"
|
|
3320
|
+
).get(GLOBAL_SUMMARIZER_SENTINEL);
|
|
3321
|
+
return row.cnt;
|
|
3322
|
+
}
|
|
3187
3323
|
function getAgentMode(chatId) {
|
|
3188
3324
|
const row = getDb().prepare("SELECT mode FROM chat_agent_mode WHERE chat_id = ?").get(chatId);
|
|
3189
3325
|
return row?.mode ?? "auto";
|
|
@@ -3233,6 +3369,27 @@ function toggleSessionLogEnabled(chatId) {
|
|
|
3233
3369
|
setSessionLogEnabled(chatId, next);
|
|
3234
3370
|
return next;
|
|
3235
3371
|
}
|
|
3372
|
+
function getPinnedSkills(chatId) {
|
|
3373
|
+
const row = getDb().prepare(
|
|
3374
|
+
"SELECT skills FROM chat_pinned_skills WHERE chat_id = ?"
|
|
3375
|
+
).get(chatId);
|
|
3376
|
+
if (!row?.skills) return [];
|
|
3377
|
+
try {
|
|
3378
|
+
return JSON.parse(row.skills);
|
|
3379
|
+
} catch {
|
|
3380
|
+
return [];
|
|
3381
|
+
}
|
|
3382
|
+
}
|
|
3383
|
+
function setPinnedSkills(chatId, skills2) {
|
|
3384
|
+
getDb().prepare(`
|
|
3385
|
+
INSERT INTO chat_pinned_skills (chat_id, skills)
|
|
3386
|
+
VALUES (?, ?)
|
|
3387
|
+
ON CONFLICT(chat_id) DO UPDATE SET skills = ?
|
|
3388
|
+
`).run(chatId, JSON.stringify(skills2), JSON.stringify(skills2));
|
|
3389
|
+
}
|
|
3390
|
+
function clearPinnedSkills(chatId) {
|
|
3391
|
+
getDb().prepare("DELETE FROM chat_pinned_skills WHERE chat_id = ?").run(chatId);
|
|
3392
|
+
}
|
|
3236
3393
|
function getAllowPaidSlots(chatId, backend2) {
|
|
3237
3394
|
const row = getDb().prepare(
|
|
3238
3395
|
"SELECT 1 FROM chat_allow_paid_slots WHERE chat_id = ? AND backend = ?"
|
|
@@ -3325,7 +3482,7 @@ function getUsage(chatId) {
|
|
|
3325
3482
|
}
|
|
3326
3483
|
function addUsage(chatId, input, output2, cacheRead, model2, backend2, contextSize) {
|
|
3327
3484
|
const db3 = getDb();
|
|
3328
|
-
const finalContextSize = contextSize ?? input + cacheRead;
|
|
3485
|
+
const finalContextSize = contextSize === null ? 0 : contextSize ?? input + cacheRead;
|
|
3329
3486
|
db3.prepare(`
|
|
3330
3487
|
INSERT INTO chat_usage (chat_id, input_tokens, output_tokens, cache_read_tokens, request_count, last_input_tokens, last_cache_read_tokens, context_size, updated_at)
|
|
3331
3488
|
VALUES (?, ?, ?, ?, 1, ?, ?, ?, datetime('now'))
|
|
@@ -3616,6 +3773,7 @@ __export(jobs_exports, {
|
|
|
3616
3773
|
cancelJobById: () => cancelJobById,
|
|
3617
3774
|
completeJobRun: () => completeJobRun,
|
|
3618
3775
|
getActiveJobs: () => getActiveJobs,
|
|
3776
|
+
getActiveJobsByBackend: () => getActiveJobsByBackend,
|
|
3619
3777
|
getAllJobs: () => getAllJobs,
|
|
3620
3778
|
getJobById: () => getJobById,
|
|
3621
3779
|
getJobRuns: () => getJobRuns,
|
|
@@ -3623,6 +3781,7 @@ __export(jobs_exports, {
|
|
|
3623
3781
|
insertJob: () => insertJob,
|
|
3624
3782
|
insertJobRun: () => insertJobRun,
|
|
3625
3783
|
pruneJobRuns: () => pruneJobRuns,
|
|
3784
|
+
reassignJobsBackend: () => reassignJobsBackend,
|
|
3626
3785
|
resetJobFailures: () => resetJobFailures,
|
|
3627
3786
|
updateJob: () => updateJob,
|
|
3628
3787
|
updateJobEnabled: () => updateJobEnabled,
|
|
@@ -3679,6 +3838,15 @@ function getActiveJobs() {
|
|
|
3679
3838
|
function getAllJobs() {
|
|
3680
3839
|
return getDb().prepare(`${JOB_SELECT} WHERE active = 1 ORDER BY id`).all().map((r) => mapJobRow(r));
|
|
3681
3840
|
}
|
|
3841
|
+
function getActiveJobsByBackend(backendId) {
|
|
3842
|
+
return getDb().prepare(`${JOB_SELECT} WHERE active = 1 AND enabled = 1 AND backend = ?`).all(backendId).map((r) => mapJobRow(r));
|
|
3843
|
+
}
|
|
3844
|
+
function reassignJobsBackend(fromBackend, toBackend) {
|
|
3845
|
+
const result = getDb().prepare(
|
|
3846
|
+
"UPDATE jobs SET backend = ? WHERE active = 1 AND enabled = 1 AND backend = ?"
|
|
3847
|
+
).run(toBackend, fromBackend);
|
|
3848
|
+
return result.changes;
|
|
3849
|
+
}
|
|
3682
3850
|
function updateJobEnabled(id, enabled) {
|
|
3683
3851
|
const result = getDb().prepare("UPDATE jobs SET enabled = ? WHERE id = ?").run(enabled ? 1 : 0, id);
|
|
3684
3852
|
return result.changes > 0;
|
|
@@ -4593,6 +4761,12 @@ var init_session_log = __esm({
|
|
|
4593
4761
|
});
|
|
4594
4762
|
|
|
4595
4763
|
// src/memory/api-context.ts
|
|
4764
|
+
var api_context_exports = {};
|
|
4765
|
+
__export(api_context_exports, {
|
|
4766
|
+
buildApiMessages: () => buildApiMessages,
|
|
4767
|
+
estimateContextUsage: () => estimateContextUsage,
|
|
4768
|
+
estimateTokens: () => estimateTokens
|
|
4769
|
+
});
|
|
4596
4770
|
import { getEncoding } from "js-tiktoken";
|
|
4597
4771
|
function estimateTokens(text) {
|
|
4598
4772
|
return enc.encode(text).length;
|
|
@@ -4621,7 +4795,7 @@ async function buildApiMessages(chatId, userMessage, systemPrompt, contextWindow
|
|
|
4621
4795
|
return { role: "assistant", content: entry.text };
|
|
4622
4796
|
});
|
|
4623
4797
|
const currentUserMessage = { role: "user", content: userMessage };
|
|
4624
|
-
const tokenBudget = Math.floor(contextWindow * 0.
|
|
4798
|
+
const tokenBudget = Math.floor(contextWindow * 0.95);
|
|
4625
4799
|
const fixedMessages = [systemMessage, currentUserMessage];
|
|
4626
4800
|
const fixedTokens = fixedMessages.reduce((sum, m) => sum + enc.encode(typeof m.content === "string" ? m.content : JSON.stringify(m.content)).length, 0);
|
|
4627
4801
|
const historyBudget = tokenBudget - fixedTokens;
|
|
@@ -4637,12 +4811,30 @@ async function buildApiMessages(chatId, userMessage, systemPrompt, contextWindow
|
|
|
4637
4811
|
}
|
|
4638
4812
|
return [systemMessage, ...truncatedHistory, currentUserMessage];
|
|
4639
4813
|
}
|
|
4640
|
-
|
|
4814
|
+
function estimateContextUsage(chatId, contextWindow) {
|
|
4815
|
+
const logEntries = getLog(chatId);
|
|
4816
|
+
if (logEntries.length === 0) {
|
|
4817
|
+
estimateCache.delete(chatId);
|
|
4818
|
+
return { estimatedTokens: 0, contextWindow, percentage: 0 };
|
|
4819
|
+
}
|
|
4820
|
+
const cached = estimateCache.get(chatId);
|
|
4821
|
+
if (cached && cached.logSize === logEntries.length) {
|
|
4822
|
+
const percentage2 = contextWindow > 0 ? cached.tokens / contextWindow * 100 : 0;
|
|
4823
|
+
return { estimatedTokens: cached.tokens, contextWindow, percentage: percentage2 };
|
|
4824
|
+
}
|
|
4825
|
+
const text = logEntries.map((e) => e.text).join("\n");
|
|
4826
|
+
const estimatedTokens = estimateTokens(text);
|
|
4827
|
+
estimateCache.set(chatId, { logSize: logEntries.length, tokens: estimatedTokens });
|
|
4828
|
+
const percentage = contextWindow > 0 ? estimatedTokens / contextWindow * 100 : 0;
|
|
4829
|
+
return { estimatedTokens, contextWindow, percentage };
|
|
4830
|
+
}
|
|
4831
|
+
var enc, estimateCache;
|
|
4641
4832
|
var init_api_context = __esm({
|
|
4642
4833
|
"src/memory/api-context.ts"() {
|
|
4643
4834
|
"use strict";
|
|
4644
4835
|
init_session_log();
|
|
4645
4836
|
enc = getEncoding("cl100k_base");
|
|
4837
|
+
estimateCache = /* @__PURE__ */ new Map();
|
|
4646
4838
|
}
|
|
4647
4839
|
});
|
|
4648
4840
|
|
|
@@ -4838,6 +5030,7 @@ __export(store_exports5, {
|
|
|
4838
5030
|
clearAgentMode: () => clearAgentMode,
|
|
4839
5031
|
clearAllPaidSlots: () => clearAllPaidSlots,
|
|
4840
5032
|
clearAllSessions: () => clearAllSessions,
|
|
5033
|
+
clearAllSummarizerOverrides: () => clearAllSummarizerOverrides,
|
|
4841
5034
|
clearBackendLimit: () => clearBackendLimit,
|
|
4842
5035
|
clearChatBackendSlot: () => clearChatBackendSlot,
|
|
4843
5036
|
clearChatGeminiSlot: () => clearChatGeminiSlot,
|
|
@@ -4847,11 +5040,13 @@ __export(store_exports5, {
|
|
|
4847
5040
|
clearMessageLog: () => clearMessageLog,
|
|
4848
5041
|
clearModel: () => clearModel,
|
|
4849
5042
|
clearModelMap: () => clearModelMap,
|
|
5043
|
+
clearPinnedSkills: () => clearPinnedSkills,
|
|
4850
5044
|
clearSession: () => clearSession,
|
|
4851
5045
|
clearSummarizer: () => clearSummarizer,
|
|
4852
5046
|
clearThinkingLevel: () => clearThinkingLevel,
|
|
4853
5047
|
clearUsage: () => clearUsage,
|
|
4854
5048
|
completeJobRun: () => completeJobRun,
|
|
5049
|
+
countSummarizerOverrides: () => countSummarizerOverrides,
|
|
4855
5050
|
deleteBookmark: () => deleteBookmark,
|
|
4856
5051
|
deleteMemoryById: () => deleteMemoryById,
|
|
4857
5052
|
deleteSessionSummary: () => deleteSessionSummary,
|
|
@@ -4864,6 +5059,7 @@ __export(store_exports5, {
|
|
|
4864
5059
|
forget: () => forget,
|
|
4865
5060
|
forgetMemory: () => forgetMemory,
|
|
4866
5061
|
getActiveJobs: () => getActiveJobs,
|
|
5062
|
+
getActiveJobsByBackend: () => getActiveJobsByBackend,
|
|
4867
5063
|
getActiveWatches: () => getActiveWatches,
|
|
4868
5064
|
getAgentMode: () => getAgentMode,
|
|
4869
5065
|
getAllBackendLimits: () => getAllBackendLimits,
|
|
@@ -4890,6 +5086,7 @@ __export(store_exports5, {
|
|
|
4890
5086
|
getChatUsageByModel: () => getChatUsageByModel,
|
|
4891
5087
|
getCwd: () => getCwd,
|
|
4892
5088
|
getDb: () => getDb,
|
|
5089
|
+
getDisabledBackends: () => getDisabledBackends,
|
|
4893
5090
|
getEligibleBackendSlots: () => getEligibleBackendSlots,
|
|
4894
5091
|
getEligibleGeminiSlots: () => getEligibleGeminiSlots,
|
|
4895
5092
|
getEnabledTools: () => getEnabledTools,
|
|
@@ -4911,6 +5108,7 @@ __export(store_exports5, {
|
|
|
4911
5108
|
getNextBackendSlot: () => getNextBackendSlot,
|
|
4912
5109
|
getNextGeminiSlot: () => getNextGeminiSlot,
|
|
4913
5110
|
getPendingEscalation: () => getPendingEscalation,
|
|
5111
|
+
getPinnedSkills: () => getPinnedSkills,
|
|
4914
5112
|
getRecentBookmarks: () => getRecentBookmarks,
|
|
4915
5113
|
getRecentMemories: () => getRecentMemories,
|
|
4916
5114
|
getRecentMessageLog: () => getRecentMessageLog,
|
|
@@ -4923,6 +5121,7 @@ __export(store_exports5, {
|
|
|
4923
5121
|
getShowThinkingUi: () => getShowThinkingUi,
|
|
4924
5122
|
getSkillSuggestionsEnabled: () => getSkillSuggestionsEnabled,
|
|
4925
5123
|
getSummarizer: () => getSummarizer,
|
|
5124
|
+
getSummarizerWithSource: () => getSummarizerWithSource,
|
|
4926
5125
|
getThinkingLevel: () => getThinkingLevel,
|
|
4927
5126
|
getToolsMap: () => getToolsMap,
|
|
4928
5127
|
getUnsummarizedChatIds: () => getUnsummarizedChatIds,
|
|
@@ -4951,6 +5150,7 @@ __export(store_exports5, {
|
|
|
4951
5150
|
pruneJobRuns: () => pruneJobRuns,
|
|
4952
5151
|
pruneMessageLog: () => pruneMessageLog,
|
|
4953
5152
|
queueHalfLifeExtension: () => queueHalfLifeExtension,
|
|
5153
|
+
reassignJobsBackend: () => reassignJobsBackend,
|
|
4954
5154
|
recall: () => recall,
|
|
4955
5155
|
reenableBackendSlot: () => reenableBackendSlot,
|
|
4956
5156
|
reenableSlot: () => reenableSlot,
|
|
@@ -4982,6 +5182,7 @@ __export(store_exports5, {
|
|
|
4982
5182
|
setAgentMode: () => setAgentMode,
|
|
4983
5183
|
setAllowPaidSlots: () => setAllowPaidSlots,
|
|
4984
5184
|
setBackend: () => setBackend,
|
|
5185
|
+
setBackendDisabled: () => setBackendDisabled,
|
|
4985
5186
|
setBackendLimit: () => setBackendLimit,
|
|
4986
5187
|
setBackendRotationMode: () => setBackendRotationMode,
|
|
4987
5188
|
setBackendSlotEnabled: () => setBackendSlotEnabled,
|
|
@@ -4997,6 +5198,7 @@ __export(store_exports5, {
|
|
|
4997
5198
|
setMode: () => setMode,
|
|
4998
5199
|
setModel: () => setModel,
|
|
4999
5200
|
setModelSignature: () => setModelSignature,
|
|
5201
|
+
setPinnedSkills: () => setPinnedSkills,
|
|
5000
5202
|
setResponseStyle: () => setResponseStyle,
|
|
5001
5203
|
setSessionId: () => setSessionId,
|
|
5002
5204
|
setSessionLogEnabled: () => setSessionLogEnabled,
|
|
@@ -5028,6 +5230,7 @@ function initDatabase() {
|
|
|
5028
5230
|
db = new Database(DB_PATH);
|
|
5029
5231
|
db.pragma("journal_mode = WAL");
|
|
5030
5232
|
db.pragma("foreign_keys = ON");
|
|
5233
|
+
_setSchemaDbGetter(() => db);
|
|
5031
5234
|
initSchema(db);
|
|
5032
5235
|
startMaintenanceTimers(db);
|
|
5033
5236
|
}
|
|
@@ -5043,6 +5246,7 @@ function getDb() {
|
|
|
5043
5246
|
function _setDbForTesting(testDb) {
|
|
5044
5247
|
db = testDb;
|
|
5045
5248
|
_testDbPath = testDb.name;
|
|
5249
|
+
_setSchemaDbGetter(() => db);
|
|
5046
5250
|
}
|
|
5047
5251
|
var db, _testDbPath;
|
|
5048
5252
|
var init_store5 = __esm({
|
|
@@ -7559,6 +7763,52 @@ var init_html = __esm({
|
|
|
7559
7763
|
}
|
|
7560
7764
|
});
|
|
7561
7765
|
|
|
7766
|
+
// src/format-time.ts
|
|
7767
|
+
function formatLocalDate(utcDatetime) {
|
|
7768
|
+
const d = parseUtcDatetime(utcDatetime);
|
|
7769
|
+
if (!d) return utcDatetime.split("T")[0] ?? utcDatetime.split(" ")[0];
|
|
7770
|
+
const year = d.getFullYear();
|
|
7771
|
+
const month = String(d.getMonth() + 1).padStart(2, "0");
|
|
7772
|
+
const day = String(d.getDate()).padStart(2, "0");
|
|
7773
|
+
return `${year}-${month}-${day}`;
|
|
7774
|
+
}
|
|
7775
|
+
function formatLocalDateTime(utcDatetime) {
|
|
7776
|
+
const d = parseUtcDatetime(utcDatetime);
|
|
7777
|
+
if (!d) return utcDatetime;
|
|
7778
|
+
const year = d.getFullYear();
|
|
7779
|
+
const month = String(d.getMonth() + 1).padStart(2, "0");
|
|
7780
|
+
const day = String(d.getDate()).padStart(2, "0");
|
|
7781
|
+
const hours = String(d.getHours()).padStart(2, "0");
|
|
7782
|
+
const minutes = String(d.getMinutes()).padStart(2, "0");
|
|
7783
|
+
const tzAbbr = getTimezoneAbbr(d);
|
|
7784
|
+
return `${year}-${month}-${day} ${hours}:${minutes} ${tzAbbr}`;
|
|
7785
|
+
}
|
|
7786
|
+
function parseUtcDatetime(utcDatetime) {
|
|
7787
|
+
if (!utcDatetime) return null;
|
|
7788
|
+
const normalized = utcDatetime.includes("T") ? utcDatetime : utcDatetime.replace(" ", "T");
|
|
7789
|
+
const withZ = normalized.endsWith("Z") ? normalized : normalized + "Z";
|
|
7790
|
+
const d = new Date(withZ);
|
|
7791
|
+
return isNaN(d.getTime()) ? null : d;
|
|
7792
|
+
}
|
|
7793
|
+
function getTimezoneAbbr(date) {
|
|
7794
|
+
try {
|
|
7795
|
+
const parts = new Intl.DateTimeFormat("en-US", {
|
|
7796
|
+
timeZone: systemTimezone,
|
|
7797
|
+
timeZoneName: "short"
|
|
7798
|
+
}).formatToParts(date);
|
|
7799
|
+
return parts.find((p) => p.type === "timeZoneName")?.value ?? "";
|
|
7800
|
+
} catch {
|
|
7801
|
+
return "";
|
|
7802
|
+
}
|
|
7803
|
+
}
|
|
7804
|
+
var systemTimezone;
|
|
7805
|
+
var init_format_time = __esm({
|
|
7806
|
+
"src/format-time.ts"() {
|
|
7807
|
+
"use strict";
|
|
7808
|
+
systemTimezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
7809
|
+
}
|
|
7810
|
+
});
|
|
7811
|
+
|
|
7562
7812
|
// src/channels/telegram-throttle.ts
|
|
7563
7813
|
var telegram_throttle_exports = {};
|
|
7564
7814
|
__export(telegram_throttle_exports, {
|
|
@@ -7581,13 +7831,15 @@ function is429(err) {
|
|
|
7581
7831
|
function sleep(ms) {
|
|
7582
7832
|
return new Promise((r) => setTimeout(r, ms));
|
|
7583
7833
|
}
|
|
7584
|
-
var PER_DM_INTERVAL_MS, PER_GROUP_INTERVAL_MS, GLOBAL_INTERVAL_MS, MAX_RETRIES2, RETRY_DELAY_MS, MAX_QUEUE_SIZE, EDIT_PRESSURE_THRESHOLD, MAX_PER_CHAT_QUEUE, MAX_TOTAL_PAUSE_MS, CIRCUIT_TRIP_THRESHOLD, CIRCUIT_TRIP_WINDOW_MS, CIRCUIT_COOLDOWN_STEP_SEC, CIRCUIT_RESET_WINDOW_MS, CircuitState, Priority, _activeThrottle, TelegramThrottle;
|
|
7834
|
+
var PER_DM_INTERVAL_MS, PER_GROUP_INTERVAL_MS, P0_PACING_MS, GLOBAL_INTERVAL_MS, MAX_RETRIES2, RETRY_DELAY_MS, MAX_QUEUE_SIZE, EDIT_PRESSURE_THRESHOLD, MAX_PER_CHAT_QUEUE, MAX_TOTAL_PAUSE_MS, CIRCUIT_TRIP_THRESHOLD, CIRCUIT_TRIP_WINDOW_MS, CIRCUIT_COOLDOWN_STEP_SEC, CIRCUIT_RESET_WINDOW_MS, CircuitState, Priority, _activeThrottle, TelegramThrottle;
|
|
7585
7835
|
var init_telegram_throttle = __esm({
|
|
7586
7836
|
"src/channels/telegram-throttle.ts"() {
|
|
7587
7837
|
"use strict";
|
|
7588
7838
|
init_log();
|
|
7839
|
+
init_format_time();
|
|
7589
7840
|
PER_DM_INTERVAL_MS = 1e3;
|
|
7590
7841
|
PER_GROUP_INTERVAL_MS = 3500;
|
|
7842
|
+
P0_PACING_MS = 150;
|
|
7591
7843
|
GLOBAL_INTERVAL_MS = 100;
|
|
7592
7844
|
MAX_RETRIES2 = 2;
|
|
7593
7845
|
RETRY_DELAY_MS = 1e3;
|
|
@@ -7766,16 +8018,17 @@ var init_telegram_throttle = __esm({
|
|
|
7766
8018
|
while (this.queue.length > 0) {
|
|
7767
8019
|
while (this.isPaused()) {
|
|
7768
8020
|
if (this.pauseStartedAt > 0 && Date.now() - this.pauseStartedAt > MAX_TOTAL_PAUSE_MS) {
|
|
7769
|
-
warn(`[throttle] Max pause duration exceeded (${MAX_TOTAL_PAUSE_MS / 6e4}min), dropping ${this.queue.length} items`);
|
|
8021
|
+
warn(`[throttle] Max pause duration exceeded (${MAX_TOTAL_PAUSE_MS / 6e4}min), dropping ${this.queue.length} items (pause remains until ${formatLocalDateTime(new Date(this.pausedUntil).toISOString())})`);
|
|
7770
8022
|
this.flushQueueWithError("Telegram rate limit exceeded max wait time");
|
|
7771
|
-
this.pausedUntil = 0;
|
|
7772
|
-
this.pauseStartedAt = 0;
|
|
7773
8023
|
break;
|
|
7774
8024
|
}
|
|
7775
8025
|
const waitMs = Math.min(this.pausedUntil - Date.now(), 5e3);
|
|
7776
8026
|
if (waitMs > 0) await sleep(waitMs);
|
|
7777
8027
|
}
|
|
7778
8028
|
if (this.queue.length === 0) break;
|
|
8029
|
+
if (!this.isPaused() && this.pauseStartedAt > 0) {
|
|
8030
|
+
this.pauseStartedAt = 0;
|
|
8031
|
+
}
|
|
7779
8032
|
this.updateCircuitState();
|
|
7780
8033
|
const item = this.selectNextItem();
|
|
7781
8034
|
if (!item) {
|
|
@@ -7783,7 +8036,8 @@ var init_telegram_throttle = __esm({
|
|
|
7783
8036
|
continue;
|
|
7784
8037
|
}
|
|
7785
8038
|
const lastChat = this.lastSendPerChat.get(item.chatId) ?? 0;
|
|
7786
|
-
const
|
|
8039
|
+
const interval = item.priority === 0 /* P0_CRITICAL */ ? P0_PACING_MS : perChatInterval(item.chatId);
|
|
8040
|
+
const chatWait = interval - (Date.now() - lastChat);
|
|
7787
8041
|
if (chatWait > 0) await sleep(chatWait);
|
|
7788
8042
|
const globalWait = GLOBAL_INTERVAL_MS - (Date.now() - this.lastGlobalSend);
|
|
7789
8043
|
if (globalWait > 0) await sleep(globalWait);
|
|
@@ -7865,6 +8119,7 @@ var init_telegram_throttle = __esm({
|
|
|
7865
8119
|
return await fn();
|
|
7866
8120
|
} catch (err) {
|
|
7867
8121
|
if (is429(err)) throw err;
|
|
8122
|
+
if (err instanceof GrammyError && err.error_code >= 400 && err.error_code < 500) throw err;
|
|
7868
8123
|
if (attempt < MAX_RETRIES2 && err instanceof GrammyError) {
|
|
7869
8124
|
warn(`[throttle] ${label2} attempt ${attempt + 1}/${MAX_RETRIES2} failed (${err.error_code}), retrying`);
|
|
7870
8125
|
await sleep(RETRY_DELAY_MS);
|
|
@@ -8606,6 +8861,70 @@ function buildHandoffContract() {
|
|
|
8606
8861
|
"**Open questions:** Anything unresolved or needing the main agent's attention."
|
|
8607
8862
|
].join("\n");
|
|
8608
8863
|
}
|
|
8864
|
+
function buildAgenticCore() {
|
|
8865
|
+
return `## Task Execution
|
|
8866
|
+
|
|
8867
|
+
- Implement rather than suggest unless the request is clearly read-only.
|
|
8868
|
+
- Read relevant files before answering questions about code or proposing changes. Do not propose changes to code you have not read.
|
|
8869
|
+
- Do not add features, refactor code, or make improvements beyond what was asked. A bug fix does not need surrounding code cleaned up. A simple feature does not need extra configurability.
|
|
8870
|
+
- Do not create helpers, utilities, or abstractions for one-time operations. Do not design for hypothetical future requirements. Three similar lines of code is better than a premature abstraction.
|
|
8871
|
+
- Do not add error handling, fallbacks, or validation for scenarios that cannot happen. Trust internal code and framework guarantees. Only validate at system boundaries (user input, external APIs).
|
|
8872
|
+
- Do not create files unless absolutely necessary. Prefer editing existing files over creating new ones.
|
|
8873
|
+
- Avoid backwards-compatibility hacks. If something is unused, delete it completely.
|
|
8874
|
+
- Be careful not to introduce security vulnerabilities (injection, XSS, SQL injection, OWASP top 10). If you write insecure code, fix it immediately.
|
|
8875
|
+
- You are highly capable. Defer to user judgement about whether a task is too large to attempt.
|
|
8876
|
+
|
|
8877
|
+
## Action Safety
|
|
8878
|
+
|
|
8879
|
+
Take local, reversible actions freely (editing files, running tests). For actions that are hard to reverse, affect shared systems, or could be destructive, confirm with the user before proceeding.
|
|
8880
|
+
|
|
8881
|
+
Actions that require confirmation:
|
|
8882
|
+
- Destructive: deleting files/branches, dropping tables, rm -rf, overwriting uncommitted changes
|
|
8883
|
+
- Hard to reverse: force-pushing, git reset --hard, amending published commits, removing dependencies
|
|
8884
|
+
- Visible to others: pushing code, creating/commenting on PRs/issues, sending messages, posting to external services
|
|
8885
|
+
|
|
8886
|
+
When encountering an obstacle, investigate root causes rather than using destructive actions as shortcuts. If you discover unexpected state (unfamiliar files, branches, configuration), investigate before deleting or overwriting \u2014 it may be the user's in-progress work.
|
|
8887
|
+
|
|
8888
|
+
## Tool Usage
|
|
8889
|
+
|
|
8890
|
+
- Call multiple tools in parallel when they have no dependencies between them. Maximize parallel tool calls for efficiency. Only use sequential calls when outputs feed inputs.
|
|
8891
|
+
- Never use placeholders or guess missing parameters.
|
|
8892
|
+
- For git operations: never amend published commits or force-push without explicit instruction.
|
|
8893
|
+
|
|
8894
|
+
## Communication
|
|
8895
|
+
|
|
8896
|
+
- Before your first tool call, state in one sentence what you are about to do.
|
|
8897
|
+
- While working, give short updates at key moments: when you find something, change direction, or hit a blocker. One sentence per update is enough.
|
|
8898
|
+
- End-of-turn summary: one or two sentences \u2014 what changed and what is next. Nothing else.
|
|
8899
|
+
- Match responses to the task: a simple question gets a direct answer, not headers and sections.
|
|
8900
|
+
- Do not narrate your internal reasoning, tool selection logic, or thought process. Only output the final answer.
|
|
8901
|
+
|
|
8902
|
+
## Grounding
|
|
8903
|
+
|
|
8904
|
+
- Never invent library APIs, file paths, CLI commands, or documentation you are not certain exist.
|
|
8905
|
+
- If uncertain, say so. Label inferences clearly: "Based on X, I expect Y" \u2014 not stated as fact.
|
|
8906
|
+
- Verify paths before acting. User paths are often approximate.`;
|
|
8907
|
+
}
|
|
8908
|
+
function buildFollowThrough() {
|
|
8909
|
+
return `PERSISTENCE RULES:
|
|
8910
|
+
- Persist through the full lifecycle: gather context \u2192 plan \u2192 implement \u2192 verify \u2192 report.
|
|
8911
|
+
- Do not stop at analysis, partial fixes, or drafts.
|
|
8912
|
+
- Do not output a plan, status update, or progress summary as your final message.
|
|
8913
|
+
If there is more work to do, do the work. A status message without a following
|
|
8914
|
+
tool call will terminate your turn prematurely.
|
|
8915
|
+
- Only terminate your turn when you are sure the problem is fully solved.
|
|
8916
|
+
|
|
8917
|
+
TOOL PERSISTENCE:
|
|
8918
|
+
- Do not stop early when another tool call would materially improve correctness or completeness.
|
|
8919
|
+
- Keep calling tools until: (1) the task is complete, and (2) verification passes.
|
|
8920
|
+
- If a tool returns empty, errors, or partial results \u2014 retry with a different strategy. Tool errors are not stop conditions.
|
|
8921
|
+
|
|
8922
|
+
BIAS TO ACTION:
|
|
8923
|
+
- Default to implementing with reasonable assumptions rather than asking clarifying questions,
|
|
8924
|
+
unless truly blocked on a detail that would change correctness or safety, or involves an
|
|
8925
|
+
irreversible action.
|
|
8926
|
+
- Never stop after completing only part of the request.`;
|
|
8927
|
+
}
|
|
8609
8928
|
var REAL_HOME, TASK_WORKER_SKILL_BUDGET;
|
|
8610
8929
|
var init_shared = __esm({
|
|
8611
8930
|
"src/bootstrap/templates/shared.ts"() {
|
|
@@ -8621,6 +8940,7 @@ var discover_exports = {};
|
|
|
8621
8940
|
__export(discover_exports, {
|
|
8622
8941
|
discoverAllSkills: () => discoverAllSkills,
|
|
8623
8942
|
invalidateSkillCache: () => invalidateSkillCache,
|
|
8943
|
+
parseFrontmatter: () => parseFrontmatter,
|
|
8624
8944
|
resolveSkillForTask: () => resolveSkillForTask,
|
|
8625
8945
|
stripFrontmatter: () => stripFrontmatter
|
|
8626
8946
|
});
|
|
@@ -8693,7 +9013,8 @@ async function scanSkillDir(skillsDir, source) {
|
|
|
8693
9013
|
contentHash: hash,
|
|
8694
9014
|
type: frontmatter.type,
|
|
8695
9015
|
status: frontmatter.status,
|
|
8696
|
-
requires: frontmatter.requires
|
|
9016
|
+
requires: frontmatter.requires,
|
|
9017
|
+
triggers: frontmatter.triggers
|
|
8697
9018
|
});
|
|
8698
9019
|
}
|
|
8699
9020
|
return results;
|
|
@@ -8725,7 +9046,8 @@ function mergeAndDeduplicate(raw) {
|
|
|
8725
9046
|
contentHash: primary.contentHash,
|
|
8726
9047
|
type: primary.type,
|
|
8727
9048
|
status: primary.status,
|
|
8728
|
-
requires: primary.requires
|
|
9049
|
+
requires: primary.requires,
|
|
9050
|
+
triggers: primary.triggers
|
|
8729
9051
|
});
|
|
8730
9052
|
} else {
|
|
8731
9053
|
for (const [hash, copies] of byHash) {
|
|
@@ -8740,7 +9062,8 @@ function mergeAndDeduplicate(raw) {
|
|
|
8740
9062
|
contentHash: hash,
|
|
8741
9063
|
type: primary.type,
|
|
8742
9064
|
status: primary.status,
|
|
8743
|
-
requires: primary.requires
|
|
9065
|
+
requires: primary.requires,
|
|
9066
|
+
triggers: primary.triggers
|
|
8744
9067
|
});
|
|
8745
9068
|
}
|
|
8746
9069
|
}
|
|
@@ -8760,7 +9083,8 @@ function parseFrontmatter(content, fallbackName, source) {
|
|
|
8760
9083
|
description: "",
|
|
8761
9084
|
type: "tool",
|
|
8762
9085
|
status: source === "cc-claw" ? "imported" : "external",
|
|
8763
|
-
requires: []
|
|
9086
|
+
requires: [],
|
|
9087
|
+
triggers: []
|
|
8764
9088
|
};
|
|
8765
9089
|
}
|
|
8766
9090
|
const fm = fmMatch[1];
|
|
@@ -8786,12 +9110,21 @@ function parseFrontmatter(content, fallbackName, source) {
|
|
|
8786
9110
|
if (cleaned2) requires.push(cleaned2);
|
|
8787
9111
|
}
|
|
8788
9112
|
}
|
|
9113
|
+
const triggersMatch = fm.match(/^triggers:\s*\[([^\]]*)\]/m);
|
|
9114
|
+
const triggers = [];
|
|
9115
|
+
if (triggersMatch?.[1]) {
|
|
9116
|
+
for (const item of triggersMatch[1].split(",")) {
|
|
9117
|
+
const cleaned2 = strip(item);
|
|
9118
|
+
if (cleaned2) triggers.push(cleaned2);
|
|
9119
|
+
}
|
|
9120
|
+
}
|
|
8789
9121
|
return {
|
|
8790
9122
|
name: nameMatch?.[1] ? strip(nameMatch[1]) : fallbackName,
|
|
8791
9123
|
description: descMatch?.[1] ? strip(descMatch[1]) : "",
|
|
8792
9124
|
type,
|
|
8793
9125
|
status,
|
|
8794
|
-
requires
|
|
9126
|
+
requires,
|
|
9127
|
+
triggers
|
|
8795
9128
|
};
|
|
8796
9129
|
}
|
|
8797
9130
|
function stripFrontmatter(content) {
|
|
@@ -8802,9 +9135,9 @@ function resolveSkillForTask(skillName) {
|
|
|
8802
9135
|
for (const candidate of SKILL_FILE_CANDIDATES3) {
|
|
8803
9136
|
const skillPath = join9(SKILLS_PATH, skillName, candidate);
|
|
8804
9137
|
try {
|
|
8805
|
-
const { readFileSync:
|
|
9138
|
+
const { readFileSync: readFileSync36, existsSync: existsSync62 } = __require("fs");
|
|
8806
9139
|
if (!existsSync62(skillPath)) continue;
|
|
8807
|
-
const raw =
|
|
9140
|
+
const raw = readFileSync36(skillPath, "utf-8");
|
|
8808
9141
|
const fm = parseFrontmatter(raw, skillName, "cc-claw");
|
|
8809
9142
|
if (fm.status !== "approved") {
|
|
8810
9143
|
log(`[skills] Skill "${skillName}" has status "${fm.status}" \u2014 only approved skills can be used as task worker identity`);
|
|
@@ -11267,6 +11600,12 @@ ${buildPlatformQuickReference()}
|
|
|
11267
11600
|
|
|
11268
11601
|
${buildToolUsageRules()}
|
|
11269
11602
|
|
|
11603
|
+
## Agentic Discipline
|
|
11604
|
+
${buildAgenticCore()}
|
|
11605
|
+
|
|
11606
|
+
## Follow-Through
|
|
11607
|
+
${buildFollowThrough()}
|
|
11608
|
+
|
|
11270
11609
|
## CC-Claw Constraints
|
|
11271
11610
|
${buildDatabaseSafetyBoundary()}
|
|
11272
11611
|
- Do not invent library APIs or documentation you are not certain exists.
|
|
@@ -11298,28 +11637,13 @@ ${user}
|
|
|
11298
11637
|
You are an autonomous agent \u2014 keep going until the user's request is completely
|
|
11299
11638
|
resolved before ending your turn and yielding back to the user.
|
|
11300
11639
|
|
|
11301
|
-
|
|
11302
|
-
- Persist through the full lifecycle: gather context \u2192 plan \u2192 implement \u2192 verify
|
|
11303
|
-
\u2192 report. Do not stop at analysis, partial fixes, or drafts.
|
|
11304
|
-
- Do not output a plan, status update, or progress summary as your final message.
|
|
11305
|
-
If there is more work to do, do the work. A status message without a following
|
|
11306
|
-
tool call will terminate your turn prematurely.
|
|
11307
|
-
- Only terminate your turn when you are sure the problem is fully solved.
|
|
11308
|
-
|
|
11309
|
-
TOOL PERSISTENCE:
|
|
11310
|
-
- Do not stop early when another tool call would materially improve correctness
|
|
11311
|
-
or completeness.
|
|
11312
|
-
- Keep calling tools until: (1) the task is complete, and (2) verification passes.
|
|
11313
|
-
- If a tool returns empty, errors, or partial results \u2014 retry with a different
|
|
11314
|
-
strategy. Tool errors are not stop conditions.
|
|
11315
|
-
|
|
11316
|
-
BIAS TO ACTION:
|
|
11317
|
-
- Default to implementing with reasonable assumptions rather than asking
|
|
11318
|
-
clarifying questions, unless truly blocked on a detail that would change
|
|
11319
|
-
correctness or safety, or involves an irreversible action.
|
|
11320
|
-
- Never stop after completing only part of the request.
|
|
11640
|
+
${buildFollowThrough()}
|
|
11321
11641
|
</default_follow_through_policy>
|
|
11322
11642
|
|
|
11643
|
+
<agentic_framework>
|
|
11644
|
+
${buildAgenticCore()}
|
|
11645
|
+
</agentic_framework>
|
|
11646
|
+
|
|
11323
11647
|
<output_contract>
|
|
11324
11648
|
- Simple answers: 1-4 sentences in Telegram HTML, no preamble
|
|
11325
11649
|
- Code tasks: show the change, explain what changed and why in 1-2 sentences
|
|
@@ -11384,18 +11708,15 @@ ${soul}
|
|
|
11384
11708
|
${user}
|
|
11385
11709
|
</user_profile>
|
|
11386
11710
|
|
|
11387
|
-
<
|
|
11388
|
-
|
|
11389
|
-
|
|
11390
|
-
|
|
11391
|
-
|
|
11392
|
-
|
|
11393
|
-
|
|
11394
|
-
</instructions>
|
|
11711
|
+
<agentic_framework>
|
|
11712
|
+
${buildAgenticCore()}
|
|
11713
|
+
</agentic_framework>
|
|
11714
|
+
|
|
11715
|
+
<follow_through_policy>
|
|
11716
|
+
${buildFollowThrough()}
|
|
11717
|
+
</follow_through_policy>
|
|
11395
11718
|
|
|
11396
11719
|
<tool_guidance>
|
|
11397
|
-
Call multiple tools in parallel when they have no dependencies.
|
|
11398
|
-
Only make changes directly requested. Do not add features or refactor beyond what was asked.
|
|
11399
11720
|
${buildToolUsageRules()}
|
|
11400
11721
|
</tool_guidance>
|
|
11401
11722
|
|
|
@@ -11423,6 +11744,138 @@ var init_cursor2 = __esm({
|
|
|
11423
11744
|
}
|
|
11424
11745
|
});
|
|
11425
11746
|
|
|
11747
|
+
// src/bootstrap/templates/api.ts
|
|
11748
|
+
function buildApiTemplate(soul, user) {
|
|
11749
|
+
const date = getCurrentDate();
|
|
11750
|
+
return `<role>
|
|
11751
|
+
You are the agent described below, operating via Telegram. Current date: ${date}.
|
|
11752
|
+
You are running as a direct API model \u2014 not a CLI tool like Claude Code or Gemini CLI.
|
|
11753
|
+
CC-Claw is your agentic harness: it provides your tools, memory, identity, and orchestration layer.
|
|
11754
|
+
Follow these system instructions precisely. They define how you operate.
|
|
11755
|
+
</role>
|
|
11756
|
+
|
|
11757
|
+
<identity>
|
|
11758
|
+
${soul}
|
|
11759
|
+
</identity>
|
|
11760
|
+
|
|
11761
|
+
<user_profile>
|
|
11762
|
+
${user}
|
|
11763
|
+
</user_profile>
|
|
11764
|
+
|
|
11765
|
+
<agentic_framework>
|
|
11766
|
+
${buildAgenticCore()}
|
|
11767
|
+
</agentic_framework>
|
|
11768
|
+
|
|
11769
|
+
<follow_through_policy>
|
|
11770
|
+
${buildFollowThrough()}
|
|
11771
|
+
</follow_through_policy>
|
|
11772
|
+
|
|
11773
|
+
<tool_routing>
|
|
11774
|
+
## API Backend Tool Routing
|
|
11775
|
+
|
|
11776
|
+
You have access to tools provided by CC-Claw via function calling. Use them directly by name.
|
|
11777
|
+
|
|
11778
|
+
External CLI tools (gsearch, pwm, gws, gemcli, nlm, curl, python3, git, npm, cc-claw, etc.)
|
|
11779
|
+
are accessed ONLY via the \`restrictedBash\` tool. NEVER call external CLIs as direct tool
|
|
11780
|
+
names \u2014 they are not registered as native tools and will fail silently.
|
|
11781
|
+
|
|
11782
|
+
Correct: restrictedBash({"command": "gsearch \\"query\\" --type news"})
|
|
11783
|
+
Incorrect: gsearch({"query": "..."}) \u2190 this WILL FAIL
|
|
11784
|
+
|
|
11785
|
+
If a skill or instruction says to use a CLI tool, always route it through restrictedBash.
|
|
11786
|
+
When unsure about available commands, run: restrictedBash({"command": "cc-claw --ai"})
|
|
11787
|
+
|
|
11788
|
+
${buildCliEnvironment()}
|
|
11789
|
+
</tool_routing>
|
|
11790
|
+
|
|
11791
|
+
<verification_loop>
|
|
11792
|
+
Before finalizing any response, silently check:
|
|
11793
|
+
1. Is this correct and complete?
|
|
11794
|
+
2. Is every factual claim grounded in something verified?
|
|
11795
|
+
3. Does the format match the output contract below?
|
|
11796
|
+
4. Are there any safety concerns with the actions taken?
|
|
11797
|
+
</verification_loop>
|
|
11798
|
+
|
|
11799
|
+
<safety_boundary>
|
|
11800
|
+
${buildDatabaseSafetyBoundary()}
|
|
11801
|
+
${buildToolUsageRules()}
|
|
11802
|
+
</safety_boundary>
|
|
11803
|
+
|
|
11804
|
+
<platform_reference>
|
|
11805
|
+
${buildSystemCapabilities()}
|
|
11806
|
+
${buildPlatformQuickReference()}
|
|
11807
|
+
</platform_reference>
|
|
11808
|
+
|
|
11809
|
+
<output_format>
|
|
11810
|
+
Respond in Telegram HTML: <b>bold</b>, <code>inline</code>, <pre>blocks</pre>.
|
|
11811
|
+
No markdown formatting. No flattery openers. No pleasantries.
|
|
11812
|
+
Prose for conversation, structure only when the content requires it.
|
|
11813
|
+
Keep casual responses concise. Reserve detail for research, code, and complex tasks.
|
|
11814
|
+
</output_format>`;
|
|
11815
|
+
}
|
|
11816
|
+
var init_api = __esm({
|
|
11817
|
+
"src/bootstrap/templates/api.ts"() {
|
|
11818
|
+
"use strict";
|
|
11819
|
+
init_shared();
|
|
11820
|
+
}
|
|
11821
|
+
});
|
|
11822
|
+
|
|
11823
|
+
// src/bootstrap/templates/api-task.ts
|
|
11824
|
+
function buildApiTaskTemplate() {
|
|
11825
|
+
const date = getCurrentDate();
|
|
11826
|
+
return `<role>
|
|
11827
|
+
You are a specialist task worker operating via CC-Claw. Current date: ${date}.
|
|
11828
|
+
Your identity and expertise are defined by the skill injected into your prompt.
|
|
11829
|
+
You are a direct API model \u2014 use restrictedBash for all external CLI tools.
|
|
11830
|
+
</role>
|
|
11831
|
+
|
|
11832
|
+
<follow_through_policy>
|
|
11833
|
+
${buildFollowThrough()}
|
|
11834
|
+
</follow_through_policy>
|
|
11835
|
+
|
|
11836
|
+
<operating_rules>
|
|
11837
|
+
${buildTaskWorkerRules()}
|
|
11838
|
+
</operating_rules>
|
|
11839
|
+
|
|
11840
|
+
<tool_routing>
|
|
11841
|
+
External CLI tools are accessed ONLY via the restrictedBash tool.
|
|
11842
|
+
NEVER call external CLIs as direct tool names \u2014 they will fail silently.
|
|
11843
|
+
Correct: restrictedBash({"command": "command here"})
|
|
11844
|
+
Incorrect: command({"arg": "..."}) \u2190 this WILL FAIL
|
|
11845
|
+
</tool_routing>
|
|
11846
|
+
|
|
11847
|
+
<cli_environment>
|
|
11848
|
+
${buildCliEnvironment()}
|
|
11849
|
+
</cli_environment>
|
|
11850
|
+
|
|
11851
|
+
<verification_loop>
|
|
11852
|
+
Before finalizing any response, silently check:
|
|
11853
|
+
1. Is this correct and complete?
|
|
11854
|
+
2. Is every factual claim grounded in something verified?
|
|
11855
|
+
3. Does the output match the task requirements?
|
|
11856
|
+
</verification_loop>
|
|
11857
|
+
|
|
11858
|
+
<grounding_rules>
|
|
11859
|
+
Never invent library APIs, file paths, or documentation. If uncertain, say so.
|
|
11860
|
+
Verify paths before acting. User paths are often approximate.
|
|
11861
|
+
</grounding_rules>
|
|
11862
|
+
|
|
11863
|
+
<safety_boundary>
|
|
11864
|
+
Confirm before: deleting files, posting to external services, database schema changes.
|
|
11865
|
+
Do not access ~/.cc-claw/data/cc-claw.db directly.
|
|
11866
|
+
</safety_boundary>
|
|
11867
|
+
|
|
11868
|
+
<output_contract>
|
|
11869
|
+
${buildHandoffContract()}
|
|
11870
|
+
</output_contract>`;
|
|
11871
|
+
}
|
|
11872
|
+
var init_api_task = __esm({
|
|
11873
|
+
"src/bootstrap/templates/api-task.ts"() {
|
|
11874
|
+
"use strict";
|
|
11875
|
+
init_shared();
|
|
11876
|
+
}
|
|
11877
|
+
});
|
|
11878
|
+
|
|
11426
11879
|
// src/bootstrap/templates/claude-task.ts
|
|
11427
11880
|
function buildClaudeTaskTemplate() {
|
|
11428
11881
|
const date = getCurrentDate();
|
|
@@ -11481,26 +11934,7 @@ Your identity and expertise are defined by the skill injected into your prompt.
|
|
|
11481
11934
|
You are an autonomous agent \u2014 keep going until the task is completely resolved
|
|
11482
11935
|
before ending your turn and yielding back to the user.
|
|
11483
11936
|
|
|
11484
|
-
|
|
11485
|
-
- Persist through the full lifecycle: gather context \u2192 plan \u2192 implement \u2192 verify
|
|
11486
|
-
\u2192 report. Do not stop at analysis, partial fixes, or drafts.
|
|
11487
|
-
- Do not output a plan, status update, or progress summary as your final message.
|
|
11488
|
-
If there is more work to do, do the work. A status message without a following
|
|
11489
|
-
tool call will terminate your turn prematurely.
|
|
11490
|
-
- Only terminate your turn when you are sure the task is fully solved.
|
|
11491
|
-
|
|
11492
|
-
TOOL PERSISTENCE:
|
|
11493
|
-
- Do not stop early when another tool call would materially improve correctness
|
|
11494
|
-
or completeness.
|
|
11495
|
-
- Keep calling tools until: (1) the task is complete, and (2) verification passes.
|
|
11496
|
-
- If a tool returns empty, errors, or partial results \u2014 retry with a different
|
|
11497
|
-
strategy. Tool errors are not stop conditions.
|
|
11498
|
-
|
|
11499
|
-
BIAS TO ACTION:
|
|
11500
|
-
- Default to implementing with reasonable assumptions rather than asking
|
|
11501
|
-
clarifying questions, unless truly blocked on a detail that would change
|
|
11502
|
-
correctness or safety.
|
|
11503
|
-
- Never stop after completing only part of the task.
|
|
11937
|
+
${buildFollowThrough()}
|
|
11504
11938
|
</default_follow_through_policy>
|
|
11505
11939
|
|
|
11506
11940
|
<operating_rules>
|
|
@@ -11664,6 +12098,10 @@ function syncNativeCliFiles() {
|
|
|
11664
12098
|
writeFileSync6(join14(IDENTITY_PATH, "CC-CLAW-codex.md"), codexContent, "utf-8");
|
|
11665
12099
|
writeFileSync6(join14(WORKSPACE_PATH, "AGENTS.md"), codexContent, "utf-8");
|
|
11666
12100
|
writeFileSync6(join14(IDENTITY_PATH, "CC-CLAW-cursor.md"), cursorContent, "utf-8");
|
|
12101
|
+
const apiContent = buildApiTemplate(soul, user);
|
|
12102
|
+
writeFileSync6(join14(IDENTITY_PATH, "CC-CLAW-api.md"), apiContent, "utf-8");
|
|
12103
|
+
const apiTaskContent = buildApiTaskTemplate();
|
|
12104
|
+
writeFileSync6(join14(IDENTITY_PATH, "CC-CLAW-api-task.md"), apiTaskContent, "utf-8");
|
|
11667
12105
|
const fallbackContent = [
|
|
11668
12106
|
"# CC-Claw System Instructions",
|
|
11669
12107
|
"",
|
|
@@ -11686,7 +12124,7 @@ function syncNativeCliFiles() {
|
|
|
11686
12124
|
const codexTaskContent = buildCodexTaskTemplate();
|
|
11687
12125
|
writeFileSync6(join14(IDENTITY_PATH, "CC-CLAW-claude-task.md"), claudeTaskContent, "utf-8");
|
|
11688
12126
|
writeFileSync6(join14(IDENTITY_PATH, "CC-CLAW-codex-task.md"), codexTaskContent, "utf-8");
|
|
11689
|
-
log("[bootstrap] Synced identity files: CC-CLAW-claude.md, GEMINI.md, CC-CLAW-codex.md, CC-CLAW-cursor.md, CC-CLAW.md (fallback), CC-CLAW-claude-task.md, CC-CLAW-codex-task.md");
|
|
12127
|
+
log("[bootstrap] Synced identity files: CC-CLAW-claude.md, GEMINI.md, CC-CLAW-codex.md, CC-CLAW-cursor.md, CC-CLAW-api.md, CC-CLAW-api-task.md, CC-CLAW.md (fallback), CC-CLAW-claude-task.md, CC-CLAW-codex-task.md");
|
|
11690
12128
|
}
|
|
11691
12129
|
var SOUL_PATH, USER_PATH, CONTEXT_DIR, LEGACY_SOUL_PATH, LEGACY_USER_PATH, LEGACY_CLAUDE_MD, LEGACY_GEMINI_MD;
|
|
11692
12130
|
var init_init = __esm({
|
|
@@ -11698,6 +12136,8 @@ var init_init = __esm({
|
|
|
11698
12136
|
init_gemini2();
|
|
11699
12137
|
init_codex2();
|
|
11700
12138
|
init_cursor2();
|
|
12139
|
+
init_api();
|
|
12140
|
+
init_api_task();
|
|
11701
12141
|
init_claude_task();
|
|
11702
12142
|
init_codex_task();
|
|
11703
12143
|
init_shared();
|
|
@@ -11712,19 +12152,333 @@ var init_init = __esm({
|
|
|
11712
12152
|
}
|
|
11713
12153
|
});
|
|
11714
12154
|
|
|
11715
|
-
// src/
|
|
11716
|
-
|
|
11717
|
-
|
|
11718
|
-
|
|
11719
|
-
|
|
11720
|
-
|
|
11721
|
-
);
|
|
12155
|
+
// src/intent/complexity.ts
|
|
12156
|
+
var complexity_exports = {};
|
|
12157
|
+
__export(complexity_exports, {
|
|
12158
|
+
classifyComplexity: () => classifyComplexity
|
|
12159
|
+
});
|
|
12160
|
+
function wordCount(text) {
|
|
12161
|
+
return text.trim().split(/\s+/).filter(Boolean).length;
|
|
12162
|
+
}
|
|
12163
|
+
function classifyComplexity(text) {
|
|
12164
|
+
const trimmed = text.trim();
|
|
12165
|
+
const lower = trimmed.toLowerCase();
|
|
12166
|
+
const words = wordCount(trimmed);
|
|
12167
|
+
if (TRIVIAL_EXACT.has(lower)) {
|
|
12168
|
+
log(`[complexity] "${lower}" -> trivial (exact match)`);
|
|
12169
|
+
return "trivial";
|
|
12170
|
+
}
|
|
12171
|
+
if (trimmed.length <= 4 && /^[\p{Emoji}\s]+$/u.test(trimmed)) {
|
|
12172
|
+
log(`[complexity] "${trimmed}" -> trivial (emoji-only)`);
|
|
12173
|
+
return "trivial";
|
|
12174
|
+
}
|
|
12175
|
+
if (COMPLEX_SIGNALS.test(trimmed) && (words > 15 || trimmed.length > 200)) {
|
|
12176
|
+
log(`[complexity] "${trimmed.slice(0, 40)}..." -> complex (signal words + length)`);
|
|
12177
|
+
return "complex";
|
|
12178
|
+
}
|
|
12179
|
+
if (MUTATION_VERBS.test(trimmed)) {
|
|
12180
|
+
const tier = words > 30 || trimmed.length > 300 ? "complex" : "moderate";
|
|
12181
|
+
log(`[complexity] "${trimmed.slice(0, 40)}..." -> ${tier} (mutation verb)`);
|
|
12182
|
+
return tier;
|
|
12183
|
+
}
|
|
12184
|
+
for (const pattern of CODE_PATTERNS) {
|
|
12185
|
+
if (pattern.test(trimmed)) {
|
|
12186
|
+
const tier = words > 30 || trimmed.length > 300 ? "complex" : "moderate";
|
|
12187
|
+
log(`[complexity] "${trimmed.slice(0, 40)}..." -> ${tier} (code pattern)`);
|
|
12188
|
+
return tier;
|
|
12189
|
+
}
|
|
12190
|
+
}
|
|
12191
|
+
if (trimmed.length > 200 || words > 25) {
|
|
12192
|
+
const tier = words > 30 || trimmed.length > 300 ? "complex" : "moderate";
|
|
12193
|
+
log(`[complexity] "${trimmed.slice(0, 40)}..." -> ${tier} (long message)`);
|
|
12194
|
+
return tier;
|
|
12195
|
+
}
|
|
12196
|
+
for (const pattern of QUESTION_PATTERNS) {
|
|
12197
|
+
if (pattern.test(trimmed)) {
|
|
12198
|
+
log(`[complexity] "${trimmed.slice(0, 40)}..." -> simple (question)`);
|
|
12199
|
+
return "simple";
|
|
12200
|
+
}
|
|
12201
|
+
}
|
|
12202
|
+
log(`[complexity] "${trimmed.slice(0, 40)}..." -> moderate (default)`);
|
|
12203
|
+
return "moderate";
|
|
12204
|
+
}
|
|
12205
|
+
var TRIVIAL_EXACT, COMPLEX_SIGNALS, MUTATION_VERBS, CODE_PATTERNS, QUESTION_PATTERNS;
|
|
12206
|
+
var init_complexity = __esm({
|
|
12207
|
+
"src/intent/complexity.ts"() {
|
|
12208
|
+
"use strict";
|
|
12209
|
+
init_log();
|
|
12210
|
+
TRIVIAL_EXACT = /* @__PURE__ */ new Set([
|
|
12211
|
+
"hey",
|
|
12212
|
+
"hi",
|
|
12213
|
+
"hello",
|
|
12214
|
+
"yo",
|
|
12215
|
+
"sup",
|
|
12216
|
+
"howdy",
|
|
12217
|
+
"hiya",
|
|
12218
|
+
"thanks",
|
|
12219
|
+
"thank you",
|
|
12220
|
+
"thx",
|
|
12221
|
+
"ty",
|
|
12222
|
+
"thank u",
|
|
12223
|
+
"ok",
|
|
12224
|
+
"okay",
|
|
12225
|
+
"k",
|
|
12226
|
+
"kk",
|
|
12227
|
+
"cool",
|
|
12228
|
+
"nice",
|
|
12229
|
+
"great",
|
|
12230
|
+
"awesome",
|
|
12231
|
+
"perfect",
|
|
12232
|
+
"good morning",
|
|
12233
|
+
"good night",
|
|
12234
|
+
"good evening",
|
|
12235
|
+
"gm",
|
|
12236
|
+
"gn",
|
|
12237
|
+
"bye",
|
|
12238
|
+
"goodbye",
|
|
12239
|
+
"later",
|
|
12240
|
+
"see ya",
|
|
12241
|
+
"cya",
|
|
12242
|
+
"lol",
|
|
12243
|
+
"lmao",
|
|
12244
|
+
"haha",
|
|
12245
|
+
"heh",
|
|
12246
|
+
"np",
|
|
12247
|
+
"no problem",
|
|
12248
|
+
"no worries",
|
|
12249
|
+
"nw",
|
|
12250
|
+
"got it",
|
|
12251
|
+
"understood",
|
|
12252
|
+
"roger",
|
|
12253
|
+
"copy",
|
|
12254
|
+
"good",
|
|
12255
|
+
"fine",
|
|
12256
|
+
"alright",
|
|
12257
|
+
"sure",
|
|
12258
|
+
"yes",
|
|
12259
|
+
"no",
|
|
12260
|
+
"yep",
|
|
12261
|
+
"nope",
|
|
12262
|
+
"yeah",
|
|
12263
|
+
"nah"
|
|
12264
|
+
]);
|
|
12265
|
+
COMPLEX_SIGNALS = /\b(architect|design|research|plan|strategy|migration|microservice|distributed|end-to-end|trade-offs|pros\s+and\s+cons)\b/i;
|
|
12266
|
+
MUTATION_VERBS = /\b(fix|create|build|deploy|refactor|implement|write|add|update|edit|test|debug)\b/i;
|
|
12267
|
+
CODE_PATTERNS = [
|
|
12268
|
+
/```/,
|
|
12269
|
+
// code blocks
|
|
12270
|
+
/\.[a-z]{1,5}\b/,
|
|
12271
|
+
// file extensions (.ts, .py, .json)
|
|
12272
|
+
/[/\\][\w.-]+/
|
|
12273
|
+
// file paths (/src/foo, .\bar)
|
|
12274
|
+
];
|
|
12275
|
+
QUESTION_PATTERNS = [
|
|
12276
|
+
/^(?:what|which|where|when|why|how)\b/i,
|
|
12277
|
+
/^(?:show|tell|list|explain)\b/i,
|
|
12278
|
+
/^(?:is|are)\b/i
|
|
12279
|
+
];
|
|
12280
|
+
}
|
|
12281
|
+
});
|
|
12282
|
+
|
|
12283
|
+
// src/skills/inject.ts
|
|
12284
|
+
import { readFileSync as readFileSync10 } from "fs";
|
|
12285
|
+
function setInjectedSkills(chatId, skillNames) {
|
|
12286
|
+
injectedSkillsMap.set(chatId, skillNames);
|
|
12287
|
+
}
|
|
12288
|
+
function getAndClearInjectedSkills(chatId) {
|
|
12289
|
+
const skills2 = injectedSkillsMap.get(chatId) ?? [];
|
|
12290
|
+
injectedSkillsMap.delete(chatId);
|
|
12291
|
+
return skills2;
|
|
11722
12292
|
}
|
|
11723
|
-
function
|
|
11724
|
-
if (
|
|
11725
|
-
|
|
11726
|
-
|
|
12293
|
+
function shouldSkipInjection(userMessage, profile, userExplicitNative) {
|
|
12294
|
+
if (userExplicitNative) return true;
|
|
12295
|
+
if (profile === "minimal") return true;
|
|
12296
|
+
const complexity = classifyComplexity(userMessage);
|
|
12297
|
+
return SKIP_COMPLEXITY.has(complexity);
|
|
12298
|
+
}
|
|
12299
|
+
function scoreSkills(userMessage, skills2) {
|
|
12300
|
+
const msgWords = tokenize(userMessage);
|
|
12301
|
+
const msgWordsAll = tokenizeAll(userMessage);
|
|
12302
|
+
if (msgWordsAll.size === 0) return [];
|
|
12303
|
+
const results = [];
|
|
12304
|
+
for (const skill of skills2) {
|
|
12305
|
+
if (skill.status !== "approved" && skill.status !== "imported") continue;
|
|
12306
|
+
let score = 0;
|
|
12307
|
+
const triggerWords = new Set(skill.triggers.map((t) => t.toLowerCase()));
|
|
12308
|
+
for (const word of msgWordsAll) {
|
|
12309
|
+
if (triggerWords.has(word)) score += 2;
|
|
12310
|
+
}
|
|
12311
|
+
const metaWords = tokenize([skill.name, skill.description].join(" "));
|
|
12312
|
+
for (const word of msgWords) {
|
|
12313
|
+
if (metaWords.has(word) && !STOPWORDS.has(word)) score++;
|
|
12314
|
+
}
|
|
12315
|
+
if (score >= MIN_SCORE) {
|
|
12316
|
+
results.push({ name: skill.name, score, filePath: skill.filePath, requires: skill.requires });
|
|
12317
|
+
}
|
|
12318
|
+
}
|
|
12319
|
+
return results.sort((a, b) => b.score - a.score);
|
|
12320
|
+
}
|
|
12321
|
+
function formatSkillBlock(skills2, budgetChars = TOTAL_BUDGET_CHARS) {
|
|
12322
|
+
if (skills2.length === 0) return null;
|
|
12323
|
+
const lines = ["[Skill context]"];
|
|
12324
|
+
let totalChars = 0;
|
|
12325
|
+
for (const skill of skills2) {
|
|
12326
|
+
let content = skill.content;
|
|
12327
|
+
const remaining = budgetChars - totalChars;
|
|
12328
|
+
if (remaining <= 0) break;
|
|
12329
|
+
if (content.length > remaining) {
|
|
12330
|
+
content = content.slice(0, remaining) + "\n\u2026(truncated)";
|
|
12331
|
+
}
|
|
12332
|
+
lines.push(`## ${skill.name}`);
|
|
12333
|
+
lines.push(content);
|
|
12334
|
+
lines.push("");
|
|
12335
|
+
totalChars += content.length;
|
|
12336
|
+
}
|
|
12337
|
+
lines.push("[End skill context]");
|
|
12338
|
+
return lines.join("\n");
|
|
11727
12339
|
}
|
|
12340
|
+
function loadWithDeps(skill, allSkills, usedNames, entries, charsSoFar, charCap) {
|
|
12341
|
+
const content = readSkillContent(skill.filePath);
|
|
12342
|
+
if (!content) return charsSoFar;
|
|
12343
|
+
if (charsSoFar + content.length > charCap) return charsSoFar;
|
|
12344
|
+
entries.push({ name: skill.name, content });
|
|
12345
|
+
usedNames.add(skill.name);
|
|
12346
|
+
let chars = charsSoFar + content.length;
|
|
12347
|
+
for (const dep of skill.requires) {
|
|
12348
|
+
if (usedNames.has(dep)) continue;
|
|
12349
|
+
const depSkill = allSkills.find((s) => s.name === dep);
|
|
12350
|
+
if (!depSkill) continue;
|
|
12351
|
+
const depContent = readSkillContent(depSkill.filePath);
|
|
12352
|
+
if (!depContent) continue;
|
|
12353
|
+
if (chars + depContent.length > charCap) continue;
|
|
12354
|
+
entries.push({ name: depSkill.name, content: depContent });
|
|
12355
|
+
usedNames.add(depSkill.name);
|
|
12356
|
+
chars += depContent.length;
|
|
12357
|
+
}
|
|
12358
|
+
return chars;
|
|
12359
|
+
}
|
|
12360
|
+
function injectSkillContext(userMessage, allSkills, pinnedSkillNames, profile, userExplicitNative, chatId) {
|
|
12361
|
+
const hasPins = pinnedSkillNames.length > 0;
|
|
12362
|
+
const skipAuto = shouldSkipInjection(userMessage, profile, userExplicitNative);
|
|
12363
|
+
if (!hasPins && skipAuto) return null;
|
|
12364
|
+
const entries = [];
|
|
12365
|
+
const usedNames = /* @__PURE__ */ new Set();
|
|
12366
|
+
let pinnedChars = 0;
|
|
12367
|
+
for (const name of pinnedSkillNames) {
|
|
12368
|
+
if (usedNames.has(name)) continue;
|
|
12369
|
+
const skill = allSkills.find((s) => s.name === name);
|
|
12370
|
+
if (!skill) continue;
|
|
12371
|
+
pinnedChars = loadWithDeps(skill, allSkills, usedNames, entries, pinnedChars, PINNED_BUDGET_CHARS);
|
|
12372
|
+
}
|
|
12373
|
+
if (!skipAuto) {
|
|
12374
|
+
const scored = scoreSkills(userMessage, allSkills);
|
|
12375
|
+
let autoChars = 0;
|
|
12376
|
+
for (const match of scored) {
|
|
12377
|
+
if (usedNames.has(match.name)) continue;
|
|
12378
|
+
const skill = allSkills.find((s) => s.name === match.name);
|
|
12379
|
+
if (!skill) continue;
|
|
12380
|
+
autoChars = loadWithDeps(skill, allSkills, usedNames, entries, autoChars, AUTO_BUDGET_CHARS);
|
|
12381
|
+
}
|
|
12382
|
+
}
|
|
12383
|
+
if (entries.length === 0) return null;
|
|
12384
|
+
const injectedNames = entries.map((e) => e.name);
|
|
12385
|
+
if (chatId) {
|
|
12386
|
+
setInjectedSkills(chatId, injectedNames);
|
|
12387
|
+
}
|
|
12388
|
+
log(`[skills] Injected ${injectedNames.length} skill(s): ${injectedNames.join(", ")}`);
|
|
12389
|
+
return formatSkillBlock(entries);
|
|
12390
|
+
}
|
|
12391
|
+
function readSkillContent(filePath) {
|
|
12392
|
+
try {
|
|
12393
|
+
const raw = readFileSync10(filePath, "utf-8");
|
|
12394
|
+
return stripFrontmatter(raw);
|
|
12395
|
+
} catch {
|
|
12396
|
+
return null;
|
|
12397
|
+
}
|
|
12398
|
+
}
|
|
12399
|
+
var AUTO_BUDGET_CHARS, PINNED_BUDGET_CHARS, TOTAL_BUDGET_CHARS, MIN_SCORE, injectedSkillsMap, SKIP_COMPLEXITY, STOPWORDS, tokenize, tokenizeAll;
|
|
12400
|
+
var init_inject2 = __esm({
|
|
12401
|
+
"src/skills/inject.ts"() {
|
|
12402
|
+
"use strict";
|
|
12403
|
+
init_complexity();
|
|
12404
|
+
init_discover();
|
|
12405
|
+
init_text_utils();
|
|
12406
|
+
init_log();
|
|
12407
|
+
AUTO_BUDGET_CHARS = 3e3;
|
|
12408
|
+
PINNED_BUDGET_CHARS = 5e3;
|
|
12409
|
+
TOTAL_BUDGET_CHARS = 8e3;
|
|
12410
|
+
MIN_SCORE = 2;
|
|
12411
|
+
injectedSkillsMap = /* @__PURE__ */ new Map();
|
|
12412
|
+
SKIP_COMPLEXITY = /* @__PURE__ */ new Set(["trivial", "simple"]);
|
|
12413
|
+
STOPWORDS = /* @__PURE__ */ new Set([
|
|
12414
|
+
"about",
|
|
12415
|
+
"after",
|
|
12416
|
+
"also",
|
|
12417
|
+
"been",
|
|
12418
|
+
"before",
|
|
12419
|
+
"being",
|
|
12420
|
+
"between",
|
|
12421
|
+
"both",
|
|
12422
|
+
"could",
|
|
12423
|
+
"does",
|
|
12424
|
+
"doing",
|
|
12425
|
+
"done",
|
|
12426
|
+
"down",
|
|
12427
|
+
"during",
|
|
12428
|
+
"each",
|
|
12429
|
+
"even",
|
|
12430
|
+
"every",
|
|
12431
|
+
"from",
|
|
12432
|
+
"have",
|
|
12433
|
+
"having",
|
|
12434
|
+
"help",
|
|
12435
|
+
"here",
|
|
12436
|
+
"into",
|
|
12437
|
+
"just",
|
|
12438
|
+
"know",
|
|
12439
|
+
"like",
|
|
12440
|
+
"make",
|
|
12441
|
+
"more",
|
|
12442
|
+
"most",
|
|
12443
|
+
"much",
|
|
12444
|
+
"need",
|
|
12445
|
+
"only",
|
|
12446
|
+
"other",
|
|
12447
|
+
"over",
|
|
12448
|
+
"some",
|
|
12449
|
+
"such",
|
|
12450
|
+
"than",
|
|
12451
|
+
"that",
|
|
12452
|
+
"their",
|
|
12453
|
+
"them",
|
|
12454
|
+
"then",
|
|
12455
|
+
"there",
|
|
12456
|
+
"these",
|
|
12457
|
+
"they",
|
|
12458
|
+
"this",
|
|
12459
|
+
"through",
|
|
12460
|
+
"very",
|
|
12461
|
+
"want",
|
|
12462
|
+
"well",
|
|
12463
|
+
"were",
|
|
12464
|
+
"what",
|
|
12465
|
+
"when",
|
|
12466
|
+
"where",
|
|
12467
|
+
"which",
|
|
12468
|
+
"while",
|
|
12469
|
+
"will",
|
|
12470
|
+
"with",
|
|
12471
|
+
"would",
|
|
12472
|
+
"your"
|
|
12473
|
+
]);
|
|
12474
|
+
tokenize = (text) => tokenizeText(text, 4);
|
|
12475
|
+
tokenizeAll = (text) => tokenizeText(text, 1);
|
|
12476
|
+
}
|
|
12477
|
+
});
|
|
12478
|
+
|
|
12479
|
+
// src/bootstrap/loader.ts
|
|
12480
|
+
import { readFileSync as readFileSync11, readdirSync as readdirSync6 } from "fs";
|
|
12481
|
+
import { join as join15 } from "path";
|
|
11728
12482
|
function loadMarkdownDir(dir, cacheRef, wordSource = (c) => c) {
|
|
11729
12483
|
if (cacheRef.v && Date.now() - cacheRef.v.timestamp < CACHE_TTL_MS4) {
|
|
11730
12484
|
return cacheRef.v.files;
|
|
@@ -11734,8 +12488,8 @@ function loadMarkdownDir(dir, cacheRef, wordSource = (c) => c) {
|
|
|
11734
12488
|
for (const entry of readdirSync6(dir)) {
|
|
11735
12489
|
if (!entry.endsWith(".md")) continue;
|
|
11736
12490
|
try {
|
|
11737
|
-
const content =
|
|
11738
|
-
if (content) files.set(entry, { content, words:
|
|
12491
|
+
const content = readFileSync11(join15(dir, entry), "utf-8").trim();
|
|
12492
|
+
if (content) files.set(entry, { content, words: tokenize2(wordSource(content)) });
|
|
11739
12493
|
} catch {
|
|
11740
12494
|
continue;
|
|
11741
12495
|
}
|
|
@@ -11746,7 +12500,7 @@ function loadMarkdownDir(dir, cacheRef, wordSource = (c) => c) {
|
|
|
11746
12500
|
return files;
|
|
11747
12501
|
}
|
|
11748
12502
|
function findBestMatch(userMessage, files, opts) {
|
|
11749
|
-
const msgWords =
|
|
12503
|
+
const msgWords = tokenize2(userMessage);
|
|
11750
12504
|
if (msgWords.size === 0) return null;
|
|
11751
12505
|
let best = null;
|
|
11752
12506
|
for (const entry of files.values()) {
|
|
@@ -11769,16 +12523,6 @@ function searchContext(userMessage) {
|
|
|
11769
12523
|
{ maxChars: MAX_CONTEXT_CHARS }
|
|
11770
12524
|
);
|
|
11771
12525
|
}
|
|
11772
|
-
function searchSkill(userMessage) {
|
|
11773
|
-
return findBestMatch(
|
|
11774
|
-
userMessage,
|
|
11775
|
-
loadMarkdownDir(SKILLS_DIR, skillCacheRef, stripFrontmatter3),
|
|
11776
|
-
{
|
|
11777
|
-
maxChars: MAX_SKILL_CHARS,
|
|
11778
|
-
filter: ({ content }) => content.includes("status: approved") || content.includes("status: imported")
|
|
11779
|
-
}
|
|
11780
|
-
);
|
|
11781
|
-
}
|
|
11782
12526
|
async function assembleBootstrapPrompt(userMessage, entityType = "main", profile = "interactive", chatId, permMode, responseStyle, agentMode, sideQuestContext, planningDirective, chatContext, backendType) {
|
|
11783
12527
|
const sections = [];
|
|
11784
12528
|
if (planningDirective) {
|
|
@@ -11792,15 +12536,24 @@ async function assembleBootstrapPrompt(userMessage, entityType = "main", profile
|
|
|
11792
12536
|
sections.push(buildPermissionNotice(permMode));
|
|
11793
12537
|
}
|
|
11794
12538
|
if (backendType === "api") {
|
|
11795
|
-
|
|
12539
|
+
if (!apiHarnessCache.main || Date.now() - apiHarnessCache.cachedMs > 5e3) {
|
|
12540
|
+
try {
|
|
12541
|
+
apiHarnessCache.main = readFileSync11(join15(IDENTITY_PATH, "CC-CLAW-api.md"), "utf-8");
|
|
12542
|
+
apiHarnessCache.task = readFileSync11(join15(IDENTITY_PATH, "CC-CLAW-api-task.md"), "utf-8");
|
|
12543
|
+
apiHarnessCache.cachedMs = Date.now();
|
|
12544
|
+
} catch {
|
|
12545
|
+
}
|
|
12546
|
+
}
|
|
12547
|
+
const harness = entityType === "task" ? apiHarnessCache.task : apiHarnessCache.main;
|
|
12548
|
+
if (harness) {
|
|
12549
|
+
sections.push(harness);
|
|
12550
|
+
} else {
|
|
12551
|
+
warn("[bootstrap] API harness files not yet generated, using fallback");
|
|
12552
|
+
sections.push(`[API Backend \u2014 Tool Usage]
|
|
11796
12553
|
You are operating as a direct API model (not a CLI like Claude Code or Gemini CLI).
|
|
11797
|
-
External tools
|
|
11798
|
-
NEVER call external CLIs as direct tools by name \u2014 they are not registered as native tools.
|
|
11799
|
-
Correct: restrictedBash({"command": "gsearch \\"query\\" --type news"})
|
|
11800
|
-
Incorrect: gsearch({"query": "..."}) \u2190 this will fail silently
|
|
11801
|
-
If a skill or instruction says to use a CLI tool, always route it through restrictedBash.
|
|
11802
|
-
|
|
12554
|
+
External tools are accessed ONLY via the \`restrictedBash\` tool. NEVER call external CLIs as direct tool names.
|
|
11803
12555
|
${buildCliEnvironment()}`);
|
|
12556
|
+
}
|
|
11804
12557
|
}
|
|
11805
12558
|
if (responseStyle) {
|
|
11806
12559
|
if (responseStyle === "concise") {
|
|
@@ -11830,10 +12583,23 @@ ${ctx}`);
|
|
|
11830
12583
|
}
|
|
11831
12584
|
}
|
|
11832
12585
|
if (profile === "interactive" || profile === "chat") {
|
|
11833
|
-
const
|
|
11834
|
-
|
|
11835
|
-
|
|
11836
|
-
|
|
12586
|
+
const pinnedSkills = chatId ? getPinnedSkills(chatId) : [];
|
|
12587
|
+
const userExplicitNative = agentMode === "native" && !sideQuestContext;
|
|
12588
|
+
try {
|
|
12589
|
+
const allSkills = await discoverAllSkills();
|
|
12590
|
+
const skillBlock = injectSkillContext(
|
|
12591
|
+
userMessage,
|
|
12592
|
+
allSkills,
|
|
12593
|
+
pinnedSkills,
|
|
12594
|
+
profile,
|
|
12595
|
+
userExplicitNative,
|
|
12596
|
+
chatId
|
|
12597
|
+
);
|
|
12598
|
+
if (skillBlock) {
|
|
12599
|
+
sections.push(skillBlock);
|
|
12600
|
+
}
|
|
12601
|
+
} catch (err) {
|
|
12602
|
+
warn(`[bootstrap] Skill injection failed: ${err instanceof Error ? err.message : err}`);
|
|
11837
12603
|
}
|
|
11838
12604
|
}
|
|
11839
12605
|
if (chatId && profile !== "minimal" && profile !== "chat") {
|
|
@@ -12001,7 +12767,7 @@ ${parts.join("\n")}${MEMORY_DISCIPLINE}`;
|
|
|
12001
12767
|
}
|
|
12002
12768
|
return null;
|
|
12003
12769
|
}
|
|
12004
|
-
var lastSyncMs, CONTEXT_DIR2, SKILLS_DIR, MAX_CONTEXT_CHARS,
|
|
12770
|
+
var lastSyncMs, apiHarnessCache, CONTEXT_DIR2, SKILLS_DIR, MAX_CONTEXT_CHARS, CACHE_TTL_MS4, tokenize2, contextCacheRef, ACTIVITY_TOKEN_BUDGET, INBOX_TOKEN_BUDGET, WHITEBOARD_TOKEN_BUDGET;
|
|
12005
12771
|
var init_loader2 = __esm({
|
|
12006
12772
|
"src/bootstrap/loader.ts"() {
|
|
12007
12773
|
"use strict";
|
|
@@ -12014,14 +12780,22 @@ var init_loader2 = __esm({
|
|
|
12014
12780
|
init_store5();
|
|
12015
12781
|
init_store();
|
|
12016
12782
|
init_backends();
|
|
12783
|
+
init_inject2();
|
|
12784
|
+
init_discover();
|
|
12785
|
+
init_chat_settings();
|
|
12786
|
+
init_text_utils();
|
|
12017
12787
|
lastSyncMs = 0;
|
|
12788
|
+
apiHarnessCache = {
|
|
12789
|
+
main: null,
|
|
12790
|
+
task: null,
|
|
12791
|
+
cachedMs: 0
|
|
12792
|
+
};
|
|
12018
12793
|
CONTEXT_DIR2 = join15(WORKSPACE_PATH, "context");
|
|
12019
12794
|
SKILLS_DIR = join15(WORKSPACE_PATH, "skills");
|
|
12020
12795
|
MAX_CONTEXT_CHARS = 4e3;
|
|
12021
|
-
MAX_SKILL_CHARS = 6e3;
|
|
12022
12796
|
CACHE_TTL_MS4 = 3e4;
|
|
12797
|
+
tokenize2 = (text) => tokenizeText(text, 4);
|
|
12023
12798
|
contextCacheRef = { v: null };
|
|
12024
|
-
skillCacheRef = { v: null };
|
|
12025
12799
|
ACTIVITY_TOKEN_BUDGET = 1500;
|
|
12026
12800
|
INBOX_TOKEN_BUDGET = 2e3;
|
|
12027
12801
|
WHITEBOARD_TOKEN_BUDGET = 500;
|
|
@@ -12056,7 +12830,8 @@ __export(client_exports, {
|
|
|
12056
12830
|
listModels: () => listModels,
|
|
12057
12831
|
ping: () => ping,
|
|
12058
12832
|
runningModels: () => runningModels,
|
|
12059
|
-
showModel: () => showModel
|
|
12833
|
+
showModel: () => showModel,
|
|
12834
|
+
unloadModel: () => unloadModel
|
|
12060
12835
|
});
|
|
12061
12836
|
function buildHeaders(apiKey) {
|
|
12062
12837
|
const headers = {
|
|
@@ -12142,6 +12917,30 @@ async function deleteModel(baseUrl, modelName, opts) {
|
|
|
12142
12917
|
clearTimeout(timeout);
|
|
12143
12918
|
}
|
|
12144
12919
|
}
|
|
12920
|
+
async function unloadModel(baseUrl, modelName, opts) {
|
|
12921
|
+
const controller = new AbortController();
|
|
12922
|
+
const timeout = setTimeout(
|
|
12923
|
+
() => controller.abort(),
|
|
12924
|
+
opts?.timeoutMs ?? 5e3
|
|
12925
|
+
// short timeout — unload is best-effort
|
|
12926
|
+
);
|
|
12927
|
+
try {
|
|
12928
|
+
const res = await fetch(`${baseUrl}/api/generate`, {
|
|
12929
|
+
method: "POST",
|
|
12930
|
+
headers: buildHeaders(opts?.apiKey),
|
|
12931
|
+
body: JSON.stringify({ model: modelName, keep_alive: 0 }),
|
|
12932
|
+
signal: controller.signal
|
|
12933
|
+
});
|
|
12934
|
+
if (!res.ok) {
|
|
12935
|
+
const body = await res.text().catch(() => "");
|
|
12936
|
+
throw new Error(`Ollama unload failed (${res.status}): ${body}`);
|
|
12937
|
+
}
|
|
12938
|
+
await res.text().catch(() => {
|
|
12939
|
+
});
|
|
12940
|
+
} finally {
|
|
12941
|
+
clearTimeout(timeout);
|
|
12942
|
+
}
|
|
12943
|
+
}
|
|
12145
12944
|
async function listModels(baseUrl, opts) {
|
|
12146
12945
|
const res = await fetch(`${baseUrl}/api/tags`, {
|
|
12147
12946
|
signal: opts?.signal,
|
|
@@ -12829,7 +13628,9 @@ __export(service_exports, {
|
|
|
12829
13628
|
isAnyServerOnline: () => isAnyServerOnline,
|
|
12830
13629
|
listServers: () => listServers2,
|
|
12831
13630
|
removeServer: () => removeServer2,
|
|
12832
|
-
runQualityGateTest: () => runQualityGateTest
|
|
13631
|
+
runQualityGateTest: () => runQualityGateTest,
|
|
13632
|
+
tryUnloadModel: () => tryUnloadModel,
|
|
13633
|
+
unloadModel: () => unloadModel2
|
|
12833
13634
|
});
|
|
12834
13635
|
function addServer2(name, host, port = 11434, apiKey) {
|
|
12835
13636
|
const existing = getServer(name);
|
|
@@ -13019,6 +13820,17 @@ function getServerForModel(modelName) {
|
|
|
13019
13820
|
apiKey: server.apiKey
|
|
13020
13821
|
};
|
|
13021
13822
|
}
|
|
13823
|
+
async function unloadModel2(modelName) {
|
|
13824
|
+
const serverInfo = getServerForModel(modelName);
|
|
13825
|
+
if (!serverInfo) return;
|
|
13826
|
+
await unloadModel(serverInfo.baseUrl, modelName, { apiKey: serverInfo.apiKey });
|
|
13827
|
+
log(`[ollama] Unloaded model: ${modelName}`);
|
|
13828
|
+
}
|
|
13829
|
+
function tryUnloadModel(modelName) {
|
|
13830
|
+
unloadModel2(modelName).catch((err) => {
|
|
13831
|
+
warn(`[ollama] Model unload failed for ${modelName}: ${err}`);
|
|
13832
|
+
});
|
|
13833
|
+
}
|
|
13022
13834
|
function extractContextWindow(show) {
|
|
13023
13835
|
const info = show.model_info ?? {};
|
|
13024
13836
|
for (const key of Object.keys(info)) {
|
|
@@ -13382,7 +14194,7 @@ async function summarizeWithFallbackChain(chatId, targetBackendId, excludeBacken
|
|
|
13382
14194
|
const cap = getOllamaTranscriptCap(ollamaModel);
|
|
13383
14195
|
const key = `${ollamaAdapter.id}:${ollamaModel}`;
|
|
13384
14196
|
tried.add(key);
|
|
13385
|
-
const directFn = (prompt) => ollamaAdapter.streamDirect(prompt, ollamaModel);
|
|
14197
|
+
const directFn = (prompt) => ollamaAdapter.streamDirect(prompt, ollamaModel, { thinkingLevel: "off" });
|
|
13386
14198
|
const result = await attemptSummarizeDirect(chatId, directFn, "ollama", ollamaModel, entries, cap);
|
|
13387
14199
|
if (result.success) {
|
|
13388
14200
|
await extractAndLogSignals(result.rawText, chatId, "ollama", ollamaModel);
|
|
@@ -13400,7 +14212,7 @@ async function summarizeWithFallbackChain(chatId, targetBackendId, excludeBacken
|
|
|
13400
14212
|
const key = `${targetAdapter.id}:${model2}`;
|
|
13401
14213
|
if (!tried.has(key)) {
|
|
13402
14214
|
tried.add(key);
|
|
13403
|
-
const result = targetAdapter.streamDirect ? await attemptSummarizeDirect(chatId, (p) => targetAdapter.streamDirect(p, model2), targetAdapter.id, model2, entries, getTranscriptCap(model2)) : await attemptSummarize(chatId, targetAdapter, model2, entries);
|
|
14215
|
+
const result = targetAdapter.streamDirect ? await attemptSummarizeDirect(chatId, (p) => targetAdapter.streamDirect(p, model2, { thinkingLevel: "off" }), targetAdapter.id, model2, entries, getTranscriptCap(model2)) : await attemptSummarize(chatId, targetAdapter, model2, entries);
|
|
13404
14216
|
if (result.success) {
|
|
13405
14217
|
await extractAndLogSignals(result.rawText, chatId, targetAdapter.id, model2);
|
|
13406
14218
|
if (clearLogAfter) clearLog(chatId);
|
|
@@ -13418,7 +14230,7 @@ async function summarizeWithFallbackChain(chatId, targetBackendId, excludeBacken
|
|
|
13418
14230
|
const key = `${adapter.id}:${model2}`;
|
|
13419
14231
|
if (!tried.has(key)) {
|
|
13420
14232
|
tried.add(key);
|
|
13421
|
-
const result = adapter.streamDirect ? await attemptSummarizeDirect(chatId, (p) => adapter.streamDirect(p, model2), adapter.id, model2, entries, adapter.id === "ollama" ? getOllamaTranscriptCap(model2) : getTranscriptCap(model2)) : await attemptSummarize(chatId, adapter, model2, entries);
|
|
14233
|
+
const result = adapter.streamDirect ? await attemptSummarizeDirect(chatId, (p) => adapter.streamDirect(p, model2, { thinkingLevel: "off" }), adapter.id, model2, entries, adapter.id === "ollama" ? getOllamaTranscriptCap(model2) : getTranscriptCap(model2)) : await attemptSummarize(chatId, adapter, model2, entries);
|
|
13422
14234
|
if (result.success) {
|
|
13423
14235
|
await extractAndLogSignals(result.rawText, chatId, adapter.id, model2);
|
|
13424
14236
|
if (clearLogAfter) clearLog(chatId);
|
|
@@ -13440,7 +14252,7 @@ async function summarizeWithFallbackChain(chatId, targetBackendId, excludeBacken
|
|
|
13440
14252
|
let result;
|
|
13441
14253
|
if (adapter.streamDirect) {
|
|
13442
14254
|
const cap = adapter.id === "ollama" ? getOllamaTranscriptCap(model2) : getTranscriptCap(model2);
|
|
13443
|
-
const directFn = (prompt) => adapter.streamDirect(prompt, model2);
|
|
14255
|
+
const directFn = (prompt) => adapter.streamDirect(prompt, model2, { thinkingLevel: "off" });
|
|
13444
14256
|
result = await attemptSummarizeDirect(chatId, directFn, adapter.id, model2, entries, cap);
|
|
13445
14257
|
} else {
|
|
13446
14258
|
result = await attemptSummarize(chatId, adapter, model2, entries);
|
|
@@ -13817,6 +14629,7 @@ function classifyReaction(emoji) {
|
|
|
13817
14629
|
return null;
|
|
13818
14630
|
}
|
|
13819
14631
|
async function detectAndLogSignals(chatId, userMessage, agentResponse, ctx = {}) {
|
|
14632
|
+
const injectedSkills = getAndClearInjectedSkills(chatId);
|
|
13820
14633
|
const db3 = getDb();
|
|
13821
14634
|
const status = getReflectionStatus(db3, chatId);
|
|
13822
14635
|
if (status === "frozen") return;
|
|
@@ -13824,18 +14637,29 @@ async function detectAndLogSignals(chatId, userMessage, agentResponse, ctx = {})
|
|
|
13824
14637
|
if (recentCount >= RATE_LIMIT_MAX_SIGNALS) return;
|
|
13825
14638
|
const signals = classifySignals(userMessage, agentResponse);
|
|
13826
14639
|
const context = agentResponse.slice(0, 500) || null;
|
|
14640
|
+
const uniqueByType = /* @__PURE__ */ new Map();
|
|
13827
14641
|
for (const signal of signals) {
|
|
13828
|
-
|
|
13829
|
-
|
|
13830
|
-
|
|
13831
|
-
|
|
13832
|
-
|
|
13833
|
-
|
|
13834
|
-
|
|
13835
|
-
|
|
13836
|
-
|
|
13837
|
-
|
|
13838
|
-
|
|
14642
|
+
const existing = uniqueByType.get(signal.signalType);
|
|
14643
|
+
if (!existing || signal.confidence > existing.confidence) {
|
|
14644
|
+
uniqueByType.set(signal.signalType, signal);
|
|
14645
|
+
}
|
|
14646
|
+
}
|
|
14647
|
+
const skillList = injectedSkills.length > 0 ? injectedSkills : [void 0];
|
|
14648
|
+
for (const signal of uniqueByType.values()) {
|
|
14649
|
+
for (const skillName of skillList) {
|
|
14650
|
+
logSignal(db3, {
|
|
14651
|
+
chatId,
|
|
14652
|
+
signalType: signal.signalType,
|
|
14653
|
+
trigger: signal.trigger,
|
|
14654
|
+
context,
|
|
14655
|
+
source: signal.source,
|
|
14656
|
+
confidence: signal.confidence,
|
|
14657
|
+
backend: ctx.backendId ?? null,
|
|
14658
|
+
model: ctx.model ?? null,
|
|
14659
|
+
thinkingLevel: ctx.thinkingLevel ?? null,
|
|
14660
|
+
skillName
|
|
14661
|
+
});
|
|
14662
|
+
}
|
|
13839
14663
|
}
|
|
13840
14664
|
}
|
|
13841
14665
|
function logReactionSignal(chatId, emoji) {
|
|
@@ -13878,6 +14702,7 @@ var init_detect = __esm({
|
|
|
13878
14702
|
"use strict";
|
|
13879
14703
|
init_store4();
|
|
13880
14704
|
init_store5();
|
|
14705
|
+
init_inject2();
|
|
13881
14706
|
CORRECTION_PATTERNS = [
|
|
13882
14707
|
// Audit O36: Tightened — require "no" followed by correction context (not "No problem", "No worries")
|
|
13883
14708
|
/^no[,.\s!]+\s*(that'?s|it'?s|i\s|you|this|the|not|don'?t|stop|wrong)/i,
|
|
@@ -13953,6 +14778,19 @@ function killProcessGroup(proc, signal = "SIGTERM") {
|
|
|
13953
14778
|
}
|
|
13954
14779
|
}
|
|
13955
14780
|
}
|
|
14781
|
+
function runCompaction(chatId, reason, onCompaction) {
|
|
14782
|
+
return summarizeWithFallbackChain(chatId).then((saved) => {
|
|
14783
|
+
if (saved) {
|
|
14784
|
+
return withChatLock(chatId, async () => {
|
|
14785
|
+
clearSession(chatId);
|
|
14786
|
+
clearUsage(chatId);
|
|
14787
|
+
onCompaction?.(chatId);
|
|
14788
|
+
});
|
|
14789
|
+
}
|
|
14790
|
+
}).catch((err) => {
|
|
14791
|
+
warn(`[agent] Compaction failed for ${chatId} (${reason}): ${err}`);
|
|
14792
|
+
});
|
|
14793
|
+
}
|
|
13956
14794
|
function sweepStaleChatEntries() {
|
|
13957
14795
|
for (const [chatId, state] of activeChats) {
|
|
13958
14796
|
if (state.process && state.process.exitCode !== null) {
|
|
@@ -14010,6 +14848,13 @@ function stopAgent(chatId) {
|
|
|
14010
14848
|
if (state.process) killProcessGroup(state.process, "SIGKILL");
|
|
14011
14849
|
}, 2e3);
|
|
14012
14850
|
}
|
|
14851
|
+
const modelToUnload = state.model;
|
|
14852
|
+
if (state.backendId === "ollama" && modelToUnload) {
|
|
14853
|
+
Promise.resolve().then(() => (init_ollama(), ollama_exports)).then(({ OllamaService }) => {
|
|
14854
|
+
OllamaService.tryUnloadModel(modelToUnload);
|
|
14855
|
+
}).catch(() => {
|
|
14856
|
+
});
|
|
14857
|
+
}
|
|
14013
14858
|
return true;
|
|
14014
14859
|
}
|
|
14015
14860
|
function getGeminiFallback(model2) {
|
|
@@ -14021,7 +14866,8 @@ function spawnQuery(adapter, config2, model2, cancelState, thinkingLevel, timeou
|
|
|
14021
14866
|
const thinkingConfig = thinkingLevel && thinkingLevel !== "auto" ? adapter.applyThinkingConfig(thinkingLevel, model2) : void 0;
|
|
14022
14867
|
const env = opts?.envOverride ? thinkingConfig?.envOverrides ? { ...opts.envOverride, ...thinkingConfig.envOverrides } : opts.envOverride : adapter.getEnv(thinkingConfig?.envOverrides);
|
|
14023
14868
|
const finalArgs = thinkingConfig?.extraArgs ? [...config2.args, ...thinkingConfig.extraArgs] : config2.args;
|
|
14024
|
-
|
|
14869
|
+
const hasResume = finalArgs.includes("--resume");
|
|
14870
|
+
log(`[agent:spawn] backend=${adapter.id} exe=${config2.executable} model=${model2} timeout=${effectiveTimeout / 1e3}s resume=${hasResume} cwd=${config2.cwd ?? "(inherited)"}`);
|
|
14025
14871
|
const stdinMode = adapter.id === "codex" ? "pipe" : "ignore";
|
|
14026
14872
|
const proc = spawn4(config2.executable, finalArgs, {
|
|
14027
14873
|
env,
|
|
@@ -14130,7 +14976,7 @@ function spawnQuery(adapter, config2, model2, cancelState, thinkingLevel, timeou
|
|
|
14130
14976
|
for (const ev of events) {
|
|
14131
14977
|
switch (ev.type) {
|
|
14132
14978
|
case "init":
|
|
14133
|
-
log(`[agent] Session init at ${elapsed()}`);
|
|
14979
|
+
log(`[agent] Session init at ${elapsed()} sessionId=${ev.sessionId ?? "(none)"}`);
|
|
14134
14980
|
if (ev.sessionId) sessionId = ev.sessionId;
|
|
14135
14981
|
resetContentSilenceTimer();
|
|
14136
14982
|
break;
|
|
@@ -14228,7 +15074,6 @@ function spawnQuery(adapter, config2, model2, cancelState, thinkingLevel, timeou
|
|
|
14228
15074
|
input += ev.usage.input;
|
|
14229
15075
|
output2 += ev.usage.output;
|
|
14230
15076
|
cacheRead += ev.usage.cacheRead;
|
|
14231
|
-
contextSize = ev.usage.input + (ev.usage.cacheRead ?? 0);
|
|
14232
15077
|
}
|
|
14233
15078
|
break;
|
|
14234
15079
|
case "result":
|
|
@@ -14246,7 +15091,6 @@ function spawnQuery(adapter, config2, model2, cancelState, thinkingLevel, timeou
|
|
|
14246
15091
|
input = ev.usage.input;
|
|
14247
15092
|
output2 = ev.usage.output;
|
|
14248
15093
|
cacheRead = ev.usage.cacheRead;
|
|
14249
|
-
contextSize = ev.usage.input + (ev.usage.cacheRead ?? 0);
|
|
14250
15094
|
}
|
|
14251
15095
|
if (adapter.shouldKillOnResult()) {
|
|
14252
15096
|
try {
|
|
@@ -14329,7 +15173,8 @@ Partial output: ${accumulatedText.slice(-500)}`;
|
|
|
14329
15173
|
return;
|
|
14330
15174
|
}
|
|
14331
15175
|
const cleanedResult = stripThinkingContent(resultText || accumulatedText);
|
|
14332
|
-
|
|
15176
|
+
log(`[agent:spawn] Resolving: sessionId=${sessionId ?? "(none)"} hasText=${!!cleanedResult} code=${code}`);
|
|
15177
|
+
resolve3({ resultText: cleanedResult, thinkingText: accumulatedThinking, sessionId, input, output: output2, cacheRead, contextSize: null, sawToolEvents, sawResultEvent });
|
|
14333
15178
|
});
|
|
14334
15179
|
});
|
|
14335
15180
|
}
|
|
@@ -14537,20 +15382,23 @@ async function askAgentImpl(chatId, userMessage, opts) {
|
|
|
14537
15382
|
const responseStyle = getResponseStyle(settingsChat);
|
|
14538
15383
|
const thinkingLevel = opts?.thinkingLevel ?? getThinkingLevel(settingsChat);
|
|
14539
15384
|
const resolvedCwd = cwd ?? WORKSPACE_PATH;
|
|
14540
|
-
const { entityType, bootstrapProfile: profile } = optsEntityType && optsProfile ? { entityType: optsEntityType, bootstrapProfile: optsProfile } : resolveFromLegacyTier(bootstrapTier ?? "full");
|
|
15385
|
+
const { entityType, bootstrapProfile: profile } = optsEntityType && optsProfile ? { entityType: optsEntityType, bootstrapProfile: optsProfile } : optsProfile ? { entityType: "main", bootstrapProfile: optsProfile } : resolveFromLegacyTier(bootstrapTier ?? "full");
|
|
14541
15386
|
const effectiveAgentMode = optsAgentMode ?? getAgentMode(settingsChat);
|
|
14542
15387
|
const sideQuestCtx = settingsSourceChatId ? { parentChatId: settingsSourceChatId, actualChatId: chatId } : void 0;
|
|
14543
15388
|
const fullPrompt = await assembleBootstrapPrompt(userMessage, entityType, profile, settingsChat, mode, responseStyle, effectiveAgentMode, sideQuestCtx, planningDirective, opts?.chatContext, adapter.type);
|
|
14544
15389
|
if (adapter.streamDirect) {
|
|
14545
15390
|
const resolvedModel2 = model2 ?? adapter.defaultModel;
|
|
14546
15391
|
const abortController = new AbortController();
|
|
14547
|
-
const cancelState2 = { cancelled: false, userMessage, abortController };
|
|
15392
|
+
const cancelState2 = { cancelled: false, userMessage, abortController, backendId: adapter.id, model: resolvedModel2 };
|
|
14548
15393
|
activeChats.set(chatId, cancelState2);
|
|
14549
15394
|
try {
|
|
14550
15395
|
let messageHistory;
|
|
15396
|
+
let apiContextSize;
|
|
14551
15397
|
if (adapter.type === "api") {
|
|
14552
15398
|
const contextWindow = adapter.contextWindow[resolvedModel2] ?? 8192;
|
|
14553
|
-
|
|
15399
|
+
const { buildApiMessages: buildMsgs, estimateContextUsage: estimateContextUsage2 } = await Promise.resolve().then(() => (init_api_context(), api_context_exports));
|
|
15400
|
+
messageHistory = await buildMsgs(chatId, userMessage, fullPrompt, contextWindow);
|
|
15401
|
+
apiContextSize = estimateContextUsage2(chatId, contextWindow).estimatedTokens;
|
|
14554
15402
|
}
|
|
14555
15403
|
const sdResult = await adapter.streamDirect(fullPrompt, resolvedModel2, {
|
|
14556
15404
|
timeoutMs: timeoutMs ?? 3e5,
|
|
@@ -14566,23 +15414,29 @@ async function askAgentImpl(chatId, userMessage, opts) {
|
|
|
14566
15414
|
});
|
|
14567
15415
|
if (!isSyntheticChatId(chatId)) {
|
|
14568
15416
|
appendToLog(chatId, userMessage, sdResult.text, adapter.id, resolvedModel2, null);
|
|
14569
|
-
|
|
14570
|
-
|
|
14571
|
-
|
|
14572
|
-
|
|
14573
|
-
|
|
14574
|
-
|
|
14575
|
-
|
|
14576
|
-
|
|
14577
|
-
|
|
14578
|
-
|
|
14579
|
-
|
|
14580
|
-
|
|
15417
|
+
if (apiContextSize && adapter.type === "api" && !compactionInFlight.has(chatId)) {
|
|
15418
|
+
const contextWindow = adapter.contextWindow[resolvedModel2] ?? 8192;
|
|
15419
|
+
const contextPct = apiContextSize / contextWindow * 100;
|
|
15420
|
+
if (contextPct >= 85) {
|
|
15421
|
+
compactionInFlight.add(chatId);
|
|
15422
|
+
log(`[agent] Context at ${contextPct.toFixed(0)}% for ${chatId} \u2014 triggering background compaction`);
|
|
15423
|
+
opts?.onCompaction?.(chatId, "triggered");
|
|
15424
|
+
runCompaction(chatId, "context-85%", opts?.onCompaction).finally(() => {
|
|
15425
|
+
compactionInFlight.delete(chatId);
|
|
15426
|
+
});
|
|
15427
|
+
}
|
|
15428
|
+
}
|
|
15429
|
+
if (adapter.type !== "api" || !compactionInFlight.has(chatId)) {
|
|
15430
|
+
const pairCount = profile !== "chat" ? getMessagePairCount(chatId) : 0;
|
|
15431
|
+
if (pairCount >= 30) {
|
|
15432
|
+
log(`[agent] Auto-summarizing chat ${chatId} after ${pairCount} turns`);
|
|
15433
|
+
runCompaction(chatId, "30-pair-threshold", opts?.onCompaction);
|
|
15434
|
+
}
|
|
14581
15435
|
}
|
|
14582
15436
|
}
|
|
14583
15437
|
const sdUsage = sdResult.usage ?? { input: 0, output: 0 };
|
|
14584
15438
|
if (sdUsage.input + sdUsage.output > 0) {
|
|
14585
|
-
addUsage(chatId, sdUsage.input, sdUsage.output, 0, resolvedModel2);
|
|
15439
|
+
addUsage(chatId, sdUsage.input, sdUsage.output, 0, resolvedModel2, adapter.id, apiContextSize);
|
|
14586
15440
|
}
|
|
14587
15441
|
if (cancelState2.cancelled) {
|
|
14588
15442
|
return { text: "Stopped.", usage: { input: sdUsage.input, output: sdUsage.output, cacheRead: 0 } };
|
|
@@ -14595,9 +15449,16 @@ async function askAgentImpl(chatId, userMessage, opts) {
|
|
|
14595
15449
|
};
|
|
14596
15450
|
} finally {
|
|
14597
15451
|
activeChats.delete(chatId);
|
|
15452
|
+
if (adapter.id === "ollama" && !cancelState2.cancelled) {
|
|
15453
|
+
Promise.resolve().then(() => (init_ollama(), ollama_exports)).then(({ OllamaService }) => {
|
|
15454
|
+
OllamaService.tryUnloadModel(resolvedModel2);
|
|
15455
|
+
}).catch(() => {
|
|
15456
|
+
});
|
|
15457
|
+
}
|
|
14598
15458
|
}
|
|
14599
15459
|
}
|
|
14600
15460
|
const existingSessionId = settingsSourceChatId ? null : getSessionId(settingsChat);
|
|
15461
|
+
log(`[agent:session] chatId=${chatId} settingsChat=${settingsChat} existingSessionId=${existingSessionId ?? "(none)"} backend=${adapter.id}`);
|
|
14601
15462
|
const allowedTools = getEnabledTools(settingsChat);
|
|
14602
15463
|
const mcpConfigPath = profile !== "minimal" && effectiveAgentMode !== "native" && MCP_CONFIG_FLAG[adapter.id] ? getMcpConfigPath(chatId) : null;
|
|
14603
15464
|
const baseConfig = adapter.buildSpawnConfig({
|
|
@@ -14836,7 +15697,10 @@ async function askAgentImpl(chatId, userMessage, opts) {
|
|
|
14836
15697
|
return { text: "Stopped.", usage: { input: result.input, output: result.output, cacheRead: result.cacheRead, contextSize: result.contextSize } };
|
|
14837
15698
|
}
|
|
14838
15699
|
if (result.sessionId && !isSyntheticChatId(chatId)) {
|
|
15700
|
+
log(`[agent:session] Persisting session for chatId=${chatId}: ${result.sessionId}`);
|
|
14839
15701
|
setSessionId(chatId, result.sessionId);
|
|
15702
|
+
} else if (!result.sessionId) {
|
|
15703
|
+
log(`[agent:session] No sessionId in result for chatId=${chatId} \u2014 session will not resume`);
|
|
14840
15704
|
}
|
|
14841
15705
|
if (adapter.id === BACKEND.CLAUDE && result.resultText && result.sessionId && /InputValidationError/i.test(result.resultText) && /missing|required parameter/i.test(result.resultText)) {
|
|
14842
15706
|
const toolMatch = result.resultText.match(/(?:parameter\s+'|required parameter\s+['"]?)([\w-]+)/i);
|
|
@@ -14884,17 +15748,12 @@ async function askAgentImpl(chatId, userMessage, opts) {
|
|
|
14884
15748
|
}
|
|
14885
15749
|
if (result.resultText && !isSyntheticChatId(chatId)) {
|
|
14886
15750
|
appendToLog(chatId, userMessage, result.resultText, adapter.id, model2 ?? null, result.sessionId ?? null);
|
|
14887
|
-
const AUTO_SUMMARIZE_THRESHOLD = 30;
|
|
14888
15751
|
const pairCount = profile !== "chat" ? getMessagePairCount(chatId) : 0;
|
|
14889
|
-
if (pairCount >=
|
|
15752
|
+
if (pairCount >= 30 && !compactionInFlight.has(chatId)) {
|
|
15753
|
+
compactionInFlight.add(chatId);
|
|
14890
15754
|
log(`[agent] Auto-summarizing chat ${chatId} after ${pairCount} turns`);
|
|
14891
|
-
|
|
14892
|
-
|
|
14893
|
-
clearSession(chatId);
|
|
14894
|
-
opts?.onCompaction?.(chatId);
|
|
14895
|
-
}
|
|
14896
|
-
}).catch((err) => {
|
|
14897
|
-
warn(`[agent] Auto-summarize failed for chat ${chatId}: ${err}`);
|
|
15755
|
+
runCompaction(chatId, "30-pair-threshold", opts?.onCompaction).finally(() => {
|
|
15756
|
+
compactionInFlight.delete(chatId);
|
|
14898
15757
|
});
|
|
14899
15758
|
}
|
|
14900
15759
|
}
|
|
@@ -14923,7 +15782,7 @@ function injectMcpConfig(adapterId, args, mcpConfigPath) {
|
|
|
14923
15782
|
if (!flag) return args;
|
|
14924
15783
|
return [...args, ...flag, mcpConfigPath, "--strict-mcp-config"];
|
|
14925
15784
|
}
|
|
14926
|
-
var activeChats, staleSweepTimer, chatLocks, SPAWN_TIMEOUT_MS, FIRST_RESPONSE_TIMEOUT_MS, CONTENT_SILENCE_TIMEOUT_MS, CONTENT_SILENCE_TIMEOUT_ERROR, FIRST_RESPONSE_TIMEOUT_ERROR, FREE_SLOTS_EXHAUSTED, GEMINI_FALLBACK_CHAIN, GEMINI_DOWNGRADE_MODELS, MCP_CONFIG_FLAG;
|
|
15785
|
+
var activeChats, compactionInFlight, staleSweepTimer, chatLocks, SPAWN_TIMEOUT_MS, FIRST_RESPONSE_TIMEOUT_MS, CONTENT_SILENCE_TIMEOUT_MS, CONTENT_SILENCE_TIMEOUT_ERROR, FIRST_RESPONSE_TIMEOUT_ERROR, FREE_SLOTS_EXHAUSTED, GEMINI_FALLBACK_CHAIN, GEMINI_DOWNGRADE_MODELS, MCP_CONFIG_FLAG;
|
|
14927
15786
|
var init_agent = __esm({
|
|
14928
15787
|
"src/agent.ts"() {
|
|
14929
15788
|
"use strict";
|
|
@@ -14940,7 +15799,6 @@ var init_agent = __esm({
|
|
|
14940
15799
|
init_strip_thinking();
|
|
14941
15800
|
init_text_utils();
|
|
14942
15801
|
init_session_log();
|
|
14943
|
-
init_api_context();
|
|
14944
15802
|
init_summarize();
|
|
14945
15803
|
init_quota();
|
|
14946
15804
|
init_store5();
|
|
@@ -14951,6 +15809,7 @@ var init_agent = __esm({
|
|
|
14951
15809
|
init_unified_config();
|
|
14952
15810
|
init_mcp_config();
|
|
14953
15811
|
activeChats = /* @__PURE__ */ new Map();
|
|
15812
|
+
compactionInFlight = /* @__PURE__ */ new Set();
|
|
14954
15813
|
chatLocks = /* @__PURE__ */ new Map();
|
|
14955
15814
|
SPAWN_TIMEOUT_MS = 10 * 60 * 1e3;
|
|
14956
15815
|
FIRST_RESPONSE_TIMEOUT_MS = parseInt(process.env.GEMINI_FIRST_RESPONSE_TIMEOUT_MS ?? "30000", 10);
|
|
@@ -15118,7 +15977,9 @@ async function runWeeklySweep(chatId, channel, backendId, model2) {
|
|
|
15118
15977
|
buttons.push([{ label: "Review Now", data: "mem:opt:start", style: "success" }]);
|
|
15119
15978
|
}
|
|
15120
15979
|
buttons.push([{ label: "Dismiss", data: "mem:sweep:dismiss" }]);
|
|
15121
|
-
|
|
15980
|
+
if (channel) {
|
|
15981
|
+
await sendOrEditKeyboard(chatId, channel, void 0, lines.join("\n"), buttons);
|
|
15982
|
+
}
|
|
15122
15983
|
return { suggestionsCount, cleanedUp };
|
|
15123
15984
|
} catch (err) {
|
|
15124
15985
|
const msg = errorMessage(err);
|
|
@@ -15438,52 +16299,6 @@ var init_pagination = __esm({
|
|
|
15438
16299
|
}
|
|
15439
16300
|
});
|
|
15440
16301
|
|
|
15441
|
-
// src/format-time.ts
|
|
15442
|
-
function formatLocalDate(utcDatetime) {
|
|
15443
|
-
const d = parseUtcDatetime(utcDatetime);
|
|
15444
|
-
if (!d) return utcDatetime.split("T")[0] ?? utcDatetime.split(" ")[0];
|
|
15445
|
-
const year = d.getFullYear();
|
|
15446
|
-
const month = String(d.getMonth() + 1).padStart(2, "0");
|
|
15447
|
-
const day = String(d.getDate()).padStart(2, "0");
|
|
15448
|
-
return `${year}-${month}-${day}`;
|
|
15449
|
-
}
|
|
15450
|
-
function formatLocalDateTime(utcDatetime) {
|
|
15451
|
-
const d = parseUtcDatetime(utcDatetime);
|
|
15452
|
-
if (!d) return utcDatetime;
|
|
15453
|
-
const year = d.getFullYear();
|
|
15454
|
-
const month = String(d.getMonth() + 1).padStart(2, "0");
|
|
15455
|
-
const day = String(d.getDate()).padStart(2, "0");
|
|
15456
|
-
const hours = String(d.getHours()).padStart(2, "0");
|
|
15457
|
-
const minutes = String(d.getMinutes()).padStart(2, "0");
|
|
15458
|
-
const tzAbbr = getTimezoneAbbr(d);
|
|
15459
|
-
return `${year}-${month}-${day} ${hours}:${minutes} ${tzAbbr}`;
|
|
15460
|
-
}
|
|
15461
|
-
function parseUtcDatetime(utcDatetime) {
|
|
15462
|
-
if (!utcDatetime) return null;
|
|
15463
|
-
const normalized = utcDatetime.includes("T") ? utcDatetime : utcDatetime.replace(" ", "T");
|
|
15464
|
-
const withZ = normalized.endsWith("Z") ? normalized : normalized + "Z";
|
|
15465
|
-
const d = new Date(withZ);
|
|
15466
|
-
return isNaN(d.getTime()) ? null : d;
|
|
15467
|
-
}
|
|
15468
|
-
function getTimezoneAbbr(date) {
|
|
15469
|
-
try {
|
|
15470
|
-
const parts = new Intl.DateTimeFormat("en-US", {
|
|
15471
|
-
timeZone: systemTimezone,
|
|
15472
|
-
timeZoneName: "short"
|
|
15473
|
-
}).formatToParts(date);
|
|
15474
|
-
return parts.find((p) => p.type === "timeZoneName")?.value ?? "";
|
|
15475
|
-
} catch {
|
|
15476
|
-
return "";
|
|
15477
|
-
}
|
|
15478
|
-
}
|
|
15479
|
-
var systemTimezone;
|
|
15480
|
-
var init_format_time = __esm({
|
|
15481
|
-
"src/format-time.ts"() {
|
|
15482
|
-
"use strict";
|
|
15483
|
-
systemTimezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
15484
|
-
}
|
|
15485
|
-
});
|
|
15486
|
-
|
|
15487
16302
|
// src/scheduler/humanize.ts
|
|
15488
16303
|
function formatTime(hour, minute) {
|
|
15489
16304
|
const h = hour % 12 || 12;
|
|
@@ -16776,11 +17591,11 @@ var init_health2 = __esm({
|
|
|
16776
17591
|
});
|
|
16777
17592
|
|
|
16778
17593
|
// src/health/checks.ts
|
|
16779
|
-
import { existsSync as existsSync16, statSync as statSync5, readFileSync as
|
|
17594
|
+
import { existsSync as existsSync16, statSync as statSync5, readFileSync as readFileSync12 } from "fs";
|
|
16780
17595
|
import { execFileSync as execFileSync2, execSync as execSync2 } from "child_process";
|
|
16781
17596
|
function getRecentErrors() {
|
|
16782
17597
|
if (!existsSync16(ERROR_LOG_PATH)) return null;
|
|
16783
|
-
const logContent =
|
|
17598
|
+
const logContent = readFileSync12(ERROR_LOG_PATH, "utf-8");
|
|
16784
17599
|
const allLines = logContent.split("\n").filter(Boolean).slice(-500);
|
|
16785
17600
|
const last24h = Date.now() - 864e5;
|
|
16786
17601
|
const lines = allLines.filter((line) => {
|
|
@@ -17009,7 +17824,7 @@ __export(heartbeat_exports, {
|
|
|
17009
17824
|
stopHeartbeatForChat: () => stopHeartbeatForChat,
|
|
17010
17825
|
updateHeartbeatConfig: () => updateHeartbeatConfig
|
|
17011
17826
|
});
|
|
17012
|
-
import { readFileSync as
|
|
17827
|
+
import { readFileSync as readFileSync13, existsSync as existsSync17 } from "fs";
|
|
17013
17828
|
import { join as join17 } from "path";
|
|
17014
17829
|
function findHeartbeatJob() {
|
|
17015
17830
|
try {
|
|
@@ -17191,7 +18006,7 @@ ${watchLines.join("\n")}`);
|
|
|
17191
18006
|
}
|
|
17192
18007
|
if (existsSync17(HEARTBEAT_MD_PATH)) {
|
|
17193
18008
|
try {
|
|
17194
|
-
const custom =
|
|
18009
|
+
const custom = readFileSync13(HEARTBEAT_MD_PATH, "utf-8").trim();
|
|
17195
18010
|
if (custom) {
|
|
17196
18011
|
sections.push(`[Custom checks from HEARTBEAT.md]
|
|
17197
18012
|
${custom}`);
|
|
@@ -17440,6 +18255,7 @@ __export(ui_exports, {
|
|
|
17440
18255
|
ROTATION_MODE_LABELS: () => ROTATION_MODE_LABELS,
|
|
17441
18256
|
buildAccountSlotKeyboard: () => buildAccountSlotKeyboard,
|
|
17442
18257
|
doBackendSwitch: () => doBackendSwitch,
|
|
18258
|
+
executeBackendDisable: () => executeBackendDisable,
|
|
17443
18259
|
getJobScheduleText: () => getJobScheduleText,
|
|
17444
18260
|
getJobStatusEmoji: () => getJobStatusEmoji,
|
|
17445
18261
|
getJobStatusLabel: () => getJobStatusLabel,
|
|
@@ -17447,6 +18263,7 @@ __export(ui_exports, {
|
|
|
17447
18263
|
sendApiToolsKeyboard: () => sendApiToolsKeyboard,
|
|
17448
18264
|
sendBackendAccountPicker: () => sendBackendAccountPicker,
|
|
17449
18265
|
sendBackendConfigPanel: () => sendBackendConfigPanel,
|
|
18266
|
+
sendBackendDisableFlow: () => sendBackendDisableFlow,
|
|
17450
18267
|
sendBackendModelPicker: () => sendBackendModelPicker,
|
|
17451
18268
|
sendBackendPicker: () => sendBackendPicker,
|
|
17452
18269
|
sendBackendSwitchConfirmation: () => sendBackendSwitchConfirmation,
|
|
@@ -18190,9 +19007,24 @@ ${unapproved.length} CC-Claw skill${unapproved.length !== 1 ? "s" : ""} need rev
|
|
|
18190
19007
|
const safePage = Math.max(1, Math.min(page, totalPages));
|
|
18191
19008
|
const start = (safePage - 1) * SKILLS_PER_PAGE;
|
|
18192
19009
|
const pageSkills = approved.slice(start, start + SKILLS_PER_PAGE);
|
|
19010
|
+
const { getPinnedSkills: getPinnedSkills2 } = await Promise.resolve().then(() => (init_chat_settings(), chat_settings_exports));
|
|
19011
|
+
const pinned = getPinnedSkills2(chatId);
|
|
19012
|
+
let correctedSkills = /* @__PURE__ */ new Set();
|
|
19013
|
+
try {
|
|
19014
|
+
const { getSkillCorrections: getSkillCorrections2 } = await Promise.resolve().then(() => (init_store4(), store_exports4));
|
|
19015
|
+
const db3 = (await Promise.resolve().then(() => (init_store5(), store_exports5))).getDb();
|
|
19016
|
+
const corrections = getSkillCorrections2(db3, 30, 3);
|
|
19017
|
+
correctedSkills = new Set(corrections.map((c) => c.skillName));
|
|
19018
|
+
} catch {
|
|
19019
|
+
}
|
|
18193
19020
|
const typeIcon = (s) => s.type === "specialist" ? "\u{1F3AF}" : "\u{1F527}";
|
|
18194
19021
|
const buttons = pageSkills.map((s) => {
|
|
18195
|
-
|
|
19022
|
+
const isPinned = pinned.includes(s.name);
|
|
19023
|
+
const correctionBadge = correctedSkills.has(s.name) ? " \u26A0\uFE0F" : "";
|
|
19024
|
+
return [
|
|
19025
|
+
{ label: `${typeIcon(s)} ${s.name}${correctionBadge}`, data: `skill:${s.source}:${s.name}`, style: "success" },
|
|
19026
|
+
{ label: isPinned ? "\u{1F4CC}" : "\u25CB Pin", data: `pin:${s.name}` }
|
|
19027
|
+
];
|
|
18196
19028
|
});
|
|
18197
19029
|
if (totalPages > 1) {
|
|
18198
19030
|
const navRow = [];
|
|
@@ -18924,6 +19756,17 @@ async function sendBackendPicker(chatId, channel, messageId) {
|
|
|
18924
19756
|
}
|
|
18925
19757
|
async function sendBackendConfigPanel(chatId, backendId, channel, messageId, switched = false) {
|
|
18926
19758
|
if (typeof channel.sendKeyboard !== "function") return;
|
|
19759
|
+
const disabled = getDisabledBackends().includes(backendId);
|
|
19760
|
+
if (disabled) {
|
|
19761
|
+
const adapter = getAdapter(backendId);
|
|
19762
|
+
const text = `\u{1F534} ${adapter.displayName} is disabled`;
|
|
19763
|
+
const buttons2 = [
|
|
19764
|
+
[{ label: "Enable", data: `bconf:enable:${backendId}`, style: "success" }],
|
|
19765
|
+
[{ label: "\u2190 Back to Backends", data: "bconf:back" }]
|
|
19766
|
+
];
|
|
19767
|
+
await sendOrEditKeyboard(chatId, channel, messageId, text, buttons2);
|
|
19768
|
+
return;
|
|
19769
|
+
}
|
|
18927
19770
|
const summary = backendConfigSummary(chatId, backendId, switched);
|
|
18928
19771
|
const buttons = [
|
|
18929
19772
|
[
|
|
@@ -18931,10 +19774,80 @@ async function sendBackendConfigPanel(chatId, backendId, channel, messageId, swi
|
|
|
18931
19774
|
{ label: "Thinking", data: `bconf:thinking:${backendId}` },
|
|
18932
19775
|
{ label: "Account", data: `bconf:account:${backendId}` }
|
|
18933
19776
|
],
|
|
19777
|
+
[{ label: "Disable", data: `bconf:disable:${backendId}`, style: "danger" }],
|
|
18934
19778
|
[{ label: "\u2190 Back to Backends", data: "bconf:back" }]
|
|
18935
19779
|
];
|
|
18936
19780
|
await sendOrEditKeyboard(chatId, channel, messageId, summary, buttons);
|
|
18937
19781
|
}
|
|
19782
|
+
async function sendBackendDisableFlow(chatId, backendId, channel, messageId) {
|
|
19783
|
+
if (typeof channel.sendKeyboard !== "function") return;
|
|
19784
|
+
const { getActiveJobsByBackend: getActiveJobsByBackend2 } = await Promise.resolve().then(() => (init_jobs(), jobs_exports));
|
|
19785
|
+
const affectedJobs = getActiveJobsByBackend2(backendId);
|
|
19786
|
+
if (affectedJobs.length > 0) {
|
|
19787
|
+
await sendDisableJobReassign(chatId, backendId, affectedJobs, channel, messageId);
|
|
19788
|
+
return;
|
|
19789
|
+
}
|
|
19790
|
+
const current = getBackend(chatId);
|
|
19791
|
+
if (current === backendId) {
|
|
19792
|
+
await sendDisableBackendSwitch(chatId, backendId, channel, messageId);
|
|
19793
|
+
return;
|
|
19794
|
+
}
|
|
19795
|
+
await executeBackendDisable(chatId, backendId, channel, messageId);
|
|
19796
|
+
}
|
|
19797
|
+
async function sendDisableJobReassign(chatId, backendId, jobs, channel, messageId) {
|
|
19798
|
+
const adapter = getAdapter(backendId);
|
|
19799
|
+
const jobList = jobs.slice(0, 5).map((j) => ` \u2022 ${j.title ?? j.description}`).join("\n");
|
|
19800
|
+
const extra = jobs.length > 5 ? `
|
|
19801
|
+
\u2026and ${jobs.length - 5} more` : "";
|
|
19802
|
+
const text = [
|
|
19803
|
+
`\u26A0\uFE0F ${jobs.length} cron job(s) use ${adapter.displayName}:`,
|
|
19804
|
+
"",
|
|
19805
|
+
jobList + extra,
|
|
19806
|
+
"",
|
|
19807
|
+
"Reassign these jobs to:"
|
|
19808
|
+
].join("\n");
|
|
19809
|
+
const ids = getAvailableChatBackendIds().filter((id) => id !== backendId);
|
|
19810
|
+
const buttons = ids.map((id) => [{
|
|
19811
|
+
label: getAdapter(id).displayName,
|
|
19812
|
+
data: `bconf:reassign:${backendId}:${id}`
|
|
19813
|
+
}]);
|
|
19814
|
+
buttons.push([{ label: "Cancel", data: `bconf:panel:${backendId}` }]);
|
|
19815
|
+
await sendOrEditKeyboard(chatId, channel, messageId, text, buttons);
|
|
19816
|
+
}
|
|
19817
|
+
async function sendDisableBackendSwitch(chatId, backendId, channel, messageId) {
|
|
19818
|
+
const adapter = getAdapter(backendId);
|
|
19819
|
+
const text = `${adapter.displayName} is your active backend.
|
|
19820
|
+
|
|
19821
|
+
Switch to:`;
|
|
19822
|
+
const ids = getAvailableChatBackendIds().filter((id) => id !== backendId);
|
|
19823
|
+
const buttons = ids.map((id) => [{
|
|
19824
|
+
label: getAdapter(id).displayName,
|
|
19825
|
+
data: `bconf:disableswitch:${backendId}:${id}`
|
|
19826
|
+
}]);
|
|
19827
|
+
buttons.push([{ label: "Cancel", data: `bconf:panel:${backendId}` }]);
|
|
19828
|
+
await sendOrEditKeyboard(chatId, channel, messageId, text, buttons);
|
|
19829
|
+
}
|
|
19830
|
+
async function executeBackendDisable(chatId, backendId, channel, messageId, opts) {
|
|
19831
|
+
const adapter = getAdapter(backendId);
|
|
19832
|
+
const parts = [];
|
|
19833
|
+
if (opts?.reassignTo) {
|
|
19834
|
+
const { reassignJobsBackend: reassignJobsBackend2 } = await Promise.resolve().then(() => (init_jobs(), jobs_exports));
|
|
19835
|
+
const count = reassignJobsBackend2(backendId, opts.reassignTo);
|
|
19836
|
+
const targetName = getAdapter(opts.reassignTo).displayName;
|
|
19837
|
+
if (count > 0) parts.push(`${count} job(s) reassigned to ${targetName}`);
|
|
19838
|
+
}
|
|
19839
|
+
if (opts?.switchTo) {
|
|
19840
|
+
await doBackendSwitch(chatId, opts.switchTo, channel, { skipContext: true });
|
|
19841
|
+
parts.push(`Switched to ${getAdapter(opts.switchTo).displayName}`);
|
|
19842
|
+
}
|
|
19843
|
+
setBackendDisabled(backendId, true);
|
|
19844
|
+
const detail = parts.length > 0 ? `
|
|
19845
|
+
${parts.join(". ")}.` : "";
|
|
19846
|
+
const text = `\u2705 ${adapter.displayName} disabled.${detail}`;
|
|
19847
|
+
await sendOrEditKeyboard(chatId, channel, messageId, text, [
|
|
19848
|
+
[{ label: "\u2190 Back to Backends", data: "bconf:back" }]
|
|
19849
|
+
]);
|
|
19850
|
+
}
|
|
18938
19851
|
async function sendBackendModelPicker(chatId, backendId, channel, messageId) {
|
|
18939
19852
|
if (typeof channel.sendKeyboard !== "function") return;
|
|
18940
19853
|
const adapter = getAdapter(backendId);
|
|
@@ -19008,7 +19921,15 @@ async function sendBackendThinkingPicker(chatId, backendId, channel, messageId)
|
|
|
19008
19921
|
return;
|
|
19009
19922
|
}
|
|
19010
19923
|
const current = getThinkingLevel(chatId) ?? "auto";
|
|
19011
|
-
const
|
|
19924
|
+
const LEVEL_LABELS = {
|
|
19925
|
+
auto: "Auto",
|
|
19926
|
+
off: "Off",
|
|
19927
|
+
low: "Low",
|
|
19928
|
+
medium: "Medium",
|
|
19929
|
+
high: backendId === "ollama" ? "On" : "High",
|
|
19930
|
+
extra_high: "Extra High"
|
|
19931
|
+
};
|
|
19932
|
+
const defaultLevels = [
|
|
19012
19933
|
["auto", "Auto"],
|
|
19013
19934
|
["off", "Off"],
|
|
19014
19935
|
["low", "Low"],
|
|
@@ -19016,6 +19937,7 @@ async function sendBackendThinkingPicker(chatId, backendId, channel, messageId)
|
|
|
19016
19937
|
["high", "High"],
|
|
19017
19938
|
["extra_high", "Extra High"]
|
|
19018
19939
|
];
|
|
19940
|
+
const levels = modelInfo?.thinkingLevels ? modelInfo.thinkingLevels.map((l) => [l, LEVEL_LABELS[l] ?? capitalize(l.replace("_", " "))]) : defaultLevels;
|
|
19019
19941
|
const buttons = levels.map(([val, label2]) => [{
|
|
19020
19942
|
label: `${val === current ? "\u2713 " : ""}${label2}`,
|
|
19021
19943
|
data: `bconf:setthinking:${backendId}:${val}`,
|
|
@@ -19076,6 +19998,16 @@ async function doBackendSwitch(chatId, backendId, channel, opts) {
|
|
|
19076
19998
|
const bridge = buildContextBridge(chatId, 15, interrupted);
|
|
19077
19999
|
if (bridge) setPendingContextBridge(chatId, bridge);
|
|
19078
20000
|
}
|
|
20001
|
+
const previousBackend = getBackend(chatId);
|
|
20002
|
+
if (previousBackend === "ollama") {
|
|
20003
|
+
const previousModel = getModel(chatId);
|
|
20004
|
+
if (previousModel) {
|
|
20005
|
+
Promise.resolve().then(() => (init_ollama(), ollama_exports)).then(({ OllamaService }) => {
|
|
20006
|
+
OllamaService.tryUnloadModel(previousModel);
|
|
20007
|
+
}).catch(() => {
|
|
20008
|
+
});
|
|
20009
|
+
}
|
|
20010
|
+
}
|
|
19079
20011
|
clearSession(chatId);
|
|
19080
20012
|
clearModel(chatId);
|
|
19081
20013
|
clearThinkingLevel(chatId);
|
|
@@ -20566,7 +21498,7 @@ __export(analyze_exports, {
|
|
|
20566
21498
|
});
|
|
20567
21499
|
import { spawn as spawn5 } from "child_process";
|
|
20568
21500
|
import { createInterface as createInterface4 } from "readline";
|
|
20569
|
-
import { readFileSync as
|
|
21501
|
+
import { readFileSync as readFileSync14, existsSync as existsSync18, readdirSync as readdirSync7, statSync as statSync6 } from "fs";
|
|
20570
21502
|
import { join as join18 } from "path";
|
|
20571
21503
|
import { homedir as homedir6 } from "os";
|
|
20572
21504
|
function applySignalDecay(confidence, createdAt) {
|
|
@@ -20588,7 +21520,7 @@ function discoverReflectionTargets() {
|
|
|
20588
21520
|
if (!existsSync18(skillFile)) continue;
|
|
20589
21521
|
let desc = "skill";
|
|
20590
21522
|
try {
|
|
20591
|
-
const content =
|
|
21523
|
+
const content = readFileSync14(skillFile, "utf-8");
|
|
20592
21524
|
const descMatch = content.match(/description:\s*["']?([^"'\n]+)/);
|
|
20593
21525
|
if (descMatch) desc = descMatch[1].trim().slice(0, 80);
|
|
20594
21526
|
} catch {
|
|
@@ -20663,6 +21595,21 @@ ${categoryList}`);
|
|
|
20663
21595
|
sections.push(skill.content);
|
|
20664
21596
|
}
|
|
20665
21597
|
}
|
|
21598
|
+
if (params.skillCorrections && params.skillCorrections.length > 0) {
|
|
21599
|
+
sections.push("[Underperforming Skills]");
|
|
21600
|
+
sections.push(
|
|
21601
|
+
"These skills have received repeated corrections. The per-model breakdown helps distinguish skill quality issues from model capability limits. If corrections concentrate on weaker models (Flash, Haiku, Lite) while stronger models (Opus, Pro) handle the skill correctly, this likely reflects model capability rather than skill quality \u2014 consider adding a simplified-instructions section for weaker models rather than a full rewrite."
|
|
21602
|
+
);
|
|
21603
|
+
for (const sc of params.skillCorrections) {
|
|
21604
|
+
const modelSummary = sc.modelBreakdown.map((m) => `${m.model}: ${m.count}`).join(", ");
|
|
21605
|
+
sections.push(`- **${sc.skillName}** \u2014 ${sc.correctionCount} corrections in last 30 days`);
|
|
21606
|
+
sections.push(` Models: ${modelSummary || "unknown"}`);
|
|
21607
|
+
const uniqueContexts = [...new Set(sc.contexts)].slice(0, 5);
|
|
21608
|
+
for (const ctx of uniqueContexts) {
|
|
21609
|
+
sections.push(` Correction: "${ctx}"`);
|
|
21610
|
+
}
|
|
21611
|
+
}
|
|
21612
|
+
}
|
|
20666
21613
|
sections.push("[Previously Applied Insights]");
|
|
20667
21614
|
if (appliedInsights.length === 0) {
|
|
20668
21615
|
sections.push("(none)");
|
|
@@ -20797,7 +21744,7 @@ function resolveReflectionAdapter(chatId) {
|
|
|
20797
21744
|
}
|
|
20798
21745
|
function readIdentityFile(filename) {
|
|
20799
21746
|
try {
|
|
20800
|
-
return
|
|
21747
|
+
return readFileSync14(join18(IDENTITY_PATH, filename), "utf-8");
|
|
20801
21748
|
} catch {
|
|
20802
21749
|
return "";
|
|
20803
21750
|
}
|
|
@@ -20956,7 +21903,7 @@ async function runAnalysisImpl(chatId, opts) {
|
|
|
20956
21903
|
try {
|
|
20957
21904
|
const fullPath = join18(ccClawHome, target.path);
|
|
20958
21905
|
if (existsSync18(fullPath)) {
|
|
20959
|
-
const content =
|
|
21906
|
+
const content = readFileSync14(fullPath, "utf-8");
|
|
20960
21907
|
if (totalSkillChars + content.length > SKILL_CONTENT_CAP) break;
|
|
20961
21908
|
skillContents.push({ path: target.path, content });
|
|
20962
21909
|
totalSkillChars += content.length;
|
|
@@ -20964,6 +21911,7 @@ async function runAnalysisImpl(chatId, opts) {
|
|
|
20964
21911
|
} catch {
|
|
20965
21912
|
}
|
|
20966
21913
|
}
|
|
21914
|
+
const skillCorrections = getSkillCorrections(db3, 30, 3);
|
|
20967
21915
|
const prompt = buildAnalysisPrompt({
|
|
20968
21916
|
signals: signals.map((s) => {
|
|
20969
21917
|
const decayedConfidence = applySignalDecay(s.confidence, s.created_at);
|
|
@@ -20986,7 +21934,8 @@ async function runAnalysisImpl(chatId, opts) {
|
|
|
20986
21934
|
})),
|
|
20987
21935
|
codebaseEnabled,
|
|
20988
21936
|
availableTargets,
|
|
20989
|
-
skillContents
|
|
21937
|
+
skillContents,
|
|
21938
|
+
skillCorrections
|
|
20990
21939
|
});
|
|
20991
21940
|
const resolved = resolveReflectionAdapter(chatId);
|
|
20992
21941
|
if (!resolved) {
|
|
@@ -21336,7 +22285,7 @@ __export(apply_exports, {
|
|
|
21336
22285
|
isTargetAllowed: () => isTargetAllowed,
|
|
21337
22286
|
rollbackInsight: () => rollbackInsight
|
|
21338
22287
|
});
|
|
21339
|
-
import { readFileSync as
|
|
22288
|
+
import { readFileSync as readFileSync15, writeFileSync as writeFileSync7, existsSync as existsSync19, mkdirSync as mkdirSync7, readdirSync as readdirSync8, unlinkSync as unlinkSync5 } from "fs";
|
|
21340
22289
|
import { join as join19, dirname as dirname4 } from "path";
|
|
21341
22290
|
function isTargetAllowed(relativePath) {
|
|
21342
22291
|
if (relativePath.includes("..")) return false;
|
|
@@ -21424,7 +22373,7 @@ async function applyInsight(insightId) {
|
|
|
21424
22373
|
const absolutePath = join19(CC_CLAW_HOME, insight.targetFile);
|
|
21425
22374
|
if (insight.proposedAction === "append" && insight.targetFile === "identity/SOUL.md") {
|
|
21426
22375
|
if (existsSync19(absolutePath)) {
|
|
21427
|
-
const currentContent =
|
|
22376
|
+
const currentContent = readFileSync15(absolutePath, "utf-8");
|
|
21428
22377
|
const lineCount = currentContent.split("\n").length;
|
|
21429
22378
|
if (lineCount >= SOUL_LINE_CAP) {
|
|
21430
22379
|
return {
|
|
@@ -21446,7 +22395,7 @@ async function applyInsight(insightId) {
|
|
|
21446
22395
|
}
|
|
21447
22396
|
let original = "";
|
|
21448
22397
|
if (existsSync19(absolutePath)) {
|
|
21449
|
-
original =
|
|
22398
|
+
original = readFileSync15(absolutePath, "utf-8");
|
|
21450
22399
|
} else if (insight.proposedAction !== "create") {
|
|
21451
22400
|
return { success: false, message: `Target file "${insight.targetFile}" does not exist` };
|
|
21452
22401
|
}
|
|
@@ -21587,7 +22536,7 @@ function computeLineDrift(baseline, absolutePath) {
|
|
|
21587
22536
|
let current = "";
|
|
21588
22537
|
try {
|
|
21589
22538
|
if (existsSync19(absolutePath)) {
|
|
21590
|
-
current =
|
|
22539
|
+
current = readFileSync15(absolutePath, "utf-8");
|
|
21591
22540
|
}
|
|
21592
22541
|
} catch {
|
|
21593
22542
|
return 1;
|
|
@@ -22319,6 +23268,14 @@ var init_api_mcp = __esm({
|
|
|
22319
23268
|
|
|
22320
23269
|
// src/backends/api-common.ts
|
|
22321
23270
|
import { streamText, stepCountIs, NoOutputGeneratedError, APICallError } from "ai";
|
|
23271
|
+
function mapToolActionToApiFormat(cb) {
|
|
23272
|
+
if (!cb) return void 0;
|
|
23273
|
+
return (event) => {
|
|
23274
|
+
const input = event.input ?? {};
|
|
23275
|
+
const result = event.type === "tool_end" ? typeof event.output === "string" ? event.output : event.output != null ? JSON.stringify(event.output) : "" : void 0;
|
|
23276
|
+
cb(event.toolName, input, result);
|
|
23277
|
+
};
|
|
23278
|
+
}
|
|
22322
23279
|
function toModelMessage(msg) {
|
|
22323
23280
|
switch (msg.role) {
|
|
22324
23281
|
case "system":
|
|
@@ -22515,6 +23472,7 @@ var init_ollama2 = __esm({
|
|
|
22515
23472
|
"src/backends/ollama.ts"() {
|
|
22516
23473
|
"use strict";
|
|
22517
23474
|
init_api_common();
|
|
23475
|
+
init_api_common();
|
|
22518
23476
|
init_ollama();
|
|
22519
23477
|
init_prompt();
|
|
22520
23478
|
OLLAMA_HTTP_SENTINEL = "__ollama_http__";
|
|
@@ -22602,26 +23560,35 @@ var init_ollama2 = __esm({
|
|
|
22602
23560
|
*/
|
|
22603
23561
|
async streamDirect(prompt, model2, opts) {
|
|
22604
23562
|
const cleanPrompt = stripForLocalModel(prompt);
|
|
22605
|
-
let
|
|
23563
|
+
let modelContextWindow;
|
|
23564
|
+
const ollamaProviderOpts = {};
|
|
22606
23565
|
try {
|
|
22607
23566
|
const { OllamaStore } = (init_ollama(), __toCommonJS(ollama_exports));
|
|
22608
23567
|
const modelRecord = OllamaStore.getModelByName(model2);
|
|
22609
|
-
|
|
22610
|
-
|
|
22611
|
-
|
|
22612
|
-
|
|
23568
|
+
const chatLevel = opts?.thinkingLevel;
|
|
23569
|
+
if (chatLevel === "off") {
|
|
23570
|
+
ollamaProviderOpts.think = false;
|
|
23571
|
+
} else if (chatLevel && chatLevel !== "auto") {
|
|
23572
|
+
ollamaProviderOpts.think = true;
|
|
23573
|
+
} else if (modelRecord?.forceThinkOff) {
|
|
23574
|
+
ollamaProviderOpts.think = false;
|
|
23575
|
+
}
|
|
23576
|
+
if (modelRecord?.contextWindow && modelRecord.contextWindow > 4096) {
|
|
23577
|
+
modelContextWindow = modelRecord.contextWindow;
|
|
22613
23578
|
}
|
|
22614
23579
|
} catch {
|
|
22615
23580
|
}
|
|
23581
|
+
if (modelContextWindow) ollamaProviderOpts.num_ctx = modelContextWindow;
|
|
22616
23582
|
const apiOpts = {
|
|
22617
23583
|
timeoutMs: opts?.timeoutMs,
|
|
22618
23584
|
onStream: opts?.onStream,
|
|
22619
23585
|
signal: opts?.signal,
|
|
22620
23586
|
messageHistory: opts?.messageHistory,
|
|
23587
|
+
onToolAction: mapToolActionToApiFormat(opts?.onToolAction),
|
|
22621
23588
|
permMode: opts?.permMode,
|
|
22622
23589
|
thinkingLevel: opts?.thinkingLevel,
|
|
22623
23590
|
onThinking: opts?.onThinking,
|
|
22624
|
-
...
|
|
23591
|
+
...Object.keys(ollamaProviderOpts).length > 0 ? { providerOptions: { ollama: ollamaProviderOpts } } : {}
|
|
22625
23592
|
};
|
|
22626
23593
|
const result = await this.streamDirectWithHistory(
|
|
22627
23594
|
cleanPrompt,
|
|
@@ -22646,10 +23613,11 @@ var init_ollama2 = __esm({
|
|
|
22646
23613
|
const { OllamaStore } = (init_ollama(), __toCommonJS(ollama_exports));
|
|
22647
23614
|
const models = OllamaStore.getAvailableModels();
|
|
22648
23615
|
for (const m of models) {
|
|
22649
|
-
const isThinkingCapable = m.capability === "thinking" && !m.forceThinkOff;
|
|
22650
23616
|
this.availableModels[m.name] = {
|
|
22651
23617
|
label: `${m.name}${m.parameterSize ? ` (${m.parameterSize})` : ""}`,
|
|
22652
|
-
thinking:
|
|
23618
|
+
thinking: "adjustable",
|
|
23619
|
+
// all Ollama models support think: true/false toggle
|
|
23620
|
+
thinkingLevels: ["auto", "off", "high"]
|
|
22653
23621
|
};
|
|
22654
23622
|
this.pricing[m.name] = { in: 0, out: 0, cache: 0 };
|
|
22655
23623
|
this.contextWindow[m.name] = m.contextWindow ?? 4096;
|
|
@@ -22666,6 +23634,7 @@ var init_openrouter = __esm({
|
|
|
22666
23634
|
"src/backends/openrouter.ts"() {
|
|
22667
23635
|
"use strict";
|
|
22668
23636
|
init_api_common();
|
|
23637
|
+
init_api_common();
|
|
22669
23638
|
OPENROUTER_HTTP_SENTINEL = "__openrouter_api__";
|
|
22670
23639
|
DEFAULT_FREE_MODEL = "qwen/qwen3.6-plus:free";
|
|
22671
23640
|
OpenRouterAdapter = class extends ApiBackendBase {
|
|
@@ -22707,7 +23676,28 @@ var init_openrouter = __esm({
|
|
|
22707
23676
|
}
|
|
22708
23677
|
summarizerModel = DEFAULT_FREE_MODEL;
|
|
22709
23678
|
pricing = {};
|
|
22710
|
-
|
|
23679
|
+
_contextWindowCache = null;
|
|
23680
|
+
_contextWindowCacheSize = 0;
|
|
23681
|
+
get contextWindow() {
|
|
23682
|
+
try {
|
|
23683
|
+
const { getApiModels: getApiModels2 } = (init_api_models(), __toCommonJS(api_models_exports));
|
|
23684
|
+
const models = getApiModels2("openrouter");
|
|
23685
|
+
if (this._contextWindowCache && models.length === this._contextWindowCacheSize) {
|
|
23686
|
+
return this._contextWindowCache;
|
|
23687
|
+
}
|
|
23688
|
+
const result = {};
|
|
23689
|
+
for (const m of models) {
|
|
23690
|
+
if (m.contextWindow) {
|
|
23691
|
+
result[m.modelId] = m.contextWindow;
|
|
23692
|
+
}
|
|
23693
|
+
}
|
|
23694
|
+
this._contextWindowCache = result;
|
|
23695
|
+
this._contextWindowCacheSize = models.length;
|
|
23696
|
+
return result;
|
|
23697
|
+
} catch {
|
|
23698
|
+
return {};
|
|
23699
|
+
}
|
|
23700
|
+
}
|
|
22711
23701
|
// ── Vercel AI SDK provider ────────────────────────────────────────
|
|
22712
23702
|
/**
|
|
22713
23703
|
* Create the Vercel AI SDK LanguageModel for a given model ID.
|
|
@@ -22734,17 +23724,12 @@ var init_openrouter = __esm({
|
|
|
22734
23724
|
* including tool actions, permission mode, thinking tokens, and conversation history.
|
|
22735
23725
|
*/
|
|
22736
23726
|
async streamDirect(prompt, model2, opts) {
|
|
22737
|
-
const onToolAction = opts?.onToolAction ? (event) => {
|
|
22738
|
-
const input = event.input ?? {};
|
|
22739
|
-
const result2 = event.type === "tool_end" ? typeof event.output === "string" ? event.output : JSON.stringify(event.output ?? "") : void 0;
|
|
22740
|
-
opts.onToolAction(event.toolName, input, result2);
|
|
22741
|
-
} : void 0;
|
|
22742
23727
|
const apiOpts = {
|
|
22743
23728
|
timeoutMs: opts?.timeoutMs,
|
|
22744
23729
|
onStream: opts?.onStream,
|
|
22745
23730
|
signal: opts?.signal,
|
|
22746
23731
|
messageHistory: opts?.messageHistory,
|
|
22747
|
-
onToolAction,
|
|
23732
|
+
onToolAction: mapToolActionToApiFormat(opts?.onToolAction),
|
|
22748
23733
|
permMode: opts?.permMode,
|
|
22749
23734
|
thinkingLevel: opts?.thinkingLevel,
|
|
22750
23735
|
onThinking: opts?.onThinking
|
|
@@ -22920,7 +23905,10 @@ function getAvailableBackendIds() {
|
|
|
22920
23905
|
return [...availableSet];
|
|
22921
23906
|
}
|
|
22922
23907
|
function getAvailableChatBackendIds() {
|
|
22923
|
-
|
|
23908
|
+
const disabled = getDisabledBackends();
|
|
23909
|
+
return CHAT_BACKEND_IDS.filter(
|
|
23910
|
+
(id) => (availableSet.size === 0 || availableSet.has(id)) && !disabled.includes(id)
|
|
23911
|
+
);
|
|
22924
23912
|
}
|
|
22925
23913
|
var openRouterAdapter, adapters, CHAT_BACKEND_IDS, availableSet;
|
|
22926
23914
|
var init_backends = __esm({
|
|
@@ -22933,6 +23921,7 @@ var init_backends = __esm({
|
|
|
22933
23921
|
init_ollama2();
|
|
22934
23922
|
init_openrouter();
|
|
22935
23923
|
init_store5();
|
|
23924
|
+
init_schema();
|
|
22936
23925
|
init_log();
|
|
22937
23926
|
init_types();
|
|
22938
23927
|
openRouterAdapter = new OpenRouterAdapter();
|
|
@@ -23222,7 +24211,7 @@ var init_retry = __esm({
|
|
|
23222
24211
|
});
|
|
23223
24212
|
|
|
23224
24213
|
// src/bootstrap/profile.ts
|
|
23225
|
-
import { readFileSync as
|
|
24214
|
+
import { readFileSync as readFileSync16, writeFileSync as writeFileSync8, existsSync as existsSync22 } from "fs";
|
|
23226
24215
|
import { join as join21 } from "path";
|
|
23227
24216
|
function hasActiveProfile(chatId) {
|
|
23228
24217
|
return activeProfiles.has(chatId);
|
|
@@ -23353,7 +24342,7 @@ function extractUserUpdates(text) {
|
|
|
23353
24342
|
}
|
|
23354
24343
|
function appendToUserProfile(key, value) {
|
|
23355
24344
|
if (!existsSync22(USER_PATH2)) return;
|
|
23356
|
-
const content =
|
|
24345
|
+
const content = readFileSync16(USER_PATH2, "utf-8");
|
|
23357
24346
|
const line = `- **${key}**: ${value}`;
|
|
23358
24347
|
if (content.includes(line)) return;
|
|
23359
24348
|
const updated = content.trimEnd() + `
|
|
@@ -24806,6 +25795,7 @@ var init_live_status = __esm({
|
|
|
24806
25795
|
/** Spinner frame counter — advances on each flush for animation. */
|
|
24807
25796
|
spinnerFrame = 0;
|
|
24808
25797
|
/** Timestamp of last successful edit — used for heartbeat force-through. */
|
|
25798
|
+
lastSuccessfulFlushAt = 0;
|
|
24809
25799
|
/** Callback to restart typing indicator as fallback. */
|
|
24810
25800
|
onTypingFallback;
|
|
24811
25801
|
/** Set a callback that restarts the typing indicator loop as a fallback. */
|
|
@@ -26655,12 +27645,12 @@ async function handleEvolveCallback(chatId, data, channel, messageId) {
|
|
|
26655
27645
|
);
|
|
26656
27646
|
break;
|
|
26657
27647
|
}
|
|
26658
|
-
const { readFileSync:
|
|
27648
|
+
const { readFileSync: readFileSync36, existsSync: existsSync62 } = await import("fs");
|
|
26659
27649
|
const { join: join42 } = await import("path");
|
|
26660
27650
|
const targetPath = join42(homedir7(), ".cc-claw", insight.targetFile);
|
|
26661
27651
|
let previewText;
|
|
26662
27652
|
if (existsSync62(targetPath)) {
|
|
26663
|
-
const current =
|
|
27653
|
+
const current = readFileSync36(targetPath, "utf-8");
|
|
26664
27654
|
const diffLines = insight.proposedDiff.split("\n");
|
|
26665
27655
|
const additions = diffLines.filter((l) => l.startsWith("+")).map((l) => ` + ${l.slice(1).trim()}`);
|
|
26666
27656
|
const removals = diffLines.filter((l) => l.startsWith("-")).map((l) => ` - ${l.slice(1).trim()}`);
|
|
@@ -26732,13 +27722,13 @@ async function handleEvolveCallback(chatId, data, channel, messageId) {
|
|
|
26732
27722
|
const { logActivity: logActivity2 } = await Promise.resolve().then(() => (init_store3(), store_exports3));
|
|
26733
27723
|
const current = getReflectionStatus2(getDb(), chatId);
|
|
26734
27724
|
if (current === "frozen") {
|
|
26735
|
-
const { readFileSync:
|
|
27725
|
+
const { readFileSync: readFileSync36, existsSync: existsSync62 } = await import("fs");
|
|
26736
27726
|
const { join: join42 } = await import("path");
|
|
26737
27727
|
const { CC_CLAW_HOME: CC_CLAW_HOME3 } = await Promise.resolve().then(() => (init_paths(), paths_exports));
|
|
26738
27728
|
const soulPath = join42(CC_CLAW_HOME3, "identity/SOUL.md");
|
|
26739
27729
|
const userPath = join42(CC_CLAW_HOME3, "identity/USER.md");
|
|
26740
|
-
const soul = existsSync62(soulPath) ?
|
|
26741
|
-
const user = existsSync62(userPath) ?
|
|
27730
|
+
const soul = existsSync62(soulPath) ? readFileSync36(soulPath, "utf-8") : "";
|
|
27731
|
+
const user = existsSync62(userPath) ? readFileSync36(userPath, "utf-8") : "";
|
|
26742
27732
|
setReflectionStatus2(getDb(), chatId, "active", soul, user);
|
|
26743
27733
|
logActivity2(getDb(), { chatId, source: "telegram", eventType: "reflection_unfrozen", summary: "Reflection enabled" });
|
|
26744
27734
|
await sendEvolveDashboard(chatId, reflChatId, channel, messageId);
|
|
@@ -26877,11 +27867,11 @@ var init_evolve2 = __esm({
|
|
|
26877
27867
|
});
|
|
26878
27868
|
|
|
26879
27869
|
// src/optimizer/identity-audit.ts
|
|
26880
|
-
import { readFileSync as
|
|
27870
|
+
import { readFileSync as readFileSync17, existsSync as existsSync26, readdirSync as readdirSync10, statSync as statSync8 } from "fs";
|
|
26881
27871
|
import { join as join26 } from "path";
|
|
26882
27872
|
function readIdentityFile2(filename) {
|
|
26883
27873
|
try {
|
|
26884
|
-
return
|
|
27874
|
+
return readFileSync17(join26(IDENTITY_PATH, filename), "utf-8");
|
|
26885
27875
|
} catch {
|
|
26886
27876
|
return "";
|
|
26887
27877
|
}
|
|
@@ -27034,7 +28024,7 @@ var init_identity_audit = __esm({
|
|
|
27034
28024
|
});
|
|
27035
28025
|
|
|
27036
28026
|
// src/optimizer/skill-audit.ts
|
|
27037
|
-
import { readFileSync as
|
|
28027
|
+
import { readFileSync as readFileSync18, existsSync as existsSync27 } from "fs";
|
|
27038
28028
|
import { join as join27, basename as basename3 } from "path";
|
|
27039
28029
|
function parseFrontmatter3(content) {
|
|
27040
28030
|
const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
@@ -27080,7 +28070,7 @@ function detectDependentSkills(content) {
|
|
|
27080
28070
|
return Array.from(deps);
|
|
27081
28071
|
}
|
|
27082
28072
|
function computeSkillStats(skillPath) {
|
|
27083
|
-
const content =
|
|
28073
|
+
const content = readFileSync18(skillPath, "utf-8");
|
|
27084
28074
|
const lines = content.split("\n");
|
|
27085
28075
|
return {
|
|
27086
28076
|
skillName: basename3(skillPath, ".md") === "SKILL" ? basename3(join27(skillPath, "..")) : basename3(skillPath, ".md"),
|
|
@@ -27109,7 +28099,7 @@ function loadDependentSkillContents(depNames, ccClawSkillsDir) {
|
|
|
27109
28099
|
for (const candidate of candidates) {
|
|
27110
28100
|
if (existsSync27(candidate)) {
|
|
27111
28101
|
try {
|
|
27112
|
-
const content =
|
|
28102
|
+
const content = readFileSync18(candidate, "utf-8");
|
|
27113
28103
|
results.push({
|
|
27114
28104
|
name,
|
|
27115
28105
|
content: content.length > 3e3 ? content.slice(0, 3e3) + "\n[...truncated]" : content
|
|
@@ -27253,7 +28243,7 @@ __export(analyze_exports2, {
|
|
|
27253
28243
|
});
|
|
27254
28244
|
import { spawn as spawn7 } from "child_process";
|
|
27255
28245
|
import { createInterface as createInterface7 } from "readline";
|
|
27256
|
-
import { readFileSync as
|
|
28246
|
+
import { readFileSync as readFileSync19, existsSync as existsSync28, readdirSync as readdirSync12 } from "fs";
|
|
27257
28247
|
import { join as join28 } from "path";
|
|
27258
28248
|
import { homedir as homedir8 } from "os";
|
|
27259
28249
|
function parseOptimizeOutput(raw, validAreas) {
|
|
@@ -27384,7 +28374,7 @@ function getModelDisplayInfo(chatId) {
|
|
|
27384
28374
|
}
|
|
27385
28375
|
function readIdentityFile3(filename) {
|
|
27386
28376
|
try {
|
|
27387
|
-
return
|
|
28377
|
+
return readFileSync19(join28(IDENTITY_PATH, filename), "utf-8");
|
|
27388
28378
|
} catch {
|
|
27389
28379
|
return "";
|
|
27390
28380
|
}
|
|
@@ -27397,7 +28387,7 @@ function loadContextFiles() {
|
|
|
27397
28387
|
for (const entry of readdirSync12(contextDir)) {
|
|
27398
28388
|
if (!entry.endsWith(".md")) continue;
|
|
27399
28389
|
try {
|
|
27400
|
-
const content =
|
|
28390
|
+
const content = readFileSync19(join28(contextDir, entry), "utf-8");
|
|
27401
28391
|
results.push({ name: entry, content });
|
|
27402
28392
|
} catch {
|
|
27403
28393
|
}
|
|
@@ -27449,7 +28439,7 @@ async function runSkillAudit(chatId, skillPath) {
|
|
|
27449
28439
|
log(`[optimizer] Running skill audit on ${stats.skillName} with ${adapter.id}:${model2}`);
|
|
27450
28440
|
const soulMd = readIdentityFile3("SOUL.md");
|
|
27451
28441
|
const ccClawSkillsDir = join28(homedir8(), ".cc-claw", "workspace", "skills");
|
|
27452
|
-
const skillContent =
|
|
28442
|
+
const skillContent = readFileSync19(skillPath, "utf-8");
|
|
27453
28443
|
const prompt = buildSkillAuditPrompt(skillContent, stats, soulMd, ccClawSkillsDir);
|
|
27454
28444
|
const raw = await spawnAnalysis2(adapter, model2, prompt);
|
|
27455
28445
|
const findings = parseOptimizeOutput(raw, VALID_SKILL_AREAS);
|
|
@@ -27472,7 +28462,7 @@ function listCcClawSkills() {
|
|
|
27472
28462
|
if (!existsSync28(skillFile)) continue;
|
|
27473
28463
|
let description = "skill";
|
|
27474
28464
|
try {
|
|
27475
|
-
const content =
|
|
28465
|
+
const content = readFileSync19(skillFile, "utf-8");
|
|
27476
28466
|
const descMatch = content.match(/description:\s*>?\s*\n?\s*(.+)/);
|
|
27477
28467
|
if (descMatch) description = descMatch[1].trim().slice(0, 60);
|
|
27478
28468
|
} catch {
|
|
@@ -27799,7 +28789,7 @@ __export(optimize_exports2, {
|
|
|
27799
28789
|
handleOptimizeCallback: () => handleOptimizeCallback,
|
|
27800
28790
|
handleOptimizeCommand: () => handleOptimizeCommand
|
|
27801
28791
|
});
|
|
27802
|
-
import { readFileSync as
|
|
28792
|
+
import { readFileSync as readFileSync20, writeFileSync as writeFileSync9, existsSync as existsSync29, readdirSync as readdirSync13, unlinkSync as unlinkSync7 } from "fs";
|
|
27803
28793
|
import { join as join29, dirname as dirname5 } from "path";
|
|
27804
28794
|
import { homedir as homedir9 } from "os";
|
|
27805
28795
|
async function handleOptimizeCommand(chatId, channel, _args, messageId) {
|
|
@@ -28029,7 +29019,7 @@ async function applyFinding(chatId, channel, index, messageId) {
|
|
|
28029
29019
|
await showFinding(chatId, channel, index + 1, effectiveMsgId);
|
|
28030
29020
|
return;
|
|
28031
29021
|
}
|
|
28032
|
-
const original =
|
|
29022
|
+
const original = readFileSync20(targetPath, "utf-8");
|
|
28033
29023
|
const backupPath = targetPath + `.bak.${Date.now()}`;
|
|
28034
29024
|
writeFileSync9(backupPath, original, "utf-8");
|
|
28035
29025
|
pruneBackups2(targetPath);
|
|
@@ -29358,6 +30348,7 @@ async function handleNewchatCommand(chatId, commandArgs, msg, channel) {
|
|
|
29358
30348
|
const summarized = await summarizeSession(chatId);
|
|
29359
30349
|
clearSession(chatId);
|
|
29360
30350
|
clearChatPaidSlots(chatId);
|
|
30351
|
+
clearUsage(chatId);
|
|
29361
30352
|
setSessionStartedAt(chatId);
|
|
29362
30353
|
logActivity(getDb(), { chatId, source: "telegram", eventType: "config_changed", summary: "New session started", detail: { field: "session", action: "reset", summarized } });
|
|
29363
30354
|
if (typeof channel.sendKeyboard === "function" && oldSessionId) {
|
|
@@ -29411,6 +30402,7 @@ async function handleClearCommand(chatId, _commandArgs, _msg, channel) {
|
|
|
29411
30402
|
stopAllSideQuests(chatId);
|
|
29412
30403
|
clearSession(chatId);
|
|
29413
30404
|
clearChatPaidSlots(chatId);
|
|
30405
|
+
clearUsage(chatId);
|
|
29414
30406
|
setSessionStartedAt(chatId);
|
|
29415
30407
|
logActivity(getDb(), { chatId, source: "telegram", eventType: "config_changed", summary: "Session cleared (no summary)", detail: { field: "session", action: "clear" } });
|
|
29416
30408
|
await channel.sendText(chatId, "\u{1F9FD} Session cleared. No summary saved.", { parseMode: "plain", priority: 0 /* P0_CRITICAL */ });
|
|
@@ -29487,6 +30479,17 @@ async function handleSummarizeCommand(chatId, commandArgs, msg, channel) {
|
|
|
29487
30479
|
}
|
|
29488
30480
|
}
|
|
29489
30481
|
}
|
|
30482
|
+
function formatSummarizerLabel(backend2, model2, fallbackModel) {
|
|
30483
|
+
if (backend2 === "off") return "Off";
|
|
30484
|
+
if (backend2) return `${backend2}: ${model2 ?? "default"}`;
|
|
30485
|
+
return `Auto (${fallbackModel ?? "default"})`;
|
|
30486
|
+
}
|
|
30487
|
+
function summarizerStatusLine(chatId, adapter) {
|
|
30488
|
+
const { config: config2, source } = getSummarizerWithSource(chatId);
|
|
30489
|
+
const label2 = formatSummarizerLabel(config2.backend, config2.model, adapter?.summarizerModel);
|
|
30490
|
+
if (source === "auto") return label2.toLowerCase();
|
|
30491
|
+
return `${label2} (${source === "global" ? "global" : "per-chat"})`;
|
|
30492
|
+
}
|
|
29490
30493
|
async function handleStatusCommand(chatId, commandArgs, msg, channel) {
|
|
29491
30494
|
const sessionId = getSessionId(chatId);
|
|
29492
30495
|
const cwd = getCwd(chatId);
|
|
@@ -29503,12 +30506,21 @@ async function handleStatusCommand(chatId, commandArgs, msg, channel) {
|
|
|
29503
30506
|
const thinking2 = getThinkingLevel(chatId);
|
|
29504
30507
|
const mode = getMode(chatId);
|
|
29505
30508
|
const modelSig = getModelSignature(chatId);
|
|
29506
|
-
|
|
29507
|
-
|
|
29508
|
-
|
|
29509
|
-
|
|
29510
|
-
|
|
29511
|
-
|
|
30509
|
+
let contextLine;
|
|
30510
|
+
if (adapter?.type === "api") {
|
|
30511
|
+
const { estimateContextUsage: estimateContextUsage2 } = await Promise.resolve().then(() => (init_api_context(), api_context_exports));
|
|
30512
|
+
const contextMax = adapter.contextWindow[model2] ?? 8192;
|
|
30513
|
+
const ctxEst = estimateContextUsage2(chatId, contextMax);
|
|
30514
|
+
const ctxBar = buildBar(ctxEst.percentage);
|
|
30515
|
+
const usedK = (ctxEst.estimatedTokens / 1e3).toFixed(1);
|
|
30516
|
+
const maxK = (contextMax / 1e3).toFixed(0);
|
|
30517
|
+
contextLine = `\u{1F4D0} Context: ${ctxBar} ${usedK}K/${maxK}K (${ctxEst.percentage.toFixed(1)}%) \xB7 compacts at 85%`;
|
|
30518
|
+
} else {
|
|
30519
|
+
const pairCount = getMessagePairCount(chatId);
|
|
30520
|
+
const threshold = 30;
|
|
30521
|
+
const remaining = Math.max(0, threshold - pairCount);
|
|
30522
|
+
contextLine = `\u{1F4D0} Session: ${pairCount}/${threshold} messages \xB7 compacts in ${remaining}`;
|
|
30523
|
+
}
|
|
29512
30524
|
const bootRow = getDb().prepare("SELECT value FROM meta WHERE key = 'boot_time'").get();
|
|
29513
30525
|
let uptimeStr = "unknown";
|
|
29514
30526
|
if (bootRow) {
|
|
@@ -29562,20 +30574,26 @@ async function handleStatusCommand(chatId, commandArgs, msg, channel) {
|
|
|
29562
30574
|
const iDeep = iStats.agentic;
|
|
29563
30575
|
const intentLine = iTotal > 0 ? `\u26A1 ${iTotal} messages: ${iQuick} quick, ${iDeep} deep` : `\u26A1 No messages classified yet`;
|
|
29564
30576
|
const sqCount = getActiveSideQuestCount(chatId);
|
|
30577
|
+
const { getPinnedSkills: getPinnedSkills2 } = await Promise.resolve().then(() => (init_chat_settings(), chat_settings_exports));
|
|
30578
|
+
const pinnedSkills = getPinnedSkills2(chatId);
|
|
30579
|
+
const pinnedLine = pinnedSkills.length > 0 ? `\u{1F4CC} Pinned: ${pinnedSkills.join(", ")}` : `\u{1F4CC} Pinned: none`;
|
|
29565
30580
|
const lines = [
|
|
29566
30581
|
`\u{1F43E} CC-Claw v${VERSION}`,
|
|
29567
30582
|
`\u23F1 Uptime: ${uptimeStr}`,
|
|
30583
|
+
isChatBusy(chatId) ? `\u{1F534} Agent: Busy` : `\u{1F7E2} Agent: Idle`,
|
|
29568
30584
|
``,
|
|
29569
30585
|
buildSectionHeader("Engine"),
|
|
29570
30586
|
`\u{1F9E0} ${adapter?.displayName ?? backendId ?? "not set"} \xB7 ${formatModelShort(model2)}${slotInfo}`,
|
|
29571
30587
|
`\u{1F4AD} Think: ${thinking2} \xB7 Mode: ${mode}`,
|
|
29572
30588
|
`\u{1F916} Agents: ${getAgentMode(chatId)}`,
|
|
30589
|
+
pinnedLine,
|
|
29573
30590
|
`\u{1F507} Voice: ${voice2 ? "on" : "off"} \xB7 Sig: ${modelSig}`,
|
|
30591
|
+
`\u{1F4DD} Summarizer: ${summarizerStatusLine(chatId, adapter)}`,
|
|
29574
30592
|
``,
|
|
29575
30593
|
buildSectionHeader("Session"),
|
|
29576
30594
|
`\u{1F4CB} ${sessionId ?? "no active session"}`,
|
|
29577
30595
|
`\u{1F4C1} ${cwd ?? "default workspace"}`,
|
|
29578
|
-
|
|
30596
|
+
contextLine,
|
|
29579
30597
|
...sqCount > 0 ? [`\u{1F5FA} Side quests: ${sqCount} active`] : [],
|
|
29580
30598
|
``,
|
|
29581
30599
|
buildSectionHeader("Usage"),
|
|
@@ -29614,11 +30632,16 @@ async function handleBackendCommand2(chatId, commandArgs, msg, channel) {
|
|
|
29614
30632
|
async function handleBackendShortcutCommand(chatId, commandArgs, msg, channel) {
|
|
29615
30633
|
const command = msg.command;
|
|
29616
30634
|
const backendId = command;
|
|
29617
|
-
if (getAllBackendIds().includes(backendId)) {
|
|
29618
|
-
await sendBackendSwitchConfirmation(chatId, backendId, channel);
|
|
29619
|
-
} else {
|
|
30635
|
+
if (!getAllBackendIds().includes(backendId)) {
|
|
29620
30636
|
await channel.sendText(chatId, `Backend "${command}" is not available.`, { parseMode: "plain" });
|
|
30637
|
+
return;
|
|
29621
30638
|
}
|
|
30639
|
+
if (getDisabledBackends().includes(backendId)) {
|
|
30640
|
+
const { sendBackendConfigPanel: sendBackendConfigPanel2 } = await Promise.resolve().then(() => (init_ui(), ui_exports));
|
|
30641
|
+
await sendBackendConfigPanel2(chatId, backendId, channel);
|
|
30642
|
+
return;
|
|
30643
|
+
}
|
|
30644
|
+
await sendBackendSwitchConfirmation(chatId, backendId, channel);
|
|
29622
30645
|
}
|
|
29623
30646
|
async function handleModelCommand(chatId, commandArgs, msg, channel) {
|
|
29624
30647
|
await sendModelKeyboard(chatId, channel);
|
|
@@ -29986,8 +31009,17 @@ async function handleSummarizerCommand(chatId, commandArgs, msg, channel) {
|
|
|
29986
31009
|
} catch {
|
|
29987
31010
|
adapter = null;
|
|
29988
31011
|
}
|
|
29989
|
-
const current =
|
|
29990
|
-
const
|
|
31012
|
+
const { config: current, source, globalConfig } = getSummarizerWithSource(chatId);
|
|
31013
|
+
const overrideCount = countSummarizerOverrides();
|
|
31014
|
+
const currentLabel = formatSummarizerLabel(current.backend, current.model, adapter?.summarizerModel);
|
|
31015
|
+
const sourceTag = source === "per-chat" ? "per-chat override" : source === "global" ? "global default" : "auto";
|
|
31016
|
+
const headerLines = [`Summarizer: ${currentLabel} (${sourceTag})`];
|
|
31017
|
+
if (globalConfig.backend) {
|
|
31018
|
+
headerLines.push(`Global default: ${formatSummarizerLabel(globalConfig.backend, globalConfig.model)}`);
|
|
31019
|
+
}
|
|
31020
|
+
if (overrideCount > 0) {
|
|
31021
|
+
headerLines.push(`${overrideCount} chat${overrideCount === 1 ? "" : "s"} with per-chat overrides`);
|
|
31022
|
+
}
|
|
29991
31023
|
if (typeof channel.sendKeyboard === "function") {
|
|
29992
31024
|
const isAuto = !current.backend && current.backend !== "off";
|
|
29993
31025
|
const isOff = current.backend === "off";
|
|
@@ -30035,7 +31067,14 @@ async function handleSummarizerCommand(chatId, commandArgs, msg, channel) {
|
|
|
30035
31067
|
}]);
|
|
30036
31068
|
}
|
|
30037
31069
|
}
|
|
30038
|
-
|
|
31070
|
+
if (overrideCount > 0) {
|
|
31071
|
+
buttons.push([{
|
|
31072
|
+
label: `Clear All Overrides (${overrideCount})`,
|
|
31073
|
+
data: "summarizer:clearall",
|
|
31074
|
+
style: "danger"
|
|
31075
|
+
}]);
|
|
31076
|
+
}
|
|
31077
|
+
await channel.sendKeyboard(chatId, headerLines.join("\n"), buttons);
|
|
30039
31078
|
} else {
|
|
30040
31079
|
await channel.sendText(chatId, `Summarizer: ${currentLabel}
|
|
30041
31080
|
|
|
@@ -30752,7 +31791,7 @@ __export(import_exports, {
|
|
|
30752
31791
|
parseClaudeConfig: () => parseClaudeConfig,
|
|
30753
31792
|
parseCursorConfig: () => parseCursorConfig
|
|
30754
31793
|
});
|
|
30755
|
-
import { readFileSync as
|
|
31794
|
+
import { readFileSync as readFileSync21 } from "fs";
|
|
30756
31795
|
import { join as join31 } from "path";
|
|
30757
31796
|
import { homedir as homedir10 } from "os";
|
|
30758
31797
|
function parseClaudeConfig(config2) {
|
|
@@ -30769,7 +31808,7 @@ async function discoverFromSource(source) {
|
|
|
30769
31808
|
case "claude": {
|
|
30770
31809
|
const path = join31(homedir10(), ".claude", "settings.json");
|
|
30771
31810
|
try {
|
|
30772
|
-
return parseClaudeConfig(JSON.parse(
|
|
31811
|
+
return parseClaudeConfig(JSON.parse(readFileSync21(path, "utf-8")));
|
|
30773
31812
|
} catch {
|
|
30774
31813
|
warn(`[mcp-import] Could not read ${path}`);
|
|
30775
31814
|
return [];
|
|
@@ -30778,7 +31817,7 @@ async function discoverFromSource(source) {
|
|
|
30778
31817
|
case "cursor": {
|
|
30779
31818
|
const path = join31(homedir10(), ".cursor", "mcp.json");
|
|
30780
31819
|
try {
|
|
30781
|
-
return parseCursorConfig(JSON.parse(
|
|
31820
|
+
return parseCursorConfig(JSON.parse(readFileSync21(path, "utf-8")));
|
|
30782
31821
|
} catch {
|
|
30783
31822
|
warn(`[mcp-import] Could not read ${path}`);
|
|
30784
31823
|
return [];
|
|
@@ -31442,6 +32481,10 @@ async function handleCallback(chatId, data, channel, messageId) {
|
|
|
31442
32481
|
if (data.startsWith("backend:")) {
|
|
31443
32482
|
const chosen = data.slice(8);
|
|
31444
32483
|
if (!getAllBackendIds().includes(chosen)) return;
|
|
32484
|
+
if (getDisabledBackends().includes(chosen)) {
|
|
32485
|
+
await sendBackendConfigPanel(chatId, chosen, channel, messageId, false);
|
|
32486
|
+
return;
|
|
32487
|
+
}
|
|
31445
32488
|
const previous = getBackend(chatId);
|
|
31446
32489
|
if (chosen === previous) {
|
|
31447
32490
|
await sendBackendConfigPanel(chatId, chosen, channel, messageId, false);
|
|
@@ -31495,6 +32538,32 @@ async function handleCallback(chatId, data, channel, messageId) {
|
|
|
31495
32538
|
}
|
|
31496
32539
|
}
|
|
31497
32540
|
await sendBackendConfigPanel(chatId, backendId, channel, messageId, false);
|
|
32541
|
+
} else if (rest.startsWith("disable:")) {
|
|
32542
|
+
const backendId = rest.slice(8);
|
|
32543
|
+
const { sendBackendDisableFlow: sendBackendDisableFlow2 } = await Promise.resolve().then(() => (init_ui(), ui_exports));
|
|
32544
|
+
await sendBackendDisableFlow2(chatId, backendId, channel, messageId);
|
|
32545
|
+
} else if (rest.startsWith("enable:")) {
|
|
32546
|
+
const backendId = rest.slice(7);
|
|
32547
|
+
setBackendDisabled(backendId, false);
|
|
32548
|
+
await sendBackendConfigPanel(chatId, backendId, channel, messageId, false);
|
|
32549
|
+
} else if (rest.startsWith("reassign:")) {
|
|
32550
|
+
const parts = rest.slice(9).split(":");
|
|
32551
|
+
const disablingBackend = parts[0];
|
|
32552
|
+
const targetBackend = parts[1];
|
|
32553
|
+
const current = getBackend(chatId);
|
|
32554
|
+
const { executeBackendDisable: executeBackendDisable2 } = await Promise.resolve().then(() => (init_ui(), ui_exports));
|
|
32555
|
+
await executeBackendDisable2(chatId, disablingBackend, channel, messageId, {
|
|
32556
|
+
reassignTo: targetBackend,
|
|
32557
|
+
...current === disablingBackend ? { switchTo: targetBackend } : {}
|
|
32558
|
+
});
|
|
32559
|
+
} else if (rest.startsWith("disableswitch:")) {
|
|
32560
|
+
const parts = rest.slice(14).split(":");
|
|
32561
|
+
const disablingBackend = parts[0];
|
|
32562
|
+
const switchToBackend = parts[1];
|
|
32563
|
+
const { executeBackendDisable: executeBackendDisable2 } = await Promise.resolve().then(() => (init_ui(), ui_exports));
|
|
32564
|
+
await executeBackendDisable2(chatId, disablingBackend, channel, messageId, {
|
|
32565
|
+
switchTo: switchToBackend
|
|
32566
|
+
});
|
|
31498
32567
|
}
|
|
31499
32568
|
} else if (data.startsWith("backend_confirm_clear:")) {
|
|
31500
32569
|
const target = data.slice("backend_confirm_clear:".length);
|
|
@@ -31558,14 +32627,40 @@ ${value ? "Full tool inputs/results will be saved to ~/.cc-claw/logs/sessions/"
|
|
|
31558
32627
|
if (rest === "auto") {
|
|
31559
32628
|
clearSummarizer(chatId);
|
|
31560
32629
|
await channel.sendText(chatId, "Summarizer set to auto (uses active backend).", { parseMode: "plain" });
|
|
31561
|
-
} else if (rest === "
|
|
31562
|
-
|
|
31563
|
-
await channel.sendText(chatId, "
|
|
32630
|
+
} else if (rest === "clearall") {
|
|
32631
|
+
const cleared = clearAllSummarizerOverrides();
|
|
32632
|
+
await channel.sendText(chatId, `Cleared ${cleared} per-chat override${cleared === 1 ? "" : "s"}. All chats now use the global default.`, { parseMode: "plain" });
|
|
32633
|
+
} else if (rest.startsWith("promote:")) {
|
|
32634
|
+
const promoteValue = rest.slice(8);
|
|
32635
|
+
const db3 = getDb();
|
|
32636
|
+
const promote = db3.transaction(() => {
|
|
32637
|
+
if (promoteValue === "off") {
|
|
32638
|
+
setGlobalSummarizer("off", null);
|
|
32639
|
+
} else {
|
|
32640
|
+
const [bk, ...modelParts] = promoteValue.split(":");
|
|
32641
|
+
setGlobalSummarizer(bk, modelParts.join(":") || null);
|
|
32642
|
+
}
|
|
32643
|
+
return clearAllSummarizerOverrides();
|
|
32644
|
+
});
|
|
32645
|
+
const cleared = promote();
|
|
32646
|
+
const label2 = promoteValue === "off" ? "Off" : promoteValue;
|
|
32647
|
+
const clearedNote = cleared > 0 ? ` Cleared ${cleared} per-chat override${cleared === 1 ? "" : "s"}.` : "";
|
|
32648
|
+
await channel.sendText(chatId, `Global summarizer set to ${label2}.${clearedNote} All chats now use this default.`, { parseMode: "plain" });
|
|
31564
32649
|
} else {
|
|
31565
|
-
const
|
|
31566
|
-
const
|
|
32650
|
+
const isOff = rest === "off";
|
|
32651
|
+
const bk = isOff ? "off" : rest.split(":")[0];
|
|
32652
|
+
const mdl = isOff ? null : rest.split(":").slice(1).join(":") || null;
|
|
31567
32653
|
setSummarizer(chatId, bk, mdl);
|
|
31568
|
-
|
|
32654
|
+
const displayLabel = isOff ? "Off" : `${bk}:${mdl ?? "default"}`;
|
|
32655
|
+
const confirmMsg = isOff ? "Session summarization disabled for this chat." : `Summarizer pinned to ${displayLabel} for this chat.`;
|
|
32656
|
+
const promoteData = `summarizer:promote:${rest}`;
|
|
32657
|
+
if (typeof channel.sendKeyboard === "function") {
|
|
32658
|
+
await channel.sendKeyboard(chatId, confirmMsg, [
|
|
32659
|
+
[{ label: "Set as Global Default", data: promoteData }]
|
|
32660
|
+
]);
|
|
32661
|
+
} else {
|
|
32662
|
+
await channel.sendText(chatId, confirmMsg, { parseMode: "plain" });
|
|
32663
|
+
}
|
|
31569
32664
|
}
|
|
31570
32665
|
} else if (data.startsWith("perms:")) {
|
|
31571
32666
|
let chosen = data.slice(6);
|
|
@@ -33309,10 +34404,10 @@ ${formatValidationReport2(validation)}` : "\n\n\u2705 Quality checks passed.";
|
|
|
33309
34404
|
return;
|
|
33310
34405
|
}
|
|
33311
34406
|
try {
|
|
33312
|
-
const { readFileSync:
|
|
34407
|
+
const { readFileSync: readFileSync36, writeFileSync: writeFileSync16 } = await import("fs");
|
|
33313
34408
|
const { updateFrontmatter: updateFrontmatter2, ensureThreeTierFrontmatter: ensureThreeTierFrontmatter2 } = await Promise.resolve().then(() => (init_frontmatter(), frontmatter_exports));
|
|
33314
34409
|
const { invalidateSkillCache: invalidateSkillCache2 } = await Promise.resolve().then(() => (init_discover(), discover_exports));
|
|
33315
|
-
const raw =
|
|
34410
|
+
const raw = readFileSync36(skill.filePath, "utf-8");
|
|
33316
34411
|
let updated = ensureThreeTierFrontmatter2(raw, { name: skillName, source: "cc-claw" });
|
|
33317
34412
|
updated = updateFrontmatter2(updated, { status: "approved" });
|
|
33318
34413
|
writeFileSync16(skill.filePath, updated, "utf-8");
|
|
@@ -33362,14 +34457,14 @@ ${stillUnapproved.length} more skill${stillUnapproved.length !== 1 ? "s" : ""} t
|
|
|
33362
34457
|
return;
|
|
33363
34458
|
}
|
|
33364
34459
|
try {
|
|
33365
|
-
const { readFileSync:
|
|
34460
|
+
const { readFileSync: readFileSync36, writeFileSync: writeFileSync16 } = await import("fs");
|
|
33366
34461
|
const { updateFrontmatter: updateFrontmatter2, ensureThreeTierFrontmatter: ensureThreeTierFrontmatter2 } = await Promise.resolve().then(() => (init_frontmatter(), frontmatter_exports));
|
|
33367
34462
|
const { invalidateSkillCache: invalidateSkillCache2 } = await Promise.resolve().then(() => (init_discover(), discover_exports));
|
|
33368
34463
|
let approvedCount = 0;
|
|
33369
34464
|
const issues = [];
|
|
33370
34465
|
for (const skill of unapproved) {
|
|
33371
34466
|
try {
|
|
33372
|
-
const raw =
|
|
34467
|
+
const raw = readFileSync36(skill.filePath, "utf-8");
|
|
33373
34468
|
let updated = ensureThreeTierFrontmatter2(raw, { name: skill.name, source: "cc-claw" });
|
|
33374
34469
|
updated = updateFrontmatter2(updated, { status: "approved" });
|
|
33375
34470
|
writeFileSync16(skill.filePath, updated, "utf-8");
|
|
@@ -33403,6 +34498,29 @@ ${issues.join("\n")}`;
|
|
|
33403
34498
|
);
|
|
33404
34499
|
}
|
|
33405
34500
|
return;
|
|
34501
|
+
} else if (data.startsWith("pin:")) {
|
|
34502
|
+
const skillName = data.slice(4);
|
|
34503
|
+
const { getPinnedSkills: getPinnedSkills2, setPinnedSkills: setPinnedSkills2 } = await Promise.resolve().then(() => (init_chat_settings(), chat_settings_exports));
|
|
34504
|
+
const pinned = getPinnedSkills2(chatId);
|
|
34505
|
+
if (pinned.includes(skillName)) {
|
|
34506
|
+
setPinnedSkills2(chatId, pinned.filter((n) => n !== skillName));
|
|
34507
|
+
await sendOrEditKeyboard(
|
|
34508
|
+
chatId,
|
|
34509
|
+
channel,
|
|
34510
|
+
messageId,
|
|
34511
|
+
`\u{1F4CC} Unpinned "${skillName}" from this chat.`,
|
|
34512
|
+
[[{ label: "\u2190 Skills", data: "skills:page:1" }]]
|
|
34513
|
+
);
|
|
34514
|
+
} else {
|
|
34515
|
+
setPinnedSkills2(chatId, [...pinned, skillName]);
|
|
34516
|
+
await sendOrEditKeyboard(
|
|
34517
|
+
chatId,
|
|
34518
|
+
channel,
|
|
34519
|
+
messageId,
|
|
34520
|
+
`\u{1F4CC} Pinned "${skillName}" to this chat. It will always inject regardless of message content.`,
|
|
34521
|
+
[[{ label: "\u2190 Skills", data: "skills:page:1" }]]
|
|
34522
|
+
);
|
|
34523
|
+
}
|
|
33406
34524
|
} else if (data.startsWith("skill:")) {
|
|
33407
34525
|
const parts = data.slice(6).split(":");
|
|
33408
34526
|
let skillName;
|
|
@@ -33610,134 +34728,6 @@ var init_thread_wrapper = __esm({
|
|
|
33610
34728
|
}
|
|
33611
34729
|
});
|
|
33612
34730
|
|
|
33613
|
-
// src/intent/complexity.ts
|
|
33614
|
-
var complexity_exports = {};
|
|
33615
|
-
__export(complexity_exports, {
|
|
33616
|
-
classifyComplexity: () => classifyComplexity
|
|
33617
|
-
});
|
|
33618
|
-
function wordCount(text) {
|
|
33619
|
-
return text.trim().split(/\s+/).filter(Boolean).length;
|
|
33620
|
-
}
|
|
33621
|
-
function classifyComplexity(text) {
|
|
33622
|
-
const trimmed = text.trim();
|
|
33623
|
-
const lower = trimmed.toLowerCase();
|
|
33624
|
-
const words = wordCount(trimmed);
|
|
33625
|
-
if (TRIVIAL_EXACT.has(lower)) {
|
|
33626
|
-
log(`[complexity] "${lower}" -> trivial (exact match)`);
|
|
33627
|
-
return "trivial";
|
|
33628
|
-
}
|
|
33629
|
-
if (trimmed.length <= 4 && /^[\p{Emoji}\s]+$/u.test(trimmed)) {
|
|
33630
|
-
log(`[complexity] "${trimmed}" -> trivial (emoji-only)`);
|
|
33631
|
-
return "trivial";
|
|
33632
|
-
}
|
|
33633
|
-
if (COMPLEX_SIGNALS.test(trimmed) && (words > 15 || trimmed.length > 200)) {
|
|
33634
|
-
log(`[complexity] "${trimmed.slice(0, 40)}..." -> complex (signal words + length)`);
|
|
33635
|
-
return "complex";
|
|
33636
|
-
}
|
|
33637
|
-
if (MUTATION_VERBS.test(trimmed)) {
|
|
33638
|
-
const tier = words > 30 || trimmed.length > 300 ? "complex" : "moderate";
|
|
33639
|
-
log(`[complexity] "${trimmed.slice(0, 40)}..." -> ${tier} (mutation verb)`);
|
|
33640
|
-
return tier;
|
|
33641
|
-
}
|
|
33642
|
-
for (const pattern of CODE_PATTERNS) {
|
|
33643
|
-
if (pattern.test(trimmed)) {
|
|
33644
|
-
const tier = words > 30 || trimmed.length > 300 ? "complex" : "moderate";
|
|
33645
|
-
log(`[complexity] "${trimmed.slice(0, 40)}..." -> ${tier} (code pattern)`);
|
|
33646
|
-
return tier;
|
|
33647
|
-
}
|
|
33648
|
-
}
|
|
33649
|
-
if (trimmed.length > 200 || words > 25) {
|
|
33650
|
-
const tier = words > 30 || trimmed.length > 300 ? "complex" : "moderate";
|
|
33651
|
-
log(`[complexity] "${trimmed.slice(0, 40)}..." -> ${tier} (long message)`);
|
|
33652
|
-
return tier;
|
|
33653
|
-
}
|
|
33654
|
-
for (const pattern of QUESTION_PATTERNS) {
|
|
33655
|
-
if (pattern.test(trimmed)) {
|
|
33656
|
-
log(`[complexity] "${trimmed.slice(0, 40)}..." -> simple (question)`);
|
|
33657
|
-
return "simple";
|
|
33658
|
-
}
|
|
33659
|
-
}
|
|
33660
|
-
log(`[complexity] "${trimmed.slice(0, 40)}..." -> moderate (default)`);
|
|
33661
|
-
return "moderate";
|
|
33662
|
-
}
|
|
33663
|
-
var TRIVIAL_EXACT, COMPLEX_SIGNALS, MUTATION_VERBS, CODE_PATTERNS, QUESTION_PATTERNS;
|
|
33664
|
-
var init_complexity = __esm({
|
|
33665
|
-
"src/intent/complexity.ts"() {
|
|
33666
|
-
"use strict";
|
|
33667
|
-
init_log();
|
|
33668
|
-
TRIVIAL_EXACT = /* @__PURE__ */ new Set([
|
|
33669
|
-
"hey",
|
|
33670
|
-
"hi",
|
|
33671
|
-
"hello",
|
|
33672
|
-
"yo",
|
|
33673
|
-
"sup",
|
|
33674
|
-
"howdy",
|
|
33675
|
-
"hiya",
|
|
33676
|
-
"thanks",
|
|
33677
|
-
"thank you",
|
|
33678
|
-
"thx",
|
|
33679
|
-
"ty",
|
|
33680
|
-
"thank u",
|
|
33681
|
-
"ok",
|
|
33682
|
-
"okay",
|
|
33683
|
-
"k",
|
|
33684
|
-
"kk",
|
|
33685
|
-
"cool",
|
|
33686
|
-
"nice",
|
|
33687
|
-
"great",
|
|
33688
|
-
"awesome",
|
|
33689
|
-
"perfect",
|
|
33690
|
-
"good morning",
|
|
33691
|
-
"good night",
|
|
33692
|
-
"good evening",
|
|
33693
|
-
"gm",
|
|
33694
|
-
"gn",
|
|
33695
|
-
"bye",
|
|
33696
|
-
"goodbye",
|
|
33697
|
-
"later",
|
|
33698
|
-
"see ya",
|
|
33699
|
-
"cya",
|
|
33700
|
-
"lol",
|
|
33701
|
-
"lmao",
|
|
33702
|
-
"haha",
|
|
33703
|
-
"heh",
|
|
33704
|
-
"np",
|
|
33705
|
-
"no problem",
|
|
33706
|
-
"no worries",
|
|
33707
|
-
"nw",
|
|
33708
|
-
"got it",
|
|
33709
|
-
"understood",
|
|
33710
|
-
"roger",
|
|
33711
|
-
"copy",
|
|
33712
|
-
"good",
|
|
33713
|
-
"fine",
|
|
33714
|
-
"alright",
|
|
33715
|
-
"sure",
|
|
33716
|
-
"yes",
|
|
33717
|
-
"no",
|
|
33718
|
-
"yep",
|
|
33719
|
-
"nope",
|
|
33720
|
-
"yeah",
|
|
33721
|
-
"nah"
|
|
33722
|
-
]);
|
|
33723
|
-
COMPLEX_SIGNALS = /\b(architect|design|research|plan|strategy|migration|microservice|distributed|end-to-end|trade-offs|pros\s+and\s+cons)\b/i;
|
|
33724
|
-
MUTATION_VERBS = /\b(fix|create|build|deploy|refactor|implement|write|add|update|edit|test|debug)\b/i;
|
|
33725
|
-
CODE_PATTERNS = [
|
|
33726
|
-
/```/,
|
|
33727
|
-
// code blocks
|
|
33728
|
-
/\.[a-z]{1,5}\b/,
|
|
33729
|
-
// file extensions (.ts, .py, .json)
|
|
33730
|
-
/[/\\][\w.-]+/
|
|
33731
|
-
// file paths (/src/foo, .\bar)
|
|
33732
|
-
];
|
|
33733
|
-
QUESTION_PATTERNS = [
|
|
33734
|
-
/^(?:what|which|where|when|why|how)\b/i,
|
|
33735
|
-
/^(?:show|tell|list|explain)\b/i,
|
|
33736
|
-
/^(?:is|are)\b/i
|
|
33737
|
-
];
|
|
33738
|
-
}
|
|
33739
|
-
});
|
|
33740
|
-
|
|
33741
34731
|
// src/intent/auto-route.ts
|
|
33742
34732
|
var auto_route_exports = {};
|
|
33743
34733
|
__export(auto_route_exports, {
|
|
@@ -34564,6 +35554,8 @@ You're still in discussion mode \u2014 try again or click a button to exit.`, {
|
|
|
34564
35554
|
return;
|
|
34565
35555
|
}
|
|
34566
35556
|
getTypingManager().acquire(chatId, channel);
|
|
35557
|
+
let stopDraftTimer = () => {
|
|
35558
|
+
};
|
|
34567
35559
|
try {
|
|
34568
35560
|
const tMode = settings.getMode();
|
|
34569
35561
|
const tVerbose = settings.getVerboseLevel();
|
|
@@ -34623,7 +35615,7 @@ You're still in discussion mode \u2014 try again or click a button to exit.`, {
|
|
|
34623
35615
|
draftState.dirty = true;
|
|
34624
35616
|
};
|
|
34625
35617
|
}
|
|
34626
|
-
|
|
35618
|
+
stopDraftTimer = () => {
|
|
34627
35619
|
if (draftState?.flushTimer) {
|
|
34628
35620
|
clearInterval(draftState.flushTimer);
|
|
34629
35621
|
draftState.flushTimer = null;
|
|
@@ -34659,9 +35651,14 @@ You're still in discussion mode \u2014 try again or click a button to exit.`, {
|
|
|
34659
35651
|
} catch {
|
|
34660
35652
|
}
|
|
34661
35653
|
},
|
|
34662
|
-
onCompaction: (cid) => {
|
|
34663
|
-
|
|
34664
|
-
|
|
35654
|
+
onCompaction: (cid, phase) => {
|
|
35655
|
+
if (phase === "triggered") {
|
|
35656
|
+
channel.sendText(cid, "\u{1F4BE} Context compaction triggered \u2014 saving conversation to memory...").catch(() => {
|
|
35657
|
+
});
|
|
35658
|
+
} else {
|
|
35659
|
+
channel.sendText(cid, "\u{1F4BE} Context saved to memory.").catch(() => {
|
|
35660
|
+
});
|
|
35661
|
+
}
|
|
34665
35662
|
},
|
|
34666
35663
|
onSlotRotation: (cid, from, to) => {
|
|
34667
35664
|
const slots = getGeminiSlots();
|
|
@@ -34679,7 +35676,7 @@ You're still in discussion mode \u2014 try again or click a button to exit.`, {
|
|
|
34679
35676
|
});
|
|
34680
35677
|
}
|
|
34681
35678
|
});
|
|
34682
|
-
|
|
35679
|
+
stopDraftTimer();
|
|
34683
35680
|
const elapsedMs = Date.now() - sigT0;
|
|
34684
35681
|
const elapsedSec = (elapsedMs / 1e3).toFixed(1);
|
|
34685
35682
|
if (liveStatus && response.thinkingText?.trim()) {
|
|
@@ -35081,6 +36078,12 @@ async function executeJob(job) {
|
|
|
35081
36078
|
log(`[scheduler] Running job #${job.id}: ${job.description} (model=${resolvedModel})`);
|
|
35082
36079
|
try {
|
|
35083
36080
|
const backendId = resolveJobBackendId(job);
|
|
36081
|
+
if (getDisabledBackends().includes(backendId)) {
|
|
36082
|
+
const msg = `Backend "${backendId}" is disabled. Reassign this job with /editjob or re-enable the backend.`;
|
|
36083
|
+
completeJobRun(runId, "skipped", { error: msg, durationMs: Date.now() - t0 });
|
|
36084
|
+
await notifyJobFailure(job, msg);
|
|
36085
|
+
return;
|
|
36086
|
+
}
|
|
35084
36087
|
const limitMsg = checkBackendLimits(backendId);
|
|
35085
36088
|
if (limitMsg) {
|
|
35086
36089
|
completeJobRun(runId, "skipped", { error: limitMsg, durationMs: Date.now() - t0 });
|
|
@@ -35511,7 +36514,7 @@ var init_wrap_backend = __esm({
|
|
|
35511
36514
|
});
|
|
35512
36515
|
|
|
35513
36516
|
// src/agents/runners/config-loader.ts
|
|
35514
|
-
import { readFileSync as
|
|
36517
|
+
import { readFileSync as readFileSync22, readdirSync as readdirSync14, existsSync as existsSync30, mkdirSync as mkdirSync10, watchFile, unwatchFile } from "fs";
|
|
35515
36518
|
import { join as join33 } from "path";
|
|
35516
36519
|
import { execFileSync as execFileSync3 } from "child_process";
|
|
35517
36520
|
function resolveExecutable2(config2) {
|
|
@@ -35653,7 +36656,7 @@ function configToRunner(config2) {
|
|
|
35653
36656
|
}
|
|
35654
36657
|
function loadRunnerConfig(filePath) {
|
|
35655
36658
|
try {
|
|
35656
|
-
const content =
|
|
36659
|
+
const content = readFileSync22(filePath, "utf-8");
|
|
35657
36660
|
return JSON.parse(content);
|
|
35658
36661
|
} catch (err) {
|
|
35659
36662
|
warn(`[runners] Failed to load config ${filePath}: ${err}`);
|
|
@@ -36269,7 +37272,7 @@ var init_telegram2 = __esm({
|
|
|
36269
37272
|
};
|
|
36270
37273
|
}
|
|
36271
37274
|
async start(handler) {
|
|
36272
|
-
await this.bot.api.setMyCommands([
|
|
37275
|
+
await this.throttle.send(this.primaryChatId, "setMyCommands", () => this.bot.api.setMyCommands([
|
|
36273
37276
|
// Core
|
|
36274
37277
|
{ command: "menu", description: "Home screen \u2014 quick-access keyboard" },
|
|
36275
37278
|
{ command: "m", description: "Home screen (alias for /menu)" },
|
|
@@ -36347,7 +37350,7 @@ var init_telegram2 = __esm({
|
|
|
36347
37350
|
// Context & info
|
|
36348
37351
|
{ command: "info", description: "Current chat context (ID, topic, sender, settings)" },
|
|
36349
37352
|
{ command: "council", description: "Multi-model debate (select models, anonymous rounds)" }
|
|
36350
|
-
]);
|
|
37353
|
+
]));
|
|
36351
37354
|
this.bot.on("message", async (ctx) => {
|
|
36352
37355
|
const chatId = ctx.chat.id.toString();
|
|
36353
37356
|
const senderId = ctx.from?.id?.toString() ?? "";
|
|
@@ -36393,15 +37396,13 @@ var init_telegram2 = __esm({
|
|
|
36393
37396
|
const chatId = ctx.callbackQuery.message?.chat?.id?.toString() ?? userId;
|
|
36394
37397
|
log(`[telegram] Callback from user ${userId} in chat ${chatId}: ${ctx.callbackQuery.data}`);
|
|
36395
37398
|
if (!this.isAuthorized(userId) && !this.isAuthorized(chatId)) {
|
|
36396
|
-
ctx.answerCallbackQuery("Unauthorized")
|
|
36397
|
-
});
|
|
37399
|
+
this.throttle.tryBestEffort(chatId, "answerCallbackQuery:unauth", () => ctx.answerCallbackQuery("Unauthorized"));
|
|
36398
37400
|
return;
|
|
36399
37401
|
}
|
|
36400
37402
|
const data = ctx.callbackQuery.data;
|
|
36401
37403
|
const messageId = ctx.callbackQuery.message?.message_id?.toString();
|
|
36402
37404
|
const threadId = ctx.callbackQuery.message?.message_thread_id;
|
|
36403
|
-
|
|
36404
|
-
});
|
|
37405
|
+
this.throttle.tryBestEffort(chatId, "answerCallbackQuery", () => ctx.answerCallbackQuery());
|
|
36405
37406
|
(async () => {
|
|
36406
37407
|
let ch = this;
|
|
36407
37408
|
if (threadId) {
|
|
@@ -36450,7 +37451,7 @@ var init_telegram2 = __esm({
|
|
|
36450
37451
|
this.keepaliveInterval = setInterval(async () => {
|
|
36451
37452
|
if (!this.pollingExpected) return;
|
|
36452
37453
|
try {
|
|
36453
|
-
await this.bot.api.getMe();
|
|
37454
|
+
await this.throttle.tryBestEffort(this.primaryChatId, "getMe:keepalive", () => this.bot.api.getMe());
|
|
36454
37455
|
this.lastPollingCheckAt = Date.now();
|
|
36455
37456
|
} catch (err) {
|
|
36456
37457
|
error("[telegram] Keepalive ping failed:", err);
|
|
@@ -36590,7 +37591,7 @@ var init_telegram2 = __esm({
|
|
|
36590
37591
|
);
|
|
36591
37592
|
}
|
|
36592
37593
|
async downloadFile(fileId) {
|
|
36593
|
-
const file = await this.bot.api.getFile(fileId);
|
|
37594
|
+
const file = await this.throttle.send(this.primaryChatId, "getFile", () => this.bot.api.getFile(fileId));
|
|
36594
37595
|
const fileUrl = `https://api.telegram.org/file/bot${process.env.TELEGRAM_BOT_TOKEN}/${file.file_path}`;
|
|
36595
37596
|
const response = await fetch(fileUrl);
|
|
36596
37597
|
return Buffer.from(await response.arrayBuffer());
|
|
@@ -36879,7 +37880,11 @@ var init_telegram2 = __esm({
|
|
|
36879
37880
|
}
|
|
36880
37881
|
}
|
|
36881
37882
|
}
|
|
36882
|
-
await
|
|
37883
|
+
await this.throttle.tryBestEffort(
|
|
37884
|
+
this.primaryChatId,
|
|
37885
|
+
"answerInlineQuery",
|
|
37886
|
+
() => ctx.answerInlineQuery(results.slice(0, 10), { cache_time: 30 })
|
|
37887
|
+
);
|
|
36883
37888
|
}
|
|
36884
37889
|
trackAgentMessage(messageId, chatId) {
|
|
36885
37890
|
if (this.agentMessageIds.size >= 1e4) {
|
|
@@ -37066,7 +38071,7 @@ var init_telegram2 = __esm({
|
|
|
37066
38071
|
});
|
|
37067
38072
|
|
|
37068
38073
|
// src/skills/bootstrap.ts
|
|
37069
|
-
import { existsSync as existsSync31, readFileSync as
|
|
38074
|
+
import { existsSync as existsSync31, readFileSync as readFileSync23, writeFileSync as writeFileSync10, copyFileSync as copyFileSync3, readdirSync as readdirSync15 } from "fs";
|
|
37070
38075
|
import { readdir as readdir6, copyFile } from "fs/promises";
|
|
37071
38076
|
import { join as join34, dirname as dirname6 } from "path";
|
|
37072
38077
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
@@ -37115,7 +38120,7 @@ function migrateSkillsToThreeTier() {
|
|
|
37115
38120
|
}
|
|
37116
38121
|
if (!skillPath) continue;
|
|
37117
38122
|
try {
|
|
37118
|
-
const raw =
|
|
38123
|
+
const raw = readFileSync23(skillPath, "utf-8");
|
|
37119
38124
|
const fmMatch = raw.match(/^---\s*\n([\s\S]*?)\n---/);
|
|
37120
38125
|
if (fmMatch) {
|
|
37121
38126
|
const fm = fmMatch[1];
|
|
@@ -37758,7 +38763,7 @@ var index_exports = {};
|
|
|
37758
38763
|
__export(index_exports, {
|
|
37759
38764
|
main: () => main
|
|
37760
38765
|
});
|
|
37761
|
-
import { mkdirSync as mkdirSync12, existsSync as existsSync33, renameSync as renameSync2, statSync as statSync9, readFileSync as
|
|
38766
|
+
import { mkdirSync as mkdirSync12, existsSync as existsSync33, renameSync as renameSync2, statSync as statSync9, readFileSync as readFileSync25 } from "fs";
|
|
37762
38767
|
import { join as join36 } from "path";
|
|
37763
38768
|
import dotenv from "dotenv";
|
|
37764
38769
|
function migrateLayout() {
|
|
@@ -37801,7 +38806,7 @@ async function main() {
|
|
|
37801
38806
|
let version = "unknown";
|
|
37802
38807
|
try {
|
|
37803
38808
|
const pkgPath = new URL("../package.json", import.meta.url);
|
|
37804
|
-
version = JSON.parse(
|
|
38809
|
+
version = JSON.parse(readFileSync25(pkgPath, "utf-8")).version;
|
|
37805
38810
|
} catch {
|
|
37806
38811
|
}
|
|
37807
38812
|
log(`[cc-claw] Starting v${version}`);
|
|
@@ -38029,6 +39034,11 @@ ${lines.join("\n")}`;
|
|
|
38029
39034
|
log("[cc-claw] Ready!");
|
|
38030
39035
|
const shutdown = async (signal) => {
|
|
38031
39036
|
log(`[cc-claw] Received ${signal}, shutting down...`);
|
|
39037
|
+
const forceExit = setTimeout(() => {
|
|
39038
|
+
error("[cc-claw] Shutdown timed out after 30s \u2014 forcing exit.");
|
|
39039
|
+
process.exit(1);
|
|
39040
|
+
}, 3e4);
|
|
39041
|
+
forceExit.unref();
|
|
38032
39042
|
try {
|
|
38033
39043
|
const { stopAllActiveAgents: stopAllActiveAgents2, stopStaleChatSweep: stopStaleChatSweep2 } = await Promise.resolve().then(() => (init_agent(), agent_exports));
|
|
38034
39044
|
stopAllActiveAgents2();
|
|
@@ -38540,13 +39550,13 @@ var init_daemon = __esm({
|
|
|
38540
39550
|
});
|
|
38541
39551
|
|
|
38542
39552
|
// src/cli/resolve-chat.ts
|
|
38543
|
-
import { readFileSync as
|
|
39553
|
+
import { readFileSync as readFileSync27 } from "fs";
|
|
38544
39554
|
function resolveChatId2(globalOpts) {
|
|
38545
39555
|
const explicit = globalOpts.chat;
|
|
38546
39556
|
if (explicit) return explicit;
|
|
38547
39557
|
if (_cachedDefault) return _cachedDefault;
|
|
38548
39558
|
try {
|
|
38549
|
-
const content =
|
|
39559
|
+
const content = readFileSync27(ENV_PATH, "utf-8");
|
|
38550
39560
|
const match = content.match(/^ALLOWED_CHAT_ID=(.+)$/m);
|
|
38551
39561
|
if (match) {
|
|
38552
39562
|
_cachedDefault = match[1].split(",")[0].trim();
|
|
@@ -39008,7 +40018,7 @@ var logs_exports = {};
|
|
|
39008
40018
|
__export(logs_exports, {
|
|
39009
40019
|
logsCommand: () => logsCommand
|
|
39010
40020
|
});
|
|
39011
|
-
import { existsSync as existsSync37, readFileSync as
|
|
40021
|
+
import { existsSync as existsSync37, readFileSync as readFileSync29, watchFile as watchFile2, unwatchFile as unwatchFile2 } from "fs";
|
|
39012
40022
|
async function logsCommand(opts) {
|
|
39013
40023
|
const logFile = opts.error ? ERROR_LOG_PATH : LOG_PATH;
|
|
39014
40024
|
if (!existsSync37(logFile)) {
|
|
@@ -39016,7 +40026,7 @@ async function logsCommand(opts) {
|
|
|
39016
40026
|
process.exit(1);
|
|
39017
40027
|
}
|
|
39018
40028
|
const maxLines = parseInt(opts.lines ?? "100", 10);
|
|
39019
|
-
const content =
|
|
40029
|
+
const content = readFileSync29(logFile, "utf-8");
|
|
39020
40030
|
const allLines = content.split("\n");
|
|
39021
40031
|
const tailLines = allLines.slice(-maxLines);
|
|
39022
40032
|
console.log(muted(` \u2500\u2500 ${logFile} (last ${tailLines.length} lines) \u2500\u2500`));
|
|
@@ -39027,7 +40037,7 @@ async function logsCommand(opts) {
|
|
|
39027
40037
|
let lastLength = content.length;
|
|
39028
40038
|
watchFile2(logFile, { interval: 500 }, () => {
|
|
39029
40039
|
try {
|
|
39030
|
-
const newContent =
|
|
40040
|
+
const newContent = readFileSync29(logFile, "utf-8");
|
|
39031
40041
|
if (newContent.length > lastLength) {
|
|
39032
40042
|
const newPart = newContent.slice(lastLength);
|
|
39033
40043
|
process.stdout.write(newPart);
|
|
@@ -39059,7 +40069,7 @@ __export(session_logs_exports, {
|
|
|
39059
40069
|
sessionLogsList: () => sessionLogsList,
|
|
39060
40070
|
sessionLogsTail: () => sessionLogsTail
|
|
39061
40071
|
});
|
|
39062
|
-
import { readFileSync as
|
|
40072
|
+
import { readFileSync as readFileSync30, watchFile as watchFile3, unwatchFile as unwatchFile3 } from "fs";
|
|
39063
40073
|
async function sessionLogsList(opts) {
|
|
39064
40074
|
const logs = listSessionLogs();
|
|
39065
40075
|
if (logs.length === 0) {
|
|
@@ -39117,12 +40127,12 @@ async function sessionLogsTail(opts) {
|
|
|
39117
40127
|
console.log(muted("\n Following... (Ctrl+C to stop)\n"));
|
|
39118
40128
|
let lastLength = 0;
|
|
39119
40129
|
try {
|
|
39120
|
-
lastLength =
|
|
40130
|
+
lastLength = readFileSync30(targetPath, "utf-8").length;
|
|
39121
40131
|
} catch {
|
|
39122
40132
|
}
|
|
39123
40133
|
watchFile3(targetPath, { interval: 500 }, () => {
|
|
39124
40134
|
try {
|
|
39125
|
-
const content =
|
|
40135
|
+
const content = readFileSync30(targetPath, "utf-8");
|
|
39126
40136
|
if (content.length > lastLength) {
|
|
39127
40137
|
process.stdout.write(content.slice(lastLength));
|
|
39128
40138
|
lastLength = content.length;
|
|
@@ -39166,7 +40176,7 @@ __export(gemini_exports, {
|
|
|
39166
40176
|
geminiReorder: () => geminiReorder,
|
|
39167
40177
|
geminiRotation: () => geminiRotation
|
|
39168
40178
|
});
|
|
39169
|
-
import { existsSync as existsSync39, mkdirSync as mkdirSync14, writeFileSync as writeFileSync13, readFileSync as
|
|
40179
|
+
import { existsSync as existsSync39, mkdirSync as mkdirSync14, writeFileSync as writeFileSync13, readFileSync as readFileSync31, chmodSync } from "fs";
|
|
39170
40180
|
import { join as join38 } from "path";
|
|
39171
40181
|
import { createInterface as createInterface8 } from "readline";
|
|
39172
40182
|
function requireDb() {
|
|
@@ -39197,7 +40207,7 @@ function resolveOAuthEmail(configHome) {
|
|
|
39197
40207
|
try {
|
|
39198
40208
|
const accountsPath = join38(configHome, ".gemini", "google_accounts.json");
|
|
39199
40209
|
if (!existsSync39(accountsPath)) return null;
|
|
39200
|
-
const accounts = JSON.parse(
|
|
40210
|
+
const accounts = JSON.parse(readFileSync31(accountsPath, "utf-8"));
|
|
39201
40211
|
return accounts.active || null;
|
|
39202
40212
|
} catch {
|
|
39203
40213
|
return null;
|
|
@@ -39460,7 +40470,7 @@ async function geminiRelogin(globalOpts, idOrLabel) {
|
|
|
39460
40470
|
setGeminiSlotEnabled2(slotId, true);
|
|
39461
40471
|
let accountEmail = slot.label;
|
|
39462
40472
|
try {
|
|
39463
|
-
const accounts = JSON.parse(
|
|
40473
|
+
const accounts = JSON.parse(readFileSync31(join38(slot.configHome, ".gemini", "google_accounts.json"), "utf-8"));
|
|
39464
40474
|
if (accounts.active) accountEmail = accounts.active;
|
|
39465
40475
|
} catch {
|
|
39466
40476
|
}
|
|
@@ -39519,7 +40529,7 @@ __export(backend_cmd_factory_exports, {
|
|
|
39519
40529
|
makeReorder: () => makeReorder,
|
|
39520
40530
|
registerBackendSlotCommands: () => registerBackendSlotCommands
|
|
39521
40531
|
});
|
|
39522
|
-
import { existsSync as existsSync40, mkdirSync as mkdirSync15, readFileSync as
|
|
40532
|
+
import { existsSync as existsSync40, mkdirSync as mkdirSync15, readFileSync as readFileSync32 } from "fs";
|
|
39523
40533
|
import { join as join39 } from "path";
|
|
39524
40534
|
import { createInterface as createInterface9 } from "readline";
|
|
39525
40535
|
function requireDb2() {
|
|
@@ -39873,7 +40883,7 @@ var init_backend_cmd_factory = __esm({
|
|
|
39873
40883
|
const claudeJsonNested = join39(slotDir, ".claude", ".claude.json");
|
|
39874
40884
|
if (existsSync40(claudeJson)) {
|
|
39875
40885
|
try {
|
|
39876
|
-
const data = JSON.parse(
|
|
40886
|
+
const data = JSON.parse(readFileSync32(claudeJson, "utf-8"));
|
|
39877
40887
|
return Boolean(data.oauthAccount);
|
|
39878
40888
|
} catch {
|
|
39879
40889
|
return false;
|
|
@@ -39881,7 +40891,7 @@ var init_backend_cmd_factory = __esm({
|
|
|
39881
40891
|
}
|
|
39882
40892
|
if (existsSync40(claudeJsonNested)) {
|
|
39883
40893
|
try {
|
|
39884
|
-
const data = JSON.parse(
|
|
40894
|
+
const data = JSON.parse(readFileSync32(claudeJsonNested, "utf-8"));
|
|
39885
40895
|
return Boolean(data.oauthAccount);
|
|
39886
40896
|
} catch {
|
|
39887
40897
|
return false;
|
|
@@ -39904,7 +40914,7 @@ var init_backend_cmd_factory = __esm({
|
|
|
39904
40914
|
try {
|
|
39905
40915
|
const claudeJson = join39(slotDir, ".claude.json");
|
|
39906
40916
|
if (existsSync40(claudeJson)) {
|
|
39907
|
-
const data = JSON.parse(
|
|
40917
|
+
const data = JSON.parse(readFileSync32(claudeJson, "utf-8"));
|
|
39908
40918
|
if (data.oauthAccount?.emailAddress) return data.oauthAccount.emailAddress;
|
|
39909
40919
|
}
|
|
39910
40920
|
} catch {
|
|
@@ -39923,7 +40933,7 @@ var init_backend_cmd_factory = __esm({
|
|
|
39923
40933
|
},
|
|
39924
40934
|
extractLabel: (slotDir) => {
|
|
39925
40935
|
try {
|
|
39926
|
-
const authData = JSON.parse(
|
|
40936
|
+
const authData = JSON.parse(readFileSync32(join39(slotDir, "auth.json"), "utf-8"));
|
|
39927
40937
|
if (authData.email) return authData.email;
|
|
39928
40938
|
if (authData.account_name) return authData.account_name;
|
|
39929
40939
|
if (authData.user?.email) return authData.user.email;
|
|
@@ -41290,7 +42300,7 @@ __export(config_exports2, {
|
|
|
41290
42300
|
configList: () => configList,
|
|
41291
42301
|
configSet: () => configSet
|
|
41292
42302
|
});
|
|
41293
|
-
import { existsSync as existsSync49, readFileSync as
|
|
42303
|
+
import { existsSync as existsSync49, readFileSync as readFileSync33 } from "fs";
|
|
41294
42304
|
async function configList(globalOpts) {
|
|
41295
42305
|
if (!existsSync49(DB_PATH)) {
|
|
41296
42306
|
outputError("DB_NOT_FOUND", "Database not found.");
|
|
@@ -41376,7 +42386,7 @@ async function configEnv(_globalOpts) {
|
|
|
41376
42386
|
outputError("ENV_NOT_FOUND", `No .env file at ${ENV_PATH}. Run cc-claw setup.`);
|
|
41377
42387
|
process.exit(1);
|
|
41378
42388
|
}
|
|
41379
|
-
const content =
|
|
42389
|
+
const content = readFileSync33(ENV_PATH, "utf-8");
|
|
41380
42390
|
const entries = {};
|
|
41381
42391
|
const secretPatterns = /TOKEN|KEY|SECRET|PASSWORD|CREDENTIALS/i;
|
|
41382
42392
|
for (const line of content.split("\n")) {
|
|
@@ -42372,11 +43382,11 @@ __export(chat_exports2, {
|
|
|
42372
43382
|
chatSend: () => chatSend
|
|
42373
43383
|
});
|
|
42374
43384
|
import { request as httpRequest2 } from "http";
|
|
42375
|
-
import { readFileSync as
|
|
43385
|
+
import { readFileSync as readFileSync34, existsSync as existsSync59 } from "fs";
|
|
42376
43386
|
function getToken2() {
|
|
42377
43387
|
if (process.env.CC_CLAW_API_TOKEN) return process.env.CC_CLAW_API_TOKEN;
|
|
42378
43388
|
try {
|
|
42379
|
-
if (existsSync59(TOKEN_PATH)) return
|
|
43389
|
+
if (existsSync59(TOKEN_PATH)) return readFileSync34(TOKEN_PATH, "utf-8").trim();
|
|
42380
43390
|
} catch {
|
|
42381
43391
|
}
|
|
42382
43392
|
return null;
|
|
@@ -43533,7 +44543,7 @@ var init_optimize3 = __esm({
|
|
|
43533
44543
|
|
|
43534
44544
|
// src/setup.ts
|
|
43535
44545
|
var setup_exports = {};
|
|
43536
|
-
import { existsSync as existsSync61, writeFileSync as writeFileSync15, readFileSync as
|
|
44546
|
+
import { existsSync as existsSync61, writeFileSync as writeFileSync15, readFileSync as readFileSync35, copyFileSync as copyFileSync5, mkdirSync as mkdirSync18, statSync as statSync12 } from "fs";
|
|
43537
44547
|
import { execFileSync as execFileSync6 } from "child_process";
|
|
43538
44548
|
import { createInterface as createInterface11 } from "readline";
|
|
43539
44549
|
import { join as join41 } from "path";
|
|
@@ -43625,7 +44635,7 @@ async function setup() {
|
|
|
43625
44635
|
if (envSource) {
|
|
43626
44636
|
console.log(yellow(` Found existing config at ${envSource} \u2014 your values will be preserved`));
|
|
43627
44637
|
console.log(yellow(" unless you enter new ones. Just press Enter to keep existing values.\n"));
|
|
43628
|
-
const existing =
|
|
44638
|
+
const existing = readFileSync35(envSource, "utf-8");
|
|
43629
44639
|
for (const line of existing.split("\n")) {
|
|
43630
44640
|
const match = line.match(/^([^#=]+)=(.*)$/);
|
|
43631
44641
|
if (match) env[match[1].trim()] = match[2].trim();
|