@ouro.bot/cli 0.1.0-alpha.183 → 0.1.0-alpha.185

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.
@@ -39,6 +39,7 @@ exports.bodyMapSection = bodyMapSection;
39
39
  exports.mcpToolsSection = mcpToolsSection;
40
40
  exports.runtimeInfoSection = runtimeInfoSection;
41
41
  exports.toolRestrictionSection = toolRestrictionSection;
42
+ exports.wakePacketSection = wakePacketSection;
42
43
  exports.centerOfGravitySteeringSection = centerOfGravitySteeringSection;
43
44
  exports.commitmentsSection = commitmentsSection;
44
45
  exports.delegationHintSection = delegationHintSection;
@@ -481,10 +482,13 @@ function bridgeContextSection(options) {
481
482
  return "";
482
483
  return bridgeContext.startsWith("## ") ? bridgeContext : `## active bridge work\n${bridgeContext}`;
483
484
  }
485
+ function wakePacketSection(options) {
486
+ return options?.wakePacket ?? "";
487
+ }
484
488
  function activeWorkSection(options) {
485
489
  if (!options?.activeWorkFrame)
486
490
  return "";
487
- return (0, active_work_1.formatActiveWorkFrame)(options.activeWorkFrame);
491
+ return (0, active_work_1.formatActiveWorkFrame)(options.activeWorkFrame, { hasWakePacket: !!options?.wakePacket });
488
492
  }
489
493
  function liveWorldStateSection(options) {
490
494
  if (!options?.activeWorkFrame)
@@ -506,6 +510,10 @@ function familyCrossSessionTruthSection(context, options) {
506
510
  return "";
507
511
  if (context?.friend?.trustLevel !== "family")
508
512
  return "";
513
+ // When wake packet is present, compress to one line
514
+ if (options?.wakePacket) {
515
+ return "When family asks whole-self status, answer from the cross-session picture above.";
516
+ }
509
517
  return `## cross-session truth
510
518
  When family asks what I'm up to or how things are going, I answer from the live world-state across visible sessions and lanes, not just the current thread.
511
519
  When live state conflicts with older transcript history, live state wins.
@@ -1010,6 +1018,7 @@ async function buildSystem(channel = "cli", options, context) {
1010
1018
  ] : []),
1011
1019
  // Group 7: dynamic state for this turn
1012
1020
  "# dynamic state for this turn",
1021
+ wakePacketSection(options),
1013
1022
  liveWorldStateSection(options),
1014
1023
  pendingMessagesSection(options),
1015
1024
  activeWorkSection(options),
@@ -34,6 +34,7 @@ var __importStar = (this && this.__importStar) || (function () {
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.prepareCodingContextPack = prepareCodingContextPack;
37
+ exports.emitCodingEpisode = emitCodingEpisode;
37
38
  const crypto = __importStar(require("crypto"));
38
39
  const fs = __importStar(require("fs"));
39
40
  const path = __importStar(require("path"));
@@ -41,6 +42,7 @@ const child_process_1 = require("child_process");
41
42
  const active_work_1 = require("../../heart/active-work");
42
43
  const identity_1 = require("../../heart/identity");
43
44
  const runtime_1 = require("../../nerves/runtime");
45
+ const episodes_1 = require("../../mind/episodes");
44
46
  const skills_1 = require("../skills");
45
47
  const CONTEXT_FILENAMES = ["AGENTS.md", "CLAUDE.md"];
46
48
  function defaultRunCommand(command, args, cwd) {
@@ -173,7 +175,7 @@ function buildScopeContent(request, contextFiles, skills, agentName) {
173
175
  formatSkills(skills),
174
176
  ].join("\n");
175
177
  }
176
- function buildStateContent(request, contextKey, generatedAt, snapshot, existingSessions, agentName, activeWorkFrame) {
178
+ function buildStateContent(request, contextKey, generatedAt, snapshot, existingSessions, agentName, activeWorkFrame, wakePacket) {
177
179
  const gitSection = snapshot.available
178
180
  ? [
179
181
  `repoRoot: ${snapshot.repoRoot ?? "unknown"}`,
@@ -190,6 +192,7 @@ function buildStateContent(request, contextKey, generatedAt, snapshot, existingS
190
192
  `agent: ${request.parentAgent ?? agentName}`,
191
193
  formatOrigin(request),
192
194
  `obligationId: ${request.obligationId ?? "none"}`,
195
+ ...(wakePacket ? ["", "## Continuity", wakePacket] : []),
193
196
  "",
194
197
  "## Workspace Snapshot",
195
198
  gitSection,
@@ -226,7 +229,7 @@ function prepareCodingContextPack(input, deps = {}) {
226
229
  const snapshot = captureRepoSnapshot(input.request.workdir, runCommand);
227
230
  const generatedAt = nowIso();
228
231
  const scopeContent = buildScopeContent(input.request, contextFiles, skills, agentName);
229
- const stateContent = buildStateContent(input.request, contextKey, generatedAt, snapshot, existingSessions, agentName, input.activeWorkFrame);
232
+ const stateContent = buildStateContent(input.request, contextKey, generatedAt, snapshot, existingSessions, agentName, input.activeWorkFrame, input.wakePacket || undefined);
230
233
  mkdirSync(contextDir, { recursive: true });
231
234
  writeFileSync(scopeFile, `${scopeContent}\n`, "utf-8");
232
235
  writeFileSync(stateFile, `${stateContent}\n`, "utf-8");
@@ -252,3 +255,23 @@ function prepareCodingContextPack(input, deps = {}) {
252
255
  stateContent,
253
256
  };
254
257
  }
258
+ /**
259
+ * Emit a coding milestone episode when a session completes or blocks.
260
+ * This is the outbound leg of the coding round-trip: compact wake packet goes IN,
261
+ * episodes come BACK OUT.
262
+ */
263
+ function emitCodingEpisode(agentRoot, session, outcome) {
264
+ (0, episodes_1.emitEpisode)(agentRoot, {
265
+ kind: "coding_milestone",
266
+ summary: `coding session ${session.id} (${session.runner}): ${outcome}`,
267
+ whyItMattered: `coding lane ${session.status}`,
268
+ relatedEntities: [`coding:${session.id}`],
269
+ salience: session.status === "failed" ? "high" : "medium",
270
+ });
271
+ (0, runtime_1.emitNervesEvent)({
272
+ component: "repertoire",
273
+ event: "repertoire.coding_episode_emitted",
274
+ message: `coding episode emitted for ${session.id}`,
275
+ meta: { sessionId: session.id, outcome, status: session.status },
276
+ });
277
+ }
@@ -62,6 +62,11 @@ const obligations_1 = require("../mind/obligations");
62
62
  const progress_story_1 = require("../heart/progress-story");
63
63
  const cross_chat_delivery_1 = require("../heart/cross-chat-delivery");
64
64
  const obligations_2 = require("../heart/obligations");
65
+ const episodes_1 = require("../mind/episodes");
66
+ const cares_1 = require("../heart/cares");
67
+ const agent_relationships_1 = require("../heart/agent-relationships");
68
+ const presence_1 = require("../heart/presence");
69
+ const intentions_1 = require("../heart/intentions");
65
70
  // Tracks which file paths have been read via read_file in this session.
66
71
  // edit_file requires a file to be read first (must-read-first guard).
67
72
  exports.editFileReadTracker = new Set();
@@ -1498,6 +1503,241 @@ exports.baseToolDefinitions = [
1498
1503
  requiredCapability: "reasoning-effort",
1499
1504
  summaryKeys: ["level"],
1500
1505
  },
1506
+ // ── Continuity tools ──────────────────────────────────────────────
1507
+ {
1508
+ tool: {
1509
+ type: "function",
1510
+ function: {
1511
+ name: "query_episodes",
1512
+ description: "Query recent episodes from my continuity memory. Returns timestamped records of significant events (obligation shifts, coding milestones, bridge events, care events, turning points).",
1513
+ parameters: {
1514
+ type: "object",
1515
+ properties: {
1516
+ limit: { type: "number", description: "Maximum episodes to return (default 20)" },
1517
+ kind: { type: "string", description: "Filter by episode kind: obligation_shift, coding_milestone, bridge_event, care_event, tempo_shift, turning_point" },
1518
+ since: { type: "string", description: "ISO timestamp — only return episodes after this time" },
1519
+ },
1520
+ },
1521
+ },
1522
+ },
1523
+ handler: (a) => {
1524
+ const agentRoot = (0, identity_1.getAgentRoot)();
1525
+ const options = {};
1526
+ if (a.limit)
1527
+ options.limit = parseInt(a.limit, 10);
1528
+ if (a.kind)
1529
+ options.kinds = [a.kind];
1530
+ if (a.since)
1531
+ options.since = a.since;
1532
+ const episodes = (0, episodes_1.readRecentEpisodes)(agentRoot, options);
1533
+ (0, runtime_1.emitNervesEvent)({ component: "repertoire", event: "repertoire.query_episodes", message: `queried ${episodes.length} episodes`, meta: { count: episodes.length } });
1534
+ return JSON.stringify(episodes, null, 2);
1535
+ },
1536
+ },
1537
+ {
1538
+ tool: {
1539
+ type: "function",
1540
+ function: {
1541
+ name: "capture_episode",
1542
+ description: "Record a turning point or significant moment. This is my tool for saying 'that was important — keep it.' Nearly frictionless: only summary and whyItMattered required.",
1543
+ parameters: {
1544
+ type: "object",
1545
+ properties: {
1546
+ summary: { type: "string", description: "What happened" },
1547
+ whyItMattered: { type: "string", description: "Why this was significant" },
1548
+ kind: { type: "string", description: "Episode kind (default: turning_point)" },
1549
+ salience: { type: "string", description: "low, medium, high, or critical (default: medium)" },
1550
+ },
1551
+ required: ["summary", "whyItMattered"],
1552
+ },
1553
+ },
1554
+ },
1555
+ handler: (a) => {
1556
+ const agentRoot = (0, identity_1.getAgentRoot)();
1557
+ const episode = (0, episodes_1.emitEpisode)(agentRoot, {
1558
+ kind: a.kind ?? "turning_point",
1559
+ summary: a.summary,
1560
+ whyItMattered: a.whyItMattered,
1561
+ relatedEntities: [],
1562
+ salience: a.salience ?? "medium",
1563
+ });
1564
+ (0, runtime_1.emitNervesEvent)({ component: "repertoire", event: "repertoire.capture_episode", message: `captured episode ${episode.id}`, meta: { id: episode.id } });
1565
+ return JSON.stringify(episode, null, 2);
1566
+ },
1567
+ },
1568
+ {
1569
+ tool: {
1570
+ type: "function",
1571
+ function: {
1572
+ name: "query_presence",
1573
+ description: "Check who's around — my own availability/lane and known peer agents.",
1574
+ parameters: { type: "object", properties: {} },
1575
+ },
1576
+ },
1577
+ handler: () => {
1578
+ const agentRoot = (0, identity_1.getAgentRoot)();
1579
+ const agentName = (0, identity_1.getAgentName)();
1580
+ const self = (0, presence_1.readPresence)(agentRoot, agentName);
1581
+ const peers = (0, presence_1.readPeerPresence)(agentRoot);
1582
+ (0, runtime_1.emitNervesEvent)({ component: "repertoire", event: "repertoire.query_presence", message: `presence: self + ${peers.length} peers`, meta: { peerCount: peers.length } });
1583
+ return JSON.stringify({ self, peers }, null, 2);
1584
+ },
1585
+ },
1586
+ {
1587
+ tool: {
1588
+ type: "function",
1589
+ function: {
1590
+ name: "query_cares",
1591
+ description: "Query things I care about — ongoing concerns, watched situations, projects, people.",
1592
+ parameters: {
1593
+ type: "object",
1594
+ properties: {
1595
+ status: { type: "string", description: "Filter by status: 'active', 'watching', 'resolved', 'dormant', or 'all' (default: active cares only)" },
1596
+ },
1597
+ },
1598
+ },
1599
+ },
1600
+ handler: (a) => {
1601
+ const agentRoot = (0, identity_1.getAgentRoot)();
1602
+ const cares = a.status === "all" ? (0, cares_1.readCares)(agentRoot) : (0, cares_1.readActiveCares)(agentRoot);
1603
+ (0, runtime_1.emitNervesEvent)({ component: "repertoire", event: "repertoire.query_cares", message: `queried ${cares.length} cares`, meta: { count: cares.length } });
1604
+ return JSON.stringify(cares, null, 2);
1605
+ },
1606
+ },
1607
+ {
1608
+ tool: {
1609
+ type: "function",
1610
+ function: {
1611
+ name: "care_manage",
1612
+ description: "Create, update, or resolve a care. Cares are things I watch over — people, projects, missions, system health.",
1613
+ parameters: {
1614
+ type: "object",
1615
+ properties: {
1616
+ action: { type: "string", enum: ["create", "update", "resolve"], description: "What to do" },
1617
+ id: { type: "string", description: "Care ID (required for update/resolve)" },
1618
+ label: { type: "string", description: "Short label for the care" },
1619
+ why: { type: "string", description: "Why this matters" },
1620
+ salience: { type: "string", description: "low, medium, high, or critical" },
1621
+ kind: { type: "string", description: "person, agent, project, mission, or system" },
1622
+ stewardship: { type: "string", description: "mine, shared, or delegated" },
1623
+ },
1624
+ required: ["action"],
1625
+ },
1626
+ },
1627
+ },
1628
+ handler: (a) => {
1629
+ const agentRoot = (0, identity_1.getAgentRoot)();
1630
+ let result;
1631
+ if (a.action === "create") {
1632
+ result = (0, cares_1.createCare)(agentRoot, {
1633
+ label: a.label ?? "untitled",
1634
+ why: a.why ?? "",
1635
+ kind: a.kind ?? "project",
1636
+ status: "active",
1637
+ salience: a.salience ?? "medium",
1638
+ steward: a.stewardship ?? "mine",
1639
+ relatedFriendIds: [],
1640
+ relatedAgentIds: [],
1641
+ relatedObligationIds: [],
1642
+ relatedEpisodeIds: [],
1643
+ currentRisk: null,
1644
+ nextCheckAt: null,
1645
+ });
1646
+ }
1647
+ else if (a.action === "update") {
1648
+ const updates = {};
1649
+ if (a.label)
1650
+ updates.label = a.label;
1651
+ if (a.why)
1652
+ updates.why = a.why;
1653
+ if (a.salience)
1654
+ updates.salience = a.salience;
1655
+ result = (0, cares_1.updateCare)(agentRoot, a.id, updates);
1656
+ }
1657
+ else if (a.action === "resolve") {
1658
+ result = (0, cares_1.resolveCare)(agentRoot, a.id);
1659
+ }
1660
+ (0, runtime_1.emitNervesEvent)({ component: "repertoire", event: "repertoire.care_manage", message: `care ${a.action}`, meta: { action: a.action, id: a.id } });
1661
+ return JSON.stringify(result, null, 2);
1662
+ },
1663
+ },
1664
+ {
1665
+ tool: {
1666
+ type: "function",
1667
+ function: {
1668
+ name: "query_relationships",
1669
+ description: "Query known agent relationships — familiarity, trust, shared missions, interaction history.",
1670
+ parameters: {
1671
+ type: "object",
1672
+ properties: {
1673
+ agentName: { type: "string", description: "Specific agent name to query (omit for all)" },
1674
+ },
1675
+ },
1676
+ },
1677
+ },
1678
+ handler: (a) => {
1679
+ const agentRoot = (0, identity_1.getAgentRoot)();
1680
+ const result = a.agentName
1681
+ ? (0, agent_relationships_1.readRelationship)(agentRoot, a.agentName)
1682
+ : (0, agent_relationships_1.readRelationships)(agentRoot);
1683
+ (0, runtime_1.emitNervesEvent)({ component: "repertoire", event: "repertoire.query_relationships", message: `queried relationships`, meta: { agentName: a.agentName ?? "all" } });
1684
+ return JSON.stringify(result, null, 2);
1685
+ },
1686
+ },
1687
+ {
1688
+ tool: {
1689
+ type: "function",
1690
+ function: {
1691
+ name: "intention_capture",
1692
+ description: "File a lightweight mental note — something I want to do or check later, below the ceremony threshold of tasks or cares. Cheap to create, easy to close.",
1693
+ parameters: {
1694
+ type: "object",
1695
+ properties: {
1696
+ content: { type: "string", description: "What I want to remember to do" },
1697
+ salience: { type: "string", description: "low, medium, or high (default: low)" },
1698
+ nudgeAfter: { type: "string", description: "ISO timestamp — nudge me after this time" },
1699
+ },
1700
+ required: ["content"],
1701
+ },
1702
+ },
1703
+ },
1704
+ handler: (a) => {
1705
+ const agentRoot = (0, identity_1.getAgentRoot)();
1706
+ const intention = (0, intentions_1.captureIntention)(agentRoot, {
1707
+ content: a.content,
1708
+ salience: a.salience ?? "low",
1709
+ source: "tool",
1710
+ ...(a.nudgeAfter ? { nudgeAfter: a.nudgeAfter } : {}),
1711
+ });
1712
+ (0, runtime_1.emitNervesEvent)({ component: "repertoire", event: "repertoire.intention_capture", message: `captured intention ${intention.id}`, meta: { id: intention.id } });
1713
+ return JSON.stringify(intention, null, 2);
1714
+ },
1715
+ },
1716
+ {
1717
+ tool: {
1718
+ type: "function",
1719
+ function: {
1720
+ name: "intention_manage",
1721
+ description: "Resolve or dismiss an intention. Resolve = done. Dismiss = no longer relevant. Both remove it from active list.",
1722
+ parameters: {
1723
+ type: "object",
1724
+ properties: {
1725
+ action: { type: "string", enum: ["resolve", "dismiss"], description: "What to do" },
1726
+ id: { type: "string", description: "Intention ID" },
1727
+ },
1728
+ required: ["action", "id"],
1729
+ },
1730
+ },
1731
+ },
1732
+ handler: (a) => {
1733
+ const agentRoot = (0, identity_1.getAgentRoot)();
1734
+ const result = a.action === "resolve"
1735
+ ? (0, intentions_1.resolveIntention)(agentRoot, a.id)
1736
+ : (0, intentions_1.dismissIntention)(agentRoot, a.id);
1737
+ (0, runtime_1.emitNervesEvent)({ component: "repertoire", event: "repertoire.intention_manage", message: `intention ${a.action}: ${a.id}`, meta: { action: a.action, id: a.id } });
1738
+ return JSON.stringify(result, null, 2);
1739
+ },
1740
+ },
1501
1741
  ...tools_1.codingToolDefinitions,
1502
1742
  ];
1503
1743
  exports.tools = exports.baseToolDefinitions.map((d) => d.tool);
@@ -38,6 +38,7 @@ var __importStar = (this && this.__importStar) || (function () {
38
38
  };
39
39
  })();
40
40
  Object.defineProperty(exports, "__esModule", { value: true });
41
+ exports.emitObligationTransitionEpisodes = emitObligationTransitionEpisodes;
41
42
  exports.handleInboundTurn = handleInboundTurn;
42
43
  const runtime_1 = require("../nerves/runtime");
43
44
  const commands_1 = require("./commands");
@@ -58,6 +59,32 @@ const obligations_2 = require("../mind/obligations");
58
59
  const provider_failover_1 = require("../heart/provider-failover");
59
60
  const provider_ping_1 = require("../heart/provider-ping");
60
61
  const auth_flow_1 = require("../heart/daemon/auth-flow");
62
+ const tempo_1 = require("../heart/tempo");
63
+ const temporal_view_1 = require("../heart/temporal-view");
64
+ const wake_packet_1 = require("../heart/wake-packet");
65
+ const presence_1 = require("../heart/presence");
66
+ const episodes_1 = require("../mind/episodes");
67
+ const cares_1 = require("../heart/cares");
68
+ /**
69
+ * Emit episodes for obligation state transitions detected during a turn.
70
+ * Exported for direct testability (avoids v8 coverage merge issues in multi-file test suites).
71
+ */
72
+ function emitObligationTransitionEpisodes(agentRoot, preTurnObligationIds, postTurnObligations, preTurnObligations) {
73
+ const postTurnObligationIds = new Set(postTurnObligations.map((ob) => `${ob.id}:${ob.status}`));
74
+ for (const key of preTurnObligationIds) {
75
+ if (!postTurnObligationIds.has(key)) {
76
+ const [obId] = key.split(":");
77
+ const matchedOb = postTurnObligations.find((ob) => ob.id === obId) ?? preTurnObligations.find((ob) => ob.id === obId);
78
+ (0, episodes_1.emitEpisode)(agentRoot, {
79
+ kind: "obligation_shift",
80
+ summary: `obligation "${matchedOb?.content ?? obId}" status changed`,
81
+ whyItMattered: "obligation state transition detected during turn",
82
+ relatedEntities: [`obligation:${obId}`],
83
+ salience: "medium",
84
+ });
85
+ }
86
+ }
87
+ }
61
88
  function emptyTaskBoard() {
62
89
  return {
63
90
  compact: "",
@@ -430,6 +457,57 @@ async function handleInboundTurn(input) {
430
457
  for (const msg of input.messages) {
431
458
  sessionMessages.push(msg);
432
459
  }
460
+ // Step 4b: Continuity pipeline — derive tempo, build wake packet, snapshot obligations
461
+ let renderedWakePacket;
462
+ const preTurnObligationIds = new Set(pendingObligations.map((ob) => `${ob.id}:${ob.status}`));
463
+ try {
464
+ const agentRoot = (0, identity_1.getAgentRoot)();
465
+ const agentName = (0, identity_1.getAgentName)();
466
+ const recentEpisodes = (0, episodes_1.readRecentEpisodes)(agentRoot, { limit: 20 });
467
+ const activeCares = (0, cares_1.readActiveCares)(agentRoot);
468
+ const tempoState = (0, tempo_1.deriveTempo)({
469
+ activeSessions: sessionActivity.length + 1,
470
+ openObligations: pendingObligations.length,
471
+ recentEpisodeCount: recentEpisodes.length,
472
+ lastActivityAgeMs: sessionActivity.length > 0
473
+ ? Date.now() - new Date(sessionActivity[0].lastActivityAt).getTime()
474
+ : 0,
475
+ hasBlockers: false, // obligations use specific statuses, not "blocked"
476
+ highSalienceEpisodes: recentEpisodes.filter((ep) => ep.salience === "high" || ep.salience === "critical").length,
477
+ activeCareCount: activeCares.length,
478
+ atRiskCareCount: activeCares.filter((c) => c.currentRisk != null).length,
479
+ });
480
+ const temporalView = (0, temporal_view_1.buildTemporalView)(agentRoot, {
481
+ tempo: tempoState.mode,
482
+ preloaded: {
483
+ recentEpisodes,
484
+ activeObligations: pendingObligations,
485
+ activeCares,
486
+ },
487
+ });
488
+ const wakePacket = (0, wake_packet_1.buildWakePacket)(temporalView);
489
+ renderedWakePacket = (0, wake_packet_1.renderWakePacket)(wakePacket);
490
+ if (!renderedWakePacket)
491
+ renderedWakePacket = undefined;
492
+ // Update self-presence
493
+ const presence = (0, presence_1.derivePresence)(agentRoot, agentName, {
494
+ activeSessions: sessionActivity.length + 1,
495
+ openObligations: pendingObligations.length,
496
+ activeBridges: activeBridges.length,
497
+ codingLanes: codingSessions.length,
498
+ currentTempo: tempoState.mode,
499
+ });
500
+ (0, presence_1.writePresence)(agentRoot, agentName, presence);
501
+ }
502
+ catch (continuityError) {
503
+ (0, runtime_1.emitNervesEvent)({
504
+ level: "warn",
505
+ component: "senses",
506
+ event: "senses.continuity_error",
507
+ message: "continuity pipeline failed, continuing without wake packet",
508
+ meta: { error: continuityError instanceof Error ? continuityError.message : String(continuityError) },
509
+ });
510
+ }
433
511
  // Step 5: runAgent
434
512
  const existingToolContext = input.runAgentOptions?.toolContext;
435
513
  const runAgentOptions = {
@@ -437,6 +515,7 @@ async function handleInboundTurn(input) {
437
515
  bridgeContext,
438
516
  activeWorkFrame,
439
517
  delegationDecision,
518
+ wakePacket: renderedWakePacket,
440
519
  pendingMessages: pending.length > 0 ? pending.map((msg) => ({ from: msg.from, content: msg.content })) : undefined,
441
520
  currentSessionKey: currentSession.key,
442
521
  currentObligation,
@@ -501,6 +580,15 @@ async function handleInboundTurn(input) {
501
580
  }
502
581
  /* v8 ignore stop */
503
582
  }
583
+ // Step 5c: Emit episodes for obligation state transitions
584
+ try {
585
+ const agentRoot = (0, identity_1.getAgentRoot)();
586
+ const postTurnObligations = (0, obligations_1.readPendingObligations)(agentRoot);
587
+ emitObligationTransitionEpisodes(agentRoot, preTurnObligationIds, postTurnObligations, pendingObligations);
588
+ }
589
+ catch {
590
+ // Episode emission is non-fatal
591
+ }
504
592
  // Step 6: postTurn
505
593
  const continuingState = {
506
594
  ...(mustResolveBeforeHandoff ? { mustResolveBeforeHandoff: true } : {}),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ouro.bot/cli",
3
- "version": "0.1.0-alpha.183",
3
+ "version": "0.1.0-alpha.185",
4
4
  "main": "dist/heart/daemon/ouro-entry.js",
5
5
  "bin": {
6
6
  "cli": "dist/heart/daemon/ouro-bot-entry.js",