@inetafrica/open-claudia 2.15.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +30 -4
- package/CHANGELOG.md +12 -0
- package/README.md +87 -66
- package/bin/agent.js +44 -18
- package/bin/cli.js +30 -28
- package/bin/dream.js +5 -11
- package/bin/loopback-client.js +29 -5
- package/bin/schedule.js +19 -5
- package/bin/tool.js +23 -5
- package/bot-agent.js +5 -2350
- package/bot.js +66 -3
- package/channels/telegram/adapter.js +6 -1
- package/channels/voice/adapter.js +1 -0
- package/channels/voice/manage.js +43 -5
- package/config-dir.js +3 -11
- package/core/actions.js +149 -40
- package/core/approvals.js +136 -29
- package/core/auth-flow.js +103 -19
- package/core/config-dir.js +19 -0
- package/core/config.js +115 -69
- package/core/doctor.js +111 -57
- package/core/dream.js +219 -62
- package/core/enforcer.js +0 -0
- package/core/entities.js +2 -1
- package/core/fsutil.js +21 -4
- package/core/handlers.js +503 -217
- package/core/ideas.js +2 -1
- package/core/jobs.js +589 -72
- package/core/lessons.js +3 -2
- package/core/loopback.js +42 -6
- package/core/memory-mutation-queue.js +241 -0
- package/core/migration-backup.js +1060 -0
- package/core/pack-review.js +295 -88
- package/core/packs.js +4 -3
- package/core/persona.js +2 -1
- package/core/process-tree.js +19 -2
- package/core/provider-migration.js +39 -0
- package/core/provider-status.js +80 -0
- package/core/providers/claude-events.js +117 -0
- package/core/providers/claude-hook.js +114 -0
- package/core/providers/claude.js +296 -0
- package/core/providers/codex-events.js +121 -0
- package/core/providers/codex-hook.js +280 -0
- package/core/providers/codex.js +286 -0
- package/core/providers/contract.js +153 -0
- package/core/providers/env.js +148 -0
- package/core/providers/events.js +170 -0
- package/core/providers/hook-command.js +22 -0
- package/core/providers/index.js +240 -0
- package/core/providers/utility-policy.js +269 -0
- package/core/recall/classic.js +2 -2
- package/core/recall/discoverer.js +71 -22
- package/core/recall/metrics.js +27 -1
- package/core/recall/tuning.js +4 -1
- package/core/recall/warm-walker.js +151 -108
- package/core/recall-filter.js +86 -61
- package/core/router.js +79 -7
- package/core/run-context.js +185 -0
- package/core/runner.js +1414 -1290
- package/core/scheduler.js +515 -210
- package/core/side-chat.js +346 -0
- package/core/state.js +1084 -97
- package/core/subagent.js +72 -98
- package/core/system-prompt.js +462 -279
- package/core/tool-guard.js +73 -39
- package/core/tools.js +22 -5
- package/core/transcripts.js +30 -1
- package/core/usage-log.js +19 -4
- package/core/utility-agent.js +654 -0
- package/core/web-auth.js +29 -13
- package/docs/CHANNEL_DESIGN.md +181 -0
- package/docs/MULTI_CHANNEL_PLAN.md +254 -0
- package/docs/PROVIDER_MIGRATION.md +67 -0
- package/health.js +50 -39
- package/package.json +48 -4
- package/setup.js +198 -38
- package/soul.md +39 -0
- package/test-ability-extraction.js +5 -0
- package/test-approval-async.js +63 -4
- package/test-cursor-removal.js +334 -0
- package/test-enforcer.js +70 -27
- package/test-fixtures/current-provider-parity.json +132 -0
- package/test-fixtures/fake-agent-cli.js +477 -0
- package/test-fixtures/migrations/claude-only/jobs.json +3 -0
- package/test-fixtures/migrations/claude-only/sessions.json +5 -0
- package/test-fixtures/migrations/claude-only/state.json +5 -0
- package/test-fixtures/migrations/cursor-selected/crons.json +3 -0
- package/test-fixtures/migrations/cursor-selected/jobs.json +3 -0
- package/test-fixtures/migrations/cursor-selected/sessions.json +5 -0
- package/test-fixtures/migrations/cursor-selected/state.json +6 -0
- package/test-fixtures/migrations/dual-provider/jobs.json +3 -0
- package/test-fixtures/migrations/dual-provider/sessions.json +7 -0
- package/test-fixtures/migrations/dual-provider/state.json +10 -0
- package/test-fixtures/migrations/malformed-partial/jobs.json +1 -0
- package/test-fixtures/migrations/malformed-partial/sessions.json +1 -0
- package/test-fixtures/migrations/malformed-partial/sessions.json.bak +3 -0
- package/test-fixtures/migrations/malformed-partial/state.json +1 -0
- package/test-fixtures/migrations/malformed-partial/state.json.bak +5 -0
- package/test-fixtures/migrations/missing-project/jobs.json +3 -0
- package/test-fixtures/migrations/missing-project/sessions.json +3 -0
- package/test-fixtures/migrations/missing-project/state.json +4 -0
- package/test-fixtures/migrations/multi-user/jobs.json +4 -0
- package/test-fixtures/migrations/multi-user/sessions.json +4 -0
- package/test-fixtures/migrations/multi-user/state.json +6 -0
- package/test-fixtures/provider-event-samples.json +17 -0
- package/test-learning-e2e.js +5 -0
- package/test-memory-mutation-queue.js +191 -0
- package/test-provider-boot-matrix.js +399 -0
- package/test-provider-bootstrap.js +158 -0
- package/test-provider-capabilities.js +232 -0
- package/test-provider-claude.js +206 -0
- package/test-provider-codex.js +228 -0
- package/test-provider-compaction.js +371 -0
- package/test-provider-core-prompt.js +264 -0
- package/test-provider-coupling-audit.js +90 -0
- package/test-provider-dream.js +312 -0
- package/test-provider-enforcer.js +252 -0
- package/test-provider-env-isolation.js +150 -0
- package/test-provider-events.js +141 -0
- package/test-provider-fixture.js +332 -0
- package/test-provider-gateway.js +508 -0
- package/test-provider-language.js +89 -0
- package/test-provider-migration-backup.js +424 -0
- package/test-provider-pack-review.js +436 -0
- package/test-provider-parity-e2e.js +537 -0
- package/test-provider-prompt-parity.js +89 -0
- package/test-provider-recall.js +271 -0
- package/test-provider-registry.js +251 -0
- package/test-provider-resume-parity.js +41 -0
- package/test-provider-run-lifecycle.js +349 -0
- package/test-provider-runner.js +271 -0
- package/test-provider-scheduler.js +689 -0
- package/test-provider-session-commands.js +185 -0
- package/test-provider-session-history.js +205 -0
- package/test-provider-side-chat.js +337 -0
- package/test-provider-state-migration.js +316 -0
- package/test-provider-stream-decoder.js +69 -0
- package/test-provider-subagent.js +228 -0
- package/test-provider-tool-hooks.js +360 -0
- package/test-recall-discoverer.js +1 -0
- package/test-recall-engine.js +3 -3
- package/test-recall-evolution.js +18 -0
- package/test-runner-watchdog-static.js +16 -8
- package/test-single-runtime.js +35 -0
- package/test-telegram-poll-recovery.js +56 -0
- package/test-tool-guard.js +56 -0
- package/test-tools.js +19 -0
- package/test-unified-identity.js +3 -1
- package/test-usage-accounting.js +92 -20
- package/test-utility-provider-policy.js +486 -0
- package/web.js +130 -35
package/core/dream.js
CHANGED
|
@@ -12,9 +12,11 @@
|
|
|
12
12
|
const fs = require("fs");
|
|
13
13
|
const path = require("path");
|
|
14
14
|
const cron = require("node-cron");
|
|
15
|
+
const { atomicWriteFileSync } = require("./fsutil");
|
|
16
|
+
const { memoryMutationQueue } = require("./memory-mutation-queue");
|
|
15
17
|
|
|
16
18
|
const CONFIG_DIR = require("../config-dir");
|
|
17
|
-
const { config
|
|
19
|
+
const { config } = require("./config");
|
|
18
20
|
const packs = require("./packs");
|
|
19
21
|
const entities = require("./entities");
|
|
20
22
|
const lessons = require("./lessons");
|
|
@@ -22,27 +24,49 @@ const ideas = require("./ideas");
|
|
|
22
24
|
const daySeeds = require("./day-seeds");
|
|
23
25
|
const packGuard = require("./pack-guard");
|
|
24
26
|
const persona = require("./persona");
|
|
25
|
-
const {
|
|
27
|
+
const { spawnUtilityAgent } = require("./utility-agent");
|
|
26
28
|
const recallMetrics = require("./recall/metrics");
|
|
27
29
|
const recallTuning = require("./recall/tuning");
|
|
28
30
|
const recallGraphLib = require("./recall/graph");
|
|
29
31
|
|
|
30
|
-
// Graph surgery + threshold tuning is the highest-judgment work
|
|
31
|
-
//
|
|
32
|
-
//
|
|
33
|
-
//
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
const tier = String(process.env.DREAM_TIER || "high").toLowerCase();
|
|
38
|
-
if (tier === "low") return "haiku";
|
|
39
|
-
if (tier === "medium") return "sonnet";
|
|
40
|
-
return DEFAULT_CLAUDE_MODEL; // high → the bot's top model (opus 4.8); tracks future upgrades
|
|
32
|
+
// Graph surgery + threshold tuning is the highest-judgment recall work, so the
|
|
33
|
+
// utility policy chooses a provider-owned model tier. The old DREAM_MODEL key
|
|
34
|
+
// remains a Claude-only compatibility override; provider-specific pins are
|
|
35
|
+
// DREAM_MODEL_CLAUDE and DREAM_MODEL_CODEX.
|
|
36
|
+
function dreamTier() {
|
|
37
|
+
const tier = String(process.env.DREAM_TIER || "high").trim().toLowerCase();
|
|
38
|
+
return new Set(["low", "medium", "high"]).has(tier) ? tier : "high";
|
|
41
39
|
}
|
|
42
|
-
const DREAM_MODEL = pickDreamModel();
|
|
43
40
|
// The dream is the foundation of long-term memory quality, so it runs at max
|
|
44
41
|
// reasoning effort by default. Overridable via DREAM_EFFORT (low|medium|high|xhigh|max).
|
|
45
42
|
const DREAM_EFFORT = process.env.DREAM_EFFORT || "max";
|
|
43
|
+
const DREAM_OUTPUT_SCHEMA = Object.freeze({
|
|
44
|
+
type: "object",
|
|
45
|
+
properties: {
|
|
46
|
+
merges: { type: "array", items: { type: "object" } },
|
|
47
|
+
umbrellas: { type: "array", items: { type: "object" } },
|
|
48
|
+
parents: { type: "array", items: { type: "object" } },
|
|
49
|
+
retag: { type: "array", items: { type: "object" } },
|
|
50
|
+
entity_merges: { type: "array", items: { type: "object" } },
|
|
51
|
+
entity_notes: { type: "array", items: { type: "object" } },
|
|
52
|
+
archive: { type: "array", items: { type: "object" } },
|
|
53
|
+
lessons: { type: "array", items: { type: "object" } },
|
|
54
|
+
persona: {},
|
|
55
|
+
tuning: {},
|
|
56
|
+
report: { type: "string" },
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
const INTROSPECTION_OUTPUT_SCHEMA = Object.freeze({
|
|
60
|
+
type: "object",
|
|
61
|
+
properties: {
|
|
62
|
+
lessons_add: { type: "array", items: { type: "object" } },
|
|
63
|
+
doc_edits: { type: "array", items: { type: "object" } },
|
|
64
|
+
entity_edits: { type: "array", items: { type: "object" } },
|
|
65
|
+
ideas: { type: "array", items: { type: "object" } },
|
|
66
|
+
persona: {},
|
|
67
|
+
report: { type: "string" },
|
|
68
|
+
},
|
|
69
|
+
});
|
|
46
70
|
const DREAM_CRON = process.env.DREAM_CRON || "0 4 * * *";
|
|
47
71
|
// Consolidation model timeout. The model weighs the WHOLE corpus in one pass,
|
|
48
72
|
// so a fixed budget that was fine at 30 packs starves at 130 (the timeout that
|
|
@@ -74,8 +98,6 @@ const PROMOTE_MIN_PROJECTS = Number(process.env.DREAM_PROMOTE_MIN_PROJECTS || 3)
|
|
|
74
98
|
const ARCHIVE_IDLE_DAYS = Number(process.env.DREAM_ARCHIVE_IDLE_DAYS || 30);
|
|
75
99
|
const ARCHIVE_MAX_USAGE = Number(process.env.DREAM_ARCHIVE_MAX_USAGE || 3);
|
|
76
100
|
|
|
77
|
-
let _dreaming = false;
|
|
78
|
-
|
|
79
101
|
function enabled() {
|
|
80
102
|
return (process.env.DREAM || "on").toLowerCase() !== "off";
|
|
81
103
|
}
|
|
@@ -119,7 +141,7 @@ function readDreamState() {
|
|
|
119
141
|
try { return JSON.parse(fs.readFileSync(DREAM_STATE_FILE, "utf8")) || {}; } catch (e) { return {}; }
|
|
120
142
|
}
|
|
121
143
|
function writeDreamState(s) {
|
|
122
|
-
try {
|
|
144
|
+
try { atomicWriteFileSync(DREAM_STATE_FILE, JSON.stringify(s, null, 2), { mode: 0o600, backup: true }); } catch (e) {}
|
|
123
145
|
}
|
|
124
146
|
|
|
125
147
|
// Windowed metric deltas between two summary snapshots (counters only grow).
|
|
@@ -660,8 +682,8 @@ function applyDream(decision, backupRoot) {
|
|
|
660
682
|
// A SECOND dream pass that runs with read-only tools. It reviews the day's
|
|
661
683
|
// compaction seeds (what was actually worked on), reflects on its current self
|
|
662
684
|
// with that self injected, may read its own code to understand how it works
|
|
663
|
-
// (read-only
|
|
664
|
-
//
|
|
685
|
+
// (provider-native read-only mode prevents edits to code or soul.md), then
|
|
686
|
+
// proposes improvements to its
|
|
665
687
|
// LEARNED MEMORY only: lessons promoted from missed corrections, sharpened
|
|
666
688
|
// pack/entity notes, and self-improvement ideas. All writes happen here in
|
|
667
689
|
// Node, bounded to memory docs, guard-scanned, and backed up first.
|
|
@@ -851,9 +873,15 @@ function writeDreamReport(data) {
|
|
|
851
873
|
fs.mkdirSync(DREAMS_DIR, { recursive: true, mode: 0o700 });
|
|
852
874
|
const stamp = new Date().toISOString();
|
|
853
875
|
const file = path.join(DREAMS_DIR, `${stamp.slice(0, 10)}.md`);
|
|
876
|
+
const consolidationIntelligence = data.intelligence || {};
|
|
877
|
+
const introspectionIntelligence = data.introspectionIntelligence || {};
|
|
878
|
+
const provider = consolidationIntelligence.provider || data.provider || "unknown";
|
|
879
|
+
const model = consolidationIntelligence.model || data.model || "unknown";
|
|
880
|
+
const effort = consolidationIntelligence.effort || data.effort || "provider default";
|
|
854
881
|
const sec = [
|
|
855
882
|
`# Dream — ${stamp}`,
|
|
856
|
-
`Model: ${
|
|
883
|
+
`Provider: ${provider} · Model: ${model} · effort: ${effort} · trigger: ${data.trigger || "?"}`,
|
|
884
|
+
`Introspection: ${introspectionIntelligence.provider || "not run"} · Model: ${introspectionIntelligence.model || "not run"} · status: ${introspectionIntelligence.status || "unknown"}`,
|
|
857
885
|
"",
|
|
858
886
|
"## Consolidation",
|
|
859
887
|
data.consolidation.report || "(no report)",
|
|
@@ -873,7 +901,7 @@ function writeDreamReport(data) {
|
|
|
873
901
|
const block = sec.join("\n") + "\n\n---\n\n";
|
|
874
902
|
let existing = "";
|
|
875
903
|
try { existing = fs.readFileSync(file, "utf-8"); } catch (e) {}
|
|
876
|
-
|
|
904
|
+
atomicWriteFileSync(file, existing + block, { mode: 0o600, backup: true });
|
|
877
905
|
return file;
|
|
878
906
|
} catch (e) { return null; }
|
|
879
907
|
}
|
|
@@ -990,16 +1018,66 @@ function negativeHebbianPhase(ev, { cap = 12 } = {}) {
|
|
|
990
1018
|
return { lines, nodes, weakened, pruned };
|
|
991
1019
|
}
|
|
992
1020
|
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
1021
|
+
function dreamUtilityOptions(purpose, { provider = null, timeoutMs, outputSchema, systemPrompt, cwd = process.cwd() } = {}) {
|
|
1022
|
+
return {
|
|
1023
|
+
purpose,
|
|
1024
|
+
provider,
|
|
1025
|
+
tier: dreamTier(),
|
|
1026
|
+
modelEnvPrefix: "DREAM_MODEL",
|
|
1027
|
+
legacyClaudeModelEnv: "DREAM_MODEL",
|
|
1028
|
+
readOnly: true,
|
|
1029
|
+
outputSchema,
|
|
1030
|
+
effort: DREAM_EFFORT,
|
|
1031
|
+
timeoutMs,
|
|
1032
|
+
cwd,
|
|
1033
|
+
systemPrompt,
|
|
1034
|
+
};
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1037
|
+
function utilityPayload(result) {
|
|
1038
|
+
return result?.json ? JSON.stringify(result.json) : String(result?.text || "");
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
function intelligenceMetadata(result, status = "succeeded") {
|
|
1042
|
+
return {
|
|
1043
|
+
provider: result?.provider || null,
|
|
1044
|
+
model: result?.model || null,
|
|
1045
|
+
effort: result?.effort || null,
|
|
1046
|
+
fallbackUsed: !!result?.fallbackUsed,
|
|
1047
|
+
status,
|
|
1048
|
+
};
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
function failedIntelligenceMetadata(error) {
|
|
1052
|
+
const result = error?.result || null;
|
|
1053
|
+
return {
|
|
1054
|
+
...intelligenceMetadata(result, "failed"),
|
|
1055
|
+
code: String(error?.code || result?.error?.code || "DREAM_UTILITY_FAILED").slice(0, 80),
|
|
1056
|
+
};
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
async function previewDreamWith(runUtility, { provider = null } = {}) {
|
|
1060
|
+
const result = await runUtility(buildDreamPrompt(), dreamUtilityOptions("dream", {
|
|
1061
|
+
provider,
|
|
1062
|
+
timeoutMs: 8 * 60 * 1000,
|
|
1063
|
+
outputSchema: DREAM_OUTPUT_SCHEMA,
|
|
1064
|
+
systemPrompt: "You are a background memory consolidation process. Reply with ONLY the requested JSON object. No prose, no markdown, no tool use.",
|
|
1065
|
+
}));
|
|
1066
|
+
const decision = parseDream(utilityPayload(result));
|
|
1067
|
+
if (!decision) {
|
|
1068
|
+
const error = new Error("Dream utility returned unreadable output");
|
|
1069
|
+
error.code = "DREAM_OUTPUT_INVALID";
|
|
1070
|
+
throw error;
|
|
1071
|
+
}
|
|
1072
|
+
return { decision, ...intelligenceMetadata(result) };
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
async function runDreamMutation({ trigger = "manual", provider = null, runUtility = spawnUtilityAgent } = {}) {
|
|
996
1076
|
const packCount = packs.listPacks().length;
|
|
997
1077
|
const entityCount = entities.listEntities().length;
|
|
998
1078
|
if (packCount === 0 && entityCount === 0) return { skipped: "no memory to consolidate yet" };
|
|
999
1079
|
|
|
1000
|
-
|
|
1001
|
-
try {
|
|
1002
|
-
const nowISO = new Date().toISOString();
|
|
1080
|
+
const nowISO = new Date().toISOString();
|
|
1003
1081
|
const dreamState = readDreamState();
|
|
1004
1082
|
const fullSweep = !dreamState.lastFullSweep ||
|
|
1005
1083
|
(Date.now() - Date.parse(dreamState.lastFullSweep)) / 86400000 >= FULL_SWEEP_DAYS;
|
|
@@ -1050,16 +1128,25 @@ async function runDream({ trigger = "manual" } = {}) {
|
|
|
1050
1128
|
// decision (applyDream no-ops), note it for the report, and carry on.
|
|
1051
1129
|
let decision = null;
|
|
1052
1130
|
let modelFailNote = "";
|
|
1131
|
+
let consolidationIntelligence = intelligenceMetadata(null, "not_run");
|
|
1053
1132
|
try {
|
|
1054
|
-
const
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1133
|
+
const dreamRun = await runUtility(
|
|
1134
|
+
buildDreamPrompt({ sinceTs: dreamState.lastRun || null, fullSweep, clusters, evidence: ev, windowDelta }),
|
|
1135
|
+
dreamUtilityOptions("dream", {
|
|
1136
|
+
provider,
|
|
1137
|
+
timeoutMs: dreamTimeoutMs(packCount),
|
|
1138
|
+
outputSchema: DREAM_OUTPUT_SCHEMA,
|
|
1139
|
+
systemPrompt: "You are a background memory consolidation process. Reply with ONLY the requested JSON object. No prose, no markdown, no tool use.",
|
|
1140
|
+
}),
|
|
1141
|
+
);
|
|
1142
|
+
consolidationIntelligence = intelligenceMetadata(dreamRun);
|
|
1143
|
+
decision = parseDream(utilityPayload(dreamRun));
|
|
1144
|
+
if (!decision) {
|
|
1145
|
+
consolidationIntelligence.status = "invalid_output";
|
|
1146
|
+
modelFailNote = "the AI consolidation step returned unreadable output";
|
|
1147
|
+
}
|
|
1062
1148
|
} catch (e) {
|
|
1149
|
+
consolidationIntelligence = failedIntelligenceMetadata(e);
|
|
1063
1150
|
modelFailNote = `the AI consolidation step didn't finish (${e.message})`;
|
|
1064
1151
|
}
|
|
1065
1152
|
if (!decision) decision = { report: "" };
|
|
@@ -1102,37 +1189,36 @@ async function runDream({ trigger = "manual" } = {}) {
|
|
|
1102
1189
|
}
|
|
1103
1190
|
|
|
1104
1191
|
// Phase 2: self-improvement introspection. Reviews the day's seeds, reads
|
|
1105
|
-
// its own code/memory
|
|
1192
|
+
// its own code/memory through provider-native read-only mode, promotes lessons
|
|
1106
1193
|
// from missed corrections, refreshes docs, captures ideas. Bounded to memory
|
|
1107
1194
|
// docs; never code or soul. Applied unless DREAM_SELF_APPLY=off, in which
|
|
1108
1195
|
// case the proposal is reported for morning approval instead.
|
|
1109
1196
|
let introReport = "";
|
|
1110
1197
|
let introApplied = [];
|
|
1111
1198
|
let introProposed = null;
|
|
1199
|
+
let introspectionIntelligence = intelligenceMetadata(null, introspectEnabled() ? "not_run" : "disabled");
|
|
1112
1200
|
if (introspectEnabled()) {
|
|
1113
1201
|
try {
|
|
1114
|
-
const introRun = await
|
|
1115
|
-
|
|
1116
|
-
effort: DREAM_EFFORT,
|
|
1202
|
+
const introRun = await runUtility(buildIntrospectionPrompt(), dreamUtilityOptions("introspection", {
|
|
1203
|
+
provider,
|
|
1117
1204
|
cwd: CODE_ROOT,
|
|
1118
1205
|
timeoutMs: 12 * 60 * 1000,
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
// can read its own code + memory but cannot edit code or soul.
|
|
1125
|
-
permissionMode: "plan",
|
|
1126
|
-
allowedTools: ["Read", "Glob", "Grep"],
|
|
1127
|
-
systemPrompt: "You are Open Claudia's nightly self-introspection. You may READ files (your code, memory, soul) to understand yourself, but you have no tools to write or run anything. After investigating, reply with ONLY the requested JSON object — no prose, no code fences.",
|
|
1128
|
-
});
|
|
1129
|
-
const intro = parseIntrospection(introRun.text);
|
|
1206
|
+
outputSchema: INTROSPECTION_OUTPUT_SCHEMA,
|
|
1207
|
+
systemPrompt: "You are Open Claudia's nightly self-introspection. You may READ files (your code, memory, soul) to understand yourself, but read-only mode forbids product side effects. After investigating, reply with ONLY the requested JSON object — no prose, no code fences.",
|
|
1208
|
+
}));
|
|
1209
|
+
introspectionIntelligence = intelligenceMetadata(introRun);
|
|
1210
|
+
const intro = parseIntrospection(utilityPayload(introRun));
|
|
1130
1211
|
if (intro) {
|
|
1131
1212
|
introReport = intro.report || "";
|
|
1132
1213
|
if (selfApplyEnabled()) introApplied = applyIntrospection(intro, backupRoot, { personaAlreadyChanged: !!decision.persona });
|
|
1133
1214
|
else introProposed = intro;
|
|
1215
|
+
} else {
|
|
1216
|
+
introspectionIntelligence.status = "invalid_output";
|
|
1134
1217
|
}
|
|
1135
|
-
} catch (e) {
|
|
1218
|
+
} catch (e) {
|
|
1219
|
+
introspectionIntelligence = failedIntelligenceMetadata(e);
|
|
1220
|
+
console.warn(`[dream] introspection phase failed: ${e.message}`);
|
|
1221
|
+
}
|
|
1136
1222
|
}
|
|
1137
1223
|
|
|
1138
1224
|
// Recall-graph maintenance: refresh structural edges, decay reinforced
|
|
@@ -1271,10 +1357,15 @@ async function runDream({ trigger = "manual" } = {}) {
|
|
|
1271
1357
|
try {
|
|
1272
1358
|
recallMetrics.appendKpi({
|
|
1273
1359
|
trigger, fullSweep,
|
|
1360
|
+
providers: {
|
|
1361
|
+
dream: consolidationIntelligence.provider,
|
|
1362
|
+
introspection: introspectionIntelligence.provider,
|
|
1363
|
+
},
|
|
1274
1364
|
models: {
|
|
1275
|
-
walker: process.env.RECALL_DISCOVERER_MODEL || "
|
|
1276
|
-
reviewer: process.env.PACK_REVIEW_MODEL || "
|
|
1277
|
-
dream:
|
|
1365
|
+
walker: process.env.RECALL_DISCOVERER_MODEL || "provider tier:low",
|
|
1366
|
+
reviewer: process.env.PACK_REVIEW_MODEL || "provider tier:medium",
|
|
1367
|
+
dream: consolidationIntelligence.model,
|
|
1368
|
+
introspection: introspectionIntelligence.model,
|
|
1278
1369
|
},
|
|
1279
1370
|
window: windowDelta || null,
|
|
1280
1371
|
health: windowDelta ? healthOf(windowDelta) : null,
|
|
@@ -1287,11 +1378,22 @@ async function runDream({ trigger = "manual" } = {}) {
|
|
|
1287
1378
|
dreamState.lastRun = nowISO;
|
|
1288
1379
|
if (fullSweep) dreamState.lastFullSweep = nowISO;
|
|
1289
1380
|
if (ev?.summary) dreamState.summarySnapshot = ev.summary;
|
|
1381
|
+
dreamState.provider = consolidationIntelligence.provider;
|
|
1382
|
+
dreamState.model = consolidationIntelligence.model;
|
|
1383
|
+
dreamState.effort = consolidationIntelligence.effort;
|
|
1384
|
+
dreamState.fallbackUsed = consolidationIntelligence.fallbackUsed;
|
|
1385
|
+
dreamState.lastRunStatus = consolidationIntelligence.status;
|
|
1386
|
+
dreamState.introspection = introspectionIntelligence;
|
|
1290
1387
|
writeDreamState(dreamState);
|
|
1291
1388
|
} catch (e) { console.warn(`[dream] state write failed: ${e.message}`); }
|
|
1292
1389
|
|
|
1293
1390
|
const reportPath = writeDreamReport({
|
|
1294
|
-
|
|
1391
|
+
provider: consolidationIntelligence.provider,
|
|
1392
|
+
model: consolidationIntelligence.model,
|
|
1393
|
+
effort: consolidationIntelligence.effort || DREAM_EFFORT,
|
|
1394
|
+
intelligence: consolidationIntelligence,
|
|
1395
|
+
introspectionIntelligence,
|
|
1396
|
+
trigger,
|
|
1295
1397
|
consolidation: { report: decision.report, lines: consolidationLines },
|
|
1296
1398
|
introspection: { report: introReport, lines: introApplied, proposed: introProposed },
|
|
1297
1399
|
healthNote, graphNote, episodicNote, toolNote, staleNote, backupRoot,
|
|
@@ -1312,16 +1414,70 @@ async function runDream({ trigger = "manual" } = {}) {
|
|
|
1312
1414
|
if (episodicNote) parts.push(episodicNote);
|
|
1313
1415
|
if (toolNote) parts.push(toolNote);
|
|
1314
1416
|
if (staleNote) parts.push(staleNote);
|
|
1417
|
+
if (consolidationIntelligence.provider || consolidationIntelligence.model) {
|
|
1418
|
+
const introEngine = introspectionIntelligence.provider
|
|
1419
|
+
? ` · introspection ${introspectionIntelligence.provider}/${introspectionIntelligence.model || "default"}`
|
|
1420
|
+
: "";
|
|
1421
|
+
parts.push(`⚙️ Dream intelligence: ${consolidationIntelligence.provider || "unknown"}/${consolidationIntelligence.model || "default"}${introEngine}`);
|
|
1422
|
+
}
|
|
1315
1423
|
if (dreamLines.length) parts.push(`🗄 Anything changed or merged away is backed up under ${backupRoot}`);
|
|
1316
1424
|
if (reportPath) parts.push(`📔 Full dream log: ${reportPath}`);
|
|
1317
1425
|
const message = parts.length ? parts.join("\n\n") : "";
|
|
1318
1426
|
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1427
|
+
return {
|
|
1428
|
+
applied: dreamLines,
|
|
1429
|
+
report,
|
|
1430
|
+
introReport,
|
|
1431
|
+
message,
|
|
1432
|
+
healthNote,
|
|
1433
|
+
staleNote,
|
|
1434
|
+
graphNote,
|
|
1435
|
+
episodicNote,
|
|
1436
|
+
toolNote,
|
|
1437
|
+
reportPath,
|
|
1438
|
+
trigger,
|
|
1439
|
+
fullSweep,
|
|
1440
|
+
provider: consolidationIntelligence.provider,
|
|
1441
|
+
model: consolidationIntelligence.model,
|
|
1442
|
+
effort: consolidationIntelligence.effort,
|
|
1443
|
+
fallbackUsed: consolidationIntelligence.fallbackUsed,
|
|
1444
|
+
introspectionProvider: introspectionIntelligence.provider,
|
|
1445
|
+
introspectionModel: introspectionIntelligence.model,
|
|
1446
|
+
introspectionFallbackUsed: introspectionIntelligence.fallbackUsed,
|
|
1447
|
+
};
|
|
1448
|
+
}
|
|
1449
|
+
|
|
1450
|
+
function createDreamService(dependencies = {}) {
|
|
1451
|
+
const runUtility = dependencies.spawnUtilityAgent || spawnUtilityAgent;
|
|
1452
|
+
const queue = dependencies.mutationQueue || memoryMutationQueue;
|
|
1453
|
+
let dreaming = false;
|
|
1454
|
+
|
|
1455
|
+
async function runDream({ trigger = "manual", provider = null } = {}) {
|
|
1456
|
+
if (!enabled()) return { skipped: "dream is disabled (DREAM=off)" };
|
|
1457
|
+
if (dreaming) return { skipped: "a dream is already in progress" };
|
|
1458
|
+
dreaming = true;
|
|
1459
|
+
try {
|
|
1460
|
+
return await queue.enqueue("dream", () => runDreamMutation({ trigger, provider, runUtility }), {
|
|
1461
|
+
purpose: "dream",
|
|
1462
|
+
trigger,
|
|
1463
|
+
provider,
|
|
1464
|
+
});
|
|
1465
|
+
} finally {
|
|
1466
|
+
dreaming = false;
|
|
1467
|
+
}
|
|
1322
1468
|
}
|
|
1469
|
+
|
|
1470
|
+
return {
|
|
1471
|
+
isDreaming: () => dreaming,
|
|
1472
|
+
previewDream: (options) => previewDreamWith(runUtility, options),
|
|
1473
|
+
runDream,
|
|
1474
|
+
};
|
|
1323
1475
|
}
|
|
1324
1476
|
|
|
1477
|
+
const defaultDreamService = createDreamService();
|
|
1478
|
+
const runDream = defaultDreamService.runDream;
|
|
1479
|
+
const previewDream = defaultDreamService.previewDream;
|
|
1480
|
+
|
|
1325
1481
|
// In-bot scheduler: one quiet-hours cron, reporting to the owner's
|
|
1326
1482
|
// Telegram chat. When the dream summary is on (default) it posts a short
|
|
1327
1483
|
// note after every run — including the charming "all tidy" line when
|
|
@@ -1346,14 +1502,15 @@ function initDream(adapters) {
|
|
|
1346
1502
|
console.error("dream: run failed:", e.message);
|
|
1347
1503
|
}
|
|
1348
1504
|
});
|
|
1349
|
-
console.log(`dream: scheduled (${DREAM_CRON},
|
|
1505
|
+
console.log(`dream: scheduled (${DREAM_CRON}, provider policy tier ${dreamTier()}, effort ${DREAM_EFFORT}, introspect ${introspectEnabled() ? "on" : "off"})`);
|
|
1350
1506
|
}
|
|
1351
1507
|
|
|
1352
1508
|
module.exports = {
|
|
1353
|
-
runDream, initDream, buildDreamPrompt, parseDream, applyDream, manageAbilityTiers, nestPrefixFamilies, dreamTimeoutMs,
|
|
1509
|
+
createDreamService, previewDream, runDream, initDream, buildDreamPrompt, parseDream, applyDream, manageAbilityTiers, nestPrefixFamilies, dreamTimeoutMs,
|
|
1354
1510
|
buildIntrospectionPrompt, parseIntrospection, applyIntrospection, writeDreamReport,
|
|
1355
1511
|
summaryDelta, healthOf, mergeClusters, selectDumpDirs, journalBackfillDedupe,
|
|
1356
1512
|
coRescueEdgesPhase, negativeHebbianPhase, readDreamState, writeDreamState,
|
|
1357
|
-
enabled, summaryEnabled, introspectEnabled, selfApplyEnabled,
|
|
1358
|
-
DREAM_CRON,
|
|
1513
|
+
enabled, summaryEnabled, introspectEnabled, selfApplyEnabled, dreamTier,
|
|
1514
|
+
DREAM_CRON, DREAM_EFFORT, DREAM_OUTPUT_SCHEMA, INTROSPECTION_OUTPUT_SCHEMA,
|
|
1515
|
+
PROMOTE_MIN_PROJECTS, DREAM_STATE_FILE, FULL_SWEEP_DAYS,
|
|
1359
1516
|
};
|
package/core/enforcer.js
CHANGED
|
Binary file
|
package/core/entities.js
CHANGED
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
|
|
19
19
|
const fs = require("fs");
|
|
20
20
|
const path = require("path");
|
|
21
|
+
const { atomicWriteFileSync } = require("./fsutil");
|
|
21
22
|
|
|
22
23
|
let DatabaseSync = null;
|
|
23
24
|
try { ({ DatabaseSync } = require("node:sqlite")); } catch (e) { /* old node — matching disabled */ }
|
|
@@ -168,7 +169,7 @@ function findEntity(nameOrSlug) {
|
|
|
168
169
|
|
|
169
170
|
function writeEntity(ent) {
|
|
170
171
|
ensureDir();
|
|
171
|
-
|
|
172
|
+
atomicWriteFileSync(entityFile(ent.slug), serialize(ent), { mode: 0o600, backup: true });
|
|
172
173
|
markIndexDirty();
|
|
173
174
|
return ent.slug;
|
|
174
175
|
}
|
package/core/fsutil.js
CHANGED
|
@@ -9,10 +9,11 @@
|
|
|
9
9
|
const fs = require("fs");
|
|
10
10
|
const path = require("path");
|
|
11
11
|
|
|
12
|
-
function atomicWriteFileSync(file, data, { backup = false } = {}) {
|
|
12
|
+
function atomicWriteFileSync(file, data, { backup = false, mode = 0o600 } = {}) {
|
|
13
13
|
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
14
14
|
const tmp = `${file}.${process.pid}.${Date.now()}.tmp`;
|
|
15
|
-
const fd = fs.openSync(tmp, "w");
|
|
15
|
+
const fd = fs.openSync(tmp, "w", mode);
|
|
16
|
+
let renamed = false;
|
|
16
17
|
try {
|
|
17
18
|
fs.writeSync(fd, data);
|
|
18
19
|
fs.fsyncSync(fd);
|
|
@@ -22,9 +23,25 @@ function atomicWriteFileSync(file, data, { backup = false } = {}) {
|
|
|
22
23
|
// Snapshot the previous good version before we overwrite, so a corrupt or
|
|
23
24
|
// wrong write is still recoverable via readJsonWithFallback.
|
|
24
25
|
if (backup) {
|
|
25
|
-
try {
|
|
26
|
+
try {
|
|
27
|
+
if (fs.existsSync(file)) {
|
|
28
|
+
fs.copyFileSync(file, `${file}.bak`);
|
|
29
|
+
fs.chmodSync(`${file}.bak`, mode);
|
|
30
|
+
}
|
|
31
|
+
} catch (e) {}
|
|
32
|
+
}
|
|
33
|
+
try {
|
|
34
|
+
fs.renameSync(tmp, file);
|
|
35
|
+
renamed = true;
|
|
36
|
+
try {
|
|
37
|
+
const dirFd = fs.openSync(path.dirname(file), "r");
|
|
38
|
+
try { fs.fsyncSync(dirFd); } finally { fs.closeSync(dirFd); }
|
|
39
|
+
} catch (_) { /* directory fsync is not supported on every platform */ }
|
|
40
|
+
} finally {
|
|
41
|
+
if (!renamed) {
|
|
42
|
+
try { fs.unlinkSync(tmp); } catch (_) {}
|
|
43
|
+
}
|
|
26
44
|
}
|
|
27
|
-
fs.renameSync(tmp, file);
|
|
28
45
|
}
|
|
29
46
|
|
|
30
47
|
// Parse JSON from `file`, transparently falling back to `<file>.bak` if the
|