@ouro.bot/cli 0.1.0-alpha.22 → 0.1.0-alpha.220

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 +1354 -2
  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 +444 -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 +59 -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 +487 -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 +202 -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 +36 -0
  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 +100 -0
  138. package/dist/repertoire/bitwarden-client.js +290 -0
  139. package/dist/repertoire/coding/codex-jsonl.js +64 -0
  140. package/dist/repertoire/coding/context-pack.js +330 -0
  141. package/dist/repertoire/coding/feedback.js +197 -30
  142. package/dist/repertoire/coding/manager.js +149 -2
  143. package/dist/repertoire/coding/spawner.js +55 -9
  144. package/dist/repertoire/coding/tools.js +170 -7
  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-files.js +342 -0
  165. package/dist/repertoire/tools-flow.js +62 -0
  166. package/dist/repertoire/tools-github.js +1 -6
  167. package/dist/repertoire/tools-memory.js +347 -0
  168. package/dist/repertoire/tools-session.js +721 -0
  169. package/dist/repertoire/tools-shell.js +120 -0
  170. package/dist/repertoire/tools-surface.js +242 -0
  171. package/dist/repertoire/tools-teams.js +12 -59
  172. package/dist/repertoire/tools-travel.js +125 -0
  173. package/dist/repertoire/tools-vault.js +185 -0
  174. package/dist/repertoire/tools.js +132 -124
  175. package/dist/repertoire/travel-api-client.js +224 -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 +1327 -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 +591 -84
  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 +143 -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
@@ -2,8 +2,20 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.formatCodingTail = formatCodingTail;
4
4
  exports.attachCodingSessionFeedback = attachCodingSessionFeedback;
5
+ const identity_1 = require("../../heart/identity");
6
+ const socket_client_1 = require("../../heart/daemon/socket-client");
7
+ const obligations_1 = require("../../arc/obligations");
5
8
  const runtime_1 = require("../../nerves/runtime");
6
9
  const TERMINAL_UPDATE_KINDS = new Set(["completed", "failed", "killed"]);
10
+ const OBLIGATION_WAKE_UPDATE_KINDS = new Set([
11
+ "waiting_input",
12
+ "stalled",
13
+ "completed",
14
+ "failed",
15
+ "killed",
16
+ ]);
17
+ const PULL_REQUEST_NUMBER_PATTERN = /\bPR\s*#(\d+)\b/i;
18
+ const PULL_REQUEST_URL_PATTERN = /\/pull\/(\d+)(?:\b|\/)?/i;
7
19
  function clip(text, maxLength = 280) {
8
20
  const trimmed = text.trim();
9
21
  if (trimmed.length <= maxLength)
@@ -42,25 +54,114 @@ function lastMeaningfulLine(text) {
42
54
  return clip(lines.at(-1));
43
55
  }
44
56
  function formatSessionLabel(session) {
45
- return `${session.runner} ${session.id}`;
57
+ const origin = session.originSession
58
+ ? ` for ${session.originSession.channel}/${session.originSession.key}`
59
+ : "";
60
+ return `${session.runner} ${session.id}${origin}`;
61
+ }
62
+ function extractPullRequestLabel(snippet) {
63
+ if (!snippet)
64
+ return null;
65
+ const numberMatch = snippet.match(PULL_REQUEST_NUMBER_PATTERN);
66
+ if (numberMatch)
67
+ return `PR #${numberMatch[1]}`;
68
+ const urlMatch = snippet.match(PULL_REQUEST_URL_PATTERN);
69
+ if (urlMatch)
70
+ return `PR #${urlMatch[1]}`;
71
+ return null;
72
+ }
73
+ function isMergedPullRequestSnippet(snippet) {
74
+ return /\bmerged\b/i.test(snippet) || /\blanded\b/i.test(snippet);
75
+ }
76
+ function deriveObligationMilestone(update) {
77
+ const snippet = pickUpdateSnippet(update);
78
+ const pullRequest = extractPullRequestLabel(snippet);
79
+ if (update.kind === "completed" && snippet && pullRequest && isMergedPullRequestSnippet(snippet)) {
80
+ return {
81
+ status: "updating_runtime",
82
+ currentSurface: { kind: "runtime", label: "ouro up" },
83
+ currentArtifact: pullRequest,
84
+ nextAction: "update runtime, verify version/changelog, then re-observe",
85
+ };
86
+ }
87
+ if (update.kind === "completed" && pullRequest) {
88
+ return {
89
+ status: "waiting_for_merge",
90
+ currentSurface: { kind: "merge", label: pullRequest },
91
+ currentArtifact: pullRequest,
92
+ nextAction: `wait for checks, merge ${pullRequest}, then update runtime`,
93
+ };
94
+ }
95
+ if (update.kind === "waiting_input") {
96
+ return {
97
+ status: "investigating",
98
+ currentSurface: { kind: "coding", label: `${update.session.runner} ${update.session.id}` },
99
+ nextAction: `answer ${update.session.runner} ${update.session.id} and continue`,
100
+ };
101
+ }
102
+ if (update.kind === "stalled") {
103
+ return {
104
+ status: "investigating",
105
+ currentSurface: { kind: "coding", label: `${update.session.runner} ${update.session.id}` },
106
+ nextAction: `unstick ${update.session.runner} ${update.session.id} and continue`,
107
+ };
108
+ }
109
+ if (update.kind === "progress" || update.kind === "spawned" || update.kind === "failed" || update.kind === "killed" || update.kind === "completed") {
110
+ return {
111
+ status: "investigating",
112
+ currentSurface: { kind: "coding", label: `${update.session.runner} ${update.session.id}` },
113
+ };
114
+ }
115
+ return null;
46
116
  }
47
117
  function isSafeProgressSnippet(snippet) {
118
+ const normalized = snippet.trim();
48
119
  const wordCount = snippet.split(/\s+/).filter(Boolean).length;
49
- return (snippet.length <= 80
120
+ return (normalized.length <= 80
121
+ && wordCount >= 2
50
122
  && wordCount <= 8
51
- && !snippet.includes(":")
52
- && !snippet.startsWith("**")
53
- && !/^Respond with\b/i.test(snippet)
54
- && !/^Coding session metadata\b/i.test(snippet)
55
- && !/^sessionId\b/i.test(snippet)
56
- && !/^taskRef\b/i.test(snippet)
57
- && !/^parentAgent\b/i.test(snippet));
123
+ && /[A-Za-z]{3,}/.test(normalized)
124
+ && !normalized.includes(":")
125
+ && !/[{}\[\]();]/.test(normalized)
126
+ && !normalized.startsWith("**")
127
+ && !/^Respond with\b/i.test(normalized)
128
+ && !/^Coding session metadata\b/i.test(normalized)
129
+ && !/^sessionId\b/i.test(normalized)
130
+ && !/^taskRef\b/i.test(normalized)
131
+ && !/^parentAgent\b/i.test(normalized));
58
132
  }
59
133
  function pickUpdateSnippet(update) {
60
- return (lastMeaningfulLine(update.text)
134
+ const checkpoint = update.session.checkpoint?.trim() || null;
135
+ return (checkpoint
136
+ ?? lastMeaningfulLine(update.text)
61
137
  ?? lastMeaningfulLine(update.session.stderrTail)
62
138
  ?? lastMeaningfulLine(update.session.stdoutTail));
63
139
  }
140
+ function renderValue(text) {
141
+ const trimmed = text?.trim();
142
+ return trimmed && trimmed.length > 0 ? trimmed : "(empty)";
143
+ }
144
+ function renderPath(text) {
145
+ return text && text.trim().length > 0 ? text : "(none)";
146
+ }
147
+ function formatCodingTail(session) {
148
+ const stdout = renderValue(session.stdoutTail);
149
+ const stderr = renderValue(session.stderrTail);
150
+ return [
151
+ `sessionId: ${session.id}`,
152
+ `runner: ${session.runner}`,
153
+ `status: ${session.status}`,
154
+ `checkpoint: ${renderValue(session.checkpoint ?? undefined)}`,
155
+ `artifactPath: ${renderPath(session.artifactPath)}`,
156
+ `workdir: ${session.workdir}`,
157
+ "",
158
+ "[stdout]",
159
+ stdout,
160
+ "",
161
+ "[stderr]",
162
+ stderr,
163
+ ].join("\n");
164
+ }
64
165
  function formatUpdateMessage(update) {
65
166
  const label = formatSessionLabel(update.session);
66
167
  const snippet = pickUpdateSnippet(update);
@@ -81,26 +182,88 @@ function formatUpdateMessage(update) {
81
182
  return `${label} started`;
82
183
  }
83
184
  }
84
- function formatCodingTail(session) {
85
- const stdout = session.stdoutTail.trim() || "(empty)";
86
- const stderr = session.stderrTail.trim() || "(empty)";
87
- return [
88
- `sessionId: ${session.id}`,
89
- `runner: ${session.runner}`,
90
- `status: ${session.status}`,
91
- `workdir: ${session.workdir}`,
92
- "",
93
- "[stdout]",
94
- stdout,
95
- "",
96
- "[stderr]",
97
- stderr,
98
- ].join("\n");
185
+ function formatReportBackMessage(update, baseMessage) {
186
+ if (!baseMessage)
187
+ return null;
188
+ if (!update.session.obligationId || !update.session.originSession) {
189
+ return baseMessage;
190
+ }
191
+ const milestone = deriveObligationMilestone(update);
192
+ const extraLines = [];
193
+ if (milestone?.currentArtifact) {
194
+ extraLines.push(`current artifact: ${milestone.currentArtifact}`);
195
+ }
196
+ if (milestone?.nextAction) {
197
+ extraLines.push(`next: ${milestone.nextAction}`);
198
+ }
199
+ return extraLines.length > 0 ? `${baseMessage}\n${extraLines.join("\n")}` : baseMessage;
200
+ }
201
+ function obligationNoteFromUpdate(update) {
202
+ const snippet = pickUpdateSnippet(update);
203
+ switch (update.kind) {
204
+ case "spawned":
205
+ return update.session.originSession
206
+ ? `coding session started for ${update.session.originSession.channel}/${update.session.originSession.key}`
207
+ : "coding session started";
208
+ case "progress":
209
+ return snippet ? `coding session progress: ${snippet}` : null;
210
+ case "waiting_input":
211
+ return snippet ? `coding session waiting: ${snippet}` : "coding session waiting for input";
212
+ case "stalled":
213
+ return snippet ? `coding session stalled: ${snippet}` : "coding session stalled";
214
+ case "completed":
215
+ return snippet
216
+ ? `coding session completed: ${snippet}; merge/update still pending`
217
+ : "coding session completed; merge/update still pending";
218
+ case "failed":
219
+ return snippet ? `coding session failed: ${snippet}` : "coding session failed";
220
+ case "killed":
221
+ return "coding session killed";
222
+ }
223
+ }
224
+ function syncObligationFromUpdate(update) {
225
+ const obligationId = update.session.obligationId;
226
+ if (!obligationId)
227
+ return;
228
+ const milestone = deriveObligationMilestone(update);
229
+ try {
230
+ (0, obligations_1.advanceObligation)((0, identity_1.getAgentRoot)(), obligationId, {
231
+ status: milestone?.status ?? "investigating",
232
+ currentSurface: milestone?.currentSurface ?? { kind: "coding", label: `${update.session.runner} ${update.session.id}` },
233
+ currentArtifact: milestone?.currentArtifact,
234
+ nextAction: milestone?.nextAction,
235
+ latestNote: obligationNoteFromUpdate(update) ?? undefined,
236
+ });
237
+ }
238
+ catch {
239
+ // Detached feedback should still reach the human even if obligation sync is unavailable.
240
+ }
241
+ }
242
+ async function wakeInnerDialogForObligation(update) {
243
+ if (!update.session.obligationId || !OBLIGATION_WAKE_UPDATE_KINDS.has(update.kind)) {
244
+ return;
245
+ }
246
+ try {
247
+ await (0, socket_client_1.requestInnerWake)((0, identity_1.getAgentName)());
248
+ }
249
+ catch (error) {
250
+ (0, runtime_1.emitNervesEvent)({
251
+ level: "warn",
252
+ component: "repertoire",
253
+ event: "repertoire.coding_feedback_wake_error",
254
+ message: "coding feedback wake request failed",
255
+ meta: {
256
+ sessionId: update.session.id,
257
+ kind: update.kind,
258
+ reason: error instanceof Error ? error.message : String(error),
259
+ },
260
+ });
261
+ }
99
262
  }
100
263
  function attachCodingSessionFeedback(manager, session, target) {
101
264
  let lastMessage = "";
102
265
  let closed = false;
103
- let unsubscribe = () => { };
266
+ let unsubscribe = null;
104
267
  const sendMessage = (message) => {
105
268
  if (closed || !message || message === lastMessage) {
106
269
  return;
@@ -119,16 +282,20 @@ function attachCodingSessionFeedback(manager, session, target) {
119
282
  });
120
283
  });
121
284
  };
122
- sendMessage(formatUpdateMessage({ kind: "spawned", session }));
285
+ const spawnedUpdate = { kind: "spawned", session };
286
+ syncObligationFromUpdate(spawnedUpdate);
287
+ sendMessage(formatReportBackMessage(spawnedUpdate, formatUpdateMessage(spawnedUpdate)));
123
288
  unsubscribe = manager.subscribe(session.id, async (update) => {
124
- sendMessage(formatUpdateMessage(update));
289
+ syncObligationFromUpdate(update);
290
+ sendMessage(formatReportBackMessage(update, formatUpdateMessage(update)));
291
+ await wakeInnerDialogForObligation(update);
125
292
  if (TERMINAL_UPDATE_KINDS.has(update.kind)) {
126
293
  closed = true;
127
- unsubscribe();
294
+ unsubscribe?.();
128
295
  }
129
296
  });
130
297
  return () => {
131
298
  closed = true;
132
- unsubscribe();
299
+ unsubscribe?.();
133
300
  };
134
301
  }
@@ -35,7 +35,6 @@ var __importStar = (this && this.__importStar) || (function () {
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.CodingSessionManager = void 0;
37
37
  const fs = __importStar(require("fs"));
38
- const os = __importStar(require("os"));
39
38
  const path = __importStar(require("path"));
40
39
  const identity_1 = require("../../heart/identity");
41
40
  const runtime_1 = require("../../nerves/runtime");
@@ -49,7 +48,10 @@ function safeAgentName() {
49
48
  }
50
49
  }
51
50
  function defaultStateFilePath(agentName) {
52
- return path.join(os.homedir(), ".agentstate", agentName, "coding", "sessions.json");
51
+ return path.join((0, identity_1.getAgentRoot)(agentName), "state", "coding", "sessions.json");
52
+ }
53
+ function defaultArtifactDirPath(agentName) {
54
+ return path.join((0, identity_1.getAgentRoot)(agentName), "state", "coding", "sessions");
53
55
  }
54
56
  function isPidAlive(pid) {
55
57
  try {
@@ -63,6 +65,9 @@ function isPidAlive(pid) {
63
65
  function cloneSession(session) {
64
66
  return {
65
67
  ...session,
68
+ originSession: session.originSession ? { ...session.originSession } : undefined,
69
+ checkpoint: session.checkpoint ?? null,
70
+ artifactPath: session.artifactPath,
66
71
  stdoutTail: session.stdoutTail,
67
72
  stderrTail: session.stderrTail,
68
73
  failure: session.failure
@@ -86,6 +91,46 @@ function appendTail(existing, nextChunk, maxLength = 2000) {
86
91
  const combined = `${existing}${nextChunk}`;
87
92
  return combined.length <= maxLength ? combined : combined.slice(combined.length - maxLength);
88
93
  }
94
+ function compactText(text) {
95
+ return text.replace(/\s+/g, " ").trim();
96
+ }
97
+ function clipText(text, maxLength = 240) {
98
+ return text.length <= maxLength ? text : `${text.slice(0, maxLength - 3)}...`;
99
+ }
100
+ function latestMeaningfulLine(text) {
101
+ const lines = text
102
+ .split(/\r?\n/)
103
+ .map((line) => compactText(line))
104
+ .filter(Boolean);
105
+ if (lines.length === 0)
106
+ return null;
107
+ return clipText(lines.at(-1));
108
+ }
109
+ function fallbackCheckpoint(status, code, signal) {
110
+ switch (status) {
111
+ case "waiting_input":
112
+ return "needs input";
113
+ case "stalled":
114
+ return "no recent output";
115
+ case "completed":
116
+ return "completed";
117
+ case "failed":
118
+ if (code !== null)
119
+ return `exit code ${code}`;
120
+ if (signal)
121
+ return `terminated by ${signal}`;
122
+ return "failed";
123
+ case "killed":
124
+ return "terminated by parent agent";
125
+ default:
126
+ return null;
127
+ }
128
+ }
129
+ function deriveCheckpoint(session) {
130
+ return (latestMeaningfulLine(session.stderrTail)
131
+ ?? latestMeaningfulLine(session.stdoutTail)
132
+ ?? fallbackCheckpoint(session.status, session.lastExitCode, session.lastSignal));
133
+ }
89
134
  function isSpawnCodingResult(value) {
90
135
  return typeof value === "object" && value !== null && "process" in value;
91
136
  }
@@ -123,6 +168,7 @@ class CodingSessionManager {
123
168
  maxRestarts;
124
169
  defaultStallThresholdMs;
125
170
  stateFilePath;
171
+ artifactDirPath;
126
172
  existsSync;
127
173
  readFileSync;
128
174
  writeFileSync;
@@ -142,6 +188,8 @@ class CodingSessionManager {
142
188
  this.pidAlive = options.pidAlive ?? isPidAlive;
143
189
  this.agentName = options.agentName ?? safeAgentName();
144
190
  this.stateFilePath = options.stateFilePath ?? defaultStateFilePath(this.agentName);
191
+ this.artifactDirPath = options.artifactDirPath
192
+ ?? (options.stateFilePath ? path.dirname(options.stateFilePath) : defaultArtifactDirPath(this.agentName));
145
193
  this.loadPersistedState();
146
194
  }
147
195
  async spawnSession(request) {
@@ -158,8 +206,12 @@ class CodingSessionManager {
158
206
  runner: normalizedRequest.runner,
159
207
  workdir: normalizedRequest.workdir,
160
208
  taskRef: normalizedRequest.taskRef,
209
+ originSession: normalizedRequest.originSession ? { ...normalizedRequest.originSession } : undefined,
210
+ obligationId: normalizedRequest.obligationId,
161
211
  scopeFile: normalizedRequest.scopeFile,
162
212
  stateFile: normalizedRequest.stateFile,
213
+ checkpoint: null,
214
+ artifactPath: this.artifactPathFor(id),
163
215
  status: "spawning",
164
216
  stdoutTail: "",
165
217
  stderrTail: "",
@@ -246,6 +298,7 @@ class CodingSessionManager {
246
298
  record.process.kill("SIGTERM");
247
299
  record.process = null;
248
300
  record.session.status = "killed";
301
+ record.session.checkpoint = "terminated by parent agent";
249
302
  record.session.endedAt = this.nowIso();
250
303
  (0, runtime_1.emitNervesEvent)({
251
304
  component: "repertoire",
@@ -268,6 +321,7 @@ class CodingSessionManager {
268
321
  continue;
269
322
  stalled += 1;
270
323
  record.session.status = "stalled";
324
+ record.session.checkpoint = deriveCheckpoint(record.session);
271
325
  (0, runtime_1.emitNervesEvent)({
272
326
  level: "warn",
273
327
  component: "repertoire",
@@ -293,6 +347,7 @@ class CodingSessionManager {
293
347
  record.process = null;
294
348
  if (record.session.status === "running" || record.session.status === "spawning") {
295
349
  record.session.status = "killed";
350
+ record.session.checkpoint = "terminated during manager shutdown";
296
351
  record.session.endedAt = this.nowIso();
297
352
  }
298
353
  }
@@ -337,6 +392,13 @@ class CodingSessionManager {
337
392
  record.session.endedAt = this.nowIso();
338
393
  updateKind = "completed";
339
394
  }
395
+ const checkpoint = latestMeaningfulLine(text);
396
+ if (checkpoint) {
397
+ record.session.checkpoint = checkpoint;
398
+ }
399
+ else if (!record.session.checkpoint) {
400
+ record.session.checkpoint = deriveCheckpoint(record.session);
401
+ }
340
402
  (0, runtime_1.emitNervesEvent)({
341
403
  component: "repertoire",
342
404
  event: "repertoire.coding_session_output",
@@ -360,12 +422,14 @@ class CodingSessionManager {
360
422
  record.session.lastSignal = signal;
361
423
  if (record.session.status === "killed" || record.session.status === "completed") {
362
424
  record.session.endedAt = this.nowIso();
425
+ record.session.checkpoint = deriveCheckpoint(record.session);
363
426
  this.persistState();
364
427
  return;
365
428
  }
366
429
  if (code === 0) {
367
430
  record.session.status = "completed";
368
431
  record.session.endedAt = this.nowIso();
432
+ record.session.checkpoint = deriveCheckpoint(record.session);
369
433
  this.persistState();
370
434
  this.notifyListeners(record.session.id, { kind: "completed", session: cloneSession(record.session) });
371
435
  return;
@@ -377,6 +441,7 @@ class CodingSessionManager {
377
441
  record.session.status = "failed";
378
442
  record.session.endedAt = this.nowIso();
379
443
  record.session.failure = defaultFailureDiagnostics(code, signal, record.command, record.args, record.stdoutTail, record.stderrTail);
444
+ record.session.checkpoint = deriveCheckpoint(record.session);
380
445
  (0, runtime_1.emitNervesEvent)({
381
446
  level: "error",
382
447
  component: "repertoire",
@@ -402,6 +467,7 @@ class CodingSessionManager {
402
467
  record.session.lastActivityAt = this.nowIso();
403
468
  record.session.endedAt = null;
404
469
  record.session.failure = null;
470
+ record.session.checkpoint = `restarted after ${reason}`;
405
471
  this.attachProcessListeners(record);
406
472
  (0, runtime_1.emitNervesEvent)({
407
473
  level: "warn",
@@ -483,15 +549,21 @@ class CodingSessionManager {
483
549
  }
484
550
  const normalizedRequest = {
485
551
  ...request,
552
+ originSession: request.originSession ? { ...request.originSession } : undefined,
486
553
  sessionId: request.sessionId ?? session.id,
554
+ obligationId: request.obligationId,
487
555
  parentAgent: request.parentAgent ?? this.agentName,
488
556
  };
489
557
  const normalizedSession = {
490
558
  ...session,
491
559
  taskRef: session.taskRef ?? normalizedRequest.taskRef,
560
+ originSession: session.originSession ?? normalizedRequest.originSession,
561
+ obligationId: session.obligationId ?? normalizedRequest.obligationId,
492
562
  failure: session.failure ?? null,
493
563
  stdoutTail: session.stdoutTail ?? session.failure?.stdoutTail ?? "",
494
564
  stderrTail: session.stderrTail ?? session.failure?.stderrTail ?? "",
565
+ checkpoint: typeof session.checkpoint === "string" ? session.checkpoint : null,
566
+ artifactPath: typeof session.artifactPath === "string" ? session.artifactPath : this.artifactPathFor(session.id),
495
567
  };
496
568
  if (typeof normalizedSession.pid === "number") {
497
569
  const alive = this.pidAlive(normalizedSession.pid);
@@ -504,6 +576,7 @@ class CodingSessionManager {
504
576
  normalizedSession.pid = null;
505
577
  }
506
578
  }
579
+ normalizedSession.checkpoint = normalizedSession.checkpoint ?? deriveCheckpoint(normalizedSession);
507
580
  this.records.set(normalizedSession.id, {
508
581
  request: normalizedRequest,
509
582
  session: normalizedSession,
@@ -543,6 +616,80 @@ class CodingSessionManager {
543
616
  meta: { path: this.stateFilePath, reason: error instanceof Error ? error.message : String(error) },
544
617
  });
545
618
  }
619
+ this.persistArtifacts();
620
+ }
621
+ artifactPathFor(sessionId) {
622
+ return path.join(this.artifactDirPath, `${sessionId}.md`);
623
+ }
624
+ renderArtifact(record) {
625
+ const { request, session } = record;
626
+ const stdout = session.stdoutTail.trim() || "(empty)";
627
+ const stderr = session.stderrTail.trim() || "(empty)";
628
+ const lines = [
629
+ "# Coding Session Artifact",
630
+ "",
631
+ "## Session",
632
+ `id: ${session.id}`,
633
+ `runner: ${session.runner}`,
634
+ `status: ${session.status}`,
635
+ `taskRef: ${session.taskRef ?? "unassigned"}`,
636
+ `workdir: ${session.workdir}`,
637
+ `startedAt: ${session.startedAt}`,
638
+ `lastActivityAt: ${session.lastActivityAt}`,
639
+ `endedAt: ${session.endedAt ?? "active"}`,
640
+ `pid: ${session.pid ?? "none"}`,
641
+ `restarts: ${session.restartCount}`,
642
+ `checkpoint: ${session.checkpoint ?? "none"}`,
643
+ `scopeFile: ${session.scopeFile ?? "none"}`,
644
+ `stateFile: ${session.stateFile ?? "none"}`,
645
+ "",
646
+ "## Request",
647
+ request.prompt,
648
+ "",
649
+ "## Stdout Tail",
650
+ stdout,
651
+ "",
652
+ "## Stderr Tail",
653
+ stderr,
654
+ ];
655
+ if (session.failure) {
656
+ lines.push("", "## Failure", `command: ${session.failure.command}`, `args: ${session.failure.args.join(" ") || "(none)"}`, `code: ${session.failure.code ?? "null"}`, `signal: ${session.failure.signal ?? "null"}`);
657
+ }
658
+ return `${lines.join("\n")}\n`;
659
+ }
660
+ persistArtifacts() {
661
+ try {
662
+ this.mkdirSync(this.artifactDirPath, { recursive: true });
663
+ }
664
+ catch (error) {
665
+ (0, runtime_1.emitNervesEvent)({
666
+ level: "warn",
667
+ component: "repertoire",
668
+ event: "repertoire.coding_artifact_persist_error",
669
+ message: "failed preparing coding artifact directory",
670
+ meta: { path: this.artifactDirPath, reason: error instanceof Error ? error.message : String(error) },
671
+ });
672
+ return;
673
+ }
674
+ for (const record of this.records.values()) {
675
+ try {
676
+ record.session.artifactPath = record.session.artifactPath ?? this.artifactPathFor(record.session.id);
677
+ this.writeFileSync(record.session.artifactPath, this.renderArtifact(record), "utf-8");
678
+ }
679
+ catch (error) {
680
+ (0, runtime_1.emitNervesEvent)({
681
+ level: "warn",
682
+ component: "repertoire",
683
+ event: "repertoire.coding_artifact_persist_error",
684
+ message: "failed writing coding session artifact",
685
+ meta: {
686
+ id: record.session.id,
687
+ path: record.session.artifactPath ?? this.artifactPathFor(record.session.id),
688
+ reason: error instanceof Error ? error.message : String(error),
689
+ },
690
+ });
691
+ }
692
+ }
546
693
  }
547
694
  }
548
695
  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`);