@vainplex/openclaw-cortex 0.1.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/LICENSE +21 -0
- package/README.md +151 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +50 -0
- package/dist/index.js.map +1 -0
- package/dist/src/boot-context.d.ts +43 -0
- package/dist/src/boot-context.d.ts.map +1 -0
- package/dist/src/boot-context.js +215 -0
- package/dist/src/boot-context.js.map +1 -0
- package/dist/src/config.d.ts +10 -0
- package/dist/src/config.d.ts.map +1 -0
- package/dist/src/config.js +100 -0
- package/dist/src/config.js.map +1 -0
- package/dist/src/decision-tracker.d.ts +48 -0
- package/dist/src/decision-tracker.d.ts.map +1 -0
- package/dist/src/decision-tracker.js +135 -0
- package/dist/src/decision-tracker.js.map +1 -0
- package/dist/src/hooks.d.ts +7 -0
- package/dist/src/hooks.d.ts.map +1 -0
- package/dist/src/hooks.js +104 -0
- package/dist/src/hooks.js.map +1 -0
- package/dist/src/narrative-generator.d.ts +34 -0
- package/dist/src/narrative-generator.d.ts.map +1 -0
- package/dist/src/narrative-generator.js +153 -0
- package/dist/src/narrative-generator.js.map +1 -0
- package/dist/src/patterns.d.ts +24 -0
- package/dist/src/patterns.d.ts.map +1 -0
- package/dist/src/patterns.js +98 -0
- package/dist/src/patterns.js.map +1 -0
- package/dist/src/pre-compaction.d.ts +21 -0
- package/dist/src/pre-compaction.d.ts.map +1 -0
- package/dist/src/pre-compaction.js +108 -0
- package/dist/src/pre-compaction.js.map +1 -0
- package/dist/src/storage.d.ts +44 -0
- package/dist/src/storage.d.ts.map +1 -0
- package/dist/src/storage.js +126 -0
- package/dist/src/storage.js.map +1 -0
- package/dist/src/thread-tracker.d.ts +75 -0
- package/dist/src/thread-tracker.d.ts.map +1 -0
- package/dist/src/thread-tracker.js +250 -0
- package/dist/src/thread-tracker.js.map +1 -0
- package/dist/src/types.d.ts +202 -0
- package/dist/src/types.d.ts.map +1 -0
- package/dist/src/types.js +24 -0
- package/dist/src/types.js.map +1 -0
- package/openclaw.plugin.json +154 -0
- package/package.json +48 -0
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { Decision, ImpactLevel, PluginLogger } from "./types.js";
|
|
2
|
+
import type { PatternLanguage } from "./patterns.js";
|
|
3
|
+
export type DecisionTrackerConfig = {
|
|
4
|
+
enabled: boolean;
|
|
5
|
+
maxDecisions: number;
|
|
6
|
+
dedupeWindowHours: number;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* Infer impact level from decision context text.
|
|
10
|
+
*/
|
|
11
|
+
export declare function inferImpact(text: string): ImpactLevel;
|
|
12
|
+
/**
|
|
13
|
+
* Decision Tracker — extracts and persists decisions from messages.
|
|
14
|
+
*/
|
|
15
|
+
export declare class DecisionTracker {
|
|
16
|
+
private decisions;
|
|
17
|
+
private readonly filePath;
|
|
18
|
+
private readonly config;
|
|
19
|
+
private readonly language;
|
|
20
|
+
private readonly logger;
|
|
21
|
+
private writeable;
|
|
22
|
+
constructor(workspace: string, config: DecisionTrackerConfig, language: PatternLanguage, logger: PluginLogger);
|
|
23
|
+
/**
|
|
24
|
+
* Process a message: scan for decision patterns, dedup, persist.
|
|
25
|
+
*/
|
|
26
|
+
processMessage(content: string, sender: string): void;
|
|
27
|
+
/**
|
|
28
|
+
* Check if a decision with the same 'what' exists within the dedup window.
|
|
29
|
+
*/
|
|
30
|
+
private isDuplicate;
|
|
31
|
+
/**
|
|
32
|
+
* Enforce maxDecisions cap — remove oldest decisions first.
|
|
33
|
+
*/
|
|
34
|
+
private enforceMax;
|
|
35
|
+
/**
|
|
36
|
+
* Persist decisions to disk.
|
|
37
|
+
*/
|
|
38
|
+
private persist;
|
|
39
|
+
/**
|
|
40
|
+
* Get all decisions (in-memory).
|
|
41
|
+
*/
|
|
42
|
+
getDecisions(): Decision[];
|
|
43
|
+
/**
|
|
44
|
+
* Get recent decisions within N days.
|
|
45
|
+
*/
|
|
46
|
+
getRecentDecisions(days: number, limit: number): Decision[];
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=decision-tracker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decision-tracker.d.ts","sourceRoot":"","sources":["../../src/decision-tracker.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,QAAQ,EAER,WAAW,EACX,YAAY,EACb,MAAM,YAAY,CAAC;AAEpB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAGrD,MAAM,MAAM,qBAAqB,GAAG;IAClC,OAAO,EAAE,OAAO,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,iBAAiB,EAAE,MAAM,CAAC;CAC3B,CAAC;AAEF;;GAEG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CAMrD;AAkBD;;GAEG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,SAAS,CAAkB;IACnC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAwB;IAC/C,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAkB;IAC3C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAe;IACtC,OAAO,CAAC,SAAS,CAAQ;gBAGvB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,qBAAqB,EAC7B,QAAQ,EAAE,eAAe,EACzB,MAAM,EAAE,YAAY;IAetB;;OAEG;IACH,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;IAsCrD;;OAEG;IACH,OAAO,CAAC,WAAW;IASnB;;OAEG;IACH,OAAO,CAAC,UAAU;IAQlB;;OAEG;IACH,OAAO,CAAC,OAAO;IAgBf;;OAEG;IACH,YAAY,IAAI,QAAQ,EAAE;IAI1B;;OAEG;IACH,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,QAAQ,EAAE;CAS5D"}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { getPatterns, HIGH_IMPACT_KEYWORDS } from "./patterns.js";
|
|
4
|
+
import { loadJson, saveJson, rebootDir, ensureRebootDir } from "./storage.js";
|
|
5
|
+
/**
|
|
6
|
+
* Infer impact level from decision context text.
|
|
7
|
+
*/
|
|
8
|
+
export function inferImpact(text) {
|
|
9
|
+
const lower = text.toLowerCase();
|
|
10
|
+
for (const kw of HIGH_IMPACT_KEYWORDS) {
|
|
11
|
+
if (lower.includes(kw))
|
|
12
|
+
return "high";
|
|
13
|
+
}
|
|
14
|
+
return "medium";
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Extract context window around a match: 50 chars before, 100 chars after.
|
|
18
|
+
*/
|
|
19
|
+
function extractContext(text, matchIndex, matchLength) {
|
|
20
|
+
const start = Math.max(0, matchIndex - 50);
|
|
21
|
+
const end = Math.min(text.length, matchIndex + matchLength + 100);
|
|
22
|
+
const what = text.slice(start, end).trim();
|
|
23
|
+
// Wider context for "why"
|
|
24
|
+
const whyStart = Math.max(0, matchIndex - 100);
|
|
25
|
+
const whyEnd = Math.min(text.length, matchIndex + matchLength + 200);
|
|
26
|
+
const why = text.slice(whyStart, whyEnd).trim();
|
|
27
|
+
return { what, why };
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Decision Tracker — extracts and persists decisions from messages.
|
|
31
|
+
*/
|
|
32
|
+
export class DecisionTracker {
|
|
33
|
+
decisions = [];
|
|
34
|
+
filePath;
|
|
35
|
+
config;
|
|
36
|
+
language;
|
|
37
|
+
logger;
|
|
38
|
+
writeable = true;
|
|
39
|
+
constructor(workspace, config, language, logger) {
|
|
40
|
+
this.config = config;
|
|
41
|
+
this.language = language;
|
|
42
|
+
this.logger = logger;
|
|
43
|
+
this.filePath = join(rebootDir(workspace), "decisions.json");
|
|
44
|
+
// Ensure directory exists
|
|
45
|
+
ensureRebootDir(workspace, logger);
|
|
46
|
+
// Load existing state
|
|
47
|
+
const data = loadJson(this.filePath);
|
|
48
|
+
this.decisions = Array.isArray(data.decisions) ? data.decisions : [];
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Process a message: scan for decision patterns, dedup, persist.
|
|
52
|
+
*/
|
|
53
|
+
processMessage(content, sender) {
|
|
54
|
+
if (!content)
|
|
55
|
+
return;
|
|
56
|
+
const patterns = getPatterns(this.language);
|
|
57
|
+
const now = new Date();
|
|
58
|
+
const dateStr = now.toISOString().slice(0, 10);
|
|
59
|
+
let changed = false;
|
|
60
|
+
for (const pattern of patterns.decision) {
|
|
61
|
+
const globalPattern = new RegExp(pattern.source, "gi");
|
|
62
|
+
let match;
|
|
63
|
+
while ((match = globalPattern.exec(content)) !== null) {
|
|
64
|
+
const { what, why } = extractContext(content, match.index, match[0].length);
|
|
65
|
+
// Deduplication: skip if identical 'what' exists within dedupeWindow
|
|
66
|
+
if (this.isDuplicate(what, now))
|
|
67
|
+
continue;
|
|
68
|
+
const decision = {
|
|
69
|
+
id: randomUUID(),
|
|
70
|
+
what,
|
|
71
|
+
date: dateStr,
|
|
72
|
+
why,
|
|
73
|
+
impact: inferImpact(what + " " + why),
|
|
74
|
+
who: sender,
|
|
75
|
+
extracted_at: now.toISOString(),
|
|
76
|
+
};
|
|
77
|
+
this.decisions.push(decision);
|
|
78
|
+
changed = true;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
if (changed) {
|
|
82
|
+
this.enforceMax();
|
|
83
|
+
this.persist();
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Check if a decision with the same 'what' exists within the dedup window.
|
|
88
|
+
*/
|
|
89
|
+
isDuplicate(what, now) {
|
|
90
|
+
const windowMs = this.config.dedupeWindowHours * 60 * 60 * 1000;
|
|
91
|
+
const cutoff = new Date(now.getTime() - windowMs).toISOString();
|
|
92
|
+
return this.decisions.some(d => d.what === what && d.extracted_at >= cutoff);
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Enforce maxDecisions cap — remove oldest decisions first.
|
|
96
|
+
*/
|
|
97
|
+
enforceMax() {
|
|
98
|
+
if (this.decisions.length > this.config.maxDecisions) {
|
|
99
|
+
this.decisions = this.decisions.slice(this.decisions.length - this.config.maxDecisions);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Persist decisions to disk.
|
|
104
|
+
*/
|
|
105
|
+
persist() {
|
|
106
|
+
if (!this.writeable)
|
|
107
|
+
return;
|
|
108
|
+
const data = {
|
|
109
|
+
version: 1,
|
|
110
|
+
updated: new Date().toISOString(),
|
|
111
|
+
decisions: this.decisions,
|
|
112
|
+
};
|
|
113
|
+
const ok = saveJson(this.filePath, data, this.logger);
|
|
114
|
+
if (!ok) {
|
|
115
|
+
this.writeable = false;
|
|
116
|
+
this.logger.warn("[cortex] Decision tracker: workspace not writable");
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Get all decisions (in-memory).
|
|
121
|
+
*/
|
|
122
|
+
getDecisions() {
|
|
123
|
+
return [...this.decisions];
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Get recent decisions within N days.
|
|
127
|
+
*/
|
|
128
|
+
getRecentDecisions(days, limit) {
|
|
129
|
+
const cutoff = new Date(Date.now() - days * 24 * 60 * 60 * 1000).toISOString().slice(0, 10);
|
|
130
|
+
return this.decisions
|
|
131
|
+
.filter(d => d.date >= cutoff)
|
|
132
|
+
.slice(-limit);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
//# sourceMappingURL=decision-tracker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decision-tracker.js","sourceRoot":"","sources":["../../src/decision-tracker.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { OpenClawPluginApi, CortexConfig } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Register all cortex hook handlers on the plugin API.
|
|
4
|
+
* Each handler is wrapped in try/catch — never throws.
|
|
5
|
+
*/
|
|
6
|
+
export declare function registerCortexHooks(api: OpenClawPluginApi, config: CortexConfig): void;
|
|
7
|
+
//# sourceMappingURL=hooks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../../src/hooks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,iBAAiB,EACjB,YAAY,EAGb,MAAM,YAAY,CAAC;AAqGpB;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,iBAAiB,EAAE,MAAM,EAAE,YAAY,GAAG,IAAI,CAUtF"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { resolveWorkspace } from "./config.js";
|
|
2
|
+
import { ThreadTracker } from "./thread-tracker.js";
|
|
3
|
+
import { DecisionTracker } from "./decision-tracker.js";
|
|
4
|
+
import { BootContextGenerator } from "./boot-context.js";
|
|
5
|
+
import { PreCompaction } from "./pre-compaction.js";
|
|
6
|
+
/**
|
|
7
|
+
* Extract message content from a hook event using the fallback chain.
|
|
8
|
+
*/
|
|
9
|
+
function extractContent(event) {
|
|
10
|
+
return event.content ?? event.message ?? event.text ?? "";
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Extract sender from a hook event.
|
|
14
|
+
*/
|
|
15
|
+
function extractSender(event) {
|
|
16
|
+
return event.from ?? event.sender ?? event.role ?? "unknown";
|
|
17
|
+
}
|
|
18
|
+
function ensureInit(state, config, logger, ctx) {
|
|
19
|
+
if (!state.workspace) {
|
|
20
|
+
state.workspace = resolveWorkspace(config, ctx);
|
|
21
|
+
}
|
|
22
|
+
if (!state.threadTracker && config.threadTracker.enabled) {
|
|
23
|
+
state.threadTracker = new ThreadTracker(state.workspace, config.threadTracker, config.patterns.language, logger);
|
|
24
|
+
}
|
|
25
|
+
if (!state.decisionTracker && config.decisionTracker.enabled) {
|
|
26
|
+
state.decisionTracker = new DecisionTracker(state.workspace, config.decisionTracker, config.patterns.language, logger);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
/** Register message hooks (message_received + message_sent). */
|
|
30
|
+
function registerMessageHooks(api, config, state) {
|
|
31
|
+
if (!config.threadTracker.enabled && !config.decisionTracker.enabled)
|
|
32
|
+
return;
|
|
33
|
+
const handler = (event, ctx, senderOverride) => {
|
|
34
|
+
try {
|
|
35
|
+
ensureInit(state, config, api.logger, ctx);
|
|
36
|
+
const content = extractContent(event);
|
|
37
|
+
const sender = senderOverride ?? extractSender(event);
|
|
38
|
+
if (!content)
|
|
39
|
+
return;
|
|
40
|
+
if (config.threadTracker.enabled && state.threadTracker)
|
|
41
|
+
state.threadTracker.processMessage(content, sender);
|
|
42
|
+
if (config.decisionTracker.enabled && state.decisionTracker)
|
|
43
|
+
state.decisionTracker.processMessage(content, sender);
|
|
44
|
+
}
|
|
45
|
+
catch (err) {
|
|
46
|
+
api.logger.warn(`[cortex] message hook error: ${err}`);
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
api.on("message_received", (event, ctx) => handler(event, ctx), { priority: 100 });
|
|
50
|
+
api.on("message_sent", (event, ctx) => handler(event, ctx, event.role ?? "assistant"), { priority: 100 });
|
|
51
|
+
}
|
|
52
|
+
/** Register session_start hook for boot context. */
|
|
53
|
+
function registerSessionHooks(api, config, state) {
|
|
54
|
+
if (!config.bootContext.enabled || !config.bootContext.onSessionStart)
|
|
55
|
+
return;
|
|
56
|
+
api.on("session_start", (_event, ctx) => {
|
|
57
|
+
try {
|
|
58
|
+
ensureInit(state, config, api.logger, ctx);
|
|
59
|
+
new BootContextGenerator(state.workspace, config.bootContext, api.logger).write();
|
|
60
|
+
api.logger.info("[cortex] Boot context generated on session start");
|
|
61
|
+
}
|
|
62
|
+
catch (err) {
|
|
63
|
+
api.logger.warn(`[cortex] session_start error: ${err}`);
|
|
64
|
+
}
|
|
65
|
+
}, { priority: 10 });
|
|
66
|
+
}
|
|
67
|
+
/** Register compaction hooks (before + after). */
|
|
68
|
+
function registerCompactionHooks(api, config, state) {
|
|
69
|
+
if (config.preCompaction.enabled) {
|
|
70
|
+
api.on("before_compaction", (event, ctx) => {
|
|
71
|
+
try {
|
|
72
|
+
ensureInit(state, config, api.logger, ctx);
|
|
73
|
+
const tracker = state.threadTracker ?? new ThreadTracker(state.workspace, config.threadTracker, config.patterns.language, api.logger);
|
|
74
|
+
const result = new PreCompaction(state.workspace, config, api.logger, tracker).run(event.compactingMessages);
|
|
75
|
+
if (result.warnings.length > 0)
|
|
76
|
+
api.logger.warn(`[cortex] Pre-compaction warnings: ${result.warnings.join("; ")}`);
|
|
77
|
+
api.logger.info(`[cortex] Pre-compaction complete: ${result.messagesSnapshotted} messages snapshotted`);
|
|
78
|
+
}
|
|
79
|
+
catch (err) {
|
|
80
|
+
api.logger.warn(`[cortex] before_compaction error: ${err}`);
|
|
81
|
+
}
|
|
82
|
+
}, { priority: 5 });
|
|
83
|
+
}
|
|
84
|
+
api.on("after_compaction", () => {
|
|
85
|
+
try {
|
|
86
|
+
api.logger.info(`[cortex] Compaction completed at ${new Date().toISOString()}`);
|
|
87
|
+
}
|
|
88
|
+
catch (err) {
|
|
89
|
+
api.logger.warn(`[cortex] after_compaction error: ${err}`);
|
|
90
|
+
}
|
|
91
|
+
}, { priority: 200 });
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Register all cortex hook handlers on the plugin API.
|
|
95
|
+
* Each handler is wrapped in try/catch — never throws.
|
|
96
|
+
*/
|
|
97
|
+
export function registerCortexHooks(api, config) {
|
|
98
|
+
const state = { workspace: null, threadTracker: null, decisionTracker: null };
|
|
99
|
+
registerMessageHooks(api, config, state);
|
|
100
|
+
registerSessionHooks(api, config, state);
|
|
101
|
+
registerCompactionHooks(api, config, state);
|
|
102
|
+
api.logger.info(`[cortex] Hooks registered — threads:${config.threadTracker.enabled} decisions:${config.decisionTracker.enabled} boot:${config.bootContext.enabled} compaction:${config.preCompaction.enabled}`);
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=hooks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hooks.js","sourceRoot":"","sources":["../../src/hooks.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { Thread, Decision, NarrativeSections, PluginLogger } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Load daily notes for today and yesterday.
|
|
4
|
+
*/
|
|
5
|
+
export declare function loadDailyNotes(workspace: string): string;
|
|
6
|
+
/**
|
|
7
|
+
* Extract timeline entries from daily notes.
|
|
8
|
+
*/
|
|
9
|
+
export declare function extractTimeline(notes: string): string[];
|
|
10
|
+
/**
|
|
11
|
+
* Build narrative sections from data.
|
|
12
|
+
*/
|
|
13
|
+
export declare function buildSections(threads: Thread[], decisions: Decision[], notes: string): NarrativeSections;
|
|
14
|
+
/**
|
|
15
|
+
* Generate a structured narrative from sections.
|
|
16
|
+
*/
|
|
17
|
+
export declare function generateStructured(sections: NarrativeSections): string;
|
|
18
|
+
/**
|
|
19
|
+
* Narrative Generator — creates a structured narrative from recent activity.
|
|
20
|
+
*/
|
|
21
|
+
export declare class NarrativeGenerator {
|
|
22
|
+
private readonly workspace;
|
|
23
|
+
private readonly logger;
|
|
24
|
+
constructor(workspace: string, logger: PluginLogger);
|
|
25
|
+
/**
|
|
26
|
+
* Generate and write narrative.md.
|
|
27
|
+
*/
|
|
28
|
+
generate(): string;
|
|
29
|
+
/**
|
|
30
|
+
* Generate and write to disk.
|
|
31
|
+
*/
|
|
32
|
+
write(): boolean;
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=narrative-generator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"narrative-generator.d.ts","sourceRoot":"","sources":["../../src/narrative-generator.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,MAAM,EACN,QAAQ,EAGR,iBAAiB,EACjB,YAAY,EACb,MAAM,YAAY,CAAC;AAGpB;;GAEG;AACH,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAiBxD;AA4BD;;GAEG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAYvD;AAED;;GAEG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,MAAM,EAAE,EACjB,SAAS,EAAE,QAAQ,EAAE,EACrB,KAAK,EAAE,MAAM,GACZ,iBAAiB,CAanB;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,iBAAiB,GAAG,MAAM,CAqDtE;AAED;;GAEG;AACH,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAe;gBAE1B,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY;IAKnD;;OAEG;IACH,QAAQ,IAAI,MAAM;IAWlB;;OAEG;IACH,KAAK,IAAI,OAAO;CAUjB"}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
import { loadJson, loadText, rebootDir, saveText, ensureRebootDir } from "./storage.js";
|
|
3
|
+
/**
|
|
4
|
+
* Load daily notes for today and yesterday.
|
|
5
|
+
*/
|
|
6
|
+
export function loadDailyNotes(workspace) {
|
|
7
|
+
const parts = [];
|
|
8
|
+
const now = new Date();
|
|
9
|
+
const today = now.toISOString().slice(0, 10);
|
|
10
|
+
const yesterday = new Date(now.getTime() - 24 * 60 * 60 * 1000)
|
|
11
|
+
.toISOString()
|
|
12
|
+
.slice(0, 10);
|
|
13
|
+
for (const date of [yesterday, today]) {
|
|
14
|
+
const filePath = join(workspace, "memory", `${date}.md`);
|
|
15
|
+
const content = loadText(filePath);
|
|
16
|
+
if (content) {
|
|
17
|
+
parts.push(`## ${date}\n${content.slice(0, 4000)}`);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return parts.join("\n\n");
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Load threads from threads.json.
|
|
24
|
+
*/
|
|
25
|
+
function loadThreads(workspace) {
|
|
26
|
+
const data = loadJson(join(rebootDir(workspace), "threads.json"));
|
|
27
|
+
return Array.isArray(data.threads) ? data.threads : [];
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Load recent decisions (from last 24h).
|
|
31
|
+
*/
|
|
32
|
+
function loadRecentDecisions(workspace) {
|
|
33
|
+
const data = loadJson(join(rebootDir(workspace), "decisions.json"));
|
|
34
|
+
const decisions = Array.isArray(data.decisions) ? data.decisions : [];
|
|
35
|
+
const yesterday = new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString().slice(0, 10);
|
|
36
|
+
return decisions.filter(d => d.date >= yesterday);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Extract timeline entries from daily notes.
|
|
40
|
+
*/
|
|
41
|
+
export function extractTimeline(notes) {
|
|
42
|
+
const entries = [];
|
|
43
|
+
for (const line of notes.split("\n")) {
|
|
44
|
+
const trimmed = line.trim();
|
|
45
|
+
// Skip date headers (## 2026-02-17)
|
|
46
|
+
if (trimmed.startsWith("## ") && !trimmed.match(/^## \d{4}-\d{2}-\d{2}/)) {
|
|
47
|
+
entries.push(trimmed.slice(3));
|
|
48
|
+
}
|
|
49
|
+
else if (trimmed.startsWith("### ")) {
|
|
50
|
+
entries.push(` ${trimmed.slice(4)}`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return entries;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Build narrative sections from data.
|
|
57
|
+
*/
|
|
58
|
+
export function buildSections(threads, decisions, notes) {
|
|
59
|
+
const now = new Date();
|
|
60
|
+
const yesterday = new Date(now.getTime() - 24 * 60 * 60 * 1000).toISOString().slice(0, 10);
|
|
61
|
+
const completed = threads.filter(t => t.status === "closed" && t.last_activity.slice(0, 10) >= yesterday);
|
|
62
|
+
const open = threads.filter(t => t.status === "open");
|
|
63
|
+
const timelineEntries = extractTimeline(notes);
|
|
64
|
+
return { completed, open, decisions, timelineEntries };
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Generate a structured narrative from sections.
|
|
68
|
+
*/
|
|
69
|
+
export function generateStructured(sections) {
|
|
70
|
+
const now = new Date();
|
|
71
|
+
const dayNames = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
|
|
72
|
+
const monthNames = [
|
|
73
|
+
"January", "February", "March", "April", "May", "June",
|
|
74
|
+
"July", "August", "September", "October", "November", "December",
|
|
75
|
+
];
|
|
76
|
+
const day = dayNames[now.getDay()];
|
|
77
|
+
const date = now.getDate();
|
|
78
|
+
const month = monthNames[now.getMonth()];
|
|
79
|
+
const year = now.getFullYear();
|
|
80
|
+
const parts = [
|
|
81
|
+
`*${day}, ${String(date).padStart(2, "0")}. ${month} ${year} — Narrative*\n`,
|
|
82
|
+
];
|
|
83
|
+
if (sections.completed.length > 0) {
|
|
84
|
+
parts.push("**Completed:**");
|
|
85
|
+
for (const t of sections.completed) {
|
|
86
|
+
parts.push(`- ✅ ${t.title}: ${(t.summary || "").slice(0, 100)}`);
|
|
87
|
+
}
|
|
88
|
+
parts.push("");
|
|
89
|
+
}
|
|
90
|
+
if (sections.open.length > 0) {
|
|
91
|
+
parts.push("**Open:**");
|
|
92
|
+
for (const t of sections.open) {
|
|
93
|
+
const emoji = t.priority === "critical" ? "🔴" : "🟡";
|
|
94
|
+
parts.push(`- ${emoji} ${t.title}: ${(t.summary || "").slice(0, 150)}`);
|
|
95
|
+
if (t.waiting_for) {
|
|
96
|
+
parts.push(` ⏳ ${t.waiting_for}`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
parts.push("");
|
|
100
|
+
}
|
|
101
|
+
if (sections.decisions.length > 0) {
|
|
102
|
+
parts.push("**Decisions:**");
|
|
103
|
+
for (const d of sections.decisions) {
|
|
104
|
+
parts.push(`- ${d.what} — ${(d.why || "").slice(0, 80)}`);
|
|
105
|
+
}
|
|
106
|
+
parts.push("");
|
|
107
|
+
}
|
|
108
|
+
if (sections.timelineEntries.length > 0) {
|
|
109
|
+
parts.push("**Timeline:**");
|
|
110
|
+
for (const entry of sections.timelineEntries) {
|
|
111
|
+
parts.push(`- ${entry}`);
|
|
112
|
+
}
|
|
113
|
+
parts.push("");
|
|
114
|
+
}
|
|
115
|
+
return parts.join("\n");
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Narrative Generator — creates a structured narrative from recent activity.
|
|
119
|
+
*/
|
|
120
|
+
export class NarrativeGenerator {
|
|
121
|
+
workspace;
|
|
122
|
+
logger;
|
|
123
|
+
constructor(workspace, logger) {
|
|
124
|
+
this.workspace = workspace;
|
|
125
|
+
this.logger = logger;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Generate and write narrative.md.
|
|
129
|
+
*/
|
|
130
|
+
generate() {
|
|
131
|
+
ensureRebootDir(this.workspace, this.logger);
|
|
132
|
+
const notes = loadDailyNotes(this.workspace);
|
|
133
|
+
const threads = loadThreads(this.workspace);
|
|
134
|
+
const decisions = loadRecentDecisions(this.workspace);
|
|
135
|
+
const sections = buildSections(threads, decisions, notes);
|
|
136
|
+
return generateStructured(sections);
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Generate and write to disk.
|
|
140
|
+
*/
|
|
141
|
+
write() {
|
|
142
|
+
try {
|
|
143
|
+
const narrative = this.generate();
|
|
144
|
+
const filePath = join(rebootDir(this.workspace), "narrative.md");
|
|
145
|
+
return saveText(filePath, narrative, this.logger);
|
|
146
|
+
}
|
|
147
|
+
catch (err) {
|
|
148
|
+
this.logger.warn(`[cortex] Narrative generation failed: ${err}`);
|
|
149
|
+
return false;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
//# sourceMappingURL=narrative-generator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"narrative-generator.js","sourceRoot":"","sources":["../../src/narrative-generator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { Mood } from "./types.js";
|
|
2
|
+
declare const MOOD_PATTERNS: Record<Exclude<Mood, "neutral">, RegExp>;
|
|
3
|
+
export type PatternLanguage = "en" | "de" | "both";
|
|
4
|
+
export type PatternSet = {
|
|
5
|
+
decision: RegExp[];
|
|
6
|
+
close: RegExp[];
|
|
7
|
+
wait: RegExp[];
|
|
8
|
+
topic: RegExp[];
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Get pattern set for the configured language.
|
|
12
|
+
* "both" merges EN + DE patterns.
|
|
13
|
+
*/
|
|
14
|
+
export declare function getPatterns(language: PatternLanguage): PatternSet;
|
|
15
|
+
/**
|
|
16
|
+
* Detect mood from text. Scans for all mood patterns; last match position wins.
|
|
17
|
+
* Returns "neutral" if no mood pattern matches.
|
|
18
|
+
*/
|
|
19
|
+
export declare function detectMood(text: string): Mood;
|
|
20
|
+
/** High-impact keywords for decision impact inference */
|
|
21
|
+
export declare const HIGH_IMPACT_KEYWORDS: string[];
|
|
22
|
+
/** Export mood patterns for testing */
|
|
23
|
+
export { MOOD_PATTERNS };
|
|
24
|
+
//# sourceMappingURL=patterns.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"patterns.d.ts","sourceRoot":"","sources":["../../src/patterns.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAyCvC,QAAA,MAAM,aAAa,EAAE,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,SAAS,CAAC,EAAE,MAAM,CAM3D,CAAC;AAMF,MAAM,MAAM,eAAe,GAAG,IAAI,GAAG,IAAI,GAAG,MAAM,CAAC;AAEnD,MAAM,MAAM,UAAU,GAAG;IACvB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB,CAAC;AAEF;;;GAGG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,eAAe,GAAG,UAAU,CAwBjE;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAmB7C;AAED,yDAAyD;AACzD,eAAO,MAAM,oBAAoB,UAKhC,CAAC;AAEF,uCAAuC;AACvC,OAAO,EAAE,aAAa,EAAE,CAAC"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
// ============================================================
|
|
2
|
+
// Pattern sets by language
|
|
3
|
+
// ============================================================
|
|
4
|
+
const DECISION_PATTERNS_EN = [
|
|
5
|
+
/(?:decided|decision|agreed|let'?s do|the plan is|approach:)/i,
|
|
6
|
+
];
|
|
7
|
+
const DECISION_PATTERNS_DE = [
|
|
8
|
+
/(?:entschieden|beschlossen|machen wir|wir machen|der plan ist|ansatz:)/i,
|
|
9
|
+
];
|
|
10
|
+
const CLOSE_PATTERNS_EN = [
|
|
11
|
+
/(?:^|\s)(?:is |it's |that's |all )?(?:done|fixed|solved|closed)(?:\s|[.!]|$)/i,
|
|
12
|
+
/(?:^|\s)(?:it |that )works(?:\s|[.!]|$)/i,
|
|
13
|
+
/✅/,
|
|
14
|
+
];
|
|
15
|
+
const CLOSE_PATTERNS_DE = [
|
|
16
|
+
/(?:^|\s)(?:ist |schon )?(?:erledigt|gefixt|gelöst|fertig)(?:\s|[.!]|$)/i,
|
|
17
|
+
/(?:^|\s)(?:es |das )funktioniert(?:\s|[.!]|$)/i,
|
|
18
|
+
];
|
|
19
|
+
const WAIT_PATTERNS_EN = [
|
|
20
|
+
/(?:waiting for|blocked by|need.*first)/i,
|
|
21
|
+
];
|
|
22
|
+
const WAIT_PATTERNS_DE = [
|
|
23
|
+
/(?:warte auf|blockiert durch|brauche.*erst)/i,
|
|
24
|
+
];
|
|
25
|
+
const TOPIC_PATTERNS_EN = [
|
|
26
|
+
/(?:back to|now about|regarding)\s+(\w[\w\s-]{2,30})/i,
|
|
27
|
+
];
|
|
28
|
+
const TOPIC_PATTERNS_DE = [
|
|
29
|
+
/(?:zurück zu|jetzt zu|bzgl\.?|wegen)\s+(\w[\w\s-]{2,30})/i,
|
|
30
|
+
];
|
|
31
|
+
const MOOD_PATTERNS = {
|
|
32
|
+
frustrated: /(?:fuck|shit|mist|nervig|genervt|damn|wtf|argh|schon wieder|zum kotzen|sucks)/i,
|
|
33
|
+
excited: /(?:geil|nice|awesome|krass|boom|läuft|yes!|🎯|🚀|perfekt|brilliant|mega|sick)/i,
|
|
34
|
+
tense: /(?:vorsicht|careful|risky|heikel|kritisch|dringend|urgent|achtung|gefährlich)/i,
|
|
35
|
+
productive: /(?:erledigt|done|fixed|works|fertig|deployed|✅|gebaut|shipped|läuft)/i,
|
|
36
|
+
exploratory: /(?:was wäre wenn|what if|könnte man|idea|idee|maybe|vielleicht|experiment)/i,
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* Get pattern set for the configured language.
|
|
40
|
+
* "both" merges EN + DE patterns.
|
|
41
|
+
*/
|
|
42
|
+
export function getPatterns(language) {
|
|
43
|
+
switch (language) {
|
|
44
|
+
case "en":
|
|
45
|
+
return {
|
|
46
|
+
decision: DECISION_PATTERNS_EN,
|
|
47
|
+
close: CLOSE_PATTERNS_EN,
|
|
48
|
+
wait: WAIT_PATTERNS_EN,
|
|
49
|
+
topic: TOPIC_PATTERNS_EN,
|
|
50
|
+
};
|
|
51
|
+
case "de":
|
|
52
|
+
return {
|
|
53
|
+
decision: DECISION_PATTERNS_DE,
|
|
54
|
+
close: CLOSE_PATTERNS_DE,
|
|
55
|
+
wait: WAIT_PATTERNS_DE,
|
|
56
|
+
topic: TOPIC_PATTERNS_DE,
|
|
57
|
+
};
|
|
58
|
+
case "both":
|
|
59
|
+
return {
|
|
60
|
+
decision: [...DECISION_PATTERNS_EN, ...DECISION_PATTERNS_DE],
|
|
61
|
+
close: [...CLOSE_PATTERNS_EN, ...CLOSE_PATTERNS_DE],
|
|
62
|
+
wait: [...WAIT_PATTERNS_EN, ...WAIT_PATTERNS_DE],
|
|
63
|
+
topic: [...TOPIC_PATTERNS_EN, ...TOPIC_PATTERNS_DE],
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Detect mood from text. Scans for all mood patterns; last match position wins.
|
|
69
|
+
* Returns "neutral" if no mood pattern matches.
|
|
70
|
+
*/
|
|
71
|
+
export function detectMood(text) {
|
|
72
|
+
if (!text)
|
|
73
|
+
return "neutral";
|
|
74
|
+
let lastMood = "neutral";
|
|
75
|
+
let lastPos = -1;
|
|
76
|
+
for (const [mood, pattern] of Object.entries(MOOD_PATTERNS)) {
|
|
77
|
+
// Use global flag for position scanning
|
|
78
|
+
const globalPattern = new RegExp(pattern.source, "gi");
|
|
79
|
+
let match;
|
|
80
|
+
while ((match = globalPattern.exec(text)) !== null) {
|
|
81
|
+
if (match.index > lastPos) {
|
|
82
|
+
lastPos = match.index;
|
|
83
|
+
lastMood = mood;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return lastMood;
|
|
88
|
+
}
|
|
89
|
+
/** High-impact keywords for decision impact inference */
|
|
90
|
+
export const HIGH_IMPACT_KEYWORDS = [
|
|
91
|
+
"architecture", "architektur", "security", "sicherheit",
|
|
92
|
+
"migration", "delete", "löschen", "production", "produktion",
|
|
93
|
+
"deploy", "breaking", "major", "critical", "kritisch",
|
|
94
|
+
"strategy", "strategie", "budget", "contract", "vertrag",
|
|
95
|
+
];
|
|
96
|
+
/** Export mood patterns for testing */
|
|
97
|
+
export { MOOD_PATTERNS };
|
|
98
|
+
//# sourceMappingURL=patterns.js.map
|