@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
@@ -106,7 +106,7 @@ function verifySanctuaryPostbootIntegrity(before, after, scenarioHandleDigest, o
106
106
  const HEALTH_SCENARIO_LABELS = new Set([
107
107
  "unit-16f-cron-fingerprint",
108
108
  "unit-16g-health-transition",
109
- "unit-16h-daily-digest",
109
+ "unit-16h-acceptance-delivery-probe",
110
110
  ]);
111
111
  const INTERACTIVE_DRIVER_LABELS = new Set([
112
112
  "unit-16l-duplicate-callback",
@@ -121,27 +121,19 @@ function hash(value) {
121
121
  }
122
122
  const CONTAINMENT_READ_SCOPE = ["ARRAY", "DASHBOARD", "DISK", "DOCKER", "INFO", "LOGS", "NOTIFICATIONS", "SHARE", "VARS"]
123
123
  .map((resource) => `${resource}:READ_ANY`).sort();
124
- const CONTAINMENT_TELEGRAM_PROFILE = ["unraid_list_containers", "unraid_get_container_logs", "unraid_get_storage", "unraid_get_disks", "unraid_get_notifications", "unraid_get_system", "unraid_restart_container", "ponder", "settle", "speak"];
125
- const CONTAINMENT_PRIVATE_PROFILE = ["send_message", "rest"];
126
124
  const CONTAINMENT_EXCLUDED_TOOLS = ["shell", "read_file", "edit_file", "vault_get", "mcp_call", "exec", "credential_get"];
127
125
  const CONTAINMENT_AUDIT_PATH = "/home/ouro/AgentBundles/sanctuary.ouro/state/acceptance/telegram-audit-chain.ndjson";
128
- const CONTAINMENT_WRITE_POLICY = { kind: "required", policyId: "sanctuary.unraid.restart.v1", actionClass: "unraid.container.restart", requiresSoleCall: true };
129
- const CONTAINMENT_TELEGRAM_SCHEMA_DIGEST = "3c66299a5f70ec82f8795cae47659284e6dbc691ef49002c2fb22edba76c59b6";
130
- const CONTAINMENT_PRIVATE_SCHEMA_DIGEST = "61b137b2467acbcf22ca7443ee01e71ed970a62728c42aabffbdcb562f4a6a70";
131
126
  function exactContainmentAudit(evidence) {
132
- const digestFields = [evidence.keyInventoryDigest, evidence.telegramSchemaDigest, evidence.privateSchemaDigest, evidence.auditLedgerDigest];
127
+ const digestFields = [evidence.keyInventoryDigest, evidence.telegramProfileDigest, evidence.telegramSchemaDigest, evidence.privateProfileDigest, evidence.privateSchemaDigest, evidence.auditLedgerDigest, evidence.writeApprovalPolicyDigest];
133
128
  return evidence.schemaVersion === "sanctuary-containment-audit-v1"
134
129
  && evidence.keyCount === 2
135
130
  && evidence.readScopeDigest === hash(CONTAINMENT_READ_SCOPE)
136
131
  && evidence.writeScopeDigest === hash([...CONTAINMENT_READ_SCOPE, "DOCKER:UPDATE_ANY"].sort())
137
132
  && evidence.keyRoleAssignmentCount === 0
138
- && evidence.telegramToolCount === CONTAINMENT_TELEGRAM_PROFILE.length
139
- && evidence.telegramProfileDigest === hash(CONTAINMENT_TELEGRAM_PROFILE)
140
- && evidence.telegramSchemaDigest === CONTAINMENT_TELEGRAM_SCHEMA_DIGEST
141
- && evidence.privateToolCount === CONTAINMENT_PRIVATE_PROFILE.length
142
- && evidence.privateProfileDigest === hash(CONTAINMENT_PRIVATE_PROFILE)
143
- && evidence.privateSchemaDigest === CONTAINMENT_PRIVATE_SCHEMA_DIGEST
144
- && evidence.resolvedHandlerCount === CONTAINMENT_TELEGRAM_PROFILE.length + CONTAINMENT_PRIVATE_PROFILE.length
133
+ && Number.isSafeInteger(evidence.telegramToolCount) && evidence.telegramToolCount > 0
134
+ && Number.isSafeInteger(evidence.privateToolCount) && evidence.privateToolCount > 0
135
+ && evidence.relationshipProfilesExact && evidence.handlersExact
136
+ && evidence.resolvedHandlerCount === evidence.telegramToolCount + evidence.privateToolCount
145
137
  && evidence.excludedToolCount === CONTAINMENT_EXCLUDED_TOOLS.length
146
138
  && evidence.excludedSchemaIntersectionCount === 0
147
139
  && evidence.fabricatedHandlerInvocationCount === 0
@@ -151,10 +143,10 @@ function exactContainmentAudit(evidence) {
151
143
  && evidence.globallyResolvableExcludedToolCount >= 1
152
144
  && evidence.auditPathDigest === (0, node_crypto_1.createHash)("sha256").update(CONTAINMENT_AUDIT_PATH).digest("hex")
153
145
  && evidence.auditRecordCount >= 2 && evidence.auditLifecyclePairCount >= 1
154
- && evidence.containerUser === "10001:10001" && evidence.liveProcessUser === "10001:10001" && evidence.mountCount === 2 && evidence.publishedPortCount === 0
146
+ && evidence.containerUser === "10001:10001" && evidence.liveProcessUser === "10001:10001" && evidence.mountCount === 4 && evidence.publishedPortCount === 0
155
147
  && evidence.networkMode === "host" && evidence.readOnlyRoot && evidence.mountsExact && evidence.securityExact && evidence.updaterDisabled
156
148
  && !evidence.writableKeyExposure && evidence.rawWriteMaterialFieldCount === 0 && evidence.typedWriteExecutorCount === 1
157
- && evidence.writeApprovalPolicyDigest === hash(CONTAINMENT_WRITE_POLICY) && !evidence.sensitiveMaterialObserved
149
+ && evidence.writeApprovalPolicyExact && !evidence.sensitiveMaterialObserved
158
150
  && digestFields.every((value) => SHA256.test(value));
159
151
  }
160
152
  function atomicPrivateJson(filePath, value) {
@@ -444,12 +436,12 @@ function deriveSanctuaryScenarioAssertions(label, before, after, _now, scenarioH
444
436
  case "unit-16b-runtime-vault-containment":
445
437
  if (!after.container)
446
438
  return null;
447
- if (!after.container.autostartExact || !after.container.exactImage || after.container.manualAuthRequired
448
- || after.container.mountCount !== 2 || Number(after.container.user.split(":")[0]) !== 10001
439
+ if (!after.container.autostartExact || !after.container.exactImage || !after.container.mountsExact || after.container.manualAuthRequired
440
+ || after.container.mountCount !== 4 || Number(after.container.user.split(":")[0]) !== 10001
449
441
  || after.container.publishedPortCount !== 0 || !after.container.readOnlyRoot
450
442
  || !after.container.updaterDisabled || !after.container.vaultUnlocked)
451
443
  return null;
452
- return { autostartExact: after.container.autostartExact, exactImage: after.container.exactImage, manualAuthRequired: after.container.manualAuthRequired, mountCount: after.container.mountCount, nonRootUid: Number(after.container.user.split(":")[0]), publishedPortCount: after.container.publishedPortCount, readOnlyRoot: after.container.readOnlyRoot, updaterDisabled: after.container.updaterDisabled, vaultUnlocked: after.container.vaultUnlocked };
444
+ return { autostartExact: after.container.autostartExact, exactImage: after.container.exactImage, manualAuthRequired: after.container.manualAuthRequired, mountCount: after.container.mountCount, mountsExact: after.container.mountsExact, nonRootUid: Number(after.container.user.split(":")[0]), publishedPortCount: after.container.publishedPortCount, readOnlyRoot: after.container.readOnlyRoot, updaterDisabled: after.container.updaterDisabled, vaultUnlocked: after.container.vaultUnlocked };
453
445
  case "unit-16c-provider-readiness":
454
446
  return after.provider && after.provider.outwardReady && after.provider.innerReady && after.provider.geminiCandidateReady && after.provider.providersDistinct && !after.provider.silentFallback
455
447
  && after.provider.credentialRevisionsPresent === true && after.provider.requestSemanticsExact === true && after.provider.fallbackAttemptCount === 0
@@ -463,45 +455,41 @@ function deriveSanctuaryScenarioAssertions(label, before, after, _now, scenarioH
463
455
  if (telegramResponses !== 1 || newTurns.length !== 1 || !exactFreshTelegramLifecycle(before, after, newTurns[0].updateDigest) || deliveredTurns[0].toolInvocationCount !== 1 || deliveredTurns[0].toolResultDigests.length !== 1 || !turnHasGroundedRead(after, deliveredTurns[0], "unraid_get_storage") || !exactGroundedResponse(after, deliveredTurns[0], "unraid_get_storage"))
464
456
  return null;
465
457
  return { accurate: true, authorized: true, grounded: true, liveFactsMatched: true, mutationCount: scenarioMutationCount, responseCount: telegramResponses, responseWithinLimit: true, telegramDelivered: true };
466
- case "unit-16d-2-unauthorized": {
467
- if (before.events.length > after.events.length || before.events.some((entry, index) => hash(entry) !== hash(after.events[index])))
468
- return null;
458
+ case "unit-16d-2-unknown-admission": {
469
459
  const freshEvents = recordsAdded(before.events, after.events, (entry) => hash(entry));
470
- const freshDrops = freshEvents.filter((entry) => entry.event === "telegram.update_dropped");
471
- if (freshDrops.length !== 1)
472
- return null;
473
- const drop = freshDrops[0];
474
- const distinctAccount = drop.meta.distinctAccount === true
475
- && typeof drop.meta.scenarioHandleDigest === "string" && SHA256.test(drop.meta.scenarioHandleDigest)
476
- && typeof drop.meta.updateDigest === "string" && SHA256.test(drop.meta.updateDigest)
477
- && typeof drop.meta.senderIdentityDigest === "string" && SHA256.test(drop.meta.senderIdentityDigest)
478
- && typeof drop.meta.authorizedIdentityDigest === "string" && SHA256.test(drop.meta.authorizedIdentityDigest)
479
- && drop.meta.senderIdentityDigest !== drop.meta.authorizedIdentityDigest
480
- && drop.meta.senderDistinct === true
481
- && typeof drop.meta.nextOffsetDigest === "string" && SHA256.test(drop.meta.nextOffsetDigest)
482
- && typeof drop.meta.dropMac === "string" && SHA256.test(drop.meta.dropMac);
460
+ const beforeAdmissions = before.telegramAdmissions ?? [];
461
+ const afterAdmissions = after.telegramAdmissions ?? [];
462
+ const newAdmissions = recordsAdded(beforeAdmissions, afterAdmissions, (entry) => entry.admissionDigest);
463
+ const admission = newAdmissions.length === 1 ? newAdmissions[0] : null;
483
464
  const providerInvocationCount = newTurns.reduce((sum, turn) => sum + turn.providerTurnCount, 0);
484
465
  const toolInvocationCount = newTurns.reduce((sum, turn) => sum + turn.toolInvocationCount, 0);
485
466
  const durableToolRecordCount = delta(after, before, "senses.sanctuary_read_receipt");
486
467
  const workItemCount = newApprovals.length;
487
- const sessionStateUnchanged = Boolean(before.identity && after.identity
488
- && typeof before.identity.sessionSurfaceDigest === "string" && SHA256.test(before.identity.sessionSurfaceDigest)
489
- && before.identity.sessionSurfaceDigest === after.identity.sessionSurfaceDigest
490
- && typeof before.identity.friendSurfaceDigest === "string" && SHA256.test(before.identity.friendSurfaceDigest)
491
- && before.identity.friendSurfaceDigest === after.identity.friendSurfaceDigest);
492
- const forbiddenWorkEvent = freshEvents.some((entry) => entry.event === "senses.telegram_turn_start" || entry.event === "senses.telegram_turn_end"
493
- || entry.event === "senses.telegram_turn_error" || entry.event === "senses.sanctuary_read_receipt"
468
+ const forbiddenWorkEvent = freshEvents.some((entry) => entry.event.startsWith("senses.telegram_turn_") || entry.event === "senses.sanctuary_read_receipt"
494
469
  || entry.event.startsWith("senses.telegram_approved_restart_") || entry.event === "senses.sanctuary_health_delivered");
495
470
  const offsetAdvanced = Number.isSafeInteger(before.telegramNextUpdateId) && Number.isSafeInteger(after.telegramNextUpdateId)
496
471
  && Number(after.telegramNextUpdateId) === Number(before.telegramNextUpdateId) + 1;
497
- const zeroWorkUnchanged = Boolean(before.zeroWork && after.zeroWork
472
+ const exactQuarantine = admission?.status === "pending" && admission.identityExact && admission.acknowledgementExact && admission.ownerCardExact
473
+ && admission.contentQuarantined && admission.relationshipAbsent && admission.capturedAt <= admission.updatedAt;
474
+ const zeroAgentWork = Boolean(before.zeroWork && after.zeroWork
498
475
  && before.zeroWork.providerToolDigest === after.zeroWork.providerToolDigest
499
- && before.zeroWork.outwardDigest === after.zeroWork.outwardDigest
500
476
  && before.zeroWork.approvalMutationDigest === after.zeroWork.approvalMutationDigest
501
477
  && before.zeroWork.sessionFriendDigest === after.zeroWork.sessionFriendDigest);
502
- if (!after.containment || !exactContainmentAudit(after.containment) || !distinctAccount || !offsetAdvanced || !zeroWorkUnchanged || !sessionStateUnchanged || newTurns.length !== 0 || providerInvocationCount !== 0 || toolInvocationCount !== 0 || telegramResponses !== 0 || workItemCount !== 0 || approvalTransitions !== 0 || newAttempts.length !== 0 || scenarioMutationCount !== 0 || durableToolRecordCount !== 0 || forbiddenWorkEvent)
478
+ if (!exactQuarantine || !offsetAdvanced || !zeroAgentWork || newTurns.length !== 0 || telegramResponses !== 0
479
+ || providerInvocationCount !== 0 || toolInvocationCount !== 0 || workItemCount !== 0 || approvalTransitions !== 0 || newAttempts.length !== 0
480
+ || scenarioMutationCount !== 0 || durableToolRecordCount !== 0 || forbiddenWorkEvent)
503
481
  return null;
504
- return { auditRejected: true, distinctAccount, mutationCount: 0, providerInvocationCount: 0, responseCount: 0, workItemCount: 0 };
482
+ return {
483
+ acknowledgementSent: true,
484
+ agentTurnCount: 0,
485
+ distinctAccount: true,
486
+ mutationCount: 0,
487
+ ownerCardSent: true,
488
+ providerInvocationCount: 0,
489
+ quarantined: true,
490
+ responseCount: 0,
491
+ workItemCount: 0,
492
+ };
505
493
  }
506
494
  case "unit-16e-containment-audit":
507
495
  if (!after.containment || !exactContainmentAudit(after.containment) || scenarioMutationCount !== 0)
@@ -515,6 +503,7 @@ function deriveSanctuaryScenarioAssertions(label, before, after, _now, scenarioH
515
503
  telegramSchemaDigest: after.containment.telegramSchemaDigest,
516
504
  privateToolCount: after.containment.privateToolCount, privateProfileDigest: after.containment.privateProfileDigest,
517
505
  privateSchemaDigest: after.containment.privateSchemaDigest, resolvedHandlerCount: after.containment.resolvedHandlerCount,
506
+ relationshipProfilesExact: after.containment.relationshipProfilesExact, handlersExact: after.containment.handlersExact,
518
507
  excludedToolCount: after.containment.excludedToolCount, excludedSchemaIntersectionCount: after.containment.excludedSchemaIntersectionCount,
519
508
  fabricatedHandlerInvocationCount: after.containment.fabricatedHandlerInvocationCount,
520
509
  excludedToolAttemptCount: after.containment.excludedToolAttemptCount, excludedToolRejectedCount: after.containment.excludedToolRejectedCount,
@@ -530,6 +519,7 @@ function deriveSanctuaryScenarioAssertions(label, before, after, _now, scenarioH
530
519
  rawWriteMaterialFieldCount: after.containment.rawWriteMaterialFieldCount,
531
520
  typedWriteExecutorCount: after.containment.typedWriteExecutorCount,
532
521
  writeApprovalPolicyDigest: after.containment.writeApprovalPolicyDigest,
522
+ writeApprovalPolicyExact: after.containment.writeApprovalPolicyExact,
533
523
  sensitiveMaterialObserved: after.containment.sensitiveMaterialObserved,
534
524
  mutationCount: scenarioMutationCount,
535
525
  };
@@ -570,16 +560,17 @@ function deriveSanctuaryScenarioAssertions(label, before, after, _now, scenarioH
570
560
  return null;
571
561
  return { alertCount: 3, productionRestored: true, transitionObserved: true };
572
562
  }
573
- case "unit-16h-daily-digest": {
563
+ case "unit-16h-acceptance-delivery-probe": {
574
564
  const probe = after.healthProbe;
575
- if (!after.container?.running || !after.container.healthy || !probe || !healthProbeRestored(probe, after.cron) || probe.clockMode !== "local-daily-boundary" || probe.privateTurnCount !== 1
565
+ if (!after.container?.running || !after.container.healthy || !probe || !healthProbeRestored(probe, after.cron) || probe.acceptanceOnly !== true
566
+ || probe.productionScheduleChanged !== false || probe.clockMode !== "local-daily-boundary" || probe.privateTurnCount !== 1
576
567
  || probe.providerInvocationCount < probe.privateTurnCount || probe.providerInvocationCount > 1_000 || probe.deliveryCount !== 1 || probe.phases.length !== 2
577
- || probe.phases[0]?.ordinal !== 1 || probe.phases[0].name !== "digest-first" || probe.phases[0].trigger !== "acceptance" || probe.phases[0].fixtureStatus !== 503
568
+ || probe.phases[0]?.ordinal !== 1 || probe.phases[0].name !== "delivery-probe-first" || probe.phases[0].trigger !== "acceptance" || probe.phases[0].fixtureStatus !== 503
578
569
  || !probe.phases[0].digestDue || probe.phases[0].deliveryKind !== "digest" || probe.phases[0].deliveryReceiptDigest === null
579
- || probe.phases[1]?.ordinal !== 2 || probe.phases[1].name !== "digest-repeat" || probe.phases[1].trigger !== "acceptance" || probe.phases[1].fixtureStatus !== 503
570
+ || probe.phases[1]?.ordinal !== 2 || probe.phases[1].name !== "delivery-probe-repeat" || probe.phases[1].trigger !== "acceptance" || probe.phases[1].fixtureStatus !== 503
580
571
  || probe.phases[1].digestDue || probe.phases[1].deliveryKind !== null || probe.phases[1].deliveryReceiptDigest !== null)
581
572
  return null;
582
- return { firedWithinMs: 0, messageCount: 1, productionRestored: true, scheduleObserved: true };
573
+ return { acceptanceOnly: true, deliveryPathObserved: true, firedWithinMs: 0, messageCount: 1, productionRestored: true, productionScheduleChanged: false };
583
574
  }
584
575
  case "unit-16i-delayed-approval":
585
576
  if (!exactFreshTelegramLifecycle(before, after) || !intendedRestartApproval(approval) || !completeAttemptLedgerLinked || approval.state !== "succeeded" || mutationCount !== 1 || scenarioMutationCount !== 1 || !restartSucceeded || approval.replayMutationCount !== 0 || !approval.continuationCompleted)
@@ -0,0 +1,398 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.SANCTUARY_BUNDLE_ROLLBACK_FILE = exports.SANCTUARY_PACKAGE_MANAGED_FILES = void 0;
37
+ exports.inspectSanctuaryPackageManagedBundleRollback = inspectSanctuaryPackageManagedBundleRollback;
38
+ exports.rollbackSanctuaryPackageManagedBundle = rollbackSanctuaryPackageManagedBundle;
39
+ exports.commitSanctuaryPackageManagedBundle = commitSanctuaryPackageManagedBundle;
40
+ exports.migrateSanctuaryPackageManagedBundle = migrateSanctuaryPackageManagedBundle;
41
+ const fs = __importStar(require("node:fs"));
42
+ const path = __importStar(require("node:path"));
43
+ const node_crypto_1 = require("node:crypto");
44
+ const runtime_1 = require("../../nerves/runtime");
45
+ const steward_policy_1 = require("../steward-policy");
46
+ exports.SANCTUARY_PACKAGE_MANAGED_FILES = [
47
+ "provider-readiness.json",
48
+ "tool-profiles.json",
49
+ "habits/sanctuary-health.md",
50
+ "psyche/ASPIRATIONS.md",
51
+ "psyche/IDENTITY.md",
52
+ "psyche/LORE.md",
53
+ "psyche/SOUL.md",
54
+ "psyche/TACIT.md",
55
+ ];
56
+ exports.SANCTUARY_BUNDLE_ROLLBACK_FILE = ".sanctuary-package-managed-rollback.json";
57
+ const SANCTUARY_BUNDLE_COMMITTING_FILE = `${exports.SANCTUARY_BUNDLE_ROLLBACK_FILE}.committing`;
58
+ const BUNDLE_META_VERSION_FIELDS = ["runtimeVersion", "bundleSchemaVersion", "lastUpdated"];
59
+ function readObject(filePath) {
60
+ const value = JSON.parse(fs.readFileSync(filePath, "utf8"));
61
+ if (!value || typeof value !== "object" || Array.isArray(value))
62
+ throw new Error(`${path.basename(filePath)} must contain an object`);
63
+ return value;
64
+ }
65
+ function requirePlainFile(filePath, label) {
66
+ let stat;
67
+ try {
68
+ stat = fs.lstatSync(filePath);
69
+ }
70
+ catch {
71
+ throw new Error(`${label} is missing`);
72
+ }
73
+ if (stat.isSymbolicLink())
74
+ throw new Error(`${label} must not be a symlink`);
75
+ if (!stat.isFile())
76
+ throw new Error(`${label} must be a regular file`);
77
+ }
78
+ function ensureSafeParent(agentRoot, relative) {
79
+ let current = agentRoot;
80
+ for (const segment of path.dirname(relative).split(path.sep).filter((value) => value !== ".")) {
81
+ current = path.join(current, segment);
82
+ if (!fs.existsSync(current))
83
+ fs.mkdirSync(current, { mode: 0o700 });
84
+ }
85
+ }
86
+ function validateDestination(agentRoot, relative) {
87
+ let current = agentRoot;
88
+ for (const segment of relative.split(path.sep)) {
89
+ current = path.join(current, segment);
90
+ let stat;
91
+ try {
92
+ stat = fs.lstatSync(current);
93
+ }
94
+ catch {
95
+ return;
96
+ }
97
+ if (stat.isSymbolicLink())
98
+ throw new Error(`package-managed destination must not be a symlink: ${relative}`);
99
+ if (current !== path.join(agentRoot, relative) && !stat.isDirectory())
100
+ throw new Error(`package-managed destination parent must be a directory: ${relative}`);
101
+ if (current === path.join(agentRoot, relative) && !stat.isFile())
102
+ throw new Error(`package-managed destination must be a regular file: ${relative}`);
103
+ }
104
+ }
105
+ function writeAtomic(filePath, content) {
106
+ const stagingDirectory = fs.mkdtempSync(`${filePath}.package-migration.`);
107
+ const temporary = path.join(stagingDirectory, "value");
108
+ try {
109
+ const fd = fs.openSync(temporary, "wx", 0o600);
110
+ try {
111
+ fs.writeFileSync(fd, content);
112
+ fs.fsyncSync(fd);
113
+ }
114
+ finally {
115
+ fs.closeSync(fd);
116
+ }
117
+ fs.renameSync(temporary, filePath);
118
+ }
119
+ finally {
120
+ fs.rmSync(temporary, { force: true });
121
+ fs.rmdirSync(stagingDirectory);
122
+ }
123
+ fs.chmodSync(filePath, 0o600);
124
+ const directory = fs.openSync(path.dirname(filePath), fs.constants.O_RDONLY | fs.constants.O_DIRECTORY | fs.constants.O_NOFOLLOW);
125
+ try {
126
+ fs.fsyncSync(directory);
127
+ }
128
+ finally {
129
+ fs.closeSync(directory);
130
+ }
131
+ }
132
+ function cleanupInterruptedWrites(filePath) {
133
+ const parent = path.dirname(filePath);
134
+ const prefix = `${path.basename(filePath)}.package-migration.`;
135
+ if (!fs.existsSync(parent))
136
+ return;
137
+ for (const name of fs.readdirSync(parent)) {
138
+ if (!name.startsWith(prefix))
139
+ continue;
140
+ const stagingDirectory = path.join(parent, name);
141
+ const stagingStat = fs.lstatSync(stagingDirectory);
142
+ if (stagingStat.isSymbolicLink() || !stagingStat.isDirectory())
143
+ throw new Error(`interrupted package migration stage is invalid: ${name}`);
144
+ const entries = fs.readdirSync(stagingDirectory);
145
+ if (entries.some((entry) => entry !== "value"))
146
+ throw new Error(`interrupted package migration stage contains unexpected entries: ${name}`);
147
+ const temporary = path.join(stagingDirectory, "value");
148
+ if (fs.existsSync(temporary)) {
149
+ const temporaryStat = fs.lstatSync(temporary);
150
+ if (temporaryStat.isSymbolicLink() || !temporaryStat.isFile())
151
+ throw new Error(`interrupted package migration value is invalid: ${name}`);
152
+ fs.unlinkSync(temporary);
153
+ }
154
+ fs.rmdirSync(stagingDirectory);
155
+ }
156
+ }
157
+ const SNAPSHOT_RELATIVES = [...exports.SANCTUARY_PACKAGE_MANAGED_FILES, "bundle-meta.json"];
158
+ function snapshotDirectoryRelatives() {
159
+ const directories = new Set();
160
+ for (const relative of SNAPSHOT_RELATIVES) {
161
+ let directory = path.dirname(relative);
162
+ while (directory !== ".") {
163
+ directories.add(directory);
164
+ directory = path.dirname(directory);
165
+ }
166
+ }
167
+ return [...directories].sort((left, right) => left.length - right.length);
168
+ }
169
+ function captureMigrationSnapshot(agentRoot) {
170
+ const files = SNAPSHOT_RELATIVES.map((relative) => {
171
+ const filePath = path.join(agentRoot, relative);
172
+ if (!fs.existsSync(filePath))
173
+ return { path: filePath, content: null, mode: null };
174
+ const stat = fs.lstatSync(filePath);
175
+ return { path: filePath, content: fs.readFileSync(filePath), mode: stat.mode & 0o777 };
176
+ });
177
+ const directories = snapshotDirectoryRelatives().map((relative) => {
178
+ const directory = path.join(agentRoot, relative);
179
+ if (!fs.existsSync(directory))
180
+ return { path: directory, mode: null };
181
+ const stat = fs.lstatSync(directory);
182
+ return { path: directory, mode: stat.mode & 0o777 };
183
+ });
184
+ return { files, directories };
185
+ }
186
+ function rollbackPath(agentRoot) {
187
+ return path.join(agentRoot, exports.SANCTUARY_BUNDLE_ROLLBACK_FILE);
188
+ }
189
+ function durableSnapshot(agentRoot, snapshot, rollbackImageId, targetImageId) {
190
+ const payload = {
191
+ schemaVersion: 1,
192
+ rollbackImageId,
193
+ targetImageId,
194
+ files: snapshot.files.map((file) => ({ relative: path.relative(agentRoot, file.path), contentBase64: file.content?.toString("base64") ?? null, mode: file.mode })),
195
+ directories: snapshot.directories.map((directory) => ({ relative: path.relative(agentRoot, directory.path), mode: directory.mode })),
196
+ };
197
+ return { ...payload, snapshotDigest: (0, node_crypto_1.createHash)("sha256").update(JSON.stringify(payload)).digest("hex") };
198
+ }
199
+ function readDurableRecord(agentRoot) {
200
+ const candidates = [rollbackPath(agentRoot), path.join(agentRoot, SANCTUARY_BUNDLE_COMMITTING_FILE)].filter((candidate) => fs.existsSync(candidate));
201
+ if (candidates.length === 0)
202
+ return null;
203
+ if (candidates.length !== 1)
204
+ throw new Error("Sanctuary bundle rollback record state is ambiguous");
205
+ const [filePath] = candidates;
206
+ const stat = fs.lstatSync(filePath);
207
+ if (stat.isSymbolicLink())
208
+ throw new Error("Sanctuary bundle rollback record must not be a symlink");
209
+ if (!stat.isFile() || (stat.mode & 0o777) !== 0o600)
210
+ throw new Error("Sanctuary bundle rollback record must be a mode-0600 regular file");
211
+ let value;
212
+ try {
213
+ value = JSON.parse(fs.readFileSync(filePath, "utf8"));
214
+ }
215
+ catch {
216
+ throw new Error("Sanctuary bundle rollback record is invalid JSON");
217
+ }
218
+ if (!value || typeof value !== "object" || Array.isArray(value))
219
+ throw new Error("Sanctuary bundle rollback record is invalid");
220
+ const record = value;
221
+ if (record.schemaVersion !== 1 || typeof record.rollbackImageId !== "string" || !/^sha256:[0-9a-f]{64}$/u.test(record.rollbackImageId) || typeof record.targetImageId !== "string" || !/^sha256:[0-9a-f]{64}$/u.test(record.targetImageId) || record.targetImageId === record.rollbackImageId || typeof record.snapshotDigest !== "string" || !/^[0-9a-f]{64}$/u.test(record.snapshotDigest) || !Array.isArray(record.files) || !Array.isArray(record.directories))
222
+ throw new Error("Sanctuary bundle rollback record is invalid");
223
+ const fileRelatives = record.files.map((file) => file?.relative);
224
+ const directoryRelatives = record.directories.map((directory) => directory?.relative);
225
+ const expectedDirectories = snapshotDirectoryRelatives();
226
+ if (JSON.stringify(fileRelatives) !== JSON.stringify(SNAPSHOT_RELATIVES) || JSON.stringify(directoryRelatives) !== JSON.stringify(expectedDirectories))
227
+ throw new Error("Sanctuary bundle rollback record paths are invalid");
228
+ for (const relative of SNAPSHOT_RELATIVES)
229
+ validateDestination(agentRoot, relative);
230
+ const validMode = (mode) => mode === null || (Number.isInteger(mode) && mode >= 0 && mode <= 0o777);
231
+ const files = record.files.map((file) => {
232
+ if (!file || typeof file.relative !== "string" || !validMode(file.mode) || (file.contentBase64 !== null && typeof file.contentBase64 !== "string") || (file.contentBase64 === null) !== (file.mode === null))
233
+ throw new Error("Sanctuary bundle rollback record file is invalid");
234
+ const content = file.contentBase64 === null ? null : Buffer.from(file.contentBase64, "base64");
235
+ if (file.contentBase64 !== null && (!content || content.toString("base64") !== file.contentBase64))
236
+ throw new Error("Sanctuary bundle rollback record content is invalid");
237
+ return { path: path.join(agentRoot, file.relative), content, mode: file.mode };
238
+ });
239
+ const directories = record.directories.map((directory) => {
240
+ if (!directory || typeof directory.relative !== "string" || !validMode(directory.mode))
241
+ throw new Error("Sanctuary bundle rollback record directory is invalid");
242
+ return { path: path.join(agentRoot, directory.relative), mode: directory.mode };
243
+ });
244
+ const payload = { schemaVersion: record.schemaVersion, rollbackImageId: record.rollbackImageId, targetImageId: record.targetImageId, files: record.files, directories: record.directories };
245
+ if ((0, node_crypto_1.createHash)("sha256").update(JSON.stringify(payload)).digest("hex") !== record.snapshotDigest)
246
+ throw new Error("Sanctuary bundle rollback record digest is invalid");
247
+ return { snapshot: { files, directories }, rollbackImageId: record.rollbackImageId, targetImageId: record.targetImageId, filePath, state: filePath === rollbackPath(agentRoot) ? "rollback" : "committing" };
248
+ }
249
+ function removeRollbackRecord(agentRoot, filePath) {
250
+ fs.unlinkSync(filePath);
251
+ const root = fs.openSync(agentRoot, fs.constants.O_RDONLY | fs.constants.O_DIRECTORY | fs.constants.O_NOFOLLOW);
252
+ try {
253
+ fs.fsyncSync(root);
254
+ }
255
+ finally {
256
+ fs.closeSync(root);
257
+ }
258
+ }
259
+ function validateAgentRoot(agentRoot) {
260
+ if (!path.isAbsolute(agentRoot))
261
+ throw new Error("agent root must be an absolute path");
262
+ const stat = fs.lstatSync(agentRoot);
263
+ if (stat.isSymbolicLink() || !stat.isDirectory())
264
+ throw new Error("agent root must be a real directory");
265
+ }
266
+ function inspectSanctuaryPackageManagedBundleRollback(agentRoot) {
267
+ validateAgentRoot(agentRoot);
268
+ const record = readDurableRecord(agentRoot);
269
+ return record ? { rollbackImageId: record.rollbackImageId, targetImageId: record.targetImageId, state: record.state } : null;
270
+ }
271
+ function rollbackSanctuaryPackageManagedBundle(agentRoot, options = {}) {
272
+ validateAgentRoot(agentRoot);
273
+ const record = readDurableRecord(agentRoot);
274
+ if (!record)
275
+ return false;
276
+ if (record.state === "committing")
277
+ throw new Error("Sanctuary bundle commit is pending and cannot be rolled back");
278
+ restoreMigrationSnapshot(record.snapshot);
279
+ if (!options.retainRecord)
280
+ removeRollbackRecord(agentRoot, record.filePath);
281
+ return true;
282
+ }
283
+ function commitSanctuaryPackageManagedBundle(agentRoot) {
284
+ validateAgentRoot(agentRoot);
285
+ const record = readDurableRecord(agentRoot);
286
+ if (!record)
287
+ return false;
288
+ const committingPath = path.join(agentRoot, SANCTUARY_BUNDLE_COMMITTING_FILE);
289
+ if (record.filePath !== committingPath) {
290
+ fs.renameSync(record.filePath, committingPath);
291
+ const root = fs.openSync(agentRoot, fs.constants.O_RDONLY | fs.constants.O_DIRECTORY | fs.constants.O_NOFOLLOW);
292
+ try {
293
+ fs.fsyncSync(root);
294
+ }
295
+ finally {
296
+ fs.closeSync(root);
297
+ }
298
+ }
299
+ removeRollbackRecord(agentRoot, committingPath);
300
+ return true;
301
+ }
302
+ function restoreMigrationSnapshot(snapshot) {
303
+ for (const file of snapshot.files) {
304
+ cleanupInterruptedWrites(file.path);
305
+ if (file.content === null) {
306
+ fs.rmSync(file.path, { force: true });
307
+ }
308
+ else {
309
+ writeAtomic(file.path, file.content);
310
+ fs.chmodSync(file.path, file.mode);
311
+ }
312
+ }
313
+ for (const directory of [...snapshot.directories].reverse()) {
314
+ if (directory.mode === null) {
315
+ if (!fs.existsSync(directory.path))
316
+ continue;
317
+ if (fs.readdirSync(directory.path).length === 0)
318
+ fs.rmdirSync(directory.path);
319
+ }
320
+ else {
321
+ fs.chmodSync(directory.path, directory.mode);
322
+ }
323
+ }
324
+ }
325
+ function migrateSanctuaryPackageManagedBundle(input) {
326
+ (0, runtime_1.emitNervesEvent)({ component: "daemon", event: "daemon.sanctuary_bundle_migration_start", message: "starting Sanctuary package-managed bundle migration", meta: { agentRoot: input.agentRoot } });
327
+ let snapshot = null;
328
+ try {
329
+ if (!path.isAbsolute(input.packageRoot) || !path.isAbsolute(input.agentRoot) || input.packageRoot === input.agentRoot)
330
+ throw new Error("package and agent roots must be distinct absolute paths");
331
+ const packageStat = fs.lstatSync(input.packageRoot);
332
+ const agentStat = fs.lstatSync(input.agentRoot);
333
+ if (packageStat.isSymbolicLink() || !packageStat.isDirectory())
334
+ throw new Error("package root must be a real directory");
335
+ if (agentStat.isSymbolicLink() || !agentStat.isDirectory())
336
+ throw new Error("agent root must be a real directory");
337
+ if (input.retainRollback && (!input.rollbackImageId || !/^sha256:[0-9a-f]{64}$/u.test(input.rollbackImageId) || !input.targetImageId || !/^sha256:[0-9a-f]{64}$/u.test(input.targetImageId) || input.targetImageId === input.rollbackImageId))
338
+ throw new Error("retained rollback requires distinct exact rollback and target image IDs");
339
+ const existingRollback = rollbackPath(input.agentRoot);
340
+ if (readDurableRecord(input.agentRoot))
341
+ throw new Error("Sanctuary bundle rollback is pending; rollback or commit it before another migration");
342
+ for (const relative of exports.SANCTUARY_PACKAGE_MANAGED_FILES) {
343
+ requirePlainFile(path.join(input.packageRoot, relative), `package-managed source ${relative}`);
344
+ validateDestination(input.agentRoot, relative);
345
+ }
346
+ requirePlainFile(path.join(input.packageRoot, "bundle-meta.json"), "packaged bundle-meta.json");
347
+ validateDestination(input.agentRoot, "bundle-meta.json");
348
+ const packagedMeta = readObject(path.join(input.packageRoot, "bundle-meta.json"));
349
+ for (const field of BUNDLE_META_VERSION_FIELDS)
350
+ if (!(field in packagedMeta))
351
+ throw new Error(`packaged bundle-meta.json is missing ${field}`);
352
+ const packagedPolicy = (0, steward_policy_1.readStewardPolicy)(input.packageRoot);
353
+ if (Object.keys(packagedPolicy.routineActionGrants).length > 0) {
354
+ throw new Error("packaged steward policy must not carry routine action grants; authorize them through an authenticated owner session");
355
+ }
356
+ snapshot = captureMigrationSnapshot(input.agentRoot);
357
+ if (input.retainRollback) {
358
+ cleanupInterruptedWrites(existingRollback);
359
+ writeAtomic(existingRollback, `${JSON.stringify(durableSnapshot(input.agentRoot, snapshot, input.rollbackImageId, input.targetImageId))}\n`);
360
+ }
361
+ let managedFilesUpdated = 0;
362
+ for (const relative of exports.SANCTUARY_PACKAGE_MANAGED_FILES) {
363
+ const source = fs.readFileSync(path.join(input.packageRoot, relative), "utf8");
364
+ const destination = path.join(input.agentRoot, relative);
365
+ ensureSafeParent(input.agentRoot, relative);
366
+ if (fs.existsSync(destination) && fs.readFileSync(destination, "utf8") === source && (fs.statSync(destination).mode & 0o777) === 0o600)
367
+ continue;
368
+ writeAtomic(destination, source);
369
+ managedFilesUpdated += 1;
370
+ }
371
+ const metaPath = path.join(input.agentRoot, "bundle-meta.json");
372
+ const currentMeta = fs.existsSync(metaPath) ? readObject(metaPath) : {};
373
+ const nextMeta = { ...currentMeta };
374
+ for (const field of BUNDLE_META_VERSION_FIELDS)
375
+ nextMeta[field] = packagedMeta[field];
376
+ const nextMetaText = `${JSON.stringify(nextMeta, null, 2)}\n`;
377
+ if (!fs.existsSync(metaPath) || fs.readFileSync(metaPath, "utf8") !== nextMetaText)
378
+ writeAtomic(metaPath, nextMetaText);
379
+ const result = { managedFilesUpdated };
380
+ (0, runtime_1.emitNervesEvent)({ component: "daemon", event: "daemon.sanctuary_bundle_migration_end", message: "completed Sanctuary package-managed bundle migration", meta: result });
381
+ return result;
382
+ }
383
+ catch (error) {
384
+ let failure = error;
385
+ if (snapshot) {
386
+ try {
387
+ restoreMigrationSnapshot(snapshot);
388
+ if (fs.existsSync(rollbackPath(input.agentRoot)))
389
+ removeRollbackRecord(input.agentRoot, rollbackPath(input.agentRoot));
390
+ }
391
+ catch (restoreError) {
392
+ failure = new AggregateError([error, restoreError], "Sanctuary bundle migration and rollback both failed");
393
+ }
394
+ }
395
+ (0, runtime_1.emitNervesEvent)({ component: "daemon", event: "daemon.sanctuary_bundle_migration_error", level: "error", message: "Sanctuary package-managed bundle migration failed", meta: { error: String(failure), agentRoot: input.agentRoot } });
396
+ throw failure;
397
+ }
398
+ }
@@ -73,6 +73,9 @@ function safeEqual(left, right) {
73
73
  return false;
74
74
  return (0, node_crypto_1.timingSafeEqual)(Buffer.from(left, "hex"), Buffer.from(right, "hex"));
75
75
  }
76
+ function isSupercronicProcess(pid, deps) {
77
+ return deps.readLink(`/proc/${pid}/exe`) === SUPERCRONIC && deps.readFile(`/proc/${pid}/cmdline`) === SUPERCRONIC_CMDLINE;
78
+ }
76
79
  function defaultSanctuarySchedulerOriginDeps(agentRoot = (0, identity_1.getAgentRoot)("sanctuary"), markerReader = () => (0, sanctuary_acceptance_marker_1.readSanctuaryAcceptanceMarker)("sanctuary")) {
77
80
  return {
78
81
  platform: process.platform, pid: process.pid, ppid: process.ppid, now: () => new Date(),
@@ -84,10 +87,10 @@ function createSanctuarySchedulerFireCommand(command, deps = defaultSanctuarySch
84
87
  if (command.agent !== "sanctuary" || command.habitName !== "sanctuary-health" || command.trigger !== "cron" || deps.platform !== "linux")
85
88
  return null;
86
89
  try {
87
- if (deps.readLink(`/proc/${deps.ppid}/exe`) !== SUPERCRONIC || deps.readFile(`/proc/${deps.ppid}/cmdline`) !== SUPERCRONIC_CMDLINE)
88
- return null;
89
90
  const invocation = processIdentity(deps.readFile(`/proc/${deps.pid}/stat`));
90
91
  const parent = processIdentity(deps.readFile(`/proc/${deps.ppid}/stat`));
92
+ if (!isSupercronicProcess(deps.ppid, deps) && !isSupercronicProcess(parent.parentPid, deps))
93
+ return null;
91
94
  const marker = deps.marker();
92
95
  if (invocation.parentPid !== deps.ppid)
93
96
  return null;
@@ -113,9 +116,12 @@ function verifySanctuarySchedulerFireCommand(command, deps) {
113
116
  throw new Error("scheduler fire authentication failed");
114
117
  const invocation = processIdentity(deps.readFile(`/proc/${command.invocationPid}/stat`));
115
118
  const parent = processIdentity(deps.readFile(`/proc/${command.parentPid}/stat`));
116
- if (command.parentPid !== deps.childPid || invocation.parentPid !== command.parentPid || invocation.startTime !== command.invocationStartTime
117
- || parent.startTime !== command.parentStartTime || deps.readLink(`/proc/${command.parentPid}/exe`) !== SUPERCRONIC
118
- || deps.readFile(`/proc/${command.parentPid}/cmdline`) !== SUPERCRONIC_CMDLINE)
119
+ if (invocation.parentPid !== command.parentPid || invocation.startTime !== command.invocationStartTime
120
+ || parent.startTime !== command.parentStartTime)
121
+ throw new Error("scheduler fire ancestry is invalid");
122
+ if (command.parentPid !== deps.childPid && parent.parentPid !== deps.childPid)
123
+ throw new Error("scheduler fire ancestry is invalid");
124
+ if (command.parentPid === deps.childPid ? !isSupercronicProcess(command.parentPid, deps) : !isSupercronicProcess(parent.parentPid, deps))
119
125
  throw new Error("scheduler fire ancestry is invalid");
120
126
  (0, runtime_1.emitNervesEvent)({ component: "daemon", event: "daemon.sanctuary_scheduler_origin_authenticated", message: "Sanctuary scheduler origin authenticated", meta: { schedulerRunId: command.schedulerRunId, slot: command.slot } });
121
127
  return {