@ouro.bot/cli 0.1.0-alpha.805 → 0.1.0-alpha.807

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,18 @@
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.807",
6
+ "changes": [
7
+ "Check provider readiness before opening chat after an explicit hatch."
8
+ ]
9
+ },
10
+ {
11
+ "version": "0.1.0-alpha.806",
12
+ "changes": [
13
+ "Require fresh install-state evidence for explicit Sanctuary owner requests."
14
+ ]
15
+ },
4
16
  {
5
17
  "version": "0.1.0-alpha.805",
6
18
  "changes": [
@@ -1,5 +1,5 @@
1
1
  {
2
- "runtimeVersion": "0.1.0-alpha.805",
2
+ "runtimeVersion": "0.1.0-alpha.807",
3
3
  "bundleSchemaVersion": 3,
4
4
  "lastUpdated": "2026-09-03T00:00:00.000Z"
5
5
  }
@@ -1,7 +1,7 @@
1
1
  <?xml version="1.0"?>
2
2
  <Container version="2">
3
3
  <Name>ouro-butler</Name>
4
- <Repository>ghcr.io/ourostack/ouroboros-butler:0.1.0-alpha.805</Repository>
4
+ <Repository>ghcr.io/ourostack/ouroboros-butler:0.1.0-alpha.807</Repository>
5
5
  <Registry>https://github.com/ourostack/ouroboros/pkgs/container/ouroboros-butler</Registry>
6
6
  <Network>host</Network>
7
7
  <Shell>sh</Shell>
@@ -8530,6 +8530,9 @@ async function runOuroCli(args, deps = (0, cli_defaults_1.createDefaultOuroCliDe
8530
8530
  await performSystemSetup(deps);
8531
8531
  const daemonResult = await ensureDaemonRunning(deps);
8532
8532
  if (deps.startChat) {
8533
+ const health = await checkProviderHealthBeforeChat(hatchInput.agentName, deps);
8534
+ if (!health.ok)
8535
+ return health.output;
8533
8536
  await deps.startChat(hatchInput.agentName);
8534
8537
  return "";
8535
8538
  }
@@ -0,0 +1,126 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.sanctuaryInstallStateRequiredToolCalls = sanctuaryInstallStateRequiredToolCalls;
4
+ const runtime_1 = require("../nerves/runtime");
5
+ const TOOL_NAME = "sanctuary_get_install_state";
6
+ const REQUEST = /\b(?:use|run|call)\s+sanctuary_get_install_state\b/iu;
7
+ const NEGATED_REQUEST = /\b(?:do not|don't|dont|never|must not|should not|cannot|can't)\s+(?:(?:under|any|all|circumstances|whatsoever|ever|please|really|actually|immediately)\s+)*(?:use|run|call)\s+sanctuary_get_install_state\b|\bnot\s+to\s+(?:use|run|call)\s+sanctuary_get_install_state\b|\b(?:do not|don't|dont)\s+(?:(?:want|need|ask)\b(?:(?![.!?;,:]).){0,64}|(?:(?:under|any|all|circumstances|whatsoever|ever|please|really|actually|immediately)\s+)*(?:attempt|try|plan|intend)\s+(?:to\s+)?)(?:use|run|call)\s+sanctuary_get_install_state\b/giu;
8
+ const HISTORICAL_QUESTION = /\b(?:did|have|has)\s+(?:you\s+)?(?:use|run|call)\s+sanctuary_get_install_state\b[^.!?;:]*/giu;
9
+ const MISMATCH_CODES = new Set([
10
+ "managed_file_missing",
11
+ "managed_file_content",
12
+ "managed_file_mode",
13
+ "bundle_meta_missing",
14
+ "bundle_meta_field",
15
+ "bundle_meta_mode",
16
+ ]);
17
+ const REPAIR_ACTIONS = new Set([
18
+ "restart_from_verified_release",
19
+ "run_verified_update_recovery",
20
+ "roll_back_or_install_verified_release",
21
+ ]);
22
+ function parseInstallState(result) {
23
+ try {
24
+ const parsed = JSON.parse(result);
25
+ const data = parsed.ok === true && parsed.data && typeof parsed.data === "object" && !Array.isArray(parsed.data)
26
+ ? parsed.data
27
+ : undefined;
28
+ const repair = data?.repair && typeof data.repair === "object" && !Array.isArray(data.repair)
29
+ ? data.repair
30
+ : undefined;
31
+ if (!data
32
+ || typeof data.runtimePackageVersion !== "string" || data.runtimePackageVersion.length === 0
33
+ || typeof data.packagedBundleVersion !== "string" || data.packagedBundleVersion.length === 0
34
+ || !(typeof data.liveBundleVersion === "string" && data.liveBundleVersion.length > 0 || data.liveBundleVersion === null)
35
+ || !["exact", "mismatch"].includes(String(data.parity))
36
+ || !Array.isArray(data.mismatchCodes)
37
+ || !data.mismatchCodes.every((code) => typeof code === "string" && MISMATCH_CODES.has(code))
38
+ || !["absent", "rollback", "committing"].includes(String(data.journalState))
39
+ || typeof data.ready !== "boolean"
40
+ || !(repair?.actor === "none" && repair.action === "none"
41
+ || repair?.actor === "human-required" && typeof repair.action === "string" && REPAIR_ACTIONS.has(repair.action)))
42
+ return undefined;
43
+ return {
44
+ runtimePackageVersion: data.runtimePackageVersion,
45
+ packagedBundleVersion: data.packagedBundleVersion,
46
+ liveBundleVersion: data.liveBundleVersion,
47
+ parity: data.parity,
48
+ journalState: data.journalState,
49
+ ready: data.ready,
50
+ repairAction: repair.action,
51
+ };
52
+ }
53
+ catch {
54
+ return undefined;
55
+ }
56
+ }
57
+ function exactAnswerPattern(current) {
58
+ const value = (raw) => raw.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
59
+ const assignment = String.raw `\s*(?:(?:is)\s+|[:=]\s*)?`;
60
+ const separator = String.raw `\s*[,;]\s*`;
61
+ return new RegExp(String.raw `runtime(?: package)?(?: version)?${assignment}${value(current.runtimePackageVersion)}${separator}`
62
+ + String.raw `packaged(?: bundle)?(?: version)?${assignment}${value(current.packagedBundleVersion)}${separator}`
63
+ + String.raw `live(?: bundle)?(?: version)?${assignment}${value(current.liveBundleVersion ?? "null")}${separator}`
64
+ + String.raw `parity${assignment}${value(current.parity)}${separator}`
65
+ + String.raw `journal(?: state)?${assignment}${value(current.journalState)}${separator}`
66
+ + String.raw `ready(?: state)?${assignment}${value(String(current.ready))}${separator}`
67
+ + String.raw `repair(?: action)?${assignment}${value(current.repairAction)}(?=$|[\s,;.!?])`, "iu");
68
+ }
69
+ const RESIDUAL_INSTALL_STATE_CLAIM = /(?:^|[.!?;,]\s*)(?:(?:and|but|however|yet)\s+)*(?:(?:the\s+)?(?:runtime(?: package)?(?: version)?|packaged(?: bundle)?(?: version)?|live(?: bundle)?(?: version)?)\s*(?:(?:is)\s+|[:=]\s*)?(?=v?\d|unknown\b|null\b|missing\b|corrupt\b)|(?:the\s+)?installed package(?: version)?\b|(?:the\s+)?parity\s*(?:(?:is)\s+|[:=]\s*)?(?=(?:not\s+)?(?:exact|mismatch)\b)|(?:the\s+)?journal(?: state)?\s*(?:(?:is)\s+|[:=]\s*)?(?=(?:not\s+)?(?:absent|rollback|committing)\b)|(?:the\s+)?ready(?: state)?\s*(?:(?:is)\s+|[:=]\s*)?(?=(?:not\s+)?(?:true|false|ready)\b)|(?:the\s+)?repair(?: action)?\s*(?:(?:is)\s+|[:=]\s*)?(?=none\b|restart_from_verified_release\b|run_verified_update_recovery\b|roll_back_or_install_verified_release\b|required\b|needed\b)|(?:the\s+)?(?:install(?:ation)?|sanctuary|bundle|service|system|it)\s+(?:is(?:\s+not)?|isn't)\s+ready\b)/iu;
70
+ function sanctuaryInstallStateRequiredToolCalls(request, advertisedToolNames) {
71
+ const normalizedRequest = request.normalize("NFKC").replace(/[‘’]/gu, "'");
72
+ const negationRequest = normalizedRequest.replace(/,/gu, " ");
73
+ const affirmativeRequest = negationRequest
74
+ .replace(NEGATED_REQUEST, " ")
75
+ .replace(HISTORICAL_QUESTION, " ");
76
+ if (!advertisedToolNames.includes(TOOL_NAME)
77
+ || !REQUEST.test(affirmativeRequest))
78
+ return undefined;
79
+ let current;
80
+ const retryMessage = "Call sanctuary_get_install_state with empty arguments, then answer only from that fresh result.";
81
+ (0, runtime_1.emitNervesEvent)({
82
+ component: "senses",
83
+ event: "senses.sanctuary_install_state_required",
84
+ message: "explicit Sanctuary install-state request requires a fresh verified read",
85
+ meta: { requiredToolName: TOOL_NAME },
86
+ });
87
+ return {
88
+ names: [TOOL_NAME],
89
+ retryMessage,
90
+ requireSuccessfulResults: true,
91
+ validateRequiredToolResult: (name, result, args) => {
92
+ if (name !== TOOL_NAME || Object.keys(args).length !== 0)
93
+ return false;
94
+ current = parseInstallState(result);
95
+ return current !== undefined;
96
+ },
97
+ validateTerminalAnswer: (answer) => {
98
+ if (!current) {
99
+ return "Answer from the fresh install-state result and include its runtime package version, packaged bundle version, live bundle version, parity, journal state, ready state, and repair action.";
100
+ }
101
+ const normalizedAnswer = answer.normalize("NFKC").replace(/[‘’]/gu, "'").replace(/\*\*|`/gu, "");
102
+ const exactMatches = [...normalizedAnswer.matchAll(new RegExp(exactAnswerPattern(current).source, "giu"))];
103
+ if (exactMatches.length === 0) {
104
+ const currentValues = [
105
+ current.runtimePackageVersion,
106
+ current.packagedBundleVersion,
107
+ current.liveBundleVersion ?? "null",
108
+ current.parity,
109
+ current.journalState,
110
+ String(current.ready),
111
+ current.repairAction,
112
+ ];
113
+ return currentValues.every((value) => normalizedAnswer.includes(value))
114
+ ? "State each fresh install-state value exactly once in one compact answer."
115
+ : "Answer from the fresh install-state result and include its runtime package version, packaged bundle version, live bundle version, parity, journal state, ready state, and repair action.";
116
+ }
117
+ if (exactMatches.length !== 1)
118
+ return "State each fresh install-state value exactly once in one compact answer.";
119
+ const match = exactMatches[0];
120
+ const residual = normalizedAnswer.replace(match[0], " ").trim();
121
+ if (RESIDUAL_INSTALL_STATE_CLAIM.test(residual))
122
+ return "State each fresh install-state value exactly once in one compact answer.";
123
+ return undefined;
124
+ },
125
+ };
126
+ }
@@ -71,6 +71,7 @@ const config_1 = require("../heart/config");
71
71
  const telegram_client_1 = require("./telegram-client");
72
72
  const sanctuary_runtime_2 = require("./sanctuary-runtime");
73
73
  const sanctuary_full_visibility_contract_1 = require("./sanctuary-full-visibility-contract");
74
+ const sanctuary_install_state_contract_1 = require("./sanctuary-install-state-contract");
74
75
  const sanctuary_media_catalog_contract_1 = require("./sanctuary-media-catalog-contract");
75
76
  const sanctuary_storage_optimization_contract_1 = require("./sanctuary-storage-optimization-contract");
76
77
  const sanctuary_grounding_1 = require("./sanctuary-grounding");
@@ -1108,9 +1109,12 @@ function createTelegramSenseApp(options) {
1108
1109
  const mediaCatalog = isSanctuaryHouseholdConversation
1109
1110
  ? (0, sanctuary_media_catalog_contract_1.sanctuaryMediaCatalogRequiredToolCalls)(input.userMessage, relationshipAuthorization.advertisedToolNames)
1110
1111
  : undefined;
1112
+ const installState = isSanctuaryOwner
1113
+ ? (0, sanctuary_install_state_contract_1.sanctuaryInstallStateRequiredToolCalls)(input.userMessage, relationshipAuthorization.advertisedToolNames)
1114
+ : undefined;
1111
1115
  if (input.fullVisibilityProgress && fullVisibility)
1112
1116
  input.fullVisibilityProgress.fallback = fullVisibility.emptyResponseFallback;
1113
- const contracts = [storageOptimization, fullVisibility, staleDockerCare, mediaCatalog].filter(Boolean);
1117
+ const contracts = [storageOptimization, fullVisibility, staleDockerCare, mediaCatalog, installState].filter(Boolean);
1114
1118
  const ownedNames = contracts.map((contract) => new Set(contract.names));
1115
1119
  const requiredToolCalls = contracts.length === 0 ? undefined : {
1116
1120
  names: [...new Set(contracts.flatMap((contract) => [...contract.names]))],
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@ouro.bot/cli",
3
- "version": "0.1.0-alpha.805",
3
+ "version": "0.1.0-alpha.807",
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.805",
9
+ "version": "0.1.0-alpha.807",
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.805",
3
+ "version": "0.1.0-alpha.807",
4
4
  "engines": {
5
5
  "node": ">=22"
6
6
  },