@ouro.bot/cli 0.1.0-alpha.674 → 0.1.0-alpha.675
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 +11 -5
- package/changelog.json +13 -0
- package/dist/arc/evolution.js +13 -10
- package/dist/arc/obligations.js +1 -1
- package/dist/heart/active-work.js +3 -3
- package/dist/heart/agent-entry.js +8 -8
- package/dist/heart/commitments.js +1 -1
- package/dist/heart/core.js +45 -23
- package/dist/heart/daemon/agent-config-check.js +24 -0
- package/dist/heart/daemon/agent-discovery.js +28 -17
- package/dist/heart/daemon/agent-service.js +6 -6
- package/dist/heart/daemon/await-private-wake.js +59 -0
- package/dist/heart/daemon/cli-exec.js +180 -24
- package/dist/heart/daemon/cli-help.js +20 -2
- package/dist/heart/daemon/cli-parse.js +45 -2
- package/dist/heart/daemon/cli-render.js +1 -0
- package/dist/heart/daemon/daemon-entry.js +122 -25
- package/dist/heart/daemon/daemon.js +223 -20
- package/dist/heart/daemon/habit-private-wake.js +56 -0
- package/dist/heart/daemon/inner-status.js +3 -3
- package/dist/heart/daemon/message-router.js +39 -3
- package/dist/heart/daemon/pulse.js +43 -7
- package/dist/heart/daemon/socket-client.js +14 -2
- package/dist/heart/daemon/startup-tui.js +9 -1
- package/dist/heart/daemon/thoughts.js +39 -39
- package/dist/heart/habits/habit-scheduler.js +24 -4
- package/dist/heart/mailbox/mailbox-view.js +1 -1
- package/dist/heart/mailbox/readers/agent-machine.js +7 -7
- package/dist/heart/primitives.js +1 -1
- package/dist/heart/private-runtime/decision-renderer.js +156 -0
- package/dist/heart/private-runtime/index.js +18 -0
- package/dist/heart/private-runtime/ledger.js +218 -0
- package/dist/heart/private-runtime/policy.js +205 -0
- package/dist/heart/private-runtime/types.js +2 -0
- package/dist/heart/progress-story.js +1 -1
- package/dist/heart/session-transcript.js +1 -1
- package/dist/heart/tool-activity-callbacks.js +1 -1
- package/dist/heart/tool-description.js +2 -2
- package/dist/heart/tool-loop.js +2 -2
- package/dist/heart/turn-context.js +5 -5
- package/dist/mailbox-ui/assets/index-J8zDwHmE.js +1 -0
- package/dist/mailbox-ui/index.html +1 -1
- package/dist/mailroom/attention.js +1 -1
- package/dist/mind/context.js +2 -2
- package/dist/mind/pending.js +12 -5
- package/dist/mind/prompt.js +32 -25
- package/dist/repertoire/coding/feedback.js +25 -4
- package/dist/repertoire/tools-awaiting.js +2 -2
- package/dist/repertoire/tools-flow.js +4 -4
- package/dist/repertoire/tools-record.js +1 -1
- package/dist/repertoire/tools-session.js +33 -67
- package/dist/repertoire/tools-surface.js +3 -3
- package/dist/senses/cli.js +2 -2
- package/dist/senses/inner-dialog-worker.js +13 -519
- package/dist/senses/inner-dialog.js +10 -961
- package/dist/senses/mail.js +21 -5
- package/dist/senses/pipeline.js +1 -1
- package/dist/senses/private-runtime-worker.js +578 -0
- package/dist/senses/private-runtime.js +1145 -0
- package/dist/senses/shared-turn.js +1 -1
- package/dist/senses/trust-gate.js +2 -2
- package/package.json +1 -1
- package/dist/mailbox-ui/assets/index-CaTIFDmv.js +0 -1
|
@@ -42,20 +42,26 @@ const ouro_version_manager_1 = require("../versioning/ouro-version-manager");
|
|
|
42
42
|
function getDaemonMessageRouterDir(homeDir) {
|
|
43
43
|
return path.join((0, ouro_version_manager_1.getOuroCliHome)(homeDir), "daemon", "messages");
|
|
44
44
|
}
|
|
45
|
-
function messageId(nowIso) {
|
|
46
|
-
|
|
45
|
+
function messageId(nowIso, sequence) {
|
|
46
|
+
const base = `msg-${nowIso.replace(/[^0-9]/g, "")}`;
|
|
47
|
+
return sequence === 1 ? base : `${base}-${sequence}`;
|
|
47
48
|
}
|
|
48
49
|
class FileMessageRouter {
|
|
49
50
|
baseDir;
|
|
50
51
|
now;
|
|
52
|
+
maxMessagesPerInbox;
|
|
53
|
+
messageIdSequences = new Map();
|
|
51
54
|
constructor(options = {}) {
|
|
52
55
|
this.baseDir = options.baseDir ?? getDaemonMessageRouterDir(options.homeDir);
|
|
53
56
|
this.now = options.now ?? (() => new Date().toISOString());
|
|
57
|
+
this.maxMessagesPerInbox = Math.max(1, Math.floor(options.maxMessagesPerInbox ?? 1000));
|
|
54
58
|
fs.mkdirSync(this.baseDir, { recursive: true });
|
|
55
59
|
}
|
|
56
60
|
async send(input) {
|
|
57
61
|
const queuedAt = this.now();
|
|
58
|
-
const
|
|
62
|
+
const sequence = (this.messageIdSequences.get(queuedAt) ?? 0) + 1;
|
|
63
|
+
this.messageIdSequences.set(queuedAt, sequence);
|
|
64
|
+
const id = messageId(queuedAt, sequence);
|
|
59
65
|
const message = {
|
|
60
66
|
id,
|
|
61
67
|
from: input.from,
|
|
@@ -68,6 +74,7 @@ class FileMessageRouter {
|
|
|
68
74
|
};
|
|
69
75
|
const inboxPath = this.inboxPath(input.to);
|
|
70
76
|
fs.appendFileSync(inboxPath, `${JSON.stringify(message)}\n`, "utf-8");
|
|
77
|
+
this.trimInbox(inboxPath);
|
|
71
78
|
(0, runtime_1.emitNervesEvent)({
|
|
72
79
|
component: "daemon",
|
|
73
80
|
event: "daemon.message_queued",
|
|
@@ -107,5 +114,34 @@ class FileMessageRouter {
|
|
|
107
114
|
inboxPath(agent) {
|
|
108
115
|
return path.join(this.baseDir, `${agent}-inbox.jsonl`);
|
|
109
116
|
}
|
|
117
|
+
trimInbox(inboxPath) {
|
|
118
|
+
const raw = fs.readFileSync(inboxPath, "utf-8");
|
|
119
|
+
const entries = raw
|
|
120
|
+
.split("\n")
|
|
121
|
+
.filter((line) => line.trim().length > 0)
|
|
122
|
+
.map((line) => {
|
|
123
|
+
try {
|
|
124
|
+
JSON.parse(line);
|
|
125
|
+
return { line, valid: true };
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
return { line, valid: false };
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
const validCount = entries.filter((entry) => entry.valid).length;
|
|
132
|
+
if (validCount <= this.maxMessagesPerInbox)
|
|
133
|
+
return;
|
|
134
|
+
let validSeen = 0;
|
|
135
|
+
const keepAfterValidIndex = validCount - this.maxMessagesPerInbox;
|
|
136
|
+
const kept = entries
|
|
137
|
+
.filter((entry) => {
|
|
138
|
+
if (!entry.valid)
|
|
139
|
+
return true;
|
|
140
|
+
validSeen += 1;
|
|
141
|
+
return validSeen > keepAfterValidIndex;
|
|
142
|
+
})
|
|
143
|
+
.map((entry) => entry.line);
|
|
144
|
+
fs.writeFileSync(inboxPath, `${kept.join("\n")}\n`, "utf-8");
|
|
145
|
+
}
|
|
110
146
|
}
|
|
111
147
|
exports.FileMessageRouter = FileMessageRouter;
|
|
@@ -41,6 +41,7 @@ exports.buildPulseState = buildPulseState;
|
|
|
41
41
|
exports.findNovelBrokenAgents = findNovelBrokenAgents;
|
|
42
42
|
exports.findRecoveredAgents = findRecoveredAgents;
|
|
43
43
|
exports.buildRecoveryAlertId = buildRecoveryAlertId;
|
|
44
|
+
exports.buildPulsePrivateWakeRequest = buildPulsePrivateWakeRequest;
|
|
44
45
|
exports.pickWakeRecipient = pickWakeRecipient;
|
|
45
46
|
exports.writePulse = writePulse;
|
|
46
47
|
exports.readPulse = readPulse;
|
|
@@ -88,7 +89,7 @@ function buildAlertId(agent, errorReason) {
|
|
|
88
89
|
return `${agent}:${hashErrorReason(errorReason)}`;
|
|
89
90
|
}
|
|
90
91
|
/**
|
|
91
|
-
* Read an agent's
|
|
92
|
+
* Read an agent's private-runtime state and format it as a short
|
|
92
93
|
* activity hint string suitable for display in another agent's pulse
|
|
93
94
|
* section. Returns null when the file is missing or malformed — both
|
|
94
95
|
* cases are silent (the pulse just doesn't include activity for that
|
|
@@ -164,8 +165,8 @@ function buildPulseState(snapshots, bundlesRoot, daemonVersion, now, readActivit
|
|
|
164
165
|
/**
|
|
165
166
|
* A "novel broken transition" is an agent that is currently broken AND
|
|
166
167
|
* either (a) wasn't broken in the previous pulse state, OR (b) was broken
|
|
167
|
-
* but with a different alertId. Used to decide when to
|
|
168
|
-
*
|
|
168
|
+
* but with a different alertId. Used to decide when to queue a proactive
|
|
169
|
+
* pulse wake.
|
|
169
170
|
*
|
|
170
171
|
* Pure: takes prev and next states, returns the list of newly-broken
|
|
171
172
|
* agents that warrant a wake.
|
|
@@ -218,8 +219,24 @@ function findRecoveredAgents(prev, next) {
|
|
|
218
219
|
function buildRecoveryAlertId(agent, recoveredAt) {
|
|
219
220
|
return `recovery:${agent}:${recoveredAt}`;
|
|
220
221
|
}
|
|
222
|
+
function buildPulsePrivateWakeRequest(options) {
|
|
223
|
+
const prefix = options.triggerSource === "pulse-alert" ? "pulse alert" : "pulse recovery";
|
|
224
|
+
const detail = options.subject.errorReason ? `: ${options.subject.errorReason}` : "";
|
|
225
|
+
const originKind = options.triggerSource === "pulse-alert" ? "pulse-alert" : "pulse-recovery";
|
|
226
|
+
return {
|
|
227
|
+
agent: options.recipient,
|
|
228
|
+
reason: `${prefix} for ${options.subject.name}${detail}`,
|
|
229
|
+
triggerSource: options.triggerSource,
|
|
230
|
+
budgetClass: "scheduled",
|
|
231
|
+
idempotencyKey: `pulse:${options.recipient}:${options.alertId}`,
|
|
232
|
+
originRefs: [
|
|
233
|
+
{ kind: originKind, id: options.alertId },
|
|
234
|
+
{ kind: "agent", id: options.subject.name },
|
|
235
|
+
],
|
|
236
|
+
};
|
|
237
|
+
}
|
|
221
238
|
/**
|
|
222
|
-
* Pick which agent should receive
|
|
239
|
+
* Pick which agent should receive a pulse wake for a fleet alert. Heuristic:
|
|
223
240
|
* the most-recently-active running agent (by `lastSeenAt`), excluding the
|
|
224
241
|
* broken agent itself and any agents that aren't currently running. Returns
|
|
225
242
|
* null if there's no eligible recipient (e.g., the only other agent on the
|
|
@@ -394,7 +411,7 @@ function pruneDeliveredState(delivered, state) {
|
|
|
394
411
|
/**
|
|
395
412
|
* Single entry point the daemon's onSnapshotChange callback uses. Builds
|
|
396
413
|
* the new pulse state, diffs against the previous, writes the file, and
|
|
397
|
-
*
|
|
414
|
+
* queues pulse wakes on novel broken transitions for the most-recently-active
|
|
398
415
|
* sibling. Persistent at-most-once delivery via the delivered state file.
|
|
399
416
|
*
|
|
400
417
|
* Pure-ish: all I/O goes through dep callbacks so tests can pin every
|
|
@@ -409,6 +426,7 @@ function flushPulse(deps) {
|
|
|
409
426
|
const readDelivered = deps.readDelivered ?? (() => readDeliveredState());
|
|
410
427
|
const writeDelivered = deps.writeDelivered ?? ((d) => writeDeliveredState(d));
|
|
411
428
|
const fireInnerWake = deps.fireInnerWake ?? null;
|
|
429
|
+
const firePrivateWake = deps.firePrivateWake ?? null;
|
|
412
430
|
/* v8 ignore stop */
|
|
413
431
|
const prev = readPrev();
|
|
414
432
|
let delivered = readDelivered();
|
|
@@ -437,7 +455,16 @@ function flushPulse(deps) {
|
|
|
437
455
|
if (broken.alertId === null)
|
|
438
456
|
continue;
|
|
439
457
|
const recipient = pickWakeRecipient(state, broken.name);
|
|
440
|
-
if (recipient !== null &&
|
|
458
|
+
if (recipient !== null && firePrivateWake !== null) {
|
|
459
|
+
firePrivateWake(buildPulsePrivateWakeRequest({
|
|
460
|
+
recipient,
|
|
461
|
+
subject: broken,
|
|
462
|
+
alertId: broken.alertId,
|
|
463
|
+
triggerSource: "pulse-alert",
|
|
464
|
+
}));
|
|
465
|
+
wakeFiredFor.push(recipient);
|
|
466
|
+
}
|
|
467
|
+
else if (recipient !== null && fireInnerWake !== null) {
|
|
441
468
|
fireInnerWake(recipient);
|
|
442
469
|
wakeFiredFor.push(recipient);
|
|
443
470
|
}
|
|
@@ -453,7 +480,16 @@ function flushPulse(deps) {
|
|
|
453
480
|
const recoveredAgent = undeliveredRecovered[i];
|
|
454
481
|
const recoveryAlertId = buildRecoveryAlertId(recoveredAgent.name, state.generatedAt);
|
|
455
482
|
const recipient = pickWakeRecipient(state, recoveredAgent.name);
|
|
456
|
-
if (recipient !== null &&
|
|
483
|
+
if (recipient !== null && firePrivateWake !== null) {
|
|
484
|
+
firePrivateWake(buildPulsePrivateWakeRequest({
|
|
485
|
+
recipient,
|
|
486
|
+
subject: recoveredAgent,
|
|
487
|
+
alertId: recoveryAlertId,
|
|
488
|
+
triggerSource: "pulse-recovery",
|
|
489
|
+
}));
|
|
490
|
+
wakeFiredFor.push(recipient);
|
|
491
|
+
}
|
|
492
|
+
else if (recipient !== null && fireInnerWake !== null) {
|
|
457
493
|
fireInnerWake(recipient);
|
|
458
494
|
wakeFiredFor.push(recipient);
|
|
459
495
|
}
|
|
@@ -38,6 +38,7 @@ exports.__bypassVitestGuardForTests = __bypassVitestGuardForTests;
|
|
|
38
38
|
exports.sendDaemonCommand = sendDaemonCommand;
|
|
39
39
|
exports.checkDaemonSocketAlive = checkDaemonSocketAlive;
|
|
40
40
|
exports.requestInnerWake = requestInnerWake;
|
|
41
|
+
exports.requestPrivateWake = requestPrivateWake;
|
|
41
42
|
const fs = __importStar(require("fs"));
|
|
42
43
|
const net = __importStar(require("net"));
|
|
43
44
|
const runtime_1 = require("../../nerves/runtime");
|
|
@@ -326,7 +327,7 @@ async function requestInnerWake(agent, socketPath = exports.DEFAULT_DAEMON_SOCKE
|
|
|
326
327
|
level: "warn",
|
|
327
328
|
component: "daemon",
|
|
328
329
|
event: "daemon.inner_wake_test_blocked",
|
|
329
|
-
message: "blocked
|
|
330
|
+
message: "blocked legacy private-runtime wake alias from leaking into real daemon under vitest",
|
|
330
331
|
meta: { agent, socketPath, isProductionSocket: isProductionDaemonSocket(socketPath) },
|
|
331
332
|
});
|
|
332
333
|
return null;
|
|
@@ -335,7 +336,7 @@ async function requestInnerWake(agent, socketPath = exports.DEFAULT_DAEMON_SOCKE
|
|
|
335
336
|
(0, runtime_1.emitNervesEvent)({
|
|
336
337
|
component: "daemon",
|
|
337
338
|
event: "daemon.inner_wake_request",
|
|
338
|
-
message: "requesting daemon-managed
|
|
339
|
+
message: "requesting daemon-managed legacy private-runtime wake alias",
|
|
339
340
|
meta: {
|
|
340
341
|
agent,
|
|
341
342
|
socketPath,
|
|
@@ -347,3 +348,14 @@ async function requestInnerWake(agent, socketPath = exports.DEFAULT_DAEMON_SOCKE
|
|
|
347
348
|
}
|
|
348
349
|
return sendDaemonCommand(socketPath, { kind: "inner.wake", agent });
|
|
349
350
|
}
|
|
351
|
+
async function requestPrivateWake(agent, socketPath = exports.DEFAULT_DAEMON_SOCKET_PATH, options = {}) {
|
|
352
|
+
if (shouldSuppressSocketCall(socketPath))
|
|
353
|
+
return null;
|
|
354
|
+
if (!fs.existsSync(socketPath))
|
|
355
|
+
return null;
|
|
356
|
+
return sendDaemonCommand(socketPath, {
|
|
357
|
+
kind: "private.wake",
|
|
358
|
+
agent,
|
|
359
|
+
...options,
|
|
360
|
+
});
|
|
361
|
+
}
|
|
@@ -42,7 +42,10 @@ function assessStability(payload, now) {
|
|
|
42
42
|
const degraded = [];
|
|
43
43
|
let allResolved = true;
|
|
44
44
|
for (const worker of payload.workers) {
|
|
45
|
-
if (worker
|
|
45
|
+
if (isPassiveStoppedWorker(worker)) {
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
else if (worker.status === "crashed") {
|
|
46
49
|
degraded.push({
|
|
47
50
|
agent: worker.agent,
|
|
48
51
|
errorReason: worker.errorReason ?? "unknown error",
|
|
@@ -281,6 +284,8 @@ function buildTimedOutStartupResult(payload, assessment, maxWaitMs) {
|
|
|
281
284
|
degradedAgents.add(worker.agent);
|
|
282
285
|
const seconds = (maxWaitMs / 1000).toFixed(0);
|
|
283
286
|
for (const worker of payload.workers) {
|
|
287
|
+
if (isPassiveStoppedWorker(worker))
|
|
288
|
+
continue;
|
|
284
289
|
if (stableAgents.has(worker.agent) || degradedAgents.has(worker.agent))
|
|
285
290
|
continue;
|
|
286
291
|
degraded.push({
|
|
@@ -293,6 +298,9 @@ function buildTimedOutStartupResult(payload, assessment, maxWaitMs) {
|
|
|
293
298
|
}
|
|
294
299
|
return { stable, degraded };
|
|
295
300
|
}
|
|
301
|
+
function isPassiveStoppedWorker(worker) {
|
|
302
|
+
return worker.autoStart === false && worker.status === "stopped";
|
|
303
|
+
}
|
|
296
304
|
function formatStartupWorkerLine(payload) {
|
|
297
305
|
const base = `- ${payload.agent}/${payload.worker}: ${payload.status}`;
|
|
298
306
|
if (payload.status === "crashed" && payload.errorReason) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
// Formats
|
|
2
|
+
// Formats private-runtime session turns for human consumption.
|
|
3
3
|
// Used by `ouro thoughts` CLI command to show what the agent has been thinking.
|
|
4
4
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
5
5
|
if (k2 === undefined) k2 = k;
|
|
@@ -36,15 +36,15 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
36
36
|
})();
|
|
37
37
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
38
38
|
exports.formatSurfacedValue = formatSurfacedValue;
|
|
39
|
-
exports.
|
|
39
|
+
exports.derivePrivateRuntimeStatus = derivePrivateRuntimeStatus;
|
|
40
40
|
exports.deriveInnerJob = deriveInnerJob;
|
|
41
|
-
exports.
|
|
41
|
+
exports.formatPrivateRuntimeStatus = formatPrivateRuntimeStatus;
|
|
42
42
|
exports.extractThoughtResponseFromMessages = extractThoughtResponseFromMessages;
|
|
43
|
-
exports.
|
|
43
|
+
exports.parsePrivateRuntimeSession = parsePrivateRuntimeSession;
|
|
44
44
|
exports.formatThoughtTurns = formatThoughtTurns;
|
|
45
|
-
exports.
|
|
46
|
-
exports.
|
|
47
|
-
exports.
|
|
45
|
+
exports.getPrivateRuntimeSessionPath = getPrivateRuntimeSessionPath;
|
|
46
|
+
exports.readPrivateRuntimeStatus = readPrivateRuntimeStatus;
|
|
47
|
+
exports.readPrivateRuntimeRawData = readPrivateRuntimeRawData;
|
|
48
48
|
exports.followThoughts = followThoughts;
|
|
49
49
|
const fs = __importStar(require("fs"));
|
|
50
50
|
const path = __importStar(require("path"));
|
|
@@ -167,11 +167,11 @@ function extractEnrichedFields(pendingMessages) {
|
|
|
167
167
|
...(delegated.obligationStatus === "pending" ? { obligationPending: true } : {}),
|
|
168
168
|
};
|
|
169
169
|
}
|
|
170
|
-
function
|
|
170
|
+
function derivePrivateRuntimeStatus(pendingMessages, turns, runtimeState) {
|
|
171
171
|
if (runtimeState?.status === "running") {
|
|
172
172
|
if (pendingMessages.length > 0) {
|
|
173
173
|
return {
|
|
174
|
-
queue: "queued to
|
|
174
|
+
queue: "queued to private runtime",
|
|
175
175
|
wake: "queued behind active turn",
|
|
176
176
|
processing: "pending",
|
|
177
177
|
surfaced: "nothing yet",
|
|
@@ -187,8 +187,8 @@ function deriveInnerDialogStatus(pendingMessages, turns, runtimeState) {
|
|
|
187
187
|
}
|
|
188
188
|
if (pendingMessages.length > 0) {
|
|
189
189
|
return {
|
|
190
|
-
queue: "queued to
|
|
191
|
-
wake: "awaiting
|
|
190
|
+
queue: "queued to private runtime",
|
|
191
|
+
wake: "awaiting private-runtime turn",
|
|
192
192
|
processing: "pending",
|
|
193
193
|
surfaced: "nothing yet",
|
|
194
194
|
...extractEnrichedFields(pendingMessages),
|
|
@@ -224,8 +224,8 @@ function deriveInnerJob(pendingMessages, turns, runtimeState) {
|
|
|
224
224
|
if (isRunning) {
|
|
225
225
|
(0, runtime_1.emitNervesEvent)({
|
|
226
226
|
component: "engine",
|
|
227
|
-
event: "engine.
|
|
228
|
-
message: "derived
|
|
227
|
+
event: "engine.private_runtime_job_derive",
|
|
228
|
+
message: "derived private-runtime job state",
|
|
229
229
|
meta: { status: "running", mode: pendingMode, hasOrigin: origin !== null, hasObligation: obligationStatus !== null },
|
|
230
230
|
});
|
|
231
231
|
return {
|
|
@@ -243,8 +243,8 @@ function deriveInnerJob(pendingMessages, turns, runtimeState) {
|
|
|
243
243
|
if (pendingMessages.length > 0) {
|
|
244
244
|
(0, runtime_1.emitNervesEvent)({
|
|
245
245
|
component: "engine",
|
|
246
|
-
event: "engine.
|
|
247
|
-
message: "derived
|
|
246
|
+
event: "engine.private_runtime_job_derive",
|
|
247
|
+
message: "derived private-runtime job state",
|
|
248
248
|
meta: { status: "queued", mode: pendingMode, hasOrigin: origin !== null, hasObligation: obligationStatus !== null },
|
|
249
249
|
});
|
|
250
250
|
return {
|
|
@@ -269,8 +269,8 @@ function deriveInnerJob(pendingMessages, turns, runtimeState) {
|
|
|
269
269
|
]);
|
|
270
270
|
(0, runtime_1.emitNervesEvent)({
|
|
271
271
|
component: "engine",
|
|
272
|
-
event: "engine.
|
|
273
|
-
message: "derived
|
|
272
|
+
event: "engine.private_runtime_job_derive",
|
|
273
|
+
message: "derived private-runtime job state",
|
|
274
274
|
meta: { status: "surfaced", mode: "reflect", hasOrigin: false, hasObligation: false },
|
|
275
275
|
});
|
|
276
276
|
return {
|
|
@@ -288,8 +288,8 @@ function deriveInnerJob(pendingMessages, turns, runtimeState) {
|
|
|
288
288
|
}
|
|
289
289
|
(0, runtime_1.emitNervesEvent)({
|
|
290
290
|
component: "engine",
|
|
291
|
-
event: "engine.
|
|
292
|
-
message: "derived
|
|
291
|
+
event: "engine.private_runtime_job_derive",
|
|
292
|
+
message: "derived private-runtime job state",
|
|
293
293
|
meta: { status: "idle", mode: "reflect", hasOrigin: false, hasObligation: false },
|
|
294
294
|
});
|
|
295
295
|
return {
|
|
@@ -304,7 +304,7 @@ function deriveInnerJob(pendingMessages, turns, runtimeState) {
|
|
|
304
304
|
surfacedAt: null,
|
|
305
305
|
};
|
|
306
306
|
}
|
|
307
|
-
function
|
|
307
|
+
function formatPrivateRuntimeStatus(status) {
|
|
308
308
|
const lines = [
|
|
309
309
|
`queue: ${status.queue}`,
|
|
310
310
|
`wake: ${status.wake}`,
|
|
@@ -418,11 +418,11 @@ function extractThoughtResponseFromMessages(messages) {
|
|
|
418
418
|
? contentToText(lastAssistant.content).trim()
|
|
419
419
|
: extractSettleAnswer(messages) || extractToolActionSummary(messages);
|
|
420
420
|
}
|
|
421
|
-
function
|
|
421
|
+
function parsePrivateRuntimeSession(sessionPath) {
|
|
422
422
|
(0, runtime_1.emitNervesEvent)({
|
|
423
423
|
component: "daemon",
|
|
424
424
|
event: "daemon.thoughts_parse",
|
|
425
|
-
message: "parsing
|
|
425
|
+
message: "parsing private-runtime session",
|
|
426
426
|
meta: { sessionPath },
|
|
427
427
|
});
|
|
428
428
|
let raw;
|
|
@@ -494,11 +494,11 @@ function parseInnerDialogSession(sessionPath) {
|
|
|
494
494
|
}
|
|
495
495
|
function formatThoughtTurns(turns, lastN) {
|
|
496
496
|
if (turns.length === 0)
|
|
497
|
-
return "no
|
|
497
|
+
return "no private-runtime activity";
|
|
498
498
|
const selected = lastN > 0 ? turns.slice(-lastN) : turns;
|
|
499
499
|
/* v8 ignore next -- unreachable: turns.length > 0 checked above, slice always returns ≥1 @preserve */
|
|
500
500
|
if (selected.length === 0)
|
|
501
|
-
return "no
|
|
501
|
+
return "no private-runtime activity";
|
|
502
502
|
const lines = [];
|
|
503
503
|
for (const turn of selected) {
|
|
504
504
|
const typeLabel = turn.type === "task" && turn.taskId
|
|
@@ -518,13 +518,13 @@ function formatThoughtTurns(turns, lastN) {
|
|
|
518
518
|
}
|
|
519
519
|
return lines.join("\n").trim();
|
|
520
520
|
}
|
|
521
|
-
function
|
|
521
|
+
function getPrivateRuntimeSessionPath(agentRoot) {
|
|
522
522
|
return path.join(agentRoot, "state", "sessions", "self", "inner", "dialog.json");
|
|
523
523
|
}
|
|
524
|
-
function
|
|
524
|
+
function getPrivateRuntimeStatePath(sessionPath) {
|
|
525
525
|
return path.join(path.dirname(sessionPath), "runtime.json");
|
|
526
526
|
}
|
|
527
|
-
function
|
|
527
|
+
function readPrivateRuntimeState(runtimePath) {
|
|
528
528
|
try {
|
|
529
529
|
const raw = fs.readFileSync(runtimePath, "utf-8");
|
|
530
530
|
const parsed = JSON.parse(raw);
|
|
@@ -543,17 +543,17 @@ function readInnerDialogRuntimeState(runtimePath) {
|
|
|
543
543
|
return null;
|
|
544
544
|
}
|
|
545
545
|
}
|
|
546
|
-
function
|
|
546
|
+
function readPrivateRuntimeStatus(sessionPath, pendingDir, runtimePath = getPrivateRuntimeStatePath(sessionPath)) {
|
|
547
547
|
const pendingMessages = readPendingMessagesForStatus(pendingDir);
|
|
548
|
-
const turns =
|
|
549
|
-
const runtimeState =
|
|
550
|
-
return
|
|
548
|
+
const turns = parsePrivateRuntimeSession(sessionPath);
|
|
549
|
+
const runtimeState = readPrivateRuntimeState(runtimePath);
|
|
550
|
+
return derivePrivateRuntimeStatus(pendingMessages, turns, runtimeState);
|
|
551
551
|
}
|
|
552
|
-
function
|
|
553
|
-
const runtimePath =
|
|
552
|
+
function readPrivateRuntimeRawData(sessionPath, pendingDir) {
|
|
553
|
+
const runtimePath = getPrivateRuntimeStatePath(sessionPath);
|
|
554
554
|
const pendingMessages = readPendingMessagesForStatus(pendingDir);
|
|
555
|
-
const turns =
|
|
556
|
-
const runtimeState =
|
|
555
|
+
const turns = parsePrivateRuntimeSession(sessionPath);
|
|
556
|
+
const runtimeState = readPrivateRuntimeState(runtimePath);
|
|
557
557
|
return { pendingMessages, turns, runtimeState };
|
|
558
558
|
}
|
|
559
559
|
/**
|
|
@@ -561,15 +561,15 @@ function readInnerDialogRawData(sessionPath, pendingDir) {
|
|
|
561
561
|
* Returns a cleanup function that stops the watcher.
|
|
562
562
|
*/
|
|
563
563
|
function followThoughts(sessionPath, onNewTurns, pollIntervalMs = 1000) {
|
|
564
|
-
let displayedCount =
|
|
564
|
+
let displayedCount = parsePrivateRuntimeSession(sessionPath).length;
|
|
565
565
|
(0, runtime_1.emitNervesEvent)({
|
|
566
566
|
component: "daemon",
|
|
567
567
|
event: "daemon.thoughts_follow_start",
|
|
568
|
-
message: "started following
|
|
568
|
+
message: "started following private-runtime session",
|
|
569
569
|
meta: { sessionPath, initialTurns: displayedCount },
|
|
570
570
|
});
|
|
571
571
|
fs.watchFile(sessionPath, { interval: pollIntervalMs }, () => {
|
|
572
|
-
const turns =
|
|
572
|
+
const turns = parsePrivateRuntimeSession(sessionPath);
|
|
573
573
|
if (turns.length > displayedCount) {
|
|
574
574
|
const newTurns = turns.slice(displayedCount);
|
|
575
575
|
onNewTurns(formatThoughtTurns(newTurns, 0));
|
|
@@ -584,7 +584,7 @@ function followThoughts(sessionPath, onNewTurns, pollIntervalMs = 1000) {
|
|
|
584
584
|
(0, runtime_1.emitNervesEvent)({
|
|
585
585
|
component: "daemon",
|
|
586
586
|
event: "daemon.thoughts_follow_end",
|
|
587
|
-
message: "stopped following
|
|
587
|
+
message: "stopped following private-runtime session",
|
|
588
588
|
meta: { sessionPath, totalTurns: displayedCount },
|
|
589
589
|
});
|
|
590
590
|
};
|
|
@@ -108,7 +108,9 @@ 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, "overdue"
|
|
111
|
+
this.onHabitFire(habit.name, "overdue", {
|
|
112
|
+
occurrenceId: this.overdueOccurrenceId(habit),
|
|
113
|
+
});
|
|
112
114
|
continue;
|
|
113
115
|
}
|
|
114
116
|
const lastRunMs = new Date(habit.lastRun).getTime();
|
|
@@ -120,7 +122,9 @@ class HabitScheduler {
|
|
|
120
122
|
message: "firing overdue habit",
|
|
121
123
|
meta: { habitName: habit.name, agent: this.agent, elapsedMs: elapsed },
|
|
122
124
|
});
|
|
123
|
-
this.onHabitFire(habit.name, "overdue"
|
|
125
|
+
this.onHabitFire(habit.name, "overdue", {
|
|
126
|
+
occurrenceId: this.overdueOccurrenceId(habit),
|
|
127
|
+
});
|
|
124
128
|
}
|
|
125
129
|
}
|
|
126
130
|
}
|
|
@@ -184,7 +188,9 @@ class HabitScheduler {
|
|
|
184
188
|
if (!job) {
|
|
185
189
|
return { ok: false, message: `unknown habit job: ${jobId}` };
|
|
186
190
|
}
|
|
187
|
-
this.onHabitFire(job.taskId, trigger
|
|
191
|
+
this.onHabitFire(job.taskId, trigger, {
|
|
192
|
+
occurrenceId: this.jobOccurrenceId(job, trigger),
|
|
193
|
+
});
|
|
188
194
|
(0, runtime_1.emitNervesEvent)({
|
|
189
195
|
component: "daemon",
|
|
190
196
|
event: "daemon.habit_job_triggered",
|
|
@@ -312,7 +318,9 @@ class HabitScheduler {
|
|
|
312
318
|
createTimerFallback(habitName, cadenceMs) {
|
|
313
319
|
const schedule = () => {
|
|
314
320
|
const timer = setTimeout(() => {
|
|
315
|
-
this.onHabitFire(habitName, "overdue"
|
|
321
|
+
this.onHabitFire(habitName, "overdue", {
|
|
322
|
+
occurrenceId: this.timerOccurrenceId(habitName, cadenceMs),
|
|
323
|
+
});
|
|
316
324
|
schedule();
|
|
317
325
|
}, cadenceMs);
|
|
318
326
|
this.timerFallbacks.set(habitName, timer);
|
|
@@ -387,5 +395,17 @@ class HabitScheduler {
|
|
|
387
395
|
}
|
|
388
396
|
return jobs;
|
|
389
397
|
}
|
|
398
|
+
overdueOccurrenceId(habit) {
|
|
399
|
+
if (habit.lastRun === null)
|
|
400
|
+
return `overdue:first-run:${habit.cadence}`;
|
|
401
|
+
return `overdue:last-run:${habit.lastRun}:cadence:${habit.cadence}`;
|
|
402
|
+
}
|
|
403
|
+
jobOccurrenceId(job, trigger) {
|
|
404
|
+
return `job:${job.id}:${trigger}:last-run:${job.lastRun ?? "never"}`;
|
|
405
|
+
}
|
|
406
|
+
timerOccurrenceId(habitName, cadenceMs) {
|
|
407
|
+
const slot = Math.floor(this.deps.now() / cadenceMs);
|
|
408
|
+
return `timer:${habitName}:cadence-ms:${cadenceMs}:slot:${slot}`;
|
|
409
|
+
}
|
|
390
410
|
}
|
|
391
411
|
exports.HabitScheduler = HabitScheduler;
|
|
@@ -154,7 +154,7 @@ function buildRecentActivity(agent) {
|
|
|
154
154
|
kind: "inner",
|
|
155
155
|
at: agent.inner.latestActivityAt,
|
|
156
156
|
label: agent.inner.surfacedSummary ?? agent.inner.status,
|
|
157
|
-
detail: agent.inner.hasPending ? "pending
|
|
157
|
+
detail: agent.inner.hasPending ? "pending private-runtime work" : agent.inner.obligationStatus ?? "no linked obligation",
|
|
158
158
|
});
|
|
159
159
|
}
|
|
160
160
|
return items
|
|
@@ -177,9 +177,9 @@ function readSessionSummary(agentName, agentRoot, now) {
|
|
|
177
177
|
return { items };
|
|
178
178
|
}
|
|
179
179
|
function readInnerSummary(agentRoot) {
|
|
180
|
-
const sessionPath = (0, thoughts_1.
|
|
180
|
+
const sessionPath = (0, thoughts_1.getPrivateRuntimeSessionPath)(agentRoot);
|
|
181
181
|
const pendingDir = path.join(agentRoot, "state", "pending", "self", "inner", "dialog");
|
|
182
|
-
const { pendingMessages, turns, runtimeState } = (0, thoughts_1.
|
|
182
|
+
const { pendingMessages, turns, runtimeState } = (0, thoughts_1.readPrivateRuntimeRawData)(sessionPath, pendingDir);
|
|
183
183
|
const job = (0, thoughts_1.deriveInnerJob)(pendingMessages, turns, runtimeState);
|
|
184
184
|
const surfacedSummary = job.surfacedResult ? (0, thoughts_1.formatSurfacedValue)(job.surfacedResult) : null;
|
|
185
185
|
const latestPendingTimestamp = pendingMessages.length > 0
|
|
@@ -189,11 +189,11 @@ function readInnerSummary(agentRoot) {
|
|
|
189
189
|
?? runtimeState?.startedAt
|
|
190
190
|
?? runtimeState?.lastCompletedAt
|
|
191
191
|
?? null;
|
|
192
|
-
// Read the return-obligation queue so the
|
|
193
|
-
// agent is actually holding right now. Before this, the
|
|
194
|
-
// panel only consulted the pending-messages dir (inbox-style)
|
|
195
|
-
//
|
|
196
|
-
//
|
|
192
|
+
// Read the return-obligation queue so the Private Runtime tab can show what the
|
|
193
|
+
// agent is actually holding right now. Before this, the private-runtime work
|
|
194
|
+
// panel only consulted the pending-messages dir (inbox-style), so it
|
|
195
|
+
// looked empty even when dozens of held items were sitting in
|
|
196
|
+
// arc/obligations/inner/ waiting to be reinjected next turn.
|
|
197
197
|
const returnObligationQueue = readReturnObligationQueueSummary(agentRoot);
|
|
198
198
|
return {
|
|
199
199
|
summary: {
|
package/dist/heart/primitives.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.HARNESS_BOOTSTRAP_PHASES = void 0;
|
|
4
|
-
exports.HARNESS_BOOTSTRAP_PHASES = ["bundle", "psyche", "
|
|
4
|
+
exports.HARNESS_BOOTSTRAP_PHASES = ["bundle", "psyche", "private-runtime"];
|