@ouro.bot/cli 0.1.0-alpha.40 → 0.1.0-alpha.400

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 (333) hide show
  1. package/README.md +109 -14
  2. package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/agent.json +3 -2
  3. package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/SOUL.md +2 -2
  4. package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/the-serpent.md +1 -1
  5. package/changelog.json +2455 -6
  6. package/dist/arc/attention-types.js +8 -0
  7. package/dist/arc/cares.js +140 -0
  8. package/dist/arc/episodes.js +117 -0
  9. package/dist/arc/intentions.js +133 -0
  10. package/dist/arc/json-store.js +117 -0
  11. package/dist/arc/obligations.js +237 -0
  12. package/dist/arc/packets.js +193 -0
  13. package/dist/arc/presence.js +185 -0
  14. package/dist/arc/task-lifecycle.js +65 -0
  15. package/dist/heart/active-work.js +832 -0
  16. package/dist/heart/agent-entry.js +58 -3
  17. package/dist/heart/attachments/image-normalize.js +194 -0
  18. package/dist/heart/attachments/materialize.js +97 -0
  19. package/dist/heart/attachments/originals.js +88 -0
  20. package/dist/heart/attachments/render.js +29 -0
  21. package/dist/heart/attachments/sources/adapter.js +2 -0
  22. package/dist/heart/attachments/sources/bluebubbles.js +156 -0
  23. package/dist/heart/attachments/sources/cli-local-file.js +78 -0
  24. package/dist/heart/attachments/sources/index.js +16 -0
  25. package/dist/heart/attachments/store.js +103 -0
  26. package/dist/heart/attachments/types.js +93 -0
  27. package/dist/heart/auth/auth-flow.js +417 -0
  28. package/dist/heart/bridges/manager.js +358 -0
  29. package/dist/heart/bridges/state-machine.js +135 -0
  30. package/dist/heart/bridges/store.js +123 -0
  31. package/dist/heart/bundle-state.js +168 -0
  32. package/dist/heart/commitments.js +111 -0
  33. package/dist/heart/config-registry.js +304 -0
  34. package/dist/heart/config.js +101 -128
  35. package/dist/heart/core.js +801 -217
  36. package/dist/heart/cross-chat-delivery.js +131 -0
  37. package/dist/heart/daemon/agent-config-check.js +397 -0
  38. package/dist/heart/daemon/agent-discovery.js +79 -3
  39. package/dist/heart/daemon/agent-service.js +360 -0
  40. package/dist/heart/daemon/agentic-repair.js +213 -0
  41. package/dist/heart/daemon/bluebubbles-health-diagnostics.js +122 -0
  42. package/dist/heart/daemon/cadence.js +70 -0
  43. package/dist/heart/daemon/cli-defaults.js +599 -0
  44. package/dist/heart/daemon/cli-exec.js +3616 -0
  45. package/dist/heart/daemon/cli-help.js +396 -0
  46. package/dist/heart/daemon/cli-parse.js +1118 -0
  47. package/dist/heart/daemon/cli-render-doctor.js +57 -0
  48. package/dist/heart/daemon/cli-render.js +560 -0
  49. package/dist/heart/daemon/cli-types.js +8 -0
  50. package/dist/heart/daemon/daemon-cli.js +28 -1582
  51. package/dist/heart/daemon/daemon-entry.js +356 -3
  52. package/dist/heart/daemon/daemon-health.js +141 -0
  53. package/dist/heart/daemon/daemon-runtime-sync.js +157 -12
  54. package/dist/heart/daemon/daemon-tombstone.js +236 -0
  55. package/dist/heart/daemon/daemon.js +684 -58
  56. package/dist/heart/daemon/doctor-types.js +8 -0
  57. package/dist/heart/daemon/doctor.js +419 -0
  58. package/dist/heart/daemon/health-monitor.js +79 -1
  59. package/dist/heart/daemon/hooks/agent-config-v2.js +33 -0
  60. package/dist/heart/daemon/hooks/bundle-meta.js +115 -1
  61. package/dist/heart/daemon/http-health-probe.js +80 -0
  62. package/dist/heart/daemon/inner-status.js +89 -0
  63. package/dist/heart/daemon/interactive-repair.js +209 -0
  64. package/dist/heart/daemon/launchd.js +46 -9
  65. package/dist/heart/daemon/log-tailer.js +82 -12
  66. package/dist/heart/daemon/logs-prune.js +105 -0
  67. package/dist/heart/daemon/message-router.js +17 -8
  68. package/dist/heart/daemon/os-cron-deps.js +134 -0
  69. package/dist/heart/daemon/ouro-bot-entry.js +4 -2
  70. package/dist/heart/daemon/ouro-entry.js +3 -1
  71. package/dist/heart/daemon/process-manager.js +201 -0
  72. package/dist/heart/daemon/provider-discovery.js +137 -0
  73. package/dist/heart/daemon/pulse.js +475 -0
  74. package/dist/heart/daemon/readiness-repair.js +216 -0
  75. package/dist/heart/daemon/run-hooks.js +2 -0
  76. package/dist/heart/daemon/runtime-logging.js +67 -16
  77. package/dist/heart/daemon/runtime-metadata.js +73 -0
  78. package/dist/heart/daemon/runtime-mode.js +67 -0
  79. package/dist/heart/daemon/safe-mode.js +161 -0
  80. package/dist/heart/daemon/sense-manager.js +119 -30
  81. package/dist/heart/daemon/session-id-resolver.js +131 -0
  82. package/dist/heart/daemon/skill-management-installer.js +94 -0
  83. package/dist/heart/daemon/socket-client.js +307 -0
  84. package/dist/heart/daemon/stale-bundle-prune.js +96 -0
  85. package/dist/heart/daemon/startup-tui.js +237 -0
  86. package/dist/heart/daemon/task-scheduler.js +3 -25
  87. package/dist/heart/daemon/thoughts.js +510 -0
  88. package/dist/heart/daemon/up-progress.js +135 -0
  89. package/dist/heart/delegation.js +62 -0
  90. package/dist/heart/habits/habit-migration.js +181 -0
  91. package/dist/heart/habits/habit-parser.js +140 -0
  92. package/dist/heart/habits/habit-scheduler.js +371 -0
  93. package/dist/heart/{daemon → hatch}/hatch-flow.js +53 -117
  94. package/dist/heart/{daemon → hatch}/hatch-specialist.js +3 -3
  95. package/dist/heart/{daemon → hatch}/specialist-prompt.js +12 -9
  96. package/dist/heart/{daemon → hatch}/specialist-tools.js +33 -12
  97. package/dist/heart/identity.js +161 -65
  98. package/dist/heart/kept-notes.js +357 -0
  99. package/dist/heart/kicks.js +1 -1
  100. package/dist/heart/machine-identity.js +161 -0
  101. package/dist/heart/mcp/mcp-server.js +653 -0
  102. package/dist/heart/migrate-config.js +100 -0
  103. package/dist/heart/model-capabilities.js +59 -0
  104. package/dist/heart/outlook/outlook-http-hooks.js +64 -0
  105. package/dist/heart/outlook/outlook-http-response.js +7 -0
  106. package/dist/heart/outlook/outlook-http-routes.js +232 -0
  107. package/dist/heart/outlook/outlook-http-static.js +99 -0
  108. package/dist/heart/outlook/outlook-http-transport.js +116 -0
  109. package/dist/heart/outlook/outlook-http.js +99 -0
  110. package/dist/heart/outlook/outlook-read.js +28 -0
  111. package/dist/heart/outlook/outlook-types.js +27 -0
  112. package/dist/heart/outlook/outlook-view.js +195 -0
  113. package/dist/heart/outlook/readers/agent-machine.js +359 -0
  114. package/dist/heart/outlook/readers/continuity-readers.js +332 -0
  115. package/dist/heart/outlook/readers/runtime-readers.js +660 -0
  116. package/dist/heart/outlook/readers/sessions.js +232 -0
  117. package/dist/heart/outlook/readers/shared.js +111 -0
  118. package/dist/heart/platform.js +81 -0
  119. package/dist/heart/progress-story.js +42 -0
  120. package/dist/heart/provider-attempt.js +133 -0
  121. package/dist/heart/provider-binding-resolver.js +239 -0
  122. package/dist/heart/provider-credentials.js +383 -0
  123. package/dist/heart/provider-failover.js +266 -0
  124. package/dist/heart/provider-models.js +81 -0
  125. package/dist/heart/provider-ping.js +237 -0
  126. package/dist/heart/provider-state.js +216 -0
  127. package/dist/heart/provider-visibility.js +186 -0
  128. package/dist/heart/providers/anthropic-token.js +131 -0
  129. package/dist/heart/providers/anthropic.js +193 -55
  130. package/dist/heart/providers/azure.js +103 -12
  131. package/dist/heart/providers/error-classification.js +63 -0
  132. package/dist/heart/providers/github-copilot.js +145 -0
  133. package/dist/heart/providers/minimax-vlm.js +189 -0
  134. package/dist/heart/providers/minimax.js +29 -7
  135. package/dist/heart/providers/openai-codex.js +39 -29
  136. package/dist/heart/runtime-credentials.js +181 -0
  137. package/dist/heart/session-activity.js +190 -0
  138. package/dist/heart/session-events.js +855 -0
  139. package/dist/heart/session-transcript.js +167 -0
  140. package/dist/heart/start-of-turn-packet.js +345 -0
  141. package/dist/heart/streaming.js +36 -27
  142. package/dist/heart/sync.js +332 -0
  143. package/dist/heart/target-resolution.js +127 -0
  144. package/dist/heart/tempo.js +93 -0
  145. package/dist/heart/temporal-view.js +41 -0
  146. package/dist/heart/tool-activity-callbacks.js +36 -0
  147. package/dist/heart/tool-description.js +135 -0
  148. package/dist/heart/tool-friction.js +55 -0
  149. package/dist/heart/tool-loop.js +200 -0
  150. package/dist/heart/turn-context.js +351 -0
  151. package/dist/heart/turn-coordinator.js +28 -0
  152. package/dist/heart/{daemon → versioning}/ouro-bot-global-installer.js +1 -1
  153. package/dist/heart/{daemon → versioning}/ouro-bot-wrapper.js +1 -1
  154. package/dist/heart/versioning/ouro-path-installer.js +301 -0
  155. package/dist/heart/versioning/ouro-version-manager.js +295 -0
  156. package/dist/heart/{daemon → versioning}/staged-restart.js +40 -8
  157. package/dist/heart/{daemon → versioning}/update-checker.js +12 -2
  158. package/dist/heart/{daemon → versioning}/update-hooks.js +63 -59
  159. package/dist/mind/bundle-manifest.js +7 -1
  160. package/dist/mind/context.js +134 -87
  161. package/dist/mind/diary-integrity.js +60 -0
  162. package/dist/mind/{memory.js → diary.js} +84 -96
  163. package/dist/mind/embedding-provider.js +60 -0
  164. package/dist/mind/file-state.js +179 -0
  165. package/dist/mind/first-impressions.js +14 -1
  166. package/dist/mind/friends/channel.js +21 -0
  167. package/dist/mind/friends/group-context.js +144 -0
  168. package/dist/mind/friends/resolver.js +38 -1
  169. package/dist/mind/friends/store-file.js +39 -3
  170. package/dist/mind/friends/trust-explanation.js +74 -0
  171. package/dist/mind/friends/types.js +1 -1
  172. package/dist/mind/journal-index.js +161 -0
  173. package/dist/mind/note-search.js +268 -0
  174. package/dist/mind/obligation-steering.js +221 -0
  175. package/dist/mind/pending.js +66 -7
  176. package/dist/mind/prompt-refresh.js +3 -2
  177. package/dist/mind/prompt.js +946 -167
  178. package/dist/mind/provenance-trust.js +26 -0
  179. package/dist/mind/scrutiny.js +173 -0
  180. package/dist/nerves/cli-logging.js +7 -1
  181. package/dist/nerves/coverage/audit-rules.js +15 -6
  182. package/dist/nerves/coverage/audit.js +28 -2
  183. package/dist/nerves/coverage/cli.js +1 -1
  184. package/dist/nerves/coverage/contract.js +5 -5
  185. package/dist/nerves/coverage/file-completeness.js +83 -5
  186. package/dist/nerves/coverage/run-artifacts.js +1 -1
  187. package/dist/nerves/event-buffer.js +111 -0
  188. package/dist/nerves/index.js +224 -4
  189. package/dist/nerves/observation.js +20 -0
  190. package/dist/nerves/redact.js +79 -0
  191. package/dist/nerves/runtime.js +5 -1
  192. package/dist/outlook-ui/assets/index-BAcU08c-.css +1 -0
  193. package/dist/outlook-ui/assets/index-D7l3l4vY.js +61 -0
  194. package/dist/outlook-ui/index.html +15 -0
  195. package/dist/repertoire/ado-client.js +15 -56
  196. package/dist/repertoire/ado-semantic.js +11 -10
  197. package/dist/repertoire/api-client.js +97 -0
  198. package/dist/repertoire/bitwarden-store.js +461 -0
  199. package/dist/repertoire/bundle-templates.js +72 -0
  200. package/dist/repertoire/bw-installer.js +79 -0
  201. package/dist/repertoire/coding/codex-jsonl.js +64 -0
  202. package/dist/repertoire/coding/context-pack.js +330 -0
  203. package/dist/repertoire/coding/feedback.js +197 -30
  204. package/dist/repertoire/coding/manager.js +158 -9
  205. package/dist/repertoire/coding/spawner.js +55 -9
  206. package/dist/repertoire/coding/tools.js +170 -7
  207. package/dist/repertoire/commerce-errors.js +109 -0
  208. package/dist/repertoire/commerce-self-test.js +156 -0
  209. package/dist/repertoire/credential-access.js +107 -0
  210. package/dist/repertoire/duffel-client.js +185 -0
  211. package/dist/repertoire/github-client.js +14 -55
  212. package/dist/repertoire/graph-client.js +11 -52
  213. package/dist/repertoire/guardrails.js +371 -0
  214. package/dist/repertoire/mcp-client.js +255 -0
  215. package/dist/repertoire/mcp-manager.js +305 -0
  216. package/dist/repertoire/mcp-tools.js +63 -0
  217. package/dist/repertoire/shell-sessions.js +133 -0
  218. package/dist/repertoire/skills.js +15 -24
  219. package/dist/repertoire/stripe-client.js +131 -0
  220. package/dist/repertoire/tasks/board.js +43 -5
  221. package/dist/repertoire/tasks/fix.js +182 -0
  222. package/dist/repertoire/tasks/index.js +26 -1
  223. package/dist/repertoire/tasks/lifecycle.js +2 -2
  224. package/dist/repertoire/tasks/parser.js +3 -2
  225. package/dist/repertoire/tasks/scanner.js +194 -37
  226. package/dist/repertoire/tasks/transitions.js +16 -78
  227. package/dist/repertoire/tool-results.js +29 -0
  228. package/dist/repertoire/tools-attachments.js +317 -0
  229. package/dist/repertoire/tools-base.js +42 -687
  230. package/dist/repertoire/tools-bluebubbles.js +1 -0
  231. package/dist/repertoire/tools-bridge.js +141 -0
  232. package/dist/repertoire/tools-bundle.js +984 -0
  233. package/dist/repertoire/tools-config.js +185 -0
  234. package/dist/repertoire/tools-continuity.js +248 -0
  235. package/dist/repertoire/tools-credential.js +182 -0
  236. package/dist/repertoire/tools-files.js +342 -0
  237. package/dist/repertoire/tools-flight.js +224 -0
  238. package/dist/repertoire/tools-flow.js +105 -0
  239. package/dist/repertoire/tools-github.js +1 -7
  240. package/dist/repertoire/tools-notes.js +376 -0
  241. package/dist/repertoire/tools-session.js +739 -0
  242. package/dist/repertoire/tools-shell.js +120 -0
  243. package/dist/repertoire/tools-stripe.js +180 -0
  244. package/dist/repertoire/tools-surface.js +243 -0
  245. package/dist/repertoire/tools-teams.js +9 -39
  246. package/dist/repertoire/tools-travel.js +125 -0
  247. package/dist/repertoire/tools-user-profile.js +144 -0
  248. package/dist/repertoire/tools-vault.js +40 -0
  249. package/dist/repertoire/tools.js +144 -113
  250. package/dist/repertoire/travel-api-client.js +360 -0
  251. package/dist/repertoire/user-profile.js +118 -0
  252. package/dist/repertoire/vault-setup.js +246 -0
  253. package/dist/repertoire/vault-unlock.js +382 -0
  254. package/dist/scripts/claude-code-hook.js +41 -0
  255. package/dist/scripts/claude-code-stop-hook.js +47 -0
  256. package/dist/senses/attention-queue.js +116 -0
  257. package/dist/senses/bluebubbles/attachment-cache.js +53 -0
  258. package/dist/senses/bluebubbles/attachment-download.js +137 -0
  259. package/dist/senses/{bluebubbles-client.js → bluebubbles/client.js} +260 -9
  260. package/dist/senses/bluebubbles/entry.js +70 -0
  261. package/dist/senses/bluebubbles/inbound-log.js +113 -0
  262. package/dist/senses/bluebubbles/index.js +1620 -0
  263. package/dist/senses/{bluebubbles-media.js → bluebubbles/media.js} +121 -70
  264. package/dist/senses/{bluebubbles-model.js → bluebubbles/model.js} +33 -12
  265. package/dist/senses/{bluebubbles-mutation-log.js → bluebubbles/mutation-log.js} +45 -3
  266. package/dist/senses/bluebubbles/replay.js +129 -0
  267. package/dist/senses/bluebubbles/runtime-state.js +109 -0
  268. package/dist/senses/{bluebubbles-session-cleanup.js → bluebubbles/session-cleanup.js} +1 -1
  269. package/dist/senses/cli/bracketed-paste.js +82 -0
  270. package/dist/senses/cli/image-paste.js +287 -0
  271. package/dist/senses/cli/image-ref-navigation.js +75 -0
  272. package/dist/senses/cli/ink-app.js +156 -0
  273. package/dist/senses/cli/inline-diff.js +64 -0
  274. package/dist/senses/cli/input-keys.js +174 -0
  275. package/dist/senses/cli/kill-ring.js +86 -0
  276. package/dist/senses/cli/message-list.js +51 -0
  277. package/dist/senses/cli/ouro-tui.js +605 -0
  278. package/dist/senses/cli/spinner-imperative.js +135 -0
  279. package/dist/senses/cli/spinner.js +101 -0
  280. package/dist/senses/cli/status-line.js +60 -0
  281. package/dist/senses/cli/streaming-markdown.js +526 -0
  282. package/dist/senses/cli/tool-display.js +83 -0
  283. package/dist/senses/cli/tool-render.js +85 -0
  284. package/dist/senses/cli/tui-store.js +240 -0
  285. package/dist/senses/cli/virtual-list.js +35 -0
  286. package/dist/senses/cli-entry.js +60 -8
  287. package/dist/senses/cli-layout.js +187 -0
  288. package/dist/senses/cli.js +526 -211
  289. package/dist/senses/commands.js +66 -3
  290. package/dist/senses/continuity.js +94 -0
  291. package/dist/senses/habit-turn-message.js +108 -0
  292. package/dist/senses/inner-dialog-worker.js +112 -19
  293. package/dist/senses/inner-dialog.js +600 -95
  294. package/dist/senses/pipeline.js +539 -61
  295. package/dist/senses/proactive-content-guard.js +51 -0
  296. package/dist/senses/shared-turn.js +205 -0
  297. package/dist/senses/surface-tool.js +68 -0
  298. package/dist/senses/teams-entry.js +60 -8
  299. package/dist/senses/teams.js +569 -237
  300. package/dist/senses/trust-gate.js +5 -5
  301. package/package.json +28 -7
  302. package/skills/agent-commerce.md +106 -0
  303. package/skills/browser-navigation.md +110 -0
  304. package/skills/commerce-setup-guide.md +116 -0
  305. package/skills/commerce-setup.md +84 -0
  306. package/skills/configure-dev-tools.md +101 -0
  307. package/skills/travel-planning.md +134 -0
  308. package/dist/heart/daemon/ouro-path-installer.js +0 -178
  309. package/dist/heart/daemon/subagent-installer.js +0 -134
  310. package/dist/mind/associative-recall.js +0 -197
  311. package/dist/senses/bluebubbles-entry.js +0 -11
  312. package/dist/senses/bluebubbles.js +0 -832
  313. package/dist/senses/debug-activity.js +0 -127
  314. package/subagents/README.md +0 -60
  315. package/subagents/work-doer.md +0 -235
  316. package/subagents/work-merger.md +0 -618
  317. package/subagents/work-planner.md +0 -382
  318. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/basilisk.md +0 -0
  319. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jafar.md +0 -0
  320. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jormungandr.md +0 -0
  321. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/kaa.md +0 -0
  322. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/medusa.md +0 -0
  323. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/monty.md +0 -0
  324. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/nagini.md +0 -0
  325. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/ouroboros.md +0 -0
  326. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/python.md +0 -0
  327. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/quetzalcoatl.md +0 -0
  328. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/sir-hiss.md +0 -0
  329. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/the-snake.md +0 -0
  330. /package/dist/heart/{daemon → hatch}/hatch-animation.js +0 -0
  331. /package/dist/heart/{daemon → hatch}/specialist-orchestrator.js +0 -0
  332. /package/dist/heart/{daemon → versioning}/ouro-uti.js +0 -0
  333. /package/dist/heart/{daemon → versioning}/wrapper-publish-guard.js +0 -0
@@ -39,17 +39,12 @@ const path = __importStar(require("path"));
39
39
  const identity_1 = require("../../heart/identity");
40
40
  const runtime_1 = require("../../nerves/runtime");
41
41
  const spawner_1 = require("./spawner");
42
- function safeAgentName() {
43
- try {
44
- return (0, identity_1.getAgentName)();
45
- }
46
- catch {
47
- return "default";
48
- }
49
- }
50
42
  function defaultStateFilePath(agentName) {
51
43
  return path.join((0, identity_1.getAgentRoot)(agentName), "state", "coding", "sessions.json");
52
44
  }
45
+ function defaultArtifactDirPath(agentName) {
46
+ return path.join((0, identity_1.getAgentRoot)(agentName), "state", "coding", "sessions");
47
+ }
53
48
  function isPidAlive(pid) {
54
49
  try {
55
50
  process.kill(pid, 0);
@@ -62,6 +57,9 @@ function isPidAlive(pid) {
62
57
  function cloneSession(session) {
63
58
  return {
64
59
  ...session,
60
+ originSession: session.originSession ? { ...session.originSession } : undefined,
61
+ checkpoint: session.checkpoint ?? null,
62
+ artifactPath: session.artifactPath,
65
63
  stdoutTail: session.stdoutTail,
66
64
  stderrTail: session.stderrTail,
67
65
  failure: session.failure
@@ -85,6 +83,46 @@ function appendTail(existing, nextChunk, maxLength = 2000) {
85
83
  const combined = `${existing}${nextChunk}`;
86
84
  return combined.length <= maxLength ? combined : combined.slice(combined.length - maxLength);
87
85
  }
86
+ function compactText(text) {
87
+ return text.replace(/\s+/g, " ").trim();
88
+ }
89
+ function clipText(text, maxLength = 240) {
90
+ return text.length <= maxLength ? text : `${text.slice(0, maxLength - 3)}...`;
91
+ }
92
+ function latestMeaningfulLine(text) {
93
+ const lines = text
94
+ .split(/\r?\n/)
95
+ .map((line) => compactText(line))
96
+ .filter(Boolean);
97
+ if (lines.length === 0)
98
+ return null;
99
+ return clipText(lines.at(-1));
100
+ }
101
+ function fallbackCheckpoint(status, code, signal) {
102
+ switch (status) {
103
+ case "waiting_input":
104
+ return "needs input";
105
+ case "stalled":
106
+ return "no recent output";
107
+ case "completed":
108
+ return "completed";
109
+ case "failed":
110
+ if (code !== null)
111
+ return `exit code ${code}`;
112
+ if (signal)
113
+ return `terminated by ${signal}`;
114
+ return "failed";
115
+ case "killed":
116
+ return "terminated by parent agent";
117
+ default:
118
+ return null;
119
+ }
120
+ }
121
+ function deriveCheckpoint(session) {
122
+ return (latestMeaningfulLine(session.stderrTail)
123
+ ?? latestMeaningfulLine(session.stdoutTail)
124
+ ?? fallbackCheckpoint(session.status, session.lastExitCode, session.lastSignal));
125
+ }
88
126
  function isSpawnCodingResult(value) {
89
127
  return typeof value === "object" && value !== null && "process" in value;
90
128
  }
@@ -122,6 +160,7 @@ class CodingSessionManager {
122
160
  maxRestarts;
123
161
  defaultStallThresholdMs;
124
162
  stateFilePath;
163
+ artifactDirPath;
125
164
  existsSync;
126
165
  readFileSync;
127
166
  writeFileSync;
@@ -139,8 +178,19 @@ class CodingSessionManager {
139
178
  this.writeFileSync = options.writeFileSync ?? fs.writeFileSync;
140
179
  this.mkdirSync = options.mkdirSync ?? fs.mkdirSync;
141
180
  this.pidAlive = options.pidAlive ?? isPidAlive;
142
- this.agentName = options.agentName ?? safeAgentName();
181
+ // No silent fallback to "default" — if there's no agentName and no
182
+ // explicit option, getAgentName() throws. The previous `safeAgentName`
183
+ // helper fell back to "default" and ended up writing coding session
184
+ // state to `~/AgentBundles/default.ouro/state/coding/sessions.json` on
185
+ // every vitest run because the coding manager singleton constructs with
186
+ // `{}`. That leaked real-fs state into the developer's home directory
187
+ // on every coverage run. Production callers always pass via argv (see
188
+ // getAgentName); test callers must either pass `agentName` explicitly
189
+ // or mock `../../heart/identity`.
190
+ this.agentName = options.agentName ?? (0, identity_1.getAgentName)();
143
191
  this.stateFilePath = options.stateFilePath ?? defaultStateFilePath(this.agentName);
192
+ this.artifactDirPath = options.artifactDirPath
193
+ ?? (options.stateFilePath ? path.dirname(options.stateFilePath) : defaultArtifactDirPath(this.agentName));
144
194
  this.loadPersistedState();
145
195
  }
146
196
  async spawnSession(request) {
@@ -157,8 +207,12 @@ class CodingSessionManager {
157
207
  runner: normalizedRequest.runner,
158
208
  workdir: normalizedRequest.workdir,
159
209
  taskRef: normalizedRequest.taskRef,
210
+ originSession: normalizedRequest.originSession ? { ...normalizedRequest.originSession } : undefined,
211
+ obligationId: normalizedRequest.obligationId,
160
212
  scopeFile: normalizedRequest.scopeFile,
161
213
  stateFile: normalizedRequest.stateFile,
214
+ checkpoint: null,
215
+ artifactPath: this.artifactPathFor(id),
162
216
  status: "spawning",
163
217
  stdoutTail: "",
164
218
  stderrTail: "",
@@ -245,6 +299,7 @@ class CodingSessionManager {
245
299
  record.process.kill("SIGTERM");
246
300
  record.process = null;
247
301
  record.session.status = "killed";
302
+ record.session.checkpoint = "terminated by parent agent";
248
303
  record.session.endedAt = this.nowIso();
249
304
  (0, runtime_1.emitNervesEvent)({
250
305
  component: "repertoire",
@@ -267,6 +322,7 @@ class CodingSessionManager {
267
322
  continue;
268
323
  stalled += 1;
269
324
  record.session.status = "stalled";
325
+ record.session.checkpoint = deriveCheckpoint(record.session);
270
326
  (0, runtime_1.emitNervesEvent)({
271
327
  level: "warn",
272
328
  component: "repertoire",
@@ -292,6 +348,7 @@ class CodingSessionManager {
292
348
  record.process = null;
293
349
  if (record.session.status === "running" || record.session.status === "spawning") {
294
350
  record.session.status = "killed";
351
+ record.session.checkpoint = "terminated during manager shutdown";
295
352
  record.session.endedAt = this.nowIso();
296
353
  }
297
354
  }
@@ -336,6 +393,13 @@ class CodingSessionManager {
336
393
  record.session.endedAt = this.nowIso();
337
394
  updateKind = "completed";
338
395
  }
396
+ const checkpoint = latestMeaningfulLine(text);
397
+ if (checkpoint) {
398
+ record.session.checkpoint = checkpoint;
399
+ }
400
+ else if (!record.session.checkpoint) {
401
+ record.session.checkpoint = deriveCheckpoint(record.session);
402
+ }
339
403
  (0, runtime_1.emitNervesEvent)({
340
404
  component: "repertoire",
341
405
  event: "repertoire.coding_session_output",
@@ -359,12 +423,14 @@ class CodingSessionManager {
359
423
  record.session.lastSignal = signal;
360
424
  if (record.session.status === "killed" || record.session.status === "completed") {
361
425
  record.session.endedAt = this.nowIso();
426
+ record.session.checkpoint = deriveCheckpoint(record.session);
362
427
  this.persistState();
363
428
  return;
364
429
  }
365
430
  if (code === 0) {
366
431
  record.session.status = "completed";
367
432
  record.session.endedAt = this.nowIso();
433
+ record.session.checkpoint = deriveCheckpoint(record.session);
368
434
  this.persistState();
369
435
  this.notifyListeners(record.session.id, { kind: "completed", session: cloneSession(record.session) });
370
436
  return;
@@ -376,6 +442,7 @@ class CodingSessionManager {
376
442
  record.session.status = "failed";
377
443
  record.session.endedAt = this.nowIso();
378
444
  record.session.failure = defaultFailureDiagnostics(code, signal, record.command, record.args, record.stdoutTail, record.stderrTail);
445
+ record.session.checkpoint = deriveCheckpoint(record.session);
379
446
  (0, runtime_1.emitNervesEvent)({
380
447
  level: "error",
381
448
  component: "repertoire",
@@ -401,6 +468,7 @@ class CodingSessionManager {
401
468
  record.session.lastActivityAt = this.nowIso();
402
469
  record.session.endedAt = null;
403
470
  record.session.failure = null;
471
+ record.session.checkpoint = `restarted after ${reason}`;
404
472
  this.attachProcessListeners(record);
405
473
  (0, runtime_1.emitNervesEvent)({
406
474
  level: "warn",
@@ -482,15 +550,21 @@ class CodingSessionManager {
482
550
  }
483
551
  const normalizedRequest = {
484
552
  ...request,
553
+ originSession: request.originSession ? { ...request.originSession } : undefined,
485
554
  sessionId: request.sessionId ?? session.id,
555
+ obligationId: request.obligationId,
486
556
  parentAgent: request.parentAgent ?? this.agentName,
487
557
  };
488
558
  const normalizedSession = {
489
559
  ...session,
490
560
  taskRef: session.taskRef ?? normalizedRequest.taskRef,
561
+ originSession: session.originSession ?? normalizedRequest.originSession,
562
+ obligationId: session.obligationId ?? normalizedRequest.obligationId,
491
563
  failure: session.failure ?? null,
492
564
  stdoutTail: session.stdoutTail ?? session.failure?.stdoutTail ?? "",
493
565
  stderrTail: session.stderrTail ?? session.failure?.stderrTail ?? "",
566
+ checkpoint: typeof session.checkpoint === "string" ? session.checkpoint : null,
567
+ artifactPath: typeof session.artifactPath === "string" ? session.artifactPath : this.artifactPathFor(session.id),
494
568
  };
495
569
  if (typeof normalizedSession.pid === "number") {
496
570
  const alive = this.pidAlive(normalizedSession.pid);
@@ -503,6 +577,7 @@ class CodingSessionManager {
503
577
  normalizedSession.pid = null;
504
578
  }
505
579
  }
580
+ normalizedSession.checkpoint = normalizedSession.checkpoint ?? deriveCheckpoint(normalizedSession);
506
581
  this.records.set(normalizedSession.id, {
507
582
  request: normalizedRequest,
508
583
  session: normalizedSession,
@@ -542,6 +617,80 @@ class CodingSessionManager {
542
617
  meta: { path: this.stateFilePath, reason: error instanceof Error ? error.message : String(error) },
543
618
  });
544
619
  }
620
+ this.persistArtifacts();
621
+ }
622
+ artifactPathFor(sessionId) {
623
+ return path.join(this.artifactDirPath, `${sessionId}.md`);
624
+ }
625
+ renderArtifact(record) {
626
+ const { request, session } = record;
627
+ const stdout = session.stdoutTail.trim() || "(empty)";
628
+ const stderr = session.stderrTail.trim() || "(empty)";
629
+ const lines = [
630
+ "# Coding Session Artifact",
631
+ "",
632
+ "## Session",
633
+ `id: ${session.id}`,
634
+ `runner: ${session.runner}`,
635
+ `status: ${session.status}`,
636
+ `taskRef: ${session.taskRef ?? "unassigned"}`,
637
+ `workdir: ${session.workdir}`,
638
+ `startedAt: ${session.startedAt}`,
639
+ `lastActivityAt: ${session.lastActivityAt}`,
640
+ `endedAt: ${session.endedAt ?? "active"}`,
641
+ `pid: ${session.pid ?? "none"}`,
642
+ `restarts: ${session.restartCount}`,
643
+ `checkpoint: ${session.checkpoint ?? "none"}`,
644
+ `scopeFile: ${session.scopeFile ?? "none"}`,
645
+ `stateFile: ${session.stateFile ?? "none"}`,
646
+ "",
647
+ "## Request",
648
+ request.prompt,
649
+ "",
650
+ "## Stdout Tail",
651
+ stdout,
652
+ "",
653
+ "## Stderr Tail",
654
+ stderr,
655
+ ];
656
+ if (session.failure) {
657
+ lines.push("", "## Failure", `command: ${session.failure.command}`, `args: ${session.failure.args.join(" ") || "(none)"}`, `code: ${session.failure.code ?? "null"}`, `signal: ${session.failure.signal ?? "null"}`);
658
+ }
659
+ return `${lines.join("\n")}\n`;
660
+ }
661
+ persistArtifacts() {
662
+ try {
663
+ this.mkdirSync(this.artifactDirPath, { recursive: true });
664
+ }
665
+ catch (error) {
666
+ (0, runtime_1.emitNervesEvent)({
667
+ level: "warn",
668
+ component: "repertoire",
669
+ event: "repertoire.coding_artifact_persist_error",
670
+ message: "failed preparing coding artifact directory",
671
+ meta: { path: this.artifactDirPath, reason: error instanceof Error ? error.message : String(error) },
672
+ });
673
+ return;
674
+ }
675
+ for (const record of this.records.values()) {
676
+ try {
677
+ record.session.artifactPath = record.session.artifactPath ?? this.artifactPathFor(record.session.id);
678
+ this.writeFileSync(record.session.artifactPath, this.renderArtifact(record), "utf-8");
679
+ }
680
+ catch (error) {
681
+ (0, runtime_1.emitNervesEvent)({
682
+ level: "warn",
683
+ component: "repertoire",
684
+ event: "repertoire.coding_artifact_persist_error",
685
+ message: "failed writing coding session artifact",
686
+ meta: {
687
+ id: record.session.id,
688
+ path: record.session.artifactPath ?? this.artifactPathFor(record.session.id),
689
+ reason: error instanceof Error ? error.message : String(error),
690
+ },
691
+ });
692
+ }
693
+ }
545
694
  }
546
695
  }
547
696
  exports.CodingSessionManager = CodingSessionManager;
@@ -36,8 +36,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.spawnCodingProcess = spawnCodingProcess;
37
37
  const child_process_1 = require("child_process");
38
38
  const fs = __importStar(require("fs"));
39
+ const os = __importStar(require("os"));
40
+ const path = __importStar(require("path"));
39
41
  const runtime_1 = require("../../nerves/runtime");
40
- function buildCommandArgs(runner, workdir) {
42
+ function buildCommandArgs(runner, workdir, parentAgent) {
41
43
  if (runner === "claude") {
42
44
  return {
43
45
  command: "claude",
@@ -53,25 +55,65 @@ function buildCommandArgs(runner, workdir) {
53
55
  ],
54
56
  };
55
57
  }
58
+ const agent = parentAgent ?? "unknown";
59
+ // Use absolute path to ouro-entry.js so MCP works in both dev and installed mode
60
+ // __dirname at runtime is dist/repertoire/coding/ — go up 2 levels to dist/
61
+ const distRoot = path.resolve(__dirname, "..", "..");
62
+ const ouroEntryPath = path.join(distRoot, "heart", "daemon", "ouro-entry.js");
56
63
  return {
57
64
  command: "codex",
58
- args: ["exec", "--skip-git-repo-check", "--cd", workdir],
65
+ args: [
66
+ "exec",
67
+ "--skip-git-repo-check",
68
+ "--cd",
69
+ workdir,
70
+ "--ephemeral",
71
+ "--json",
72
+ "-c",
73
+ `mcp_servers.ouro.command=node`,
74
+ "-c",
75
+ `mcp_servers.ouro.args=["${ouroEntryPath}","mcp-serve","--agent","${agent}"]`,
76
+ ],
59
77
  };
60
78
  }
79
+ function buildSpawnEnv(baseEnv, homeDir) {
80
+ const binDir = path.join(homeDir, ".ouro-cli", "bin");
81
+ const existingPath = baseEnv.PATH ?? "";
82
+ const pathEntries = existingPath.split(path.delimiter).filter((entry) => entry.length > 0);
83
+ if (!pathEntries.includes(binDir)) {
84
+ pathEntries.unshift(binDir);
85
+ }
86
+ return {
87
+ ...baseEnv,
88
+ PATH: pathEntries.join(path.delimiter),
89
+ };
90
+ }
91
+ function appendFileSection(sections, label, filePath, deps) {
92
+ if (!filePath || !deps.existsSync(filePath))
93
+ return;
94
+ const content = deps.readFileSync(filePath, "utf-8").trim();
95
+ if (content.length === 0)
96
+ return;
97
+ sections.push(`${label} (${filePath}):\n${content}`);
98
+ }
61
99
  function buildPrompt(request, deps) {
62
100
  const sections = [];
101
+ sections.push([
102
+ "Execution contract:",
103
+ "- You are a subordinate coding session launched by a parent Ouro agent.",
104
+ "- Execute the concrete request in the supplied workdir directly.",
105
+ "- Do not switch into planning/doing workflows, approval gates, or repo-management rituals unless the request explicitly asks for them.",
106
+ "- Treat the request, scope file, and state file as the authoritative briefing for this session.",
107
+ "- Prefer direct execution and verification over narration.",
108
+ ].join("\n"));
63
109
  sections.push([
64
110
  "Coding session metadata:",
65
111
  `sessionId: ${request.sessionId ?? "pending"}`,
66
112
  `parentAgent: ${request.parentAgent ?? "unknown"}`,
67
113
  `taskRef: ${request.taskRef ?? "unassigned"}`,
68
114
  ].join("\n"));
69
- if (request.stateFile && deps.existsSync(request.stateFile)) {
70
- const stateContent = deps.readFileSync(request.stateFile, "utf-8").trim();
71
- if (stateContent.length > 0) {
72
- sections.push(`State file (${request.stateFile}):\n${stateContent}`);
73
- }
74
- }
115
+ appendFileSection(sections, "Scope file", request.scopeFile, deps);
116
+ appendFileSection(sections, "State file", request.stateFile, deps);
75
117
  sections.push(request.prompt);
76
118
  return sections.join("\n\n---\n\n");
77
119
  }
@@ -79,8 +121,11 @@ function spawnCodingProcess(request, deps = {}) {
79
121
  const spawnFn = deps.spawnFn ?? ((command, args, options) => (0, child_process_1.spawn)(command, args, options));
80
122
  const existsSync = deps.existsSync ?? fs.existsSync;
81
123
  const readFileSync = deps.readFileSync ?? fs.readFileSync;
124
+ const homeDir = deps.homeDir ?? os.homedir();
125
+ const baseEnv = deps.baseEnv ?? process.env;
82
126
  const prompt = buildPrompt(request, { existsSync, readFileSync });
83
- const { command, args } = buildCommandArgs(request.runner, request.workdir);
127
+ const { command, args } = buildCommandArgs(request.runner, request.workdir, request.parentAgent);
128
+ const env = buildSpawnEnv(baseEnv, homeDir);
84
129
  (0, runtime_1.emitNervesEvent)({
85
130
  component: "repertoire",
86
131
  event: "repertoire.coding_spawn_start",
@@ -89,6 +134,7 @@ function spawnCodingProcess(request, deps = {}) {
89
134
  });
90
135
  const proc = spawnFn(command, args, {
91
136
  cwd: request.workdir,
137
+ env,
92
138
  stdio: ["pipe", "pipe", "pipe"],
93
139
  });
94
140
  proc.stdin.end(`${prompt}\n`);
@@ -1,8 +1,13 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.codingToolDefinitions = void 0;
4
+ exports.countFilesInSessionOutput = countFilesInSessionOutput;
4
5
  const index_1 = require("./index");
6
+ const context_pack_1 = require("./context-pack");
7
+ const identity_1 = require("../../heart/identity");
8
+ const obligations_1 = require("../../arc/obligations");
5
9
  const runtime_1 = require("../../nerves/runtime");
10
+ const scrutiny_1 = require("../../mind/scrutiny");
6
11
  const RUNNERS = ["claude", "codex"];
7
12
  function requireArg(args, key) {
8
13
  const value = args[key];
@@ -29,11 +34,112 @@ function emitCodingToolEvent(toolName) {
29
34
  meta: { toolName },
30
35
  });
31
36
  }
37
+ /**
38
+ * Count distinct file paths mentioned in a coding session's stdout output.
39
+ * Looks for path-like tokens (containing / and a file extension).
40
+ * Returns the count of unique paths found.
41
+ */
42
+ function countFilesInSessionOutput(session) {
43
+ const text = `${session.stdoutTail}\n${session.stderrTail}`;
44
+ // Match path-like tokens: contain at least one / and a file extension
45
+ const pathPattern = /(?:^|\s)((?:\/|\.\/|\.\.\/)?(?:[\w.@-]+\/)+[\w.-]+\.[\w]+)/gm;
46
+ const paths = new Set();
47
+ let match;
48
+ while ((match = pathPattern.exec(text)) !== null) {
49
+ paths.add(match[1]);
50
+ }
51
+ return paths.size;
52
+ }
53
+ /**
54
+ * If a coding session is completed, append scrutiny to the result.
55
+ * Returns the original result with scrutiny appended, or unchanged if
56
+ * the session is not completed or has no file changes.
57
+ */
58
+ function appendCompletionScrutiny(result, session) {
59
+ if (session.status !== "completed")
60
+ return result;
61
+ const fileCount = countFilesInSessionOutput(session);
62
+ const scrutiny = (0, scrutiny_1.getCodingCompletionScrutiny)(fileCount);
63
+ return scrutiny ? `${result}\n\n${scrutiny}` : result;
64
+ }
65
+ function sameOriginSession(left, right) {
66
+ if (!left && !right)
67
+ return true;
68
+ if (!left || !right)
69
+ return false;
70
+ return left.friendId === right.friendId && left.channel === right.channel && left.key === right.key;
71
+ }
72
+ function matchesReusableCodingSession(session, request) {
73
+ if (session.status !== "spawning" && session.status !== "running" && session.status !== "waiting_input" && session.status !== "stalled") {
74
+ return false;
75
+ }
76
+ const scopeMatches = request.scopeFile ? session.scopeFile === request.scopeFile : true;
77
+ const stateMatches = request.stateFile ? session.stateFile === request.stateFile : true;
78
+ return (session.runner === request.runner &&
79
+ session.workdir === request.workdir &&
80
+ session.taskRef === request.taskRef &&
81
+ scopeMatches &&
82
+ stateMatches &&
83
+ session.obligationId === request.obligationId &&
84
+ sameOriginSession(request.originSession, session.originSession));
85
+ }
86
+ function latestSessionFirst(left, right) {
87
+ const lastActivityDelta = Date.parse(right.lastActivityAt) - Date.parse(left.lastActivityAt);
88
+ if (lastActivityDelta !== 0)
89
+ return lastActivityDelta;
90
+ return right.id.localeCompare(left.id);
91
+ }
92
+ function findReusableCodingSession(sessions, request) {
93
+ const matches = sessions.filter((session) => matchesReusableCodingSession(session, request)).sort(latestSessionFirst);
94
+ return matches[0] ?? null;
95
+ }
96
+ function isLiveCodingStatus(status) {
97
+ return status === "spawning" || status === "running" || status === "waiting_input" || status === "stalled";
98
+ }
99
+ function rankCodingStatusSession(session, currentSession) {
100
+ return sameOriginSession({
101
+ friendId: currentSession.friendId,
102
+ channel: currentSession.channel,
103
+ key: currentSession.key,
104
+ }, session.originSession)
105
+ ? 0
106
+ : 1;
107
+ }
108
+ function selectCodingStatusSessions(sessions, currentSession) {
109
+ if (sessions.length === 0)
110
+ return [];
111
+ if (!currentSession) {
112
+ return sessions;
113
+ }
114
+ const activeSessions = sessions.filter((session) => isLiveCodingStatus(session.status)).sort(latestSessionFirst);
115
+ if (activeSessions.length > 0) {
116
+ return activeSessions.sort((left, right) => {
117
+ const rankDelta = rankCodingStatusSession(left, currentSession) - rankCodingStatusSession(right, currentSession);
118
+ if (rankDelta !== 0)
119
+ return rankDelta;
120
+ return latestSessionFirst(left, right);
121
+ });
122
+ }
123
+ const matchingClosedSessions = sessions
124
+ .filter((session) => sameOriginSession({
125
+ friendId: currentSession.friendId,
126
+ channel: currentSession.channel,
127
+ key: currentSession.key,
128
+ }, session.originSession))
129
+ .sort(latestSessionFirst);
130
+ if (matchingClosedSessions.length > 0) {
131
+ return matchingClosedSessions;
132
+ }
133
+ return [...sessions].sort(latestSessionFirst);
134
+ }
135
+ function buildCodingObligationContent(taskRef) {
136
+ return `finish ${taskRef} and bring the result back`;
137
+ }
32
138
  const codingSpawnTool = {
33
139
  type: "function",
34
140
  function: {
35
141
  name: "coding_spawn",
36
- description: "spawn a coding session using claude/codex and explicit task-threaded guidance",
142
+ description: "Spawn a coding session using claude or codex with task-threaded guidance. The coding session runs as a separate process with its own context. Give it a COMPLETE, SELF-CONTAINED task description -- it cannot see this conversation, doesn't know what you've tried, doesn't understand the broader context. Include: what to do, why, what files are involved, what 'done' looks like. Never delegate understanding -- don't write 'based on the conversation, fix the bug.' Write the specific file paths, line numbers, and what to change. Include any required verification steps or tests in the task description so the coding session knows how to prove the work is done.",
37
143
  parameters: {
38
144
  type: "object",
39
145
  properties: {
@@ -52,7 +158,7 @@ const codingStatusTool = {
52
158
  type: "function",
53
159
  function: {
54
160
  name: "coding_status",
55
- description: "inspect coding sessions; omit sessionId to list all active/known sessions",
161
+ description: "Inspect coding sessions. Omit sessionId to list all active/known sessions with their status. Use this to check progress before asking the human for a status update.",
56
162
  parameters: {
57
163
  type: "object",
58
164
  properties: {
@@ -65,7 +171,7 @@ const codingTailTool = {
65
171
  type: "function",
66
172
  function: {
67
173
  name: "coding_tail",
68
- description: "show recent stdout/stderr tail for a coding session in a readable format",
174
+ description: "Show recent stdout/stderr output from a coding session. Use this to understand what the session is doing or why it might be stuck. Read the actual output before reporting status -- don't guess.",
69
175
  parameters: {
70
176
  type: "object",
71
177
  properties: {
@@ -130,6 +236,17 @@ exports.codingToolDefinitions = [
130
236
  prompt,
131
237
  taskRef,
132
238
  };
239
+ if (ctx?.currentSession && ctx.currentSession.channel !== "inner") {
240
+ request.originSession = {
241
+ friendId: ctx.currentSession.friendId,
242
+ channel: ctx.currentSession.channel,
243
+ key: ctx.currentSession.key,
244
+ };
245
+ const obligation = (0, obligations_1.findPendingObligationForOrigin)((0, identity_1.getAgentRoot)(), request.originSession);
246
+ if (obligation) {
247
+ request.obligationId = obligation.id;
248
+ }
249
+ }
133
250
  const scopeFile = optionalArg(args, "scopeFile");
134
251
  if (scopeFile)
135
252
  request.scopeFile = scopeFile;
@@ -137,7 +254,48 @@ exports.codingToolDefinitions = [
137
254
  if (stateFile)
138
255
  request.stateFile = stateFile;
139
256
  const manager = (0, index_1.getCodingSessionManager)();
257
+ const existingSessions = manager.listSessions();
258
+ const existingSession = findReusableCodingSession(existingSessions, request);
259
+ if (existingSession) {
260
+ (0, runtime_1.emitNervesEvent)({
261
+ component: "repertoire",
262
+ event: "repertoire.coding_session_reused",
263
+ message: "reused active coding session",
264
+ meta: { id: existingSession.id, runner: existingSession.runner, taskRef: existingSession.taskRef },
265
+ });
266
+ if (ctx?.codingFeedback) {
267
+ (0, index_1.attachCodingSessionFeedback)(manager, existingSession, ctx.codingFeedback);
268
+ }
269
+ return JSON.stringify({ ...existingSession, reused: true });
270
+ }
271
+ if (request.originSession && !request.obligationId) {
272
+ const created = (0, obligations_1.createObligation)((0, identity_1.getAgentRoot)(), {
273
+ origin: request.originSession,
274
+ content: buildCodingObligationContent(taskRef),
275
+ });
276
+ request.obligationId = created.id;
277
+ }
278
+ if (!request.scopeFile || !request.stateFile) {
279
+ const generated = (0, context_pack_1.prepareCodingContextPack)({
280
+ request: { ...request },
281
+ existingSessions,
282
+ activeWorkFrame: ctx?.activeWorkFrame,
283
+ });
284
+ if (!request.scopeFile)
285
+ request.scopeFile = generated.scopeFile;
286
+ if (!request.stateFile)
287
+ request.stateFile = generated.stateFile;
288
+ }
140
289
  const session = await manager.spawnSession(request);
290
+ if (session.obligationId) {
291
+ (0, obligations_1.advanceObligation)((0, identity_1.getAgentRoot)(), session.obligationId, {
292
+ status: "investigating",
293
+ currentSurface: { kind: "coding", label: `${session.runner} ${session.id}` },
294
+ latestNote: session.originSession
295
+ ? `coding session started for ${session.originSession.channel}/${session.originSession.key}`
296
+ : "coding session started",
297
+ });
298
+ }
141
299
  if (args.runner === "codex" && args.taskRef) {
142
300
  (0, runtime_1.emitNervesEvent)({
143
301
  component: "repertoire",
@@ -151,21 +309,23 @@ exports.codingToolDefinitions = [
151
309
  }
152
310
  return JSON.stringify(session);
153
311
  },
312
+ summaryKeys: ["runner", "workdir", "taskRef"],
154
313
  },
155
314
  {
156
315
  tool: codingStatusTool,
157
- handler: (args) => {
316
+ handler: (args, ctx) => {
158
317
  emitCodingToolEvent("coding_status");
159
318
  const manager = (0, index_1.getCodingSessionManager)();
160
319
  const sessionId = requireArg(args, "sessionId");
161
320
  if (!sessionId) {
162
- return JSON.stringify(manager.listSessions());
321
+ return JSON.stringify(selectCodingStatusSessions(manager.listSessions(), ctx?.currentSession));
163
322
  }
164
323
  const session = manager.getSession(sessionId);
165
324
  if (!session)
166
325
  return `session not found: ${sessionId}`;
167
- return JSON.stringify(session);
326
+ return appendCompletionScrutiny(JSON.stringify(session), session);
168
327
  },
328
+ summaryKeys: ["sessionId"],
169
329
  },
170
330
  {
171
331
  tool: codingTailTool,
@@ -177,8 +337,9 @@ exports.codingToolDefinitions = [
177
337
  const session = (0, index_1.getCodingSessionManager)().getSession(sessionId);
178
338
  if (!session)
179
339
  return `session not found: ${sessionId}`;
180
- return (0, index_1.formatCodingTail)(session);
340
+ return appendCompletionScrutiny((0, index_1.formatCodingTail)(session), session);
181
341
  },
342
+ summaryKeys: ["sessionId"],
182
343
  },
183
344
  {
184
345
  tool: codingSendInputTool,
@@ -192,6 +353,7 @@ exports.codingToolDefinitions = [
192
353
  return "input is required";
193
354
  return JSON.stringify((0, index_1.getCodingSessionManager)().sendInput(sessionId, input));
194
355
  },
356
+ summaryKeys: ["sessionId", "input"],
195
357
  },
196
358
  {
197
359
  tool: codingKillTool,
@@ -202,5 +364,6 @@ exports.codingToolDefinitions = [
202
364
  return "sessionId is required";
203
365
  return JSON.stringify((0, index_1.getCodingSessionManager)().killSession(sessionId));
204
366
  },
367
+ summaryKeys: ["sessionId"],
205
368
  },
206
369
  ];