@ouro.bot/cli 0.1.0-alpha.139 → 0.1.0-alpha.140
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/changelog.json +12 -0
- package/dist/heart/daemon/agent-service.js +467 -0
- package/dist/heart/daemon/daemon-cli.js +54 -0
- package/dist/heart/daemon/daemon.js +27 -0
- package/dist/heart/daemon/mcp-server.js +464 -0
- package/dist/mind/prompt.js +2 -0
- package/dist/repertoire/coding/codex-jsonl.js +64 -0
- package/dist/repertoire/coding/spawner.js +20 -3
- package/dist/repertoire/skills.js +17 -3
- package/package.json +2 -1
- package/skills/configure-dev-tools.md +109 -0
package/changelog.json
CHANGED
|
@@ -1,6 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"_note": "This changelog is maintained as part of the PR/version-bump workflow. Agent-curated, not auto-generated. Agents read this file directly via read_file to understand what changed between versions.",
|
|
3
3
|
"versions": [
|
|
4
|
+
{
|
|
5
|
+
"version": "0.1.0-alpha.140",
|
|
6
|
+
"changes": [
|
|
7
|
+
"MCP server: agents can serve as MCP peers for dev tools (Claude Code, Codex) via ouro mcp-serve --agent <name>.",
|
|
8
|
+
"13 MCP tools: ask, status, catchup, delegate, get_context, search_memory, get_task, check_scope, request_decision, check_guidance, report_progress, report_blocker, report_complete.",
|
|
9
|
+
"Standalone mode: MCP shim calls agent-service directly, no daemon dependency for read-only ops.",
|
|
10
|
+
"Auto-detect framing: supports both Content-Length (Claude Code) and newline-delimited JSON (Codex).",
|
|
11
|
+
"Harness-level skills: 3-tier fallback (agent > protocol > harness), configure-dev-tools skill ships with npm package.",
|
|
12
|
+
"Codex spawner refactor: --ephemeral, --json, -c MCP injection, JSONL event parsing replaces magic strings.",
|
|
13
|
+
"Prompt awareness: body map lists ouro mcp-serve, runtime info mentions MCP serve capability."
|
|
14
|
+
]
|
|
15
|
+
},
|
|
4
16
|
{
|
|
5
17
|
"version": "0.1.0-alpha.139",
|
|
6
18
|
"changes": [
|
|
@@ -0,0 +1,467 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Agent service layer — handles MCP-facing daemon commands.
|
|
4
|
+
* Each handler receives { agent, friendId, ...params } and returns DaemonResponse.
|
|
5
|
+
* For MVP, these read from agent state on the filesystem.
|
|
6
|
+
* Full inference support can come later.
|
|
7
|
+
*/
|
|
8
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
9
|
+
if (k2 === undefined) k2 = k;
|
|
10
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
11
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
12
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
13
|
+
}
|
|
14
|
+
Object.defineProperty(o, k2, desc);
|
|
15
|
+
}) : (function(o, m, k, k2) {
|
|
16
|
+
if (k2 === undefined) k2 = k;
|
|
17
|
+
o[k2] = m[k];
|
|
18
|
+
}));
|
|
19
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
20
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
21
|
+
}) : function(o, v) {
|
|
22
|
+
o["default"] = v;
|
|
23
|
+
});
|
|
24
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
25
|
+
var ownKeys = function(o) {
|
|
26
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
27
|
+
var ar = [];
|
|
28
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
29
|
+
return ar;
|
|
30
|
+
};
|
|
31
|
+
return ownKeys(o);
|
|
32
|
+
};
|
|
33
|
+
return function (mod) {
|
|
34
|
+
if (mod && mod.__esModule) return mod;
|
|
35
|
+
var result = {};
|
|
36
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
37
|
+
__setModuleDefault(result, mod);
|
|
38
|
+
return result;
|
|
39
|
+
};
|
|
40
|
+
})();
|
|
41
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
42
|
+
exports.handleAgentStatus = handleAgentStatus;
|
|
43
|
+
exports.handleAgentAsk = handleAgentAsk;
|
|
44
|
+
exports.handleAgentCatchup = handleAgentCatchup;
|
|
45
|
+
exports.handleAgentDelegate = handleAgentDelegate;
|
|
46
|
+
exports.handleAgentGetContext = handleAgentGetContext;
|
|
47
|
+
exports.handleAgentSearchMemory = handleAgentSearchMemory;
|
|
48
|
+
exports.handleAgentGetTask = handleAgentGetTask;
|
|
49
|
+
exports.handleAgentCheckScope = handleAgentCheckScope;
|
|
50
|
+
exports.handleAgentRequestDecision = handleAgentRequestDecision;
|
|
51
|
+
exports.handleAgentCheckGuidance = handleAgentCheckGuidance;
|
|
52
|
+
exports.handleAgentReportProgress = handleAgentReportProgress;
|
|
53
|
+
exports.handleAgentReportBlocker = handleAgentReportBlocker;
|
|
54
|
+
exports.handleAgentReportComplete = handleAgentReportComplete;
|
|
55
|
+
const fs = __importStar(require("fs"));
|
|
56
|
+
const path = __importStar(require("path"));
|
|
57
|
+
const identity_1 = require("../identity");
|
|
58
|
+
const runtime_1 = require("../../nerves/runtime");
|
|
59
|
+
function readAgentFile(agent, ...segments) {
|
|
60
|
+
const filePath = path.join((0, identity_1.getAgentRoot)(agent), ...segments);
|
|
61
|
+
if (!fs.existsSync(filePath))
|
|
62
|
+
return null;
|
|
63
|
+
return fs.readFileSync(filePath, "utf-8");
|
|
64
|
+
}
|
|
65
|
+
/** Read agent memory from multiple possible locations. */
|
|
66
|
+
function readAgentMemory(agent) {
|
|
67
|
+
// Try memory/MEMORY.md first, then psyche/memory/facts.jsonl
|
|
68
|
+
return readAgentFile(agent, "memory", "MEMORY.md")
|
|
69
|
+
?? readAgentFile(agent, "psyche", "memory", "facts.jsonl");
|
|
70
|
+
}
|
|
71
|
+
function listStateFiles(agent, ...segments) {
|
|
72
|
+
const dirPath = path.join((0, identity_1.getAgentStateRoot)(agent), ...segments);
|
|
73
|
+
if (!fs.existsSync(dirPath))
|
|
74
|
+
return [];
|
|
75
|
+
return fs.readdirSync(dirPath).filter((f) => f.endsWith(".json") || f.endsWith(".jsonl") || f.endsWith(".md"));
|
|
76
|
+
}
|
|
77
|
+
async function handleAgentStatus(params) {
|
|
78
|
+
(0, runtime_1.emitNervesEvent)({
|
|
79
|
+
component: "daemon",
|
|
80
|
+
event: "daemon.agent_service_start",
|
|
81
|
+
message: "handling agent.status",
|
|
82
|
+
meta: { agent: params.agent, friendId: params.friendId },
|
|
83
|
+
});
|
|
84
|
+
const sessionFiles = listStateFiles(params.agent, "sessions");
|
|
85
|
+
const memoryContent = readAgentMemory(params.agent);
|
|
86
|
+
(0, runtime_1.emitNervesEvent)({
|
|
87
|
+
component: "daemon",
|
|
88
|
+
event: "daemon.agent_service_end",
|
|
89
|
+
message: "completed agent.status",
|
|
90
|
+
meta: { agent: params.agent },
|
|
91
|
+
});
|
|
92
|
+
return {
|
|
93
|
+
ok: true,
|
|
94
|
+
message: `Agent ${params.agent} status`,
|
|
95
|
+
data: {
|
|
96
|
+
agent: params.agent,
|
|
97
|
+
status: "active",
|
|
98
|
+
sessionCount: sessionFiles.length,
|
|
99
|
+
hasMemory: memoryContent !== null,
|
|
100
|
+
},
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
async function handleAgentAsk(params) {
|
|
104
|
+
(0, runtime_1.emitNervesEvent)({
|
|
105
|
+
component: "daemon",
|
|
106
|
+
event: "daemon.agent_service_start",
|
|
107
|
+
message: "handling agent.ask",
|
|
108
|
+
meta: { agent: params.agent, friendId: params.friendId },
|
|
109
|
+
});
|
|
110
|
+
const question = params.question;
|
|
111
|
+
if (!question) {
|
|
112
|
+
(0, runtime_1.emitNervesEvent)({
|
|
113
|
+
level: "error",
|
|
114
|
+
component: "daemon",
|
|
115
|
+
event: "daemon.agent_service_error",
|
|
116
|
+
message: "agent.ask missing question parameter",
|
|
117
|
+
meta: { agent: params.agent },
|
|
118
|
+
});
|
|
119
|
+
return { ok: false, error: "Missing required parameter: question" };
|
|
120
|
+
}
|
|
121
|
+
// MVP: return memory + recent session context without running inference
|
|
122
|
+
const memoryContent = readAgentMemory(params.agent);
|
|
123
|
+
const context = memoryContent
|
|
124
|
+
? `Based on agent memory:\n${memoryContent.slice(0, 2000)}`
|
|
125
|
+
: `Agent ${params.agent} has no memory file. Question was: ${question}`;
|
|
126
|
+
(0, runtime_1.emitNervesEvent)({
|
|
127
|
+
component: "daemon",
|
|
128
|
+
event: "daemon.agent_service_end",
|
|
129
|
+
message: "completed agent.ask",
|
|
130
|
+
meta: { agent: params.agent },
|
|
131
|
+
});
|
|
132
|
+
return { ok: true, message: context };
|
|
133
|
+
}
|
|
134
|
+
async function handleAgentCatchup(params) {
|
|
135
|
+
(0, runtime_1.emitNervesEvent)({
|
|
136
|
+
component: "daemon",
|
|
137
|
+
event: "daemon.agent_service_start",
|
|
138
|
+
message: "handling agent.catchup",
|
|
139
|
+
meta: { agent: params.agent, friendId: params.friendId },
|
|
140
|
+
});
|
|
141
|
+
const sessionFiles = listStateFiles(params.agent, "sessions");
|
|
142
|
+
const recentSessions = sessionFiles.slice(-5);
|
|
143
|
+
(0, runtime_1.emitNervesEvent)({
|
|
144
|
+
component: "daemon",
|
|
145
|
+
event: "daemon.agent_service_end",
|
|
146
|
+
message: "completed agent.catchup",
|
|
147
|
+
meta: { agent: params.agent },
|
|
148
|
+
});
|
|
149
|
+
return {
|
|
150
|
+
ok: true,
|
|
151
|
+
message: recentSessions.length > 0
|
|
152
|
+
? `Recent activity: ${recentSessions.length} recent sessions found`
|
|
153
|
+
: `No recent activity for ${params.agent}`,
|
|
154
|
+
data: { recentSessions },
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
async function handleAgentDelegate(params) {
|
|
158
|
+
(0, runtime_1.emitNervesEvent)({
|
|
159
|
+
component: "daemon",
|
|
160
|
+
event: "daemon.agent_service_start",
|
|
161
|
+
message: "handling agent.delegate",
|
|
162
|
+
meta: { agent: params.agent, friendId: params.friendId },
|
|
163
|
+
});
|
|
164
|
+
const task = params.task;
|
|
165
|
+
if (!task) {
|
|
166
|
+
(0, runtime_1.emitNervesEvent)({
|
|
167
|
+
level: "error",
|
|
168
|
+
component: "daemon",
|
|
169
|
+
event: "daemon.agent_service_error",
|
|
170
|
+
message: "agent.delegate missing task parameter",
|
|
171
|
+
meta: { agent: params.agent },
|
|
172
|
+
});
|
|
173
|
+
return { ok: false, error: "Missing required parameter: task" };
|
|
174
|
+
}
|
|
175
|
+
(0, runtime_1.emitNervesEvent)({
|
|
176
|
+
component: "daemon",
|
|
177
|
+
event: "daemon.agent_service_end",
|
|
178
|
+
message: "completed agent.delegate",
|
|
179
|
+
meta: { agent: params.agent },
|
|
180
|
+
});
|
|
181
|
+
return {
|
|
182
|
+
ok: true,
|
|
183
|
+
message: `Task queued for delegate to ${params.agent}: ${task}`,
|
|
184
|
+
data: { task, context: params.context ?? null, queuedAt: new Date().toISOString() },
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
async function handleAgentGetContext(params) {
|
|
188
|
+
(0, runtime_1.emitNervesEvent)({
|
|
189
|
+
component: "daemon",
|
|
190
|
+
event: "daemon.agent_service_start",
|
|
191
|
+
message: "handling agent.getContext",
|
|
192
|
+
meta: { agent: params.agent, friendId: params.friendId },
|
|
193
|
+
});
|
|
194
|
+
const memoryContent = readAgentMemory(params.agent);
|
|
195
|
+
const taskFiles = listStateFiles(params.agent, "tasks");
|
|
196
|
+
(0, runtime_1.emitNervesEvent)({
|
|
197
|
+
component: "daemon",
|
|
198
|
+
event: "daemon.agent_service_end",
|
|
199
|
+
message: "completed agent.getContext",
|
|
200
|
+
meta: { agent: params.agent },
|
|
201
|
+
});
|
|
202
|
+
return {
|
|
203
|
+
ok: true,
|
|
204
|
+
data: {
|
|
205
|
+
agent: params.agent,
|
|
206
|
+
hasMemory: memoryContent !== null,
|
|
207
|
+
memorySummary: memoryContent ? memoryContent.slice(0, 500) : null,
|
|
208
|
+
taskCount: taskFiles.length,
|
|
209
|
+
},
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
async function handleAgentSearchMemory(params) {
|
|
213
|
+
(0, runtime_1.emitNervesEvent)({
|
|
214
|
+
component: "daemon",
|
|
215
|
+
event: "daemon.agent_service_start",
|
|
216
|
+
message: "handling agent.searchMemory",
|
|
217
|
+
meta: { agent: params.agent, friendId: params.friendId },
|
|
218
|
+
});
|
|
219
|
+
const query = params.query;
|
|
220
|
+
if (!query) {
|
|
221
|
+
(0, runtime_1.emitNervesEvent)({
|
|
222
|
+
level: "error",
|
|
223
|
+
component: "daemon",
|
|
224
|
+
event: "daemon.agent_service_error",
|
|
225
|
+
message: "agent.searchMemory missing query parameter",
|
|
226
|
+
meta: { agent: params.agent },
|
|
227
|
+
});
|
|
228
|
+
return { ok: false, error: "Missing required parameter: query" };
|
|
229
|
+
}
|
|
230
|
+
// MVP: simple substring search in memory file
|
|
231
|
+
const memoryContent = readAgentMemory(params.agent);
|
|
232
|
+
const matches = [];
|
|
233
|
+
if (memoryContent) {
|
|
234
|
+
const lines = memoryContent.split("\n");
|
|
235
|
+
const queryLower = query.toLowerCase();
|
|
236
|
+
for (const line of lines) {
|
|
237
|
+
if (line.toLowerCase().includes(queryLower)) {
|
|
238
|
+
matches.push(line.trim());
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
(0, runtime_1.emitNervesEvent)({
|
|
243
|
+
component: "daemon",
|
|
244
|
+
event: "daemon.agent_service_end",
|
|
245
|
+
message: "completed agent.searchMemory",
|
|
246
|
+
meta: { agent: params.agent, matchCount: matches.length },
|
|
247
|
+
});
|
|
248
|
+
return {
|
|
249
|
+
ok: true,
|
|
250
|
+
message: matches.length > 0 ? `Found ${matches.length} matches` : "No matches found",
|
|
251
|
+
data: { query, matches: matches.slice(0, 20) },
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
async function handleAgentGetTask(params) {
|
|
255
|
+
(0, runtime_1.emitNervesEvent)({
|
|
256
|
+
component: "daemon",
|
|
257
|
+
event: "daemon.agent_service_start",
|
|
258
|
+
message: "handling agent.getTask",
|
|
259
|
+
meta: { agent: params.agent, friendId: params.friendId },
|
|
260
|
+
});
|
|
261
|
+
const taskFiles = listStateFiles(params.agent, "tasks");
|
|
262
|
+
(0, runtime_1.emitNervesEvent)({
|
|
263
|
+
component: "daemon",
|
|
264
|
+
event: "daemon.agent_service_end",
|
|
265
|
+
message: "completed agent.getTask",
|
|
266
|
+
meta: { agent: params.agent },
|
|
267
|
+
});
|
|
268
|
+
return {
|
|
269
|
+
ok: true,
|
|
270
|
+
message: taskFiles.length > 0
|
|
271
|
+
? `Current tasks: ${taskFiles.join(", ")}`
|
|
272
|
+
: `No active tasks for ${params.agent}`,
|
|
273
|
+
data: { tasks: taskFiles },
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
async function handleAgentCheckScope(params) {
|
|
277
|
+
(0, runtime_1.emitNervesEvent)({
|
|
278
|
+
component: "daemon",
|
|
279
|
+
event: "daemon.agent_service_start",
|
|
280
|
+
message: "handling agent.checkScope",
|
|
281
|
+
meta: { agent: params.agent, friendId: params.friendId },
|
|
282
|
+
});
|
|
283
|
+
const item = params.item;
|
|
284
|
+
if (!item) {
|
|
285
|
+
(0, runtime_1.emitNervesEvent)({
|
|
286
|
+
level: "error",
|
|
287
|
+
component: "daemon",
|
|
288
|
+
event: "daemon.agent_service_error",
|
|
289
|
+
message: "agent.checkScope missing item parameter",
|
|
290
|
+
meta: { agent: params.agent },
|
|
291
|
+
});
|
|
292
|
+
return { ok: false, error: "Missing required parameter: item" };
|
|
293
|
+
}
|
|
294
|
+
(0, runtime_1.emitNervesEvent)({
|
|
295
|
+
component: "daemon",
|
|
296
|
+
event: "daemon.agent_service_end",
|
|
297
|
+
message: "completed agent.checkScope",
|
|
298
|
+
meta: { agent: params.agent },
|
|
299
|
+
});
|
|
300
|
+
return {
|
|
301
|
+
ok: true,
|
|
302
|
+
message: `Scope check for: ${item}`,
|
|
303
|
+
data: { item, inScope: true, reason: "MVP: scope check requires inference — defaulting to in-scope" },
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
async function handleAgentRequestDecision(params) {
|
|
307
|
+
(0, runtime_1.emitNervesEvent)({
|
|
308
|
+
component: "daemon",
|
|
309
|
+
event: "daemon.agent_service_start",
|
|
310
|
+
message: "handling agent.requestDecision",
|
|
311
|
+
meta: { agent: params.agent, friendId: params.friendId },
|
|
312
|
+
});
|
|
313
|
+
const topic = params.topic;
|
|
314
|
+
if (!topic) {
|
|
315
|
+
(0, runtime_1.emitNervesEvent)({
|
|
316
|
+
level: "error",
|
|
317
|
+
component: "daemon",
|
|
318
|
+
event: "daemon.agent_service_error",
|
|
319
|
+
message: "agent.requestDecision missing topic parameter",
|
|
320
|
+
meta: { agent: params.agent },
|
|
321
|
+
});
|
|
322
|
+
return { ok: false, error: "Missing required parameter: topic" };
|
|
323
|
+
}
|
|
324
|
+
(0, runtime_1.emitNervesEvent)({
|
|
325
|
+
component: "daemon",
|
|
326
|
+
event: "daemon.agent_service_end",
|
|
327
|
+
message: "completed agent.requestDecision",
|
|
328
|
+
meta: { agent: params.agent },
|
|
329
|
+
});
|
|
330
|
+
return {
|
|
331
|
+
ok: true,
|
|
332
|
+
message: `Decision request queued: ${topic}`,
|
|
333
|
+
data: {
|
|
334
|
+
topic,
|
|
335
|
+
options: params.options ?? null,
|
|
336
|
+
status: "pending",
|
|
337
|
+
note: "MVP: decision requires inference — queued for agent review",
|
|
338
|
+
},
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
async function handleAgentCheckGuidance(params) {
|
|
342
|
+
(0, runtime_1.emitNervesEvent)({
|
|
343
|
+
component: "daemon",
|
|
344
|
+
event: "daemon.agent_service_start",
|
|
345
|
+
message: "handling agent.checkGuidance",
|
|
346
|
+
meta: { agent: params.agent, friendId: params.friendId },
|
|
347
|
+
});
|
|
348
|
+
const topic = params.topic;
|
|
349
|
+
if (!topic) {
|
|
350
|
+
(0, runtime_1.emitNervesEvent)({
|
|
351
|
+
level: "error",
|
|
352
|
+
component: "daemon",
|
|
353
|
+
event: "daemon.agent_service_error",
|
|
354
|
+
message: "agent.checkGuidance missing topic parameter",
|
|
355
|
+
meta: { agent: params.agent },
|
|
356
|
+
});
|
|
357
|
+
return { ok: false, error: "Missing required parameter: topic" };
|
|
358
|
+
}
|
|
359
|
+
// MVP: check memory for relevant guidance
|
|
360
|
+
const memoryContent = readAgentMemory(params.agent);
|
|
361
|
+
let guidance = `No specific guidance found for: ${topic}`;
|
|
362
|
+
if (memoryContent) {
|
|
363
|
+
const lines = memoryContent.split("\n");
|
|
364
|
+
const topicLower = topic.toLowerCase();
|
|
365
|
+
const relevant = lines.filter((line) => line.toLowerCase().includes(topicLower));
|
|
366
|
+
if (relevant.length > 0) {
|
|
367
|
+
guidance = `Relevant guidance:\n${relevant.slice(0, 10).join("\n")}`;
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
(0, runtime_1.emitNervesEvent)({
|
|
371
|
+
component: "daemon",
|
|
372
|
+
event: "daemon.agent_service_end",
|
|
373
|
+
message: "completed agent.checkGuidance",
|
|
374
|
+
meta: { agent: params.agent },
|
|
375
|
+
});
|
|
376
|
+
return { ok: true, message: guidance };
|
|
377
|
+
}
|
|
378
|
+
async function handleAgentReportProgress(params) {
|
|
379
|
+
(0, runtime_1.emitNervesEvent)({
|
|
380
|
+
component: "daemon",
|
|
381
|
+
event: "daemon.agent_service_start",
|
|
382
|
+
message: "handling agent.reportProgress",
|
|
383
|
+
meta: { agent: params.agent, friendId: params.friendId },
|
|
384
|
+
});
|
|
385
|
+
const summary = params.summary;
|
|
386
|
+
if (!summary) {
|
|
387
|
+
(0, runtime_1.emitNervesEvent)({
|
|
388
|
+
level: "error",
|
|
389
|
+
component: "daemon",
|
|
390
|
+
event: "daemon.agent_service_error",
|
|
391
|
+
message: "agent.reportProgress missing summary parameter",
|
|
392
|
+
meta: { agent: params.agent },
|
|
393
|
+
});
|
|
394
|
+
return { ok: false, error: "Missing required parameter: summary" };
|
|
395
|
+
}
|
|
396
|
+
(0, runtime_1.emitNervesEvent)({
|
|
397
|
+
component: "daemon",
|
|
398
|
+
event: "daemon.agent_service_end",
|
|
399
|
+
message: "completed agent.reportProgress",
|
|
400
|
+
meta: { agent: params.agent },
|
|
401
|
+
});
|
|
402
|
+
return {
|
|
403
|
+
ok: true,
|
|
404
|
+
message: `Progress noted: ${summary}`,
|
|
405
|
+
data: { summary, receivedAt: new Date().toISOString() },
|
|
406
|
+
};
|
|
407
|
+
}
|
|
408
|
+
async function handleAgentReportBlocker(params) {
|
|
409
|
+
(0, runtime_1.emitNervesEvent)({
|
|
410
|
+
component: "daemon",
|
|
411
|
+
event: "daemon.agent_service_start",
|
|
412
|
+
message: "handling agent.reportBlocker",
|
|
413
|
+
meta: { agent: params.agent, friendId: params.friendId },
|
|
414
|
+
});
|
|
415
|
+
const blocker = params.blocker;
|
|
416
|
+
if (!blocker) {
|
|
417
|
+
(0, runtime_1.emitNervesEvent)({
|
|
418
|
+
level: "error",
|
|
419
|
+
component: "daemon",
|
|
420
|
+
event: "daemon.agent_service_error",
|
|
421
|
+
message: "agent.reportBlocker missing blocker parameter",
|
|
422
|
+
meta: { agent: params.agent },
|
|
423
|
+
});
|
|
424
|
+
return { ok: false, error: "Missing required parameter: blocker" };
|
|
425
|
+
}
|
|
426
|
+
(0, runtime_1.emitNervesEvent)({
|
|
427
|
+
component: "daemon",
|
|
428
|
+
event: "daemon.agent_service_end",
|
|
429
|
+
message: "completed agent.reportBlocker",
|
|
430
|
+
meta: { agent: params.agent },
|
|
431
|
+
});
|
|
432
|
+
return {
|
|
433
|
+
ok: true,
|
|
434
|
+
message: `Blocker reported: ${blocker}`,
|
|
435
|
+
data: { blocker, receivedAt: new Date().toISOString() },
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
async function handleAgentReportComplete(params) {
|
|
439
|
+
(0, runtime_1.emitNervesEvent)({
|
|
440
|
+
component: "daemon",
|
|
441
|
+
event: "daemon.agent_service_start",
|
|
442
|
+
message: "handling agent.reportComplete",
|
|
443
|
+
meta: { agent: params.agent, friendId: params.friendId },
|
|
444
|
+
});
|
|
445
|
+
const summary = params.summary;
|
|
446
|
+
if (!summary) {
|
|
447
|
+
(0, runtime_1.emitNervesEvent)({
|
|
448
|
+
level: "error",
|
|
449
|
+
component: "daemon",
|
|
450
|
+
event: "daemon.agent_service_error",
|
|
451
|
+
message: "agent.reportComplete missing summary parameter",
|
|
452
|
+
meta: { agent: params.agent },
|
|
453
|
+
});
|
|
454
|
+
return { ok: false, error: "Missing required parameter: summary" };
|
|
455
|
+
}
|
|
456
|
+
(0, runtime_1.emitNervesEvent)({
|
|
457
|
+
component: "daemon",
|
|
458
|
+
event: "daemon.agent_service_end",
|
|
459
|
+
message: "completed agent.reportComplete",
|
|
460
|
+
meta: { agent: params.agent },
|
|
461
|
+
});
|
|
462
|
+
return {
|
|
463
|
+
ok: true,
|
|
464
|
+
message: `Completion reported: ${summary}`,
|
|
465
|
+
data: { summary, receivedAt: new Date().toISOString() },
|
|
466
|
+
};
|
|
467
|
+
}
|
|
@@ -891,6 +891,28 @@ function parseMcpCommand(args) {
|
|
|
891
891
|
}
|
|
892
892
|
throw new Error(`Usage\n${usage()}`);
|
|
893
893
|
}
|
|
894
|
+
function parseMcpServeCommand(args) {
|
|
895
|
+
let agent;
|
|
896
|
+
let friendId;
|
|
897
|
+
let socketOverride;
|
|
898
|
+
for (let i = 0; i < args.length; i++) {
|
|
899
|
+
if (args[i] === "--agent" && args[i + 1]) {
|
|
900
|
+
agent = args[++i];
|
|
901
|
+
continue;
|
|
902
|
+
}
|
|
903
|
+
if (args[i] === "--friend" && args[i + 1]) {
|
|
904
|
+
friendId = args[++i];
|
|
905
|
+
continue;
|
|
906
|
+
}
|
|
907
|
+
if (args[i] === "--socket" && args[i + 1]) {
|
|
908
|
+
socketOverride = args[++i];
|
|
909
|
+
continue;
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
if (!agent)
|
|
913
|
+
throw new Error("mcp-serve requires --agent <name>");
|
|
914
|
+
return { kind: "mcp-serve", agent, ...(friendId ? { friendId } : {}), ...(socketOverride ? { socketOverride } : {}) };
|
|
915
|
+
}
|
|
894
916
|
function parseOuroCommand(args) {
|
|
895
917
|
const [head, second] = args;
|
|
896
918
|
if (!head)
|
|
@@ -976,6 +998,8 @@ function parseOuroCommand(args) {
|
|
|
976
998
|
return parsePokeCommand(args.slice(1));
|
|
977
999
|
if (head === "link")
|
|
978
1000
|
return parseLinkCommand(args.slice(1));
|
|
1001
|
+
if (head === "mcp-serve")
|
|
1002
|
+
return parseMcpServeCommand(args.slice(1));
|
|
979
1003
|
throw new Error(`Unknown command '${args.join(" ")}'.\n${usage()}`);
|
|
980
1004
|
}
|
|
981
1005
|
function defaultStartDaemonProcess(socketPath) {
|
|
@@ -2169,6 +2193,36 @@ async function runOuroCli(args, deps = createDefaultOuroCliDeps()) {
|
|
|
2169
2193
|
deps.tailLogs();
|
|
2170
2194
|
return "";
|
|
2171
2195
|
}
|
|
2196
|
+
/* v8 ignore start — mcp-serve block binds to process.stdin/stdout; tested via mcp-server unit tests */
|
|
2197
|
+
// ── mcp-serve: start MCP server in-process on stdin/stdout ──
|
|
2198
|
+
if (command.kind === "mcp-serve") {
|
|
2199
|
+
const { createMcpServer } = await Promise.resolve().then(() => __importStar(require("./mcp-server")));
|
|
2200
|
+
const friendId = command.friendId ?? `local-${os.userInfo().username}`;
|
|
2201
|
+
const mcpSocketPath = command.socketOverride ?? deps.socketPath;
|
|
2202
|
+
const server = createMcpServer({
|
|
2203
|
+
agent: command.agent,
|
|
2204
|
+
friendId,
|
|
2205
|
+
socketPath: mcpSocketPath,
|
|
2206
|
+
stdin: process.stdin,
|
|
2207
|
+
stdout: process.stdout,
|
|
2208
|
+
});
|
|
2209
|
+
server.start();
|
|
2210
|
+
(0, runtime_1.emitNervesEvent)({
|
|
2211
|
+
component: "daemon",
|
|
2212
|
+
event: "daemon.mcp_serve_started",
|
|
2213
|
+
message: "MCP server started via CLI",
|
|
2214
|
+
meta: { agent: command.agent, friendId },
|
|
2215
|
+
});
|
|
2216
|
+
// Keep process alive until stdin closes
|
|
2217
|
+
await new Promise((resolve) => {
|
|
2218
|
+
process.stdin.on("end", () => {
|
|
2219
|
+
server.stop();
|
|
2220
|
+
resolve();
|
|
2221
|
+
});
|
|
2222
|
+
});
|
|
2223
|
+
return "";
|
|
2224
|
+
}
|
|
2225
|
+
/* v8 ignore stop */
|
|
2172
2226
|
// ── mcp subcommands (routed through daemon socket) ──
|
|
2173
2227
|
if (command.kind === "mcp.list" || command.kind === "mcp.call") {
|
|
2174
2228
|
const daemonCommand = toDaemonCommand(command);
|
|
@@ -51,6 +51,7 @@ const update_checker_1 = require("./update-checker");
|
|
|
51
51
|
const staged_restart_1 = require("./staged-restart");
|
|
52
52
|
const child_process_1 = require("child_process");
|
|
53
53
|
const pending_1 = require("../../mind/pending");
|
|
54
|
+
const agent_service_1 = require("./agent-service");
|
|
54
55
|
const channel_1 = require("../../mind/friends/channel");
|
|
55
56
|
const mcp_manager_1 = require("../../repertoire/mcp-manager");
|
|
56
57
|
const PIDFILE_PATH = path.join(os.homedir(), ".ouro-cli", "daemon.pids");
|
|
@@ -519,6 +520,32 @@ class OuroDaemon {
|
|
|
519
520
|
case "agent.restart":
|
|
520
521
|
await this.processManager.restartAgent?.(command.agent);
|
|
521
522
|
return { ok: true, message: `restarted ${command.agent}` };
|
|
523
|
+
case "agent.ask":
|
|
524
|
+
return (0, agent_service_1.handleAgentAsk)(command);
|
|
525
|
+
case "agent.status":
|
|
526
|
+
return (0, agent_service_1.handleAgentStatus)(command);
|
|
527
|
+
case "agent.catchup":
|
|
528
|
+
return (0, agent_service_1.handleAgentCatchup)(command);
|
|
529
|
+
case "agent.delegate":
|
|
530
|
+
return (0, agent_service_1.handleAgentDelegate)(command);
|
|
531
|
+
case "agent.getContext":
|
|
532
|
+
return (0, agent_service_1.handleAgentGetContext)(command);
|
|
533
|
+
case "agent.searchMemory":
|
|
534
|
+
return (0, agent_service_1.handleAgentSearchMemory)(command);
|
|
535
|
+
case "agent.getTask":
|
|
536
|
+
return (0, agent_service_1.handleAgentGetTask)(command);
|
|
537
|
+
case "agent.checkScope":
|
|
538
|
+
return (0, agent_service_1.handleAgentCheckScope)(command);
|
|
539
|
+
case "agent.requestDecision":
|
|
540
|
+
return (0, agent_service_1.handleAgentRequestDecision)(command);
|
|
541
|
+
case "agent.checkGuidance":
|
|
542
|
+
return (0, agent_service_1.handleAgentCheckGuidance)(command);
|
|
543
|
+
case "agent.reportProgress":
|
|
544
|
+
return (0, agent_service_1.handleAgentReportProgress)(command);
|
|
545
|
+
case "agent.reportBlocker":
|
|
546
|
+
return (0, agent_service_1.handleAgentReportBlocker)(command);
|
|
547
|
+
case "agent.reportComplete":
|
|
548
|
+
return (0, agent_service_1.handleAgentReportComplete)(command);
|
|
522
549
|
case "cron.list": {
|
|
523
550
|
const jobs = this.scheduler.listJobs();
|
|
524
551
|
const summary = jobs.length === 0
|