@ouro.bot/cli 0.1.0-alpha.7 → 0.1.0-alpha.70
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/agent.json +70 -9
- package/AdoptionSpecialist.ouro/psyche/SOUL.md +5 -2
- package/AdoptionSpecialist.ouro/psyche/identities/monty.md +2 -2
- package/README.md +147 -205
- package/assets/ouroboros.png +0 -0
- package/changelog.json +387 -0
- package/dist/heart/active-work.js +178 -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/config.js +68 -23
- package/dist/heart/core.js +282 -92
- 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 +409 -0
- package/dist/heart/daemon/daemon-cli.js +1395 -248
- package/dist/heart/daemon/daemon-entry.js +55 -6
- package/dist/heart/daemon/daemon-runtime-sync.js +212 -0
- package/dist/heart/daemon/daemon.js +216 -10
- package/dist/heart/daemon/hatch-animation.js +10 -3
- package/dist/heart/daemon/hatch-flow.js +7 -82
- 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-entry.js +0 -0
- package/dist/heart/daemon/ouro-bot-global-installer.js +128 -0
- package/dist/heart/daemon/ouro-entry.js +0 -0
- package/dist/heart/daemon/ouro-path-installer.js +178 -0
- package/dist/heart/daemon/ouro-uti.js +11 -2
- package/dist/heart/daemon/process-manager.js +14 -1
- 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 +307 -0
- 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 +53 -84
- package/dist/heart/daemon/specialist-prompt.js +64 -5
- package/dist/heart/daemon/specialist-tools.js +213 -58
- package/dist/heart/daemon/staged-restart.js +114 -0
- package/dist/heart/daemon/thoughts.js +379 -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 +126 -21
- package/dist/heart/kicks.js +1 -19
- package/dist/heart/model-capabilities.js +48 -0
- package/dist/heart/progress-story.js +42 -0
- package/dist/heart/providers/anthropic.js +74 -9
- package/dist/heart/providers/azure.js +86 -7
- package/dist/heart/providers/github-copilot.js +149 -0
- package/dist/heart/providers/minimax.js +4 -0
- package/dist/heart/providers/openai-codex.js +12 -3
- package/dist/heart/safe-workspace.js +228 -0
- package/dist/heart/sense-truth.js +61 -0
- package/dist/heart/session-activity.js +169 -0
- package/dist/heart/session-recall.js +116 -0
- package/dist/heart/streaming.js +100 -22
- package/dist/heart/target-resolution.js +123 -0
- package/dist/heart/turn-coordinator.js +28 -0
- package/dist/mind/associative-recall.js +14 -2
- package/dist/mind/bundle-manifest.js +70 -0
- package/dist/mind/context.js +27 -11
- 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/pending.js +72 -9
- package/dist/mind/phrases.js +1 -0
- package/dist/mind/prompt.js +358 -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/repertoire/ado-client.js +4 -2
- package/dist/repertoire/coding/feedback.js +134 -0
- package/dist/repertoire/coding/index.js +4 -1
- package/dist/repertoire/coding/manager.js +62 -4
- package/dist/repertoire/coding/spawner.js +3 -3
- package/dist/repertoire/coding/tools.js +41 -2
- package/dist/repertoire/data/ado-endpoints.json +188 -0
- package/dist/repertoire/guardrails.js +279 -0
- package/dist/repertoire/mcp-client.js +254 -0
- package/dist/repertoire/mcp-manager.js +195 -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 +642 -251
- package/dist/repertoire/tools-bluebubbles.js +93 -0
- package/dist/repertoire/tools-teams.js +58 -25
- package/dist/repertoire/tools.js +93 -52
- 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 +893 -45
- package/dist/senses/cli-layout.js +87 -0
- package/dist/senses/cli.js +348 -144
- package/dist/senses/continuity.js +94 -0
- package/dist/senses/debug-activity.js +148 -0
- package/dist/senses/inner-dialog-worker.js +47 -18
- package/dist/senses/inner-dialog.js +333 -84
- package/dist/senses/pipeline.js +278 -0
- package/dist/senses/teams.js +573 -129
- package/dist/senses/trust-gate.js +112 -2
- package/package.json +14 -3
- package/subagents/README.md +4 -70
- package/dist/heart/daemon/specialist-session.js +0 -142
- package/dist/heart/daemon/subagent-installer.js +0 -125
- package/dist/inner-worker-entry.js +0 -4
- package/subagents/work-doer.md +0 -233
- package/subagents/work-merger.md +0 -624
- package/subagents/work-planner.md +0 -373
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.formatCodingTail = formatCodingTail;
|
|
4
|
+
exports.attachCodingSessionFeedback = attachCodingSessionFeedback;
|
|
5
|
+
const runtime_1 = require("../../nerves/runtime");
|
|
6
|
+
const TERMINAL_UPDATE_KINDS = new Set(["completed", "failed", "killed"]);
|
|
7
|
+
function clip(text, maxLength = 280) {
|
|
8
|
+
const trimmed = text.trim();
|
|
9
|
+
if (trimmed.length <= maxLength)
|
|
10
|
+
return trimmed;
|
|
11
|
+
return `${trimmed.slice(0, maxLength - 3)}...`;
|
|
12
|
+
}
|
|
13
|
+
function isNoiseLine(line) {
|
|
14
|
+
return (/^-+$/.test(line)
|
|
15
|
+
|| /^Reading prompt from stdin/i.test(line)
|
|
16
|
+
|| /^OpenAI Codex v/i.test(line)
|
|
17
|
+
|| /^workdir:/i.test(line)
|
|
18
|
+
|| /^model:/i.test(line)
|
|
19
|
+
|| /^provider:/i.test(line)
|
|
20
|
+
|| /^approval:/i.test(line)
|
|
21
|
+
|| /^sandbox:/i.test(line)
|
|
22
|
+
|| /^reasoning effort:/i.test(line)
|
|
23
|
+
|| /^reasoning summaries:/i.test(line)
|
|
24
|
+
|| /^session id:/i.test(line)
|
|
25
|
+
|| /^mcp startup:/i.test(line)
|
|
26
|
+
|| /^tokens used$/i.test(line)
|
|
27
|
+
|| /^\d{1,3}(,\d{3})*$/.test(line)
|
|
28
|
+
|| /^\d{4}-\d{2}-\d{2}T.*\bWARN\b/.test(line)
|
|
29
|
+
|| line === "user"
|
|
30
|
+
|| line === "codex");
|
|
31
|
+
}
|
|
32
|
+
function lastMeaningfulLine(text) {
|
|
33
|
+
if (!text)
|
|
34
|
+
return null;
|
|
35
|
+
const lines = text
|
|
36
|
+
.split(/\r?\n/)
|
|
37
|
+
.map((line) => line.trim())
|
|
38
|
+
.filter(Boolean)
|
|
39
|
+
.filter((line) => !isNoiseLine(line));
|
|
40
|
+
if (lines.length === 0)
|
|
41
|
+
return null;
|
|
42
|
+
return clip(lines.at(-1));
|
|
43
|
+
}
|
|
44
|
+
function formatSessionLabel(session) {
|
|
45
|
+
return `${session.runner} ${session.id}`;
|
|
46
|
+
}
|
|
47
|
+
function isSafeProgressSnippet(snippet) {
|
|
48
|
+
const wordCount = snippet.split(/\s+/).filter(Boolean).length;
|
|
49
|
+
return (snippet.length <= 80
|
|
50
|
+
&& 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));
|
|
58
|
+
}
|
|
59
|
+
function pickUpdateSnippet(update) {
|
|
60
|
+
return (lastMeaningfulLine(update.text)
|
|
61
|
+
?? lastMeaningfulLine(update.session.stderrTail)
|
|
62
|
+
?? lastMeaningfulLine(update.session.stdoutTail));
|
|
63
|
+
}
|
|
64
|
+
function formatUpdateMessage(update) {
|
|
65
|
+
const label = formatSessionLabel(update.session);
|
|
66
|
+
const snippet = pickUpdateSnippet(update);
|
|
67
|
+
switch (update.kind) {
|
|
68
|
+
case "progress":
|
|
69
|
+
return snippet && isSafeProgressSnippet(snippet) ? `${label}: ${snippet}` : null;
|
|
70
|
+
case "waiting_input":
|
|
71
|
+
return snippet ? `${label} waiting: ${snippet}` : `${label} waiting`;
|
|
72
|
+
case "stalled":
|
|
73
|
+
return snippet ? `${label} stalled: ${snippet}` : `${label} stalled`;
|
|
74
|
+
case "completed":
|
|
75
|
+
return snippet ? `${label} completed: ${snippet}` : `${label} completed`;
|
|
76
|
+
case "failed":
|
|
77
|
+
return snippet ? `${label} failed: ${snippet}` : `${label} failed`;
|
|
78
|
+
case "killed":
|
|
79
|
+
return `${label} killed`;
|
|
80
|
+
case "spawned":
|
|
81
|
+
return `${label} started`;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
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");
|
|
99
|
+
}
|
|
100
|
+
function attachCodingSessionFeedback(manager, session, target) {
|
|
101
|
+
let lastMessage = "";
|
|
102
|
+
let closed = false;
|
|
103
|
+
let unsubscribe = () => { };
|
|
104
|
+
const sendMessage = (message) => {
|
|
105
|
+
if (closed || !message || message === lastMessage) {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
lastMessage = message;
|
|
109
|
+
void Promise.resolve(target.send(message)).catch((error) => {
|
|
110
|
+
(0, runtime_1.emitNervesEvent)({
|
|
111
|
+
level: "warn",
|
|
112
|
+
component: "repertoire",
|
|
113
|
+
event: "repertoire.coding_feedback_error",
|
|
114
|
+
message: "coding feedback transport failed",
|
|
115
|
+
meta: {
|
|
116
|
+
sessionId: session.id,
|
|
117
|
+
reason: error instanceof Error ? error.message : String(error),
|
|
118
|
+
},
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
};
|
|
122
|
+
sendMessage(formatUpdateMessage({ kind: "spawned", session }));
|
|
123
|
+
unsubscribe = manager.subscribe(session.id, async (update) => {
|
|
124
|
+
sendMessage(formatUpdateMessage(update));
|
|
125
|
+
if (TERMINAL_UPDATE_KINDS.has(update.kind)) {
|
|
126
|
+
closed = true;
|
|
127
|
+
unsubscribe();
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
return () => {
|
|
131
|
+
closed = true;
|
|
132
|
+
unsubscribe();
|
|
133
|
+
};
|
|
134
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.formatCodingMonitorReport = exports.CodingSessionMonitor = exports.CodingSessionManager = void 0;
|
|
3
|
+
exports.formatCodingTail = exports.attachCodingSessionFeedback = exports.formatCodingMonitorReport = exports.CodingSessionMonitor = exports.CodingSessionManager = void 0;
|
|
4
4
|
exports.getCodingSessionManager = getCodingSessionManager;
|
|
5
5
|
exports.resetCodingSessionManager = resetCodingSessionManager;
|
|
6
6
|
const runtime_1 = require("../../nerves/runtime");
|
|
@@ -34,3 +34,6 @@ var monitor_1 = require("./monitor");
|
|
|
34
34
|
Object.defineProperty(exports, "CodingSessionMonitor", { enumerable: true, get: function () { return monitor_1.CodingSessionMonitor; } });
|
|
35
35
|
var reporter_1 = require("./reporter");
|
|
36
36
|
Object.defineProperty(exports, "formatCodingMonitorReport", { enumerable: true, get: function () { return reporter_1.formatCodingMonitorReport; } });
|
|
37
|
+
var feedback_1 = require("./feedback");
|
|
38
|
+
Object.defineProperty(exports, "attachCodingSessionFeedback", { enumerable: true, get: function () { return feedback_1.attachCodingSessionFeedback; } });
|
|
39
|
+
Object.defineProperty(exports, "formatCodingTail", { enumerable: true, get: function () { return feedback_1.formatCodingTail; } });
|
|
@@ -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,7 @@ 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");
|
|
53
52
|
}
|
|
54
53
|
function isPidAlive(pid) {
|
|
55
54
|
try {
|
|
@@ -63,6 +62,8 @@ function isPidAlive(pid) {
|
|
|
63
62
|
function cloneSession(session) {
|
|
64
63
|
return {
|
|
65
64
|
...session,
|
|
65
|
+
stdoutTail: session.stdoutTail,
|
|
66
|
+
stderrTail: session.stderrTail,
|
|
66
67
|
failure: session.failure
|
|
67
68
|
? {
|
|
68
69
|
...session.failure,
|
|
@@ -115,6 +116,7 @@ function defaultFailureDiagnostics(code, signal, command, args, stdoutTail, stde
|
|
|
115
116
|
}
|
|
116
117
|
class CodingSessionManager {
|
|
117
118
|
records = new Map();
|
|
119
|
+
listeners = new Map();
|
|
118
120
|
spawnProcess;
|
|
119
121
|
nowIso;
|
|
120
122
|
maxRestarts;
|
|
@@ -158,6 +160,8 @@ class CodingSessionManager {
|
|
|
158
160
|
scopeFile: normalizedRequest.scopeFile,
|
|
159
161
|
stateFile: normalizedRequest.stateFile,
|
|
160
162
|
status: "spawning",
|
|
163
|
+
stdoutTail: "",
|
|
164
|
+
stderrTail: "",
|
|
161
165
|
pid: null,
|
|
162
166
|
startedAt: now,
|
|
163
167
|
lastActivityAt: now,
|
|
@@ -188,6 +192,7 @@ class CodingSessionManager {
|
|
|
188
192
|
meta: { id, runner: normalizedRequest.runner, pid: session.pid },
|
|
189
193
|
});
|
|
190
194
|
this.persistState();
|
|
195
|
+
this.notifyListeners(id, { kind: "spawned", session: cloneSession(session) });
|
|
191
196
|
return cloneSession(session);
|
|
192
197
|
}
|
|
193
198
|
listSessions() {
|
|
@@ -199,6 +204,20 @@ class CodingSessionManager {
|
|
|
199
204
|
const record = this.records.get(sessionId);
|
|
200
205
|
return record ? cloneSession(record.session) : null;
|
|
201
206
|
}
|
|
207
|
+
subscribe(sessionId, listener) {
|
|
208
|
+
const listeners = this.listeners.get(sessionId) ?? new Set();
|
|
209
|
+
listeners.add(listener);
|
|
210
|
+
this.listeners.set(sessionId, listeners);
|
|
211
|
+
return () => {
|
|
212
|
+
const current = this.listeners.get(sessionId);
|
|
213
|
+
if (!current)
|
|
214
|
+
return;
|
|
215
|
+
current.delete(listener);
|
|
216
|
+
if (current.size === 0) {
|
|
217
|
+
this.listeners.delete(sessionId);
|
|
218
|
+
}
|
|
219
|
+
};
|
|
220
|
+
}
|
|
202
221
|
sendInput(sessionId, input) {
|
|
203
222
|
const record = this.records.get(sessionId);
|
|
204
223
|
if (!record || !record.process) {
|
|
@@ -234,6 +253,7 @@ class CodingSessionManager {
|
|
|
234
253
|
meta: { id: sessionId },
|
|
235
254
|
});
|
|
236
255
|
this.persistState();
|
|
256
|
+
this.notifyListeners(sessionId, { kind: "killed", session: cloneSession(record.session) });
|
|
237
257
|
return { ok: true, message: `killed ${sessionId}` };
|
|
238
258
|
}
|
|
239
259
|
checkStalls(nowMs = Date.now()) {
|
|
@@ -254,6 +274,7 @@ class CodingSessionManager {
|
|
|
254
274
|
message: "coding session stalled",
|
|
255
275
|
meta: { id: record.session.id, elapsedMs: elapsed },
|
|
256
276
|
});
|
|
277
|
+
this.notifyListeners(record.session.id, { kind: "stalled", session: cloneSession(record.session) });
|
|
257
278
|
if (record.request.autoRestartOnStall !== false && record.session.restartCount < this.maxRestarts) {
|
|
258
279
|
this.restartSession(record, "stalled");
|
|
259
280
|
}
|
|
@@ -297,18 +318,23 @@ class CodingSessionManager {
|
|
|
297
318
|
}
|
|
298
319
|
onOutput(record, text, stream) {
|
|
299
320
|
record.session.lastActivityAt = this.nowIso();
|
|
321
|
+
let updateKind = "progress";
|
|
300
322
|
if (stream === "stdout") {
|
|
301
323
|
record.stdoutTail = appendTail(record.stdoutTail, text);
|
|
324
|
+
record.session.stdoutTail = record.stdoutTail;
|
|
302
325
|
}
|
|
303
326
|
else {
|
|
304
327
|
record.stderrTail = appendTail(record.stderrTail, text);
|
|
328
|
+
record.session.stderrTail = record.stderrTail;
|
|
305
329
|
}
|
|
306
330
|
if (text.includes("status: NEEDS_REVIEW") || text.includes("❌ blocked")) {
|
|
307
331
|
record.session.status = "waiting_input";
|
|
332
|
+
updateKind = "waiting_input";
|
|
308
333
|
}
|
|
309
334
|
if (text.includes("✅ all units complete")) {
|
|
310
335
|
record.session.status = "completed";
|
|
311
336
|
record.session.endedAt = this.nowIso();
|
|
337
|
+
updateKind = "completed";
|
|
312
338
|
}
|
|
313
339
|
(0, runtime_1.emitNervesEvent)({
|
|
314
340
|
component: "repertoire",
|
|
@@ -317,6 +343,12 @@ class CodingSessionManager {
|
|
|
317
343
|
meta: { id: record.session.id, status: record.session.status },
|
|
318
344
|
});
|
|
319
345
|
this.persistState();
|
|
346
|
+
this.notifyListeners(record.session.id, {
|
|
347
|
+
kind: updateKind,
|
|
348
|
+
session: cloneSession(record.session),
|
|
349
|
+
stream,
|
|
350
|
+
text,
|
|
351
|
+
});
|
|
320
352
|
}
|
|
321
353
|
onExit(record, code, signal) {
|
|
322
354
|
if (!record.process)
|
|
@@ -334,6 +366,7 @@ class CodingSessionManager {
|
|
|
334
366
|
record.session.status = "completed";
|
|
335
367
|
record.session.endedAt = this.nowIso();
|
|
336
368
|
this.persistState();
|
|
369
|
+
this.notifyListeners(record.session.id, { kind: "completed", session: cloneSession(record.session) });
|
|
337
370
|
return;
|
|
338
371
|
}
|
|
339
372
|
if (record.request.autoRestartOnCrash !== false && record.session.restartCount < this.maxRestarts) {
|
|
@@ -351,6 +384,7 @@ class CodingSessionManager {
|
|
|
351
384
|
meta: { id: record.session.id, code, signal, command: record.command },
|
|
352
385
|
});
|
|
353
386
|
this.persistState();
|
|
387
|
+
this.notifyListeners(record.session.id, { kind: "failed", session: cloneSession(record.session) });
|
|
354
388
|
}
|
|
355
389
|
restartSession(record, reason) {
|
|
356
390
|
const replacement = normalizeSpawnResult(this.spawnProcess(record.request));
|
|
@@ -359,6 +393,8 @@ class CodingSessionManager {
|
|
|
359
393
|
record.args = [...replacement.args];
|
|
360
394
|
record.stdoutTail = "";
|
|
361
395
|
record.stderrTail = "";
|
|
396
|
+
record.session.stdoutTail = "";
|
|
397
|
+
record.session.stderrTail = "";
|
|
362
398
|
record.session.pid = replacement.process.pid ?? null;
|
|
363
399
|
record.session.restartCount += 1;
|
|
364
400
|
record.session.status = "running";
|
|
@@ -375,6 +411,26 @@ class CodingSessionManager {
|
|
|
375
411
|
});
|
|
376
412
|
this.persistState();
|
|
377
413
|
}
|
|
414
|
+
notifyListeners(sessionId, update) {
|
|
415
|
+
const listeners = this.listeners.get(sessionId);
|
|
416
|
+
if (!listeners || listeners.size === 0)
|
|
417
|
+
return;
|
|
418
|
+
for (const listener of listeners) {
|
|
419
|
+
void Promise.resolve(listener(update)).catch((error) => {
|
|
420
|
+
(0, runtime_1.emitNervesEvent)({
|
|
421
|
+
level: "warn",
|
|
422
|
+
component: "repertoire",
|
|
423
|
+
event: "repertoire.coding_feedback_listener_error",
|
|
424
|
+
message: "coding session listener failed",
|
|
425
|
+
meta: {
|
|
426
|
+
sessionId,
|
|
427
|
+
kind: update.kind,
|
|
428
|
+
reason: error instanceof Error ? error.message : String(error),
|
|
429
|
+
},
|
|
430
|
+
});
|
|
431
|
+
});
|
|
432
|
+
}
|
|
433
|
+
}
|
|
378
434
|
loadPersistedState() {
|
|
379
435
|
if (!this.existsSync(this.stateFilePath)) {
|
|
380
436
|
return;
|
|
@@ -433,6 +489,8 @@ class CodingSessionManager {
|
|
|
433
489
|
...session,
|
|
434
490
|
taskRef: session.taskRef ?? normalizedRequest.taskRef,
|
|
435
491
|
failure: session.failure ?? null,
|
|
492
|
+
stdoutTail: session.stdoutTail ?? session.failure?.stdoutTail ?? "",
|
|
493
|
+
stderrTail: session.stderrTail ?? session.failure?.stderrTail ?? "",
|
|
436
494
|
};
|
|
437
495
|
if (typeof normalizedSession.pid === "number") {
|
|
438
496
|
const alive = this.pidAlive(normalizedSession.pid);
|
|
@@ -451,8 +509,8 @@ class CodingSessionManager {
|
|
|
451
509
|
process: null,
|
|
452
510
|
command: normalizedSession.failure?.command ?? "restored",
|
|
453
511
|
args: normalizedSession.failure ? [...normalizedSession.failure.args] : [],
|
|
454
|
-
stdoutTail: normalizedSession.
|
|
455
|
-
stderrTail: normalizedSession.
|
|
512
|
+
stdoutTail: normalizedSession.stdoutTail,
|
|
513
|
+
stderrTail: normalizedSession.stderrTail,
|
|
456
514
|
});
|
|
457
515
|
this.sequence = Math.max(this.sequence, extractSequence(normalizedSession.id));
|
|
458
516
|
}
|
|
@@ -43,11 +43,11 @@ function buildCommandArgs(runner, workdir) {
|
|
|
43
43
|
command: "claude",
|
|
44
44
|
args: [
|
|
45
45
|
"-p",
|
|
46
|
+
"--verbose",
|
|
47
|
+
"--no-session-persistence",
|
|
46
48
|
"--dangerously-skip-permissions",
|
|
47
49
|
"--add-dir",
|
|
48
50
|
workdir,
|
|
49
|
-
"--input-format",
|
|
50
|
-
"stream-json",
|
|
51
51
|
"--output-format",
|
|
52
52
|
"stream-json",
|
|
53
53
|
],
|
|
@@ -91,7 +91,7 @@ function spawnCodingProcess(request, deps = {}) {
|
|
|
91
91
|
cwd: request.workdir,
|
|
92
92
|
stdio: ["pipe", "pipe", "pipe"],
|
|
93
93
|
});
|
|
94
|
-
proc.stdin.
|
|
94
|
+
proc.stdin.end(`${prompt}\n`);
|
|
95
95
|
(0, runtime_1.emitNervesEvent)({
|
|
96
96
|
component: "repertoire",
|
|
97
97
|
event: "repertoire.coding_spawn_end",
|
|
@@ -61,6 +61,20 @@ const codingStatusTool = {
|
|
|
61
61
|
},
|
|
62
62
|
},
|
|
63
63
|
};
|
|
64
|
+
const codingTailTool = {
|
|
65
|
+
type: "function",
|
|
66
|
+
function: {
|
|
67
|
+
name: "coding_tail",
|
|
68
|
+
description: "show recent stdout/stderr tail for a coding session in a readable format",
|
|
69
|
+
parameters: {
|
|
70
|
+
type: "object",
|
|
71
|
+
properties: {
|
|
72
|
+
sessionId: { type: "string" },
|
|
73
|
+
},
|
|
74
|
+
required: ["sessionId"],
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
};
|
|
64
78
|
const codingSendInputTool = {
|
|
65
79
|
type: "function",
|
|
66
80
|
function: {
|
|
@@ -93,7 +107,7 @@ const codingKillTool = {
|
|
|
93
107
|
exports.codingToolDefinitions = [
|
|
94
108
|
{
|
|
95
109
|
tool: codingSpawnTool,
|
|
96
|
-
handler: async (args) => {
|
|
110
|
+
handler: async (args, ctx) => {
|
|
97
111
|
emitCodingToolEvent("coding_spawn");
|
|
98
112
|
const rawRunner = requireArg(args, "runner");
|
|
99
113
|
if (!rawRunner)
|
|
@@ -122,7 +136,19 @@ exports.codingToolDefinitions = [
|
|
|
122
136
|
const stateFile = optionalArg(args, "stateFile");
|
|
123
137
|
if (stateFile)
|
|
124
138
|
request.stateFile = stateFile;
|
|
125
|
-
const
|
|
139
|
+
const manager = (0, index_1.getCodingSessionManager)();
|
|
140
|
+
const session = await manager.spawnSession(request);
|
|
141
|
+
if (args.runner === "codex" && args.taskRef) {
|
|
142
|
+
(0, runtime_1.emitNervesEvent)({
|
|
143
|
+
component: "repertoire",
|
|
144
|
+
event: "repertoire.coding_codex_spawned",
|
|
145
|
+
message: "spawned codex coding session",
|
|
146
|
+
meta: { sessionId: session.id, taskRef: args.taskRef },
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
if (ctx?.codingFeedback) {
|
|
150
|
+
(0, index_1.attachCodingSessionFeedback)(manager, session, ctx.codingFeedback);
|
|
151
|
+
}
|
|
126
152
|
return JSON.stringify(session);
|
|
127
153
|
},
|
|
128
154
|
},
|
|
@@ -141,6 +167,19 @@ exports.codingToolDefinitions = [
|
|
|
141
167
|
return JSON.stringify(session);
|
|
142
168
|
},
|
|
143
169
|
},
|
|
170
|
+
{
|
|
171
|
+
tool: codingTailTool,
|
|
172
|
+
handler: (args) => {
|
|
173
|
+
emitCodingToolEvent("coding_tail");
|
|
174
|
+
const sessionId = requireArg(args, "sessionId");
|
|
175
|
+
if (!sessionId)
|
|
176
|
+
return "sessionId is required";
|
|
177
|
+
const session = (0, index_1.getCodingSessionManager)().getSession(sessionId);
|
|
178
|
+
if (!session)
|
|
179
|
+
return `session not found: ${sessionId}`;
|
|
180
|
+
return (0, index_1.formatCodingTail)(session);
|
|
181
|
+
},
|
|
182
|
+
},
|
|
144
183
|
{
|
|
145
184
|
tool: codingSendInputTool,
|
|
146
185
|
handler: (args) => {
|
|
@@ -35,6 +35,12 @@
|
|
|
35
35
|
"description": "Delete a work item (moves to recycle bin)",
|
|
36
36
|
"params": "destroy (boolean, permanently delete)"
|
|
37
37
|
},
|
|
38
|
+
{
|
|
39
|
+
"path": "/{project}/_apis/wit/workitemtypes",
|
|
40
|
+
"method": "GET",
|
|
41
|
+
"description": "List all work item types available in a project (Bug, Task, Epic, User Story, etc.)",
|
|
42
|
+
"params": ""
|
|
43
|
+
},
|
|
38
44
|
{
|
|
39
45
|
"path": "/_apis/git/repositories",
|
|
40
46
|
"method": "GET",
|
|
@@ -118,5 +124,187 @@
|
|
|
118
124
|
"method": "GET",
|
|
119
125
|
"description": "List saved work item queries (shared and personal)",
|
|
120
126
|
"params": "$depth, $expand"
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
"path": "/_apis/groupentitlements?api-version=7.1",
|
|
130
|
+
"method": "GET",
|
|
131
|
+
"host": "vsaex.dev.azure.com",
|
|
132
|
+
"description": "List group entitlements (group rules that auto-assign licenses). Use host vsaex.dev.azure.com.",
|
|
133
|
+
"params": ""
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
"path": "/_apis/groupentitlements?api-version=7.1",
|
|
137
|
+
"method": "POST",
|
|
138
|
+
"host": "vsaex.dev.azure.com",
|
|
139
|
+
"description": "Create a group entitlement rule — maps an AAD group to an access level (e.g. Basic) and project membership. All members of the AAD group automatically get the specified license. Use host vsaex.dev.azure.com. This is the best way to bulk-provision users.",
|
|
140
|
+
"params": "body: { group: { origin: 'aad', originId: '<AAD-group-object-id>', subjectKind: 'group' }, licenseRule: { licensingSource: 'account', accountLicenseType: 'express', licenseDisplayName: 'Basic' }, projectEntitlements: [{ group: { groupType: 'projectContributor' }, projectRef: { id: '<project-id>' } }] }"
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
"path": "/_apis/groupentitlements/{groupId}?api-version=7.1",
|
|
144
|
+
"method": "GET",
|
|
145
|
+
"host": "vsaex.dev.azure.com",
|
|
146
|
+
"description": "Get a specific group entitlement by ID. Use host vsaex.dev.azure.com.",
|
|
147
|
+
"params": ""
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
"path": "/_apis/groupentitlements/{groupId}?api-version=7.1",
|
|
151
|
+
"method": "PATCH",
|
|
152
|
+
"host": "vsaex.dev.azure.com",
|
|
153
|
+
"description": "Update a group entitlement (change license rule, project access). Use host vsaex.dev.azure.com.",
|
|
154
|
+
"params": "JSON Patch array: [{op, path, value}]"
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
"path": "/_apis/groupentitlements/{groupId}?api-version=7.1",
|
|
158
|
+
"method": "DELETE",
|
|
159
|
+
"host": "vsaex.dev.azure.com",
|
|
160
|
+
"description": "Delete a group entitlement rule. Use host vsaex.dev.azure.com.",
|
|
161
|
+
"params": ""
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
"path": "/_apis/memberentitlementmanagement/memberentitlements?api-version=7.1-preview.3",
|
|
165
|
+
"method": "GET",
|
|
166
|
+
"host": "vsapm.dev.azure.com",
|
|
167
|
+
"description": "List individual member entitlements (users and their access levels). Use host vsapm.dev.azure.com. For bulk provisioning, prefer the Group Entitlements API on vsaex.dev.azure.com instead.",
|
|
168
|
+
"params": "$top, $skip, $filter, $orderBy, $select"
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
"path": "/_apis/memberentitlementmanagement/memberentitlements?api-version=7.1-preview.3",
|
|
172
|
+
"method": "POST",
|
|
173
|
+
"host": "vsapm.dev.azure.com",
|
|
174
|
+
"description": "Add a single member entitlement. Use host vsapm.dev.azure.com. For bulk provisioning, prefer the Group Entitlements API on vsaex.dev.azure.com instead.",
|
|
175
|
+
"params": "body: { accessLevel: { accountLicenseType: 'express'|'stakeholder', licensingSource: 'account' }, user: { principalName: 'user@domain.com', subjectKind: 'user' }, projectEntitlements: [{ group: { groupType: 'projectContributor' }, projectRef: { id: projectId } }] }"
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
"path": "/_apis/memberentitlementmanagement/memberentitlements/{memberId}?api-version=7.1-preview.3",
|
|
179
|
+
"method": "PATCH",
|
|
180
|
+
"host": "vsapm.dev.azure.com",
|
|
181
|
+
"description": "Update a member entitlement (change access level, project access). Use host vsapm.dev.azure.com.",
|
|
182
|
+
"params": "JSON Patch array: [{op, path, value}]"
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
"path": "/_apis/memberentitlementmanagement/memberentitlements/{memberId}?api-version=7.1-preview.3",
|
|
186
|
+
"method": "DELETE",
|
|
187
|
+
"host": "vsapm.dev.azure.com",
|
|
188
|
+
"description": "Remove a member entitlement (revoke user access). Use host vsapm.dev.azure.com.",
|
|
189
|
+
"params": ""
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
"path": "/_apis/graph/users?api-version=7.1-preview.1",
|
|
193
|
+
"method": "GET",
|
|
194
|
+
"host": "vssps.dev.azure.com",
|
|
195
|
+
"description": "List users in the organization (Graph API). Use host vssps.dev.azure.com. IMPORTANT: include the full path with api-version as shown.",
|
|
196
|
+
"params": "subjectTypes (aad, msa, etc.), continuationToken"
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
"path": "/_apis/graph/groups?api-version=7.1-preview.1",
|
|
200
|
+
"method": "GET",
|
|
201
|
+
"host": "vssps.dev.azure.com",
|
|
202
|
+
"description": "List groups in the organization. Use host vssps.dev.azure.com. IMPORTANT: include the full path with api-version as shown.",
|
|
203
|
+
"params": "subjectTypes, continuationToken"
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
"path": "/_apis/graph/memberships/{subjectDescriptor}?api-version=7.1-preview.1",
|
|
207
|
+
"method": "GET",
|
|
208
|
+
"host": "vssps.dev.azure.com",
|
|
209
|
+
"description": "List group memberships for a user or group. Use host vssps.dev.azure.com. IMPORTANT: include the full path with api-version as shown.",
|
|
210
|
+
"params": "direction (up = groups user belongs to, down = members of group)"
|
|
211
|
+
},
|
|
212
|
+
{
|
|
213
|
+
"path": "/_apis/graph/memberships/{subjectDescriptor}/{containerDescriptor}?api-version=7.1-preview.1",
|
|
214
|
+
"method": "PUT",
|
|
215
|
+
"host": "vssps.dev.azure.com",
|
|
216
|
+
"description": "Add a user to a group. Use host vssps.dev.azure.com. IMPORTANT: include the full path with api-version as shown.",
|
|
217
|
+
"params": ""
|
|
218
|
+
},
|
|
219
|
+
{
|
|
220
|
+
"path": "/_apis/graph/memberships/{subjectDescriptor}/{containerDescriptor}?api-version=7.1-preview.1",
|
|
221
|
+
"method": "DELETE",
|
|
222
|
+
"host": "vssps.dev.azure.com",
|
|
223
|
+
"description": "Remove a user from a group. Use host vssps.dev.azure.com. IMPORTANT: include the full path with api-version as shown.",
|
|
224
|
+
"params": ""
|
|
225
|
+
},
|
|
226
|
+
{
|
|
227
|
+
"path": "/_apis/projects/{projectId}/teams",
|
|
228
|
+
"method": "GET",
|
|
229
|
+
"description": "List teams in a project",
|
|
230
|
+
"params": "$top, $skip"
|
|
231
|
+
},
|
|
232
|
+
{
|
|
233
|
+
"path": "/_apis/projects/{projectId}/teams/{teamId}",
|
|
234
|
+
"method": "GET",
|
|
235
|
+
"description": "Get a specific team by ID",
|
|
236
|
+
"params": ""
|
|
237
|
+
},
|
|
238
|
+
{
|
|
239
|
+
"path": "/_apis/projects/{projectId}/teams",
|
|
240
|
+
"method": "POST",
|
|
241
|
+
"description": "Create a new team in a project",
|
|
242
|
+
"params": "name, description"
|
|
243
|
+
},
|
|
244
|
+
{
|
|
245
|
+
"path": "/_apis/projects/{projectId}/teams/{teamId}/members",
|
|
246
|
+
"method": "GET",
|
|
247
|
+
"description": "List members of a team",
|
|
248
|
+
"params": "$top, $skip"
|
|
249
|
+
},
|
|
250
|
+
{
|
|
251
|
+
"path": "/{project}/{team}/_apis/work/teamsettings/iterations",
|
|
252
|
+
"method": "GET",
|
|
253
|
+
"description": "List iterations (sprints) for a team",
|
|
254
|
+
"params": "$timeframe (current, past, future)"
|
|
255
|
+
},
|
|
256
|
+
{
|
|
257
|
+
"path": "/{project}/{team}/_apis/work/teamsettings/iterations",
|
|
258
|
+
"method": "POST",
|
|
259
|
+
"description": "Add an iteration to a team's sprint schedule",
|
|
260
|
+
"params": "id (iteration node ID)"
|
|
261
|
+
},
|
|
262
|
+
{
|
|
263
|
+
"path": "/{project}/{team}/_apis/work/teamsettings/iterations/{iterationId}",
|
|
264
|
+
"method": "DELETE",
|
|
265
|
+
"description": "Remove an iteration from a team's sprint schedule",
|
|
266
|
+
"params": ""
|
|
267
|
+
},
|
|
268
|
+
{
|
|
269
|
+
"path": "/{project}/_apis/wit/classificationnodes/iterations",
|
|
270
|
+
"method": "GET",
|
|
271
|
+
"description": "List iteration path tree (project-level iteration nodes)",
|
|
272
|
+
"params": "$depth"
|
|
273
|
+
},
|
|
274
|
+
{
|
|
275
|
+
"path": "/{project}/_apis/wit/classificationnodes/iterations",
|
|
276
|
+
"method": "POST",
|
|
277
|
+
"description": "Create a new iteration node (sprint)",
|
|
278
|
+
"params": "name, attributes: { startDate, finishDate }"
|
|
279
|
+
},
|
|
280
|
+
{
|
|
281
|
+
"path": "/{project}/_apis/wit/classificationnodes/areas",
|
|
282
|
+
"method": "GET",
|
|
283
|
+
"description": "List area path tree (project-level area nodes)",
|
|
284
|
+
"params": "$depth"
|
|
285
|
+
},
|
|
286
|
+
{
|
|
287
|
+
"path": "/{project}/_apis/wit/classificationnodes/areas",
|
|
288
|
+
"method": "POST",
|
|
289
|
+
"description": "Create a new area path node",
|
|
290
|
+
"params": "name"
|
|
291
|
+
},
|
|
292
|
+
{
|
|
293
|
+
"path": "/{project}/_apis/wit/classificationnodes/{structureGroup}/{path}",
|
|
294
|
+
"method": "DELETE",
|
|
295
|
+
"description": "Delete a classification node (area or iteration). structureGroup is 'areas' or 'iterations'.",
|
|
296
|
+
"params": "$reclassifyId (move items to this node before deleting)"
|
|
297
|
+
},
|
|
298
|
+
{
|
|
299
|
+
"path": "/_apis/hooks/subscriptions",
|
|
300
|
+
"method": "GET",
|
|
301
|
+
"description": "List service hook subscriptions (webhooks for events)",
|
|
302
|
+
"params": ""
|
|
303
|
+
},
|
|
304
|
+
{
|
|
305
|
+
"path": "/_apis/hooks/subscriptions",
|
|
306
|
+
"method": "POST",
|
|
307
|
+
"description": "Create a service hook subscription (webhook)",
|
|
308
|
+
"params": "publisherId, eventType, consumerId, consumerActionId, publisherInputs, consumerInputs"
|
|
121
309
|
}
|
|
122
310
|
]
|