@ouro.bot/cli 0.1.0-alpha.665 → 0.1.0-alpha.667

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 (35) hide show
  1. package/changelog.json +13 -0
  2. package/dist/arc/flight-recorder.js +324 -5
  3. package/dist/heart/core.js +167 -4
  4. package/dist/heart/cross-chat-delivery.js +3 -2
  5. package/dist/heart/daemon/cli-exec.js +139 -1
  6. package/dist/heart/daemon/cli-help.js +13 -2
  7. package/dist/heart/daemon/cli-parse.js +138 -2
  8. package/dist/heart/daemon/daemon-entry.js +24 -5
  9. package/dist/heart/daemon/daemon.js +10 -1
  10. package/dist/heart/habits/habit-parser.js +8 -0
  11. package/dist/heart/habits/habit-runtime-state.js +17 -3
  12. package/dist/heart/habits/habit-scheduler.js +24 -5
  13. package/dist/heart/habits/habit-session-summary.js +318 -0
  14. package/dist/heart/habits/habit-session.js +618 -0
  15. package/dist/heart/mailbox/mailbox-http-hooks.js +29 -1
  16. package/dist/heart/mailbox/mailbox-http-routes.js +122 -1
  17. package/dist/heart/mailbox/mailbox-read.js +5 -1
  18. package/dist/heart/mailbox/readers/runtime-readers.js +87 -0
  19. package/dist/mailbox-ui/assets/index-CaTIFDmv.js +1 -0
  20. package/dist/mailbox-ui/assets/index-Du_9G9WO.css +1 -0
  21. package/dist/mailbox-ui/assets/vendor-CcN1XpQ9.js +61 -0
  22. package/dist/mailbox-ui/index.html +3 -2
  23. package/dist/repertoire/tools-notes.js +50 -0
  24. package/dist/repertoire/tools-record.js +13 -0
  25. package/dist/repertoire/tools-session.js +140 -0
  26. package/dist/repertoire/tools-surface.js +11 -0
  27. package/dist/repertoire/tools.js +7 -0
  28. package/dist/senses/habit-turn-message.js +41 -3
  29. package/dist/senses/inner-dialog-worker.js +264 -68
  30. package/dist/senses/inner-dialog.js +29 -15
  31. package/dist/senses/pipeline.js +2 -11
  32. package/dist/senses/surface-tool.js +2 -1
  33. package/package.json +1 -1
  34. package/dist/mailbox-ui/assets/index-BZ60na8O.js +0 -61
  35. package/dist/mailbox-ui/assets/index-DG6Xf5uL.css +0 -1
@@ -6,8 +6,9 @@
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-BZ60na8O.js"></script>
10
- <link rel="stylesheet" crossorigin href="/assets/index-DG6Xf5uL.css">
9
+ <script type="module" crossorigin src="/assets/index-CaTIFDmv.js"></script>
10
+ <link rel="modulepreload" crossorigin href="/assets/vendor-CcN1XpQ9.js">
11
+ <link rel="stylesheet" crossorigin href="/assets/index-Du_9G9WO.css">
11
12
  </head>
12
13
  <body>
13
14
  <div id="app"></div>
@@ -1,13 +1,57 @@
1
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
+ })();
2
35
  Object.defineProperty(exports, "__esModule", { value: true });
3
36
  exports.notesToolDefinitions = void 0;
4
37
  const child_process_1 = require("child_process");
38
+ const path = __importStar(require("path"));
5
39
  const skills_1 = require("./skills");
6
40
  const config_1 = require("../heart/config");
7
41
  const runtime_1 = require("../nerves/runtime");
42
+ const identity_1 = require("../heart/identity");
8
43
  const diary_1 = require("../mind/diary");
44
+ const record_paths_1 = require("../mind/record-paths");
9
45
  const provenance_trust_1 = require("../mind/provenance-trust");
10
46
  const CLAUDE_READ_ONLY_TOOLS = "Read,Grep,Glob,LS";
47
+ function bundleRelativeLocator(filePath, ctx) {
48
+ const agentRoot = ctx?.agentRoot ?? (0, identity_1.getAgentRoot)();
49
+ const relativePath = path.relative(agentRoot, filePath);
50
+ if (relativePath && !relativePath.startsWith("..") && !path.isAbsolute(relativePath)) {
51
+ return relativePath.split(path.sep).join("/");
52
+ }
53
+ return filePath.split(path.sep).join("/");
54
+ }
11
55
  exports.notesToolDefinitions = [
12
56
  {
13
57
  tool: {
@@ -252,6 +296,12 @@ exports.notesToolDefinitions = [
252
296
  about: typeof a.about === "string" ? a.about : undefined,
253
297
  provenance,
254
298
  });
299
+ if (result.added > 0) {
300
+ ctx?.habitSession?.recordProducedRef?.({
301
+ kind: "desk_record",
302
+ locator: bundleRelativeLocator(path.join((0, record_paths_1.resolveRecordDiaryRoot)(), "facts.jsonl"), ctx),
303
+ });
304
+ }
255
305
  return `saved diary entry (added=${result.added}, skipped=${result.skipped})`;
256
306
  },
257
307
  summaryKeys: ["entry", "about"],
@@ -42,6 +42,7 @@ const note_search_1 = require("../mind/note-search");
42
42
  const record_paths_1 = require("../mind/record-paths");
43
43
  const types_1 = require("../mind/friends/types");
44
44
  const runtime_1 = require("../nerves/runtime");
45
+ const identity_1 = require("../heart/identity");
45
46
  const NOTES_INDEX_VERSION = 1;
46
47
  const NOTE_SLUG_MAX_CHARS = 40;
47
48
  const DEFAULT_LIMIT = 5;
@@ -61,6 +62,14 @@ function hasRecordReadTrust(ctx) {
61
62
  const friend = ctx?.context?.friend;
62
63
  return Boolean(friend) && (0, types_1.isTrustedLevel)(friend?.trustLevel);
63
64
  }
65
+ function bundleRelativeLocator(filePath, ctx) {
66
+ const agentRoot = ctx?.agentRoot ?? (0, identity_1.getAgentRoot)();
67
+ const relativePath = path.relative(agentRoot, filePath);
68
+ if (relativePath && !relativePath.startsWith("..") && !path.isAbsolute(relativePath)) {
69
+ return relativePath.split(path.sep).join("/");
70
+ }
71
+ return filePath.split(path.sep).join("/");
72
+ }
64
73
  function normalizeTags(value) {
65
74
  if (value === undefined || value === null)
66
75
  return undefined;
@@ -371,6 +380,10 @@ exports.recordToolDefinitions = [
371
380
  const savedPath = ensureUniquePath(notesDir, date, slugForContent(content));
372
381
  fs.writeFileSync(savedPath, renderNote(createdAt, cappedContent, tags), "utf8");
373
382
  await updateIndexForSavedNote(notesDir, indexPath, savedPath, provider);
383
+ ctx?.habitSession?.recordProducedRef?.({
384
+ kind: "desk_record",
385
+ locator: bundleRelativeLocator(savedPath, ctx),
386
+ });
374
387
  (0, runtime_1.emitNervesEvent)({
375
388
  component: "repertoire",
376
389
  event: "repertoire.record_note_saved",
@@ -51,11 +51,13 @@ const coding_1 = require("./coding");
51
51
  const pending_1 = require("../mind/pending");
52
52
  const obligations_1 = require("../arc/obligations");
53
53
  const progress_story_1 = require("../heart/progress-story");
54
+ const habit_session_summary_1 = require("../heart/habits/habit-session-summary");
54
55
  const cross_chat_delivery_1 = require("../heart/cross-chat-delivery");
55
56
  const mail_import_discovery_1 = require("../heart/mail-import-discovery");
56
57
  const outbound_1 = require("../senses/voice/outbound");
57
58
  const NO_SESSION_FOUND_MESSAGE = "no session found for that friend/channel/key combination.";
58
59
  const EMPTY_SESSION_MESSAGE = "session exists but has no non-system messages.";
60
+ const VALID_HABIT_SUMMARY_WHICH = new Set(["latest", "previous", "latest-success", "latest-failure"]);
59
61
  async function summarizeSessionTailSafely(options) {
60
62
  try {
61
63
  return await (0, session_transcript_1.summarizeSessionTail)(options);
@@ -99,6 +101,79 @@ function normalizeProgressOutcome(text) {
99
101
  }
100
102
  return trimmed;
101
103
  }
104
+ function optionalArg(args, key) {
105
+ const value = args[key];
106
+ if (value === undefined || value === null)
107
+ return undefined;
108
+ if (typeof value !== "string")
109
+ return null;
110
+ const trimmed = value.trim();
111
+ return trimmed ? trimmed : undefined;
112
+ }
113
+ function validateSessionSummarySelector(args) {
114
+ const runId = optionalArg(args, "runId");
115
+ const habitName = optionalArg(args, "habitName");
116
+ const operationId = optionalArg(args, "operationId");
117
+ const which = optionalArg(args, "which");
118
+ if (runId === null || habitName === null || operationId === null || which === null) {
119
+ return {
120
+ ok: false,
121
+ code: which === null ? "invalid_which" : "selector_required",
122
+ message: which === null
123
+ ? "which must be latest, previous, latest-success, or latest-failure"
124
+ : "selector fields must be strings",
125
+ };
126
+ }
127
+ if (runId !== undefined) {
128
+ if (habitName !== undefined || operationId !== undefined || which !== undefined) {
129
+ return {
130
+ ok: false,
131
+ code: "run_id_exclusive",
132
+ message: "runId cannot be combined with habitName, operationId, or which",
133
+ };
134
+ }
135
+ return { ok: true, selector: { runId } };
136
+ }
137
+ if (habitName === undefined && operationId === undefined) {
138
+ return {
139
+ ok: false,
140
+ code: "selector_required",
141
+ message: "provide runId, habitName, or operationId",
142
+ };
143
+ }
144
+ if (which !== undefined && !VALID_HABIT_SUMMARY_WHICH.has(which)) {
145
+ return {
146
+ ok: false,
147
+ code: "invalid_which",
148
+ message: "which must be latest, previous, latest-success, or latest-failure",
149
+ };
150
+ }
151
+ return {
152
+ ok: true,
153
+ selector: {
154
+ ...(habitName !== undefined ? { habitName } : {}),
155
+ ...(operationId !== undefined ? { operationId } : {}),
156
+ ...(which !== undefined ? { which } : {}),
157
+ },
158
+ };
159
+ }
160
+ function renderSessionSummaryText(summary) {
161
+ const lines = [
162
+ `habit ${summary.habitName} run ${summary.runId} finished with ${summary.status}.`,
163
+ summary.operationId ? `operation: ${summary.operationId}` : null,
164
+ summary.summary,
165
+ summary.nextLikelyStep ? `next: ${summary.nextLikelyStep}` : null,
166
+ summary.decisions.length > 0 ? `decisions: ${summary.decisions.join("; ")}` : null,
167
+ summary.pending.count > 0 ? `pending: ${summary.pending.count} file(s) (${summary.pending.files.join(", ")})` : "pending: none",
168
+ summary.messagesSent.length > 0 ? `messages: ${summary.messagesSent.length}` : "messages: none",
169
+ summary.toolsUsed.length > 0 ? `tools: ${summary.toolsUsed.join(", ")}` : "tools: none",
170
+ summary.errors.length > 0 ? `errors: ${summary.errors.join("; ")}` : null,
171
+ summary.warnings.length > 0 ? `warnings: ${summary.warnings.join("; ")}` : null,
172
+ `receipt: ${summary.sources.receipt}`,
173
+ `session: ${summary.sources.session}`,
174
+ ];
175
+ return lines.filter((line) => Boolean(line)).join("\n");
176
+ }
102
177
  function writePendingEnvelope(queueDir, message) {
103
178
  fs.mkdirSync(queueDir, { recursive: true });
104
179
  const fileName = `${message.timestamp}-${Math.random().toString(36).slice(2, 10)}.json`;
@@ -127,6 +202,19 @@ function renderCrossChatDeliveryStatus(target, result) {
127
202
  outcomeText: `${lead}\n${result.detail}`,
128
203
  }));
129
204
  }
205
+ function normalizeHabitSendStatus(status) {
206
+ return status === "queued_for_later" ? "queued" : status;
207
+ }
208
+ function recordHabitSendAttempt(ctx, args, result) {
209
+ ctx?.habitSession?.recordSurfaceAttempt?.({
210
+ recipient: args.friendId,
211
+ channel: args.channel,
212
+ reason: result.status === "blocked" ? "blocked" : result.status === "failed" ? "other" : "status",
213
+ result: normalizeHabitSendStatus(result.status),
214
+ rawStatus: result.rawStatus ?? result.status,
215
+ ...(result.status === "blocked" || result.status === "failed" ? { error: result.detail } : {}),
216
+ });
217
+ }
130
218
  async function deliverVoiceChannelMessage(request, agentName, initialAudio) {
131
219
  const result = await (0, outbound_1.placeTrustedFriendVoiceOutboundCall)({
132
220
  agentName,
@@ -356,6 +444,57 @@ exports.sessionToolDefinitions = [
356
444
  return `this is my current top-level live world-state.\nanswer whole-self status questions from this before drilling into individual sessions.\n\n${(0, active_work_1.formatActiveWorkFrame)(frame)}`;
357
445
  },
358
446
  },
447
+ {
448
+ tool: {
449
+ type: "function",
450
+ function: {
451
+ name: "session_summary",
452
+ description: "read-only orientation for habit runs. returns a structured live summary from habit receipts, session files, pending dirs, and runtime cursors without writing state.",
453
+ parameters: {
454
+ type: "object",
455
+ properties: {
456
+ runId: { type: "string", description: "exact habit run id; cannot be combined with habitName, operationId, or which" },
457
+ habitName: { type: "string", description: "habit name to select from" },
458
+ operationId: { type: "string", description: "operation id for stateful habit run groups, such as habit:heartbeat" },
459
+ which: {
460
+ type: "string",
461
+ enum: ["latest", "previous", "latest-success", "latest-failure"],
462
+ description: "which matching run to read; defaults to latest",
463
+ },
464
+ },
465
+ },
466
+ },
467
+ },
468
+ handler: (args) => {
469
+ const validation = validateSessionSummarySelector(args);
470
+ if (!validation.ok) {
471
+ return JSON.stringify({
472
+ kind: "invalid_selector",
473
+ code: validation.code,
474
+ message: validation.message,
475
+ }, null, 2);
476
+ }
477
+ const summary = (0, habit_session_summary_1.readHabitSessionSummary)((0, identity_1.getAgentRoot)(), validation.selector);
478
+ if (!summary) {
479
+ return JSON.stringify({
480
+ kind: "not_found",
481
+ message: "no habit run matched selector",
482
+ selector: validation.selector,
483
+ }, null, 2);
484
+ }
485
+ return JSON.stringify({
486
+ kind: "habit_session_summary",
487
+ text: renderSessionSummaryText(summary),
488
+ summary,
489
+ }, null, 2);
490
+ },
491
+ riskProfile: {
492
+ mutates: "none",
493
+ risk: "low",
494
+ reason: "reads habit run summaries from local receipts and session artifacts",
495
+ },
496
+ summaryKeys: ["runId", "habitName", "operationId", "which"],
497
+ },
359
498
  {
360
499
  tool: {
361
500
  type: "function",
@@ -724,6 +863,7 @@ exports.sessionToolDefinitions = [
724
863
  voice: async (request) => deliverVoiceChannelMessage(request, agentName, voiceInitialAudio),
725
864
  },
726
865
  });
866
+ recordHabitSendAttempt(ctx, { friendId, channel, key }, deliveryResult);
727
867
  return renderCrossChatDeliveryStatus(`${friendId} on ${channel}/${key}`, deliveryResult);
728
868
  },
729
869
  riskProfile: sendMessageRiskProfile,
@@ -332,6 +332,17 @@ exports.surfaceToolDefinition = {
332
332
  }
333
333
  /* v8 ignore stop */
334
334
  },
335
+ onRouteResult: ({ targetFriendId, queueItem, result }) => {
336
+ ctx?.habitSession?.recordSurfaceAttempt?.({
337
+ recipient: targetFriendId,
338
+ channel: queueItem?.channel ?? args.channel ?? "surface",
339
+ reason: result.status === "failed" ? "blocked" : "answer",
340
+ result: result.status,
341
+ rawStatus: result.status,
342
+ ...(queueItem ? { routeKind: "originator" } : {}),
343
+ ...(result.status === "failed" ? { error: result.detail ?? "surface route failed" } : {}),
344
+ });
345
+ },
335
346
  });
336
347
  },
337
348
  summaryKeys: ["content", "delegationId", "friendId", "channel"],
@@ -3,6 +3,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.surfaceToolDef = exports.speakTool = exports.restTool = exports.ponderTool = exports.observeTool = exports.settleTool = exports.tools = void 0;
4
4
  exports.resetMcpDefinitions = resetMcpDefinitions;
5
5
  exports.getToolsForChannel = getToolsForChannel;
6
+ exports.shellRiskProfile = shellRiskProfile;
7
+ exports.riskProfileForTool = riskProfileForTool;
8
+ exports.riskProfileForToolName = riskProfileForToolName;
6
9
  exports.execTool = execTool;
7
10
  exports.summarizeArgs = summarizeArgs;
8
11
  exports.buildToolResultSummary = buildToolResultSummary;
@@ -202,6 +205,10 @@ function riskProfileForTool(def, name, args) {
202
205
  return def.riskProfile(args);
203
206
  return def.riskProfile ?? { mutates: "none", risk: "low" };
204
207
  }
208
+ function riskProfileForToolName(name, args) {
209
+ const def = findDefinition(name);
210
+ return def ? riskProfileForTool(def, name, args) : null;
211
+ }
205
212
  function orientationHoldMessage(name, profile, reason) {
206
213
  return `orientation hold: ${reason} Available: orientation_get plus read-only inspection tools like trip_status, query_session, read_config, read_file, grep, search_facts, consult_diary, and consult_notes. Resolve the referent/correction, then retry ${name} if the action is still correct. Blocked ${mutationKindsFor(profile).join(", ")}. ${profile.reason}.`;
207
214
  }
@@ -2,6 +2,8 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.buildHabitTurnMessage = buildHabitTurnMessage;
4
4
  const runtime_1 = require("../nerves/runtime");
5
+ const PRIOR_SESSION_SUMMARY_LIMIT = 1600;
6
+ const PRIOR_SESSION_TRUNCATION_SUFFIX = "\n[truncated]";
5
7
  function formatElapsed(ms) {
6
8
  const minutes = Math.floor(ms / 60000);
7
9
  if (minutes < 60) {
@@ -11,9 +13,9 @@ function formatElapsed(ms) {
11
13
  return `${hours} ${hours === 1 ? "hour" : "hours"}`;
12
14
  }
13
15
  function buildHabitTurnMessage(options) {
14
- const { habitName, habitTitle, habitBody, lastRun, checkpoint, alsoDue, staleObligations, parseErrors, degradedComponents, arcResume, deskOrientation, surfacePolicy, now, } = options;
16
+ const { habitName, habitTitle, habitBody, lastRun, checkpoint, alsoDue, staleObligations, parseErrors, degradedComponents, arcResume, deskOrientation, surfacePolicy, priorSessionSummary, now, } = options;
15
17
  const hasBody = habitBody !== undefined && habitBody !== "";
16
- const leadingSections = buildLeadingSections(arcResume, deskOrientation, surfacePolicy);
18
+ const leadingSections = buildLeadingSections(arcResume, deskOrientation, surfacePolicy, priorSessionSummary);
17
19
  // First beat: lastRun is null
18
20
  if (lastRun === null) {
19
21
  // Cold start: no checkpoint, no body — bare awareness
@@ -86,7 +88,7 @@ function buildHabitTurnMessage(options) {
86
88
  });
87
89
  return joinSections(leadingSections, sections);
88
90
  }
89
- function buildLeadingSections(arcResume, deskOrientation, surfacePolicy) {
91
+ function buildLeadingSections(arcResume, deskOrientation, surfacePolicy, priorSessionSummary) {
90
92
  const sections = [];
91
93
  if (arcResume?.trim())
92
94
  sections.push(arcResume.trim());
@@ -94,11 +96,47 @@ function buildLeadingSections(arcResume, deskOrientation, surfacePolicy) {
94
96
  sections.push(deskOrientation.trim());
95
97
  if (surfacePolicy?.trim())
96
98
  sections.push(surfacePolicy.trim());
99
+ const priorSummary = buildPriorSessionSummarySection(priorSessionSummary);
100
+ if (priorSummary)
101
+ sections.push(priorSummary);
97
102
  return sections;
98
103
  }
99
104
  function joinSections(leadingSections, sections) {
100
105
  return [...leadingSections, ...sections].filter((section) => section.trim().length > 0).join("\n\n");
101
106
  }
107
+ function truncatePriorSummary(summary) {
108
+ if (summary.length <= PRIOR_SESSION_SUMMARY_LIMIT)
109
+ return summary;
110
+ return `${summary.slice(0, PRIOR_SESSION_SUMMARY_LIMIT - PRIOR_SESSION_TRUNCATION_SUFFIX.length)}${PRIOR_SESSION_TRUNCATION_SUFFIX}`;
111
+ }
112
+ function buildPriorSessionSummarySection(priorSessionSummary) {
113
+ if (!priorSessionSummary || priorSessionSummary.mode !== "stateful")
114
+ return null;
115
+ const lines = ["## Prior habit session summary"];
116
+ const summary = priorSessionSummary.summary?.trim();
117
+ if (summary) {
118
+ lines.push(truncatePriorSummary(summary));
119
+ }
120
+ else {
121
+ lines.push("No prior stateful habit summary found for this operation.");
122
+ }
123
+ const sourceLines = ["receipt", "session", "pending", "runtimeState"]
124
+ .flatMap((key) => {
125
+ const value = cleanSourceLocator(priorSessionSummary.sources[key]);
126
+ return value ? [`${key}: ${value}`] : [];
127
+ });
128
+ if (sourceLines.length > 0)
129
+ lines.push(...sourceLines);
130
+ if (priorSessionSummary.warnings.length > 0)
131
+ lines.push(`warnings: ${priorSessionSummary.warnings.join("; ")}`);
132
+ return lines.join("\n");
133
+ }
134
+ function cleanSourceLocator(value) {
135
+ if (typeof value !== "string")
136
+ return null;
137
+ const trimmed = value.trim();
138
+ return trimmed.length > 0 ? trimmed : null;
139
+ }
102
140
  function appendTrailingExtras(sections, alsoDue, staleObligations, parseErrors, degradedComponents) {
103
141
  // 4. Also-due
104
142
  if (alsoDue) {