@ouro.bot/cli 0.1.0-alpha.29 → 0.1.0-alpha.290

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 (281) hide show
  1. package/README.md +188 -190
  2. package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/agent.json +3 -2
  3. package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/SOUL.md +1 -1
  4. package/changelog.json +1756 -0
  5. package/dist/arc/attention-types.js +8 -0
  6. package/dist/arc/cares.js +140 -0
  7. package/dist/arc/episodes.js +117 -0
  8. package/dist/arc/intentions.js +133 -0
  9. package/dist/arc/json-store.js +117 -0
  10. package/dist/arc/obligations.js +237 -0
  11. package/dist/arc/packets.js +203 -0
  12. package/dist/arc/presence.js +185 -0
  13. package/dist/heart/active-work.js +832 -0
  14. package/dist/heart/agent-entry.js +37 -2
  15. package/dist/heart/attachments/image-normalize.js +194 -0
  16. package/dist/heart/attachments/materialize.js +230 -0
  17. package/dist/heart/attachments/render.js +29 -0
  18. package/dist/heart/attachments/store.js +103 -0
  19. package/dist/heart/attachments/types.js +138 -0
  20. package/dist/heart/auth/auth-flow.js +456 -0
  21. package/dist/heart/bridges/manager.js +358 -0
  22. package/dist/heart/bridges/state-machine.js +135 -0
  23. package/dist/heart/bridges/store.js +123 -0
  24. package/dist/heart/bundle-state.js +147 -0
  25. package/dist/heart/commitments.js +111 -0
  26. package/dist/heart/config-registry.js +304 -0
  27. package/dist/heart/config.js +63 -30
  28. package/dist/heart/core.js +669 -195
  29. package/dist/heart/cross-chat-delivery.js +131 -0
  30. package/dist/heart/daemon/agent-config-check.js +149 -0
  31. package/dist/heart/daemon/agent-discovery.js +79 -3
  32. package/dist/heart/daemon/agent-service.js +360 -0
  33. package/dist/heart/daemon/cadence.js +70 -0
  34. package/dist/heart/daemon/cli-defaults.js +574 -0
  35. package/dist/heart/daemon/cli-exec.js +1932 -0
  36. package/dist/heart/daemon/cli-parse.js +762 -0
  37. package/dist/heart/daemon/cli-render.js +503 -0
  38. package/dist/heart/daemon/cli-types.js +8 -0
  39. package/dist/heart/daemon/daemon-cli.js +29 -1171
  40. package/dist/heart/daemon/daemon-entry.js +235 -3
  41. package/dist/heart/daemon/daemon-health.js +136 -0
  42. package/dist/heart/daemon/daemon-runtime-sync.js +153 -12
  43. package/dist/heart/daemon/daemon-tombstone.js +236 -0
  44. package/dist/heart/daemon/daemon.js +706 -57
  45. package/dist/heart/daemon/hooks/agent-config-v2.js +33 -0
  46. package/dist/heart/daemon/hooks/bundle-meta.js +115 -1
  47. package/dist/heart/daemon/inner-status.js +89 -0
  48. package/dist/heart/daemon/launchd.js +46 -9
  49. package/dist/heart/daemon/log-tailer.js +82 -12
  50. package/dist/heart/daemon/logs-prune.js +105 -0
  51. package/dist/heart/daemon/message-router.js +17 -8
  52. package/dist/heart/daemon/os-cron-deps.js +134 -0
  53. package/dist/heart/daemon/ouro-bot-entry.js +1 -1
  54. package/dist/heart/daemon/process-manager.js +180 -0
  55. package/dist/heart/daemon/pulse.js +463 -0
  56. package/dist/heart/daemon/run-hooks.js +2 -0
  57. package/dist/heart/daemon/runtime-logging.js +67 -16
  58. package/dist/heart/daemon/runtime-metadata.js +101 -0
  59. package/dist/heart/daemon/runtime-mode.js +67 -0
  60. package/dist/heart/daemon/safe-mode.js +161 -0
  61. package/dist/heart/daemon/sense-manager.js +72 -3
  62. package/dist/heart/daemon/session-id-resolver.js +131 -0
  63. package/dist/heart/daemon/skill-management-installer.js +94 -0
  64. package/dist/heart/daemon/socket-client.js +307 -0
  65. package/dist/heart/daemon/task-scheduler.js +3 -25
  66. package/dist/heart/daemon/thoughts.js +510 -0
  67. package/dist/heart/delegation.js +62 -0
  68. package/dist/heart/habits/habit-migration.js +181 -0
  69. package/dist/heart/habits/habit-parser.js +140 -0
  70. package/dist/heart/habits/habit-scheduler.js +371 -0
  71. package/dist/heart/{daemon → hatch}/hatch-flow.js +30 -120
  72. package/dist/heart/{daemon → hatch}/hatch-specialist.js +3 -3
  73. package/dist/heart/{daemon → hatch}/specialist-prompt.js +10 -7
  74. package/dist/heart/{daemon → hatch}/specialist-tools.js +49 -3
  75. package/dist/heart/identity.js +163 -60
  76. package/dist/heart/kicks.js +2 -20
  77. package/dist/heart/mcp/mcp-server.js +653 -0
  78. package/dist/heart/migrate-config.js +127 -0
  79. package/dist/heart/model-capabilities.js +59 -0
  80. package/dist/heart/outlook/outlook-http.js +439 -0
  81. package/dist/heart/outlook/outlook-read.js +1618 -0
  82. package/dist/heart/outlook/outlook-render.js +1032 -0
  83. package/dist/heart/outlook/outlook-types.js +7 -0
  84. package/dist/heart/outlook/outlook-view.js +194 -0
  85. package/dist/heart/progress-story.js +42 -0
  86. package/dist/heart/provider-failover.js +88 -0
  87. package/dist/heart/provider-ping.js +162 -0
  88. package/dist/heart/providers/anthropic-token.js +163 -0
  89. package/dist/heart/providers/anthropic.js +169 -46
  90. package/dist/heart/providers/azure.js +98 -11
  91. package/dist/heart/providers/error-classification.js +63 -0
  92. package/dist/heart/providers/github-copilot.js +136 -0
  93. package/dist/heart/providers/minimax-vlm.js +189 -0
  94. package/dist/heart/providers/minimax.js +23 -5
  95. package/dist/heart/providers/openai-codex.js +33 -22
  96. package/dist/heart/session-activity.js +172 -0
  97. package/dist/heart/session-recall.js +216 -0
  98. package/dist/heart/start-of-turn-packet.js +339 -0
  99. package/dist/heart/streaming.js +36 -27
  100. package/dist/heart/sync.js +277 -0
  101. package/dist/heart/target-resolution.js +127 -0
  102. package/dist/heart/tempo.js +93 -0
  103. package/dist/heart/temporal-view.js +41 -0
  104. package/dist/heart/tool-activity-callbacks.js +36 -0
  105. package/dist/heart/tool-description.js +135 -0
  106. package/dist/heart/tool-friction.js +55 -0
  107. package/dist/heart/tool-loop.js +200 -0
  108. package/dist/heart/turn-context.js +358 -0
  109. package/dist/heart/turn-coordinator.js +28 -0
  110. package/dist/heart/{daemon → versioning}/ouro-bot-global-installer.js +1 -1
  111. package/dist/heart/{daemon → versioning}/ouro-bot-wrapper.js +1 -1
  112. package/dist/heart/{daemon → versioning}/ouro-path-installer.js +78 -35
  113. package/dist/heart/versioning/ouro-version-manager.js +295 -0
  114. package/dist/heart/{daemon → versioning}/staged-restart.js +40 -8
  115. package/dist/heart/{daemon → versioning}/update-checker.js +12 -2
  116. package/dist/heart/{daemon → versioning}/update-hooks.js +63 -59
  117. package/dist/mind/associative-recall.js +126 -66
  118. package/dist/mind/bundle-manifest.js +8 -1
  119. package/dist/mind/context.js +125 -14
  120. package/dist/mind/{memory.js → diary.js} +68 -96
  121. package/dist/mind/embedding-provider.js +60 -0
  122. package/dist/mind/file-state.js +179 -0
  123. package/dist/mind/first-impressions.js +14 -1
  124. package/dist/mind/friends/channel.js +56 -0
  125. package/dist/mind/friends/group-context.js +144 -0
  126. package/dist/mind/friends/resolver.js +37 -0
  127. package/dist/mind/friends/store-file.js +58 -3
  128. package/dist/mind/friends/trust-explanation.js +74 -0
  129. package/dist/mind/friends/types.js +8 -0
  130. package/dist/mind/journal-index.js +161 -0
  131. package/dist/mind/obligation-steering.js +221 -0
  132. package/dist/mind/pending.js +76 -9
  133. package/dist/mind/prompt.js +885 -109
  134. package/dist/mind/scrutiny.js +173 -0
  135. package/dist/mind/token-estimate.js +8 -12
  136. package/dist/nerves/cli-logging.js +7 -1
  137. package/dist/nerves/coverage/audit.js +1 -1
  138. package/dist/nerves/coverage/file-completeness.js +56 -5
  139. package/dist/nerves/coverage/run-artifacts.js +1 -1
  140. package/dist/nerves/event-buffer.js +111 -0
  141. package/dist/nerves/index.js +224 -4
  142. package/dist/nerves/observation.js +20 -0
  143. package/dist/nerves/redact.js +79 -0
  144. package/dist/nerves/runtime.js +5 -1
  145. package/dist/outlook-ui/assets/index-Ck8agNeO.js +61 -0
  146. package/dist/outlook-ui/assets/index-LwChZTgL.css +1 -0
  147. package/dist/outlook-ui/index.html +15 -0
  148. package/dist/repertoire/ado-client.js +15 -56
  149. package/dist/repertoire/ado-semantic.js +11 -10
  150. package/dist/repertoire/api-client.js +97 -0
  151. package/dist/repertoire/bitwarden-store.js +319 -0
  152. package/dist/repertoire/bundle-templates.js +72 -0
  153. package/dist/repertoire/bw-installer.js +79 -0
  154. package/dist/repertoire/coding/codex-jsonl.js +64 -0
  155. package/dist/repertoire/coding/context-pack.js +330 -0
  156. package/dist/repertoire/coding/feedback.js +197 -30
  157. package/dist/repertoire/coding/manager.js +149 -2
  158. package/dist/repertoire/coding/spawner.js +55 -9
  159. package/dist/repertoire/coding/tools.js +170 -7
  160. package/dist/repertoire/commerce-errors.js +109 -0
  161. package/dist/repertoire/commerce-self-test.js +156 -0
  162. package/dist/repertoire/credential-access.js +515 -0
  163. package/dist/repertoire/duffel-client.js +185 -0
  164. package/dist/repertoire/github-client.js +14 -55
  165. package/dist/repertoire/graph-client.js +11 -52
  166. package/dist/repertoire/guardrails.js +375 -0
  167. package/dist/repertoire/mcp-client.js +255 -0
  168. package/dist/repertoire/mcp-manager.js +305 -0
  169. package/dist/repertoire/mcp-tools.js +63 -0
  170. package/dist/repertoire/shell-sessions.js +133 -0
  171. package/dist/repertoire/skills.js +14 -23
  172. package/dist/repertoire/stripe-client.js +131 -0
  173. package/dist/repertoire/tasks/board.js +43 -5
  174. package/dist/repertoire/tasks/fix.js +182 -0
  175. package/dist/repertoire/tasks/index.js +28 -10
  176. package/dist/repertoire/tasks/lifecycle.js +2 -2
  177. package/dist/repertoire/tasks/parser.js +3 -2
  178. package/dist/repertoire/tasks/scanner.js +194 -37
  179. package/dist/repertoire/tasks/transitions.js +11 -28
  180. package/dist/repertoire/tool-results.js +29 -0
  181. package/dist/repertoire/tools-attachments.js +316 -0
  182. package/dist/repertoire/tools-base.js +45 -771
  183. package/dist/repertoire/tools-bluebubbles.js +94 -0
  184. package/dist/repertoire/tools-bridge.js +141 -0
  185. package/dist/repertoire/tools-bundle.js +978 -0
  186. package/dist/repertoire/tools-config.js +185 -0
  187. package/dist/repertoire/tools-continuity.js +248 -0
  188. package/dist/repertoire/tools-credential.js +182 -0
  189. package/dist/repertoire/tools-files.js +342 -0
  190. package/dist/repertoire/tools-flight.js +224 -0
  191. package/dist/repertoire/tools-flow.js +105 -0
  192. package/dist/repertoire/tools-github.js +1 -7
  193. package/dist/repertoire/tools-memory.js +347 -0
  194. package/dist/repertoire/tools-session.js +739 -0
  195. package/dist/repertoire/tools-shell.js +120 -0
  196. package/dist/repertoire/tools-stripe.js +180 -0
  197. package/dist/repertoire/tools-surface.js +243 -0
  198. package/dist/repertoire/tools-teams.js +12 -62
  199. package/dist/repertoire/tools-travel.js +125 -0
  200. package/dist/repertoire/tools-user-profile.js +144 -0
  201. package/dist/repertoire/tools-vault.js +110 -0
  202. package/dist/repertoire/tools.js +148 -130
  203. package/dist/repertoire/travel-api-client.js +360 -0
  204. package/dist/repertoire/user-profile.js +118 -0
  205. package/dist/repertoire/vault-setup.js +241 -0
  206. package/dist/scripts/claude-code-hook.js +41 -0
  207. package/dist/scripts/claude-code-stop-hook.js +47 -0
  208. package/dist/senses/attention-queue.js +116 -0
  209. package/dist/senses/bluebubbles/attachment-cache.js +53 -0
  210. package/dist/senses/bluebubbles/attachment-download.js +137 -0
  211. package/dist/senses/{bluebubbles-client.js → bluebubbles/client.js} +143 -9
  212. package/dist/senses/bluebubbles/entry.js +13 -0
  213. package/dist/senses/bluebubbles/inbound-log.js +110 -0
  214. package/dist/senses/bluebubbles/index.js +1410 -0
  215. package/dist/senses/{bluebubbles-media.js → bluebubbles/media.js} +122 -70
  216. package/dist/senses/{bluebubbles-model.js → bluebubbles/model.js} +29 -16
  217. package/dist/senses/{bluebubbles-mutation-log.js → bluebubbles/mutation-log.js} +46 -6
  218. package/dist/senses/bluebubbles/runtime-state.js +109 -0
  219. package/dist/senses/bluebubbles/session-cleanup.js +72 -0
  220. package/dist/senses/cli/bracketed-paste.js +82 -0
  221. package/dist/senses/cli/image-paste.js +287 -0
  222. package/dist/senses/cli/image-ref-navigation.js +75 -0
  223. package/dist/senses/cli/ink-app.js +156 -0
  224. package/dist/senses/cli/inline-diff.js +64 -0
  225. package/dist/senses/cli/input-keys.js +174 -0
  226. package/dist/senses/cli/kill-ring.js +86 -0
  227. package/dist/senses/cli/message-list.js +51 -0
  228. package/dist/senses/cli/ouro-tui.js +595 -0
  229. package/dist/senses/cli/spinner-imperative.js +135 -0
  230. package/dist/senses/cli/spinner.js +101 -0
  231. package/dist/senses/cli/status-line.js +60 -0
  232. package/dist/senses/cli/streaming-markdown.js +526 -0
  233. package/dist/senses/cli/tool-display.js +83 -0
  234. package/dist/senses/cli/tool-render.js +85 -0
  235. package/dist/senses/cli/tui-store.js +240 -0
  236. package/dist/senses/cli/virtual-list.js +35 -0
  237. package/dist/senses/cli-entry.js +1 -1
  238. package/dist/senses/cli-layout.js +187 -0
  239. package/dist/senses/cli.js +588 -246
  240. package/dist/senses/commands.js +65 -1
  241. package/dist/senses/continuity.js +94 -0
  242. package/dist/senses/habit-turn-message.js +108 -0
  243. package/dist/senses/inner-dialog-worker.js +112 -19
  244. package/dist/senses/inner-dialog.js +633 -86
  245. package/dist/senses/pipeline.js +560 -0
  246. package/dist/senses/shared-turn.js +195 -0
  247. package/dist/senses/surface-tool.js +68 -0
  248. package/dist/senses/teams.js +664 -165
  249. package/dist/senses/trust-gate.js +112 -2
  250. package/package.json +14 -7
  251. package/skills/agent-commerce.md +106 -0
  252. package/skills/browser-navigation.md +110 -0
  253. package/skills/commerce-setup-guide.md +116 -0
  254. package/skills/commerce-setup.md +84 -0
  255. package/skills/configure-dev-tools.md +81 -0
  256. package/skills/travel-planning.md +138 -0
  257. package/dist/heart/daemon/subagent-installer.js +0 -134
  258. package/dist/senses/bluebubbles-entry.js +0 -11
  259. package/dist/senses/bluebubbles.js +0 -421
  260. package/dist/senses/debug-activity.js +0 -108
  261. package/subagents/README.md +0 -73
  262. package/subagents/work-doer.md +0 -235
  263. package/subagents/work-merger.md +0 -618
  264. package/subagents/work-planner.md +0 -382
  265. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/basilisk.md +0 -0
  266. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jafar.md +0 -0
  267. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jormungandr.md +0 -0
  268. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/kaa.md +0 -0
  269. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/medusa.md +0 -0
  270. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/monty.md +0 -0
  271. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/nagini.md +0 -0
  272. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/ouroboros.md +0 -0
  273. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/python.md +0 -0
  274. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/quetzalcoatl.md +0 -0
  275. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/sir-hiss.md +0 -0
  276. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/the-serpent.md +0 -0
  277. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/the-snake.md +0 -0
  278. /package/dist/heart/{daemon → hatch}/hatch-animation.js +0 -0
  279. /package/dist/heart/{daemon → hatch}/specialist-orchestrator.js +0 -0
  280. /package/dist/heart/{daemon → versioning}/ouro-uti.js +0 -0
  281. /package/dist/heart/{daemon → versioning}/wrapper-publish-guard.js +0 -0
@@ -35,59 +35,42 @@ 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
- const config_1 = require("../heart/config");
42
- const identity_1 = require("../heart/identity");
43
42
  const runtime_1 = require("../nerves/runtime");
44
- const DEFAULT_EMBEDDING_MODEL = "text-embedding-3-small";
43
+ const diary_1 = require("./diary");
44
+ const embedding_provider_1 = require("./embedding-provider");
45
45
  const DEFAULT_MIN_SCORE = 0.5;
46
46
  const DEFAULT_TOP_K = 3;
47
- class OpenAIEmbeddingProvider {
48
- apiKey;
49
- model;
50
- constructor(apiKey, model = DEFAULT_EMBEDDING_MODEL) {
51
- this.apiKey = apiKey;
52
- this.model = model;
53
- }
54
- async embed(texts) {
55
- const response = await fetch("https://api.openai.com/v1/embeddings", {
56
- method: "POST",
57
- headers: {
58
- Authorization: `Bearer ${this.apiKey}`,
59
- "Content-Type": "application/json",
60
- },
61
- body: JSON.stringify({
62
- model: this.model,
63
- input: texts,
64
- }),
65
- });
66
- if (!response.ok) {
67
- throw new Error(`embedding request failed: ${response.status} ${response.statusText}`);
68
- }
69
- const payload = (await response.json());
70
- if (!payload.data || payload.data.length !== texts.length) {
71
- throw new Error("embedding response missing expected vectors");
72
- }
73
- return payload.data.map((entry) => entry.embedding);
74
- }
75
- }
76
47
  function createDefaultProvider() {
77
- const apiKey = (0, config_1.getOpenAIEmbeddingsApiKey)();
78
- if (!apiKey) {
48
+ const provider = (0, embedding_provider_1.createDefaultEmbeddingProvider)();
49
+ if (!provider) {
79
50
  throw new Error("openaiEmbeddingsApiKey not configured");
80
51
  }
81
- return new OpenAIEmbeddingProvider(apiKey);
52
+ return provider;
82
53
  }
83
- function readFacts(memoryRoot) {
84
- const factsPath = path.join(memoryRoot, "facts.jsonl");
54
+ function readFacts(diaryRoot) {
55
+ const factsPath = path.join(diaryRoot, "facts.jsonl");
85
56
  if (!fs.existsSync(factsPath))
86
57
  return [];
87
58
  const raw = fs.readFileSync(factsPath, "utf8").trim();
88
59
  if (!raw)
89
60
  return [];
90
- return raw.split("\n").map((line) => JSON.parse(line));
61
+ const facts = [];
62
+ for (const line of raw.split("\n")) {
63
+ const trimmed = line.trim();
64
+ if (!trimmed)
65
+ continue;
66
+ try {
67
+ facts.push(JSON.parse(trimmed));
68
+ }
69
+ catch {
70
+ // Skip corrupt lines (e.g. partial write from a crash).
71
+ }
72
+ }
73
+ return facts;
91
74
  }
92
75
  function getLatestUserText(messages) {
93
76
  for (let i = messages.length - 1; i >= 0; i--) {
@@ -133,6 +116,40 @@ async function recallFactsForQuery(query, facts, provider, options) {
133
116
  .sort((left, right) => right.score - left.score)
134
117
  .slice(0, topK);
135
118
  }
119
+ function readJournalIndex(journalDir) {
120
+ const indexPath = path.join(journalDir, ".index.json");
121
+ try {
122
+ const raw = fs.readFileSync(indexPath, "utf8");
123
+ const parsed = JSON.parse(raw);
124
+ if (!Array.isArray(parsed))
125
+ return [];
126
+ return parsed;
127
+ }
128
+ catch {
129
+ return [];
130
+ }
131
+ }
132
+ function searchJournalIndex(queryEmbedding, entries, options) {
133
+ const minScore = options?.minScore ?? DEFAULT_MIN_SCORE;
134
+ const topK = options?.topK ?? DEFAULT_TOP_K;
135
+ return entries
136
+ .filter((entry) => Array.isArray(entry.embedding) && entry.embedding.length > 0)
137
+ .map((entry) => ({
138
+ filename: entry.filename,
139
+ preview: entry.preview,
140
+ score: cosineSimilarity(queryEmbedding, entry.embedding),
141
+ }))
142
+ .filter((entry) => entry.score >= minScore)
143
+ .sort((left, right) => right.score - left.score)
144
+ .slice(0, topK);
145
+ }
146
+ function resolveJournalDir(diaryRoot, explicitJournalDir) {
147
+ if (explicitJournalDir)
148
+ return explicitJournalDir;
149
+ // journal/ is a sibling of diary/ at the agent root level
150
+ const agentRoot = path.dirname(diaryRoot);
151
+ return path.join(agentRoot, "journal");
152
+ }
136
153
  async function injectAssociativeRecall(messages, options) {
137
154
  try {
138
155
  if (messages[0]?.role !== "system" || typeof messages[0].content !== "string")
@@ -140,37 +157,80 @@ async function injectAssociativeRecall(messages, options) {
140
157
  const query = getLatestUserText(messages);
141
158
  if (!query)
142
159
  return;
143
- const memoryRoot = options?.memoryRoot ?? path.join((0, identity_1.getAgentRoot)(), "psyche", "memory");
144
- const facts = readFacts(memoryRoot);
145
- if (facts.length === 0)
160
+ const diaryRoot = options?.diaryRoot ?? (0, diary_1.resolveDiaryRoot)();
161
+ const facts = readFacts(diaryRoot);
162
+ const journalDir = resolveJournalDir(diaryRoot, options?.journalDir);
163
+ const journalEntries = readJournalIndex(journalDir);
164
+ if (facts.length === 0 && journalEntries.length === 0)
146
165
  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 },
166
+ // Build combined result lines tagged by source
167
+ const resultLines = [];
168
+ let queryEmbedding;
169
+ // Search diary entries
170
+ if (facts.length > 0) {
171
+ let recalled;
172
+ try {
173
+ const provider = options?.provider ?? createDefaultProvider();
174
+ recalled = await recallFactsForQuery(query, facts, provider, options);
175
+ // Compute query embedding for journal search while provider is available
176
+ if (journalEntries.length > 0) {
177
+ const [qe] = await provider.embed([query.trim()]);
178
+ queryEmbedding = qe;
179
+ }
180
+ }
181
+ catch {
182
+ // Embeddings unavailable — fall back to substring matching
183
+ const lowerQuery = query.toLowerCase();
184
+ const topK = options?.topK ?? DEFAULT_TOP_K;
185
+ recalled = facts
186
+ .filter((fact) => fact.text.toLowerCase().includes(lowerQuery))
187
+ .slice(0, topK)
188
+ .map((fact) => ({ ...fact, score: 1 }));
189
+ if (recalled.length > 0) {
190
+ (0, runtime_1.emitNervesEvent)({
191
+ level: "warn",
192
+ component: "mind",
193
+ event: "mind.associative_recall_fallback",
194
+ message: "embeddings unavailable, used substring fallback",
195
+ meta: { matchCount: recalled.length },
196
+ });
197
+ }
198
+ }
199
+ for (const fact of recalled) {
200
+ resultLines.push({
201
+ text: `[diary] ${fact.text} [score=${fact.score.toFixed(3)} source=${fact.source}]`,
202
+ score: fact.score,
167
203
  });
168
204
  }
169
205
  }
170
- if (recalled.length === 0)
206
+ // Search journal entries (works whether diary had results or not)
207
+ if (journalEntries.length > 0) {
208
+ try {
209
+ if (!queryEmbedding) {
210
+ const provider = options?.provider ?? createDefaultProvider();
211
+ const [qe] = await provider.embed([query.trim()]);
212
+ queryEmbedding = qe;
213
+ }
214
+ if (queryEmbedding) {
215
+ const journalResults = searchJournalIndex(queryEmbedding, journalEntries, options);
216
+ for (const entry of journalResults) {
217
+ resultLines.push({
218
+ text: `[journal] ${entry.filename}: ${entry.preview} [score=${entry.score.toFixed(3)}]`,
219
+ score: entry.score,
220
+ });
221
+ }
222
+ }
223
+ }
224
+ catch {
225
+ // Embeddings unavailable — no journal fallback
226
+ }
227
+ }
228
+ if (resultLines.length === 0)
171
229
  return;
172
- const recallSection = recalled
173
- .map((fact, index) => `${index + 1}. ${fact.text} [score=${fact.score.toFixed(3)} source=${fact.source}]`)
230
+ // Sort all results by score descending
231
+ resultLines.sort((left, right) => right.score - left.score);
232
+ const recallSection = resultLines
233
+ .map((entry, index) => `${index + 1}. ${entry.text}`)
174
234
  .join("\n");
175
235
  messages[0] = {
176
236
  role: "system",
@@ -180,7 +240,7 @@ async function injectAssociativeRecall(messages, options) {
180
240
  component: "mind",
181
241
  event: "mind.associative_recall",
182
242
  message: "associative recall injected",
183
- meta: { count: recalled.length },
243
+ meta: { count: resultLines.length },
184
244
  });
185
245
  }
186
246
  catch (error) {
@@ -190,7 +250,7 @@ async function injectAssociativeRecall(messages, options) {
190
250
  event: "mind.associative_recall_error",
191
251
  message: "associative recall failed",
192
252
  meta: {
193
- reason: error instanceof Error ? error.message : String(error),
253
+ reason: error instanceof Error ? error.message : /* v8 ignore start -- defensive: non-Error catch branch @preserve */ String(error) /* v8 ignore stop */,
194
254
  },
195
255
  });
196
256
  }
@@ -52,8 +52,15 @@ exports.CANONICAL_BUNDLE_MANIFEST = [
52
52
  { path: "psyche/LORE.md", kind: "file" },
53
53
  { path: "psyche/TACIT.md", kind: "file" },
54
54
  { path: "psyche/ASPIRATIONS.md", kind: "file" },
55
- { path: "psyche/memory", kind: "dir" },
55
+ { path: "arc", kind: "dir" },
56
+ { path: "arc/episodes", kind: "dir" },
57
+ { path: "arc/obligations", kind: "dir" },
58
+ { path: "arc/cares", kind: "dir" },
59
+ { path: "arc/intentions", kind: "dir" },
60
+ { path: "diary", kind: "dir" },
61
+ { path: "journal", kind: "dir" },
56
62
  { path: "friends", kind: "dir" },
63
+ { path: "state", kind: "dir" },
57
64
  { path: "tasks", kind: "dir" },
58
65
  { path: "skills", kind: "dir" },
59
66
  { path: "senses", kind: "dir" },
@@ -36,7 +36,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.trimMessages = trimMessages;
37
37
  exports.validateSessionMessages = validateSessionMessages;
38
38
  exports.repairSessionMessages = repairSessionMessages;
39
+ exports.migrateToolNames = migrateToolNames;
39
40
  exports.saveSession = saveSession;
41
+ exports.appendSyntheticAssistantMessage = appendSyntheticAssistantMessage;
40
42
  exports.loadSession = loadSession;
41
43
  exports.postTurn = postTurn;
42
44
  exports.deleteSession = deleteSession;
@@ -50,17 +52,17 @@ function buildTrimmableBlocks(messages) {
50
52
  let i = 0;
51
53
  while (i < messages.length) {
52
54
  const msg = messages[i];
53
- if (msg?.role === "system") {
55
+ if (msg.role === "system") {
54
56
  i++;
55
57
  continue;
56
58
  }
57
59
  // Tool coherence block: assistant message with tool_calls + immediately following tool results
58
- if (msg?.role === "assistant" && Array.isArray(msg.tool_calls) && msg.tool_calls.length > 0) {
60
+ if (msg.role === "assistant" && Array.isArray(msg.tool_calls) && msg.tool_calls.length > 0) {
59
61
  const indices = [i];
60
62
  i++;
61
63
  while (i < messages.length) {
62
64
  const next = messages[i];
63
- if (next?.role !== "tool")
65
+ if (next.role !== "tool")
64
66
  break;
65
67
  indices.push(i);
66
68
  i++;
@@ -78,13 +80,13 @@ function buildTrimmableBlocks(messages) {
78
80
  function getSystemMessageIndices(messages) {
79
81
  const indices = [];
80
82
  for (let i = 0; i < messages.length; i++) {
81
- if (messages[i]?.role === "system")
83
+ if (messages[i].role === "system")
82
84
  indices.push(i);
83
85
  }
84
86
  return indices;
85
87
  }
86
88
  function buildTrimmedMessages(messages, kept) {
87
- return messages.filter((m, idx) => m?.role === "system" || kept.has(idx));
89
+ return messages.filter((m, idx) => m.role === "system" || kept.has(idx));
88
90
  }
89
91
  function trimMessages(messages, maxTokens, contextMargin, actualTokenCount) {
90
92
  const targetTokens = Math.floor(maxTokens * (1 - contextMargin / 100));
@@ -132,7 +134,7 @@ function trimMessages(messages, maxTokens, contextMargin, actualTokenCount) {
132
134
  let remaining = actualTokenCount;
133
135
  const kept = new Set();
134
136
  for (let i = 0; i < messages.length; i++) {
135
- if (messages[i]?.role !== "system")
137
+ if (messages[i].role !== "system")
136
138
  kept.add(i);
137
139
  }
138
140
  // Drop oldest blocks until we fall under target.
@@ -146,7 +148,7 @@ function trimMessages(messages, maxTokens, contextMargin, actualTokenCount) {
146
148
  let trimmed = buildTrimmedMessages(messages, kept);
147
149
  // If we're still above budget after dropping everything trimmable, preserve system only.
148
150
  if (remaining > targetTokens) {
149
- trimmed = messages.filter((m) => m?.role === "system");
151
+ trimmed = messages.filter((m) => m.role === "system");
150
152
  }
151
153
  const estimatedAfter = (0, token_estimate_1.estimateTokensForMessages)(trimmed);
152
154
  (0, runtime_1.emitNervesEvent)({
@@ -219,7 +221,7 @@ function repairSessionMessages(messages) {
219
221
  result.push(msg);
220
222
  }
221
223
  (0, runtime_1.emitNervesEvent)({
222
- level: "warn",
224
+ level: "info",
223
225
  event: "mind.session_invariant_repair",
224
226
  component: "mind",
225
227
  message: "repaired session invariant violations",
@@ -227,11 +229,79 @@ function repairSessionMessages(messages) {
227
229
  });
228
230
  return result;
229
231
  }
230
- function saveSession(filePath, messages, lastUsage) {
232
+ function stripOrphanedToolResults(messages) {
233
+ const validCallIds = new Set();
234
+ for (const msg of messages) {
235
+ if (msg.role !== "assistant" || !Array.isArray(msg.tool_calls))
236
+ continue;
237
+ for (const toolCall of msg.tool_calls)
238
+ validCallIds.add(toolCall.id);
239
+ }
240
+ let removed = 0;
241
+ const repaired = messages.filter((msg) => {
242
+ if (msg.role !== "tool")
243
+ return true;
244
+ const keep = validCallIds.has(msg.tool_call_id);
245
+ if (!keep)
246
+ removed++;
247
+ return keep;
248
+ });
249
+ if (removed > 0) {
250
+ (0, runtime_1.emitNervesEvent)({
251
+ level: "info",
252
+ event: "mind.session_orphan_tool_result_repair",
253
+ component: "mind",
254
+ message: "removed orphaned tool results from session history",
255
+ meta: { removed },
256
+ });
257
+ }
258
+ return repaired;
259
+ }
260
+ // Tool renames that have shipped. Old names in session history confuse the
261
+ // model into calling tools that no longer exist. Applied on session load so
262
+ // the transcript uses the current vocabulary.
263
+ const TOOL_NAME_MIGRATIONS = {
264
+ final_answer: "settle",
265
+ no_response: "observe",
266
+ go_inward: "ponder",
267
+ descend: "ponder",
268
+ memory_save: "diary_write",
269
+ memory_search: "recall",
270
+ };
271
+ function migrateToolNames(messages) {
272
+ let migrated = 0;
273
+ const result = messages.map((msg) => {
274
+ if (msg.role !== "assistant" || !Array.isArray(msg.tool_calls) || msg.tool_calls.length === 0)
275
+ return msg;
276
+ let changed = false;
277
+ const updatedCalls = msg.tool_calls.map((tc) => {
278
+ if (tc.type !== "function")
279
+ return tc;
280
+ const newName = TOOL_NAME_MIGRATIONS[tc.function.name];
281
+ if (!newName)
282
+ return tc;
283
+ changed = true;
284
+ migrated++;
285
+ return { ...tc, function: { ...tc.function, name: newName } };
286
+ });
287
+ return changed ? { ...msg, tool_calls: updatedCalls } : msg;
288
+ });
289
+ if (migrated > 0) {
290
+ (0, runtime_1.emitNervesEvent)({
291
+ level: "info",
292
+ event: "mind.session_tool_name_migration",
293
+ component: "mind",
294
+ message: "migrated deprecated tool names in session history",
295
+ meta: { migrated },
296
+ });
297
+ }
298
+ return result;
299
+ }
300
+ function saveSession(filePath, messages, lastUsage, state) {
231
301
  const violations = validateSessionMessages(messages);
232
302
  if (violations.length > 0) {
233
303
  (0, runtime_1.emitNervesEvent)({
234
- level: "warn",
304
+ level: "info",
235
305
  event: "mind.session_invariant_violation",
236
306
  component: "mind",
237
307
  message: "session invariant violated on save",
@@ -239,12 +309,41 @@ function saveSession(filePath, messages, lastUsage) {
239
309
  });
240
310
  messages = repairSessionMessages(messages);
241
311
  }
312
+ messages = stripOrphanedToolResults(messages);
242
313
  fs.mkdirSync(path.dirname(filePath), { recursive: true });
243
314
  const envelope = { version: 1, messages };
244
315
  if (lastUsage)
245
316
  envelope.lastUsage = lastUsage;
317
+ if (state?.mustResolveBeforeHandoff === true || typeof state?.lastFriendActivityAt === "string") {
318
+ envelope.state = {
319
+ ...(state?.mustResolveBeforeHandoff === true ? { mustResolveBeforeHandoff: true } : {}),
320
+ ...(typeof state?.lastFriendActivityAt === "string" ? { lastFriendActivityAt: state.lastFriendActivityAt } : {}),
321
+ };
322
+ }
246
323
  fs.writeFileSync(filePath, JSON.stringify(envelope, null, 2));
247
324
  }
325
+ function appendSyntheticAssistantMessage(filePath, content) {
326
+ try {
327
+ if (!fs.existsSync(filePath))
328
+ return false;
329
+ const raw = fs.readFileSync(filePath, "utf-8");
330
+ const data = JSON.parse(raw);
331
+ if (data.version !== 1 || !Array.isArray(data.messages))
332
+ return false;
333
+ data.messages.push({ role: "assistant", content });
334
+ fs.writeFileSync(filePath, JSON.stringify(data, null, 2));
335
+ (0, runtime_1.emitNervesEvent)({
336
+ component: "mind",
337
+ event: "mind.session_synthetic_message_appended",
338
+ message: "appended synthetic assistant message to session",
339
+ meta: { path: filePath, contentLength: content.length },
340
+ });
341
+ return true;
342
+ }
343
+ catch {
344
+ return false;
345
+ }
346
+ }
248
347
  function loadSession(filePath) {
249
348
  try {
250
349
  const raw = fs.readFileSync(filePath, "utf-8");
@@ -255,7 +354,7 @@ function loadSession(filePath) {
255
354
  const violations = validateSessionMessages(messages);
256
355
  if (violations.length > 0) {
257
356
  (0, runtime_1.emitNervesEvent)({
258
- level: "warn",
357
+ level: "info",
259
358
  event: "mind.session_invariant_violation",
260
359
  component: "mind",
261
360
  message: "session invariant violated on load",
@@ -263,13 +362,25 @@ function loadSession(filePath) {
263
362
  });
264
363
  messages = repairSessionMessages(messages);
265
364
  }
266
- return { messages, lastUsage: data.lastUsage };
365
+ messages = stripOrphanedToolResults(messages);
366
+ messages = migrateToolNames(messages);
367
+ const rawState = data?.state && typeof data.state === "object" && data.state !== null
368
+ ? data.state
369
+ : undefined;
370
+ const state = rawState && (rawState.mustResolveBeforeHandoff === true
371
+ || typeof rawState.lastFriendActivityAt === "string")
372
+ ? {
373
+ ...(rawState.mustResolveBeforeHandoff === true ? { mustResolveBeforeHandoff: true } : {}),
374
+ ...(typeof rawState.lastFriendActivityAt === "string" ? { lastFriendActivityAt: rawState.lastFriendActivityAt } : {}),
375
+ }
376
+ : undefined;
377
+ return { messages, lastUsage: data.lastUsage, state };
267
378
  }
268
379
  catch {
269
380
  return null;
270
381
  }
271
382
  }
272
- function postTurn(messages, sessPath, usage, hooks) {
383
+ function postTurn(messages, sessPath, usage, hooks, state) {
273
384
  if (hooks?.beforeTrim) {
274
385
  try {
275
386
  hooks.beforeTrim([...messages]);
@@ -289,7 +400,7 @@ function postTurn(messages, sessPath, usage, hooks) {
289
400
  const { maxTokens, contextMargin } = (0, config_1.getContextConfig)();
290
401
  const trimmed = trimMessages(messages, maxTokens, contextMargin, usage?.input_tokens);
291
402
  messages.splice(0, messages.length, ...trimmed);
292
- saveSession(sessPath, messages, usage);
403
+ saveSession(sessPath, messages, usage, state);
293
404
  }
294
405
  function deleteSession(filePath) {
295
406
  try {