@ouro.bot/cli 0.1.0-alpha.23 → 0.1.0-alpha.231

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 (241) 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 +1408 -1
  5. package/dist/arc/attention-types.js +8 -0
  6. package/dist/arc/cares.js +140 -0
  7. package/dist/arc/episodes.js +119 -0
  8. package/dist/arc/intentions.js +133 -0
  9. package/dist/arc/json-store.js +120 -0
  10. package/dist/arc/obligations.js +237 -0
  11. package/dist/arc/presence.js +185 -0
  12. package/dist/heart/active-work.js +832 -0
  13. package/dist/heart/agent-entry.js +37 -2
  14. package/dist/heart/auth/auth-flow.js +447 -0
  15. package/dist/heart/bridges/manager.js +358 -0
  16. package/dist/heart/bridges/state-machine.js +135 -0
  17. package/dist/heart/bridges/store.js +123 -0
  18. package/dist/heart/commitments.js +111 -0
  19. package/dist/heart/config-registry.js +305 -0
  20. package/dist/heart/config.js +63 -30
  21. package/dist/heart/core.js +601 -145
  22. package/dist/heart/cross-chat-delivery.js +131 -0
  23. package/dist/heart/daemon/agent-config-check.js +149 -0
  24. package/dist/heart/daemon/agent-discovery.js +81 -0
  25. package/dist/heart/daemon/agent-service.js +360 -0
  26. package/dist/heart/daemon/cadence.js +70 -0
  27. package/dist/heart/daemon/cli-defaults.js +557 -0
  28. package/dist/heart/daemon/cli-exec.js +1847 -0
  29. package/dist/heart/daemon/cli-parse.js +759 -0
  30. package/dist/heart/daemon/cli-render.js +316 -0
  31. package/dist/heart/daemon/cli-types.js +8 -0
  32. package/dist/heart/daemon/daemon-cli.js +29 -1163
  33. package/dist/heart/daemon/daemon-entry.js +202 -4
  34. package/dist/heart/daemon/daemon-health.js +136 -0
  35. package/dist/heart/daemon/daemon-runtime-sync.js +153 -12
  36. package/dist/heart/daemon/daemon-tombstone.js +123 -0
  37. package/dist/heart/daemon/daemon.js +489 -20
  38. package/dist/heart/daemon/hooks/agent-config-v2.js +33 -0
  39. package/dist/heart/daemon/hooks/bundle-meta.js +115 -1
  40. package/dist/heart/daemon/inner-status.js +89 -0
  41. package/dist/heart/daemon/launchd.js +39 -8
  42. package/dist/heart/daemon/log-tailer.js +4 -3
  43. package/dist/heart/daemon/message-router.js +17 -8
  44. package/dist/heart/daemon/os-cron-deps.js +134 -0
  45. package/dist/heart/daemon/ouro-bot-entry.js +1 -1
  46. package/dist/heart/daemon/process-manager.js +123 -0
  47. package/dist/heart/daemon/run-hooks.js +2 -0
  48. package/dist/heart/daemon/runtime-logging.js +58 -15
  49. package/dist/heart/daemon/runtime-metadata.js +101 -0
  50. package/dist/heart/daemon/runtime-mode.js +67 -0
  51. package/dist/heart/daemon/safe-mode.js +161 -0
  52. package/dist/heart/daemon/sense-manager.js +72 -3
  53. package/dist/heart/daemon/session-id-resolver.js +131 -0
  54. package/dist/heart/daemon/skill-management-installer.js +94 -0
  55. package/dist/heart/daemon/socket-client.js +201 -0
  56. package/dist/heart/daemon/task-scheduler.js +3 -25
  57. package/dist/heart/daemon/thoughts.js +507 -0
  58. package/dist/heart/delegation.js +62 -0
  59. package/dist/heart/habits/habit-migration.js +181 -0
  60. package/dist/heart/habits/habit-parser.js +119 -0
  61. package/dist/heart/habits/habit-scheduler.js +369 -0
  62. package/dist/heart/{daemon → hatch}/hatch-flow.js +29 -119
  63. package/dist/heart/{daemon → hatch}/hatch-specialist.js +3 -3
  64. package/dist/heart/{daemon → hatch}/specialist-prompt.js +10 -7
  65. package/dist/heart/{daemon → hatch}/specialist-tools.js +49 -3
  66. package/dist/heart/identity.js +144 -55
  67. package/dist/heart/kicks.js +2 -20
  68. package/dist/heart/mcp/mcp-server.js +608 -0
  69. package/dist/heart/migrate-config.js +127 -0
  70. package/dist/heart/model-capabilities.js +48 -0
  71. package/dist/heart/outlook/outlook-http.js +439 -0
  72. package/dist/heart/outlook/outlook-read.js +1618 -0
  73. package/dist/heart/outlook/outlook-render.js +1032 -0
  74. package/dist/heart/outlook/outlook-types.js +7 -0
  75. package/dist/heart/outlook/outlook-view.js +194 -0
  76. package/dist/heart/progress-story.js +42 -0
  77. package/dist/heart/provider-failover.js +88 -0
  78. package/dist/heart/provider-ping.js +162 -0
  79. package/dist/heart/providers/anthropic-token.js +163 -0
  80. package/dist/heart/providers/anthropic.js +187 -41
  81. package/dist/heart/providers/azure.js +116 -10
  82. package/dist/heart/providers/github-copilot.js +157 -0
  83. package/dist/heart/providers/minimax.js +33 -3
  84. package/dist/heart/providers/openai-codex.js +51 -16
  85. package/dist/heart/session-activity.js +172 -0
  86. package/dist/heart/session-recall.js +216 -0
  87. package/dist/heart/start-of-turn-packet.js +330 -0
  88. package/dist/heart/streaming.js +36 -27
  89. package/dist/heart/sync.js +217 -0
  90. package/dist/heart/target-resolution.js +127 -0
  91. package/dist/heart/tempo.js +93 -0
  92. package/dist/heart/temporal-view.js +41 -0
  93. package/dist/heart/tool-activity-callbacks.js +36 -0
  94. package/dist/heart/tool-description.js +135 -0
  95. package/dist/heart/tool-loop.js +200 -0
  96. package/dist/heart/turn-context.js +358 -0
  97. package/dist/heart/turn-coordinator.js +28 -0
  98. package/dist/heart/versioning/ouro-bot-global-installer.js +128 -0
  99. package/dist/heart/{daemon → versioning}/ouro-bot-wrapper.js +1 -1
  100. package/dist/heart/{daemon → versioning}/ouro-path-installer.js +78 -35
  101. package/dist/heart/versioning/ouro-version-manager.js +171 -0
  102. package/dist/heart/{daemon → versioning}/staged-restart.js +30 -6
  103. package/dist/heart/{daemon → versioning}/update-checker.js +9 -1
  104. package/dist/heart/{daemon → versioning}/update-hooks.js +20 -2
  105. package/dist/heart/{daemon → versioning}/wrapper-publish-guard.js +41 -3
  106. package/dist/mind/associative-recall.js +126 -66
  107. package/dist/mind/bundle-manifest.js +8 -1
  108. package/dist/mind/context.js +125 -14
  109. package/dist/mind/{memory.js → diary.js} +73 -96
  110. package/dist/mind/embedding-provider.js +60 -0
  111. package/dist/mind/file-state.js +179 -0
  112. package/dist/mind/first-impressions.js +16 -2
  113. package/dist/mind/friends/channel.js +56 -0
  114. package/dist/mind/friends/group-context.js +144 -0
  115. package/dist/mind/friends/store-file.js +58 -3
  116. package/dist/mind/friends/trust-explanation.js +74 -0
  117. package/dist/mind/friends/types.js +8 -0
  118. package/dist/mind/journal-index.js +161 -0
  119. package/dist/mind/obligation-steering.js +221 -0
  120. package/dist/mind/pending.js +76 -9
  121. package/dist/mind/phrases.js +1 -0
  122. package/dist/mind/prompt.js +779 -109
  123. package/dist/mind/scrutiny.js +173 -0
  124. package/dist/mind/token-estimate.js +8 -12
  125. package/dist/nerves/coverage/audit.js +1 -1
  126. package/dist/nerves/coverage/file-completeness.js +47 -5
  127. package/dist/nerves/coverage/run-artifacts.js +1 -1
  128. package/dist/nerves/event-buffer.js +111 -0
  129. package/dist/nerves/index.js +64 -4
  130. package/dist/nerves/observation.js +20 -0
  131. package/dist/nerves/runtime.js +5 -1
  132. package/dist/outlook-ui/assets/index-Ck8agNeO.js +61 -0
  133. package/dist/outlook-ui/assets/index-LwChZTgL.css +1 -0
  134. package/dist/outlook-ui/index.html +15 -0
  135. package/dist/repertoire/ado-client.js +15 -56
  136. package/dist/repertoire/ado-semantic.js +11 -5
  137. package/dist/repertoire/api-client.js +97 -0
  138. package/dist/repertoire/coding/codex-jsonl.js +64 -0
  139. package/dist/repertoire/coding/context-pack.js +330 -0
  140. package/dist/repertoire/coding/feedback.js +197 -30
  141. package/dist/repertoire/coding/manager.js +149 -2
  142. package/dist/repertoire/coding/spawner.js +55 -9
  143. package/dist/repertoire/coding/tools.js +170 -7
  144. package/dist/repertoire/credential-access.js +449 -0
  145. package/dist/repertoire/github-client.js +14 -55
  146. package/dist/repertoire/graph-client.js +11 -52
  147. package/dist/repertoire/guardrails.js +342 -0
  148. package/dist/repertoire/mcp-client.js +254 -0
  149. package/dist/repertoire/mcp-manager.js +251 -0
  150. package/dist/repertoire/shell-sessions.js +133 -0
  151. package/dist/repertoire/skills.js +14 -23
  152. package/dist/repertoire/tasks/board.js +43 -5
  153. package/dist/repertoire/tasks/fix.js +182 -0
  154. package/dist/repertoire/tasks/index.js +28 -10
  155. package/dist/repertoire/tasks/lifecycle.js +2 -2
  156. package/dist/repertoire/tasks/parser.js +3 -2
  157. package/dist/repertoire/tasks/scanner.js +194 -37
  158. package/dist/repertoire/tasks/transitions.js +11 -28
  159. package/dist/repertoire/tools-base.js +35 -771
  160. package/dist/repertoire/tools-bluebubbles.js +94 -0
  161. package/dist/repertoire/tools-bridge.js +141 -0
  162. package/dist/repertoire/tools-config.js +266 -0
  163. package/dist/repertoire/tools-continuity.js +248 -0
  164. package/dist/repertoire/tools-credential.js +184 -0
  165. package/dist/repertoire/tools-files.js +342 -0
  166. package/dist/repertoire/tools-flow.js +62 -0
  167. package/dist/repertoire/tools-github.js +1 -6
  168. package/dist/repertoire/tools-memory.js +347 -0
  169. package/dist/repertoire/tools-session.js +739 -0
  170. package/dist/repertoire/tools-shell.js +120 -0
  171. package/dist/repertoire/tools-surface.js +243 -0
  172. package/dist/repertoire/tools-teams.js +12 -59
  173. package/dist/repertoire/tools-travel.js +125 -0
  174. package/dist/repertoire/tools.js +132 -124
  175. package/dist/repertoire/travel-api-client.js +214 -0
  176. package/dist/scripts/claude-code-hook.js +41 -0
  177. package/dist/scripts/claude-code-stop-hook.js +47 -0
  178. package/dist/senses/attention-queue.js +97 -0
  179. package/dist/senses/{bluebubbles-client.js → bluebubbles/client.js} +109 -10
  180. package/dist/senses/bluebubbles/entry.js +13 -0
  181. package/dist/senses/bluebubbles/inbound-log.js +109 -0
  182. package/dist/senses/bluebubbles/index.js +1374 -0
  183. package/dist/senses/{bluebubbles-media.js → bluebubbles/media.js} +119 -24
  184. package/dist/senses/{bluebubbles-model.js → bluebubbles/model.js} +13 -5
  185. package/dist/senses/{bluebubbles-mutation-log.js → bluebubbles/mutation-log.js} +46 -6
  186. package/dist/senses/bluebubbles/runtime-state.js +109 -0
  187. package/dist/senses/bluebubbles/session-cleanup.js +72 -0
  188. package/dist/senses/cli/adapter.js +274 -0
  189. package/dist/senses/cli/ink-app.js +156 -0
  190. package/dist/senses/cli/inline-diff.js +64 -0
  191. package/dist/senses/cli/message-list.js +51 -0
  192. package/dist/senses/cli/ouro-tui.js +358 -0
  193. package/dist/senses/cli/spinner-imperative.js +135 -0
  194. package/dist/senses/cli/spinner.js +101 -0
  195. package/dist/senses/cli/status-line.js +60 -0
  196. package/dist/senses/cli/streaming-markdown.js +526 -0
  197. package/dist/senses/cli/tool-display.js +83 -0
  198. package/dist/senses/cli/tool-render.js +85 -0
  199. package/dist/senses/cli/tui-store.js +198 -0
  200. package/dist/senses/cli/virtual-list.js +35 -0
  201. package/dist/senses/cli-entry.js +1 -1
  202. package/dist/senses/cli-layout.js +187 -0
  203. package/dist/senses/cli.js +503 -244
  204. package/dist/senses/commands.js +65 -1
  205. package/dist/senses/continuity.js +94 -0
  206. package/dist/senses/habit-turn-message.js +108 -0
  207. package/dist/senses/inner-dialog-worker.js +112 -19
  208. package/dist/senses/inner-dialog.js +594 -85
  209. package/dist/senses/pipeline.js +559 -0
  210. package/dist/senses/shared-turn.js +196 -0
  211. package/dist/senses/surface-tool.js +68 -0
  212. package/dist/senses/teams.js +667 -120
  213. package/dist/senses/trust-gate.js +112 -2
  214. package/package.json +12 -6
  215. package/skills/browser-navigation.md +110 -0
  216. package/skills/configure-dev-tools.md +81 -0
  217. package/skills/travel-planning.md +138 -0
  218. package/dist/heart/daemon/subagent-installer.js +0 -134
  219. package/dist/senses/bluebubbles-entry.js +0 -11
  220. package/dist/senses/bluebubbles.js +0 -421
  221. package/dist/senses/debug-activity.js +0 -107
  222. package/subagents/README.md +0 -73
  223. package/subagents/work-doer.md +0 -235
  224. package/subagents/work-merger.md +0 -618
  225. package/subagents/work-planner.md +0 -382
  226. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/basilisk.md +0 -0
  227. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jafar.md +0 -0
  228. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jormungandr.md +0 -0
  229. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/kaa.md +0 -0
  230. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/medusa.md +0 -0
  231. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/monty.md +0 -0
  232. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/nagini.md +0 -0
  233. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/ouroboros.md +0 -0
  234. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/python.md +0 -0
  235. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/quetzalcoatl.md +0 -0
  236. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/sir-hiss.md +0 -0
  237. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/the-serpent.md +0 -0
  238. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/the-snake.md +0 -0
  239. /package/dist/heart/{daemon → hatch}/hatch-animation.js +0 -0
  240. /package/dist/heart/{daemon → hatch}/specialist-orchestrator.js +0 -0
  241. /package/dist/heart/{daemon → versioning}/ouro-uti.js +0 -0
@@ -39,6 +39,9 @@ const fs = __importStar(require("fs"));
39
39
  const path = __importStar(require("path"));
40
40
  const identity_1 = require("../heart/identity");
41
41
  const runtime_1 = require("../nerves/runtime");
42
+ const types_1 = require("../mind/friends/types");
43
+ const pending_1 = require("../mind/pending");
44
+ // Canned reply; eventually agents should compose their own first-contact message
42
45
  exports.STRANGER_AUTO_REPLY = "I'm sorry, I'm not allowed to talk to strangers";
43
46
  function buildExternalKey(provider, externalId, tenantId) {
44
47
  return `${provider}:${tenantId ?? ""}:${externalId}`;
@@ -77,16 +80,107 @@ function appendPrimaryNotification(bundleRoot, provider, externalId, tenantId, n
77
80
  fs.mkdirSync(inboxDir, { recursive: true });
78
81
  fs.appendFileSync(notificationsPath, `${JSON.stringify(payload)}\n`, "utf8");
79
82
  }
83
+ function writeInnerPendingNotice(bundleRoot, noticeContent, nowIso) {
84
+ const innerPendingDir = path.join(bundleRoot, "state", "pending", pending_1.INNER_DIALOG_PENDING.friendId, pending_1.INNER_DIALOG_PENDING.channel, pending_1.INNER_DIALOG_PENDING.key);
85
+ const fileName = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}.json`;
86
+ const filePath = path.join(innerPendingDir, fileName);
87
+ const payload = {
88
+ from: "instinct",
89
+ content: noticeContent,
90
+ timestamp: Date.now(),
91
+ at: nowIso,
92
+ };
93
+ fs.mkdirSync(innerPendingDir, { recursive: true });
94
+ fs.writeFileSync(filePath, JSON.stringify(payload), "utf-8");
95
+ }
80
96
  function enforceTrustGate(input) {
97
+ const { senseType } = input;
98
+ // Local (CLI) and internal (inner dialog) — always allow
99
+ if (senseType === "local" || senseType === "internal") {
100
+ return { allowed: true };
101
+ }
102
+ // Closed senses (Teams) — org already gates access, allow all trust levels
103
+ if (senseType === "closed") {
104
+ return { allowed: true };
105
+ }
106
+ // Open senses (BlueBubbles/iMessage) — enforce trust rules
81
107
  const trustLevel = input.friend.trustLevel ?? "friend";
82
- if (trustLevel !== "stranger") {
108
+ // Family and friend — always allow on open
109
+ if ((0, types_1.isTrustedLevel)(trustLevel)) {
83
110
  return { allowed: true };
84
111
  }
85
112
  const bundleRoot = input.bundleRoot ?? (0, identity_1.getAgentRoot)();
86
- const repliesPath = path.join(bundleRoot, "stranger-replies.json");
87
113
  const nowIso = (input.now ?? (() => new Date()))().toISOString();
114
+ // Acquaintance rules
115
+ if (trustLevel === "acquaintance") {
116
+ return handleAcquaintance(input, bundleRoot, nowIso);
117
+ }
118
+ // Stranger rules (trustLevel === "stranger")
119
+ return handleStranger(input, bundleRoot, nowIso);
120
+ }
121
+ function handleAcquaintance(input, bundleRoot, nowIso) {
122
+ const { isGroupChat, groupHasFamilyMember, hasExistingGroupWithFamily } = input;
123
+ // Group chat with family member present — allow
124
+ if (isGroupChat && groupHasFamilyMember) {
125
+ return { allowed: true };
126
+ }
127
+ let result;
128
+ let noticeDetail;
129
+ if (isGroupChat) {
130
+ // Group chat without family member — reject silently
131
+ result = { allowed: false, reason: "acquaintance_group_no_family" };
132
+ noticeDetail = `acquaintance "${input.friend.name}" messaged in a group chat without a family member present`;
133
+ }
134
+ else if (hasExistingGroupWithFamily) {
135
+ // 1:1 but shares a group with family — redirect
136
+ result = {
137
+ allowed: false,
138
+ reason: "acquaintance_1on1_has_group",
139
+ autoReply: "Hey! Reach me in our group chat instead.",
140
+ };
141
+ noticeDetail = `acquaintance "${input.friend.name}" DMed me directly — redirected to our group chat`;
142
+ }
143
+ else {
144
+ // 1:1, no shared group with family — redirect to any group
145
+ result = {
146
+ allowed: false,
147
+ reason: "acquaintance_1on1_no_group",
148
+ autoReply: "Hey! Reach me in a group chat instead.",
149
+ };
150
+ noticeDetail = `acquaintance "${input.friend.name}" DMed me directly — asked to reach me in a group chat`;
151
+ }
152
+ (0, runtime_1.emitNervesEvent)({
153
+ level: "warn",
154
+ component: "senses",
155
+ event: "senses.trust_gate",
156
+ message: "acquaintance message blocked",
157
+ meta: {
158
+ channel: input.channel,
159
+ provider: input.provider,
160
+ reason: result.reason,
161
+ },
162
+ });
163
+ try {
164
+ writeInnerPendingNotice(bundleRoot, noticeDetail, nowIso);
165
+ }
166
+ catch (error) {
167
+ (0, runtime_1.emitNervesEvent)({
168
+ level: "error",
169
+ component: "senses",
170
+ event: "senses.trust_gate_error",
171
+ message: "failed to write inner pending notice",
172
+ meta: {
173
+ reason: error instanceof Error ? error.message : String(error),
174
+ },
175
+ });
176
+ }
177
+ return result;
178
+ }
179
+ function handleStranger(input, bundleRoot, nowIso) {
180
+ const repliesPath = path.join(bundleRoot, "stranger-replies.json");
88
181
  const externalKey = buildExternalKey(input.provider, input.externalId, input.tenantId);
89
182
  const state = loadRepliesState(repliesPath);
183
+ // Subsequent contact — silent drop
90
184
  if (state[externalKey]) {
91
185
  (0, runtime_1.emitNervesEvent)({
92
186
  level: "warn",
@@ -103,6 +197,7 @@ function enforceTrustGate(input) {
103
197
  reason: "stranger_silent_drop",
104
198
  };
105
199
  }
200
+ // First contact — auto-reply, persist state, notify agent
106
201
  state[externalKey] = nowIso;
107
202
  try {
108
203
  persistRepliesState(repliesPath, state);
@@ -132,6 +227,21 @@ function enforceTrustGate(input) {
132
227
  },
133
228
  });
134
229
  }
230
+ const noticeDetail = `stranger "${input.friend.name}" tried to reach me via ${input.channel}. Auto-replied once.`;
231
+ try {
232
+ writeInnerPendingNotice(bundleRoot, noticeDetail, nowIso);
233
+ }
234
+ catch (error) {
235
+ (0, runtime_1.emitNervesEvent)({
236
+ level: "error",
237
+ component: "senses",
238
+ event: "senses.trust_gate_error",
239
+ message: "failed to write inner pending notice",
240
+ meta: {
241
+ reason: error instanceof Error ? error.message : String(error),
242
+ },
243
+ });
244
+ }
135
245
  (0, runtime_1.emitNervesEvent)({
136
246
  level: "warn",
137
247
  component: "senses",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ouro.bot/cli",
3
- "version": "0.1.0-alpha.23",
3
+ "version": "0.1.0-alpha.231",
4
4
  "main": "dist/heart/daemon/ouro-entry.js",
5
5
  "bin": {
6
6
  "cli": "dist/heart/daemon/ouro-bot-entry.js",
@@ -9,8 +9,8 @@
9
9
  },
10
10
  "files": [
11
11
  "dist/",
12
- "AdoptionSpecialist.ouro/",
13
- "subagents/",
12
+ "SerpentGuide.ouro/",
13
+ "skills/",
14
14
  "assets/",
15
15
  "changelog.json"
16
16
  ],
@@ -19,23 +19,29 @@
19
19
  "./runOuroCli": "./dist/heart/daemon/daemon-cli.js"
20
20
  },
21
21
  "scripts": {
22
- "dev": "tsc && node dist/senses/cli-entry.js --agent ouroboros",
22
+ "dev": "tsc && node dist/heart/daemon/ouro-bot-entry.js dev",
23
+ "cli": "tsc && node dist/senses/cli-entry.js",
23
24
  "daemon": "tsc && node dist/heart/daemon/daemon-entry.js",
24
25
  "ouro": "tsc && node dist/heart/daemon/ouro-entry.js",
25
26
  "teams": "tsc && node dist/senses/teams-entry.js --agent ouroboros",
26
- "bluebubbles": "tsc && node dist/senses/bluebubbles-entry.js --agent ouroboros",
27
+ "bluebubbles": "tsc && node dist/senses/bluebubbles/entry.js --agent ouroboros",
27
28
  "test": "vitest run",
28
29
  "test:coverage:vitest": "vitest run --coverage",
29
30
  "test:coverage": "node scripts/run-coverage-gate.cjs",
30
- "build": "tsc",
31
+ "build": "tsc && (cd packages/outlook-ui && npm install --ignore-scripts 2>/dev/null && npm run build && cp -r dist ../../dist/outlook-ui) || echo 'outlook-ui build skipped'",
31
32
  "lint": "eslint src/",
32
33
  "audit:nerves": "npm run build && node dist/nerves/coverage/cli-main.js"
33
34
  },
34
35
  "dependencies": {
35
36
  "@anthropic-ai/sdk": "^0.78.0",
37
+ "@azure/identity": "^4.13.0",
36
38
  "@microsoft/teams.apps": "^2.0.5",
37
39
  "@microsoft/teams.dev": "^2.0.5",
40
+ "@types/react": "^17.0.91",
41
+ "fast-glob": "^3.3.3",
42
+ "ink": "^3.2.0",
38
43
  "openai": "^6.27.0",
44
+ "react": "^17.0.2",
39
45
  "semver": "^7.7.4"
40
46
  },
41
47
  "repository": {
@@ -0,0 +1,110 @@
1
+ # Browser Navigation Skill
2
+
3
+ When to use browser tools and how to navigate effectively.
4
+
5
+ ## When to Use Browser Tools
6
+
7
+ Use browser navigation when:
8
+ - The target site has no API (e.g., Airbnb, VRBO, travel blogs)
9
+ - Content is dynamic or requires JavaScript rendering
10
+ - Login-required pages need session-based access
11
+ - You need to verify visual content (screenshots, layouts)
12
+ - Price comparison requires real-time scraping
13
+
14
+ Do NOT use browser tools when:
15
+ - A dedicated API or MCP server exists (prefer `weather_lookup`, Duffel MCP, etc.)
16
+ - The data is available via a public JSON endpoint
17
+ - The task can be completed with `web_search`
18
+
19
+ ## Stealth Browsing Best Practices
20
+
21
+ The `@playwright/mcp` server is configured with realistic user-agent and viewport settings. To avoid detection on travel sites:
22
+
23
+ - **Add delays between navigations**: Wait 2-5 seconds between page loads. Never hammer requests in rapid succession.
24
+ - **Vary timing**: Use random delays rather than fixed intervals (e.g., 2-5s, not exactly 3s every time).
25
+ - **Avoid predictable patterns**: Don't navigate the same path repeatedly in short succession.
26
+ - **Use realistic scroll behavior**: Scroll gradually through pages rather than jumping to specific elements.
27
+ - **Respect robots.txt**: Check for rate limits and crawling restrictions.
28
+
29
+ ## Navigation Workflow
30
+
31
+ Follow this pattern for every page interaction:
32
+
33
+ 1. **Navigate**: Use `browser_navigate` to load the page
34
+ 2. **Wait**: Allow the page to fully load (use `browser_wait` if needed)
35
+ 3. **Snapshot**: Take a `browser_snapshot` to understand the page structure
36
+ 4. **Extract**: Parse the accessibility tree for relevant data
37
+ 5. **Screenshot** (optional): Use `browser_screenshot` for visual confirmation
38
+
39
+ ## Form Filling Patterns
40
+
41
+ ### Login Flows
42
+ 1. Navigate to the login page
43
+ 2. Take a snapshot to identify form fields
44
+ 3. Use `browser_type` for username/password fields
45
+ 4. Use `browser_click` to submit
46
+ 5. Wait for redirect, then verify login succeeded via snapshot
47
+ 6. NEVER hardcode credentials -- use `credential_get` to retrieve login info
48
+
49
+ ### Search Forms (Hotels, Flights, Rentals)
50
+ 1. Navigate to the search page
51
+ 2. Snapshot to identify input fields
52
+ 3. Fill location/date fields with `browser_type`
53
+ 4. Select options with `browser_select_option` or `browser_click`
54
+ 5. Submit the search
55
+ 6. Wait for results to load (travel sites often have loading animations)
56
+ 7. Snapshot the results page to extract listings
57
+
58
+ ### Booking Forms
59
+ 1. **ALWAYS confirm with the user before proceeding to payment**
60
+ 2. Fill traveler information
61
+ 3. Use stored credentials for payment (via `credential_get` -- NEVER type raw card numbers)
62
+ 4. Screenshot the final review page for user confirmation
63
+ 5. Only click "Book" / "Confirm" after explicit user approval
64
+
65
+ ## Anti-Bot Detection Avoidance
66
+
67
+ The stealth configuration handles most fingerprinting automatically. Additionally:
68
+
69
+ - **Don't access detection endpoints**: Avoid URLs containing "captcha", "challenge", "verify"
70
+ - **Handle CAPTCHAs**: If you encounter a CAPTCHA, pause and inform the user. Do not attempt automated solving.
71
+ - **Rotate viewport sizes occasionally**: Use different viewport sizes across sessions
72
+ - **Maintain cookies**: Use persistent `user-data-dir` to appear as a returning user
73
+ - **Avoid headless tells**: The user-agent is set to a real browser string
74
+
75
+ ## Travel Site Patterns
76
+
77
+ ### Airbnb / VRBO
78
+ 1. Search by location + check-in/check-out dates + guests
79
+ 2. Results load dynamically -- scroll to load more listings
80
+ 3. Extract: title, price per night, total price, rating, number of reviews
81
+ 4. For detailed info, click into each listing and snapshot
82
+ 5. Compare top 3-5 options
83
+
84
+ ### Hotel Sites (Booking.com, Hotels.com)
85
+ 1. Search by destination + dates + guests + rooms
86
+ 2. Filter by price range, star rating, amenities
87
+ 3. Extract: name, price, location, rating, key amenities
88
+ 4. Check cancellation policies (important for travel planning)
89
+
90
+ ### Flight Comparison
91
+ 1. Prefer Duffel MCP for flight search (structured API data)
92
+ 2. Use browser only if Duffel doesn't cover the airline
93
+ 3. Google Flights is useful for price comparison but requires careful navigation
94
+
95
+ ## Error Handling
96
+
97
+ - **Page timeouts**: Retry once after 5 seconds. If still failing, inform the user.
98
+ - **CAPTCHAs**: Stop and ask the user to solve manually.
99
+ - **Stale elements**: Re-snapshot the page and retry the interaction.
100
+ - **Blocked/403**: The site may have detected automation. Wait 30 seconds and try with a different approach (e.g., direct URL instead of navigation).
101
+ - **Session expired**: Re-login using stored stored credentials.
102
+
103
+ ## Human Confirmation Gates
104
+
105
+ **ALWAYS** confirm with the user before:
106
+ - Any booking or payment action
107
+ - Entering personal information (name, address, phone)
108
+ - Agreeing to terms of service
109
+ - Subscribing to any service
110
+ - Any action that creates a financial obligation
@@ -0,0 +1,81 @@
1
+ # Configure Dev Tools for MCP Agent Bridge
2
+
3
+ Set up your development tools (Claude Code, Codex) to communicate with Ouroboros agents via MCP. One command does everything.
4
+
5
+ ## Setup
6
+
7
+ ### Claude Code
8
+
9
+ ```bash
10
+ ouro setup --tool claude-code --agent <agent-name>
11
+ ```
12
+
13
+ This command:
14
+ 1. Registers the MCP server with Claude Code via `claude mcp add`
15
+ 2. Configures lifecycle hooks (SessionStart, Stop, PostToolUse) for passive awareness
16
+ 3. Detects dev vs installed mode automatically and uses the correct command path
17
+
18
+ ### Codex
19
+
20
+ ```bash
21
+ ouro setup --tool codex --agent <agent-name>
22
+ ```
23
+
24
+ This command:
25
+ 1. Registers the MCP server with Codex via `codex mcp add`
26
+ 2. Detects dev vs installed mode automatically
27
+
28
+ ## Verification
29
+
30
+ After setup, verify the connection:
31
+
32
+ 1. **Check daemon is running**: `ouro up` (installed) or `ouro dev --repo-path <path>` (dev mode).
33
+ 2. **Test from Claude Code**: Start a new session and use the `status` tool.
34
+ 3. **Test from Codex**: Run `codex exec "Use the <agent-name> status tool"`.
35
+ 4. **Check registration**: `claude mcp list` or `codex mcp list`.
36
+
37
+ ## Available MCP Tools
38
+
39
+ Once connected, these tools are available:
40
+
41
+ ### Conversation tools (new)
42
+ - **send_message** -- Send a message and get a synchronous agent response (full turn with tools)
43
+ - **check_response** -- Check for pending messages from the agent (after ponder or proactive surface)
44
+
45
+ ### Read-only tools
46
+ - **ask** -- Ask the agent a question (uses memory and context)
47
+ - **status** -- Get agent's current status and activity
48
+ - **catchup** -- Get recent activity summary
49
+ - **get_context** -- Get agent's current working context
50
+ - **search_memory** -- Search agent's memory for specific topics
51
+ - **get_task** -- Get details of the agent's current task
52
+ - **check_scope** -- Verify if something is in scope for current work
53
+ - **check_guidance** -- Get guidance on how to approach something
54
+
55
+ ### Write tools
56
+ - **delegate** -- Request the agent to handle a task (runs full conversation turn)
57
+ - **request_decision** -- Ask agent to make a decision about something
58
+ - **report_progress** -- Report progress on delegated work
59
+ - **report_blocker** -- Report a blocker on delegated work
60
+ - **report_complete** -- Report completion of delegated work
61
+
62
+ ## Troubleshooting
63
+
64
+ ### "Daemon not running" error
65
+ Most read-only tools work without the daemon (reads filesystem directly). For write operations and `send_message`, start the daemon with `ouro up` or `ouro dev`.
66
+
67
+ ### MCP server not appearing
68
+ - Run `claude mcp list` or `codex mcp list` to verify registration
69
+ - Re-run `ouro setup` to fix
70
+ - Restart your dev tool (MCP loads at session start)
71
+
72
+ ### Connection timeouts
73
+ - Ensure `dist/` is built: `npm run build`
74
+ - Check that the entry point path is correct (setup auto-detects this)
75
+
76
+ ### Removing
77
+ ```bash
78
+ claude mcp remove ouro-<agent-name>
79
+ # or
80
+ codex mcp remove ouro-<agent-name>
81
+ ```
@@ -0,0 +1,138 @@
1
+ # Travel Planning Skill
2
+
3
+ Compose all travel infrastructure to plan and book trips effectively.
4
+
5
+ ## Research Phase
6
+
7
+ ### Destination Research
8
+ 1. Use `geocode_search` to find candidate destinations and get coordinates
9
+ 2. Use `weather_lookup` to check climate for travel dates
10
+ 3. Use `travel_advisory` to check US State Dept safety levels (1-4):
11
+ - Level 1: Exercise Normal Precautions
12
+ - Level 2: Exercise Increased Caution
13
+ - Level 3: Reconsider Travel
14
+ - Level 4: Do Not Travel
15
+ 4. Use browser tools (see `browser-navigation` skill) for destination reviews, travel blogs, local tips
16
+ 5. Compare multiple destinations on weather, safety, cost, and flight availability
17
+
18
+ ### Information Gathering Workflow
19
+ ```
20
+ geocode_search -> get coordinates
21
+ weather_lookup -> check weather at coordinates
22
+ travel_advisory -> check safety level
23
+ browser tools -> reviews, local info, pricing
24
+ ```
25
+
26
+ ## Flight Search
27
+
28
+ ### Using Duffel MCP (when available)
29
+ - Search via `ouro mcp call duffel search_flights`
30
+ - Provide: origin, destination, dates, passengers, cabin class
31
+ - Compare options on price, duration, stops, airline
32
+
33
+ ### Using Browser (fallback)
34
+ - Use browser-navigation skill patterns for Google Flights, airline sites
35
+ - Add delays between searches to avoid detection
36
+
37
+ ### Comparison Criteria
38
+ Present top 3-5 options comparing:
39
+ - Total price (including bags, seat selection)
40
+ - Duration and number of stops
41
+ - Airline and aircraft
42
+ - Departure/arrival times
43
+ - Cancellation/change policy
44
+
45
+ ## Accommodation Search
46
+
47
+ ### Hotel Search
48
+ - Use Expedia MCP when available (`ouro mcp call expedia search_hotels`)
49
+ - Compare: price per night, total stay cost, location, rating, amenities
50
+ - Check cancellation policies (crucial for travel planning)
51
+
52
+ ### Vacation Rentals (Browser-based)
53
+ - Use browser-navigation skill for Airbnb/VRBO
54
+ - Search by location, dates, guests, budget
55
+ - Compare: price, location proximity, reviews, amenities, host rating
56
+ - Screenshot listings for user review
57
+
58
+ ### Comparison Criteria
59
+ - Price per night and total cost
60
+ - Location (distance to key attractions/activities)
61
+ - Reviews and ratings
62
+ - Amenities (WiFi, kitchen, parking, pool)
63
+ - Cancellation flexibility
64
+
65
+ ## Booking Workflow
66
+
67
+ ### Critical Safety Gates
68
+ - **ALWAYS confirm with user before any booking action**
69
+ - **ALWAYS confirm before entering payment information**
70
+ - **ALWAYS confirm before agreeing to terms**
71
+ - **Never proceed with a financial commitment without explicit approval**
72
+
73
+ ### Credential Handling
74
+
75
+ Credentials are managed through the credential access layer, which stores
76
+ agent-owned secrets encrypted in the bundle vault. Raw passwords never
77
+ enter model context.
78
+
79
+ - Use `credential_get` to check what credentials exist for a domain (metadata only, never passwords)
80
+ - Use `credential_store` to save credentials the agent acquired (e.g., during sign-up for a service)
81
+ - The credential gateway automatically injects secrets into API requests via `getRawSecret()`
82
+ - Use browser-navigation skill form patterns for entering credentials during interactive sessions
83
+
84
+ **How credentials work:**
85
+ - Agent-owned credentials are AES-256-GCM encrypted in `~/AgentBundles/<agent>.ouro/vault/`
86
+ - The master key lives at `~/.agentsecrets/<agent>/vault.key` (auto-generated on first use)
87
+ - The agent can sign up for services and store its own credentials
88
+ - Stored passwords are never returned to the model — only metadata (domain, username, notes)
89
+
90
+ **Upgrade path:** Install `aac` (Bitwarden Agent Access CLI) for vault-backed credentials.
91
+ When `aac` is available, the harness can read credentials from Bitwarden directly,
92
+ enabling access to human-managed vault items without storing them locally.
93
+
94
+ ### Post-Booking
95
+ - Save confirmation details (confirmation number, dates, hotel name, airline, booking reference)
96
+ - Save to diary/journal for future reference
97
+ - Set reminders for check-in windows
98
+ - Note cancellation deadlines
99
+
100
+ ## Preference Management
101
+
102
+ ### Storing Preferences
103
+ Track and reference these travel preferences:
104
+ - Preferred airlines and alliance status
105
+ - Preferred hotel chains and loyalty numbers
106
+ - Seat preferences (window/aisle, extra legroom)
107
+ - Dietary needs for in-flight meals
108
+ - Budget ranges (per night for hotels, per flight)
109
+ - Must-have amenities (WiFi, gym, pool)
110
+
111
+ ### Using Preferences
112
+ - Reference stored preferences when searching
113
+ - Apply airline preferences to flight comparisons
114
+ - Apply hotel brand preferences to accommodation searches
115
+ - Adjust budget ranges based on destination
116
+
117
+ ## Tool Reference
118
+
119
+ ### Native Tools (always available)
120
+ - `weather_lookup` - Current weather by city or coordinates
121
+ - `travel_advisory` - US State Dept advisory by country code
122
+ - `geocode_search` - Location/POI search with coordinates
123
+ - `credential_get` - Check credential metadata for a domain (never returns passwords)
124
+ - `credential_store` - Store credentials the agent acquired (family trust, confirmation required)
125
+ - `credential_list` - List stored credential domains
126
+ - `credential_delete` - Delete stored credentials (family trust, confirmation required)
127
+
128
+ ### MCP Tools (when configured)
129
+ - Browser tools via `@playwright/mcp` - see `browser-navigation` skill
130
+ - Duffel flight search (when MCP server available)
131
+ - Expedia hotel search (when MCP server available)
132
+
133
+ ### Human Confirmation Required For
134
+ - Any booking or payment
135
+ - Entering personal information
136
+ - Agreeing to terms of service
137
+ - Creating financial obligations
138
+ - Sharing credentials with third parties
@@ -1,134 +0,0 @@
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.installSubagentsForAvailableCli = installSubagentsForAvailableCli;
37
- const fs = __importStar(require("fs"));
38
- const os = __importStar(require("os"));
39
- const path = __importStar(require("path"));
40
- const child_process_1 = require("child_process");
41
- const runtime_1 = require("../../nerves/runtime");
42
- function detectCliBinary(binary) {
43
- const result = (0, child_process_1.spawnSync)("which", [binary], { encoding: "utf-8" });
44
- if (result.status !== 0)
45
- return null;
46
- const resolved = result.stdout.trim();
47
- return resolved.length > 0 ? resolved : null;
48
- }
49
- function listSubagentSources(subagentsDir) {
50
- if (!fs.existsSync(subagentsDir))
51
- return [];
52
- return fs.readdirSync(subagentsDir)
53
- .filter((name) => name.endsWith(".md"))
54
- .filter((name) => name !== "README.md")
55
- .map((name) => path.join(subagentsDir, name))
56
- .sort((a, b) => a.localeCompare(b));
57
- }
58
- function pathExists(target) {
59
- try {
60
- fs.lstatSync(target);
61
- return true;
62
- }
63
- catch {
64
- return false;
65
- }
66
- }
67
- function ensureSymlink(source, target) {
68
- fs.mkdirSync(path.dirname(target), { recursive: true });
69
- if (pathExists(target)) {
70
- const stats = fs.lstatSync(target);
71
- if (stats.isSymbolicLink()) {
72
- const linkedPath = fs.readlinkSync(target);
73
- if (linkedPath === source)
74
- return false;
75
- }
76
- fs.unlinkSync(target);
77
- }
78
- fs.symlinkSync(source, target);
79
- return true;
80
- }
81
- async function installSubagentsForAvailableCli(options = {}) {
82
- const repoRoot = options.repoRoot ?? path.resolve(__dirname, "..", "..", "..");
83
- const homeDir = options.homeDir ?? os.homedir();
84
- const which = options.which ?? detectCliBinary;
85
- const subagentsDir = path.join(repoRoot, "subagents");
86
- const sources = listSubagentSources(subagentsDir);
87
- const notes = [];
88
- (0, runtime_1.emitNervesEvent)({
89
- component: "daemon",
90
- event: "daemon.subagent_install_start",
91
- message: "starting subagent auto-install",
92
- meta: { sources: sources.length },
93
- });
94
- if (sources.length === 0) {
95
- notes.push(`no subagent files found at ${subagentsDir}`);
96
- return { claudeInstalled: 0, codexInstalled: 0, notes };
97
- }
98
- let claudeInstalled = 0;
99
- let codexInstalled = 0;
100
- const claudePath = which("claude");
101
- if (!claudePath) {
102
- notes.push("claude CLI not found; skipping subagent install");
103
- }
104
- else {
105
- const claudeAgentsDir = path.join(homeDir, ".claude", "agents");
106
- for (const source of sources) {
107
- const target = path.join(claudeAgentsDir, path.basename(source));
108
- if (ensureSymlink(source, target)) {
109
- claudeInstalled += 1;
110
- }
111
- }
112
- }
113
- const codexPath = which("codex");
114
- if (!codexPath) {
115
- notes.push("codex CLI not found; skipping subagent install");
116
- }
117
- else {
118
- const codexSkillsDir = path.join(homeDir, ".codex", "skills");
119
- for (const source of sources) {
120
- const skillName = path.basename(source, ".md");
121
- const target = path.join(codexSkillsDir, skillName, "SKILL.md");
122
- if (ensureSymlink(source, target)) {
123
- codexInstalled += 1;
124
- }
125
- }
126
- }
127
- (0, runtime_1.emitNervesEvent)({
128
- component: "daemon",
129
- event: "daemon.subagent_install_end",
130
- message: "completed subagent auto-install",
131
- meta: { claudeInstalled, codexInstalled, notes: notes.length },
132
- });
133
- return { claudeInstalled, codexInstalled, notes };
134
- }
@@ -1,11 +0,0 @@
1
- "use strict";
2
- // Thin entrypoint for `npm run bluebubbles` / `node dist/senses/bluebubbles-entry.js --agent <name>`.
3
- // Separated from bluebubbles.ts so the BlueBubbles adapter stays testable.
4
- Object.defineProperty(exports, "__esModule", { value: true });
5
- if (!process.argv.includes("--agent")) {
6
- // eslint-disable-next-line no-console -- pre-boot guard: --agent check before imports
7
- console.error("Missing required --agent <name> argument.\nUsage: node dist/senses/bluebubbles-entry.js --agent ouroboros");
8
- process.exit(1);
9
- }
10
- const bluebubbles_1 = require("./bluebubbles");
11
- (0, bluebubbles_1.startBlueBubblesApp)();