@ouro.bot/cli 0.1.0-alpha.596 → 0.1.0-alpha.598
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,18 @@
|
|
|
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.598",
|
|
6
|
+
"changes": [
|
|
7
|
+
"New `restart_runtime({ reason })` tool. Agent self-maintenance: asking Ari to restart the daemon over BlueBubbles is now a thing of the past. Sends `daemon.restart` over the existing socket — daemon logs the reason as `daemon.restart_requested`, runs its normal stop pathway, and exits. launchctl's KeepAlive policy auto-respawns the daemon, so the agent comes back fresh on the other side. The agent will not see this tool's return value — its process exits with the daemon and a clean boot replaces it. In dev mode (no launchctl) the daemon just exits; same observable behavior as `daemon.stop`, with the restart-requested audit event distinguishing intent."
|
|
8
|
+
]
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
"version": "0.1.0-alpha.597",
|
|
12
|
+
"changes": [
|
|
13
|
+
"New `let_go({ id, reason? })` tool. Closes a gap surfaced live by Slugger after #720 (await_condition): when a held work item is resolved externally (e.g. an obligation whose underlying issue was merged in a separate PR), the agent had no way to dismiss it. The existing path to terminal state — fulfilling via `surface` — requires delivering a response, which doesn't fit externally-resolved work. The agent kept seeing the same stale items in its prompt every turn for a month with nothing to act on. `let_go` is dismissal WITHOUT delivery: tries `arc/obligations/inner/<id>.json` (ReturnObligation → `returned` with returnTarget=`surface`), falls through to `arc/obligations/<id>.json` (Obligation → `fulfilled` with `latestNote=reason`). Idempotent — calling on an already-terminal item returns the existing status, not an error. Emits `repertoire.obligation_let_go` nerves event recording the reason for future-me. id is the bracketed value in the prompt's 'held work items' section."
|
|
14
|
+
]
|
|
15
|
+
},
|
|
4
16
|
{
|
|
5
17
|
"version": "0.1.0-alpha.596",
|
|
6
18
|
"changes": [
|
|
@@ -1073,6 +1073,27 @@ class OuroDaemon {
|
|
|
1073
1073
|
await this.stop();
|
|
1074
1074
|
this.onStopCommandComplete?.();
|
|
1075
1075
|
return { ok: true, message: "daemon stopped" };
|
|
1076
|
+
case "daemon.restart": {
|
|
1077
|
+
// Restart is "stop + let launchctl respawn." Under launchctl's KeepAlive
|
|
1078
|
+
// policy the process is auto-restarted on exit, so daemon.restart and
|
|
1079
|
+
// daemon.stop differ only in intent + audit trail. In dev (no launchctl),
|
|
1080
|
+
// the process simply exits — same observable behavior as daemon.stop.
|
|
1081
|
+
(0, runtime_1.emitNervesEvent)({
|
|
1082
|
+
component: "daemon",
|
|
1083
|
+
event: "daemon.restart_requested",
|
|
1084
|
+
message: "daemon restart requested",
|
|
1085
|
+
meta: {
|
|
1086
|
+
reason: command.reason ?? null,
|
|
1087
|
+
requestedBy: command.requestedBy ?? null,
|
|
1088
|
+
},
|
|
1089
|
+
});
|
|
1090
|
+
await this.stop();
|
|
1091
|
+
this.onStopCommandComplete?.();
|
|
1092
|
+
return {
|
|
1093
|
+
ok: true,
|
|
1094
|
+
message: "daemon restarting — launchctl will respawn",
|
|
1095
|
+
};
|
|
1096
|
+
}
|
|
1076
1097
|
case "daemon.status": {
|
|
1077
1098
|
const data = this.buildStatusPayload();
|
|
1078
1099
|
return {
|
|
@@ -19,6 +19,8 @@ const tools_attachments_1 = require("./tools-attachments");
|
|
|
19
19
|
const tools_mail_1 = require("./tools-mail");
|
|
20
20
|
const tools_trip_1 = require("./tools-trip");
|
|
21
21
|
const tools_awaiting_1 = require("./tools-awaiting");
|
|
22
|
+
const tools_obligations_1 = require("./tools-obligations");
|
|
23
|
+
const tools_runtime_1 = require("./tools-runtime");
|
|
22
24
|
// Re-export flow tools for consumers that import them from tools-base
|
|
23
25
|
var tools_flow_1 = require("./tools-flow");
|
|
24
26
|
Object.defineProperty(exports, "ponderTool", { enumerable: true, get: function () { return tools_flow_1.ponderTool; } });
|
|
@@ -53,6 +55,8 @@ exports.baseToolDefinitions = [
|
|
|
53
55
|
...tools_mail_1.mailToolDefinitions,
|
|
54
56
|
...tools_trip_1.tripToolDefinitions,
|
|
55
57
|
...tools_awaiting_1.awaitingToolDefinitions,
|
|
58
|
+
...tools_obligations_1.obligationToolDefinitions,
|
|
59
|
+
...tools_runtime_1.runtimeToolDefinitions,
|
|
56
60
|
];
|
|
57
61
|
// Convenience array of just the tool schemas (no handler/integration metadata).
|
|
58
62
|
// Used by consumers that need the OpenAI function-tool format.
|
|
@@ -0,0 +1,142 @@
|
|
|
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.obligationToolDefinitions = void 0;
|
|
37
|
+
const path = __importStar(require("path"));
|
|
38
|
+
const identity_1 = require("../heart/identity");
|
|
39
|
+
const runtime_1 = require("../nerves/runtime");
|
|
40
|
+
const json_store_1 = require("../arc/json-store");
|
|
41
|
+
const obligations_1 = require("../arc/obligations");
|
|
42
|
+
/**
|
|
43
|
+
* `let_go` lets the agent release a held work item from its attention loop.
|
|
44
|
+
*
|
|
45
|
+
* Two stores need release semantics:
|
|
46
|
+
* 1. ReturnObligations at `arc/obligations/inner/<id>.json` (status: queued|running|returned|deferred)
|
|
47
|
+
* — these are what surface in the "held work items" section of the prompt.
|
|
48
|
+
* 2. Outer Obligations at `arc/obligations/<id>.json` (status: pending|...|fulfilled)
|
|
49
|
+
* — these surface as "i owe <friend>: ..." in the commitments section.
|
|
50
|
+
*
|
|
51
|
+
* The pre-existing path to terminal state for both is the `surface` tool, which fulfills
|
|
52
|
+
* an obligation as a side-effect of delivering a response. When work is resolved EXTERNALLY
|
|
53
|
+
* (e.g. a PR merged fixed the underlying issue), there is no response to surface — the
|
|
54
|
+
* agent has no way to clear the item, so it cycles in the prompt every turn.
|
|
55
|
+
*
|
|
56
|
+
* `let_go` is the missing primitive: dismissal WITHOUT delivery.
|
|
57
|
+
*/
|
|
58
|
+
function outerObligationsDir(agentRoot) {
|
|
59
|
+
return path.join(agentRoot, "arc", "obligations");
|
|
60
|
+
}
|
|
61
|
+
function readOuterObligation(agentRoot, id) {
|
|
62
|
+
return (0, json_store_1.readJsonFile)(outerObligationsDir(agentRoot), id) ?? null;
|
|
63
|
+
}
|
|
64
|
+
function letGo(args, agentRoot, agentName) {
|
|
65
|
+
if (typeof args.id !== "string" || args.id.trim().length === 0) {
|
|
66
|
+
return JSON.stringify({ error: "id is required" });
|
|
67
|
+
}
|
|
68
|
+
const id = args.id.trim();
|
|
69
|
+
const reason = typeof args.reason === "string" && args.reason.trim().length > 0
|
|
70
|
+
? args.reason.trim()
|
|
71
|
+
: null;
|
|
72
|
+
// 1. ReturnObligation (inner) first — these are what slugger sees in "held work items".
|
|
73
|
+
const ret = (0, obligations_1.readReturnObligation)(agentName, id);
|
|
74
|
+
if (ret) {
|
|
75
|
+
if (ret.status === "returned" || ret.status === "deferred") {
|
|
76
|
+
return JSON.stringify({ kind: "return_obligation", id, already: ret.status });
|
|
77
|
+
}
|
|
78
|
+
(0, obligations_1.advanceReturnObligation)(agentName, id, {
|
|
79
|
+
status: "returned",
|
|
80
|
+
returnedAt: Date.now(),
|
|
81
|
+
returnTarget: "surface",
|
|
82
|
+
});
|
|
83
|
+
(0, runtime_1.emitNervesEvent)({
|
|
84
|
+
component: "repertoire",
|
|
85
|
+
event: "repertoire.obligation_let_go",
|
|
86
|
+
message: "agent let go of return obligation",
|
|
87
|
+
meta: { kind: "return_obligation", id, reason: reason ?? null },
|
|
88
|
+
});
|
|
89
|
+
return JSON.stringify({ kind: "return_obligation", let_go: id, reason });
|
|
90
|
+
}
|
|
91
|
+
// 2. Outer Obligation — these surface as "i owe ..." in commitments.
|
|
92
|
+
const outer = readOuterObligation(agentRoot, id);
|
|
93
|
+
if (outer) {
|
|
94
|
+
if (outer.status === "fulfilled") {
|
|
95
|
+
return JSON.stringify({ kind: "obligation", id, already: "fulfilled" });
|
|
96
|
+
}
|
|
97
|
+
(0, obligations_1.fulfillObligation)(agentRoot, id);
|
|
98
|
+
if (reason !== null) {
|
|
99
|
+
// Persist the dismissal reason as the obligation's latestNote so future-me
|
|
100
|
+
// can read why this was released (the nerves event also captures it).
|
|
101
|
+
(0, obligations_1.advanceObligation)(agentRoot, id, { latestNote: reason });
|
|
102
|
+
}
|
|
103
|
+
(0, runtime_1.emitNervesEvent)({
|
|
104
|
+
component: "repertoire",
|
|
105
|
+
event: "repertoire.obligation_let_go",
|
|
106
|
+
message: "agent let go of outer obligation",
|
|
107
|
+
meta: { kind: "obligation", id, reason: reason ?? null },
|
|
108
|
+
});
|
|
109
|
+
return JSON.stringify({ kind: "obligation", let_go: id, reason });
|
|
110
|
+
}
|
|
111
|
+
return JSON.stringify({ error: `no obligation found with id "${id}"` });
|
|
112
|
+
}
|
|
113
|
+
exports.obligationToolDefinitions = [
|
|
114
|
+
{
|
|
115
|
+
tool: {
|
|
116
|
+
type: "function",
|
|
117
|
+
function: {
|
|
118
|
+
name: "let_go",
|
|
119
|
+
description: "release a held work item from my attention. for items that have been resolved externally, no longer apply, or are stale and just cycling in my prompt with nothing to act on. takes the id i see in the 'held work items' section (or any obligation id from arc/obligations/). optional reason is recorded for future me. idempotent — calling on an already-released item returns the existing status, not an error.",
|
|
120
|
+
parameters: {
|
|
121
|
+
type: "object",
|
|
122
|
+
properties: {
|
|
123
|
+
id: {
|
|
124
|
+
type: "string",
|
|
125
|
+
description: "the obligation id — the bracketed id in 'held work items' (e.g. '1775976317954-s5pno43r'), or any obligation file's id from arc/obligations/.",
|
|
126
|
+
},
|
|
127
|
+
reason: {
|
|
128
|
+
type: "string",
|
|
129
|
+
description: "optional one-line reason i'm letting go (e.g. 'resolved externally by PR #701').",
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
required: ["id"],
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
},
|
|
136
|
+
handler: (args) => {
|
|
137
|
+
const agentRoot = (0, identity_1.getAgentRoot)();
|
|
138
|
+
const agentName = (0, identity_1.getAgentName)();
|
|
139
|
+
return letGo({ id: args.id, reason: args.reason }, agentRoot, agentName);
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
];
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.runtimeToolDefinitions = void 0;
|
|
4
|
+
const identity_1 = require("../heart/identity");
|
|
5
|
+
const runtime_1 = require("../nerves/runtime");
|
|
6
|
+
const socket_client_1 = require("../heart/daemon/socket-client");
|
|
7
|
+
async function restartRuntime(args, agentName) {
|
|
8
|
+
if (typeof args.reason !== "string" || args.reason.trim().length === 0) {
|
|
9
|
+
return JSON.stringify({ error: "reason is required (one-line audit string)" });
|
|
10
|
+
}
|
|
11
|
+
const reason = args.reason.trim();
|
|
12
|
+
(0, runtime_1.emitNervesEvent)({
|
|
13
|
+
component: "repertoire",
|
|
14
|
+
event: "repertoire.runtime_restart_requested",
|
|
15
|
+
message: "agent requested runtime restart",
|
|
16
|
+
meta: { agent: agentName, reason },
|
|
17
|
+
});
|
|
18
|
+
try {
|
|
19
|
+
const response = await (0, socket_client_1.sendDaemonCommand)(socket_client_1.DEFAULT_DAEMON_SOCKET_PATH, {
|
|
20
|
+
kind: "daemon.restart",
|
|
21
|
+
reason,
|
|
22
|
+
requestedBy: agentName,
|
|
23
|
+
});
|
|
24
|
+
return JSON.stringify({
|
|
25
|
+
requested: true,
|
|
26
|
+
reason,
|
|
27
|
+
detail: response.message ?? "daemon restart requested",
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
catch (error) {
|
|
31
|
+
return JSON.stringify({
|
|
32
|
+
error: "failed to reach daemon socket",
|
|
33
|
+
detail: error instanceof Error ? error.message : String(error),
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
exports.runtimeToolDefinitions = [
|
|
38
|
+
{
|
|
39
|
+
tool: {
|
|
40
|
+
type: "function",
|
|
41
|
+
function: {
|
|
42
|
+
name: "restart_runtime",
|
|
43
|
+
description: "ask my runtime (the daemon hosting me) to restart itself. for when something is wedged — stale state, recovery queue jammed, version mismatch, or i just need a fresh boot. under launchctl the daemon auto-respawns, so i come back on the other side with a clean slate. takes a one-line reason that lands in the audit log. i will NOT see this tool's response — my process exits with the daemon and i wake up fresh.",
|
|
44
|
+
parameters: {
|
|
45
|
+
type: "object",
|
|
46
|
+
properties: {
|
|
47
|
+
reason: {
|
|
48
|
+
type: "string",
|
|
49
|
+
description: "one-line audit reason (e.g. 'bluebubbles recovery queue wedged for 4h', 'picking up daemon version update').",
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
required: ["reason"],
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
handler: async (args) => {
|
|
57
|
+
const agentName = (0, identity_1.getAgentName)();
|
|
58
|
+
return restartRuntime({ reason: args.reason }, agentName);
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
];
|