@ouro.bot/cli 0.1.0-alpha.744 → 0.1.0-alpha.746

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,19 @@
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.746",
6
+ "changes": [
7
+ "Run packaged Sanctuary auditors with constrained root privileges so root-private inspection and staged policy inputs remain readable without widening file permissions.",
8
+ "Audit immutable container image identity from Docker's resolved top-level Image and validate the portable effective root Mounts contract, including private bind propagation, across real Docker inspect output."
9
+ ]
10
+ },
11
+ {
12
+ "version": "0.1.0-alpha.745",
13
+ "changes": [
14
+ "Tell newly admitted Telegram contacts to resend attachments that stayed safely undownloaded during unknown-sender quarantine."
15
+ ]
16
+ },
4
17
  {
5
18
  "version": "0.1.0-alpha.744",
6
19
  "changes": [
@@ -42,6 +42,7 @@ Effective-spec audit helper:
42
42
  docker image inspect "$AUDIT_EXPECTED_IMAGE" >"$INSPECT_DIR/image.json" || return $?
43
43
  chmod 0600 "$INSPECT_DIR/container.json" "$INSPECT_DIR/image.json" || return $?
44
44
  docker run --rm --pull=never --network=none \
45
+ --user 0:0 --read-only --cap-drop=ALL --security-opt=no-new-privileges \
45
46
  --entrypoint /opt/ouro/deploy/unraid/audit-container-spec.sh \
46
47
  --mount "type=bind,src=$INSPECT_DIR/container.json,dst=/audit/container.json,readonly" \
47
48
  --mount "type=bind,src=$INSPECT_DIR/image.json,dst=/audit/image.json,readonly" \
@@ -1444,6 +1445,7 @@ Update:
1444
1445
  audit the staged template and runtime policy with the packaged auditor, then
1445
1446
  remove the private stage before any Butler mutation:
1446
1447
  docker run --rm --pull=never --network=none \
1448
+ --user 0:0 --read-only --cap-drop=ALL --security-opt=no-new-privileges \
1447
1449
  --entrypoint /opt/ouro/deploy/unraid/audit-container-spec.sh \
1448
1450
  --mount "type=bind,src=$STAGED_TEMPLATE,dst=/audit/sanctuary.xml,readonly" \
1449
1451
  --mount "type=bind,src=$STAGED_RUNTIME_POLICY,dst=/audit/container-runtime.json,readonly" \
@@ -1,5 +1,5 @@
1
1
  {
2
- "runtimeVersion": "0.1.0-alpha.744",
2
+ "runtimeVersion": "0.1.0-alpha.746",
3
3
  "bundleSchemaVersion": 3,
4
4
  "lastUpdated": "2026-08-30T00:00:00.000Z"
5
5
  }
@@ -1,7 +1,7 @@
1
1
  <?xml version="1.0"?>
2
2
  <Container version="2">
3
3
  <Name>Mendelow Cloud Butler</Name>
4
- <Repository>ghcr.io/ourostack/ouroboros-butler:0.1.0-alpha.744</Repository>
4
+ <Repository>ghcr.io/ourostack/ouroboros-butler:0.1.0-alpha.746</Repository>
5
5
  <Registry>https://github.com/ourostack/ouroboros/pkgs/container/ouroboros-butler</Registry>
6
6
  <Network>host</Network>
7
7
  <Shell>sh</Shell>
@@ -10,13 +10,12 @@ const EXPECTED_BINDS = [
10
10
  "/mnt/user/appdata/sabnzbd/sabnzbd.ini:/run/sanctuary/sabnzbd.ini:ro",
11
11
  ];
12
12
  const EXPECTED_MOUNTS = [
13
- ["/mnt/user/appdata/ouro-butler/runtime/.ouro-cli", "/home/ouro/.ouro-cli", true],
14
- ["/mnt/user/appdata/ouro-butler/agent/sanctuary.ouro", "/home/ouro/AgentBundles/sanctuary.ouro", true],
15
- ["/boot/config/custom/ouro-events/spool", "/run/ouro-events", false],
16
- ["/mnt/user/appdata/sabnzbd/sabnzbd.ini", "/run/sanctuary/sabnzbd.ini", false],
13
+ ["/mnt/user/appdata/ouro-butler/runtime/.ouro-cli", "/home/ouro/.ouro-cli", true, "rprivate"],
14
+ ["/mnt/user/appdata/ouro-butler/agent/sanctuary.ouro", "/home/ouro/AgentBundles/sanctuary.ouro", true, "rprivate"],
15
+ ["/boot/config/custom/ouro-events/spool", "/run/ouro-events", false, "rprivate"],
16
+ ["/mnt/user/appdata/sabnzbd/sabnzbd.ini", "/run/sanctuary/sabnzbd.ini", false, "rprivate"],
17
17
  ];
18
18
  const LEGACY_ALPHA742_IMAGE = "sha256:681449ad47a2621705cd339b481e6339236b31dc65e195b1cf5025d0f2191d7d";
19
- const LEGACY_ALPHA742_BINDS = EXPECTED_BINDS.slice(0, 2);
20
19
  const LEGACY_ALPHA742_MOUNTS = EXPECTED_MOUNTS.slice(0, 2);
21
20
  const EXPECTED_EXTRA_PARAMS = "--restart=unless-stopped --user=10001:10001";
22
21
  function record(value) {
@@ -46,9 +45,8 @@ function auditSanctuaryContainerSpec(value, options) {
46
45
  const mountContract = options.mountContract ?? "canonical";
47
46
  if (mountContract === "legacy-alpha742" && options.expectedImage !== LEGACY_ALPHA742_IMAGE)
48
47
  violations.push("legacy mount exception requires the pinned alpha.742 image ID");
49
- const expectedBinds = mountContract === "legacy-alpha742" ? LEGACY_ALPHA742_BINDS : EXPECTED_BINDS;
50
48
  const expectedMounts = mountContract === "legacy-alpha742" ? LEGACY_ALPHA742_MOUNTS : EXPECTED_MOUNTS;
51
- if (config.Image !== options.expectedImage)
49
+ if (root.Image !== options.expectedImage)
52
50
  violations.push("image does not match the reviewed exact local Docker image ID");
53
51
  if (root.Path !== "node")
54
52
  violations.push("effective container path must be node");
@@ -92,14 +90,11 @@ function auditSanctuaryContainerSpec(value, options) {
92
90
  violations.push("container must not publish all exposed ports");
93
91
  if (!isEmptyRecord(network?.Ports))
94
92
  violations.push("effective network ports must be empty");
95
- const binds = stringArray(host.Binds);
96
- if (!binds || JSON.stringify([...binds].sort()) !== JSON.stringify([...expectedBinds].sort()))
97
- violations.push("bind set does not match the selected Sanctuary mount contract");
98
93
  const mounts = Array.isArray(root.Mounts) ? root.Mounts : [];
99
94
  const normalizedMounts = mounts.map((mount) => {
100
95
  const item = record(mount);
101
- return item && item.Type === "bind" && typeof item.RW === "boolean" && typeof item.Source === "string" && typeof item.Destination === "string"
102
- ? [item.Source, item.Destination, item.RW]
96
+ return item && item.Type === "bind" && typeof item.RW === "boolean" && typeof item.Source === "string" && typeof item.Destination === "string" && item.Propagation === "rprivate"
97
+ ? [item.Source, item.Destination, item.RW, item.Propagation]
103
98
  : null;
104
99
  });
105
100
  if (normalizedMounts.some((mount) => mount === null)
@@ -46,6 +46,7 @@ const telegram_client_1 = require("./telegram-client");
46
46
  exports.NEWLY_ADMITTED_ORIENTATION = {
47
47
  kind: "newly_admitted",
48
48
  instruction: "welcome_and_explain_capabilities",
49
+ attachmentsNeedResend: false,
49
50
  };
50
51
  const ACTIVE_STATUSES = new Set([
51
52
  "pending", "approved", "friend_bound", "ingress_committed", "turn_queued",
@@ -562,7 +563,10 @@ function createTelegramAdmissionController(options) {
562
563
  hasAttachments: record.hasAttachments,
563
564
  synthetic: false,
564
565
  });
565
- const committed = { ...committedBase, orientation: exports.NEWLY_ADMITTED_ORIENTATION };
566
+ const committed = {
567
+ ...committedBase,
568
+ orientation: { ...exports.NEWLY_ADMITTED_ORIENTATION, attachmentsNeedResend: record.hasAttachments },
569
+ };
566
570
  if (committed.admissionId !== record.id || committed.friendId !== record.friendId || committed.reference !== `telegram-admission:${record.id}`) {
567
571
  throw new Error("Telegram admission committed ingress changed identity");
568
572
  }
@@ -574,7 +578,14 @@ function createTelegramAdmissionController(options) {
574
578
  }
575
579
  if (!record.friendId || !record.ingressSessionKey || !record.ingressEventId || !record.ingressReference)
576
580
  throw new Error("Telegram admission queued work lost its durable reference");
577
- const settlement = await options.dispatchApprovedWork({ admissionId: record.id, friendId: record.friendId, sessionKey: record.ingressSessionKey, eventId: record.ingressEventId, reference: record.ingressReference, orientation: exports.NEWLY_ADMITTED_ORIENTATION });
581
+ const settlement = await options.dispatchApprovedWork({
582
+ admissionId: record.id,
583
+ friendId: record.friendId,
584
+ sessionKey: record.ingressSessionKey,
585
+ eventId: record.ingressEventId,
586
+ reference: record.ingressReference,
587
+ orientation: { ...exports.NEWLY_ADMITTED_ORIENTATION, attachmentsNeedResend: record.hasAttachments },
588
+ });
578
589
  if (settlement === "indeterminate") {
579
590
  await compensateFriend(record);
580
591
  return options.store.compareAndSwap({ admissionId: record.id, expectedStatus: "turn_queued", nextStatus: "indeterminate" });
@@ -1094,7 +1094,9 @@ function createTelegramSenseApp(options) {
1094
1094
  source: {
1095
1095
  kind: "telegram_newly_admitted",
1096
1096
  authority: "presentation_only",
1097
- routingHint: "This is this person’s first admitted turn. Welcome them warmly and briefly explain what the household Butler can help with before answering their request.",
1097
+ routingHint: input.orientation.attachmentsNeedResend
1098
+ ? "This is this person’s first admitted turn. Welcome them warmly and briefly explain what the household Butler can help with before answering their request. Their original message included attachments that were not downloaded; ask them to resend those attachments now."
1099
+ : "This is this person’s first admitted turn. Welcome them warmly and briefly explain what the household Butler can help with before answering their request.",
1098
1100
  },
1099
1101
  }
1100
1102
  : undefined;
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@ouro.bot/cli",
3
- "version": "0.1.0-alpha.744",
3
+ "version": "0.1.0-alpha.746",
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.744",
9
+ "version": "0.1.0-alpha.746",
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.744",
3
+ "version": "0.1.0-alpha.746",
4
4
  "engines": {
5
5
  "node": ">=22"
6
6
  },