@ouro.bot/cli 0.1.0-alpha.665 → 0.1.0-alpha.667
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/changelog.json +13 -0
- package/dist/arc/flight-recorder.js +324 -5
- package/dist/heart/core.js +167 -4
- package/dist/heart/cross-chat-delivery.js +3 -2
- package/dist/heart/daemon/cli-exec.js +139 -1
- package/dist/heart/daemon/cli-help.js +13 -2
- package/dist/heart/daemon/cli-parse.js +138 -2
- package/dist/heart/daemon/daemon-entry.js +24 -5
- package/dist/heart/daemon/daemon.js +10 -1
- package/dist/heart/habits/habit-parser.js +8 -0
- package/dist/heart/habits/habit-runtime-state.js +17 -3
- package/dist/heart/habits/habit-scheduler.js +24 -5
- package/dist/heart/habits/habit-session-summary.js +318 -0
- package/dist/heart/habits/habit-session.js +618 -0
- package/dist/heart/mailbox/mailbox-http-hooks.js +29 -1
- package/dist/heart/mailbox/mailbox-http-routes.js +122 -1
- package/dist/heart/mailbox/mailbox-read.js +5 -1
- package/dist/heart/mailbox/readers/runtime-readers.js +87 -0
- package/dist/mailbox-ui/assets/index-CaTIFDmv.js +1 -0
- package/dist/mailbox-ui/assets/index-Du_9G9WO.css +1 -0
- package/dist/mailbox-ui/assets/vendor-CcN1XpQ9.js +61 -0
- package/dist/mailbox-ui/index.html +3 -2
- package/dist/repertoire/tools-notes.js +50 -0
- package/dist/repertoire/tools-record.js +13 -0
- package/dist/repertoire/tools-session.js +140 -0
- package/dist/repertoire/tools-surface.js +11 -0
- package/dist/repertoire/tools.js +7 -0
- package/dist/senses/habit-turn-message.js +41 -3
- package/dist/senses/inner-dialog-worker.js +264 -68
- package/dist/senses/inner-dialog.js +29 -15
- package/dist/senses/pipeline.js +2 -11
- package/dist/senses/surface-tool.js +2 -1
- package/package.json +1 -1
- package/dist/mailbox-ui/assets/index-BZ60na8O.js +0 -61
- package/dist/mailbox-ui/assets/index-DG6Xf5uL.css +0 -1
|
@@ -108,7 +108,7 @@ class HabitScheduler {
|
|
|
108
108
|
message: "firing overdue habit (never run)",
|
|
109
109
|
meta: { habitName: habit.name, agent: this.agent },
|
|
110
110
|
});
|
|
111
|
-
this.onHabitFire(habit.name);
|
|
111
|
+
this.onHabitFire(habit.name, "overdue");
|
|
112
112
|
continue;
|
|
113
113
|
}
|
|
114
114
|
const lastRunMs = new Date(habit.lastRun).getTime();
|
|
@@ -120,7 +120,7 @@ class HabitScheduler {
|
|
|
120
120
|
message: "firing overdue habit",
|
|
121
121
|
meta: { habitName: habit.name, agent: this.agent, elapsedMs: elapsed },
|
|
122
122
|
});
|
|
123
|
-
this.onHabitFire(habit.name);
|
|
123
|
+
this.onHabitFire(habit.name, "overdue");
|
|
124
124
|
}
|
|
125
125
|
}
|
|
126
126
|
}
|
|
@@ -174,6 +174,25 @@ class HabitScheduler {
|
|
|
174
174
|
return null;
|
|
175
175
|
}
|
|
176
176
|
}
|
|
177
|
+
listJobs() {
|
|
178
|
+
return this.buildJobs(this.scanHabits())
|
|
179
|
+
.map((job) => ({ id: job.id, schedule: job.schedule, lastRun: job.lastRun }))
|
|
180
|
+
.sort((a, b) => a.id.localeCompare(b.id));
|
|
181
|
+
}
|
|
182
|
+
async triggerJob(jobId, trigger = "cron") {
|
|
183
|
+
const job = this.buildJobs(this.scanHabits()).find((candidate) => candidate.id === jobId);
|
|
184
|
+
if (!job) {
|
|
185
|
+
return { ok: false, message: `unknown habit job: ${jobId}` };
|
|
186
|
+
}
|
|
187
|
+
this.onHabitFire(job.taskId, trigger);
|
|
188
|
+
(0, runtime_1.emitNervesEvent)({
|
|
189
|
+
component: "daemon",
|
|
190
|
+
event: "daemon.habit_job_triggered",
|
|
191
|
+
message: "habit scheduler job triggered",
|
|
192
|
+
meta: { agent: job.agent, habitName: job.taskId, jobId, trigger },
|
|
193
|
+
});
|
|
194
|
+
return { ok: true, message: `triggered habit ${jobId}` };
|
|
195
|
+
}
|
|
177
196
|
watchForChanges() {
|
|
178
197
|
const watchFn = this.deps.watch;
|
|
179
198
|
if (!watchFn)
|
|
@@ -278,7 +297,7 @@ class HabitScheduler {
|
|
|
278
297
|
const output = this.execForVerify("crontab -l");
|
|
279
298
|
const lines = output.split("\n");
|
|
280
299
|
for (const line of lines) {
|
|
281
|
-
const match = line.match(/ouro poke \S+ --habit (\S+)
|
|
300
|
+
const match = line.match(/ouro poke \S+ --habit (\S+)(?:\s+--trigger\s+\S+)?/);
|
|
282
301
|
if (match) {
|
|
283
302
|
verified.add(match[1]);
|
|
284
303
|
}
|
|
@@ -293,7 +312,7 @@ class HabitScheduler {
|
|
|
293
312
|
createTimerFallback(habitName, cadenceMs) {
|
|
294
313
|
const schedule = () => {
|
|
295
314
|
const timer = setTimeout(() => {
|
|
296
|
-
this.onHabitFire(habitName);
|
|
315
|
+
this.onHabitFire(habitName, "overdue");
|
|
297
316
|
schedule();
|
|
298
317
|
}, cadenceMs);
|
|
299
318
|
this.timerFallbacks.set(habitName, timer);
|
|
@@ -362,7 +381,7 @@ class HabitScheduler {
|
|
|
362
381
|
taskId: habit.name,
|
|
363
382
|
schedule: cronSchedule,
|
|
364
383
|
lastRun: habit.lastRun,
|
|
365
|
-
command: `${this.deps.ouroPath} poke ${this.agent} --habit ${habit.name}`,
|
|
384
|
+
command: `${this.deps.ouroPath} poke ${this.agent} --habit ${habit.name} --trigger launchd`,
|
|
366
385
|
taskPath: path.join(this.habitsDir, `${habit.name}.md`),
|
|
367
386
|
});
|
|
368
387
|
}
|
|
@@ -0,0 +1,318 @@
|
|
|
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.selectHabitRunReceipt = selectHabitRunReceipt;
|
|
37
|
+
exports.readHabitSessionSummary = readHabitSessionSummary;
|
|
38
|
+
const fs = __importStar(require("fs"));
|
|
39
|
+
const path = __importStar(require("path"));
|
|
40
|
+
const flight_recorder_1 = require("../../arc/flight-recorder");
|
|
41
|
+
const runtime_1 = require("../../nerves/runtime");
|
|
42
|
+
const session_events_1 = require("../session-events");
|
|
43
|
+
const VALID_WHICH = new Set(["latest", "previous", "latest-success", "latest-failure"]);
|
|
44
|
+
const SUCCESS_OUTCOMES = new Set([
|
|
45
|
+
"no_change",
|
|
46
|
+
"wrote_arc",
|
|
47
|
+
"updated_desk",
|
|
48
|
+
"wrote_record",
|
|
49
|
+
"surfaced",
|
|
50
|
+
]);
|
|
51
|
+
const FAILURE_OUTCOMES = new Set(["blocked", "error"]);
|
|
52
|
+
const MAX_SUMMARY_CHARS = 1600;
|
|
53
|
+
const TRUNCATION_SUFFIX = "\n[truncated]";
|
|
54
|
+
function selectorError(code, message) {
|
|
55
|
+
return { ok: false, error: { code, message } };
|
|
56
|
+
}
|
|
57
|
+
function normalizeWhich(value) {
|
|
58
|
+
if (value === undefined)
|
|
59
|
+
return "latest";
|
|
60
|
+
return VALID_WHICH.has(value) ? value : null;
|
|
61
|
+
}
|
|
62
|
+
function sortNewestFirst(receipts) {
|
|
63
|
+
return [...receipts].sort((left, right) => right.endedAt.localeCompare(left.endedAt) || right.runId.localeCompare(left.runId));
|
|
64
|
+
}
|
|
65
|
+
function filterReceipts(receipts, selector) {
|
|
66
|
+
return receipts.filter((receipt) => {
|
|
67
|
+
if (selector.habitName !== undefined && receipt.habitName !== selector.habitName)
|
|
68
|
+
return false;
|
|
69
|
+
if (selector.operationId !== undefined && receipt.operationId !== selector.operationId)
|
|
70
|
+
return false;
|
|
71
|
+
return true;
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
function filterByWhich(receipts, which) {
|
|
75
|
+
if (which === "latest" || which === "previous")
|
|
76
|
+
return receipts;
|
|
77
|
+
const outcomes = which === "latest-success" ? SUCCESS_OUTCOMES : FAILURE_OUTCOMES;
|
|
78
|
+
return receipts.filter((receipt) => outcomes.has(receipt.outcome));
|
|
79
|
+
}
|
|
80
|
+
function selectHabitRunReceipt(receipts, selector) {
|
|
81
|
+
if (selector.runId !== undefined) {
|
|
82
|
+
if (selector.habitName !== undefined || selector.operationId !== undefined || selector.which !== undefined) {
|
|
83
|
+
return selectorError("run_id_exclusive", "runId cannot be combined with habitName, operationId, or which");
|
|
84
|
+
}
|
|
85
|
+
const receipt = receipts.find((entry) => entry.runId === selector.runId);
|
|
86
|
+
return receipt ? { ok: true, receipt } : selectorError("not_found", "no habit run matched selector");
|
|
87
|
+
}
|
|
88
|
+
if (selector.habitName === undefined && selector.operationId === undefined) {
|
|
89
|
+
return selectorError("selector_required", "provide runId, habitName, or operationId");
|
|
90
|
+
}
|
|
91
|
+
const which = normalizeWhich(selector.which);
|
|
92
|
+
if (which === null) {
|
|
93
|
+
return selectorError("invalid_which", "which must be latest, previous, latest-success, or latest-failure");
|
|
94
|
+
}
|
|
95
|
+
const matches = filterByWhich(sortNewestFirst(filterReceipts([...receipts], selector)), which);
|
|
96
|
+
const index = which === "previous" ? 1 : 0;
|
|
97
|
+
const receipt = matches[index];
|
|
98
|
+
return receipt ? { ok: true, receipt } : selectorError("not_found", "no habit run matched selector");
|
|
99
|
+
}
|
|
100
|
+
function isRecord(value) {
|
|
101
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
102
|
+
}
|
|
103
|
+
function stringValue(value) {
|
|
104
|
+
return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
|
|
105
|
+
}
|
|
106
|
+
function summarySnapshot(receipt) {
|
|
107
|
+
const snapshot = receipt.summarySnapshot;
|
|
108
|
+
return {
|
|
109
|
+
summary: snapshot.summary,
|
|
110
|
+
decisions: snapshot.decisions,
|
|
111
|
+
nextLikelyStep: snapshot.nextLikelyStep,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
function relativeSource(receipt, key) {
|
|
115
|
+
if (key === "receipt")
|
|
116
|
+
return receipt.receiptLocator;
|
|
117
|
+
if (key === "session")
|
|
118
|
+
return receipt.sessionLocator;
|
|
119
|
+
if (key === "pending")
|
|
120
|
+
return receipt.pendingLocator;
|
|
121
|
+
return receipt.runtimeStateLocator;
|
|
122
|
+
}
|
|
123
|
+
function safeSourcePath(agentRoot, locator, key, expectedPrefix) {
|
|
124
|
+
const normalizedInput = locator.replace(/\\/g, "/");
|
|
125
|
+
const normalized = path.posix.normalize(normalizedInput);
|
|
126
|
+
const unsafe = path.isAbsolute(locator)
|
|
127
|
+
|| normalizedInput.startsWith("/")
|
|
128
|
+
|| normalized === "."
|
|
129
|
+
|| normalized === ".."
|
|
130
|
+
|| normalized.startsWith("../")
|
|
131
|
+
|| normalized !== normalizedInput
|
|
132
|
+
|| !normalized.startsWith(expectedPrefix);
|
|
133
|
+
if (unsafe) {
|
|
134
|
+
return { ok: false, warning: `${key} locator unsafe: ${locator}` };
|
|
135
|
+
}
|
|
136
|
+
const root = path.resolve(agentRoot);
|
|
137
|
+
const filePath = path.resolve(agentRoot, normalized);
|
|
138
|
+
/* v8 ignore next -- defensive containment check after normalized bundle-relative locator validation @preserve */
|
|
139
|
+
if (filePath !== root && !filePath.startsWith(`${root}${path.sep}`)) {
|
|
140
|
+
return { ok: false, warning: `${key} locator escaped bundle: ${locator}` };
|
|
141
|
+
}
|
|
142
|
+
return { ok: true, filePath };
|
|
143
|
+
}
|
|
144
|
+
function safeExistingRealPath(agentRoot, filePath, key, locator) {
|
|
145
|
+
try {
|
|
146
|
+
const root = fs.realpathSync.native(agentRoot);
|
|
147
|
+
const realPath = fs.realpathSync.native(filePath);
|
|
148
|
+
if (realPath !== root && !realPath.startsWith(`${root}${path.sep}`)) {
|
|
149
|
+
return { ok: false, warning: `${key} locator escaped bundle via symlink: ${locator}` };
|
|
150
|
+
}
|
|
151
|
+
return { ok: true, filePath: realPath };
|
|
152
|
+
}
|
|
153
|
+
catch {
|
|
154
|
+
/* v8 ignore next -- defensive realpath race/permission guard; existence is checked before callers invoke this @preserve */
|
|
155
|
+
return { ok: false, warning: `${key} locator unreadable: ${locator}` };
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
function sessionSummaryFromMessages(messages) {
|
|
159
|
+
const toolsUsed = new Set();
|
|
160
|
+
for (const message of messages) {
|
|
161
|
+
/* v8 ignore next -- defensive: session projection returns message records; this protects malformed direct projection callers @preserve */
|
|
162
|
+
if (!isRecord(message))
|
|
163
|
+
continue;
|
|
164
|
+
const name = stringValue(message.name) ?? stringValue(message.toolName);
|
|
165
|
+
/* v8 ignore next -- defensive: canonical projections expose tool names from assistant tool_calls; legacy direct tool-name projections still count if present @preserve */
|
|
166
|
+
if (name && message.role === "tool")
|
|
167
|
+
toolsUsed.add(name);
|
|
168
|
+
if (Array.isArray(message.tool_calls)) {
|
|
169
|
+
/* v8 ignore start -- defensive: projected provider messages normally expose structured tool calls; malformed direct envelopes are kept inert @preserve */
|
|
170
|
+
for (const toolCall of message.tool_calls) {
|
|
171
|
+
if (!isRecord(toolCall))
|
|
172
|
+
continue;
|
|
173
|
+
const fn = isRecord(toolCall.function) ? toolCall.function : null;
|
|
174
|
+
const toolName = fn ? stringValue(fn.name) : null;
|
|
175
|
+
if (toolName)
|
|
176
|
+
toolsUsed.add(toolName);
|
|
177
|
+
}
|
|
178
|
+
/* v8 ignore stop @preserve */
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
const warnings = messages.length === 0 ? ["session file had no usable messages"] : [];
|
|
182
|
+
return {
|
|
183
|
+
decisions: [],
|
|
184
|
+
nextLikelyStep: null,
|
|
185
|
+
toolsUsed: [...toolsUsed].sort(),
|
|
186
|
+
warnings,
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
function readSessionEnrichment(agentRoot, receipt) {
|
|
190
|
+
const source = safeSourcePath(agentRoot, receipt.sessionLocator, "session", "state/habit-sessions/");
|
|
191
|
+
if (!source.ok) {
|
|
192
|
+
return { decisions: [], nextLikelyStep: null, toolsUsed: [], warnings: [source.warning] };
|
|
193
|
+
}
|
|
194
|
+
if (!fs.existsSync(source.filePath)) {
|
|
195
|
+
return { decisions: [], nextLikelyStep: null, toolsUsed: [], warnings: ["session file missing"] };
|
|
196
|
+
}
|
|
197
|
+
const realSource = safeExistingRealPath(agentRoot, source.filePath, "session", receipt.sessionLocator);
|
|
198
|
+
if (!realSource.ok) {
|
|
199
|
+
return { decisions: [], nextLikelyStep: null, toolsUsed: [], warnings: [realSource.warning] };
|
|
200
|
+
}
|
|
201
|
+
const envelope = (0, session_events_1.loadSessionEnvelopeFile)(realSource.filePath);
|
|
202
|
+
if (!envelope) {
|
|
203
|
+
return { decisions: [], nextLikelyStep: null, toolsUsed: [], warnings: ["session file malformed"] };
|
|
204
|
+
}
|
|
205
|
+
return sessionSummaryFromMessages((0, session_events_1.projectProviderMessages)(envelope));
|
|
206
|
+
}
|
|
207
|
+
function readPending(agentRoot, receipt) {
|
|
208
|
+
const source = safeSourcePath(agentRoot, receipt.pendingLocator, "pending", "state/habit-sessions/");
|
|
209
|
+
if (!source.ok)
|
|
210
|
+
return { pending: { count: 0, files: [] }, warnings: [source.warning] };
|
|
211
|
+
try {
|
|
212
|
+
if (!fs.existsSync(source.filePath))
|
|
213
|
+
return { pending: { count: 0, files: [] }, warnings: [] };
|
|
214
|
+
const realSource = safeExistingRealPath(agentRoot, source.filePath, "pending", receipt.pendingLocator);
|
|
215
|
+
if (!realSource.ok)
|
|
216
|
+
return { pending: { count: 0, files: [] }, warnings: [realSource.warning] };
|
|
217
|
+
const entries = fs.readdirSync(realSource.filePath, { withFileTypes: true })
|
|
218
|
+
.filter((entry) => entry.isFile())
|
|
219
|
+
.map((entry) => entry.name)
|
|
220
|
+
.sort();
|
|
221
|
+
return { pending: { count: entries.length, files: entries }, warnings: [] };
|
|
222
|
+
}
|
|
223
|
+
catch {
|
|
224
|
+
return { pending: { count: 0, files: [] }, warnings: [] };
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
function runtimeOperationId(agentRoot, receipt) {
|
|
228
|
+
const source = safeSourcePath(agentRoot, receipt.runtimeStateLocator, "runtimeState", "state/habits/");
|
|
229
|
+
if (!source.ok)
|
|
230
|
+
return { operationId: null, warnings: [source.warning] };
|
|
231
|
+
try {
|
|
232
|
+
if (!fs.existsSync(source.filePath))
|
|
233
|
+
return { operationId: null, warnings: [] };
|
|
234
|
+
const realSource = safeExistingRealPath(agentRoot, source.filePath, "runtimeState", receipt.runtimeStateLocator);
|
|
235
|
+
if (!realSource.ok)
|
|
236
|
+
return { operationId: null, warnings: [realSource.warning] };
|
|
237
|
+
const parsed = JSON.parse(fs.readFileSync(realSource.filePath, "utf-8"));
|
|
238
|
+
if (!isRecord(parsed))
|
|
239
|
+
return { operationId: null, warnings: [] };
|
|
240
|
+
if (parsed.schemaVersion !== 1)
|
|
241
|
+
return { operationId: null, warnings: [] };
|
|
242
|
+
if (stringValue(parsed.name) !== receipt.habitName)
|
|
243
|
+
return { operationId: null, warnings: [] };
|
|
244
|
+
if (stringValue(parsed.latestRunId) !== receipt.runId)
|
|
245
|
+
return { operationId: null, warnings: [] };
|
|
246
|
+
if (stringValue(parsed.latestReceiptLocator) !== receipt.receiptLocator)
|
|
247
|
+
return { operationId: null, warnings: [] };
|
|
248
|
+
return { operationId: stringValue(parsed.activeOperationId), warnings: [] };
|
|
249
|
+
}
|
|
250
|
+
catch {
|
|
251
|
+
return { operationId: null, warnings: [] };
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
function uniqueStrings(values) {
|
|
255
|
+
return [...new Set(values)];
|
|
256
|
+
}
|
|
257
|
+
function truncateSummary(value) {
|
|
258
|
+
if (value.length <= MAX_SUMMARY_CHARS)
|
|
259
|
+
return value;
|
|
260
|
+
return `${value.slice(0, MAX_SUMMARY_CHARS - TRUNCATION_SUFFIX.length)}${TRUNCATION_SUFFIX}`;
|
|
261
|
+
}
|
|
262
|
+
function legacySummaryWarnings(receipt) {
|
|
263
|
+
return receipt.permissionEnvelope.warnings.some((warning) => warning.includes("legacy receipt normalized"))
|
|
264
|
+
? ["legacy receipt normalized"]
|
|
265
|
+
: [];
|
|
266
|
+
}
|
|
267
|
+
function readHabitSessionSummary(agentRoot, selector) {
|
|
268
|
+
const selection = selectHabitRunReceipt((0, flight_recorder_1.listHabitRunReceipts)(agentRoot), selector);
|
|
269
|
+
if (!selection.ok)
|
|
270
|
+
return null;
|
|
271
|
+
const receipt = selection.receipt;
|
|
272
|
+
const snapshot = summarySnapshot(receipt);
|
|
273
|
+
const session = readSessionEnrichment(agentRoot, receipt);
|
|
274
|
+
const pending = readPending(agentRoot, receipt);
|
|
275
|
+
const runtime = runtimeOperationId(agentRoot, receipt);
|
|
276
|
+
const operationId = receipt.operationId ?? runtime.operationId;
|
|
277
|
+
const decisions = uniqueStrings([
|
|
278
|
+
...snapshot.decisions,
|
|
279
|
+
...session.decisions,
|
|
280
|
+
]);
|
|
281
|
+
const summary = truncateSummary(snapshot.summary);
|
|
282
|
+
const nextLikelyStep = snapshot.nextLikelyStep ?? session.nextLikelyStep;
|
|
283
|
+
const result = {
|
|
284
|
+
runId: receipt.runId,
|
|
285
|
+
habitName: receipt.habitName,
|
|
286
|
+
operationId: operationId ?? null,
|
|
287
|
+
status: receipt.outcome,
|
|
288
|
+
triggeredAt: receipt.startedAt,
|
|
289
|
+
completedAt: receipt.endedAt,
|
|
290
|
+
summary,
|
|
291
|
+
decisions,
|
|
292
|
+
pending: pending.pending,
|
|
293
|
+
messagesSent: receipt.surfaceAttempts,
|
|
294
|
+
toolsUsed: session.toolsUsed,
|
|
295
|
+
producedRefs: receipt.producedRefs,
|
|
296
|
+
errors: receipt.errors,
|
|
297
|
+
nextLikelyStep: nextLikelyStep ?? null,
|
|
298
|
+
sources: {
|
|
299
|
+
receipt: relativeSource(receipt, "receipt"),
|
|
300
|
+
session: relativeSource(receipt, "session"),
|
|
301
|
+
pending: relativeSource(receipt, "pending"),
|
|
302
|
+
runtimeState: relativeSource(receipt, "runtimeState"),
|
|
303
|
+
},
|
|
304
|
+
warnings: [
|
|
305
|
+
...legacySummaryWarnings(receipt),
|
|
306
|
+
...session.warnings,
|
|
307
|
+
...pending.warnings,
|
|
308
|
+
...runtime.warnings,
|
|
309
|
+
],
|
|
310
|
+
};
|
|
311
|
+
(0, runtime_1.emitNervesEvent)({
|
|
312
|
+
component: "daemon",
|
|
313
|
+
event: "daemon.habit_session_summary_read",
|
|
314
|
+
message: "habit session summary read",
|
|
315
|
+
meta: { agentRoot, runId: receipt.runId, habitName: receipt.habitName, warningCount: result.warnings.length },
|
|
316
|
+
});
|
|
317
|
+
return result;
|
|
318
|
+
}
|