@ouro.bot/cli 0.1.0-alpha.141 → 0.1.0-alpha.142
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
CHANGED
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"_note": "This changelog is maintained as part of the PR/version-bump workflow. Agent-curated, not auto-generated. Agents read this file directly via read_file to understand what changed between versions.",
|
|
3
3
|
"versions": [
|
|
4
|
+
{
|
|
5
|
+
"version": "0.1.0-alpha.142",
|
|
6
|
+
"changes": [
|
|
7
|
+
"Surface tool fulfills heart obligations on successful routing: findPendingObligationForOrigin + fulfillObligation called after inner obligation advance, wrapped in try/catch.",
|
|
8
|
+
"New fulfillHeartObligation callback on HandleSurfaceInput — origin-based lookup independent of inner obligationId.",
|
|
9
|
+
"ouro inner status command: reads runtime.json, journal dir, heartbeat cadence, attention count. Shows last turn, status, heartbeat health, journal listing, held thoughts."
|
|
10
|
+
]
|
|
11
|
+
},
|
|
4
12
|
{
|
|
5
13
|
"version": "0.1.0-alpha.141",
|
|
6
14
|
"changes": [
|
|
@@ -295,6 +295,7 @@ function usage() {
|
|
|
295
295
|
" ouro friend create --name <name> [--trust <level>] [--agent <name>]",
|
|
296
296
|
" ouro friend update <id> --trust <level> [--agent <name>]",
|
|
297
297
|
" ouro thoughts [--last <n>] [--json] [--follow] [--agent <name>]",
|
|
298
|
+
" ouro inner [--agent <name>]",
|
|
298
299
|
" ouro friend link <agent> --friend <id> --provider <p> --external-id <eid>",
|
|
299
300
|
" ouro friend unlink <agent> --friend <id> --provider <p> --external-id <eid>",
|
|
300
301
|
" ouro whoami [--agent <name>]",
|
|
@@ -987,6 +988,10 @@ function parseOuroCommand(args) {
|
|
|
987
988
|
return parseThoughtsCommand(args.slice(1));
|
|
988
989
|
if (head === "attention")
|
|
989
990
|
return parseAttentionCommand(args.slice(1));
|
|
991
|
+
if (head === "inner") {
|
|
992
|
+
const { agent } = extractAgentFlag(args.slice(1));
|
|
993
|
+
return { kind: "inner.status", ...(agent ? { agent } : {}) };
|
|
994
|
+
}
|
|
990
995
|
if (head === "chat") {
|
|
991
996
|
if (!second)
|
|
992
997
|
throw new Error(`Usage\n${usage()}`);
|
|
@@ -2607,6 +2612,89 @@ async function runOuroCli(args, deps = createDefaultOuroCliDeps()) {
|
|
|
2607
2612
|
}
|
|
2608
2613
|
}
|
|
2609
2614
|
/* v8 ignore stop */
|
|
2615
|
+
// ── inner dialog status (local, no daemon socket needed) ──
|
|
2616
|
+
/* v8 ignore start -- inner status handler: requires real agent state on disk @preserve */
|
|
2617
|
+
if (command.kind === "inner.status") {
|
|
2618
|
+
try {
|
|
2619
|
+
const agentName = command.agent ?? (0, identity_1.getAgentName)();
|
|
2620
|
+
const agentRoot = (0, identity_1.getAgentRoot)(agentName);
|
|
2621
|
+
const { buildInnerStatusOutput } = await Promise.resolve().then(() => __importStar(require("./inner-status")));
|
|
2622
|
+
const { sessionPath: getSessionPath } = await Promise.resolve().then(() => __importStar(require("../config")));
|
|
2623
|
+
const { parseCadenceMs, DEFAULT_CADENCE_MS } = await Promise.resolve().then(() => __importStar(require("./heartbeat-timer")));
|
|
2624
|
+
const { parseFrontmatter } = await Promise.resolve().then(() => __importStar(require("../../repertoire/tasks/parser")));
|
|
2625
|
+
const { listActiveObligations } = await Promise.resolve().then(() => __importStar(require("../../mind/obligations")));
|
|
2626
|
+
// Read runtime state
|
|
2627
|
+
const innerSessionPath = getSessionPath("inner-dialog", "inner", "session");
|
|
2628
|
+
const runtimeJsonPath = path.join(path.dirname(innerSessionPath), "runtime.json");
|
|
2629
|
+
let runtimeState = null;
|
|
2630
|
+
try {
|
|
2631
|
+
const raw = fs.readFileSync(runtimeJsonPath, "utf-8");
|
|
2632
|
+
runtimeState = JSON.parse(raw);
|
|
2633
|
+
}
|
|
2634
|
+
catch { /* missing or corrupt — will show "unknown" */ }
|
|
2635
|
+
// Read journal files
|
|
2636
|
+
const journalDir = path.join(agentRoot, "journal");
|
|
2637
|
+
let journalFiles = [];
|
|
2638
|
+
try {
|
|
2639
|
+
const entries = fs.readdirSync(journalDir, { withFileTypes: true });
|
|
2640
|
+
journalFiles = entries
|
|
2641
|
+
.filter((e) => e.isFile() && !e.name.startsWith("."))
|
|
2642
|
+
.map((e) => {
|
|
2643
|
+
const stat = fs.statSync(path.join(journalDir, e.name));
|
|
2644
|
+
return { name: e.name, mtimeMs: stat.mtimeMs };
|
|
2645
|
+
});
|
|
2646
|
+
}
|
|
2647
|
+
catch { /* missing dir — will show (empty) */ }
|
|
2648
|
+
// Read heartbeat cadence
|
|
2649
|
+
let heartbeat = null;
|
|
2650
|
+
try {
|
|
2651
|
+
const habitsDir = path.join(agentRoot, "habits");
|
|
2652
|
+
const habitFiles = fs.readdirSync(habitsDir);
|
|
2653
|
+
const heartbeatFile = habitFiles.find((f) => f.endsWith("-heartbeat.md"));
|
|
2654
|
+
let cadenceMs = DEFAULT_CADENCE_MS;
|
|
2655
|
+
if (heartbeatFile) {
|
|
2656
|
+
const content = fs.readFileSync(path.join(habitsDir, heartbeatFile), "utf-8");
|
|
2657
|
+
const lines = content.split(/\r?\n/);
|
|
2658
|
+
if (lines[0]?.trim() === "---") {
|
|
2659
|
+
const closing = lines.findIndex((line, index) => index > 0 && line.trim() === "---");
|
|
2660
|
+
if (closing !== -1) {
|
|
2661
|
+
const rawFrontmatter = lines.slice(1, closing).join("\n");
|
|
2662
|
+
const frontmatter = parseFrontmatter(rawFrontmatter);
|
|
2663
|
+
const parsed = parseCadenceMs(frontmatter.cadence);
|
|
2664
|
+
if (parsed !== null)
|
|
2665
|
+
cadenceMs = parsed;
|
|
2666
|
+
}
|
|
2667
|
+
}
|
|
2668
|
+
}
|
|
2669
|
+
let lastCompletedAt = null;
|
|
2670
|
+
if (runtimeState?.lastCompletedAt) {
|
|
2671
|
+
const ms = new Date(runtimeState.lastCompletedAt).getTime();
|
|
2672
|
+
if (!Number.isNaN(ms))
|
|
2673
|
+
lastCompletedAt = ms;
|
|
2674
|
+
}
|
|
2675
|
+
heartbeat = { cadenceMs, lastCompletedAt };
|
|
2676
|
+
}
|
|
2677
|
+
catch { /* no habits — heartbeat unknown */ }
|
|
2678
|
+
// Attention count
|
|
2679
|
+
const activeObligations = listActiveObligations(agentName);
|
|
2680
|
+
const message = buildInnerStatusOutput({
|
|
2681
|
+
agentName,
|
|
2682
|
+
runtimeState,
|
|
2683
|
+
journalFiles,
|
|
2684
|
+
heartbeat,
|
|
2685
|
+
attentionCount: activeObligations.length,
|
|
2686
|
+
now: Date.now(),
|
|
2687
|
+
});
|
|
2688
|
+
deps.writeStdout(message);
|
|
2689
|
+
return message;
|
|
2690
|
+
}
|
|
2691
|
+
catch {
|
|
2692
|
+
const message = "error: no agent context — use --agent <name> to specify";
|
|
2693
|
+
deps.writeStdout(message);
|
|
2694
|
+
return message;
|
|
2695
|
+
}
|
|
2696
|
+
}
|
|
2697
|
+
/* v8 ignore stop */
|
|
2610
2698
|
// ── session list (local, no daemon socket needed) ──
|
|
2611
2699
|
if (command.kind === "session.list") {
|
|
2612
2700
|
/* v8 ignore start -- production default: requires full identity setup @preserve */
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.buildInnerStatusOutput = buildInnerStatusOutput;
|
|
4
|
+
const runtime_1 = require("../../nerves/runtime");
|
|
5
|
+
function formatRelativeTime(elapsedMs) {
|
|
6
|
+
const minutes = Math.floor(elapsedMs / (60 * 1000));
|
|
7
|
+
if (minutes < 1)
|
|
8
|
+
return "just now";
|
|
9
|
+
if (minutes === 1)
|
|
10
|
+
return "1 minute ago";
|
|
11
|
+
if (minutes < 60)
|
|
12
|
+
return `${minutes} minutes ago`;
|
|
13
|
+
const hours = Math.floor(minutes / 60);
|
|
14
|
+
if (hours === 1)
|
|
15
|
+
return "1 hour ago";
|
|
16
|
+
return `${hours} hours ago`;
|
|
17
|
+
}
|
|
18
|
+
function formatCadence(cadenceMs) {
|
|
19
|
+
const minutes = Math.round(cadenceMs / (60 * 1000));
|
|
20
|
+
if (minutes >= 60) {
|
|
21
|
+
const hours = Math.round(minutes / 60);
|
|
22
|
+
return `${hours}h`;
|
|
23
|
+
}
|
|
24
|
+
return `${minutes}m`;
|
|
25
|
+
}
|
|
26
|
+
function buildInnerStatusOutput(input) {
|
|
27
|
+
const { agentName, runtimeState, journalFiles, heartbeat, attentionCount, now } = input;
|
|
28
|
+
const lines = [];
|
|
29
|
+
lines.push(`inner dialog status: ${agentName}`);
|
|
30
|
+
// Last turn
|
|
31
|
+
if (runtimeState?.lastCompletedAt) {
|
|
32
|
+
const lastMs = new Date(runtimeState.lastCompletedAt).getTime();
|
|
33
|
+
const elapsed = now - lastMs;
|
|
34
|
+
const relativeTime = formatRelativeTime(elapsed);
|
|
35
|
+
const reasonSuffix = runtimeState.reason ? ` (${runtimeState.reason})` : "";
|
|
36
|
+
lines.push(` last turn: ${relativeTime}${reasonSuffix}`);
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
lines.push(" last turn: unknown");
|
|
40
|
+
}
|
|
41
|
+
// Status
|
|
42
|
+
if (runtimeState) {
|
|
43
|
+
const reasonSuffix = runtimeState.status === "running" && runtimeState.reason ? ` (${runtimeState.reason})` : "";
|
|
44
|
+
lines.push(` status: ${runtimeState.status}${reasonSuffix}`);
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
lines.push(" status: unknown");
|
|
48
|
+
}
|
|
49
|
+
// Heartbeat health
|
|
50
|
+
if (heartbeat && heartbeat.lastCompletedAt !== null) {
|
|
51
|
+
const elapsed = now - heartbeat.lastCompletedAt;
|
|
52
|
+
const threshold = heartbeat.cadenceMs * 1.5;
|
|
53
|
+
const health = elapsed < threshold ? "healthy" : "overdue";
|
|
54
|
+
const cadenceStr = formatCadence(heartbeat.cadenceMs);
|
|
55
|
+
const sinceStr = formatRelativeTime(elapsed);
|
|
56
|
+
lines.push(` heartbeat: ${health} (cadence ${cadenceStr}, ${sinceStr})`);
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
lines.push(" heartbeat: unknown");
|
|
60
|
+
}
|
|
61
|
+
// Journal
|
|
62
|
+
if (journalFiles.length === 0) {
|
|
63
|
+
lines.push(" journal: (empty)");
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
lines.push(" journal:");
|
|
67
|
+
const sorted = [...journalFiles].sort((a, b) => b.mtimeMs - a.mtimeMs);
|
|
68
|
+
for (const file of sorted) {
|
|
69
|
+
const elapsed = now - file.mtimeMs;
|
|
70
|
+
const relativeTime = formatRelativeTime(elapsed);
|
|
71
|
+
lines.push(` - ${file.name} (${relativeTime})`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
// Attention
|
|
75
|
+
const thoughtWord = attentionCount === 1 ? "thought" : "thoughts";
|
|
76
|
+
lines.push(` attention: ${attentionCount} held ${thoughtWord}`);
|
|
77
|
+
(0, runtime_1.emitNervesEvent)({
|
|
78
|
+
component: "daemon",
|
|
79
|
+
event: "daemon.inner_status_read",
|
|
80
|
+
message: "inner dialog status read",
|
|
81
|
+
meta: {
|
|
82
|
+
agentName,
|
|
83
|
+
status: runtimeState?.status ?? "unknown",
|
|
84
|
+
journalCount: journalFiles.length,
|
|
85
|
+
attentionCount,
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
return lines.join("\n");
|
|
89
|
+
}
|
package/dist/repertoire/tools.js
CHANGED
|
@@ -48,6 +48,7 @@ const guardrails_1 = require("./guardrails");
|
|
|
48
48
|
const identity_1 = require("../heart/identity");
|
|
49
49
|
const surface_tool_1 = require("../senses/surface-tool");
|
|
50
50
|
const obligations_1 = require("../mind/obligations");
|
|
51
|
+
const obligations_2 = require("../heart/obligations");
|
|
51
52
|
const session_activity_1 = require("../heart/session-activity");
|
|
52
53
|
const path = __importStar(require("path"));
|
|
53
54
|
function safeGetAgentRoot() {
|
|
@@ -207,6 +208,20 @@ const surfaceToolDefinition = {
|
|
|
207
208
|
}
|
|
208
209
|
/* v8 ignore stop */
|
|
209
210
|
},
|
|
211
|
+
fulfillHeartObligation: (origin) => {
|
|
212
|
+
/* v8 ignore start -- heart obligation fulfillment: tested via surface-tool.test.ts @preserve */
|
|
213
|
+
try {
|
|
214
|
+
const agentRoot = (0, identity_1.getAgentRoot)();
|
|
215
|
+
const heartObligation = (0, obligations_2.findPendingObligationForOrigin)(agentRoot, origin);
|
|
216
|
+
if (heartObligation) {
|
|
217
|
+
(0, obligations_2.fulfillObligation)(agentRoot, heartObligation.id);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
catch {
|
|
221
|
+
// swallowed — heart obligation fulfillment must never break surface delivery
|
|
222
|
+
}
|
|
223
|
+
/* v8 ignore stop */
|
|
224
|
+
},
|
|
210
225
|
});
|
|
211
226
|
},
|
|
212
227
|
summaryKeys: ["content", "delegationId"],
|
|
@@ -31,7 +31,7 @@ exports.surfaceToolDef = {
|
|
|
31
31
|
},
|
|
32
32
|
};
|
|
33
33
|
async function handleSurface(input) {
|
|
34
|
-
const { content, delegationId, friendId, queue, routeToFriend, advanceObligation } = input;
|
|
34
|
+
const { content, delegationId, friendId, queue, routeToFriend, advanceObligation, fulfillHeartObligation } = input;
|
|
35
35
|
// Resolve target friend
|
|
36
36
|
let targetFriendId;
|
|
37
37
|
let queueItem;
|
|
@@ -74,6 +74,19 @@ async function handleSurface(input) {
|
|
|
74
74
|
returnTarget: "surface",
|
|
75
75
|
});
|
|
76
76
|
}
|
|
77
|
+
// Fulfill the heart obligation for this origin (separate from inner/mind obligation)
|
|
78
|
+
if (fulfillHeartObligation) {
|
|
79
|
+
try {
|
|
80
|
+
fulfillHeartObligation({
|
|
81
|
+
friendId: queueItem.friendId,
|
|
82
|
+
channel: queueItem.channel,
|
|
83
|
+
key: queueItem.key,
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
// swallowed — heart obligation fulfillment must never break surface delivery
|
|
88
|
+
}
|
|
89
|
+
}
|
|
77
90
|
(0, attention_queue_1.dequeueAttentionItem)(queue, delegationId);
|
|
78
91
|
}
|
|
79
92
|
// Return delivery status
|