@ouro.bot/cli 0.1.0-alpha.13 → 0.1.0-alpha.131

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 (126) hide show
  1. package/AdoptionSpecialist.ouro/psyche/SOUL.md +2 -2
  2. package/AdoptionSpecialist.ouro/psyche/identities/monty.md +2 -2
  3. package/README.md +147 -205
  4. package/changelog.json +814 -0
  5. package/dist/heart/active-work.js +622 -0
  6. package/dist/heart/bridges/manager.js +358 -0
  7. package/dist/heart/bridges/state-machine.js +135 -0
  8. package/dist/heart/bridges/store.js +123 -0
  9. package/dist/heart/commitments.js +105 -0
  10. package/dist/heart/config.js +66 -21
  11. package/dist/heart/core.js +518 -100
  12. package/dist/heart/cross-chat-delivery.js +146 -0
  13. package/dist/heart/daemon/agent-discovery.js +81 -0
  14. package/dist/heart/daemon/auth-flow.js +457 -0
  15. package/dist/heart/daemon/daemon-cli.js +1516 -195
  16. package/dist/heart/daemon/daemon-entry.js +43 -2
  17. package/dist/heart/daemon/daemon-runtime-sync.js +212 -0
  18. package/dist/heart/daemon/daemon.js +261 -1
  19. package/dist/heart/daemon/hatch-animation.js +10 -3
  20. package/dist/heart/daemon/hatch-flow.js +7 -72
  21. package/dist/heart/daemon/hooks/bundle-meta.js +92 -0
  22. package/dist/heart/daemon/launchd.js +159 -0
  23. package/dist/heart/daemon/log-tailer.js +4 -3
  24. package/dist/heart/daemon/message-router.js +17 -8
  25. package/dist/heart/daemon/ouro-bot-global-installer.js +128 -0
  26. package/dist/heart/daemon/ouro-path-installer.js +57 -29
  27. package/dist/heart/daemon/ouro-version-manager.js +171 -0
  28. package/dist/heart/daemon/process-manager.js +13 -0
  29. package/dist/heart/daemon/run-hooks.js +37 -0
  30. package/dist/heart/daemon/runtime-logging.js +58 -15
  31. package/dist/heart/daemon/runtime-metadata.js +219 -0
  32. package/dist/heart/daemon/runtime-mode.js +67 -0
  33. package/dist/heart/daemon/sense-manager.js +50 -2
  34. package/dist/heart/daemon/skill-management-installer.js +94 -0
  35. package/dist/heart/daemon/socket-client.js +202 -0
  36. package/dist/heart/daemon/specialist-orchestrator.js +2 -2
  37. package/dist/heart/daemon/specialist-prompt.js +7 -4
  38. package/dist/heart/daemon/specialist-tools.js +52 -3
  39. package/dist/heart/daemon/staged-restart.js +114 -0
  40. package/dist/heart/daemon/thoughts.js +507 -0
  41. package/dist/heart/daemon/update-checker.js +111 -0
  42. package/dist/heart/daemon/update-hooks.js +138 -0
  43. package/dist/heart/daemon/wrapper-publish-guard.js +86 -0
  44. package/dist/heart/delegation.js +62 -0
  45. package/dist/heart/identity.js +64 -21
  46. package/dist/heart/kicks.js +1 -19
  47. package/dist/heart/model-capabilities.js +48 -0
  48. package/dist/heart/obligations.js +197 -0
  49. package/dist/heart/progress-story.js +42 -0
  50. package/dist/heart/provider-failover.js +88 -0
  51. package/dist/heart/provider-ping.js +159 -0
  52. package/dist/heart/providers/anthropic-token.js +163 -0
  53. package/dist/heart/providers/anthropic.js +195 -34
  54. package/dist/heart/providers/azure.js +115 -9
  55. package/dist/heart/providers/github-copilot.js +157 -0
  56. package/dist/heart/providers/minimax.js +33 -3
  57. package/dist/heart/providers/openai-codex.js +49 -14
  58. package/dist/heart/safe-workspace.js +381 -0
  59. package/dist/heart/session-activity.js +173 -0
  60. package/dist/heart/session-recall.js +216 -0
  61. package/dist/heart/streaming.js +108 -24
  62. package/dist/heart/target-resolution.js +123 -0
  63. package/dist/heart/tool-loop.js +194 -0
  64. package/dist/heart/turn-coordinator.js +28 -0
  65. package/dist/mind/associative-recall.js +14 -2
  66. package/dist/mind/bundle-manifest.js +12 -0
  67. package/dist/mind/context.js +60 -14
  68. package/dist/mind/first-impressions.js +16 -2
  69. package/dist/mind/friends/channel.js +35 -0
  70. package/dist/mind/friends/group-context.js +144 -0
  71. package/dist/mind/friends/store-file.js +19 -0
  72. package/dist/mind/friends/trust-explanation.js +74 -0
  73. package/dist/mind/friends/types.js +8 -0
  74. package/dist/mind/memory.js +27 -26
  75. package/dist/mind/obligation-steering.js +221 -0
  76. package/dist/mind/pending.js +76 -9
  77. package/dist/mind/phrases.js +1 -0
  78. package/dist/mind/prompt.js +456 -77
  79. package/dist/mind/token-estimate.js +8 -12
  80. package/dist/nerves/cli-logging.js +15 -2
  81. package/dist/nerves/coverage/run-artifacts.js +1 -1
  82. package/dist/nerves/index.js +12 -0
  83. package/dist/nerves/runtime.js +5 -1
  84. package/dist/repertoire/ado-client.js +4 -2
  85. package/dist/repertoire/coding/context-pack.js +254 -0
  86. package/dist/repertoire/coding/feedback.js +301 -0
  87. package/dist/repertoire/coding/index.js +4 -1
  88. package/dist/repertoire/coding/manager.js +210 -4
  89. package/dist/repertoire/coding/spawner.js +39 -9
  90. package/dist/repertoire/coding/tools.js +171 -4
  91. package/dist/repertoire/data/ado-endpoints.json +188 -0
  92. package/dist/repertoire/guardrails.js +290 -0
  93. package/dist/repertoire/mcp-client.js +254 -0
  94. package/dist/repertoire/mcp-manager.js +198 -0
  95. package/dist/repertoire/skills.js +3 -26
  96. package/dist/repertoire/tasks/board.js +12 -0
  97. package/dist/repertoire/tasks/index.js +23 -9
  98. package/dist/repertoire/tasks/transitions.js +1 -2
  99. package/dist/repertoire/tools-base.js +925 -250
  100. package/dist/repertoire/tools-bluebubbles.js +93 -0
  101. package/dist/repertoire/tools-teams.js +58 -25
  102. package/dist/repertoire/tools.js +106 -53
  103. package/dist/senses/bluebubbles-client.js +210 -5
  104. package/dist/senses/bluebubbles-entry.js +2 -0
  105. package/dist/senses/bluebubbles-inbound-log.js +109 -0
  106. package/dist/senses/bluebubbles-media.js +339 -0
  107. package/dist/senses/bluebubbles-model.js +12 -4
  108. package/dist/senses/bluebubbles-mutation-log.js +45 -5
  109. package/dist/senses/bluebubbles-runtime-state.js +109 -0
  110. package/dist/senses/bluebubbles-session-cleanup.js +72 -0
  111. package/dist/senses/bluebubbles.js +915 -45
  112. package/dist/senses/cli-layout.js +187 -0
  113. package/dist/senses/cli.js +374 -131
  114. package/dist/senses/continuity.js +94 -0
  115. package/dist/senses/debug-activity.js +154 -0
  116. package/dist/senses/inner-dialog-worker.js +47 -18
  117. package/dist/senses/inner-dialog.js +388 -83
  118. package/dist/senses/pipeline.js +444 -0
  119. package/dist/senses/teams.js +607 -129
  120. package/dist/senses/trust-gate.js +112 -2
  121. package/package.json +9 -3
  122. package/subagents/README.md +4 -70
  123. package/dist/heart/daemon/subagent-installer.js +0 -134
  124. package/subagents/work-doer.md +0 -233
  125. package/subagents/work-merger.md +0 -624
  126. package/subagents/work-planner.md +0 -373
@@ -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,11 @@ 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,
71
+ stdoutTail: session.stdoutTail,
72
+ stderrTail: session.stderrTail,
66
73
  failure: session.failure
67
74
  ? {
68
75
  ...session.failure,
@@ -84,6 +91,46 @@ function appendTail(existing, nextChunk, maxLength = 2000) {
84
91
  const combined = `${existing}${nextChunk}`;
85
92
  return combined.length <= maxLength ? combined : combined.slice(combined.length - maxLength);
86
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
+ }
87
134
  function isSpawnCodingResult(value) {
88
135
  return typeof value === "object" && value !== null && "process" in value;
89
136
  }
@@ -115,11 +162,13 @@ function defaultFailureDiagnostics(code, signal, command, args, stdoutTail, stde
115
162
  }
116
163
  class CodingSessionManager {
117
164
  records = new Map();
165
+ listeners = new Map();
118
166
  spawnProcess;
119
167
  nowIso;
120
168
  maxRestarts;
121
169
  defaultStallThresholdMs;
122
170
  stateFilePath;
171
+ artifactDirPath;
123
172
  existsSync;
124
173
  readFileSync;
125
174
  writeFileSync;
@@ -139,6 +188,8 @@ class CodingSessionManager {
139
188
  this.pidAlive = options.pidAlive ?? isPidAlive;
140
189
  this.agentName = options.agentName ?? safeAgentName();
141
190
  this.stateFilePath = options.stateFilePath ?? defaultStateFilePath(this.agentName);
191
+ this.artifactDirPath = options.artifactDirPath
192
+ ?? (options.stateFilePath ? path.dirname(options.stateFilePath) : defaultArtifactDirPath(this.agentName));
142
193
  this.loadPersistedState();
143
194
  }
144
195
  async spawnSession(request) {
@@ -155,9 +206,15 @@ class CodingSessionManager {
155
206
  runner: normalizedRequest.runner,
156
207
  workdir: normalizedRequest.workdir,
157
208
  taskRef: normalizedRequest.taskRef,
209
+ originSession: normalizedRequest.originSession ? { ...normalizedRequest.originSession } : undefined,
210
+ obligationId: normalizedRequest.obligationId,
158
211
  scopeFile: normalizedRequest.scopeFile,
159
212
  stateFile: normalizedRequest.stateFile,
213
+ checkpoint: null,
214
+ artifactPath: this.artifactPathFor(id),
160
215
  status: "spawning",
216
+ stdoutTail: "",
217
+ stderrTail: "",
161
218
  pid: null,
162
219
  startedAt: now,
163
220
  lastActivityAt: now,
@@ -188,6 +245,7 @@ class CodingSessionManager {
188
245
  meta: { id, runner: normalizedRequest.runner, pid: session.pid },
189
246
  });
190
247
  this.persistState();
248
+ this.notifyListeners(id, { kind: "spawned", session: cloneSession(session) });
191
249
  return cloneSession(session);
192
250
  }
193
251
  listSessions() {
@@ -199,6 +257,20 @@ class CodingSessionManager {
199
257
  const record = this.records.get(sessionId);
200
258
  return record ? cloneSession(record.session) : null;
201
259
  }
260
+ subscribe(sessionId, listener) {
261
+ const listeners = this.listeners.get(sessionId) ?? new Set();
262
+ listeners.add(listener);
263
+ this.listeners.set(sessionId, listeners);
264
+ return () => {
265
+ const current = this.listeners.get(sessionId);
266
+ if (!current)
267
+ return;
268
+ current.delete(listener);
269
+ if (current.size === 0) {
270
+ this.listeners.delete(sessionId);
271
+ }
272
+ };
273
+ }
202
274
  sendInput(sessionId, input) {
203
275
  const record = this.records.get(sessionId);
204
276
  if (!record || !record.process) {
@@ -226,6 +298,7 @@ class CodingSessionManager {
226
298
  record.process.kill("SIGTERM");
227
299
  record.process = null;
228
300
  record.session.status = "killed";
301
+ record.session.checkpoint = "terminated by parent agent";
229
302
  record.session.endedAt = this.nowIso();
230
303
  (0, runtime_1.emitNervesEvent)({
231
304
  component: "repertoire",
@@ -234,6 +307,7 @@ class CodingSessionManager {
234
307
  meta: { id: sessionId },
235
308
  });
236
309
  this.persistState();
310
+ this.notifyListeners(sessionId, { kind: "killed", session: cloneSession(record.session) });
237
311
  return { ok: true, message: `killed ${sessionId}` };
238
312
  }
239
313
  checkStalls(nowMs = Date.now()) {
@@ -247,6 +321,7 @@ class CodingSessionManager {
247
321
  continue;
248
322
  stalled += 1;
249
323
  record.session.status = "stalled";
324
+ record.session.checkpoint = deriveCheckpoint(record.session);
250
325
  (0, runtime_1.emitNervesEvent)({
251
326
  level: "warn",
252
327
  component: "repertoire",
@@ -254,6 +329,7 @@ class CodingSessionManager {
254
329
  message: "coding session stalled",
255
330
  meta: { id: record.session.id, elapsedMs: elapsed },
256
331
  });
332
+ this.notifyListeners(record.session.id, { kind: "stalled", session: cloneSession(record.session) });
257
333
  if (record.request.autoRestartOnStall !== false && record.session.restartCount < this.maxRestarts) {
258
334
  this.restartSession(record, "stalled");
259
335
  }
@@ -271,6 +347,7 @@ class CodingSessionManager {
271
347
  record.process = null;
272
348
  if (record.session.status === "running" || record.session.status === "spawning") {
273
349
  record.session.status = "killed";
350
+ record.session.checkpoint = "terminated during manager shutdown";
274
351
  record.session.endedAt = this.nowIso();
275
352
  }
276
353
  }
@@ -297,18 +374,30 @@ class CodingSessionManager {
297
374
  }
298
375
  onOutput(record, text, stream) {
299
376
  record.session.lastActivityAt = this.nowIso();
377
+ let updateKind = "progress";
300
378
  if (stream === "stdout") {
301
379
  record.stdoutTail = appendTail(record.stdoutTail, text);
380
+ record.session.stdoutTail = record.stdoutTail;
302
381
  }
303
382
  else {
304
383
  record.stderrTail = appendTail(record.stderrTail, text);
384
+ record.session.stderrTail = record.stderrTail;
305
385
  }
306
386
  if (text.includes("status: NEEDS_REVIEW") || text.includes("❌ blocked")) {
307
387
  record.session.status = "waiting_input";
388
+ updateKind = "waiting_input";
308
389
  }
309
390
  if (text.includes("✅ all units complete")) {
310
391
  record.session.status = "completed";
311
392
  record.session.endedAt = this.nowIso();
393
+ updateKind = "completed";
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);
312
401
  }
313
402
  (0, runtime_1.emitNervesEvent)({
314
403
  component: "repertoire",
@@ -317,6 +406,12 @@ class CodingSessionManager {
317
406
  meta: { id: record.session.id, status: record.session.status },
318
407
  });
319
408
  this.persistState();
409
+ this.notifyListeners(record.session.id, {
410
+ kind: updateKind,
411
+ session: cloneSession(record.session),
412
+ stream,
413
+ text,
414
+ });
320
415
  }
321
416
  onExit(record, code, signal) {
322
417
  if (!record.process)
@@ -327,13 +422,16 @@ class CodingSessionManager {
327
422
  record.session.lastSignal = signal;
328
423
  if (record.session.status === "killed" || record.session.status === "completed") {
329
424
  record.session.endedAt = this.nowIso();
425
+ record.session.checkpoint = deriveCheckpoint(record.session);
330
426
  this.persistState();
331
427
  return;
332
428
  }
333
429
  if (code === 0) {
334
430
  record.session.status = "completed";
335
431
  record.session.endedAt = this.nowIso();
432
+ record.session.checkpoint = deriveCheckpoint(record.session);
336
433
  this.persistState();
434
+ this.notifyListeners(record.session.id, { kind: "completed", session: cloneSession(record.session) });
337
435
  return;
338
436
  }
339
437
  if (record.request.autoRestartOnCrash !== false && record.session.restartCount < this.maxRestarts) {
@@ -343,6 +441,7 @@ class CodingSessionManager {
343
441
  record.session.status = "failed";
344
442
  record.session.endedAt = this.nowIso();
345
443
  record.session.failure = defaultFailureDiagnostics(code, signal, record.command, record.args, record.stdoutTail, record.stderrTail);
444
+ record.session.checkpoint = deriveCheckpoint(record.session);
346
445
  (0, runtime_1.emitNervesEvent)({
347
446
  level: "error",
348
447
  component: "repertoire",
@@ -351,6 +450,7 @@ class CodingSessionManager {
351
450
  meta: { id: record.session.id, code, signal, command: record.command },
352
451
  });
353
452
  this.persistState();
453
+ this.notifyListeners(record.session.id, { kind: "failed", session: cloneSession(record.session) });
354
454
  }
355
455
  restartSession(record, reason) {
356
456
  const replacement = normalizeSpawnResult(this.spawnProcess(record.request));
@@ -359,12 +459,15 @@ class CodingSessionManager {
359
459
  record.args = [...replacement.args];
360
460
  record.stdoutTail = "";
361
461
  record.stderrTail = "";
462
+ record.session.stdoutTail = "";
463
+ record.session.stderrTail = "";
362
464
  record.session.pid = replacement.process.pid ?? null;
363
465
  record.session.restartCount += 1;
364
466
  record.session.status = "running";
365
467
  record.session.lastActivityAt = this.nowIso();
366
468
  record.session.endedAt = null;
367
469
  record.session.failure = null;
470
+ record.session.checkpoint = `restarted after ${reason}`;
368
471
  this.attachProcessListeners(record);
369
472
  (0, runtime_1.emitNervesEvent)({
370
473
  level: "warn",
@@ -375,6 +478,26 @@ class CodingSessionManager {
375
478
  });
376
479
  this.persistState();
377
480
  }
481
+ notifyListeners(sessionId, update) {
482
+ const listeners = this.listeners.get(sessionId);
483
+ if (!listeners || listeners.size === 0)
484
+ return;
485
+ for (const listener of listeners) {
486
+ void Promise.resolve(listener(update)).catch((error) => {
487
+ (0, runtime_1.emitNervesEvent)({
488
+ level: "warn",
489
+ component: "repertoire",
490
+ event: "repertoire.coding_feedback_listener_error",
491
+ message: "coding session listener failed",
492
+ meta: {
493
+ sessionId,
494
+ kind: update.kind,
495
+ reason: error instanceof Error ? error.message : String(error),
496
+ },
497
+ });
498
+ });
499
+ }
500
+ }
378
501
  loadPersistedState() {
379
502
  if (!this.existsSync(this.stateFilePath)) {
380
503
  return;
@@ -426,13 +549,21 @@ class CodingSessionManager {
426
549
  }
427
550
  const normalizedRequest = {
428
551
  ...request,
552
+ originSession: request.originSession ? { ...request.originSession } : undefined,
429
553
  sessionId: request.sessionId ?? session.id,
554
+ obligationId: request.obligationId,
430
555
  parentAgent: request.parentAgent ?? this.agentName,
431
556
  };
432
557
  const normalizedSession = {
433
558
  ...session,
434
559
  taskRef: session.taskRef ?? normalizedRequest.taskRef,
560
+ originSession: session.originSession ?? normalizedRequest.originSession,
561
+ obligationId: session.obligationId ?? normalizedRequest.obligationId,
435
562
  failure: session.failure ?? null,
563
+ stdoutTail: session.stdoutTail ?? session.failure?.stdoutTail ?? "",
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),
436
567
  };
437
568
  if (typeof normalizedSession.pid === "number") {
438
569
  const alive = this.pidAlive(normalizedSession.pid);
@@ -445,14 +576,15 @@ class CodingSessionManager {
445
576
  normalizedSession.pid = null;
446
577
  }
447
578
  }
579
+ normalizedSession.checkpoint = normalizedSession.checkpoint ?? deriveCheckpoint(normalizedSession);
448
580
  this.records.set(normalizedSession.id, {
449
581
  request: normalizedRequest,
450
582
  session: normalizedSession,
451
583
  process: null,
452
584
  command: normalizedSession.failure?.command ?? "restored",
453
585
  args: normalizedSession.failure ? [...normalizedSession.failure.args] : [],
454
- stdoutTail: normalizedSession.failure?.stdoutTail ?? "",
455
- stderrTail: normalizedSession.failure?.stderrTail ?? "",
586
+ stdoutTail: normalizedSession.stdoutTail,
587
+ stderrTail: normalizedSession.stderrTail,
456
588
  });
457
589
  this.sequence = Math.max(this.sequence, extractSequence(normalizedSession.id));
458
590
  }
@@ -484,6 +616,80 @@ class CodingSessionManager {
484
616
  meta: { path: this.stateFilePath, reason: error instanceof Error ? error.message : String(error) },
485
617
  });
486
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
+ }
487
693
  }
488
694
  }
489
695
  exports.CodingSessionManager = CodingSessionManager;
@@ -36,6 +36,8 @@ 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
42
  function buildCommandArgs(runner, workdir) {
41
43
  if (runner === "claude") {
@@ -43,11 +45,11 @@ function buildCommandArgs(runner, workdir) {
43
45
  command: "claude",
44
46
  args: [
45
47
  "-p",
48
+ "--verbose",
49
+ "--no-session-persistence",
46
50
  "--dangerously-skip-permissions",
47
51
  "--add-dir",
48
52
  workdir,
49
- "--input-format",
50
- "stream-json",
51
53
  "--output-format",
52
54
  "stream-json",
53
55
  ],
@@ -58,20 +60,44 @@ function buildCommandArgs(runner, workdir) {
58
60
  args: ["exec", "--skip-git-repo-check", "--cd", workdir],
59
61
  };
60
62
  }
63
+ function buildSpawnEnv(baseEnv, homeDir) {
64
+ const binDir = path.join(homeDir, ".ouro-cli", "bin");
65
+ const existingPath = baseEnv.PATH ?? "";
66
+ const pathEntries = existingPath.split(path.delimiter).filter((entry) => entry.length > 0);
67
+ if (!pathEntries.includes(binDir)) {
68
+ pathEntries.unshift(binDir);
69
+ }
70
+ return {
71
+ ...baseEnv,
72
+ PATH: pathEntries.join(path.delimiter),
73
+ };
74
+ }
75
+ function appendFileSection(sections, label, filePath, deps) {
76
+ if (!filePath || !deps.existsSync(filePath))
77
+ return;
78
+ const content = deps.readFileSync(filePath, "utf-8").trim();
79
+ if (content.length === 0)
80
+ return;
81
+ sections.push(`${label} (${filePath}):\n${content}`);
82
+ }
61
83
  function buildPrompt(request, deps) {
62
84
  const sections = [];
85
+ sections.push([
86
+ "Execution contract:",
87
+ "- You are a subordinate coding session launched by a parent Ouro agent.",
88
+ "- Execute the concrete request in the supplied workdir directly.",
89
+ "- Do not switch into planning/doing workflows, approval gates, or repo-management rituals unless the request explicitly asks for them.",
90
+ "- Treat the request, scope file, and state file as the authoritative briefing for this session.",
91
+ "- Prefer direct execution and verification over narration.",
92
+ ].join("\n"));
63
93
  sections.push([
64
94
  "Coding session metadata:",
65
95
  `sessionId: ${request.sessionId ?? "pending"}`,
66
96
  `parentAgent: ${request.parentAgent ?? "unknown"}`,
67
97
  `taskRef: ${request.taskRef ?? "unassigned"}`,
68
98
  ].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
- }
99
+ appendFileSection(sections, "Scope file", request.scopeFile, deps);
100
+ appendFileSection(sections, "State file", request.stateFile, deps);
75
101
  sections.push(request.prompt);
76
102
  return sections.join("\n\n---\n\n");
77
103
  }
@@ -79,8 +105,11 @@ function spawnCodingProcess(request, deps = {}) {
79
105
  const spawnFn = deps.spawnFn ?? ((command, args, options) => (0, child_process_1.spawn)(command, args, options));
80
106
  const existsSync = deps.existsSync ?? fs.existsSync;
81
107
  const readFileSync = deps.readFileSync ?? fs.readFileSync;
108
+ const homeDir = deps.homeDir ?? os.homedir();
109
+ const baseEnv = deps.baseEnv ?? process.env;
82
110
  const prompt = buildPrompt(request, { existsSync, readFileSync });
83
111
  const { command, args } = buildCommandArgs(request.runner, request.workdir);
112
+ const env = buildSpawnEnv(baseEnv, homeDir);
84
113
  (0, runtime_1.emitNervesEvent)({
85
114
  component: "repertoire",
86
115
  event: "repertoire.coding_spawn_start",
@@ -89,9 +118,10 @@ function spawnCodingProcess(request, deps = {}) {
89
118
  });
90
119
  const proc = spawnFn(command, args, {
91
120
  cwd: request.workdir,
121
+ env,
92
122
  stdio: ["pipe", "pipe", "pipe"],
93
123
  });
94
- proc.stdin.write(`${prompt}\n`);
124
+ proc.stdin.end(`${prompt}\n`);
95
125
  (0, runtime_1.emitNervesEvent)({
96
126
  component: "repertoire",
97
127
  event: "repertoire.coding_spawn_end",