@ouro.bot/cli 0.1.0-alpha.657 → 0.1.0-alpha.659

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 (60) hide show
  1. package/README.md +13 -13
  2. package/changelog.json +15 -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 +5 -5
  8. package/dist/heart/context-loss-gauntlet.js +354 -0
  9. package/dist/heart/daemon/agent-config-check.js +1 -1
  10. package/dist/heart/daemon/agent-service.js +18 -17
  11. package/dist/heart/daemon/cli-exec.js +40 -12
  12. package/dist/heart/daemon/cli-help.js +21 -0
  13. package/dist/heart/daemon/cli-parse.js +27 -0
  14. package/dist/heart/daemon/daemon-entry.js +1 -1
  15. package/dist/heart/daemon/daemon.js +3 -3
  16. package/dist/heart/daemon/hooks/bundle-meta.js +29 -9
  17. package/dist/heart/daemon/inner-status.js +4 -15
  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/kept-notes.js +5 -73
  22. package/dist/heart/mailbox/mailbox-http-hooks.js +1 -0
  23. package/dist/heart/mailbox/mailbox-http-routes.js +4 -0
  24. package/dist/heart/mailbox/mailbox-read.js +2 -1
  25. package/dist/heart/mailbox/readers/continuity-readers.js +5 -0
  26. package/dist/heart/mailbox/readers/runtime-readers.js +21 -49
  27. package/dist/heart/mcp/mcp-server.js +8 -8
  28. package/dist/heart/session-events.js +1 -31
  29. package/dist/heart/start-of-turn-packet.js +8 -2
  30. package/dist/heart/tool-description.js +15 -3
  31. package/dist/heart/turn-context.js +27 -7
  32. package/dist/heart/work-card.js +386 -0
  33. package/dist/mailbox-ui/assets/index-B-V9vRQ0.js +61 -0
  34. package/dist/mailbox-ui/assets/index-BOZbGbkL.css +1 -0
  35. package/dist/mailbox-ui/index.html +2 -2
  36. package/dist/mind/bundle-manifest.js +9 -3
  37. package/dist/mind/context.js +1 -2
  38. package/dist/mind/desk-section.js +53 -1
  39. package/dist/mind/diary.js +2 -3
  40. package/dist/mind/note-search.js +36 -106
  41. package/dist/mind/prompt.js +37 -102
  42. package/dist/mind/record-paths.js +312 -0
  43. package/dist/repertoire/bundle-templates.js +4 -5
  44. package/dist/repertoire/tools-bundle.js +1 -1
  45. package/dist/repertoire/tools-evolution.js +4 -4
  46. package/dist/repertoire/tools-notes.js +42 -62
  47. package/dist/repertoire/tools-record.js +16 -11
  48. package/dist/repertoire/tools-session.js +4 -4
  49. package/dist/repertoire/tools.js +1 -1
  50. package/dist/senses/habit-turn-message.js +19 -5
  51. package/dist/senses/inner-dialog-worker.js +58 -9
  52. package/dist/senses/inner-dialog.js +30 -11
  53. package/dist/senses/pipeline.js +135 -1
  54. package/dist/util/frontmatter.js +17 -1
  55. package/package.json +3 -3
  56. package/skills/configure-dev-tools.md +1 -1
  57. package/skills/travel-planning.md +1 -1
  58. package/dist/mailbox-ui/assets/index-9-AxCxuB.js +0 -61
  59. package/dist/mailbox-ui/assets/index-CWzt267f.css +0 -1
  60. package/dist/mind/journal-index.js +0 -162
@@ -123,7 +123,7 @@ const TOOL_TO_COMMAND = {
123
123
  status: "agent.status",
124
124
  catchup: "agent.catchup",
125
125
  get_context: "agent.getContext",
126
- search_notes: "agent.searchNotes",
126
+ search_facts: "agent.searchFacts",
127
127
  get_task: "agent.getTask",
128
128
  };
129
129
  const UUID_PREFIX_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-/i;
@@ -326,7 +326,7 @@ function createMcpServer(options) {
326
326
  status: "handleAgentStatus",
327
327
  catchup: "handleAgentCatchup",
328
328
  get_context: "handleAgentGetContext",
329
- search_notes: "handleAgentSearchNotes",
329
+ search_facts: "handleAgentSearchFacts",
330
330
  get_task: "handleAgentGetTask",
331
331
  };
332
332
  function stringArg(args, key) {
@@ -537,7 +537,7 @@ function getToolSchemas() {
537
537
  return [
538
538
  {
539
539
  name: "ask",
540
- description: "Ask the agent a question through a full conversation turn. This has the same identity, tools, and session continuity as send_message; use search_notes for read-only note lookup.",
540
+ description: "Ask the agent a question through a full conversation turn. This has the same identity, tools, and session continuity as send_message; use search_facts or consult_notes for read-only record lookup.",
541
541
  inputSchema: {
542
542
  type: "object",
543
543
  properties: {
@@ -548,7 +548,7 @@ function getToolSchemas() {
548
548
  },
549
549
  {
550
550
  name: "status",
551
- description: "Get the agent's current status including active sessions, diary and journal state, and activity level.",
551
+ description: "Get the agent's current status including active sessions, Desk record state, and activity level.",
552
552
  inputSchema: {
553
553
  type: "object",
554
554
  properties: {},
@@ -576,19 +576,19 @@ function getToolSchemas() {
576
576
  },
577
577
  {
578
578
  name: "get_context",
579
- description: "Get the agent's current working context including note summary, active tasks, and relevant state.",
579
+ description: "Get the agent's current working context including Desk record summary, active tasks, and relevant state.",
580
580
  inputSchema: {
581
581
  type: "object",
582
582
  properties: {},
583
583
  },
584
584
  },
585
585
  {
586
- name: "search_notes",
587
- description: "Read-only note search. Returns matching diary lines without running an agent turn or treating missing matches as absence of agent belief.",
586
+ name: "search_facts",
587
+ description: "Read-only fact search. Returns matching Desk record diary lines without running an agent turn or treating missing matches as absence of agent belief.",
588
588
  inputSchema: {
589
589
  type: "object",
590
590
  properties: {
591
- query: { type: "string", description: "Search term to look for in agent notes" },
591
+ query: { type: "string", description: "Search term to look for in agent facts" },
592
592
  },
593
593
  required: ["query"],
594
594
  },
@@ -57,12 +57,10 @@ exports.migrateLegacySessionEnvelope = migrateLegacySessionEnvelope;
57
57
  exports.parseSessionEnvelope = parseSessionEnvelope;
58
58
  exports.loadSessionEnvelopeFile = loadSessionEnvelopeFile;
59
59
  exports.buildCanonicalSessionEnvelope = buildCanonicalSessionEnvelope;
60
- exports.appendEvictedToArchive = appendEvictedToArchive;
61
60
  exports.appendSyntheticAssistantEvent = appendSyntheticAssistantEvent;
62
61
  const fs = __importStar(require("fs"));
63
62
  const runtime_1 = require("../nerves/runtime");
64
63
  const structured_output_1 = require("./structured-output");
65
- let archiveDisabledEmitted = false;
66
64
  exports.EVENT_CONTENT_MAX_CHARS = 256 * 1024;
67
65
  function truncateLargeEventContent(content, maxChars) {
68
66
  if (typeof content !== "string") {
@@ -120,7 +118,7 @@ const TOOL_NAME_MIGRATIONS = {
120
118
  go_inward: "ponder",
121
119
  descend: "ponder",
122
120
  [`${LEGACY_WRITTEN_NOTE_PREFIX}_save`]: "diary_write",
123
- [`${LEGACY_WRITTEN_NOTE_PREFIX}_search`]: "search_notes",
121
+ [`${LEGACY_WRITTEN_NOTE_PREFIX}_search`]: "search_facts",
124
122
  };
125
123
  function normalizeUsage(usage) {
126
124
  if (!usage || typeof usage !== "object")
@@ -1115,34 +1113,6 @@ function buildCanonicalSessionEnvelope(options) {
1115
1113
  evictedEvents,
1116
1114
  };
1117
1115
  }
1118
- function agentFromSessionPath(sessPath) {
1119
- const match = sessPath.match(/(?:^|[/\\])AgentBundles[/\\]([^/\\]+)\.ouro(?:[/\\]|$)/);
1120
- return match?.[1] ?? "unknown";
1121
- }
1122
- /**
1123
- * Archive writes are intentionally disabled. The session envelope remains the
1124
- * bounded working-memory record; evicted events are no longer persisted to an
1125
- * unbounded sidecar.
1126
- */
1127
- function appendEvictedToArchive(sessPath, evictedEvents) {
1128
- if (evictedEvents.length === 0)
1129
- return;
1130
- if (!archiveDisabledEmitted) {
1131
- archiveDisabledEmitted = true;
1132
- (0, runtime_1.emitNervesEvent)({
1133
- component: "heart",
1134
- event: "heart.session_archive_disabled",
1135
- message: "session archive append disabled",
1136
- meta: {
1137
- type: "session_archive_disabled",
1138
- agent: agentFromSessionPath(sessPath),
1139
- sessionPath: sessPath,
1140
- evictedCount: evictedEvents.length,
1141
- ts: new Date().toISOString(),
1142
- },
1143
- });
1144
- }
1145
- }
1146
1116
  function appendSyntheticAssistantEvent(envelope, content, recordedAt) {
1147
1117
  // Use nextEventSequence(events) instead of `events.length + 1` so any gap
1148
1118
  // (from pruning, archive replay, or self-heal dedup) cannot collide with
@@ -42,6 +42,7 @@ const path = __importStar(require("path"));
42
42
  const runtime_1 = require("../nerves/runtime");
43
43
  const bundle_state_1 = require("./bundle-state");
44
44
  const tempo_1 = require("./tempo");
45
+ const flight_recorder_1 = require("../arc/flight-recorder");
45
46
  function estimateTokens(text) {
46
47
  return Math.ceil(text.length / 4);
47
48
  }
@@ -191,6 +192,7 @@ function buildStartOfTurnPacket(view, opts) {
191
192
  obligations: buildObligationsSection(effectiveObligations),
192
193
  cares: buildCaresSection(view.activeCares),
193
194
  presence: buildPresenceSection(view.peerPresence),
195
+ arcResume: opts?.flightRecorderResume ? (0, flight_recorder_1.formatFlightRecorderResume)(opts.flightRecorderResume) : undefined,
194
196
  resumeHint: buildResumeHint(view, opts?.canonicalObligations ? effectiveObligations : undefined),
195
197
  currentSessionTiming: opts?.currentSessionTiming,
196
198
  tempo,
@@ -231,6 +233,7 @@ function renderStartOfTurnPacket(packet) {
231
233
  { label: "provider", content: packet.providerSelection ?? "", priority: 8 },
232
234
  { label: "bundleState", content: (0, bundle_state_1.renderBundleStateHint)(packet.bundleState ?? []), priority: 7 },
233
235
  { label: "syncFailure", content: packet.syncFailure ?? "", priority: 7 },
236
+ { label: "arc", content: packet.arcResume ?? "", priority: 7 },
234
237
  { label: "resume", content: packet.resumeHint, priority: 6 },
235
238
  { label: "sessionTiming", content: packet.currentSessionTiming ?? "", priority: 5 },
236
239
  { label: "obligations", content: packet.obligations, priority: 5 },
@@ -256,8 +259,8 @@ function renderStartOfTurnPacket(packet) {
256
259
  for (const section of sortedByPriority) {
257
260
  if (tokens <= budget.max)
258
261
  break;
259
- // Skip resumeHintit's PROTECTED
260
- if (section.label === "resume")
262
+ // Skip continuity sections they are protected.
263
+ if (section.label === "resume" || section.label === "arc")
261
264
  continue;
262
265
  // Remove this section entirely
263
266
  const idx = sections.findIndex((s) => s.label === section.label);
@@ -285,6 +288,9 @@ function formatSections(sections) {
285
288
  case "resume":
286
289
  parts.push(`**Next:** ${section.content}`);
287
290
  break;
291
+ case "arc":
292
+ parts.push(`**Arc:**\n${section.content}`);
293
+ break;
288
294
  case "obligations":
289
295
  parts.push(`**Owed:**\n${section.content}`);
290
296
  break;
@@ -56,11 +56,23 @@ const TOOL_DESCRIPTIONS = {
56
56
  return `searching code for '${truncate(p, 40)}'...`;
57
57
  },
58
58
  // Notes and knowledge
59
- search_notes: (args) => {
59
+ search_facts: (args) => {
60
60
  const q = args.query;
61
61
  if (!q)
62
- return "searching notes...";
63
- return `searching notes for '${truncate(q, 40)}'...`;
62
+ return "searching facts...";
63
+ return `searching facts for '${truncate(q, 40)}'...`;
64
+ },
65
+ consult_diary: (args) => {
66
+ const q = args.query;
67
+ if (!q)
68
+ return "checking recent diary facts...";
69
+ return `consulting diary facts for '${truncate(q, 40)}'...`;
70
+ },
71
+ consult_notes: (args) => {
72
+ const q = args.query;
73
+ if (!q)
74
+ return "checking Desk record notes...";
75
+ return `consulting Desk record notes for '${truncate(q, 40)}'...`;
64
76
  },
65
77
  diary_write: (args) => {
66
78
  const about = args.about;
@@ -58,9 +58,9 @@ const cares_1 = require("../arc/cares");
58
58
  const config_1 = require("./config");
59
59
  const runtime_credentials_1 = require("./runtime-credentials");
60
60
  const daemon_health_1 = require("./daemon/daemon-health");
61
- const prompt_1 = require("../mind/prompt");
62
61
  const provider_visibility_1 = require("./provider-visibility");
63
62
  const mail_import_discovery_1 = require("./mail-import-discovery");
63
+ const flight_recorder_1 = require("../arc/flight-recorder");
64
64
  // ── Helpers ─────────────────────────────────────────────────────────
65
65
  const DAEMON_SOCKET_PATH = "/tmp/ouroboros-daemon.sock";
66
66
  function isLiveCodingSessionStatus(status) {
@@ -229,6 +229,26 @@ function readBundleMetaFile() {
229
229
  /* v8 ignore stop */
230
230
  }
231
231
  }
232
+ function degradedFlightRecorderResume(issue) {
233
+ return {
234
+ schemaVersion: 1,
235
+ hasCompleteState: false,
236
+ canContinue: false,
237
+ missing: ["currentAsk", "nextSafeAction"],
238
+ gaps: [],
239
+ currentAsk: { value: null, confidence: "unknown", sourceEventIds: [] },
240
+ nextSafeAction: { value: null, stopBefore: [], sourceEventIds: [] },
241
+ blockedBecause: [],
242
+ activeObligationIds: [],
243
+ activeReturnObligationIds: [],
244
+ activePacketIds: [],
245
+ openEvolutionCaseIds: [],
246
+ recentClaimIds: [],
247
+ unverifiedClaimIds: [],
248
+ lastSafeCheckpoint: { turnId: null, sessionRef: null, recordedAt: null, sourceEventIds: [] },
249
+ recorderHealth: { status: "degraded", issues: [issue] },
250
+ };
251
+ }
232
252
  // ── Builder ─────────────────────────────────────────────────────────
233
253
  async function buildTurnContext(input) {
234
254
  const agentRoot = (0, identity_1.getAgentRoot)();
@@ -355,13 +375,13 @@ async function buildTurnContext(input) {
355
375
  daemonHealth = null;
356
376
  /* v8 ignore stop */
357
377
  }
358
- let journalFiles = [];
378
+ let flightRecorderResume;
359
379
  try {
360
- const journalDir = path.join(agentRoot, "journal");
361
- journalFiles = (0, prompt_1.readJournalFiles)(journalDir);
380
+ flightRecorderResume = (0, flight_recorder_1.readFlightRecorderResume)(agentRoot);
362
381
  }
363
- catch { /* v8 ignore start -- defensive: fallback on read failure @preserve */
364
- journalFiles = [];
382
+ catch (error) { /* v8 ignore start -- defensive: fallback on read failure @preserve */
383
+ const reason = error instanceof Error ? error.message : String(error);
384
+ flightRecorderResume = degradedFlightRecorderResume(`flight recorder read failed: ${reason}`);
365
385
  /* v8 ignore stop */
366
386
  }
367
387
  (0, runtime_1.emitNervesEvent)({
@@ -397,6 +417,6 @@ async function buildTurnContext(input) {
397
417
  senseStatusLines,
398
418
  bundleMeta,
399
419
  daemonHealth,
400
- journalFiles,
420
+ flightRecorderResume,
401
421
  };
402
422
  }
@@ -0,0 +1,386 @@
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
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.validateWorkCardAgentName = validateWorkCardAgentName;
37
+ exports.buildWorkCard = buildWorkCard;
38
+ exports.formatWorkCardText = formatWorkCardText;
39
+ const fs = __importStar(require("fs"));
40
+ const path = __importStar(require("path"));
41
+ const runtime_1 = require("../nerves/runtime");
42
+ const provider_visibility_1 = require("./provider-visibility");
43
+ const evolution_1 = require("../arc/evolution");
44
+ const obligations_1 = require("../arc/obligations");
45
+ const packets_1 = require("../arc/packets");
46
+ const flight_recorder_1 = require("../arc/flight-recorder");
47
+ const ACTIVE_PACKET_STATUSES = new Set([
48
+ "drafting",
49
+ "processing",
50
+ "validating",
51
+ "collaborating",
52
+ "paused",
53
+ "blocked",
54
+ ]);
55
+ function isoFromMs(ms) {
56
+ return new Date(ms).toISOString();
57
+ }
58
+ function obligationLocator(id) {
59
+ return `arc/obligations/${id}.json`;
60
+ }
61
+ function returnObligationLocator(id) {
62
+ return `arc/obligations/inner/${id}.json`;
63
+ }
64
+ function packetLocator(id) {
65
+ return `arc/packets/${id}.json`;
66
+ }
67
+ function evolutionLocator(id) {
68
+ return `arc/evolution/cases/${id}.json`;
69
+ }
70
+ function flightRecorderLocator() {
71
+ return "arc/flight-recorder/latest.json";
72
+ }
73
+ function validateWorkCardAgentName(agentName) {
74
+ const trimmed = agentName.trim();
75
+ if (!/^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(trimmed)) {
76
+ throw new Error("work card requires a safe agent name (letters, numbers, dot, underscore, hyphen; no path separators)");
77
+ }
78
+ return trimmed;
79
+ }
80
+ function source(kind, locator, freshness = "current", redaction = "summary") {
81
+ return { kind, locator, freshness, redaction };
82
+ }
83
+ function issue(code, sourceRef, detail, severity = "degraded") {
84
+ return { code, severity, source: sourceRef, detail };
85
+ }
86
+ function readJsonDiagnostic(filePath) {
87
+ try {
88
+ return { ok: true, parsed: JSON.parse(fs.readFileSync(filePath, "utf-8")) };
89
+ }
90
+ catch (error) {
91
+ return {
92
+ ok: false,
93
+ detail: error instanceof Error ? error.message : /* v8 ignore next -- defensive: non-Error catch branch @preserve */ String(error),
94
+ };
95
+ }
96
+ }
97
+ function scanJsonDir(dir, sourceKind, locatorForFile, isValid) {
98
+ let entries;
99
+ try {
100
+ entries = fs.readdirSync(dir);
101
+ }
102
+ catch {
103
+ return [];
104
+ }
105
+ return entries
106
+ .filter((entry) => entry.endsWith(".json"))
107
+ .flatMap((entry) => {
108
+ const filePath = path.join(dir, entry);
109
+ const sourceRef = source(sourceKind, locatorForFile(path.basename(entry, ".json")), "unknown");
110
+ const read = readJsonDiagnostic(filePath);
111
+ if (!read.ok) {
112
+ return [issue("arc_json_unreadable", sourceRef, `${sourceRef.locator} could not be parsed: ${read.detail}`)];
113
+ }
114
+ if (!isValid(read.parsed)) {
115
+ return [issue("arc_json_invalid_shape", sourceRef, `${sourceRef.locator} does not match the expected Work Card source shape`)];
116
+ }
117
+ return [];
118
+ });
119
+ }
120
+ function hasStringRecordFields(value, fields) {
121
+ if (!value || typeof value !== "object" || Array.isArray(value))
122
+ return false;
123
+ const record = value;
124
+ return fields.every((field) => typeof record[field] === "string");
125
+ }
126
+ function scanArcSourceIssues(agentRoot) {
127
+ return [
128
+ ...scanJsonDir(path.join(agentRoot, "arc", "obligations"), "obligation", obligationLocator, (value) => hasStringRecordFields(value, ["id", "content", "status"])),
129
+ ...scanJsonDir(path.join(agentRoot, "arc", "obligations", "inner"), "return_obligation", returnObligationLocator, (value) => hasStringRecordFields(value, ["id", "status", "delegatedContent"])
130
+ && typeof value.createdAt === "number"),
131
+ ...scanJsonDir(path.join(agentRoot, "arc", "packets"), "ponder_packet", packetLocator, (value) => hasStringRecordFields(value, ["id", "kind", "status", "objective"])),
132
+ ...scanJsonDir(path.join(agentRoot, "arc", "evolution", "cases"), "evolution_case", evolutionLocator, (value) => hasStringRecordFields(value, ["id", "title", "status"])),
133
+ ];
134
+ }
135
+ function obligationItem(obligation) {
136
+ const freshness = obligation.meaning?.stalenessClass === "at-risk" ? "stale_risky" : "current";
137
+ return {
138
+ id: obligation.id,
139
+ title: obligation.content,
140
+ status: obligation.status,
141
+ source: source("obligation", obligationLocator(obligation.id), freshness),
142
+ ...(obligation.latestNote ? { summary: obligation.latestNote } : {}),
143
+ ...(obligation.nextAction ? { nextAction: obligation.nextAction } : {}),
144
+ updatedAt: obligation.updatedAt ?? obligation.createdAt,
145
+ };
146
+ }
147
+ function returnObligationItem(obligation) {
148
+ return {
149
+ id: obligation.id,
150
+ title: obligation.delegatedContent,
151
+ status: obligation.status,
152
+ source: source("return_obligation", returnObligationLocator(obligation.id)),
153
+ ...(obligation.packetId ? { summary: `packet: ${obligation.packetId}` } : {}),
154
+ nextAction: obligation.status === "queued" ? "start private work and preserve the return route" : "finish and surface the delegated result",
155
+ updatedAt: isoFromMs(obligation.startedAt ?? obligation.createdAt),
156
+ };
157
+ }
158
+ function packetItem(packet) {
159
+ return {
160
+ id: packet.id,
161
+ title: packet.objective,
162
+ status: packet.status,
163
+ source: source("ponder_packet", packetLocator(packet.id)),
164
+ summary: packet.summary,
165
+ nextAction: packet.status === "blocked" ? "resolve blocker or mark waiting" : "advance packet toward validation and return",
166
+ updatedAt: isoFromMs(packet.updatedAt),
167
+ };
168
+ }
169
+ function evolutionItem(item) {
170
+ return {
171
+ id: item.id,
172
+ title: item.title,
173
+ status: item.status,
174
+ source: source("evolution_case", evolutionLocator(item.id)),
175
+ summary: item.problemStatement,
176
+ nextAction: (0, evolution_1.nextEvolutionActionForStatus)(item.status),
177
+ updatedAt: item.updatedAt,
178
+ };
179
+ }
180
+ function waitingOnHuman(items) {
181
+ return items.filter((item) => {
182
+ const lowerStatus = item.status.toLowerCase();
183
+ return lowerStatus.includes("waiting")
184
+ || lowerStatus.includes("blocked");
185
+ });
186
+ }
187
+ function chooseNextAction(input) {
188
+ if (input.waiting.length > 0) {
189
+ return {
190
+ actor: "human",
191
+ summary: input.waiting[0].nextAction ?? input.waiting[0].title,
192
+ source: input.waiting[0].source,
193
+ };
194
+ }
195
+ if (input.flightRecorderResume.blockedBecause.length > 0) {
196
+ return {
197
+ actor: "unknown",
198
+ summary: `flight recorder blocked: ${input.flightRecorderResume.blockedBecause.join("; ")}`,
199
+ source: source("flight_recorder", flightRecorderLocator(), input.flightRecorderResume.recorderHealth.status === "ok" ? "current" : "unknown"),
200
+ };
201
+ }
202
+ if (input.flightRecorderResume.recorderHealth.status === "ok"
203
+ && input.flightRecorderResume.canContinue
204
+ && input.flightRecorderResume.nextSafeAction.value) {
205
+ return {
206
+ actor: "agent",
207
+ summary: input.flightRecorderResume.nextSafeAction.value,
208
+ source: source("flight_recorder", flightRecorderLocator()),
209
+ };
210
+ }
211
+ if (input.returnObligations[0]) {
212
+ return {
213
+ actor: "agent",
214
+ summary: input.returnObligations[0].nextAction,
215
+ source: input.returnObligations[0].source,
216
+ };
217
+ }
218
+ if (input.owed[0]) {
219
+ return {
220
+ actor: "agent",
221
+ summary: input.owed[0].nextAction ?? input.owed[0].title,
222
+ source: input.owed[0].source,
223
+ };
224
+ }
225
+ if (input.activeWork[0]) {
226
+ return {
227
+ actor: "agent",
228
+ summary: input.activeWork[0].nextAction,
229
+ source: input.activeWork[0].source,
230
+ };
231
+ }
232
+ return {
233
+ actor: "unknown",
234
+ summary: "no open work found in arc; verify against current session context before claiming clear state",
235
+ };
236
+ }
237
+ function buildWorkCard(agentName, agentRoot, options = {}) {
238
+ const safeAgentName = validateWorkCardAgentName(agentName);
239
+ const generatedAt = (options.now ?? (() => new Date()))().toISOString();
240
+ const sourceIssues = scanArcSourceIssues(agentRoot);
241
+ const owed = (0, obligations_1.readPendingObligations)(agentRoot).map(obligationItem);
242
+ const returnObligations = (0, obligations_1.listActiveReturnObligationsForRoot)(agentRoot, { now: options.nowMs }).map(returnObligationItem);
243
+ const activePackets = (0, packets_1.listPonderPackets)(agentRoot)
244
+ .filter((packet) => ACTIVE_PACKET_STATUSES.has(packet.status))
245
+ .map(packetItem);
246
+ const evolutionCases = (0, evolution_1.listOpenEvolutionCases)(agentRoot).map(evolutionItem);
247
+ const activeWork = [...activePackets, ...evolutionCases];
248
+ const waiting = waitingOnHuman([...owed, ...returnObligations, ...activeWork]);
249
+ const flightRecorderResume = options.flightRecorderResume ?? (0, flight_recorder_1.readFlightRecorderResume)(agentRoot);
250
+ const claims = {
251
+ available: false,
252
+ unavailableReason: "WorkClaim store is not implemented yet; unverified claim counts are unknown, not zero.",
253
+ counts: {
254
+ unverified: null,
255
+ partial: null,
256
+ failed: null,
257
+ unverifiable: null,
258
+ staleRisky: null,
259
+ verified: null,
260
+ },
261
+ items: [],
262
+ };
263
+ const recorderIssues = flightRecorderResume.recorderHealth.status === "ok"
264
+ ? []
265
+ : flightRecorderResume.recorderHealth.issues.map((recorderIssue) => issue("flight_recorder_degraded", source("flight_recorder", flightRecorderLocator(), "unknown"), recorderIssue, flightRecorderResume.recorderHealth.status === "unavailable" ? "unavailable" : "degraded"));
266
+ const providers = (0, provider_visibility_1.buildAgentProviderVisibility)({ agentName: safeAgentName, agentRoot, homeDir: options.homeDir });
267
+ const nextAction = chooseNextAction({
268
+ owed,
269
+ returnObligations,
270
+ activeWork,
271
+ waiting,
272
+ claims,
273
+ flightRecorderResume,
274
+ });
275
+ const sources = [...owed, ...returnObligations, ...activeWork, ...claims.items].map((item) => item.source);
276
+ sources.push(source("claim_store", "arc/claims", "unknown"));
277
+ sources.push(source("flight_recorder", flightRecorderLocator(), flightRecorderResume.recorderHealth.status === "ok" ? "current" : "unknown"));
278
+ const issues = [...sourceIssues, ...recorderIssues];
279
+ const card = {
280
+ schemaVersion: 1,
281
+ projection: {
282
+ owner: "arc/work-card",
283
+ scope: "durable-arc-work",
284
+ relationToActiveWorkFrame: "complements-live-turn-frame",
285
+ },
286
+ agent: safeAgentName,
287
+ generatedAt,
288
+ degraded: {
289
+ status: issues.length > 0 ? "degraded" : "ok",
290
+ issues,
291
+ },
292
+ currentAsk: {
293
+ available: Boolean(flightRecorderResume.currentAsk.value),
294
+ ...(flightRecorderResume.currentAsk.value ? { value: flightRecorderResume.currentAsk.value } : {}),
295
+ source: flightRecorderResume.currentAsk.value ? "flight_recorder" : "not_tracked_yet",
296
+ confidence: flightRecorderResume.currentAsk.confidence,
297
+ },
298
+ counts: {
299
+ owed: owed.length,
300
+ returnObligations: returnObligations.length,
301
+ activePackets: activePackets.length,
302
+ evolutionCases: evolutionCases.length,
303
+ waitingOnHuman: waiting.length,
304
+ unverifiedClaims: null,
305
+ staleRiskyClaims: null,
306
+ },
307
+ owed,
308
+ returnObligations,
309
+ activeWork,
310
+ waitingOnOthers: waiting,
311
+ claims,
312
+ capabilityHealth: {
313
+ available: true,
314
+ providers,
315
+ },
316
+ nextAction,
317
+ sources,
318
+ };
319
+ (0, runtime_1.emitNervesEvent)({
320
+ component: "engine",
321
+ event: "engine.work_card_compiled",
322
+ message: "work card compiled from arc records",
323
+ meta: {
324
+ agent: agentName,
325
+ owedCount: card.counts.owed,
326
+ returnObligationCount: card.counts.returnObligations,
327
+ activeWorkCount: card.counts.activePackets + card.counts.evolutionCases,
328
+ waitingOnHumanCount: card.counts.waitingOnHuman,
329
+ claimsAvailable: card.claims.available,
330
+ sourceIssueCount: sourceIssues.length,
331
+ },
332
+ });
333
+ return card;
334
+ }
335
+ function formatItems(items, empty) {
336
+ if (items.length === 0)
337
+ return [` ${empty}`];
338
+ return items.map((item) => {
339
+ const suffixes = [
340
+ item.nextAction ? `next: ${item.nextAction}` : null,
341
+ `source: ${item.source.locator}`,
342
+ ].filter(Boolean);
343
+ return ` - [${item.status}] ${item.title} (${suffixes.join("; ")})`;
344
+ });
345
+ }
346
+ function formatWorkCardText(card) {
347
+ return [
348
+ `Work Card — ${card.agent}`,
349
+ `generated: ${card.generatedAt}`,
350
+ `health: ${card.degraded.status} (${card.degraded.issues.length} issue${card.degraded.issues.length === 1 ? "" : "s"})`,
351
+ "",
352
+ `counts: owed=${card.counts.owed} return_obligations=${card.counts.returnObligations} active_packets=${card.counts.activePackets} evolution_cases=${card.counts.evolutionCases} waiting_on_human=${card.counts.waitingOnHuman} unverified_claims=${card.counts.unverifiedClaims ?? "unknown"} stale_risky_claims=${card.counts.staleRiskyClaims ?? "unknown"}`,
353
+ "",
354
+ "Current Ask",
355
+ card.currentAsk.available
356
+ ? ` ${card.currentAsk.value} (source: ${card.currentAsk.source}, confidence: ${card.currentAsk.confidence})`
357
+ : ` unavailable (${card.currentAsk.source})`,
358
+ "",
359
+ "Owed",
360
+ ...formatItems(card.owed, "none found in arc/obligations"),
361
+ "",
362
+ "Return Obligations",
363
+ ...formatItems(card.returnObligations, "none active"),
364
+ "",
365
+ "Active Work",
366
+ ...formatItems(card.activeWork, "none found in packets or evolution cases"),
367
+ "",
368
+ "Waiting On Others",
369
+ ...formatItems(card.waitingOnOthers, "none detected from durable records"),
370
+ "",
371
+ "Claims",
372
+ ` unavailable: ${card.claims.unavailableReason}`,
373
+ "",
374
+ "Source Issues",
375
+ ...card.degraded.issues.map((item) => ` - [${item.severity}] ${item.code}: ${item.detail} (source: ${item.source.locator})`),
376
+ "",
377
+ "Capability Health",
378
+ card.capabilityHealth.available
379
+ ? ` provider lanes: ${card.capabilityHealth.providers.lanes.map((lane) => `${lane.lane}:${lane.status}/${lane.readiness.status}`).join(", ")}`
380
+ : ` unavailable: ${card.capabilityHealth.unavailableReason}`,
381
+ "",
382
+ "Next Action",
383
+ ` ${card.nextAction.actor}: ${card.nextAction.summary}`,
384
+ card.nextAction.source ? ` source: ${card.nextAction.source.locator}` : "",
385
+ ].filter((line, index, lines) => line !== "" || lines[index - 1] !== "").join("\n").trim();
386
+ }