@ouro.bot/cli 0.1.0-alpha.905 → 0.1.0-alpha.907

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,20 @@
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.907",
6
+ "changes": [
7
+ "When Ari hands his chat to someone, the Butler greets them in that same chat and starts the interview, instead of asking them to message it somewhere else first."
8
+ ]
9
+ },
10
+ {
11
+ "version": "0.1.0-alpha.906",
12
+ "changes": [
13
+ "Shepherd's judge now finds the verdict when a model thinks aloud in <think> blocks or writes a sentence around its JSON, so judges on models such as MiniMax stop letting every return through as unclear.",
14
+ "When the judge's answer still is not JSON, the returns log shows the first 160 characters of what it said, so the next model mismatch is visible.",
15
+ "A key Shepherd would press is refused when its JSON was found inside the model's prose, since the screen may have planted it, and a logged unparseable answer is passed through the secret filter first."
16
+ ]
17
+ },
4
18
  {
5
19
  "version": "0.1.0-alpha.905",
6
20
  "changes": [
@@ -1,5 +1,5 @@
1
1
  {
2
- "runtimeVersion": "0.1.0-alpha.905",
2
+ "runtimeVersion": "0.1.0-alpha.907",
3
3
  "bundleSchemaVersion": 3,
4
4
  "lastUpdated": "2026-09-03T00:00:00.000Z"
5
5
  }
@@ -27,3 +27,5 @@ I never tell Ari to click, open a settings page, or do a ten-second manual step.
27
27
  When Ari asks me to be brief, my replies are plain sentences with no headers or bold labels, about 600 characters at most. The runtime holds me to this once he has asked.
28
28
 
29
29
  I say a title is on the shelf only when a library lookup in the same turn found it there; otherwise I say I can check whether we have it.
30
+
31
+ When Ari hands his chat to someone ("say hi to her"), that person is reading this same chat now. I greet them here by name and start right away, with a question when he asked me to find out what they want; I never ask them to message me somewhere else first.
@@ -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.905</Repository>
4
+ <Repository>ghcr.io/ourostack/ouroboros-butler:0.1.0-alpha.907</Repository>
5
5
  <Registry>https://github.com/ourostack/ouroboros/pkgs/container/ouroboros-butler</Registry>
6
6
  <Network>host</Network>
7
7
  <Shell>sh</Shell>
@@ -6,6 +6,7 @@ exports.parseVerdict = parseVerdict;
6
6
  exports.judgeMessages = judgeMessages;
7
7
  exports.createShepherdJudge = createShepherdJudge;
8
8
  const runtime_1 = require("../../nerves/runtime");
9
+ const redact_1 = require("./redact");
9
10
  exports.REPLY_MAX_CHARS = 240;
10
11
  const KEYS = new Set(["enter", "escape", "up", "down", "tab", "1", "2", "3", "4", "5", "6", "7", "8", "9", "y", "n"]);
11
12
  const KINDS = new Set(["premature", "gate", "done", "working", "unclear"]);
@@ -24,25 +25,50 @@ function safeReplyText(text) {
24
25
  return null;
25
26
  return line;
26
27
  }
28
+ /**
29
+ * The JSON in a model's answer. Models differ: some wrap it in a code fence, some think aloud in
30
+ * `<think>` blocks or write a sentence around it, so the first `{` to the last `}` is tried too.
31
+ * `inProse` says the JSON came from inside other text, where it might be an object the model
32
+ * quoted from the screen rather than its own verdict.
33
+ */
34
+ function answerJson(content) {
35
+ const text = content.replace(/<think>[\s\S]*?(?:<\/think>|$)/g, "").trim().replace(/^```(?:json)?\s*|\s*```$/g, "");
36
+ const start = text.indexOf("{");
37
+ const end = text.lastIndexOf("}");
38
+ const candidates = start >= 0 && end > start ? [text, text.slice(start, end + 1)] : [text];
39
+ for (const [index, candidate] of candidates.entries()) {
40
+ try {
41
+ return { ok: true, value: JSON.parse(candidate), inProse: index > 0 };
42
+ }
43
+ catch {
44
+ // try the next candidate
45
+ }
46
+ }
47
+ return { ok: false, text };
48
+ }
27
49
  function parseVerdict(content) {
28
- try {
29
- const raw = JSON.parse(content.trim().replace(/^```(?:json)?\s*|\s*```$/g, ""));
30
- const value = raw && typeof raw === "object" ? raw : {};
31
- const kind = typeof value.kind === "string" && KINDS.has(value.kind) ? value.kind : "unclear";
32
- const reason = typeof value.reason === "string" && value.reason.trim() ? value.reason.trim().replace(/\s+/g, " ").slice(0, 300) : "no reason given";
33
- if (kind === "working" && value.news === true)
34
- return { kind, reply: null, reason, news: true };
35
- if (kind !== "premature")
36
- return { kind, reply: null, reason };
37
- const reply = value.reply && typeof value.reply === "object" ? value.reply : {};
38
- if (typeof reply.key === "string" && KEYS.has(reply.key))
39
- return { kind, reply: { key: reply.key }, reason };
40
- const text = typeof reply.text === "string" ? safeReplyText(reply.text) : null;
41
- return text ? { kind, reply: { text }, reason } : { kind: "unclear", reply: null, reason: `premature, but the judge gave no safe reply: ${reason}` };
50
+ const parsed = answerJson(content);
51
+ if (!parsed.ok) {
52
+ const seen = (0, redact_1.redactSecrets)((parsed.text || content).replace(/\s+/g, " ").trim()).slice(0, 160);
53
+ return { kind: "unclear", reply: null, reason: `the judge's answer was not JSON: ${seen || "(empty)"}` };
42
54
  }
43
- catch {
44
- return { kind: "unclear", reply: null, reason: "the judge's answer was not JSON" };
55
+ const raw = parsed.value;
56
+ const value = raw && typeof raw === "object" ? raw : {};
57
+ const kind = typeof value.kind === "string" && KINDS.has(value.kind) ? value.kind : "unclear";
58
+ const reason = typeof value.reason === "string" && value.reason.trim() ? value.reason.trim().replace(/\s+/g, " ").slice(0, 300) : "no reason given";
59
+ if (kind === "working" && value.news === true)
60
+ return { kind, reply: null, reason, news: true };
61
+ if (kind !== "premature")
62
+ return { kind, reply: null, reason };
63
+ const reply = value.reply && typeof value.reply === "object" ? value.reply : {};
64
+ if (typeof reply.key === "string" && KEYS.has(reply.key)) {
65
+ // A key can approve a prompt, and a key from JSON found inside prose may be one the screen planted.
66
+ if (parsed.inProse)
67
+ return { kind: "unclear", reply: null, reason: `premature, but the key came from JSON inside prose: ${reason}` };
68
+ return { kind, reply: { key: reply.key }, reason };
45
69
  }
70
+ const text = typeof reply.text === "string" ? safeReplyText(reply.text) : null;
71
+ return text ? { kind, reply: { text }, reason } : { kind: "unclear", reply: null, reason: `premature, but the judge gave no safe reply: ${reason}` };
46
72
  }
47
73
  function judgeMessages(packet) {
48
74
  return [
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@ouro.bot/cli",
3
- "version": "0.1.0-alpha.905",
3
+ "version": "0.1.0-alpha.907",
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.905",
9
+ "version": "0.1.0-alpha.907",
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.905",
3
+ "version": "0.1.0-alpha.907",
4
4
  "engines": {
5
5
  "node": ">=22"
6
6
  },