@ouro.bot/cli 0.1.0-alpha.19 → 0.1.0-alpha.191

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 (202) hide show
  1. package/README.md +187 -191
  2. package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/agent.json +3 -2
  3. package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/SOUL.md +1 -1
  4. package/changelog.json +1200 -0
  5. package/dist/heart/active-work.js +832 -0
  6. package/dist/heart/bridges/manager.js +358 -0
  7. package/dist/heart/bridges/state-machine.js +135 -0
  8. package/dist/heart/bridges/store.js +123 -0
  9. package/dist/heart/cares.js +174 -0
  10. package/dist/heart/commitments.js +111 -0
  11. package/dist/heart/config.js +40 -30
  12. package/dist/heart/core.js +620 -124
  13. package/dist/heart/cross-chat-delivery.js +146 -0
  14. package/dist/heart/daemon/agent-discovery.js +81 -0
  15. package/dist/heart/daemon/agent-service.js +360 -0
  16. package/dist/heart/daemon/auth-flow.js +477 -0
  17. package/dist/heart/daemon/cadence.js +70 -0
  18. package/dist/heart/daemon/daemon-cli.js +2470 -210
  19. package/dist/heart/daemon/daemon-entry.js +157 -4
  20. package/dist/heart/daemon/daemon-health.js +136 -0
  21. package/dist/heart/daemon/daemon-runtime-sync.js +212 -0
  22. package/dist/heart/daemon/daemon-tombstone.js +123 -0
  23. package/dist/heart/daemon/daemon.js +449 -17
  24. package/dist/heart/daemon/habit-migration.js +181 -0
  25. package/dist/heart/daemon/habit-parser.js +119 -0
  26. package/dist/heart/daemon/habit-scheduler.js +369 -0
  27. package/dist/heart/daemon/hatch-flow.js +31 -112
  28. package/dist/heart/daemon/hatch-specialist.js +3 -3
  29. package/dist/heart/daemon/hooks/agent-config-v2.js +33 -0
  30. package/dist/heart/daemon/hooks/bundle-meta.js +92 -0
  31. package/dist/heart/daemon/inner-status.js +89 -0
  32. package/dist/heart/daemon/launchd.js +165 -0
  33. package/dist/heart/daemon/log-tailer.js +4 -3
  34. package/dist/heart/daemon/mcp-server.js +591 -0
  35. package/dist/heart/daemon/message-router.js +17 -8
  36. package/dist/heart/daemon/os-cron-deps.js +134 -0
  37. package/dist/heart/daemon/ouro-bot-global-installer.js +128 -0
  38. package/dist/heart/daemon/ouro-path-installer.js +78 -34
  39. package/dist/heart/daemon/ouro-version-manager.js +171 -0
  40. package/dist/heart/daemon/outlook-http.js +399 -0
  41. package/dist/heart/daemon/outlook-read.js +1331 -0
  42. package/dist/heart/daemon/outlook-render.js +1032 -0
  43. package/dist/heart/daemon/outlook-types.js +7 -0
  44. package/dist/heart/daemon/outlook-view.js +194 -0
  45. package/dist/heart/daemon/process-manager.js +75 -0
  46. package/dist/heart/daemon/run-hooks.js +39 -0
  47. package/dist/heart/daemon/runtime-logging.js +58 -15
  48. package/dist/heart/daemon/runtime-metadata.js +219 -0
  49. package/dist/heart/daemon/runtime-mode.js +67 -0
  50. package/dist/heart/daemon/safe-mode.js +161 -0
  51. package/dist/heart/daemon/sense-manager.js +50 -2
  52. package/dist/heart/daemon/session-id-resolver.js +131 -0
  53. package/dist/heart/daemon/skill-management-installer.js +94 -0
  54. package/dist/heart/daemon/socket-client.js +202 -0
  55. package/dist/heart/daemon/specialist-prompt.js +10 -7
  56. package/dist/heart/daemon/specialist-tools.js +49 -3
  57. package/dist/heart/daemon/staged-restart.js +114 -0
  58. package/dist/heart/daemon/task-scheduler.js +3 -25
  59. package/dist/heart/daemon/thoughts.js +507 -0
  60. package/dist/heart/daemon/update-checker.js +111 -0
  61. package/dist/heart/daemon/update-hooks.js +138 -0
  62. package/dist/heart/daemon/wrapper-publish-guard.js +86 -0
  63. package/dist/heart/delegation.js +62 -0
  64. package/dist/heart/identity.js +136 -55
  65. package/dist/heart/intentions.js +168 -0
  66. package/dist/heart/kicks.js +2 -20
  67. package/dist/heart/migrate-config.js +127 -0
  68. package/dist/heart/model-capabilities.js +48 -0
  69. package/dist/heart/obligations.js +220 -0
  70. package/dist/heart/presence.js +185 -0
  71. package/dist/heart/progress-story.js +42 -0
  72. package/dist/heart/provider-failover.js +88 -0
  73. package/dist/heart/provider-ping.js +172 -0
  74. package/dist/heart/providers/anthropic-token.js +163 -0
  75. package/dist/heart/providers/anthropic.js +187 -41
  76. package/dist/heart/providers/azure.js +116 -10
  77. package/dist/heart/providers/github-copilot.js +157 -0
  78. package/dist/heart/providers/minimax.js +33 -3
  79. package/dist/heart/providers/openai-codex.js +51 -16
  80. package/dist/heart/session-activity.js +172 -0
  81. package/dist/heart/session-recall.js +216 -0
  82. package/dist/heart/streaming.js +36 -27
  83. package/dist/heart/target-resolution.js +127 -0
  84. package/dist/heart/tempo.js +93 -0
  85. package/dist/heart/temporal-view.js +41 -0
  86. package/dist/heart/tool-activity-callbacks.js +36 -0
  87. package/dist/heart/tool-description.js +135 -0
  88. package/dist/heart/tool-loop.js +200 -0
  89. package/dist/heart/turn-coordinator.js +28 -0
  90. package/dist/heart/wake-packet.js +226 -0
  91. package/dist/mind/associative-recall.js +122 -32
  92. package/dist/mind/bundle-manifest.js +14 -0
  93. package/dist/mind/context.js +125 -14
  94. package/dist/mind/{memory.js → diary.js} +70 -56
  95. package/dist/mind/episodes.js +116 -0
  96. package/dist/mind/file-state.js +179 -0
  97. package/dist/mind/first-impressions.js +16 -2
  98. package/dist/mind/friends/channel.js +56 -0
  99. package/dist/mind/friends/group-context.js +144 -0
  100. package/dist/mind/friends/store-file.js +36 -0
  101. package/dist/mind/friends/trust-explanation.js +74 -0
  102. package/dist/mind/friends/types.js +8 -0
  103. package/dist/mind/journal-index.js +161 -0
  104. package/dist/mind/obligation-steering.js +221 -0
  105. package/dist/mind/obligations.js +134 -0
  106. package/dist/mind/pending.js +76 -9
  107. package/dist/mind/phrases.js +1 -0
  108. package/dist/mind/prompt.js +779 -102
  109. package/dist/mind/scrutiny.js +173 -0
  110. package/dist/mind/token-estimate.js +8 -12
  111. package/dist/nerves/coverage/audit.js +1 -1
  112. package/dist/nerves/coverage/run-artifacts.js +1 -1
  113. package/dist/nerves/event-buffer.js +111 -0
  114. package/dist/nerves/index.js +63 -3
  115. package/dist/nerves/observation.js +20 -0
  116. package/dist/nerves/runtime.js +5 -1
  117. package/dist/outlook-ui/assets/index-DN6vVYPm.css +1 -0
  118. package/dist/outlook-ui/assets/index-M1l1tDTX.js +61 -0
  119. package/dist/outlook-ui/index.html +15 -0
  120. package/dist/repertoire/ado-semantic.js +11 -5
  121. package/dist/repertoire/coding/codex-jsonl.js +64 -0
  122. package/dist/repertoire/coding/context-pack.js +330 -0
  123. package/dist/repertoire/coding/feedback.js +197 -30
  124. package/dist/repertoire/coding/manager.js +149 -2
  125. package/dist/repertoire/coding/spawner.js +55 -9
  126. package/dist/repertoire/coding/tools.js +170 -7
  127. package/dist/repertoire/guardrails.js +291 -0
  128. package/dist/repertoire/mcp-client.js +254 -0
  129. package/dist/repertoire/mcp-manager.js +251 -0
  130. package/dist/repertoire/shell-sessions.js +133 -0
  131. package/dist/repertoire/skills.js +14 -23
  132. package/dist/repertoire/tasks/board.js +43 -5
  133. package/dist/repertoire/tasks/fix.js +182 -0
  134. package/dist/repertoire/tasks/index.js +28 -10
  135. package/dist/repertoire/tasks/lifecycle.js +2 -2
  136. package/dist/repertoire/tasks/parser.js +3 -2
  137. package/dist/repertoire/tasks/scanner.js +194 -37
  138. package/dist/repertoire/tasks/transitions.js +11 -28
  139. package/dist/repertoire/tools-base.js +1345 -320
  140. package/dist/repertoire/tools-bluebubbles.js +94 -0
  141. package/dist/repertoire/tools-github.js +1 -6
  142. package/dist/repertoire/tools-teams.js +12 -59
  143. package/dist/repertoire/tools.js +318 -124
  144. package/dist/scripts/claude-code-hook.js +41 -0
  145. package/dist/scripts/claude-code-stop-hook.js +47 -0
  146. package/dist/senses/attention-queue.js +97 -0
  147. package/dist/senses/bluebubbles-client.js +102 -3
  148. package/dist/senses/bluebubbles-entry.js +2 -0
  149. package/dist/senses/bluebubbles-inbound-log.js +109 -0
  150. package/dist/senses/bluebubbles-media.js +118 -23
  151. package/dist/senses/bluebubbles-model.js +12 -4
  152. package/dist/senses/bluebubbles-mutation-log.js +45 -5
  153. package/dist/senses/bluebubbles-runtime-state.js +109 -0
  154. package/dist/senses/bluebubbles-session-cleanup.js +72 -0
  155. package/dist/senses/bluebubbles.js +1008 -102
  156. package/dist/senses/cli/adapter.js +274 -0
  157. package/dist/senses/cli/ink-app.js +156 -0
  158. package/dist/senses/cli/inline-diff.js +64 -0
  159. package/dist/senses/cli/message-list.js +51 -0
  160. package/dist/senses/cli/ouro-tui.js +349 -0
  161. package/dist/senses/cli/spinner-imperative.js +135 -0
  162. package/dist/senses/cli/spinner.js +101 -0
  163. package/dist/senses/cli/status-line.js +60 -0
  164. package/dist/senses/cli/streaming-markdown.js +526 -0
  165. package/dist/senses/cli/tool-display.js +83 -0
  166. package/dist/senses/cli/tool-render.js +85 -0
  167. package/dist/senses/cli/tui-store.js +198 -0
  168. package/dist/senses/cli/virtual-list.js +35 -0
  169. package/dist/senses/cli-entry.js +1 -1
  170. package/dist/senses/cli-layout.js +187 -0
  171. package/dist/senses/cli.js +508 -243
  172. package/dist/senses/commands.js +65 -1
  173. package/dist/senses/continuity.js +94 -0
  174. package/dist/senses/habit-turn-message.js +108 -0
  175. package/dist/senses/inner-dialog-worker.js +112 -19
  176. package/dist/senses/inner-dialog.js +592 -84
  177. package/dist/senses/pipeline.js +633 -0
  178. package/dist/senses/shared-turn.js +196 -0
  179. package/dist/senses/surface-tool.js +95 -0
  180. package/dist/senses/teams.js +667 -120
  181. package/dist/senses/trust-gate.js +112 -2
  182. package/package.json +16 -6
  183. package/skills/configure-dev-tools.md +81 -0
  184. package/subagents/README.md +4 -70
  185. package/dist/heart/daemon/subagent-installer.js +0 -134
  186. package/dist/senses/debug-activity.js +0 -107
  187. package/subagents/work-doer.md +0 -235
  188. package/subagents/work-merger.md +0 -618
  189. package/subagents/work-planner.md +0 -382
  190. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/basilisk.md +0 -0
  191. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jafar.md +0 -0
  192. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jormungandr.md +0 -0
  193. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/kaa.md +0 -0
  194. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/medusa.md +0 -0
  195. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/monty.md +0 -0
  196. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/nagini.md +0 -0
  197. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/ouroboros.md +0 -0
  198. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/python.md +0 -0
  199. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/quetzalcoatl.md +0 -0
  200. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/sir-hiss.md +0 -0
  201. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/the-serpent.md +0 -0
  202. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/the-snake.md +0 -0
@@ -39,13 +39,19 @@ const fs = __importStar(require("fs"));
39
39
  const os = __importStar(require("os"));
40
40
  const path = __importStar(require("path"));
41
41
  const identity_1 = require("../identity");
42
+ const config_1 = require("../config");
42
43
  const runtime_1 = require("../../nerves/runtime");
44
+ const auth_flow_1 = require("./auth-flow");
45
+ const habit_parser_1 = require("./habit-parser");
43
46
  const hatch_specialist_1 = require("./hatch-specialist");
44
47
  function requiredCredentialKeys(provider) {
45
48
  if (provider === "anthropic")
46
49
  return ["setupToken"];
47
50
  if (provider === "openai-codex")
48
51
  return ["oauthAccessToken"];
52
+ /* v8 ignore next -- branch tested via requiredCredentialKeys unit test @preserve */
53
+ if (provider === "github-copilot")
54
+ return ["githubToken"];
49
55
  if (provider === "minimax")
50
56
  return ["apiKey"];
51
57
  return ["apiKey", "endpoint", "deployment"];
@@ -66,70 +72,8 @@ function validateCredentials(provider, credentials) {
66
72
  throw new Error(`Missing required credentials for ${provider}: ${missing.join(", ")}`);
67
73
  }
68
74
  }
69
- function buildSecretsTemplate() {
70
- return {
71
- providers: {
72
- azure: {
73
- modelName: "gpt-4o-mini",
74
- apiKey: "",
75
- endpoint: "",
76
- deployment: "",
77
- apiVersion: "2025-04-01-preview",
78
- },
79
- minimax: {
80
- model: "minimax-text-01",
81
- apiKey: "",
82
- },
83
- anthropic: {
84
- model: "claude-opus-4-6",
85
- setupToken: "",
86
- },
87
- "openai-codex": {
88
- model: "gpt-5.4",
89
- oauthAccessToken: "",
90
- },
91
- },
92
- teams: {
93
- clientId: "",
94
- clientSecret: "",
95
- tenantId: "",
96
- },
97
- oauth: {
98
- graphConnectionName: "graph",
99
- adoConnectionName: "ado",
100
- githubConnectionName: "",
101
- },
102
- teamsChannel: {
103
- skipConfirmation: true,
104
- port: 3978,
105
- },
106
- integrations: {
107
- perplexityApiKey: "",
108
- openaiEmbeddingsApiKey: "",
109
- },
110
- };
111
- }
112
75
  function writeSecretsFile(agentName, provider, credentials, secretsRoot) {
113
- const secrets = buildSecretsTemplate();
114
- if (provider === "anthropic") {
115
- secrets.providers.anthropic.setupToken = credentials.setupToken.trim();
116
- }
117
- else if (provider === "openai-codex") {
118
- secrets.providers["openai-codex"].oauthAccessToken = credentials.oauthAccessToken.trim();
119
- }
120
- else if (provider === "minimax") {
121
- secrets.providers.minimax.apiKey = credentials.apiKey.trim();
122
- }
123
- else {
124
- secrets.providers.azure.apiKey = credentials.apiKey.trim();
125
- secrets.providers.azure.endpoint = credentials.endpoint.trim();
126
- secrets.providers.azure.deployment = credentials.deployment.trim();
127
- }
128
- const secretsDir = path.join(secretsRoot, agentName);
129
- fs.mkdirSync(secretsDir, { recursive: true });
130
- const secretsPath = path.join(secretsDir, "secrets.json");
131
- fs.writeFileSync(secretsPath, `${JSON.stringify(secrets, null, 2)}\n`, "utf-8");
132
- return secretsPath;
76
+ return (0, auth_flow_1.writeProviderCredentials)(agentName, provider, credentials, { secretsRoot }).secretsPath;
133
77
  }
134
78
  function writeReadme(dir, purpose) {
135
79
  fs.mkdirSync(dir, { recursive: true });
@@ -138,51 +82,24 @@ function writeReadme(dir, purpose) {
138
82
  fs.writeFileSync(readmePath, `# ${path.basename(dir)}\n\n${purpose}\n`, "utf-8");
139
83
  }
140
84
  }
141
- function slugify(value) {
142
- const trimmed = value.trim().toLowerCase();
143
- const slug = trimmed
144
- .replace(/[^a-z0-9]+/g, "-")
145
- .replace(/^-+/, "")
146
- .replace(/-+$/, "");
147
- return slug || "friend";
148
- }
149
- function pad(value) {
150
- return String(value).padStart(2, "0");
151
- }
152
- function formatTaskStem(now) {
153
- return `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())}-${pad(now.getHours())}${pad(now.getMinutes())}`;
154
- }
155
- function writeHeartbeatTask(bundleRoot, now) {
156
- const habitsDir = path.join(bundleRoot, "tasks", "habits");
85
+ function writeHeartbeatHabit(bundleRoot, now) {
86
+ const habitsDir = path.join(bundleRoot, "habits");
157
87
  fs.mkdirSync(habitsDir, { recursive: true });
158
- const stem = formatTaskStem(now);
159
- const filePath = path.join(habitsDir, `${stem}-heartbeat.md`);
160
- const iso = now.toISOString();
161
- const content = [
162
- "---",
163
- "type: habit",
164
- "category: runtime",
165
- "title: Heartbeat check-in",
166
- "status: processing",
167
- `created: ${iso}`,
168
- `updated: ${iso}`,
169
- "requester: system",
170
- "validator: null",
171
- "cadence: \"30m\"",
172
- "scheduledAt: null",
173
- "lastRun: null",
174
- "---",
175
- "",
176
- "Run a lightweight heartbeat cycle. Review task board and inbox.",
177
- "",
178
- ].join("\n");
88
+ const filePath = path.join(habitsDir, "heartbeat.md");
89
+ const content = (0, habit_parser_1.renderHabitFile)({
90
+ title: "Heartbeat check-in",
91
+ cadence: "30m",
92
+ status: "active",
93
+ lastRun: "null",
94
+ created: now.toISOString(),
95
+ }, "Run a lightweight heartbeat cycle. Review task board and inbox.\nCheck on pending obligations. Journal anything important.");
179
96
  fs.writeFileSync(filePath, content, "utf-8");
180
97
  }
181
98
  function writeFriendImprint(bundleRoot, humanName, now) {
182
99
  const friendsDir = path.join(bundleRoot, "friends");
183
100
  fs.mkdirSync(friendsDir, { recursive: true });
184
101
  const nowIso = now.toISOString();
185
- const id = `friend-${slugify(humanName)}`;
102
+ const id = `friend-${(0, config_1.slugify)(humanName) || "friend"}`;
186
103
  const localExternalId = `${os.userInfo().username}@${os.hostname()}`;
187
104
  const record = {
188
105
  id,
@@ -207,16 +124,18 @@ function writeFriendImprint(bundleRoot, humanName, now) {
207
124
  };
208
125
  fs.writeFileSync(path.join(friendsDir, `${id}.json`), `${JSON.stringify(record, null, 2)}\n`, "utf-8");
209
126
  }
210
- function writeMemoryScaffold(bundleRoot) {
211
- const memoryRoot = path.join(bundleRoot, "psyche", "memory");
212
- fs.mkdirSync(path.join(memoryRoot, "daily"), { recursive: true });
213
- fs.mkdirSync(path.join(memoryRoot, "archive"), { recursive: true });
214
- fs.writeFileSync(path.join(memoryRoot, "facts.jsonl"), "", "utf-8");
215
- fs.writeFileSync(path.join(memoryRoot, "entities.json"), "{}\n", "utf-8");
127
+ function writeDiaryScaffold(bundleRoot) {
128
+ const diaryRoot = path.join(bundleRoot, "diary");
129
+ fs.mkdirSync(path.join(diaryRoot, "daily"), { recursive: true });
130
+ fs.mkdirSync(path.join(diaryRoot, "archive"), { recursive: true });
131
+ fs.writeFileSync(path.join(diaryRoot, "facts.jsonl"), "", "utf-8");
132
+ fs.writeFileSync(path.join(diaryRoot, "entities.json"), "{}\n", "utf-8");
216
133
  }
217
134
  function writeHatchlingAgentConfig(bundleRoot, input) {
218
135
  const template = (0, identity_1.buildDefaultAgentTemplate)(input.agentName);
219
136
  template.provider = input.provider;
137
+ template.humanFacing = { provider: input.provider, model: template.humanFacing.model };
138
+ template.agentFacing = { provider: input.provider, model: template.agentFacing.model };
220
139
  template.enabled = true;
221
140
  fs.writeFileSync(path.join(bundleRoot, "agent.json"), `${JSON.stringify(template, null, 2)}\n`, "utf-8");
222
141
  }
@@ -242,25 +161,25 @@ async function runHatchFlow(input, deps = {}) {
242
161
  identitiesDir: targetIdentities,
243
162
  random,
244
163
  });
245
- const specialistSecretsPath = writeSecretsFile("AdoptionSpecialist", input.provider, input.credentials, secretsRoot);
164
+ const specialistSecretsPath = writeSecretsFile("SerpentGuide", input.provider, input.credentials, secretsRoot);
246
165
  const hatchlingSecretsPath = writeSecretsFile(input.agentName, input.provider, input.credentials, secretsRoot);
247
166
  const bundleRoot = path.join(bundlesRoot, `${input.agentName}.ouro`);
248
167
  fs.mkdirSync(bundleRoot, { recursive: true });
249
168
  writeReadme(bundleRoot, "Root of this agent bundle.");
250
169
  writeReadme(path.join(bundleRoot, "psyche"), "Identity and behavior files.");
251
- writeReadme(path.join(bundleRoot, "psyche", "memory"), "Persistent memory store.");
170
+ writeReadme(path.join(bundleRoot, "diary"), "Persistent diary — things I've learned and remember.");
252
171
  writeReadme(path.join(bundleRoot, "friends"), "Known friend records.");
253
172
  writeReadme(path.join(bundleRoot, "tasks"), "Task files.");
254
- writeReadme(path.join(bundleRoot, "tasks", "habits"), "Recurring tasks.");
255
173
  writeReadme(path.join(bundleRoot, "tasks", "one-shots"), "One-shot tasks.");
256
174
  writeReadme(path.join(bundleRoot, "tasks", "ongoing"), "Ongoing tasks.");
175
+ writeReadme(path.join(bundleRoot, "habits"), "Recurring habits and autonomous rhythms.");
257
176
  writeReadme(path.join(bundleRoot, "skills"), "Local skill files.");
258
177
  writeReadme(path.join(bundleRoot, "senses"), "Sense-specific config.");
259
178
  writeReadme(path.join(bundleRoot, "senses", "teams"), "Teams sense config.");
260
179
  writeHatchlingAgentConfig(bundleRoot, input);
261
- writeMemoryScaffold(bundleRoot);
180
+ writeDiaryScaffold(bundleRoot);
262
181
  writeFriendImprint(bundleRoot, input.humanName, now);
263
- writeHeartbeatTask(bundleRoot, now);
182
+ writeHeartbeatHabit(bundleRoot, now);
264
183
  (0, runtime_1.emitNervesEvent)({
265
184
  component: "daemon",
266
185
  event: "daemon.hatch_flow_end",
@@ -43,14 +43,14 @@ const path = __importStar(require("path"));
43
43
  const runtime_1 = require("../../nerves/runtime");
44
44
  function getSpecialistIdentitySourceDir() {
45
45
  // Prefer ~/AgentBundles/ if it exists (user may have customized identities)
46
- const userSource = path.join(os.homedir(), "AgentBundles", "AdoptionSpecialist.ouro", "psyche", "identities");
46
+ const userSource = path.join(os.homedir(), "AgentBundles", "SerpentGuide.ouro", "psyche", "identities");
47
47
  if (fs.existsSync(userSource))
48
48
  return userSource;
49
49
  // Fall back to the bundled copy shipped with the npm package
50
- return path.join(__dirname, "..", "..", "..", "AdoptionSpecialist.ouro", "psyche", "identities");
50
+ return path.join(__dirname, "..", "..", "..", "SerpentGuide.ouro", "psyche", "identities");
51
51
  }
52
52
  function getRepoSpecialistIdentitiesDir() {
53
- return path.join(process.cwd(), "AdoptionSpecialist.ouro", "psyche", "identities");
53
+ return path.join(process.cwd(), "SerpentGuide.ouro", "psyche", "identities");
54
54
  }
55
55
  function listMarkdownIdentityFiles(dir) {
56
56
  let entries;
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.agentConfigV2Hook = agentConfigV2Hook;
4
+ const runtime_1 = require("../../../nerves/runtime");
5
+ const migrate_config_1 = require("../../migrate-config");
6
+ async function agentConfigV2Hook(ctx) {
7
+ (0, runtime_1.emitNervesEvent)({
8
+ component: "daemon",
9
+ event: "daemon.agent_config_v2_hook_start",
10
+ message: "running agent-config-v2 update hook",
11
+ meta: { agentRoot: ctx.agentRoot, currentVersion: ctx.currentVersion },
12
+ });
13
+ try {
14
+ (0, migrate_config_1.migrateAgentConfigV1ToV2)(ctx.agentRoot);
15
+ }
16
+ catch (err) {
17
+ const errorMessage = err instanceof Error ? err.message : /* v8 ignore next -- defensive: non-Error catch branch @preserve */ String(err);
18
+ (0, runtime_1.emitNervesEvent)({
19
+ component: "daemon",
20
+ event: "daemon.agent_config_v2_hook_error",
21
+ message: "agent-config-v2 hook migration failed",
22
+ meta: { agentRoot: ctx.agentRoot, error: errorMessage },
23
+ });
24
+ return { ok: false, error: errorMessage };
25
+ }
26
+ (0, runtime_1.emitNervesEvent)({
27
+ component: "daemon",
28
+ event: "daemon.agent_config_v2_hook_end",
29
+ message: "agent-config-v2 hook completed",
30
+ meta: { agentRoot: ctx.agentRoot },
31
+ });
32
+ return { ok: true };
33
+ }
@@ -0,0 +1,92 @@
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.bundleMetaHook = bundleMetaHook;
37
+ const fs = __importStar(require("fs"));
38
+ const path = __importStar(require("path"));
39
+ const runtime_1 = require("../../../nerves/runtime");
40
+ async function bundleMetaHook(ctx) {
41
+ (0, runtime_1.emitNervesEvent)({
42
+ component: "daemon",
43
+ event: "daemon.bundle_meta_hook_start",
44
+ message: "running bundle-meta update hook",
45
+ meta: { agentRoot: ctx.agentRoot, currentVersion: ctx.currentVersion },
46
+ });
47
+ const metaPath = path.join(ctx.agentRoot, "bundle-meta.json");
48
+ let existing;
49
+ try {
50
+ if (fs.existsSync(metaPath)) {
51
+ const raw = fs.readFileSync(metaPath, "utf-8");
52
+ existing = JSON.parse(raw);
53
+ }
54
+ }
55
+ catch {
56
+ // Malformed JSON -- treat as missing, will overwrite with fresh
57
+ existing = undefined;
58
+ }
59
+ const updated = {
60
+ runtimeVersion: ctx.currentVersion,
61
+ bundleSchemaVersion: existing?.bundleSchemaVersion ?? 1,
62
+ lastUpdated: new Date().toISOString(),
63
+ };
64
+ // Save old runtimeVersion as previousRuntimeVersion (if there was one)
65
+ if (existing?.runtimeVersion) {
66
+ updated.previousRuntimeVersion = existing.runtimeVersion;
67
+ }
68
+ try {
69
+ fs.writeFileSync(metaPath, JSON.stringify(updated, null, 2) + "\n", "utf-8");
70
+ }
71
+ catch (err) {
72
+ const errorMessage = err instanceof Error ? err.message : /* v8 ignore next -- defensive: non-Error catch branch @preserve */ String(err);
73
+ (0, runtime_1.emitNervesEvent)({
74
+ component: "daemon",
75
+ event: "daemon.bundle_meta_hook_error",
76
+ message: "bundle-meta hook write failed",
77
+ meta: { agentRoot: ctx.agentRoot, error: errorMessage },
78
+ });
79
+ return { ok: false, error: errorMessage };
80
+ }
81
+ (0, runtime_1.emitNervesEvent)({
82
+ component: "daemon",
83
+ event: "daemon.bundle_meta_hook_end",
84
+ message: "bundle-meta updated",
85
+ meta: {
86
+ agentRoot: ctx.agentRoot,
87
+ runtimeVersion: updated.runtimeVersion,
88
+ previousRuntimeVersion: updated.previousRuntimeVersion,
89
+ },
90
+ });
91
+ return { ok: true };
92
+ }
@@ -0,0 +1,89 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildInnerStatusOutput = buildInnerStatusOutput;
4
+ const runtime_1 = require("../../nerves/runtime");
5
+ function formatRelativeTime(elapsedMs) {
6
+ const minutes = Math.floor(elapsedMs / (60 * 1000));
7
+ if (minutes < 1)
8
+ return "just now";
9
+ if (minutes === 1)
10
+ return "1 minute ago";
11
+ if (minutes < 60)
12
+ return `${minutes} minutes ago`;
13
+ const hours = Math.floor(minutes / 60);
14
+ if (hours === 1)
15
+ return "1 hour ago";
16
+ return `${hours} hours ago`;
17
+ }
18
+ function formatCadence(cadenceMs) {
19
+ const minutes = Math.round(cadenceMs / (60 * 1000));
20
+ if (minutes >= 60) {
21
+ const hours = Math.round(minutes / 60);
22
+ return `${hours}h`;
23
+ }
24
+ return `${minutes}m`;
25
+ }
26
+ function buildInnerStatusOutput(input) {
27
+ const { agentName, runtimeState, journalFiles, heartbeat, attentionCount, now } = input;
28
+ const lines = [];
29
+ lines.push(`inner dialog status: ${agentName}`);
30
+ // Last turn
31
+ if (runtimeState?.lastCompletedAt) {
32
+ const lastMs = new Date(runtimeState.lastCompletedAt).getTime();
33
+ const elapsed = now - lastMs;
34
+ const relativeTime = formatRelativeTime(elapsed);
35
+ const reasonSuffix = runtimeState.reason ? ` (${runtimeState.reason})` : "";
36
+ lines.push(` last turn: ${relativeTime}${reasonSuffix}`);
37
+ }
38
+ else {
39
+ lines.push(" last turn: unknown");
40
+ }
41
+ // Status
42
+ if (runtimeState) {
43
+ const reasonSuffix = runtimeState.status === "running" && runtimeState.reason ? ` (${runtimeState.reason})` : "";
44
+ lines.push(` status: ${runtimeState.status}${reasonSuffix}`);
45
+ }
46
+ else {
47
+ lines.push(" status: unknown");
48
+ }
49
+ // Heartbeat health
50
+ if (heartbeat && heartbeat.lastCompletedAt !== null) {
51
+ const elapsed = now - heartbeat.lastCompletedAt;
52
+ const threshold = heartbeat.cadenceMs * 1.5;
53
+ const health = elapsed < threshold ? "healthy" : "overdue";
54
+ const cadenceStr = formatCadence(heartbeat.cadenceMs);
55
+ const sinceStr = formatRelativeTime(elapsed);
56
+ lines.push(` heartbeat: ${health} (cadence ${cadenceStr}, ${sinceStr})`);
57
+ }
58
+ else {
59
+ lines.push(" heartbeat: unknown");
60
+ }
61
+ // Journal
62
+ if (journalFiles.length === 0) {
63
+ lines.push(" journal: (empty)");
64
+ }
65
+ else {
66
+ lines.push(" journal:");
67
+ const sorted = [...journalFiles].sort((a, b) => b.mtimeMs - a.mtimeMs);
68
+ for (const file of sorted) {
69
+ const elapsed = now - file.mtimeMs;
70
+ const relativeTime = formatRelativeTime(elapsed);
71
+ lines.push(` - ${file.name} (${relativeTime})`);
72
+ }
73
+ }
74
+ // Attention
75
+ const thoughtWord = attentionCount === 1 ? "thought" : "thoughts";
76
+ lines.push(` attention: ${attentionCount} held ${thoughtWord}`);
77
+ (0, runtime_1.emitNervesEvent)({
78
+ component: "daemon",
79
+ event: "daemon.inner_status_read",
80
+ message: "inner dialog status read",
81
+ meta: {
82
+ agentName,
83
+ status: runtimeState?.status ?? "unknown",
84
+ journalCount: journalFiles.length,
85
+ attentionCount,
86
+ },
87
+ });
88
+ return lines.join("\n");
89
+ }
@@ -0,0 +1,165 @@
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.DAEMON_PLIST_LABEL = void 0;
37
+ exports.generateDaemonPlist = generateDaemonPlist;
38
+ exports.writeLaunchAgentPlist = writeLaunchAgentPlist;
39
+ exports.installLaunchAgent = installLaunchAgent;
40
+ exports.uninstallLaunchAgent = uninstallLaunchAgent;
41
+ exports.isDaemonInstalled = isDaemonInstalled;
42
+ const path = __importStar(require("path"));
43
+ const runtime_1 = require("../../nerves/runtime");
44
+ exports.DAEMON_PLIST_LABEL = "bot.ouro.daemon";
45
+ function plistFilePath(homeDir) {
46
+ return path.join(homeDir, "Library", "LaunchAgents", `${exports.DAEMON_PLIST_LABEL}.plist`);
47
+ }
48
+ function userLaunchDomain(userUid) {
49
+ return `gui/${userUid}`;
50
+ }
51
+ function generateDaemonPlist(options) {
52
+ (0, runtime_1.emitNervesEvent)({
53
+ component: "daemon",
54
+ event: "daemon.launchd_generate_plist",
55
+ message: "generating daemon plist",
56
+ meta: { entryPath: options.entryPath, socketPath: options.socketPath },
57
+ });
58
+ const lines = [
59
+ `<?xml version="1.0" encoding="UTF-8"?>`,
60
+ `<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">`,
61
+ `<plist version="1.0">`,
62
+ `<dict>`,
63
+ ` <key>Label</key>`,
64
+ ` <string>${exports.DAEMON_PLIST_LABEL}</string>`,
65
+ ` <key>ProgramArguments</key>`,
66
+ ` <array>`,
67
+ ` <string>${options.nodePath}</string>`,
68
+ ` <string>${options.entryPath}</string>`,
69
+ ` <string>--socket</string>`,
70
+ ` <string>${options.socketPath}</string>`,
71
+ ` </array>`,
72
+ ` <key>RunAtLoad</key>`,
73
+ ` <true/>`,
74
+ ` <key>KeepAlive</key>`,
75
+ ` <true/>`,
76
+ ];
77
+ if (options.envPath) {
78
+ lines.push(` <key>EnvironmentVariables</key>`, ` <dict>`, ` <key>PATH</key>`, ` <string>${options.envPath}</string>`, ` </dict>`);
79
+ }
80
+ if (options.logDir) {
81
+ lines.push(` <key>StandardOutPath</key>`, ` <string>${path.join(options.logDir, "ouro-daemon-stdout.log")}</string>`, ` <key>StandardErrorPath</key>`, ` <string>${path.join(options.logDir, "ouro-daemon-stderr.log")}</string>`);
82
+ }
83
+ lines.push(`</dict>`, `</plist>`, ``);
84
+ return lines.join("\n");
85
+ }
86
+ function writeLaunchAgentPlist(deps, options) {
87
+ const launchAgentsDir = path.join(deps.homeDir, "Library", "LaunchAgents");
88
+ deps.mkdirp(launchAgentsDir);
89
+ if (options.logDir) {
90
+ deps.mkdirp(options.logDir);
91
+ }
92
+ const fullPath = plistFilePath(deps.homeDir);
93
+ const xml = generateDaemonPlist(options);
94
+ deps.writeFile(fullPath, xml);
95
+ (0, runtime_1.emitNervesEvent)({
96
+ component: "daemon",
97
+ event: "daemon.launchd_plist_written",
98
+ message: "daemon launch agent plist written",
99
+ meta: { plistPath: fullPath, entryPath: options.entryPath, socketPath: options.socketPath },
100
+ });
101
+ return fullPath;
102
+ }
103
+ function installLaunchAgent(deps, options) {
104
+ (0, runtime_1.emitNervesEvent)({
105
+ component: "daemon",
106
+ event: "daemon.launchd_install",
107
+ message: "installing launch agent",
108
+ meta: { entryPath: options.entryPath, socketPath: options.socketPath },
109
+ });
110
+ const fullPath = plistFilePath(deps.homeDir);
111
+ const domain = userLaunchDomain(deps.userUid);
112
+ // Unload existing (best effort) for idempotent re-install
113
+ if (deps.existsFile(fullPath)) {
114
+ try {
115
+ deps.exec(`launchctl bootout ${domain} "${fullPath}"`);
116
+ }
117
+ catch { /* best effort */ }
118
+ }
119
+ writeLaunchAgentPlist(deps, options);
120
+ // Bootstrap the plist so launchd manages crash recovery via KeepAlive.
121
+ // This is safe because ouro up calls this AFTER the daemon is already running,
122
+ // so launchd sees the existing process and just registers for KeepAlive.
123
+ try {
124
+ deps.exec(`launchctl bootstrap ${domain} "${fullPath}"`);
125
+ }
126
+ catch { /* already loaded */ }
127
+ (0, runtime_1.emitNervesEvent)({
128
+ component: "daemon",
129
+ event: "daemon.launchd_installed",
130
+ message: "launch agent installed with KeepAlive",
131
+ meta: { plistPath: fullPath },
132
+ });
133
+ }
134
+ function uninstallLaunchAgent(deps) {
135
+ (0, runtime_1.emitNervesEvent)({
136
+ component: "daemon",
137
+ event: "daemon.launchd_uninstall",
138
+ message: "uninstalling launch agent",
139
+ meta: {},
140
+ });
141
+ const fullPath = plistFilePath(deps.homeDir);
142
+ const domain = userLaunchDomain(deps.userUid);
143
+ if (deps.existsFile(fullPath)) {
144
+ try {
145
+ deps.exec(`launchctl bootout ${domain} "${fullPath}"`);
146
+ }
147
+ catch { /* best effort */ }
148
+ deps.removeFile(fullPath);
149
+ }
150
+ (0, runtime_1.emitNervesEvent)({
151
+ component: "daemon",
152
+ event: "daemon.launchd_uninstalled",
153
+ message: "launch agent uninstalled",
154
+ meta: { plistPath: fullPath },
155
+ });
156
+ }
157
+ function isDaemonInstalled(deps) {
158
+ (0, runtime_1.emitNervesEvent)({
159
+ component: "daemon",
160
+ event: "daemon.launchd_check_installed",
161
+ message: "checking if daemon is installed",
162
+ meta: {},
163
+ });
164
+ return deps.existsFile(plistFilePath(deps.homeDir));
165
+ }
@@ -37,10 +37,10 @@ exports.discoverLogFiles = discoverLogFiles;
37
37
  exports.readLastLines = readLastLines;
38
38
  exports.formatLogLine = formatLogLine;
39
39
  exports.tailLogs = tailLogs;
40
- const os = __importStar(require("os"));
41
40
  const path = __importStar(require("path"));
42
41
  const nerves_1 = require("../../nerves");
43
42
  const runtime_1 = require("../../nerves/runtime");
43
+ const identity_1 = require("../identity");
44
44
  const LEVEL_COLORS = {
45
45
  debug: "\x1b[2m",
46
46
  info: "\x1b[36m",
@@ -49,11 +49,12 @@ const LEVEL_COLORS = {
49
49
  };
50
50
  function discoverLogFiles(options) {
51
51
  /* v8 ignore start -- integration: default DI stubs for real OS @preserve */
52
- const homeDir = options.homeDir ?? os.homedir();
53
52
  const existsSync = options.existsSync ?? (() => false);
54
53
  const readdirSync = options.readdirSync ?? (() => []);
55
54
  /* v8 ignore stop */
56
- const logDir = path.join(homeDir, ".agentstate", "daemon", "logs");
55
+ const logDir = options.homeDir
56
+ ? path.join(options.homeDir, "AgentBundles", `${options.agentName ?? "slugger"}.ouro`, "state", "daemon", "logs")
57
+ : (0, identity_1.getAgentDaemonLogsDir)(options.agentName);
57
58
  const files = [];
58
59
  if (existsSync(logDir)) {
59
60
  for (const name of readdirSync(logDir)) {