cc-claw 0.11.3 → 0.12.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +634 -276
- 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.2" : (() => {
|
|
76
76
|
try {
|
|
77
77
|
return JSON.parse(readFileSync(join2(process.cwd(), "package.json"), "utf-8")).version ?? "unknown";
|
|
78
78
|
} catch {
|
|
@@ -678,22 +678,25 @@ var init_identity = __esm({
|
|
|
678
678
|
// src/reflection/store.ts
|
|
679
679
|
var store_exports4 = {};
|
|
680
680
|
__export(store_exports4, {
|
|
681
|
+
advanceReviewSession: () => advanceReviewSession,
|
|
681
682
|
cleanupBackupFiles: () => cleanupBackupFiles,
|
|
682
683
|
cleanupReflectionData: () => cleanupReflectionData,
|
|
684
|
+
createReviewSession: () => createReviewSession,
|
|
685
|
+
deleteReviewSession: () => deleteReviewSession,
|
|
686
|
+
getAppliedCountTodayForFile: () => getAppliedCountTodayForFile,
|
|
683
687
|
getAppliedInsightCountToday: () => getAppliedInsightCountToday,
|
|
684
688
|
getAppliedInsights: () => getAppliedInsights,
|
|
685
|
-
getConflictingInsights: () => getConflictingInsights,
|
|
686
689
|
getGrowthMetrics: () => getGrowthMetrics,
|
|
687
690
|
getInsightById: () => getInsightById,
|
|
688
691
|
getLastAnalysisTime: () => getLastAnalysisTime,
|
|
689
692
|
getPendingInsightCount: () => getPendingInsightCount,
|
|
690
693
|
getPendingInsights: () => getPendingInsights,
|
|
691
|
-
getRecommendations: () => getRecommendations,
|
|
692
694
|
getReflectionModelConfig: () => getReflectionModelConfig,
|
|
695
|
+
getReflectionSettings: () => getReflectionSettings,
|
|
693
696
|
getReflectionStatus: () => getReflectionStatus,
|
|
694
697
|
getRejectedInsights: () => getRejectedInsights,
|
|
698
|
+
getReviewSession: () => getReviewSession,
|
|
695
699
|
getSignalCountForChat: () => getSignalCountForChat,
|
|
696
|
-
getSignalsForPeriod: () => getSignalsForPeriod,
|
|
697
700
|
getUnprocessedSignalCount: () => getUnprocessedSignalCount,
|
|
698
701
|
getUnprocessedSignals: () => getUnprocessedSignals,
|
|
699
702
|
initReflectionTables: () => initReflectionTables,
|
|
@@ -701,6 +704,7 @@ __export(store_exports4, {
|
|
|
701
704
|
logSignal: () => logSignal,
|
|
702
705
|
markSignalsProcessed: () => markSignalsProcessed,
|
|
703
706
|
setReflectionModelConfig: () => setReflectionModelConfig,
|
|
707
|
+
setReflectionSettings: () => setReflectionSettings,
|
|
704
708
|
setReflectionStatus: () => setReflectionStatus,
|
|
705
709
|
updateInsightEffectiveness: () => updateInsightEffectiveness,
|
|
706
710
|
updateInsightProposal: () => updateInsightProposal,
|
|
@@ -710,16 +714,37 @@ __export(store_exports4, {
|
|
|
710
714
|
});
|
|
711
715
|
import { existsSync, readdirSync, statSync, unlinkSync } from "fs";
|
|
712
716
|
import { join as join3 } from "path";
|
|
713
|
-
function cleanupBackupFiles(ccClawHome) {
|
|
714
|
-
const
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
717
|
+
function cleanupBackupFiles(ccClawHome, retentionDays = 7) {
|
|
718
|
+
const cutoffMs = Date.now() - retentionDays * 24 * 60 * 60 * 1e3;
|
|
719
|
+
const dirs = [
|
|
720
|
+
join3(ccClawHome, "identity"),
|
|
721
|
+
join3(ccClawHome, "workspace", "context")
|
|
722
|
+
];
|
|
723
|
+
const skillsRoot = join3(ccClawHome, "workspace", "skills");
|
|
724
|
+
try {
|
|
725
|
+
if (existsSync(skillsRoot)) {
|
|
726
|
+
for (const entry of readdirSync(skillsRoot)) {
|
|
727
|
+
const p = join3(skillsRoot, entry);
|
|
728
|
+
try {
|
|
729
|
+
if (statSync(p).isDirectory()) dirs.push(p);
|
|
730
|
+
} catch {
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
} catch {
|
|
735
|
+
}
|
|
736
|
+
for (const dir of dirs) {
|
|
737
|
+
if (!existsSync(dir)) continue;
|
|
720
738
|
try {
|
|
721
|
-
const
|
|
722
|
-
|
|
739
|
+
for (const file of readdirSync(dir)) {
|
|
740
|
+
if (!file.includes(".bak.")) continue;
|
|
741
|
+
const fullPath = join3(dir, file);
|
|
742
|
+
try {
|
|
743
|
+
const stat2 = statSync(fullPath);
|
|
744
|
+
if (stat2.mtimeMs < cutoffMs) unlinkSync(fullPath);
|
|
745
|
+
} catch {
|
|
746
|
+
}
|
|
747
|
+
}
|
|
723
748
|
} catch {
|
|
724
749
|
}
|
|
725
750
|
}
|
|
@@ -795,9 +820,25 @@ function initReflectionTables(db3) {
|
|
|
795
820
|
model TEXT,
|
|
796
821
|
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
797
822
|
);
|
|
823
|
+
|
|
824
|
+
CREATE TABLE IF NOT EXISTS review_sessions (
|
|
825
|
+
chatId TEXT PRIMARY KEY,
|
|
826
|
+
insightIds TEXT NOT NULL,
|
|
827
|
+
currentIndex INTEGER NOT NULL DEFAULT 0,
|
|
828
|
+
results TEXT NOT NULL DEFAULT '{}',
|
|
829
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
830
|
+
);
|
|
831
|
+
|
|
832
|
+
CREATE TABLE IF NOT EXISTS reflection_settings (
|
|
833
|
+
chatId TEXT PRIMARY KEY,
|
|
834
|
+
perFileCap INTEGER NOT NULL DEFAULT 3,
|
|
835
|
+
backupRetentionDays INTEGER NOT NULL DEFAULT 7,
|
|
836
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
837
|
+
);
|
|
798
838
|
`);
|
|
799
839
|
cleanupReflectionData(db3);
|
|
800
|
-
|
|
840
|
+
const settings = getReflectionSettings(db3, "global");
|
|
841
|
+
cleanupBackupFiles(CC_CLAW_HOME, settings.backupRetentionDays);
|
|
801
842
|
}
|
|
802
843
|
function logSignal(db3, params) {
|
|
803
844
|
const result = db3.prepare(`
|
|
@@ -839,13 +880,6 @@ function getSignalCountForChat(db3, chatId, minutesBack) {
|
|
|
839
880
|
`).get(chatId, minutesBack);
|
|
840
881
|
return row.count;
|
|
841
882
|
}
|
|
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
883
|
function getUnprocessedSignalCount(db3, chatId) {
|
|
850
884
|
const row = db3.prepare(`
|
|
851
885
|
SELECT COUNT(*) as count FROM feedback_signals
|
|
@@ -916,14 +950,6 @@ function getRejectedInsights(db3, chatId, limit = 20) {
|
|
|
916
950
|
LIMIT ?
|
|
917
951
|
`).all(chatId, limit);
|
|
918
952
|
}
|
|
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
953
|
function updateInsightStatus(db3, id, status) {
|
|
928
954
|
if (status === "applied") {
|
|
929
955
|
db3.prepare(`
|
|
@@ -944,13 +970,6 @@ function updateInsightRollback(db3, id, rollbackData) {
|
|
|
944
970
|
function updateInsightEffectiveness(db3, id, score) {
|
|
945
971
|
db3.prepare("UPDATE insights SET effectiveness = ? WHERE id = ?").run(score, id);
|
|
946
972
|
}
|
|
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
973
|
function getPendingInsightCount(db3, chatId) {
|
|
955
974
|
const row = db3.prepare(`
|
|
956
975
|
SELECT COUNT(*) as count FROM insights WHERE chatId = ? AND status = 'pending'
|
|
@@ -960,10 +979,17 @@ function getPendingInsightCount(db3, chatId) {
|
|
|
960
979
|
function getAppliedInsightCountToday(db3, chatId) {
|
|
961
980
|
const row = db3.prepare(`
|
|
962
981
|
SELECT COUNT(*) as count FROM insights
|
|
963
|
-
WHERE chatId = ? AND status = 'applied' AND date(
|
|
982
|
+
WHERE chatId = ? AND status = 'applied' AND appliedAt >= date('now') AND appliedAt < date('now', '+1 day')
|
|
964
983
|
`).get(chatId);
|
|
965
984
|
return row.count;
|
|
966
985
|
}
|
|
986
|
+
function getAppliedCountTodayForFile(db3, chatId, targetFile) {
|
|
987
|
+
const row = db3.prepare(`
|
|
988
|
+
SELECT COUNT(*) as count FROM insights
|
|
989
|
+
WHERE chatId = ? AND status = 'applied' AND targetFile = ? AND appliedAt >= date('now') AND appliedAt < date('now', '+1 day')
|
|
990
|
+
`).get(chatId, targetFile);
|
|
991
|
+
return row.count;
|
|
992
|
+
}
|
|
967
993
|
function upsertGrowthMetric(db3, chatId, period, data) {
|
|
968
994
|
db3.prepare(`
|
|
969
995
|
INSERT OR REPLACE INTO growth_metrics (chatId, period, corrections, praises, errors, insightsApplied)
|
|
@@ -991,6 +1017,58 @@ function setReflectionModelConfig(db3, chatId, mode, backend2, model2) {
|
|
|
991
1017
|
VALUES (?, ?, ?, ?)
|
|
992
1018
|
`).run(chatId, mode, backend2 ?? null, model2 ?? null);
|
|
993
1019
|
}
|
|
1020
|
+
function createReviewSession(db3, chatId, insightIds) {
|
|
1021
|
+
db3.prepare(`
|
|
1022
|
+
INSERT OR REPLACE INTO review_sessions (chatId, insightIds, currentIndex, results)
|
|
1023
|
+
VALUES (?, ?, 0, '{}')
|
|
1024
|
+
`).run(chatId, JSON.stringify(insightIds));
|
|
1025
|
+
}
|
|
1026
|
+
function getReviewSession(db3, chatId) {
|
|
1027
|
+
const row = db3.prepare(
|
|
1028
|
+
"SELECT * FROM review_sessions WHERE chatId = ?"
|
|
1029
|
+
).get(chatId);
|
|
1030
|
+
if (!row) return null;
|
|
1031
|
+
return {
|
|
1032
|
+
chatId: row.chatId,
|
|
1033
|
+
insightIds: JSON.parse(row.insightIds),
|
|
1034
|
+
currentIndex: row.currentIndex,
|
|
1035
|
+
results: JSON.parse(row.results),
|
|
1036
|
+
created_at: row.created_at
|
|
1037
|
+
};
|
|
1038
|
+
}
|
|
1039
|
+
function advanceReviewSession(db3, chatId, insightId, action) {
|
|
1040
|
+
const session2 = getReviewSession(db3, chatId);
|
|
1041
|
+
if (!session2) return null;
|
|
1042
|
+
session2.results[insightId] = action;
|
|
1043
|
+
session2.currentIndex = session2.currentIndex + 1;
|
|
1044
|
+
db3.prepare(`
|
|
1045
|
+
UPDATE review_sessions SET currentIndex = ?, results = ? WHERE chatId = ?
|
|
1046
|
+
`).run(session2.currentIndex, JSON.stringify(session2.results), chatId);
|
|
1047
|
+
return session2;
|
|
1048
|
+
}
|
|
1049
|
+
function deleteReviewSession(db3, chatId) {
|
|
1050
|
+
db3.prepare("DELETE FROM review_sessions WHERE chatId = ?").run(chatId);
|
|
1051
|
+
}
|
|
1052
|
+
function getReflectionSettings(db3, chatId) {
|
|
1053
|
+
const row = db3.prepare(
|
|
1054
|
+
"SELECT perFileCap, backupRetentionDays FROM reflection_settings WHERE chatId = ?"
|
|
1055
|
+
).get(chatId);
|
|
1056
|
+
return {
|
|
1057
|
+
perFileCap: row?.perFileCap ?? 3,
|
|
1058
|
+
backupRetentionDays: row?.backupRetentionDays ?? 7
|
|
1059
|
+
};
|
|
1060
|
+
}
|
|
1061
|
+
function setReflectionSettings(db3, chatId, settings) {
|
|
1062
|
+
const current = getReflectionSettings(db3, chatId);
|
|
1063
|
+
db3.prepare(`
|
|
1064
|
+
INSERT OR REPLACE INTO reflection_settings (chatId, perFileCap, backupRetentionDays)
|
|
1065
|
+
VALUES (?, ?, ?)
|
|
1066
|
+
`).run(
|
|
1067
|
+
chatId,
|
|
1068
|
+
settings.perFileCap ?? current.perFileCap,
|
|
1069
|
+
settings.backupRetentionDays ?? current.backupRetentionDays
|
|
1070
|
+
);
|
|
1071
|
+
}
|
|
994
1072
|
function cleanupReflectionData(db3) {
|
|
995
1073
|
db3.prepare(`
|
|
996
1074
|
DELETE FROM feedback_signals WHERE created_at < datetime('now', '-90 days')
|
|
@@ -1001,6 +1079,9 @@ function cleanupReflectionData(db3) {
|
|
|
1001
1079
|
db3.prepare(`
|
|
1002
1080
|
DELETE FROM growth_metrics WHERE created_at < datetime('now', '-365 days')
|
|
1003
1081
|
`).run();
|
|
1082
|
+
db3.prepare(`
|
|
1083
|
+
DELETE FROM review_sessions WHERE created_at < datetime('now', '-1 day')
|
|
1084
|
+
`).run();
|
|
1004
1085
|
}
|
|
1005
1086
|
var init_store4 = __esm({
|
|
1006
1087
|
"src/reflection/store.ts"() {
|
|
@@ -1558,6 +1639,7 @@ function initDatabase() {
|
|
|
1558
1639
|
target TEXT,
|
|
1559
1640
|
delivery_mode TEXT NOT NULL DEFAULT 'announce',
|
|
1560
1641
|
timezone TEXT NOT NULL DEFAULT 'UTC',
|
|
1642
|
+
job_type TEXT DEFAULT 'normal',
|
|
1561
1643
|
enabled INTEGER NOT NULL DEFAULT 1,
|
|
1562
1644
|
active INTEGER NOT NULL DEFAULT 1,
|
|
1563
1645
|
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
@@ -1594,6 +1676,7 @@ function initDatabase() {
|
|
|
1594
1676
|
target TEXT,
|
|
1595
1677
|
delivery_mode TEXT NOT NULL DEFAULT 'announce',
|
|
1596
1678
|
timezone TEXT NOT NULL DEFAULT 'UTC',
|
|
1679
|
+
job_type TEXT DEFAULT 'normal',
|
|
1597
1680
|
enabled INTEGER NOT NULL DEFAULT 1,
|
|
1598
1681
|
active INTEGER NOT NULL DEFAULT 1,
|
|
1599
1682
|
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
@@ -1681,6 +1764,14 @@ function initDatabase() {
|
|
|
1681
1764
|
db.exec("ALTER TABLE jobs ADD COLUMN title TEXT");
|
|
1682
1765
|
} catch {
|
|
1683
1766
|
}
|
|
1767
|
+
try {
|
|
1768
|
+
db.exec("ALTER TABLE jobs ADD COLUMN job_type TEXT DEFAULT 'normal'");
|
|
1769
|
+
} catch {
|
|
1770
|
+
}
|
|
1771
|
+
try {
|
|
1772
|
+
db.exec("UPDATE jobs SET job_type = 'reflection' WHERE description LIKE '%reflection analysis%' AND job_type = 'normal'");
|
|
1773
|
+
} catch {
|
|
1774
|
+
}
|
|
1684
1775
|
backfillJobTitles(db);
|
|
1685
1776
|
}
|
|
1686
1777
|
} catch {
|
|
@@ -2529,8 +2620,8 @@ function getBackendUsageInWindow(backend2, windowType) {
|
|
|
2529
2620
|
function insertJob(params) {
|
|
2530
2621
|
const result = db.prepare(`
|
|
2531
2622
|
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 (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
2623
|
+
backend, model, thinking, timeout, fallbacks, session_type, channel, target, delivery_mode, timezone, job_type)
|
|
2624
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
2534
2625
|
`).run(
|
|
2535
2626
|
params.scheduleType,
|
|
2536
2627
|
params.cron ?? null,
|
|
@@ -2548,7 +2639,8 @@ function insertJob(params) {
|
|
|
2548
2639
|
params.channel ?? null,
|
|
2549
2640
|
params.target ?? null,
|
|
2550
2641
|
params.deliveryMode ?? "announce",
|
|
2551
|
-
params.timezone ?? "UTC"
|
|
2642
|
+
params.timezone ?? "UTC",
|
|
2643
|
+
params.jobType ?? "normal"
|
|
2552
2644
|
);
|
|
2553
2645
|
return getJobById(Number(result.lastInsertRowid));
|
|
2554
2646
|
}
|
|
@@ -2629,7 +2721,8 @@ function updateJob(id, fields) {
|
|
|
2629
2721
|
channel: "channel",
|
|
2630
2722
|
target: "target",
|
|
2631
2723
|
deliveryMode: "delivery_mode",
|
|
2632
|
-
timezone: "timezone"
|
|
2724
|
+
timezone: "timezone",
|
|
2725
|
+
jobType: "job_type"
|
|
2633
2726
|
};
|
|
2634
2727
|
for (const [key, val] of Object.entries(fields)) {
|
|
2635
2728
|
const col = fieldMap[key];
|
|
@@ -3066,7 +3159,7 @@ var init_store5 = __esm({
|
|
|
3066
3159
|
SELECT id, schedule_type as scheduleType, cron, at_time as atTime, every_ms as everyMs,
|
|
3067
3160
|
title, description, chat_id as chatId, backend, model, thinking, timeout, fallbacks,
|
|
3068
3161
|
session_type as sessionType, channel, target, delivery_mode as deliveryMode,
|
|
3069
|
-
timezone, enabled, active, created_at as createdAt, last_run_at as lastRunAt,
|
|
3162
|
+
timezone, job_type as jobType, enabled, active, created_at as createdAt, last_run_at as lastRunAt,
|
|
3070
3163
|
next_run_at as nextRunAt, consecutive_failures as consecutiveFailures
|
|
3071
3164
|
FROM jobs
|
|
3072
3165
|
`;
|
|
@@ -4958,7 +5051,7 @@ ${transcript}`;
|
|
|
4958
5051
|
if (!resultText) resultText = accumulatedText;
|
|
4959
5052
|
if (!resultText) {
|
|
4960
5053
|
warn(`[summarize] ${adapter.id}:${model2} returned empty result for chat ${chatId}`);
|
|
4961
|
-
return false;
|
|
5054
|
+
return { success: false, rawText: "" };
|
|
4962
5055
|
}
|
|
4963
5056
|
const summaryMatch = resultText.match(/SUMMARY:\s*(.+?)(?=\nKEY_DETAILS:|\nTOPICS:|$)/s);
|
|
4964
5057
|
const keyDetailsMatch = resultText.match(/KEY_DETAILS:\s*(.+?)(?=\nTOPICS:|$)/s);
|
|
@@ -4970,16 +5063,44 @@ Key details: ${keyDetails}`;
|
|
|
4970
5063
|
const topics = topicsMatch?.[1]?.trim() ?? "";
|
|
4971
5064
|
saveSessionSummaryWithEmbedding(chatId, summary, topics, Math.floor(entries.length / 2));
|
|
4972
5065
|
log(`[summarize] Saved summary via ${adapter.id}:${model2} for chat ${chatId}`);
|
|
4973
|
-
return true;
|
|
5066
|
+
return { success: true, rawText: resultText };
|
|
4974
5067
|
} catch (err) {
|
|
4975
5068
|
warn(`[summarize] ${adapter.id}:${model2} failed for ${chatId}: ${errorMessage(err)}`);
|
|
4976
|
-
return false;
|
|
5069
|
+
return { success: false, rawText: "" };
|
|
4977
5070
|
}
|
|
4978
5071
|
}
|
|
4979
|
-
async function
|
|
5072
|
+
async function extractAndLogSignals(rawText, chatId, adapterId, model2) {
|
|
5073
|
+
try {
|
|
5074
|
+
const { getReflectionStatus: getReflectionStatus2, logSignal: logSignal2 } = await Promise.resolve().then(() => (init_store4(), store_exports4));
|
|
5075
|
+
const { isSyntheticChatId: isSyntheticChatId3 } = await Promise.resolve().then(() => (init_agent(), agent_exports));
|
|
5076
|
+
const db3 = getDb();
|
|
5077
|
+
if (isSyntheticChatId3(chatId) || getReflectionStatus2(db3, chatId) === "frozen") return;
|
|
5078
|
+
const signalMatches = rawText.matchAll(/^-\s*\[(\w+)\]\s*(.+)/gm);
|
|
5079
|
+
for (const match of signalMatches) {
|
|
5080
|
+
const signalType = match[1].toLowerCase();
|
|
5081
|
+
const trigger = match[2].trim();
|
|
5082
|
+
if (VALID_SIGNAL_TYPES.includes(signalType)) {
|
|
5083
|
+
logSignal2(db3, {
|
|
5084
|
+
chatId,
|
|
5085
|
+
signalType,
|
|
5086
|
+
trigger,
|
|
5087
|
+
context: null,
|
|
5088
|
+
source: "llm_summary",
|
|
5089
|
+
confidence: 0.7,
|
|
5090
|
+
backend: adapterId,
|
|
5091
|
+
model: model2,
|
|
5092
|
+
thinkingLevel: null
|
|
5093
|
+
});
|
|
5094
|
+
}
|
|
5095
|
+
}
|
|
5096
|
+
} catch (e) {
|
|
5097
|
+
warn(`[summarize] Signal extraction failed for ${chatId}: ${e}`);
|
|
5098
|
+
}
|
|
5099
|
+
}
|
|
5100
|
+
async function summarizeWithFallbackChain(chatId, targetBackendId, excludeBackend, clearLogAfter = true) {
|
|
4980
5101
|
const pairCount = getMessagePairCount(chatId);
|
|
4981
5102
|
if (pairCount < MIN_PAIRS) {
|
|
4982
|
-
clearLog(chatId);
|
|
5103
|
+
if (clearLogAfter) clearLog(chatId);
|
|
4983
5104
|
return false;
|
|
4984
5105
|
}
|
|
4985
5106
|
const entries = getLog(chatId);
|
|
@@ -4996,8 +5117,10 @@ async function summarizeWithFallbackChain(chatId, targetBackendId, excludeBacken
|
|
|
4996
5117
|
const model2 = config2.model ?? adapter.summarizerModel;
|
|
4997
5118
|
const key = `${adapter.id}:${model2}`;
|
|
4998
5119
|
tried.add(key);
|
|
4999
|
-
|
|
5000
|
-
|
|
5120
|
+
const result = await attemptSummarize(chatId, adapter, model2, entries);
|
|
5121
|
+
if (result.success) {
|
|
5122
|
+
await extractAndLogSignals(result.rawText, chatId, adapter.id, model2);
|
|
5123
|
+
if (clearLogAfter) clearLog(chatId);
|
|
5001
5124
|
return true;
|
|
5002
5125
|
}
|
|
5003
5126
|
}
|
|
@@ -5010,8 +5133,10 @@ async function summarizeWithFallbackChain(chatId, targetBackendId, excludeBacken
|
|
|
5010
5133
|
const key = `${targetAdapter.id}:${model2}`;
|
|
5011
5134
|
if (!tried.has(key)) {
|
|
5012
5135
|
tried.add(key);
|
|
5013
|
-
|
|
5014
|
-
|
|
5136
|
+
const result = await attemptSummarize(chatId, targetAdapter, model2, entries);
|
|
5137
|
+
if (result.success) {
|
|
5138
|
+
await extractAndLogSignals(result.rawText, chatId, targetAdapter.id, model2);
|
|
5139
|
+
if (clearLogAfter) clearLog(chatId);
|
|
5015
5140
|
return true;
|
|
5016
5141
|
}
|
|
5017
5142
|
}
|
|
@@ -5023,8 +5148,10 @@ async function summarizeWithFallbackChain(chatId, targetBackendId, excludeBacken
|
|
|
5023
5148
|
const key = `${adapter.id}:${model2}`;
|
|
5024
5149
|
if (!tried.has(key)) {
|
|
5025
5150
|
tried.add(key);
|
|
5026
|
-
|
|
5027
|
-
|
|
5151
|
+
const result = await attemptSummarize(chatId, adapter, model2, entries);
|
|
5152
|
+
if (result.success) {
|
|
5153
|
+
await extractAndLogSignals(result.rawText, chatId, adapter.id, model2);
|
|
5154
|
+
if (clearLogAfter) clearLog(chatId);
|
|
5028
5155
|
return true;
|
|
5029
5156
|
}
|
|
5030
5157
|
}
|
|
@@ -5032,8 +5159,8 @@ async function summarizeWithFallbackChain(chatId, targetBackendId, excludeBacken
|
|
|
5032
5159
|
warn(`[summarize] All fallback attempts failed for chat ${chatId} \u2014 raw log preserved`);
|
|
5033
5160
|
return false;
|
|
5034
5161
|
}
|
|
5035
|
-
async function summarizeSession(chatId) {
|
|
5036
|
-
return summarizeWithFallbackChain(chatId);
|
|
5162
|
+
async function summarizeSession(chatId, clearLogAfter = true) {
|
|
5163
|
+
return summarizeWithFallbackChain(chatId, void 0, void 0, clearLogAfter);
|
|
5037
5164
|
}
|
|
5038
5165
|
async function summarizeAllPending() {
|
|
5039
5166
|
const allIds = getLoggedChatIds();
|
|
@@ -5051,7 +5178,7 @@ async function summarizeAllPending() {
|
|
|
5051
5178
|
await summarizeSession(chatId);
|
|
5052
5179
|
}
|
|
5053
5180
|
}
|
|
5054
|
-
var MIN_PAIRS, USER_MSG_LIMIT, AGENT_MSG_LIMIT, TRANSCRIPT_CAP, SUMMARIZE_TIMEOUT_MS, SUMMARIZE_PROMPT, CONTEXT_BRIDGE_PROMPT;
|
|
5181
|
+
var MIN_PAIRS, USER_MSG_LIMIT, AGENT_MSG_LIMIT, TRANSCRIPT_CAP, SUMMARIZE_TIMEOUT_MS, SUMMARIZE_PROMPT, CONTEXT_BRIDGE_PROMPT, VALID_SIGNAL_TYPES;
|
|
5055
5182
|
var init_summarize = __esm({
|
|
5056
5183
|
"src/memory/summarize.ts"() {
|
|
5057
5184
|
"use strict";
|
|
@@ -5086,7 +5213,15 @@ SUMMARY: <detailed summary>
|
|
|
5086
5213
|
KEY_DETAILS:
|
|
5087
5214
|
- <detail 1>
|
|
5088
5215
|
- <detail 2>
|
|
5089
|
-
TOPICS: <specific-keyword1, specific-keyword2,
|
|
5216
|
+
TOPICS: <specific-keyword1, specific-keyword2, ...>
|
|
5217
|
+
|
|
5218
|
+
SIGNALS (0-5 items, only if the conversation contains user feedback about the assistant's performance):
|
|
5219
|
+
- [correction] brief description of what the user corrected
|
|
5220
|
+
- [preference] brief description of a preference the user expressed
|
|
5221
|
+
- [frustration] brief description of user frustration or dissatisfaction
|
|
5222
|
+
- [praise] brief description of user satisfaction or approval
|
|
5223
|
+
- [error_pattern] brief description of a recurring error the user pointed out
|
|
5224
|
+
If no feedback signals are present, omit this section entirely.`;
|
|
5090
5225
|
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
5226
|
- What the user is actively working on RIGHT NOW
|
|
5092
5227
|
- Exact current state: what's done, what's in progress, what's blocked
|
|
@@ -5095,6 +5230,7 @@ TOPICS: <specific-keyword1, specific-keyword2, ...>`;
|
|
|
5095
5230
|
- Any critical constraints or preferences expressed by the user
|
|
5096
5231
|
|
|
5097
5232
|
Be specific. The new assistant has no prior context. Cover any domain \u2014 personal, research, scheduling, content, technical \u2014 whatever applies to this session.`;
|
|
5233
|
+
VALID_SIGNAL_TYPES = ["correction", "preference", "praise", "frustration", "error_pattern"];
|
|
5098
5234
|
}
|
|
5099
5235
|
});
|
|
5100
5236
|
|
|
@@ -6446,9 +6582,9 @@ function aggregateDailyMetrics(db3, chatId, date) {
|
|
|
6446
6582
|
const signalCounts = db3.prepare(`
|
|
6447
6583
|
SELECT signalType, COUNT(*) as count
|
|
6448
6584
|
FROM feedback_signals
|
|
6449
|
-
WHERE chatId = ? AND date(
|
|
6585
|
+
WHERE chatId = ? AND created_at >= ? AND created_at < date(?, '+1 day')
|
|
6450
6586
|
GROUP BY signalType
|
|
6451
|
-
`).all(chatId, period);
|
|
6587
|
+
`).all(chatId, period, period);
|
|
6452
6588
|
let corrections = 0;
|
|
6453
6589
|
let praises = 0;
|
|
6454
6590
|
let errors = 0;
|
|
@@ -6467,8 +6603,8 @@ function aggregateDailyMetrics(db3, chatId, date) {
|
|
|
6467
6603
|
}
|
|
6468
6604
|
const insightsRow = db3.prepare(`
|
|
6469
6605
|
SELECT COUNT(*) as count FROM insights
|
|
6470
|
-
WHERE chatId = ? AND date(
|
|
6471
|
-
`).get(chatId, period);
|
|
6606
|
+
WHERE chatId = ? AND appliedAt >= ? AND appliedAt < date(?, '+1 day')
|
|
6607
|
+
`).get(chatId, period, period);
|
|
6472
6608
|
upsertGrowthMetric(db3, chatId, period, {
|
|
6473
6609
|
corrections,
|
|
6474
6610
|
praises,
|
|
@@ -6659,6 +6795,7 @@ Rules:
|
|
|
6659
6795
|
- WHY field max 2 sentences
|
|
6660
6796
|
- Only propose changes you are confident about (confidence >= 0.6)
|
|
6661
6797
|
- Never re-propose previously rejected insights
|
|
6798
|
+
- Do not propose more than 3 changes to the same target file in a single analysis (spread across files to avoid drift)
|
|
6662
6799
|
|
|
6663
6800
|
Valid categories:
|
|
6664
6801
|
${categoryList}`);
|
|
@@ -6774,7 +6911,7 @@ function resolveReflectionAdapter(chatId) {
|
|
|
6774
6911
|
if (config2.mode === "pinned" && config2.backend) {
|
|
6775
6912
|
try {
|
|
6776
6913
|
const adapter = getAdapter(config2.backend);
|
|
6777
|
-
const model2 = config2.model ?? adapter.
|
|
6914
|
+
const model2 = config2.model ?? adapter.defaultModel;
|
|
6778
6915
|
return { adapter, model: model2 };
|
|
6779
6916
|
} catch {
|
|
6780
6917
|
return null;
|
|
@@ -6795,11 +6932,11 @@ function resolveReflectionAdapter(chatId) {
|
|
|
6795
6932
|
}
|
|
6796
6933
|
try {
|
|
6797
6934
|
const adapter = getAdapterForChat(chatId);
|
|
6798
|
-
return { adapter, model: adapter.
|
|
6935
|
+
return { adapter, model: adapter.defaultModel };
|
|
6799
6936
|
} catch {
|
|
6800
6937
|
try {
|
|
6801
6938
|
const adapter = getAdapter("claude");
|
|
6802
|
-
return { adapter, model: adapter.
|
|
6939
|
+
return { adapter, model: adapter.defaultModel };
|
|
6803
6940
|
} catch {
|
|
6804
6941
|
return null;
|
|
6805
6942
|
}
|
|
@@ -6816,7 +6953,7 @@ function getTodayConversations(chatId) {
|
|
|
6816
6953
|
const db3 = getDb();
|
|
6817
6954
|
const rows = db3.prepare(`
|
|
6818
6955
|
SELECT role, content FROM message_log
|
|
6819
|
-
WHERE chat_id = ? AND date(
|
|
6956
|
+
WHERE chat_id = ? AND created_at >= date('now') AND created_at < date('now', '+1 day')
|
|
6820
6957
|
ORDER BY created_at ASC
|
|
6821
6958
|
LIMIT 200
|
|
6822
6959
|
`).all(chatId);
|
|
@@ -6835,7 +6972,7 @@ function getTodayConversations(chatId) {
|
|
|
6835
6972
|
}
|
|
6836
6973
|
return lines.join("\n\n");
|
|
6837
6974
|
}
|
|
6838
|
-
async function spawnAnalysis(adapter, model2, prompt) {
|
|
6975
|
+
async function spawnAnalysis(adapter, model2, prompt, timeoutMs = ANALYSIS_TIMEOUT_MS) {
|
|
6839
6976
|
const config2 = adapter.buildSpawnConfig({
|
|
6840
6977
|
prompt,
|
|
6841
6978
|
model: model2,
|
|
@@ -6858,7 +6995,7 @@ async function spawnAnalysis(adapter, model2, prompt) {
|
|
|
6858
6995
|
rl2.close();
|
|
6859
6996
|
proc.kill("SIGTERM");
|
|
6860
6997
|
setTimeout(() => proc.kill("SIGKILL"), 2e3);
|
|
6861
|
-
},
|
|
6998
|
+
}, timeoutMs);
|
|
6862
6999
|
rl2.on("line", (line) => {
|
|
6863
7000
|
if (!line.trim()) return;
|
|
6864
7001
|
let msg;
|
|
@@ -6950,7 +7087,7 @@ async function runAnalysis(chatId, opts = {}) {
|
|
|
6950
7087
|
log(`[reflection] Running analysis via ${adapter.id}:${model2} for chat ${chatId} (${signals.length} signals, force=${force})`);
|
|
6951
7088
|
let rawOutput;
|
|
6952
7089
|
try {
|
|
6953
|
-
rawOutput = await spawnAnalysis(adapter, model2, prompt);
|
|
7090
|
+
rawOutput = await spawnAnalysis(adapter, model2, prompt, opts.timeoutMs);
|
|
6954
7091
|
} catch (err) {
|
|
6955
7092
|
warn(`[reflection] Analysis spawn failed for ${chatId}: ${err instanceof Error ? err.message : String(err)}`);
|
|
6956
7093
|
return [];
|
|
@@ -7043,7 +7180,7 @@ __export(apply_exports, {
|
|
|
7043
7180
|
isTargetAllowed: () => isTargetAllowed,
|
|
7044
7181
|
rollbackInsight: () => rollbackInsight
|
|
7045
7182
|
});
|
|
7046
|
-
import { readFileSync as readFileSync7, writeFileSync as writeFileSync3, existsSync as existsSync12, mkdirSync as mkdirSync3 } from "fs";
|
|
7183
|
+
import { readFileSync as readFileSync7, writeFileSync as writeFileSync3, existsSync as existsSync12, mkdirSync as mkdirSync3, readdirSync as readdirSync6, unlinkSync as unlinkSync4 } from "fs";
|
|
7047
7184
|
import { join as join11, dirname as dirname2 } from "path";
|
|
7048
7185
|
function isTargetAllowed(relativePath) {
|
|
7049
7186
|
if (relativePath.includes("..")) return false;
|
|
@@ -7094,6 +7231,21 @@ function applyDiff(original, diff, action) {
|
|
|
7094
7231
|
warn(`[reflection/apply] Unknown diff action "${action}", returning original`);
|
|
7095
7232
|
return original;
|
|
7096
7233
|
}
|
|
7234
|
+
function pruneBackups(absolutePath) {
|
|
7235
|
+
const dir = dirname2(absolutePath);
|
|
7236
|
+
const baseName = absolutePath.split("/").pop() ?? "";
|
|
7237
|
+
try {
|
|
7238
|
+
const backups = readdirSync6(dir).filter((f) => f.startsWith(baseName + ".bak.")).sort().map((f) => join11(dir, f));
|
|
7239
|
+
while (backups.length > MAX_BACKUPS_PER_FILE) {
|
|
7240
|
+
const oldest = backups.shift();
|
|
7241
|
+
try {
|
|
7242
|
+
unlinkSync4(oldest);
|
|
7243
|
+
} catch {
|
|
7244
|
+
}
|
|
7245
|
+
}
|
|
7246
|
+
} catch {
|
|
7247
|
+
}
|
|
7248
|
+
}
|
|
7097
7249
|
async function applyInsight(insightId) {
|
|
7098
7250
|
const db3 = getDb();
|
|
7099
7251
|
const insight = getInsightById(db3, insightId);
|
|
@@ -7123,11 +7275,13 @@ async function applyInsight(insightId) {
|
|
|
7123
7275
|
}
|
|
7124
7276
|
}
|
|
7125
7277
|
const chatId = insight.chatId ?? "global";
|
|
7126
|
-
const
|
|
7127
|
-
|
|
7278
|
+
const settings = getReflectionSettings(db3, chatId);
|
|
7279
|
+
const perFileCap = settings.perFileCap;
|
|
7280
|
+
const appliedTodayForFile = getAppliedCountTodayForFile(db3, chatId, insight.targetFile);
|
|
7281
|
+
if (appliedTodayForFile >= perFileCap) {
|
|
7128
7282
|
return {
|
|
7129
7283
|
success: false,
|
|
7130
|
-
message: `
|
|
7284
|
+
message: `Reflection cap reached for ${insight.targetFile} (${appliedTodayForFile}/${perFileCap} today). Try again tomorrow.`
|
|
7131
7285
|
};
|
|
7132
7286
|
}
|
|
7133
7287
|
let original = "";
|
|
@@ -7145,6 +7299,7 @@ async function applyInsight(insightId) {
|
|
|
7145
7299
|
}
|
|
7146
7300
|
if (original) {
|
|
7147
7301
|
writeFileSync3(backupPath, original, "utf-8");
|
|
7302
|
+
pruneBackups(absolutePath);
|
|
7148
7303
|
}
|
|
7149
7304
|
const newContent = applyDiff(original, insight.proposedDiff, insight.proposedAction);
|
|
7150
7305
|
writeFileSync3(absolutePath, newContent, "utf-8");
|
|
@@ -7265,7 +7420,7 @@ function computeLineDrift(baseline, absolutePath) {
|
|
|
7265
7420
|
const changed = totalBaseline - unchanged;
|
|
7266
7421
|
return changed / totalBaseline;
|
|
7267
7422
|
}
|
|
7268
|
-
var ALLOWED_TARGETS, ALLOWED_PREFIXES, SOUL_LINE_CAP,
|
|
7423
|
+
var ALLOWED_TARGETS, ALLOWED_PREFIXES, SOUL_LINE_CAP, MAX_BACKUPS_PER_FILE;
|
|
7269
7424
|
var init_apply = __esm({
|
|
7270
7425
|
"src/reflection/apply.ts"() {
|
|
7271
7426
|
"use strict";
|
|
@@ -7284,7 +7439,7 @@ var init_apply = __esm({
|
|
|
7284
7439
|
"workspace/skills/"
|
|
7285
7440
|
];
|
|
7286
7441
|
SOUL_LINE_CAP = 100;
|
|
7287
|
-
|
|
7442
|
+
MAX_BACKUPS_PER_FILE = 3;
|
|
7288
7443
|
}
|
|
7289
7444
|
});
|
|
7290
7445
|
|
|
@@ -7651,8 +7806,7 @@ function startDashboard() {
|
|
|
7651
7806
|
const body = JSON.parse(await readBody(req));
|
|
7652
7807
|
const { setBackend: setBackend2, clearSession: clearSession2, clearModel: clearModel2, clearThinkingLevel: clearThinkingLevel2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
|
|
7653
7808
|
const { summarizeSession: summarizeSession2 } = await Promise.resolve().then(() => (init_summarize(), summarize_exports));
|
|
7654
|
-
summarizeSession2(body.chatId)
|
|
7655
|
-
});
|
|
7809
|
+
await summarizeSession2(body.chatId);
|
|
7656
7810
|
clearSession2(body.chatId);
|
|
7657
7811
|
clearModel2(body.chatId);
|
|
7658
7812
|
clearThinkingLevel2(body.chatId);
|
|
@@ -8103,6 +8257,21 @@ data: ${JSON.stringify(data)}
|
|
|
8103
8257
|
return jsonResponse(res, { error: errorMessage(err) }, 400);
|
|
8104
8258
|
}
|
|
8105
8259
|
}
|
|
8260
|
+
if (url.pathname === "/api/evolve/settings" && req.method === "POST") {
|
|
8261
|
+
try {
|
|
8262
|
+
const body = JSON.parse(await readBody(req));
|
|
8263
|
+
const { setReflectionSettings: setReflectionSettings2, getReflectionSettings: getReflectionSettings2 } = await Promise.resolve().then(() => (init_store4(), store_exports4));
|
|
8264
|
+
const chatId = body.chatId || (process.env.ALLOWED_CHAT_ID ?? "").split(",")[0]?.trim() || "default";
|
|
8265
|
+
const updates = {};
|
|
8266
|
+
if (body.perFileCap !== void 0) updates.perFileCap = body.perFileCap;
|
|
8267
|
+
if (body.backupRetentionDays !== void 0) updates.backupRetentionDays = body.backupRetentionDays;
|
|
8268
|
+
setReflectionSettings2(getDb(), chatId, updates);
|
|
8269
|
+
const current = getReflectionSettings2(getDb(), chatId);
|
|
8270
|
+
return jsonResponse(res, { success: true, ...current });
|
|
8271
|
+
} catch (err) {
|
|
8272
|
+
return jsonResponse(res, { error: errorMessage(err) }, 400);
|
|
8273
|
+
}
|
|
8274
|
+
}
|
|
8106
8275
|
if (url.pathname === "/" || url.pathname === "/index.html") {
|
|
8107
8276
|
return htmlResponse(res, DASHBOARD_HTML.replace("__TOKEN__", DASHBOARD_TOKEN));
|
|
8108
8277
|
}
|
|
@@ -8577,7 +8746,8 @@ __export(agent_exports, {
|
|
|
8577
8746
|
getInFlightMessage: () => getInFlightMessage,
|
|
8578
8747
|
isChatBusy: () => isChatBusy,
|
|
8579
8748
|
isSyntheticChatId: () => isSyntheticChatId,
|
|
8580
|
-
stopAgent: () => stopAgent
|
|
8749
|
+
stopAgent: () => stopAgent,
|
|
8750
|
+
stopAllActiveAgents: () => stopAllActiveAgents
|
|
8581
8751
|
});
|
|
8582
8752
|
import { spawn as spawn5 } from "child_process";
|
|
8583
8753
|
import { createInterface as createInterface4 } from "readline";
|
|
@@ -8605,13 +8775,22 @@ function withChatLock(chatId, fn) {
|
|
|
8605
8775
|
}
|
|
8606
8776
|
const next = prev.then(fn, fn);
|
|
8607
8777
|
chatLocks.set(chatId, next.then(() => {
|
|
8778
|
+
chatLocks.delete(chatId);
|
|
8608
8779
|
}, () => {
|
|
8780
|
+
chatLocks.delete(chatId);
|
|
8609
8781
|
}));
|
|
8610
8782
|
return next;
|
|
8611
8783
|
}
|
|
8612
8784
|
function isChatBusy(chatId) {
|
|
8613
8785
|
return activeChats.has(chatId);
|
|
8614
8786
|
}
|
|
8787
|
+
function stopAllActiveAgents() {
|
|
8788
|
+
for (const [, state] of activeChats) {
|
|
8789
|
+
if (state.process) {
|
|
8790
|
+
killProcessGroup(state.process);
|
|
8791
|
+
}
|
|
8792
|
+
}
|
|
8793
|
+
}
|
|
8615
8794
|
function stopAgent(chatId) {
|
|
8616
8795
|
const state = activeChats.get(chatId);
|
|
8617
8796
|
if (!state) return false;
|
|
@@ -8867,7 +9046,7 @@ async function askAgentImpl(chatId, userMessage, opts) {
|
|
|
8867
9046
|
const effectiveAgentMode = optsAgentMode ?? getAgentMode(settingsChat);
|
|
8868
9047
|
const sideQuestCtx = settingsSourceChatId ? { parentChatId: settingsSourceChatId, actualChatId: chatId } : void 0;
|
|
8869
9048
|
const fullPrompt = await assembleBootstrapPrompt(userMessage, tier, settingsChat, mode, responseStyle, effectiveAgentMode, sideQuestCtx);
|
|
8870
|
-
const existingSessionId = getSessionId(settingsChat);
|
|
9049
|
+
const existingSessionId = settingsSourceChatId ? null : getSessionId(settingsChat);
|
|
8871
9050
|
const allowedTools = getEnabledTools(settingsChat);
|
|
8872
9051
|
const mcpConfigPath = tier !== "slim" && effectiveAgentMode !== "native" && MCP_CONFIG_FLAG[adapter.id] ? getMcpConfigPath(chatId) : null;
|
|
8873
9052
|
const baseConfig = adapter.buildSpawnConfig({
|
|
@@ -8925,17 +9104,19 @@ async function askAgentImpl(chatId, userMessage, opts) {
|
|
|
8925
9104
|
}
|
|
8926
9105
|
}
|
|
8927
9106
|
} catch (spawnErr) {
|
|
8928
|
-
|
|
8929
|
-
|
|
8930
|
-
|
|
8931
|
-
|
|
8932
|
-
|
|
8933
|
-
|
|
8934
|
-
|
|
8935
|
-
|
|
8936
|
-
|
|
9107
|
+
if (!isSyntheticChatId(chatId)) {
|
|
9108
|
+
try {
|
|
9109
|
+
const errMsg = spawnErr instanceof Error ? spawnErr.message : String(spawnErr);
|
|
9110
|
+
const { logErrorSignal: logErrorSignal2 } = await Promise.resolve().then(() => (init_detect(), detect_exports));
|
|
9111
|
+
if (/timeout/i.test(errMsg)) {
|
|
9112
|
+
logErrorSignal2(chatId, "timeout", `Backend timed out`, { backendId: adapter.id, model: resolvedModel });
|
|
9113
|
+
} else if (/repetition.?loop/i.test(errMsg)) {
|
|
9114
|
+
logErrorSignal2(chatId, "repetition_loop", `Repetition loop detected`, { backendId: adapter.id, model: resolvedModel });
|
|
9115
|
+
} else {
|
|
9116
|
+
logErrorSignal2(chatId, "spawn_error", errMsg.slice(0, 300), { backendId: adapter.id, model: resolvedModel });
|
|
9117
|
+
}
|
|
9118
|
+
} catch {
|
|
8937
9119
|
}
|
|
8938
|
-
} catch {
|
|
8939
9120
|
}
|
|
8940
9121
|
throw spawnErr;
|
|
8941
9122
|
} finally {
|
|
@@ -9070,9 +9251,10 @@ ${responseText.slice(0, 500)}`);
|
|
|
9070
9251
|
return true;
|
|
9071
9252
|
}
|
|
9072
9253
|
try {
|
|
9254
|
+
const text = responseText.replace(/\s*\[REACT:[^\]]*\]\s*/g, " ").trim();
|
|
9073
9255
|
if (job.deliveryMode === "webhook") {
|
|
9074
|
-
await deliverWebhook(job,
|
|
9075
|
-
appendToLog(job.chatId, `[cron:#${job.id}] ${job.description}`,
|
|
9256
|
+
await deliverWebhook(job, text);
|
|
9257
|
+
appendToLog(job.chatId, `[cron:#${job.id}] ${job.description}`, text, job.backend ?? "claude", job.model, null);
|
|
9076
9258
|
return true;
|
|
9077
9259
|
}
|
|
9078
9260
|
const channelName = job.channel ?? "telegram";
|
|
@@ -9082,7 +9264,7 @@ ${responseText.slice(0, 500)}`);
|
|
|
9082
9264
|
return false;
|
|
9083
9265
|
}
|
|
9084
9266
|
const targetChatId = job.target ?? job.chatId;
|
|
9085
|
-
const cleanText = await processFileSends(targetChatId, channel,
|
|
9267
|
+
const cleanText = await processFileSends(targetChatId, channel, text);
|
|
9086
9268
|
if (!cleanText) return true;
|
|
9087
9269
|
if (channelName === "telegram") {
|
|
9088
9270
|
const parsed = parseTelegramTarget(targetChatId);
|
|
@@ -9313,6 +9495,173 @@ var init_health2 = __esm({
|
|
|
9313
9495
|
}
|
|
9314
9496
|
});
|
|
9315
9497
|
|
|
9498
|
+
// src/reflection/propose.ts
|
|
9499
|
+
var propose_exports = {};
|
|
9500
|
+
__export(propose_exports, {
|
|
9501
|
+
buildEvolveMenuKeyboard: () => buildEvolveMenuKeyboard,
|
|
9502
|
+
buildEvolveOnboardingKeyboard: () => buildEvolveOnboardingKeyboard,
|
|
9503
|
+
buildModelKeyboard: () => buildModelKeyboard,
|
|
9504
|
+
buildProposalKeyboard: () => buildProposalKeyboard,
|
|
9505
|
+
buildReviewCompleteMessage: () => buildReviewCompleteMessage,
|
|
9506
|
+
buildUndoKeyboard: () => buildUndoKeyboard,
|
|
9507
|
+
formatDiffCodeBlock: () => formatDiffCodeBlock,
|
|
9508
|
+
formatGrowthReport: () => formatGrowthReport,
|
|
9509
|
+
formatNightlySummary: () => formatNightlySummary,
|
|
9510
|
+
formatProposalCard: () => formatProposalCard,
|
|
9511
|
+
formatProposalCardWithProgress: () => formatProposalCardWithProgress
|
|
9512
|
+
});
|
|
9513
|
+
function pct(ratio) {
|
|
9514
|
+
return `${Math.round(ratio * 100)}%`;
|
|
9515
|
+
}
|
|
9516
|
+
function formatProposalCard(params) {
|
|
9517
|
+
const { category, insight, why, targetFile, confidence, proposedAction, proposedDiff } = params;
|
|
9518
|
+
const header2 = `[${category}] ${insight}`;
|
|
9519
|
+
const whyLine = why ? `Why: ${why}` : null;
|
|
9520
|
+
const confidencePct = pct(confidence);
|
|
9521
|
+
const showTarget = category !== "codebase" && targetFile && targetFile !== "codebase";
|
|
9522
|
+
const targetLine = showTarget ? `Target: ${targetFile} | Confidence: ${confidencePct}` : `Confidence: ${confidencePct}`;
|
|
9523
|
+
const lines = [header2, ""];
|
|
9524
|
+
if (whyLine) lines.push(whyLine);
|
|
9525
|
+
lines.push(targetLine);
|
|
9526
|
+
if (proposedDiff) {
|
|
9527
|
+
const actionLabel = proposedAction ? ` (${proposedAction})` : "";
|
|
9528
|
+
lines.push("", `Proposed change${actionLabel}:`, "```diff", proposedDiff, "```");
|
|
9529
|
+
}
|
|
9530
|
+
return lines.join("\n");
|
|
9531
|
+
}
|
|
9532
|
+
function formatProposalCardWithProgress(params, index, total) {
|
|
9533
|
+
const progress = `\u{1F4CB} Proposal ${index + 1} of ${total}`;
|
|
9534
|
+
const separator = "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500";
|
|
9535
|
+
const card = formatProposalCard(params);
|
|
9536
|
+
return `${progress}
|
|
9537
|
+
${separator}
|
|
9538
|
+
${card}`;
|
|
9539
|
+
}
|
|
9540
|
+
function buildReviewCompleteMessage(results) {
|
|
9541
|
+
const values = Object.values(results);
|
|
9542
|
+
const applied = values.filter((v) => v === "applied").length;
|
|
9543
|
+
const rejected = values.filter((v) => v === "rejected").length;
|
|
9544
|
+
const skipped = values.filter((v) => v === "skipped").length;
|
|
9545
|
+
const total = values.length;
|
|
9546
|
+
const parts = [];
|
|
9547
|
+
if (applied > 0) parts.push(`${applied} applied`);
|
|
9548
|
+
if (rejected > 0) parts.push(`${rejected} rejected`);
|
|
9549
|
+
if (skipped > 0) parts.push(`${skipped} skipped`);
|
|
9550
|
+
return `\u2705 Review complete \u2014 ${total} proposal${total === 1 ? "" : "s"}: ${parts.join(", ")}
|
|
9551
|
+
|
|
9552
|
+
Skipped proposals will appear in your next review.`;
|
|
9553
|
+
}
|
|
9554
|
+
function formatNightlySummary(insights) {
|
|
9555
|
+
const count = insights.length;
|
|
9556
|
+
const header2 = `Nightly Reflection \u2014 ${count} proposal${count === 1 ? "" : "s"} ready`;
|
|
9557
|
+
const list = insights.map((ins, i) => `${i + 1}. [${ins.category}] ${ins.insight}`).join("\n");
|
|
9558
|
+
return `${header2}
|
|
9559
|
+
|
|
9560
|
+
${list}
|
|
9561
|
+
|
|
9562
|
+
Review with /evolve`;
|
|
9563
|
+
}
|
|
9564
|
+
function formatGrowthReport(metrics, modelPerformance) {
|
|
9565
|
+
const { correctionsBefore, correctionsAfter, praiseRatio, insightsApplied, pendingCount, topInsight } = metrics;
|
|
9566
|
+
const reduction = correctionsBefore > 0 ? Math.round((correctionsBefore - correctionsAfter) / correctionsBefore * 100) : 0;
|
|
9567
|
+
const lines = [];
|
|
9568
|
+
lines.push("Growth Report");
|
|
9569
|
+
lines.push("");
|
|
9570
|
+
lines.push(`Corrections: ${correctionsBefore.toFixed(1)} -> ${correctionsAfter.toFixed(1)} (${reduction}% reduction)`);
|
|
9571
|
+
lines.push(`Praise ratio: ${praiseRatio.toFixed(1)}x`);
|
|
9572
|
+
lines.push(`Insights applied: ${insightsApplied} | Pending: ${pendingCount}`);
|
|
9573
|
+
if (topInsight) {
|
|
9574
|
+
lines.push(`Top insight: "${topInsight.insight}" (${pct(topInsight.effectiveness)} effective)`);
|
|
9575
|
+
}
|
|
9576
|
+
if (modelPerformance.length > 0) {
|
|
9577
|
+
lines.push("");
|
|
9578
|
+
lines.push("Model Performance:");
|
|
9579
|
+
for (const row of modelPerformance) {
|
|
9580
|
+
const total = row.praises + row.corrections + row.errors;
|
|
9581
|
+
const scoreLabel = total > 0 ? ` (${row.praises}P / ${row.corrections}C / ${row.errors}E)` : "";
|
|
9582
|
+
lines.push(` ${row.backend} ${row.model} [${row.thinking}]${scoreLabel}`);
|
|
9583
|
+
}
|
|
9584
|
+
}
|
|
9585
|
+
return lines.join("\n");
|
|
9586
|
+
}
|
|
9587
|
+
function formatDiffCodeBlock(diff) {
|
|
9588
|
+
return `\`\`\`diff
|
|
9589
|
+
${diff}
|
|
9590
|
+
\`\`\``;
|
|
9591
|
+
}
|
|
9592
|
+
function buildProposalKeyboard(insightId, category) {
|
|
9593
|
+
if (category === "codebase") {
|
|
9594
|
+
return [
|
|
9595
|
+
[
|
|
9596
|
+
{ label: "Show Diff", data: `evolve:diff:${insightId}`, style: "primary" },
|
|
9597
|
+
{ label: "Discuss", data: `evolve:discuss:${insightId}` }
|
|
9598
|
+
],
|
|
9599
|
+
[
|
|
9600
|
+
{ label: "Skip \u23ED", data: `evolve:skip:${insightId}` },
|
|
9601
|
+
{ label: "Reject", data: `evolve:reject:${insightId}`, style: "danger" }
|
|
9602
|
+
]
|
|
9603
|
+
];
|
|
9604
|
+
}
|
|
9605
|
+
return [
|
|
9606
|
+
[
|
|
9607
|
+
{ label: "Apply", data: `evolve:apply:${insightId}`, style: "success" },
|
|
9608
|
+
{ label: "Discuss", data: `evolve:discuss:${insightId}` }
|
|
9609
|
+
],
|
|
9610
|
+
[
|
|
9611
|
+
{ label: "Skip \u23ED", data: `evolve:skip:${insightId}` },
|
|
9612
|
+
{ label: "Reject", data: `evolve:reject:${insightId}`, style: "danger" }
|
|
9613
|
+
]
|
|
9614
|
+
];
|
|
9615
|
+
}
|
|
9616
|
+
function buildEvolveOnboardingKeyboard() {
|
|
9617
|
+
return [
|
|
9618
|
+
[{ label: "\u2705 Enable Self-Learning", data: "evolve:toggle", style: "success" }],
|
|
9619
|
+
[{ label: "One-Time Analysis", data: "evolve:analyze" }]
|
|
9620
|
+
];
|
|
9621
|
+
}
|
|
9622
|
+
function buildEvolveMenuKeyboard(ctx) {
|
|
9623
|
+
const reviewLabel = ctx.pendingProposals > 0 ? `Review (${ctx.pendingProposals})` : "Review";
|
|
9624
|
+
return [
|
|
9625
|
+
[
|
|
9626
|
+
{ label: "\u{1F50D} Analyze Now", data: "evolve:analyze", style: "success" },
|
|
9627
|
+
ctx.pendingProposals > 0 ? { label: reviewLabel, data: "evolve:review", style: "primary" } : { label: reviewLabel, data: "evolve:review" }
|
|
9628
|
+
],
|
|
9629
|
+
[
|
|
9630
|
+
{ label: "Stats", data: "evolve:stats" },
|
|
9631
|
+
{ label: "History", data: "evolve:history" }
|
|
9632
|
+
],
|
|
9633
|
+
[
|
|
9634
|
+
{ label: "Model", data: "evolve:model" },
|
|
9635
|
+
{ label: "Undo", data: "evolve:undo", style: "danger" },
|
|
9636
|
+
{ label: "\u23F8 Disable", data: "evolve:toggle", style: "danger" }
|
|
9637
|
+
]
|
|
9638
|
+
];
|
|
9639
|
+
}
|
|
9640
|
+
function buildUndoKeyboard(insights) {
|
|
9641
|
+
return insights.map((ins) => [
|
|
9642
|
+
{ label: `Undo #${ins.id}: ${ins.insight}`, data: `evolve:undo:${ins.id}`, style: "danger" }
|
|
9643
|
+
]);
|
|
9644
|
+
}
|
|
9645
|
+
function buildModelKeyboard(currentMode) {
|
|
9646
|
+
const modes = [
|
|
9647
|
+
{ label: "Auto", mode: "auto" },
|
|
9648
|
+
{ label: "Pinned", mode: "pinned" },
|
|
9649
|
+
{ label: "Cheap", mode: "cheap" }
|
|
9650
|
+
];
|
|
9651
|
+
return [
|
|
9652
|
+
modes.map(({ label: label2, mode }) => ({
|
|
9653
|
+
label: mode === currentMode ? `[${label2}]` : label2,
|
|
9654
|
+
data: `evolve:model:${mode}`,
|
|
9655
|
+
...mode === currentMode ? { style: "primary" } : {}
|
|
9656
|
+
}))
|
|
9657
|
+
];
|
|
9658
|
+
}
|
|
9659
|
+
var init_propose = __esm({
|
|
9660
|
+
"src/reflection/propose.ts"() {
|
|
9661
|
+
"use strict";
|
|
9662
|
+
}
|
|
9663
|
+
});
|
|
9664
|
+
|
|
9316
9665
|
// src/scheduler/cron.ts
|
|
9317
9666
|
var cron_exports = {};
|
|
9318
9667
|
__export(cron_exports, {
|
|
@@ -9533,6 +9882,17 @@ async function executeJob(job) {
|
|
|
9533
9882
|
async function runWithRetry(job, model2, runId, t0) {
|
|
9534
9883
|
let lastError;
|
|
9535
9884
|
const chatId = job.sessionType === "isolated" ? `cron:${job.id}:${runId}` : job.chatId;
|
|
9885
|
+
if (job.jobType === "reflection") {
|
|
9886
|
+
const { runAnalysis: runAnalysis2 } = await Promise.resolve().then(() => (init_analyze(), analyze_exports));
|
|
9887
|
+
const { formatNightlySummary: formatNightlySummary2 } = await Promise.resolve().then(() => (init_propose(), propose_exports));
|
|
9888
|
+
const timeoutMs2 = job.timeout ? job.timeout * 1e3 : void 0;
|
|
9889
|
+
const insights = await runAnalysis2(job.chatId, { force: true, timeoutMs: timeoutMs2 });
|
|
9890
|
+
if (insights.length === 0) {
|
|
9891
|
+
return { text: "Nightly reflection: no new insights from recent interactions." };
|
|
9892
|
+
}
|
|
9893
|
+
const items = insights.map((ins, i) => ({ id: i + 1, ...ins }));
|
|
9894
|
+
return { text: formatNightlySummary2(items) };
|
|
9895
|
+
}
|
|
9536
9896
|
if (job.thinking && job.thinking !== "auto") {
|
|
9537
9897
|
setThinkingLevel(chatId, job.thinking);
|
|
9538
9898
|
}
|
|
@@ -9769,7 +10129,7 @@ var init_wrap_backend = __esm({
|
|
|
9769
10129
|
});
|
|
9770
10130
|
|
|
9771
10131
|
// src/agents/runners/config-loader.ts
|
|
9772
|
-
import { readFileSync as readFileSync8, readdirSync as
|
|
10132
|
+
import { readFileSync as readFileSync8, readdirSync as readdirSync7, existsSync as existsSync14, mkdirSync as mkdirSync5, watchFile, unwatchFile } from "fs";
|
|
9773
10133
|
import { join as join13 } from "path";
|
|
9774
10134
|
import { execFileSync } from "child_process";
|
|
9775
10135
|
function resolveExecutable(config2) {
|
|
@@ -9923,7 +10283,7 @@ function loadAllRunnerConfigs() {
|
|
|
9923
10283
|
mkdirSync5(RUNNERS_PATH, { recursive: true });
|
|
9924
10284
|
return [];
|
|
9925
10285
|
}
|
|
9926
|
-
const files =
|
|
10286
|
+
const files = readdirSync7(RUNNERS_PATH).filter((f) => f.endsWith(".json"));
|
|
9927
10287
|
const configs = [];
|
|
9928
10288
|
for (const file of files) {
|
|
9929
10289
|
const config2 = loadRunnerConfig(join13(RUNNERS_PATH, file));
|
|
@@ -9954,7 +10314,7 @@ function watchRunnerConfigs(onChange) {
|
|
|
9954
10314
|
watchedFiles.delete(prev);
|
|
9955
10315
|
}
|
|
9956
10316
|
}
|
|
9957
|
-
const files =
|
|
10317
|
+
const files = readdirSync7(RUNNERS_PATH).filter((f) => f.endsWith(".json"));
|
|
9958
10318
|
for (const file of files) {
|
|
9959
10319
|
const fullPath = join13(RUNNERS_PATH, file);
|
|
9960
10320
|
if (watchedFiles.has(fullPath)) continue;
|
|
@@ -12786,143 +13146,6 @@ var init_pagination = __esm({
|
|
|
12786
13146
|
}
|
|
12787
13147
|
});
|
|
12788
13148
|
|
|
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
13149
|
// src/router.ts
|
|
12927
13150
|
import { readFile as readFile5, writeFile as writeFile3, unlink as unlink2, mkdir as mkdir2, readdir as readdir3, stat } from "fs/promises";
|
|
12928
13151
|
import { existsSync as existsSync19 } from "fs";
|
|
@@ -13235,6 +13458,28 @@ function stopAllSideQuests(chatId) {
|
|
|
13235
13458
|
}
|
|
13236
13459
|
}
|
|
13237
13460
|
}
|
|
13461
|
+
async function sendCurrentProposal(chatId, channel) {
|
|
13462
|
+
const { getReviewSession: getReviewSession2, getInsightById: getInsightById2, deleteReviewSession: deleteReviewSession2 } = await Promise.resolve().then(() => (init_store4(), store_exports4));
|
|
13463
|
+
const { formatProposalCardWithProgress: formatProposalCardWithProgress2, buildProposalKeyboard: buildProposalKeyboard2, buildReviewCompleteMessage: buildReviewCompleteMessage2 } = await Promise.resolve().then(() => (init_propose(), propose_exports));
|
|
13464
|
+
const session2 = getReviewSession2(getDb(), chatId);
|
|
13465
|
+
if (!session2) return;
|
|
13466
|
+
if (session2.currentIndex >= session2.insightIds.length) {
|
|
13467
|
+
const summary = buildReviewCompleteMessage2(session2.results);
|
|
13468
|
+
deleteReviewSession2(getDb(), chatId);
|
|
13469
|
+
await channel.sendText(chatId, summary, { parseMode: "plain" });
|
|
13470
|
+
return;
|
|
13471
|
+
}
|
|
13472
|
+
const insightId = session2.insightIds[session2.currentIndex];
|
|
13473
|
+
const insight = getInsightById2(getDb(), insightId);
|
|
13474
|
+
if (!insight || insight.status !== "pending") {
|
|
13475
|
+
const { advanceReviewSession: advanceReviewSession2 } = await Promise.resolve().then(() => (init_store4(), store_exports4));
|
|
13476
|
+
advanceReviewSession2(getDb(), chatId, insightId, "skipped");
|
|
13477
|
+
return sendCurrentProposal(chatId, channel);
|
|
13478
|
+
}
|
|
13479
|
+
const card = formatProposalCardWithProgress2(insight, session2.currentIndex, session2.insightIds.length);
|
|
13480
|
+
const kb = buildProposalKeyboard2(insight.id, insight.category);
|
|
13481
|
+
await channel.sendKeyboard(chatId, card, kb);
|
|
13482
|
+
}
|
|
13238
13483
|
async function handleResponseExhaustion(responseText, chatId, msg, channel) {
|
|
13239
13484
|
const raw = responseText.replace(/\n\n🧠 \[.+$/, "").trim();
|
|
13240
13485
|
if (raw.length > 300 || !isExhaustedMessage(raw)) return false;
|
|
@@ -14838,32 +15083,43 @@ Message: "${testMsg}"`, { parseMode: "plain" });
|
|
|
14838
15083
|
break;
|
|
14839
15084
|
}
|
|
14840
15085
|
case "reflect": {
|
|
14841
|
-
const
|
|
14842
|
-
|
|
14843
|
-
|
|
15086
|
+
const lastReflect = lastReflectTime.get(chatId);
|
|
15087
|
+
if (lastReflect && Date.now() - lastReflect < REFLECT_COOLDOWN_MS) {
|
|
15088
|
+
const remaining = Math.ceil((REFLECT_COOLDOWN_MS - (Date.now() - lastReflect)) / 1e3);
|
|
15089
|
+
await channel.sendText(chatId, `Please wait ${remaining}s before reflecting again.`, { parseMode: "plain" });
|
|
15090
|
+
break;
|
|
15091
|
+
}
|
|
15092
|
+
const { getMessagePairCount: getMessagePairCount2 } = await Promise.resolve().then(() => (init_session_log(), session_log_exports));
|
|
15093
|
+
const pairCount = getMessagePairCount2(chatId);
|
|
15094
|
+
if (pairCount < 2) {
|
|
15095
|
+
await channel.sendText(chatId, "Need more conversation to analyze \u2014 at least 2 message exchanges.", { parseMode: "plain" });
|
|
15096
|
+
break;
|
|
15097
|
+
}
|
|
15098
|
+
lastReflectTime.set(chatId, Date.now());
|
|
15099
|
+
await channel.sendText(chatId, "Step 1/3: Summarizing conversation...", { parseMode: "plain" });
|
|
15100
|
+
try {
|
|
15101
|
+
const { summarizeSession: summarizeSession2 } = await Promise.resolve().then(() => (init_summarize(), summarize_exports));
|
|
15102
|
+
await summarizeSession2(chatId, false);
|
|
15103
|
+
} catch (e) {
|
|
15104
|
+
await channel.sendText(chatId, `Summarization failed: ${e}`, { parseMode: "plain" });
|
|
15105
|
+
break;
|
|
15106
|
+
}
|
|
15107
|
+
await channel.sendText(chatId, "Step 2/3: Analyzing for insights...", { parseMode: "plain" });
|
|
14844
15108
|
try {
|
|
15109
|
+
const { runAnalysis: runAnalysis2 } = await Promise.resolve().then(() => (init_analyze(), analyze_exports));
|
|
14845
15110
|
const insights = await runAnalysis2(chatId, { force: true });
|
|
14846
15111
|
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
|
-
}
|
|
15112
|
+
await channel.sendText(chatId, "Step 3/3: No actionable improvements found in this session.", { parseMode: "plain" });
|
|
14861
15113
|
} else {
|
|
14862
|
-
|
|
14863
|
-
|
|
15114
|
+
await channel.sendText(chatId, `Step 3/3: Found ${insights.length} proposal(s). Let's review them one by one.`, { parseMode: "plain" });
|
|
15115
|
+
const { getPendingInsights: getPendingInsights2, createReviewSession: createReviewSession2 } = await Promise.resolve().then(() => (init_store4(), store_exports4));
|
|
15116
|
+
const pending = getPendingInsights2(getDb(), chatId);
|
|
15117
|
+
const insightIds = pending.slice(0, 5).map((p) => p.id);
|
|
15118
|
+
createReviewSession2(getDb(), chatId, insightIds);
|
|
15119
|
+
await sendCurrentProposal(chatId, channel);
|
|
14864
15120
|
}
|
|
14865
15121
|
} catch (e) {
|
|
14866
|
-
await channel.sendText(chatId, `Analysis failed: ${e}
|
|
15122
|
+
await channel.sendText(chatId, `Analysis failed: ${e}. You can review any pending proposals with /evolve.`, { parseMode: "plain" });
|
|
14867
15123
|
}
|
|
14868
15124
|
break;
|
|
14869
15125
|
}
|
|
@@ -15317,7 +15573,8 @@ async function handleSideQuest(parentChatId, msg, channel) {
|
|
|
15317
15573
|
backendId: adapter.id,
|
|
15318
15574
|
model: model2 ?? adapter.defaultModel
|
|
15319
15575
|
});
|
|
15320
|
-
} catch {
|
|
15576
|
+
} catch (e) {
|
|
15577
|
+
log(`[reflection] Side quest signal detection error: ${e}`);
|
|
15321
15578
|
}
|
|
15322
15579
|
} catch (err) {
|
|
15323
15580
|
typingActive = false;
|
|
@@ -16245,9 +16502,12 @@ ${PERM_MODES[chosen]}`,
|
|
|
16245
16502
|
const pending2 = pendingInterrupts.get(targetChatId);
|
|
16246
16503
|
if (pending2) {
|
|
16247
16504
|
pendingInterrupts.delete(targetChatId);
|
|
16248
|
-
|
|
16249
|
-
|
|
16250
|
-
|
|
16505
|
+
await channel.sendText(chatId, "\u{1F5FA} Launching side quest\u2026", { parseMode: "plain" });
|
|
16506
|
+
handleSideQuest(targetChatId, pending2.msg, pending2.channel).catch((err) => {
|
|
16507
|
+
error(`[router] Side quest error for ${targetChatId}:`, err);
|
|
16508
|
+
channel.sendText(targetChatId, `\u{1F5FA} Side quest failed: ${err.message}`, { parseMode: "plain" }).catch(() => {
|
|
16509
|
+
});
|
|
16510
|
+
});
|
|
16251
16511
|
} else {
|
|
16252
16512
|
await channel.sendText(chatId, "Main task finished \u2014 your message was already processed.", { parseMode: "plain" });
|
|
16253
16513
|
}
|
|
@@ -16603,21 +16863,16 @@ Result: ${task.result.slice(0, 500)}` : ""
|
|
|
16603
16863
|
break;
|
|
16604
16864
|
}
|
|
16605
16865
|
case "review": {
|
|
16606
|
-
const { getPendingInsights: getPendingInsights2 } = await Promise.resolve().then(() => (init_store4(), store_exports4));
|
|
16607
|
-
const { formatProposalCard: formatProposalCard2, buildProposalKeyboard: buildProposalKeyboard2 } = await Promise.resolve().then(() => (init_propose(), propose_exports));
|
|
16866
|
+
const { getPendingInsights: getPendingInsights2, createReviewSession: createReviewSession2 } = await Promise.resolve().then(() => (init_store4(), store_exports4));
|
|
16608
16867
|
const { getDb: getDb2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
|
|
16609
16868
|
const pending = getPendingInsights2(getDb2(), chatId);
|
|
16610
16869
|
if (pending.length === 0) {
|
|
16611
16870
|
await channel.sendText(chatId, "No pending proposals.", { parseMode: "plain" });
|
|
16612
16871
|
} else {
|
|
16613
|
-
|
|
16614
|
-
|
|
16615
|
-
|
|
16616
|
-
|
|
16617
|
-
}
|
|
16618
|
-
if (pending.length > 5) {
|
|
16619
|
-
await channel.sendText(chatId, `${pending.length - 5} more proposals. Run /evolve again to see next batch.`, { parseMode: "plain" });
|
|
16620
|
-
}
|
|
16872
|
+
const insightIds = pending.slice(0, 5).map((p) => p.id);
|
|
16873
|
+
createReviewSession2(getDb2(), chatId, insightIds);
|
|
16874
|
+
await channel.sendText(chatId, `${pending.length} proposal(s) ready. Let's review them one by one.`, { parseMode: "plain" });
|
|
16875
|
+
await sendCurrentProposal(chatId, channel);
|
|
16621
16876
|
}
|
|
16622
16877
|
break;
|
|
16623
16878
|
}
|
|
@@ -16635,10 +16890,16 @@ Result: ${task.result.slice(0, 500)}` : ""
|
|
|
16635
16890
|
const { applyInsight: applyInsight2 } = await Promise.resolve().then(() => (init_apply(), apply_exports));
|
|
16636
16891
|
const result = await applyInsight2(parseInt(idStr, 10));
|
|
16637
16892
|
await channel.sendText(chatId, result.message, { parseMode: "plain" });
|
|
16893
|
+
const { advanceReviewSession: arAdvance } = await Promise.resolve().then(() => (init_store4(), store_exports4));
|
|
16894
|
+
arAdvance(getDb(), chatId, parseInt(idStr, 10), "applied");
|
|
16895
|
+
await sendCurrentProposal(chatId, channel);
|
|
16638
16896
|
break;
|
|
16639
16897
|
}
|
|
16640
16898
|
case "skip": {
|
|
16641
16899
|
await channel.sendText(chatId, "Skipped \u2014 will show again next review.", { parseMode: "plain" });
|
|
16900
|
+
const { advanceReviewSession: skAdvance } = await Promise.resolve().then(() => (init_store4(), store_exports4));
|
|
16901
|
+
skAdvance(getDb(), chatId, parseInt(idStr, 10), "skipped");
|
|
16902
|
+
await sendCurrentProposal(chatId, channel);
|
|
16642
16903
|
break;
|
|
16643
16904
|
}
|
|
16644
16905
|
case "discuss": {
|
|
@@ -16710,10 +16971,10 @@ Result: ${task.result.slice(0, 500)}` : ""
|
|
|
16710
16971
|
join19(homedir6(), ".cc-claw", "workspace", "skills")
|
|
16711
16972
|
];
|
|
16712
16973
|
try {
|
|
16713
|
-
const { readdirSync:
|
|
16974
|
+
const { readdirSync: readdirSync8, statSync: statSync9 } = await import("fs");
|
|
16714
16975
|
for (const dir of skillDirs) {
|
|
16715
16976
|
if (!existsSync19(dir)) continue;
|
|
16716
|
-
for (const entry of
|
|
16977
|
+
for (const entry of readdirSync8(dir)) {
|
|
16717
16978
|
if (statSync9(join19(dir, entry)).isDirectory()) {
|
|
16718
16979
|
targets.push({ label: `skills/${entry}`, path: `workspace/skills/${entry}/SKILL.md` });
|
|
16719
16980
|
}
|
|
@@ -16788,14 +17049,20 @@ Pick a different file for this change. Identity files (SOUL/USER) shape personal
|
|
|
16788
17049
|
break;
|
|
16789
17050
|
}
|
|
16790
17051
|
case "discuss-back": {
|
|
16791
|
-
const { getInsightById: bkIns } = await Promise.resolve().then(() => (init_store4(), store_exports4));
|
|
16792
|
-
const { formatProposalCard:
|
|
17052
|
+
const { getInsightById: bkIns, getReviewSession: bkSession } = await Promise.resolve().then(() => (init_store4(), store_exports4));
|
|
17053
|
+
const { formatProposalCardWithProgress: formatProposalCardWithProgress2, formatProposalCard: bkCardFn, buildProposalKeyboard: bkKb } = await Promise.resolve().then(() => (init_propose(), propose_exports));
|
|
16793
17054
|
const { getDb: bkDb } = await Promise.resolve().then(() => (init_store5(), store_exports5));
|
|
16794
17055
|
const bkInsight = bkIns(bkDb(), parseInt(idStr, 10));
|
|
16795
17056
|
if (bkInsight && bkInsight.status === "pending") {
|
|
16796
|
-
const
|
|
17057
|
+
const session2 = bkSession(bkDb(), chatId);
|
|
16797
17058
|
const kb = bkKb(bkInsight.id, bkInsight.category);
|
|
16798
|
-
|
|
17059
|
+
if (session2) {
|
|
17060
|
+
const card = formatProposalCardWithProgress2(bkInsight, session2.currentIndex, session2.insightIds.length);
|
|
17061
|
+
await channel.sendKeyboard(chatId, card, kb);
|
|
17062
|
+
} else {
|
|
17063
|
+
const card = bkCardFn(bkInsight);
|
|
17064
|
+
await channel.sendKeyboard(chatId, card, kb);
|
|
17065
|
+
}
|
|
16799
17066
|
}
|
|
16800
17067
|
break;
|
|
16801
17068
|
}
|
|
@@ -16804,6 +17071,9 @@ Pick a different file for this change. Identity files (SOUL/USER) shape personal
|
|
|
16804
17071
|
const { getDb: getDb2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
|
|
16805
17072
|
updateInsightStatus2(getDb2(), parseInt(idStr, 10), "rejected");
|
|
16806
17073
|
await channel.sendText(chatId, "Rejected. Won't propose similar changes.", { parseMode: "plain" });
|
|
17074
|
+
const { advanceReviewSession: rjAdvance } = await Promise.resolve().then(() => (init_store4(), store_exports4));
|
|
17075
|
+
rjAdvance(getDb2(), chatId, parseInt(idStr, 10), "rejected");
|
|
17076
|
+
await sendCurrentProposal(chatId, channel);
|
|
16807
17077
|
break;
|
|
16808
17078
|
}
|
|
16809
17079
|
case "stats": {
|
|
@@ -17457,7 +17727,7 @@ Use /skills <page> to navigate (e.g. /skills 2)` : "";
|
|
|
17457
17727
|
const header2 = totalPages > 1 ? `${skills2.length} skills (page ${safePage}/${totalPages}). Select one to invoke:` : `${skills2.length} skills available. Select one to invoke:`;
|
|
17458
17728
|
await channel.sendKeyboard(chatId, header2, buttons);
|
|
17459
17729
|
}
|
|
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;
|
|
17730
|
+
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
17731
|
var init_router = __esm({
|
|
17462
17732
|
"src/router.ts"() {
|
|
17463
17733
|
"use strict";
|
|
@@ -17600,6 +17870,8 @@ var init_router = __esm({
|
|
|
17600
17870
|
MAX_SIDE_QUESTS = 2;
|
|
17601
17871
|
pendingFallbackMessages = /* @__PURE__ */ new Map();
|
|
17602
17872
|
dashboardClawWarnings = /* @__PURE__ */ new Map();
|
|
17873
|
+
lastReflectTime = /* @__PURE__ */ new Map();
|
|
17874
|
+
REFLECT_COOLDOWN_MS = 12e4;
|
|
17603
17875
|
pendingSummaryUndo = /* @__PURE__ */ new Map();
|
|
17604
17876
|
pendingNewchatUndo = /* @__PURE__ */ new Map();
|
|
17605
17877
|
CLI_INSTALL_HINTS = {
|
|
@@ -18614,6 +18886,8 @@ async function main() {
|
|
|
18614
18886
|
const shutdown = async (signal) => {
|
|
18615
18887
|
log(`[cc-claw] Received ${signal}, shutting down...`);
|
|
18616
18888
|
try {
|
|
18889
|
+
const { stopAllActiveAgents: stopAllActiveAgents2 } = await Promise.resolve().then(() => (init_agent(), agent_exports));
|
|
18890
|
+
stopAllActiveAgents2();
|
|
18617
18891
|
stopAllHeartbeats();
|
|
18618
18892
|
stopHealthMonitor3();
|
|
18619
18893
|
stopMonitor();
|
|
@@ -18784,7 +19058,7 @@ __export(service_exports, {
|
|
|
18784
19058
|
serviceStatus: () => serviceStatus,
|
|
18785
19059
|
uninstallService: () => uninstallService
|
|
18786
19060
|
});
|
|
18787
|
-
import { existsSync as existsSync24, mkdirSync as mkdirSync9, writeFileSync as writeFileSync7, unlinkSync as
|
|
19061
|
+
import { existsSync as existsSync24, mkdirSync as mkdirSync9, writeFileSync as writeFileSync7, unlinkSync as unlinkSync5 } from "fs";
|
|
18788
19062
|
import { execFileSync as execFileSync2, execSync as execSync7 } from "child_process";
|
|
18789
19063
|
import { homedir as homedir8, platform } from "os";
|
|
18790
19064
|
import { join as join23, dirname as dirname4 } from "path";
|
|
@@ -18881,7 +19155,7 @@ function uninstallMacOS() {
|
|
|
18881
19155
|
execFileSync2("launchctl", ["unload", PLIST_PATH]);
|
|
18882
19156
|
} catch {
|
|
18883
19157
|
}
|
|
18884
|
-
|
|
19158
|
+
unlinkSync5(PLIST_PATH);
|
|
18885
19159
|
console.log(" Service uninstalled.");
|
|
18886
19160
|
}
|
|
18887
19161
|
function formatUptime(seconds) {
|
|
@@ -18969,7 +19243,7 @@ function uninstallLinux() {
|
|
|
18969
19243
|
execFileSync2("systemctl", ["--user", "disable", "cc-claw"]);
|
|
18970
19244
|
} catch {
|
|
18971
19245
|
}
|
|
18972
|
-
|
|
19246
|
+
unlinkSync5(UNIT_PATH);
|
|
18973
19247
|
execFileSync2("systemctl", ["--user", "daemon-reload"]);
|
|
18974
19248
|
console.log(" Service uninstalled.");
|
|
18975
19249
|
}
|
|
@@ -19381,14 +19655,18 @@ async function doctorCommand(globalOpts, localOpts) {
|
|
|
19381
19655
|
checks.push({ name: "Environment", status: "error", message: "No .env found", fix: "cc-claw setup" });
|
|
19382
19656
|
}
|
|
19383
19657
|
const CLI_BINARIES = { claude: "claude", gemini: "gemini", codex: "codex", cursor: "agent" };
|
|
19658
|
+
let installedBackends = 0;
|
|
19384
19659
|
for (const [label2, binary] of Object.entries(CLI_BINARIES)) {
|
|
19385
19660
|
try {
|
|
19386
19661
|
const path = execFileSync3("which", [binary], { encoding: "utf-8", timeout: 5e3 }).trim();
|
|
19387
19662
|
checks.push({ name: `${label2} CLI`, status: "ok", message: path });
|
|
19663
|
+
installedBackends++;
|
|
19388
19664
|
} catch {
|
|
19389
|
-
checks.push({ name: `${label2} CLI`, status: "warning", message: "not found in PATH" });
|
|
19390
19665
|
}
|
|
19391
19666
|
}
|
|
19667
|
+
if (installedBackends === 0) {
|
|
19668
|
+
checks.push({ name: "Backend CLIs", status: "error", message: "No backend CLIs found. Install at least one (claude, gemini, codex, or cursor agent)." });
|
|
19669
|
+
}
|
|
19392
19670
|
try {
|
|
19393
19671
|
const { isDaemonRunning: isDaemonRunning2, apiGet: apiGet2 } = await Promise.resolve().then(() => (init_api_client(), api_client_exports));
|
|
19394
19672
|
const running = await isDaemonRunning2();
|
|
@@ -19418,6 +19696,13 @@ async function doctorCommand(globalOpts, localOpts) {
|
|
|
19418
19696
|
} catch {
|
|
19419
19697
|
checks.push({ name: "Daemon", status: "warning", message: "could not probe" });
|
|
19420
19698
|
}
|
|
19699
|
+
try {
|
|
19700
|
+
const latest = execFileSync3("npm", ["view", "cc-claw", "version"], { encoding: "utf-8", timeout: 1e4 }).trim();
|
|
19701
|
+
if (latest && latest !== VERSION) {
|
|
19702
|
+
checks.push({ name: "Update available", status: "warning", message: `v${latest} available (current: v${VERSION})`, fix: "npm install -g cc-claw@latest" });
|
|
19703
|
+
}
|
|
19704
|
+
} catch {
|
|
19705
|
+
}
|
|
19421
19706
|
const tokenPath = `${DATA_PATH}/api-token`;
|
|
19422
19707
|
if (existsSync26(tokenPath)) {
|
|
19423
19708
|
try {
|
|
@@ -22047,6 +22332,7 @@ __export(evolve_exports, {
|
|
|
22047
22332
|
evolveOff: () => evolveOff,
|
|
22048
22333
|
evolveOn: () => evolveOn,
|
|
22049
22334
|
evolveReject: () => evolveReject,
|
|
22335
|
+
evolveSettings: () => evolveSettings,
|
|
22050
22336
|
evolveStats: () => evolveStats,
|
|
22051
22337
|
evolveStatus: () => evolveStatus,
|
|
22052
22338
|
evolveUndo: () => evolveUndo
|
|
@@ -22403,6 +22689,60 @@ async function evolveHistory(globalOpts, opts) {
|
|
|
22403
22689
|
return lines.join("\n");
|
|
22404
22690
|
});
|
|
22405
22691
|
}
|
|
22692
|
+
async function evolveSettings(globalOpts, opts) {
|
|
22693
|
+
ensureDb3();
|
|
22694
|
+
const { openDatabaseReadOnly: openDatabaseReadOnly2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
|
|
22695
|
+
const chatId = resolveChatId(globalOpts);
|
|
22696
|
+
const hasUpdates = opts.perFileCap !== void 0 || opts.backupRetentionDays !== void 0;
|
|
22697
|
+
if (hasUpdates) {
|
|
22698
|
+
await ensureDaemon();
|
|
22699
|
+
const { apiPost: apiPost2 } = await Promise.resolve().then(() => (init_api_client(), api_client_exports));
|
|
22700
|
+
const body = {};
|
|
22701
|
+
if (opts.perFileCap !== void 0) {
|
|
22702
|
+
const val = parseInt(opts.perFileCap, 10);
|
|
22703
|
+
if (isNaN(val) || val < 1) {
|
|
22704
|
+
outputError("INVALID_VALUE", "per-file-cap must be a positive integer.");
|
|
22705
|
+
process.exit(1);
|
|
22706
|
+
}
|
|
22707
|
+
body.perFileCap = val;
|
|
22708
|
+
}
|
|
22709
|
+
if (opts.backupRetentionDays !== void 0) {
|
|
22710
|
+
const val = parseInt(opts.backupRetentionDays, 10);
|
|
22711
|
+
if (isNaN(val) || val < 1) {
|
|
22712
|
+
outputError("INVALID_VALUE", "backup-retention-days must be a positive integer.");
|
|
22713
|
+
process.exit(1);
|
|
22714
|
+
}
|
|
22715
|
+
body.backupRetentionDays = val;
|
|
22716
|
+
}
|
|
22717
|
+
const res = await apiPost2("/api/evolve/settings", { chatId, ...body });
|
|
22718
|
+
if (res.ok) {
|
|
22719
|
+
output(res.data, () => `
|
|
22720
|
+
${success("Reflection settings updated.")}
|
|
22721
|
+
`);
|
|
22722
|
+
} else {
|
|
22723
|
+
outputError("SETTINGS_FAILED", `Failed: ${JSON.stringify(res.data)}`);
|
|
22724
|
+
process.exit(1);
|
|
22725
|
+
}
|
|
22726
|
+
} else {
|
|
22727
|
+
const readDb = openDatabaseReadOnly2();
|
|
22728
|
+
const { getReflectionSettings: getReflectionSettings2 } = await Promise.resolve().then(() => (init_store4(), store_exports4));
|
|
22729
|
+
const settings = getReflectionSettings2(readDb, chatId);
|
|
22730
|
+
readDb.close();
|
|
22731
|
+
output(settings, () => {
|
|
22732
|
+
const lines = [
|
|
22733
|
+
"",
|
|
22734
|
+
divider("Reflection Settings"),
|
|
22735
|
+
"",
|
|
22736
|
+
kvLine("Per-file cap", `${settings.perFileCap} changes/day per file`),
|
|
22737
|
+
kvLine("Backup retention", `${settings.backupRetentionDays} days`),
|
|
22738
|
+
"",
|
|
22739
|
+
muted("Update with: cc-claw evolve settings --per-file-cap <n> --backup-retention-days <n>"),
|
|
22740
|
+
""
|
|
22741
|
+
];
|
|
22742
|
+
return lines.join("\n");
|
|
22743
|
+
});
|
|
22744
|
+
}
|
|
22745
|
+
}
|
|
22406
22746
|
var init_evolve = __esm({
|
|
22407
22747
|
"src/cli/commands/evolve.ts"() {
|
|
22408
22748
|
"use strict";
|
|
@@ -23318,6 +23658,10 @@ evolve.command("history").description("Insight history").option("--status <statu
|
|
|
23318
23658
|
const { evolveHistory: evolveHistory2 } = await Promise.resolve().then(() => (init_evolve(), evolve_exports));
|
|
23319
23659
|
await evolveHistory2(program.opts(), opts);
|
|
23320
23660
|
});
|
|
23661
|
+
evolve.command("settings").description("View or update reflection settings").option("--per-file-cap <n>", "Max applies per file per day").option("--backup-retention-days <n>", "Days to keep backup files").action(async (opts) => {
|
|
23662
|
+
const { evolveSettings: evolveSettings2 } = await Promise.resolve().then(() => (init_evolve(), evolve_exports));
|
|
23663
|
+
await evolveSettings2(program.opts(), opts);
|
|
23664
|
+
});
|
|
23321
23665
|
program.command("start", { hidden: true }).description("Run the bot in the foreground (use 'service start' for background daemon)").action(async () => {
|
|
23322
23666
|
await Promise.resolve().then(() => (init_index(), index_exports));
|
|
23323
23667
|
});
|
|
@@ -23333,6 +23677,20 @@ program.command("setup").description("Interactive configuration wizard").action(
|
|
|
23333
23677
|
await Promise.resolve().then(() => (init_setup(), setup_exports));
|
|
23334
23678
|
});
|
|
23335
23679
|
async function run(argv = process.argv) {
|
|
23680
|
+
if (argv.includes("--version") || argv.includes("-V")) {
|
|
23681
|
+
console.log(VERSION);
|
|
23682
|
+
try {
|
|
23683
|
+
const { execFileSync: execFileSync5 } = await import("child_process");
|
|
23684
|
+
const latest = execFileSync5("npm", ["view", "cc-claw", "version"], { encoding: "utf-8", timeout: 1e4 }).trim();
|
|
23685
|
+
if (latest && latest !== VERSION) {
|
|
23686
|
+
console.log(`
|
|
23687
|
+
Update available: v${latest} (current: v${VERSION})`);
|
|
23688
|
+
console.log(`Run: npm install -g cc-claw@latest`);
|
|
23689
|
+
}
|
|
23690
|
+
} catch {
|
|
23691
|
+
}
|
|
23692
|
+
return;
|
|
23693
|
+
}
|
|
23336
23694
|
if (argv.includes("--ai")) {
|
|
23337
23695
|
const { generateAiSkill: generateAiSkill2, installAiSkill: installAiSkill2 } = await Promise.resolve().then(() => (init_ai_skill(), ai_skill_exports));
|
|
23338
23696
|
if (argv.includes("--install")) {
|