@ouro.bot/cli 0.1.0-alpha.655 → 0.1.0-alpha.658

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 (55) hide show
  1. package/README.md +13 -13
  2. package/changelog.json +21 -0
  3. package/dist/arc/evolution.js +1 -1
  4. package/dist/arc/flight-recorder.js +369 -0
  5. package/dist/arc/obligations.js +24 -2
  6. package/dist/heart/active-work.js +1 -1
  7. package/dist/heart/config-registry.js +14 -5
  8. package/dist/heart/daemon/agent-config-check.js +1 -1
  9. package/dist/heart/daemon/agent-service.js +18 -17
  10. package/dist/heart/daemon/cli-exec.js +134 -15
  11. package/dist/heart/daemon/cli-help.js +21 -2
  12. package/dist/heart/daemon/cli-parse.js +31 -3
  13. package/dist/heart/daemon/daemon-entry.js +1 -1
  14. package/dist/heart/daemon/daemon.js +3 -3
  15. package/dist/heart/daemon/hooks/bundle-meta.js +29 -9
  16. package/dist/heart/daemon/inner-status.js +4 -15
  17. package/dist/heart/daemon/sense-manager.js +16 -1
  18. package/dist/heart/habits/habit-parser.js +64 -1
  19. package/dist/heart/hatch/hatch-flow.js +17 -9
  20. package/dist/heart/hatch/specialist-tools.js +15 -11
  21. package/dist/heart/identity.js +4 -1
  22. package/dist/heart/kept-notes.js +5 -73
  23. package/dist/heart/mailbox/readers/runtime-readers.js +21 -49
  24. package/dist/heart/mcp/mcp-server.js +8 -8
  25. package/dist/heart/sense-truth.js +2 -0
  26. package/dist/heart/session-events.js +1 -31
  27. package/dist/heart/start-of-turn-packet.js +8 -2
  28. package/dist/heart/tool-description.js +15 -3
  29. package/dist/heart/turn-context.js +34 -7
  30. package/dist/heart/work-card.js +386 -0
  31. package/dist/mailbox-ui/assets/{index-9-AxCxuB.js → index-Cbasiy6y.js} +1 -1
  32. package/dist/mailbox-ui/index.html +1 -1
  33. package/dist/mind/bundle-manifest.js +9 -3
  34. package/dist/mind/context.js +1 -2
  35. package/dist/mind/desk-section.js +53 -1
  36. package/dist/mind/diary.js +2 -3
  37. package/dist/mind/note-search.js +36 -106
  38. package/dist/mind/prompt.js +45 -102
  39. package/dist/mind/record-paths.js +312 -0
  40. package/dist/repertoire/bundle-templates.js +4 -5
  41. package/dist/repertoire/tools-bundle.js +1 -1
  42. package/dist/repertoire/tools-evolution.js +4 -4
  43. package/dist/repertoire/tools-notes.js +42 -62
  44. package/dist/repertoire/tools-record.js +16 -11
  45. package/dist/repertoire/tools-session.js +4 -4
  46. package/dist/repertoire/tools.js +1 -1
  47. package/dist/senses/habit-turn-message.js +19 -5
  48. package/dist/senses/inner-dialog-worker.js +58 -9
  49. package/dist/senses/inner-dialog.js +30 -11
  50. package/dist/senses/pipeline.js +135 -1
  51. package/dist/util/frontmatter.js +17 -1
  52. package/package.json +3 -3
  53. package/skills/configure-dev-tools.md +1 -1
  54. package/skills/travel-planning.md +1 -1
  55. package/dist/mind/journal-index.js +0 -162
@@ -6,7 +6,7 @@
6
6
  <meta name="color-scheme" content="dark" />
7
7
  <title>Ouro Mailbox</title>
8
8
  <meta name="description" content="The daemon-hosted shared orientation surface for agents alive on this machine." />
9
- <script type="module" crossorigin src="/assets/index-9-AxCxuB.js"></script>
9
+ <script type="module" crossorigin src="/assets/index-Cbasiy6y.js"></script>
10
10
  <link rel="stylesheet" crossorigin href="/assets/index-CWzt267f.css">
11
11
  </head>
12
12
  <body>
@@ -57,8 +57,10 @@ exports.CANONICAL_BUNDLE_MANIFEST = [
57
57
  { path: "arc/obligations", kind: "dir" },
58
58
  { path: "arc/cares", kind: "dir" },
59
59
  { path: "arc/intentions", kind: "dir" },
60
- { path: "diary", kind: "dir" },
61
- { path: "journal", kind: "dir" },
60
+ { path: "arc/flight-recorder", kind: "dir" },
61
+ { path: "arc/flight-recorder/events", kind: "dir" },
62
+ { path: "arc/flight-recorder/habit-receipts", kind: "dir" },
63
+ { path: "arc/claims", kind: "dir" },
62
64
  { path: "friends", kind: "dir" },
63
65
  { path: "state", kind: "dir" },
64
66
  { path: "tasks", kind: "dir" },
@@ -72,6 +74,10 @@ exports.CANONICAL_BUNDLE_MANIFEST = [
72
74
  // separate from the legacy `tasks/one-shots/` flat layout.
73
75
  { path: "plugins", kind: "dir" },
74
76
  { path: "desk", kind: "dir" },
77
+ { path: "desk/_record", kind: "dir" },
78
+ { path: "desk/_record/diary", kind: "dir" },
79
+ { path: "desk/_record/diary/daily", kind: "dir" },
80
+ { path: "desk/_record/notes", kind: "dir" },
75
81
  ];
76
82
  function getChangelogPath() {
77
83
  const changelogPath = path.resolve(__dirname, "../../changelog.json");
@@ -98,7 +104,7 @@ function getPackageVersion() {
98
104
  function createBundleMeta() {
99
105
  return {
100
106
  runtimeVersion: getPackageVersion(),
101
- bundleSchemaVersion: 1,
107
+ bundleSchemaVersion: 3,
102
108
  lastUpdated: new Date().toISOString(),
103
109
  };
104
110
  }
@@ -382,7 +382,7 @@ function postTurnTrim(messages, usage, hooks) {
382
382
  function postTurnPersist(sessPath, prepared, usage, state) {
383
383
  const existing = (0, session_events_1.loadSessionEnvelopeFile)(sessPath);
384
384
  const previousMessages = existing ? (0, session_events_1.projectProviderMessages)(existing) : [];
385
- const { envelope, evictedEvents } = (0, session_events_1.buildCanonicalSessionEnvelope)({
385
+ const { envelope } = (0, session_events_1.buildCanonicalSessionEnvelope)({
386
386
  existing,
387
387
  previousMessages,
388
388
  currentMessages: prepared.currentMessages,
@@ -397,7 +397,6 @@ function postTurnPersist(sessPath, prepared, usage, state) {
397
397
  inputTokens: usage?.input_tokens ?? null,
398
398
  },
399
399
  });
400
- (0, session_events_1.appendEvictedToArchive)(sessPath, evictedEvents);
401
400
  writeSessionEnvelope(sessPath, envelope);
402
401
  return envelope.events;
403
402
  }
@@ -34,11 +34,13 @@ var __importStar = (this && this.__importStar) || (function () {
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.formatRelative = formatRelative;
37
+ exports.deskRecordOrientationSection = deskRecordOrientationSection;
37
38
  exports.deskSection = deskSection;
38
39
  const fs = __importStar(require("fs"));
39
40
  const path = __importStar(require("path"));
40
41
  const identity_1 = require("../heart/identity");
41
42
  const runtime_1 = require("../nerves/runtime");
43
+ const record_paths_1 = require("./record-paths");
42
44
  // ──────────────────────────────────────────────────────────────────────────────
43
45
  // Static body — the agent's daily-read description of its desk.
44
46
  //
@@ -66,7 +68,10 @@ i have a desk. it lives at \`desk/\` — a quiet room of my work, persistent acr
66
68
  **what doesn't:**
67
69
  - a single-turn answer — it'll be done before the page turns
68
70
  - ephemeral debugging that resolves in the same exchange
69
- - work that has its own room. trips live in a travel folder; habits keep their own ledger; attention items, diary entries, journal entrieseach has a separate home. the desk *links* to them when relevant; it doesn't absorb them.
71
+ - live continuity, claims, and obligationsthose belong in Arc
72
+ - habit definitions — those stay in habits/
73
+ - scratch thinking that is not worth recording — it can disappear with the session
74
+ - stale top-level rooms. the maintained record belongs under desk/_record, not in a separate scratch workspace.
70
75
 
71
76
  **shape.** tracks group related work — drawers in the cabinet, or sections of a shelf if you prefer the library framing. tasks live in tracks. each task has iterations: one per work session, with \`planning.md\` and \`doing.md\` laid side-by-side on the page.
72
77
 
@@ -291,6 +296,53 @@ function renderCurrently(deskRoot, now = new Date()) {
291
296
  lines.push(`tasks still open: ${nonTerminalCount}`);
292
297
  return lines.join("\n");
293
298
  }
299
+ function countDiaryFacts(agentRoot) {
300
+ const paths = (0, record_paths_1.resolveDeskRecordPaths)(agentRoot);
301
+ try {
302
+ return fs.readFileSync(paths.factsPath, "utf-8")
303
+ .split(/\r?\n/)
304
+ .filter((line) => line.trim().length > 0)
305
+ .length;
306
+ }
307
+ catch {
308
+ return 0;
309
+ }
310
+ }
311
+ function countRecordNotes(agentRoot) {
312
+ const paths = (0, record_paths_1.resolveDeskRecordPaths)(agentRoot);
313
+ try {
314
+ return fs.readdirSync(paths.notesRoot, { withFileTypes: true })
315
+ .filter((entry) => entry.isFile() && !entry.name.startsWith(".") && entry.name.endsWith(".md"))
316
+ .length;
317
+ }
318
+ catch {
319
+ return 0;
320
+ }
321
+ }
322
+ function deskRecordOrientationSection(agentRoot = (0, identity_1.getAgentRoot)(), now = new Date()) {
323
+ const deskRoot = path.join(agentRoot, "desk");
324
+ const tracks = (() => {
325
+ try {
326
+ return listSubdirs(deskRoot)
327
+ .map((slug) => readTrack(deskRoot, slug))
328
+ .filter((track) => Boolean(track));
329
+ }
330
+ catch {
331
+ return [];
332
+ }
333
+ })();
334
+ const activeTracks = tracks.filter((track) => !TERMINAL_TRACK_STATUSES.has(track.status));
335
+ const openTaskCount = tracks.reduce((sum, track) => sum + nonTerminalTasks(track).length, 0);
336
+ const lines = ["## Desk orientation"];
337
+ lines.push(`active tracks: ${activeTracks.length}`);
338
+ lines.push(`open tasks: ${openTaskCount}`);
339
+ lines.push(`diary facts: ${countDiaryFacts(agentRoot)}`);
340
+ lines.push(`record notes: ${countRecordNotes(agentRoot)}`);
341
+ const currently = fs.existsSync(deskRoot) ? renderCurrently(deskRoot, now) : "";
342
+ if (currently)
343
+ lines.push(currently);
344
+ return lines.join("\n");
345
+ }
294
346
  // ──────────────────────────────────────────────────────────────────────────────
295
347
  // Public entry — every-turn synchronous read
296
348
  // ──────────────────────────────────────────────────────────────────────────────
@@ -43,12 +43,12 @@ exports.searchDiaryEntries = searchDiaryEntries;
43
43
  const fs = __importStar(require("fs"));
44
44
  const path = __importStar(require("path"));
45
45
  const crypto_1 = require("crypto");
46
- const identity_1 = require("../heart/identity");
47
46
  const session_events_1 = require("../heart/session-events");
48
47
  const runtime_1 = require("../nerves/runtime");
49
48
  const note_search_1 = require("./note-search");
50
49
  const diary_integrity_1 = require("./diary-integrity");
51
50
  const embedding_provider_1 = require("./embedding-provider");
51
+ const record_paths_1 = require("./record-paths");
52
52
  const DEDUP_THRESHOLD = 0.6;
53
53
  const SEMANTIC_DEDUP_THRESHOLD = 0.95;
54
54
  const ENTITY_TOKEN = /[a-z0-9]+/g;
@@ -223,8 +223,7 @@ async function buildEmbedding(text, embeddingProvider) {
223
223
  function resolveDiaryRoot(explicitRoot) {
224
224
  if (explicitRoot)
225
225
  return explicitRoot;
226
- const agentRoot = (0, identity_1.getAgentRoot)();
227
- return path.join(agentRoot, "diary");
226
+ return (0, record_paths_1.resolveRecordDiaryRoot)();
228
227
  }
229
228
  function readDiaryEntries(diaryRoot) {
230
229
  return readExistingEntries(path.join(resolveDiaryRoot(diaryRoot), "facts.jsonl"));
@@ -35,7 +35,6 @@ var __importStar = (this && this.__importStar) || (function () {
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.cosineSimilarity = cosineSimilarity;
37
37
  exports.searchDiaryFactsForQuery = searchDiaryFactsForQuery;
38
- exports.searchJournalIndex = searchJournalIndex;
39
38
  exports.injectNoteSearchContext = injectNoteSearchContext;
40
39
  const fs = __importStar(require("fs"));
41
40
  const path = __importStar(require("path"));
@@ -117,40 +116,6 @@ async function searchDiaryFactsForQuery(query, facts, provider, options) {
117
116
  .sort((left, right) => right.score - left.score)
118
117
  .slice(0, topK);
119
118
  }
120
- function readJournalIndex(journalDir) {
121
- const indexPath = path.join(journalDir, ".index.json");
122
- try {
123
- const raw = fs.readFileSync(indexPath, "utf8");
124
- const parsed = JSON.parse(raw);
125
- if (!Array.isArray(parsed))
126
- return [];
127
- return parsed;
128
- }
129
- catch {
130
- return [];
131
- }
132
- }
133
- function searchJournalIndex(queryEmbedding, entries, options) {
134
- const minScore = options?.minScore ?? DEFAULT_MIN_SCORE;
135
- const topK = options?.topK ?? DEFAULT_TOP_K;
136
- return entries
137
- .filter((entry) => Array.isArray(entry.embedding) && entry.embedding.length > 0)
138
- .map((entry) => ({
139
- filename: entry.filename,
140
- preview: entry.preview,
141
- score: cosineSimilarity(queryEmbedding, entry.embedding),
142
- }))
143
- .filter((entry) => entry.score >= minScore)
144
- .sort((left, right) => right.score - left.score)
145
- .slice(0, topK);
146
- }
147
- function resolveJournalDir(diaryRoot, explicitJournalDir) {
148
- if (explicitJournalDir)
149
- return explicitJournalDir;
150
- // journal/ is a sibling of diary/ at the agent root level
151
- const agentRoot = path.dirname(diaryRoot);
152
- return path.join(agentRoot, "journal");
153
- }
154
119
  async function injectNoteSearchContext(messages, options) {
155
120
  try {
156
121
  if (messages[0]?.role !== "system" || typeof messages[0].content !== "string")
@@ -160,92 +125,57 @@ async function injectNoteSearchContext(messages, options) {
160
125
  return;
161
126
  const diaryRoot = options?.diaryRoot ?? (0, diary_1.resolveDiaryRoot)();
162
127
  const facts = readFacts(diaryRoot);
163
- const journalDir = resolveJournalDir(diaryRoot, options?.journalDir);
164
- const journalEntries = readJournalIndex(journalDir);
165
- if (facts.length === 0 && journalEntries.length === 0)
128
+ if (facts.length === 0)
166
129
  return;
167
- // Build combined result lines tagged by source
168
130
  const resultLines = [];
169
- let queryEmbedding;
170
- // Search diary entries
171
- if (facts.length > 0) {
172
- let found;
173
- try {
174
- const provider = options?.provider ?? createDefaultProvider();
175
- found = await searchDiaryFactsForQuery(query, facts, provider, options);
176
- // Compute query embedding for journal search while provider is available
177
- if (journalEntries.length > 0) {
178
- const [qe] = await provider.embed([query.trim()]);
179
- queryEmbedding = qe;
180
- }
181
- }
182
- catch {
183
- // Embeddings unavailable — fall back to substring matching
184
- const lowerQuery = query.toLowerCase();
185
- const topK = options?.topK ?? DEFAULT_TOP_K;
186
- found = facts
187
- .filter((fact) => fact.text.toLowerCase().includes(lowerQuery))
188
- .slice(0, topK)
189
- .map((fact) => ({ ...fact, score: 1 }));
190
- if (found.length > 0) {
191
- (0, runtime_1.emitNervesEvent)({
192
- level: "warn",
193
- component: "mind",
194
- event: "mind.note_search_fallback",
195
- message: "embeddings unavailable, used substring fallback",
196
- meta: { matchCount: found.length },
197
- });
198
- }
199
- }
200
- for (const fact of found) {
201
- let meta = `score=${fact.score.toFixed(3)} source=${fact.source}`;
202
- if (fact.provenance) {
203
- if (fact.provenance.channel)
204
- meta += ` channel=${fact.provenance.channel}`;
205
- if (fact.provenance.friendName)
206
- meta += ` friend=${fact.provenance.friendName}`;
207
- if (fact.provenance.trust)
208
- meta += ` trust=${fact.provenance.trust}`;
209
- }
210
- const tag = (0, provenance_trust_1.classifyProvenanceTrust)(fact.provenance) === "external" ? "diary/external" : "diary";
211
- resultLines.push({
212
- text: `[${tag}] ${fact.text} [${meta}]`,
213
- score: fact.score,
131
+ let found;
132
+ try {
133
+ const provider = options?.provider ?? createDefaultProvider();
134
+ found = await searchDiaryFactsForQuery(query, facts, provider, options);
135
+ }
136
+ catch {
137
+ // Embeddings unavailable fall back to substring matching
138
+ const lowerQuery = query.toLowerCase();
139
+ const topK = options?.topK ?? DEFAULT_TOP_K;
140
+ found = facts
141
+ .filter((fact) => fact.text.toLowerCase().includes(lowerQuery))
142
+ .slice(0, topK)
143
+ .map((fact) => ({ ...fact, score: 1 }));
144
+ if (found.length > 0) {
145
+ (0, runtime_1.emitNervesEvent)({
146
+ level: "warn",
147
+ component: "mind",
148
+ event: "mind.note_search_fallback",
149
+ message: "embeddings unavailable, used substring fallback",
150
+ meta: { matchCount: found.length },
214
151
  });
215
152
  }
216
153
  }
217
- // Search journal entries (works whether diary had results or not)
218
- if (journalEntries.length > 0) {
219
- try {
220
- if (!queryEmbedding) {
221
- const provider = options?.provider ?? createDefaultProvider();
222
- const [qe] = await provider.embed([query.trim()]);
223
- queryEmbedding = qe;
224
- }
225
- if (queryEmbedding) {
226
- const journalResults = searchJournalIndex(queryEmbedding, journalEntries, options);
227
- for (const entry of journalResults) {
228
- resultLines.push({
229
- text: `[journal] ${entry.filename}: ${entry.preview} [score=${entry.score.toFixed(3)}]`,
230
- score: entry.score,
231
- });
232
- }
233
- }
234
- }
235
- catch {
236
- // Embeddings unavailable — no journal fallback
154
+ for (const fact of found) {
155
+ let meta = `score=${fact.score.toFixed(3)} source=${fact.source}`;
156
+ if (fact.provenance) {
157
+ if (fact.provenance.channel)
158
+ meta += ` channel=${fact.provenance.channel}`;
159
+ if (fact.provenance.friendName)
160
+ meta += ` friend=${fact.provenance.friendName}`;
161
+ if (fact.provenance.trust)
162
+ meta += ` trust=${fact.provenance.trust}`;
237
163
  }
164
+ const tag = (0, provenance_trust_1.classifyProvenanceTrust)(fact.provenance) === "external" ? "diary/external" : "diary";
165
+ resultLines.push({
166
+ text: `[${tag}] ${fact.text} [${meta}]`,
167
+ score: fact.score,
168
+ });
238
169
  }
239
170
  if (resultLines.length === 0)
240
171
  return;
241
- // Sort all results by score descending
242
172
  resultLines.sort((left, right) => right.score - left.score);
243
173
  const noteSection = resultLines
244
174
  .map((entry, index) => `${index + 1}. ${entry.text}`)
245
175
  .join("\n");
246
176
  messages[0] = {
247
177
  role: "system",
248
- content: `${messages[0].content}\n\n## from my diary and journal\n${noteSection}`,
178
+ content: `${messages[0].content}\n\n## retrieved from my Desk record diary\n${noteSection}`,
249
179
  };
250
180
  (0, runtime_1.emitNervesEvent)({
251
181
  component: "mind",