cc-claw 0.11.3 → 0.12.1
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 +341 -238
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -72,7 +72,7 @@ var VERSION;
|
|
|
72
72
|
var init_version = __esm({
|
|
73
73
|
"src/version.ts"() {
|
|
74
74
|
"use strict";
|
|
75
|
-
VERSION = true ? "0.
|
|
75
|
+
VERSION = true ? "0.12.1" : (() => {
|
|
76
76
|
try {
|
|
77
77
|
return JSON.parse(readFileSync(join2(process.cwd(), "package.json"), "utf-8")).version ?? "unknown";
|
|
78
78
|
} catch {
|
|
@@ -682,18 +682,15 @@ __export(store_exports4, {
|
|
|
682
682
|
cleanupReflectionData: () => cleanupReflectionData,
|
|
683
683
|
getAppliedInsightCountToday: () => getAppliedInsightCountToday,
|
|
684
684
|
getAppliedInsights: () => getAppliedInsights,
|
|
685
|
-
getConflictingInsights: () => getConflictingInsights,
|
|
686
685
|
getGrowthMetrics: () => getGrowthMetrics,
|
|
687
686
|
getInsightById: () => getInsightById,
|
|
688
687
|
getLastAnalysisTime: () => getLastAnalysisTime,
|
|
689
688
|
getPendingInsightCount: () => getPendingInsightCount,
|
|
690
689
|
getPendingInsights: () => getPendingInsights,
|
|
691
|
-
getRecommendations: () => getRecommendations,
|
|
692
690
|
getReflectionModelConfig: () => getReflectionModelConfig,
|
|
693
691
|
getReflectionStatus: () => getReflectionStatus,
|
|
694
692
|
getRejectedInsights: () => getRejectedInsights,
|
|
695
693
|
getSignalCountForChat: () => getSignalCountForChat,
|
|
696
|
-
getSignalsForPeriod: () => getSignalsForPeriod,
|
|
697
694
|
getUnprocessedSignalCount: () => getUnprocessedSignalCount,
|
|
698
695
|
getUnprocessedSignals: () => getUnprocessedSignals,
|
|
699
696
|
initReflectionTables: () => initReflectionTables,
|
|
@@ -839,13 +836,6 @@ function getSignalCountForChat(db3, chatId, minutesBack) {
|
|
|
839
836
|
`).get(chatId, minutesBack);
|
|
840
837
|
return row.count;
|
|
841
838
|
}
|
|
842
|
-
function getSignalsForPeriod(db3, chatId, period) {
|
|
843
|
-
return db3.prepare(`
|
|
844
|
-
SELECT * FROM feedback_signals
|
|
845
|
-
WHERE chatId = ? AND date(created_at) = ?
|
|
846
|
-
ORDER BY created_at ASC
|
|
847
|
-
`).all(chatId, period);
|
|
848
|
-
}
|
|
849
839
|
function getUnprocessedSignalCount(db3, chatId) {
|
|
850
840
|
const row = db3.prepare(`
|
|
851
841
|
SELECT COUNT(*) as count FROM feedback_signals
|
|
@@ -916,14 +906,6 @@ function getRejectedInsights(db3, chatId, limit = 20) {
|
|
|
916
906
|
LIMIT ?
|
|
917
907
|
`).all(chatId, limit);
|
|
918
908
|
}
|
|
919
|
-
function getRecommendations(db3, chatId, limit = 20) {
|
|
920
|
-
return db3.prepare(`
|
|
921
|
-
SELECT * FROM insights
|
|
922
|
-
WHERE chatId = ? AND status = 'recommendation'
|
|
923
|
-
ORDER BY created_at DESC
|
|
924
|
-
LIMIT ?
|
|
925
|
-
`).all(chatId, limit);
|
|
926
|
-
}
|
|
927
909
|
function updateInsightStatus(db3, id, status) {
|
|
928
910
|
if (status === "applied") {
|
|
929
911
|
db3.prepare(`
|
|
@@ -944,13 +926,6 @@ function updateInsightRollback(db3, id, rollbackData) {
|
|
|
944
926
|
function updateInsightEffectiveness(db3, id, score) {
|
|
945
927
|
db3.prepare("UPDATE insights SET effectiveness = ? WHERE id = ?").run(score, id);
|
|
946
928
|
}
|
|
947
|
-
function getConflictingInsights(db3, chatId, targetFile) {
|
|
948
|
-
return db3.prepare(`
|
|
949
|
-
SELECT * FROM insights
|
|
950
|
-
WHERE chatId = ? AND targetFile = ? AND status = 'applied'
|
|
951
|
-
ORDER BY appliedAt DESC
|
|
952
|
-
`).all(chatId, targetFile);
|
|
953
|
-
}
|
|
954
929
|
function getPendingInsightCount(db3, chatId) {
|
|
955
930
|
const row = db3.prepare(`
|
|
956
931
|
SELECT COUNT(*) as count FROM insights WHERE chatId = ? AND status = 'pending'
|
|
@@ -960,7 +935,7 @@ function getPendingInsightCount(db3, chatId) {
|
|
|
960
935
|
function getAppliedInsightCountToday(db3, chatId) {
|
|
961
936
|
const row = db3.prepare(`
|
|
962
937
|
SELECT COUNT(*) as count FROM insights
|
|
963
|
-
WHERE chatId = ? AND status = 'applied' AND date(
|
|
938
|
+
WHERE chatId = ? AND status = 'applied' AND appliedAt >= date('now') AND appliedAt < date('now', '+1 day')
|
|
964
939
|
`).get(chatId);
|
|
965
940
|
return row.count;
|
|
966
941
|
}
|
|
@@ -1558,6 +1533,7 @@ function initDatabase() {
|
|
|
1558
1533
|
target TEXT,
|
|
1559
1534
|
delivery_mode TEXT NOT NULL DEFAULT 'announce',
|
|
1560
1535
|
timezone TEXT NOT NULL DEFAULT 'UTC',
|
|
1536
|
+
job_type TEXT DEFAULT 'normal',
|
|
1561
1537
|
enabled INTEGER NOT NULL DEFAULT 1,
|
|
1562
1538
|
active INTEGER NOT NULL DEFAULT 1,
|
|
1563
1539
|
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
@@ -1594,6 +1570,7 @@ function initDatabase() {
|
|
|
1594
1570
|
target TEXT,
|
|
1595
1571
|
delivery_mode TEXT NOT NULL DEFAULT 'announce',
|
|
1596
1572
|
timezone TEXT NOT NULL DEFAULT 'UTC',
|
|
1573
|
+
job_type TEXT DEFAULT 'normal',
|
|
1597
1574
|
enabled INTEGER NOT NULL DEFAULT 1,
|
|
1598
1575
|
active INTEGER NOT NULL DEFAULT 1,
|
|
1599
1576
|
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
@@ -1681,6 +1658,14 @@ function initDatabase() {
|
|
|
1681
1658
|
db.exec("ALTER TABLE jobs ADD COLUMN title TEXT");
|
|
1682
1659
|
} catch {
|
|
1683
1660
|
}
|
|
1661
|
+
try {
|
|
1662
|
+
db.exec("ALTER TABLE jobs ADD COLUMN job_type TEXT DEFAULT 'normal'");
|
|
1663
|
+
} catch {
|
|
1664
|
+
}
|
|
1665
|
+
try {
|
|
1666
|
+
db.exec("UPDATE jobs SET job_type = 'reflection' WHERE description LIKE '%reflection analysis%' AND job_type = 'normal'");
|
|
1667
|
+
} catch {
|
|
1668
|
+
}
|
|
1684
1669
|
backfillJobTitles(db);
|
|
1685
1670
|
}
|
|
1686
1671
|
} catch {
|
|
@@ -2529,8 +2514,8 @@ function getBackendUsageInWindow(backend2, windowType) {
|
|
|
2529
2514
|
function insertJob(params) {
|
|
2530
2515
|
const result = db.prepare(`
|
|
2531
2516
|
INSERT INTO jobs (schedule_type, cron, at_time, every_ms, title, description, chat_id,
|
|
2532
|
-
backend, model, thinking, timeout, fallbacks, session_type, channel, target, delivery_mode, timezone)
|
|
2533
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
2517
|
+
backend, model, thinking, timeout, fallbacks, session_type, channel, target, delivery_mode, timezone, job_type)
|
|
2518
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
2534
2519
|
`).run(
|
|
2535
2520
|
params.scheduleType,
|
|
2536
2521
|
params.cron ?? null,
|
|
@@ -2548,7 +2533,8 @@ function insertJob(params) {
|
|
|
2548
2533
|
params.channel ?? null,
|
|
2549
2534
|
params.target ?? null,
|
|
2550
2535
|
params.deliveryMode ?? "announce",
|
|
2551
|
-
params.timezone ?? "UTC"
|
|
2536
|
+
params.timezone ?? "UTC",
|
|
2537
|
+
params.jobType ?? "normal"
|
|
2552
2538
|
);
|
|
2553
2539
|
return getJobById(Number(result.lastInsertRowid));
|
|
2554
2540
|
}
|
|
@@ -2629,7 +2615,8 @@ function updateJob(id, fields) {
|
|
|
2629
2615
|
channel: "channel",
|
|
2630
2616
|
target: "target",
|
|
2631
2617
|
deliveryMode: "delivery_mode",
|
|
2632
|
-
timezone: "timezone"
|
|
2618
|
+
timezone: "timezone",
|
|
2619
|
+
jobType: "job_type"
|
|
2633
2620
|
};
|
|
2634
2621
|
for (const [key, val] of Object.entries(fields)) {
|
|
2635
2622
|
const col = fieldMap[key];
|
|
@@ -3066,7 +3053,7 @@ var init_store5 = __esm({
|
|
|
3066
3053
|
SELECT id, schedule_type as scheduleType, cron, at_time as atTime, every_ms as everyMs,
|
|
3067
3054
|
title, description, chat_id as chatId, backend, model, thinking, timeout, fallbacks,
|
|
3068
3055
|
session_type as sessionType, channel, target, delivery_mode as deliveryMode,
|
|
3069
|
-
timezone, enabled, active, created_at as createdAt, last_run_at as lastRunAt,
|
|
3056
|
+
timezone, job_type as jobType, enabled, active, created_at as createdAt, last_run_at as lastRunAt,
|
|
3070
3057
|
next_run_at as nextRunAt, consecutive_failures as consecutiveFailures
|
|
3071
3058
|
FROM jobs
|
|
3072
3059
|
`;
|
|
@@ -4958,7 +4945,7 @@ ${transcript}`;
|
|
|
4958
4945
|
if (!resultText) resultText = accumulatedText;
|
|
4959
4946
|
if (!resultText) {
|
|
4960
4947
|
warn(`[summarize] ${adapter.id}:${model2} returned empty result for chat ${chatId}`);
|
|
4961
|
-
return false;
|
|
4948
|
+
return { success: false, rawText: "" };
|
|
4962
4949
|
}
|
|
4963
4950
|
const summaryMatch = resultText.match(/SUMMARY:\s*(.+?)(?=\nKEY_DETAILS:|\nTOPICS:|$)/s);
|
|
4964
4951
|
const keyDetailsMatch = resultText.match(/KEY_DETAILS:\s*(.+?)(?=\nTOPICS:|$)/s);
|
|
@@ -4970,16 +4957,44 @@ Key details: ${keyDetails}`;
|
|
|
4970
4957
|
const topics = topicsMatch?.[1]?.trim() ?? "";
|
|
4971
4958
|
saveSessionSummaryWithEmbedding(chatId, summary, topics, Math.floor(entries.length / 2));
|
|
4972
4959
|
log(`[summarize] Saved summary via ${adapter.id}:${model2} for chat ${chatId}`);
|
|
4973
|
-
return true;
|
|
4960
|
+
return { success: true, rawText: resultText };
|
|
4974
4961
|
} catch (err) {
|
|
4975
4962
|
warn(`[summarize] ${adapter.id}:${model2} failed for ${chatId}: ${errorMessage(err)}`);
|
|
4976
|
-
return false;
|
|
4963
|
+
return { success: false, rawText: "" };
|
|
4977
4964
|
}
|
|
4978
4965
|
}
|
|
4979
|
-
async function
|
|
4966
|
+
async function extractAndLogSignals(rawText, chatId, adapterId, model2) {
|
|
4967
|
+
try {
|
|
4968
|
+
const { getReflectionStatus: getReflectionStatus2, logSignal: logSignal2 } = await Promise.resolve().then(() => (init_store4(), store_exports4));
|
|
4969
|
+
const { isSyntheticChatId: isSyntheticChatId3 } = await Promise.resolve().then(() => (init_agent(), agent_exports));
|
|
4970
|
+
const db3 = getDb();
|
|
4971
|
+
if (isSyntheticChatId3(chatId) || getReflectionStatus2(db3, chatId) === "frozen") return;
|
|
4972
|
+
const signalMatches = rawText.matchAll(/^-\s*\[(\w+)\]\s*(.+)/gm);
|
|
4973
|
+
for (const match of signalMatches) {
|
|
4974
|
+
const signalType = match[1].toLowerCase();
|
|
4975
|
+
const trigger = match[2].trim();
|
|
4976
|
+
if (VALID_SIGNAL_TYPES.includes(signalType)) {
|
|
4977
|
+
logSignal2(db3, {
|
|
4978
|
+
chatId,
|
|
4979
|
+
signalType,
|
|
4980
|
+
trigger,
|
|
4981
|
+
context: null,
|
|
4982
|
+
source: "llm_summary",
|
|
4983
|
+
confidence: 0.7,
|
|
4984
|
+
backend: adapterId,
|
|
4985
|
+
model: model2,
|
|
4986
|
+
thinkingLevel: null
|
|
4987
|
+
});
|
|
4988
|
+
}
|
|
4989
|
+
}
|
|
4990
|
+
} catch (e) {
|
|
4991
|
+
warn(`[summarize] Signal extraction failed for ${chatId}: ${e}`);
|
|
4992
|
+
}
|
|
4993
|
+
}
|
|
4994
|
+
async function summarizeWithFallbackChain(chatId, targetBackendId, excludeBackend, clearLogAfter = true) {
|
|
4980
4995
|
const pairCount = getMessagePairCount(chatId);
|
|
4981
4996
|
if (pairCount < MIN_PAIRS) {
|
|
4982
|
-
clearLog(chatId);
|
|
4997
|
+
if (clearLogAfter) clearLog(chatId);
|
|
4983
4998
|
return false;
|
|
4984
4999
|
}
|
|
4985
5000
|
const entries = getLog(chatId);
|
|
@@ -4996,8 +5011,10 @@ async function summarizeWithFallbackChain(chatId, targetBackendId, excludeBacken
|
|
|
4996
5011
|
const model2 = config2.model ?? adapter.summarizerModel;
|
|
4997
5012
|
const key = `${adapter.id}:${model2}`;
|
|
4998
5013
|
tried.add(key);
|
|
4999
|
-
|
|
5000
|
-
|
|
5014
|
+
const result = await attemptSummarize(chatId, adapter, model2, entries);
|
|
5015
|
+
if (result.success) {
|
|
5016
|
+
await extractAndLogSignals(result.rawText, chatId, adapter.id, model2);
|
|
5017
|
+
if (clearLogAfter) clearLog(chatId);
|
|
5001
5018
|
return true;
|
|
5002
5019
|
}
|
|
5003
5020
|
}
|
|
@@ -5010,8 +5027,10 @@ async function summarizeWithFallbackChain(chatId, targetBackendId, excludeBacken
|
|
|
5010
5027
|
const key = `${targetAdapter.id}:${model2}`;
|
|
5011
5028
|
if (!tried.has(key)) {
|
|
5012
5029
|
tried.add(key);
|
|
5013
|
-
|
|
5014
|
-
|
|
5030
|
+
const result = await attemptSummarize(chatId, targetAdapter, model2, entries);
|
|
5031
|
+
if (result.success) {
|
|
5032
|
+
await extractAndLogSignals(result.rawText, chatId, targetAdapter.id, model2);
|
|
5033
|
+
if (clearLogAfter) clearLog(chatId);
|
|
5015
5034
|
return true;
|
|
5016
5035
|
}
|
|
5017
5036
|
}
|
|
@@ -5023,8 +5042,10 @@ async function summarizeWithFallbackChain(chatId, targetBackendId, excludeBacken
|
|
|
5023
5042
|
const key = `${adapter.id}:${model2}`;
|
|
5024
5043
|
if (!tried.has(key)) {
|
|
5025
5044
|
tried.add(key);
|
|
5026
|
-
|
|
5027
|
-
|
|
5045
|
+
const result = await attemptSummarize(chatId, adapter, model2, entries);
|
|
5046
|
+
if (result.success) {
|
|
5047
|
+
await extractAndLogSignals(result.rawText, chatId, adapter.id, model2);
|
|
5048
|
+
if (clearLogAfter) clearLog(chatId);
|
|
5028
5049
|
return true;
|
|
5029
5050
|
}
|
|
5030
5051
|
}
|
|
@@ -5032,8 +5053,8 @@ async function summarizeWithFallbackChain(chatId, targetBackendId, excludeBacken
|
|
|
5032
5053
|
warn(`[summarize] All fallback attempts failed for chat ${chatId} \u2014 raw log preserved`);
|
|
5033
5054
|
return false;
|
|
5034
5055
|
}
|
|
5035
|
-
async function summarizeSession(chatId) {
|
|
5036
|
-
return summarizeWithFallbackChain(chatId);
|
|
5056
|
+
async function summarizeSession(chatId, clearLogAfter = true) {
|
|
5057
|
+
return summarizeWithFallbackChain(chatId, void 0, void 0, clearLogAfter);
|
|
5037
5058
|
}
|
|
5038
5059
|
async function summarizeAllPending() {
|
|
5039
5060
|
const allIds = getLoggedChatIds();
|
|
@@ -5051,7 +5072,7 @@ async function summarizeAllPending() {
|
|
|
5051
5072
|
await summarizeSession(chatId);
|
|
5052
5073
|
}
|
|
5053
5074
|
}
|
|
5054
|
-
var MIN_PAIRS, USER_MSG_LIMIT, AGENT_MSG_LIMIT, TRANSCRIPT_CAP, SUMMARIZE_TIMEOUT_MS, SUMMARIZE_PROMPT, CONTEXT_BRIDGE_PROMPT;
|
|
5075
|
+
var MIN_PAIRS, USER_MSG_LIMIT, AGENT_MSG_LIMIT, TRANSCRIPT_CAP, SUMMARIZE_TIMEOUT_MS, SUMMARIZE_PROMPT, CONTEXT_BRIDGE_PROMPT, VALID_SIGNAL_TYPES;
|
|
5055
5076
|
var init_summarize = __esm({
|
|
5056
5077
|
"src/memory/summarize.ts"() {
|
|
5057
5078
|
"use strict";
|
|
@@ -5086,7 +5107,15 @@ SUMMARY: <detailed summary>
|
|
|
5086
5107
|
KEY_DETAILS:
|
|
5087
5108
|
- <detail 1>
|
|
5088
5109
|
- <detail 2>
|
|
5089
|
-
TOPICS: <specific-keyword1, specific-keyword2,
|
|
5110
|
+
TOPICS: <specific-keyword1, specific-keyword2, ...>
|
|
5111
|
+
|
|
5112
|
+
SIGNALS (0-5 items, only if the conversation contains user feedback about the assistant's performance):
|
|
5113
|
+
- [correction] brief description of what the user corrected
|
|
5114
|
+
- [preference] brief description of a preference the user expressed
|
|
5115
|
+
- [frustration] brief description of user frustration or dissatisfaction
|
|
5116
|
+
- [praise] brief description of user satisfaction or approval
|
|
5117
|
+
- [error_pattern] brief description of a recurring error the user pointed out
|
|
5118
|
+
If no feedback signals are present, omit this section entirely.`;
|
|
5090
5119
|
CONTEXT_BRIDGE_PROMPT = `You are handing off an active work session to a new AI assistant. Write a concise handoff brief (3-8 sentences) covering:
|
|
5091
5120
|
- What the user is actively working on RIGHT NOW
|
|
5092
5121
|
- Exact current state: what's done, what's in progress, what's blocked
|
|
@@ -5095,6 +5124,7 @@ TOPICS: <specific-keyword1, specific-keyword2, ...>`;
|
|
|
5095
5124
|
- Any critical constraints or preferences expressed by the user
|
|
5096
5125
|
|
|
5097
5126
|
Be specific. The new assistant has no prior context. Cover any domain \u2014 personal, research, scheduling, content, technical \u2014 whatever applies to this session.`;
|
|
5127
|
+
VALID_SIGNAL_TYPES = ["correction", "preference", "praise", "frustration", "error_pattern"];
|
|
5098
5128
|
}
|
|
5099
5129
|
});
|
|
5100
5130
|
|
|
@@ -6446,9 +6476,9 @@ function aggregateDailyMetrics(db3, chatId, date) {
|
|
|
6446
6476
|
const signalCounts = db3.prepare(`
|
|
6447
6477
|
SELECT signalType, COUNT(*) as count
|
|
6448
6478
|
FROM feedback_signals
|
|
6449
|
-
WHERE chatId = ? AND date(
|
|
6479
|
+
WHERE chatId = ? AND created_at >= ? AND created_at < date(?, '+1 day')
|
|
6450
6480
|
GROUP BY signalType
|
|
6451
|
-
`).all(chatId, period);
|
|
6481
|
+
`).all(chatId, period, period);
|
|
6452
6482
|
let corrections = 0;
|
|
6453
6483
|
let praises = 0;
|
|
6454
6484
|
let errors = 0;
|
|
@@ -6467,8 +6497,8 @@ function aggregateDailyMetrics(db3, chatId, date) {
|
|
|
6467
6497
|
}
|
|
6468
6498
|
const insightsRow = db3.prepare(`
|
|
6469
6499
|
SELECT COUNT(*) as count FROM insights
|
|
6470
|
-
WHERE chatId = ? AND date(
|
|
6471
|
-
`).get(chatId, period);
|
|
6500
|
+
WHERE chatId = ? AND appliedAt >= ? AND appliedAt < date(?, '+1 day')
|
|
6501
|
+
`).get(chatId, period, period);
|
|
6472
6502
|
upsertGrowthMetric(db3, chatId, period, {
|
|
6473
6503
|
corrections,
|
|
6474
6504
|
praises,
|
|
@@ -6774,7 +6804,7 @@ function resolveReflectionAdapter(chatId) {
|
|
|
6774
6804
|
if (config2.mode === "pinned" && config2.backend) {
|
|
6775
6805
|
try {
|
|
6776
6806
|
const adapter = getAdapter(config2.backend);
|
|
6777
|
-
const model2 = config2.model ?? adapter.
|
|
6807
|
+
const model2 = config2.model ?? adapter.defaultModel;
|
|
6778
6808
|
return { adapter, model: model2 };
|
|
6779
6809
|
} catch {
|
|
6780
6810
|
return null;
|
|
@@ -6795,11 +6825,11 @@ function resolveReflectionAdapter(chatId) {
|
|
|
6795
6825
|
}
|
|
6796
6826
|
try {
|
|
6797
6827
|
const adapter = getAdapterForChat(chatId);
|
|
6798
|
-
return { adapter, model: adapter.
|
|
6828
|
+
return { adapter, model: adapter.defaultModel };
|
|
6799
6829
|
} catch {
|
|
6800
6830
|
try {
|
|
6801
6831
|
const adapter = getAdapter("claude");
|
|
6802
|
-
return { adapter, model: adapter.
|
|
6832
|
+
return { adapter, model: adapter.defaultModel };
|
|
6803
6833
|
} catch {
|
|
6804
6834
|
return null;
|
|
6805
6835
|
}
|
|
@@ -6816,7 +6846,7 @@ function getTodayConversations(chatId) {
|
|
|
6816
6846
|
const db3 = getDb();
|
|
6817
6847
|
const rows = db3.prepare(`
|
|
6818
6848
|
SELECT role, content FROM message_log
|
|
6819
|
-
WHERE chat_id = ? AND date(
|
|
6849
|
+
WHERE chat_id = ? AND created_at >= date('now') AND created_at < date('now', '+1 day')
|
|
6820
6850
|
ORDER BY created_at ASC
|
|
6821
6851
|
LIMIT 200
|
|
6822
6852
|
`).all(chatId);
|
|
@@ -6835,7 +6865,7 @@ function getTodayConversations(chatId) {
|
|
|
6835
6865
|
}
|
|
6836
6866
|
return lines.join("\n\n");
|
|
6837
6867
|
}
|
|
6838
|
-
async function spawnAnalysis(adapter, model2, prompt) {
|
|
6868
|
+
async function spawnAnalysis(adapter, model2, prompt, timeoutMs = ANALYSIS_TIMEOUT_MS) {
|
|
6839
6869
|
const config2 = adapter.buildSpawnConfig({
|
|
6840
6870
|
prompt,
|
|
6841
6871
|
model: model2,
|
|
@@ -6858,7 +6888,7 @@ async function spawnAnalysis(adapter, model2, prompt) {
|
|
|
6858
6888
|
rl2.close();
|
|
6859
6889
|
proc.kill("SIGTERM");
|
|
6860
6890
|
setTimeout(() => proc.kill("SIGKILL"), 2e3);
|
|
6861
|
-
},
|
|
6891
|
+
}, timeoutMs);
|
|
6862
6892
|
rl2.on("line", (line) => {
|
|
6863
6893
|
if (!line.trim()) return;
|
|
6864
6894
|
let msg;
|
|
@@ -6950,7 +6980,7 @@ async function runAnalysis(chatId, opts = {}) {
|
|
|
6950
6980
|
log(`[reflection] Running analysis via ${adapter.id}:${model2} for chat ${chatId} (${signals.length} signals, force=${force})`);
|
|
6951
6981
|
let rawOutput;
|
|
6952
6982
|
try {
|
|
6953
|
-
rawOutput = await spawnAnalysis(adapter, model2, prompt);
|
|
6983
|
+
rawOutput = await spawnAnalysis(adapter, model2, prompt, opts.timeoutMs);
|
|
6954
6984
|
} catch (err) {
|
|
6955
6985
|
warn(`[reflection] Analysis spawn failed for ${chatId}: ${err instanceof Error ? err.message : String(err)}`);
|
|
6956
6986
|
return [];
|
|
@@ -7651,8 +7681,7 @@ function startDashboard() {
|
|
|
7651
7681
|
const body = JSON.parse(await readBody(req));
|
|
7652
7682
|
const { setBackend: setBackend2, clearSession: clearSession2, clearModel: clearModel2, clearThinkingLevel: clearThinkingLevel2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
|
|
7653
7683
|
const { summarizeSession: summarizeSession2 } = await Promise.resolve().then(() => (init_summarize(), summarize_exports));
|
|
7654
|
-
summarizeSession2(body.chatId)
|
|
7655
|
-
});
|
|
7684
|
+
await summarizeSession2(body.chatId);
|
|
7656
7685
|
clearSession2(body.chatId);
|
|
7657
7686
|
clearModel2(body.chatId);
|
|
7658
7687
|
clearThinkingLevel2(body.chatId);
|
|
@@ -8577,7 +8606,8 @@ __export(agent_exports, {
|
|
|
8577
8606
|
getInFlightMessage: () => getInFlightMessage,
|
|
8578
8607
|
isChatBusy: () => isChatBusy,
|
|
8579
8608
|
isSyntheticChatId: () => isSyntheticChatId,
|
|
8580
|
-
stopAgent: () => stopAgent
|
|
8609
|
+
stopAgent: () => stopAgent,
|
|
8610
|
+
stopAllActiveAgents: () => stopAllActiveAgents
|
|
8581
8611
|
});
|
|
8582
8612
|
import { spawn as spawn5 } from "child_process";
|
|
8583
8613
|
import { createInterface as createInterface4 } from "readline";
|
|
@@ -8605,13 +8635,22 @@ function withChatLock(chatId, fn) {
|
|
|
8605
8635
|
}
|
|
8606
8636
|
const next = prev.then(fn, fn);
|
|
8607
8637
|
chatLocks.set(chatId, next.then(() => {
|
|
8638
|
+
chatLocks.delete(chatId);
|
|
8608
8639
|
}, () => {
|
|
8640
|
+
chatLocks.delete(chatId);
|
|
8609
8641
|
}));
|
|
8610
8642
|
return next;
|
|
8611
8643
|
}
|
|
8612
8644
|
function isChatBusy(chatId) {
|
|
8613
8645
|
return activeChats.has(chatId);
|
|
8614
8646
|
}
|
|
8647
|
+
function stopAllActiveAgents() {
|
|
8648
|
+
for (const [, state] of activeChats) {
|
|
8649
|
+
if (state.process) {
|
|
8650
|
+
killProcessGroup(state.process);
|
|
8651
|
+
}
|
|
8652
|
+
}
|
|
8653
|
+
}
|
|
8615
8654
|
function stopAgent(chatId) {
|
|
8616
8655
|
const state = activeChats.get(chatId);
|
|
8617
8656
|
if (!state) return false;
|
|
@@ -8867,7 +8906,7 @@ async function askAgentImpl(chatId, userMessage, opts) {
|
|
|
8867
8906
|
const effectiveAgentMode = optsAgentMode ?? getAgentMode(settingsChat);
|
|
8868
8907
|
const sideQuestCtx = settingsSourceChatId ? { parentChatId: settingsSourceChatId, actualChatId: chatId } : void 0;
|
|
8869
8908
|
const fullPrompt = await assembleBootstrapPrompt(userMessage, tier, settingsChat, mode, responseStyle, effectiveAgentMode, sideQuestCtx);
|
|
8870
|
-
const existingSessionId = getSessionId(settingsChat);
|
|
8909
|
+
const existingSessionId = settingsSourceChatId ? null : getSessionId(settingsChat);
|
|
8871
8910
|
const allowedTools = getEnabledTools(settingsChat);
|
|
8872
8911
|
const mcpConfigPath = tier !== "slim" && effectiveAgentMode !== "native" && MCP_CONFIG_FLAG[adapter.id] ? getMcpConfigPath(chatId) : null;
|
|
8873
8912
|
const baseConfig = adapter.buildSpawnConfig({
|
|
@@ -8925,17 +8964,19 @@ async function askAgentImpl(chatId, userMessage, opts) {
|
|
|
8925
8964
|
}
|
|
8926
8965
|
}
|
|
8927
8966
|
} catch (spawnErr) {
|
|
8928
|
-
|
|
8929
|
-
|
|
8930
|
-
|
|
8931
|
-
|
|
8932
|
-
|
|
8933
|
-
|
|
8934
|
-
|
|
8935
|
-
|
|
8936
|
-
|
|
8967
|
+
if (!isSyntheticChatId(chatId)) {
|
|
8968
|
+
try {
|
|
8969
|
+
const errMsg = spawnErr instanceof Error ? spawnErr.message : String(spawnErr);
|
|
8970
|
+
const { logErrorSignal: logErrorSignal2 } = await Promise.resolve().then(() => (init_detect(), detect_exports));
|
|
8971
|
+
if (/timeout/i.test(errMsg)) {
|
|
8972
|
+
logErrorSignal2(chatId, "timeout", `Backend timed out`, { backendId: adapter.id, model: resolvedModel });
|
|
8973
|
+
} else if (/repetition.?loop/i.test(errMsg)) {
|
|
8974
|
+
logErrorSignal2(chatId, "repetition_loop", `Repetition loop detected`, { backendId: adapter.id, model: resolvedModel });
|
|
8975
|
+
} else {
|
|
8976
|
+
logErrorSignal2(chatId, "spawn_error", errMsg.slice(0, 300), { backendId: adapter.id, model: resolvedModel });
|
|
8977
|
+
}
|
|
8978
|
+
} catch {
|
|
8937
8979
|
}
|
|
8938
|
-
} catch {
|
|
8939
8980
|
}
|
|
8940
8981
|
throw spawnErr;
|
|
8941
8982
|
} finally {
|
|
@@ -9070,9 +9111,10 @@ ${responseText.slice(0, 500)}`);
|
|
|
9070
9111
|
return true;
|
|
9071
9112
|
}
|
|
9072
9113
|
try {
|
|
9114
|
+
const text = responseText.replace(/\s*\[REACT:[^\]]*\]\s*/g, " ").trim();
|
|
9073
9115
|
if (job.deliveryMode === "webhook") {
|
|
9074
|
-
await deliverWebhook(job,
|
|
9075
|
-
appendToLog(job.chatId, `[cron:#${job.id}] ${job.description}`,
|
|
9116
|
+
await deliverWebhook(job, text);
|
|
9117
|
+
appendToLog(job.chatId, `[cron:#${job.id}] ${job.description}`, text, job.backend ?? "claude", job.model, null);
|
|
9076
9118
|
return true;
|
|
9077
9119
|
}
|
|
9078
9120
|
const channelName = job.channel ?? "telegram";
|
|
@@ -9082,7 +9124,7 @@ ${responseText.slice(0, 500)}`);
|
|
|
9082
9124
|
return false;
|
|
9083
9125
|
}
|
|
9084
9126
|
const targetChatId = job.target ?? job.chatId;
|
|
9085
|
-
const cleanText = await processFileSends(targetChatId, channel,
|
|
9127
|
+
const cleanText = await processFileSends(targetChatId, channel, text);
|
|
9086
9128
|
if (!cleanText) return true;
|
|
9087
9129
|
if (channelName === "telegram") {
|
|
9088
9130
|
const parsed = parseTelegramTarget(targetChatId);
|
|
@@ -9313,6 +9355,143 @@ var init_health2 = __esm({
|
|
|
9313
9355
|
}
|
|
9314
9356
|
});
|
|
9315
9357
|
|
|
9358
|
+
// src/reflection/propose.ts
|
|
9359
|
+
var propose_exports = {};
|
|
9360
|
+
__export(propose_exports, {
|
|
9361
|
+
buildEvolveMenuKeyboard: () => buildEvolveMenuKeyboard,
|
|
9362
|
+
buildEvolveOnboardingKeyboard: () => buildEvolveOnboardingKeyboard,
|
|
9363
|
+
buildModelKeyboard: () => buildModelKeyboard,
|
|
9364
|
+
buildProposalKeyboard: () => buildProposalKeyboard,
|
|
9365
|
+
buildUndoKeyboard: () => buildUndoKeyboard,
|
|
9366
|
+
formatDiffCodeBlock: () => formatDiffCodeBlock,
|
|
9367
|
+
formatGrowthReport: () => formatGrowthReport,
|
|
9368
|
+
formatNightlySummary: () => formatNightlySummary,
|
|
9369
|
+
formatProposalCard: () => formatProposalCard
|
|
9370
|
+
});
|
|
9371
|
+
function pct(ratio) {
|
|
9372
|
+
return `${Math.round(ratio * 100)}%`;
|
|
9373
|
+
}
|
|
9374
|
+
function formatProposalCard(params) {
|
|
9375
|
+
const { category, insight, why, targetFile, confidence, proposedAction, proposedDiff } = params;
|
|
9376
|
+
const header2 = `[${category}] ${insight}`;
|
|
9377
|
+
const whyLine = why ? `Why: ${why}` : null;
|
|
9378
|
+
const confidencePct = pct(confidence);
|
|
9379
|
+
const showTarget = category !== "codebase" && targetFile && targetFile !== "codebase";
|
|
9380
|
+
const targetLine = showTarget ? `Target: ${targetFile} | Confidence: ${confidencePct}` : `Confidence: ${confidencePct}`;
|
|
9381
|
+
const lines = [header2, ""];
|
|
9382
|
+
if (whyLine) lines.push(whyLine);
|
|
9383
|
+
lines.push(targetLine);
|
|
9384
|
+
if (proposedDiff) {
|
|
9385
|
+
const actionLabel = proposedAction ? ` (${proposedAction})` : "";
|
|
9386
|
+
lines.push("", `Proposed change${actionLabel}:`, "```diff", proposedDiff, "```");
|
|
9387
|
+
}
|
|
9388
|
+
return lines.join("\n");
|
|
9389
|
+
}
|
|
9390
|
+
function formatNightlySummary(insights) {
|
|
9391
|
+
const count = insights.length;
|
|
9392
|
+
const header2 = `Nightly Reflection \u2014 ${count} proposal${count === 1 ? "" : "s"} ready`;
|
|
9393
|
+
const list = insights.map((ins, i) => `${i + 1}. [${ins.category}] ${ins.insight}`).join("\n");
|
|
9394
|
+
return `${header2}
|
|
9395
|
+
|
|
9396
|
+
${list}
|
|
9397
|
+
|
|
9398
|
+
Review with /evolve`;
|
|
9399
|
+
}
|
|
9400
|
+
function formatGrowthReport(metrics, modelPerformance) {
|
|
9401
|
+
const { correctionsBefore, correctionsAfter, praiseRatio, insightsApplied, pendingCount, topInsight } = metrics;
|
|
9402
|
+
const reduction = correctionsBefore > 0 ? Math.round((correctionsBefore - correctionsAfter) / correctionsBefore * 100) : 0;
|
|
9403
|
+
const lines = [];
|
|
9404
|
+
lines.push("Growth Report");
|
|
9405
|
+
lines.push("");
|
|
9406
|
+
lines.push(`Corrections: ${correctionsBefore.toFixed(1)} -> ${correctionsAfter.toFixed(1)} (${reduction}% reduction)`);
|
|
9407
|
+
lines.push(`Praise ratio: ${praiseRatio.toFixed(1)}x`);
|
|
9408
|
+
lines.push(`Insights applied: ${insightsApplied} | Pending: ${pendingCount}`);
|
|
9409
|
+
if (topInsight) {
|
|
9410
|
+
lines.push(`Top insight: "${topInsight.insight}" (${pct(topInsight.effectiveness)} effective)`);
|
|
9411
|
+
}
|
|
9412
|
+
if (modelPerformance.length > 0) {
|
|
9413
|
+
lines.push("");
|
|
9414
|
+
lines.push("Model Performance:");
|
|
9415
|
+
for (const row of modelPerformance) {
|
|
9416
|
+
const total = row.praises + row.corrections + row.errors;
|
|
9417
|
+
const scoreLabel = total > 0 ? ` (${row.praises}P / ${row.corrections}C / ${row.errors}E)` : "";
|
|
9418
|
+
lines.push(` ${row.backend} ${row.model} [${row.thinking}]${scoreLabel}`);
|
|
9419
|
+
}
|
|
9420
|
+
}
|
|
9421
|
+
return lines.join("\n");
|
|
9422
|
+
}
|
|
9423
|
+
function formatDiffCodeBlock(diff) {
|
|
9424
|
+
return `\`\`\`diff
|
|
9425
|
+
${diff}
|
|
9426
|
+
\`\`\``;
|
|
9427
|
+
}
|
|
9428
|
+
function buildProposalKeyboard(insightId, category) {
|
|
9429
|
+
if (category === "codebase") {
|
|
9430
|
+
return [
|
|
9431
|
+
[
|
|
9432
|
+
{ label: "Show Diff", data: `evolve:diff:${insightId}`, style: "primary" },
|
|
9433
|
+
{ label: "Discuss", data: `evolve:discuss:${insightId}` },
|
|
9434
|
+
{ label: "Reject", data: `evolve:reject:${insightId}`, style: "danger" }
|
|
9435
|
+
]
|
|
9436
|
+
];
|
|
9437
|
+
}
|
|
9438
|
+
return [
|
|
9439
|
+
[
|
|
9440
|
+
{ label: "Apply", data: `evolve:apply:${insightId}`, style: "success" },
|
|
9441
|
+
{ label: "Discuss", data: `evolve:discuss:${insightId}` },
|
|
9442
|
+
{ label: "Reject", data: `evolve:reject:${insightId}`, style: "danger" }
|
|
9443
|
+
]
|
|
9444
|
+
];
|
|
9445
|
+
}
|
|
9446
|
+
function buildEvolveOnboardingKeyboard() {
|
|
9447
|
+
return [
|
|
9448
|
+
[{ label: "\u2705 Enable Self-Learning", data: "evolve:toggle", style: "success" }],
|
|
9449
|
+
[{ label: "One-Time Analysis", data: "evolve:analyze" }]
|
|
9450
|
+
];
|
|
9451
|
+
}
|
|
9452
|
+
function buildEvolveMenuKeyboard(ctx) {
|
|
9453
|
+
const reviewLabel = ctx.pendingProposals > 0 ? `Review (${ctx.pendingProposals})` : "Review";
|
|
9454
|
+
return [
|
|
9455
|
+
[
|
|
9456
|
+
{ label: "\u{1F50D} Analyze Now", data: "evolve:analyze", style: "success" },
|
|
9457
|
+
ctx.pendingProposals > 0 ? { label: reviewLabel, data: "evolve:review", style: "primary" } : { label: reviewLabel, data: "evolve:review" }
|
|
9458
|
+
],
|
|
9459
|
+
[
|
|
9460
|
+
{ label: "Stats", data: "evolve:stats" },
|
|
9461
|
+
{ label: "History", data: "evolve:history" }
|
|
9462
|
+
],
|
|
9463
|
+
[
|
|
9464
|
+
{ label: "Model", data: "evolve:model" },
|
|
9465
|
+
{ label: "Undo", data: "evolve:undo", style: "danger" },
|
|
9466
|
+
{ label: "\u23F8 Disable", data: "evolve:toggle", style: "danger" }
|
|
9467
|
+
]
|
|
9468
|
+
];
|
|
9469
|
+
}
|
|
9470
|
+
function buildUndoKeyboard(insights) {
|
|
9471
|
+
return insights.map((ins) => [
|
|
9472
|
+
{ label: `Undo #${ins.id}: ${ins.insight}`, data: `evolve:undo:${ins.id}`, style: "danger" }
|
|
9473
|
+
]);
|
|
9474
|
+
}
|
|
9475
|
+
function buildModelKeyboard(currentMode) {
|
|
9476
|
+
const modes = [
|
|
9477
|
+
{ label: "Auto", mode: "auto" },
|
|
9478
|
+
{ label: "Pinned", mode: "pinned" },
|
|
9479
|
+
{ label: "Cheap", mode: "cheap" }
|
|
9480
|
+
];
|
|
9481
|
+
return [
|
|
9482
|
+
modes.map(({ label: label2, mode }) => ({
|
|
9483
|
+
label: mode === currentMode ? `[${label2}]` : label2,
|
|
9484
|
+
data: `evolve:model:${mode}`,
|
|
9485
|
+
...mode === currentMode ? { style: "primary" } : {}
|
|
9486
|
+
}))
|
|
9487
|
+
];
|
|
9488
|
+
}
|
|
9489
|
+
var init_propose = __esm({
|
|
9490
|
+
"src/reflection/propose.ts"() {
|
|
9491
|
+
"use strict";
|
|
9492
|
+
}
|
|
9493
|
+
});
|
|
9494
|
+
|
|
9316
9495
|
// src/scheduler/cron.ts
|
|
9317
9496
|
var cron_exports = {};
|
|
9318
9497
|
__export(cron_exports, {
|
|
@@ -9533,6 +9712,17 @@ async function executeJob(job) {
|
|
|
9533
9712
|
async function runWithRetry(job, model2, runId, t0) {
|
|
9534
9713
|
let lastError;
|
|
9535
9714
|
const chatId = job.sessionType === "isolated" ? `cron:${job.id}:${runId}` : job.chatId;
|
|
9715
|
+
if (job.jobType === "reflection") {
|
|
9716
|
+
const { runAnalysis: runAnalysis2 } = await Promise.resolve().then(() => (init_analyze(), analyze_exports));
|
|
9717
|
+
const { formatNightlySummary: formatNightlySummary2 } = await Promise.resolve().then(() => (init_propose(), propose_exports));
|
|
9718
|
+
const timeoutMs2 = job.timeout ? job.timeout * 1e3 : void 0;
|
|
9719
|
+
const insights = await runAnalysis2(job.chatId, { force: true, timeoutMs: timeoutMs2 });
|
|
9720
|
+
if (insights.length === 0) {
|
|
9721
|
+
return { text: "Nightly reflection: no new insights from recent interactions." };
|
|
9722
|
+
}
|
|
9723
|
+
const items = insights.map((ins, i) => ({ id: i + 1, ...ins }));
|
|
9724
|
+
return { text: formatNightlySummary2(items) };
|
|
9725
|
+
}
|
|
9536
9726
|
if (job.thinking && job.thinking !== "auto") {
|
|
9537
9727
|
setThinkingLevel(chatId, job.thinking);
|
|
9538
9728
|
}
|
|
@@ -12786,143 +12976,6 @@ var init_pagination = __esm({
|
|
|
12786
12976
|
}
|
|
12787
12977
|
});
|
|
12788
12978
|
|
|
12789
|
-
// src/reflection/propose.ts
|
|
12790
|
-
var propose_exports = {};
|
|
12791
|
-
__export(propose_exports, {
|
|
12792
|
-
buildEvolveMenuKeyboard: () => buildEvolveMenuKeyboard,
|
|
12793
|
-
buildEvolveOnboardingKeyboard: () => buildEvolveOnboardingKeyboard,
|
|
12794
|
-
buildModelKeyboard: () => buildModelKeyboard,
|
|
12795
|
-
buildProposalKeyboard: () => buildProposalKeyboard,
|
|
12796
|
-
buildUndoKeyboard: () => buildUndoKeyboard,
|
|
12797
|
-
formatDiffCodeBlock: () => formatDiffCodeBlock,
|
|
12798
|
-
formatGrowthReport: () => formatGrowthReport,
|
|
12799
|
-
formatNightlySummary: () => formatNightlySummary,
|
|
12800
|
-
formatProposalCard: () => formatProposalCard
|
|
12801
|
-
});
|
|
12802
|
-
function pct(ratio) {
|
|
12803
|
-
return `${Math.round(ratio * 100)}%`;
|
|
12804
|
-
}
|
|
12805
|
-
function formatProposalCard(params) {
|
|
12806
|
-
const { category, insight, why, targetFile, confidence, proposedAction, proposedDiff } = params;
|
|
12807
|
-
const header2 = `[${category}] ${insight}`;
|
|
12808
|
-
const whyLine = why ? `Why: ${why}` : null;
|
|
12809
|
-
const confidencePct = pct(confidence);
|
|
12810
|
-
const showTarget = category !== "codebase" && targetFile && targetFile !== "codebase";
|
|
12811
|
-
const targetLine = showTarget ? `Target: ${targetFile} | Confidence: ${confidencePct}` : `Confidence: ${confidencePct}`;
|
|
12812
|
-
const lines = [header2, ""];
|
|
12813
|
-
if (whyLine) lines.push(whyLine);
|
|
12814
|
-
lines.push(targetLine);
|
|
12815
|
-
if (proposedDiff) {
|
|
12816
|
-
const actionLabel = proposedAction ? ` (${proposedAction})` : "";
|
|
12817
|
-
lines.push("", `Proposed change${actionLabel}:`, "```diff", proposedDiff, "```");
|
|
12818
|
-
}
|
|
12819
|
-
return lines.join("\n");
|
|
12820
|
-
}
|
|
12821
|
-
function formatNightlySummary(insights) {
|
|
12822
|
-
const count = insights.length;
|
|
12823
|
-
const header2 = `Nightly Reflection \u2014 ${count} proposal${count === 1 ? "" : "s"} ready`;
|
|
12824
|
-
const list = insights.map((ins, i) => `${i + 1}. [${ins.category}] ${ins.insight}`).join("\n");
|
|
12825
|
-
return `${header2}
|
|
12826
|
-
|
|
12827
|
-
${list}
|
|
12828
|
-
|
|
12829
|
-
Review with /evolve`;
|
|
12830
|
-
}
|
|
12831
|
-
function formatGrowthReport(metrics, modelPerformance) {
|
|
12832
|
-
const { correctionsBefore, correctionsAfter, praiseRatio, insightsApplied, pendingCount, topInsight } = metrics;
|
|
12833
|
-
const reduction = correctionsBefore > 0 ? Math.round((correctionsBefore - correctionsAfter) / correctionsBefore * 100) : 0;
|
|
12834
|
-
const lines = [];
|
|
12835
|
-
lines.push("Growth Report");
|
|
12836
|
-
lines.push("");
|
|
12837
|
-
lines.push(`Corrections: ${correctionsBefore.toFixed(1)} -> ${correctionsAfter.toFixed(1)} (${reduction}% reduction)`);
|
|
12838
|
-
lines.push(`Praise ratio: ${praiseRatio.toFixed(1)}x`);
|
|
12839
|
-
lines.push(`Insights applied: ${insightsApplied} | Pending: ${pendingCount}`);
|
|
12840
|
-
if (topInsight) {
|
|
12841
|
-
lines.push(`Top insight: "${topInsight.insight}" (${pct(topInsight.effectiveness)} effective)`);
|
|
12842
|
-
}
|
|
12843
|
-
if (modelPerformance.length > 0) {
|
|
12844
|
-
lines.push("");
|
|
12845
|
-
lines.push("Model Performance:");
|
|
12846
|
-
for (const row of modelPerformance) {
|
|
12847
|
-
const total = row.praises + row.corrections + row.errors;
|
|
12848
|
-
const scoreLabel = total > 0 ? ` (${row.praises}P / ${row.corrections}C / ${row.errors}E)` : "";
|
|
12849
|
-
lines.push(` ${row.backend} ${row.model} [${row.thinking}]${scoreLabel}`);
|
|
12850
|
-
}
|
|
12851
|
-
}
|
|
12852
|
-
return lines.join("\n");
|
|
12853
|
-
}
|
|
12854
|
-
function formatDiffCodeBlock(diff) {
|
|
12855
|
-
return `\`\`\`diff
|
|
12856
|
-
${diff}
|
|
12857
|
-
\`\`\``;
|
|
12858
|
-
}
|
|
12859
|
-
function buildProposalKeyboard(insightId, category) {
|
|
12860
|
-
if (category === "codebase") {
|
|
12861
|
-
return [
|
|
12862
|
-
[
|
|
12863
|
-
{ label: "Show Diff", data: `evolve:diff:${insightId}`, style: "primary" },
|
|
12864
|
-
{ label: "Discuss", data: `evolve:discuss:${insightId}` },
|
|
12865
|
-
{ label: "Reject", data: `evolve:reject:${insightId}`, style: "danger" }
|
|
12866
|
-
]
|
|
12867
|
-
];
|
|
12868
|
-
}
|
|
12869
|
-
return [
|
|
12870
|
-
[
|
|
12871
|
-
{ label: "Apply", data: `evolve:apply:${insightId}`, style: "success" },
|
|
12872
|
-
{ label: "Discuss", data: `evolve:discuss:${insightId}` },
|
|
12873
|
-
{ label: "Reject", data: `evolve:reject:${insightId}`, style: "danger" }
|
|
12874
|
-
]
|
|
12875
|
-
];
|
|
12876
|
-
}
|
|
12877
|
-
function buildEvolveOnboardingKeyboard() {
|
|
12878
|
-
return [
|
|
12879
|
-
[{ label: "\u2705 Enable Self-Learning", data: "evolve:toggle", style: "success" }],
|
|
12880
|
-
[{ label: "One-Time Analysis", data: "evolve:analyze" }]
|
|
12881
|
-
];
|
|
12882
|
-
}
|
|
12883
|
-
function buildEvolveMenuKeyboard(ctx) {
|
|
12884
|
-
const reviewLabel = ctx.pendingProposals > 0 ? `Review (${ctx.pendingProposals})` : "Review";
|
|
12885
|
-
return [
|
|
12886
|
-
[
|
|
12887
|
-
{ label: "\u{1F50D} Analyze Now", data: "evolve:analyze", style: "success" },
|
|
12888
|
-
ctx.pendingProposals > 0 ? { label: reviewLabel, data: "evolve:review", style: "primary" } : { label: reviewLabel, data: "evolve:review" }
|
|
12889
|
-
],
|
|
12890
|
-
[
|
|
12891
|
-
{ label: "Stats", data: "evolve:stats" },
|
|
12892
|
-
{ label: "History", data: "evolve:history" }
|
|
12893
|
-
],
|
|
12894
|
-
[
|
|
12895
|
-
{ label: "Model", data: "evolve:model" },
|
|
12896
|
-
{ label: "Undo", data: "evolve:undo", style: "danger" },
|
|
12897
|
-
{ label: "\u23F8 Disable", data: "evolve:toggle", style: "danger" }
|
|
12898
|
-
]
|
|
12899
|
-
];
|
|
12900
|
-
}
|
|
12901
|
-
function buildUndoKeyboard(insights) {
|
|
12902
|
-
return insights.map((ins) => [
|
|
12903
|
-
{ label: `Undo #${ins.id}: ${ins.insight}`, data: `evolve:undo:${ins.id}`, style: "danger" }
|
|
12904
|
-
]);
|
|
12905
|
-
}
|
|
12906
|
-
function buildModelKeyboard(currentMode) {
|
|
12907
|
-
const modes = [
|
|
12908
|
-
{ label: "Auto", mode: "auto" },
|
|
12909
|
-
{ label: "Pinned", mode: "pinned" },
|
|
12910
|
-
{ label: "Cheap", mode: "cheap" }
|
|
12911
|
-
];
|
|
12912
|
-
return [
|
|
12913
|
-
modes.map(({ label: label2, mode }) => ({
|
|
12914
|
-
label: mode === currentMode ? `[${label2}]` : label2,
|
|
12915
|
-
data: `evolve:model:${mode}`,
|
|
12916
|
-
...mode === currentMode ? { style: "primary" } : {}
|
|
12917
|
-
}))
|
|
12918
|
-
];
|
|
12919
|
-
}
|
|
12920
|
-
var init_propose = __esm({
|
|
12921
|
-
"src/reflection/propose.ts"() {
|
|
12922
|
-
"use strict";
|
|
12923
|
-
}
|
|
12924
|
-
});
|
|
12925
|
-
|
|
12926
12979
|
// src/router.ts
|
|
12927
12980
|
import { readFile as readFile5, writeFile as writeFile3, unlink as unlink2, mkdir as mkdir2, readdir as readdir3, stat } from "fs/promises";
|
|
12928
12981
|
import { existsSync as existsSync19 } from "fs";
|
|
@@ -14838,32 +14891,49 @@ Message: "${testMsg}"`, { parseMode: "plain" });
|
|
|
14838
14891
|
break;
|
|
14839
14892
|
}
|
|
14840
14893
|
case "reflect": {
|
|
14841
|
-
const
|
|
14842
|
-
|
|
14843
|
-
|
|
14894
|
+
const lastReflect = lastReflectTime.get(chatId);
|
|
14895
|
+
if (lastReflect && Date.now() - lastReflect < REFLECT_COOLDOWN_MS) {
|
|
14896
|
+
const remaining = Math.ceil((REFLECT_COOLDOWN_MS - (Date.now() - lastReflect)) / 1e3);
|
|
14897
|
+
await channel.sendText(chatId, `Please wait ${remaining}s before reflecting again.`, { parseMode: "plain" });
|
|
14898
|
+
break;
|
|
14899
|
+
}
|
|
14900
|
+
const { getMessagePairCount: getMessagePairCount2 } = await Promise.resolve().then(() => (init_session_log(), session_log_exports));
|
|
14901
|
+
const pairCount = getMessagePairCount2(chatId);
|
|
14902
|
+
if (pairCount < 2) {
|
|
14903
|
+
await channel.sendText(chatId, "Need more conversation to analyze \u2014 at least 2 message exchanges.", { parseMode: "plain" });
|
|
14904
|
+
break;
|
|
14905
|
+
}
|
|
14906
|
+
lastReflectTime.set(chatId, Date.now());
|
|
14907
|
+
await channel.sendText(chatId, "Step 1/3: Summarizing conversation...", { parseMode: "plain" });
|
|
14844
14908
|
try {
|
|
14909
|
+
const { summarizeSession: summarizeSession2 } = await Promise.resolve().then(() => (init_summarize(), summarize_exports));
|
|
14910
|
+
await summarizeSession2(chatId, false);
|
|
14911
|
+
} catch (e) {
|
|
14912
|
+
await channel.sendText(chatId, `Summarization failed: ${e}`, { parseMode: "plain" });
|
|
14913
|
+
break;
|
|
14914
|
+
}
|
|
14915
|
+
await channel.sendText(chatId, "Step 2/3: Analyzing for insights...", { parseMode: "plain" });
|
|
14916
|
+
try {
|
|
14917
|
+
const { runAnalysis: runAnalysis2 } = await Promise.resolve().then(() => (init_analyze(), analyze_exports));
|
|
14918
|
+
const { formatProposalCard: formatProposalCard2, buildProposalKeyboard: buildProposalKeyboard2 } = await Promise.resolve().then(() => (init_propose(), propose_exports));
|
|
14845
14919
|
const insights = await runAnalysis2(chatId, { force: true });
|
|
14846
14920
|
if (insights.length === 0) {
|
|
14847
|
-
|
|
14848
|
-
const isFrozen = getRefStatus(getDb(), chatId) === "frozen";
|
|
14849
|
-
if (isFrozen) {
|
|
14850
|
-
const msg2 = "No insights found. Self-learning is disabled \u2014\nenable it with /evolve so feedback signals are\ncaptured automatically between analyses.";
|
|
14851
|
-
if (typeof channel.sendKeyboard === "function") {
|
|
14852
|
-
await channel.sendKeyboard(chatId, msg2, [[
|
|
14853
|
-
{ label: "Open /evolve", data: "evolve:menu", style: "primary" }
|
|
14854
|
-
]]);
|
|
14855
|
-
} else {
|
|
14856
|
-
await channel.sendText(chatId, msg2, { parseMode: "plain" });
|
|
14857
|
-
}
|
|
14858
|
-
} else {
|
|
14859
|
-
await channel.sendText(chatId, "No new insights from recent interactions.", { parseMode: "plain" });
|
|
14860
|
-
}
|
|
14921
|
+
await channel.sendText(chatId, "Step 3/3: No actionable improvements found in this session.", { parseMode: "plain" });
|
|
14861
14922
|
} else {
|
|
14862
|
-
|
|
14863
|
-
|
|
14923
|
+
await channel.sendText(chatId, `Step 3/3: Found ${insights.length} proposal(s).`, { parseMode: "plain" });
|
|
14924
|
+
const { getPendingInsights: getPendingInsights2 } = await Promise.resolve().then(() => (init_store4(), store_exports4));
|
|
14925
|
+
const pending = getPendingInsights2(getDb(), chatId);
|
|
14926
|
+
for (const insight of pending.slice(0, 5)) {
|
|
14927
|
+
const card = formatProposalCard2(insight);
|
|
14928
|
+
const kb = buildProposalKeyboard2(insight.id, insight.category);
|
|
14929
|
+
await channel.sendKeyboard(chatId, card, kb);
|
|
14930
|
+
}
|
|
14931
|
+
if (insights.length > 5) {
|
|
14932
|
+
await channel.sendText(chatId, `${insights.length - 5} more proposals. Use /evolve to review all.`, { parseMode: "plain" });
|
|
14933
|
+
}
|
|
14864
14934
|
}
|
|
14865
14935
|
} catch (e) {
|
|
14866
|
-
await channel.sendText(chatId, `Analysis failed: ${e}
|
|
14936
|
+
await channel.sendText(chatId, `Analysis failed: ${e}. You can review any pending proposals with /evolve.`, { parseMode: "plain" });
|
|
14867
14937
|
}
|
|
14868
14938
|
break;
|
|
14869
14939
|
}
|
|
@@ -15317,7 +15387,8 @@ async function handleSideQuest(parentChatId, msg, channel) {
|
|
|
15317
15387
|
backendId: adapter.id,
|
|
15318
15388
|
model: model2 ?? adapter.defaultModel
|
|
15319
15389
|
});
|
|
15320
|
-
} catch {
|
|
15390
|
+
} catch (e) {
|
|
15391
|
+
log(`[reflection] Side quest signal detection error: ${e}`);
|
|
15321
15392
|
}
|
|
15322
15393
|
} catch (err) {
|
|
15323
15394
|
typingActive = false;
|
|
@@ -16245,9 +16316,12 @@ ${PERM_MODES[chosen]}`,
|
|
|
16245
16316
|
const pending2 = pendingInterrupts.get(targetChatId);
|
|
16246
16317
|
if (pending2) {
|
|
16247
16318
|
pendingInterrupts.delete(targetChatId);
|
|
16248
|
-
|
|
16249
|
-
|
|
16250
|
-
|
|
16319
|
+
await channel.sendText(chatId, "\u{1F5FA} Launching side quest\u2026", { parseMode: "plain" });
|
|
16320
|
+
handleSideQuest(targetChatId, pending2.msg, pending2.channel).catch((err) => {
|
|
16321
|
+
error(`[router] Side quest error for ${targetChatId}:`, err);
|
|
16322
|
+
channel.sendText(targetChatId, `\u{1F5FA} Side quest failed: ${err.message}`, { parseMode: "plain" }).catch(() => {
|
|
16323
|
+
});
|
|
16324
|
+
});
|
|
16251
16325
|
} else {
|
|
16252
16326
|
await channel.sendText(chatId, "Main task finished \u2014 your message was already processed.", { parseMode: "plain" });
|
|
16253
16327
|
}
|
|
@@ -17457,7 +17531,7 @@ Use /skills <page> to navigate (e.g. /skills 2)` : "";
|
|
|
17457
17531
|
const header2 = totalPages > 1 ? `${skills2.length} skills (page ${safePage}/${totalPages}). Select one to invoke:` : `${skills2.length} skills available. Select one to invoke:`;
|
|
17458
17532
|
await channel.sendKeyboard(chatId, header2, buttons);
|
|
17459
17533
|
}
|
|
17460
|
-
var PERM_MODES, VERBOSE_LEVELS, HELP_CATEGORIES, USAGE_WINDOW_MAP, MEDIA_INCOMING_PATH, TONE_PATTERNS, pendingInterrupts, bypassBusyCheck, activeSideQuests, MAX_SIDE_QUESTS, pendingFallbackMessages, dashboardClawWarnings, pendingSummaryUndo, pendingNewchatUndo, CLI_INSTALL_HINTS, BLOCKED_PATH_PATTERNS2, ALLOWED_REACTION_EMOJIS, SKILLS_PER_PAGE;
|
|
17534
|
+
var PERM_MODES, VERBOSE_LEVELS, HELP_CATEGORIES, USAGE_WINDOW_MAP, MEDIA_INCOMING_PATH, TONE_PATTERNS, pendingInterrupts, bypassBusyCheck, activeSideQuests, MAX_SIDE_QUESTS, pendingFallbackMessages, dashboardClawWarnings, lastReflectTime, REFLECT_COOLDOWN_MS, pendingSummaryUndo, pendingNewchatUndo, CLI_INSTALL_HINTS, BLOCKED_PATH_PATTERNS2, ALLOWED_REACTION_EMOJIS, SKILLS_PER_PAGE;
|
|
17461
17535
|
var init_router = __esm({
|
|
17462
17536
|
"src/router.ts"() {
|
|
17463
17537
|
"use strict";
|
|
@@ -17600,6 +17674,8 @@ var init_router = __esm({
|
|
|
17600
17674
|
MAX_SIDE_QUESTS = 2;
|
|
17601
17675
|
pendingFallbackMessages = /* @__PURE__ */ new Map();
|
|
17602
17676
|
dashboardClawWarnings = /* @__PURE__ */ new Map();
|
|
17677
|
+
lastReflectTime = /* @__PURE__ */ new Map();
|
|
17678
|
+
REFLECT_COOLDOWN_MS = 12e4;
|
|
17603
17679
|
pendingSummaryUndo = /* @__PURE__ */ new Map();
|
|
17604
17680
|
pendingNewchatUndo = /* @__PURE__ */ new Map();
|
|
17605
17681
|
CLI_INSTALL_HINTS = {
|
|
@@ -18614,6 +18690,8 @@ async function main() {
|
|
|
18614
18690
|
const shutdown = async (signal) => {
|
|
18615
18691
|
log(`[cc-claw] Received ${signal}, shutting down...`);
|
|
18616
18692
|
try {
|
|
18693
|
+
const { stopAllActiveAgents: stopAllActiveAgents2 } = await Promise.resolve().then(() => (init_agent(), agent_exports));
|
|
18694
|
+
stopAllActiveAgents2();
|
|
18617
18695
|
stopAllHeartbeats();
|
|
18618
18696
|
stopHealthMonitor3();
|
|
18619
18697
|
stopMonitor();
|
|
@@ -19381,14 +19459,18 @@ async function doctorCommand(globalOpts, localOpts) {
|
|
|
19381
19459
|
checks.push({ name: "Environment", status: "error", message: "No .env found", fix: "cc-claw setup" });
|
|
19382
19460
|
}
|
|
19383
19461
|
const CLI_BINARIES = { claude: "claude", gemini: "gemini", codex: "codex", cursor: "agent" };
|
|
19462
|
+
let installedBackends = 0;
|
|
19384
19463
|
for (const [label2, binary] of Object.entries(CLI_BINARIES)) {
|
|
19385
19464
|
try {
|
|
19386
19465
|
const path = execFileSync3("which", [binary], { encoding: "utf-8", timeout: 5e3 }).trim();
|
|
19387
19466
|
checks.push({ name: `${label2} CLI`, status: "ok", message: path });
|
|
19467
|
+
installedBackends++;
|
|
19388
19468
|
} catch {
|
|
19389
|
-
checks.push({ name: `${label2} CLI`, status: "warning", message: "not found in PATH" });
|
|
19390
19469
|
}
|
|
19391
19470
|
}
|
|
19471
|
+
if (installedBackends === 0) {
|
|
19472
|
+
checks.push({ name: "Backend CLIs", status: "error", message: "No backend CLIs found. Install at least one (claude, gemini, codex, or cursor agent)." });
|
|
19473
|
+
}
|
|
19392
19474
|
try {
|
|
19393
19475
|
const { isDaemonRunning: isDaemonRunning2, apiGet: apiGet2 } = await Promise.resolve().then(() => (init_api_client(), api_client_exports));
|
|
19394
19476
|
const running = await isDaemonRunning2();
|
|
@@ -19418,6 +19500,13 @@ async function doctorCommand(globalOpts, localOpts) {
|
|
|
19418
19500
|
} catch {
|
|
19419
19501
|
checks.push({ name: "Daemon", status: "warning", message: "could not probe" });
|
|
19420
19502
|
}
|
|
19503
|
+
try {
|
|
19504
|
+
const latest = execFileSync3("npm", ["view", "cc-claw", "version"], { encoding: "utf-8", timeout: 1e4 }).trim();
|
|
19505
|
+
if (latest && latest !== VERSION) {
|
|
19506
|
+
checks.push({ name: "Update available", status: "warning", message: `v${latest} available (current: v${VERSION})`, fix: "npm install -g cc-claw@latest" });
|
|
19507
|
+
}
|
|
19508
|
+
} catch {
|
|
19509
|
+
}
|
|
19421
19510
|
const tokenPath = `${DATA_PATH}/api-token`;
|
|
19422
19511
|
if (existsSync26(tokenPath)) {
|
|
19423
19512
|
try {
|
|
@@ -23333,6 +23422,20 @@ program.command("setup").description("Interactive configuration wizard").action(
|
|
|
23333
23422
|
await Promise.resolve().then(() => (init_setup(), setup_exports));
|
|
23334
23423
|
});
|
|
23335
23424
|
async function run(argv = process.argv) {
|
|
23425
|
+
if (argv.includes("--version") || argv.includes("-V")) {
|
|
23426
|
+
console.log(VERSION);
|
|
23427
|
+
try {
|
|
23428
|
+
const { execFileSync: execFileSync5 } = await import("child_process");
|
|
23429
|
+
const latest = execFileSync5("npm", ["view", "cc-claw", "version"], { encoding: "utf-8", timeout: 1e4 }).trim();
|
|
23430
|
+
if (latest && latest !== VERSION) {
|
|
23431
|
+
console.log(`
|
|
23432
|
+
Update available: v${latest} (current: v${VERSION})`);
|
|
23433
|
+
console.log(`Run: npm install -g cc-claw@latest`);
|
|
23434
|
+
}
|
|
23435
|
+
} catch {
|
|
23436
|
+
}
|
|
23437
|
+
return;
|
|
23438
|
+
}
|
|
23336
23439
|
if (argv.includes("--ai")) {
|
|
23337
23440
|
const { generateAiSkill: generateAiSkill2, installAiSkill: installAiSkill2 } = await Promise.resolve().then(() => (init_ai_skill(), ai_skill_exports));
|
|
23338
23441
|
if (argv.includes("--install")) {
|
package/package.json
CHANGED