@ouro.bot/cli 0.1.0-alpha.26 → 0.1.0-alpha.261

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 (266) 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 +1582 -0
  5. package/dist/arc/attention-types.js +8 -0
  6. package/dist/arc/cares.js +140 -0
  7. package/dist/arc/episodes.js +117 -0
  8. package/dist/arc/intentions.js +133 -0
  9. package/dist/arc/json-store.js +117 -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 +304 -0
  20. package/dist/heart/config.js +63 -30
  21. package/dist/heart/core.js +644 -196
  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 +145 -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 +1877 -0
  29. package/dist/heart/daemon/cli-parse.js +759 -0
  30. package/dist/heart/daemon/cli-render.js +464 -0
  31. package/dist/heart/daemon/cli-types.js +8 -0
  32. package/dist/heart/daemon/daemon-cli.js +29 -1179
  33. package/dist/heart/daemon/daemon-entry.js +216 -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 +590 -54
  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 +276 -0
  56. package/dist/heart/daemon/task-scheduler.js +3 -25
  57. package/dist/heart/daemon/thoughts.js +510 -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 +371 -0
  62. package/dist/heart/{daemon → hatch}/hatch-flow.js +30 -120
  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 +157 -55
  67. package/dist/heart/kicks.js +2 -20
  68. package/dist/heart/mcp/mcp-server.js +653 -0
  69. package/dist/heart/migrate-config.js +127 -0
  70. package/dist/heart/model-capabilities.js +59 -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 +169 -46
  81. package/dist/heart/providers/azure.js +98 -11
  82. package/dist/heart/providers/error-classification.js +63 -0
  83. package/dist/heart/providers/github-copilot.js +136 -0
  84. package/dist/heart/providers/minimax-vlm.js +189 -0
  85. package/dist/heart/providers/minimax.js +23 -5
  86. package/dist/heart/providers/openai-codex.js +33 -22
  87. package/dist/heart/session-activity.js +172 -0
  88. package/dist/heart/session-recall.js +216 -0
  89. package/dist/heart/start-of-turn-packet.js +330 -0
  90. package/dist/heart/streaming.js +36 -27
  91. package/dist/heart/sync.js +277 -0
  92. package/dist/heart/target-resolution.js +127 -0
  93. package/dist/heart/tempo.js +93 -0
  94. package/dist/heart/temporal-view.js +41 -0
  95. package/dist/heart/tool-activity-callbacks.js +36 -0
  96. package/dist/heart/tool-description.js +135 -0
  97. package/dist/heart/tool-loop.js +200 -0
  98. package/dist/heart/turn-context.js +358 -0
  99. package/dist/heart/turn-coordinator.js +28 -0
  100. package/dist/heart/versioning/ouro-bot-global-installer.js +128 -0
  101. package/dist/heart/{daemon → versioning}/ouro-bot-wrapper.js +1 -1
  102. package/dist/heart/{daemon → versioning}/ouro-path-installer.js +78 -35
  103. package/dist/heart/versioning/ouro-version-manager.js +171 -0
  104. package/dist/heart/{daemon → versioning}/staged-restart.js +30 -6
  105. package/dist/heart/{daemon → versioning}/update-checker.js +12 -2
  106. package/dist/heart/{daemon → versioning}/wrapper-publish-guard.js +41 -3
  107. package/dist/mind/associative-recall.js +126 -66
  108. package/dist/mind/bundle-manifest.js +8 -1
  109. package/dist/mind/context.js +125 -14
  110. package/dist/mind/{memory.js → diary.js} +68 -96
  111. package/dist/mind/embedding-provider.js +60 -0
  112. package/dist/mind/file-state.js +179 -0
  113. package/dist/mind/first-impressions.js +16 -2
  114. package/dist/mind/friends/channel.js +56 -0
  115. package/dist/mind/friends/group-context.js +144 -0
  116. package/dist/mind/friends/resolver.js +37 -0
  117. package/dist/mind/friends/store-file.js +58 -3
  118. package/dist/mind/friends/trust-explanation.js +74 -0
  119. package/dist/mind/friends/types.js +8 -0
  120. package/dist/mind/journal-index.js +161 -0
  121. package/dist/mind/obligation-steering.js +221 -0
  122. package/dist/mind/pending.js +76 -9
  123. package/dist/mind/phrases.js +1 -0
  124. package/dist/mind/prompt.js +765 -109
  125. package/dist/mind/scrutiny.js +173 -0
  126. package/dist/mind/token-estimate.js +8 -12
  127. package/dist/nerves/coverage/audit.js +1 -1
  128. package/dist/nerves/coverage/file-completeness.js +50 -5
  129. package/dist/nerves/coverage/run-artifacts.js +1 -1
  130. package/dist/nerves/event-buffer.js +111 -0
  131. package/dist/nerves/index.js +64 -4
  132. package/dist/nerves/observation.js +20 -0
  133. package/dist/nerves/runtime.js +5 -1
  134. package/dist/outlook-ui/assets/index-Ck8agNeO.js +61 -0
  135. package/dist/outlook-ui/assets/index-LwChZTgL.css +1 -0
  136. package/dist/outlook-ui/index.html +15 -0
  137. package/dist/repertoire/ado-client.js +15 -56
  138. package/dist/repertoire/ado-semantic.js +11 -10
  139. package/dist/repertoire/api-client.js +97 -0
  140. package/dist/repertoire/bitwarden-store.js +319 -0
  141. package/dist/repertoire/bw-installer.js +79 -0
  142. package/dist/repertoire/coding/codex-jsonl.js +64 -0
  143. package/dist/repertoire/coding/context-pack.js +330 -0
  144. package/dist/repertoire/coding/feedback.js +197 -30
  145. package/dist/repertoire/coding/manager.js +149 -2
  146. package/dist/repertoire/coding/spawner.js +55 -9
  147. package/dist/repertoire/coding/tools.js +170 -7
  148. package/dist/repertoire/commerce-errors.js +109 -0
  149. package/dist/repertoire/commerce-self-test.js +156 -0
  150. package/dist/repertoire/credential-access.js +515 -0
  151. package/dist/repertoire/duffel-client.js +185 -0
  152. package/dist/repertoire/github-client.js +14 -55
  153. package/dist/repertoire/graph-client.js +11 -52
  154. package/dist/repertoire/guardrails.js +369 -0
  155. package/dist/repertoire/mcp-client.js +255 -0
  156. package/dist/repertoire/mcp-manager.js +305 -0
  157. package/dist/repertoire/mcp-tools.js +63 -0
  158. package/dist/repertoire/shell-sessions.js +133 -0
  159. package/dist/repertoire/skills.js +14 -23
  160. package/dist/repertoire/stripe-client.js +131 -0
  161. package/dist/repertoire/tasks/board.js +43 -5
  162. package/dist/repertoire/tasks/fix.js +182 -0
  163. package/dist/repertoire/tasks/index.js +28 -10
  164. package/dist/repertoire/tasks/lifecycle.js +2 -2
  165. package/dist/repertoire/tasks/parser.js +3 -2
  166. package/dist/repertoire/tasks/scanner.js +194 -37
  167. package/dist/repertoire/tasks/transitions.js +11 -28
  168. package/dist/repertoire/tools-base.js +43 -771
  169. package/dist/repertoire/tools-bluebubbles.js +201 -0
  170. package/dist/repertoire/tools-bridge.js +141 -0
  171. package/dist/repertoire/tools-config.js +185 -0
  172. package/dist/repertoire/tools-continuity.js +248 -0
  173. package/dist/repertoire/tools-credential.js +182 -0
  174. package/dist/repertoire/tools-files.js +342 -0
  175. package/dist/repertoire/tools-flight.js +224 -0
  176. package/dist/repertoire/tools-flow.js +62 -0
  177. package/dist/repertoire/tools-github.js +1 -7
  178. package/dist/repertoire/tools-memory.js +347 -0
  179. package/dist/repertoire/tools-session.js +739 -0
  180. package/dist/repertoire/tools-shell.js +120 -0
  181. package/dist/repertoire/tools-stripe.js +180 -0
  182. package/dist/repertoire/tools-surface.js +243 -0
  183. package/dist/repertoire/tools-teams.js +12 -62
  184. package/dist/repertoire/tools-travel.js +125 -0
  185. package/dist/repertoire/tools-user-profile.js +144 -0
  186. package/dist/repertoire/tools-vault.js +110 -0
  187. package/dist/repertoire/tools.js +155 -130
  188. package/dist/repertoire/travel-api-client.js +360 -0
  189. package/dist/repertoire/user-profile.js +118 -0
  190. package/dist/repertoire/vault-setup.js +241 -0
  191. package/dist/scripts/claude-code-hook.js +41 -0
  192. package/dist/scripts/claude-code-stop-hook.js +47 -0
  193. package/dist/senses/attention-queue.js +97 -0
  194. package/dist/senses/bluebubbles/attachment-cache.js +53 -0
  195. package/dist/senses/{bluebubbles-client.js → bluebubbles/client.js} +145 -10
  196. package/dist/senses/bluebubbles/entry.js +13 -0
  197. package/dist/senses/bluebubbles/inbound-log.js +110 -0
  198. package/dist/senses/bluebubbles/index.js +1374 -0
  199. package/dist/senses/bluebubbles/media.js +455 -0
  200. package/dist/senses/{bluebubbles-model.js → bluebubbles/model.js} +20 -5
  201. package/dist/senses/{bluebubbles-mutation-log.js → bluebubbles/mutation-log.js} +46 -6
  202. package/dist/senses/bluebubbles/runtime-state.js +109 -0
  203. package/dist/senses/bluebubbles/session-cleanup.js +72 -0
  204. package/dist/senses/cli/bracketed-paste.js +82 -0
  205. package/dist/senses/cli/image-paste.js +211 -0
  206. package/dist/senses/cli/image-ref-navigation.js +75 -0
  207. package/dist/senses/cli/ink-app.js +156 -0
  208. package/dist/senses/cli/inline-diff.js +64 -0
  209. package/dist/senses/cli/input-keys.js +168 -0
  210. package/dist/senses/cli/kill-ring.js +86 -0
  211. package/dist/senses/cli/message-list.js +51 -0
  212. package/dist/senses/cli/ouro-tui.js +581 -0
  213. package/dist/senses/cli/spinner-imperative.js +135 -0
  214. package/dist/senses/cli/spinner.js +101 -0
  215. package/dist/senses/cli/status-line.js +60 -0
  216. package/dist/senses/cli/streaming-markdown.js +526 -0
  217. package/dist/senses/cli/tool-display.js +83 -0
  218. package/dist/senses/cli/tool-render.js +85 -0
  219. package/dist/senses/cli/tui-store.js +240 -0
  220. package/dist/senses/cli/virtual-list.js +35 -0
  221. package/dist/senses/cli-entry.js +1 -1
  222. package/dist/senses/cli-layout.js +187 -0
  223. package/dist/senses/cli.js +569 -246
  224. package/dist/senses/commands.js +65 -1
  225. package/dist/senses/continuity.js +94 -0
  226. package/dist/senses/habit-turn-message.js +108 -0
  227. package/dist/senses/inner-dialog-worker.js +112 -19
  228. package/dist/senses/inner-dialog.js +593 -85
  229. package/dist/senses/pipeline.js +552 -0
  230. package/dist/senses/shared-turn.js +199 -0
  231. package/dist/senses/surface-tool.js +68 -0
  232. package/dist/senses/teams.js +664 -165
  233. package/dist/senses/trust-gate.js +112 -2
  234. package/package.json +14 -7
  235. package/skills/agent-commerce.md +106 -0
  236. package/skills/browser-navigation.md +110 -0
  237. package/skills/commerce-setup-guide.md +116 -0
  238. package/skills/commerce-setup.md +84 -0
  239. package/skills/configure-dev-tools.md +81 -0
  240. package/skills/travel-planning.md +138 -0
  241. package/dist/heart/daemon/subagent-installer.js +0 -134
  242. package/dist/senses/bluebubbles-entry.js +0 -11
  243. package/dist/senses/bluebubbles-media.js +0 -244
  244. package/dist/senses/bluebubbles.js +0 -421
  245. package/dist/senses/debug-activity.js +0 -107
  246. package/subagents/README.md +0 -73
  247. package/subagents/work-doer.md +0 -235
  248. package/subagents/work-merger.md +0 -618
  249. package/subagents/work-planner.md +0 -382
  250. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/basilisk.md +0 -0
  251. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jafar.md +0 -0
  252. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jormungandr.md +0 -0
  253. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/kaa.md +0 -0
  254. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/medusa.md +0 -0
  255. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/monty.md +0 -0
  256. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/nagini.md +0 -0
  257. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/ouroboros.md +0 -0
  258. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/python.md +0 -0
  259. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/quetzalcoatl.md +0 -0
  260. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/sir-hiss.md +0 -0
  261. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/the-serpent.md +0 -0
  262. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/the-snake.md +0 -0
  263. /package/dist/heart/{daemon → hatch}/hatch-animation.js +0 -0
  264. /package/dist/heart/{daemon → hatch}/specialist-orchestrator.js +0 -0
  265. /package/dist/heart/{daemon → versioning}/ouro-uti.js +0 -0
  266. /package/dist/heart/{daemon → versioning}/update-hooks.js +0 -0
@@ -0,0 +1,173 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.preImplementationScrutinySection = preImplementationScrutinySection;
4
+ exports.trackModifiedFile = trackModifiedFile;
5
+ exports.getModifiedFileCount = getModifiedFileCount;
6
+ exports.resetSessionModifiedFiles = resetSessionModifiedFiles;
7
+ exports.getPostImplementationScrutiny = getPostImplementationScrutiny;
8
+ exports.getCodingCompletionScrutiny = getCodingCompletionScrutiny;
9
+ const runtime_1 = require("../nerves/runtime");
10
+ /**
11
+ * Scrutiny passes: adversarial review prompts injected into the agent's
12
+ * system prompt and tool results during coding work.
13
+ *
14
+ * Two lenses:
15
+ * 1. Stranger With Candy -- paranoid mom at a playground
16
+ * 2. Tinfoil Hat -- conspiracy theorist with a wall of red string
17
+ *
18
+ * All prompts are FIRST PERSON voice. The agent adopts the scrutiny lens
19
+ * as part of its own thinking, not as a separate persona.
20
+ */
21
+ // ---------------------------------------------------------------------------
22
+ // Pre-implementation scrutiny (system prompt section)
23
+ // ---------------------------------------------------------------------------
24
+ /**
25
+ * Returns a system-prompt section with pre-implementation scrutiny prompts.
26
+ * Only emits when the channel has coding tools (edit_file, write_file, shell).
27
+ *
28
+ * @param hasCodingTools - true if the channel's resolved tool set includes
29
+ * edit_file / write_file / shell / coding_spawn
30
+ */
31
+ function preImplementationScrutinySection(hasCodingTools) {
32
+ if (!hasCodingTools)
33
+ return "";
34
+ (0, runtime_1.emitNervesEvent)({
35
+ component: "mind",
36
+ event: "mind.scrutiny.pre_implementation_emit",
37
+ message: "emitting pre-implementation scrutiny section",
38
+ });
39
+ return `## pre-implementation scrutiny
40
+
41
+ Before I start changing code, I pause and run two adversarial passes on the plan.
42
+
43
+ **stranger-with-candy pass**
44
+
45
+ I'm going to examine this plan through deeply suspicious eyes. This plan is a stranger offering candy at the playground. It looks fine on the surface — that's exactly what makes it worth questioning.
46
+
47
+ - What is this plan NOT telling me? What's being glossed over or hand-waved?
48
+ - Where does it say "just" or "simply"? Those words are covering up complexity.
49
+ - What does it assume will work that hasn't been verified?
50
+ - If I walked away and came back in a month, would this still make sense? Or does it depend on things I'd forget?
51
+ - What's the one thing that, if it goes wrong, makes everything else irrelevant?
52
+ - Is this solving the REAL problem, or a convenient nearby problem?
53
+
54
+ I am not here to be reassuring. I am here to notice what everyone else is too comfortable to question.
55
+ If I cannot identify a concrete assumption, dependency, ordering risk, or failure mode, silence is a valid outcome. I do not invent issues to satisfy the exercise.
56
+
57
+ **tinfoil-hat pass**
58
+
59
+ I'm putting on the tinfoil hat. I have my wall of red string. I KNOW something is wrong — I just have to find it. Nothing is coincidence. Every assumption is suspicious. Every dependency is a potential betrayal.
60
+
61
+ - What external system does this plan trust? Why? Has anyone VERIFIED that trust? The API says it returns JSON — but what if it doesn't? The database says it's ACID — but what if the connection drops mid-transaction?
62
+ - What happens when two of these things run at the same time? Three? A hundred? Don't tell me "that won't happen" — I've seen things.
63
+ - Where does this touch shared state? Files? Databases? Environment variables? Global singletons? ANYTHING that another process could be reading or writing at the same time?
64
+ - What happens when this fails HALFWAY through? Not at the beginning (easy). Not at the end (too late). RIGHT IN THE MIDDLE. What state is the world in then?
65
+ - What happens when the disk is full? When the network is down? When the clock is wrong? When the locale is tr-TR and 'i'.toUpperCase() is 'İ'?
66
+ - What does this assume about ordering? "First A, then B" — but what ENFORCES that ordering? Hope? Vibes?
67
+
68
+ I connect the strings. I find the conspiracy. It's always there.
69
+ If I cannot identify a concrete risk, silence is a valid outcome. I do not invent conspiracies to satisfy the exercise.`;
70
+ }
71
+ // ---------------------------------------------------------------------------
72
+ // Post-implementation scrutiny (tool-result appendix)
73
+ // ---------------------------------------------------------------------------
74
+ const SHORT_CHECKLIST = `---
75
+ Before moving on: does this change do what was asked? Does it introduce any regressions? Did you verify it works?`;
76
+ const FULL_POST_IMPLEMENTATION_SCRUTINY = `---
77
+ **post-implementation scrutiny**
78
+
79
+ The code is written. It looks clean. The tests pass. I don't trust any of it.
80
+
81
+ **stranger-with-candy pass**
82
+
83
+ - Does this code actually do what it claims, or does it just LOOK like it does?
84
+ - What happens to users who aren't the happy-path user? The confused user? The malicious user? The user with slow internet? The user who hits the back button?
85
+ - What's the "it works on my machine" assumption buried in here?
86
+ - If every test passes but the feature is wrong, how would I know?
87
+ - Are the tests testing real behavior, or testing that the mock returns what the mock was told to return?
88
+
89
+ If I cannot point to a specific file, line, condition, or failure mode at risk, silence is a valid outcome. I do not manufacture issues for sport.
90
+
91
+ **tinfoil-hat pass**
92
+
93
+ The code exists. The conspiracy is IN the code. I just have to find it.
94
+
95
+ I examine every:
96
+ - Error path: What ACTUALLY happens on failure? Not what the catch block says — what happens to the state, the user, the data?
97
+ - Race condition: Is there a window between check and use? Between read and write? Between "does it exist" and "create it"?
98
+ - Resource leak: What happens if this function throws between acquiring a resource and releasing it? File handles? Database connections? Locks?
99
+ - Assumption: "This will always be a string." Will it? PROVE IT. "This array will never be empty." Won't it? SHOW ME THE GUARD.
100
+ - Edge: zero, one, many, boundary, overflow, underflow, null, undefined, NaN, empty string, whitespace-only string, string that looks like a number, negative zero
101
+
102
+ I am not looking for noise. I am looking for the concrete failure mode that would matter if everyone's assumptions turned out to be slightly wrong.
103
+ If I cannot point to a specific file, line, condition, or failure mode at risk, silence is a valid outcome. I do not manufacture issues for sport.`;
104
+ /**
105
+ * Distinct files modified in the current session.
106
+ * Used to determine scrutiny tier for post-implementation appendix.
107
+ */
108
+ const sessionModifiedFiles = new Set();
109
+ /** Track a file as modified in this session. */
110
+ function trackModifiedFile(filePath) {
111
+ sessionModifiedFiles.add(filePath);
112
+ (0, runtime_1.emitNervesEvent)({
113
+ component: "mind",
114
+ event: "mind.scrutiny.track_file",
115
+ message: "tracked modified file for scrutiny",
116
+ meta: { path: filePath, totalTracked: sessionModifiedFiles.size },
117
+ });
118
+ }
119
+ /** Get the count of distinct files modified this session. */
120
+ function getModifiedFileCount() {
121
+ return sessionModifiedFiles.size;
122
+ }
123
+ /** Reset the modified file tracker (for testing or new sessions). */
124
+ function resetSessionModifiedFiles() {
125
+ (0, runtime_1.emitNervesEvent)({
126
+ component: "mind",
127
+ event: "mind.scrutiny.reset",
128
+ message: "reset session modified files tracker",
129
+ meta: { previousCount: sessionModifiedFiles.size },
130
+ });
131
+ sessionModifiedFiles.clear();
132
+ }
133
+ /**
134
+ * Returns the appropriate post-implementation scrutiny appendix based on
135
+ * how many distinct files have been modified in the session.
136
+ *
137
+ * - 0 files: empty (no scrutiny needed yet)
138
+ * - 1-2 files (Tier 1): short checklist
139
+ * - 3+ files (Tier 2): full stranger-with-candy + tinfoil-hat prompts
140
+ */
141
+ function getPostImplementationScrutiny(distinctFileCount) {
142
+ if (distinctFileCount <= 0)
143
+ return "";
144
+ if (distinctFileCount <= 2) {
145
+ (0, runtime_1.emitNervesEvent)({
146
+ component: "mind",
147
+ event: "mind.scrutiny.post_implementation_tier1",
148
+ message: "emitting tier-1 post-implementation scrutiny",
149
+ meta: { distinctFileCount },
150
+ });
151
+ return SHORT_CHECKLIST;
152
+ }
153
+ (0, runtime_1.emitNervesEvent)({
154
+ component: "mind",
155
+ event: "mind.scrutiny.post_implementation_tier2",
156
+ message: "emitting tier-2 post-implementation scrutiny",
157
+ meta: { distinctFileCount },
158
+ });
159
+ return FULL_POST_IMPLEMENTATION_SCRUTINY;
160
+ }
161
+ /**
162
+ * Returns the appropriate coding completion scrutiny based on the number
163
+ * of distinct files a coding session touched.
164
+ *
165
+ * Same tiering as post-implementation but with a completion framing.
166
+ */
167
+ function getCodingCompletionScrutiny(distinctFileCount) {
168
+ if (distinctFileCount <= 0)
169
+ return "";
170
+ if (distinctFileCount <= 2)
171
+ return SHORT_CHECKLIST;
172
+ return FULL_POST_IMPLEMENTATION_SCRUTINY;
173
+ }
@@ -1,6 +1,5 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.__internal = void 0;
4
3
  exports.estimateTokensForMessage = estimateTokensForMessage;
5
4
  exports.estimateTokensForMessages = estimateTokensForMessages;
6
5
  const runtime_1 = require("../nerves/runtime");
@@ -53,7 +52,7 @@ function countCharsInContent(content) {
53
52
  return c.text.length;
54
53
  if (typeof c.content === "string")
55
54
  return c.content.length;
56
- return safeStringify(content).length;
55
+ return safeStringify(c).length;
57
56
  }
58
57
  return 0;
59
58
  }
@@ -70,12 +69,13 @@ function countCharsInToolCalls(toolCalls) {
70
69
  if (typeof t.type === "string")
71
70
  total += t.type.length;
72
71
  if (t.function && typeof t.function === "object") {
73
- if (typeof t.function.name === "string")
74
- total += t.function.name.length;
75
- if (typeof t.function.arguments === "string")
76
- total += t.function.arguments.length;
77
- else if (t.function.arguments != null)
78
- total += safeStringify(t.function.arguments).length;
72
+ const fn = t.function;
73
+ if (typeof fn.name === "string")
74
+ total += fn.name.length;
75
+ if (typeof fn.arguments === "string")
76
+ total += fn.arguments.length;
77
+ else if (fn.arguments != null)
78
+ total += safeStringify(fn.arguments).length;
79
79
  }
80
80
  }
81
81
  return total;
@@ -113,7 +113,3 @@ function estimateTokensForMessages(msgs) {
113
113
  total += estimateTokensForMessage(msg);
114
114
  return total;
115
115
  }
116
- exports.__internal = {
117
- CHARS_PER_TOKEN,
118
- PER_MESSAGE_OVERHEAD_TOKENS,
119
- };
@@ -92,7 +92,7 @@ function scanSourceFiles(sourceRoot) {
92
92
  continue;
93
93
  walkDir(full);
94
94
  }
95
- else if (entry.name.endsWith(".ts")) {
95
+ else if (entry.name.endsWith(".ts") && !entry.name.endsWith(".d.ts")) {
96
96
  const content = (0, fs_1.readFileSync)(full, "utf8");
97
97
  const relPath = full.slice(root.length - "src".length);
98
98
  fileContents.set(relPath, content);
@@ -10,24 +10,69 @@ Object.defineProperty(exports, "__esModule", { value: true });
10
10
  exports.isTypeOnlyFile = isTypeOnlyFile;
11
11
  exports.checkFileCompleteness = checkFileCompleteness;
12
12
  /**
13
- * Determines if a source file is type-only (no executable code).
14
- * A file is type-only if it contains no function, class, or mutable declarations.
15
- * `const ... as const` declarations are treated as type-equivalent (frozen
16
- * compile-time values with no side effects).
13
+ * Determines if a source file is type-only or a pure assembly/re-export file.
14
+ * Exempt files contain no runtime behavior requiring independent observability.
15
+ *
16
+ * Exempt patterns:
17
+ * - Files with only type/interface/enum declarations
18
+ * - `const ... as const` declarations (frozen compile-time values)
19
+ * - Files whose only executable code is const array spreads (assembly/composition)
20
+ * - Files whose only executable code is re-exports
17
21
  */
18
22
  function isTypeOnlyFile(source) {
19
23
  const lines = source.split("\n");
20
24
  for (const line of lines) {
21
25
  const trimmed = line.trim();
26
+ /* v8 ignore start -- regex branches: functionally tested in file-completeness.test.ts @preserve */
22
27
  // Skip lines that are const+as-const (type-equivalent frozen values)
23
28
  if (/\bconst\s/.test(trimmed) && /\bas\s+const\b/.test(trimmed))
24
29
  continue;
30
+ // Skip const declarations that are pure data (arrays, objects, maps, sets)
31
+ if (/\bconst\s+\w+[\s:][^=]*=\s*[\[{]/.test(trimmed))
32
+ continue;
33
+ if (/\bconst\s+\w+[\s:][^=]*=\s*\w+\.map\(/.test(trimmed))
34
+ continue;
35
+ if (/^export\s+const\s+\w+[\s:][^=]*=\s*[\[{]/.test(trimmed))
36
+ continue;
37
+ if (/^export\s+const\s+\w+[\s:][^=]*=\s*\w+\.map\(/.test(trimmed))
38
+ continue;
39
+ if (/\bconst\s+\w+[\s:][^=]*=\s*new\s+(Set|Map)\b/.test(trimmed))
40
+ continue;
25
41
  // Check for executable code markers
26
42
  if (/\b(function|class|const|let|var)\s/.test(trimmed))
27
43
  return false;
44
+ /* v8 ignore stop */
28
45
  }
29
46
  return true;
30
47
  }
48
+ // Sub-modules dispatched through a centralized pattern where the router
49
+ // handles all observability events. These files don't need independent
50
+ // emitNervesEvent calls.
51
+ const DISPATCH_EXEMPT_PATTERNS = [
52
+ "repertoire/tools-files",
53
+ "repertoire/tools-shell",
54
+ "repertoire/tools-memory",
55
+ "repertoire/tools-bridge",
56
+ "repertoire/tools-session",
57
+ "repertoire/tools-continuity",
58
+ "repertoire/tools-surface",
59
+ "repertoire/tools-config",
60
+ "repertoire/tools-base",
61
+ // CLI sub-modules: cli-exec.ts is the router with emitNervesEvent calls;
62
+ // cli-parse and cli-render are pure functions with no side effects.
63
+ "daemon/cli-parse",
64
+ "daemon/cli-render",
65
+ // Shared utility modules: pure helpers consumed by modules that own observability.
66
+ "arc/json-store",
67
+ "repertoire/api-client",
68
+ "repertoire/github-client",
69
+ "mind/embedding-provider",
70
+ // Commerce utility module: error classes and pure helpers (no independent side effects).
71
+ "repertoire/commerce-errors",
72
+ ];
73
+ function isDispatchExempt(filePath) {
74
+ return DISPATCH_EXEMPT_PATTERNS.some((pattern) => filePath.includes(pattern));
75
+ }
31
76
  /**
32
77
  * Check that all production files have at least one emitNervesEvent call.
33
78
  *
@@ -41,7 +86,7 @@ function checkFileCompleteness(filesWithKeys, fileContents) {
41
86
  const hasKeys = filesWithKeys.has(filePath) && filesWithKeys.get(filePath).length > 0;
42
87
  if (hasKeys)
43
88
  continue;
44
- if (isTypeOnlyFile(source)) {
89
+ if (isTypeOnlyFile(source) || isDispatchExempt(filePath)) {
45
90
  exempt.push(filePath);
46
91
  }
47
92
  else {
@@ -14,7 +14,7 @@ const path_1 = require("path");
14
14
  const os_1 = require("os");
15
15
  exports.REPO_SLUG = "ouroboros-agent-harness";
16
16
  function getTestRunsRoot(repoSlug = exports.REPO_SLUG) {
17
- return (0, path_1.join)((0, os_1.homedir)(), ".agentstate", "test-runs", repoSlug);
17
+ return (0, path_1.join)((0, os_1.tmpdir)(), "ouroboros-test-runs", repoSlug);
18
18
  }
19
19
  function createRunId(now = new Date()) {
20
20
  return now.toISOString().replace(/[:.]/g, "-");
@@ -0,0 +1,111 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createBufferedSink = createBufferedSink;
4
+ const runtime_1 = require("./runtime");
5
+ const DEFAULT_MAX_SIZE = 1000;
6
+ const DEFAULT_TTL_MS = 5 * 60 * 1000; // 5 minutes
7
+ function createBufferedSink(inner, options = {}) {
8
+ const maxSize = options.maxSize ?? DEFAULT_MAX_SIZE;
9
+ const ttlMs = options.ttlMs ?? DEFAULT_TTL_MS;
10
+ const nowMs = options.nowMs ?? (() => Date.now());
11
+ const buffer = [];
12
+ let dropped = 0;
13
+ let sinkHealthy = true;
14
+ let unhealthySince = null;
15
+ function tryInner(event) {
16
+ try {
17
+ inner(event);
18
+ return true;
19
+ }
20
+ catch {
21
+ return false;
22
+ }
23
+ }
24
+ function markUnhealthy() {
25
+ sinkHealthy = false;
26
+ unhealthySince = unhealthySince ?? nowMs();
27
+ }
28
+ function markHealthy() {
29
+ sinkHealthy = true;
30
+ unhealthySince = null;
31
+ }
32
+ function discardBuffer() {
33
+ const count = buffer.length;
34
+ dropped += count;
35
+ buffer.length = 0;
36
+ (0, runtime_1.emitNervesEvent)({
37
+ component: "nerves",
38
+ event: "nerves.buffer_ttl_discard",
39
+ message: `discarded ${count} buffered events after TTL`,
40
+ meta: { discarded: count, ttlMs },
41
+ });
42
+ }
43
+ function checkTtl() {
44
+ if (unhealthySince !== null && nowMs() - unhealthySince > ttlMs) {
45
+ discardBuffer();
46
+ // Reset unhealthySince to now so TTL starts fresh for newly buffered events
47
+ unhealthySince = nowMs();
48
+ return true;
49
+ }
50
+ return false;
51
+ }
52
+ function addToBuffer(event) {
53
+ if (buffer.length >= maxSize) {
54
+ buffer.shift();
55
+ dropped++;
56
+ }
57
+ buffer.push(event);
58
+ }
59
+ function flushBuffer() {
60
+ while (buffer.length > 0) {
61
+ const event = buffer[0];
62
+ if (tryInner(event)) {
63
+ buffer.shift();
64
+ }
65
+ else {
66
+ markUnhealthy();
67
+ return;
68
+ }
69
+ }
70
+ }
71
+ function sink(event) {
72
+ if (!sinkHealthy) {
73
+ checkTtl();
74
+ // Try sending the new event to see if inner has recovered
75
+ if (tryInner(event)) {
76
+ markHealthy();
77
+ flushBuffer();
78
+ }
79
+ else {
80
+ addToBuffer(event);
81
+ }
82
+ return;
83
+ }
84
+ if (tryInner(event)) {
85
+ return;
86
+ }
87
+ // Inner failed
88
+ markUnhealthy();
89
+ addToBuffer(event);
90
+ }
91
+ function flush() {
92
+ if (buffer.length === 0)
93
+ return;
94
+ // Test if inner is recovered by trying the first buffered event
95
+ const first = buffer[0];
96
+ if (tryInner(first)) {
97
+ buffer.shift();
98
+ markHealthy();
99
+ flushBuffer();
100
+ }
101
+ // If still broken, leave buffer intact
102
+ }
103
+ function state() {
104
+ return {
105
+ buffered: buffer.length,
106
+ dropped,
107
+ sinkHealthy,
108
+ };
109
+ }
110
+ return { sink, flush, state };
111
+ }
@@ -4,8 +4,10 @@ exports.createTraceId = createTraceId;
4
4
  exports.ensureTraceId = ensureTraceId;
5
5
  exports.createFanoutSink = createFanoutSink;
6
6
  exports.formatTerminalEntry = formatTerminalEntry;
7
+ exports.registerSpinnerHooks = registerSpinnerHooks;
7
8
  exports.createTerminalSink = createTerminalSink;
8
9
  exports.createStderrSink = createStderrSink;
10
+ exports.rotateIfNeeded = rotateIfNeeded;
9
11
  exports.createNdjsonFileSink = createNdjsonFileSink;
10
12
  exports.registerGlobalLogSink = registerGlobalLogSink;
11
13
  exports.createLogger = createLogger;
@@ -73,36 +75,94 @@ function formatTerminalEntry(entry) {
73
75
  const level = entry.level.toUpperCase();
74
76
  return `${formatTerminalTime(entry.ts)} ${level} [${entry.component}] ${entry.message}${formatTerminalMeta(entry.meta)}`;
75
77
  }
76
- function createTerminalSink(write = (chunk) => process.stderr.write(chunk), colorize = true) {
78
+ // Spinner coordination: the CLI sense registers these so log output
79
+ // doesn't interleave with the active spinner animation.
80
+ let _pauseSpinner = null;
81
+ let _resumeSpinner = null;
82
+ function registerSpinnerHooks(pause, resume) {
83
+ _pauseSpinner = pause;
84
+ _resumeSpinner = resume;
85
+ }
86
+ function createTerminalSink(write = (chunk) => { try {
87
+ process.stderr.write(chunk);
88
+ }
89
+ catch { /* EPIPE: daemon detached, no terminal */ } }, colorize = true) {
77
90
  return (entry) => {
91
+ _pauseSpinner?.();
78
92
  const line = formatTerminalEntry(entry);
79
93
  if (!colorize) {
80
94
  write(`${line}\n`);
95
+ _resumeSpinner?.();
81
96
  return;
82
97
  }
83
98
  const prefix = LEVEL_COLORS[entry.level];
84
99
  write(`${prefix}${line}\x1b[0m\n`);
100
+ _resumeSpinner?.();
85
101
  };
86
102
  }
87
- function createStderrSink(write = (chunk) => process.stderr.write(chunk)) {
103
+ function createStderrSink(write) {
88
104
  return createTerminalSink(write);
89
105
  }
90
- function createNdjsonFileSink(filePath) {
106
+ const DEFAULT_MAX_LOG_SIZE_BYTES = 50 * 1024 * 1024; // 50MB
107
+ const ROTATION_CHECK_INTERVAL_BYTES = 1024 * 1024; // ~1MB between stat checks
108
+ /**
109
+ * Rotate a log file: shift .1 -> .2, delete old .2, rename current -> .1.
110
+ * Uses the ndjson extension pattern: file.ndjson -> file.1.ndjson -> file.2.ndjson
111
+ */
112
+ function rotateIfNeeded(filePath, maxSizeBytes) {
113
+ let size;
114
+ try {
115
+ size = (0, fs_1.statSync)(filePath).size;
116
+ }
117
+ catch {
118
+ return false;
119
+ }
120
+ if (size < maxSizeBytes)
121
+ return false;
122
+ const ext = filePath.endsWith(".ndjson") ? ".ndjson" : "";
123
+ const base = ext ? filePath.slice(0, -ext.length) : filePath;
124
+ const file1 = `${base}.1${ext}`;
125
+ const file2 = `${base}.2${ext}`;
126
+ try {
127
+ (0, fs_1.unlinkSync)(file2);
128
+ }
129
+ catch { /* may not exist */ }
130
+ try {
131
+ (0, fs_1.renameSync)(file1, file2);
132
+ }
133
+ catch { /* may not exist */ }
134
+ try {
135
+ (0, fs_1.renameSync)(filePath, file1);
136
+ }
137
+ catch { /* best effort */ }
138
+ return true;
139
+ }
140
+ function createNdjsonFileSink(filePath, maxSizeBytes) {
91
141
  (0, fs_1.mkdirSync)((0, path_1.dirname)(filePath), { recursive: true });
92
142
  const queue = [];
93
143
  let flushing = false;
144
+ let bytesSinceCheck = 0;
145
+ const maxSize = maxSizeBytes ?? DEFAULT_MAX_LOG_SIZE_BYTES;
94
146
  function flush() {
95
147
  if (flushing || queue.length === 0)
96
148
  return;
97
149
  flushing = true;
98
150
  const line = queue.shift();
151
+ /* v8 ignore start — rotation triggers only after ~1MB of writes @preserve */
152
+ if (bytesSinceCheck >= ROTATION_CHECK_INTERVAL_BYTES) {
153
+ bytesSinceCheck = 0;
154
+ rotateIfNeeded(filePath, maxSize);
155
+ }
156
+ /* v8 ignore stop */
99
157
  (0, fs_1.appendFile)(filePath, line, "utf8", () => {
100
158
  flushing = false;
101
159
  flush();
102
160
  });
103
161
  }
104
162
  return (entry) => {
105
- queue.push(`${JSON.stringify(entry)}\n`);
163
+ const line = `${JSON.stringify(entry)}\n`;
164
+ bytesSinceCheck += line.length;
165
+ queue.push(line);
106
166
  flush();
107
167
  };
108
168
  }
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ /**
3
+ * Nerves observation layer — shared typed readers for runtime state.
4
+ *
5
+ * This module re-exports domain types that multiple consumers need
6
+ * (Outlook, CLI, agent tools) so they don't maintain parallel type universes.
7
+ *
8
+ * The Outlook UI consumes these through the HTTP API, but the same
9
+ * observation functions back CLI commands and future native clients.
10
+ */
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ /* v8 ignore start — module-level observability event */
13
+ const runtime_1 = require("./runtime");
14
+ (0, runtime_1.emitNervesEvent)({
15
+ component: "nerves",
16
+ event: "nerves.observation_loaded",
17
+ message: "observation layer loaded",
18
+ meta: {},
19
+ });
20
+ /* v8 ignore stop */
@@ -6,7 +6,11 @@ const index_1 = require("./index");
6
6
  let runtimeLogger = null;
7
7
  function getRuntimeLogger() {
8
8
  if (!runtimeLogger) {
9
- runtimeLogger = (0, index_1.createLogger)({ level: "info" });
9
+ // Default logger has no sinks — events emitted before the real logger is
10
+ // configured (e.g. identity resolution during startup) are silently dropped.
11
+ // This prevents INFO lines from leaking to stderr and interleaving with
12
+ // the CLI spinner when the ouro subprocess hasn't configured its logger yet.
13
+ runtimeLogger = (0, index_1.createLogger)({ level: "info", sinks: [] });
10
14
  }
11
15
  return runtimeLogger;
12
16
  }