@ouro.bot/cli 0.1.0-alpha.666 → 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.
@@ -43,6 +43,8 @@ exports.readLogView = readLogView;
43
43
  exports.readHabitView = readHabitView;
44
44
  exports.readHabitRunView = readHabitRunView;
45
45
  exports.readHabitRunReceiptView = readHabitRunReceiptView;
46
+ exports.readHabitSessionSummaryListView = readHabitSessionSummaryListView;
47
+ exports.readHabitSessionSummaryView = readHabitSessionSummaryView;
46
48
  exports.readNeedsMeView = readNeedsMeView;
47
49
  exports.readDeskPrefs = readDeskPrefs;
48
50
  const fs = __importStar(require("fs"));
@@ -50,6 +52,7 @@ const path = __importStar(require("path"));
50
52
  const runtime_1 = require("../../../nerves/runtime");
51
53
  const habit_parser_1 = require("../../habits/habit-parser");
52
54
  const habit_runtime_state_1 = require("../../habits/habit-runtime-state");
55
+ const habit_session_summary_1 = require("../../habits/habit-session-summary");
53
56
  const identity_1 = require("../../identity");
54
57
  const daemon_health_1 = require("../../daemon/daemon-health");
55
58
  const flight_recorder_1 = require("../../../arc/flight-recorder");
@@ -656,6 +659,34 @@ function readHabitRunReceiptView(agentRoot, runId) {
656
659
  });
657
660
  return receipt ? { receipt } : null;
658
661
  }
662
+ function readHabitSessionSummaryListView(agentRoot, options = {}) {
663
+ const limit = normalizeHabitRunLimit(options.limit);
664
+ const receipts = (0, flight_recorder_1.listHabitRunReceipts)(agentRoot);
665
+ const items = receipts.slice(0, limit)
666
+ .map((receipt) => (0, habit_session_summary_1.readHabitSessionSummary)(agentRoot, { runId: receipt.runId }))
667
+ .filter((summary) => summary !== null);
668
+ (0, runtime_1.emitNervesEvent)({
669
+ component: "heart",
670
+ event: "heart.mailbox_habit_run_summaries_read",
671
+ message: "reading mailbox habit run summaries",
672
+ meta: { agentRoot, totalCount: receipts.length, limit, itemCount: items.length },
673
+ });
674
+ return {
675
+ totalCount: receipts.length,
676
+ limit,
677
+ items,
678
+ };
679
+ }
680
+ function readHabitSessionSummaryView(agentRoot, selector) {
681
+ const summary = (0, habit_session_summary_1.readHabitSessionSummary)(agentRoot, selector);
682
+ (0, runtime_1.emitNervesEvent)({
683
+ component: "heart",
684
+ event: "heart.mailbox_habit_run_summary_read",
685
+ message: "reading mailbox habit run summary",
686
+ meta: { agentRoot, runId: summary?.runId ?? null, habitName: summary?.habitName ?? selector.habitName ?? null, found: summary !== null },
687
+ });
688
+ return summary;
689
+ }
659
690
  function readNeedsMeView(agentName, options = {}) {
660
691
  const bundlesRoot = options.bundlesRoot ?? (0, identity_1.getAgentBundlesRoot)();
661
692
  const now = options.now?.() ?? new Date();
@@ -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`;
@@ -369,6 +444,57 @@ exports.sessionToolDefinitions = [
369
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)}`;
370
445
  },
371
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
+ },
372
498
  {
373
499
  tool: {
374
500
  type: "function",
@@ -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) {
@@ -43,8 +43,10 @@ const runtime_1 = require("../nerves/runtime");
43
43
  const identity_1 = require("../heart/identity");
44
44
  const pending_1 = require("../mind/pending");
45
45
  const habit_parser_1 = require("../heart/habits/habit-parser");
46
+ const habit_runtime_state_1 = require("../heart/habits/habit-runtime-state");
46
47
  const habit_session_1 = require("../heart/habits/habit-session");
47
48
  const flight_recorder_1 = require("../arc/flight-recorder");
49
+ const habit_session_summary_1 = require("../heart/habits/habit-session-summary");
48
50
  const store_file_1 = require("../mind/friends/store-file");
49
51
  const tools_base_1 = require("../repertoire/tools-base");
50
52
  const tools_surface_1 = require("../repertoire/tools-surface");
@@ -89,6 +91,79 @@ function isHeartbeatOkRestResult(result) {
89
91
  const maybeResult = result;
90
92
  return maybeResult.turnOutcome === "rested" && maybeResult.restStatus === "HEARTBEAT_OK";
91
93
  }
94
+ function isRecord(value) {
95
+ return !!value && typeof value === "object" && !Array.isArray(value);
96
+ }
97
+ function contentToText(content) {
98
+ if (typeof content === "string")
99
+ return content.trim();
100
+ if (!Array.isArray(content))
101
+ return "";
102
+ return content
103
+ .map((part) => isRecord(part) && typeof part.text === "string" ? part.text : "")
104
+ .filter((text) => text.trim().length > 0)
105
+ .join("\n")
106
+ .trim();
107
+ }
108
+ function resultMessages(result) {
109
+ if (Array.isArray(result))
110
+ return result.flatMap((entry) => resultMessages(entry));
111
+ return isRecord(result) && Array.isArray(result.messages) ? result.messages : [];
112
+ }
113
+ function latestAssistantText(results) {
114
+ for (let resultIndex = results.length - 1; resultIndex >= 0; resultIndex--) {
115
+ const messages = resultMessages(results[resultIndex]);
116
+ for (let messageIndex = messages.length - 1; messageIndex >= 0; messageIndex--) {
117
+ const message = messages[messageIndex];
118
+ if (!isRecord(message) || message.role !== "assistant")
119
+ continue;
120
+ const text = contentToText(message.content);
121
+ if (text.length > 0)
122
+ return text.replace(/^checkpoint\s*:\s*/i, "").trim() || text;
123
+ }
124
+ }
125
+ return null;
126
+ }
127
+ function deriveHabitSummarySnapshot(habitRun) {
128
+ const assistant = latestAssistantText(habitRun.results);
129
+ if (assistant)
130
+ return { summary: assistant, decisions: [], nextLikelyStep: null };
131
+ if (habitRun.errors.length > 0) {
132
+ return {
133
+ summary: `Habit ${habitRun.habit.name} finished with errors: ${habitRun.errors.join("; ")}`,
134
+ decisions: [],
135
+ nextLikelyStep: null,
136
+ };
137
+ }
138
+ const surfaced = habitRun.surfaceAttempts.find((attempt) => attempt.result !== "blocked" && attempt.result !== "failed" && attempt.result !== "unavailable");
139
+ if (surfaced) {
140
+ return {
141
+ summary: `Habit ${habitRun.habit.name} surfaced via ${surfaced.recipient}/${surfaced.channel}.`,
142
+ decisions: [],
143
+ nextLikelyStep: null,
144
+ };
145
+ }
146
+ const produced = habitRun.producedRefs.find((ref) => ref.kind !== "none");
147
+ if (produced) {
148
+ return {
149
+ summary: `Habit ${habitRun.habit.name} produced ${produced.kind}: ${produced.locator}.`,
150
+ decisions: [],
151
+ nextLikelyStep: null,
152
+ };
153
+ }
154
+ if (habitRun.results.some(isHeartbeatOkRestResult)) {
155
+ return {
156
+ summary: `Habit ${habitRun.habit.name} rested with HEARTBEAT_OK.`,
157
+ decisions: [],
158
+ nextLikelyStep: null,
159
+ };
160
+ }
161
+ return {
162
+ summary: `Habit ${habitRun.habit.name} completed without additional surfaced output.`,
163
+ decisions: [],
164
+ nextLikelyStep: null,
165
+ };
166
+ }
92
167
  function fallbackHabitFile(habitName) {
93
168
  return {
94
169
  name: habitName,
@@ -100,6 +175,7 @@ function fallbackHabitFile(habitName) {
100
175
  tools: [],
101
176
  origin: null,
102
177
  surface: { family: false, originator: false, extra: [] },
178
+ continuity: { mode: "fresh" },
103
179
  body: "",
104
180
  };
105
181
  }
@@ -124,8 +200,10 @@ function readHabitForRun(agentRoot, habitName, errors) {
124
200
  async function prepareHabitRun(habitName, trigger, startedAt) {
125
201
  const agentRoot = (0, identity_1.getAgentRoot)();
126
202
  const errors = [];
127
- const habit = readHabitForRun(agentRoot, habitName, errors);
203
+ const habit = (0, habit_runtime_state_1.applyHabitRuntimeState)(agentRoot, readHabitForRun(agentRoot, habitName, errors));
128
204
  const runId = (0, flight_recorder_1.createHabitRunId)(habitName, new Date(startedAt));
205
+ const operationId = habit.continuity.mode === "stateful" ? `habit:${habit.name}` : null;
206
+ const priorSessionSummary = readPriorSessionSummary(agentRoot, operationId);
129
207
  const paths = (0, habit_session_1.createHabitSessionPaths)(agentRoot, runId, habit.name);
130
208
  const friendStore = new store_file_1.FileFriendStore(path.join(agentRoot, "friends"));
131
209
  const permissionEnvelope = await (0, habit_session_1.normalizeHabitPermissionEnvelope)(habit, { agentRoot, friendStore });
@@ -134,8 +212,10 @@ async function prepareHabitRun(habitName, trigger, startedAt) {
134
212
  agentRoot,
135
213
  habit,
136
214
  runId,
215
+ operationId,
137
216
  trigger,
138
217
  startedAt,
218
+ priorSessionSummary,
139
219
  paths,
140
220
  permissionEnvelope,
141
221
  toolPolicy,
@@ -150,6 +230,29 @@ function riskProfileForHabitPolicy(definition, name) {
150
230
  const probeArgs = name === "shell" ? { command: "touch /tmp/habit-policy-probe" } : {};
151
231
  return (0, tools_1.riskProfileForTool)(definition, name, probeArgs);
152
232
  }
233
+ function readPriorSessionSummary(agentRoot, operationId) {
234
+ if (operationId === null)
235
+ return undefined;
236
+ try {
237
+ const summary = (0, habit_session_summary_1.readHabitSessionSummary)(agentRoot, { operationId, which: "latest" });
238
+ if (!summary)
239
+ return { mode: "stateful", summary: null, sources: {}, warnings: [] };
240
+ return {
241
+ mode: "stateful",
242
+ summary: summary.summary,
243
+ sources: summary.sources,
244
+ warnings: summary.warnings,
245
+ };
246
+ }
247
+ catch (error) {
248
+ return {
249
+ mode: "stateful",
250
+ summary: null,
251
+ sources: {},
252
+ warnings: [`prior summary read failed: ${String(error)}`],
253
+ };
254
+ }
255
+ }
153
256
  function createInnerDialogWorker(runTurn = (options) => (0, inner_dialog_1.runInnerDialogTurn)(options), hasPendingWork = (pendingDir) => (0, pending_1.hasPendingMessages)(pendingDir ?? (0, pending_1.getInnerDialogPendingDir)((0, identity_1.getAgentName)())), nowSource = () => Date.now()) {
154
257
  let running = false;
155
258
  const queue = [];
@@ -164,11 +267,13 @@ function createInnerDialogWorker(runTurn = (options) => (0, inner_dialog_1.runIn
164
267
  trigger: habitRun.trigger,
165
268
  startedAt: habitRun.startedAt,
166
269
  endedAt,
270
+ operationId: habitRun.operationId,
167
271
  permissionEnvelope: habitRun.permissionEnvelope,
168
272
  toolPolicy: habitRun.toolPolicy,
169
273
  producedRefs: habitRun.producedRefs,
170
274
  surfaceAttempts: habitRun.surfaceAttempts,
171
275
  errors: habitRun.errors,
276
+ summarySnapshot: deriveHabitSummarySnapshot(habitRun),
172
277
  });
173
278
  }
174
279
  function clearHeartbeatRestShield() {
@@ -286,6 +391,13 @@ function createInnerDialogWorker(runTurn = (options) => (0, inner_dialog_1.runIn
286
391
  ...(currentHabitRun
287
392
  ? {
288
393
  trigger: currentHabitRun.trigger,
394
+ preparedHabit: {
395
+ runId: currentHabitRun.runId,
396
+ trigger: currentHabitRun.trigger,
397
+ operationId: currentHabitRun.operationId,
398
+ habit: currentHabitRun.habit,
399
+ priorSessionSummary: currentHabitRun.priorSessionSummary,
400
+ },
289
401
  habitSession: {
290
402
  runId: currentHabitRun.runId,
291
403
  sessionPath: currentHabitRun.paths.sessionPath,
@@ -692,24 +692,35 @@ async function runInnerDialogTurn(options) {
692
692
  const agentRoot = (0, identity_1.getAgentRoot)();
693
693
  const habitName = options.habitName;
694
694
  const habitFilePath = path.join(agentRoot, "habits", `${habitName}.md`);
695
+ const preparedHabit = options.preparedHabit?.habit.name === habitName ? options.preparedHabit.habit : null;
695
696
  // Read and parse the habit file
696
697
  let habitBody;
697
698
  let habitTitle = habitName;
698
699
  let habitLastRun = null;
699
700
  let habitOrigin = null;
700
701
  let habitSurface = { family: true, originator: true, extra: [] };
701
- try {
702
- const habitContent = fs.readFileSync(habitFilePath, "utf-8");
703
- const parsed = (0, habit_runtime_state_1.applyHabitRuntimeState)(agentRoot, (0, habit_parser_1.parseHabitFile)(habitContent, habitFilePath));
704
- habitBody = parsed.body || undefined;
705
- habitTitle = parsed.title || habitName;
706
- habitLastRun = parsed.lastRun;
707
- habitTools = parsed.tools;
708
- habitOrigin = parsed.origin;
709
- habitSurface = parsed.surface;
702
+ if (preparedHabit) {
703
+ habitBody = preparedHabit.body || undefined;
704
+ habitTitle = preparedHabit.title || habitName;
705
+ habitLastRun = preparedHabit.lastRun;
706
+ habitTools = preparedHabit.tools;
707
+ habitOrigin = preparedHabit.origin;
708
+ habitSurface = preparedHabit.surface;
710
709
  }
711
- catch {
712
- // Habit file missing or unreadable
710
+ else {
711
+ try {
712
+ const habitContent = fs.readFileSync(habitFilePath, "utf-8");
713
+ const parsed = (0, habit_runtime_state_1.applyHabitRuntimeState)(agentRoot, (0, habit_parser_1.parseHabitFile)(habitContent, habitFilePath));
714
+ habitBody = parsed.body || undefined;
715
+ habitTitle = parsed.title || habitName;
716
+ habitLastRun = parsed.lastRun;
717
+ habitTools = parsed.tools;
718
+ habitOrigin = parsed.origin;
719
+ habitSurface = parsed.surface;
720
+ }
721
+ catch {
722
+ // Habit file missing or unreadable
723
+ }
713
724
  }
714
725
  // If the habit file couldn't be read at all (no body, no title parsed), error message
715
726
  if (habitBody === undefined && habitTitle === habitName) {
@@ -753,6 +764,7 @@ async function runInnerDialogTurn(options) {
753
764
  arcResume,
754
765
  deskOrientation,
755
766
  surfacePolicy,
767
+ priorSessionSummary: options.preparedHabit?.habit.name === habitName ? options.preparedHabit.priorSessionSummary : undefined,
756
768
  now,
757
769
  });
758
770
  }
@@ -875,7 +887,7 @@ async function runInnerDialogTurn(options) {
875
887
  runAgent: core_1.runAgent,
876
888
  postTurn: (turnMessages, sessionPathArg, usage, hooks, state) => {
877
889
  const prepared = (0, context_1.postTurnTrim)(turnMessages, usage, hooks);
878
- (0, context_1.deferPostTurnPersist)(sessionPathArg, prepared, usage, state);
890
+ return (0, context_1.deferPostTurnPersist)(sessionPathArg, prepared, usage, state);
879
891
  },
880
892
  accumulateFriendTokens: tokens_1.accumulateFriendTokens,
881
893
  signal: options?.signal,
@@ -827,7 +827,7 @@ async function handleInboundTurn(input) {
827
827
  /* v8 ignore next -- defensive: error always set when errored @preserve */
828
828
  result.error?.message ?? "unknown error", classification, currentProvider, currentBinding.model, agentName, inventory, {}, { currentLane });
829
829
  input.failoverState.pending = failoverContext;
830
- input.postTurn(sessionMessages, session.sessionPath, result.usage);
830
+ await input.postTurn(sessionMessages, session.sessionPath, result.usage);
831
831
  try {
832
832
  const agentRoot = (0, identity_1.getAgentRoot)();
833
833
  const postTurnArc = readPostTurnFlightRecorderArcSnapshot(agentRoot);
@@ -902,7 +902,7 @@ async function handleInboundTurn(input) {
902
902
  ? { lastFriendActivityAt }
903
903
  : undefined)
904
904
  : (Object.keys(continuingState).length > 0 ? continuingState : undefined);
905
- input.postTurn(sessionMessages, session.sessionPath, result.usage, undefined, nextState);
905
+ await input.postTurn(sessionMessages, session.sessionPath, result.usage, undefined, nextState);
906
906
  try {
907
907
  const agentRoot = (0, identity_1.getAgentRoot)();
908
908
  const postTurnArc = readPostTurnFlightRecorderArcSnapshot(agentRoot);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ouro.bot/cli",
3
- "version": "0.1.0-alpha.666",
3
+ "version": "0.1.0-alpha.667",
4
4
  "main": "dist/heart/daemon/ouro-entry.js",
5
5
  "bin": {
6
6
  "cli": "dist/heart/daemon/ouro-bot-entry.js",