@ouro.bot/cli 0.1.0-alpha.655 → 0.1.0-alpha.658
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -13
- package/changelog.json +21 -0
- package/dist/arc/evolution.js +1 -1
- package/dist/arc/flight-recorder.js +369 -0
- package/dist/arc/obligations.js +24 -2
- package/dist/heart/active-work.js +1 -1
- package/dist/heart/config-registry.js +14 -5
- package/dist/heart/daemon/agent-config-check.js +1 -1
- package/dist/heart/daemon/agent-service.js +18 -17
- package/dist/heart/daemon/cli-exec.js +134 -15
- package/dist/heart/daemon/cli-help.js +21 -2
- package/dist/heart/daemon/cli-parse.js +31 -3
- package/dist/heart/daemon/daemon-entry.js +1 -1
- package/dist/heart/daemon/daemon.js +3 -3
- package/dist/heart/daemon/hooks/bundle-meta.js +29 -9
- package/dist/heart/daemon/inner-status.js +4 -15
- package/dist/heart/daemon/sense-manager.js +16 -1
- package/dist/heart/habits/habit-parser.js +64 -1
- package/dist/heart/hatch/hatch-flow.js +17 -9
- package/dist/heart/hatch/specialist-tools.js +15 -11
- package/dist/heart/identity.js +4 -1
- package/dist/heart/kept-notes.js +5 -73
- package/dist/heart/mailbox/readers/runtime-readers.js +21 -49
- package/dist/heart/mcp/mcp-server.js +8 -8
- package/dist/heart/sense-truth.js +2 -0
- package/dist/heart/session-events.js +1 -31
- package/dist/heart/start-of-turn-packet.js +8 -2
- package/dist/heart/tool-description.js +15 -3
- package/dist/heart/turn-context.js +34 -7
- package/dist/heart/work-card.js +386 -0
- package/dist/mailbox-ui/assets/{index-9-AxCxuB.js → index-Cbasiy6y.js} +1 -1
- package/dist/mailbox-ui/index.html +1 -1
- package/dist/mind/bundle-manifest.js +9 -3
- package/dist/mind/context.js +1 -2
- package/dist/mind/desk-section.js +53 -1
- package/dist/mind/diary.js +2 -3
- package/dist/mind/note-search.js +36 -106
- package/dist/mind/prompt.js +45 -102
- package/dist/mind/record-paths.js +312 -0
- package/dist/repertoire/bundle-templates.js +4 -5
- package/dist/repertoire/tools-bundle.js +1 -1
- package/dist/repertoire/tools-evolution.js +4 -4
- package/dist/repertoire/tools-notes.js +42 -62
- package/dist/repertoire/tools-record.js +16 -11
- package/dist/repertoire/tools-session.js +4 -4
- package/dist/repertoire/tools.js +1 -1
- package/dist/senses/habit-turn-message.js +19 -5
- package/dist/senses/inner-dialog-worker.js +58 -9
- package/dist/senses/inner-dialog.js +30 -11
- package/dist/senses/pipeline.js +135 -1
- package/dist/util/frontmatter.js +17 -1
- package/package.json +3 -3
- package/skills/configure-dev-tools.md +1 -1
- package/skills/travel-planning.md +1 -1
- package/dist/mind/journal-index.js +0 -162
|
@@ -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`]: "
|
|
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
|
|
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
|
-
|
|
59
|
+
search_facts: (args) => {
|
|
60
60
|
const q = args.query;
|
|
61
61
|
if (!q)
|
|
62
|
-
return "searching
|
|
63
|
-
return `searching
|
|
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) {
|
|
@@ -141,6 +141,7 @@ function readSenseStatusLines() {
|
|
|
141
141
|
mail: configuredSenses.mail ?? { enabled: false },
|
|
142
142
|
voice: configuredSenses.voice ?? { enabled: false },
|
|
143
143
|
a2a: configuredSenses.a2a ?? { enabled: false },
|
|
144
|
+
workbench: configuredSenses.workbench ?? { enabled: false },
|
|
144
145
|
};
|
|
145
146
|
const payload = (0, config_1.loadConfig)();
|
|
146
147
|
const agentName = (0, identity_1.getAgentName)();
|
|
@@ -185,6 +186,8 @@ function readSenseStatusLines() {
|
|
|
185
186
|
? openAIRealtimeVoiceReady
|
|
186
187
|
: cascadeVoiceReady,
|
|
187
188
|
a2a: true,
|
|
189
|
+
workbench: typeof config.mcpServers?.ouro_workbench?.command === "string"
|
|
190
|
+
&& config.mcpServers.ouro_workbench.command.trim().length > 0,
|
|
188
191
|
};
|
|
189
192
|
const rows = [
|
|
190
193
|
{ label: "CLI", status: "interactive" },
|
|
@@ -208,6 +211,10 @@ function readSenseStatusLines() {
|
|
|
208
211
|
label: "A2A",
|
|
209
212
|
status: senses.a2a.enabled ? "ready" : "disabled",
|
|
210
213
|
},
|
|
214
|
+
{
|
|
215
|
+
label: "Workbench",
|
|
216
|
+
status: !senses.workbench.enabled ? "disabled" : configured.workbench ? "ready" : "needs_config",
|
|
217
|
+
},
|
|
211
218
|
];
|
|
212
219
|
return rows.map((row) => `- ${row.label}: ${row.status}`);
|
|
213
220
|
}
|
|
@@ -222,6 +229,26 @@ function readBundleMetaFile() {
|
|
|
222
229
|
/* v8 ignore stop */
|
|
223
230
|
}
|
|
224
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
|
+
}
|
|
225
252
|
// ── Builder ─────────────────────────────────────────────────────────
|
|
226
253
|
async function buildTurnContext(input) {
|
|
227
254
|
const agentRoot = (0, identity_1.getAgentRoot)();
|
|
@@ -348,13 +375,13 @@ async function buildTurnContext(input) {
|
|
|
348
375
|
daemonHealth = null;
|
|
349
376
|
/* v8 ignore stop */
|
|
350
377
|
}
|
|
351
|
-
let
|
|
378
|
+
let flightRecorderResume;
|
|
352
379
|
try {
|
|
353
|
-
|
|
354
|
-
journalFiles = (0, prompt_1.readJournalFiles)(journalDir);
|
|
380
|
+
flightRecorderResume = (0, flight_recorder_1.readFlightRecorderResume)(agentRoot);
|
|
355
381
|
}
|
|
356
|
-
catch { /* v8 ignore start -- defensive: fallback on read failure @preserve */
|
|
357
|
-
|
|
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}`);
|
|
358
385
|
/* v8 ignore stop */
|
|
359
386
|
}
|
|
360
387
|
(0, runtime_1.emitNervesEvent)({
|
|
@@ -390,6 +417,6 @@ async function buildTurnContext(input) {
|
|
|
390
417
|
senseStatusLines,
|
|
391
418
|
bundleMeta,
|
|
392
419
|
daemonHealth,
|
|
393
|
-
|
|
420
|
+
flightRecorderResume,
|
|
394
421
|
};
|
|
395
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 = (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
|
+
}
|