@ouro.bot/cli 0.1.0-alpha.861 → 0.1.0-alpha.862
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 +9 -0
- package/deploy/unraid/sanctuary.ouro/bundle-meta.json +1 -1
- package/deploy/unraid/sanctuary.xml +1 -1
- package/dist/a2a/server.js +1 -1
- package/dist/repertoire/tools-awaiting.js +3 -3
- package/dist/repertoire/tools-runtime.js +92 -4
- package/dist/repertoire/tools-unraid.js +1 -1
- package/dist/senses/private-runtime.js +29 -1
- package/dist/senses/sanctuary-runtime.js +5 -0
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
package/changelog.json
CHANGED
|
@@ -1,6 +1,15 @@
|
|
|
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.862",
|
|
6
|
+
"changes": [
|
|
7
|
+
"Sanctuary tool context is now attached to every private-runtime turn of the sanctuary agent (await, habit, instinct, external-event), so await turns can observe the download queue.",
|
|
8
|
+
"The missing-Sanctuary-context result now uses code tool_context_missing and says it is a harness wiring gap, not a runtime outage.",
|
|
9
|
+
"restart_runtime now refuses to restart the daemon from autonomous turns (await, habit, external-event, instinct, scheduler) and enforces a persisted 6-hour cooldown, started only after the daemon accepts a restart.",
|
|
10
|
+
"resolve_await now constrains verdict to \"yes\" or \"no\", says that still pending is \"no\", and returns an explicit error naming the bad value and what to send."
|
|
11
|
+
]
|
|
12
|
+
},
|
|
4
13
|
{
|
|
5
14
|
"version": "0.1.0-alpha.861",
|
|
6
15
|
"changes": [
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<?xml version="1.0"?>
|
|
2
2
|
<Container version="2">
|
|
3
3
|
<Name>ouro-butler</Name>
|
|
4
|
-
<Repository>ghcr.io/ourostack/ouroboros-butler:0.1.0-alpha.
|
|
4
|
+
<Repository>ghcr.io/ourostack/ouroboros-butler:0.1.0-alpha.862</Repository>
|
|
5
5
|
<Registry>https://github.com/ourostack/ouroboros/pkgs/container/ouroboros-butler</Registry>
|
|
6
6
|
<Network>host</Network>
|
|
7
7
|
<Shell>sh</Shell>
|
package/dist/a2a/server.js
CHANGED
|
@@ -165,7 +165,7 @@ async function defaultTurnRunner(input) {
|
|
|
165
165
|
// every Unraid read answered "Sanctuary runtime is unavailable"). The relationship
|
|
166
166
|
// profile still decides which of those tools the peer may call.
|
|
167
167
|
toolContext: {
|
|
168
|
-
...(input.agentName
|
|
168
|
+
...((0, sanctuary_runtime_1.isSanctuaryAgent)(input.agentName) ? (0, sanctuary_runtime_1.createSanctuaryToolContext)(input.agentName) : {}),
|
|
169
169
|
...(input.relationshipAuthorization ? { relationshipAuthorization: input.relationshipAuthorization } : {}),
|
|
170
170
|
...(input.delegatedCommand ? { delegatedCommand: input.delegatedCommand } : {}),
|
|
171
171
|
},
|
|
@@ -243,7 +243,7 @@ async function resolveAwaitTool(name, verdict, observation, agentRoot, agentName
|
|
|
243
243
|
return JSON.stringify({ error: `await "${name}" is not pending (status: ${existing.status})` });
|
|
244
244
|
}
|
|
245
245
|
if (verdict !== "yes" && verdict !== "no") {
|
|
246
|
-
return JSON.stringify({ error:
|
|
246
|
+
return JSON.stringify({ error: `verdict must be exactly "yes" or "no" (got ${JSON.stringify(verdict)}); use "no" with an observation while still waiting` });
|
|
247
247
|
}
|
|
248
248
|
if (!observation || !observation.trim()) {
|
|
249
249
|
return JSON.stringify({ error: "observation is required" });
|
|
@@ -521,12 +521,12 @@ exports.awaitingToolDefinitions = [
|
|
|
521
521
|
type: "function",
|
|
522
522
|
function: {
|
|
523
523
|
name: "resolve_await",
|
|
524
|
-
description: "Resolve a pending await with a verdict. verdict='yes' archives and fires the alert. verdict='no' records the observation and continues polling.",
|
|
524
|
+
description: "Resolve a pending await with a verdict. verdict='yes' archives and fires the alert. verdict='no' records the observation and continues polling; still pending is always 'no'.",
|
|
525
525
|
parameters: {
|
|
526
526
|
type: "object",
|
|
527
527
|
properties: {
|
|
528
528
|
name: { type: "string", description: "Await name (filename stem)." },
|
|
529
|
-
verdict: { type: "string", description: "'yes' if the condition is met, 'no'
|
|
529
|
+
verdict: { type: "string", enum: ["yes", "no"], description: "Exactly 'yes' if the condition is met. Exactly 'no' for anything else, including still pending, not yet, or unable to tell: send 'no' with an observation and polling continues." },
|
|
530
530
|
observation: { type: "string", description: "One-line summary of what I saw this tick." },
|
|
531
531
|
},
|
|
532
532
|
required: ["name", "verdict", "observation"],
|
|
@@ -1,14 +1,99 @@
|
|
|
1
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
|
+
})();
|
|
2
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.runtimeToolDefinitions = void 0;
|
|
36
|
+
exports.runtimeToolDefinitions = exports.RESTART_COOLDOWN_MS = void 0;
|
|
37
|
+
const fs = __importStar(require("node:fs"));
|
|
38
|
+
const path = __importStar(require("node:path"));
|
|
39
|
+
const node_crypto_1 = require("node:crypto");
|
|
4
40
|
const identity_1 = require("../heart/identity");
|
|
5
41
|
const runtime_1 = require("../nerves/runtime");
|
|
6
42
|
const socket_client_1 = require("../heart/daemon/socket-client");
|
|
7
|
-
|
|
43
|
+
exports.RESTART_COOLDOWN_MS = 6 * 60 * 60 * 1000;
|
|
44
|
+
function restartCooldownPath(agentName) {
|
|
45
|
+
return path.join((0, identity_1.getAgentStateRoot)(agentName), "daemon", "restart-runtime-cooldown.json");
|
|
46
|
+
}
|
|
47
|
+
function readLastRestartMs(agentName) {
|
|
48
|
+
try {
|
|
49
|
+
const parsed = JSON.parse(fs.readFileSync(restartCooldownPath(agentName), "utf8"));
|
|
50
|
+
return typeof parsed.requestedAtMs === "number" ? parsed.requestedAtMs : null;
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
// The cooldown is stored per agent, but the restart it limits is daemon-wide.
|
|
57
|
+
function recordRestart(agentName, nowMs) {
|
|
58
|
+
try {
|
|
59
|
+
const file = restartCooldownPath(agentName);
|
|
60
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
61
|
+
const temporary = `${file}.tmp-${process.pid}-${(0, node_crypto_1.randomUUID)()}`;
|
|
62
|
+
fs.writeFileSync(temporary, JSON.stringify({ requestedAtMs: nowMs }), "utf8");
|
|
63
|
+
fs.renameSync(temporary, file);
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
// A failed cooldown write must not block an otherwise allowed restart.
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
async function restartRuntime(args, agentName, ctx) {
|
|
8
70
|
if (typeof args.reason !== "string" || args.reason.trim().length === 0) {
|
|
9
71
|
return JSON.stringify({ error: "reason is required (one-line audit string)" });
|
|
10
72
|
}
|
|
11
73
|
const reason = args.reason.trim();
|
|
74
|
+
if (ctx?.autonomousTurnKind) {
|
|
75
|
+
(0, runtime_1.emitNervesEvent)({
|
|
76
|
+
component: "repertoire",
|
|
77
|
+
event: "repertoire.runtime_restart_refused",
|
|
78
|
+
message: "restart_runtime refused in an autonomous turn",
|
|
79
|
+
meta: { agent: agentName, turnKind: ctx.autonomousTurnKind, reason },
|
|
80
|
+
});
|
|
81
|
+
return JSON.stringify({
|
|
82
|
+
refused: true,
|
|
83
|
+
code: "autonomous_turn_restart_refused",
|
|
84
|
+
error: `restart_runtime is not allowed from an autonomous ${ctx.autonomousTurnKind} turn: restarting the daemon kills in-flight work for every agent and habit. A tool error or missing capability is not evidence of a dead runtime. Record or report the problem (note it, surface it to the owner) instead of restarting.`,
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
const lastMs = readLastRestartMs(agentName);
|
|
88
|
+
const nowMs = Date.now();
|
|
89
|
+
if (lastMs !== null && lastMs <= nowMs && nowMs - lastMs < exports.RESTART_COOLDOWN_MS) {
|
|
90
|
+
return JSON.stringify({
|
|
91
|
+
refused: true,
|
|
92
|
+
code: "restart_cooldown",
|
|
93
|
+
error: `a restart was already requested ${Math.round((nowMs - lastMs) / 60000)} minutes ago; agent-requested restarts are limited to one per 6 hours. Record or report the problem instead.`,
|
|
94
|
+
retryAfterMs: exports.RESTART_COOLDOWN_MS - (nowMs - lastMs),
|
|
95
|
+
});
|
|
96
|
+
}
|
|
12
97
|
(0, runtime_1.emitNervesEvent)({
|
|
13
98
|
component: "repertoire",
|
|
14
99
|
event: "repertoire.runtime_restart_requested",
|
|
@@ -21,6 +106,9 @@ async function restartRuntime(args, agentName) {
|
|
|
21
106
|
reason,
|
|
22
107
|
requestedBy: agentName,
|
|
23
108
|
});
|
|
109
|
+
// Only a daemon that accepted the command starts the cooldown; a failed attempt must not lock the owner out.
|
|
110
|
+
if (response.ok)
|
|
111
|
+
recordRestart(agentName, nowMs);
|
|
24
112
|
return JSON.stringify({
|
|
25
113
|
requested: true,
|
|
26
114
|
reason,
|
|
@@ -110,9 +198,9 @@ exports.runtimeToolDefinitions = [
|
|
|
110
198
|
},
|
|
111
199
|
},
|
|
112
200
|
},
|
|
113
|
-
handler: async (args) => {
|
|
201
|
+
handler: async (args, ctx) => {
|
|
114
202
|
const agentName = (0, identity_1.getAgentName)();
|
|
115
|
-
return restartRuntime({ reason: args.reason }, agentName);
|
|
203
|
+
return restartRuntime({ reason: args.reason }, agentName, ctx);
|
|
116
204
|
},
|
|
117
205
|
riskProfile: { mutates: "external_side_effect", risk: "high", reason: "restarts the hosting runtime" },
|
|
118
206
|
},
|
|
@@ -336,7 +336,7 @@ function createUnraidReadTools(client) {
|
|
|
336
336
|
}
|
|
337
337
|
const emptyParameters = { type: "object", properties: {}, additionalProperties: false };
|
|
338
338
|
function missingRuntime() {
|
|
339
|
-
return JSON.stringify({ ok: false, error: { code: "
|
|
339
|
+
return JSON.stringify({ ok: false, error: { code: "tool_context_missing", message: "Sanctuary tools are not wired into this turn; this is a harness wiring gap, not a runtime outage — do not restart; report it.", degraded: true } });
|
|
340
340
|
}
|
|
341
341
|
function householdRepairObligation(selection, target) {
|
|
342
342
|
if (selection?.kind !== "standing" || selection.requester.kind !== "household_request")
|
|
@@ -46,6 +46,7 @@ exports.enrichDelegatedFromWithBridge = enrichDelegatedFromWithBridge;
|
|
|
46
46
|
exports.routeDelegatedCompletion = routeDelegatedCompletion;
|
|
47
47
|
exports.buildParseErrorNudge = buildParseErrorNudge;
|
|
48
48
|
exports.buildHeldReturnWakeMessage = buildHeldReturnWakeMessage;
|
|
49
|
+
exports.sanctuaryToolContextFor = sanctuaryToolContextFor;
|
|
49
50
|
exports.runPrivateRuntimeTurn = runPrivateRuntimeTurn;
|
|
50
51
|
const fs = __importStar(require("fs"));
|
|
51
52
|
const path = __importStar(require("path"));
|
|
@@ -968,6 +969,28 @@ function reduceHabitSessionToNoSend(habitSession) {
|
|
|
968
969
|
},
|
|
969
970
|
};
|
|
970
971
|
}
|
|
972
|
+
/** Sanctuary tool context for a private turn: always for the sanctuary agent, and for sanctuary external events.
|
|
973
|
+
* Sanctuary external events keep the strict behavior (a missing config fails the turn); other turns degrade to
|
|
974
|
+
* no context so a config gap cannot stop the agent from waking at all. */
|
|
975
|
+
function sanctuaryToolContextFor(agentName, externalEventSource) {
|
|
976
|
+
const sanctuaryEvent = externalEventSource === "sanctuary-health" || externalEventSource === "sanctuary-usenet";
|
|
977
|
+
if (sanctuaryEvent)
|
|
978
|
+
return (0, sanctuary_runtime_1.createSanctuaryToolContext)(agentName);
|
|
979
|
+
if (!(0, sanctuary_runtime_1.isSanctuaryAgent)(agentName))
|
|
980
|
+
return {};
|
|
981
|
+
try {
|
|
982
|
+
return (0, sanctuary_runtime_1.createSanctuaryToolContext)(agentName);
|
|
983
|
+
}
|
|
984
|
+
catch (error) {
|
|
985
|
+
(0, runtime_1.emitNervesEvent)({
|
|
986
|
+
component: "senses",
|
|
987
|
+
event: "senses.sanctuary_tool_context_unavailable",
|
|
988
|
+
message: "sanctuary tool context could not be built for a private turn",
|
|
989
|
+
meta: { agent: agentName, reason: String(error) },
|
|
990
|
+
});
|
|
991
|
+
return {};
|
|
992
|
+
}
|
|
993
|
+
}
|
|
971
994
|
async function runPrivateRuntimeTurn(options) {
|
|
972
995
|
const now = options?.now ?? (() => new Date());
|
|
973
996
|
const reason = options?.reason ?? "instinct";
|
|
@@ -1476,6 +1499,12 @@ async function runPrivateRuntimeTurn(options) {
|
|
|
1476
1499
|
toolContext: {
|
|
1477
1500
|
signin: async () => undefined,
|
|
1478
1501
|
delegatedOrigins: attentionQueue,
|
|
1502
|
+
// Every private-runtime turn is autonomous; restart_runtime refuses to act on one.
|
|
1503
|
+
autonomousTurnKind: options?.externalEvent ? "external-event"
|
|
1504
|
+
: reason === "await" || reason === "habit" || reason === "instinct" ? reason : "scheduler",
|
|
1505
|
+
// The Butler's Sanctuary read tools need the typed runtime on every private turn
|
|
1506
|
+
// (await, habit, instinct, external-event), not only on sanctuary external events.
|
|
1507
|
+
...(sanctuaryToolContextFor(agentName, options?.externalEvent?.source)),
|
|
1479
1508
|
...(relationshipAwait ? {
|
|
1480
1509
|
friendStore: relationshipAwait.store,
|
|
1481
1510
|
context: relationshipAwait.context,
|
|
@@ -1489,7 +1518,6 @@ async function runPrivateRuntimeTurn(options) {
|
|
|
1489
1518
|
relationshipAuthorization: externalEventRelationship.relationshipAuthorization,
|
|
1490
1519
|
externalEventAuthority: externalEventRelationship.externalEventAuthority,
|
|
1491
1520
|
externalEventEffects: externalEventRelationship.externalEventEffects,
|
|
1492
|
-
...(["sanctuary-health", "sanctuary-usenet"].includes(options.externalEvent.source) ? (0, sanctuary_runtime_1.createSanctuaryToolContext)(agentName) : {}),
|
|
1493
1521
|
} : {}),
|
|
1494
1522
|
...(options?.noSend ? { noSend: true } : {}),
|
|
1495
1523
|
...(effectiveHabitSession ? { habitSession: effectiveHabitSession } : {}),
|
|
@@ -33,6 +33,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.isSanctuaryAgent = isSanctuaryAgent;
|
|
36
37
|
exports.runWithSanctuaryToolReceiptCollection = runWithSanctuaryToolReceiptCollection;
|
|
37
38
|
exports.loadSanctuarySabApiKey = loadSanctuarySabApiKey;
|
|
38
39
|
exports.ensureSanctuarySourceRuntimeReady = ensureSanctuarySourceRuntimeReady;
|
|
@@ -60,6 +61,10 @@ const bundle_manifest_1 = require("../mind/bundle-manifest");
|
|
|
60
61
|
const sanctuaryToolReceipts = new node_async_hooks_1.AsyncLocalStorage();
|
|
61
62
|
const sanctuaryToolGroundings = new node_async_hooks_1.AsyncLocalStorage();
|
|
62
63
|
const acceptanceLedgerTails = new Map();
|
|
64
|
+
/** The Butler agent whose private and A2A turns carry the Sanctuary tool context. */
|
|
65
|
+
function isSanctuaryAgent(agentName) {
|
|
66
|
+
return agentName === "sanctuary";
|
|
67
|
+
}
|
|
63
68
|
function isSanctuaryExternalEventSource(source) {
|
|
64
69
|
return source === "sanctuary-health" || source === "sanctuary-usenet";
|
|
65
70
|
}
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ouro.bot/cli",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.862",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@ouro.bot/cli",
|
|
9
|
-
"version": "0.1.0-alpha.
|
|
9
|
+
"version": "0.1.0-alpha.862",
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"@anthropic-ai/sdk": "^0.78.0",
|
|
12
12
|
"@azure/identity": "^4.13.0",
|