@ouro.bot/cli 0.1.0-alpha.741 → 0.1.0-alpha.743

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 (81) hide show
  1. package/changelog.json +42 -0
  2. package/deploy/unraid/Dockerfile +7 -2
  3. package/deploy/unraid/README.txt +717 -106
  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/agent.json +2 -2
  13. package/deploy/unraid/sanctuary.ouro/bundle-meta.json +2 -2
  14. package/deploy/unraid/sanctuary.ouro/habits/sanctuary-health.md +1 -1
  15. package/deploy/unraid/sanctuary.ouro/provider-readiness.json +3 -10
  16. package/deploy/unraid/sanctuary.ouro/psyche/ASPIRATIONS.md +3 -1
  17. package/deploy/unraid/sanctuary.ouro/psyche/IDENTITY.md +3 -1
  18. package/deploy/unraid/sanctuary.ouro/psyche/LORE.md +4 -0
  19. package/deploy/unraid/sanctuary.ouro/psyche/SOUL.md +6 -2
  20. package/deploy/unraid/sanctuary.ouro/psyche/TACIT.md +4 -2
  21. package/deploy/unraid/sanctuary.ouro/state/policy/steward.json +7 -0
  22. package/deploy/unraid/sanctuary.ouro/tool-profiles.json +19 -17
  23. package/deploy/unraid/sanctuary.xml +12 -9
  24. package/dist/arc/cares.js +171 -37
  25. package/dist/arc/obligations.js +38 -0
  26. package/dist/heart/awaiting/await-alert.js +4 -2
  27. package/dist/heart/awaiting/await-expiry.js +39 -4
  28. package/dist/heart/awaiting/await-parser.js +8 -0
  29. package/dist/heart/context-loss-gauntlet.js +31 -2
  30. package/dist/heart/context-loss-sentinel.js +2 -0
  31. package/dist/heart/core.js +37 -7
  32. package/dist/heart/cross-chat-delivery.js +1 -0
  33. package/dist/heart/daemon/container-healthcheck.js +3 -1
  34. package/dist/heart/daemon/container-runtime.js +13 -0
  35. package/dist/heart/daemon/container-spec-auditor-main.js +11 -7
  36. package/dist/heart/daemon/container-spec-auditor.js +38 -11
  37. package/dist/heart/daemon/daemon-entry.js +19 -5
  38. package/dist/heart/daemon/daemon.js +154 -22
  39. package/dist/heart/daemon/sanctuary-acceptance-adapter.js +151 -87
  40. package/dist/heart/daemon/sanctuary-acceptance-harness.js +26 -29
  41. package/dist/heart/daemon/sanctuary-acceptance-scenarios.js +42 -51
  42. package/dist/heart/daemon/sanctuary-bundle-migration.js +398 -0
  43. package/dist/heart/daemon/sanctuary-scheduler-origin.js +11 -5
  44. package/dist/heart/daemon/supercronic-supervisor.js +16 -1
  45. package/dist/heart/external-events/router.js +1025 -43
  46. package/dist/heart/provider-models.js +10 -3
  47. package/dist/heart/session-events.js +23 -5
  48. package/dist/heart/steward-policy.js +302 -0
  49. package/dist/heart/tool-approval.js +3 -1
  50. package/dist/heart/turn-context.js +1 -0
  51. package/dist/mind/context.js +8 -3
  52. package/dist/mind/pending.js +25 -0
  53. package/dist/mind/prompt.js +49 -1
  54. package/dist/repertoire/guardrails.js +2 -1
  55. package/dist/repertoire/relationship-authorization.js +194 -0
  56. package/dist/repertoire/tools-awaiting.js +229 -26
  57. package/dist/repertoire/tools-base.js +15 -0
  58. package/dist/repertoire/tools-continuity.js +259 -4
  59. package/dist/repertoire/tools-notes.js +29 -3
  60. package/dist/repertoire/tools-session.js +219 -4
  61. package/dist/repertoire/tools-steward-policy.js +88 -0
  62. package/dist/repertoire/tools-telegram-contacts.js +37 -0
  63. package/dist/repertoire/tools-unraid.js +113 -6
  64. package/dist/repertoire/tools.js +104 -9
  65. package/dist/repertoire/unraid-restart.js +86 -4
  66. package/dist/senses/private-runtime-worker.js +36 -3
  67. package/dist/senses/private-runtime.js +310 -63
  68. package/dist/senses/sanctuary-health-acceptance-probe.js +8 -6
  69. package/dist/senses/sanctuary-health-runner.js +51 -96
  70. package/dist/senses/sanctuary-health.js +54 -93
  71. package/dist/senses/sanctuary-runtime.js +36 -2
  72. package/dist/senses/sanctuary-sab.js +80 -0
  73. package/dist/senses/shared-turn.js +64 -9
  74. package/dist/senses/telegram-admission.js +684 -0
  75. package/dist/senses/telegram-approval-runtime.js +46 -6
  76. package/dist/senses/telegram-client.js +236 -72
  77. package/dist/senses/telegram-effect-adapter.js +787 -0
  78. package/dist/senses/telegram-entry.js +4 -1
  79. package/dist/senses/telegram.js +728 -48
  80. package/npm-shrinkwrap.json +6 -6
  81. 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");
@@ -78,20 +83,13 @@ const KEY_ID = /^[A-Za-z0-9._:-]+$/u;
78
83
  const AUTH_PROBE = "query AcceptanceAuthProbe { info { os { hostname } } }";
79
84
  const WRITE_PROBE = "mutation AcceptanceWriteProbe($id: PrefixedID!) { docker { restart(id: $id) { id } } }";
80
85
  function evaluateSanctuaryProviderReadinessContract(input) {
81
- const credentialIdentity = (_provider, secret) => (0, node_crypto_1.createHmac)("sha256", input.identityKey)
82
- .update(`sanctuary-provider-credential-v1\0${secret}`).digest();
83
- const glmIdentity = credentialIdentity("openai-compatible", input.glm.apiKey);
84
- const geminiIdentity = credentialIdentity("openai-compatible-gemini", input.geminiCredential.apiKey);
85
86
  return {
86
- modelsExact: input.outward.provider === "openai-compatible" && input.outward.model === "glm-5.2"
87
- && input.inner.provider === "openai-compatible" && input.inner.model === "glm-5.2"
88
- && input.gemini.provider === "openai-compatible-gemini" && input.gemini.model === "gemini-3.6-flash",
89
- baseUrlsExact: input.glm.baseUrl === "https://api.z.ai/api/paas/v4/"
90
- && input.geminiCredential.baseUrl === "https://generativelanguage.googleapis.com/v1beta/openai/",
91
- vaultCoordinatesExact: input.glm.vaultItem === "providers/openai-compatible"
92
- && input.gemini.vaultItem === "providers/openai-compatible-gemini"
87
+ modelsExact: input.outward.provider === "minimax" && input.outward.model === "MiniMax-M3"
88
+ && input.inner.provider === "minimax" && input.inner.model === "MiniMax-M3",
89
+ baseUrlsExact: true,
90
+ vaultCoordinatesExact: input.minimax.vaultItem === "providers/minimax"
93
91
  && input.selectionPolicy === "explicit-same-lane-only",
94
- credentialIdentitiesDistinct: !(0, node_crypto_1.timingSafeEqual)(glmIdentity, geminiIdentity),
92
+ credentialIdentitiesDistinct: typeof input.minimax.apiKey === "string" && input.minimax.apiKey.trim().length > 0,
95
93
  };
96
94
  }
97
95
  const MISSING_CONTAINER_ID = "Docker:ouro-acceptance-guaranteed-missing";
@@ -111,7 +109,6 @@ const POSTBOOT_HEALTH_FILE = "/run/ouro-acceptance/postboot-health.json";
111
109
  const BOOT_ID_FILE = "/run/ouro-acceptance/boot-id";
112
110
  const TELEGRAM_POLLER_COUNT_FILE = "/run/ouro-acceptance/telegram-poller-count.json";
113
111
  const CONTRACT_FILE = "/opt/ouro/deploy/unraid/sanctuary-acceptance-contract.json";
114
- const SANCTUARY_TOOL_PROFILES_FILE = "/opt/ouro/deploy/unraid/sanctuary.ouro/tool-profiles.json";
115
112
  const CLOSED_INVENTORY_FILE = "/run/ouro-acceptance/closed-inventory.json";
116
113
  const HOST_BROKER_SOCKET = "/run/ouro-host-acceptance/adapter.sock";
117
114
  const MAX_BROKER_RESPONSE = 256 * 1024;
@@ -125,7 +122,7 @@ const APPROVAL_EVIDENCE_LABELS = new Set([
125
122
  const HEALTH_ACCEPTANCE_LABELS = new Set([
126
123
  "unit-16f-cron-fingerprint",
127
124
  "unit-16g-health-transition",
128
- "unit-16h-daily-digest",
125
+ "unit-16h-acceptance-delivery-probe",
129
126
  ]);
130
127
  const PERMISSION_RESOURCES = new Set([
131
128
  "ACTIVATION_CODE", "API_KEY", "ARRAY", "CLOUD", "CONFIG", "CONNECT", "CONNECT__REMOTE_ACCESS",
@@ -512,6 +509,67 @@ function parsedJson(raw) {
512
509
  return null;
513
510
  return object(JSON.parse(raw), "scenario source");
514
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
+ }
515
573
  function canonicalIso(value) {
516
574
  return typeof value === "string" && value.length <= 30 && Number.isFinite(Date.parse(value)) && new Date(Date.parse(value)).toISOString() === value;
517
575
  }
@@ -618,10 +676,10 @@ function parseHealthProbeReceipt(raw, label, scenarioHandleDigest, identityKey)
618
676
  if (Buffer.byteLength(raw) > 128 * 1024)
619
677
  throw new Error("Sanctuary health probe receipt exceeds its bound");
620
678
  const receipt = object(JSON.parse(raw), "Sanctuary health probe receipt");
621
- 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();
622
- 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";
623
681
  const digestFields = ["ownerImageDigestBefore", "ownerImageDigestAfter", "ownerContainerDigestBefore", "ownerContainerDigestAfter", "beforeStateDigest", "restoredStateDigest", "cronFingerprintBefore", "cronFingerprintAfter", "fixtureSequenceDigest"];
624
- 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"];
625
683
  if (!supportedLabel || JSON.stringify(Object.keys(receipt).sort()) !== JSON.stringify(exactKeys) || receipt.schemaVersion !== "sanctuary-health-probe-receipt-v1"
626
684
  || receipt.label !== label || receipt.scenarioHandleDigest !== scenarioHandleDigest || !SHA256.test(scenarioHandleDigest)
627
685
  || !digestFields.every((field) => typeof receipt[field] === "string" && SHA256.test(receipt[field]))
@@ -696,10 +754,10 @@ function parseHealthProbeReceipt(raw, label, scenarioHandleDigest, identityKey)
696
754
  if (receipt.ownerImageDigestBefore !== receipt.ownerImageDigestAfter || receipt.ownerContainerDigestBefore !== receipt.ownerContainerDigestAfter
697
755
  || (label !== "unit-16f-cron-fingerprint" && receipt.beforeStateDigest !== receipt.restoredStateDigest) || receipt.cronFingerprintBefore !== receipt.cronFingerprintAfter
698
756
  || receipt.cronRegisteredBefore !== true || receipt.cronRegisteredAfter !== true || receipt.cronDegradedBefore !== false || receipt.cronDegradedAfter !== false
699
- || 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) {
700
758
  throw new Error("Sanctuary health probe did not restore its exact production owner and state");
701
759
  }
702
- if (label === "unit-16h-daily-digest") {
760
+ if (label === "unit-16h-acceptance-delivery-probe") {
703
761
  const effective = new Date(receipt.effectiveNow);
704
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);
705
763
  const part = (type) => Number(parts.find((entry) => entry.type === type).value);
@@ -1177,9 +1235,6 @@ async function readDefaultSanctuaryScenarioFacts(label, scenarioHandleDigest, de
1177
1235
  identityKey: identityRaw.trim(),
1178
1236
  });
1179
1237
  const auditEntries = auditLedgerEntries.filter((entry) => entry.meta.scenarioHandleDigest === scenarioHandleDigest);
1180
- const toolProfiles = parsedJson(optionalFixedFile(deps, SANCTUARY_TOOL_PROFILES_FILE));
1181
- const profiles = toolProfiles ? object(toolProfiles.profiles, "Sanctuary tool profiles") : null;
1182
- const telegramProfile = profiles?.["sanctuary-telegram"];
1183
1238
  let approvalRecords = [];
1184
1239
  const approvalDatabasePath = pathFor(agentRoot, "state/approvals/approvals.sqlite");
1185
1240
  if ((0, node_fs_1.existsSync)(approvalDatabasePath))
@@ -1261,30 +1316,6 @@ async function readDefaultSanctuaryScenarioFacts(label, scenarioHandleDigest, de
1261
1316
  throw new Error("Telegram audit lifecycle MAC is invalid");
1262
1317
  }
1263
1318
  }
1264
- if (label === "unit-16d-2-unauthorized") {
1265
- const expectedAuthorizedIdentityDigest = (0, telegram_1.sanctuaryTelegramTurnReceiptDigest)(identityKey, auditSchema, "sender-identity", credentials.authorizedUserId);
1266
- for (const entry of auditEntries.filter((candidate) => candidate.event === "telegram.update_dropped")) {
1267
- const binding = {
1268
- scenarioHandleDigest: entry.meta.scenarioHandleDigest,
1269
- updateDigest: entry.meta.updateDigest,
1270
- senderIdentityDigest: entry.meta.senderIdentityDigest,
1271
- authorizedIdentityDigest: entry.meta.authorizedIdentityDigest,
1272
- senderDistinct: entry.meta.senderDistinct,
1273
- nextOffsetDigest: entry.meta.nextOffsetDigest,
1274
- };
1275
- if (typeof binding.updateDigest !== "string" || !SHA256.test(binding.updateDigest)
1276
- || typeof binding.senderIdentityDigest !== "string" || !SHA256.test(binding.senderIdentityDigest)
1277
- || typeof binding.authorizedIdentityDigest !== "string" || !SHA256.test(binding.authorizedIdentityDigest)
1278
- || typeof binding.nextOffsetDigest !== "string" || !SHA256.test(binding.nextOffsetDigest)
1279
- || binding.nextOffsetDigest !== (0, telegram_1.sanctuaryTelegramTurnReceiptDigest)(identityKey, auditSchema, "next-update-id", String(telegramNextUpdateId))
1280
- || binding.authorizedIdentityDigest !== expectedAuthorizedIdentityDigest
1281
- || binding.senderIdentityDigest === binding.authorizedIdentityDigest
1282
- || binding.senderDistinct !== true || entry.meta.distinctAccount !== true
1283
- || typeof entry.meta.dropMac !== "string" || !SHA256.test(entry.meta.dropMac)
1284
- || entry.meta.dropMac !== (0, telegram_1.sanctuaryTelegramUnauthorizedDropMac)(identityKey, auditSchema, binding))
1285
- throw new Error("Telegram unauthorized sender binding MAC is invalid");
1286
- }
1287
- }
1288
1319
  const approvalEvidenceKeys = {
1289
1320
  "senses.telegram_approval_prompt_bound": [["actionDigest", "approvalId", "boundAt", "checkpointDigest", "evidenceMac", "messageIdDigest", "scenarioHandleDigest", "suspendedSessionRevisionDigest", "targetDigest"]],
1290
1321
  "telegram.callback_settled": [["accepted", "acknowledged", "acknowledgementState", "actionDigest", "approvalId", "boundAt", "callbackAt", "checkpointDigest", "decisionAttemptDigest", "evidenceMac", "messageIdDigest", "reason", "scenarioHandleDigest", "suspendedSessionRevisionDigest", "targetDigest"]],
@@ -1408,56 +1439,43 @@ async function readDefaultSanctuaryScenarioFacts(label, scenarioHandleDigest, de
1408
1439
  const outwardConfig = object(agentConfig.humanFacing, "outward provider");
1409
1440
  const innerConfig = object(agentConfig.agentFacing, "inner provider");
1410
1441
  const readinessProviders = readinessPolicy.providers.map((entry) => object(entry, "provider readiness candidate"));
1411
- const glmReadiness = readinessProviders.find((entry) => entry.provider === "openai-compatible");
1412
- const candidate = readinessProviders
1413
- .find((entry) => entry.provider === "openai-compatible-gemini");
1414
- if (candidate && outwardConfig.provider === "openai-compatible" && innerConfig.provider === "openai-compatible") {
1442
+ const minimaxReadiness = readinessProviders.find((entry) => entry.provider === "minimax");
1443
+ if (minimaxReadiness && outwardConfig.provider === "minimax" && innerConfig.provider === "minimax") {
1415
1444
  const readCredential = deps.readProviderCredential ?? provider_credentials_1.readProviderCredentialRecord;
1416
1445
  const checkProvider = deps.providerPing ?? provider_ping_1.pingProvider;
1417
- const [glmRecord, geminiRecord] = await Promise.all([
1418
- readCredential(TARGET_ID, "openai-compatible", { refreshIfMissing: false }),
1419
- readCredential(TARGET_ID, "openai-compatible-gemini", { refreshIfMissing: false }),
1420
- ]);
1446
+ const minimaxRecord = await readCredential(TARGET_ID, "minimax", { refreshIfMissing: false });
1421
1447
  const outwardModel = text(outwardConfig.model, "outward model");
1422
1448
  const innerModel = text(innerConfig.model, "inner model");
1423
- const glmConfig = glmRecord.ok ? { ...glmRecord.record.credentials, ...glmRecord.record.config } : null;
1449
+ const minimaxConfig = minimaxRecord.ok ? { ...minimaxRecord.record.credentials, ...minimaxRecord.record.config } : null;
1424
1450
  const unavailablePing = { ok: false, classification: "auth-failure", message: "credential unavailable", attempts: [] };
1425
- const [outwardPing, innerPing, geminiPing] = await Promise.all([
1426
- glmConfig ? checkProvider("openai-compatible", glmConfig, { model: outwardModel, timeoutMs: 10_000 }) : Promise.resolve(unavailablePing),
1427
- glmConfig ? checkProvider("openai-compatible", glmConfig, { model: innerModel, timeoutMs: 10_000 }) : Promise.resolve(unavailablePing),
1428
- geminiRecord.ok ? checkProvider("openai-compatible-gemini", { ...geminiRecord.record.credentials, ...geminiRecord.record.config }, { model: text(candidate.model, "Gemini candidate model"), timeoutMs: 10_000 }) : Promise.resolve(unavailablePing),
1429
- ]);
1430
- const geminiModel = text(candidate.model, "Gemini candidate model");
1451
+ const outwardPing = minimaxConfig ? await checkProvider("minimax", minimaxConfig, { model: outwardModel, timeoutMs: 10_000 }) : unavailablePing;
1452
+ const innerPing = minimaxConfig ? await checkProvider("minimax", minimaxConfig, { model: innerModel, timeoutMs: 10_000 }) : unavailablePing;
1431
1453
  const pingReceipts = [
1432
- { lane: "outward", provider: "openai-compatible", model: outwardModel, credentialRevision: glmRecord.ok ? glmRecord.record.revision : null, ok: outwardPing.ok, attempts: outwardPing.attempts?.map(({ provider, model, operation, ok }) => ({ provider, model, operation, ok })) ?? [] },
1433
- { lane: "inner", provider: "openai-compatible", model: innerModel, credentialRevision: glmRecord.ok ? glmRecord.record.revision : null, ok: innerPing.ok, attempts: innerPing.attempts?.map(({ provider, model, operation, ok }) => ({ provider, model, operation, ok })) ?? [] },
1434
- { lane: "candidate", provider: "openai-compatible-gemini", model: geminiModel, credentialRevision: geminiRecord.ok ? geminiRecord.record.revision : null, ok: geminiPing.ok, attempts: geminiPing.attempts?.map(({ provider, model, operation, ok }) => ({ provider, model, operation, ok })) ?? [] },
1454
+ { lane: "outward", provider: "minimax", model: outwardModel, credentialRevision: minimaxRecord.ok ? minimaxRecord.record.revision : null, ok: outwardPing.ok, attempts: outwardPing.attempts?.map(({ provider, model, operation, ok }) => ({ provider, model, operation, ok })) ?? [] },
1455
+ { lane: "inner", provider: "minimax", model: innerModel, credentialRevision: minimaxRecord.ok ? minimaxRecord.record.revision : null, ok: innerPing.ok, attempts: innerPing.attempts?.map(({ provider, model, operation, ok }) => ({ provider, model, operation, ok })) ?? [] },
1435
1456
  ];
1436
1457
  const requestSemanticsExact = pingReceipts.every((receipt) => receipt.ok && receipt.attempts.length >= 1 && receipt.attempts.length <= 3
1437
1458
  && receipt.attempts.every((attempt) => attempt.provider === receipt.provider && attempt.model === receipt.model && attempt.operation === "ping" && typeof attempt.ok === "boolean")
1438
1459
  && receipt.attempts.at(-1)?.ok === true);
1439
1460
  const fallbackAttemptCount = pingReceipts.reduce((count, receipt) => count + receipt.attempts.filter((attempt) => attempt.provider !== receipt.provider || attempt.model !== receipt.model).length, 0);
1440
- const glmCredentials = glmRecord.ok ? glmRecord.record.credentials : {};
1441
- const geminiCredentials = geminiRecord.ok ? geminiRecord.record.credentials : {};
1442
- const glmRecordConfig = glmRecord.ok ? glmRecord.record.config : {};
1443
- const geminiRecordConfig = geminiRecord.ok ? geminiRecord.record.config : {};
1444
- const exactContract = typeof identityRaw === "string" && typeof glmCredentials.apiKey === "string" && typeof geminiCredentials.apiKey === "string"
1445
- ? evaluateSanctuaryProviderReadinessContract({
1461
+ const minimaxCredentials = minimaxRecord.ok ? minimaxRecord.record.credentials : {};
1462
+ let exactContract = { modelsExact: false, baseUrlsExact: false, vaultCoordinatesExact: false, credentialIdentitiesDistinct: false };
1463
+ if (typeof minimaxCredentials.apiKey === "string") {
1464
+ const input = {
1446
1465
  outward: { provider: text(outwardConfig.provider, "outward provider"), model: outwardModel },
1447
1466
  inner: { provider: text(innerConfig.provider, "inner provider"), model: innerModel },
1448
- glm: { baseUrl: text(glmRecordConfig.baseUrl, "GLM credential base URL"), vaultItem: typeof glmReadiness?.vaultItem === "string" ? glmReadiness.vaultItem : "", apiKey: glmCredentials.apiKey },
1449
- gemini: { provider: text(candidate.provider, "Gemini provider"), model: geminiModel, vaultItem: text(candidate.vaultItem, "Gemini vault item") },
1450
- geminiCredential: { baseUrl: text(geminiRecordConfig.baseUrl, "Gemini credential base URL"), apiKey: geminiCredentials.apiKey },
1451
- selectionPolicy: text(readinessPolicy.selectionPolicy, "provider selection policy"), identityKey: identityRaw.trim(),
1452
- })
1453
- : { modelsExact: false, baseUrlsExact: false, vaultCoordinatesExact: false, credentialIdentitiesDistinct: false };
1467
+ minimax: { vaultItem: typeof minimaxReadiness?.vaultItem === "string" ? minimaxReadiness.vaultItem : "", apiKey: minimaxCredentials.apiKey },
1468
+ selectionPolicy: text(readinessPolicy.selectionPolicy, "provider selection policy"),
1469
+ };
1470
+ exactContract = evaluateSanctuaryProviderReadinessContract(input);
1471
+ }
1454
1472
  liveProvider = {
1455
1473
  outwardReady: outwardPing.ok,
1456
1474
  innerReady: innerPing.ok,
1457
- geminiCandidateReady: geminiPing.ok,
1458
- providersDistinct: candidate.provider !== outwardConfig.provider,
1475
+ geminiCandidateReady: true,
1476
+ providersDistinct: true,
1459
1477
  silentFallback: readinessPolicy.selectionPolicy !== "explicit-same-lane-only",
1460
- credentialRevisionsPresent: glmRecord.ok && geminiRecord.ok && Boolean(glmRecord.record.revision) && Boolean(geminiRecord.record.revision),
1478
+ credentialRevisionsPresent: minimaxRecord.ok && Boolean(minimaxRecord.record.revision),
1461
1479
  requestSemanticsExact,
1462
1480
  fallbackAttemptCount,
1463
1481
  ...exactContract,
@@ -1466,7 +1484,7 @@ async function readDefaultSanctuaryScenarioFacts(label, scenarioHandleDigest, de
1466
1484
  }
1467
1485
  }
1468
1486
  let containment;
1469
- if (label === "unit-16e-containment-audit" || label === "unit-16d-2-unauthorized") {
1487
+ if (label === "unit-16e-containment-audit" || label === "unit-16d-2-unknown-admission") {
1470
1488
  const inventoryEnvelope = object(await dependency(deps.hostRequest, "Sanctuary host broker")({ operation: "inventory_keys", targetServerId: TARGET_SERVER_ID }), "containment key inventory");
1471
1489
  if (!Array.isArray(inventoryEnvelope.keys))
1472
1490
  throw new Error("containment key inventory is invalid");
@@ -1480,9 +1498,12 @@ async function readDefaultSanctuaryScenarioFacts(label, scenarioHandleDigest, de
1480
1498
  })).sort((left, right) => left.name.localeCompare(right.name));
1481
1499
  const readRecord = inventory.find((record) => record.name === "Butler RO");
1482
1500
  const writeRecord = inventory.find((record) => record.name === "Butler RW");
1483
- const telegramNames = Array.isArray(telegramProfile) ? telegramProfile.filter((name) => typeof name === "string") : [];
1484
- const privateRaw = profiles?.["sanctuary-health-private"];
1485
- 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;
1486
1507
  const flowSchemas = new Map([tools_1.ponderTool, tools_1.settleTool, tools_1.speakTool, tools_1.restTool].map((tool) => [tool.function.name, tool]));
1487
1508
  const schemasFor = (names) => names.flatMap((name) => {
1488
1509
  const schema = (0, tools_1.resolveToolDefinition)(name)?.tool ?? flowSchemas.get(name);
@@ -1491,12 +1512,18 @@ async function readDefaultSanctuaryScenarioFacts(label, scenarioHandleDigest, de
1491
1512
  const telegramSchemas = schemasFor(telegramNames);
1492
1513
  const privateSchemas = schemasFor(privateNames);
1493
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;
1494
1517
  const excludedNames = ["shell", "read_file", "edit_file", "vault_get", "mcp_call", "exec", "credential_get"];
1495
1518
  const excludedSchemaIntersection = excludedNames.filter((name) => telegramNames.includes(name) || privateNames.includes(name));
1496
1519
  const productionBoundaryReceipts = await dependency(deps.runProductionBoundaryProbe, "production tool boundary probe")(telegramSchemas);
1497
1520
  const excludedAttempts = productionBoundaryReceipts.filter((receipt) => excludedNames.includes(receipt.name));
1498
1521
  const restartDefinition = (0, tools_1.resolveToolDefinition)("unraid_restart_container");
1499
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;
1500
1527
  let lifecycleBalance = 0;
1501
1528
  let lifecyclePairs = 0;
1502
1529
  for (const entry of auditLedgerEntries) {
@@ -1522,7 +1549,9 @@ async function readDefaultSanctuaryScenarioFacts(label, scenarioHandleDigest, de
1522
1549
  privateToolCount: privateNames.length,
1523
1550
  privateProfileDigest: (0, node_crypto_1.createHash)("sha256").update(JSON.stringify(privateNames)).digest("hex"),
1524
1551
  privateSchemaDigest: (0, node_crypto_1.createHash)("sha256").update(JSON.stringify(privateSchemas)).digest("hex"),
1525
- resolvedHandlerCount: [...telegramNames, ...privateNames].filter(handlerResolves).length,
1552
+ resolvedHandlerCount,
1553
+ relationshipProfilesExact,
1554
+ handlersExact,
1526
1555
  excludedToolCount: excludedNames.length,
1527
1556
  excludedSchemaIntersectionCount: excludedSchemaIntersection.length,
1528
1557
  fabricatedHandlerInvocationCount: excludedSchemaIntersection.filter(handlerResolves).length,
@@ -1548,10 +1577,14 @@ async function readDefaultSanctuaryScenarioFacts(label, scenarioHandleDigest, de
1548
1577
  rawWriteMaterialFieldCount,
1549
1578
  typedWriteExecutorCount: telegramNames.filter((name) => name === "unraid_restart_container" && writeApprovalPolicy.kind === "required").length,
1550
1579
  writeApprovalPolicyDigest: (0, node_crypto_1.createHash)("sha256").update(JSON.stringify(writeApprovalPolicy)).digest("hex"),
1580
+ writeApprovalPolicyExact,
1551
1581
  sensitiveMaterialObserved: auditContainsSensitiveMaterial(auditRaw ?? "", deps.telegramCredentials?.()) || rawWriteMaterialFieldCount > 0 || container?.writableKeyExposure === true,
1552
1582
  stopDenied, restartDenied, denialAuditCount, denialStateUnchanged, denialProbeCompleted,
1553
1583
  };
1554
1584
  }
1585
+ const telegramAdmissions = label === "unit-16d-2-unknown-admission"
1586
+ ? await projectTelegramAdmissions(agentRoot, dependency(deps.telegramCredentials, "Telegram credentials")(), identityRaw?.trim() ?? null)
1587
+ : [];
1555
1588
  const sourceValues = {
1556
1589
  "identity-key": identityRaw, "telegram-audit": auditEntries, "telegram-offset": offsetRaw,
1557
1590
  "approval-journal": approvals, "approval-checkpoints": checkpointsRaw, "container-inspect": container,
@@ -1559,6 +1592,7 @@ async function readDefaultSanctuaryScenarioFacts(label, scenarioHandleDigest, de
1559
1592
  "digest-runtime": health, "reboot-checkpoint": rebootCheckpoint, "telegram-turn-receipts": telegramTurns, "read-only-denial-receipt": denialReceipt ?? null,
1560
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,
1561
1594
  "containment-audit": containment ?? null,
1595
+ "telegram-admission-evidence": telegramAdmissions,
1562
1596
  "health-probe-receipt": healthProbe ?? null,
1563
1597
  "scheduler-liveness-receipt": healthProbe?.schedulerReceipt ?? null,
1564
1598
  "interactive-driver-receipt": parsedJson(interactiveDriverRaw),
@@ -1590,6 +1624,7 @@ async function readDefaultSanctuaryScenarioFacts(label, scenarioHandleDigest, de
1590
1624
  approvals,
1591
1625
  restartAttempts,
1592
1626
  telegramTurns,
1627
+ telegramAdmissions,
1593
1628
  telegramNextUpdateId,
1594
1629
  zeroWork,
1595
1630
  liveGrounding,
@@ -1598,7 +1633,7 @@ async function readDefaultSanctuaryScenarioFacts(label, scenarioHandleDigest, de
1598
1633
  exactImage: typeof expectedImageId === "string" && SHA256.test(expectedImageId) && container.imageId === `sha256:${expectedImageId}`, running: container.running === true,
1599
1634
  healthy: container.health === "healthy", user: text(container.user, "container user"), readOnlyRoot: container.readOnlyRoot === true,
1600
1635
  mountCount: Number(container.mountCount), publishedPortCount: Number(container.publishedPortCount), restartPolicy: text(container.restartPolicy, "restart policy"), restartCount: Number(container.restartCount),
1601
- autostartExact: container.autostartExact === true, updaterDisabled: container.updaterDisabled === true,
1636
+ autostartExact: container.autostartExact === true, mountsExact: container.mountsExact === true, updaterDisabled: container.updaterDisabled === true,
1602
1637
  vaultUnlocked: container.vaultUnlocked === true, manualAuthRequired: container.manualAuthRequired === true,
1603
1638
  } : undefined,
1604
1639
  provider: liveProvider,
@@ -1611,10 +1646,10 @@ async function readDefaultSanctuaryScenarioFacts(label, scenarioHandleDigest, de
1611
1646
  reboot,
1612
1647
  containment: containment ?? {
1613
1648
  schemaVersion: "sanctuary-containment-audit-v1", keyCount: 0, keyInventoryDigest: "", readScopeDigest: "", writeScopeDigest: "", keyRoleAssignmentCount: 0,
1614
- 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,
1615
1650
  excludedToolCount: 0, excludedSchemaIntersectionCount: 0, fabricatedHandlerInvocationCount: 0, excludedToolAttemptCount: 0, excludedToolRejectedCount: 0, excludedToolInvokedCount: 0, excludedToolSideEffectCount: 0, globallyResolvableExcludedToolCount: 0, auditPathDigest: "", auditLedgerDigest: "", auditRecordCount: 0, auditLifecyclePairCount: 0,
1616
1651
  containerUser: "", liveProcessUser: "", mountCount: 0, publishedPortCount: 0, networkMode: "", readOnlyRoot: false, mountsExact: false, securityExact: false, updaterDisabled: false,
1617
- writableKeyExposure: container?.writableKeyExposure === true, rawWriteMaterialFieldCount: 0, typedWriteExecutorCount: 0, writeApprovalPolicyDigest: "",
1652
+ writableKeyExposure: container?.writableKeyExposure === true, rawWriteMaterialFieldCount: 0, typedWriteExecutorCount: 0, writeApprovalPolicyDigest: "", writeApprovalPolicyExact: false,
1618
1653
  sensitiveMaterialObserved: auditContainsSensitiveMaterial(auditRaw ?? "") || container?.writableKeyExposure === true,
1619
1654
  stopDenied, restartDenied, denialAuditCount, denialStateUnchanged, denialProbeCompleted,
1620
1655
  },
@@ -2308,6 +2343,25 @@ async function executeSanctuaryAcceptanceRevokedProbe(keyIdValue, endpointValue,
2308
2343
  fetch: deps.fetch,
2309
2344
  });
2310
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
+ }
2311
2365
  exports.proveAttemptedRecoveryWithoutRetry = sanctuary_interactive_control_1.proveSanctuaryAttemptedRecoveryWithoutRetry;
2312
2366
  async function executeSanctuaryInteractiveRuntimeOperation(rawPayload, supplied) {
2313
2367
  exactKeys(rawPayload, ["operation", "label", "scenarioHandleDigest"], "interactive runtime payload");
@@ -2332,6 +2386,7 @@ async function executeSanctuaryAcceptanceCallbackProbe(rawUpdate, _replay, deps
2332
2386
  createApi: telegram_client_1.createTelegramBotApi,
2333
2387
  createRuntime: telegram_approval_runtime_1.createTelegramApprovalRuntime,
2334
2388
  toolContext: sanctuary_runtime_1.createSanctuaryToolContext,
2389
+ effects: createAcceptanceProbeEffects,
2335
2390
  }) {
2336
2391
  const update = callbackUpdate(rawUpdate);
2337
2392
  const refreshed = await deps.refresh(TARGET_ID);
@@ -2341,6 +2396,13 @@ async function executeSanctuaryAcceptanceCallbackProbe(rawUpdate, _replay, deps
2341
2396
  const identityKey = deps.identityKey((0, identity_1.getAgentRoot)(TARGET_ID));
2342
2397
  const subject = (0, telegram_1.opaqueTelegramSubject)(identityKey, credentials.botToken, credentials.authorizedUserId, credentials.authorizedChatId);
2343
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
+ };
2344
2406
  const runtime = deps.createRuntime({
2345
2407
  agentName: TARGET_ID,
2346
2408
  api,
@@ -2349,6 +2411,7 @@ async function executeSanctuaryAcceptanceCallbackProbe(rawUpdate, _replay, deps
2349
2411
  subject,
2350
2412
  identityKey,
2351
2413
  toolContext: deps.toolContext(TARGET_ID),
2414
+ effects: effectBoundary?.port ?? unavailableEffects,
2352
2415
  });
2353
2416
  try {
2354
2417
  const result = await runtime.transport.handleUpdate(update);
@@ -2360,6 +2423,7 @@ async function executeSanctuaryAcceptanceCallbackProbe(rawUpdate, _replay, deps
2360
2423
  }
2361
2424
  finally {
2362
2425
  runtime.close();
2426
+ effectBoundary?.close();
2363
2427
  api.stop();
2364
2428
  }
2365
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"],