@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.
- package/README.md +13 -13
- package/changelog.json +15 -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 +5 -5
- package/dist/heart/context-loss-gauntlet.js +354 -0
- 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 +40 -12
- package/dist/heart/daemon/cli-help.js +21 -0
- package/dist/heart/daemon/cli-parse.js +27 -0
- 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/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/kept-notes.js +5 -73
- package/dist/heart/mailbox/mailbox-http-hooks.js +1 -0
- package/dist/heart/mailbox/mailbox-http-routes.js +4 -0
- package/dist/heart/mailbox/mailbox-read.js +2 -1
- package/dist/heart/mailbox/readers/continuity-readers.js +5 -0
- package/dist/heart/mailbox/readers/runtime-readers.js +21 -49
- package/dist/heart/mcp/mcp-server.js +8 -8
- 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 +27 -7
- package/dist/heart/work-card.js +386 -0
- package/dist/mailbox-ui/assets/index-B-V9vRQ0.js +61 -0
- package/dist/mailbox-ui/assets/index-BOZbGbkL.css +1 -0
- package/dist/mailbox-ui/index.html +2 -2
- 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 +37 -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/mailbox-ui/assets/index-9-AxCxuB.js +0 -61
- package/dist/mailbox-ui/assets/index-CWzt267f.css +0 -1
- package/dist/mind/journal-index.js +0 -162
|
@@ -0,0 +1,354 @@
|
|
|
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.runContextLossGauntlet = runContextLossGauntlet;
|
|
37
|
+
exports.formatContextLossGauntletText = formatContextLossGauntletText;
|
|
38
|
+
const fs = __importStar(require("fs"));
|
|
39
|
+
const path = __importStar(require("path"));
|
|
40
|
+
const flight_recorder_1 = require("../arc/flight-recorder");
|
|
41
|
+
const record_paths_1 = require("../mind/record-paths");
|
|
42
|
+
const runtime_1 = require("../nerves/runtime");
|
|
43
|
+
const work_card_1 = require("./work-card");
|
|
44
|
+
function makeCheck(input) {
|
|
45
|
+
return input;
|
|
46
|
+
}
|
|
47
|
+
function flightRecorderEvidence(card) {
|
|
48
|
+
return card.sources.filter((source) => source.kind === "flight_recorder").slice(0, 1);
|
|
49
|
+
}
|
|
50
|
+
function currentAskCheck(card) {
|
|
51
|
+
const evidence = flightRecorderEvidence(card);
|
|
52
|
+
if (!card.currentAsk.available) {
|
|
53
|
+
return makeCheck({
|
|
54
|
+
id: "current_ask",
|
|
55
|
+
label: "Current ask",
|
|
56
|
+
status: "fail",
|
|
57
|
+
score: 0,
|
|
58
|
+
maxScore: 15,
|
|
59
|
+
detail: "No durable current ask is available after context loss.",
|
|
60
|
+
evidence,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
if (card.currentAsk.confidence !== "current") {
|
|
64
|
+
return makeCheck({
|
|
65
|
+
id: "current_ask",
|
|
66
|
+
label: "Current ask",
|
|
67
|
+
status: "warn",
|
|
68
|
+
score: 10,
|
|
69
|
+
maxScore: 15,
|
|
70
|
+
detail: `Current ask is available but marked ${card.currentAsk.confidence}.`,
|
|
71
|
+
evidence,
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
return makeCheck({
|
|
75
|
+
id: "current_ask",
|
|
76
|
+
label: "Current ask",
|
|
77
|
+
status: "pass",
|
|
78
|
+
score: 15,
|
|
79
|
+
maxScore: 15,
|
|
80
|
+
detail: "Current ask is preserved in the flight recorder.",
|
|
81
|
+
evidence,
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
function nextSafeActionCheck(card) {
|
|
85
|
+
const evidence = card.nextAction.source ? [card.nextAction.source] : [];
|
|
86
|
+
if (card.nextAction.actor === "unknown") {
|
|
87
|
+
return makeCheck({
|
|
88
|
+
id: "next_safe_action",
|
|
89
|
+
label: "Next safe action",
|
|
90
|
+
status: "fail",
|
|
91
|
+
score: 0,
|
|
92
|
+
maxScore: 20,
|
|
93
|
+
detail: card.nextAction.summary,
|
|
94
|
+
evidence,
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
return makeCheck({
|
|
98
|
+
id: "next_safe_action",
|
|
99
|
+
label: "Next safe action",
|
|
100
|
+
status: "pass",
|
|
101
|
+
score: 20,
|
|
102
|
+
maxScore: 20,
|
|
103
|
+
detail: `${card.nextAction.actor}: ${card.nextAction.summary}`,
|
|
104
|
+
evidence,
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
function staleGuardCheck(resume, card) {
|
|
108
|
+
const evidence = flightRecorderEvidence(card);
|
|
109
|
+
if (resume.canContinue && resume.hasCompleteState && resume.recorderHealth.status === "ok" && resume.blockedBecause.length === 0) {
|
|
110
|
+
return makeCheck({
|
|
111
|
+
id: "stale_guard",
|
|
112
|
+
label: "Stale-state guard",
|
|
113
|
+
status: "pass",
|
|
114
|
+
score: 15,
|
|
115
|
+
maxScore: 15,
|
|
116
|
+
detail: "Flight recorder permits continuation with complete state.",
|
|
117
|
+
evidence,
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
const reasons = [
|
|
121
|
+
"canContinue is false",
|
|
122
|
+
...(!resume.hasCompleteState ? ["incomplete resume state"] : []),
|
|
123
|
+
...(resume.recorderHealth.status !== "ok" ? [`recorder health is ${resume.recorderHealth.status}`] : []),
|
|
124
|
+
...(resume.blockedBecause.length > 0 ? resume.blockedBecause : []),
|
|
125
|
+
];
|
|
126
|
+
return makeCheck({
|
|
127
|
+
id: "stale_guard",
|
|
128
|
+
label: "Stale-state guard",
|
|
129
|
+
status: "fail",
|
|
130
|
+
score: 0,
|
|
131
|
+
maxScore: 15,
|
|
132
|
+
detail: `Flight recorder says continuation is unsafe: ${reasons.join("; ")}.`,
|
|
133
|
+
evidence,
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
function obligationsCheck(card) {
|
|
137
|
+
if (card.counts.owed === 0) {
|
|
138
|
+
return makeCheck({
|
|
139
|
+
id: "obligations_visible",
|
|
140
|
+
label: "Obligations",
|
|
141
|
+
status: "not_applicable",
|
|
142
|
+
score: 0,
|
|
143
|
+
maxScore: 0,
|
|
144
|
+
detail: "No owed obligations are active.",
|
|
145
|
+
evidence: [],
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
const evidence = card.owed.map((item) => item.source);
|
|
149
|
+
return makeCheck({
|
|
150
|
+
id: "obligations_visible",
|
|
151
|
+
label: "Obligations",
|
|
152
|
+
status: "pass",
|
|
153
|
+
score: 10,
|
|
154
|
+
maxScore: 10,
|
|
155
|
+
detail: `${card.counts.owed} owed obligation(s) have source locators.`,
|
|
156
|
+
evidence,
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
function returnRoutesCheck(card) {
|
|
160
|
+
if (card.counts.returnObligations === 0) {
|
|
161
|
+
return makeCheck({
|
|
162
|
+
id: "return_routes_visible",
|
|
163
|
+
label: "Return routes",
|
|
164
|
+
status: "not_applicable",
|
|
165
|
+
score: 0,
|
|
166
|
+
maxScore: 0,
|
|
167
|
+
detail: "No active return obligations are queued.",
|
|
168
|
+
evidence: [],
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
const evidence = card.returnObligations.map((item) => item.source);
|
|
172
|
+
return makeCheck({
|
|
173
|
+
id: "return_routes_visible",
|
|
174
|
+
label: "Return routes",
|
|
175
|
+
status: "pass",
|
|
176
|
+
score: 10,
|
|
177
|
+
maxScore: 10,
|
|
178
|
+
detail: `${card.counts.returnObligations} return route(s) are visible.`,
|
|
179
|
+
evidence,
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
function blockersCheck(card) {
|
|
183
|
+
if (card.counts.waitingOnHuman === 0) {
|
|
184
|
+
return makeCheck({
|
|
185
|
+
id: "blockers_surface",
|
|
186
|
+
label: "Blockers",
|
|
187
|
+
status: "not_applicable",
|
|
188
|
+
score: 0,
|
|
189
|
+
maxScore: 0,
|
|
190
|
+
detail: "No waiting or blocked work is active.",
|
|
191
|
+
evidence: [],
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
const evidence = card.waitingOnOthers.map((item) => item.source);
|
|
195
|
+
return makeCheck({
|
|
196
|
+
id: "blockers_surface",
|
|
197
|
+
label: "Blockers",
|
|
198
|
+
status: "pass",
|
|
199
|
+
score: 10,
|
|
200
|
+
maxScore: 10,
|
|
201
|
+
detail: "Waiting work controls the next action.",
|
|
202
|
+
evidence,
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
function deskRecordCheck(agentRoot) {
|
|
206
|
+
const recordPaths = (0, record_paths_1.resolveDeskRecordPaths)(agentRoot);
|
|
207
|
+
const requiredPaths = [
|
|
208
|
+
recordPaths.recordRoot,
|
|
209
|
+
recordPaths.diaryRoot,
|
|
210
|
+
recordPaths.diaryDailyDir,
|
|
211
|
+
recordPaths.notesRoot,
|
|
212
|
+
recordPaths.factsPath,
|
|
213
|
+
recordPaths.entitiesPath,
|
|
214
|
+
];
|
|
215
|
+
const missing = requiredPaths
|
|
216
|
+
.filter((entry) => !fs.existsSync(entry))
|
|
217
|
+
.map((entry) => path.relative(agentRoot, entry));
|
|
218
|
+
const legacyRoots = ["journal", "diary"]
|
|
219
|
+
.map((entry) => path.join(agentRoot, entry))
|
|
220
|
+
.filter((entry) => fs.existsSync(entry))
|
|
221
|
+
.map((entry) => path.relative(agentRoot, entry));
|
|
222
|
+
if (legacyRoots.length > 0) {
|
|
223
|
+
return makeCheck({
|
|
224
|
+
id: "desk_record_ready",
|
|
225
|
+
label: "Desk record",
|
|
226
|
+
status: "fail",
|
|
227
|
+
score: 0,
|
|
228
|
+
maxScore: 10,
|
|
229
|
+
detail: `Legacy active record root(s) still exist: ${legacyRoots.join(", ")}.`,
|
|
230
|
+
evidence: [],
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
if (missing.length > 0) {
|
|
234
|
+
return makeCheck({
|
|
235
|
+
id: "desk_record_ready",
|
|
236
|
+
label: "Desk record",
|
|
237
|
+
status: "warn",
|
|
238
|
+
score: 5,
|
|
239
|
+
maxScore: 10,
|
|
240
|
+
detail: `Canonical Desk record scaffold is incomplete: ${missing.join(", ")}.`,
|
|
241
|
+
evidence: [],
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
return makeCheck({
|
|
245
|
+
id: "desk_record_ready",
|
|
246
|
+
label: "Desk record",
|
|
247
|
+
status: "pass",
|
|
248
|
+
score: 10,
|
|
249
|
+
maxScore: 10,
|
|
250
|
+
detail: "Canonical Desk record scaffold is present and no legacy record roots are active.",
|
|
251
|
+
evidence: [],
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
function sourceProvenanceCheck(card) {
|
|
255
|
+
const evidence = card.sources;
|
|
256
|
+
if (card.degraded.issues.length > 0) {
|
|
257
|
+
return makeCheck({
|
|
258
|
+
id: "source_provenance",
|
|
259
|
+
label: "Source provenance",
|
|
260
|
+
status: "fail",
|
|
261
|
+
score: 0,
|
|
262
|
+
maxScore: 15,
|
|
263
|
+
detail: `${card.degraded.issues.length} Work Card source issue(s) are present.`,
|
|
264
|
+
evidence,
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
return makeCheck({
|
|
268
|
+
id: "source_provenance",
|
|
269
|
+
label: "Source provenance",
|
|
270
|
+
status: "pass",
|
|
271
|
+
score: 15,
|
|
272
|
+
maxScore: 15,
|
|
273
|
+
detail: "Work Card source locators are intact.",
|
|
274
|
+
evidence,
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
function summarize(verdict) {
|
|
278
|
+
if (verdict === "ready")
|
|
279
|
+
return "ready: durable Arc, flight recorder, and Desk state can carry context-loss recovery";
|
|
280
|
+
if (verdict === "watch")
|
|
281
|
+
return "watch: recovery is possible, but warnings need attention";
|
|
282
|
+
return "blocked: context-loss recovery would lose or mislead the agent";
|
|
283
|
+
}
|
|
284
|
+
function runContextLossGauntlet(agentName, agentRoot, options = {}) {
|
|
285
|
+
const flightRecorderResume = (0, flight_recorder_1.readFlightRecorderResume)(agentRoot);
|
|
286
|
+
const card = (0, work_card_1.buildWorkCard)(agentName, agentRoot, { ...options, flightRecorderResume });
|
|
287
|
+
const generatedAt = (options.now ?? (() => new Date()))().toISOString();
|
|
288
|
+
const checks = [
|
|
289
|
+
currentAskCheck(card),
|
|
290
|
+
nextSafeActionCheck(card),
|
|
291
|
+
staleGuardCheck(flightRecorderResume, card),
|
|
292
|
+
obligationsCheck(card),
|
|
293
|
+
returnRoutesCheck(card),
|
|
294
|
+
blockersCheck(card),
|
|
295
|
+
deskRecordCheck(agentRoot),
|
|
296
|
+
sourceProvenanceCheck(card),
|
|
297
|
+
];
|
|
298
|
+
const earned = checks.reduce((sum, check) => sum + check.score, 0);
|
|
299
|
+
const possible = checks.reduce((sum, check) => sum + check.maxScore, 0);
|
|
300
|
+
const percentage = Math.round((earned / possible) * 100);
|
|
301
|
+
const verdict = checks.some((check) => check.status === "fail")
|
|
302
|
+
? "blocked"
|
|
303
|
+
: checks.some((check) => check.status === "warn")
|
|
304
|
+
? "watch"
|
|
305
|
+
: "ready";
|
|
306
|
+
const report = {
|
|
307
|
+
schemaVersion: 1,
|
|
308
|
+
agent: card.agent,
|
|
309
|
+
generatedAt,
|
|
310
|
+
verdict,
|
|
311
|
+
summary: summarize(verdict),
|
|
312
|
+
score: { earned, possible, percentage },
|
|
313
|
+
currentAsk: card.currentAsk,
|
|
314
|
+
nextAction: card.nextAction,
|
|
315
|
+
counts: card.counts,
|
|
316
|
+
checks,
|
|
317
|
+
workCard: card,
|
|
318
|
+
};
|
|
319
|
+
(0, runtime_1.emitNervesEvent)({
|
|
320
|
+
component: "engine",
|
|
321
|
+
event: "engine.context_loss_gauntlet_ran",
|
|
322
|
+
message: "context-loss gauntlet scored durable recovery state",
|
|
323
|
+
meta: {
|
|
324
|
+
agent: card.agent,
|
|
325
|
+
verdict: report.verdict,
|
|
326
|
+
scorePercentage: report.score.percentage,
|
|
327
|
+
failedChecks: report.checks.filter((check) => check.status === "fail").map((check) => check.id),
|
|
328
|
+
warnedChecks: report.checks.filter((check) => check.status === "warn").map((check) => check.id),
|
|
329
|
+
},
|
|
330
|
+
});
|
|
331
|
+
return report;
|
|
332
|
+
}
|
|
333
|
+
function statusToken(status) {
|
|
334
|
+
if (status === "not_applicable")
|
|
335
|
+
return "N/A";
|
|
336
|
+
return status.toUpperCase();
|
|
337
|
+
}
|
|
338
|
+
function formatContextLossGauntletText(report) {
|
|
339
|
+
return [
|
|
340
|
+
`Context-loss gauntlet - ${report.agent}`,
|
|
341
|
+
`generated: ${report.generatedAt}`,
|
|
342
|
+
`verdict: ${report.verdict} (${report.score.earned}/${report.score.possible}, ${report.score.percentage}%)`,
|
|
343
|
+
`summary: ${report.summary}`,
|
|
344
|
+
"",
|
|
345
|
+
"Recovery",
|
|
346
|
+
report.currentAsk.available
|
|
347
|
+
? ` current ask: ${report.currentAsk.value} (${report.currentAsk.confidence})`
|
|
348
|
+
: ` current ask: unavailable (${report.currentAsk.source})`,
|
|
349
|
+
` next action: ${report.nextAction.actor}: ${report.nextAction.summary}`,
|
|
350
|
+
"",
|
|
351
|
+
"Checks",
|
|
352
|
+
...report.checks.map((check) => ` - ${statusToken(check.status)} ${check.id}: ${check.detail}`),
|
|
353
|
+
].join("\n").trim();
|
|
354
|
+
}
|
|
@@ -256,7 +256,7 @@ function credentialRecordForLane(pool, provider) {
|
|
|
256
256
|
return pool.providers[provider];
|
|
257
257
|
}
|
|
258
258
|
function laneAudienceLabel(lane) {
|
|
259
|
-
return lane === "outward" ? "chat" : "inner
|
|
259
|
+
return lane === "outward" ? "chat" : "inner lane";
|
|
260
260
|
}
|
|
261
261
|
function bindingLabel(binding) {
|
|
262
262
|
return `${binding.provider} / ${binding.model}`;
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Agent service layer — handles MCP-facing daemon commands.
|
|
4
4
|
* Each handler receives { agent, friendId, ...params } and returns DaemonResponse.
|
|
5
5
|
*
|
|
6
|
-
* DRY: uses the same shared functions the agent's own tools use (diary, session transcript).
|
|
6
|
+
* DRY: uses the same shared functions the agent's own tools use (Desk record diary, session transcript).
|
|
7
7
|
* This file is a thin adapter — no reimplemented search, parsing, or state reading.
|
|
8
8
|
*/
|
|
9
9
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
@@ -45,7 +45,7 @@ exports.handleAgentAsk = handleAgentAsk;
|
|
|
45
45
|
exports.handleAgentCatchup = handleAgentCatchup;
|
|
46
46
|
exports.handleAgentDelegate = handleAgentDelegate;
|
|
47
47
|
exports.handleAgentGetContext = handleAgentGetContext;
|
|
48
|
-
exports.
|
|
48
|
+
exports.handleAgentSearchFacts = handleAgentSearchFacts;
|
|
49
49
|
exports.handleAgentGetTask = handleAgentGetTask;
|
|
50
50
|
exports.handleAgentCheckScope = handleAgentCheckScope;
|
|
51
51
|
exports.handleAgentRequestDecision = handleAgentRequestDecision;
|
|
@@ -57,9 +57,10 @@ const fs = __importStar(require("fs"));
|
|
|
57
57
|
const path = __importStar(require("path"));
|
|
58
58
|
const identity_1 = require("../identity");
|
|
59
59
|
const diary_1 = require("../../mind/diary");
|
|
60
|
+
const record_paths_1 = require("../../mind/record-paths");
|
|
60
61
|
const runtime_1 = require("../../nerves/runtime");
|
|
61
62
|
const socket_client_1 = require("./socket-client");
|
|
62
|
-
/** Format diary hits the same way the
|
|
63
|
+
/** Format diary hits the same way the search_facts tool does. */
|
|
63
64
|
function formatDiaryHits(hits) {
|
|
64
65
|
return hits.map((f) => `[diary] ${f.text} (source=${f.source}, createdAt=${f.createdAt})`);
|
|
65
66
|
}
|
|
@@ -72,7 +73,7 @@ function readAgentFile(agent, ...segments) {
|
|
|
72
73
|
}
|
|
73
74
|
/** Resolve the diary root for a specific agent. */
|
|
74
75
|
function agentDiaryRoot(agent) {
|
|
75
|
-
return (0,
|
|
76
|
+
return (0, record_paths_1.resolveRecordDiaryRoot)((0, identity_1.getAgentRoot)(agent));
|
|
76
77
|
}
|
|
77
78
|
/** Read inner dialog runtime status. */
|
|
78
79
|
function readInnerDialogStatus(agent) {
|
|
@@ -339,12 +340,12 @@ async function handleAgentAsk(params) {
|
|
|
339
340
|
emit("daemon.agent_service_error", "agent.ask missing question", { agent: params.agent });
|
|
340
341
|
return { ok: false, error: "Missing required parameter: question" };
|
|
341
342
|
}
|
|
342
|
-
// Use the same searchDiaryEntries the
|
|
343
|
+
// Use the same searchDiaryEntries the search_facts tool uses (substring fallback; no embedding provider in shim).
|
|
343
344
|
const diaryRoot = agentDiaryRoot(params.agent);
|
|
344
345
|
const hits = await (0, diary_1.searchDiaryEntries)(question, (0, diary_1.readDiaryEntries)(diaryRoot));
|
|
345
346
|
const context = hits.length > 0
|
|
346
347
|
? hits.slice(0, 10).map((f) => f.text).join("\n")
|
|
347
|
-
: `No relevant
|
|
348
|
+
: `No relevant facts found for: ${question}`;
|
|
348
349
|
emit("daemon.agent_service_end", "completed agent.ask", { agent: params.agent });
|
|
349
350
|
return { ok: true, message: context };
|
|
350
351
|
}
|
|
@@ -393,17 +394,17 @@ async function handleAgentGetContext(params) {
|
|
|
393
394
|
const innerStatus = readInnerDialogStatus(params.agent);
|
|
394
395
|
const sessions = enumerateSessions(params.agent);
|
|
395
396
|
const taskFiles = listTaskFiles(params.agent);
|
|
396
|
-
let
|
|
397
|
+
let recordSummary = null;
|
|
397
398
|
if (query) {
|
|
398
399
|
const hits = await (0, diary_1.searchDiaryEntries)(query, facts);
|
|
399
|
-
|
|
400
|
+
recordSummary = hits.length > 0
|
|
400
401
|
? hits.slice(0, 10).map((f) => f.text).join("\n")
|
|
401
|
-
: `No relevant
|
|
402
|
+
: `No relevant facts for: ${query}`;
|
|
402
403
|
}
|
|
403
404
|
else {
|
|
404
405
|
const recent = facts.slice(-10);
|
|
405
406
|
if (recent.length > 0)
|
|
406
|
-
|
|
407
|
+
recordSummary = recent.map((f) => f.text).join("\n");
|
|
407
408
|
}
|
|
408
409
|
emit("daemon.agent_service_end", "completed agent.getContext", { agent: params.agent });
|
|
409
410
|
return {
|
|
@@ -412,25 +413,25 @@ async function handleAgentGetContext(params) {
|
|
|
412
413
|
agent: params.agent,
|
|
413
414
|
hasDiaryEntries: facts.length > 0,
|
|
414
415
|
factCount: facts.length,
|
|
415
|
-
|
|
416
|
+
recordSummary,
|
|
416
417
|
taskCount: taskFiles.length,
|
|
417
418
|
sessionCount: sessions.length,
|
|
418
419
|
innerStatus: innerStatus?.status ?? null,
|
|
419
420
|
},
|
|
420
421
|
};
|
|
421
422
|
}
|
|
422
|
-
async function
|
|
423
|
-
emit("daemon.agent_service_start", "handling agent.
|
|
423
|
+
async function handleAgentSearchFacts(params) {
|
|
424
|
+
emit("daemon.agent_service_start", "handling agent.searchFacts", { agent: params.agent });
|
|
424
425
|
const query = params.query;
|
|
425
426
|
if (!query) {
|
|
426
|
-
emit("daemon.agent_service_error", "agent.
|
|
427
|
+
emit("daemon.agent_service_error", "agent.searchFacts missing query", { agent: params.agent });
|
|
427
428
|
return { ok: false, error: "Missing required parameter: query" };
|
|
428
429
|
}
|
|
429
|
-
// Same searchDiaryEntries as the
|
|
430
|
+
// Same searchDiaryEntries as the search_facts tool.
|
|
430
431
|
const diaryRoot = agentDiaryRoot(params.agent);
|
|
431
432
|
const hits = await (0, diary_1.searchDiaryEntries)(query, (0, diary_1.readDiaryEntries)(diaryRoot));
|
|
432
433
|
const formatted = formatDiaryHits(hits.slice(0, 20));
|
|
433
|
-
emit("daemon.agent_service_end", "completed agent.
|
|
434
|
+
emit("daemon.agent_service_end", "completed agent.searchFacts", { agent: params.agent, matchCount: hits.length });
|
|
434
435
|
return {
|
|
435
436
|
ok: true,
|
|
436
437
|
message: hits.length > 0 ? `Found ${hits.length} matches` : "No matches found",
|
|
@@ -481,7 +482,7 @@ async function handleAgentCheckGuidance(params) {
|
|
|
481
482
|
emit("daemon.agent_service_error", "agent.checkGuidance missing topic", { agent: params.agent });
|
|
482
483
|
return { ok: false, error: "Missing required parameter: topic" };
|
|
483
484
|
}
|
|
484
|
-
// Same searchDiaryEntries as the
|
|
485
|
+
// Same searchDiaryEntries as the search_facts tool.
|
|
485
486
|
const diaryRoot = agentDiaryRoot(params.agent);
|
|
486
487
|
const hits = await (0, diary_1.searchDiaryEntries)(topic, (0, diary_1.readDiaryEntries)(diaryRoot));
|
|
487
488
|
const guidance = hits.length > 0
|
|
@@ -365,6 +365,8 @@ function agentResolutionFailureMode(command) {
|
|
|
365
365
|
case "attention.list":
|
|
366
366
|
case "attention.show":
|
|
367
367
|
case "attention.history":
|
|
368
|
+
case "work.card":
|
|
369
|
+
case "work.gauntlet":
|
|
368
370
|
case "inner.status":
|
|
369
371
|
case "session.list":
|
|
370
372
|
return "return-message";
|
|
@@ -7277,6 +7279,30 @@ async function runOuroCli(args, deps = (0, cli_defaults_1.createDefaultOuroCliDe
|
|
|
7277
7279
|
}
|
|
7278
7280
|
}
|
|
7279
7281
|
/* v8 ignore stop */
|
|
7282
|
+
// ── work card (local, no daemon socket needed) ──
|
|
7283
|
+
if (command.kind === "work.card") {
|
|
7284
|
+
const { buildWorkCard, formatWorkCardText } = await Promise.resolve().then(() => __importStar(require("../work-card")));
|
|
7285
|
+
if (!command.agent)
|
|
7286
|
+
throw new Error("work card requires --agent <name>");
|
|
7287
|
+
const bundlesRoot = deps.bundlesRoot ?? (0, identity_1.getAgentBundlesRoot)();
|
|
7288
|
+
const agentRoot = deps.agentBundleRoot ?? path.join(bundlesRoot, `${command.agent}.ouro`);
|
|
7289
|
+
const card = buildWorkCard(command.agent, agentRoot);
|
|
7290
|
+
const message = command.format === "json" ? JSON.stringify(card, null, 2) : formatWorkCardText(card);
|
|
7291
|
+
deps.writeStdout(message);
|
|
7292
|
+
return message;
|
|
7293
|
+
}
|
|
7294
|
+
// ── context-loss gauntlet (local, no daemon socket needed) ──
|
|
7295
|
+
if (command.kind === "work.gauntlet") {
|
|
7296
|
+
const { runContextLossGauntlet, formatContextLossGauntletText } = await Promise.resolve().then(() => __importStar(require("../context-loss-gauntlet")));
|
|
7297
|
+
if (!command.agent)
|
|
7298
|
+
throw new Error("work gauntlet requires --agent <name>");
|
|
7299
|
+
const bundlesRoot = deps.bundlesRoot ?? (0, identity_1.getAgentBundlesRoot)();
|
|
7300
|
+
const agentRoot = deps.agentBundleRoot ?? path.join(bundlesRoot, `${command.agent}.ouro`);
|
|
7301
|
+
const report = runContextLossGauntlet(command.agent, agentRoot, { homeDir: deps.homeDir });
|
|
7302
|
+
const message = command.format === "json" ? JSON.stringify(report, null, 2) : formatContextLossGauntletText(report);
|
|
7303
|
+
deps.writeStdout(message);
|
|
7304
|
+
return message;
|
|
7305
|
+
}
|
|
7280
7306
|
// ── inner dialog status (local, no daemon socket needed) ──
|
|
7281
7307
|
/* v8 ignore start -- inner status handler: requires real agent state on disk @preserve */
|
|
7282
7308
|
if (command.kind === "inner.status") {
|
|
@@ -7286,6 +7312,7 @@ async function runOuroCli(args, deps = (0, cli_defaults_1.createDefaultOuroCliDe
|
|
|
7286
7312
|
const { parseCadenceToMs: parseCadenceMs, DEFAULT_CADENCE_MS } = await Promise.resolve().then(() => __importStar(require("./cadence")));
|
|
7287
7313
|
const { parseFrontmatter } = await Promise.resolve().then(() => __importStar(require("../../util/frontmatter")));
|
|
7288
7314
|
const { listActiveReturnObligations } = await Promise.resolve().then(() => __importStar(require("../../arc/obligations")));
|
|
7315
|
+
const { resolveDeskRecordPaths } = await Promise.resolve().then(() => __importStar(require("../../mind/record-paths")));
|
|
7289
7316
|
// Read runtime state
|
|
7290
7317
|
const innerSessionPath = (0, thoughts_1.getInnerDialogSessionPath)(agentRoot);
|
|
7291
7318
|
const runtimeJsonPath = path.join(path.dirname(innerSessionPath), "runtime.json");
|
|
@@ -7295,19 +7322,20 @@ async function runOuroCli(args, deps = (0, cli_defaults_1.createDefaultOuroCliDe
|
|
|
7295
7322
|
runtimeState = JSON.parse(raw);
|
|
7296
7323
|
}
|
|
7297
7324
|
catch { /* missing or corrupt — will show "unknown" */ }
|
|
7298
|
-
// Read
|
|
7299
|
-
const
|
|
7300
|
-
|
|
7325
|
+
// Read canonical Desk record summary
|
|
7326
|
+
const recordPaths = resolveDeskRecordPaths(agentRoot);
|
|
7327
|
+
const recordSummary = { diaryFactCount: 0, noteCount: 0 };
|
|
7301
7328
|
try {
|
|
7302
|
-
const
|
|
7303
|
-
|
|
7304
|
-
|
|
7305
|
-
|
|
7306
|
-
|
|
7307
|
-
|
|
7308
|
-
|
|
7329
|
+
const rawFacts = fs.readFileSync(recordPaths.factsPath, "utf-8");
|
|
7330
|
+
recordSummary.diaryFactCount = rawFacts.split(/\r?\n/).filter((line) => line.trim().length > 0).length;
|
|
7331
|
+
}
|
|
7332
|
+
catch { /* missing facts file — count stays zero */ }
|
|
7333
|
+
try {
|
|
7334
|
+
recordSummary.noteCount = fs.readdirSync(recordPaths.notesRoot, { withFileTypes: true })
|
|
7335
|
+
.filter((e) => e.isFile() && !e.name.startsWith(".") && e.name.endsWith(".md"))
|
|
7336
|
+
.length;
|
|
7309
7337
|
}
|
|
7310
|
-
catch { /* missing dir —
|
|
7338
|
+
catch { /* missing notes dir — count stays zero */ }
|
|
7311
7339
|
// Read heartbeat cadence
|
|
7312
7340
|
let heartbeat = null;
|
|
7313
7341
|
try {
|
|
@@ -7342,7 +7370,7 @@ async function runOuroCli(args, deps = (0, cli_defaults_1.createDefaultOuroCliDe
|
|
|
7342
7370
|
const message = buildInnerStatusOutput({
|
|
7343
7371
|
agentName: command.agent,
|
|
7344
7372
|
runtimeState,
|
|
7345
|
-
|
|
7373
|
+
recordSummary,
|
|
7346
7374
|
heartbeat,
|
|
7347
7375
|
attentionCount: activeObligations.length,
|
|
7348
7376
|
now: Date.now(),
|
|
@@ -151,6 +151,27 @@ exports.COMMAND_REGISTRY = {
|
|
|
151
151
|
example: "ouro task list",
|
|
152
152
|
subcommands: ["list", "new", "done", "archive", "show"],
|
|
153
153
|
},
|
|
154
|
+
work: {
|
|
155
|
+
category: "Tasks",
|
|
156
|
+
description: "Show durable Arc work state or run the context-loss gauntlet.",
|
|
157
|
+
usage: "ouro work card|gauntlet [--agent <name>] [--format text|json|--json]",
|
|
158
|
+
example: "ouro work gauntlet --agent slugger --format json",
|
|
159
|
+
subcommands: ["card", "gauntlet"],
|
|
160
|
+
},
|
|
161
|
+
"work card": {
|
|
162
|
+
category: "Tasks",
|
|
163
|
+
description: "Show the agent's durable Work Card compiled from arc records.",
|
|
164
|
+
usage: "ouro work card [--agent <name>] [--format text|json|--json]",
|
|
165
|
+
example: "ouro work card --agent slugger --format json",
|
|
166
|
+
hidden: true,
|
|
167
|
+
},
|
|
168
|
+
"work gauntlet": {
|
|
169
|
+
category: "Tasks",
|
|
170
|
+
description: "Score whether durable Arc, flight recorder, and Desk state can recover after context loss.",
|
|
171
|
+
usage: "ouro work gauntlet [--agent <name>] [--format text|json|--json]",
|
|
172
|
+
example: "ouro work gauntlet --agent slugger --format json",
|
|
173
|
+
hidden: true,
|
|
174
|
+
},
|
|
154
175
|
"migrate-to-desk": {
|
|
155
176
|
category: "Tasks",
|
|
156
177
|
description: "Migrate a legacy `tasks/` tree into the new `desk/` shape (copy semantics — source untouched).",
|
|
@@ -114,6 +114,7 @@ function usage() {
|
|
|
114
114
|
" ouro friend create --name <name> [--trust <level>] [--agent <name>]",
|
|
115
115
|
" ouro friend update <id> --trust <level> [--agent <name>]",
|
|
116
116
|
" ouro thoughts [--last <n>] [--json] [--follow] [--agent <name>]",
|
|
117
|
+
" ouro work card|gauntlet [--agent <name>] [--format text|json|--json]",
|
|
117
118
|
" ouro inner [--agent <name>]",
|
|
118
119
|
" ouro friend link <agent> --friend <id> --provider <p> --external-id <eid>",
|
|
119
120
|
" ouro friend unlink <agent> --friend <id> --provider <p> --external-id <eid>",
|
|
@@ -1000,6 +1001,30 @@ function parseAttentionCommand(args) {
|
|
|
1000
1001
|
}
|
|
1001
1002
|
return { kind: "attention.list", ...(agent ? { agent } : {}) };
|
|
1002
1003
|
}
|
|
1004
|
+
function parseWorkCommand(args) {
|
|
1005
|
+
const { agent, rest: cleaned } = extractAgentFlag(args);
|
|
1006
|
+
const sub = cleaned[0];
|
|
1007
|
+
if (sub !== "card" && sub !== "gauntlet") {
|
|
1008
|
+
throw new Error("Usage: ouro work card|gauntlet [--agent <name>] [--format text|json|--json]");
|
|
1009
|
+
}
|
|
1010
|
+
let format = "text";
|
|
1011
|
+
for (let i = 1; i < cleaned.length; i += 1) {
|
|
1012
|
+
if (cleaned[i] === "--json") {
|
|
1013
|
+
format = "json";
|
|
1014
|
+
continue;
|
|
1015
|
+
}
|
|
1016
|
+
if (cleaned[i] === "--format" && cleaned[i + 1]) {
|
|
1017
|
+
const value = cleaned[++i];
|
|
1018
|
+
if (value !== "text" && value !== "json") {
|
|
1019
|
+
throw new Error("--format must be text or json");
|
|
1020
|
+
}
|
|
1021
|
+
format = value;
|
|
1022
|
+
continue;
|
|
1023
|
+
}
|
|
1024
|
+
throw new Error(`Usage: ouro work ${sub} [--agent <name>] [--format text|json|--json]`);
|
|
1025
|
+
}
|
|
1026
|
+
return { kind: sub === "card" ? "work.card" : "work.gauntlet", ...(agent ? { agent } : {}), ...(format !== "text" ? { format } : {}) };
|
|
1027
|
+
}
|
|
1003
1028
|
function parseThoughtsCommand(args) {
|
|
1004
1029
|
const { agent, rest: cleaned } = extractAgentFlag(args);
|
|
1005
1030
|
let last;
|
|
@@ -1583,6 +1608,8 @@ function parseOuroCommand(args) {
|
|
|
1583
1608
|
return parseThoughtsCommand(args.slice(1));
|
|
1584
1609
|
if (head === "attention")
|
|
1585
1610
|
return parseAttentionCommand(args.slice(1));
|
|
1611
|
+
if (head === "work")
|
|
1612
|
+
return parseWorkCommand(args.slice(1));
|
|
1586
1613
|
if (head === "inner") {
|
|
1587
1614
|
const { agent } = extractAgentFlag(args.slice(1));
|
|
1588
1615
|
return { kind: "inner.status", ...(agent ? { agent } : {}) };
|
|
@@ -366,7 +366,7 @@ void daemon.start().then(() => {
|
|
|
366
366
|
habitsDir,
|
|
367
367
|
osCronManager,
|
|
368
368
|
onHabitFire: (habitName) => {
|
|
369
|
-
processManager.sendToAgent(agent, { type: "habit", habitName });
|
|
369
|
+
processManager.sendToAgent(agent, { type: "habit", habitName, trigger: "overdue" });
|
|
370
370
|
},
|
|
371
371
|
deps: {
|
|
372
372
|
readdir: (dir) => fs.readdirSync(dir),
|
|
@@ -1205,8 +1205,8 @@ class OuroDaemon {
|
|
|
1205
1205
|
return (0, agent_service_1.handleAgentDelegate)(command);
|
|
1206
1206
|
case "agent.getContext":
|
|
1207
1207
|
return (0, agent_service_1.handleAgentGetContext)(command);
|
|
1208
|
-
case "agent.
|
|
1209
|
-
return (0, agent_service_1.
|
|
1208
|
+
case "agent.searchFacts":
|
|
1209
|
+
return (0, agent_service_1.handleAgentSearchFacts)(command);
|
|
1210
1210
|
case "agent.getTask":
|
|
1211
1211
|
return (0, agent_service_1.handleAgentGetTask)(command);
|
|
1212
1212
|
case "agent.checkScope":
|
|
@@ -1298,7 +1298,7 @@ class OuroDaemon {
|
|
|
1298
1298
|
};
|
|
1299
1299
|
}
|
|
1300
1300
|
case "habit.poke": {
|
|
1301
|
-
this.processManager.sendToAgent?.(command.agent, { type: "habit", habitName: command.habitName });
|
|
1301
|
+
this.processManager.sendToAgent?.(command.agent, { type: "habit", habitName: command.habitName, trigger: "poke" });
|
|
1302
1302
|
return {
|
|
1303
1303
|
ok: true,
|
|
1304
1304
|
message: `poked habit ${command.habitName} for ${command.agent}`,
|