@ouro.bot/cli 0.1.0-alpha.14 → 0.1.0-alpha.140

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.
Files changed (138) hide show
  1. package/AdoptionSpecialist.ouro/psyche/SOUL.md +2 -2
  2. package/AdoptionSpecialist.ouro/psyche/identities/monty.md +2 -2
  3. package/README.md +151 -205
  4. package/changelog.json +902 -0
  5. package/dist/heart/active-work.js +633 -0
  6. package/dist/heart/bridges/manager.js +358 -0
  7. package/dist/heart/bridges/state-machine.js +135 -0
  8. package/dist/heart/bridges/store.js +123 -0
  9. package/dist/heart/commitments.js +105 -0
  10. package/dist/heart/config.js +66 -21
  11. package/dist/heart/core.js +579 -112
  12. package/dist/heart/cross-chat-delivery.js +146 -0
  13. package/dist/heart/daemon/agent-discovery.js +81 -0
  14. package/dist/heart/daemon/agent-service.js +467 -0
  15. package/dist/heart/daemon/auth-flow.js +457 -0
  16. package/dist/heart/daemon/daemon-cli.js +1840 -196
  17. package/dist/heart/daemon/daemon-entry.js +80 -3
  18. package/dist/heart/daemon/daemon-runtime-sync.js +212 -0
  19. package/dist/heart/daemon/daemon.js +301 -1
  20. package/dist/heart/daemon/hatch-animation.js +10 -3
  21. package/dist/heart/daemon/hatch-flow.js +15 -80
  22. package/dist/heart/daemon/heartbeat-timer.js +131 -0
  23. package/dist/heart/daemon/hooks/bundle-meta.js +92 -0
  24. package/dist/heart/daemon/launchd.js +159 -0
  25. package/dist/heart/daemon/log-tailer.js +4 -3
  26. package/dist/heart/daemon/mcp-server.js +464 -0
  27. package/dist/heart/daemon/message-router.js +17 -8
  28. package/dist/heart/daemon/ouro-bot-global-installer.js +128 -0
  29. package/dist/heart/daemon/ouro-path-installer.js +67 -34
  30. package/dist/heart/daemon/ouro-version-manager.js +171 -0
  31. package/dist/heart/daemon/process-manager.js +13 -0
  32. package/dist/heart/daemon/run-hooks.js +37 -0
  33. package/dist/heart/daemon/runtime-logging.js +58 -15
  34. package/dist/heart/daemon/runtime-metadata.js +219 -0
  35. package/dist/heart/daemon/runtime-mode.js +67 -0
  36. package/dist/heart/daemon/sense-manager.js +50 -2
  37. package/dist/heart/daemon/skill-management-installer.js +94 -0
  38. package/dist/heart/daemon/socket-client.js +202 -0
  39. package/dist/heart/daemon/specialist-orchestrator.js +2 -2
  40. package/dist/heart/daemon/specialist-prompt.js +10 -7
  41. package/dist/heart/daemon/specialist-tools.js +52 -3
  42. package/dist/heart/daemon/staged-restart.js +114 -0
  43. package/dist/heart/daemon/thoughts.js +507 -0
  44. package/dist/heart/daemon/update-checker.js +111 -0
  45. package/dist/heart/daemon/update-hooks.js +138 -0
  46. package/dist/heart/daemon/wrapper-publish-guard.js +86 -0
  47. package/dist/heart/delegation.js +62 -0
  48. package/dist/heart/identity.js +64 -21
  49. package/dist/heart/kicks.js +2 -20
  50. package/dist/heart/model-capabilities.js +48 -0
  51. package/dist/heart/obligations.js +197 -0
  52. package/dist/heart/progress-story.js +42 -0
  53. package/dist/heart/provider-failover.js +88 -0
  54. package/dist/heart/provider-ping.js +159 -0
  55. package/dist/heart/providers/anthropic-token.js +163 -0
  56. package/dist/heart/providers/anthropic.js +195 -34
  57. package/dist/heart/providers/azure.js +115 -9
  58. package/dist/heart/providers/github-copilot.js +157 -0
  59. package/dist/heart/providers/minimax.js +33 -3
  60. package/dist/heart/providers/openai-codex.js +49 -14
  61. package/dist/heart/session-activity.js +173 -0
  62. package/dist/heart/session-recall.js +216 -0
  63. package/dist/heart/streaming.js +117 -33
  64. package/dist/heart/target-resolution.js +123 -0
  65. package/dist/heart/tool-loop.js +198 -0
  66. package/dist/heart/turn-coordinator.js +28 -0
  67. package/dist/mind/associative-recall.js +122 -32
  68. package/dist/mind/bundle-manifest.js +14 -0
  69. package/dist/mind/context.js +124 -14
  70. package/dist/mind/{memory.js → diary.js} +70 -56
  71. package/dist/mind/first-impressions.js +16 -2
  72. package/dist/mind/friends/channel.js +35 -0
  73. package/dist/mind/friends/group-context.js +144 -0
  74. package/dist/mind/friends/store-file.js +19 -0
  75. package/dist/mind/friends/trust-explanation.js +74 -0
  76. package/dist/mind/friends/types.js +8 -0
  77. package/dist/mind/journal-index.js +161 -0
  78. package/dist/mind/obligation-steering.js +221 -0
  79. package/dist/mind/obligations.js +134 -0
  80. package/dist/mind/pending.js +76 -9
  81. package/dist/mind/phrases.js +1 -0
  82. package/dist/mind/prompt.js +554 -88
  83. package/dist/mind/token-estimate.js +8 -12
  84. package/dist/nerves/cli-logging.js +15 -2
  85. package/dist/nerves/coverage/run-artifacts.js +1 -1
  86. package/dist/nerves/index.js +12 -0
  87. package/dist/nerves/runtime.js +5 -1
  88. package/dist/repertoire/ado-client.js +4 -2
  89. package/dist/repertoire/ado-semantic.js +6 -0
  90. package/dist/repertoire/coding/codex-jsonl.js +64 -0
  91. package/dist/repertoire/coding/context-pack.js +254 -0
  92. package/dist/repertoire/coding/feedback.js +301 -0
  93. package/dist/repertoire/coding/index.js +4 -1
  94. package/dist/repertoire/coding/manager.js +210 -4
  95. package/dist/repertoire/coding/spawner.js +59 -12
  96. package/dist/repertoire/coding/tools.js +176 -4
  97. package/dist/repertoire/data/ado-endpoints.json +188 -0
  98. package/dist/repertoire/guardrails.js +290 -0
  99. package/dist/repertoire/mcp-client.js +254 -0
  100. package/dist/repertoire/mcp-manager.js +198 -0
  101. package/dist/repertoire/skills.js +14 -23
  102. package/dist/repertoire/tasks/board.js +12 -0
  103. package/dist/repertoire/tasks/index.js +23 -9
  104. package/dist/repertoire/tasks/transitions.js +1 -2
  105. package/dist/repertoire/tools-base.js +975 -271
  106. package/dist/repertoire/tools-bluebubbles.js +94 -0
  107. package/dist/repertoire/tools-github.js +1 -6
  108. package/dist/repertoire/tools-teams.js +65 -59
  109. package/dist/repertoire/tools.js +265 -91
  110. package/dist/senses/attention-queue.js +97 -0
  111. package/dist/senses/bluebubbles-client.js +210 -5
  112. package/dist/senses/bluebubbles-entry.js +2 -0
  113. package/dist/senses/bluebubbles-inbound-log.js +109 -0
  114. package/dist/senses/bluebubbles-media.js +339 -0
  115. package/dist/senses/bluebubbles-model.js +12 -4
  116. package/dist/senses/bluebubbles-mutation-log.js +45 -5
  117. package/dist/senses/bluebubbles-runtime-state.js +109 -0
  118. package/dist/senses/bluebubbles-session-cleanup.js +72 -0
  119. package/dist/senses/bluebubbles.js +944 -45
  120. package/dist/senses/cli-entry.js +1 -1
  121. package/dist/senses/cli-layout.js +187 -0
  122. package/dist/senses/cli.js +374 -131
  123. package/dist/senses/contextual-heartbeat.js +102 -0
  124. package/dist/senses/continuity.js +94 -0
  125. package/dist/senses/debug-activity.js +154 -0
  126. package/dist/senses/inner-dialog-worker.js +47 -18
  127. package/dist/senses/inner-dialog.js +500 -84
  128. package/dist/senses/pipeline.js +454 -0
  129. package/dist/senses/surface-tool.js +82 -0
  130. package/dist/senses/teams.js +607 -129
  131. package/dist/senses/trust-gate.js +112 -2
  132. package/package.json +12 -4
  133. package/skills/configure-dev-tools.md +109 -0
  134. package/subagents/README.md +4 -70
  135. package/dist/heart/daemon/subagent-installer.js +0 -134
  136. package/subagents/work-doer.md +0 -233
  137. package/subagents/work-merger.md +0 -624
  138. package/subagents/work-planner.md +0 -373
@@ -0,0 +1,198 @@
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.GLOBAL_CIRCUIT_BREAKER_LIMIT = exports.PING_PONG_WINDOW = exports.GENERIC_REPEAT_LIMIT = exports.POLL_NO_PROGRESS_LIMIT = exports.TOOL_LOOP_HISTORY_LIMIT = void 0;
37
+ exports.createToolLoopState = createToolLoopState;
38
+ exports.recordToolOutcome = recordToolOutcome;
39
+ exports.detectToolLoop = detectToolLoop;
40
+ const crypto = __importStar(require("crypto"));
41
+ const runtime_1 = require("../nerves/runtime");
42
+ exports.TOOL_LOOP_HISTORY_LIMIT = 30;
43
+ exports.POLL_NO_PROGRESS_LIMIT = 3;
44
+ exports.GENERIC_REPEAT_LIMIT = 4;
45
+ exports.PING_PONG_WINDOW = 6;
46
+ exports.GLOBAL_CIRCUIT_BREAKER_LIMIT = 24;
47
+ function stableStringify(value) {
48
+ if (value === null || typeof value !== "object") {
49
+ return JSON.stringify(value);
50
+ }
51
+ /* v8 ignore next -- stableStringify currently receives only objects/strings from normalized loop inputs @preserve */
52
+ if (Array.isArray(value)) {
53
+ return `[${value.map((entry) => stableStringify(entry)).join(",")}]`;
54
+ }
55
+ const record = value;
56
+ return `{${Object.keys(record).sort().map((key) => `${JSON.stringify(key)}:${stableStringify(record[key])}`).join(",")}}`;
57
+ }
58
+ function digest(value) {
59
+ return crypto.createHash("sha256").update(stableStringify(value)).digest("hex");
60
+ }
61
+ function normalizeArgs(toolName, args) {
62
+ if (toolName === "query_active_work") {
63
+ return { toolName };
64
+ }
65
+ if (toolName === "coding_status" || toolName === "coding_tail") {
66
+ return {
67
+ toolName,
68
+ sessionId: args.sessionId ?? "",
69
+ };
70
+ }
71
+ if (toolName === "query_session") {
72
+ return {
73
+ toolName,
74
+ mode: args.mode ?? "",
75
+ friendId: args.friendId ?? "",
76
+ channel: args.channel ?? "",
77
+ key: args.key ?? "",
78
+ query: args.query ?? "",
79
+ };
80
+ }
81
+ return {
82
+ toolName,
83
+ args,
84
+ };
85
+ }
86
+ function normalizeOutcome(result, success) {
87
+ return {
88
+ success,
89
+ result: result.replace(/\s+/g, " ").trim(),
90
+ };
91
+ }
92
+ function isKnownPollTool(toolName, args) {
93
+ return toolName === "query_active_work"
94
+ || toolName === "coding_status"
95
+ || toolName === "coding_tail"
96
+ || (toolName === "query_session" && (args.mode ?? "").trim() === "status");
97
+ }
98
+ function emitDetection(detector, toolName, count, message, pairedToolName) {
99
+ (0, runtime_1.emitNervesEvent)({
100
+ level: "warn",
101
+ component: "engine",
102
+ event: "engine.tool_loop_detected",
103
+ message: "tool loop guard detected repeated non-progress work",
104
+ meta: {
105
+ detector,
106
+ toolName,
107
+ count,
108
+ pairedToolName: pairedToolName ?? null,
109
+ },
110
+ });
111
+ return {
112
+ stuck: true,
113
+ detector,
114
+ count,
115
+ message,
116
+ pairedToolName,
117
+ };
118
+ }
119
+ function countTrailingRepeats(history, toolName, callHash) {
120
+ let count = 0;
121
+ let outcomeHash;
122
+ for (let index = history.length - 1; index >= 0; index--) {
123
+ const record = history[index];
124
+ if (record.toolName !== toolName || record.callHash !== callHash) {
125
+ break;
126
+ }
127
+ if (!outcomeHash) {
128
+ outcomeHash = record.outcomeHash;
129
+ }
130
+ if (record.outcomeHash !== outcomeHash) {
131
+ break;
132
+ }
133
+ count += 1;
134
+ }
135
+ return { count, outcomeHash };
136
+ }
137
+ function detectPingPong(history, toolName, callHash) {
138
+ if (history.length < exports.PING_PONG_WINDOW) {
139
+ return { stuck: false };
140
+ }
141
+ const recent = history.slice(-exports.PING_PONG_WINDOW);
142
+ const first = recent[0];
143
+ const second = recent[1];
144
+ if (!first.isKnownPoll || !second.isKnownPoll) {
145
+ return { stuck: false };
146
+ }
147
+ if (first.toolName === second.toolName && first.callHash === second.callHash) {
148
+ return { stuck: false };
149
+ }
150
+ for (let index = 0; index < recent.length; index++) {
151
+ const expected = index % 2 === 0 ? first : second;
152
+ const actual = recent[index];
153
+ if (actual.toolName !== expected.toolName
154
+ || actual.callHash !== expected.callHash
155
+ || actual.outcomeHash !== expected.outcomeHash) {
156
+ return { stuck: false };
157
+ }
158
+ }
159
+ const matchesPair = (toolName === first.toolName && callHash === first.callHash)
160
+ || (toolName === second.toolName && callHash === second.callHash);
161
+ if (!matchesPair) {
162
+ return { stuck: false };
163
+ }
164
+ const pairedToolName = toolName === first.toolName ? second.toolName : first.toolName;
165
+ return emitDetection("ping_pong", toolName, exports.PING_PONG_WINDOW, `repeated ${first.toolName}/${second.toolName} polling is not changing. stop cycling between status checks and either act on the current state, change approach, or answer truthfully with what is known.`, pairedToolName);
166
+ }
167
+ function createToolLoopState() {
168
+ return { history: [] };
169
+ }
170
+ function recordToolOutcome(state, toolName, args, result, success) {
171
+ state.history.push({
172
+ toolName,
173
+ callHash: digest(normalizeArgs(toolName, args)),
174
+ outcomeHash: digest(normalizeOutcome(result, success)),
175
+ isKnownPoll: isKnownPollTool(toolName, args),
176
+ });
177
+ if (state.history.length > exports.TOOL_LOOP_HISTORY_LIMIT) {
178
+ state.history.splice(0, state.history.length - exports.TOOL_LOOP_HISTORY_LIMIT);
179
+ }
180
+ }
181
+ // Tools that must never be blocked by the circuit breaker.
182
+ // settle = end the turn, surface = deliver results outward.
183
+ // Blocking these traps the agent: it can think all it wants but can never speak.
184
+ const CIRCUIT_BREAKER_EXEMPT = new Set(["settle", "surface"]);
185
+ function detectToolLoop(state, toolName, args) {
186
+ if (state.history.length >= exports.GLOBAL_CIRCUIT_BREAKER_LIMIT && !CIRCUIT_BREAKER_EXEMPT.has(toolName)) {
187
+ return emitDetection("global_circuit_breaker", toolName, state.history.length, `this turn has already made ${state.history.length} tool calls. stop thrashing, use the current evidence, and either change approach or answer truthfully with the best grounded status.`);
188
+ }
189
+ const callHash = digest(normalizeArgs(toolName, args));
190
+ const trailing = countTrailingRepeats(state.history, toolName, callHash);
191
+ if (isKnownPollTool(toolName, args) && trailing.count >= exports.POLL_NO_PROGRESS_LIMIT) {
192
+ return emitDetection("known_poll_no_progress", toolName, trailing.count, `repeated ${toolName} calls have returned the same state ${trailing.count} times. stop polling and either act on the current state, wait for a meaningful change, or answer truthfully with what is known.`);
193
+ }
194
+ if (trailing.count >= exports.GENERIC_REPEAT_LIMIT) {
195
+ return emitDetection("generic_repeat", toolName, trailing.count, `repeating ${toolName} with the same inputs is not producing new information. change approach, use the evidence already gathered, or answer truthfully with what is known.`);
196
+ }
197
+ return detectPingPong(state.history, toolName, callHash);
198
+ }
@@ -1,7 +1,16 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.createTurnCoordinator = createTurnCoordinator;
4
+ exports.withSharedTurnLock = withSharedTurnLock;
5
+ exports.tryBeginSharedTurn = tryBeginSharedTurn;
6
+ exports.endSharedTurn = endSharedTurn;
7
+ exports.isSharedTurnActive = isSharedTurnActive;
8
+ exports.enqueueSharedFollowUp = enqueueSharedFollowUp;
9
+ exports.drainSharedFollowUps = drainSharedFollowUps;
4
10
  const runtime_1 = require("../nerves/runtime");
11
+ function scopedKey(scope, key) {
12
+ return `${scope}:${key}`;
13
+ }
5
14
  function createTurnCoordinator() {
6
15
  const turnLocks = new Map();
7
16
  const activeTurns = new Set();
@@ -60,3 +69,22 @@ function createTurnCoordinator() {
60
69
  },
61
70
  };
62
71
  }
72
+ const _sharedTurnCoordinator = createTurnCoordinator();
73
+ function withSharedTurnLock(scope, key, fn) {
74
+ return _sharedTurnCoordinator.withTurnLock(scopedKey(scope, key), fn);
75
+ }
76
+ function tryBeginSharedTurn(scope, key) {
77
+ return _sharedTurnCoordinator.tryBeginTurn(scopedKey(scope, key));
78
+ }
79
+ function endSharedTurn(scope, key) {
80
+ _sharedTurnCoordinator.endTurn(scopedKey(scope, key));
81
+ }
82
+ function isSharedTurnActive(scope, key) {
83
+ return _sharedTurnCoordinator.isTurnActive(scopedKey(scope, key));
84
+ }
85
+ function enqueueSharedFollowUp(scope, key, followUp) {
86
+ _sharedTurnCoordinator.enqueueFollowUp(scopedKey(scope, key), followUp);
87
+ }
88
+ function drainSharedFollowUps(scope, key) {
89
+ return _sharedTurnCoordinator.drainFollowUps(scopedKey(scope, key));
90
+ }
@@ -35,12 +35,13 @@ var __importStar = (this && this.__importStar) || (function () {
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.cosineSimilarity = cosineSimilarity;
37
37
  exports.recallFactsForQuery = recallFactsForQuery;
38
+ exports.searchJournalIndex = searchJournalIndex;
38
39
  exports.injectAssociativeRecall = injectAssociativeRecall;
39
40
  const fs = __importStar(require("fs"));
40
41
  const path = __importStar(require("path"));
41
42
  const config_1 = require("../heart/config");
42
- const identity_1 = require("../heart/identity");
43
43
  const runtime_1 = require("../nerves/runtime");
44
+ const diary_1 = require("./diary");
44
45
  const DEFAULT_EMBEDDING_MODEL = "text-embedding-3-small";
45
46
  const DEFAULT_MIN_SCORE = 0.5;
46
47
  const DEFAULT_TOP_K = 3;
@@ -80,14 +81,26 @@ function createDefaultProvider() {
80
81
  }
81
82
  return new OpenAIEmbeddingProvider(apiKey);
82
83
  }
83
- function readFacts(memoryRoot) {
84
- const factsPath = path.join(memoryRoot, "facts.jsonl");
84
+ function readFacts(diaryRoot) {
85
+ const factsPath = path.join(diaryRoot, "facts.jsonl");
85
86
  if (!fs.existsSync(factsPath))
86
87
  return [];
87
88
  const raw = fs.readFileSync(factsPath, "utf8").trim();
88
89
  if (!raw)
89
90
  return [];
90
- return raw.split("\n").map((line) => JSON.parse(line));
91
+ const facts = [];
92
+ for (const line of raw.split("\n")) {
93
+ const trimmed = line.trim();
94
+ if (!trimmed)
95
+ continue;
96
+ try {
97
+ facts.push(JSON.parse(trimmed));
98
+ }
99
+ catch {
100
+ // Skip corrupt lines (e.g. partial write from a crash).
101
+ }
102
+ }
103
+ return facts;
91
104
  }
92
105
  function getLatestUserText(messages) {
93
106
  for (let i = messages.length - 1; i >= 0; i--) {
@@ -133,6 +146,40 @@ async function recallFactsForQuery(query, facts, provider, options) {
133
146
  .sort((left, right) => right.score - left.score)
134
147
  .slice(0, topK);
135
148
  }
149
+ function readJournalIndex(journalDir) {
150
+ const indexPath = path.join(journalDir, ".index.json");
151
+ try {
152
+ const raw = fs.readFileSync(indexPath, "utf8");
153
+ const parsed = JSON.parse(raw);
154
+ if (!Array.isArray(parsed))
155
+ return [];
156
+ return parsed;
157
+ }
158
+ catch {
159
+ return [];
160
+ }
161
+ }
162
+ function searchJournalIndex(queryEmbedding, entries, options) {
163
+ const minScore = options?.minScore ?? DEFAULT_MIN_SCORE;
164
+ const topK = options?.topK ?? DEFAULT_TOP_K;
165
+ return entries
166
+ .filter((entry) => Array.isArray(entry.embedding) && entry.embedding.length > 0)
167
+ .map((entry) => ({
168
+ filename: entry.filename,
169
+ preview: entry.preview,
170
+ score: cosineSimilarity(queryEmbedding, entry.embedding),
171
+ }))
172
+ .filter((entry) => entry.score >= minScore)
173
+ .sort((left, right) => right.score - left.score)
174
+ .slice(0, topK);
175
+ }
176
+ function resolveJournalDir(diaryRoot, explicitJournalDir) {
177
+ if (explicitJournalDir)
178
+ return explicitJournalDir;
179
+ // journal/ is a sibling of diary/ at the agent root level
180
+ const agentRoot = path.dirname(diaryRoot);
181
+ return path.join(agentRoot, "journal");
182
+ }
136
183
  async function injectAssociativeRecall(messages, options) {
137
184
  try {
138
185
  if (messages[0]?.role !== "system" || typeof messages[0].content !== "string")
@@ -140,37 +187,80 @@ async function injectAssociativeRecall(messages, options) {
140
187
  const query = getLatestUserText(messages);
141
188
  if (!query)
142
189
  return;
143
- const memoryRoot = options?.memoryRoot ?? path.join((0, identity_1.getAgentRoot)(), "psyche", "memory");
144
- const facts = readFacts(memoryRoot);
145
- if (facts.length === 0)
190
+ const diaryRoot = options?.diaryRoot ?? (0, diary_1.resolveDiaryRoot)();
191
+ const facts = readFacts(diaryRoot);
192
+ const journalDir = resolveJournalDir(diaryRoot, options?.journalDir);
193
+ const journalEntries = readJournalIndex(journalDir);
194
+ if (facts.length === 0 && journalEntries.length === 0)
146
195
  return;
147
- let recalled;
148
- try {
149
- const provider = options?.provider ?? createDefaultProvider();
150
- recalled = await recallFactsForQuery(query, facts, provider, options);
151
- }
152
- catch {
153
- // Embeddings unavailable — fall back to substring matching
154
- const lowerQuery = query.toLowerCase();
155
- const topK = options?.topK ?? DEFAULT_TOP_K;
156
- recalled = facts
157
- .filter((fact) => fact.text.toLowerCase().includes(lowerQuery))
158
- .slice(0, topK)
159
- .map((fact) => ({ ...fact, score: 1 }));
160
- if (recalled.length > 0) {
161
- (0, runtime_1.emitNervesEvent)({
162
- level: "warn",
163
- component: "mind",
164
- event: "mind.associative_recall_fallback",
165
- message: "embeddings unavailable, used substring fallback",
166
- meta: { matchCount: recalled.length },
196
+ // Build combined result lines tagged by source
197
+ const resultLines = [];
198
+ let queryEmbedding;
199
+ // Search diary entries
200
+ if (facts.length > 0) {
201
+ let recalled;
202
+ try {
203
+ const provider = options?.provider ?? createDefaultProvider();
204
+ recalled = await recallFactsForQuery(query, facts, provider, options);
205
+ // Compute query embedding for journal search while provider is available
206
+ if (journalEntries.length > 0) {
207
+ const [qe] = await provider.embed([query.trim()]);
208
+ queryEmbedding = qe;
209
+ }
210
+ }
211
+ catch {
212
+ // Embeddings unavailable — fall back to substring matching
213
+ const lowerQuery = query.toLowerCase();
214
+ const topK = options?.topK ?? DEFAULT_TOP_K;
215
+ recalled = facts
216
+ .filter((fact) => fact.text.toLowerCase().includes(lowerQuery))
217
+ .slice(0, topK)
218
+ .map((fact) => ({ ...fact, score: 1 }));
219
+ if (recalled.length > 0) {
220
+ (0, runtime_1.emitNervesEvent)({
221
+ level: "warn",
222
+ component: "mind",
223
+ event: "mind.associative_recall_fallback",
224
+ message: "embeddings unavailable, used substring fallback",
225
+ meta: { matchCount: recalled.length },
226
+ });
227
+ }
228
+ }
229
+ for (const fact of recalled) {
230
+ resultLines.push({
231
+ text: `[diary] ${fact.text} [score=${fact.score.toFixed(3)} source=${fact.source}]`,
232
+ score: fact.score,
167
233
  });
168
234
  }
169
235
  }
170
- if (recalled.length === 0)
236
+ // Search journal entries (works whether diary had results or not)
237
+ if (journalEntries.length > 0) {
238
+ try {
239
+ if (!queryEmbedding) {
240
+ const provider = options?.provider ?? createDefaultProvider();
241
+ const [qe] = await provider.embed([query.trim()]);
242
+ queryEmbedding = qe;
243
+ }
244
+ if (queryEmbedding) {
245
+ const journalResults = searchJournalIndex(queryEmbedding, journalEntries, options);
246
+ for (const entry of journalResults) {
247
+ resultLines.push({
248
+ text: `[journal] ${entry.filename}: ${entry.preview} [score=${entry.score.toFixed(3)}]`,
249
+ score: entry.score,
250
+ });
251
+ }
252
+ }
253
+ }
254
+ catch {
255
+ // Embeddings unavailable — no journal fallback
256
+ }
257
+ }
258
+ if (resultLines.length === 0)
171
259
  return;
172
- const recallSection = recalled
173
- .map((fact, index) => `${index + 1}. ${fact.text} [score=${fact.score.toFixed(3)} source=${fact.source}]`)
260
+ // Sort all results by score descending
261
+ resultLines.sort((left, right) => right.score - left.score);
262
+ const recallSection = resultLines
263
+ .map((entry, index) => `${index + 1}. ${entry.text}`)
174
264
  .join("\n");
175
265
  messages[0] = {
176
266
  role: "system",
@@ -180,7 +270,7 @@ async function injectAssociativeRecall(messages, options) {
180
270
  component: "mind",
181
271
  event: "mind.associative_recall",
182
272
  message: "associative recall injected",
183
- meta: { count: recalled.length },
273
+ meta: { count: resultLines.length },
184
274
  });
185
275
  }
186
276
  catch (error) {
@@ -190,7 +280,7 @@ async function injectAssociativeRecall(messages, options) {
190
280
  event: "mind.associative_recall_error",
191
281
  message: "associative recall failed",
192
282
  meta: {
193
- reason: error instanceof Error ? error.message : String(error),
283
+ reason: error instanceof Error ? error.message : /* v8 ignore start -- defensive: non-Error catch branch @preserve */ String(error) /* v8 ignore stop */,
194
284
  },
195
285
  });
196
286
  }
@@ -34,6 +34,7 @@ var __importStar = (this && this.__importStar) || (function () {
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.CANONICAL_BUNDLE_MANIFEST = void 0;
37
+ exports.getChangelogPath = getChangelogPath;
37
38
  exports.getPackageVersion = getPackageVersion;
38
39
  exports.createBundleMeta = createBundleMeta;
39
40
  exports.backfillBundleMeta = backfillBundleMeta;
@@ -51,13 +52,26 @@ exports.CANONICAL_BUNDLE_MANIFEST = [
51
52
  { path: "psyche/LORE.md", kind: "file" },
52
53
  { path: "psyche/TACIT.md", kind: "file" },
53
54
  { path: "psyche/ASPIRATIONS.md", kind: "file" },
55
+ { path: "diary", kind: "dir" },
56
+ { path: "journal", kind: "dir" },
54
57
  { path: "psyche/memory", kind: "dir" },
55
58
  { path: "friends", kind: "dir" },
59
+ { path: "state", kind: "dir" },
56
60
  { path: "tasks", kind: "dir" },
57
61
  { path: "skills", kind: "dir" },
58
62
  { path: "senses", kind: "dir" },
59
63
  { path: "senses/teams", kind: "dir" },
60
64
  ];
65
+ function getChangelogPath() {
66
+ const changelogPath = path.resolve(__dirname, "../../changelog.json");
67
+ (0, runtime_1.emitNervesEvent)({
68
+ component: "mind",
69
+ event: "mind.changelog_path_resolved",
70
+ message: "resolved changelog path",
71
+ meta: { path: changelogPath },
72
+ });
73
+ return changelogPath;
74
+ }
61
75
  function getPackageVersion() {
62
76
  const packageJsonPath = path.resolve(__dirname, "../../package.json");
63
77
  const raw = fs.readFileSync(packageJsonPath, "utf-8");