@ouro.bot/cli 0.1.0-alpha.121 → 0.1.0-alpha.123

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,19 @@
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.123",
6
+ "changes": [
7
+ "Fixed Anthropic API request format: effort parameter moved from thinking object to output_config. Was causing 400 errors that masked real model access restrictions."
8
+ ]
9
+ },
10
+ {
11
+ "version": "0.1.0-alpha.122",
12
+ "changes": [
13
+ "Daemon now uses a pidfile (~/.ouro-cli/daemon.pids) for process cleanup instead of fragile ps-based name matching. All managed PIDs (daemon, agents, senses) are written on startup and reaped on next restart.",
14
+ "Failover reply matching now uses includes() instead of exact match, fixing switch commands in BlueBubbles where channel metadata prefixes the user text."
15
+ ]
16
+ },
4
17
  {
5
18
  "version": "0.1.0-alpha.121",
6
19
  "changes": [
@@ -35,8 +35,10 @@ var __importStar = (this && this.__importStar) || (function () {
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.OuroDaemon = void 0;
37
37
  exports.killOrphanProcesses = killOrphanProcesses;
38
+ exports.writePidfile = writePidfile;
38
39
  const fs = __importStar(require("fs"));
39
40
  const net = __importStar(require("net"));
41
+ const os = __importStar(require("os"));
40
42
  const path = __importStar(require("path"));
41
43
  const identity_1 = require("../identity");
42
44
  const runtime_1 = require("../../nerves/runtime");
@@ -51,28 +53,41 @@ const child_process_1 = require("child_process");
51
53
  const pending_1 = require("../../mind/pending");
52
54
  const channel_1 = require("../../mind/friends/channel");
53
55
  const mcp_manager_1 = require("../../repertoire/mcp-manager");
56
+ const PIDFILE_PATH = path.join(os.homedir(), ".ouro-cli", "daemon.pids");
54
57
  /**
55
- * Kill ALL orphaned ouro processes from previous daemon instances.
56
- * On each `ouro up`, old daemon AND agent processes persist because:
57
- * - Daemons are spawned detached and never killed on restart
58
- * - Each old daemon keeps respawning agents from its own (stale) code
59
- * This scans for both daemon-entry.js and agent-entry.js processes
60
- * and kills everything except the current process.
58
+ * Kill all ouro processes from the previous daemon instance using the pidfile.
59
+ * On startup, reads PIDs from ~/.ouro-cli/daemon.pids, kills them all, then
60
+ * deletes the file. The new daemon writes its own PIDs after spawning.
61
+ *
62
+ * Falls back to ps-based scanning if the pidfile doesn't exist (first run
63
+ * or manual cleanup).
61
64
  */
62
- /* v8 ignore start -- orphan cleanup: uses ps/kill, tested via deployment @preserve */
65
+ /* v8 ignore start -- process lifecycle: uses kill/ps, tested via deployment @preserve */
63
66
  function killOrphanProcesses() {
64
67
  try {
65
- const myPid = process.pid;
66
- const result = (0, child_process_1.execSync)("ps -eo pid,command", { encoding: "utf-8", timeout: 5000 });
67
- const pidsToKill = [];
68
- for (const line of result.split("\n")) {
69
- if (!line.includes("agent-entry.js") && !line.includes("daemon-entry.js") && !line.includes("-entry.js --agent"))
70
- continue;
71
- const trimmed = line.trim();
72
- const pid = parseInt(trimmed, 10);
73
- if (isNaN(pid) || pid === myPid)
74
- continue;
75
- pidsToKill.push(pid);
68
+ let pidsToKill = [];
69
+ // Primary: read pidfile from previous daemon
70
+ try {
71
+ const raw = fs.readFileSync(PIDFILE_PATH, "utf-8");
72
+ pidsToKill = raw.split("\n").map((s) => parseInt(s.trim(), 10)).filter((n) => !isNaN(n) && n !== process.pid);
73
+ fs.unlinkSync(PIDFILE_PATH);
74
+ }
75
+ catch {
76
+ // No pidfile fall back to ps scan
77
+ }
78
+ // Fallback: scan ps for any ouro entry processes we missed
79
+ if (pidsToKill.length === 0) {
80
+ try {
81
+ const result = (0, child_process_1.execSync)("ps -eo pid,command", { encoding: "utf-8", timeout: 5000 });
82
+ for (const line of result.split("\n")) {
83
+ if (!line.includes("agent-entry.js") && !line.includes("daemon-entry.js") && !line.includes("-entry.js --agent"))
84
+ continue;
85
+ const pid = parseInt(line.trim(), 10);
86
+ if (!isNaN(pid) && pid !== process.pid)
87
+ pidsToKill.push(pid);
88
+ }
89
+ }
90
+ catch { /* ps failed — best effort */ }
76
91
  }
77
92
  if (pidsToKill.length > 0) {
78
93
  for (const pid of pidsToKill) {
@@ -99,6 +114,18 @@ function killOrphanProcesses() {
99
114
  });
100
115
  }
101
116
  }
117
+ /**
118
+ * Write all managed PIDs (daemon + children) to the pidfile.
119
+ * Called after all agents and senses are spawned.
120
+ */
121
+ function writePidfile(extraPids = []) {
122
+ try {
123
+ const pids = [process.pid, ...extraPids].filter(Boolean);
124
+ fs.mkdirSync(path.dirname(PIDFILE_PATH), { recursive: true });
125
+ fs.writeFileSync(PIDFILE_PATH, pids.join("\n") + "\n", "utf-8");
126
+ }
127
+ catch { /* best effort */ }
128
+ }
102
129
  function buildWorkerRows(snapshots) {
103
130
  return snapshots.map((snapshot) => ({
104
131
  agent: snapshot.name,
@@ -207,9 +234,17 @@ class OuroDaemon {
207
234
  // Pre-initialize MCP connections so they're ready for the first command (non-blocking)
208
235
  /* v8 ignore next -- catch callback: getSharedMcpManager logs errors internally @preserve */
209
236
  (0, mcp_manager_1.getSharedMcpManager)().catch(() => { });
237
+ /* v8 ignore start -- orphan cleanup + pidfile: calls process management functions @preserve */
210
238
  killOrphanProcesses();
239
+ /* v8 ignore stop */
211
240
  await this.processManager.startAutoStartAgents();
212
241
  await this.senseManager?.startAutoStartSenses();
242
+ // Write all managed PIDs to disk so the next daemon can clean up
243
+ /* v8 ignore start -- pidfile write: collects PIDs from process managers @preserve */
244
+ const agentPids = this.processManager.listAgentSnapshots().map((s) => s.pid).filter((p) => p !== null);
245
+ const sensePids = this.senseManager?.listManagedPids?.() ?? [];
246
+ writePidfile([...agentPids, ...sensePids]);
247
+ /* v8 ignore stop */
213
248
  this.scheduler.start?.();
214
249
  await this.scheduler.reconcile?.();
215
250
  await this.drainPendingBundleMessages();
@@ -251,6 +251,13 @@ class DaemonSenseManager {
251
251
  async stopAll() {
252
252
  await this.processManager.stopAll();
253
253
  }
254
+ /* v8 ignore start -- pid collection for orphan cleanup pidfile @preserve */
255
+ listManagedPids() {
256
+ return this.processManager.listAgentSnapshots()
257
+ .map((s) => s.pid)
258
+ .filter((pid) => pid !== null && pid !== undefined);
259
+ }
260
+ /* v8 ignore stop */
254
261
  listSenseRows() {
255
262
  const runtime = new Map();
256
263
  for (const snapshot of this.processManager.listAgentSnapshots()) {
@@ -80,7 +80,7 @@ function buildFailoverContext(_errorMessage, classification, currentProvider, cu
80
80
  function handleFailoverReply(reply, context) {
81
81
  const lower = reply.toLowerCase().trim();
82
82
  for (const provider of context.workingProviders) {
83
- if (lower === `switch to ${provider}` || lower === provider) {
83
+ if (lower.includes(`switch to ${provider}`) || lower === provider) {
84
84
  return { action: "switch", provider };
85
85
  }
86
86
  }
@@ -234,7 +234,8 @@ async function streamAnthropicMessages(client, model, request) {
234
234
  max_tokens: maxTokens,
235
235
  messages,
236
236
  stream: true,
237
- thinking: { type: "adaptive", effort: request.reasoningEffort ?? "medium" },
237
+ thinking: { type: "adaptive" },
238
+ output_config: { effort: request.reasoningEffort ?? "medium" },
238
239
  };
239
240
  if (system)
240
241
  params.system = system;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ouro.bot/cli",
3
- "version": "0.1.0-alpha.121",
3
+ "version": "0.1.0-alpha.123",
4
4
  "main": "dist/heart/daemon/ouro-entry.js",
5
5
  "bin": {
6
6
  "cli": "dist/heart/daemon/ouro-bot-entry.js",