@ouro.bot/cli 0.1.0-alpha.840 → 0.1.0-alpha.842

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.842",
6
+ "changes": [
7
+ "A2A: a verified friend's turn with the Sanctuary agent gets the same typed Sanctuary runtime the Telegram sense attaches, so the Butler's house tools work over A2A (every Unraid read had answered 'Sanctuary runtime is unavailable'). The relationship profile still decides which tools the peer may call."
8
+ ]
9
+ },
10
+ {
11
+ "version": "0.1.0-alpha.841",
12
+ "changes": [
13
+ "Sanctuary: the root lifecycle accepts the host keeper as the boot owner (D-045), so staging and retirement no longer refuse on a keeper-managed Unraid host; the upgrade orchestrator prunes old Butler images after a successful upgrade, keeping the live and rollback images (D-046: docker.img had reached 88%)."
14
+ ]
15
+ },
4
16
  {
5
17
  "version": "0.1.0-alpha.840",
6
18
  "changes": [
@@ -823,6 +823,21 @@ function pinTemplate(version) {
823
823
  ok(`DockerMan template pins ${image(version)}`)
824
824
  }
825
825
 
826
+ // Every release pulls a ~1 GB Butler image, and Unraid's docker.img filled to 88% with
827
+ // twenty of them (D-046). After a successful upgrade keep only the live image and the
828
+ // authority's recorded rollback image; Docker itself refuses to remove an image in use.
829
+ function pruneButlerImages() {
830
+ const activation = JSON.parse(readFileSync(`${ROOT}/activation.json`, "utf8"))
831
+ const keep = new Set([activation.targetImageId, activation.rollbackImageId])
832
+ let removed = 0
833
+ for (const line of docker(["images", "--no-trunc", "--format", "{{.ID}} {{.Tag}}", "ghcr.io/ourostack/ouroboros-butler"]).trim().split("\n").filter(Boolean)) {
834
+ const [id, tag] = line.split(" ")
835
+ if (keep.has(id) || !tag || tag === "<none>") continue
836
+ try { docker(["rmi", `ghcr.io/ourostack/ouroboros-butler:${tag}`], { stdio: ["ignore", "pipe", "pipe"] }); removed += 1 } catch { /* in use */ }
837
+ }
838
+ ok(`pruned ${removed} old Butler image(s); kept the live and rollback images`)
839
+ }
840
+
826
841
  function lifecycleFailure(e) {
827
842
  const detail = [e.stderr, e.stdout].map((x) => (x ? String(x).trim() : "")).filter(Boolean).join(" | ")
828
843
  let last = ""
@@ -871,7 +886,7 @@ function upgrade(version, rehearse) {
871
886
  resumeSupervision()
872
887
  fail(`ROLLBACK FAILED: ${lifecycleFailure(e)}\nThe journal is kept; the next authority boot (or \`upgrade-rollback\`) retries it.`)
873
888
  }
874
- } else pinTemplate(version)
889
+ } else { pinTemplate(version); pruneButlerImages() }
875
890
  resumeSupervision()
876
891
  say("preservation")
877
892
  docker(["inspect", "jellyfin", "--format", "{{.Id}}|{{.Image}}|{{.RestartCount}}|{{.State.StartedAt}}"]).trim() === jellyfinBefore ? ok("jellyfin unchanged") : fail("JELLYFIN CHANGED")
@@ -1,5 +1,5 @@
1
1
  {
2
- "runtimeVersion": "0.1.0-alpha.840",
2
+ "runtimeVersion": "0.1.0-alpha.842",
3
3
  "bundleSchemaVersion": 3,
4
4
  "lastUpdated": "2026-09-03T00:00:00.000Z"
5
5
  }
@@ -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.840</Repository>
4
+ <Repository>ghcr.io/ourostack/ouroboros-butler:0.1.0-alpha.842</Repository>
5
5
  <Registry>https://github.com/ourostack/ouroboros/pkgs/container/ouroboros-butler</Registry>
6
6
  <Network>host</Network>
7
7
  <Shell>sh</Shell>
@@ -48,6 +48,7 @@ const pin_store_1 = require("./pin-store");
48
48
  const seen_ledger_1 = require("./seen-ledger");
49
49
  const did_resolution_1 = require("./did-resolution");
50
50
  const inbound_share_1 = require("./inbound-share");
51
+ const sanctuary_runtime_1 = require("../senses/sanctuary-runtime");
51
52
  const sealed_chat_1 = require("./sealed-chat");
52
53
  const relationship_authorization_1 = require("../repertoire/relationship-authorization");
53
54
  const mission_result_wire_1 = require("./mission-result-wire");
@@ -158,7 +159,14 @@ async function defaultTurnRunner(input) {
158
159
  externalId: input.peerAgentId,
159
160
  displayName: input.peerName,
160
161
  },
161
- ...(input.relationshipAuthorization ? { toolContext: { relationshipAuthorization: input.relationshipAuthorization } } : {}),
162
+ // The Butler's house tools read a typed Sanctuary runtime from the tool context; the
163
+ // Telegram sense attaches it, and a verified A2A friend's turn needs it too (without it
164
+ // every Unraid read answered "Sanctuary runtime is unavailable"). The relationship
165
+ // profile still decides which of those tools the peer may call.
166
+ toolContext: {
167
+ ...(input.agentName === "sanctuary" ? (0, sanctuary_runtime_1.createSanctuaryToolContext)(input.agentName) : {}),
168
+ ...(input.relationshipAuthorization ? { relationshipAuthorization: input.relationshipAuthorization } : {}),
169
+ },
162
170
  });
163
171
  return { response: result.response };
164
172
  }
@@ -34,7 +34,7 @@ var __importStar = (this && this.__importStar) || (function () {
34
34
  })();
35
35
  var _a;
36
36
  Object.defineProperty(exports, "__esModule", { value: true });
37
- exports.SanctuaryAuthorityRootLifecycle = exports.SANCTUARY_UPGRADE_STEPS = void 0;
37
+ exports.SanctuaryAuthorityRootLifecycle = exports.SANCTUARY_UPGRADE_STEPS = exports.SANCTUARY_KEEPER_BOOT_LINES = void 0;
38
38
  exports.recordSanctuaryRootLifecycleFailure = recordSanctuaryRootLifecycleFailure;
39
39
  exports.runSanctuaryAuthorityRootCli = runSanctuaryAuthorityRootCli;
40
40
  const node_child_process_1 = require("node:child_process");
@@ -58,6 +58,16 @@ const STAGING = "/var/lib/ouro-authority/staging";
58
58
  const CGROUP = "/sys/fs/cgroup/ouro-authority";
59
59
  const BOOT = "/boot/config/custom/ouro-authority/start.sh";
60
60
  const BOOT_LINE = `/bin/sh ${BOOT} --boot & # ouro-authority`;
61
+ // On Unraid the host keeper owns boot instead (it runs BOOT --boot itself, D-032), and the
62
+ // upgrade orchestrator installs exactly these lines in place of BOOT_LINE (D-045).
63
+ const KEEPER_SUPERVISOR = "/boot/config/custom/ouro-authority/gateway-supervisor.sh";
64
+ const KEEPER_WATCHDOG = "/boot/config/custom/ouro-authority/gateway-keeper-watchdog.sh";
65
+ exports.SANCTUARY_KEEPER_BOOT_LINES = {
66
+ supervisor: `setsid /bin/sh ${KEEPER_SUPERVISOR} >/dev/null 2>&1 & # ouro-authority-gateway`,
67
+ watchdog: `(crontab -l 2>/dev/null | grep -v gateway-keeper-watchdog; echo "*/2 * * * * /bin/sh ${KEEPER_WATCHDOG} # ouro-authority-gateway-watchdog") | crontab - # ouro-authority-gateway-watchdog`,
68
+ };
69
+ const BOOT_OWNERS = [BOOT_LINE, exports.SANCTUARY_KEEPER_BOOT_LINES.supervisor];
70
+ const KNOWN_BOOT_LINES = [...BOOT_OWNERS, exports.SANCTUARY_KEEPER_BOOT_LINES.watchdog];
61
71
  const DIGEST = /^sha256:[a-f0-9]{64}$/u;
62
72
  // The gateway verifies every pinned package file before it reports ready. With a cold
63
73
  // page cache on Unraid's array that takes minutes, so readiness gets a generous budget
@@ -293,7 +303,7 @@ class SanctuaryAuthorityRootLifecycle {
293
303
  return false;
294
304
  this.#record(`${this.#epochRoot()}/stage.json`, { packageDigest: this.#request.packageDigest });
295
305
  this.#verifyPackage(undefined, hostExecution);
296
- if (this.#bootScript().text.split("\n").filter((line) => line === BOOT_LINE).length !== 1
306
+ if (this.#bootScript().text.split("\n").filter((line) => BOOT_OWNERS.includes(line)).length !== 1
297
307
  || digest(this.#private(BOOT)) !== digest(fs.readFileSync(this.#p(`${ROOT}/package/deploy/unraid/sanctuary-authority-service.sh`))))
298
308
  throw new Error("Sanctuary installed boot lifecycle changed");
299
309
  return true;
@@ -379,10 +389,10 @@ class SanctuaryAuthorityRootLifecycle {
379
389
  }
380
390
  this.#verifyPackage(undefined, hostExecution);
381
391
  const { text: boot, mode: bootMode } = this.#bootScript();
382
- if (boot.split("\n").some((line) => line.includes("ouro-authority") && line !== BOOT_LINE))
392
+ if (boot.split("\n").some((line) => line.includes("ouro-authority") && !KNOWN_BOOT_LINES.includes(line)))
383
393
  throw new Error("Sanctuary boot ownership is ambiguous");
384
394
  this.#write(BOOT, fs.readFileSync(this.#p(`${ROOT}/package/deploy/unraid/sanctuary-authority-service.sh`), "utf8"));
385
- if (!boot.split("\n").includes(BOOT_LINE))
395
+ if (!boot.split("\n").some((line) => BOOT_OWNERS.includes(line)))
386
396
  this.#write("/boot/config/go", `${boot}${boot.endsWith("\n") ? "" : "\n"}${BOOT_LINE}\n`, bootMode);
387
397
  this.#write(`${this.#epochRoot()}/stage.json`, JSON.stringify({ packageDigest: this.#request.packageDigest }));
388
398
  }
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@ouro.bot/cli",
3
- "version": "0.1.0-alpha.840",
3
+ "version": "0.1.0-alpha.842",
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.840",
9
+ "version": "0.1.0-alpha.842",
10
10
  "dependencies": {
11
11
  "@anthropic-ai/sdk": "^0.78.0",
12
12
  "@azure/identity": "^4.13.0",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ouro.bot/cli",
3
- "version": "0.1.0-alpha.840",
3
+ "version": "0.1.0-alpha.842",
4
4
  "engines": {
5
5
  "node": ">=22"
6
6
  },