@ouro.bot/cli 0.1.0-alpha.184 → 0.1.0-alpha.185

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.
@@ -0,0 +1,185 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.derivePresence = derivePresence;
37
+ exports.writePresence = writePresence;
38
+ exports.readPresence = readPresence;
39
+ exports.readPeerPresence = readPeerPresence;
40
+ const fs = __importStar(require("fs"));
41
+ const path = __importStar(require("path"));
42
+ const os = __importStar(require("os"));
43
+ const runtime_1 = require("../nerves/runtime");
44
+ function presenceDir(agentRoot) {
45
+ return path.join(agentRoot, "state", "presence");
46
+ }
47
+ function deriveAvailability(inputs) {
48
+ if (inputs.currentTempo === "crisis")
49
+ return "dnd";
50
+ if (inputs.activeSessions > 0)
51
+ return "active";
52
+ if (inputs.openObligations > 0)
53
+ return "idle";
54
+ return "away";
55
+ }
56
+ function deriveLane(inputs) {
57
+ const hasCoding = inputs.codingLanes > 0;
58
+ const hasBridges = inputs.activeBridges > 0;
59
+ if (hasCoding && hasBridges)
60
+ return "mixed";
61
+ if (hasCoding)
62
+ return "coding";
63
+ if (hasBridges)
64
+ return "conversation";
65
+ return "idle";
66
+ }
67
+ function deriveMission(inputs) {
68
+ const parts = [];
69
+ if (inputs.openObligations > 0) {
70
+ parts.push(`${inputs.openObligations} open obligation${inputs.openObligations !== 1 ? "s" : ""}`);
71
+ }
72
+ if (inputs.activeBridges > 0) {
73
+ parts.push(`${inputs.activeBridges} active bridge${inputs.activeBridges !== 1 ? "s" : ""}`);
74
+ }
75
+ if (inputs.codingLanes > 0) {
76
+ parts.push(`${inputs.codingLanes} coding lane${inputs.codingLanes !== 1 ? "s" : ""}`);
77
+ }
78
+ if (parts.length === 0)
79
+ return "no active work";
80
+ return parts.join(", ");
81
+ }
82
+ function derivePresence(_agentRoot, agentName, inputs) {
83
+ const presence = {
84
+ agentName,
85
+ availability: deriveAvailability(inputs),
86
+ lane: deriveLane(inputs),
87
+ mission: deriveMission(inputs),
88
+ tempo: inputs.currentTempo,
89
+ updatedAt: new Date().toISOString(),
90
+ };
91
+ (0, runtime_1.emitNervesEvent)({
92
+ component: "heart",
93
+ event: "heart.presence_derived",
94
+ message: `presence derived: ${presence.availability}/${presence.lane}`,
95
+ meta: {
96
+ agentName,
97
+ availability: presence.availability,
98
+ lane: presence.lane,
99
+ tempo: presence.tempo,
100
+ },
101
+ });
102
+ return presence;
103
+ }
104
+ function writePresence(agentRoot, _agentName, presence) {
105
+ const dir = presenceDir(agentRoot);
106
+ fs.mkdirSync(dir, { recursive: true });
107
+ fs.writeFileSync(path.join(dir, "self.json"), JSON.stringify(presence, null, 2), "utf-8");
108
+ (0, runtime_1.emitNervesEvent)({
109
+ component: "heart",
110
+ event: "heart.presence_written",
111
+ message: `presence written for ${presence.agentName}`,
112
+ meta: { agentName: presence.agentName },
113
+ });
114
+ }
115
+ function readPresence(agentRoot, _agentName) {
116
+ const filePath = path.join(presenceDir(agentRoot), "self.json");
117
+ if (!fs.existsSync(filePath)) {
118
+ (0, runtime_1.emitNervesEvent)({
119
+ component: "heart",
120
+ event: "heart.presence_read",
121
+ message: "presence file not found",
122
+ meta: { found: false },
123
+ });
124
+ return null;
125
+ }
126
+ try {
127
+ const raw = fs.readFileSync(filePath, "utf-8");
128
+ const presence = JSON.parse(raw);
129
+ (0, runtime_1.emitNervesEvent)({
130
+ component: "heart",
131
+ event: "heart.presence_read",
132
+ message: `presence read: ${presence.availability}`,
133
+ meta: { agentName: presence.agentName, found: true },
134
+ });
135
+ return presence;
136
+ }
137
+ catch {
138
+ (0, runtime_1.emitNervesEvent)({
139
+ component: "heart",
140
+ event: "heart.presence_read_error",
141
+ message: "failed to read presence file",
142
+ meta: { error: "parse error" },
143
+ });
144
+ return null;
145
+ }
146
+ }
147
+ function readPeerPresence(agentRoot) {
148
+ const relDir = path.join(agentRoot, "state", "relationships");
149
+ if (!fs.existsSync(relDir)) {
150
+ (0, runtime_1.emitNervesEvent)({
151
+ component: "heart",
152
+ event: "heart.peer_presence_read",
153
+ message: "no relationships directory, no peers",
154
+ meta: { peerCount: 0 },
155
+ });
156
+ return [];
157
+ }
158
+ const files = fs.readdirSync(relDir).filter((f) => f.endsWith(".json"));
159
+ const peers = [];
160
+ for (const file of files) {
161
+ try {
162
+ const raw = fs.readFileSync(path.join(relDir, file), "utf-8");
163
+ const rel = JSON.parse(raw);
164
+ const peerName = rel.agentName;
165
+ // Read peer's presence from their bundle
166
+ const peerRoot = path.join(os.homedir(), "AgentBundles", `${peerName}.ouro`);
167
+ const peerPresencePath = path.join(peerRoot, "state", "presence", "self.json");
168
+ if (fs.existsSync(peerPresencePath)) {
169
+ const presenceRaw = fs.readFileSync(peerPresencePath, "utf-8");
170
+ const presence = JSON.parse(presenceRaw);
171
+ peers.push(presence);
172
+ }
173
+ }
174
+ catch {
175
+ // Skip peers with unreadable data
176
+ }
177
+ }
178
+ (0, runtime_1.emitNervesEvent)({
179
+ component: "heart",
180
+ event: "heart.peer_presence_read",
181
+ message: `read ${peers.length} peer presence records`,
182
+ meta: { peerCount: peers.length },
183
+ });
184
+ return peers;
185
+ }
@@ -0,0 +1,93 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TEMPO_BUDGETS = void 0;
4
+ exports.deriveTempo = deriveTempo;
5
+ exports.getTokenBudget = getTokenBudget;
6
+ exports.detectTempoShift = detectTempoShift;
7
+ const runtime_1 = require("../nerves/runtime");
8
+ exports.TEMPO_BUDGETS = {
9
+ brief: { min: 150, max: 250 },
10
+ standard: { min: 450, max: 700 },
11
+ dense: { min: 900, max: 1100 },
12
+ crisis: { min: 700, max: 1000 },
13
+ };
14
+ const FIVE_MINUTES_MS = 5 * 60 * 1000;
15
+ const TWO_HOURS_MS = 2 * 60 * 60 * 1000;
16
+ function deriveMode(inputs) {
17
+ if (inputs.hasBlockers)
18
+ return "crisis";
19
+ if (inputs.openObligations > 3 || inputs.recentEpisodeCount > 10)
20
+ return "dense";
21
+ if (inputs.activeSessions > 0)
22
+ return "standard";
23
+ return "brief";
24
+ }
25
+ function deriveSignificance(inputs) {
26
+ if (inputs.highSalienceEpisodes > 0 || inputs.atRiskCareCount > 0)
27
+ return "high";
28
+ if (inputs.activeCareCount > 0)
29
+ return "medium";
30
+ return "low";
31
+ }
32
+ function deriveReentryDepth(inputs) {
33
+ if (inputs.lastActivityAgeMs < FIVE_MINUTES_MS)
34
+ return "warm";
35
+ if (inputs.lastActivityAgeMs < TWO_HOURS_MS)
36
+ return "cooled";
37
+ return "cold";
38
+ }
39
+ function computeEffectiveBudget(mode, significance, reentryDepth) {
40
+ const base = exports.TEMPO_BUDGETS[mode];
41
+ // Crisis always uses crisis budget regardless
42
+ if (mode === "crisis")
43
+ return { ...base };
44
+ const range = base.max - base.min;
45
+ // Start at the mode minimum
46
+ let effectiveMin = base.min;
47
+ // Significance pushes up within range
48
+ if (significance === "high") {
49
+ effectiveMin += Math.round(range * 0.5);
50
+ }
51
+ else if (significance === "medium") {
52
+ effectiveMin += Math.round(range * 0.25);
53
+ }
54
+ // Cold re-entry pushes up (need more context to re-orient)
55
+ if (reentryDepth === "cold") {
56
+ effectiveMin += Math.round(range * 0.3);
57
+ }
58
+ else if (reentryDepth === "cooled") {
59
+ effectiveMin += Math.round(range * 0.1);
60
+ }
61
+ // Clamp within mode range
62
+ effectiveMin = Math.min(effectiveMin, base.max);
63
+ return { min: effectiveMin, max: base.max };
64
+ }
65
+ function deriveTempo(inputs) {
66
+ const mode = deriveMode(inputs);
67
+ const significance = deriveSignificance(inputs);
68
+ const reentryDepth = deriveReentryDepth(inputs);
69
+ const effectiveBudget = computeEffectiveBudget(mode, significance, reentryDepth);
70
+ (0, runtime_1.emitNervesEvent)({
71
+ component: "heart",
72
+ event: "heart.tempo_derived",
73
+ message: `tempo derived: ${mode}/${significance}/${reentryDepth}`,
74
+ meta: {
75
+ mode,
76
+ significance,
77
+ reentryDepth,
78
+ effectiveMin: effectiveBudget.min,
79
+ effectiveMax: effectiveBudget.max,
80
+ },
81
+ });
82
+ return { mode, significance, reentryDepth, effectiveBudget };
83
+ }
84
+ function getTokenBudget(state) {
85
+ return state.effectiveBudget;
86
+ }
87
+ function detectTempoShift(previous, current) {
88
+ if (!previous)
89
+ return true;
90
+ return (previous.mode !== current.mode
91
+ || previous.significance !== current.significance
92
+ || previous.reentryDepth !== current.reentryDepth);
93
+ }
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildTemporalView = buildTemporalView;
4
+ const runtime_1 = require("../nerves/runtime");
5
+ const episodes_1 = require("../mind/episodes");
6
+ const obligations_1 = require("./obligations");
7
+ const cares_1 = require("./cares");
8
+ const intentions_1 = require("./intentions");
9
+ const presence_1 = require("./presence");
10
+ function buildTemporalView(agentRoot, options) {
11
+ const episodeLimit = options?.episodeLimit ?? 20;
12
+ const tempo = options?.tempo ?? "brief";
13
+ const recentEpisodes = options?.preloaded?.recentEpisodes ?? (0, episodes_1.readRecentEpisodes)(agentRoot, { limit: episodeLimit });
14
+ const activeObligations = options?.preloaded?.activeObligations ?? (0, obligations_1.readPendingObligations)(agentRoot);
15
+ const activeCares = options?.preloaded?.activeCares ?? (0, cares_1.readActiveCares)(agentRoot);
16
+ const openIntentions = (0, intentions_1.readOpenIntentions)(agentRoot);
17
+ const peerPresence = (0, presence_1.readPeerPresence)(agentRoot);
18
+ const view = {
19
+ recentEpisodes,
20
+ activeObligations,
21
+ activeCares,
22
+ openIntentions,
23
+ peerPresence,
24
+ tempo,
25
+ assembledAt: new Date().toISOString(),
26
+ };
27
+ (0, runtime_1.emitNervesEvent)({
28
+ component: "heart",
29
+ event: "heart.temporal_view_assembled",
30
+ message: `temporal view assembled: ${recentEpisodes.length} episodes, ${activeObligations.length} obligations, ${activeCares.length} cares, ${openIntentions.length} intentions`,
31
+ meta: {
32
+ episodeCount: recentEpisodes.length,
33
+ obligationCount: activeObligations.length,
34
+ careCount: activeCares.length,
35
+ intentionCount: openIntentions.length,
36
+ peerCount: peerPresence.length,
37
+ tempo,
38
+ },
39
+ });
40
+ return view;
41
+ }
@@ -0,0 +1,226 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildWakePacket = buildWakePacket;
4
+ exports.renderWakePacket = renderWakePacket;
5
+ exports.renderCompactWakePacket = renderCompactWakePacket;
6
+ const runtime_1 = require("../nerves/runtime");
7
+ const tempo_1 = require("./tempo");
8
+ function estimateTokens(text) {
9
+ return Math.ceil(text.length / 4);
10
+ }
11
+ // --- Section builders (pure derivation from authored data, no embellishment) ---
12
+ function buildPlotLine(episodes, tempo) {
13
+ if (episodes.length === 0)
14
+ return "";
15
+ const limit = tempo === "crisis" ? 3 : tempo === "brief" ? 2 : 5;
16
+ const selected = episodes.slice(0, limit);
17
+ const lines = selected.map((ep) => {
18
+ const parts = [`- ${ep.summary}`];
19
+ if (ep.whyItMattered && tempo !== "brief") {
20
+ parts[0] += ` (${ep.whyItMattered})`;
21
+ }
22
+ return parts[0];
23
+ });
24
+ return lines.join("\n");
25
+ }
26
+ function buildObligationsSection(obligations) {
27
+ if (obligations.length === 0)
28
+ return "";
29
+ return obligations
30
+ .map((ob) => {
31
+ const parts = [`- ${ob.content}`];
32
+ if (ob.meaning?.resumeHint) {
33
+ parts.push(` [hint: ${ob.meaning.resumeHint}]`);
34
+ }
35
+ if (ob.meaning?.stalenessClass && ob.meaning.stalenessClass !== "fresh") {
36
+ parts.push(` (${ob.meaning.stalenessClass})`);
37
+ }
38
+ if (ob.meaning?.waitingOn) {
39
+ parts.push(` waiting on ${ob.meaning.waitingOn.kind}: ${ob.meaning.waitingOn.target}`);
40
+ }
41
+ return parts.join("");
42
+ })
43
+ .join("\n");
44
+ }
45
+ function buildCaresSection(cares) {
46
+ if (cares.length === 0)
47
+ return "";
48
+ return cares
49
+ .map((c) => {
50
+ const parts = [`- ${c.label}`];
51
+ if (c.salience !== "low") {
52
+ parts.push(` [${c.salience}]`);
53
+ }
54
+ if (c.currentRisk) {
55
+ parts.push(` risk: ${c.currentRisk}`);
56
+ }
57
+ return parts.join("");
58
+ })
59
+ .join("\n");
60
+ }
61
+ function buildPresenceSection(peers) {
62
+ if (peers.length === 0)
63
+ return "";
64
+ return peers
65
+ .map((p) => `- ${p.agentName}: ${p.availability}/${p.lane}`)
66
+ .join("\n");
67
+ }
68
+ function buildResumeHint(view) {
69
+ // Compose from authored obligation resumeHints and top intentions
70
+ const hints = [];
71
+ for (const ob of view.activeObligations) {
72
+ if (ob.meaning?.resumeHint) {
73
+ hints.push(ob.meaning.resumeHint);
74
+ }
75
+ }
76
+ for (const intent of view.openIntentions.slice(0, 3)) {
77
+ hints.push(intent.content);
78
+ }
79
+ if (hints.length === 0) {
80
+ // Fall back to top obligation content
81
+ if (view.activeObligations.length > 0) {
82
+ hints.push(view.activeObligations[0].content);
83
+ }
84
+ }
85
+ if (hints.length === 0)
86
+ return "";
87
+ return hints.join("; ");
88
+ }
89
+ function buildWakePacket(view) {
90
+ const tempo = view.tempo;
91
+ const tokenBudget = tempo_1.TEMPO_BUDGETS[tempo];
92
+ const packet = {
93
+ plotLine: buildPlotLine(view.recentEpisodes, tempo),
94
+ obligations: buildObligationsSection(view.activeObligations),
95
+ cares: buildCaresSection(view.activeCares),
96
+ presence: buildPresenceSection(view.peerPresence),
97
+ resumeHint: buildResumeHint(view),
98
+ tempo,
99
+ tokenBudget,
100
+ assembledAt: new Date().toISOString(),
101
+ };
102
+ (0, runtime_1.emitNervesEvent)({
103
+ component: "heart",
104
+ event: "heart.wake_packet_built",
105
+ message: `wake packet built: tempo=${tempo}`,
106
+ meta: {
107
+ tempo,
108
+ plotLineTokens: estimateTokens(packet.plotLine),
109
+ obligationsTokens: estimateTokens(packet.obligations),
110
+ caresTokens: estimateTokens(packet.cares),
111
+ presenceTokens: estimateTokens(packet.presence),
112
+ resumeHintTokens: estimateTokens(packet.resumeHint),
113
+ },
114
+ });
115
+ return packet;
116
+ }
117
+ /**
118
+ * Renders a wake packet to prompt text, respecting token budget.
119
+ * Truncation priority (last truncated first):
120
+ * resumeHint (PROTECTED) > obligations > cares > plotLine > presence
121
+ * So presence is truncated first, then plotLine, then cares, then obligations.
122
+ * resumeHint is never truncated.
123
+ */
124
+ function renderWakePacket(packet) {
125
+ const budget = packet.tokenBudget;
126
+ // Assemble sections in priority order (highest priority first for budget allocation)
127
+ // Each section is { label, content, priority } where lower priority number = truncated first
128
+ const sections = [
129
+ { label: "resume", content: packet.resumeHint, priority: 5 },
130
+ { label: "obligations", content: packet.obligations, priority: 4 },
131
+ { label: "cares", content: packet.cares, priority: 3 },
132
+ { label: "plot", content: packet.plotLine, priority: 2 },
133
+ { label: "presence", content: packet.presence, priority: 1 },
134
+ ].filter((s) => s.content.length > 0);
135
+ if (sections.length === 0) {
136
+ (0, runtime_1.emitNervesEvent)({
137
+ component: "heart",
138
+ event: "heart.wake_packet_rendered",
139
+ message: "wake packet rendered: empty",
140
+ meta: { tokens: 0, tempo: packet.tempo },
141
+ });
142
+ return "";
143
+ }
144
+ // Build the rendered output, truncating lower-priority sections first
145
+ let rendered = formatSections(sections);
146
+ let tokens = estimateTokens(rendered);
147
+ // Truncate sections from lowest priority until we fit budget
148
+ const sortedByPriority = [...sections].sort((a, b) => a.priority - b.priority);
149
+ for (const section of sortedByPriority) {
150
+ if (tokens <= budget.max)
151
+ break;
152
+ // Skip resumeHint — it's PROTECTED
153
+ if (section.label === "resume")
154
+ continue;
155
+ // Remove this section entirely
156
+ const idx = sections.findIndex((s) => s.label === section.label);
157
+ sections.splice(idx, 1);
158
+ rendered = formatSections(sections);
159
+ tokens = estimateTokens(rendered);
160
+ }
161
+ // If still over budget after removing all non-protected sections, trim what's left
162
+ if (tokens > budget.max) {
163
+ const maxChars = budget.max * 4;
164
+ rendered = rendered.slice(0, maxChars);
165
+ }
166
+ (0, runtime_1.emitNervesEvent)({
167
+ component: "heart",
168
+ event: "heart.wake_packet_rendered",
169
+ message: `wake packet rendered: ${tokens} tokens`,
170
+ meta: { tokens, tempo: packet.tempo, sectionCount: sections.length },
171
+ });
172
+ return rendered;
173
+ }
174
+ function formatSections(sections) {
175
+ const parts = [];
176
+ for (const section of sections) {
177
+ switch (section.label) {
178
+ case "resume":
179
+ parts.push(`**Next:** ${section.content}`);
180
+ break;
181
+ case "obligations":
182
+ parts.push(`**Owed:**\n${section.content}`);
183
+ break;
184
+ case "cares":
185
+ parts.push(`**Cares:**\n${section.content}`);
186
+ break;
187
+ case "plot":
188
+ parts.push(`**Recent:**\n${section.content}`);
189
+ break;
190
+ case "presence":
191
+ parts.push(`**Peers:**\n${section.content}`);
192
+ break;
193
+ }
194
+ }
195
+ return parts.join("\n\n");
196
+ }
197
+ /**
198
+ * Ultra-compact version for coding context (max 200 tokens).
199
+ * Just resumeHint + top obligation + top care, single-line bullets.
200
+ */
201
+ function renderCompactWakePacket(packet) {
202
+ const parts = [];
203
+ if (packet.resumeHint) {
204
+ parts.push(`next: ${packet.resumeHint}`);
205
+ }
206
+ if (packet.obligations) {
207
+ // Just first line of obligations
208
+ const firstOb = packet.obligations.split("\n")[0];
209
+ parts.push(`owed: ${firstOb.replace(/^- /, "")}`);
210
+ }
211
+ if (packet.cares) {
212
+ const firstCare = packet.cares.split("\n")[0];
213
+ parts.push(`care: ${firstCare.replace(/^- /, "")}`);
214
+ }
215
+ const compact = parts.join(" | ");
216
+ // Hard cap at 200 tokens (800 chars)
217
+ const maxChars = 200 * 4;
218
+ const result = compact.length > maxChars ? compact.slice(0, maxChars) : compact;
219
+ (0, runtime_1.emitNervesEvent)({
220
+ component: "heart",
221
+ event: "heart.wake_packet_compact_rendered",
222
+ message: `compact wake packet: ${estimateTokens(result)} tokens`,
223
+ meta: { tokens: estimateTokens(result) },
224
+ });
225
+ return result;
226
+ }
@@ -0,0 +1,116 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.emitEpisode = emitEpisode;
37
+ exports.readRecentEpisodes = readRecentEpisodes;
38
+ const fs = __importStar(require("fs"));
39
+ const path = __importStar(require("path"));
40
+ const runtime_1 = require("../nerves/runtime");
41
+ function episodesDir(agentRoot) {
42
+ return path.join(agentRoot, "state", "episodes");
43
+ }
44
+ function generateId() {
45
+ const timestamp = Date.now();
46
+ const random = Math.random().toString(36).slice(2, 10);
47
+ return `ep-${timestamp}-${random}`;
48
+ }
49
+ function emitEpisode(agentRoot, input) {
50
+ const now = new Date().toISOString();
51
+ const id = generateId();
52
+ const episode = {
53
+ id,
54
+ timestamp: now,
55
+ kind: input.kind,
56
+ summary: input.summary,
57
+ whyItMattered: input.whyItMattered,
58
+ relatedEntities: input.relatedEntities,
59
+ salience: input.salience,
60
+ ...(input.meta ? { meta: input.meta } : {}),
61
+ };
62
+ const dir = episodesDir(agentRoot);
63
+ fs.mkdirSync(dir, { recursive: true });
64
+ fs.writeFileSync(path.join(dir, `${id}.json`), JSON.stringify(episode, null, 2), "utf-8");
65
+ (0, runtime_1.emitNervesEvent)({
66
+ component: "mind",
67
+ event: "mind.episode_emitted",
68
+ message: `episode emitted: ${input.kind}`,
69
+ meta: {
70
+ episodeId: id,
71
+ kind: input.kind,
72
+ salience: input.salience,
73
+ },
74
+ });
75
+ return episode;
76
+ }
77
+ function readRecentEpisodes(agentRoot, options) {
78
+ const dir = episodesDir(agentRoot);
79
+ if (!fs.existsSync(dir)) {
80
+ (0, runtime_1.emitNervesEvent)({
81
+ component: "mind",
82
+ event: "mind.episodes_read",
83
+ message: "read episodes: directory missing, returning empty",
84
+ meta: { count: 0 },
85
+ });
86
+ return [];
87
+ }
88
+ const limit = options?.limit ?? 50;
89
+ const since = options?.since;
90
+ const kinds = options?.kinds;
91
+ const files = fs.readdirSync(dir).filter((f) => f.endsWith(".json"));
92
+ const episodes = [];
93
+ for (const file of files) {
94
+ try {
95
+ const content = fs.readFileSync(path.join(dir, file), "utf-8");
96
+ const episode = JSON.parse(content);
97
+ if (since && episode.timestamp < since)
98
+ continue;
99
+ if (kinds && !kinds.includes(episode.kind))
100
+ continue;
101
+ episodes.push(episode);
102
+ }
103
+ catch {
104
+ // Skip malformed JSON files gracefully
105
+ }
106
+ }
107
+ episodes.sort((a, b) => b.timestamp.localeCompare(a.timestamp));
108
+ const result = episodes.slice(0, limit);
109
+ (0, runtime_1.emitNervesEvent)({
110
+ component: "mind",
111
+ event: "mind.episodes_read",
112
+ message: `read ${result.length} recent episodes`,
113
+ meta: { count: result.length, total: episodes.length, limit },
114
+ });
115
+ return result;
116
+ }