@ouro.bot/cli 0.1.0-alpha.869 → 0.1.0-alpha.870

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,12 @@
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.870",
6
+ "changes": [
7
+ "Await ticks now say whether my owner was asked about this await (read from the runtime ask ledger for this await instance, never from model text) and that awaiting a decision is not true when they never were, and the ask_owner guidance counts a stall marked owner_decision_needed; media_queue marks confirmed stalls (no_peers, never_started, unfinished_for_days) with owner_decision_needed, likely_fix ask_owner and the keep_waiting, find_another_release, give_up choices shared with the diagnosis."
8
+ ]
9
+ },
4
10
  {
5
11
  "version": "0.1.0-alpha.869",
6
12
  "changes": [
@@ -1,5 +1,5 @@
1
1
  {
2
- "runtimeVersion": "0.1.0-alpha.869",
2
+ "runtimeVersion": "0.1.0-alpha.870",
3
3
  "bundleSchemaVersion": 3,
4
4
  "lastUpdated": "2026-09-03T00:00:00.000Z"
5
5
  }
@@ -611,6 +611,9 @@ const rowPercent = (r) => ((r.size ?? 0) > 0 ? Math.round((((r.size ?? 0) - (r.s
611
611
  // surfaces. `previous` is the persisted progress memory for this download (or undefined).
612
612
  // Returns null while the download may still be moving; otherwise the kind, whether the stall
613
613
  // is confirmed (only confirmed stalls are acted on) and the facts the owner is told.
614
+ // The owner's three choices when a download is confirmed dead; one source for every surface that asks.
615
+ export const STALL_OWNER_CHOICES = ["keep_waiting", "find_another_release", "give_up"]
616
+
614
617
  export function classifyStall(row, nowMs, previous) {
615
618
  const why = stallReason(row, nowMs, previous)
616
619
  if (!why) return null
@@ -689,7 +692,7 @@ export function diagnose({ shelf, result, chain, entity, kind }) {
689
692
  })
690
693
  return { stuck_stage: "download", stuck_reason: "stalled_partial_download",
691
694
  likely_fix: "ask_owner", human_action_required: true,
692
- choices: ["keep_waiting", "find_another_release", "give_up"],
695
+ choices: STALL_OWNER_CHOICES,
693
696
  percent_complete: result.download.percent_complete,
694
697
  detail: items,
695
698
  ...(neverStarted.length ? { never_started: { likely_fix: "blocklist_and_research", items: neverStarted } } : {}),
@@ -762,7 +765,7 @@ async function mediaDiagnoseAndFix(a) {
762
765
  }
763
766
  if (action === "ask_owner") {
764
767
  return { action, result: "owner_decision_needed", before, after: null, human_action_required: true,
765
- choices: before.diagnosis?.choices ?? ["keep_waiting", "find_another_release", "give_up"],
768
+ choices: before.diagnosis?.choices ?? STALL_OWNER_CHOICES,
766
769
  ...(before.diagnosis?.never_started ? { never_started: before.diagnosis.never_started } : {}),
767
770
  human_action_reason: before.diagnosis?.summary ?? "The owner decides." }
768
771
  }
@@ -939,7 +942,8 @@ export function queueRowView(r, nowMs, previous) {
939
942
  percent: pct(r), timeleft: r.timeleft ?? null, size_left_bytes: r.sizeleft ?? 0, size_bytes: r.size ?? 0,
940
943
  protocol: r.protocol ?? null, download_client: r.downloadClient ?? null, indexer: r.indexer ?? null,
941
944
  added: r.added ?? null, stalled: Boolean(c?.confirmed),
942
- ...(c ? { stall: { kind: c.kind, state: c.state, percent_complete: c.percent_complete, age_hours: c.age_hours, no_eta: c.no_eta, unchanged_since: c.unchanged_since } } : {}),
945
+ ...(c ? { stall: { kind: c.kind, state: c.state, percent_complete: c.percent_complete, age_hours: c.age_hours, no_eta: c.no_eta, unchanged_since: c.unchanged_since,
946
+ ...(c.confirmed ? { owner_decision_needed: true, likely_fix: "ask_owner", choices: STALL_OWNER_CHOICES } : {}) } } : {}),
943
947
  ...(r.errorMessage ? { error_message: String(r.errorMessage).slice(0, 300) } : {}),
944
948
  }
945
949
  }
@@ -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.869</Repository>
4
+ <Repository>ghcr.io/ourostack/ouroboros-butler:0.1.0-alpha.870</Repository>
5
5
  <Registry>https://github.com/ourostack/ouroboros/pkgs/container/ouroboros-butler</Registry>
6
6
  <Network>host</Network>
7
7
  <Shell>sh</Shell>
@@ -37,6 +37,7 @@ exports.awaitingToolDefinitions = void 0;
37
37
  exports.readAwaitDefinition = readAwaitDefinition;
38
38
  exports.setAwaitToolDeps = setAwaitToolDeps;
39
39
  exports.resetAwaitToolDeps = resetAwaitToolDeps;
40
+ exports.readOwnerAskForAwait = readOwnerAskForAwait;
40
41
  exports.cancelAwaitTool = cancelAwaitTool;
41
42
  exports.cancelStaleAwait = cancelStaleAwait;
42
43
  exports.isBrokenBindingReason = isBrokenBindingReason;
@@ -187,6 +188,37 @@ function recordAsk(agentRoot, entry) {
187
188
  fs.mkdirSync(awaitingDir(agentRoot), { recursive: true });
188
189
  fs.appendFileSync(askLedgerPath(agentRoot), `${JSON.stringify(entry)}\n`, "utf-8");
189
190
  }
191
+ /**
192
+ * The most recent time the owner was asked about this await instance, read from the runtime ask ledger (never from model text).
193
+ * An instance is the await name filed at `createdAt`; an ask recorded before that belongs to an earlier use of the name.
194
+ */
195
+ function readOwnerAskForAwait(agentRoot, name, createdAt) {
196
+ let raw;
197
+ try {
198
+ raw = fs.readFileSync(askLedgerPath(agentRoot), "utf-8");
199
+ }
200
+ catch {
201
+ return null;
202
+ }
203
+ const createdMs = createdAt ? Date.parse(createdAt) : Number.NaN;
204
+ let found = null;
205
+ for (const line of raw.split("\n")) {
206
+ try {
207
+ const entry = JSON.parse(line);
208
+ if (entry.name !== name || typeof entry.at !== "string")
209
+ continue;
210
+ const atMs = Date.parse(entry.at);
211
+ if (!Number.isFinite(atMs) || (Number.isFinite(createdMs) && atMs < createdMs))
212
+ continue;
213
+ if (!found || atMs >= Date.parse(found.at))
214
+ found = { at: entry.at, question: typeof entry.question === "string" && entry.question.length > 0 ? entry.question : null };
215
+ }
216
+ catch {
217
+ continue;
218
+ }
219
+ }
220
+ return found;
221
+ }
190
222
  function recentAskCount(agentRoot, filer, now) {
191
223
  let raw;
192
224
  try {
@@ -286,7 +318,7 @@ async function askOwnerTool(name, observation, question, choices, existing, agen
286
318
  /* v8 ignore next -- defensive: archiveAwait only fails on the file-disappears-mid-call race already covered by v8 ignore inside archiveAwait @preserve */
287
319
  if (!archive.ok)
288
320
  return JSON.stringify({ error: archive.error });
289
- recordAsk(agentRoot, { filer: filerId, name, at: askedAt });
321
+ recordAsk(agentRoot, { filer: filerId, name, at: askedAt, question: parsed.question });
290
322
  if (!tellsFriend || delivered(friendNotice))
291
323
  fulfillAwaitObligation(agentRoot, archive.file);
292
324
  (0, runtime_1.emitNervesEvent)({
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.buildAwaitTurnMessage = buildAwaitTurnMessage;
4
4
  const runtime_1 = require("../nerves/runtime");
5
+ const WAITING_ON_OWNER = /\b(await(ing)?|wait(ing)?)\b[^.]{0,40}\b(ari|owner|decision|decide|choice)\b|\bdecision\b/i;
5
6
  function formatElapsed(ms) {
6
7
  if (ms < 60_000)
7
8
  return "<1m ago";
@@ -48,12 +49,17 @@ function buildAwaitTurnMessage(options) {
48
49
  lines.push("");
49
50
  lines.push(`history: checked ${options.checkedCount}x so far. last checked ${age ?? "(unknown)"}. ${obs}.`);
50
51
  }
52
+ const ask = options.ownerAsk ?? null;
53
+ lines.push(ask ? `owner asked: ${ask.at}${ask.question ? ` ("${ask.question}")` : ""}` : "owner asked: never");
54
+ if (!ask && options.lastObservation && WAITING_ON_OWNER.test(options.lastObservation)) {
55
+ lines.push("my owner has not been asked about this await, so \"awaiting a decision\" is not true. if the download is confirmed stalled, or media_queue marks it owner_decision_needed, ask now with verdict 'ask_owner'.");
56
+ }
51
57
  if (options.checkpoint) {
52
58
  lines.push("");
53
59
  lines.push(`last checkpoint: ${options.checkpoint}`);
54
60
  }
55
61
  lines.push("");
56
62
  lines.push("look around and decide. if the condition is met, call resolve_await with verdict='yes' and a one-line observation. otherwise call resolve_await with verdict='no' and a one-line observation of what i saw this tick.");
57
- lines.push("verdicts: 'yes' (condition met), 'no' (not yet or unsure), 'ask_owner' (only when the condition cannot be met without my owner deciding, e.g. a confirmed dead download; give a plain question and 2-4 choices).");
63
+ lines.push("verdicts: 'yes' (condition met), 'no' (not yet or unsure), 'ask_owner' (only when the condition cannot be met without my owner deciding, e.g. a confirmed dead download, or a stall the tool marks owner_decision_needed; give a plain question and 2-4 choices).");
58
64
  return lines.join("\n");
59
65
  }
@@ -1194,6 +1194,7 @@ async function runPrivateRuntimeTurn(options) {
1194
1194
  // No checkpoint: every await shares one private-runtime session, so its last assistant text is whatever the previous
1195
1195
  // (possibly different) await's tick said. An await's own state is its last observation and checked count.
1196
1196
  checkpoint: undefined,
1197
+ ownerAsk: (0, tools_awaiting_1.readOwnerAskForAwait)(agentRoot, awaitName, parsedAwait?.created_at ?? null),
1197
1198
  now,
1198
1199
  });
1199
1200
  }
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@ouro.bot/cli",
3
- "version": "0.1.0-alpha.869",
3
+ "version": "0.1.0-alpha.870",
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.869",
9
+ "version": "0.1.0-alpha.870",
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.869",
3
+ "version": "0.1.0-alpha.870",
4
4
  "engines": {
5
5
  "node": ">=22"
6
6
  },