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

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,13 @@
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.869",
6
+ "changes": [
7
+ "Await ticks no longer inherit the last assistant text of the shared inner session as their checkpoint: a true note about one await (\"already archived as yes\") was handed to every later tick, so the replay await never checked its condition and the Butler's own awaits could be skipped the same way; an await now carries only its own last observation and check count.",
8
+ "Replay gate await case: the await has a name unique to the run, new awaits are told from archived ones by creation time instead of name alone, the readback reports the exact archive status and cancel reason, the case waits up to 8 minutes, and the replay window default is 45 minutes so it covers that wait."
9
+ ]
10
+ },
4
11
  {
5
12
  "version": "0.1.0-alpha.868",
6
13
  "changes": [
@@ -74,7 +74,7 @@ const SHUTDOWN_FLAG = "/run/ouro-authority-shutdown"
74
74
  const GATE_SCRIPT = `${ROOT}/package/deploy/unraid/sanctuary-replay-gate.mjs`
75
75
  const GATE_PROVISION = `${BUNDLE}/state/replay-client/provision.json`
76
76
  // The whole gate (every case, including the six-minute await wait) must finish well inside this; a hung gate is a failed gate.
77
- export const GATE_TIMEOUT_MS = 45 * 60 * 1000
77
+ export const GATE_TIMEOUT_MS = 60 * 60 * 1000
78
78
  const UPGRADE_STEPS = ["stop", "switch", "resident", "migrate", "start"]
79
79
 
80
80
  const sh = (file, args, opts = {}) => execFileSync(file, args, { encoding: "utf8", maxBuffer: 64 << 20, ...opts })
@@ -22,8 +22,10 @@ export const CONTAINER = "ouro-butler"
22
22
  // The uid the A2A sense runs as inside the container; the trust probe must see the window exactly as that process does.
23
23
  export const BUTLER_USER = "10001:10001"
24
24
  export const CLI_ENTRY = "/opt/ouro/dist/heart/daemon/ouro-entry.js"
25
- export const DEFAULT_WINDOW_MINUTES = 30
26
- export const AWAIT_TIMEOUT_MS = 6 * 60 * 1000
25
+ // Long enough for every case at its slowest (five chat cases at up to 5.5 minutes each, plus the await case's 8 minute wait),
26
+ // and well inside the 2 hour cap: an await that has not resolved when the window closes is cancelled by the Butler.
27
+ export const DEFAULT_WINDOW_MINUTES = 45
28
+ export const AWAIT_TIMEOUT_MS = 8 * 60 * 1000
27
29
  export const AWAIT_POLL_MS = 15 * 1000
28
30
  export const MESSAGE_TIMEOUT_MS = 330 * 1000
29
31
  const PEER_PROFILE = "sanctuary-agent-peer"
@@ -184,7 +186,9 @@ export const CASES = [
184
186
  },
185
187
  {
186
188
  id: "await-self-resolve",
187
- words: "file an await that resolves once the ouro-butler container is running, check every 1m, max 15m",
189
+ // A name unique to the run: an earlier run leaves an archived await of the same name behind, and the Butler's
190
+ // shared inner session remembers it.
191
+ words: ({ context }) => `file an await named replay_${context.slice(0, 8)} that resolves once the ouro-butler container is running, check every 1m, max 15m`,
188
192
  sender: "principal",
189
193
  // An await filed from a plain A2A chat has no request id, so the runtime cancels it as legacy provenance. Only a
190
194
  // delegated principal command carries the follow-up authority an await needs.
@@ -198,7 +202,7 @@ export const CASES = [
198
202
  const filed = newAwaits(before, after)
199
203
  return [
200
204
  check("an await was filed", filed.length > 0),
201
- check("every new await was archived as resolved", filed.length > 0 && filed.every((entry) => entry.done && entry.status === "resolved"), filed.map((entry) => `${entry.name}:${entry.done ? entry.status : "pending"}`).join(",")),
205
+ check("every new await was archived as resolved", filed.length > 0 && filed.every((entry) => entry.done && entry.status === "resolved"), filed.map((entry) => `${entry.name}:${entry.done ? entry.status : "pending"}${entry.reason ? ` (${entry.reason})` : ""}`).join(",")),
202
206
  check("its delivery went to the sink", sinkFor(sink, friends.principal, (line) => !isDelegatedNotice(line)).length > 0),
203
207
  ]
204
208
  },
@@ -207,10 +211,11 @@ export const CASES = [
207
211
 
208
212
  /** Awaits present after the case that were not present before it, with their archive state. */
209
213
  export function newAwaits(before, after) {
210
- const known = new Set([...before.awaiting.map((entry) => entry.name), ...before.done.map((entry) => entry.name)])
211
- const pending = after.awaiting.filter((entry) => !known.has(entry.name)).map((entry) => ({ name: entry.name, done: false, status: entry.status }))
212
- const archived = after.done.filter((entry) => !known.has(entry.name)).map((entry) => ({ name: entry.name, done: true, status: entry.status }))
213
- return [...pending, ...archived]
214
+ // Name alone is not identity: an archived await from an earlier run can share a name with a new one.
215
+ const key = (entry) => `${entry.name}|${entry.createdAt ?? ""}`
216
+ const known = new Set([...before.awaiting, ...before.done].map(key))
217
+ const view = (entry, done) => ({ name: entry.name, done, status: entry.status, createdAt: entry.createdAt ?? "", reason: entry.reason ?? "" })
218
+ return [...after.awaiting.filter((entry) => !known.has(key(entry))).map((entry) => view(entry, false)), ...after.done.filter((entry) => !known.has(key(entry))).map((entry) => view(entry, true))]
214
219
  }
215
220
 
216
221
  // ---- orchestration (all I/O is behind `host`) --------------------------------------------------------------------
@@ -225,7 +230,8 @@ export async function runCase(host, testCase, { plant } = {}) {
225
230
  return { id: testCase.id, status: "skipped", reason: skipReason, checks: [] }
226
231
  }
227
232
  const context = randomUUID()
228
- const sent = await host.send({ who: testCase.sender, text: testCase.words, delegated: testCase.delegated, context })
233
+ const text = typeof testCase.words === "function" ? testCase.words({ context }) : testCase.words
234
+ const sent = await host.send({ who: testCase.sender, text, delegated: testCase.delegated, context })
229
235
  let after = await host.observe()
230
236
  if (testCase.poll) {
231
237
  const deadline = host.now() + testCase.poll.timeoutMs
@@ -372,7 +378,7 @@ export function makeHost({ bundle = DEFAULT_BUNDLE, cardUrl, log = console.error
372
378
  const list = (dir) => (existsSync(dir) ? readdirSync(dir) : [])
373
379
  const awaitEntries = (dir) => list(dir).filter((name) => name.endsWith(".md")).map((name) => {
374
380
  const text = readFileSync(path.join(dir, name), "utf8")
375
- return { name, status: /^status:\s*(\S+)/m.exec(text)?.[1] ?? "pending" }
381
+ return { name, status: /^status:\s*(\S+)/m.exec(text)?.[1] ?? "pending", createdAt: /^created_at:\s*(\S+)/m.exec(text)?.[1] ?? "", reason: /^cancel_reason:\s*(.+)$/m.exec(text)?.[1]?.trim() ?? "" }
376
382
  })
377
383
  return {
378
384
  friends: { principal: provisioned.principal.friendId, stranger: provisioned.stranger.friendId },
@@ -1,5 +1,5 @@
1
1
  {
2
- "runtimeVersion": "0.1.0-alpha.868",
2
+ "runtimeVersion": "0.1.0-alpha.869",
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.868</Repository>
4
+ <Repository>ghcr.io/ourostack/ouroboros-butler:0.1.0-alpha.869</Repository>
5
5
  <Registry>https://github.com/ourostack/ouroboros/pkgs/container/ouroboros-butler</Registry>
6
6
  <Network>host</Network>
7
7
  <Shell>sh</Shell>
@@ -1191,7 +1191,9 @@ async function runPrivateRuntimeTurn(options) {
1191
1191
  lastCheckedAt,
1192
1192
  lastObservation,
1193
1193
  checkedCount,
1194
- checkpoint: displayCheckpoint(state.checkpoint),
1194
+ // No checkpoint: every await shares one private-runtime session, so its last assistant text is whatever the previous
1195
+ // (possibly different) await's tick said. An await's own state is its last observation and checked count.
1196
+ checkpoint: undefined,
1195
1197
  now,
1196
1198
  });
1197
1199
  }
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@ouro.bot/cli",
3
- "version": "0.1.0-alpha.868",
3
+ "version": "0.1.0-alpha.869",
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.868",
9
+ "version": "0.1.0-alpha.869",
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.868",
3
+ "version": "0.1.0-alpha.869",
4
4
  "engines": {
5
5
  "node": ">=22"
6
6
  },