@rivus/agent 0.4.1 → 0.5.1
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 +3 -3
- package/dist/agent-memory.d.ts +4 -2
- package/dist/cli.js +1 -1
- package/dist/index.d.ts +88 -1
- package/dist/index.js +130 -16
- package/dist/pi.d.ts +12 -0
- package/dist/pi.js +33 -0
- package/dist/rivus-daemon-cli.js +145 -12
- package/dist/rivus-plugin-registry.js +10 -2
- package/dist/rivus-plugin-testkit.d.ts +3 -0
- package/examples/pi-feishu-deployment.bootstrap.ts +39 -9
- package/package.json +5 -1
package/dist/rivus-daemon-cli.js
CHANGED
|
@@ -3,8 +3,8 @@ import { createRequire } from "node:module";
|
|
|
3
3
|
import { Effect } from "effect";
|
|
4
4
|
import { createHash, randomUUID } from "node:crypto";
|
|
5
5
|
import { pathToFileURL } from "node:url";
|
|
6
|
-
import { dirname, isAbsolute, join, relative, sep } from "node:path";
|
|
7
|
-
import { open, readFile, realpath, stat } from "node:fs/promises";
|
|
6
|
+
import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
7
|
+
import { lstat, open, readFile, readdir, realpath, stat } from "node:fs/promises";
|
|
8
8
|
import { constants } from "node:fs";
|
|
9
9
|
//#region src/application/plugin/rivus-automation-runtime-definition.ts
|
|
10
10
|
function resolveRivusAutomationRuntimeDefinition(definition, requestedToolIds, requestedSkillIds) {
|
|
@@ -108,7 +108,12 @@ async function loadRivusDeployment(options) {
|
|
|
108
108
|
continue;
|
|
109
109
|
}
|
|
110
110
|
try {
|
|
111
|
-
const
|
|
111
|
+
const baseDefinition = resolveRivusAgentDefinition(catalog, agent);
|
|
112
|
+
const projectSpace = agent.projectSpaceId ? options.manifest.projectSpaces?.find(({ id }) => id === agent.projectSpaceId) : void 0;
|
|
113
|
+
const definition = projectSpace ? deepFreeze({
|
|
114
|
+
...baseDefinition,
|
|
115
|
+
projectSpaceRevision: `project-space-declaration:${JSON.stringify(projectSpace)}`
|
|
116
|
+
}) : baseDefinition;
|
|
112
117
|
definitions.push(definition);
|
|
113
118
|
agentStatuses.push(Object.freeze({
|
|
114
119
|
agentId: agent.agentId,
|
|
@@ -158,6 +163,20 @@ async function loadRivusDeployment(options) {
|
|
|
158
163
|
});
|
|
159
164
|
}
|
|
160
165
|
function validateRivusDeploymentManifest(manifest) {
|
|
166
|
+
const projectSpaceIds = /* @__PURE__ */ new Set();
|
|
167
|
+
for (const projectSpace of manifest.projectSpaces ?? []) {
|
|
168
|
+
if (projectSpaceIds.has(projectSpace.id)) throw new Error(`duplicate project space: ${projectSpace.id}`);
|
|
169
|
+
projectSpaceIds.add(projectSpace.id);
|
|
170
|
+
validateRelativeProjectPath(projectSpace.root, `project space ${projectSpace.id} root`);
|
|
171
|
+
validateRelativeProjectPath(projectSpace.workingDirectory, `project space ${projectSpace.id} working directory`);
|
|
172
|
+
if (projectSpace.skills.sources.length === 0) throw new Error(`project space ${projectSpace.id} must declare at least one Skill source`);
|
|
173
|
+
const sources = /* @__PURE__ */ new Set();
|
|
174
|
+
for (const source of projectSpace.skills.sources) {
|
|
175
|
+
validateRelativeProjectPath(source, `project space ${projectSpace.id} Skill source`);
|
|
176
|
+
if (sources.has(source)) throw new Error(`project space ${projectSpace.id} contains duplicate Skill source: ${source}`);
|
|
177
|
+
sources.add(source);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
161
180
|
const pluginIds = /* @__PURE__ */ new Set();
|
|
162
181
|
for (const plugin of manifest.plugins) {
|
|
163
182
|
if (pluginIds.has(plugin.id)) throw new Error(`duplicate plugin declaration: ${plugin.id}`);
|
|
@@ -170,6 +189,7 @@ function validateRivusDeploymentManifest(manifest) {
|
|
|
170
189
|
if (agentIds.has(agent.agentId)) throw new Error(`duplicate agent deployment: ${agent.agentId}`);
|
|
171
190
|
agentIds.add(agent.agentId);
|
|
172
191
|
if (!pluginIds.has(agent.pluginId)) throw new Error(`agent ${agent.agentId} references undeclared plugin: ${agent.pluginId}`);
|
|
192
|
+
if (agent.projectSpaceId && !projectSpaceIds.has(agent.projectSpaceId)) throw new Error(`agent ${agent.agentId} references unknown project space: ${agent.projectSpaceId}`);
|
|
173
193
|
agentById.set(agent.agentId, agent);
|
|
174
194
|
}
|
|
175
195
|
const automationIds = /* @__PURE__ */ new Set();
|
|
@@ -204,6 +224,9 @@ function validateRivusDeploymentManifest(manifest) {
|
|
|
204
224
|
if (defaultEndpoint.agentId !== defaultAgent.agentId) throw new Error("default endpoint is not bound to the default agent");
|
|
205
225
|
if (!defaultEndpoint.enabled) throw new Error("default endpoint must be enabled");
|
|
206
226
|
}
|
|
227
|
+
function validateRelativeProjectPath(value, owner) {
|
|
228
|
+
if (value.trim() === "" || isAbsolute(value) || value.includes("\0")) throw new Error(`${owner} must be a non-empty relative path`);
|
|
229
|
+
}
|
|
207
230
|
function validateModuleSpecifier(moduleSpecifier) {
|
|
208
231
|
if (moduleSpecifier.trim() === "" || isAbsolute(moduleSpecifier) || /^[a-z][a-z+.-]*:/i.test(moduleSpecifier) || moduleSpecifier.includes("\0")) throw new Error(`invalid plugin module specifier: ${moduleSpecifier}`);
|
|
209
232
|
if ((moduleSpecifier.startsWith("./") || moduleSpecifier.startsWith("../")) && moduleSpecifier.split(/[\\/]/).includes("..")) throw new Error(`plugin module escapes deployment root: ${moduleSpecifier}`);
|
|
@@ -321,8 +344,9 @@ function parseManifest(value) {
|
|
|
321
344
|
"defaultAgentId",
|
|
322
345
|
"defaultEndpointId",
|
|
323
346
|
"endpoints",
|
|
324
|
-
"plugins"
|
|
325
|
-
|
|
347
|
+
"plugins",
|
|
348
|
+
"projectSpaces"
|
|
349
|
+
], "manifest", ["automations", "projectSpaces"]);
|
|
326
350
|
const plugins = array(root.plugins, "manifest.plugins").map((entry, index) => {
|
|
327
351
|
const plugin = record(entry, `manifest.plugins[${index}]`);
|
|
328
352
|
exactKeys(plugin, [
|
|
@@ -344,9 +368,10 @@ function parseManifest(value) {
|
|
|
344
368
|
"memory",
|
|
345
369
|
"pluginId",
|
|
346
370
|
"profileId",
|
|
371
|
+
"projectSpaceId",
|
|
347
372
|
"skills",
|
|
348
373
|
"tools"
|
|
349
|
-
], `manifest.agents[${index}]`, ["memory"]);
|
|
374
|
+
], `manifest.agents[${index}]`, ["memory", "projectSpaceId"]);
|
|
350
375
|
const memory = agent.memory === void 0 ? void 0 : record(agent.memory, `manifest.agents[${index}].memory`);
|
|
351
376
|
if (memory) exactKeys(memory, ["scopes", "tool"], `manifest.agents[${index}].memory`);
|
|
352
377
|
const skills = record(agent.skills, `manifest.agents[${index}].skills`);
|
|
@@ -362,6 +387,7 @@ function parseManifest(value) {
|
|
|
362
387
|
}) } : {},
|
|
363
388
|
pluginId: string(agent.pluginId, `manifest.agents[${index}].pluginId`),
|
|
364
389
|
profileId: string(agent.profileId, `manifest.agents[${index}].profileId`),
|
|
390
|
+
...agent.projectSpaceId === void 0 ? {} : { projectSpaceId: string(agent.projectSpaceId, `manifest.agents[${index}].projectSpaceId`) },
|
|
365
391
|
skills: Object.freeze({ allow: Object.freeze(array(skills.allow, `manifest.agents[${index}].skills.allow`).map((item, itemIndex) => string(item, `manifest.agents[${index}].skills.allow[${itemIndex}]`))) }),
|
|
366
392
|
tools: Object.freeze({ allow: Object.freeze(array(tools.allow, `manifest.agents[${index}].tools.allow`).map((item, itemIndex) => string(item, `manifest.agents[${index}].tools.allow[${itemIndex}]`))) })
|
|
367
393
|
});
|
|
@@ -428,13 +454,31 @@ function parseManifest(value) {
|
|
|
428
454
|
timeZone: string(automation.timeZone, `manifest.automations[${index}].timeZone`)
|
|
429
455
|
});
|
|
430
456
|
});
|
|
457
|
+
const projectSpaces = array(root.projectSpaces ?? [], "manifest.projectSpaces").map((entry, index) => {
|
|
458
|
+
const projectSpace = record(entry, `manifest.projectSpaces[${index}]`);
|
|
459
|
+
exactKeys(projectSpace, [
|
|
460
|
+
"id",
|
|
461
|
+
"root",
|
|
462
|
+
"skills",
|
|
463
|
+
"workingDirectory"
|
|
464
|
+
], `manifest.projectSpaces[${index}]`);
|
|
465
|
+
const skills = record(projectSpace.skills, `manifest.projectSpaces[${index}].skills`);
|
|
466
|
+
exactKeys(skills, ["sources"], `manifest.projectSpaces[${index}].skills`);
|
|
467
|
+
return Object.freeze({
|
|
468
|
+
id: string(projectSpace.id, `manifest.projectSpaces[${index}].id`),
|
|
469
|
+
root: string(projectSpace.root, `manifest.projectSpaces[${index}].root`),
|
|
470
|
+
skills: Object.freeze({ sources: Object.freeze(array(skills.sources, `manifest.projectSpaces[${index}].skills.sources`).map((item, itemIndex) => string(item, `manifest.projectSpaces[${index}].skills.sources[${itemIndex}]`))) }),
|
|
471
|
+
workingDirectory: string(projectSpace.workingDirectory, `manifest.projectSpaces[${index}].workingDirectory`)
|
|
472
|
+
});
|
|
473
|
+
});
|
|
431
474
|
return Object.freeze({
|
|
432
475
|
agents: Object.freeze(agents),
|
|
433
476
|
automations: Object.freeze(automations),
|
|
434
477
|
defaultAgentId: string(root.defaultAgentId, "manifest.defaultAgentId"),
|
|
435
478
|
defaultEndpointId: string(root.defaultEndpointId, "manifest.defaultEndpointId"),
|
|
436
479
|
endpoints: Object.freeze(endpoints),
|
|
437
|
-
plugins: Object.freeze(plugins)
|
|
480
|
+
plugins: Object.freeze(plugins),
|
|
481
|
+
projectSpaces: Object.freeze(projectSpaces)
|
|
438
482
|
});
|
|
439
483
|
}
|
|
440
484
|
function record(value, path) {
|
|
@@ -458,7 +502,7 @@ function positiveInteger(value, path) {
|
|
|
458
502
|
return value;
|
|
459
503
|
}
|
|
460
504
|
function memoryScope(value, path) {
|
|
461
|
-
if (typeof value !== "string" || !MEMORY_SCOPES.includes(value)) throw new Error(`${path} must be conversation, agent-private, or shared-user-profile`);
|
|
505
|
+
if (typeof value !== "string" || !MEMORY_SCOPES.includes(value)) throw new Error(`${path} must be conversation, agent-private, project, or shared-user-profile`);
|
|
462
506
|
return value;
|
|
463
507
|
}
|
|
464
508
|
function groupPolicy(value, path) {
|
|
@@ -796,6 +840,9 @@ function createAgentInstanceRegistry(options = {}) {
|
|
|
796
840
|
const runtimeGenerationId = createStableId("generation", {
|
|
797
841
|
agentId: definition.agentId,
|
|
798
842
|
profileRevision: definition.profileRevision,
|
|
843
|
+
projectSpaceId: definition.projectSpaceId ?? null,
|
|
844
|
+
projectSpaceRevision: definition.projectSpaceRevision ?? null,
|
|
845
|
+
skillGrantRevision: definition.skillGrantSet.revision,
|
|
799
846
|
toolGrantRevision: definition.toolGrantSet.revision
|
|
800
847
|
});
|
|
801
848
|
const bindingId = binding.kind === "endpoint" ? binding.endpointId : binding.automationId;
|
|
@@ -955,16 +1002,26 @@ var RivusDeploymentAutomationReadinessError = class extends Error {
|
|
|
955
1002
|
};
|
|
956
1003
|
async function createRivusDeploymentDaemon(options) {
|
|
957
1004
|
const deployment = await loadRivusDeployment(options);
|
|
1005
|
+
if ((deployment.manifest.projectSpaces?.length ?? 0) > 0 && !options.resolveProjectSpace) throw new RivusDeploymentDaemonLifecycleError("deployment bootstrap does not provide a Project Space resolver");
|
|
1006
|
+
const projectSpaces = new Map(await Promise.all((deployment.manifest.projectSpaces ?? []).map(async (declaration) => {
|
|
1007
|
+
const projectSpace = await options.resolveProjectSpace({
|
|
1008
|
+
declaration,
|
|
1009
|
+
deploymentRoot: options.deploymentRoot
|
|
1010
|
+
});
|
|
1011
|
+
return [projectSpace.id, projectSpace];
|
|
1012
|
+
})));
|
|
958
1013
|
const definitions = new Map(deployment.definitions.map((definition) => [definition.agentId, definition]));
|
|
959
1014
|
const automationDefinitions = new Map(deployment.automationDefinitions.map((definition) => [definition.id, definition]));
|
|
960
1015
|
const runtimePool = createAgentRuntimePool({
|
|
961
1016
|
createRuntime: (instance) => {
|
|
962
1017
|
const definition = instance.binding.kind === "automation" ? automationDefinitions.get(instance.binding.automationId)?.runtimeDefinition : definitions.get(instance.agentId);
|
|
963
1018
|
if (!definition) throw new RivusDeploymentDaemonLifecycleError(`runtime instance references unknown agent: ${instance.agentId}`);
|
|
1019
|
+
const projectSpace = definition.projectSpaceId ? projectSpaces.get(definition.projectSpaceId) : void 0;
|
|
964
1020
|
return options.createRuntime({
|
|
965
1021
|
...instance,
|
|
966
1022
|
catalog: deployment.catalog,
|
|
967
|
-
definition
|
|
1023
|
+
definition,
|
|
1024
|
+
...projectSpace ? { projectSpace } : {}
|
|
968
1025
|
});
|
|
969
1026
|
},
|
|
970
1027
|
registry: createAgentInstanceRegistry(options.initialInstanceRecords ? { initialRecords: options.initialInstanceRecords } : {})
|
|
@@ -1063,7 +1120,8 @@ async function createRivusDeploymentDaemon(options) {
|
|
|
1063
1120
|
definition: slot.definition,
|
|
1064
1121
|
endpointId: slot.definition.id,
|
|
1065
1122
|
handle: (input) => handleEndpoint(slot.definition.id, input),
|
|
1066
|
-
instanceId: host.resolveEndpoint(slot.definition.id).instanceId
|
|
1123
|
+
instanceId: host.resolveEndpoint(slot.definition.id).instanceId,
|
|
1124
|
+
...definitions.get(slot.definition.agentId)?.projectSpaceId ? { projectSpaceId: definitions.get(slot.definition.agentId).projectSpaceId } : {}
|
|
1067
1125
|
}));
|
|
1068
1126
|
degraded ||= slotDegraded;
|
|
1069
1127
|
}
|
|
@@ -1171,6 +1229,79 @@ async function stopSlots(slots, errors) {
|
|
|
1171
1229
|
}
|
|
1172
1230
|
}
|
|
1173
1231
|
//#endregion
|
|
1232
|
+
//#region src/domain/project-space.ts
|
|
1233
|
+
var InvalidRivusProjectSpace = class extends Error {
|
|
1234
|
+
name = "InvalidRivusProjectSpace";
|
|
1235
|
+
};
|
|
1236
|
+
//#endregion
|
|
1237
|
+
//#region src/infrastructure/workspace/rivus-project-space.ts
|
|
1238
|
+
async function resolveRivusProjectSpace(input) {
|
|
1239
|
+
const root = await resolveContainedDirectory(await resolveExistingDirectory(input.deploymentRoot, "deployment root"), input.declaration.root, "Project Space root");
|
|
1240
|
+
const workingDirectory = await resolveContainedDirectory(root, input.declaration.workingDirectory, "Project Space working directory");
|
|
1241
|
+
const skillPaths = await Promise.all(input.declaration.skills.sources.map((source) => resolveContainedPath(root, source, "Project Space Skill source")));
|
|
1242
|
+
await Promise.all(skillPaths.map((path) => assertTreeContainsNoSymbolicLinks(path)));
|
|
1243
|
+
return Object.freeze({
|
|
1244
|
+
id: input.declaration.id,
|
|
1245
|
+
revision: createStableId("project-space", {
|
|
1246
|
+
id: input.declaration.id,
|
|
1247
|
+
root,
|
|
1248
|
+
skillPaths,
|
|
1249
|
+
workingDirectory
|
|
1250
|
+
}),
|
|
1251
|
+
root,
|
|
1252
|
+
skillPaths: Object.freeze(skillPaths),
|
|
1253
|
+
workingDirectory
|
|
1254
|
+
});
|
|
1255
|
+
}
|
|
1256
|
+
async function assertTreeContainsNoSymbolicLinks(path) {
|
|
1257
|
+
const metadata = await lstat(path);
|
|
1258
|
+
if (metadata.isSymbolicLink()) throw new InvalidRivusProjectSpace(`Project Space Skill source contains a symbolic link: ${path}`);
|
|
1259
|
+
if (!metadata.isDirectory()) return;
|
|
1260
|
+
for (const entry of await readdir(path, { withFileTypes: true })) {
|
|
1261
|
+
const child = resolve(path, entry.name);
|
|
1262
|
+
if (entry.isSymbolicLink()) throw new InvalidRivusProjectSpace(`Project Space Skill source contains a symbolic link: ${child}`);
|
|
1263
|
+
if (entry.isDirectory()) await assertTreeContainsNoSymbolicLinks(child);
|
|
1264
|
+
}
|
|
1265
|
+
}
|
|
1266
|
+
async function resolveContainedDirectory(base, path, owner) {
|
|
1267
|
+
const resolved = await resolveContainedPath(base, path, owner);
|
|
1268
|
+
if (!(await stat(resolved)).isDirectory()) throw new InvalidRivusProjectSpace(`${owner} must be a directory: ${path}`);
|
|
1269
|
+
return resolved;
|
|
1270
|
+
}
|
|
1271
|
+
async function resolveContainedPath(base, path, owner) {
|
|
1272
|
+
validateRelativePath(path, owner);
|
|
1273
|
+
const candidate = resolve(base, path);
|
|
1274
|
+
assertContained(base, candidate, owner);
|
|
1275
|
+
let resolved;
|
|
1276
|
+
try {
|
|
1277
|
+
resolved = await realpath(candidate);
|
|
1278
|
+
} catch (cause) {
|
|
1279
|
+
throw new InvalidRivusProjectSpace(`${owner} does not exist: ${path}`, { cause });
|
|
1280
|
+
}
|
|
1281
|
+
assertContained(base, resolved, owner);
|
|
1282
|
+
const metadata = await stat(resolved);
|
|
1283
|
+
if (!metadata.isDirectory() && !metadata.isFile()) throw new InvalidRivusProjectSpace(`${owner} must be a file or directory: ${path}`);
|
|
1284
|
+
return resolved;
|
|
1285
|
+
}
|
|
1286
|
+
async function resolveExistingDirectory(path, owner) {
|
|
1287
|
+
let resolved;
|
|
1288
|
+
try {
|
|
1289
|
+
resolved = await realpath(path);
|
|
1290
|
+
} catch (cause) {
|
|
1291
|
+
throw new InvalidRivusProjectSpace(`${owner} does not exist`, { cause });
|
|
1292
|
+
}
|
|
1293
|
+
if (!(await stat(resolved)).isDirectory()) throw new InvalidRivusProjectSpace(`${owner} must be a directory`);
|
|
1294
|
+
return resolved;
|
|
1295
|
+
}
|
|
1296
|
+
function validateRelativePath(path, owner) {
|
|
1297
|
+
if (!path.trim() || path.includes("\0") || isAbsolute(path)) throw new InvalidRivusProjectSpace(`${owner} must be a non-empty relative path`);
|
|
1298
|
+
}
|
|
1299
|
+
function assertContained(base, candidate, owner) {
|
|
1300
|
+
const relation = relative(base, candidate);
|
|
1301
|
+
if (relation === "" || !relation.startsWith("..") && !isAbsolute(relation)) return;
|
|
1302
|
+
throw new InvalidRivusProjectSpace(`${owner} escapes its trusted root`);
|
|
1303
|
+
}
|
|
1304
|
+
//#endregion
|
|
1174
1305
|
//#region src/infrastructure/runtime/configured-rivus-deployment-daemon.ts
|
|
1175
1306
|
async function createConfiguredRivusDeploymentDaemon(options) {
|
|
1176
1307
|
const manifest = await loadRivusDeploymentManifest(options.manifestPath, options.manifestOptions);
|
|
@@ -1181,7 +1312,8 @@ async function createConfiguredRivusDeploymentDaemon(options) {
|
|
|
1181
1312
|
deploymentRoot: dirname(options.manifestPath),
|
|
1182
1313
|
...options.initialInstanceRecords ? { initialInstanceRecords: options.initialInstanceRecords } : {},
|
|
1183
1314
|
loadModule: loadNodeRivusPluginModule,
|
|
1184
|
-
manifest
|
|
1315
|
+
manifest,
|
|
1316
|
+
resolveProjectSpace: (input) => resolveRivusProjectSpace(input)
|
|
1185
1317
|
});
|
|
1186
1318
|
}
|
|
1187
1319
|
//#endregion
|
|
@@ -1284,6 +1416,7 @@ async function createRivusDeploymentCliProcess(factory, context) {
|
|
|
1284
1416
|
memory: {
|
|
1285
1417
|
audience: "private",
|
|
1286
1418
|
...context.env.RIVUS_LOCAL_CONVERSATION_ID?.trim() ? { conversationId: context.env.RIVUS_LOCAL_CONVERSATION_ID.trim() } : {},
|
|
1419
|
+
...daemon.deployment.definitions.find(({ agentId }) => agentId === daemon.deployment.manifest.defaultAgentId)?.projectSpaceId ? { projectId: daemon.deployment.definitions.find(({ agentId }) => agentId === daemon.deployment.manifest.defaultAgentId).projectSpaceId } : {},
|
|
1287
1420
|
subjectId: context.env.RIVUS_LOCAL_SUBJECT_ID?.trim() || "local-operator",
|
|
1288
1421
|
tenantId: context.env.RIVUS_MEMORY_TENANT_ID?.trim() || "local"
|
|
1289
1422
|
},
|
|
@@ -2627,4 +2760,4 @@ function hasRecoveryRunner(daemon) {
|
|
|
2627
2760
|
return typeof daemon.openRecoveryControl === "function";
|
|
2628
2761
|
}
|
|
2629
2762
|
//#endregion
|
|
2630
|
-
export {
|
|
2763
|
+
export { loadMergedLocalEnvFile as A, loadRivusDaemonConfig as C, loadRivusDeploymentManifest as D, RivusDeploymentManifestError as E, loadRivusDeployment as M, validateRivusDeploymentManifest as N, FeishuEndpointCredentialError as O, RivusDaemonConfigError as S, resolveNodeRivusPluginModulePath as T, createStableId as _, InvalidRivusProjectSpace as a, createRivusEnvFromOpenClawConfig as b, RivusDeploymentReadinessError as c, createRivusAgentHost as d, AgentInstanceBusy as f, createAgentInstanceRegistry as g, AgentInstanceConflict as h, resolveRivusProjectSpace as i, RivusPluginLoadError as j, resolveFeishuEndpointCredentials as k, createRivusDeploymentDaemon as l, createAgentRuntimePool as m, createRivusDeploymentCliProcess as n, RivusDeploymentAutomationReadinessError as o, AgentRuntimeDisposed as p, createConfiguredRivusDeploymentDaemon as r, RivusDeploymentDaemonLifecycleError as s, runRivusDaemonCli as t, InvalidRivusEndpointBinding as u, createRivusDaemonShutdownController as v, loadNodeRivusPluginModule as w, formatRivusEnvFile as x, OpenClawEnvImportError as y };
|
|
@@ -15,6 +15,7 @@ var InvalidRivusPlugin = class extends Error {
|
|
|
15
15
|
const MEMORY_SCOPES = [
|
|
16
16
|
"conversation",
|
|
17
17
|
"agent-private",
|
|
18
|
+
"project",
|
|
18
19
|
"shared-user-profile"
|
|
19
20
|
];
|
|
20
21
|
const RIVUS_MEMORY_TOOL_ID = "memory";
|
|
@@ -36,6 +37,12 @@ function createMemoryNamespace(binding) {
|
|
|
36
37
|
binding.subjectId,
|
|
37
38
|
binding.scope
|
|
38
39
|
].map(encode).join("/");
|
|
40
|
+
case "project": return [
|
|
41
|
+
binding.tenantId,
|
|
42
|
+
binding.agentId,
|
|
43
|
+
binding.projectId ?? "",
|
|
44
|
+
binding.scope
|
|
45
|
+
].map(encode).join("/");
|
|
39
46
|
case "shared-user-profile": return [
|
|
40
47
|
binding.tenantId,
|
|
41
48
|
binding.subjectId,
|
|
@@ -44,7 +51,7 @@ function createMemoryNamespace(binding) {
|
|
|
44
51
|
}
|
|
45
52
|
}
|
|
46
53
|
function restrictMemoryScopesForAudience(scopes, audience) {
|
|
47
|
-
return audience === "group" ? scopes.filter((scope) => scope === "conversation") : [...scopes];
|
|
54
|
+
return audience === "group" ? scopes.filter((scope) => scope === "conversation" || scope === "project") : [...scopes];
|
|
48
55
|
}
|
|
49
56
|
function createRivusMemoryToolContract(scopes) {
|
|
50
57
|
return Object.freeze({
|
|
@@ -92,7 +99,7 @@ function createRivusMemoryToolContract(scopes) {
|
|
|
92
99
|
type: "string"
|
|
93
100
|
},
|
|
94
101
|
...scopes.length === 0 ? {} : { scope: {
|
|
95
|
-
description: "Optional Host-granted scope for search or propose. Shared User Profile
|
|
102
|
+
description: "Optional Host-granted scope for search or propose. Confirmed Project and Shared User Profile Memory are read-only to the model.",
|
|
96
103
|
enum: [...scopes],
|
|
97
104
|
type: "string"
|
|
98
105
|
} }
|
|
@@ -250,6 +257,7 @@ function resolveRivusAgentDefinition(catalog, deployment) {
|
|
|
250
257
|
model: profile.model,
|
|
251
258
|
pluginId: deployment.pluginId,
|
|
252
259
|
profileId: deployment.profileId,
|
|
260
|
+
...deployment.projectSpaceId ? { projectSpaceId: deployment.projectSpaceId } : {},
|
|
253
261
|
profileRevision,
|
|
254
262
|
skillGrantSet,
|
|
255
263
|
skills,
|
|
@@ -118,6 +118,7 @@ interface RivusAgentDeployment {
|
|
|
118
118
|
readonly agentId: string;
|
|
119
119
|
readonly pluginId: string;
|
|
120
120
|
readonly profileId: string;
|
|
121
|
+
readonly projectSpaceId?: string;
|
|
121
122
|
readonly endpointIds: ReadonlyArray<string>;
|
|
122
123
|
readonly memory?: {
|
|
123
124
|
readonly scopes: ReadonlyArray<MemoryScope>;
|
|
@@ -152,6 +153,8 @@ interface ResolvedRivusAgentDefinition {
|
|
|
152
153
|
readonly agentId: string;
|
|
153
154
|
readonly pluginId: string;
|
|
154
155
|
readonly profileId: string;
|
|
156
|
+
readonly projectSpaceId?: string;
|
|
157
|
+
readonly projectSpaceRevision?: string;
|
|
155
158
|
readonly endpointIds: ReadonlyArray<string>;
|
|
156
159
|
readonly profileRevision: string;
|
|
157
160
|
readonly systemPrompt: string;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
2
2
|
import { createHash } from "node:crypto";
|
|
3
|
-
import { join } from "node:path";
|
|
3
|
+
import { join, relative } from "node:path";
|
|
4
4
|
import { Effect } from "effect";
|
|
5
5
|
import * as Lark from "@larksuiteoapi/node-sdk";
|
|
6
6
|
import {
|
|
@@ -35,6 +35,7 @@ import {
|
|
|
35
35
|
createLangfuseAgentTelemetry,
|
|
36
36
|
createLazyFeishuWebSocketEventDispatcher,
|
|
37
37
|
createPiAgentLoop,
|
|
38
|
+
createProjectMemoryPromptPreparer,
|
|
38
39
|
createPiSkillRuntime,
|
|
39
40
|
createPiToolNameResolver,
|
|
40
41
|
createPiToolProxyDefinitions,
|
|
@@ -54,6 +55,8 @@ import {
|
|
|
54
55
|
openJsonlRecoveryControl,
|
|
55
56
|
resolveFeishuEndpointCredentials,
|
|
56
57
|
resolveLangfuseTelemetryConfig,
|
|
58
|
+
validateProjectSkillCatalog,
|
|
59
|
+
validateProjectSkillCommand,
|
|
57
60
|
type CreateRivusDeploymentEndpointInput,
|
|
58
61
|
type CreateRivusDeploymentAutomationInput,
|
|
59
62
|
type CreateRivusDeploymentRuntimeInput,
|
|
@@ -63,6 +66,7 @@ import {
|
|
|
63
66
|
type RivusDeploymentBootstrapContext,
|
|
64
67
|
type RivusThinkingLevel
|
|
65
68
|
} from "@rivus/agent";
|
|
69
|
+
import { createPiProjectSkillReadTool } from "@rivus/agent/pi";
|
|
66
70
|
|
|
67
71
|
type PiSessionOptions = NonNullable<Parameters<typeof createAgentSession>[0]>;
|
|
68
72
|
|
|
@@ -88,12 +92,6 @@ export async function createRivusDeploymentAdapters(context: RivusDeploymentBoot
|
|
|
88
92
|
request: (input) => request(input).pipe(Effect.map((response) => response as ConfiguredFeishuOpenApiResponse))
|
|
89
93
|
});
|
|
90
94
|
const interactionRegistry = createHumanInteractionEndpointRegistry();
|
|
91
|
-
const workspaceInstructions = await createAgentsMdInstructionsProvider().resolve({
|
|
92
|
-
maxBytes: 64 * 1024,
|
|
93
|
-
workingDirectory: ".",
|
|
94
|
-
workspaceRoot: await createWorkspaceRootHandle(process.cwd())
|
|
95
|
-
});
|
|
96
|
-
|
|
97
95
|
return {
|
|
98
96
|
dispose: () => telemetry?.shutdown(),
|
|
99
97
|
createRecoveryControl: () =>
|
|
@@ -240,6 +238,7 @@ export async function createRivusDeploymentAdapters(context: RivusDeploymentBoot
|
|
|
240
238
|
interactions,
|
|
241
239
|
maxPendingMessages: 100,
|
|
242
240
|
memoryTenantId,
|
|
241
|
+
...(input.projectSpaceId ? { projectSpaceId: input.projectSpaceId } : {}),
|
|
243
242
|
onCapacityExceeded: (payload) =>
|
|
244
243
|
replies.reply(payload.event.message.message_id, "Rivus is busy. Please retry in a moment."),
|
|
245
244
|
prepareRun: createFeishuPresentationPreparation({
|
|
@@ -278,10 +277,33 @@ export async function createRivusDeploymentAdapters(context: RivusDeploymentBoot
|
|
|
278
277
|
});
|
|
279
278
|
const resolveToolName = createPiToolNameResolver(input.definition.tools);
|
|
280
279
|
const skillRuntime = createPiSkillRuntime(input.definition.skills);
|
|
280
|
+
const workingDirectory = input.projectSpace?.workingDirectory ?? process.cwd();
|
|
281
|
+
const workspaceRoot = input.projectSpace?.root ?? process.cwd();
|
|
282
|
+
const workspaceInstructions = await createAgentsMdInstructionsProvider().resolve({
|
|
283
|
+
maxBytes: 64 * 1024,
|
|
284
|
+
workingDirectory: relative(workspaceRoot, workingDirectory) || ".",
|
|
285
|
+
workspaceRoot: await createWorkspaceRootHandle(workspaceRoot)
|
|
286
|
+
});
|
|
287
|
+
const prepareProjectMemory =
|
|
288
|
+
input.projectSpace && input.definition.memory.scopes.includes("project")
|
|
289
|
+
? createProjectMemoryPromptPreparer({
|
|
290
|
+
agentId: input.agentId,
|
|
291
|
+
memory,
|
|
292
|
+
projectId: input.projectSpace.id
|
|
293
|
+
})
|
|
294
|
+
: undefined;
|
|
281
295
|
const sessionRegistry = createPiSessionRegistry({
|
|
282
296
|
createSession: async (firstInput) => {
|
|
283
297
|
let activeInput = firstInput;
|
|
284
298
|
const customTools = [
|
|
299
|
+
...(input.projectSpace
|
|
300
|
+
? [
|
|
301
|
+
createPiProjectSkillReadTool({
|
|
302
|
+
cwd: input.projectSpace.workingDirectory,
|
|
303
|
+
skillPaths: input.projectSpace.skillPaths
|
|
304
|
+
})
|
|
305
|
+
]
|
|
306
|
+
: []),
|
|
285
307
|
...createPiToolProxyDefinitions({
|
|
286
308
|
agentId: input.agentId,
|
|
287
309
|
approvals: createHumanInteractionToolApprovalGateway({ registry: interactionRegistry }),
|
|
@@ -296,9 +318,10 @@ export async function createRivusDeploymentAdapters(context: RivusDeploymentBoot
|
|
|
296
318
|
];
|
|
297
319
|
const resourceLoader = new DefaultResourceLoader({
|
|
298
320
|
agentDir: PI_AGENT_DIR,
|
|
321
|
+
...(input.projectSpace ? { additionalSkillPaths: [...input.projectSpace.skillPaths] } : {}),
|
|
299
322
|
appendSystemPromptOverride: () =>
|
|
300
323
|
[workspaceInstructions.content, skillRuntime.prompt].filter((content) => content.length > 0),
|
|
301
|
-
cwd:
|
|
324
|
+
cwd: workingDirectory,
|
|
302
325
|
noContextFiles: true,
|
|
303
326
|
noExtensions: true,
|
|
304
327
|
noPromptTemplates: true,
|
|
@@ -307,11 +330,14 @@ export async function createRivusDeploymentAdapters(context: RivusDeploymentBoot
|
|
|
307
330
|
systemPromptOverride: () => input.definition.systemPrompt
|
|
308
331
|
});
|
|
309
332
|
await resourceLoader.reload();
|
|
333
|
+
const nativeSkillNames = input.projectSpace
|
|
334
|
+
? validateProjectSkillCatalog(resourceLoader.getSkills())
|
|
335
|
+
: new Set<string>();
|
|
310
336
|
const result = await createAgentSession({
|
|
311
337
|
...piOptions,
|
|
312
338
|
customTools,
|
|
313
339
|
resourceLoader,
|
|
314
|
-
sessionManager: SessionManager.create(
|
|
340
|
+
sessionManager: SessionManager.create(workingDirectory, join(instanceState, "sessions")),
|
|
315
341
|
tools: customTools.map(({ name }) => name)
|
|
316
342
|
});
|
|
317
343
|
return {
|
|
@@ -319,6 +345,10 @@ export async function createRivusDeploymentAdapters(context: RivusDeploymentBoot
|
|
|
319
345
|
activeInput = loopInput;
|
|
320
346
|
},
|
|
321
347
|
dispose: () => result.session.dispose(),
|
|
348
|
+
preparePrompt: async (loopInput) => {
|
|
349
|
+
validateProjectSkillCommand(loopInput.text, nativeSkillNames);
|
|
350
|
+
return prepareProjectMemory ? prepareProjectMemory(loopInput) : loopInput.text;
|
|
351
|
+
},
|
|
322
352
|
resolveToolName,
|
|
323
353
|
session: result.session
|
|
324
354
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rivus/agent",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.1",
|
|
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",
|
|
@@ -34,6 +34,10 @@
|
|
|
34
34
|
"types": "./dist/acp.d.ts",
|
|
35
35
|
"import": "./dist/acp.js"
|
|
36
36
|
},
|
|
37
|
+
"./pi": {
|
|
38
|
+
"types": "./dist/pi.d.ts",
|
|
39
|
+
"import": "./dist/pi.js"
|
|
40
|
+
},
|
|
37
41
|
"./testing": {
|
|
38
42
|
"types": "./dist/testing/index.d.ts",
|
|
39
43
|
"import": "./dist/testing/index.js"
|