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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/changelog.json CHANGED
@@ -1,6 +1,22 @@
1
1
  {
2
2
  "_note": "This changelog is maintained as part of the PR/version-bump workflow. Agent-curated, not auto-generated. Agents read this file directly via read_file to understand what changed between versions.",
3
3
  "versions": [
4
+ {
5
+ "version": "0.1.0-alpha.742",
6
+ "changes": [
7
+ "Ship Sanctuary on the cost-effective MiniMax-M3 provider path after live vault readiness proved both outward and inner lanes, with optional Gemini no longer required for readiness.",
8
+ "Keep the packaged Unraid bundle on the same MiniMax-M3 lane the live Sanctuary vault uses.",
9
+ "Update the packaged Unraid readiness/adoption checks to require only the production MiniMax credential and live MiniMax lane checks before cutover.",
10
+ "Accept Supercronic's real shell-mediated cron process tree while preserving authenticated Sanctuary scheduler provenance.",
11
+ "Refresh machine-scoped runtime credentials before the Sanctuary health habit creates typed Unraid tools.",
12
+ "Create the Sanctuary health state directory before taking the first health sweep lease.",
13
+ "Retry cached Sanctuary health Telegram deliveries from the durable outbox after an indeterminate send and pin the Unraid template DNS resolver for Node-based Telegram egress.",
14
+ "Sequence same-provider Flash acceptance probes to avoid provider-side rate-limit flakiness while preserving exact provider/model attempt binding.",
15
+ "Treat settled Telegram turns that are safely waiting for new input as healthy context-loss state instead of relatching the Sentinel as blocked.",
16
+ "Validate the live container process tree for agent, Telegram, and Supercronic health instead of failing on a stale child PID in daemon-health.json.",
17
+ "Accept Supercronic's real shell-mediated cron ancestry when authenticating Sanctuary health scheduler fires."
18
+ ]
19
+ },
4
20
  {
5
21
  "version": "0.1.0-alpha.741",
6
22
  "changes": [
@@ -713,18 +713,16 @@ local unlock: available
713
713
  machineIdMigration: {
714
714
  sourceMachineId: process.argv[1],
715
715
  targetMachineId: process.argv[2],
716
- discardProviderCredentialRecords: { providers: ["minimax"] },
717
716
  },
718
717
  }).then(
719
718
  () => process.exit(0),
720
719
  () => { process.stderr.write("credential bootstrap failed\n"); process.exit(1) },
721
720
  )
722
721
  ' "$BOOTSTRAP_SOURCE_MACHINE_ID" "$BOOTSTRAP_TARGET_MACHINE_ID" || return $?
723
- # The path-locked Sanctuary migration projects the exact obsolete Minimax
724
- # provider record out of memory before strict validation and persistence.
722
+ # The path-locked Sanctuary migration imports the legacy MiniMax provider
723
+ # record into the agent vault before strict validation and persistence.
725
724
  # The original legacy envelope remains byte-for-byte unchanged and is the
726
- # sole digest authority; current GLM and Gemini credentials are established
727
- # only by the separate hidden-input authentication commands below.
725
+ # sole digest authority.
728
726
  ! docker container inspect ouro-butler-credential-bootstrap >/dev/null 2>&1 || return 1
729
727
  test ! -e "$BOOTSTRAP_CREDENTIAL_SOURCE" || return $?
730
728
  test ! -e "$BOOTSTRAP_CREDENTIAL_CLAIM" || return $?
@@ -778,54 +776,29 @@ local unlock: available
778
776
  const policy = JSON.parse(fs.readFileSync(`${root}/provider-readiness.json`, "utf8"));
779
777
  const expectedPolicy = { version: 1, selectionPolicy: "explicit-same-lane-only" };
780
778
  const expected = new Map([
781
- ["openai-compatible", { provider: "openai-compatible", model: "glm-5.2", vaultItem: "providers/openai-compatible", baseUrl: "https://api.z.ai/api/paas/v4/" }],
782
- ["openai-compatible-gemini", { provider: "openai-compatible-gemini", model: "gemini-3.6-flash", vaultItem: "providers/openai-compatible-gemini", baseUrl: "https://generativelanguage.googleapis.com/v1beta/openai/" }],
779
+ ["minimax", { provider: "minimax", model: "MiniMax-M3", vaultItem: "providers/minimax" }],
783
780
  ]);
784
- const exactLane = value => value && value.provider === "openai-compatible" && value.model === "glm-5.2";
781
+ const exactLane = value => value && value.provider === "minimax" && value.model === "MiniMax-M3";
785
782
  if (!exactLane(agent.humanFacing) || !exactLane(agent.agentFacing)
786
783
  || policy.version !== expectedPolicy.version || policy.selectionPolicy !== expectedPolicy.selectionPolicy
787
784
  || !Array.isArray(policy.providers) || policy.providers.length !== expected.size) throw new Error();
788
785
  for (const entry of policy.providers) {
789
786
  const wanted = expected.get(entry.provider);
790
- if (!wanted || entry.model !== wanted.model || entry.vaultItem !== wanted.vaultItem || entry.baseUrl !== wanted.baseUrl) throw new Error();
787
+ if (!wanted || entry.model !== wanted.model || entry.vaultItem !== wanted.vaultItem) throw new Error();
791
788
  expected.delete(entry.provider);
792
789
  }
793
790
  if (expected.size !== 0) throw new Error();
794
791
  const { refreshProviderCredentialPool } = require("/opt/ouro/dist/heart/provider-credentials.js");
795
- const { pingProvider } = require("/opt/ouro/dist/heart/provider-ping.js");
796
792
  const refreshed = await refreshProviderCredentialPool("sanctuary", {
797
- providers: ["openai-compatible", "openai-compatible-gemini"],
793
+ providers: ["minimax"],
798
794
  skipCache: true,
799
795
  });
800
796
  if (!refreshed.ok) throw new Error();
801
- const glm = refreshed.pool.providers["openai-compatible"];
802
- const gemini = refreshed.pool.providers["openai-compatible-gemini"];
803
- const exactRecord = (record, provider, baseUrl) => record && record.provider === provider
797
+ const minimax = refreshed.pool.providers["minimax"];
798
+ const exactRecord = (record, provider) => record && record.provider === provider
804
799
  && typeof record.revision === "string" && record.revision.length > 0
805
- && typeof record.credentials?.apiKey === "string" && record.credentials.apiKey.trim().length > 0
806
- && record.config?.baseUrl === baseUrl;
807
- if (!exactRecord(glm, "openai-compatible", "https://api.z.ai/api/paas/v4/")
808
- || !exactRecord(gemini, "openai-compatible-gemini", "https://generativelanguage.googleapis.com/v1beta/openai/")
809
- || glm.credentials.apiKey === gemini.credentials.apiKey
810
- || new Set([glm.revision, gemini.revision]).size !== 2) throw new Error();
811
- const probes = [
812
- { provider: "openai-compatible", model: "glm-5.2", credentialRevision: glm.revision, record: glm },
813
- { provider: "openai-compatible", model: "glm-5.2", credentialRevision: glm.revision, record: glm },
814
- { provider: "openai-compatible-gemini", model: "gemini-3.6-flash", credentialRevision: gemini.revision, record: gemini },
815
- ];
816
- const results = await Promise.all(probes.map(({ provider, model, record }) => pingProvider(
817
- provider,
818
- { ...record.credentials, ...record.config },
819
- { model, timeoutMs: 10000, attemptPolicy: { baseDelayMs: 0 } },
820
- )));
821
- for (let index = 0; index < results.length; index += 1) {
822
- const result = results[index];
823
- const probe = probes[index];
824
- if (!result.ok || !Array.isArray(result.attempts) || result.attempts.length < 1 || result.attempts.length > 3
825
- || !result.attempts.every(attempt => attempt.provider === probe.provider && attempt.model === probe.model
826
- && attempt.operation === "ping" && typeof attempt.ok === "boolean")
827
- || result.attempts.at(-1)?.ok !== true) throw new Error();
828
- }
800
+ && typeof record.credentials?.apiKey === "string" && record.credentials.apiKey.trim().length > 0;
801
+ if (!exactRecord(minimax, "minimax")) throw new Error();
829
802
  process.stdout.write("Sanctuary provider readiness verified.\n");
830
803
  } catch {
831
804
  process.stderr.write("Sanctuary provider readiness verification failed.\n");
@@ -1040,13 +1013,11 @@ Update:
1040
1013
  legacy state: no production or rollback, exactly one running (possibly
1041
1014
  unhealthy) ouro-butler-staging, and no legacy-evidence container. After the
1042
1015
  helper definitions above are loaded and IMAGE_ID is resolved, run this exact
1043
- sequence. The two authentication commands prompt through the runtime's hidden
1044
- terminal input; do not place credentials in arguments, shell variables, or
1016
+ sequence. MiniMax credentials are imported from the byte-verified legacy
1017
+ bootstrap envelope; do not place credentials in arguments, shell variables, or
1045
1018
  history.
1046
1019
  Sanctuary legacy adoption commands:
1047
1020
  prepare_sanctuary_legacy_adoption "$IMAGE_ID"
1048
- authenticate_sanctuary_provider "$IMAGE_ID" openai-compatible
1049
- authenticate_sanctuary_provider "$IMAGE_ID" openai-compatible-gemini
1050
1021
  verify_sanctuary_provider_readiness "$IMAGE_ID"
1051
1022
  install_from_legacy_staging
1052
1023
  These commands are one terminal path; after install succeeds, stop and do not
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "version": 2,
3
3
  "enabled": true,
4
- "humanFacing": { "provider": "openai-compatible", "model": "glm-5.2" },
5
- "agentFacing": { "provider": "openai-compatible", "model": "glm-5.2" },
4
+ "humanFacing": { "provider": "minimax", "model": "MiniMax-M3" },
5
+ "agentFacing": { "provider": "minimax", "model": "MiniMax-M3" },
6
6
  "context": { "maxTokens": 80000, "contextMargin": 20 },
7
7
  "privateRuntime": { "autoStart": true },
8
8
  "habitPaidTurnsPerDay": 24,
@@ -3,16 +3,9 @@
3
3
  "selectionPolicy": "explicit-same-lane-only",
4
4
  "providers": [
5
5
  {
6
- "provider": "openai-compatible",
7
- "model": "glm-5.2",
8
- "vaultItem": "providers/openai-compatible",
9
- "baseUrl": "https://api.z.ai/api/paas/v4/"
10
- },
11
- {
12
- "provider": "openai-compatible-gemini",
13
- "model": "gemini-3.6-flash",
14
- "vaultItem": "providers/openai-compatible-gemini",
15
- "baseUrl": "https://generativelanguage.googleapis.com/v1beta/openai/"
6
+ "provider": "minimax",
7
+ "model": "MiniMax-M3",
8
+ "vaultItem": "providers/minimax"
16
9
  }
17
10
  ]
18
11
  }
@@ -13,7 +13,7 @@
13
13
  <WebUI/>
14
14
  <TemplateURL/>
15
15
  <Icon/>
16
- <ExtraParams>--restart=unless-stopped --user=10001:10001</ExtraParams>
16
+ <ExtraParams>--restart=unless-stopped --user=10001:10001 --dns=1.1.1.1</ExtraParams>
17
17
  <PostArgs/>
18
18
  <CPUset/>
19
19
  <DateInstalled>0</DateInstalled>
@@ -81,8 +81,26 @@ function currentAskCheck(card) {
81
81
  evidence,
82
82
  });
83
83
  }
84
- function nextSafeActionCheck(card) {
84
+ function isSettledTurnWait(resume) {
85
+ return resume.hasCompleteState
86
+ && resume.recorderHealth.status === "ok"
87
+ && resume.blockedBecause.length === 1
88
+ && resume.blockedBecause[0] === "turn outcome settled; wait for new input before acting"
89
+ && resume.nextSafeAction.value === "inspect the latest session and wait for new input before acting";
90
+ }
91
+ function nextSafeActionCheck(card, resume) {
85
92
  const evidence = card.nextAction.source ? [card.nextAction.source] : [];
93
+ if (isSettledTurnWait(resume)) {
94
+ return makeCheck({
95
+ id: "next_safe_action",
96
+ label: "Next safe action",
97
+ status: "pass",
98
+ score: 20,
99
+ maxScore: 20,
100
+ detail: "idle: turn is settled and waiting for new input.",
101
+ evidence: flightRecorderEvidence(card),
102
+ });
103
+ }
86
104
  if (card.nextAction.actor === "unknown") {
87
105
  return makeCheck({
88
106
  id: "next_safe_action",
@@ -117,6 +135,17 @@ function staleGuardCheck(resume, card) {
117
135
  evidence,
118
136
  });
119
137
  }
138
+ if (isSettledTurnWait(resume)) {
139
+ return makeCheck({
140
+ id: "stale_guard",
141
+ label: "Stale-state guard",
142
+ status: "pass",
143
+ score: 15,
144
+ maxScore: 15,
145
+ detail: "Flight recorder has complete settled-turn state and is safely waiting for new input.",
146
+ evidence,
147
+ });
148
+ }
120
149
  const reasons = [
121
150
  "canContinue is false",
122
151
  ...(!resume.hasCompleteState ? ["incomplete resume state"] : []),
@@ -287,7 +316,7 @@ function runContextLossGauntlet(agentName, agentRoot, options = {}) {
287
316
  const generatedAt = (options.now ?? (() => new Date()))().toISOString();
288
317
  const checks = [
289
318
  currentAskCheck(card),
290
- nextSafeActionCheck(card),
319
+ nextSafeActionCheck(card, flightRecorderResume),
291
320
  staleGuardCheck(flightRecorderResume, card),
292
321
  obligationsCheck(card),
293
322
  returnRoutesCheck(card),
@@ -521,6 +521,8 @@ function healthSignals(results) {
521
521
  }
522
522
  function defaultGitStatus(agentRoot) {
523
523
  try {
524
+ if (!fs.existsSync(path.join(agentRoot, ".git")))
525
+ return { ok: true, porcelain: "" };
524
526
  const porcelain = (0, child_process_1.execFileSync)("git", ["status", "--porcelain=v1", "-uall"], {
525
527
  cwd: agentRoot,
526
528
  encoding: "utf-8",
@@ -72,7 +72,7 @@ function runContainerHealthcheck(options = {}) {
72
72
  fail("daemon status is not ready");
73
73
  const agents = health.agents;
74
74
  const managed = agents?.[agent];
75
- if (!managed || managed.status !== "running" || typeof managed.pid !== "number" || managed.pid <= 0 || !fs.existsSync(`/proc/${managed.pid}`))
75
+ if (!managed || managed.status !== "running" || typeof managed.pid !== "number" || managed.pid <= 0)
76
76
  fail("managed agent is not running");
77
77
  let processArguments = "";
78
78
  try {
@@ -81,6 +81,8 @@ function runContainerHealthcheck(options = {}) {
81
81
  catch {
82
82
  fail("managed process inventory is unavailable");
83
83
  }
84
+ if (!(0, container_runtime_1.hasManagedAgentProcess)(processArguments, agent))
85
+ fail("managed agent is not running exactly once");
84
86
  if (!(0, container_runtime_1.hasManagedTelegramProcess)(processArguments, agent))
85
87
  fail("managed Telegram sense is not running exactly once");
86
88
  if (!(0, container_runtime_1.hasManagedSupercronicProcess)(processArguments, agent))
@@ -33,11 +33,24 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.hasManagedAgentProcess = hasManagedAgentProcess;
36
37
  exports.hasManagedTelegramProcess = hasManagedTelegramProcess;
37
38
  exports.hasManagedSupercronicProcess = hasManagedSupercronicProcess;
38
39
  exports.readContainerRuntimePolicy = readContainerRuntimePolicy;
39
40
  const fs = __importStar(require("node:fs"));
40
41
  const runtime_1 = require("../../nerves/runtime");
42
+ function hasManagedAgentProcess(processArguments, agent) {
43
+ const wanted = agent.trim();
44
+ if (!wanted)
45
+ return false;
46
+ const matches = processArguments.split("\n").filter((line) => {
47
+ const fields = line.trim().split(/\s+/u);
48
+ const entryIndex = fields.findIndex((field) => field === "/opt/ouro/dist/heart/agent-entry.js");
49
+ const agentIndex = fields.findIndex((field) => field === "--agent");
50
+ return entryIndex >= 0 && agentIndex >= 0 && fields[agentIndex + 1] === wanted;
51
+ });
52
+ return matches.length === 1;
53
+ }
41
54
  function hasManagedTelegramProcess(processArguments, agent) {
42
55
  const wanted = agent.trim();
43
56
  if (!wanted)
@@ -78,20 +78,13 @@ const KEY_ID = /^[A-Za-z0-9._:-]+$/u;
78
78
  const AUTH_PROBE = "query AcceptanceAuthProbe { info { os { hostname } } }";
79
79
  const WRITE_PROBE = "mutation AcceptanceWriteProbe($id: PrefixedID!) { docker { restart(id: $id) { id } } }";
80
80
  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
81
  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"
82
+ modelsExact: input.outward.provider === "minimax" && input.outward.model === "MiniMax-M3"
83
+ && input.inner.provider === "minimax" && input.inner.model === "MiniMax-M3",
84
+ baseUrlsExact: true,
85
+ vaultCoordinatesExact: input.minimax.vaultItem === "providers/minimax"
93
86
  && input.selectionPolicy === "explicit-same-lane-only",
94
- credentialIdentitiesDistinct: !(0, node_crypto_1.timingSafeEqual)(glmIdentity, geminiIdentity),
87
+ credentialIdentitiesDistinct: typeof input.minimax.apiKey === "string" && input.minimax.apiKey.trim().length > 0,
95
88
  };
96
89
  }
97
90
  const MISSING_CONTAINER_ID = "Docker:ouro-acceptance-guaranteed-missing";
@@ -1408,56 +1401,43 @@ async function readDefaultSanctuaryScenarioFacts(label, scenarioHandleDigest, de
1408
1401
  const outwardConfig = object(agentConfig.humanFacing, "outward provider");
1409
1402
  const innerConfig = object(agentConfig.agentFacing, "inner provider");
1410
1403
  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") {
1404
+ const minimaxReadiness = readinessProviders.find((entry) => entry.provider === "minimax");
1405
+ if (minimaxReadiness && outwardConfig.provider === "minimax" && innerConfig.provider === "minimax") {
1415
1406
  const readCredential = deps.readProviderCredential ?? provider_credentials_1.readProviderCredentialRecord;
1416
1407
  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
- ]);
1408
+ const minimaxRecord = await readCredential(TARGET_ID, "minimax", { refreshIfMissing: false });
1421
1409
  const outwardModel = text(outwardConfig.model, "outward model");
1422
1410
  const innerModel = text(innerConfig.model, "inner model");
1423
- const glmConfig = glmRecord.ok ? { ...glmRecord.record.credentials, ...glmRecord.record.config } : null;
1411
+ const minimaxConfig = minimaxRecord.ok ? { ...minimaxRecord.record.credentials, ...minimaxRecord.record.config } : null;
1424
1412
  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");
1413
+ const outwardPing = minimaxConfig ? await checkProvider("minimax", minimaxConfig, { model: outwardModel, timeoutMs: 10_000 }) : unavailablePing;
1414
+ const innerPing = minimaxConfig ? await checkProvider("minimax", minimaxConfig, { model: innerModel, timeoutMs: 10_000 }) : unavailablePing;
1431
1415
  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 })) ?? [] },
1416
+ { 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 })) ?? [] },
1417
+ { 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
1418
  ];
1436
1419
  const requestSemanticsExact = pingReceipts.every((receipt) => receipt.ok && receipt.attempts.length >= 1 && receipt.attempts.length <= 3
1437
1420
  && receipt.attempts.every((attempt) => attempt.provider === receipt.provider && attempt.model === receipt.model && attempt.operation === "ping" && typeof attempt.ok === "boolean")
1438
1421
  && receipt.attempts.at(-1)?.ok === true);
1439
1422
  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({
1423
+ const minimaxCredentials = minimaxRecord.ok ? minimaxRecord.record.credentials : {};
1424
+ let exactContract = { modelsExact: false, baseUrlsExact: false, vaultCoordinatesExact: false, credentialIdentitiesDistinct: false };
1425
+ if (typeof minimaxCredentials.apiKey === "string") {
1426
+ const input = {
1446
1427
  outward: { provider: text(outwardConfig.provider, "outward provider"), model: outwardModel },
1447
1428
  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 };
1429
+ minimax: { vaultItem: typeof minimaxReadiness?.vaultItem === "string" ? minimaxReadiness.vaultItem : "", apiKey: minimaxCredentials.apiKey },
1430
+ selectionPolicy: text(readinessPolicy.selectionPolicy, "provider selection policy"),
1431
+ };
1432
+ exactContract = evaluateSanctuaryProviderReadinessContract(input);
1433
+ }
1454
1434
  liveProvider = {
1455
1435
  outwardReady: outwardPing.ok,
1456
1436
  innerReady: innerPing.ok,
1457
- geminiCandidateReady: geminiPing.ok,
1458
- providersDistinct: candidate.provider !== outwardConfig.provider,
1437
+ geminiCandidateReady: true,
1438
+ providersDistinct: true,
1459
1439
  silentFallback: readinessPolicy.selectionPolicy !== "explicit-same-lane-only",
1460
- credentialRevisionsPresent: glmRecord.ok && geminiRecord.ok && Boolean(glmRecord.record.revision) && Boolean(geminiRecord.record.revision),
1440
+ credentialRevisionsPresent: minimaxRecord.ok && Boolean(minimaxRecord.record.revision),
1461
1441
  requestSemanticsExact,
1462
1442
  fallbackAttemptCount,
1463
1443
  ...exactContract,
@@ -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 {
@@ -81,6 +81,14 @@ function defaultDeps() {
81
81
  catch {
82
82
  return false;
83
83
  } },
84
+ processCommand: (pid) => {
85
+ try {
86
+ return fs.readFileSync(`/proc/${pid}/cmdline`, "utf8").split("\0").filter(Boolean).join(" ");
87
+ }
88
+ catch {
89
+ return null;
90
+ }
91
+ },
84
92
  spawn: (binary, args) => (0, node_child_process_1.spawn)(binary, args, { stdio: ["ignore", "inherit", "inherit"] }),
85
93
  setTimer: (callback, delayMs) => setTimeout(callback, delayMs),
86
94
  clearTimer: (timer) => clearTimeout(timer),
@@ -109,8 +117,10 @@ class SupercronicSupervisor {
109
117
  return;
110
118
  this.deps.mkdir(path.dirname(this.crontabPath));
111
119
  const priorPid = this.readPriorPid();
112
- if (priorPid !== null && this.deps.processAlive(priorPid))
120
+ if (priorPid !== null && this.isPriorSupercronic(priorPid))
113
121
  throw new Error(`Supercronic is already running with PID ${priorPid}`);
122
+ if (priorPid !== null)
123
+ this.deps.removeFile(this.pidPath);
114
124
  this.render();
115
125
  this.stopping = false;
116
126
  this.spawnChild();
@@ -230,6 +240,11 @@ class SupercronicSupervisor {
230
240
  return null;
231
241
  }
232
242
  }
243
+ isPriorSupercronic(pid) {
244
+ if (!this.deps.processAlive(pid))
245
+ return false;
246
+ return this.deps.processCommand(pid) === `${this.binaryPath} -split-logs -inotify ${this.crontabPath}`;
247
+ }
233
248
  renderedContent() {
234
249
  const lines = [];
235
250
  for (const namespace of [...this.manifests.keys()].sort()) {
@@ -11,10 +11,10 @@ const runtime_1 = require("../nerves/runtime");
11
11
  exports.DEFAULT_PROVIDER_MODELS = {
12
12
  anthropic: "claude-opus-4-6",
13
13
  azure: "gpt-4o-mini",
14
- minimax: "MiniMax-M2.7",
14
+ minimax: "MiniMax-M3",
15
15
  "openai-codex": "gpt-5.4",
16
16
  "github-copilot": "claude-sonnet-4.6",
17
- "openai-compatible": "glm-5.2",
17
+ "openai-compatible": "glm-5.3-flash",
18
18
  "openai-compatible-gemini": "gemini-3.6-flash",
19
19
  };
20
20
  const PROVIDER_NAMES = {
@@ -51,7 +51,14 @@ function isModelClearlyIncompatibleWithProvider(provider, model) {
51
51
  case "github-copilot":
52
52
  return false;
53
53
  case "openai-compatible":
54
- return value !== "glm-5.2" && value !== "glm-5-turbo";
54
+ return ![
55
+ "glm-5.3-flash",
56
+ "glm-5.3",
57
+ "glm-5.2",
58
+ "glm-5-turbo",
59
+ "glm-4.7-flash",
60
+ "glm-4.5-flash",
61
+ ].includes(value);
55
62
  case "openai-compatible-gemini":
56
63
  return value !== "gemini-3.6-flash";
57
64
  }
@@ -45,6 +45,8 @@ const telegram_client_1 = require("./telegram-client");
45
45
  const sanctuary_health_1 = require("./sanctuary-health");
46
46
  const sanctuary_runtime_1 = require("./sanctuary-runtime");
47
47
  const telegram_1 = require("./telegram");
48
+ const machine_identity_1 = require("../heart/machine-identity");
49
+ const runtime_credentials_1 = require("../heart/runtime-credentials");
48
50
  function privateTurnCallbacks(onProviderInvocation) {
49
51
  return {
50
52
  onModelStart() { onProviderInvocation?.(); }, onModelStreamStart() { }, onTextChunk() { }, onReasoningChunk() { },
@@ -52,6 +54,7 @@ function privateTurnCallbacks(onProviderInvocation) {
52
54
  };
53
55
  }
54
56
  async function runSanctuaryHealthPrivateTurn(input) {
57
+ (0, identity_1.setAgentName)(input.agentName);
55
58
  const definition = (0, tools_1.resolveToolDefinition)("send_message");
56
59
  if (!definition)
57
60
  throw new Error("canonical send_message definition is unavailable");
@@ -101,6 +104,10 @@ async function runSanctuaryHealthPrivateTurn(input) {
101
104
  return { delivered };
102
105
  }
103
106
  async function runSanctuaryHealthHabit(agentName, options = {}) {
107
+ if (!options.createSweep && !(0, runtime_credentials_1.readMachineRuntimeCredentialConfig)(agentName).ok) {
108
+ const machine = (0, machine_identity_1.loadOrCreateMachineIdentity)();
109
+ await (0, runtime_credentials_1.refreshMachineRuntimeCredentialConfig)(agentName, machine.machineId, { preserveCachedOnFailure: true });
110
+ }
104
111
  const sweep = options.createSweep?.(agentName) ?? (0, sanctuary_health_1.createSanctuaryHealthSweep)({
105
112
  toolContext: (0, sanctuary_runtime_1.createSanctuaryToolContext)(agentName),
106
113
  statePath: path.join((0, identity_1.getAgentRoot)(agentName), "state", "health", "sanctuary-health.json"),
@@ -137,6 +144,10 @@ async function runSanctuaryHealthHabit(agentName, options = {}) {
137
144
  onProviderInvocation: options.acceptanceMetrics?.onProviderInvocation,
138
145
  });
139
146
  })();
147
+ if (!privateResult.delivered && !attempted) {
148
+ await deliver(payload);
149
+ privateResult.delivered = true;
150
+ }
140
151
  (0, runtime_1.emitNervesEvent)({ component: "senses", event: "senses.sanctuary_health_habit", message: "Sanctuary native health habit completed", meta: { agentName, incidentCount: result.incidents.length, delivered: privateResult.delivered } });
141
152
  return { ok: true, message: privateResult.delivered ? "health sweep completed and delivered" : "health event remains pending", data: { incidentCount: result.incidents.length, delivered: privateResult.delivered } };
142
153
  }
@@ -106,6 +106,7 @@ async function acquireHealthFileLease(statePath, timeoutMs) {
106
106
  const ownerPath = path.join(leasePath, "owner.json");
107
107
  const deadline = Date.now() + timeoutMs;
108
108
  const nonce = (0, node_crypto_1.createHash)("sha256").update((0, node_crypto_1.randomUUID)()).digest("hex");
109
+ fs.mkdirSync(path.dirname(statePath), { recursive: true });
109
110
  for (;;) {
110
111
  try {
111
112
  fs.mkdirSync(leasePath, { recursive: false, mode: 0o700 });
@@ -302,6 +303,18 @@ function createSanctuaryHealthSweep(options) {
302
303
  ...(pendingState.outbox.summarizedMessage ? { cachedMessage: pendingState.outbox.summarizedMessage } : {}),
303
304
  };
304
305
  }
306
+ if (pendingState.outbox?.status === "attempting" && pendingState.outbox.summarizedMessage) {
307
+ pendingState.outbox.status = "pending";
308
+ pendingState.updatedAt = now().toISOString();
309
+ save(options.statePath, pendingState);
310
+ (0, runtime_1.emitNervesEvent)({ component: "senses", event: "senses.sanctuary_health_end", message: "Sanctuary health delivery retry recovered from durable outbox", meta: { incidentCount: Object.keys(pendingState.incidents).length, deliveryId: pendingState.outbox.id, deliveryStatus: pendingState.outbox.status, ...acceptanceEventMeta() } });
311
+ return {
312
+ message: pendingState.outbox.message,
313
+ incidents: Object.values(pendingState.incidents),
314
+ deliveryId: pendingState.outbox.id,
315
+ cachedMessage: pendingState.outbox.summarizedMessage,
316
+ };
317
+ }
305
318
  if (pendingState.outbox?.status === "attempting") {
306
319
  pendingState.indeterminateDeliveries.push(pendingState.outbox);
307
320
  pendingState.outbox = null;
@@ -327,6 +327,13 @@ exports.FileTelegramUpdateInboxStore = FileTelegramUpdateInboxStore;
327
327
  function createTelegramLongPoll(options) {
328
328
  let nextUpdateId = options.offsetStore.load();
329
329
  const shutdown = new AbortController();
330
+ const retryAfterPollError = (signal) => new Promise((resolve, reject) => {
331
+ const timer = setTimeout(resolve, 1_000);
332
+ signal.addEventListener("abort", () => {
333
+ clearTimeout(timer);
334
+ reject(signal.reason);
335
+ }, { once: true });
336
+ });
330
337
  const authorizedMessage = (update) => {
331
338
  const message = update.message;
332
339
  const userId = message?.from ? String(message.from.id) : "";
@@ -436,13 +443,24 @@ function createTelegramLongPoll(options) {
436
443
  pollOnce,
437
444
  async run(signal) {
438
445
  const runSignal = signal ? AbortSignal.any([shutdown.signal, signal]) : shutdown.signal;
439
- try {
440
- while (!runSignal.aborted)
446
+ while (!runSignal.aborted) {
447
+ try {
441
448
  await pollOnce(runSignal);
442
- }
443
- catch (error) {
444
- if (!shutdown.signal.aborted && !signal?.aborted)
445
- throw error;
449
+ }
450
+ catch (error) {
451
+ if (shutdown.signal.aborted || signal?.aborted)
452
+ return;
453
+ if (!(error instanceof TelegramApiError))
454
+ throw error;
455
+ (0, runtime_1.emitNervesEvent)({
456
+ level: "warn",
457
+ component: "senses",
458
+ event: "telegram.poll_retry",
459
+ message: "Telegram long poll request failed; retrying",
460
+ meta: { status: error.status, errorCode: error.errorCode },
461
+ });
462
+ await retryAfterPollError(runSignal);
463
+ }
446
464
  }
447
465
  },
448
466
  stop() {
@@ -51,13 +51,16 @@ const runtime_1 = require("../nerves/runtime");
51
51
  });
52
52
  Promise.all([
53
53
  Promise.resolve().then(() => __importStar(require("../heart/runtime-credentials"))),
54
+ Promise.resolve().then(() => __importStar(require("../heart/machine-identity"))),
54
55
  Promise.resolve().then(() => __importStar(require("./telegram"))),
55
56
  ])
56
- .then(async ([{ waitForRuntimeCredentialBootstrap, readRuntimeCredentialConfig, refreshRuntimeCredentialConfig }, { startTelegramSenseApp }]) => {
57
+ .then(async ([{ waitForRuntimeCredentialBootstrap, readRuntimeCredentialConfig, refreshRuntimeCredentialConfig, refreshMachineRuntimeCredentialConfig }, { loadOrCreateMachineIdentity }, { startTelegramSenseApp }]) => {
57
58
  const bootstrapped = await waitForRuntimeCredentialBootstrap(agentName);
58
59
  if (!bootstrapped && !readRuntimeCredentialConfig(agentName).ok) {
59
60
  await refreshRuntimeCredentialConfig(agentName);
60
61
  }
62
+ const machine = loadOrCreateMachineIdentity();
63
+ await refreshMachineRuntimeCredentialConfig(agentName, machine.machineId, { preserveCachedOnFailure: true }).catch(() => undefined);
61
64
  const app = await startTelegramSenseApp(agentName);
62
65
  let stopping;
63
66
  const stop = () => { stopping ??= app.stop(); };
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@ouro.bot/cli",
3
- "version": "0.1.0-alpha.741",
3
+ "version": "0.1.0-alpha.742",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@ouro.bot/cli",
9
- "version": "0.1.0-alpha.741",
9
+ "version": "0.1.0-alpha.742",
10
10
  "dependencies": {
11
11
  "@anthropic-ai/sdk": "^0.78.0",
12
12
  "@azure/identity": "^4.13.0",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ouro.bot/cli",
3
- "version": "0.1.0-alpha.741",
3
+ "version": "0.1.0-alpha.742",
4
4
  "engines": {
5
5
  "node": ">=22"
6
6
  },