@rivus/agent 0.7.0 → 0.8.0
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/README.md +1 -1
- package/dist/acp.d.ts +11 -0
- package/dist/acp.js +14 -1
- package/dist/background-session-authority.js +224 -0
- package/dist/background-session-input.js +45 -0
- package/dist/background-session-service.d.ts +291 -0
- package/dist/index.d.ts +1 -287
- package/dist/index.js +14 -31
- package/dist/mcp.d.ts +86 -0
- package/dist/mcp.js +416 -0
- package/dist/rivus-daemon-cli.js +2 -1
- package/dist/rivus-plugin-registry.js +2 -223
- package/package.json +5 -1
package/dist/mcp.js
ADDED
|
@@ -0,0 +1,416 @@
|
|
|
1
|
+
import { l as createBackgroundSessionToolContracts, s as createBackgroundSessionKey } from "./background-session-authority.js";
|
|
2
|
+
import { a as readBackgroundSessionWaitInput, i as readBackgroundSessionString, n as readBackgroundSessionObject, r as readBackgroundSessionPhase, t as readBackgroundSessionInteger } from "./background-session-input.js";
|
|
3
|
+
import { randomUUID } from "node:crypto";
|
|
4
|
+
import { pathToFileURL } from "node:url";
|
|
5
|
+
import { createServer } from "node:http";
|
|
6
|
+
//#region src/infrastructure/mcp/background-session-mcp-server.ts
|
|
7
|
+
const BACKGROUND_SESSION_MCP_SERVER_NAME = "rivus-background-sessions";
|
|
8
|
+
const BACKGROUND_SESSION_MCP_SERVER_VERSION = "1.0.0";
|
|
9
|
+
var BackgroundSessionMcpError = class extends Error {
|
|
10
|
+
name = "BackgroundSessionMcpError";
|
|
11
|
+
};
|
|
12
|
+
function createBackgroundSessionMcpServer(options) {
|
|
13
|
+
const input = options.input ?? process.stdin;
|
|
14
|
+
const output = options.output ?? process.stdout;
|
|
15
|
+
const fetchImplementation = options.fetch ?? fetch;
|
|
16
|
+
const enabledTools = new Set(options.enabledTools ?? [
|
|
17
|
+
"start",
|
|
18
|
+
"wait",
|
|
19
|
+
"list",
|
|
20
|
+
"status",
|
|
21
|
+
"send",
|
|
22
|
+
"stop"
|
|
23
|
+
]);
|
|
24
|
+
const tools = createBackgroundSessionToolContracts().filter(({ id }) => enabledTools.has(id.slice(11))).map((contract) => ({
|
|
25
|
+
description: contract.description,
|
|
26
|
+
inputSchema: contract.inputSchema,
|
|
27
|
+
name: contract.id,
|
|
28
|
+
outputSchema: { type: "object" }
|
|
29
|
+
}));
|
|
30
|
+
const serverInfo = {
|
|
31
|
+
name: options.serverName ?? "rivus-background-sessions",
|
|
32
|
+
version: options.serverVersion ?? "1.0.0"
|
|
33
|
+
};
|
|
34
|
+
return { run: () => runMcpServer({
|
|
35
|
+
controlContext: options.controlContext,
|
|
36
|
+
controlToken: options.controlToken,
|
|
37
|
+
controlUrl: options.controlUrl,
|
|
38
|
+
fetchImplementation,
|
|
39
|
+
input,
|
|
40
|
+
output,
|
|
41
|
+
serverInfo,
|
|
42
|
+
tools
|
|
43
|
+
}) };
|
|
44
|
+
}
|
|
45
|
+
async function runBackgroundSessionMcpServer(options = {}) {
|
|
46
|
+
const controlUrl = requireEnv("RIVUS_MCP_CONTROL_URL");
|
|
47
|
+
const controlToken = requireEnv("RIVUS_MCP_CONTROL_TOKEN");
|
|
48
|
+
const context = parseContext(requireEnv("RIVUS_MCP_CONTEXT"));
|
|
49
|
+
const enabledTools = optionalEnv("RIVUS_MCP_TOOLS")?.split(",").map((tool) => tool.trim()).filter(Boolean);
|
|
50
|
+
await createBackgroundSessionMcpServer({
|
|
51
|
+
controlContext: context,
|
|
52
|
+
controlToken,
|
|
53
|
+
controlUrl,
|
|
54
|
+
...enabledTools ? { enabledTools } : {},
|
|
55
|
+
...options.fetch ? { fetch: options.fetch } : {},
|
|
56
|
+
...options.input ? { input: options.input } : {},
|
|
57
|
+
...options.output ? { output: options.output } : {}
|
|
58
|
+
}).run();
|
|
59
|
+
}
|
|
60
|
+
async function runMcpServer(options) {
|
|
61
|
+
const send = (message) => {
|
|
62
|
+
const body = JSON.stringify(message);
|
|
63
|
+
options.output.write(`Content-Length: ${Buffer.byteLength(body)}\r\n\r\n${body}`);
|
|
64
|
+
};
|
|
65
|
+
for await (const message of readMcpMessages(options.input)) {
|
|
66
|
+
if (!isRecord(message) || typeof message.id !== "number") continue;
|
|
67
|
+
const method = typeof message.method === "string" ? message.method : void 0;
|
|
68
|
+
if (!method) continue;
|
|
69
|
+
try {
|
|
70
|
+
switch (method) {
|
|
71
|
+
case "initialize":
|
|
72
|
+
send({
|
|
73
|
+
id: message.id,
|
|
74
|
+
result: {
|
|
75
|
+
capabilities: { tools: { listChanged: false } },
|
|
76
|
+
protocolVersion: "2025-03-26",
|
|
77
|
+
serverInfo: options.serverInfo
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
break;
|
|
81
|
+
case "ping":
|
|
82
|
+
send({
|
|
83
|
+
id: message.id,
|
|
84
|
+
result: {}
|
|
85
|
+
});
|
|
86
|
+
break;
|
|
87
|
+
case "tools/list":
|
|
88
|
+
send({
|
|
89
|
+
id: message.id,
|
|
90
|
+
result: { tools: options.tools }
|
|
91
|
+
});
|
|
92
|
+
break;
|
|
93
|
+
case "tools/call": {
|
|
94
|
+
const params = isRecord(message.params) ? message.params : {};
|
|
95
|
+
const name = typeof params.name === "string" ? params.name : "";
|
|
96
|
+
const command = name.startsWith("background.") ? name.slice(11) : void 0;
|
|
97
|
+
if (!command || !options.tools.some((tool) => tool.name === name)) {
|
|
98
|
+
send({
|
|
99
|
+
id: message.id,
|
|
100
|
+
error: {
|
|
101
|
+
code: -32602,
|
|
102
|
+
message: `unknown tool: ${name}`
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
break;
|
|
106
|
+
}
|
|
107
|
+
const response = await options.fetchImplementation(options.controlUrl, {
|
|
108
|
+
body: JSON.stringify({
|
|
109
|
+
command,
|
|
110
|
+
context: options.controlContext,
|
|
111
|
+
input: params.arguments ?? {}
|
|
112
|
+
}),
|
|
113
|
+
headers: {
|
|
114
|
+
authorization: `Bearer ${options.controlToken}`,
|
|
115
|
+
"content-type": "application/json"
|
|
116
|
+
},
|
|
117
|
+
method: "POST"
|
|
118
|
+
});
|
|
119
|
+
const envelope = await response.json();
|
|
120
|
+
if (!response.ok || envelope.error) {
|
|
121
|
+
const messageText = envelope.error?.message ?? `background session control failed with ${response.status}`;
|
|
122
|
+
send({
|
|
123
|
+
id: message.id,
|
|
124
|
+
error: {
|
|
125
|
+
code: -32e3,
|
|
126
|
+
message: messageText
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
break;
|
|
130
|
+
}
|
|
131
|
+
send({
|
|
132
|
+
id: message.id,
|
|
133
|
+
result: {
|
|
134
|
+
content: [{
|
|
135
|
+
text: JSON.stringify(envelope.result ?? null),
|
|
136
|
+
type: "text"
|
|
137
|
+
}],
|
|
138
|
+
isError: false
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
break;
|
|
142
|
+
}
|
|
143
|
+
default: send({
|
|
144
|
+
id: message.id,
|
|
145
|
+
error: {
|
|
146
|
+
code: -32601,
|
|
147
|
+
message: `method not found: ${method}`
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
} catch (error) {
|
|
152
|
+
send({
|
|
153
|
+
id: message.id,
|
|
154
|
+
error: {
|
|
155
|
+
code: -32e3,
|
|
156
|
+
message: error instanceof Error ? error.message : String(error)
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
async function* readMcpMessages(input) {
|
|
163
|
+
let buffer = "";
|
|
164
|
+
for await (const chunk of input) {
|
|
165
|
+
buffer += chunk.toString("utf8");
|
|
166
|
+
let parsed = parseNextMessage(buffer);
|
|
167
|
+
while (parsed) {
|
|
168
|
+
yield parsed.message;
|
|
169
|
+
buffer = Buffer.from(buffer).subarray(parsed.byteLength).toString("utf8");
|
|
170
|
+
parsed = parseNextMessage(buffer);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
function parseNextMessage(buffer) {
|
|
175
|
+
const headerEnd = buffer.indexOf("\r\n\r\n");
|
|
176
|
+
if (headerEnd < 0) return void 0;
|
|
177
|
+
const contentLength = buffer.slice(0, headerEnd).split("\r\n").find((header) => /^Content-Length:\s*\d+$/i.test(header));
|
|
178
|
+
if (!contentLength) throw new BackgroundSessionMcpError("MCP message is missing a Content-Length header");
|
|
179
|
+
const byteLength = Number(contentLength.slice(15).trim());
|
|
180
|
+
const headerBytes = Buffer.byteLength(buffer.slice(0, headerEnd + 4));
|
|
181
|
+
if (Buffer.byteLength(buffer) < headerBytes + byteLength) return void 0;
|
|
182
|
+
const payload = Buffer.from(buffer).subarray(headerBytes, headerBytes + byteLength).toString("utf8");
|
|
183
|
+
try {
|
|
184
|
+
return {
|
|
185
|
+
byteLength: headerBytes + byteLength,
|
|
186
|
+
message: JSON.parse(payload)
|
|
187
|
+
};
|
|
188
|
+
} catch {
|
|
189
|
+
throw new BackgroundSessionMcpError("invalid MCP JSON payload");
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
function parseContext(value) {
|
|
193
|
+
const parsed = JSON.parse(value);
|
|
194
|
+
if (!isRecord(parsed)) throw new BackgroundSessionMcpError("RIVUS_MCP_CONTEXT must be a JSON object");
|
|
195
|
+
return parsed;
|
|
196
|
+
}
|
|
197
|
+
function requireEnv(name) {
|
|
198
|
+
const value = process.env[name]?.trim();
|
|
199
|
+
if (!value) throw new BackgroundSessionMcpError(`${name} is required`);
|
|
200
|
+
return value;
|
|
201
|
+
}
|
|
202
|
+
function optionalEnv(name) {
|
|
203
|
+
return process.env[name]?.trim() || void 0;
|
|
204
|
+
}
|
|
205
|
+
function isRecord(value) {
|
|
206
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
207
|
+
}
|
|
208
|
+
//#endregion
|
|
209
|
+
//#region src/application/background-session/background-session-control.ts
|
|
210
|
+
function createBackgroundSessionControl(options) {
|
|
211
|
+
return { handle: async (command, context, input) => {
|
|
212
|
+
const executionContext = {
|
|
213
|
+
agentId: context.agentId,
|
|
214
|
+
callId: `mcp-call:${randomUUID()}`,
|
|
215
|
+
instanceId: `mcp:${context.agentId}`,
|
|
216
|
+
policyEpoch: context.policyEpoch,
|
|
217
|
+
runId: context.runId,
|
|
218
|
+
sessionKey: context.sessionKey,
|
|
219
|
+
sourceMessageId: context.sourceMessageId,
|
|
220
|
+
toolId: `background.${command}`,
|
|
221
|
+
toolVersion: "1.0.0",
|
|
222
|
+
origin: toToolOrigin(context.origin)
|
|
223
|
+
};
|
|
224
|
+
switch (command) {
|
|
225
|
+
case "start": {
|
|
226
|
+
const { displayName, prompt } = readObject(input, ["displayName", "prompt"]);
|
|
227
|
+
if (typeof prompt !== "string" || prompt.trim() === "") throw new BackgroundSessionControlError("background.start requires a non-empty prompt");
|
|
228
|
+
const sessionId = `bg-${randomUUID()}`;
|
|
229
|
+
return options.service.start({
|
|
230
|
+
authority: {
|
|
231
|
+
...context.authority,
|
|
232
|
+
sessionKey: createBackgroundSessionKey(sessionId)
|
|
233
|
+
},
|
|
234
|
+
context: executionContext,
|
|
235
|
+
...displayName === void 0 ? {} : { displayName: readString(displayName, "displayName") },
|
|
236
|
+
prompt,
|
|
237
|
+
sessionId
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
case "wait": return options.service.wait({
|
|
241
|
+
context: executionContext,
|
|
242
|
+
...readBackgroundSessionWaitInput(input, (message) => new BackgroundSessionControlError(message))
|
|
243
|
+
});
|
|
244
|
+
case "list": {
|
|
245
|
+
const { limit, phase } = readObject(input, ["limit", "phase"]);
|
|
246
|
+
return options.service.list({
|
|
247
|
+
context: executionContext,
|
|
248
|
+
...limit === void 0 ? {} : { limit: readInteger(limit, "limit") },
|
|
249
|
+
...phase === void 0 ? {} : { phase: readPhase(phase) }
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
case "status": {
|
|
253
|
+
const { sessionId } = readObject(input, ["sessionId"]);
|
|
254
|
+
return options.service.status({
|
|
255
|
+
context: executionContext,
|
|
256
|
+
sessionId: readString(sessionId, "sessionId")
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
case "send": {
|
|
260
|
+
const { message, sessionId } = readObject(input, ["message", "sessionId"]);
|
|
261
|
+
return options.service.send({
|
|
262
|
+
context: executionContext,
|
|
263
|
+
message: readString(message, "message"),
|
|
264
|
+
sessionId: readString(sessionId, "sessionId")
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
case "stop": {
|
|
268
|
+
const { reason, sessionId } = readObject(input, ["reason", "sessionId"]);
|
|
269
|
+
return options.service.stop({
|
|
270
|
+
context: executionContext,
|
|
271
|
+
...reason === void 0 ? {} : { reason: readString(reason, "reason") },
|
|
272
|
+
sessionId: readString(sessionId, "sessionId")
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
} };
|
|
277
|
+
}
|
|
278
|
+
var BackgroundSessionControlError = class extends Error {
|
|
279
|
+
name = "BackgroundSessionControlError";
|
|
280
|
+
};
|
|
281
|
+
function toControlContext(input) {
|
|
282
|
+
return {
|
|
283
|
+
agentId: input.agentId,
|
|
284
|
+
authority: input.authority,
|
|
285
|
+
origin: {
|
|
286
|
+
allowedActorOpenIds: input.origin.allowedActorOpenIds,
|
|
287
|
+
...input.origin.conversationId === void 0 ? {} : { conversationId: input.origin.conversationId },
|
|
288
|
+
endpointId: input.origin.endpointId,
|
|
289
|
+
tenantKey: input.origin.tenantKey
|
|
290
|
+
},
|
|
291
|
+
policyEpoch: input.policyEpoch,
|
|
292
|
+
runId: `mcp:${randomUUID()}`,
|
|
293
|
+
sessionKey: input.sessionKey,
|
|
294
|
+
sourceMessageId: input.sourceMessageId ?? `mcp:${randomUUID()}`
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
function toToolOrigin(origin) {
|
|
298
|
+
return {
|
|
299
|
+
allowedActorOpenIds: origin.allowedActorOpenIds,
|
|
300
|
+
endpointId: origin.endpointId,
|
|
301
|
+
tenantKey: origin.tenantKey,
|
|
302
|
+
...origin.conversationId === void 0 ? {} : { conversationId: origin.conversationId }
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
function readObject(input, allowed) {
|
|
306
|
+
return readBackgroundSessionObject(input, allowed, (message) => new BackgroundSessionControlError(message));
|
|
307
|
+
}
|
|
308
|
+
function readString(value, name) {
|
|
309
|
+
return readBackgroundSessionString(value, name, (message) => new BackgroundSessionControlError(message));
|
|
310
|
+
}
|
|
311
|
+
function readInteger(value, name) {
|
|
312
|
+
return readBackgroundSessionInteger(value, name, (message) => new BackgroundSessionControlError(message));
|
|
313
|
+
}
|
|
314
|
+
function readPhase(value) {
|
|
315
|
+
return readBackgroundSessionPhase(value, (message) => new BackgroundSessionControlError(message));
|
|
316
|
+
}
|
|
317
|
+
//#endregion
|
|
318
|
+
//#region src/infrastructure/http/background-session-control-http-server.ts
|
|
319
|
+
var BackgroundSessionControlHttpError = class extends Error {
|
|
320
|
+
statusCode;
|
|
321
|
+
name = "BackgroundSessionControlHttpError";
|
|
322
|
+
constructor(statusCode, message) {
|
|
323
|
+
super(message);
|
|
324
|
+
this.statusCode = statusCode;
|
|
325
|
+
}
|
|
326
|
+
};
|
|
327
|
+
const COMMANDS = [
|
|
328
|
+
"start",
|
|
329
|
+
"wait",
|
|
330
|
+
"list",
|
|
331
|
+
"status",
|
|
332
|
+
"send",
|
|
333
|
+
"stop"
|
|
334
|
+
];
|
|
335
|
+
function createBackgroundSessionControlHttpServer(options) {
|
|
336
|
+
let server;
|
|
337
|
+
let boundPort = options.port;
|
|
338
|
+
const serverHandle = createServer((request, response) => {
|
|
339
|
+
(async () => {
|
|
340
|
+
try {
|
|
341
|
+
if (request.method !== "POST" || request.url !== "/background-sessions") throw new BackgroundSessionControlHttpError(404, "not found");
|
|
342
|
+
if (request.headers.authorization !== `Bearer ${options.token}`) throw new BackgroundSessionControlHttpError(401, "unauthorized");
|
|
343
|
+
const envelope = parseEnvelope(await readBody(request, 64 * 1024));
|
|
344
|
+
const command = envelope.command;
|
|
345
|
+
if (!COMMANDS.includes(command)) throw new BackgroundSessionControlHttpError(400, `unsupported background session command: ${String(command)}`);
|
|
346
|
+
respond(response, 200, {
|
|
347
|
+
ok: true,
|
|
348
|
+
result: await options.control.handle(command, envelope.context, envelope.input)
|
|
349
|
+
});
|
|
350
|
+
} catch (error) {
|
|
351
|
+
respond(response, error instanceof BackgroundSessionControlHttpError ? error.statusCode : 500, {
|
|
352
|
+
error: {
|
|
353
|
+
message: error instanceof Error ? error.message : String(error),
|
|
354
|
+
name: error instanceof Error ? error.name : "Error"
|
|
355
|
+
},
|
|
356
|
+
ok: false
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
})();
|
|
360
|
+
});
|
|
361
|
+
serverHandle.on("error", () => void 0);
|
|
362
|
+
return {
|
|
363
|
+
port: () => boundPort,
|
|
364
|
+
close: async () => {
|
|
365
|
+
const active = server;
|
|
366
|
+
server = void 0;
|
|
367
|
+
if (active) await new Promise((resolve) => active.close(() => resolve()));
|
|
368
|
+
},
|
|
369
|
+
start: async () => {
|
|
370
|
+
if (server) return;
|
|
371
|
+
await new Promise((resolve) => {
|
|
372
|
+
server = serverHandle;
|
|
373
|
+
serverHandle.listen(options.port, options.host ?? "127.0.0.1", () => {
|
|
374
|
+
const address = serverHandle.address();
|
|
375
|
+
if (address !== null && typeof address === "object") boundPort = address.port;
|
|
376
|
+
resolve();
|
|
377
|
+
});
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
function respond(response, statusCode, body) {
|
|
383
|
+
response.writeHead(statusCode, { "content-type": "application/json" });
|
|
384
|
+
response.end(JSON.stringify(body));
|
|
385
|
+
}
|
|
386
|
+
function parseEnvelope(body) {
|
|
387
|
+
const parsed = JSON.parse(body);
|
|
388
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) throw new BackgroundSessionControlHttpError(400, "invalid background session control envelope");
|
|
389
|
+
const record = parsed;
|
|
390
|
+
if (typeof record.command !== "string" || record.context === void 0) throw new BackgroundSessionControlHttpError(400, "background session control envelope requires command and context");
|
|
391
|
+
return {
|
|
392
|
+
command: record.command,
|
|
393
|
+
context: record.context,
|
|
394
|
+
input: record.input
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
function readBody(request, maxBytes) {
|
|
398
|
+
return new Promise((resolve, reject) => {
|
|
399
|
+
const chunks = [];
|
|
400
|
+
let total = 0;
|
|
401
|
+
request.on("data", (chunk) => {
|
|
402
|
+
total += chunk.length;
|
|
403
|
+
if (total > maxBytes) {
|
|
404
|
+
reject(new BackgroundSessionControlHttpError(413, "background session control request too large"));
|
|
405
|
+
return;
|
|
406
|
+
}
|
|
407
|
+
chunks.push(chunk);
|
|
408
|
+
});
|
|
409
|
+
request.on("end", () => resolve(Buffer.concat(chunks).toString("utf8")));
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
//#endregion
|
|
413
|
+
//#region src/mcp.ts
|
|
414
|
+
if (pathToFileURL(process.argv[1] ?? "").href === import.meta.url) await runBackgroundSessionMcpServer();
|
|
415
|
+
//#endregion
|
|
416
|
+
export { BACKGROUND_SESSION_MCP_SERVER_NAME, BACKGROUND_SESSION_MCP_SERVER_VERSION, BackgroundSessionControlError, BackgroundSessionControlHttpError, BackgroundSessionMcpError, createBackgroundSessionControl, createBackgroundSessionControlHttpServer, createBackgroundSessionMcpServer, runBackgroundSessionMcpServer, toControlContext };
|
package/dist/rivus-daemon-cli.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { t as MEMORY_SCOPES } from "./agent-memory.js";
|
|
2
|
-
import {
|
|
2
|
+
import { f as narrowBackgroundSessionDefinition } from "./background-session-authority.js";
|
|
3
|
+
import { n as resolveRivusAgentDefinition, r as deepFreeze, t as createRivusPluginCatalog } from "./rivus-plugin-registry.js";
|
|
3
4
|
import { createRequire } from "node:module";
|
|
4
5
|
import { Effect } from "effect";
|
|
5
6
|
import { createHash, randomUUID } from "node:crypto";
|
|
@@ -1,227 +1,6 @@
|
|
|
1
1
|
import { c as InvalidRivusPlugin, o as createRivusMemoryToolContract, r as RIVUS_MEMORY_TOOL_PLUGIN_ID, t as MEMORY_SCOPES } from "./agent-memory.js";
|
|
2
|
+
import { u as extendBackgroundSessionDefinition } from "./background-session-authority.js";
|
|
2
3
|
import { createHash } from "node:crypto";
|
|
3
|
-
//#region src/application/background-session/background-session-authority.ts
|
|
4
|
-
const BACKGROUND_SESSION_TOOL_IDS = [
|
|
5
|
-
"background.start",
|
|
6
|
-
"background.wait",
|
|
7
|
-
"background.list",
|
|
8
|
-
"background.status",
|
|
9
|
-
"background.send",
|
|
10
|
-
"background.stop"
|
|
11
|
-
];
|
|
12
|
-
const BACKGROUND_SESSION_START_TOOL_ID = "background.start";
|
|
13
|
-
const BACKGROUND_SESSION_TOOL_PLUGIN_ID = "rivus-core";
|
|
14
|
-
const BACKGROUND_SESSION_TOOL_VERSION = "1.0.0";
|
|
15
|
-
const BACKGROUND_SESSION_SESSION_KEY_PREFIX = "background";
|
|
16
|
-
function createBackgroundSessionKey(sessionId) {
|
|
17
|
-
return `${BACKGROUND_SESSION_SESSION_KEY_PREFIX}:${sessionId}`;
|
|
18
|
-
}
|
|
19
|
-
function createBackgroundSessionStepSourceMessageId(sessionId, stepCount) {
|
|
20
|
-
return `bg:${sessionId}:step:${stepCount}`;
|
|
21
|
-
}
|
|
22
|
-
function createBackgroundSessionToolContracts() {
|
|
23
|
-
return [
|
|
24
|
-
Object.freeze({
|
|
25
|
-
description: "Start a background agent session. Use when the request must wait for external changes, observe over time, or continue working after the foreground run ends. Returns a stable session id immediately; the foreground response can finish here. The detached session continues with the granted Skills, CLI, Tools, Project Space, and Memory of this agent.",
|
|
26
|
-
digest: contractDigest("background.start"),
|
|
27
|
-
id: "background.start",
|
|
28
|
-
idempotency: "supported",
|
|
29
|
-
inputSchema: Object.freeze({
|
|
30
|
-
additionalProperties: false,
|
|
31
|
-
properties: Object.freeze({
|
|
32
|
-
displayName: {
|
|
33
|
-
type: "string",
|
|
34
|
-
maxLength: 200
|
|
35
|
-
},
|
|
36
|
-
prompt: {
|
|
37
|
-
type: "string",
|
|
38
|
-
minLength: 1,
|
|
39
|
-
maxLength: 2e4
|
|
40
|
-
}
|
|
41
|
-
}),
|
|
42
|
-
required: ["prompt"],
|
|
43
|
-
type: "object"
|
|
44
|
-
}),
|
|
45
|
-
pluginId: BACKGROUND_SESSION_TOOL_PLUGIN_ID,
|
|
46
|
-
risk: "mutate",
|
|
47
|
-
version: BACKGROUND_SESSION_TOOL_VERSION
|
|
48
|
-
}),
|
|
49
|
-
Object.freeze({
|
|
50
|
-
description: "Pause the current background session durably and end the current step. Call with delayMs to resume after a delay, with until to resume at an absolute ISO time, or with neither to wait for user input. After this call no further tool calls are accepted in this step.",
|
|
51
|
-
digest: contractDigest("background.wait"),
|
|
52
|
-
id: "background.wait",
|
|
53
|
-
idempotency: "supported",
|
|
54
|
-
inputSchema: Object.freeze({
|
|
55
|
-
additionalProperties: false,
|
|
56
|
-
properties: Object.freeze({
|
|
57
|
-
delayMs: {
|
|
58
|
-
type: "integer",
|
|
59
|
-
minimum: 1e3,
|
|
60
|
-
maximum: 864e5
|
|
61
|
-
},
|
|
62
|
-
reason: {
|
|
63
|
-
type: "string",
|
|
64
|
-
maxLength: 500
|
|
65
|
-
},
|
|
66
|
-
until: {
|
|
67
|
-
type: "string",
|
|
68
|
-
maxLength: 64
|
|
69
|
-
}
|
|
70
|
-
}),
|
|
71
|
-
type: "object"
|
|
72
|
-
}),
|
|
73
|
-
pluginId: BACKGROUND_SESSION_TOOL_PLUGIN_ID,
|
|
74
|
-
risk: "mutate",
|
|
75
|
-
version: BACKGROUND_SESSION_TOOL_VERSION
|
|
76
|
-
}),
|
|
77
|
-
Object.freeze({
|
|
78
|
-
description: "List background sessions owned by this conversation, newest first. Optionally filter by phase and limit the number of results.",
|
|
79
|
-
digest: contractDigest("background.list"),
|
|
80
|
-
id: "background.list",
|
|
81
|
-
idempotency: "supported",
|
|
82
|
-
inputSchema: Object.freeze({
|
|
83
|
-
additionalProperties: false,
|
|
84
|
-
properties: Object.freeze({
|
|
85
|
-
limit: {
|
|
86
|
-
type: "integer",
|
|
87
|
-
minimum: 1,
|
|
88
|
-
maximum: 50
|
|
89
|
-
},
|
|
90
|
-
phase: {
|
|
91
|
-
enum: [
|
|
92
|
-
"queued",
|
|
93
|
-
"running",
|
|
94
|
-
"waiting",
|
|
95
|
-
"input-required",
|
|
96
|
-
"stopping",
|
|
97
|
-
"stopped",
|
|
98
|
-
"completed",
|
|
99
|
-
"failed",
|
|
100
|
-
"reconciliation-required"
|
|
101
|
-
],
|
|
102
|
-
type: "string"
|
|
103
|
-
}
|
|
104
|
-
}),
|
|
105
|
-
type: "object"
|
|
106
|
-
}),
|
|
107
|
-
pluginId: BACKGROUND_SESSION_TOOL_PLUGIN_ID,
|
|
108
|
-
risk: "observe",
|
|
109
|
-
version: BACKGROUND_SESSION_TOOL_VERSION
|
|
110
|
-
}),
|
|
111
|
-
Object.freeze({
|
|
112
|
-
description: "Return the current phase, step counts, wake time, and result of one background session owned by this conversation.",
|
|
113
|
-
digest: contractDigest("background.status"),
|
|
114
|
-
id: "background.status",
|
|
115
|
-
idempotency: "supported",
|
|
116
|
-
inputSchema: Object.freeze({
|
|
117
|
-
additionalProperties: false,
|
|
118
|
-
properties: Object.freeze({ sessionId: {
|
|
119
|
-
type: "string",
|
|
120
|
-
minLength: 1,
|
|
121
|
-
maxLength: 200
|
|
122
|
-
} }),
|
|
123
|
-
required: ["sessionId"],
|
|
124
|
-
type: "object"
|
|
125
|
-
}),
|
|
126
|
-
pluginId: BACKGROUND_SESSION_TOOL_PLUGIN_ID,
|
|
127
|
-
risk: "observe",
|
|
128
|
-
version: BACKGROUND_SESSION_TOOL_VERSION
|
|
129
|
-
}),
|
|
130
|
-
Object.freeze({
|
|
131
|
-
description: "Send new user instruction text to a background session owned by this conversation and wake it. The input is delivered exactly once in the next step.",
|
|
132
|
-
digest: contractDigest("background.send"),
|
|
133
|
-
id: "background.send",
|
|
134
|
-
idempotency: "supported",
|
|
135
|
-
inputSchema: Object.freeze({
|
|
136
|
-
additionalProperties: false,
|
|
137
|
-
properties: Object.freeze({
|
|
138
|
-
message: {
|
|
139
|
-
type: "string",
|
|
140
|
-
minLength: 1,
|
|
141
|
-
maxLength: 2e4
|
|
142
|
-
},
|
|
143
|
-
sessionId: {
|
|
144
|
-
type: "string",
|
|
145
|
-
minLength: 1,
|
|
146
|
-
maxLength: 200
|
|
147
|
-
}
|
|
148
|
-
}),
|
|
149
|
-
required: ["message", "sessionId"],
|
|
150
|
-
type: "object"
|
|
151
|
-
}),
|
|
152
|
-
pluginId: BACKGROUND_SESSION_TOOL_PLUGIN_ID,
|
|
153
|
-
risk: "mutate",
|
|
154
|
-
version: BACKGROUND_SESSION_TOOL_VERSION
|
|
155
|
-
}),
|
|
156
|
-
Object.freeze({
|
|
157
|
-
description: "Stop a background session owned by this conversation. Persists the cancellation, aborts the active step and its owned process, and delivers a terminal notice.",
|
|
158
|
-
digest: contractDigest("background.stop"),
|
|
159
|
-
id: "background.stop",
|
|
160
|
-
idempotency: "supported",
|
|
161
|
-
inputSchema: Object.freeze({
|
|
162
|
-
additionalProperties: false,
|
|
163
|
-
properties: Object.freeze({
|
|
164
|
-
reason: {
|
|
165
|
-
type: "string",
|
|
166
|
-
maxLength: 500
|
|
167
|
-
},
|
|
168
|
-
sessionId: {
|
|
169
|
-
type: "string",
|
|
170
|
-
minLength: 1,
|
|
171
|
-
maxLength: 200
|
|
172
|
-
}
|
|
173
|
-
}),
|
|
174
|
-
required: ["sessionId"],
|
|
175
|
-
type: "object"
|
|
176
|
-
}),
|
|
177
|
-
pluginId: BACKGROUND_SESSION_TOOL_PLUGIN_ID,
|
|
178
|
-
risk: "mutate",
|
|
179
|
-
version: BACKGROUND_SESSION_TOOL_VERSION
|
|
180
|
-
})
|
|
181
|
-
];
|
|
182
|
-
}
|
|
183
|
-
function backgroundSessionToolIds() {
|
|
184
|
-
return [...BACKGROUND_SESSION_TOOL_IDS];
|
|
185
|
-
}
|
|
186
|
-
function isBackgroundSessionToolId(toolId) {
|
|
187
|
-
return BACKGROUND_SESSION_TOOL_IDS.includes(toolId);
|
|
188
|
-
}
|
|
189
|
-
function extendBackgroundSessionDefinition(definition) {
|
|
190
|
-
const contracts = createBackgroundSessionToolContracts();
|
|
191
|
-
const existingIds = new Set(definition.tools.map(({ id }) => id));
|
|
192
|
-
const additions = contracts.filter((contract) => !existingIds.has(contract.id));
|
|
193
|
-
const toolGrantSet = Object.freeze({
|
|
194
|
-
revision: grantRevision(definition.toolGrantSet.revision, additions.map(({ id }) => id)),
|
|
195
|
-
toolIds: Object.freeze([...definition.toolGrantSet.toolIds, ...additions.map(({ id }) => id)].sort())
|
|
196
|
-
});
|
|
197
|
-
return Object.freeze({
|
|
198
|
-
...definition,
|
|
199
|
-
tools: Object.freeze([...definition.tools, ...additions]),
|
|
200
|
-
toolGrantSet
|
|
201
|
-
});
|
|
202
|
-
}
|
|
203
|
-
function narrowBackgroundSessionDefinition(definition) {
|
|
204
|
-
const childToolIds = definition.toolGrantSet.toolIds.filter((id) => id !== BACKGROUND_SESSION_START_TOOL_ID);
|
|
205
|
-
const toolGrantSet = Object.freeze({
|
|
206
|
-
revision: grantRevision(definition.toolGrantSet.revision, childToolIds),
|
|
207
|
-
toolIds: Object.freeze(childToolIds)
|
|
208
|
-
});
|
|
209
|
-
return Object.freeze({
|
|
210
|
-
...definition,
|
|
211
|
-
tools: Object.freeze(definition.tools.filter(({ id }) => id !== BACKGROUND_SESSION_START_TOOL_ID)),
|
|
212
|
-
toolGrantSet
|
|
213
|
-
});
|
|
214
|
-
}
|
|
215
|
-
function grantRevision(parentRevision, toolIds) {
|
|
216
|
-
return `sha256:${createHash("sha256").update(JSON.stringify({
|
|
217
|
-
parentRevision,
|
|
218
|
-
toolIds: [...toolIds].sort()
|
|
219
|
-
})).digest("hex")}`;
|
|
220
|
-
}
|
|
221
|
-
function contractDigest(toolId) {
|
|
222
|
-
return `sha256:${createHash("sha256").update(`background-tool:${toolId}:${BACKGROUND_SESSION_TOOL_VERSION}`).digest("hex")}`;
|
|
223
|
-
}
|
|
224
|
-
//#endregion
|
|
225
4
|
//#region src/application/plugin/deep-freeze.ts
|
|
226
5
|
function deepFreeze(value) {
|
|
227
6
|
if (value !== null && typeof value === "object" && !Object.isFrozen(value)) {
|
|
@@ -433,4 +212,4 @@ function stableJson(value) {
|
|
|
433
212
|
return JSON.stringify(value);
|
|
434
213
|
}
|
|
435
214
|
//#endregion
|
|
436
|
-
export {
|
|
215
|
+
export { resolveRivusAgentDefinition as n, deepFreeze as r, createRivusPluginCatalog as t };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rivus/agent",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "A local agent daemon core built around a usable agent harness and domain events.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -41,6 +41,10 @@
|
|
|
41
41
|
"./testing": {
|
|
42
42
|
"types": "./dist/testing/index.d.ts",
|
|
43
43
|
"import": "./dist/testing/index.js"
|
|
44
|
+
},
|
|
45
|
+
"./mcp": {
|
|
46
|
+
"types": "./dist/mcp.d.ts",
|
|
47
|
+
"import": "./dist/mcp.js"
|
|
44
48
|
}
|
|
45
49
|
},
|
|
46
50
|
"files": [
|