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

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.
Files changed (71) hide show
  1. package/changelog.json +32 -0
  2. package/deploy/unraid/Dockerfile +7 -2
  3. package/deploy/unraid/README.txt +704 -64
  4. package/deploy/unraid/migrate-sanctuary-bundle.mjs +21 -0
  5. package/deploy/unraid/ouro-events/bootstrap-spool.sh +92 -0
  6. package/deploy/unraid/ouro-events/emit-event.mjs +310 -0
  7. package/deploy/unraid/ouro-events/emit-usenet-event.sh +54 -0
  8. package/deploy/unraid/ouro-events/install-usenet-guard.sh +434 -0
  9. package/deploy/unraid/ouro-events/usenet-health.sh +178 -0
  10. package/deploy/unraid/sanctuary-acceptance-contract.json +4 -3
  11. package/deploy/unraid/sanctuary-unit16-host-broker.mjs +5 -1
  12. package/deploy/unraid/sanctuary.ouro/bundle-meta.json +2 -2
  13. package/deploy/unraid/sanctuary.ouro/habits/sanctuary-health.md +1 -1
  14. package/deploy/unraid/sanctuary.ouro/psyche/ASPIRATIONS.md +3 -1
  15. package/deploy/unraid/sanctuary.ouro/psyche/IDENTITY.md +3 -1
  16. package/deploy/unraid/sanctuary.ouro/psyche/LORE.md +4 -0
  17. package/deploy/unraid/sanctuary.ouro/psyche/SOUL.md +6 -2
  18. package/deploy/unraid/sanctuary.ouro/psyche/TACIT.md +4 -2
  19. package/deploy/unraid/sanctuary.ouro/state/policy/steward.json +7 -0
  20. package/deploy/unraid/sanctuary.ouro/tool-profiles.json +19 -17
  21. package/deploy/unraid/sanctuary.xml +13 -10
  22. package/dist/arc/cares.js +171 -37
  23. package/dist/arc/obligations.js +38 -0
  24. package/dist/heart/awaiting/await-alert.js +4 -2
  25. package/dist/heart/awaiting/await-expiry.js +39 -4
  26. package/dist/heart/awaiting/await-parser.js +8 -0
  27. package/dist/heart/core.js +37 -7
  28. package/dist/heart/cross-chat-delivery.js +1 -0
  29. package/dist/heart/daemon/container-spec-auditor-main.js +11 -7
  30. package/dist/heart/daemon/container-spec-auditor.js +38 -11
  31. package/dist/heart/daemon/daemon-entry.js +19 -5
  32. package/dist/heart/daemon/daemon.js +154 -22
  33. package/dist/heart/daemon/sanctuary-acceptance-adapter.js +126 -42
  34. package/dist/heart/daemon/sanctuary-acceptance-harness.js +26 -29
  35. package/dist/heart/daemon/sanctuary-acceptance-scenarios.js +42 -51
  36. package/dist/heart/daemon/sanctuary-bundle-migration.js +398 -0
  37. package/dist/heart/external-events/router.js +1025 -43
  38. package/dist/heart/session-events.js +23 -5
  39. package/dist/heart/steward-policy.js +302 -0
  40. package/dist/heart/tool-approval.js +3 -1
  41. package/dist/heart/turn-context.js +1 -0
  42. package/dist/mind/context.js +8 -3
  43. package/dist/mind/pending.js +25 -0
  44. package/dist/mind/prompt.js +49 -1
  45. package/dist/repertoire/guardrails.js +2 -1
  46. package/dist/repertoire/relationship-authorization.js +194 -0
  47. package/dist/repertoire/tools-awaiting.js +229 -26
  48. package/dist/repertoire/tools-base.js +15 -0
  49. package/dist/repertoire/tools-continuity.js +259 -4
  50. package/dist/repertoire/tools-notes.js +29 -3
  51. package/dist/repertoire/tools-session.js +219 -4
  52. package/dist/repertoire/tools-steward-policy.js +88 -0
  53. package/dist/repertoire/tools-telegram-contacts.js +37 -0
  54. package/dist/repertoire/tools-unraid.js +113 -6
  55. package/dist/repertoire/tools.js +104 -9
  56. package/dist/repertoire/unraid-restart.js +86 -4
  57. package/dist/senses/private-runtime-worker.js +36 -3
  58. package/dist/senses/private-runtime.js +310 -63
  59. package/dist/senses/sanctuary-health-acceptance-probe.js +8 -6
  60. package/dist/senses/sanctuary-health-runner.js +47 -103
  61. package/dist/senses/sanctuary-health.js +53 -105
  62. package/dist/senses/sanctuary-runtime.js +36 -2
  63. package/dist/senses/sanctuary-sab.js +80 -0
  64. package/dist/senses/shared-turn.js +64 -9
  65. package/dist/senses/telegram-admission.js +684 -0
  66. package/dist/senses/telegram-approval-runtime.js +46 -6
  67. package/dist/senses/telegram-client.js +212 -66
  68. package/dist/senses/telegram-effect-adapter.js +787 -0
  69. package/dist/senses/telegram.js +728 -48
  70. package/npm-shrinkwrap.json +6 -6
  71. package/package.json +2 -2
@@ -55,15 +55,20 @@ const node_crypto_1 = require("node:crypto");
55
55
  const node_fs_1 = require("node:fs");
56
56
  const node_net_1 = require("node:net");
57
57
  const path = __importStar(require("node:path"));
58
+ const friends_1 = require("@ouro.bot/friends");
58
59
  const runtime_1 = require("../../nerves/runtime");
59
60
  const telegram_approval_runtime_1 = require("../../senses/telegram-approval-runtime");
60
61
  const telegram_client_1 = require("../../senses/telegram-client");
62
+ const telegram_effect_adapter_1 = require("../../senses/telegram-effect-adapter");
63
+ const telegram_admission_1 = require("../../senses/telegram-admission");
61
64
  const sanctuary_interactive_control_1 = require("../../senses/sanctuary-interactive-control");
65
+ const shared_turn_1 = require("../../senses/shared-turn");
62
66
  const telegram_1 = require("../../senses/telegram");
63
67
  const telegram_audit_ledger_1 = require("../../senses/telegram-audit-ledger");
64
68
  const sanctuary_runtime_1 = require("../../senses/sanctuary-runtime");
65
69
  const sanctuary_grounding_1 = require("../../senses/sanctuary-grounding");
66
70
  const tools_1 = require("../../repertoire/tools");
71
+ const relationship_authorization_1 = require("../../repertoire/relationship-authorization");
67
72
  const core_1 = require("../core");
68
73
  const identity_1 = require("../identity");
69
74
  const approval_store_1 = require("../approval-store");
@@ -104,7 +109,6 @@ const POSTBOOT_HEALTH_FILE = "/run/ouro-acceptance/postboot-health.json";
104
109
  const BOOT_ID_FILE = "/run/ouro-acceptance/boot-id";
105
110
  const TELEGRAM_POLLER_COUNT_FILE = "/run/ouro-acceptance/telegram-poller-count.json";
106
111
  const CONTRACT_FILE = "/opt/ouro/deploy/unraid/sanctuary-acceptance-contract.json";
107
- const SANCTUARY_TOOL_PROFILES_FILE = "/opt/ouro/deploy/unraid/sanctuary.ouro/tool-profiles.json";
108
112
  const CLOSED_INVENTORY_FILE = "/run/ouro-acceptance/closed-inventory.json";
109
113
  const HOST_BROKER_SOCKET = "/run/ouro-host-acceptance/adapter.sock";
110
114
  const MAX_BROKER_RESPONSE = 256 * 1024;
@@ -118,7 +122,7 @@ const APPROVAL_EVIDENCE_LABELS = new Set([
118
122
  const HEALTH_ACCEPTANCE_LABELS = new Set([
119
123
  "unit-16f-cron-fingerprint",
120
124
  "unit-16g-health-transition",
121
- "unit-16h-daily-digest",
125
+ "unit-16h-acceptance-delivery-probe",
122
126
  ]);
123
127
  const PERMISSION_RESOURCES = new Set([
124
128
  "ACTIVATION_CODE", "API_KEY", "ARRAY", "CLOUD", "CONFIG", "CONNECT", "CONNECT__REMOTE_ACCESS",
@@ -505,6 +509,67 @@ function parsedJson(raw) {
505
509
  return null;
506
510
  return object(JSON.parse(raw), "scenario source");
507
511
  }
512
+ async function projectTelegramAdmissions(agentRoot, credentials, identityKey) {
513
+ const admissionsRoot = path.join(agentRoot, "state", "senses", "telegram", "admissions");
514
+ const effectsRoot = path.join(agentRoot, "state", "telegram", "effects");
515
+ if (!(0, node_fs_1.existsSync)(admissionsRoot) || !(0, node_fs_1.existsSync)(effectsRoot) || !identityKey || !/^[A-Za-z0-9_-]{43}$/u.test(identityKey))
516
+ return [];
517
+ const admissionStore = new telegram_admission_1.FileTelegramAdmissionStore(admissionsRoot);
518
+ const effectStore = new telegram_effect_adapter_1.FileTelegramEffectJournal(effectsRoot);
519
+ try {
520
+ const effects = new Map(effectStore.list().map((artifact) => [artifact.id, artifact]));
521
+ const botId = (0, telegram_1.telegramBotIdFromToken)(credentials.botToken);
522
+ const ownerSessionKey = `telegram:${(0, telegram_1.opaqueTelegramSubject)(identityKey, botId, credentials.authorizedUserId, credentials.authorizedChatId)}`;
523
+ const friendsRoot = path.join(agentRoot, "friends");
524
+ const friendStore = (0, node_fs_1.existsSync)(friendsRoot) ? new friends_1.FileFriendStore(friendsRoot) : null;
525
+ const owner = friendStore ? await friendStore.findByExternalId("telegram-user", credentials.authorizedUserId, botId) : null;
526
+ const exactSameBotIdentity = (friend, userId) => {
527
+ const bindings = friend.externalIds.filter((external) => external.provider === "telegram-user" && external.tenantId === botId);
528
+ return bindings.length === 1 && bindings[0].externalId === userId;
529
+ };
530
+ const ownerExact = Boolean(owner && exactSameBotIdentity(owner, credentials.authorizedUserId) && owner.trustLevel === "family" && owner.admissionState === "active"
531
+ && owner.initiativePolicy === "proactive" && owner.capabilityProfileId === "sanctuary-owner");
532
+ const projections = await Promise.all(admissionStore.list().map(async (record) => {
533
+ const acknowledgement = record.acknowledgementArtifactId ? effects.get(record.acknowledgementArtifactId) : undefined;
534
+ const ownerCard = record.ownerCardArtifactId ? effects.get(record.ownerCardArtifactId) : undefined;
535
+ const expectedButtons = [[
536
+ { text: "Allow", callbackData: `admit:${record.id}:allow` },
537
+ { text: "Deny", callbackData: `admit:${record.id}:deny` },
538
+ { text: "Block", callbackData: `admit:${record.id}:block` },
539
+ ]];
540
+ const admittedFriend = friendStore ? await friendStore.findByExternalId("telegram-user", record.userId, record.botId) : null;
541
+ const acknowledgementExact = Boolean(acknowledgement?.idempotencyKey === `ack:${record.id}`
542
+ && acknowledgement.authorClass === "control" && acknowledgement.target.kind === "admission_gate"
543
+ && acknowledgement.target.admissionId === record.id && acknowledgement.target.botId === record.botId
544
+ && acknowledgement.target.userId === record.userId && acknowledgement.target.chatId === record.chatId
545
+ && acknowledgement.effect.kind === "admission_ack" && acknowledgement.effect.text === telegram_effect_adapter_1.FIXED_ADMISSION_ACKNOWLEDGEMENT
546
+ && acknowledgement.parts.every((part) => part.state === "accepted" || part.state === "session_recorded"));
547
+ const ownerCardExact = Boolean(ownerExact && owner && ownerCard?.idempotencyKey === `owner-card:${record.id}`
548
+ && ownerCard.authorClass === "control" && ownerCard.target.kind === "approved_relationship"
549
+ && ownerCard.target.friendId === owner.id && ownerCard.target.sessionKey === ownerSessionKey && ownerCard.target.requestId === record.id
550
+ && ownerCard.effect.kind === "card" && ownerCard.effect.text.startsWith("Unknown Telegram contact\n")
551
+ && ownerCard.effect.text.includes("The message is quarantined and has not been processed.")
552
+ && JSON.stringify(ownerCard.effect.buttons) === JSON.stringify(expectedButtons)
553
+ && ownerCard.parts.every((part) => part.state === "session_recorded"));
554
+ return {
555
+ admissionDigest: (0, telegram_1.sanctuaryTelegramTurnReceiptDigest)(identityKey, "sanctuary-telegram-turn-receipt-v3", "admission", `${record.id}\0${record.botId}\0${record.userId}\0${record.chatId}\0${record.updateId}\0${record.messageId}`),
556
+ status: record.status,
557
+ identityExact: record.botId === botId && record.userId === record.chatId && record.userId !== credentials.authorizedUserId,
558
+ acknowledgementExact,
559
+ ownerCardExact,
560
+ contentQuarantined: typeof record.quarantinedText === "string" && (0, node_crypto_1.createHash)("sha256").update(record.quarantinedText).digest("hex") === record.contentDigest,
561
+ relationshipAbsent: record.friendId === null && admittedFriend === null,
562
+ capturedAt: record.createdAt,
563
+ updatedAt: record.updatedAt,
564
+ };
565
+ }));
566
+ return projections.sort((left, right) => left.admissionDigest.localeCompare(right.admissionDigest));
567
+ }
568
+ finally {
569
+ admissionStore.close();
570
+ effectStore.close();
571
+ }
572
+ }
508
573
  function canonicalIso(value) {
509
574
  return typeof value === "string" && value.length <= 30 && Number.isFinite(Date.parse(value)) && new Date(Date.parse(value)).toISOString() === value;
510
575
  }
@@ -611,10 +676,10 @@ function parseHealthProbeReceipt(raw, label, scenarioHandleDigest, identityKey)
611
676
  if (Buffer.byteLength(raw) > 128 * 1024)
612
677
  throw new Error("Sanctuary health probe receipt exceeds its bound");
613
678
  const receipt = object(JSON.parse(raw), "Sanctuary health probe receipt");
614
- const exactKeys = ["beforeStateDigest", "clockMode", "cronDegradedAfter", "cronDegradedBefore", "cronFingerprintAfter", "cronFingerprintBefore", "cronRegisteredAfter", "cronRegisteredBefore", "deliveryCount", "effectiveNow", "fixtureSequenceDigest", "label", "ownerContainerDigestAfter", "ownerContainerDigestBefore", "ownerImageDigestAfter", "ownerImageDigestBefore", "phases", "privateTurnCount", "productionRestored", "providerInvocationCount", "realCheckEquivalent", "restoredStateDigest", "scenarioHandleDigest", "schedulerReceipt", "schemaVersion", "snapshotAbsent", "socketAbsent", "workspaceAbsent"].sort();
615
- const supportedLabel = label === "unit-16f-cron-fingerprint" || label === "unit-16g-health-transition" || label === "unit-16h-daily-digest";
679
+ const exactKeys = ["acceptanceOnly", "beforeStateDigest", "clockMode", "cronDegradedAfter", "cronDegradedBefore", "cronFingerprintAfter", "cronFingerprintBefore", "cronRegisteredAfter", "cronRegisteredBefore", "deliveryCount", "effectiveNow", "fixtureSequenceDigest", "label", "ownerContainerDigestAfter", "ownerContainerDigestBefore", "ownerImageDigestAfter", "ownerImageDigestBefore", "phases", "privateTurnCount", "productionRestored", "productionScheduleChanged", "providerInvocationCount", "realCheckEquivalent", "restoredStateDigest", "scenarioHandleDigest", "schedulerReceipt", "schemaVersion", "snapshotAbsent", "socketAbsent", "workspaceAbsent"].sort();
680
+ const supportedLabel = label === "unit-16f-cron-fingerprint" || label === "unit-16g-health-transition" || label === "unit-16h-acceptance-delivery-probe";
616
681
  const digestFields = ["ownerImageDigestBefore", "ownerImageDigestAfter", "ownerContainerDigestBefore", "ownerContainerDigestAfter", "beforeStateDigest", "restoredStateDigest", "cronFingerprintBefore", "cronFingerprintAfter", "fixtureSequenceDigest"];
617
- const booleanFields = ["cronRegisteredBefore", "cronRegisteredAfter", "cronDegradedBefore", "cronDegradedAfter", "workspaceAbsent", "socketAbsent", "snapshotAbsent", "realCheckEquivalent", "productionRestored"];
682
+ const booleanFields = ["acceptanceOnly", "productionScheduleChanged", "cronRegisteredBefore", "cronRegisteredAfter", "cronDegradedBefore", "cronDegradedAfter", "workspaceAbsent", "socketAbsent", "snapshotAbsent", "realCheckEquivalent", "productionRestored"];
618
683
  if (!supportedLabel || JSON.stringify(Object.keys(receipt).sort()) !== JSON.stringify(exactKeys) || receipt.schemaVersion !== "sanctuary-health-probe-receipt-v1"
619
684
  || receipt.label !== label || receipt.scenarioHandleDigest !== scenarioHandleDigest || !SHA256.test(scenarioHandleDigest)
620
685
  || !digestFields.every((field) => typeof receipt[field] === "string" && SHA256.test(receipt[field]))
@@ -689,10 +754,10 @@ function parseHealthProbeReceipt(raw, label, scenarioHandleDigest, identityKey)
689
754
  if (receipt.ownerImageDigestBefore !== receipt.ownerImageDigestAfter || receipt.ownerContainerDigestBefore !== receipt.ownerContainerDigestAfter
690
755
  || (label !== "unit-16f-cron-fingerprint" && receipt.beforeStateDigest !== receipt.restoredStateDigest) || receipt.cronFingerprintBefore !== receipt.cronFingerprintAfter
691
756
  || receipt.cronRegisteredBefore !== true || receipt.cronRegisteredAfter !== true || receipt.cronDegradedBefore !== false || receipt.cronDegradedAfter !== false
692
- || receipt.workspaceAbsent !== true || receipt.socketAbsent !== true || receipt.snapshotAbsent !== true || receipt.realCheckEquivalent !== true || receipt.productionRestored !== true) {
757
+ || receipt.acceptanceOnly !== true || receipt.productionScheduleChanged !== false || receipt.workspaceAbsent !== true || receipt.socketAbsent !== true || receipt.snapshotAbsent !== true || receipt.realCheckEquivalent !== true || receipt.productionRestored !== true) {
693
758
  throw new Error("Sanctuary health probe did not restore its exact production owner and state");
694
759
  }
695
- if (label === "unit-16h-daily-digest") {
760
+ if (label === "unit-16h-acceptance-delivery-probe") {
696
761
  const effective = new Date(receipt.effectiveNow);
697
762
  const parts = new Intl.DateTimeFormat("en-US", { timeZone: "America/Los_Angeles", hour: "2-digit", minute: "2-digit", second: "2-digit", hourCycle: "h23" }).formatToParts(effective);
698
763
  const part = (type) => Number(parts.find((entry) => entry.type === type).value);
@@ -1170,9 +1235,6 @@ async function readDefaultSanctuaryScenarioFacts(label, scenarioHandleDigest, de
1170
1235
  identityKey: identityRaw.trim(),
1171
1236
  });
1172
1237
  const auditEntries = auditLedgerEntries.filter((entry) => entry.meta.scenarioHandleDigest === scenarioHandleDigest);
1173
- const toolProfiles = parsedJson(optionalFixedFile(deps, SANCTUARY_TOOL_PROFILES_FILE));
1174
- const profiles = toolProfiles ? object(toolProfiles.profiles, "Sanctuary tool profiles") : null;
1175
- const telegramProfile = profiles?.["sanctuary-telegram"];
1176
1238
  let approvalRecords = [];
1177
1239
  const approvalDatabasePath = pathFor(agentRoot, "state/approvals/approvals.sqlite");
1178
1240
  if ((0, node_fs_1.existsSync)(approvalDatabasePath))
@@ -1254,30 +1316,6 @@ async function readDefaultSanctuaryScenarioFacts(label, scenarioHandleDigest, de
1254
1316
  throw new Error("Telegram audit lifecycle MAC is invalid");
1255
1317
  }
1256
1318
  }
1257
- if (label === "unit-16d-2-unauthorized") {
1258
- const expectedAuthorizedIdentityDigest = (0, telegram_1.sanctuaryTelegramTurnReceiptDigest)(identityKey, auditSchema, "sender-identity", credentials.authorizedUserId);
1259
- for (const entry of auditEntries.filter((candidate) => candidate.event === "telegram.update_dropped")) {
1260
- const binding = {
1261
- scenarioHandleDigest: entry.meta.scenarioHandleDigest,
1262
- updateDigest: entry.meta.updateDigest,
1263
- senderIdentityDigest: entry.meta.senderIdentityDigest,
1264
- authorizedIdentityDigest: entry.meta.authorizedIdentityDigest,
1265
- senderDistinct: entry.meta.senderDistinct,
1266
- nextOffsetDigest: entry.meta.nextOffsetDigest,
1267
- };
1268
- if (typeof binding.updateDigest !== "string" || !SHA256.test(binding.updateDigest)
1269
- || typeof binding.senderIdentityDigest !== "string" || !SHA256.test(binding.senderIdentityDigest)
1270
- || typeof binding.authorizedIdentityDigest !== "string" || !SHA256.test(binding.authorizedIdentityDigest)
1271
- || typeof binding.nextOffsetDigest !== "string" || !SHA256.test(binding.nextOffsetDigest)
1272
- || binding.nextOffsetDigest !== (0, telegram_1.sanctuaryTelegramTurnReceiptDigest)(identityKey, auditSchema, "next-update-id", String(telegramNextUpdateId))
1273
- || binding.authorizedIdentityDigest !== expectedAuthorizedIdentityDigest
1274
- || binding.senderIdentityDigest === binding.authorizedIdentityDigest
1275
- || binding.senderDistinct !== true || entry.meta.distinctAccount !== true
1276
- || typeof entry.meta.dropMac !== "string" || !SHA256.test(entry.meta.dropMac)
1277
- || entry.meta.dropMac !== (0, telegram_1.sanctuaryTelegramUnauthorizedDropMac)(identityKey, auditSchema, binding))
1278
- throw new Error("Telegram unauthorized sender binding MAC is invalid");
1279
- }
1280
- }
1281
1319
  const approvalEvidenceKeys = {
1282
1320
  "senses.telegram_approval_prompt_bound": [["actionDigest", "approvalId", "boundAt", "checkpointDigest", "evidenceMac", "messageIdDigest", "scenarioHandleDigest", "suspendedSessionRevisionDigest", "targetDigest"]],
1283
1321
  "telegram.callback_settled": [["accepted", "acknowledged", "acknowledgementState", "actionDigest", "approvalId", "boundAt", "callbackAt", "checkpointDigest", "decisionAttemptDigest", "evidenceMac", "messageIdDigest", "reason", "scenarioHandleDigest", "suspendedSessionRevisionDigest", "targetDigest"]],
@@ -1446,7 +1484,7 @@ async function readDefaultSanctuaryScenarioFacts(label, scenarioHandleDigest, de
1446
1484
  }
1447
1485
  }
1448
1486
  let containment;
1449
- if (label === "unit-16e-containment-audit" || label === "unit-16d-2-unauthorized") {
1487
+ if (label === "unit-16e-containment-audit" || label === "unit-16d-2-unknown-admission") {
1450
1488
  const inventoryEnvelope = object(await dependency(deps.hostRequest, "Sanctuary host broker")({ operation: "inventory_keys", targetServerId: TARGET_SERVER_ID }), "containment key inventory");
1451
1489
  if (!Array.isArray(inventoryEnvelope.keys))
1452
1490
  throw new Error("containment key inventory is invalid");
@@ -1460,9 +1498,12 @@ async function readDefaultSanctuaryScenarioFacts(label, scenarioHandleDigest, de
1460
1498
  })).sort((left, right) => left.name.localeCompare(right.name));
1461
1499
  const readRecord = inventory.find((record) => record.name === "Butler RO");
1462
1500
  const writeRecord = inventory.find((record) => record.name === "Butler RW");
1463
- const telegramNames = Array.isArray(telegramProfile) ? telegramProfile.filter((name) => typeof name === "string") : [];
1464
- const privateRaw = profiles?.["sanctuary-health-private"];
1465
- const privateNames = Array.isArray(privateRaw) ? privateRaw.filter((name) => typeof name === "string") : [];
1501
+ const relationshipRegistry = (0, relationship_authorization_1.loadRelationshipCapabilityRegistry)(agentRoot);
1502
+ const telegramProfile = relationshipRegistry.profiles["sanctuary-owner"];
1503
+ const privateProfile = relationshipRegistry.profiles["sanctuary-event"];
1504
+ const telegramNames = telegramProfile?.toolNames ?? [];
1505
+ const privateNames = privateProfile?.toolNames ?? [];
1506
+ const relationshipProfilesExact = telegramNames.length > 0 && privateNames.length > 0;
1466
1507
  const flowSchemas = new Map([tools_1.ponderTool, tools_1.settleTool, tools_1.speakTool, tools_1.restTool].map((tool) => [tool.function.name, tool]));
1467
1508
  const schemasFor = (names) => names.flatMap((name) => {
1468
1509
  const schema = (0, tools_1.resolveToolDefinition)(name)?.tool ?? flowSchemas.get(name);
@@ -1471,12 +1512,18 @@ async function readDefaultSanctuaryScenarioFacts(label, scenarioHandleDigest, de
1471
1512
  const telegramSchemas = schemasFor(telegramNames);
1472
1513
  const privateSchemas = schemasFor(privateNames);
1473
1514
  const handlerResolves = (name) => typeof (0, tools_1.resolveToolDefinition)(name)?.handler === "function" || flowSchemas.has(name);
1515
+ const resolvedHandlerCount = [...telegramNames, ...privateNames].filter(handlerResolves).length;
1516
+ const handlersExact = resolvedHandlerCount === telegramNames.length + privateNames.length;
1474
1517
  const excludedNames = ["shell", "read_file", "edit_file", "vault_get", "mcp_call", "exec", "credential_get"];
1475
1518
  const excludedSchemaIntersection = excludedNames.filter((name) => telegramNames.includes(name) || privateNames.includes(name));
1476
1519
  const productionBoundaryReceipts = await dependency(deps.runProductionBoundaryProbe, "production tool boundary probe")(telegramSchemas);
1477
1520
  const excludedAttempts = productionBoundaryReceipts.filter((receipt) => excludedNames.includes(receipt.name));
1478
1521
  const restartDefinition = (0, tools_1.resolveToolDefinition)("unraid_restart_container");
1479
1522
  const writeApprovalPolicy = restartDefinition.approvalPolicy({ container: "calibre-web" });
1523
+ const writeApprovalPolicyExact = writeApprovalPolicy.kind === "required"
1524
+ && writeApprovalPolicy.policyId === "sanctuary.unraid.restart.v1"
1525
+ && writeApprovalPolicy.actionClass === "unraid.container.restart"
1526
+ && writeApprovalPolicy.requiresSoleCall === true;
1480
1527
  let lifecycleBalance = 0;
1481
1528
  let lifecyclePairs = 0;
1482
1529
  for (const entry of auditLedgerEntries) {
@@ -1502,7 +1549,9 @@ async function readDefaultSanctuaryScenarioFacts(label, scenarioHandleDigest, de
1502
1549
  privateToolCount: privateNames.length,
1503
1550
  privateProfileDigest: (0, node_crypto_1.createHash)("sha256").update(JSON.stringify(privateNames)).digest("hex"),
1504
1551
  privateSchemaDigest: (0, node_crypto_1.createHash)("sha256").update(JSON.stringify(privateSchemas)).digest("hex"),
1505
- resolvedHandlerCount: [...telegramNames, ...privateNames].filter(handlerResolves).length,
1552
+ resolvedHandlerCount,
1553
+ relationshipProfilesExact,
1554
+ handlersExact,
1506
1555
  excludedToolCount: excludedNames.length,
1507
1556
  excludedSchemaIntersectionCount: excludedSchemaIntersection.length,
1508
1557
  fabricatedHandlerInvocationCount: excludedSchemaIntersection.filter(handlerResolves).length,
@@ -1528,10 +1577,14 @@ async function readDefaultSanctuaryScenarioFacts(label, scenarioHandleDigest, de
1528
1577
  rawWriteMaterialFieldCount,
1529
1578
  typedWriteExecutorCount: telegramNames.filter((name) => name === "unraid_restart_container" && writeApprovalPolicy.kind === "required").length,
1530
1579
  writeApprovalPolicyDigest: (0, node_crypto_1.createHash)("sha256").update(JSON.stringify(writeApprovalPolicy)).digest("hex"),
1580
+ writeApprovalPolicyExact,
1531
1581
  sensitiveMaterialObserved: auditContainsSensitiveMaterial(auditRaw ?? "", deps.telegramCredentials?.()) || rawWriteMaterialFieldCount > 0 || container?.writableKeyExposure === true,
1532
1582
  stopDenied, restartDenied, denialAuditCount, denialStateUnchanged, denialProbeCompleted,
1533
1583
  };
1534
1584
  }
1585
+ const telegramAdmissions = label === "unit-16d-2-unknown-admission"
1586
+ ? await projectTelegramAdmissions(agentRoot, dependency(deps.telegramCredentials, "Telegram credentials")(), identityRaw?.trim() ?? null)
1587
+ : [];
1535
1588
  const sourceValues = {
1536
1589
  "identity-key": identityRaw, "telegram-audit": auditEntries, "telegram-offset": offsetRaw,
1537
1590
  "approval-journal": approvals, "approval-checkpoints": checkpointsRaw, "container-inspect": container,
@@ -1539,6 +1592,7 @@ async function readDefaultSanctuaryScenarioFacts(label, scenarioHandleDigest, de
1539
1592
  "digest-runtime": health, "reboot-checkpoint": rebootCheckpoint, "telegram-turn-receipts": telegramTurns, "read-only-denial-receipt": denialReceipt ?? null,
1540
1593
  "identity-surface-audit": identity ? { inspectedRecordCount: identity.inspectedRecordCount, opaqueSubjectCount: identity.opaqueSubjectCount, mismatchCount: identity.mismatchCount, rawLeakCount: identity.rawLeakCount, canonicalSessionCount: identity.canonicalSessionCount, canonicalFriendCount: identity.canonicalFriendCount, sessionSurfaceDigest: identity.sessionSurfaceDigest, friendSurfaceDigest: identity.friendSurfaceDigest, surfaceDigest: identity.surfaceDigest } : null,
1541
1594
  "containment-audit": containment ?? null,
1595
+ "telegram-admission-evidence": telegramAdmissions,
1542
1596
  "health-probe-receipt": healthProbe ?? null,
1543
1597
  "scheduler-liveness-receipt": healthProbe?.schedulerReceipt ?? null,
1544
1598
  "interactive-driver-receipt": parsedJson(interactiveDriverRaw),
@@ -1570,6 +1624,7 @@ async function readDefaultSanctuaryScenarioFacts(label, scenarioHandleDigest, de
1570
1624
  approvals,
1571
1625
  restartAttempts,
1572
1626
  telegramTurns,
1627
+ telegramAdmissions,
1573
1628
  telegramNextUpdateId,
1574
1629
  zeroWork,
1575
1630
  liveGrounding,
@@ -1578,7 +1633,7 @@ async function readDefaultSanctuaryScenarioFacts(label, scenarioHandleDigest, de
1578
1633
  exactImage: typeof expectedImageId === "string" && SHA256.test(expectedImageId) && container.imageId === `sha256:${expectedImageId}`, running: container.running === true,
1579
1634
  healthy: container.health === "healthy", user: text(container.user, "container user"), readOnlyRoot: container.readOnlyRoot === true,
1580
1635
  mountCount: Number(container.mountCount), publishedPortCount: Number(container.publishedPortCount), restartPolicy: text(container.restartPolicy, "restart policy"), restartCount: Number(container.restartCount),
1581
- autostartExact: container.autostartExact === true, updaterDisabled: container.updaterDisabled === true,
1636
+ autostartExact: container.autostartExact === true, mountsExact: container.mountsExact === true, updaterDisabled: container.updaterDisabled === true,
1582
1637
  vaultUnlocked: container.vaultUnlocked === true, manualAuthRequired: container.manualAuthRequired === true,
1583
1638
  } : undefined,
1584
1639
  provider: liveProvider,
@@ -1591,10 +1646,10 @@ async function readDefaultSanctuaryScenarioFacts(label, scenarioHandleDigest, de
1591
1646
  reboot,
1592
1647
  containment: containment ?? {
1593
1648
  schemaVersion: "sanctuary-containment-audit-v1", keyCount: 0, keyInventoryDigest: "", readScopeDigest: "", writeScopeDigest: "", keyRoleAssignmentCount: 0,
1594
- telegramToolCount: 0, telegramProfileDigest: "", telegramSchemaDigest: "", privateToolCount: 0, privateProfileDigest: "", privateSchemaDigest: "", resolvedHandlerCount: 0,
1649
+ telegramToolCount: 0, telegramProfileDigest: "", telegramSchemaDigest: "", privateToolCount: 0, privateProfileDigest: "", privateSchemaDigest: "", resolvedHandlerCount: 0, relationshipProfilesExact: false, handlersExact: false,
1595
1650
  excludedToolCount: 0, excludedSchemaIntersectionCount: 0, fabricatedHandlerInvocationCount: 0, excludedToolAttemptCount: 0, excludedToolRejectedCount: 0, excludedToolInvokedCount: 0, excludedToolSideEffectCount: 0, globallyResolvableExcludedToolCount: 0, auditPathDigest: "", auditLedgerDigest: "", auditRecordCount: 0, auditLifecyclePairCount: 0,
1596
1651
  containerUser: "", liveProcessUser: "", mountCount: 0, publishedPortCount: 0, networkMode: "", readOnlyRoot: false, mountsExact: false, securityExact: false, updaterDisabled: false,
1597
- writableKeyExposure: container?.writableKeyExposure === true, rawWriteMaterialFieldCount: 0, typedWriteExecutorCount: 0, writeApprovalPolicyDigest: "",
1652
+ writableKeyExposure: container?.writableKeyExposure === true, rawWriteMaterialFieldCount: 0, typedWriteExecutorCount: 0, writeApprovalPolicyDigest: "", writeApprovalPolicyExact: false,
1598
1653
  sensitiveMaterialObserved: auditContainsSensitiveMaterial(auditRaw ?? "") || container?.writableKeyExposure === true,
1599
1654
  stopDenied, restartDenied, denialAuditCount, denialStateUnchanged, denialProbeCompleted,
1600
1655
  },
@@ -2288,6 +2343,25 @@ async function executeSanctuaryAcceptanceRevokedProbe(keyIdValue, endpointValue,
2288
2343
  fetch: deps.fetch,
2289
2344
  });
2290
2345
  }
2346
+ function createAcceptanceProbeEffects(input) {
2347
+ const store = new telegram_effect_adapter_1.FileTelegramEffectJournal(path.join(input.agentRoot, "state", "telegram", "effects"));
2348
+ const target = { kind: "approved_relationship", friendId: `telegram-user:${input.subject}`, sessionKey: `telegram:${input.subject}` };
2349
+ const execute = (0, telegram_effect_adapter_1.createTelegramAuthorizedEffectExecutor)({
2350
+ store,
2351
+ api: input.api,
2352
+ authorize: ({ target: candidate }) => JSON.stringify(candidate) === JSON.stringify(target)
2353
+ ? { allowed: true, receiptId: `configured-owner:${input.subject}`, expiresAt: new Date(Date.now() + 5 * 60_000).toISOString(), transport: { chatId: input.chatId } }
2354
+ : { allowed: false, reason: "effect target is not the configured owner relationship" },
2355
+ });
2356
+ const sessionPath = (0, shared_turn_1.getSenseSessionPath)(TARGET_ID, target.friendId, "telegram", target.sessionKey, input.agentRoot);
2357
+ const port = (0, telegram_effect_adapter_1.createTelegramApprovalEffectPort)({
2358
+ target,
2359
+ chatId: input.chatId,
2360
+ execute,
2361
+ record: async (artifact) => { await (0, telegram_effect_adapter_1.recordTelegramEffectsInSession)({ store, sessionPath, artifacts: [artifact] }); },
2362
+ });
2363
+ return { port, close: () => store.close() };
2364
+ }
2291
2365
  exports.proveAttemptedRecoveryWithoutRetry = sanctuary_interactive_control_1.proveSanctuaryAttemptedRecoveryWithoutRetry;
2292
2366
  async function executeSanctuaryInteractiveRuntimeOperation(rawPayload, supplied) {
2293
2367
  exactKeys(rawPayload, ["operation", "label", "scenarioHandleDigest"], "interactive runtime payload");
@@ -2312,6 +2386,7 @@ async function executeSanctuaryAcceptanceCallbackProbe(rawUpdate, _replay, deps
2312
2386
  createApi: telegram_client_1.createTelegramBotApi,
2313
2387
  createRuntime: telegram_approval_runtime_1.createTelegramApprovalRuntime,
2314
2388
  toolContext: sanctuary_runtime_1.createSanctuaryToolContext,
2389
+ effects: createAcceptanceProbeEffects,
2315
2390
  }) {
2316
2391
  const update = callbackUpdate(rawUpdate);
2317
2392
  const refreshed = await deps.refresh(TARGET_ID);
@@ -2321,6 +2396,13 @@ async function executeSanctuaryAcceptanceCallbackProbe(rawUpdate, _replay, deps
2321
2396
  const identityKey = deps.identityKey((0, identity_1.getAgentRoot)(TARGET_ID));
2322
2397
  const subject = (0, telegram_1.opaqueTelegramSubject)(identityKey, credentials.botToken, credentials.authorizedUserId, credentials.authorizedChatId);
2323
2398
  const api = deps.createApi({ token: credentials.botToken });
2399
+ const effectBoundary = deps.effects?.({ agentRoot: (0, identity_1.getAgentRoot)(TARGET_ID), api, subject, chatId: credentials.authorizedChatId });
2400
+ const unavailableEffects = {
2401
+ sendText: async () => { throw new Error("Telegram acceptance probe effect boundary is unavailable"); },
2402
+ sendCard: async () => { throw new Error("Telegram acceptance probe effect boundary is unavailable"); },
2403
+ edit: async () => { throw new Error("Telegram acceptance probe effect boundary is unavailable"); },
2404
+ acknowledge: async () => { throw new Error("Telegram acceptance probe effect boundary is unavailable"); },
2405
+ };
2324
2406
  const runtime = deps.createRuntime({
2325
2407
  agentName: TARGET_ID,
2326
2408
  api,
@@ -2329,6 +2411,7 @@ async function executeSanctuaryAcceptanceCallbackProbe(rawUpdate, _replay, deps
2329
2411
  subject,
2330
2412
  identityKey,
2331
2413
  toolContext: deps.toolContext(TARGET_ID),
2414
+ effects: effectBoundary?.port ?? unavailableEffects,
2332
2415
  });
2333
2416
  try {
2334
2417
  const result = await runtime.transport.handleUpdate(update);
@@ -2340,6 +2423,7 @@ async function executeSanctuaryAcceptanceCallbackProbe(rawUpdate, _replay, deps
2340
2423
  }
2341
2424
  finally {
2342
2425
  runtime.close();
2426
+ effectBoundary?.close();
2343
2427
  api.stop();
2344
2428
  }
2345
2429
  }
@@ -333,13 +333,13 @@ exports.SANCTUARY_UNIT_16_EVIDENCE_LABELS = [
333
333
  "unit-16c-provider-readiness",
334
334
  "unit-16d-whats-up",
335
335
  "unit-16d-1-space",
336
- "unit-16d-2-unauthorized",
336
+ "unit-16d-2-unknown-admission",
337
337
  "unit-16e-containment-audit",
338
338
  "unit-16e-1-stop-denial",
339
339
  "unit-16e-2-restart-denial",
340
340
  "unit-16f-cron-fingerprint",
341
341
  "unit-16g-health-transition",
342
- "unit-16h-daily-digest",
342
+ "unit-16h-acceptance-delivery-probe",
343
343
  "unit-16i-delayed-approval",
344
344
  "unit-16j-denial",
345
345
  "unit-16k-timeout-stale",
@@ -370,7 +370,7 @@ function sanctuaryScenarioTimeoutBudget(label) {
370
370
  return 125_000;
371
371
  if (label === "unit-15c-1-no-callback-terminalization")
372
372
  return 365_000;
373
- if (label === "unit-16h-daily-digest")
373
+ if (label === "unit-16h-acceptance-delivery-probe")
374
374
  return 1_025_000;
375
375
  if (label === "unit-16f-cron-fingerprint")
376
376
  return 1_025_000;
@@ -435,10 +435,10 @@ function validateSanctuaryUnit16EvidenceAssertions(label, raw) {
435
435
  opaqueDigest(value.processBindingDigest, `${label} processBindingDigest`);
436
436
  break;
437
437
  case "unit-16b-runtime-vault-containment":
438
- exact(["autostartExact", "exactImage", "manualAuthRequired", "mountCount", "nonRootUid", "publishedPortCount", "readOnlyRoot", "updaterDisabled", "vaultUnlocked"]);
439
- allTrue(["autostartExact", "exactImage", "readOnlyRoot", "updaterDisabled", "vaultUnlocked"]);
438
+ exact(["autostartExact", "exactImage", "manualAuthRequired", "mountCount", "mountsExact", "nonRootUid", "publishedPortCount", "readOnlyRoot", "updaterDisabled", "vaultUnlocked"]);
439
+ allTrue(["autostartExact", "exactImage", "mountsExact", "readOnlyRoot", "updaterDisabled", "vaultUnlocked"]);
440
440
  requiredFalse(value, "manualAuthRequired", label);
441
- requiredInteger(value, "mountCount", 2, label);
441
+ requiredInteger(value, "mountCount", 4, label);
442
442
  requiredInteger(value, "nonRootUid", 10001, label);
443
443
  requiredInteger(value, "publishedPortCount", 0, label);
444
444
  break;
@@ -458,19 +458,19 @@ function validateSanctuaryUnit16EvidenceAssertions(label, raw) {
458
458
  requiredInteger(value, "responseCount", 1, label);
459
459
  requiredInteger(value, "mutationCount", 0, label);
460
460
  break;
461
- case "unit-16d-2-unauthorized":
462
- exact(["auditRejected", "distinctAccount", "mutationCount", "providerInvocationCount", "responseCount", "workItemCount"]);
463
- allTrue(["auditRejected", "distinctAccount"]);
464
- allZero(["mutationCount", "providerInvocationCount", "responseCount", "workItemCount"]);
461
+ case "unit-16d-2-unknown-admission":
462
+ exact(["acknowledgementSent", "agentTurnCount", "distinctAccount", "mutationCount", "ownerCardSent", "providerInvocationCount", "quarantined", "responseCount", "workItemCount"]);
463
+ allTrue(["acknowledgementSent", "distinctAccount", "ownerCardSent", "quarantined"]);
464
+ allZero(["agentTurnCount", "mutationCount", "providerInvocationCount", "responseCount", "workItemCount"]);
465
465
  break;
466
466
  case "unit-16e-containment-audit":
467
467
  exact([
468
468
  "schemaVersion", "keyCount", "keyInventoryDigest", "readScopeDigest", "writeScopeDigest", "keyRoleAssignmentCount",
469
- "telegramToolCount", "telegramProfileDigest", "telegramSchemaDigest", "privateToolCount", "privateProfileDigest", "privateSchemaDigest", "resolvedHandlerCount",
469
+ "telegramToolCount", "telegramProfileDigest", "telegramSchemaDigest", "privateToolCount", "privateProfileDigest", "privateSchemaDigest", "resolvedHandlerCount", "relationshipProfilesExact", "handlersExact",
470
470
  "excludedToolCount", "excludedSchemaIntersectionCount", "fabricatedHandlerInvocationCount", "excludedToolAttemptCount", "excludedToolRejectedCount", "excludedToolInvokedCount", "excludedToolSideEffectCount", "globallyResolvableExcludedToolCount",
471
471
  "auditPathDigest", "auditLedgerDigest", "auditRecordCount", "auditLifecyclePairCount",
472
472
  "containerUser", "liveProcessUser", "mountCount", "publishedPortCount", "networkMode", "readOnlyRoot", "mountsExact", "securityExact", "updaterDisabled", "writableKeyExposure",
473
- "rawWriteMaterialFieldCount", "typedWriteExecutorCount", "writeApprovalPolicyDigest", "sensitiveMaterialObserved", "mutationCount",
473
+ "rawWriteMaterialFieldCount", "typedWriteExecutorCount", "writeApprovalPolicyDigest", "writeApprovalPolicyExact", "sensitiveMaterialObserved", "mutationCount",
474
474
  ]);
475
475
  if (text(value.schemaVersion, `${label} schemaVersion`) !== "sanctuary-containment-audit-v1")
476
476
  throw new Error(`${label} schemaVersion is invalid`);
@@ -479,20 +479,16 @@ function validateSanctuaryUnit16EvidenceAssertions(label, raw) {
479
479
  for (const [key, expected] of Object.entries({
480
480
  readScopeDigest: "9914469afdcb574937d1020a03faa82e3c02d767169d3eccae4b81863dafa06e",
481
481
  writeScopeDigest: "1de873b2bc3c7769010c32c69fcc8ea55343a5647cfdb0294769e831142945ec",
482
- telegramProfileDigest: "a7f26934c5e60737582b9d13c78944b8bcbb941366899d82d58c01ca296e14e2",
483
- telegramSchemaDigest: "3c66299a5f70ec82f8795cae47659284e6dbc691ef49002c2fb22edba76c59b6",
484
- privateProfileDigest: "a100ffcaf436842bf9fceaf3d2fd1a1b766c04238300487474d6e9fcb7946369",
485
- privateSchemaDigest: "61b137b2467acbcf22ca7443ee01e71ed970a62728c42aabffbdcb562f4a6a70",
486
482
  auditPathDigest: "1cb8f1a00c544a5d10b0577090dbf070a07a5b6a99de13ccd27c11a257f84b75",
487
- writeApprovalPolicyDigest: "24b1726edf1a2bbd524e9be63d3f0f726d996a8a009425462e01a5c4916ef42b",
488
483
  }))
489
484
  if (value[key] !== expected)
490
485
  throw new Error(`${label} ${key} does not match the canonical contract`);
491
486
  requiredInteger(value, "keyCount", 2, label);
492
487
  requiredInteger(value, "keyRoleAssignmentCount", 0, label);
493
- requiredInteger(value, "telegramToolCount", 10, label);
494
- requiredInteger(value, "privateToolCount", 2, label);
495
- requiredInteger(value, "resolvedHandlerCount", 12, label);
488
+ const telegramToolCount = integer(value.telegramToolCount, `${label} telegramToolCount`, 1);
489
+ const privateToolCount = integer(value.privateToolCount, `${label} privateToolCount`, 1);
490
+ if (integer(value.resolvedHandlerCount, `${label} resolvedHandlerCount`, 1) !== telegramToolCount + privateToolCount)
491
+ throw new Error(`${label} handler count does not match the live relationship profiles`);
496
492
  requiredInteger(value, "excludedToolCount", 7, label);
497
493
  requiredInteger(value, "excludedToolAttemptCount", 7, label);
498
494
  requiredInteger(value, "excludedToolRejectedCount", 7, label);
@@ -504,9 +500,9 @@ function validateSanctuaryUnit16EvidenceAssertions(label, raw) {
504
500
  integer(value.auditLifecyclePairCount, `${label} auditLifecyclePairCount`, 1);
505
501
  if (text(value.containerUser, `${label} containerUser`) !== "10001:10001" || text(value.liveProcessUser, `${label} liveProcessUser`) !== "10001:10001" || text(value.networkMode, `${label} networkMode`) !== "host")
506
502
  throw new Error(`${label} container identity or network is invalid`);
507
- requiredInteger(value, "mountCount", 2, label);
503
+ requiredInteger(value, "mountCount", 4, label);
508
504
  requiredInteger(value, "typedWriteExecutorCount", 1, label);
509
- allTrue(["readOnlyRoot", "mountsExact", "securityExact", "updaterDisabled"]);
505
+ allTrue(["relationshipProfilesExact", "handlersExact", "writeApprovalPolicyExact", "readOnlyRoot", "mountsExact", "securityExact", "updaterDisabled"]);
510
506
  break;
511
507
  case "unit-16e-1-stop-denial":
512
508
  case "unit-16e-2-restart-denial":
@@ -526,9 +522,10 @@ function validateSanctuaryUnit16EvidenceAssertions(label, raw) {
526
522
  allTrue(["productionRestored", "transitionObserved"]);
527
523
  requiredInteger(value, "alertCount", 3, label);
528
524
  break;
529
- case "unit-16h-daily-digest":
530
- exact(["firedWithinMs", "messageCount", "productionRestored", "scheduleObserved"]);
531
- allTrue(["productionRestored", "scheduleObserved"]);
525
+ case "unit-16h-acceptance-delivery-probe":
526
+ exact(["acceptanceOnly", "deliveryPathObserved", "firedWithinMs", "messageCount", "productionRestored", "productionScheduleChanged"]);
527
+ allTrue(["acceptanceOnly", "deliveryPathObserved", "productionRestored"]);
528
+ requiredFalse(value, "productionScheduleChanged", label);
532
529
  requiredInteger(value, "messageCount", 1, label);
533
530
  if (integer(value.firedWithinMs, `${label} firedWithinMs`, 0) > 960_000)
534
531
  throw new Error(`${label} fired outside the 16-minute bound`);
@@ -644,13 +641,13 @@ exports.SANCTUARY_SCENARIO_GATES = {
644
641
  "unit-16c-provider-readiness": "none",
645
642
  "unit-16d-whats-up": "authorized-telegram-message",
646
643
  "unit-16d-1-space": "authorized-telegram-message",
647
- "unit-16d-2-unauthorized": "distinct-telegram-account-message",
644
+ "unit-16d-2-unknown-admission": "unknown-telegram-admission",
648
645
  "unit-16e-containment-audit": "none",
649
646
  "unit-16e-1-stop-denial": "none",
650
647
  "unit-16e-2-restart-denial": "none",
651
648
  "unit-16f-cron-fingerprint": "none",
652
649
  "unit-16g-health-transition": "none",
653
- "unit-16h-daily-digest": "none",
650
+ "unit-16h-acceptance-delivery-probe": "none",
654
651
  "unit-16i-delayed-approval": "telegram-delayed-approve",
655
652
  "unit-16j-denial": "telegram-deny",
656
653
  "unit-16k-timeout-stale": "telegram-stale-callback",
@@ -668,13 +665,13 @@ exports.SANCTUARY_SCENARIO_SOURCES = {
668
665
  "unit-16c-provider-readiness": ["provider-live-check"],
669
666
  "unit-16d-whats-up": ["telegram-audit", "telegram-offset", "telegram-turn-receipts", "live-grounding-read"],
670
667
  "unit-16d-1-space": ["telegram-audit", "telegram-offset", "telegram-turn-receipts", "restart-attempt-ledger", "container-inspect", "live-grounding-read"],
671
- "unit-16d-2-unauthorized": ["telegram-audit", "telegram-offset", "telegram-turn-receipts", "approval-journal", "restart-attempt-ledger", "container-inspect", "containment-audit"],
668
+ "unit-16d-2-unknown-admission": ["identity-key", "telegram-audit", "telegram-offset", "telegram-turn-receipts", "telegram-admission-evidence", "approval-journal", "restart-attempt-ledger", "container-inspect", "containment-audit"],
672
669
  "unit-16e-containment-audit": ["telegram-audit", "container-inspect", "containment-audit"],
673
670
  "unit-16e-1-stop-denial": ["read-only-denial-receipt", "container-inspect"],
674
671
  "unit-16e-2-restart-denial": ["read-only-denial-receipt", "container-inspect"],
675
672
  "unit-16f-cron-fingerprint": ["health-probe-receipt", "scheduler-liveness-receipt", "cron-runtime", "telegram-audit", "container-inspect"],
676
673
  "unit-16g-health-transition": ["health-probe-receipt", "telegram-audit", "container-inspect"],
677
- "unit-16h-daily-digest": ["health-probe-receipt", "cron-runtime", "telegram-audit", "container-inspect"],
674
+ "unit-16h-acceptance-delivery-probe": ["health-probe-receipt", "cron-runtime", "telegram-audit", "container-inspect"],
678
675
  "unit-16i-delayed-approval": ["identity-key", "telegram-audit", "approval-journal", "approval-checkpoints", "restart-attempt-ledger", "container-inspect"],
679
676
  "unit-16j-denial": ["identity-key", "telegram-audit", "approval-journal", "approval-checkpoints", "restart-attempt-ledger", "container-inspect"],
680
677
  "unit-16k-timeout-stale": ["identity-key", "telegram-audit", "approval-journal", "approval-checkpoints", "restart-attempt-ledger", "container-inspect", "interactive-driver-receipt"],