@ouro.bot/cli 0.1.0-alpha.1 → 0.1.0-alpha.10

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.
Files changed (46) hide show
  1. package/AdoptionSpecialist.ouro/psyche/SOUL.md +3 -1
  2. package/dist/heart/config.js +34 -0
  3. package/dist/heart/core.js +41 -2
  4. package/dist/heart/daemon/daemon-cli.js +280 -22
  5. package/dist/heart/daemon/daemon.js +3 -0
  6. package/dist/heart/daemon/hatch-animation.js +28 -0
  7. package/dist/heart/daemon/hatch-flow.js +3 -1
  8. package/dist/heart/daemon/hatch-specialist.js +6 -1
  9. package/dist/heart/daemon/log-tailer.js +146 -0
  10. package/dist/heart/daemon/os-cron.js +260 -0
  11. package/dist/heart/daemon/ouro-bot-entry.js +0 -0
  12. package/dist/heart/daemon/ouro-bot-wrapper.js +4 -3
  13. package/dist/heart/daemon/ouro-entry.js +0 -0
  14. package/dist/heart/daemon/process-manager.js +18 -1
  15. package/dist/heart/daemon/runtime-logging.js +9 -5
  16. package/dist/heart/daemon/specialist-orchestrator.js +161 -0
  17. package/dist/heart/daemon/specialist-prompt.js +56 -0
  18. package/dist/heart/daemon/specialist-session.js +150 -0
  19. package/dist/heart/daemon/specialist-tools.js +132 -0
  20. package/dist/heart/daemon/task-scheduler.js +4 -1
  21. package/dist/heart/identity.js +28 -3
  22. package/dist/heart/providers/anthropic.js +3 -0
  23. package/dist/heart/streaming.js +3 -0
  24. package/dist/mind/associative-recall.js +23 -2
  25. package/dist/mind/context.js +85 -1
  26. package/dist/mind/friends/channel.js +8 -0
  27. package/dist/mind/friends/types.js +1 -1
  28. package/dist/mind/memory.js +62 -0
  29. package/dist/mind/pending.js +93 -0
  30. package/dist/mind/prompt-refresh.js +20 -0
  31. package/dist/mind/prompt.js +101 -0
  32. package/dist/nerves/coverage/file-completeness.js +14 -4
  33. package/dist/repertoire/tools-base.js +92 -0
  34. package/dist/repertoire/tools.js +3 -3
  35. package/dist/senses/bluebubbles-client.js +279 -0
  36. package/dist/senses/bluebubbles-entry.js +11 -0
  37. package/dist/senses/bluebubbles-model.js +253 -0
  38. package/dist/senses/bluebubbles-mutation-log.js +76 -0
  39. package/dist/senses/bluebubbles.js +332 -0
  40. package/dist/senses/cli.js +89 -8
  41. package/dist/senses/inner-dialog.js +15 -0
  42. package/dist/senses/session-lock.js +119 -0
  43. package/dist/senses/teams.js +1 -0
  44. package/package.json +4 -3
  45. package/subagents/README.md +3 -1
  46. package/subagents/work-merger.md +33 -2
@@ -0,0 +1,146 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.discoverLogFiles = discoverLogFiles;
37
+ exports.readLastLines = readLastLines;
38
+ exports.formatLogLine = formatLogLine;
39
+ exports.tailLogs = tailLogs;
40
+ const os = __importStar(require("os"));
41
+ const path = __importStar(require("path"));
42
+ const nerves_1 = require("../../nerves");
43
+ const runtime_1 = require("../../nerves/runtime");
44
+ const LEVEL_COLORS = {
45
+ debug: "\x1b[2m",
46
+ info: "\x1b[36m",
47
+ warn: "\x1b[33m",
48
+ error: "\x1b[31m",
49
+ };
50
+ function discoverLogFiles(options) {
51
+ /* v8 ignore start -- integration: default DI stubs for real OS @preserve */
52
+ const homeDir = options.homeDir ?? os.homedir();
53
+ const existsSync = options.existsSync ?? (() => false);
54
+ const readdirSync = options.readdirSync ?? (() => []);
55
+ /* v8 ignore stop */
56
+ const logDir = path.join(homeDir, ".agentstate", "daemon", "logs");
57
+ const files = [];
58
+ if (existsSync(logDir)) {
59
+ for (const name of readdirSync(logDir)) {
60
+ if (!name.endsWith(".ndjson"))
61
+ continue;
62
+ if (options.agentFilter && !name.includes(options.agentFilter))
63
+ continue;
64
+ files.push(path.join(logDir, name));
65
+ }
66
+ }
67
+ return files.sort();
68
+ }
69
+ function readLastLines(filePath, count, readFileSync) {
70
+ let content;
71
+ try {
72
+ content = readFileSync(filePath, "utf-8");
73
+ }
74
+ catch {
75
+ return [];
76
+ }
77
+ const lines = content.split("\n").filter((l) => l.trim().length > 0);
78
+ return lines.slice(-count);
79
+ }
80
+ function formatLogLine(ndjsonLine) {
81
+ try {
82
+ const entry = JSON.parse(ndjsonLine);
83
+ const formatted = (0, nerves_1.formatTerminalEntry)(entry);
84
+ const color = LEVEL_COLORS[entry.level] ?? "";
85
+ return `${color}${formatted}\x1b[0m`;
86
+ }
87
+ catch {
88
+ return ndjsonLine;
89
+ }
90
+ }
91
+ function tailLogs(options = {}) {
92
+ /* v8 ignore start -- integration: default DI stubs for real OS @preserve */
93
+ const writer = options.writer ?? ((text) => process.stdout.write(text));
94
+ const lineCount = options.lines ?? 20;
95
+ const readFileSync = options.readFileSync ?? (() => "");
96
+ /* v8 ignore stop */
97
+ const watchFile = options.watchFile;
98
+ const unwatchFile = options.unwatchFile;
99
+ const files = discoverLogFiles(options);
100
+ (0, runtime_1.emitNervesEvent)({ component: "daemon", event: "daemon.log_tailer_started", message: "log tailer started", meta: { fileCount: files.length, follow: !!options.follow } });
101
+ const fileSizes = new Map();
102
+ // Read initial lines
103
+ for (const file of files) {
104
+ const lines = readLastLines(file, lineCount, readFileSync);
105
+ for (const line of lines) {
106
+ writer(`${formatLogLine(line)}\n`);
107
+ }
108
+ try {
109
+ const content = readFileSync(file, "utf-8");
110
+ fileSizes.set(file, content.length);
111
+ }
112
+ catch {
113
+ fileSizes.set(file, 0);
114
+ }
115
+ }
116
+ // Follow mode
117
+ if (options.follow && watchFile && unwatchFile) {
118
+ for (const file of files) {
119
+ watchFile(file, () => {
120
+ let content;
121
+ try {
122
+ content = readFileSync(file, "utf-8");
123
+ }
124
+ catch {
125
+ return;
126
+ }
127
+ /* v8 ignore next -- defensive: fileSizes always populated above @preserve */
128
+ const prevSize = fileSizes.get(file) ?? 0;
129
+ if (content.length <= prevSize)
130
+ return;
131
+ fileSizes.set(file, content.length);
132
+ const newContent = content.slice(prevSize);
133
+ const newLines = newContent.split("\n").filter((l) => l.trim().length > 0);
134
+ for (const line of newLines) {
135
+ writer(`${formatLogLine(line)}\n`);
136
+ }
137
+ });
138
+ }
139
+ return () => {
140
+ for (const file of files) {
141
+ unwatchFile(file);
142
+ }
143
+ };
144
+ }
145
+ return () => { };
146
+ }
@@ -0,0 +1,260 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.CrontabCronManager = exports.LaunchdCronManager = void 0;
37
+ exports.createOsCronManager = createOsCronManager;
38
+ exports.cadenceToSeconds = cadenceToSeconds;
39
+ exports.scheduleToCalendarInterval = scheduleToCalendarInterval;
40
+ exports.generatePlistXml = generatePlistXml;
41
+ exports.plistLabel = plistLabel;
42
+ exports.crontabLine = crontabLine;
43
+ const os = __importStar(require("os"));
44
+ const runtime_1 = require("../../nerves/runtime");
45
+ const PLIST_PREFIX = "bot.ouro.";
46
+ function plistLabel(job) {
47
+ return `${PLIST_PREFIX}${job.agent}.${job.taskId}`;
48
+ }
49
+ function cadenceToSeconds(schedule) {
50
+ const parts = schedule.trim().split(/\s+/);
51
+ if (parts.length !== 5)
52
+ return null;
53
+ const [minute, hour, day, month, weekday] = parts;
54
+ // Simple interval patterns only
55
+ if (month !== "*" || weekday !== "*" || day !== "*")
56
+ return null;
57
+ const everyNMinutes = /^\*\/(\d+)$/.exec(minute);
58
+ if (everyNMinutes && hour === "*") {
59
+ return parseInt(everyNMinutes[1], 10) * 60;
60
+ }
61
+ const everyNHours = /^\*\/(\d+)$/.exec(hour);
62
+ if (everyNHours && minute === "0") {
63
+ return parseInt(everyNHours[1], 10) * 3600;
64
+ }
65
+ return null;
66
+ }
67
+ function scheduleToCalendarInterval(schedule) {
68
+ const parts = schedule.trim().split(/\s+/);
69
+ if (parts.length !== 5)
70
+ return null;
71
+ const [minute, hour, day, month] = parts;
72
+ const result = {};
73
+ if (minute !== "*" && !/^\*\//.test(minute))
74
+ result.Minute = parseInt(minute, 10);
75
+ if (hour !== "*" && !/^\*\//.test(hour))
76
+ result.Hour = parseInt(hour, 10);
77
+ if (day !== "*")
78
+ result.Day = parseInt(day, 10);
79
+ if (month !== "*")
80
+ result.Month = parseInt(month, 10);
81
+ return Object.keys(result).length > 0 ? result : null;
82
+ }
83
+ function generatePlistXml(job) {
84
+ const label = plistLabel(job);
85
+ const seconds = cadenceToSeconds(job.schedule);
86
+ const calendar = seconds === null ? scheduleToCalendarInterval(job.schedule) : null;
87
+ let triggerXml;
88
+ if (seconds !== null) {
89
+ triggerXml = ` <key>StartInterval</key>\n <integer>${seconds}</integer>`;
90
+ }
91
+ else if (calendar !== null) {
92
+ const entries = Object.entries(calendar)
93
+ .map(([k, v]) => ` <key>${k}</key>\n <integer>${v}</integer>`)
94
+ .join("\n");
95
+ triggerXml = ` <key>StartCalendarInterval</key>\n <dict>\n${entries}\n </dict>`;
96
+ }
97
+ else {
98
+ triggerXml = ` <key>StartInterval</key>\n <integer>1800</integer>`;
99
+ }
100
+ return [
101
+ `<?xml version="1.0" encoding="UTF-8"?>`,
102
+ `<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">`,
103
+ `<plist version="1.0">`,
104
+ `<dict>`,
105
+ ` <key>Label</key>`,
106
+ ` <string>${label}</string>`,
107
+ ` <key>ProgramArguments</key>`,
108
+ ` <array>`,
109
+ ` <string>${job.command.split(" ")[0]}</string>`,
110
+ ...job.command.split(" ").slice(1).map((arg) => ` <string>${arg}</string>`),
111
+ ` </array>`,
112
+ triggerXml,
113
+ ` <key>StandardOutPath</key>`,
114
+ ` <string>/tmp/${label}.stdout.log</string>`,
115
+ ` <key>StandardErrorPath</key>`,
116
+ ` <string>/tmp/${label}.stderr.log</string>`,
117
+ `</dict>`,
118
+ `</plist>`,
119
+ ``,
120
+ ].join("\n");
121
+ }
122
+ class LaunchdCronManager {
123
+ deps;
124
+ constructor(deps) {
125
+ this.deps = deps;
126
+ }
127
+ get launchAgentsDir() {
128
+ return `${this.deps.homeDir}/Library/LaunchAgents`;
129
+ }
130
+ sync(jobs) {
131
+ this.deps.mkdirp(this.launchAgentsDir);
132
+ const desiredLabels = new Set(jobs.map(plistLabel));
133
+ // Remove stale plists
134
+ const existing = this.listPlistFiles();
135
+ for (const filename of existing) {
136
+ const label = filename.replace(".plist", "");
137
+ if (!desiredLabels.has(label)) {
138
+ const fullPath = `${this.launchAgentsDir}/${filename}`;
139
+ try {
140
+ this.deps.exec(`launchctl unload "${fullPath}"`);
141
+ }
142
+ catch { /* best effort */ }
143
+ this.deps.removeFile(fullPath);
144
+ }
145
+ }
146
+ // Write current plists
147
+ for (const job of jobs) {
148
+ const label = plistLabel(job);
149
+ const filename = `${label}.plist`;
150
+ const fullPath = `${this.launchAgentsDir}/${filename}`;
151
+ const xml = generatePlistXml(job);
152
+ try {
153
+ this.deps.exec(`launchctl unload "${fullPath}"`);
154
+ }
155
+ catch { /* best effort */ }
156
+ this.deps.writeFile(fullPath, xml);
157
+ try {
158
+ this.deps.exec(`launchctl load "${fullPath}"`);
159
+ }
160
+ catch { /* best effort */ }
161
+ }
162
+ (0, runtime_1.emitNervesEvent)({ component: "daemon", event: "daemon.os_cron_synced", message: "synced OS cron entries", meta: { platform: "darwin", jobCount: jobs.length } });
163
+ }
164
+ removeAll() {
165
+ const existing = this.listPlistFiles();
166
+ for (const filename of existing) {
167
+ const fullPath = `${this.launchAgentsDir}/${filename}`;
168
+ try {
169
+ this.deps.exec(`launchctl unload "${fullPath}"`);
170
+ }
171
+ catch { /* best effort */ }
172
+ this.deps.removeFile(fullPath);
173
+ }
174
+ }
175
+ list() {
176
+ return this.listPlistFiles().map((f) => f.replace(".plist", ""));
177
+ }
178
+ listPlistFiles() {
179
+ if (!this.deps.existsFile(this.launchAgentsDir))
180
+ return [];
181
+ return this.deps.listDir(this.launchAgentsDir).filter((f) => f.startsWith(PLIST_PREFIX) && f.endsWith(".plist"));
182
+ }
183
+ }
184
+ exports.LaunchdCronManager = LaunchdCronManager;
185
+ const CRONTAB_MARKER_PREFIX = "# ouro:";
186
+ function crontabLine(job) {
187
+ return `${CRONTAB_MARKER_PREFIX}${job.id}\n${job.schedule} ${job.command}`;
188
+ }
189
+ class CrontabCronManager {
190
+ deps;
191
+ constructor(deps) {
192
+ this.deps = deps;
193
+ }
194
+ sync(jobs) {
195
+ const currentLines = this.readCrontab();
196
+ const cleaned = this.removeOuroLines(currentLines);
197
+ const newLines = jobs.map(crontabLine);
198
+ const combined = [...cleaned, ...newLines].join("\n").trim();
199
+ this.deps.execWrite("crontab -", combined ? `${combined}\n` : "");
200
+ }
201
+ removeAll() {
202
+ const currentLines = this.readCrontab();
203
+ const cleaned = this.removeOuroLines(currentLines);
204
+ const combined = cleaned.join("\n").trim();
205
+ this.deps.execWrite("crontab -", combined ? `${combined}\n` : "");
206
+ }
207
+ list() {
208
+ const lines = this.readCrontab();
209
+ return lines
210
+ .filter((l) => l.startsWith(CRONTAB_MARKER_PREFIX))
211
+ .map((l) => l.slice(CRONTAB_MARKER_PREFIX.length));
212
+ }
213
+ readCrontab() {
214
+ try {
215
+ return this.deps.execOutput("crontab -l").split("\n");
216
+ }
217
+ catch {
218
+ return [];
219
+ }
220
+ }
221
+ removeOuroLines(lines) {
222
+ const result = [];
223
+ let skipNext = false;
224
+ for (const line of lines) {
225
+ if (line.startsWith(CRONTAB_MARKER_PREFIX)) {
226
+ skipNext = true;
227
+ continue;
228
+ }
229
+ if (skipNext) {
230
+ skipNext = false;
231
+ continue;
232
+ }
233
+ result.push(line);
234
+ }
235
+ return result;
236
+ }
237
+ }
238
+ exports.CrontabCronManager = CrontabCronManager;
239
+ function createOsCronManager(options = {}) {
240
+ const platform = options.platform ?? process.platform;
241
+ if (platform === "darwin") {
242
+ /* v8 ignore start -- integration: default stubs for real OS operations @preserve */
243
+ const deps = options.launchdDeps ?? {
244
+ exec: () => { },
245
+ writeFile: () => { },
246
+ removeFile: () => { },
247
+ existsFile: () => false,
248
+ listDir: () => [],
249
+ mkdirp: () => { },
250
+ homeDir: os.homedir(),
251
+ };
252
+ /* v8 ignore stop */
253
+ return new LaunchdCronManager(deps);
254
+ }
255
+ const deps = options.crontabDeps ?? {
256
+ execOutput: () => "",
257
+ execWrite: () => { },
258
+ };
259
+ return new CrontabCronManager(deps);
260
+ }
File without changes
@@ -37,8 +37,9 @@ exports.runOuroBotWrapper = runOuroBotWrapper;
37
37
  const runtime_1 = require("../../nerves/runtime");
38
38
  const daemon_cli_1 = require("./daemon-cli");
39
39
  async function defaultLoadCanonicalRunner() {
40
- const packageName = "@ouro.bot/cli";
41
- const specifier = packageName;
40
+ // Use the subpath export so we get the daemon-cli module directly,
41
+ // NOT the root entry point which has side-effects (immediately runs the CLI).
42
+ const specifier = "@ouro.bot/cli/runOuroCli";
42
43
  const loaded = await Promise.resolve(`${specifier}`).then(s => __importStar(require(s)));
43
44
  const candidate = Object.prototype.hasOwnProperty.call(loaded, "runOuroCli")
44
45
  ? loaded["runOuroCli"]
@@ -46,7 +47,7 @@ async function defaultLoadCanonicalRunner() {
46
47
  if (typeof candidate === "function") {
47
48
  return candidate;
48
49
  }
49
- throw new Error("@ouro.bot/cli does not export runOuroCli");
50
+ throw new Error("@ouro.bot/cli/runOuroCli does not export runOuroCli");
50
51
  }
51
52
  function defaultWriteStdout(_text) {
52
53
  // Wrapper is intentionally silent by default to avoid duplicate terminal output.
File without changes
@@ -100,7 +100,7 @@ class DaemonProcessManager {
100
100
  const child = this.spawnFn("node", args, {
101
101
  cwd: runCwd,
102
102
  env: state.config.env ? { ...process.env, ...state.config.env } : process.env,
103
- stdio: ["ignore", "ignore", "ignore"],
103
+ stdio: ["ignore", "ignore", "ignore", "ipc"],
104
104
  });
105
105
  state.process = child;
106
106
  state.snapshot.status = "running";
@@ -151,6 +151,23 @@ class DaemonProcessManager {
151
151
  await this.stopAgent(state.config.name);
152
152
  }
153
153
  }
154
+ sendToAgent(agent, message) {
155
+ const state = this.requireAgent(agent);
156
+ if (!state.process)
157
+ return;
158
+ try {
159
+ state.process.send(message);
160
+ }
161
+ catch {
162
+ (0, runtime_1.emitNervesEvent)({
163
+ level: "warn",
164
+ component: "daemon",
165
+ event: "daemon.agent_ipc_send_error",
166
+ message: "failed to send IPC message to managed agent",
167
+ meta: { agent },
168
+ });
169
+ }
170
+ }
154
171
  getAgentSnapshot(agent) {
155
172
  return this.agents.get(agent)?.snapshot;
156
173
  }
@@ -43,23 +43,27 @@ const DEFAULT_RUNTIME_LOGGING = {
43
43
  level: "info",
44
44
  sinks: ["terminal", "ndjson"],
45
45
  };
46
+ function defaultLevelForProcess(processName) {
47
+ return processName === "daemon" ? "info" : "warn";
48
+ }
46
49
  function isLogLevel(value) {
47
50
  return value === "debug" || value === "info" || value === "warn" || value === "error";
48
51
  }
49
- function resolveRuntimeLoggingConfig(configPath) {
52
+ function resolveRuntimeLoggingConfig(configPath, processName) {
53
+ const defaultLevel = defaultLevelForProcess(processName);
50
54
  let parsed = null;
51
55
  try {
52
56
  const raw = fs.readFileSync(configPath, "utf-8");
53
57
  parsed = JSON.parse(raw);
54
58
  }
55
59
  catch {
56
- return { ...DEFAULT_RUNTIME_LOGGING };
60
+ return { ...DEFAULT_RUNTIME_LOGGING, level: defaultLevel };
57
61
  }
58
62
  if (!parsed || typeof parsed !== "object") {
59
- return { ...DEFAULT_RUNTIME_LOGGING };
63
+ return { ...DEFAULT_RUNTIME_LOGGING, level: defaultLevel };
60
64
  }
61
65
  const candidate = parsed;
62
- const level = isLogLevel(candidate.level) ? candidate.level : DEFAULT_RUNTIME_LOGGING.level;
66
+ const level = isLogLevel(candidate.level) ? candidate.level : defaultLevel;
63
67
  const sinks = Array.isArray(candidate.sinks)
64
68
  ? candidate.sinks.filter((entry) => entry === "terminal" || entry === "ndjson")
65
69
  : DEFAULT_RUNTIME_LOGGING.sinks;
@@ -71,7 +75,7 @@ function resolveRuntimeLoggingConfig(configPath) {
71
75
  function configureDaemonRuntimeLogger(processName, options = {}) {
72
76
  const homeDir = options.homeDir ?? os.homedir();
73
77
  const configPath = options.configPath ?? path.join(homeDir, ".agentstate", "daemon", "logging.json");
74
- const config = resolveRuntimeLoggingConfig(configPath);
78
+ const config = resolveRuntimeLoggingConfig(configPath, processName);
75
79
  const sinks = config.sinks.map((sinkName) => {
76
80
  if (sinkName === "terminal") {
77
81
  return (0, nerves_1.createTerminalSink)();
@@ -0,0 +1,161 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.runAdoptionSpecialist = runAdoptionSpecialist;
37
+ const fs = __importStar(require("fs"));
38
+ const path = __importStar(require("path"));
39
+ const runtime_1 = require("../../nerves/runtime");
40
+ const identity_1 = require("../identity");
41
+ const config_1 = require("../config");
42
+ const core_1 = require("../core");
43
+ const hatch_flow_1 = require("./hatch-flow");
44
+ const specialist_prompt_1 = require("./specialist-prompt");
45
+ const specialist_tools_1 = require("./specialist-tools");
46
+ const specialist_session_1 = require("./specialist-session");
47
+ function listExistingBundles(bundlesRoot) {
48
+ let entries;
49
+ try {
50
+ entries = fs.readdirSync(bundlesRoot, { withFileTypes: true });
51
+ }
52
+ catch {
53
+ return [];
54
+ }
55
+ const discovered = [];
56
+ for (const entry of entries) {
57
+ if (!entry.isDirectory() || !entry.name.endsWith(".ouro"))
58
+ continue;
59
+ const agentName = entry.name.slice(0, -5);
60
+ discovered.push(agentName);
61
+ }
62
+ return discovered.sort((a, b) => a.localeCompare(b));
63
+ }
64
+ function pickRandomIdentity(identitiesDir, random) {
65
+ const files = fs.readdirSync(identitiesDir).filter((f) => f.endsWith(".md"));
66
+ if (files.length === 0) {
67
+ return { fileName: "default", content: "I am the adoption specialist." };
68
+ }
69
+ const idx = Math.floor(random() * files.length);
70
+ const fileName = files[idx];
71
+ const content = fs.readFileSync(path.join(identitiesDir, fileName), "utf-8");
72
+ return { fileName, content };
73
+ }
74
+ /**
75
+ * Run the full adoption specialist flow:
76
+ * 1. Pick a random identity from the bundled AdoptionSpecialist.ouro
77
+ * 2. Read SOUL.md
78
+ * 3. List existing bundles
79
+ * 4. Build system prompt
80
+ * 5. Set up provider (setAgentName, setAgentConfigOverride, writeSecretsFile, reset caches)
81
+ * 6. Run the specialist session
82
+ * 7. Clean up identity/config overrides
83
+ * 8. Return hatchling name
84
+ */
85
+ async function runAdoptionSpecialist(deps) {
86
+ const { bundleSourceDir, bundlesRoot, secretsRoot, provider, credentials, humanName, callbacks, signal } = deps;
87
+ const random = deps.random ?? Math.random;
88
+ (0, runtime_1.emitNervesEvent)({
89
+ component: "daemon",
90
+ event: "daemon.specialist_orchestrator_start",
91
+ message: "starting adoption specialist orchestrator",
92
+ meta: { provider, bundleSourceDir },
93
+ });
94
+ // 1. Read SOUL.md
95
+ const soulPath = path.join(bundleSourceDir, "psyche", "SOUL.md");
96
+ let soulText = "";
97
+ try {
98
+ soulText = fs.readFileSync(soulPath, "utf-8");
99
+ }
100
+ catch {
101
+ // No SOUL.md -- proceed without it
102
+ }
103
+ // 2. Pick random identity
104
+ const identitiesDir = path.join(bundleSourceDir, "psyche", "identities");
105
+ const identity = pickRandomIdentity(identitiesDir, random);
106
+ (0, runtime_1.emitNervesEvent)({
107
+ component: "daemon",
108
+ event: "daemon.specialist_identity_picked",
109
+ message: "picked specialist identity",
110
+ meta: { identity: identity.fileName },
111
+ });
112
+ // 3. List existing bundles
113
+ const existingBundles = listExistingBundles(bundlesRoot);
114
+ // 4. Build system prompt
115
+ const systemPrompt = (0, specialist_prompt_1.buildSpecialistSystemPrompt)(soulText, identity.content, existingBundles);
116
+ // 5. Set up provider
117
+ (0, identity_1.setAgentName)("AdoptionSpecialist");
118
+ (0, identity_1.setAgentConfigOverride)({
119
+ version: 1,
120
+ enabled: true,
121
+ provider,
122
+ phrases: { thinking: ["thinking"], tool: ["checking"], followup: ["processing"] },
123
+ });
124
+ (0, hatch_flow_1.writeSecretsFile)("AdoptionSpecialist", provider, credentials, secretsRoot);
125
+ (0, config_1.resetConfigCache)();
126
+ (0, core_1.resetProviderRuntime)();
127
+ try {
128
+ // Create provider runtime
129
+ const providerRuntime = (0, core_1.createProviderRegistry)().resolve();
130
+ if (!providerRuntime) {
131
+ throw new Error("Failed to create provider runtime for adoption specialist");
132
+ }
133
+ // 6. Run session
134
+ const tools = (0, specialist_tools_1.getSpecialistTools)();
135
+ const readline = deps.createReadline();
136
+ const result = await (0, specialist_session_1.runSpecialistSession)({
137
+ providerRuntime,
138
+ systemPrompt,
139
+ tools,
140
+ execTool: (name, args) => (0, specialist_tools_1.execSpecialistTool)(name, args, {
141
+ humanName,
142
+ provider,
143
+ credentials,
144
+ bundlesRoot,
145
+ secretsRoot,
146
+ specialistIdentitiesDir: identitiesDir,
147
+ }),
148
+ readline,
149
+ callbacks,
150
+ signal,
151
+ kickoffMessage: "hi, i just ran ouro for the first time",
152
+ });
153
+ return result.hatchedAgentName;
154
+ }
155
+ finally {
156
+ // 7. Cleanup: restore identity/config state
157
+ (0, identity_1.setAgentConfigOverride)(null);
158
+ (0, config_1.resetConfigCache)();
159
+ (0, core_1.resetProviderRuntime)();
160
+ }
161
+ }