@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.
- package/AdoptionSpecialist.ouro/psyche/SOUL.md +2 -2
- package/AdoptionSpecialist.ouro/psyche/identities/monty.md +2 -2
- package/README.md +147 -205
- package/changelog.json +814 -0
- package/dist/heart/active-work.js +622 -0
- package/dist/heart/bridges/manager.js +358 -0
- package/dist/heart/bridges/state-machine.js +135 -0
- package/dist/heart/bridges/store.js +123 -0
- package/dist/heart/commitments.js +105 -0
- package/dist/heart/config.js +66 -21
- package/dist/heart/core.js +518 -100
- package/dist/heart/cross-chat-delivery.js +146 -0
- package/dist/heart/daemon/agent-discovery.js +81 -0
- package/dist/heart/daemon/auth-flow.js +457 -0
- package/dist/heart/daemon/daemon-cli.js +1516 -195
- package/dist/heart/daemon/daemon-entry.js +43 -2
- package/dist/heart/daemon/daemon-runtime-sync.js +212 -0
- package/dist/heart/daemon/daemon.js +261 -1
- package/dist/heart/daemon/hatch-animation.js +10 -3
- package/dist/heart/daemon/hatch-flow.js +7 -72
- package/dist/heart/daemon/hooks/bundle-meta.js +92 -0
- package/dist/heart/daemon/launchd.js +159 -0
- package/dist/heart/daemon/log-tailer.js +4 -3
- package/dist/heart/daemon/message-router.js +17 -8
- package/dist/heart/daemon/ouro-bot-global-installer.js +128 -0
- package/dist/heart/daemon/ouro-path-installer.js +57 -29
- package/dist/heart/daemon/ouro-version-manager.js +171 -0
- package/dist/heart/daemon/process-manager.js +13 -0
- package/dist/heart/daemon/run-hooks.js +37 -0
- package/dist/heart/daemon/runtime-logging.js +58 -15
- package/dist/heart/daemon/runtime-metadata.js +219 -0
- package/dist/heart/daemon/runtime-mode.js +67 -0
- package/dist/heart/daemon/sense-manager.js +50 -2
- package/dist/heart/daemon/skill-management-installer.js +94 -0
- package/dist/heart/daemon/socket-client.js +202 -0
- package/dist/heart/daemon/specialist-orchestrator.js +2 -2
- package/dist/heart/daemon/specialist-prompt.js +7 -4
- package/dist/heart/daemon/specialist-tools.js +52 -3
- package/dist/heart/daemon/staged-restart.js +114 -0
- package/dist/heart/daemon/thoughts.js +507 -0
- package/dist/heart/daemon/update-checker.js +111 -0
- package/dist/heart/daemon/update-hooks.js +138 -0
- package/dist/heart/daemon/wrapper-publish-guard.js +86 -0
- package/dist/heart/delegation.js +62 -0
- package/dist/heart/identity.js +64 -21
- package/dist/heart/kicks.js +1 -19
- package/dist/heart/model-capabilities.js +48 -0
- package/dist/heart/obligations.js +197 -0
- package/dist/heart/progress-story.js +42 -0
- package/dist/heart/provider-failover.js +88 -0
- package/dist/heart/provider-ping.js +159 -0
- package/dist/heart/providers/anthropic-token.js +163 -0
- package/dist/heart/providers/anthropic.js +195 -34
- package/dist/heart/providers/azure.js +115 -9
- package/dist/heart/providers/github-copilot.js +157 -0
- package/dist/heart/providers/minimax.js +33 -3
- package/dist/heart/providers/openai-codex.js +49 -14
- package/dist/heart/safe-workspace.js +381 -0
- package/dist/heart/session-activity.js +173 -0
- package/dist/heart/session-recall.js +216 -0
- package/dist/heart/streaming.js +108 -24
- package/dist/heart/target-resolution.js +123 -0
- package/dist/heart/tool-loop.js +194 -0
- package/dist/heart/turn-coordinator.js +28 -0
- package/dist/mind/associative-recall.js +14 -2
- package/dist/mind/bundle-manifest.js +12 -0
- package/dist/mind/context.js +60 -14
- package/dist/mind/first-impressions.js +16 -2
- package/dist/mind/friends/channel.js +35 -0
- package/dist/mind/friends/group-context.js +144 -0
- package/dist/mind/friends/store-file.js +19 -0
- package/dist/mind/friends/trust-explanation.js +74 -0
- package/dist/mind/friends/types.js +8 -0
- package/dist/mind/memory.js +27 -26
- package/dist/mind/obligation-steering.js +221 -0
- package/dist/mind/pending.js +76 -9
- package/dist/mind/phrases.js +1 -0
- package/dist/mind/prompt.js +456 -77
- package/dist/mind/token-estimate.js +8 -12
- package/dist/nerves/cli-logging.js +15 -2
- package/dist/nerves/coverage/run-artifacts.js +1 -1
- package/dist/nerves/index.js +12 -0
- package/dist/nerves/runtime.js +5 -1
- package/dist/repertoire/ado-client.js +4 -2
- package/dist/repertoire/coding/context-pack.js +254 -0
- package/dist/repertoire/coding/feedback.js +301 -0
- package/dist/repertoire/coding/index.js +4 -1
- package/dist/repertoire/coding/manager.js +210 -4
- package/dist/repertoire/coding/spawner.js +39 -9
- package/dist/repertoire/coding/tools.js +171 -4
- package/dist/repertoire/data/ado-endpoints.json +188 -0
- package/dist/repertoire/guardrails.js +290 -0
- package/dist/repertoire/mcp-client.js +254 -0
- package/dist/repertoire/mcp-manager.js +198 -0
- package/dist/repertoire/skills.js +3 -26
- package/dist/repertoire/tasks/board.js +12 -0
- package/dist/repertoire/tasks/index.js +23 -9
- package/dist/repertoire/tasks/transitions.js +1 -2
- package/dist/repertoire/tools-base.js +925 -250
- package/dist/repertoire/tools-bluebubbles.js +93 -0
- package/dist/repertoire/tools-teams.js +58 -25
- package/dist/repertoire/tools.js +106 -53
- package/dist/senses/bluebubbles-client.js +210 -5
- package/dist/senses/bluebubbles-entry.js +2 -0
- package/dist/senses/bluebubbles-inbound-log.js +109 -0
- package/dist/senses/bluebubbles-media.js +339 -0
- package/dist/senses/bluebubbles-model.js +12 -4
- package/dist/senses/bluebubbles-mutation-log.js +45 -5
- package/dist/senses/bluebubbles-runtime-state.js +109 -0
- package/dist/senses/bluebubbles-session-cleanup.js +72 -0
- package/dist/senses/bluebubbles.js +915 -45
- package/dist/senses/cli-layout.js +187 -0
- package/dist/senses/cli.js +374 -131
- package/dist/senses/continuity.js +94 -0
- package/dist/senses/debug-activity.js +154 -0
- package/dist/senses/inner-dialog-worker.js +47 -18
- package/dist/senses/inner-dialog.js +388 -83
- package/dist/senses/pipeline.js +444 -0
- package/dist/senses/teams.js +607 -129
- package/dist/senses/trust-gate.js +112 -2
- package/package.json +9 -3
- package/subagents/README.md +4 -70
- package/dist/heart/daemon/subagent-installer.js +0 -134
- package/subagents/work-doer.md +0 -233
- package/subagents/work-merger.md +0 -624
- 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(
|
|
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.
|
|
455
|
-
stderrTail: normalizedSession.
|
|
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
|
-
|
|
70
|
-
|
|
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.
|
|
124
|
+
proc.stdin.end(`${prompt}\n`);
|
|
95
125
|
(0, runtime_1.emitNervesEvent)({
|
|
96
126
|
component: "repertoire",
|
|
97
127
|
event: "repertoire.coding_spawn_end",
|