@rivus/agent 0.16.1 → 0.16.6

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.
@@ -1,3839 +0,0 @@
1
- import { t as createSha256Digest } from "./sha256-digest.js";
2
- import { f as isRivusRuntimeToolId, o as createRivusHostToolDescriptorProvider, p as deepFreeze, t as createRivusAgentCatalog } from "./rivus-agent-definition-resolver.js";
3
- import { l as narrowBackgroundSessionDefinition } from "./background-session-authority.js";
4
- import { createRequire } from "node:module";
5
- import { Cause, Deferred, Effect, Either, Exit, Option } from "effect";
6
- import { createHash, randomUUID } from "node:crypto";
7
- import { access, chmod, constants, lstat, mkdir, open, readFile, readdir, realpath, stat } from "node:fs/promises";
8
- import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
9
- import { homedir } from "node:os";
10
- import { pathToFileURL } from "node:url";
11
- import { constants as constants$1 } from "node:fs";
12
- //#region src/platform/home/config/runtime/local-env-file.ts
13
- var LocalEnvFileError = class extends Error {
14
- constructor(message) {
15
- super(message);
16
- this.name = "LocalEnvFileError";
17
- }
18
- };
19
- async function loadMergedLocalEnvFile(filePath, overrideEnv) {
20
- return mergeRivusDaemonEnv(await loadLocalEnvFile(filePath), overrideEnv);
21
- }
22
- async function loadLocalEnvFile(filePath) {
23
- return parseLocalEnvFile(await readFile(filePath, "utf8"));
24
- }
25
- function parseLocalEnvFile(contents) {
26
- const env = {};
27
- const lines = contents.split(/\r?\n/);
28
- for (let index = 0; index < lines.length; index += 1) {
29
- const trimmed = lines[index].trim();
30
- if (!trimmed || trimmed.startsWith("#")) continue;
31
- const match = (trimmed.startsWith("export ") ? trimmed.slice(7).trimStart() : trimmed).match(/^([A-Za-z_][A-Za-z0-9_]*)=(.*)$/);
32
- if (!match) throw new LocalEnvFileError(`Invalid env file line ${index + 1}`);
33
- const key = match[1];
34
- const rawValue = match[2];
35
- env[key] = parseEnvValue(rawValue, index + 1);
36
- }
37
- return env;
38
- }
39
- function mergeRivusDaemonEnv(fileEnv, overrideEnv) {
40
- const merged = { ...fileEnv };
41
- for (const [key, value] of Object.entries(overrideEnv)) if (value !== void 0) merged[key] = value;
42
- return merged;
43
- }
44
- function parseEnvValue(rawValue, lineNumber) {
45
- const value = rawValue.trim();
46
- if (!value) return "";
47
- if (value.startsWith("'")) {
48
- if (!value.endsWith("'")) throw new LocalEnvFileError(`Invalid single-quoted env value on line ${lineNumber}`);
49
- return value.slice(1, -1).replaceAll("'\\''", "'");
50
- }
51
- if (value.startsWith("\"")) {
52
- if (!value.endsWith("\"")) throw new LocalEnvFileError(`Invalid double-quoted env value on line ${lineNumber}`);
53
- return unescapeDoubleQuotedValue(value.slice(1, -1));
54
- }
55
- return value;
56
- }
57
- function unescapeDoubleQuotedValue(value) {
58
- return value.replace(/\\(["\\nrt])/g, (_match, escaped) => {
59
- switch (escaped) {
60
- case "n": return "\n";
61
- case "r": return "\r";
62
- case "t": return " ";
63
- default: return escaped;
64
- }
65
- });
66
- }
67
- //#endregion
68
- //#region src/platform/home/config/model-management/rivus-model-management-config-migration.ts
69
- const RIVUS_MODEL_MANAGEMENT_ENABLED_ENV = "RIVUS_MODEL_MANAGEMENT_ENABLED";
70
- const RIVUS_MODEL_MANAGEMENT_OWNER_OPEN_ID_ENV = "RIVUS_MODEL_MANAGEMENT_OWNER_OPEN_ID";
71
- const RIVUS_MODEL_MANAGEMENT_TENANT_KEY_ENV = "RIVUS_MODEL_MANAGEMENT_TENANT_KEY";
72
- const RIVUS_MODEL_MANAGEMENT_REQUIRE_APPROVAL_ENV = "RIVUS_MODEL_MANAGEMENT_REQUIRE_APPROVAL";
73
- var RivusModelManagementConfigError = class extends Error {
74
- code;
75
- name = "RivusModelManagementConfigError";
76
- constructor(code, message) {
77
- super(message);
78
- this.code = code;
79
- }
80
- };
81
- /** Parse the Home capability flags without accepting truthy arbitrary strings. */
82
- function parseRivusModelManagementHomeConfig(env) {
83
- const enabled = parseRivusModelManagementEnabled(env[RIVUS_MODEL_MANAGEMENT_ENABLED_ENV]);
84
- const requireApproval = parseStrictBoolean(env[RIVUS_MODEL_MANAGEMENT_REQUIRE_APPROVAL_ENV], RIVUS_MODEL_MANAGEMENT_REQUIRE_APPROVAL_ENV, false);
85
- const ownerOpenId = optional$1(env[RIVUS_MODEL_MANAGEMENT_OWNER_OPEN_ID_ENV]);
86
- const tenantKey = optional$1(env[RIVUS_MODEL_MANAGEMENT_TENANT_KEY_ENV]);
87
- if (enabled && !ownerOpenId) throw new RivusModelManagementConfigError("owner_missing", `${RIVUS_MODEL_MANAGEMENT_OWNER_OPEN_ID_ENV} is required when model management is enabled`);
88
- if (enabled && !tenantKey) throw new RivusModelManagementConfigError("tenant_missing", `${RIVUS_MODEL_MANAGEMENT_TENANT_KEY_ENV} is required when model management is enabled`);
89
- return Object.freeze({
90
- enabled,
91
- ...ownerOpenId ? { ownerOpenId } : {},
92
- requireApproval,
93
- ...tenantKey ? { tenantKey } : {}
94
- });
95
- }
96
- function parseRivusModelManagementEnabled(value) {
97
- return parseStrictBoolean(value, RIVUS_MODEL_MANAGEMENT_ENABLED_ENV, false);
98
- }
99
- function parseStrictBoolean(value, variable, defaultValue) {
100
- if (value === void 0) return defaultValue;
101
- const normalized = value.trim();
102
- if (normalized === "true") return true;
103
- if (normalized === "false") return false;
104
- throw new RivusModelManagementConfigError("invalid_enabled", `${variable} must be true or false`);
105
- }
106
- /**
107
- * Migrate only the legacy model selection at the explicit enable/disable
108
- * boundary. The helper does not read or write managed model state.
109
- */
110
- async function migrateRivusModelManagementConfig(input) {
111
- if (typeof input.managementEnabled !== "boolean") throw new RivusModelManagementConfigError("invalid_enabled", "model management enabled state must be boolean");
112
- if (input.managementEnabled) return enableModelManagement(input);
113
- return disableModelManagement(input, parseModelReference(input.knownGoodProvider, input.knownGoodModel));
114
- }
115
- async function enableModelManagement(input) {
116
- if (hasAmbientModelOverride(input.env)) return {
117
- initialModel: parseConfiguredModel(input.env.PI_MODEL),
118
- source: "ambient",
119
- status: "enabled"
120
- };
121
- return {
122
- initialModel: parseConfiguredModel((await loadEnvFile(requireEnvFilePath(input.envFilePath), input.env)).PI_MODEL),
123
- source: "env-file",
124
- status: "enabled"
125
- };
126
- }
127
- async function disableModelManagement(input, knownGood) {
128
- const envFilePath = requireEnvFilePath(input.envFilePath);
129
- if (hasAmbientModelOverride(input.env)) throw new RivusModelManagementConfigError("ambient_model_override", "ambient PI_MODEL overrides the original env source and prevents verified model readback");
130
- const original = await readWritableEnvFile(envFilePath);
131
- const existingModel = parseOptionalConfiguredModel((await loadEnvFile(envFilePath, input.env)).PI_MODEL);
132
- if (existingModel && sameModel(existingModel, knownGood)) return {
133
- restoredModel: knownGood,
134
- source: "env-file",
135
- status: "unchanged"
136
- };
137
- const next = replacePiModel(original, knownGood);
138
- try {
139
- await input.writeAtomicTextFile(envFilePath, next, { durable: true });
140
- } catch {
141
- throw new RivusModelManagementConfigError("env_source_write_failed", "the original env source write did not complete with a known durable result");
142
- }
143
- let readBack;
144
- try {
145
- readBack = parseConfiguredModel((await loadMergedLocalEnvFile(envFilePath, input.env)).PI_MODEL);
146
- } catch {
147
- throw new RivusModelManagementConfigError("readback_mismatch", "the original env source could not be verified after restoring PI_MODEL");
148
- }
149
- if (readBack.provider !== knownGood.provider || readBack.model !== knownGood.model) throw new RivusModelManagementConfigError("readback_mismatch", "the original env source did not read back the known-good PI_MODEL");
150
- return {
151
- restoredModel: knownGood,
152
- source: "env-file",
153
- status: "exported"
154
- };
155
- }
156
- function parseModelReference(provider, model) {
157
- if (!isModelPart(provider) || !isModelPart(model)) throw new RivusModelManagementConfigError("invalid_model", "known-good model provider and id are invalid");
158
- return Object.freeze({
159
- model,
160
- provider
161
- });
162
- }
163
- function parseConfiguredModel(value) {
164
- const configured = optional$1(value);
165
- if (!configured) throw new RivusModelManagementConfigError("model_missing", "PI_MODEL is missing from the effective legacy config");
166
- const separator = configured.indexOf("/");
167
- if (separator <= 0 || separator === configured.length - 1 || configured.indexOf("/", separator + 1) !== -1) throw new RivusModelManagementConfigError("invalid_model", "PI_MODEL must use provider/model form");
168
- return parseModelReference(configured.slice(0, separator), configured.slice(separator + 1));
169
- }
170
- function parseOptionalConfiguredModel(value) {
171
- if (!optional$1(value)) return void 0;
172
- try {
173
- return parseConfiguredModel(value);
174
- } catch {
175
- return;
176
- }
177
- }
178
- function sameModel(left, right) {
179
- return left.provider === right.provider && left.model === right.model;
180
- }
181
- async function loadEnvFile(envFilePath, env) {
182
- try {
183
- return await loadMergedLocalEnvFile(envFilePath, env);
184
- } catch {
185
- throw new RivusModelManagementConfigError("env_source_unreadable", "the original env source could not be loaded");
186
- }
187
- }
188
- async function readWritableEnvFile(envFilePath) {
189
- try {
190
- const contents = await readFile(envFilePath, "utf8");
191
- if (((await stat(envFilePath)).mode & 146) === 0) throw new RivusModelManagementConfigError("env_source_unwritable", "the original env source is not writable");
192
- await access(envFilePath, constants.W_OK);
193
- return contents;
194
- } catch (error) {
195
- if (error instanceof RivusModelManagementConfigError) throw error;
196
- if (isPermissionError(error)) throw new RivusModelManagementConfigError("env_source_unwritable", "the original env source is not writable");
197
- throw new RivusModelManagementConfigError("env_source_unreadable", "the original env source is unavailable");
198
- }
199
- }
200
- function replacePiModel(contents, model) {
201
- const modelValue = `${model.provider}/${model.model}`;
202
- const parts = contents.split(/(\r\n|\n|\r)/);
203
- let replaced = false;
204
- for (let index = 0; index < parts.length; index += 2) {
205
- const line = parts[index];
206
- if (line === void 0) continue;
207
- const match = line.match(/^(\s*(?:export\s+)?PI_MODEL\s*=\s*)(.*)$/);
208
- if (!match) continue;
209
- const inlineComment = match[2].match(/(\s+#.*)$/)?.[1] ?? "";
210
- parts[index] = `${match[1]}${modelValue}${inlineComment}`;
211
- replaced = true;
212
- }
213
- if (replaced) return parts.join("");
214
- const newline = contents.includes("\r\n") ? "\r\n" : contents.includes("\n") ? "\n" : "\n";
215
- if (!contents) return `PI_MODEL=${modelValue}${newline}`;
216
- return /(?:\r\n|\n|\r)$/.test(contents) ? `${contents}PI_MODEL=${modelValue}${newline}` : `${contents}${newline}PI_MODEL=${modelValue}`;
217
- }
218
- function requireEnvFilePath(value) {
219
- const normalized = optional$1(value);
220
- if (!normalized) throw new RivusModelManagementConfigError("env_source_missing", "an explicit writable original env source is required for model management migration");
221
- return normalized;
222
- }
223
- function hasAmbientModelOverride(env) {
224
- return Object.prototype.hasOwnProperty.call(env, "PI_MODEL") && env.PI_MODEL !== void 0;
225
- }
226
- function isModelPart(value) {
227
- return value.trim() === value && value.length > 0 && !/[/\s]/.test(value);
228
- }
229
- function optional$1(value) {
230
- return value?.trim() || void 0;
231
- }
232
- function isPermissionError(error) {
233
- return typeof error === "object" && error !== null && "code" in error && (error.code === "EACCES" || error.code === "EPERM" || error.code === "EROFS");
234
- }
235
- //#endregion
236
- //#region src/platform/home/config/model-management/rivus-runtime-management-skill-installer.ts
237
- const SKILL_NAME = "runtime-management";
238
- const SKILL_FILE = "SKILL.md";
239
- const INSTALL_METADATA_FILE = ".rivus-install.json";
240
- async function installRivusRuntimeManagementSkill(options) {
241
- const writeAtomicTextFile = options.writeAtomicTextFile;
242
- const skillDirectory = join(options.homeDirectory ?? homedir(), ".agents", "skills", SKILL_NAME);
243
- const destination = join(skillDirectory, SKILL_FILE);
244
- const metadataPath = join(skillDirectory, INSTALL_METADATA_FILE);
245
- const source = await readFile(options.sourcePath, "utf8");
246
- const sourceDigest = digest(source);
247
- await mkdir(skillDirectory, { recursive: true });
248
- const current = await readOptional(destination);
249
- if (current === void 0) {
250
- await writeAtomicTextFile(destination, source, { mode: 384 });
251
- await writeMetadata(metadataPath, sourceDigest, writeAtomicTextFile);
252
- return {
253
- destination,
254
- requiresRefresh: true,
255
- sourceDigest,
256
- status: "installed"
257
- };
258
- }
259
- const currentDigest = digest(current);
260
- if (currentDigest === sourceDigest) {
261
- await writeMetadata(metadataPath, sourceDigest, writeAtomicTextFile);
262
- return {
263
- destination,
264
- requiresRefresh: false,
265
- sourceDigest,
266
- status: "unchanged"
267
- };
268
- }
269
- if ((await readMetadata(metadataPath))?.installedDigest !== currentDigest) return {
270
- destination,
271
- requiresRefresh: false,
272
- sourceDigest,
273
- status: "conflict"
274
- };
275
- await writeAtomicTextFile(destination, source, { mode: 384 });
276
- await writeMetadata(metadataPath, sourceDigest, writeAtomicTextFile);
277
- return {
278
- destination,
279
- requiresRefresh: true,
280
- sourceDigest,
281
- status: "updated"
282
- };
283
- }
284
- function digest(value) {
285
- return createHash("sha256").update(value, "utf8").digest("hex");
286
- }
287
- async function readOptional(path) {
288
- try {
289
- return await readFile(path, "utf8");
290
- } catch (error) {
291
- if (isMissing$1(error)) return void 0;
292
- throw error;
293
- }
294
- }
295
- async function readMetadata(path) {
296
- const contents = await readOptional(path);
297
- if (contents === void 0) return void 0;
298
- try {
299
- const value = JSON.parse(contents);
300
- if (!isRecord(value) || typeof value.installedDigest !== "string") return void 0;
301
- return { installedDigest: value.installedDigest };
302
- } catch {
303
- return;
304
- }
305
- }
306
- async function writeMetadata(path, installedDigest, writeAtomicTextFile) {
307
- await writeAtomicTextFile(path, `${JSON.stringify({
308
- installedDigest,
309
- version: 1
310
- }, null, 2)}\n`, { mode: 384 });
311
- }
312
- function isRecord(value) {
313
- return typeof value === "object" && value !== null && !Array.isArray(value);
314
- }
315
- function isMissing$1(error) {
316
- return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
317
- }
318
- //#endregion
319
- //#region src/platform/home/config/model-management/rivus-model-management-cli-launcher.ts
320
- const BIN_DIRECTORY_NAME = "bin";
321
- const LAUNCHER_NAME = "rivus";
322
- const PRIVATE_MODE = 448;
323
- /**
324
- * Install the private launcher used by model-management child processes.
325
- *
326
- * The launcher contains only fixed absolute executable paths. Per-Run
327
- * context, credentials, and other authority data stay in the spawn
328
- * environment owned by the Host and are never persisted in this file.
329
- */
330
- async function installRivusModelManagementCliLauncher(options) {
331
- assertAbsolutePath("directory", options.directory);
332
- assertAbsolutePath("nodeExecutable", options.nodeExecutable);
333
- assertAbsolutePath("cliEntryPath", options.cliEntryPath);
334
- const binDirectory = join(options.directory, BIN_DIRECTORY_NAME);
335
- const launcherPath = join(binDirectory, LAUNCHER_NAME);
336
- await mkdir(binDirectory, {
337
- mode: PRIVATE_MODE,
338
- recursive: true
339
- });
340
- await chmod(binDirectory, PRIVATE_MODE);
341
- await chmodIfPresent(launcherPath, PRIVATE_MODE);
342
- await options.writeAtomicTextFile(launcherPath, renderLauncher(options), { mode: PRIVATE_MODE });
343
- await chmod(launcherPath, PRIVATE_MODE);
344
- return Object.freeze({
345
- binDirectory,
346
- launcherPath
347
- });
348
- }
349
- function renderLauncher(options) {
350
- return `#!/bin/sh\nexec ${shellQuote(options.nodeExecutable)} ${shellQuote(options.cliEntryPath)} "$@"\n`;
351
- }
352
- function shellQuote(value) {
353
- if (value.includes("\0")) throw new Error("launcher paths must not contain NUL bytes");
354
- return `'${value.replaceAll("'", "'\\''")}'`;
355
- }
356
- function assertAbsolutePath(name, value) {
357
- if (value.length === 0 || !isAbsolute(value)) throw new Error(`${name} must be an absolute path`);
358
- if (value.includes("\0")) throw new Error(`${name} must not contain NUL bytes`);
359
- }
360
- async function chmodIfPresent(path, mode) {
361
- try {
362
- await chmod(path, mode);
363
- } catch (error) {
364
- if (isMissing(error)) return;
365
- throw error;
366
- }
367
- }
368
- function isMissing(error) {
369
- return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
370
- }
371
- //#endregion
372
- //#region src/platform/home/config/runtime/rivus-daemon-config.ts
373
- var RivusDaemonConfigError = class {
374
- variable;
375
- message;
376
- _tag = "RivusDaemonConfigError";
377
- constructor(variable, message) {
378
- this.variable = variable;
379
- this.message = message;
380
- }
381
- };
382
- const DEFAULT_AGENT_ID$1 = "main";
383
- const DEFAULT_CARD_STREAM_LEASE_MS = 51e4;
384
- const DEFAULT_FEISHU_BASE_URL$1 = "https://open.feishu.cn";
385
- const DEFAULT_STREAM_MIN_INTERVAL_MS$1 = 200;
386
- const THINKING_LEVELS$1 = /* @__PURE__ */ new Set([
387
- "off",
388
- "minimal",
389
- "low",
390
- "medium",
391
- "high",
392
- "xhigh"
393
- ]);
394
- function loadRivusDaemonConfig(env, options = {}) {
395
- return Effect.gen(function* () {
396
- const appId = yield* required$1(env, "FEISHU_APP_ID");
397
- const appSecret = yield* required$1(env, "FEISHU_APP_SECRET");
398
- const streamMinIntervalMs = yield* optionalPositiveInteger(env.FEISHU_STREAM_MIN_INTERVAL_MS, "FEISHU_STREAM_MIN_INTERVAL_MS", DEFAULT_STREAM_MIN_INTERVAL_MS$1);
399
- const cardStreamLeaseMs = yield* optionalPositiveInteger(env.FEISHU_CARD_STREAM_LEASE_MS, "FEISHU_CARD_STREAM_LEASE_MS", DEFAULT_CARD_STREAM_LEASE_MS);
400
- const thinkingLevel = yield* optionalThinkingLevel(env.PI_THINKING_LEVEL);
401
- const apiKey = yield* optionalPiApiKey(env, options.readTextFile ?? readUtf8File);
402
- const baseUrl = optional(env.PI_BASE_URL);
403
- const model = optional(env.PI_MODEL);
404
- return {
405
- agentId: optional(env.RIVUS_AGENT_ID) ?? DEFAULT_AGENT_ID$1,
406
- feishu: {
407
- appId,
408
- appSecret,
409
- baseUrl: optional(env.FEISHU_BASE_URL) ?? DEFAULT_FEISHU_BASE_URL$1,
410
- cardStreamLeaseMs,
411
- streamMinIntervalMs
412
- },
413
- pi: {
414
- ...apiKey ? { apiKey } : {},
415
- ...baseUrl ? { baseUrl } : {},
416
- ...model ? { model } : {},
417
- ...thinkingLevel ? { thinkingLevel } : {}
418
- }
419
- };
420
- });
421
- }
422
- function optional(value) {
423
- const trimmed = value?.trim();
424
- return trimmed ? trimmed : void 0;
425
- }
426
- function readUtf8File(path) {
427
- return readFile(path, "utf8");
428
- }
429
- function optionalPiApiKey(env, readTextFile) {
430
- const inlineApiKey = optional(env.PI_API_KEY);
431
- const apiKeyFile = optional(env.PI_API_KEY_FILE);
432
- if (inlineApiKey && apiKeyFile) return Effect.fail(new RivusDaemonConfigError("PI_API_KEY", "PI_API_KEY and PI_API_KEY_FILE cannot both be set"));
433
- if (inlineApiKey) return Effect.succeed(inlineApiKey);
434
- if (!apiKeyFile) return Effect.succeed(void 0);
435
- return Effect.tryPromise({
436
- try: async () => readTextFile(apiKeyFile),
437
- catch: (error) => new RivusDaemonConfigError("PI_API_KEY_FILE", `PI_API_KEY_FILE could not be read: ${formatConfigError(error)}`)
438
- }).pipe(Effect.flatMap((contents) => {
439
- const apiKey = optional(contents);
440
- return apiKey ? Effect.succeed(apiKey) : Effect.fail(new RivusDaemonConfigError("PI_API_KEY_FILE", "PI_API_KEY_FILE must not be empty"));
441
- }));
442
- }
443
- function formatConfigError(error) {
444
- return error instanceof Error ? error.message : String(error);
445
- }
446
- function optionalPositiveInteger(value, variable, fallback) {
447
- const normalized = optional(value);
448
- if (!normalized) return Effect.succeed(fallback);
449
- if (/^[1-9]\d*$/.test(normalized)) return Effect.succeed(Number(normalized));
450
- return Effect.fail(new RivusDaemonConfigError(variable, `${variable} must be a positive integer`));
451
- }
452
- function required$1(env, variable) {
453
- const value = optional(env[variable]);
454
- if (value) return Effect.succeed(value);
455
- return Effect.fail(new RivusDaemonConfigError(variable, `${variable} is required`));
456
- }
457
- function optionalThinkingLevel(value) {
458
- const normalized = optional(value);
459
- if (!normalized) return Effect.succeed(void 0);
460
- if (THINKING_LEVELS$1.has(normalized)) return Effect.succeed(normalized);
461
- return Effect.fail(new RivusDaemonConfigError("PI_THINKING_LEVEL", "PI_THINKING_LEVEL must be one of off, minimal, low, medium, high, xhigh"));
462
- }
463
- //#endregion
464
- //#region src/bootstrap/deployment/effect-runner.ts
465
- async function runDeploymentProcessEffect(effect) {
466
- const exit = await Effect.runPromiseExit(effect);
467
- if (Exit.isSuccess(exit)) return exit.value;
468
- const failure = Cause.failureOption(exit.cause);
469
- throw Option.isSome(failure) ? failure.value : Cause.squash(exit.cause);
470
- }
471
- //#endregion
472
- //#region src/platform/runtime/runtime-cache.ts
473
- function createRuntimeCache() {
474
- const entries = /* @__PURE__ */ new Map();
475
- const serial = Effect.unsafeMakeSemaphore(1);
476
- const reserve = (key) => serial.withPermits(1)(Effect.gen(function* () {
477
- const current = entries.get(key);
478
- if (current) return {
479
- created: false,
480
- entry: current
481
- };
482
- const entry = {
483
- deferred: yield* Deferred.make(),
484
- initializationStarted: false,
485
- key
486
- };
487
- entries.set(key, entry);
488
- return {
489
- created: true,
490
- entry
491
- };
492
- }));
493
- const start = (entry, create) => Effect.uninterruptible(Effect.gen(function* () {
494
- if (!(yield* serial.withPermits(1)(Effect.sync(() => {
495
- if (entry.initializationStarted) return false;
496
- entry.initializationStarted = true;
497
- return true;
498
- })))) return;
499
- const initialization = create().pipe(Effect.tapError(() => serial.withPermits(1)(Effect.sync(() => {
500
- if (entries.get(entry.key) === entry) entries.delete(entry.key);
501
- }))), Effect.exit, Effect.flatMap((exit) => Deferred.done(entry.deferred, exit)), Effect.asVoid);
502
- yield* Effect.forkDaemon(initialization);
503
- }));
504
- const getOrCreate = (key, create) => Effect.uninterruptibleMask((restore) => Effect.gen(function* () {
505
- const selected = yield* reserve(key);
506
- yield* start(selected.entry, create);
507
- return {
508
- entry: selected.entry,
509
- runtime: yield* restore(Deferred.await(selected.entry.deferred))
510
- };
511
- }));
512
- return {
513
- drain: () => serial.withPermits(1)(Effect.sync(() => {
514
- const drained = Object.freeze([...entries.values()]);
515
- entries.clear();
516
- return drained;
517
- })),
518
- getExisting: (key) => Effect.gen(function* () {
519
- const entry = yield* serial.withPermits(1)(Effect.sync(() => entries.get(key)));
520
- if (!entry) return void 0;
521
- return {
522
- entry,
523
- runtime: yield* Deferred.await(entry.deferred)
524
- };
525
- }),
526
- getOrCreate,
527
- isCurrent: (key, entry) => serial.withPermits(1)(Effect.sync(() => entries.get(key) === entry)),
528
- reserve,
529
- size: () => serial.withPermits(1)(Effect.sync(() => entries.size)),
530
- start
531
- };
532
- }
533
- function disposeRuntimeCacheEntries(options) {
534
- const disposal = Effect.gen(function* () {
535
- const completions = yield* Effect.forEach(options.entries, (entry) => Effect.gen(function* () {
536
- const completion = yield* Deferred.make();
537
- yield* Effect.forkDaemon(Deferred.await(entry.deferred).pipe(Effect.flatMap(options.dispose), Effect.exit, Effect.flatMap((exit) => Deferred.succeed(completion, exit)), Effect.asVoid));
538
- return completion;
539
- }), { concurrency: "unbounded" });
540
- const failures = (yield* Effect.forEach(completions, Deferred.await, { concurrency: "unbounded" })).filter(Exit.isFailure).map(({ cause }) => Cause.squash(cause));
541
- if (failures.length > 0) return yield* Effect.fail(new AggregateError(failures, options.failureMessage));
542
- });
543
- return options.timeout ? disposal.pipe(Effect.timeoutFail({
544
- duration: options.timeout.milliseconds,
545
- onTimeout: options.timeout.onTimeout
546
- })) : disposal;
547
- }
548
- function invokeRuntimeControl(runtime, control) {
549
- return runtime.pipe(Effect.flatMap((selected) => selected ? control(selected) ?? Effect.succeed(false) : Effect.succeed(false)));
550
- }
551
- //#endregion
552
- //#region src/adapters/agent/runtime/process-agent-runtime-adapter.ts
553
- function toEffectAgentRuntimeInput(input) {
554
- const onUpdate = input.onUpdate;
555
- return {
556
- ...runtimeInputFields(input),
557
- ...onUpdate ? { onUpdate: (update) => Effect.tryPromise({
558
- try: async () => onUpdate(update),
559
- catch: (failure) => failure
560
- }) } : {}
561
- };
562
- }
563
- function toProcessAgentRuntimeInput(input, runEffect) {
564
- const onUpdate = input.onUpdate;
565
- return {
566
- ...runtimeInputFields(input),
567
- ...onUpdate ? { onUpdate: (update) => runEffect(onUpdate(update)) } : {}
568
- };
569
- }
570
- function toEffectAgentRuntime(runtime, runEffect) {
571
- const cancel = runtime.cancel?.bind(runtime);
572
- const dispose = runtime.dispose?.bind(runtime);
573
- const steer = runtime.steer?.bind(runtime);
574
- return {
575
- ...runtime.concurrency ? { concurrency: runtime.concurrency } : {},
576
- ...cancel ? { cancel: (input) => Effect.tryPromise({
577
- try: () => cancel(input),
578
- catch: (failure) => failure
579
- }) } : {},
580
- ...dispose ? { dispose: () => Effect.tryPromise({
581
- try: async () => dispose(),
582
- catch: (failure) => failure
583
- }) } : {},
584
- run: (input) => Effect.tryPromise({
585
- try: () => runtime.run(toProcessAgentRuntimeInput(input, runEffect)),
586
- catch: (failure) => failure
587
- }),
588
- ...steer ? { steer: (input) => Effect.tryPromise({
589
- try: () => steer(input),
590
- catch: (failure) => failure
591
- }) } : {}
592
- };
593
- }
594
- function runtimeInputFields(input) {
595
- return {
596
- ...input.invocation ? { invocation: input.invocation } : {},
597
- ...input.payload === void 0 ? {} : { payload: input.payload },
598
- sessionKey: input.sessionKey,
599
- text: input.text
600
- };
601
- }
602
- //#endregion
603
- //#region src/adapters/feishu/config/feishu-endpoint-credentials.ts
604
- var FeishuEndpointCredentialError = class extends Error {
605
- name = "FeishuEndpointCredentialError";
606
- };
607
- function resolveFeishuEndpointCredentials(credentialRef, env) {
608
- if (!credentialRef.startsWith("env:")) throw new FeishuEndpointCredentialError("Feishu endpoint credentialRef must use env:<PREFIX>");
609
- const prefix = credentialRef.slice(4);
610
- if (!/^[A-Z][A-Z0-9_]*$/.test(prefix)) throw new FeishuEndpointCredentialError(`Invalid environment prefix in credentialRef: ${credentialRef}`);
611
- return Object.freeze({
612
- appId: required(env, `${prefix}_APP_ID`),
613
- appSecret: required(env, `${prefix}_APP_SECRET`)
614
- });
615
- }
616
- function required(env, variable) {
617
- const value = env[variable]?.trim();
618
- if (!value) throw new FeishuEndpointCredentialError(`${variable} is required`);
619
- return value;
620
- }
621
- //#endregion
622
- //#region src/adapters/openclaw/config/openclaw-env-import.ts
623
- var OpenClawEnvImportError = class extends Error {
624
- constructor(message) {
625
- super(message);
626
- this.name = "OpenClawEnvImportError";
627
- }
628
- };
629
- const DEFAULT_AGENT_ID = "main";
630
- const DEFAULT_FEISHU_BASE_URL = "https://open.feishu.cn";
631
- const DEFAULT_LARK_BASE_URL = "https://open.larksuite.com";
632
- const DEFAULT_STREAM_MIN_INTERVAL_MS = 200;
633
- const THINKING_LEVELS = /* @__PURE__ */ new Set([
634
- "off",
635
- "minimal",
636
- "low",
637
- "medium",
638
- "high",
639
- "xhigh"
640
- ]);
641
- const ENV_FILE_ORDER = [
642
- "FEISHU_APP_ID",
643
- "FEISHU_APP_SECRET",
644
- "FEISHU_BASE_URL",
645
- "FEISHU_STREAM_MIN_INTERVAL_MS",
646
- "RIVUS_AGENT_ID",
647
- "PI_API_KEY_FILE",
648
- "PI_BASE_URL",
649
- "PI_MODEL",
650
- "PI_THINKING_LEVEL"
651
- ];
652
- function createRivusEnvFromOpenClawConfig(openClawConfig, options = {}) {
653
- const config = asRecord(openClawConfig, "OpenClaw config");
654
- const feishu = asRecord(readPath(config, ["channels", "feishu"]), "channels.feishu");
655
- const appId = requiredString(feishu, "appId", "channels.feishu.appId");
656
- const appSecret = requiredString(feishu, "appSecret", "channels.feishu.appSecret");
657
- const modelReference = findPrimaryModelReference(config);
658
- const providerId = modelReference ? parseProviderId(modelReference) : void 0;
659
- const provider = providerId ? optionalRecord(readPath(config, [
660
- "models",
661
- "providers",
662
- providerId
663
- ])) : void 0;
664
- const providerBaseUrl = provider ? optionalString(provider.baseUrl) : void 0;
665
- const thinkingLevel = modelReference ? findThinkingLevel(config, modelReference) : void 0;
666
- const warnings = thinkingLevel?.warning ? [thinkingLevel.warning] : [];
667
- return {
668
- env: {
669
- FEISHU_APP_ID: appId,
670
- FEISHU_APP_SECRET: appSecret,
671
- FEISHU_BASE_URL: options.feishuBaseUrl ?? inferFeishuBaseUrl(optionalString(feishu.domain)),
672
- FEISHU_STREAM_MIN_INTERVAL_MS: String(options.streamMinIntervalMs ?? DEFAULT_STREAM_MIN_INTERVAL_MS),
673
- RIVUS_AGENT_ID: findAgentId(config),
674
- ...options.piApiKeyFile ? { PI_API_KEY_FILE: options.piApiKeyFile } : {},
675
- ...providerBaseUrl ? { PI_BASE_URL: providerBaseUrl } : {},
676
- ...modelReference ? { PI_MODEL: modelReference } : {},
677
- ...thinkingLevel?.level ? { PI_THINKING_LEVEL: thinkingLevel.level } : {}
678
- },
679
- warnings
680
- };
681
- }
682
- function formatRivusEnvFile(env) {
683
- return `${[...ENV_FILE_ORDER.filter((key) => Object.hasOwn(env, key)), ...Object.keys(env).filter((key) => !ENV_FILE_ORDER.includes(key)).sort()].map((key) => `${key}=${quoteEnvValue(env[key] ?? "")}`).join("\n")}\n`;
684
- }
685
- function findAgentId(config) {
686
- const agents = optionalRecord(config.agents);
687
- return optionalString(optionalRecord((Array.isArray(agents?.list) ? agents.list : [])[0])?.id) ?? DEFAULT_AGENT_ID;
688
- }
689
- function findPrimaryModelReference(config) {
690
- const primary = optionalString(readPath(config, [
691
- "agents",
692
- "defaults",
693
- "model",
694
- "primary"
695
- ]));
696
- if (primary) return primary;
697
- const providers = optionalRecord(readPath(config, ["models", "providers"]));
698
- if (!providers) return;
699
- for (const [providerId, provider] of Object.entries(providers)) {
700
- const model = firstModelId(optionalRecord(provider));
701
- if (model) return `${providerId}/${model}`;
702
- }
703
- }
704
- function firstModelId(provider) {
705
- return optionalString(optionalRecord((Array.isArray(provider?.models) ? provider.models : [])[0])?.id);
706
- }
707
- function parseProviderId(modelReference) {
708
- const separator = modelReference.indexOf("/");
709
- return separator > 0 ? modelReference.slice(0, separator) : void 0;
710
- }
711
- function findThinkingLevel(config, modelReference) {
712
- const providerId = parseProviderId(modelReference);
713
- const modelId = readModelId(modelReference);
714
- const rawLevel = [
715
- readPath(config, [
716
- "agents",
717
- "defaults",
718
- "models",
719
- modelReference,
720
- "thinkingLevel"
721
- ]),
722
- readPath(config, [
723
- "agents",
724
- "defaults",
725
- "models",
726
- modelReference,
727
- "thinkLevel"
728
- ]),
729
- readPath(config, [
730
- "agents",
731
- "defaults",
732
- "models",
733
- modelReference,
734
- "reasoningLevel"
735
- ]),
736
- ...providerId && modelId ? readProviderModelThinkingCandidates(config, providerId, modelId) : []
737
- ].map(optionalString).find(Boolean);
738
- if (!rawLevel) return;
739
- if (THINKING_LEVELS.has(rawLevel)) return { level: rawLevel };
740
- return { warning: `Unsupported OpenClaw thinking level '${rawLevel}' was ignored` };
741
- }
742
- function readProviderModelThinkingCandidates(config, providerId, modelId) {
743
- const providerModels = readPath(config, [
744
- "models",
745
- "providers",
746
- providerId,
747
- "models"
748
- ]);
749
- if (!Array.isArray(providerModels)) return [];
750
- const model = providerModels.map(optionalRecord).find((candidate) => optionalString(candidate?.id) === modelId);
751
- if (!model) return [];
752
- return [
753
- model.thinkingLevel,
754
- model.thinkLevel,
755
- model.reasoningLevel,
756
- readPath(model, ["reasoning", "level"]),
757
- readPath(model, ["reasoning", "thinkingLevel"]),
758
- readPath(model, ["reasoning", "thinkLevel"])
759
- ];
760
- }
761
- function readModelId(modelReference) {
762
- const separator = modelReference.indexOf("/");
763
- return separator >= 0 && separator < modelReference.length - 1 ? modelReference.slice(separator + 1) : void 0;
764
- }
765
- function inferFeishuBaseUrl(domain) {
766
- return domain === "lark" ? DEFAULT_LARK_BASE_URL : DEFAULT_FEISHU_BASE_URL;
767
- }
768
- function quoteEnvValue(value) {
769
- return `'${value.replaceAll("'", "'\\''")}'`;
770
- }
771
- function requiredString(record, key, path) {
772
- const value = optionalString(record[key]);
773
- if (!value) throw new OpenClawEnvImportError(`${path} is required`);
774
- return value;
775
- }
776
- function optionalString(value) {
777
- return typeof value === "string" && value.trim() ? value.trim() : void 0;
778
- }
779
- function asRecord(value, path) {
780
- const record = optionalRecord(value);
781
- if (!record) throw new OpenClawEnvImportError(`${path} must be an object`);
782
- return record;
783
- }
784
- function optionalRecord(value) {
785
- return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
786
- }
787
- function readPath(record, path) {
788
- let value = record;
789
- for (const segment of path) {
790
- const current = optionalRecord(value);
791
- if (!current) return;
792
- value = current[segment];
793
- }
794
- return value;
795
- }
796
- //#endregion
797
- //#region src/platform/daemon/process/rivus-daemon-shutdown-controller.ts
798
- const DEFAULT_SIGNALS = ["SIGINT", "SIGTERM"];
799
- function createRivusDaemonShutdownController(options) {
800
- let shutdown;
801
- const handle = (signal) => {
802
- shutdown ??= Effect.runPromise(options.daemon.stop()).then(() => options.onStopped?.(signal)).catch(async (error) => {
803
- await options.onError?.(error, signal);
804
- throw error;
805
- });
806
- return shutdown;
807
- };
808
- return {
809
- handle,
810
- install: () => {
811
- for (const signal of options.signals ?? DEFAULT_SIGNALS) options.signalSource.on(signal, () => {
812
- handle(signal);
813
- });
814
- },
815
- stopping: () => shutdown !== void 0
816
- };
817
- }
818
- //#endregion
819
- //#region src/adapters/cli/config/rivus-daemon-config.ts
820
- async function loadRivusEnvFromOpenClawConfig(filePath, piApiKeyFile) {
821
- return createRivusEnvFromOpenClawConfig(JSON.parse(await readFile(filePath, "utf8")), piApiKeyFile ? { piApiKeyFile } : {});
822
- }
823
- function toRedactedConfig(config) {
824
- return {
825
- agentId: config.agentId,
826
- feishu: {
827
- appIdPresent: Boolean(config.feishu.appId),
828
- appSecretPresent: Boolean(config.feishu.appSecret),
829
- baseUrl: config.feishu.baseUrl,
830
- cardStreamLeaseMs: config.feishu.cardStreamLeaseMs,
831
- streamMinIntervalMs: config.feishu.streamMinIntervalMs
832
- },
833
- pi: {
834
- apiKeyPresent: Boolean(config.pi.apiKey),
835
- ...config.pi.baseUrl ? { baseUrl: config.pi.baseUrl } : {},
836
- ...config.pi.model ? { model: config.pi.model } : {},
837
- ...config.pi.thinkingLevel ? { thinkingLevel: config.pi.thinkingLevel } : {}
838
- }
839
- };
840
- }
841
- function toRedactedDeploymentManifest(manifest) {
842
- return {
843
- agents: manifest.agents.map(({ agentId, endpointIds, pluginId, profileId }) => ({
844
- agentId,
845
- endpointIds,
846
- pluginId,
847
- profileId
848
- })),
849
- automations: (manifest.automations ?? []).map(({ agentId, delivery, enabled, id, required, schedule, templateId, timeZone }) => ({
850
- agentId,
851
- delivery: {
852
- endpointId: delivery.endpointId,
853
- targetRef: delivery.targetRef,
854
- targetType: delivery.targetType
855
- },
856
- enabled,
857
- id,
858
- required,
859
- schedule,
860
- templateId,
861
- timeZone
862
- })),
863
- ...manifest.backgroundSessions ? { backgroundSessions: {
864
- enabled: manifest.backgroundSessions.enabled,
865
- leaseMs: manifest.backgroundSessions.leaseMs,
866
- leaseRenewalIntervalMs: manifest.backgroundSessions.leaseRenewalIntervalMs,
867
- maxConsecutiveFailures: manifest.backgroundSessions.maxConsecutiveFailures,
868
- maxConcurrentSessions: manifest.backgroundSessions.maxConcurrentSessions,
869
- required: manifest.backgroundSessions.required,
870
- retryBackoffMs: manifest.backgroundSessions.retryBackoffMs,
871
- sessionLifetimeMs: manifest.backgroundSessions.sessionLifetimeMs,
872
- stepTimeoutMs: manifest.backgroundSessions.stepTimeoutMs
873
- } } : {},
874
- defaultAgentId: manifest.defaultAgentId,
875
- defaultEndpointId: manifest.defaultEndpointId,
876
- endpoints: manifest.endpoints.map(({ agentId, credentialRef, enabled, id, required, sessionNamespace }) => ({
877
- agentId,
878
- credentialRef,
879
- enabled,
880
- id,
881
- required,
882
- sessionNamespace
883
- })),
884
- plugins: manifest.plugins.map(({ id, module, required }) => ({
885
- id,
886
- module,
887
- required
888
- }))
889
- };
890
- }
891
- async function loadCliEnv(envFilePath, overrideEnv) {
892
- if (!envFilePath) return overrideEnv;
893
- return loadMergedLocalEnvFile(envFilePath, overrideEnv);
894
- }
895
- //#endregion
896
- //#region src/adapters/cli/recovery/rivus-recovery-cli.ts
897
- function createRivusRecoveryCliParser() {
898
- const input = { recoveryList: false };
899
- return {
900
- build: () => buildCommand(input),
901
- consume: (argument, next) => consumeArgument(input, argument, next)
902
- };
903
- }
904
- async function runRivusRecoveryCliCommand(control, command) {
905
- switch (command.type) {
906
- case "inspect": return Effect.runPromise(control.inspect());
907
- case "requeue-dead-letter": return Effect.runPromise(control.requeueDeadLetter({
908
- actorId: "local-cli",
909
- deliveryId: command.deliveryId,
910
- endpointId: command.endpointId,
911
- expectedRevision: command.expectedRevision,
912
- note: await readPrivateTextFile(command.noteFilePath, "Recovery note", 16 * 1024)
913
- }));
914
- case "resolve-tool-operation": {
915
- const note = await readPrivateTextFile(command.noteFilePath, "Recovery note", 16 * 1024);
916
- const outcome = command.outcome.status === "applied" ? {
917
- result: parseToolResult(await readPrivateTextFile(command.outcome.resultFilePath, "Tool result", 1024 * 1024)),
918
- status: "applied"
919
- } : command.outcome;
920
- return Effect.runPromise(control.resolveToolOperation({
921
- actorId: "local-cli",
922
- expectedRevision: command.expectedRevision,
923
- instanceId: command.instanceId,
924
- note,
925
- operationId: command.operationId,
926
- outcome
927
- }));
928
- }
929
- }
930
- }
931
- function consumeArgument(input, argument, next) {
932
- if (argument === "--recovery-list") {
933
- input.recoveryList = true;
934
- return {
935
- consumed: 0,
936
- handled: true
937
- };
938
- }
939
- for (const [flag, key] of [
940
- ["--requeue-dead-letter", "requeueDeadLetterId"],
941
- ["--resolve-tool-operation", "resolveToolOperationId"],
942
- ["--endpoint-id", "endpointId"],
943
- ["--instance-id", "instanceId"],
944
- ["--expected-revision", "expectedRevision"],
945
- ["--recovery-note-file", "recoveryNoteFile"],
946
- ["--tool-outcome", "toolOutcome"],
947
- ["--tool-result-file", "toolResultFile"]
948
- ]) {
949
- if (argument === flag) {
950
- if (!next) return {
951
- consumed: 0,
952
- error: `${flag} requires a value`,
953
- handled: true
954
- };
955
- input[key] = next;
956
- return {
957
- consumed: 1,
958
- handled: true
959
- };
960
- }
961
- if (argument?.startsWith(`${flag}=`)) {
962
- const value = argument.slice(flag.length + 1);
963
- if (!value) return {
964
- consumed: 0,
965
- error: `${flag} requires a value`,
966
- handled: true
967
- };
968
- input[key] = value;
969
- return {
970
- consumed: 0,
971
- handled: true
972
- };
973
- }
974
- }
975
- return {
976
- consumed: 0,
977
- handled: false
978
- };
979
- }
980
- function buildCommand(input) {
981
- const actionCount = Number(input.recoveryList) + Number(input.requeueDeadLetterId !== void 0) + Number(input.resolveToolOperationId !== void 0);
982
- const hasOptions = input.endpointId !== void 0 || input.expectedRevision !== void 0 || input.instanceId !== void 0 || input.recoveryNoteFile !== void 0 || input.toolOutcome !== void 0 || input.toolResultFile !== void 0;
983
- if (actionCount === 0) return hasOptions ? { error: "Recovery options require --recovery-list, --requeue-dead-letter, or --resolve-tool-operation" } : {};
984
- if (actionCount > 1) return { error: "Choose only one recovery command" };
985
- if (input.recoveryList) return hasOptions ? { error: "--recovery-list does not accept mutation options" } : { command: { type: "inspect" } };
986
- const expectedRevision = parsePositiveInteger(input.expectedRevision);
987
- if (expectedRevision === void 0) return { error: "Recovery mutations require --expected-revision with a positive integer" };
988
- const noteFilePath = input.recoveryNoteFile?.trim();
989
- if (!noteFilePath) return { error: "Recovery mutations require --recovery-note-file" };
990
- if (input.requeueDeadLetterId !== void 0) return buildDeadLetterCommand(input, expectedRevision, noteFilePath);
991
- return buildToolOperationCommand(input, expectedRevision, noteFilePath);
992
- }
993
- function buildDeadLetterCommand(input, expectedRevision, noteFilePath) {
994
- const endpointId = input.endpointId?.trim();
995
- if (!endpointId) return { error: "--requeue-dead-letter requires --endpoint-id" };
996
- if (input.instanceId !== void 0 || input.toolOutcome !== void 0 || input.toolResultFile !== void 0) return { error: "--requeue-dead-letter cannot use Tool reconciliation options" };
997
- return { command: {
998
- deliveryId: input.requeueDeadLetterId,
999
- endpointId,
1000
- expectedRevision,
1001
- noteFilePath,
1002
- type: "requeue-dead-letter"
1003
- } };
1004
- }
1005
- function buildToolOperationCommand(input, expectedRevision, noteFilePath) {
1006
- const instanceId = input.instanceId?.trim();
1007
- if (!instanceId) return { error: "--resolve-tool-operation requires --instance-id" };
1008
- if (input.endpointId !== void 0) return { error: "--resolve-tool-operation cannot use --endpoint-id" };
1009
- if (input.toolOutcome !== "applied" && input.toolOutcome !== "not-applied") return { error: "--resolve-tool-operation requires --tool-outcome applied or not-applied" };
1010
- if (input.toolOutcome === "not-applied") {
1011
- if (input.toolResultFile !== void 0) return { error: "--tool-result-file is only valid when --tool-outcome is applied" };
1012
- return { command: {
1013
- expectedRevision,
1014
- instanceId,
1015
- noteFilePath,
1016
- operationId: input.resolveToolOperationId,
1017
- outcome: { status: "not-applied" },
1018
- type: "resolve-tool-operation"
1019
- } };
1020
- }
1021
- const resultFilePath = input.toolResultFile?.trim();
1022
- if (!resultFilePath) return { error: "--tool-outcome applied requires --tool-result-file" };
1023
- return { command: {
1024
- expectedRevision,
1025
- instanceId,
1026
- noteFilePath,
1027
- operationId: input.resolveToolOperationId,
1028
- outcome: {
1029
- resultFilePath,
1030
- status: "applied"
1031
- },
1032
- type: "resolve-tool-operation"
1033
- } };
1034
- }
1035
- function parsePositiveInteger(value) {
1036
- return value && /^[1-9]\d*$/.test(value) ? Number(value) : void 0;
1037
- }
1038
- async function readPrivateTextFile(filePath, label, maxBytes) {
1039
- const handle = await open(filePath, constants$1.O_RDONLY | constants$1.O_NOFOLLOW);
1040
- try {
1041
- const metadata = await handle.stat();
1042
- if (!metadata.isFile()) throw new Error(`${label} must be a regular file`);
1043
- if ((metadata.mode & 63) !== 0) throw new Error(`${label} file permissions must be 0600 or stricter`);
1044
- if (metadata.size > maxBytes) throw new Error(`${label} file exceeds ${maxBytes} bytes`);
1045
- return (await handle.readFile("utf8")).trim();
1046
- } finally {
1047
- await handle.close();
1048
- }
1049
- }
1050
- function parseToolResult(value) {
1051
- try {
1052
- return JSON.parse(value);
1053
- } catch {
1054
- throw new Error("Tool result file must contain valid JSON");
1055
- }
1056
- }
1057
- //#endregion
1058
- //#region src/adapters/cli/command/rivus-daemon-arguments.ts
1059
- const RIVUS_DAEMON_USAGE = `Usage: rivus --bootstrap <module> [--manifest <rivus.config.json>]
1060
-
1061
- Starts a local Rivus Agent daemon from an injected bootstrap module.
1062
-
1063
- Project commands:
1064
- rivus setup [directory]
1065
- rivus start
1066
- rivus status
1067
- rivus check-config
1068
- rivus init [directory]
1069
- rivus doctor [directory] [--env-file <path>]
1070
- rivus model <status|set|rollback> ...
1071
-
1072
- Options:
1073
- --bootstrap <module> Module exporting createRivusDaemonProcess(context)
1074
- --manifest <path> Start the manifest-driven multi-agent deployment; bootstrap exports createRivusDeploymentAdapters(context)
1075
- --env-file <path> Load local KEY=value config before reading environment
1076
- --check-config Print redacted local config JSON and exit
1077
- --prompt <text> Run one local prompt through bootstrap promptText() and exit
1078
- --replay-feishu-event <json>
1079
- Replay one Feishu receive-message payload through bootstrap replayReceiveMessage() and exit
1080
- --replay-feishu-text <text>
1081
- Replay one synthetic Feishu receive-message text payload without Feishu side effects and exit
1082
- --feishu-message-id <id>
1083
- Message id for --replay-feishu-text (default om_cli_<timestamp>)
1084
- --feishu-chat-id <id> Chat id for --replay-feishu-text (default oc_cli)
1085
- --feishu-thread-id <id>
1086
- Thread id for --replay-feishu-text (default omt_cli)
1087
- --feishu-tenant-key <key>
1088
- Tenant key for --replay-feishu-text (default tenant_cli)
1089
- --print-openclaw-env <json>
1090
- Print a Rivus env file from an OpenClaw config JSON and exit
1091
- --pi-api-key-file <path>
1092
- Add PI_API_KEY_FILE when using --print-openclaw-env
1093
- --session-key <key> Session key for --prompt (default local:<agent-id>:cli)
1094
- --status Print bootstrap status JSON without starting the daemon
1095
- --recovery-list List Dead Letters and Tool operations requiring reconciliation
1096
- --requeue-dead-letter <message-id>
1097
- Requeue one Dead Letter; requires --endpoint-id, --expected-revision, and --recovery-note-file
1098
- --resolve-tool-operation <operation-id>
1099
- Resolve one uncertain Tool operation; requires --instance-id, --expected-revision,
1100
- --tool-outcome, and --recovery-note-file
1101
- --endpoint-id <id> Endpoint owning the Dead Letter
1102
- --instance-id <id> Agent Instance owning the Tool operation
1103
- --expected-revision <n>
1104
- Exact record revision required by a recovery mutation
1105
- --recovery-note-file <path>
1106
- Private (0600) file containing the required audit note
1107
- --tool-outcome <applied|not-applied>
1108
- Confirm whether the external Tool effect occurred
1109
- --tool-result-file <path>
1110
- Private (0600) JSON file required when --tool-outcome is applied
1111
- --status-url <url> Print live daemon status JSON from a running daemon
1112
- --wait-receive <kind> Wait with --status-url or a started bootstrap daemon until receive.lastAccepted or receive.lastHandled exists
1113
- --wait-receive-text <text>
1114
- With --wait-receive handled, wait until receive.lastHandled.intake.text contains text
1115
- --wait-receive-message-id <id>
1116
- With --wait-receive, wait until the accepted or handled receive observation has this Feishu message id
1117
- --wait-receive-observed-after <iso>
1118
- With --wait-receive, ignore receive observations at or before this ISO timestamp
1119
- --wait-timeout-ms <n> Timeout for --wait-receive (default 30000)
1120
- --wait-poll-ms <n> Poll interval for --wait-receive (default 500)
1121
- --help Show this help
1122
-
1123
- Environment:
1124
- RIVUS_HOME selects Rivus Home (default ~/.rivus-agent) for Home commands.
1125
- RIVUS_BOOTSTRAP_MODULE may be used instead of --bootstrap.
1126
- FEISHU_APP_ID and FEISHU_APP_SECRET are required by the default config loader.
1127
- FEISHU_CARD_STREAM_LEASE_MS shortens or extends the proactive CardKit rollover threshold.
1128
- PI_API_KEY_FILE may point to a local BYOK key file instead of PI_API_KEY.
1129
- `;
1130
- function parseRivusDaemonArguments(argv) {
1131
- let bootstrap;
1132
- let checkConfig = false;
1133
- let envFilePath;
1134
- let feishuReplayChatId;
1135
- let feishuReplayMessageId;
1136
- let feishuReplayTenantKey;
1137
- let feishuReplayThreadId;
1138
- let manifestPath;
1139
- let piApiKeyFile;
1140
- let prompt;
1141
- let printOpenClawEnvPath;
1142
- let replayFeishuEventPath;
1143
- let replayFeishuText;
1144
- const recoveryCli = createRivusRecoveryCliParser();
1145
- let sessionKey;
1146
- let status = false;
1147
- let statusUrl;
1148
- let waitPollMs;
1149
- let waitReceive;
1150
- let waitReceiveMessageId;
1151
- let waitReceiveObservedAfter;
1152
- let waitReceiveText;
1153
- let waitTimeoutMs;
1154
- for (let index = 0; index < argv.length; index += 1) {
1155
- const arg = argv[index];
1156
- if (arg === "--help" || arg === "-h") return {
1157
- help: true,
1158
- status,
1159
- ...statusUrl ? { statusUrl } : {}
1160
- };
1161
- if (arg === "--status") {
1162
- status = true;
1163
- continue;
1164
- }
1165
- const recoveryArgument = recoveryCli.consume(arg, argv[index + 1]);
1166
- if (recoveryArgument.handled) {
1167
- if (recoveryArgument.error) return {
1168
- error: recoveryArgument.error,
1169
- help: false,
1170
- status
1171
- };
1172
- index += recoveryArgument.consumed;
1173
- continue;
1174
- }
1175
- if (arg === "--check-config") {
1176
- checkConfig = true;
1177
- continue;
1178
- }
1179
- if (arg === "--env-file") {
1180
- const value = argv[index + 1];
1181
- if (!value) return {
1182
- error: "--env-file requires a path",
1183
- help: false,
1184
- status
1185
- };
1186
- envFilePath = value;
1187
- index += 1;
1188
- continue;
1189
- }
1190
- if (arg?.startsWith("--env-file=")) {
1191
- envFilePath = arg.slice(11);
1192
- if (!envFilePath) return {
1193
- error: "--env-file requires a path",
1194
- help: false,
1195
- status
1196
- };
1197
- continue;
1198
- }
1199
- if (arg === "--prompt") {
1200
- const value = argv[index + 1];
1201
- if (!value) return {
1202
- error: "--prompt requires text",
1203
- help: false,
1204
- status
1205
- };
1206
- prompt = value;
1207
- index += 1;
1208
- continue;
1209
- }
1210
- if (arg?.startsWith("--prompt=")) {
1211
- prompt = arg.slice(9);
1212
- if (!prompt) return {
1213
- error: "--prompt requires text",
1214
- help: false,
1215
- status
1216
- };
1217
- continue;
1218
- }
1219
- if (arg === "--session-key") {
1220
- const value = argv[index + 1];
1221
- if (!value) return {
1222
- error: "--session-key requires a value",
1223
- help: false,
1224
- status
1225
- };
1226
- sessionKey = value;
1227
- index += 1;
1228
- continue;
1229
- }
1230
- if (arg === "--print-openclaw-env") {
1231
- const value = argv[index + 1];
1232
- if (!value) return {
1233
- error: "--print-openclaw-env requires a JSON file path",
1234
- help: false,
1235
- status
1236
- };
1237
- printOpenClawEnvPath = value;
1238
- index += 1;
1239
- continue;
1240
- }
1241
- if (arg?.startsWith("--print-openclaw-env=")) {
1242
- printOpenClawEnvPath = arg.slice(21);
1243
- if (!printOpenClawEnvPath) return {
1244
- error: "--print-openclaw-env requires a JSON file path",
1245
- help: false,
1246
- status
1247
- };
1248
- continue;
1249
- }
1250
- if (arg === "--pi-api-key-file") {
1251
- const value = argv[index + 1];
1252
- if (!value) return {
1253
- error: "--pi-api-key-file requires a path",
1254
- help: false,
1255
- status
1256
- };
1257
- piApiKeyFile = value;
1258
- index += 1;
1259
- continue;
1260
- }
1261
- if (arg?.startsWith("--pi-api-key-file=")) {
1262
- piApiKeyFile = arg.slice(18);
1263
- if (!piApiKeyFile) return {
1264
- error: "--pi-api-key-file requires a path",
1265
- help: false,
1266
- status
1267
- };
1268
- continue;
1269
- }
1270
- if (arg === "--replay-feishu-event") {
1271
- const value = argv[index + 1];
1272
- if (!value) return {
1273
- error: "--replay-feishu-event requires a JSON file path",
1274
- help: false,
1275
- status
1276
- };
1277
- replayFeishuEventPath = value;
1278
- index += 1;
1279
- continue;
1280
- }
1281
- if (arg?.startsWith("--replay-feishu-event=")) {
1282
- replayFeishuEventPath = arg.slice(22);
1283
- if (!replayFeishuEventPath) return {
1284
- error: "--replay-feishu-event requires a JSON file path",
1285
- help: false,
1286
- status
1287
- };
1288
- continue;
1289
- }
1290
- if (arg === "--replay-feishu-text") {
1291
- const value = argv[index + 1];
1292
- if (!value) return {
1293
- error: "--replay-feishu-text requires text",
1294
- help: false,
1295
- status
1296
- };
1297
- replayFeishuText = value;
1298
- index += 1;
1299
- continue;
1300
- }
1301
- if (arg?.startsWith("--replay-feishu-text=")) {
1302
- replayFeishuText = arg.slice(21);
1303
- if (!replayFeishuText) return {
1304
- error: "--replay-feishu-text requires text",
1305
- help: false,
1306
- status
1307
- };
1308
- continue;
1309
- }
1310
- if (arg === "--feishu-message-id") {
1311
- const value = argv[index + 1];
1312
- if (!value) return {
1313
- error: "--feishu-message-id requires a value",
1314
- help: false,
1315
- status
1316
- };
1317
- feishuReplayMessageId = value;
1318
- index += 1;
1319
- continue;
1320
- }
1321
- if (arg?.startsWith("--feishu-message-id=")) {
1322
- feishuReplayMessageId = arg.slice(20);
1323
- if (!feishuReplayMessageId) return {
1324
- error: "--feishu-message-id requires a value",
1325
- help: false,
1326
- status
1327
- };
1328
- continue;
1329
- }
1330
- if (arg === "--feishu-chat-id") {
1331
- const value = argv[index + 1];
1332
- if (!value) return {
1333
- error: "--feishu-chat-id requires a value",
1334
- help: false,
1335
- status
1336
- };
1337
- feishuReplayChatId = value;
1338
- index += 1;
1339
- continue;
1340
- }
1341
- if (arg?.startsWith("--feishu-chat-id=")) {
1342
- feishuReplayChatId = arg.slice(17);
1343
- if (!feishuReplayChatId) return {
1344
- error: "--feishu-chat-id requires a value",
1345
- help: false,
1346
- status
1347
- };
1348
- continue;
1349
- }
1350
- if (arg === "--feishu-thread-id") {
1351
- const value = argv[index + 1];
1352
- if (!value) return {
1353
- error: "--feishu-thread-id requires a value",
1354
- help: false,
1355
- status
1356
- };
1357
- feishuReplayThreadId = value;
1358
- index += 1;
1359
- continue;
1360
- }
1361
- if (arg?.startsWith("--feishu-thread-id=")) {
1362
- feishuReplayThreadId = arg.slice(19);
1363
- if (!feishuReplayThreadId) return {
1364
- error: "--feishu-thread-id requires a value",
1365
- help: false,
1366
- status
1367
- };
1368
- continue;
1369
- }
1370
- if (arg === "--feishu-tenant-key") {
1371
- const value = argv[index + 1];
1372
- if (!value) return {
1373
- error: "--feishu-tenant-key requires a value",
1374
- help: false,
1375
- status
1376
- };
1377
- feishuReplayTenantKey = value;
1378
- index += 1;
1379
- continue;
1380
- }
1381
- if (arg?.startsWith("--feishu-tenant-key=")) {
1382
- feishuReplayTenantKey = arg.slice(20);
1383
- if (!feishuReplayTenantKey) return {
1384
- error: "--feishu-tenant-key requires a value",
1385
- help: false,
1386
- status
1387
- };
1388
- continue;
1389
- }
1390
- if (arg?.startsWith("--session-key=")) {
1391
- sessionKey = arg.slice(14);
1392
- if (!sessionKey) return {
1393
- error: "--session-key requires a value",
1394
- help: false,
1395
- status
1396
- };
1397
- continue;
1398
- }
1399
- if (arg === "--status-url") {
1400
- const value = argv[index + 1];
1401
- if (!value) return {
1402
- error: "--status-url requires a URL",
1403
- help: false,
1404
- status
1405
- };
1406
- statusUrl = value;
1407
- index += 1;
1408
- continue;
1409
- }
1410
- if (arg?.startsWith("--status-url=")) {
1411
- statusUrl = arg.slice(13);
1412
- if (!statusUrl) return {
1413
- error: "--status-url requires a URL",
1414
- help: false,
1415
- status
1416
- };
1417
- continue;
1418
- }
1419
- if (arg === "--wait-receive") {
1420
- const parsedWait = parseWaitReceive(argv[index + 1]);
1421
- if (!parsedWait) return {
1422
- error: "--wait-receive requires accepted or handled",
1423
- help: false,
1424
- status
1425
- };
1426
- waitReceive = parsedWait;
1427
- index += 1;
1428
- continue;
1429
- }
1430
- if (arg?.startsWith("--wait-receive=")) {
1431
- const parsedWait = parseWaitReceive(arg.slice(15));
1432
- if (!parsedWait) return {
1433
- error: "--wait-receive requires accepted or handled",
1434
- help: false,
1435
- status
1436
- };
1437
- waitReceive = parsedWait;
1438
- continue;
1439
- }
1440
- if (arg === "--wait-receive-text") {
1441
- const value = argv[index + 1];
1442
- if (!value) return {
1443
- error: "--wait-receive-text requires text",
1444
- help: false,
1445
- status
1446
- };
1447
- waitReceiveText = value;
1448
- index += 1;
1449
- continue;
1450
- }
1451
- if (arg?.startsWith("--wait-receive-text=")) {
1452
- waitReceiveText = arg.slice(20);
1453
- if (!waitReceiveText) return {
1454
- error: "--wait-receive-text requires text",
1455
- help: false,
1456
- status
1457
- };
1458
- continue;
1459
- }
1460
- if (arg === "--wait-receive-message-id") {
1461
- const value = argv[index + 1];
1462
- if (!value) return {
1463
- error: "--wait-receive-message-id requires a message id",
1464
- help: false,
1465
- status
1466
- };
1467
- waitReceiveMessageId = value;
1468
- index += 1;
1469
- continue;
1470
- }
1471
- if (arg?.startsWith("--wait-receive-message-id=")) {
1472
- waitReceiveMessageId = arg.slice(26);
1473
- if (!waitReceiveMessageId) return {
1474
- error: "--wait-receive-message-id requires a message id",
1475
- help: false,
1476
- status
1477
- };
1478
- continue;
1479
- }
1480
- if (arg === "--wait-receive-observed-after") {
1481
- const value = parseIsoTimestampArgument(argv[index + 1]);
1482
- if (!value) return {
1483
- error: "--wait-receive-observed-after requires an ISO timestamp",
1484
- help: false,
1485
- status
1486
- };
1487
- waitReceiveObservedAfter = value;
1488
- index += 1;
1489
- continue;
1490
- }
1491
- if (arg?.startsWith("--wait-receive-observed-after=")) {
1492
- const value = parseIsoTimestampArgument(arg.slice(30));
1493
- if (!value) return {
1494
- error: "--wait-receive-observed-after requires an ISO timestamp",
1495
- help: false,
1496
- status
1497
- };
1498
- waitReceiveObservedAfter = value;
1499
- continue;
1500
- }
1501
- if (arg === "--wait-timeout-ms") {
1502
- const parsedMs = parsePositiveIntegerArgument(argv[index + 1]);
1503
- if (parsedMs === void 0) return {
1504
- error: "--wait-timeout-ms requires a positive integer",
1505
- help: false,
1506
- status
1507
- };
1508
- waitTimeoutMs = parsedMs;
1509
- index += 1;
1510
- continue;
1511
- }
1512
- if (arg?.startsWith("--wait-timeout-ms=")) {
1513
- const parsedMs = parsePositiveIntegerArgument(arg.slice(18));
1514
- if (parsedMs === void 0) return {
1515
- error: "--wait-timeout-ms requires a positive integer",
1516
- help: false,
1517
- status
1518
- };
1519
- waitTimeoutMs = parsedMs;
1520
- continue;
1521
- }
1522
- if (arg === "--wait-poll-ms") {
1523
- const parsedMs = parsePositiveIntegerArgument(argv[index + 1]);
1524
- if (parsedMs === void 0) return {
1525
- error: "--wait-poll-ms requires a positive integer",
1526
- help: false,
1527
- status
1528
- };
1529
- waitPollMs = parsedMs;
1530
- index += 1;
1531
- continue;
1532
- }
1533
- if (arg?.startsWith("--wait-poll-ms=")) {
1534
- const parsedMs = parsePositiveIntegerArgument(arg.slice(15));
1535
- if (parsedMs === void 0) return {
1536
- error: "--wait-poll-ms requires a positive integer",
1537
- help: false,
1538
- status
1539
- };
1540
- waitPollMs = parsedMs;
1541
- continue;
1542
- }
1543
- if (arg === "--bootstrap") {
1544
- const value = argv[index + 1];
1545
- if (!value) return {
1546
- error: "--bootstrap requires a module specifier",
1547
- help: false,
1548
- status
1549
- };
1550
- bootstrap = value;
1551
- index += 1;
1552
- continue;
1553
- }
1554
- if (arg === "--manifest") {
1555
- const value = argv[index + 1];
1556
- if (!value) return {
1557
- error: "--manifest requires a path",
1558
- help: false,
1559
- status
1560
- };
1561
- manifestPath = value;
1562
- index += 1;
1563
- continue;
1564
- }
1565
- if (arg?.startsWith("--manifest=")) {
1566
- manifestPath = arg.slice(11);
1567
- if (!manifestPath) return {
1568
- error: "--manifest requires a path",
1569
- help: false,
1570
- status
1571
- };
1572
- continue;
1573
- }
1574
- if (arg?.startsWith("--bootstrap=")) {
1575
- bootstrap = arg.slice(12);
1576
- if (!bootstrap) return {
1577
- error: "--bootstrap requires a module specifier",
1578
- help: false,
1579
- status
1580
- };
1581
- continue;
1582
- }
1583
- return {
1584
- error: `Unknown argument: ${arg}`,
1585
- help: false,
1586
- status
1587
- };
1588
- }
1589
- if (prompt !== void 0 && status) return {
1590
- error: "--prompt cannot be combined with --status",
1591
- help: false,
1592
- status
1593
- };
1594
- if (checkConfig && status) return {
1595
- error: "--check-config cannot be combined with --status",
1596
- help: false,
1597
- status,
1598
- checkConfig
1599
- };
1600
- if (checkConfig && prompt !== void 0) return {
1601
- error: "--check-config cannot be combined with --prompt",
1602
- help: false,
1603
- status,
1604
- checkConfig
1605
- };
1606
- if (checkConfig && replayFeishuEventPath !== void 0) return {
1607
- error: "--check-config cannot be combined with --replay-feishu-event",
1608
- help: false,
1609
- status,
1610
- checkConfig
1611
- };
1612
- if (checkConfig && replayFeishuText !== void 0) return {
1613
- error: "--check-config cannot be combined with --replay-feishu-text",
1614
- help: false,
1615
- status,
1616
- checkConfig
1617
- };
1618
- if (checkConfig && printOpenClawEnvPath !== void 0) return {
1619
- error: "--check-config cannot be combined with --print-openclaw-env",
1620
- help: false,
1621
- status,
1622
- checkConfig
1623
- };
1624
- if (checkConfig && statusUrl) return {
1625
- error: "--check-config cannot be combined with --status-url",
1626
- help: false,
1627
- status,
1628
- checkConfig
1629
- };
1630
- if (prompt !== void 0 && statusUrl) return {
1631
- error: "--prompt cannot be combined with --status-url",
1632
- help: false,
1633
- status
1634
- };
1635
- if (printOpenClawEnvPath !== void 0 && prompt !== void 0) return {
1636
- error: "--print-openclaw-env cannot be combined with --prompt",
1637
- help: false,
1638
- status
1639
- };
1640
- if (printOpenClawEnvPath !== void 0 && replayFeishuEventPath !== void 0) return {
1641
- error: "--print-openclaw-env cannot be combined with --replay-feishu-event",
1642
- help: false,
1643
- status
1644
- };
1645
- if (printOpenClawEnvPath !== void 0 && replayFeishuText !== void 0) return {
1646
- error: "--print-openclaw-env cannot be combined with --replay-feishu-text",
1647
- help: false,
1648
- status
1649
- };
1650
- if (printOpenClawEnvPath !== void 0 && status) return {
1651
- error: "--print-openclaw-env cannot be combined with --status",
1652
- help: false,
1653
- status
1654
- };
1655
- if (printOpenClawEnvPath !== void 0 && statusUrl) return {
1656
- error: "--print-openclaw-env cannot be combined with --status-url",
1657
- help: false,
1658
- status
1659
- };
1660
- if (piApiKeyFile !== void 0 && printOpenClawEnvPath === void 0) return {
1661
- error: "--pi-api-key-file requires --print-openclaw-env",
1662
- help: false,
1663
- status
1664
- };
1665
- if (replayFeishuEventPath !== void 0 && prompt !== void 0) return {
1666
- error: "--replay-feishu-event cannot be combined with --prompt",
1667
- help: false,
1668
- status
1669
- };
1670
- if (replayFeishuEventPath !== void 0 && replayFeishuText !== void 0) return {
1671
- error: "--replay-feishu-event cannot be combined with --replay-feishu-text",
1672
- help: false,
1673
- status
1674
- };
1675
- if (replayFeishuEventPath !== void 0 && status) return {
1676
- error: "--replay-feishu-event cannot be combined with --status",
1677
- help: false,
1678
- status
1679
- };
1680
- if (replayFeishuEventPath !== void 0 && statusUrl) return {
1681
- error: "--replay-feishu-event cannot be combined with --status-url",
1682
- help: false,
1683
- status
1684
- };
1685
- if (replayFeishuText !== void 0 && prompt !== void 0) return {
1686
- error: "--replay-feishu-text cannot be combined with --prompt",
1687
- help: false,
1688
- status
1689
- };
1690
- if (replayFeishuText !== void 0 && status) return {
1691
- error: "--replay-feishu-text cannot be combined with --status",
1692
- help: false,
1693
- status
1694
- };
1695
- if (replayFeishuText !== void 0 && statusUrl) return {
1696
- error: "--replay-feishu-text cannot be combined with --status-url",
1697
- help: false,
1698
- status
1699
- };
1700
- if (feishuReplayMessageId !== void 0 && replayFeishuText === void 0) return {
1701
- error: "--feishu-message-id requires --replay-feishu-text",
1702
- help: false,
1703
- status
1704
- };
1705
- if (feishuReplayChatId !== void 0 && replayFeishuText === void 0) return {
1706
- error: "--feishu-chat-id requires --replay-feishu-text",
1707
- help: false,
1708
- status
1709
- };
1710
- if (feishuReplayThreadId !== void 0 && replayFeishuText === void 0) return {
1711
- error: "--feishu-thread-id requires --replay-feishu-text",
1712
- help: false,
1713
- status
1714
- };
1715
- if (feishuReplayTenantKey !== void 0 && replayFeishuText === void 0) return {
1716
- error: "--feishu-tenant-key requires --replay-feishu-text",
1717
- help: false,
1718
- status
1719
- };
1720
- if (sessionKey !== void 0 && prompt === void 0) return {
1721
- error: "--session-key requires --prompt",
1722
- help: false,
1723
- status
1724
- };
1725
- if (waitReceive !== void 0 && status) return {
1726
- error: "--wait-receive cannot be combined with --status",
1727
- help: false,
1728
- status
1729
- };
1730
- if (waitReceive !== void 0 && checkConfig) return {
1731
- error: "--wait-receive cannot be combined with --check-config",
1732
- help: false,
1733
- status,
1734
- checkConfig
1735
- };
1736
- if (waitReceive !== void 0 && prompt !== void 0) return {
1737
- error: "--wait-receive cannot be combined with --prompt",
1738
- help: false,
1739
- status
1740
- };
1741
- if (waitReceive !== void 0 && replayFeishuEventPath !== void 0) return {
1742
- error: "--wait-receive cannot be combined with --replay-feishu-event",
1743
- help: false,
1744
- status
1745
- };
1746
- if (waitReceive !== void 0 && replayFeishuText !== void 0) return {
1747
- error: "--wait-receive cannot be combined with --replay-feishu-text",
1748
- help: false,
1749
- status
1750
- };
1751
- if (waitReceive !== void 0 && printOpenClawEnvPath !== void 0) return {
1752
- error: "--wait-receive cannot be combined with --print-openclaw-env",
1753
- help: false,
1754
- status
1755
- };
1756
- if (waitTimeoutMs !== void 0 && waitReceive === void 0) return {
1757
- error: "--wait-timeout-ms requires --wait-receive",
1758
- help: false,
1759
- status
1760
- };
1761
- if (waitPollMs !== void 0 && waitReceive === void 0) return {
1762
- error: "--wait-poll-ms requires --wait-receive",
1763
- help: false,
1764
- status
1765
- };
1766
- if (waitReceiveText !== void 0 && waitReceive !== "handled") return {
1767
- error: "--wait-receive-text requires --wait-receive handled",
1768
- help: false,
1769
- status
1770
- };
1771
- if (waitReceiveMessageId !== void 0 && waitReceive === void 0) return {
1772
- error: "--wait-receive-message-id requires --wait-receive",
1773
- help: false,
1774
- status
1775
- };
1776
- if (waitReceiveObservedAfter !== void 0 && waitReceive === void 0) return {
1777
- error: "--wait-receive-observed-after requires --wait-receive",
1778
- help: false,
1779
- status
1780
- };
1781
- const recovery = recoveryCli.build();
1782
- if (recovery.error) return {
1783
- error: recovery.error,
1784
- help: false,
1785
- status
1786
- };
1787
- if (recovery.command) {
1788
- if (!manifestPath) return {
1789
- error: "Recovery commands require --manifest",
1790
- help: false,
1791
- status
1792
- };
1793
- if (checkConfig || prompt !== void 0 || printOpenClawEnvPath !== void 0 || replayFeishuEventPath !== void 0 || replayFeishuText !== void 0 || status || statusUrl !== void 0 || waitReceive !== void 0) return {
1794
- error: "Recovery commands cannot be combined with another one-shot command",
1795
- help: false,
1796
- status
1797
- };
1798
- }
1799
- return {
1800
- ...bootstrap ? { bootstrap } : {},
1801
- checkConfig,
1802
- ...envFilePath ? { envFilePath } : {},
1803
- ...feishuReplayChatId !== void 0 ? { feishuReplayChatId } : {},
1804
- ...feishuReplayMessageId !== void 0 ? { feishuReplayMessageId } : {},
1805
- ...feishuReplayTenantKey !== void 0 ? { feishuReplayTenantKey } : {},
1806
- ...feishuReplayThreadId !== void 0 ? { feishuReplayThreadId } : {},
1807
- help: false,
1808
- ...manifestPath ? { manifestPath } : {},
1809
- ...piApiKeyFile !== void 0 ? { piApiKeyFile } : {},
1810
- ...prompt !== void 0 ? { prompt } : {},
1811
- ...printOpenClawEnvPath !== void 0 ? { printOpenClawEnvPath } : {},
1812
- ...replayFeishuEventPath !== void 0 ? { replayFeishuEventPath } : {},
1813
- ...replayFeishuText !== void 0 ? { replayFeishuText } : {},
1814
- ...recovery.command ? { recoveryCommand: recovery.command } : {},
1815
- ...sessionKey ? { sessionKey } : {},
1816
- status,
1817
- ...statusUrl ? { statusUrl } : {},
1818
- ...waitPollMs !== void 0 ? { waitPollMs } : {},
1819
- ...waitReceive !== void 0 ? { waitReceive } : {},
1820
- ...waitReceiveMessageId !== void 0 ? { waitReceiveMessageId } : {},
1821
- ...waitReceiveObservedAfter !== void 0 ? { waitReceiveObservedAfter } : {},
1822
- ...waitReceiveText !== void 0 ? { waitReceiveText } : {},
1823
- ...waitTimeoutMs !== void 0 ? { waitTimeoutMs } : {}
1824
- };
1825
- }
1826
- function parseWaitReceive(value) {
1827
- return value === "accepted" || value === "handled" ? value : void 0;
1828
- }
1829
- function parsePositiveIntegerArgument(value) {
1830
- return value && /^[1-9]\d*$/.test(value) ? Number(value) : void 0;
1831
- }
1832
- function parseIsoTimestampArgument(value) {
1833
- if (!value) return;
1834
- const time = Date.parse(value);
1835
- return Number.isNaN(time) ? void 0 : new Date(time).toISOString();
1836
- }
1837
- //#endregion
1838
- //#region src/adapters/cli/command/rivus-daemon-output.ts
1839
- const RIVUS_DAEMON_STARTED_MESSAGE = "Rivus Agent daemon started\n";
1840
- function formatRivusDaemonUsageError(error) {
1841
- return `${error}\n\n${RIVUS_DAEMON_USAGE}`;
1842
- }
1843
- function formatRivusDaemonJson(value) {
1844
- return `${JSON.stringify(value, null, 2)}\n`;
1845
- }
1846
- function formatRivusDaemonLine(text) {
1847
- return `${text}\n`;
1848
- }
1849
- function formatRivusDaemonWarning(warning) {
1850
- return `Warning: ${warning}\n`;
1851
- }
1852
- function formatRivusDaemonCapabilityError(capability) {
1853
- return `${{
1854
- recovery: "Bootstrap daemon does not expose openRecoveryControl()",
1855
- status: "Bootstrap daemon does not expose status()",
1856
- prompt: "Bootstrap daemon does not expose promptText(command)",
1857
- replay: "Bootstrap daemon does not expose replayReceiveMessage(payload)"
1858
- }[capability]}\n`;
1859
- }
1860
- function formatRivusDaemonShutdownFailure(signal, error) {
1861
- return `Failed to stop daemon after ${signal}: ${error}\n`;
1862
- }
1863
- //#endregion
1864
- //#region src/adapters/cli/replay/rivus-daemon-replay.ts
1865
- async function readFeishuReceiveMessagePayload(filePath) {
1866
- return JSON.parse(await readFile(filePath, "utf8"));
1867
- }
1868
- function createSyntheticFeishuTextReplayPayload(text, options) {
1869
- return { event: {
1870
- message: {
1871
- chat_id: options.chatId ?? "oc_cli",
1872
- content: JSON.stringify({ text }),
1873
- message_id: options.messageId ?? `om_cli_${Date.now()}`,
1874
- message_type: "text",
1875
- thread_id: options.threadId ?? "omt_cli"
1876
- },
1877
- sender: { tenant_key: options.tenantKey ?? "tenant_cli" }
1878
- } };
1879
- }
1880
- function formatRivusDaemonError(error) {
1881
- if (error instanceof ReceiveWaitTimeoutError) return `${error.message}\nLast status:\n${JSON.stringify(error.lastStatus, null, 2)}`;
1882
- return error instanceof Error ? error.message : String(error);
1883
- }
1884
- var ReceiveWaitTimeoutError = class extends Error {
1885
- target;
1886
- lastStatus;
1887
- messageId;
1888
- observedAfter;
1889
- text;
1890
- constructor(target, lastStatus, messageId, observedAfter, text) {
1891
- super(`Timed out waiting for ${target === "accepted" ? "receive.lastAccepted" : "receive.lastHandled"}${messageId ? ` with message id ${JSON.stringify(messageId)}` : ""}${observedAfter ? ` observed after ${JSON.stringify(observedAfter)}` : ""}${text ? ` containing text ${JSON.stringify(text)}` : ""}`);
1892
- this.target = target;
1893
- this.lastStatus = lastStatus;
1894
- this.messageId = messageId;
1895
- this.observedAfter = observedAfter;
1896
- this.text = text;
1897
- this.name = "ReceiveWaitTimeoutError";
1898
- }
1899
- };
1900
- async function fetchLiveStatus(url) {
1901
- const response = await fetch(url, { headers: { accept: "application/json" } });
1902
- if (!response.ok) throw new Error(`Status request failed with HTTP ${response.status}`);
1903
- return response.json();
1904
- }
1905
- function waitForReceiveStatus(readStatus, target, options) {
1906
- return Effect.suspend(() => {
1907
- const startedAt = Date.now();
1908
- let lastStatus;
1909
- const poll = () => readStatus().pipe(Effect.flatMap((status) => {
1910
- lastStatus = status;
1911
- if (hasReceiveObservation(status, target, {
1912
- ...options.messageId ? { messageId: options.messageId } : {},
1913
- ...options.observedAfter ? { observedAfter: options.observedAfter } : {},
1914
- ...options.text ? { text: options.text } : {}
1915
- })) return Effect.succeed(status);
1916
- if (Date.now() - startedAt >= options.timeoutMs) return Effect.fail(new ReceiveWaitTimeoutError(target, lastStatus, options.messageId, options.observedAfter, options.text));
1917
- return Effect.sleep(options.pollMs).pipe(Effect.flatMap(poll));
1918
- }));
1919
- return poll();
1920
- });
1921
- }
1922
- function hasReceiveObservation(status, target, criteria) {
1923
- const receive = isStatusRecord(status) ? status.receive : void 0;
1924
- if (!isStatusRecord(receive)) return false;
1925
- if (target === "accepted") {
1926
- const lastAccepted = receive.lastAccepted;
1927
- if (!isStatusRecord(lastAccepted)) return false;
1928
- if (criteria.messageId === void 0) return observedAfterMatches(lastAccepted, criteria.observedAfter);
1929
- const message = lastAccepted.message;
1930
- return isStatusRecord(message) && message.messageId === criteria.messageId && observedAfterMatches(lastAccepted, criteria.observedAfter);
1931
- }
1932
- const lastHandled = receive.lastHandled;
1933
- if (!isStatusRecord(lastHandled)) return false;
1934
- if (criteria.messageId !== void 0 && lastHandled.messageId !== criteria.messageId) return false;
1935
- if (!observedAfterMatches(lastHandled, criteria.observedAfter)) return false;
1936
- if (criteria.text === void 0) return true;
1937
- const intake = lastHandled.intake;
1938
- return isStatusRecord(intake) && typeof intake.text === "string" && intake.text.includes(criteria.text);
1939
- }
1940
- function isStatusRecord(value) {
1941
- return typeof value === "object" && value !== null && !Array.isArray(value);
1942
- }
1943
- function observedAfterMatches(observation, observedAfter) {
1944
- if (observedAfter === void 0) return true;
1945
- const observedAtMs = readObservedAtMs(observation.observedAt);
1946
- return observedAtMs !== void 0 && observedAtMs > Date.parse(observedAfter);
1947
- }
1948
- function readObservedAtMs(value) {
1949
- if (value instanceof Date) {
1950
- const time = value.getTime();
1951
- return Number.isNaN(time) ? void 0 : time;
1952
- }
1953
- if (typeof value === "string") {
1954
- const time = Date.parse(value);
1955
- return Number.isNaN(time) ? void 0 : time;
1956
- }
1957
- }
1958
- //#endregion
1959
- //#region src/core/application/deployment/manifest/deployment-manifest.ts
1960
- const DEFAULT_MANIFEST_BACKGROUND_SESSION_LEASE_MS = 3e4;
1961
- const DEFAULT_MANIFEST_BACKGROUND_SESSION_LEASE_RENEWAL_INTERVAL_MS = 1e4;
1962
- const DEFAULT_MANIFEST_BACKGROUND_SESSION_MAX_CONSECUTIVE_FAILURES = 3;
1963
- const DEFAULT_MANIFEST_BACKGROUND_SESSION_MAX_CONCURRENT_SESSIONS = 4;
1964
- const DEFAULT_MANIFEST_BACKGROUND_SESSION_RETRY_BACKOFF_MS = 3e4;
1965
- const DEFAULT_MANIFEST_BACKGROUND_SESSION_LIFETIME_MS = 1440 * 60 * 1e3;
1966
- const DEFAULT_MANIFEST_BACKGROUND_SESSION_STEP_TIMEOUT_MS = 300 * 1e3;
1967
- const DEFAULT_MANIFEST_CARD_STREAM_LEASE_MS = 51e4;
1968
- const DEFAULT_MANIFEST_CONVERSATION_PROGRESS_DISPLAY = "collapsed";
1969
- const MEMORY_SCOPES = [
1970
- "conversation",
1971
- "agent-private",
1972
- "project",
1973
- "shared-user-profile"
1974
- ];
1975
- var InvalidRivusProjectSpace = class extends Error {
1976
- name = "InvalidRivusProjectSpace";
1977
- };
1978
- function validateRivusProjectSpaceDeployments(declarations) {
1979
- const projectSpaceIds = /* @__PURE__ */ new Set();
1980
- for (const declaration of declarations) {
1981
- if (projectSpaceIds.has(declaration.id)) throw new InvalidRivusProjectSpace(`duplicate project space: ${declaration.id}`);
1982
- projectSpaceIds.add(declaration.id);
1983
- validateRivusProjectSpaceDeployment(declaration);
1984
- }
1985
- return projectSpaceIds;
1986
- }
1987
- function validateRivusProjectSpaceDeployment(declaration) {
1988
- validateRelativeProjectPath(declaration.root, `project space ${declaration.id} root`);
1989
- validateRelativeProjectPath(declaration.workingDirectory, `project space ${declaration.id} working directory`);
1990
- const sources = /* @__PURE__ */ new Set();
1991
- for (const source of declaration.skills.sources) {
1992
- validateRelativeProjectPath(source, `project space ${declaration.id} Skill source`);
1993
- if (sources.has(source)) throw new InvalidRivusProjectSpace(`project space ${declaration.id} contains duplicate Skill source: ${source}`);
1994
- sources.add(source);
1995
- }
1996
- }
1997
- function validateRelativeProjectPath(value, owner) {
1998
- if (value.trim() === "" || isAbsoluteProjectPath(value) || value.includes("\0")) throw new InvalidRivusProjectSpace(`${owner} must be a non-empty relative path`);
1999
- }
2000
- function isAbsoluteProjectPath(value) {
2001
- return value.startsWith("/") || value.startsWith("\\") || /^[a-z]:[\\/]/i.test(value);
2002
- }
2003
- function parseRivusDeploymentManifest(value) {
2004
- const root = record(value, "manifest");
2005
- exactKeys(root, [
2006
- "agents",
2007
- "automations",
2008
- "backgroundSessions",
2009
- "defaultAgentId",
2010
- "defaultEndpointId",
2011
- "endpoints",
2012
- "plugins",
2013
- "projectSpaces"
2014
- ], "manifest", [
2015
- "automations",
2016
- "backgroundSessions",
2017
- "projectSpaces"
2018
- ]);
2019
- const plugins = array(root.plugins, "manifest.plugins").map((entry, index) => {
2020
- const plugin = record(entry, `manifest.plugins[${index}]`);
2021
- exactKeys(plugin, [
2022
- "id",
2023
- "module",
2024
- "required"
2025
- ], `manifest.plugins[${index}]`);
2026
- return Object.freeze({
2027
- id: string(plugin.id, `manifest.plugins[${index}].id`),
2028
- module: string(plugin.module, `manifest.plugins[${index}].module`),
2029
- required: boolean(plugin.required, `manifest.plugins[${index}].required`)
2030
- });
2031
- });
2032
- const agents = array(root.agents, "manifest.agents").map((entry, index) => {
2033
- const agent = record(entry, `manifest.agents[${index}]`);
2034
- exactKeys(agent, [
2035
- "agentId",
2036
- "endpointIds",
2037
- "memory",
2038
- "pluginId",
2039
- "profileId",
2040
- "projectSpaceId",
2041
- "runtimeTools",
2042
- "skills",
2043
- "tools"
2044
- ], `manifest.agents[${index}]`, [
2045
- "memory",
2046
- "projectSpaceId",
2047
- "runtimeTools"
2048
- ]);
2049
- const memory = agent.memory === void 0 ? void 0 : record(agent.memory, `manifest.agents[${index}].memory`);
2050
- if (memory) exactKeys(memory, ["scopes", "tool"], `manifest.agents[${index}].memory`);
2051
- const skills = record(agent.skills, `manifest.agents[${index}].skills`);
2052
- exactKeys(skills, ["allow"], `manifest.agents[${index}].skills`);
2053
- const tools = record(agent.tools, `manifest.agents[${index}].tools`);
2054
- exactKeys(tools, ["allow"], `manifest.agents[${index}].tools`);
2055
- const runtimeTools = agent.runtimeTools === void 0 ? void 0 : record(agent.runtimeTools, `manifest.agents[${index}].runtimeTools`);
2056
- if (runtimeTools) exactKeys(runtimeTools, ["allow"], `manifest.agents[${index}].runtimeTools`);
2057
- return Object.freeze({
2058
- agentId: string(agent.agentId, `manifest.agents[${index}].agentId`),
2059
- endpointIds: Object.freeze(array(agent.endpointIds, `manifest.agents[${index}].endpointIds`).map((item, itemIndex) => string(item, `manifest.agents[${index}].endpointIds[${itemIndex}]`))),
2060
- ...memory ? { memory: Object.freeze({
2061
- scopes: Object.freeze(array(memory.scopes, `manifest.agents[${index}].memory.scopes`).map((item, itemIndex) => memoryScope(item, `manifest.agents[${index}].memory.scopes[${itemIndex}]`))),
2062
- tool: boolean(memory.tool, `manifest.agents[${index}].memory.tool`)
2063
- }) } : {},
2064
- pluginId: string(agent.pluginId, `manifest.agents[${index}].pluginId`),
2065
- profileId: string(agent.profileId, `manifest.agents[${index}].profileId`),
2066
- ...agent.projectSpaceId === void 0 ? {} : { projectSpaceId: string(agent.projectSpaceId, `manifest.agents[${index}].projectSpaceId`) },
2067
- ...runtimeTools ? { runtimeTools: Object.freeze({ allow: Object.freeze(uniqueRuntimeTools(array(runtimeTools.allow, `manifest.agents[${index}].runtimeTools.allow`).map((item, itemIndex) => runtimeToolId(item, `manifest.agents[${index}].runtimeTools.allow[${itemIndex}]`)), `manifest.agents[${index}].runtimeTools.allow`)) }) } : {},
2068
- 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}]`))) }),
2069
- 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}]`))) })
2070
- });
2071
- });
2072
- const endpoints = array(root.endpoints, "manifest.endpoints").map((entry, index) => {
2073
- const endpoint = record(entry, `manifest.endpoints[${index}]`);
2074
- exactKeys(endpoint, [
2075
- "agentId",
2076
- "baseUrl",
2077
- "cardStreamLeaseMs",
2078
- "credentialRef",
2079
- "enabled",
2080
- "experimental",
2081
- "groupPolicy",
2082
- "id",
2083
- "progressDisplay",
2084
- "required",
2085
- "sessionNamespace",
2086
- "streamMinIntervalMs"
2087
- ], `manifest.endpoints[${index}]`, [
2088
- "cardStreamLeaseMs",
2089
- "experimental",
2090
- "progressDisplay"
2091
- ]);
2092
- const experimental = endpoint.experimental === void 0 ? void 0 : record(endpoint.experimental, `manifest.endpoints[${index}].experimental`);
2093
- if (experimental) exactKeys(experimental, ["cotMessages"], `manifest.endpoints[${index}].experimental`);
2094
- return Object.freeze({
2095
- agentId: string(endpoint.agentId, `manifest.endpoints[${index}].agentId`),
2096
- baseUrl: string(endpoint.baseUrl, `manifest.endpoints[${index}].baseUrl`),
2097
- cardStreamLeaseMs: endpoint.cardStreamLeaseMs === void 0 ? DEFAULT_MANIFEST_CARD_STREAM_LEASE_MS : positiveInteger(endpoint.cardStreamLeaseMs, `manifest.endpoints[${index}].cardStreamLeaseMs`),
2098
- credentialRef: string(endpoint.credentialRef, `manifest.endpoints[${index}].credentialRef`),
2099
- enabled: boolean(endpoint.enabled, `manifest.endpoints[${index}].enabled`),
2100
- ...experimental ? { experimental: Object.freeze({ cotMessages: boolean(experimental.cotMessages, `manifest.endpoints[${index}].experimental.cotMessages`) }) } : {},
2101
- groupPolicy: groupPolicy(endpoint.groupPolicy, `manifest.endpoints[${index}].groupPolicy`),
2102
- id: string(endpoint.id, `manifest.endpoints[${index}].id`),
2103
- progressDisplay: endpoint.progressDisplay === void 0 ? DEFAULT_MANIFEST_CONVERSATION_PROGRESS_DISPLAY : progressDisplay(endpoint.progressDisplay, `manifest.endpoints[${index}].progressDisplay`),
2104
- required: boolean(endpoint.required, `manifest.endpoints[${index}].required`),
2105
- sessionNamespace: string(endpoint.sessionNamespace, `manifest.endpoints[${index}].sessionNamespace`),
2106
- streamMinIntervalMs: positiveInteger(endpoint.streamMinIntervalMs, `manifest.endpoints[${index}].streamMinIntervalMs`)
2107
- });
2108
- });
2109
- const automations = array(root.automations ?? [], "manifest.automations").map((entry, index) => {
2110
- const automation = record(entry, `manifest.automations[${index}]`);
2111
- exactKeys(automation, [
2112
- "agentId",
2113
- "delivery",
2114
- "enabled",
2115
- "id",
2116
- "required",
2117
- "schedule",
2118
- "templateId",
2119
- "timeZone"
2120
- ], `manifest.automations[${index}]`);
2121
- const delivery = record(automation.delivery, `manifest.automations[${index}].delivery`);
2122
- exactKeys(delivery, [
2123
- "endpointId",
2124
- "targetRef",
2125
- "targetType"
2126
- ], `manifest.automations[${index}].delivery`);
2127
- return Object.freeze({
2128
- agentId: string(automation.agentId, `manifest.automations[${index}].agentId`),
2129
- delivery: Object.freeze({
2130
- endpointId: string(delivery.endpointId, `manifest.automations[${index}].delivery.endpointId`),
2131
- targetRef: string(delivery.targetRef, `manifest.automations[${index}].delivery.targetRef`),
2132
- targetType: automationTargetType(delivery.targetType, `manifest.automations[${index}].delivery.targetType`)
2133
- }),
2134
- enabled: boolean(automation.enabled, `manifest.automations[${index}].enabled`),
2135
- id: string(automation.id, `manifest.automations[${index}].id`),
2136
- required: boolean(automation.required, `manifest.automations[${index}].required`),
2137
- schedule: string(automation.schedule, `manifest.automations[${index}].schedule`),
2138
- templateId: string(automation.templateId, `manifest.automations[${index}].templateId`),
2139
- timeZone: string(automation.timeZone, `manifest.automations[${index}].timeZone`)
2140
- });
2141
- });
2142
- const projectSpaces = array(root.projectSpaces ?? [], "manifest.projectSpaces").map((entry, index) => {
2143
- const projectSpace = record(entry, `manifest.projectSpaces[${index}]`);
2144
- exactKeys(projectSpace, [
2145
- "id",
2146
- "root",
2147
- "skills",
2148
- "workingDirectory"
2149
- ], `manifest.projectSpaces[${index}]`);
2150
- const skills = record(projectSpace.skills, `manifest.projectSpaces[${index}].skills`);
2151
- exactKeys(skills, ["sources"], `manifest.projectSpaces[${index}].skills`);
2152
- return Object.freeze({
2153
- id: string(projectSpace.id, `manifest.projectSpaces[${index}].id`),
2154
- root: string(projectSpace.root, `manifest.projectSpaces[${index}].root`),
2155
- 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}]`))) }),
2156
- workingDirectory: string(projectSpace.workingDirectory, `manifest.projectSpaces[${index}].workingDirectory`)
2157
- });
2158
- });
2159
- const backgroundSessions = root.backgroundSessions === void 0 ? void 0 : parseBackgroundSessions(record(root.backgroundSessions, "manifest.backgroundSessions"));
2160
- return Object.freeze({
2161
- agents: Object.freeze(agents),
2162
- automations: Object.freeze(automations),
2163
- ...backgroundSessions ? { backgroundSessions } : {},
2164
- defaultAgentId: string(root.defaultAgentId, "manifest.defaultAgentId"),
2165
- defaultEndpointId: string(root.defaultEndpointId, "manifest.defaultEndpointId"),
2166
- endpoints: Object.freeze(endpoints),
2167
- plugins: Object.freeze(plugins),
2168
- projectSpaces: Object.freeze(projectSpaces)
2169
- });
2170
- }
2171
- function validateRivusDeploymentManifest(manifest) {
2172
- const projectSpaceIds = validateRivusProjectSpaceDeployments(manifest.projectSpaces ?? []);
2173
- const pluginIds = /* @__PURE__ */ new Set();
2174
- for (const plugin of manifest.plugins) {
2175
- if (pluginIds.has(plugin.id)) throw new Error(`duplicate plugin declaration: ${plugin.id}`);
2176
- validateModuleSpecifier(plugin.module);
2177
- pluginIds.add(plugin.id);
2178
- }
2179
- const agentIds = /* @__PURE__ */ new Set();
2180
- const agentById = /* @__PURE__ */ new Map();
2181
- for (const agent of manifest.agents) {
2182
- if (agentIds.has(agent.agentId)) throw new Error(`duplicate agent deployment: ${agent.agentId}`);
2183
- agentIds.add(agent.agentId);
2184
- if (!pluginIds.has(agent.pluginId)) throw new Error(`agent ${agent.agentId} references undeclared plugin: ${agent.pluginId}`);
2185
- if (agent.projectSpaceId && !projectSpaceIds.has(agent.projectSpaceId)) throw new Error(`agent ${agent.agentId} references unknown project space: ${agent.projectSpaceId}`);
2186
- agentById.set(agent.agentId, agent);
2187
- }
2188
- const endpointIds = /* @__PURE__ */ new Set();
2189
- const sessionNamespaces = /* @__PURE__ */ new Set();
2190
- for (const endpoint of manifest.endpoints) {
2191
- if (endpointIds.has(endpoint.id)) throw new Error(`duplicate endpoint binding: ${endpoint.id}`);
2192
- endpointIds.add(endpoint.id);
2193
- if (sessionNamespaces.has(endpoint.sessionNamespace)) throw new Error(`duplicate endpoint session namespace: ${endpoint.sessionNamespace}`);
2194
- sessionNamespaces.add(endpoint.sessionNamespace);
2195
- const agent = agentById.get(endpoint.agentId);
2196
- if (!agent) throw new Error(`endpoint ${endpoint.id} references unknown agent: ${endpoint.agentId}`);
2197
- if (!agent.endpointIds.includes(endpoint.id)) throw new Error(`endpoint ${endpoint.id} is not declared by agent ${endpoint.agentId}`);
2198
- }
2199
- for (const agent of manifest.agents) for (const endpointId of agent.endpointIds) {
2200
- const endpoint = manifest.endpoints.find(({ id }) => id === endpointId);
2201
- if (!endpoint) throw new Error(`agent ${agent.agentId} references unknown endpoint: ${endpointId}`);
2202
- if (endpoint.agentId !== agent.agentId) throw new Error(`endpoint ${endpointId} is bound to a different agent`);
2203
- }
2204
- const automationIds = /* @__PURE__ */ new Set();
2205
- for (const automation of manifest.automations ?? []) {
2206
- if (automationIds.has(automation.id)) throw new Error(`duplicate automation binding: ${automation.id}`);
2207
- automationIds.add(automation.id);
2208
- if (!agentById.has(automation.agentId)) throw new Error(`automation ${automation.id} references unknown agent: ${automation.agentId}`);
2209
- const endpoint = manifest.endpoints.find(({ id }) => id === automation.delivery.endpointId);
2210
- if (!endpoint) throw new Error(`automation ${automation.id} references unknown delivery endpoint: ${automation.delivery.endpointId}`);
2211
- if (automation.enabled && !endpoint.enabled) throw new Error(`automation ${automation.id} delivery endpoint must be enabled`);
2212
- }
2213
- const defaultAgent = agentById.get(manifest.defaultAgentId);
2214
- if (!defaultAgent) throw new Error(`default agent does not exist: ${manifest.defaultAgentId}`);
2215
- const defaultEndpoint = manifest.endpoints.find(({ id }) => id === manifest.defaultEndpointId);
2216
- if (!defaultEndpoint) throw new Error(`default endpoint does not exist: ${manifest.defaultEndpointId}`);
2217
- if (defaultEndpoint.agentId !== defaultAgent.agentId) throw new Error("default endpoint is not bound to the default agent");
2218
- if (!defaultEndpoint.enabled) throw new Error("default endpoint must be enabled");
2219
- if (manifest.backgroundSessions) validateBackgroundSessions(manifest.backgroundSessions);
2220
- }
2221
- function validateBackgroundSessions(config) {
2222
- if (config.required && !config.enabled) throw new Error("backgroundSessions.required requires backgroundSessions.enabled");
2223
- if (config.leaseRenewalIntervalMs >= config.leaseMs) throw new Error("backgroundSessions.leaseRenewalIntervalMs must be shorter than leaseMs");
2224
- if (config.stepTimeoutMs <= 0 || config.maxConcurrentSessions <= 0 || config.leaseMs <= 0) throw new Error("backgroundSessions durations and concurrency must be positive");
2225
- if (config.retryBackoffMs <= 0 || config.sessionLifetimeMs <= 0 || config.maxConsecutiveFailures <= 0) throw new Error("backgroundSessions retry and lifetime limits must be positive");
2226
- }
2227
- function validateModuleSpecifier(moduleSpecifier) {
2228
- const absolutePath = moduleSpecifier.startsWith("/") || moduleSpecifier.startsWith("\\\\") || /^[a-zA-Z]:[\\/]/.test(moduleSpecifier);
2229
- if (moduleSpecifier.trim() === "" || absolutePath || /^[a-z][a-z+.-]*:/i.test(moduleSpecifier) || moduleSpecifier.includes("\0")) throw new Error(`invalid plugin module specifier: ${moduleSpecifier}`);
2230
- if ((moduleSpecifier.startsWith("./") || moduleSpecifier.startsWith("../")) && moduleSpecifier.split(/[\\/]/).includes("..")) throw new Error(`plugin module escapes deployment root: ${moduleSpecifier}`);
2231
- }
2232
- function parseBackgroundSessions(value) {
2233
- exactKeys(value, [
2234
- "enabled",
2235
- "leaseMs",
2236
- "leaseRenewalIntervalMs",
2237
- "maxConsecutiveFailures",
2238
- "maxConcurrentSessions",
2239
- "required",
2240
- "retryBackoffMs",
2241
- "sessionLifetimeMs",
2242
- "stepTimeoutMs"
2243
- ], "manifest.backgroundSessions", [
2244
- "leaseMs",
2245
- "leaseRenewalIntervalMs",
2246
- "maxConsecutiveFailures",
2247
- "maxConcurrentSessions",
2248
- "retryBackoffMs",
2249
- "sessionLifetimeMs",
2250
- "stepTimeoutMs"
2251
- ]);
2252
- return Object.freeze({
2253
- enabled: boolean(value.enabled, "manifest.backgroundSessions.enabled"),
2254
- leaseMs: positiveInteger(value.leaseMs ?? DEFAULT_MANIFEST_BACKGROUND_SESSION_LEASE_MS, "manifest.backgroundSessions.leaseMs"),
2255
- leaseRenewalIntervalMs: positiveInteger(value.leaseRenewalIntervalMs ?? DEFAULT_MANIFEST_BACKGROUND_SESSION_LEASE_RENEWAL_INTERVAL_MS, "manifest.backgroundSessions.leaseRenewalIntervalMs"),
2256
- maxConsecutiveFailures: positiveInteger(value.maxConsecutiveFailures ?? DEFAULT_MANIFEST_BACKGROUND_SESSION_MAX_CONSECUTIVE_FAILURES, "manifest.backgroundSessions.maxConsecutiveFailures"),
2257
- maxConcurrentSessions: positiveInteger(value.maxConcurrentSessions ?? DEFAULT_MANIFEST_BACKGROUND_SESSION_MAX_CONCURRENT_SESSIONS, "manifest.backgroundSessions.maxConcurrentSessions"),
2258
- required: boolean(value.required, "manifest.backgroundSessions.required"),
2259
- retryBackoffMs: positiveInteger(value.retryBackoffMs ?? DEFAULT_MANIFEST_BACKGROUND_SESSION_RETRY_BACKOFF_MS, "manifest.backgroundSessions.retryBackoffMs"),
2260
- sessionLifetimeMs: positiveInteger(value.sessionLifetimeMs ?? DEFAULT_MANIFEST_BACKGROUND_SESSION_LIFETIME_MS, "manifest.backgroundSessions.sessionLifetimeMs"),
2261
- stepTimeoutMs: positiveInteger(value.stepTimeoutMs ?? DEFAULT_MANIFEST_BACKGROUND_SESSION_STEP_TIMEOUT_MS, "manifest.backgroundSessions.stepTimeoutMs")
2262
- });
2263
- }
2264
- function record(value, path) {
2265
- if (value === null || typeof value !== "object" || Array.isArray(value)) throw new Error(`${path} must be an object`);
2266
- return value;
2267
- }
2268
- function array(value, path) {
2269
- if (!Array.isArray(value)) throw new Error(`${path} must be an array`);
2270
- return value;
2271
- }
2272
- function string(value, path) {
2273
- if (typeof value !== "string" || value.trim() === "") throw new Error(`${path} must be a non-empty string`);
2274
- return value;
2275
- }
2276
- function boolean(value, path) {
2277
- if (typeof value !== "boolean") throw new Error(`${path} must be a boolean`);
2278
- return value;
2279
- }
2280
- function positiveInteger(value, path) {
2281
- if (!Number.isSafeInteger(value) || value <= 0) throw new Error(`${path} must be a positive integer`);
2282
- return value;
2283
- }
2284
- function memoryScope(value, path) {
2285
- if (typeof value !== "string" || !MEMORY_SCOPES.includes(value)) throw new Error(`${path} must be conversation, agent-private, project, or shared-user-profile`);
2286
- return value;
2287
- }
2288
- function groupPolicy(value, path) {
2289
- if (value !== "mention-only" && value !== "ignore-unmentioned" && value !== "default-responder") throw new Error(`${path} must be mention-only, ignore-unmentioned, or default-responder`);
2290
- return value;
2291
- }
2292
- function progressDisplay(value, path) {
2293
- if (value !== "hidden" && value !== "collapsed" && value !== "expanded") throw new Error(`${path} must be hidden, collapsed, or expanded`);
2294
- return value;
2295
- }
2296
- function runtimeToolId(value, path) {
2297
- const id = string(value, path);
2298
- if (!isRivusRuntimeToolId(id)) throw new Error(`${path} must be read, bash, edit, write, grep, find, or ls`);
2299
- return id;
2300
- }
2301
- function uniqueRuntimeTools(ids, path) {
2302
- const result = /* @__PURE__ */ new Set();
2303
- for (const id of ids) {
2304
- if (result.has(id)) throw new Error(`${path} contains duplicate Runtime Tool: ${id}`);
2305
- result.add(id);
2306
- }
2307
- return [...result];
2308
- }
2309
- function automationTargetType(value, path) {
2310
- if (value !== "chat_id" && value !== "open_id" && value !== "user_id" && value !== "union_id" && value !== "email") throw new Error(`${path} must be chat_id, open_id, user_id, union_id, or email`);
2311
- return value;
2312
- }
2313
- function exactKeys(value, allowed, path, optional = []) {
2314
- const unexpected = Object.keys(value).find((key) => !allowed.includes(key));
2315
- if (unexpected) throw new Error(`${path} contains unsupported field: ${unexpected}`);
2316
- const missing = allowed.find((key) => !optional.includes(key) && !Object.hasOwn(value, key));
2317
- if (missing) throw new Error(`${path} is missing required field: ${missing}`);
2318
- }
2319
- //#endregion
2320
- //#region src/adapters/deployment/manifest/node-rivus-deployment-manifest.ts
2321
- var RivusDeploymentManifestError = class extends Error {
2322
- manifestPath;
2323
- name = "RivusDeploymentManifestError";
2324
- constructor(manifestPath, message, options) {
2325
- super(message, options);
2326
- this.manifestPath = manifestPath;
2327
- }
2328
- };
2329
- function loadRivusDeploymentManifest(manifestPath, options = {}) {
2330
- const maxBytes = options.maxBytes ?? 1024 * 1024;
2331
- return Effect.tryPromise({
2332
- try: async () => {
2333
- const metadata = await stat(manifestPath);
2334
- if (!metadata.isFile()) throw new Error("deployment manifest must be a regular file");
2335
- if (metadata.size > maxBytes) throw new Error(`deployment manifest exceeds ${maxBytes} byte limit`);
2336
- const bytes = await readFile(manifestPath);
2337
- const text = new TextDecoder("utf-8", { fatal: true }).decode(bytes);
2338
- return parseRivusDeploymentManifest(JSON.parse(text));
2339
- },
2340
- catch: (cause) => cause instanceof RivusDeploymentManifestError ? cause : new RivusDeploymentManifestError(manifestPath, `failed to load Rivus deployment manifest: ${cause instanceof Error ? cause.message : String(cause)}`, { cause })
2341
- });
2342
- }
2343
- //#endregion
2344
- //#region src/adapters/deployment/process/process-deployment-adapters.ts
2345
- function createProcessDeploymentAdapterPorts(factories, runEffect) {
2346
- return {
2347
- ...factories.createAutomation ? { automationFactory: { create: (input) => adaptAutomationFactory(factories.createAutomation, input, runEffect) } } : {},
2348
- ...factories.createBackgroundSession ? { backgroundSessionFactory: { create: (input) => adaptBackgroundSessionFactory(factories.createBackgroundSession, input, runEffect) } } : {},
2349
- endpointFactory: { create: (input) => adaptEndpointFactory(factories.createEndpoint, input, runEffect) },
2350
- runtimeFactory: { create: (input) => Effect.tryPromise({
2351
- try: () => Promise.resolve(factories.createRuntime(input)),
2352
- catch: toError$3
2353
- }).pipe(Effect.map((runtime) => toEffectAgentRuntime(runtime, runEffect))) }
2354
- };
2355
- }
2356
- function adaptEndpointFactory(create, input, runEffect) {
2357
- return Effect.tryPromise({
2358
- try: async () => {
2359
- return toLifecycleAdapter(await create({
2360
- agentId: input.agentId,
2361
- cancel: (request) => runEffect(input.cancel(request)),
2362
- definition: input.definition,
2363
- endpointId: input.endpointId,
2364
- handle: (request) => runEffect(input.handle(toEffectAgentRuntimeInput(request))),
2365
- instanceId: input.instanceId,
2366
- ...input.projectSpaceId ? { projectSpaceId: input.projectSpaceId } : {},
2367
- steer: (request) => runEffect(input.steer(request))
2368
- }));
2369
- },
2370
- catch: toError$3
2371
- });
2372
- }
2373
- function adaptAutomationFactory(create, input, runEffect) {
2374
- return Effect.tryPromise({
2375
- try: async () => toLifecycleAdapter(await create({
2376
- automationId: input.automationId,
2377
- definition: input.definition,
2378
- deliveryEndpoint: input.deliveryEndpoint,
2379
- instanceId: input.instanceId,
2380
- run: (request) => runEffect(input.run(request))
2381
- })),
2382
- catch: toError$3
2383
- });
2384
- }
2385
- function adaptBackgroundSessionFactory(create, input, runEffect) {
2386
- return Effect.tryPromise({
2387
- try: async () => {
2388
- const adapter = await create({
2389
- agentIds: input.agentIds,
2390
- cancel: (request) => runEffect(input.cancel(request)),
2391
- config: input.config,
2392
- run: (request) => runEffect(input.run({
2393
- agentId: request.agentId,
2394
- invocation: request.invocation,
2395
- ...request.onUpdate ? { onUpdate: (update) => Effect.tryPromise({
2396
- try: async () => request.onUpdate(update),
2397
- catch: toError$3
2398
- }) } : {},
2399
- sessionKey: request.sessionKey,
2400
- text: request.text
2401
- }))
2402
- });
2403
- return {
2404
- ...toLifecycleAdapter(adapter),
2405
- ...adapter.status ? { status: () => adapter.status() } : {}
2406
- };
2407
- },
2408
- catch: toError$3
2409
- });
2410
- }
2411
- function toLifecycleAdapter(adapter) {
2412
- return {
2413
- running: () => adapter.running(),
2414
- start: () => Effect.tryPromise({
2415
- try: () => Promise.resolve(adapter.start()),
2416
- catch: toError$3
2417
- }),
2418
- stop: () => Effect.tryPromise({
2419
- try: () => Promise.resolve(adapter.stop()),
2420
- catch: toError$3
2421
- })
2422
- };
2423
- }
2424
- function toError$3(error) {
2425
- return error instanceof Error ? error : new Error(String(error));
2426
- }
2427
- //#endregion
2428
- //#region src/platform/runtime/runtime-pool.ts
2429
- var RuntimeResourceBusy = class extends Error {
2430
- name = "RuntimeResourceBusy";
2431
- };
2432
- var RuntimeResourceDisposed = class extends Error {
2433
- name = "RuntimeResourceDisposed";
2434
- };
2435
- var RuntimePoolDisposalTimedOut = class extends Error {
2436
- name = "RuntimePoolDisposalTimedOut";
2437
- };
2438
- const DEFAULT_RUNTIME_DISPOSE_TIMEOUT_MS = 1e4;
2439
- function createEffectRuntimePool(options) {
2440
- const runtimes = createRuntimeCache();
2441
- const active = /* @__PURE__ */ new Map();
2442
- const lifecycle = Effect.unsafeMakeSemaphore(1);
2443
- const existingRuntime = (instance) => Effect.gen(function* () {
2444
- const selected = yield* runtimes.getExisting(instance.instanceId);
2445
- if (!selected || !(yield* runtimes.isCurrent(instance.instanceId, selected.entry))) return void 0;
2446
- return selected.runtime;
2447
- });
2448
- return {
2449
- cancel: (instance, input) => invokeRuntimeControl(existingRuntime(instance), (runtime) => runtime.cancel?.(input)),
2450
- disposeAll: () => Effect.gen(function* () {
2451
- const entries = yield* runtimes.drain();
2452
- yield* lifecycle.withPermits(1)(Effect.sync(() => active.clear()));
2453
- yield* disposeRuntimeCacheEntries({
2454
- dispose: (runtime) => runtime.dispose?.() ?? Effect.void,
2455
- entries,
2456
- failureMessage: "runtime pool disposal failed",
2457
- timeout: {
2458
- milliseconds: options.disposeTimeoutMs ?? DEFAULT_RUNTIME_DISPOSE_TIMEOUT_MS,
2459
- onTimeout: () => new RuntimePoolDisposalTimedOut(`runtime pool disposal timed out after ${options.disposeTimeoutMs ?? DEFAULT_RUNTIME_DISPOSE_TIMEOUT_MS}ms`)
2460
- }
2461
- });
2462
- }),
2463
- run: (instance, input) => Effect.gen(function* () {
2464
- const selected = yield* runtimes.getOrCreate(instance.instanceId, () => options.createRuntime(instance));
2465
- if (!(yield* runtimes.isCurrent(instance.instanceId, selected.entry))) return yield* Effect.fail(new RuntimeResourceDisposed(`runtime was disposed before run start: ${instance.instanceId}`));
2466
- if (selected.runtime.concurrency === "managed") return yield* selected.runtime.run(input);
2467
- const token = yield* lifecycle.withPermits(1)(Effect.gen(function* () {
2468
- if (!(yield* runtimes.isCurrent(instance.instanceId, selected.entry))) return yield* Effect.fail(new RuntimeResourceDisposed(`runtime was disposed before run start: ${instance.instanceId}`));
2469
- if (active.has(instance.instanceId)) return yield* Effect.fail(new RuntimeResourceBusy(`runtime already has an active run: ${instance.instanceId}`));
2470
- const activeToken = Symbol(instance.instanceId);
2471
- active.set(instance.instanceId, activeToken);
2472
- return activeToken;
2473
- }));
2474
- return yield* selected.runtime.run(input).pipe(Effect.ensuring(lifecycle.withPermits(1)(Effect.sync(() => {
2475
- if (active.get(instance.instanceId) === token) active.delete(instance.instanceId);
2476
- }))));
2477
- }),
2478
- steer: (instance, input) => invokeRuntimeControl(existingRuntime(instance), (runtime) => runtime.steer?.(input))
2479
- };
2480
- }
2481
- //#endregion
2482
- //#region src/adapters/agent/runtime/effect-agent-runtime-pool.ts
2483
- var AgentInstanceBusy = class extends Error {
2484
- name = "AgentInstanceBusy";
2485
- };
2486
- var AgentRuntimeDisposed = class extends Error {
2487
- name = "AgentRuntimeDisposed";
2488
- };
2489
- function createAgentHostRuntimePool(factory, disposeTimeoutMs) {
2490
- const pool = createEffectRuntimePool({
2491
- createRuntime: (instance) => factory.create(instance),
2492
- ...disposeTimeoutMs === void 0 ? {} : { disposeTimeoutMs }
2493
- });
2494
- return {
2495
- cancel: (instance, input) => pool.cancel(instance, input),
2496
- disposeAll: () => pool.disposeAll().pipe(Effect.mapError(mapRuntimePoolError)),
2497
- run: (instance, input) => pool.run(instance, input).pipe(Effect.mapError(mapRuntimePoolError)),
2498
- steer: (instance, input) => pool.steer(instance, input)
2499
- };
2500
- }
2501
- function mapRuntimePoolError(error) {
2502
- if (error instanceof RuntimeResourceBusy) return new AgentInstanceBusy(error.message.replace(/^runtime /, "agent instance "));
2503
- if (error instanceof RuntimeResourceDisposed) return new AgentRuntimeDisposed(error.message.replace(/^runtime /, "agent runtime "));
2504
- if (error instanceof RuntimePoolDisposalTimedOut) return new AgentRuntimeDisposed(error.message.replace(/^runtime pool /, "agent runtime "));
2505
- return error;
2506
- }
2507
- //#endregion
2508
- //#region src/platform/identity/stable-id.ts
2509
- function createStableId(prefix, value) {
2510
- return `${prefix}:${createHash("sha256").update(JSON.stringify(value)).digest("hex")}`;
2511
- }
2512
- //#endregion
2513
- //#region src/core/application/agent-host/registry/agent-instance-registry.ts
2514
- var AgentInstanceConflict = class extends Error {
2515
- name = "AgentInstanceConflict";
2516
- };
2517
- function createEffectAgentInstanceRegistry(identity, options = {}) {
2518
- const records = /* @__PURE__ */ new Map();
2519
- for (const record of options.initialRecords ?? []) {
2520
- if (bindingKey(record.binding, record.agentId) !== record.bindingKey) throw new AgentInstanceConflict(`invalid binding key: ${record.bindingKey}`);
2521
- if (records.has(record.bindingKey)) throw new AgentInstanceConflict(`duplicate binding: ${record.bindingKey}`);
2522
- records.set(record.bindingKey, freezeRecord(record));
2523
- }
2524
- const resolveBinding = (instanceBinding, definition) => Effect.try({
2525
- try: () => {
2526
- const runtimeGenerationId = identity.createRuntimeGenerationId({
2527
- agentId: definition.agentId,
2528
- profileRevision: definition.profileRevision,
2529
- projectSpaceId: definition.projectSpaceId ?? null,
2530
- projectSpaceRevision: definition.projectSpaceRevision ?? null,
2531
- runtimeToolGrantRevision: definition.runtimeToolGrantSet.revision,
2532
- skillGrantRevision: definition.skillGrantSet.revision,
2533
- toolGrantRevision: definition.toolGrantSet.revision
2534
- });
2535
- const key = bindingKey(instanceBinding, definition.agentId);
2536
- const existing = records.get(key);
2537
- if (existing) {
2538
- if (existing.runtimeGenerationId !== runtimeGenerationId) throw new AgentInstanceConflict(`binding ${key} belongs to a different runtime generation`);
2539
- return existing;
2540
- }
2541
- const record = freezeRecord({
2542
- agentId: definition.agentId,
2543
- binding: instanceBinding,
2544
- bindingKey: key,
2545
- instanceId: identity.createInstanceId({
2546
- bindingKey: key,
2547
- runtimeGenerationId
2548
- }),
2549
- runtimeGenerationId
2550
- });
2551
- records.set(key, record);
2552
- return record;
2553
- },
2554
- catch: (error) => error instanceof AgentInstanceConflict ? error : new AgentInstanceConflict(String(error))
2555
- });
2556
- return {
2557
- resolveAutomation: (automationId, definition) => resolveBinding({
2558
- automationId,
2559
- kind: "automation"
2560
- }, definition),
2561
- resolveBackgroundSession: (agentId, definition) => resolveBinding({
2562
- agentId,
2563
- kind: "background-session"
2564
- }, definition),
2565
- resolveEndpoint: (endpointId, definition) => resolveBinding({
2566
- endpointId,
2567
- kind: "endpoint"
2568
- }, definition),
2569
- snapshot: () => Effect.sync(() => Object.freeze([...records.values()]))
2570
- };
2571
- }
2572
- function bindingKey(binding, agentId) {
2573
- const bindingId = binding.kind === "endpoint" ? binding.endpointId : binding.kind === "automation" ? binding.automationId : binding.agentId;
2574
- return `${binding.kind}:${bindingId}:${agentId}`;
2575
- }
2576
- function freezeRecord(record) {
2577
- return Object.freeze({
2578
- ...record,
2579
- binding: Object.freeze({ ...record.binding })
2580
- });
2581
- }
2582
- //#endregion
2583
- //#region src/core/application/agent-host/routing/agent-host.ts
2584
- var InvalidAgentHostBinding = class extends Error {
2585
- name = "InvalidAgentHostBinding";
2586
- };
2587
- function createEffectAgentHost(options) {
2588
- return Effect.gen(function* () {
2589
- const definitions = new Map(options.definitions.map((definition) => [definition.agentId, definition]));
2590
- const endpoints = /* @__PURE__ */ new Map();
2591
- const automations = /* @__PURE__ */ new Map();
2592
- const backgroundSessions = /* @__PURE__ */ new Map();
2593
- for (const endpoint of options.endpoints) {
2594
- if (endpoints.has(endpoint.id)) return yield* Effect.fail(new InvalidAgentHostBinding(`duplicate endpoint: ${endpoint.id}`));
2595
- const definition = definitions.get(endpoint.agentId);
2596
- if (!definition) return yield* Effect.fail(new InvalidAgentHostBinding(`unknown endpoint agent: ${endpoint.agentId}`));
2597
- if (!definition.endpointIds.includes(endpoint.id)) return yield* Effect.fail(new InvalidAgentHostBinding(`endpoint ${endpoint.id} is not declared by ${endpoint.agentId}`));
2598
- endpoints.set(endpoint.id, yield* options.registry.resolveEndpoint(endpoint.id, definition));
2599
- }
2600
- for (const automation of options.automations ?? []) {
2601
- if (automations.has(automation.id)) return yield* Effect.fail(new InvalidAgentHostBinding(`duplicate automation: ${automation.id}`));
2602
- if (!definitions.has(automation.definition.agentId)) return yield* Effect.fail(new InvalidAgentHostBinding(`unknown automation agent: ${automation.definition.agentId}`));
2603
- automations.set(automation.id, yield* options.registry.resolveAutomation(automation.id, automation.definition));
2604
- }
2605
- for (const backgroundSession of options.backgroundSessions ?? []) {
2606
- if (backgroundSession.definition.agentId !== backgroundSession.agentId) return yield* Effect.fail(new InvalidAgentHostBinding(`background session ${backgroundSession.agentId} cannot bind definition ${backgroundSession.definition.agentId}`));
2607
- if (backgroundSessions.has(backgroundSession.agentId)) return yield* Effect.fail(new InvalidAgentHostBinding(`duplicate background session agent: ${backgroundSession.agentId}`));
2608
- if (!definitions.has(backgroundSession.agentId)) return yield* Effect.fail(new InvalidAgentHostBinding(`unknown background session agent: ${backgroundSession.agentId}`));
2609
- backgroundSessions.set(backgroundSession.agentId, yield* options.registry.resolveBackgroundSession(backgroundSession.agentId, backgroundSession.definition));
2610
- }
2611
- const resolve = (records, id, message) => {
2612
- const instance = records.get(id);
2613
- return instance ? Effect.succeed(instance) : Effect.fail(new InvalidAgentHostBinding(message));
2614
- };
2615
- const resolveEndpoint = (endpointId) => resolve(endpoints, endpointId, `unknown endpoint: ${endpointId}`);
2616
- const resolveAutomation = (automationId) => resolve(automations, automationId, `unknown automation: ${automationId}`);
2617
- const resolveBackgroundSession = (agentId) => resolve(backgroundSessions, agentId, `unknown background session agent: ${agentId}`);
2618
- return {
2619
- cancelBackgroundSession: (agentId, input) => resolveBackgroundSession(agentId).pipe(Effect.flatMap((instance) => options.runtime.cancel(instance, input))),
2620
- cancelEndpoint: (endpointId, input) => resolveEndpoint(endpointId).pipe(Effect.flatMap((instance) => options.runtime.cancel(instance, input))),
2621
- dispose: () => options.runtime.disposeAll(),
2622
- handleAutomation: (automationId, input) => resolveAutomation(automationId).pipe(Effect.flatMap((instance) => options.runtime.run(instance, input))),
2623
- handleBackgroundSession: (agentId, input) => resolveBackgroundSession(agentId).pipe(Effect.flatMap((instance) => options.runtime.run(instance, input))),
2624
- handleEndpoint: (endpointId, input) => resolveEndpoint(endpointId).pipe(Effect.flatMap((instance) => options.runtime.run(instance, input))),
2625
- resolveAutomation,
2626
- resolveBackgroundSession,
2627
- resolveEndpoint,
2628
- steerEndpoint: (endpointId, input) => resolveEndpoint(endpointId).pipe(Effect.flatMap((instance) => options.runtime.steer(instance, input)))
2629
- };
2630
- });
2631
- }
2632
- //#endregion
2633
- //#region src/bootstrap/deployment/deployment-control-ports.ts
2634
- const agentCatalogRuntime = Object.freeze({
2635
- digest: createSha256Digest,
2636
- deepFreeze
2637
- });
2638
- function createProcessDeploymentControlPorts(factories, backgroundSessions, runEffect) {
2639
- return {
2640
- agentCatalog: createRivusAgentCatalog([createRivusHostToolDescriptorProvider({ backgroundSessions })], agentCatalogRuntime),
2641
- agentHostFactory: { create: (input) => {
2642
- const registry = createEffectAgentInstanceRegistry({
2643
- createInstanceId: (identity) => createStableId("instance", identity),
2644
- createRuntimeGenerationId: (identity) => createStableId("generation", identity)
2645
- }, input.initialInstanceRecords ? { initialRecords: input.initialInstanceRecords } : {});
2646
- const runtime = createAgentHostRuntimePool(input.runtimeFactory);
2647
- return createEffectAgentHost({
2648
- automations: input.automations,
2649
- backgroundSessions: input.backgroundSessions,
2650
- definitions: input.definitions,
2651
- endpoints: input.endpoints,
2652
- registry,
2653
- runtime
2654
- });
2655
- } },
2656
- ...createProcessDeploymentAdapterPorts(factories, runEffect)
2657
- };
2658
- }
2659
- //#endregion
2660
- //#region src/adapters/deployment/plugin/rivus-plugin-module.ts
2661
- function resolveRivusPluginModule(module) {
2662
- const candidate = "default" in module ? module.default : module;
2663
- return (typeof candidate === "function" ? Effect.tryPromise({
2664
- try: () => Promise.resolve(candidate()),
2665
- catch: toError$2
2666
- }) : Effect.succeed(candidate)).pipe(Effect.flatMap((plugin) => isRivusPlugin(plugin) ? Effect.succeed(plugin) : Effect.fail(/* @__PURE__ */ new Error("plugin module default export is not a RivusPlugin or factory"))));
2667
- }
2668
- function isRivusPlugin(value) {
2669
- return value !== null && typeof value === "object" && "manifest" in value && "register" in value && typeof value.register === "function";
2670
- }
2671
- function toError$2(error) {
2672
- return error instanceof Error ? error : new Error(String(error));
2673
- }
2674
- //#endregion
2675
- //#region src/adapters/deployment/plugin/trusted-package-root.ts
2676
- function findTrustedPackageRoot(packageManifestPath) {
2677
- return Effect.tryPromise({
2678
- try: async () => {
2679
- const packageRoot = dirname(await realpath(packageManifestPath));
2680
- let current = packageRoot;
2681
- for (;;) {
2682
- if (basename(current) === "node_modules") return current;
2683
- const parent = dirname(current);
2684
- if (parent === current) return packageRoot;
2685
- current = parent;
2686
- }
2687
- },
2688
- catch: (error) => error instanceof Error ? error : new Error(String(error))
2689
- });
2690
- }
2691
- //#endregion
2692
- //#region src/platform/filesystem/path-boundary.ts
2693
- function isPathWithin(root, candidate) {
2694
- const child = relative(root, candidate);
2695
- return child === "" || !child.startsWith(`..${sep}`) && child !== ".." && !isAbsolute(child);
2696
- }
2697
- function assertPathWithin(root, candidate, message) {
2698
- if (!isPathWithin(root, candidate)) throw new Error(message);
2699
- }
2700
- //#endregion
2701
- //#region src/adapters/deployment/plugin/trusted-module-path.ts
2702
- function validateTrustedModulePath(trustedRoot, resolvedPath, subject) {
2703
- return Effect.try({
2704
- try: () => {
2705
- assertPathWithin(trustedRoot, resolvedPath, `${subject} resolves outside trusted module root: ${resolvedPath}`);
2706
- return resolvedPath;
2707
- },
2708
- catch: (failure) => failure instanceof Error ? failure : new Error(String(failure))
2709
- });
2710
- }
2711
- //#endregion
2712
- //#region src/adapters/deployment/plugin/node-rivus-plugin-module-loader.ts
2713
- function createNodeRivusPluginModuleLoader(options = {}) {
2714
- return { load: (request) => loadNodeRivusPluginModule(request, options).pipe(Effect.flatMap(resolveRivusPluginModule)) };
2715
- }
2716
- function loadNodeRivusPluginModule(request, options = {}) {
2717
- return resolveNodeRivusPluginModulePath(request, options).pipe(Effect.flatMap((resolvedRealpath) => Effect.tryPromise({
2718
- try: () => import(pathToFileURL(resolvedRealpath).href),
2719
- catch: toError$1
2720
- })));
2721
- }
2722
- function resolveNodeRivusPluginModulePath(request, options = {}) {
2723
- return Effect.gen(function* () {
2724
- const deploymentRoot = yield* Effect.tryPromise({
2725
- try: () => realpath(request.deploymentRoot),
2726
- catch: toError$1
2727
- });
2728
- const relativeModule = request.module.startsWith("./") || request.module.startsWith("../");
2729
- const resolutionManifest = relativeModule ? join(deploymentRoot, "package.json") : options.packageManifestPath ?? join(deploymentRoot, "package.json");
2730
- const resolved = yield* Effect.try({
2731
- try: () => createRequire(resolutionManifest).resolve(request.module),
2732
- catch: toError$1
2733
- });
2734
- const resolvedRealpath = yield* Effect.tryPromise({
2735
- try: () => realpath(resolved),
2736
- catch: toError$1
2737
- });
2738
- return yield* validateTrustedModulePath(relativeModule ? deploymentRoot : options.packageManifestPath ? yield* findTrustedPackageRoot(options.packageManifestPath) : deploymentRoot, resolvedRealpath, `plugin module ${request.module}`);
2739
- });
2740
- }
2741
- function toError$1(error) {
2742
- return error instanceof Error ? error : new Error(String(error));
2743
- }
2744
- //#endregion
2745
- //#region src/core/application/deployment/failure/deployment-failure.ts
2746
- function formatDeploymentFailure(failure) {
2747
- return failure instanceof Error ? failure.message : String(failure);
2748
- }
2749
- function toDeploymentFailure(failure) {
2750
- return failure instanceof Error ? failure : new Error(String(failure));
2751
- }
2752
- //#endregion
2753
- //#region src/core/application/deployment/resolution/deployment-resolution.ts
2754
- var RivusPluginLoadError = class extends Error {
2755
- pluginId;
2756
- moduleSpecifier;
2757
- name = "RivusPluginLoadError";
2758
- constructor(pluginId, moduleSpecifier, message, options) {
2759
- super(message, options);
2760
- this.pluginId = pluginId;
2761
- this.moduleSpecifier = moduleSpecifier;
2762
- }
2763
- };
2764
- function resolveRivusDeployment(input) {
2765
- return Effect.gen(function* () {
2766
- yield* Effect.try({
2767
- try: () => validateRivusDeploymentManifest(input.manifest),
2768
- catch: toDeploymentFailure
2769
- });
2770
- const catalog = input.agentCatalog.createPluginCatalog();
2771
- const pluginStatuses = [];
2772
- const statusByPlugin = /* @__PURE__ */ new Map();
2773
- for (const declaration of input.manifest.plugins) {
2774
- const loaded = yield* input.pluginLoader.load({
2775
- deploymentRoot: input.deploymentRoot,
2776
- module: declaration.module,
2777
- pluginId: declaration.id
2778
- }).pipe(Effect.flatMap((plugin) => Effect.try({
2779
- try: () => {
2780
- if (plugin.manifest.id !== declaration.id) throw new Error(`plugin manifest id ${plugin.manifest.id} does not match declaration ${declaration.id}`);
2781
- catalog.registerPlugin(plugin);
2782
- return plugin;
2783
- },
2784
- catch: toDeploymentFailure
2785
- })), Effect.either);
2786
- if (Either.isLeft(loaded)) {
2787
- const message = formatDeploymentFailure(loaded.left);
2788
- if (declaration.required) return yield* Effect.fail(new RivusPluginLoadError(declaration.id, declaration.module, `required plugin ${declaration.id} failed to load: ${message}`, { cause: loaded.left }));
2789
- const status = Object.freeze({
2790
- error: message,
2791
- id: declaration.id,
2792
- module: declaration.module,
2793
- required: false,
2794
- status: "failed"
2795
- });
2796
- pluginStatuses.push(status);
2797
- statusByPlugin.set(declaration.id, status);
2798
- continue;
2799
- }
2800
- const status = Object.freeze({
2801
- id: declaration.id,
2802
- module: declaration.module,
2803
- required: declaration.required,
2804
- status: "loaded",
2805
- version: loaded.right.manifest.version
2806
- });
2807
- pluginStatuses.push(status);
2808
- statusByPlugin.set(declaration.id, status);
2809
- }
2810
- const agentStatuses = [];
2811
- const definitions = [];
2812
- for (const agent of input.manifest.agents) {
2813
- const pluginStatus = statusByPlugin.get(agent.pluginId);
2814
- if (pluginStatus.status === "failed") {
2815
- agentStatuses.push(Object.freeze({
2816
- agentId: agent.agentId,
2817
- pluginId: agent.pluginId,
2818
- profileId: agent.profileId,
2819
- reason: `plugin ${agent.pluginId} is unavailable: ${pluginStatus.error}`,
2820
- status: "disabled"
2821
- }));
2822
- continue;
2823
- }
2824
- const resolved = yield* Effect.try({
2825
- try: () => {
2826
- const baseDefinition = input.agentCatalog.resolve(catalog, agent);
2827
- const projectSpace = agent.projectSpaceId ? input.manifest.projectSpaces?.find(({ id }) => id === agent.projectSpaceId) : void 0;
2828
- return projectSpace ? input.deepFreeze({
2829
- ...baseDefinition,
2830
- projectSpaceRevision: input.createStableId("project-space-declaration", {
2831
- id: projectSpace.id,
2832
- root: projectSpace.root,
2833
- skills: { sources: projectSpace.skills.sources },
2834
- workingDirectory: projectSpace.workingDirectory
2835
- })
2836
- }) : baseDefinition;
2837
- },
2838
- catch: toDeploymentFailure
2839
- }).pipe(Effect.either);
2840
- if (Either.isLeft(resolved)) {
2841
- const declaration = input.manifest.plugins.find(({ id }) => id === agent.pluginId);
2842
- if (declaration.required) return yield* Effect.fail(new RivusPluginLoadError(declaration.id, declaration.module, `deployment ${agent.agentId} failed to resolve: ${resolved.left.message}`, { cause: resolved.left }));
2843
- agentStatuses.push(Object.freeze({
2844
- agentId: agent.agentId,
2845
- pluginId: agent.pluginId,
2846
- profileId: agent.profileId,
2847
- reason: resolved.left.message,
2848
- status: "disabled"
2849
- }));
2850
- continue;
2851
- }
2852
- definitions.push(resolved.right);
2853
- agentStatuses.push(Object.freeze({
2854
- agentId: agent.agentId,
2855
- definition: resolved.right,
2856
- pluginId: agent.pluginId,
2857
- profileId: agent.profileId,
2858
- status: "enabled"
2859
- }));
2860
- }
2861
- const agentStatusById = new Map(agentStatuses.map((agent) => [agent.agentId, agent]));
2862
- const automationTemplates = new Map(catalog.snapshot().automations.map((template) => [template.id, template]));
2863
- const automationDefinitions = [];
2864
- for (const automation of input.manifest.automations ?? []) {
2865
- const agent = agentStatusById.get(automation.agentId);
2866
- if (!agent || agent.status === "disabled" || !agent.definition) continue;
2867
- const deliveryEndpoint = input.manifest.endpoints.find(({ id }) => id === automation.delivery.endpointId);
2868
- const presentationAgent = agentStatusById.get(deliveryEndpoint.agentId);
2869
- if (!presentationAgent || presentationAgent.status === "disabled" || !presentationAgent.definition) continue;
2870
- const template = automationTemplates.get(automation.templateId);
2871
- if (!template) return yield* Effect.fail(/* @__PURE__ */ new Error(`automation ${automation.id} references unknown template: ${automation.templateId}`));
2872
- if (template.pluginId !== agent.pluginId || template.profileId !== agent.profileId) return yield* Effect.fail(/* @__PURE__ */ new Error(`automation ${automation.id} template is not owned by agent profile ${agent.profileId}`));
2873
- const definition = yield* Effect.try({
2874
- try: () => input.deepFreeze({
2875
- ...automation,
2876
- runtimeDefinition: input.agentCatalog.restrictGrants(agent.definition, {
2877
- memory: {
2878
- scopes: [],
2879
- tool: false
2880
- },
2881
- runtimeToolIds: [],
2882
- skillIds: template.requestedSkillIds,
2883
- toolIds: template.requestedToolIds
2884
- }),
2885
- template
2886
- }),
2887
- catch: toDeploymentFailure
2888
- });
2889
- automationDefinitions.push(definition);
2890
- }
2891
- return Object.freeze({
2892
- agents: Object.freeze(agentStatuses),
2893
- automationDefinitions: Object.freeze(automationDefinitions),
2894
- catalog,
2895
- definitions: Object.freeze(definitions),
2896
- manifest: input.manifest,
2897
- plugins: Object.freeze(pluginStatuses)
2898
- });
2899
- });
2900
- }
2901
- //#endregion
2902
- //#region src/core/application/deployment/lifecycle/deployment-lifecycle.ts
2903
- var InvalidDeploymentLifecycleTransition = class extends Error {
2904
- name = "InvalidDeploymentLifecycleTransition";
2905
- };
2906
- var RivusDeploymentDaemonLifecycleError = class extends Error {
2907
- name = "RivusDeploymentDaemonLifecycleError";
2908
- };
2909
- const componentTransitions = Object.freeze({
2910
- "cleanup-required": ["cleanup-required", "stopping"],
2911
- degraded: ["degraded", "stopping"],
2912
- disabled: ["disabled"],
2913
- running: [
2914
- "degraded",
2915
- "running",
2916
- "stopping"
2917
- ],
2918
- starting: ["degraded", "running"],
2919
- stopped: ["starting", "stopped"],
2920
- stopping: [
2921
- "cleanup-required",
2922
- "disabled",
2923
- "stopped"
2924
- ]
2925
- });
2926
- const controlTransitions = Object.freeze({
2927
- "cleanup-required": ["cleanup-required", "stopping"],
2928
- degraded: ["degraded", "stopping"],
2929
- running: [
2930
- "degraded",
2931
- "running",
2932
- "stopping"
2933
- ],
2934
- starting: ["degraded", "running"],
2935
- stopped: [
2936
- "starting",
2937
- "stopped",
2938
- "stopping"
2939
- ],
2940
- stopping: ["cleanup-required", "stopped"]
2941
- });
2942
- function transitionDeploymentComponentLifecycle(current, next) {
2943
- if (!componentTransitions[current].includes(next)) throw new InvalidDeploymentLifecycleTransition(`cannot transition deployment component from ${current} to ${next}`);
2944
- return next;
2945
- }
2946
- function transitionDeploymentControlLifecycle(current, next) {
2947
- if (!controlTransitions[current].includes(next)) throw new InvalidDeploymentLifecycleTransition(`cannot transition Deployment Control from ${current} to ${next}`);
2948
- return next;
2949
- }
2950
- function isRequiredDeploymentComponentReady(input) {
2951
- return !input.enabled || !input.required || input.lifecycle === "running" && input.running;
2952
- }
2953
- //#endregion
2954
- //#region src/core/application/deployment/lifecycle/deployment-preparation.ts
2955
- function prepareDeployment(input) {
2956
- return Effect.gen(function* () {
2957
- const deployment = input.deployment;
2958
- if ((deployment.manifest.projectSpaces?.length ?? 0) > 0 && !input.projectSpaceResolver) return yield* Effect.fail(new RivusDeploymentDaemonLifecycleError("deployment bootstrap does not provide a Project Space resolver"));
2959
- const projectSpaces = /* @__PURE__ */ new Map();
2960
- for (const declaration of deployment.manifest.projectSpaces ?? []) {
2961
- const resolved = yield* input.projectSpaceResolver.resolve({
2962
- declaration,
2963
- deploymentRoot: input.deploymentRoot
2964
- }).pipe(Effect.mapError(toDeploymentFailure));
2965
- projectSpaces.set(resolved.id, resolved);
2966
- }
2967
- const definitions = yield* Effect.try({
2968
- try: () => new Map(deployment.definitions.map((definition) => {
2969
- const resolved = bindResolvedProjectSpaceRevision(definition, projectSpaces);
2970
- return [resolved.agentId, resolved];
2971
- })),
2972
- catch: toDeploymentFailure
2973
- });
2974
- const automationDefinitions = yield* Effect.try({
2975
- try: () => new Map(deployment.automationDefinitions.map((definition) => [definition.id, Object.freeze({
2976
- ...definition,
2977
- runtimeDefinition: bindResolvedProjectSpaceRevision(definition.runtimeDefinition, projectSpaces)
2978
- })])),
2979
- catch: toDeploymentFailure
2980
- });
2981
- const effectiveDeployment = Object.freeze({
2982
- ...deployment,
2983
- agents: Object.freeze(deployment.agents.map((agent) => {
2984
- if (!agent.definition) return agent;
2985
- const definition = definitions.get(agent.agentId);
2986
- return definition ? Object.freeze({
2987
- ...agent,
2988
- definition
2989
- }) : agent;
2990
- })),
2991
- automationDefinitions: Object.freeze([...automationDefinitions.values()]),
2992
- definitions: Object.freeze([...definitions.values()])
2993
- });
2994
- const backgroundConfig = deployment.manifest.backgroundSessions;
2995
- const backgroundDefinitions = /* @__PURE__ */ new Map();
2996
- if (backgroundConfig?.enabled) for (const agent of effectiveDeployment.agents) {
2997
- if (agent.status !== "enabled" || !agent.definition) continue;
2998
- const narrowed = yield* Effect.try({
2999
- try: () => narrowBackgroundSessionDefinition(definitions.get(agent.agentId), input.digest),
3000
- catch: toDeploymentFailure
3001
- });
3002
- backgroundDefinitions.set(agent.agentId, narrowed);
3003
- }
3004
- const agentStatuses = new Map(effectiveDeployment.agents.map((agent) => [agent.agentId, agent]));
3005
- const endpointSlots = deployment.manifest.endpoints.map((definition) => {
3006
- return createSlot(definition, agentStatuses.get(definition.agentId)?.status === "enabled");
3007
- });
3008
- const automationSlots = (deployment.manifest.automations ?? []).map((definition) => {
3009
- const agentEnabled = agentStatuses.get(definition.agentId)?.status === "enabled";
3010
- const resolvedDefinition = automationDefinitions.get(definition.id);
3011
- return {
3012
- ...createSlot(definition, agentEnabled && resolvedDefinition !== void 0),
3013
- agentEnabled,
3014
- ...resolvedDefinition ? { resolvedDefinition } : {}
3015
- };
3016
- });
3017
- const backgroundSessionSlot = backgroundConfig ? createSlot(backgroundConfig, backgroundDefinitions.size > 0) : void 0;
3018
- const host = yield* input.agentHostFactory.create({
3019
- automations: automationSlots.filter((slot) => slot.definition.enabled && slot.agentEnabled && slot.resolvedDefinition !== void 0).map((slot) => ({
3020
- definition: slot.resolvedDefinition.runtimeDefinition,
3021
- id: slot.definition.id
3022
- })),
3023
- backgroundSessions: [...backgroundDefinitions].map(([agentId, definition]) => ({
3024
- agentId,
3025
- definition
3026
- })),
3027
- definitions: [...definitions.values()],
3028
- endpoints: endpointSlots.filter((slot) => slot.definition.enabled && slot.agentEnabled).map((slot) => ({
3029
- agentId: slot.definition.agentId,
3030
- id: slot.definition.id
3031
- })),
3032
- ...input.initialInstanceRecords ? { initialInstanceRecords: input.initialInstanceRecords } : {},
3033
- runtimeFactory: { create: (instance) => {
3034
- const definition = instance.binding.kind === "automation" ? automationDefinitions.get(instance.binding.automationId)?.runtimeDefinition : instance.binding.kind === "background-session" ? backgroundDefinitions.get(instance.agentId) : definitions.get(instance.agentId);
3035
- if (!definition) return Effect.fail(new RivusDeploymentDaemonLifecycleError(`runtime instance references unknown agent: ${instance.agentId}`));
3036
- const projectSpace = definition.projectSpaceId ? projectSpaces.get(definition.projectSpaceId) : void 0;
3037
- return input.runtimeFactory.create({
3038
- ...instance,
3039
- catalog: effectiveDeployment.catalog,
3040
- definition,
3041
- ...projectSpace ? { projectSpace } : {}
3042
- });
3043
- } }
3044
- }).pipe(Effect.mapError(toDeploymentFailure));
3045
- return {
3046
- automationSlots,
3047
- ...backgroundSessionSlot ? { backgroundSessionSlot } : {},
3048
- definitions,
3049
- deployment: effectiveDeployment,
3050
- endpointSlots,
3051
- host
3052
- };
3053
- });
3054
- }
3055
- function createSlot(definition, agentEnabled) {
3056
- return {
3057
- agentEnabled,
3058
- definition,
3059
- ...!definition.enabled ? { lifecycle: "disabled" } : agentEnabled ? { lifecycle: "stopped" } : {
3060
- error: "component is unavailable",
3061
- lifecycle: "degraded"
3062
- }
3063
- };
3064
- }
3065
- function bindResolvedProjectSpaceRevision(definition, projectSpaces) {
3066
- if (!definition.projectSpaceId) return definition;
3067
- const projectSpace = projectSpaces.get(definition.projectSpaceId);
3068
- if (!projectSpace) throw new RivusDeploymentDaemonLifecycleError(`agent ${definition.agentId} references unresolved Project Space: ${definition.projectSpaceId}`);
3069
- return Object.freeze({
3070
- ...definition,
3071
- projectSpaceRevision: projectSpace.revision
3072
- });
3073
- }
3074
- //#endregion
3075
- //#region src/core/application/deployment/lifecycle/deployment-control.ts
3076
- var RivusDeploymentReadinessError = class extends Error {
3077
- endpointIds;
3078
- name = "RivusDeploymentReadinessError";
3079
- constructor(endpointIds) {
3080
- super(`required endpoint startup failed: ${endpointIds.join(", ")}`);
3081
- this.endpointIds = endpointIds;
3082
- }
3083
- };
3084
- var RivusDeploymentAutomationReadinessError = class extends Error {
3085
- automationIds;
3086
- name = "RivusDeploymentAutomationReadinessError";
3087
- constructor(automationIds) {
3088
- super(`required automation startup failed: ${automationIds.join(", ")}`);
3089
- this.automationIds = automationIds;
3090
- }
3091
- };
3092
- var RivusDeploymentBackgroundSessionReadinessError = class extends Error {
3093
- name = "RivusDeploymentBackgroundSessionReadinessError";
3094
- constructor() {
3095
- super("required Background Session startup failed");
3096
- }
3097
- };
3098
- function createRivusDeploymentControl(input) {
3099
- return Effect.gen(function* () {
3100
- return makeDeploymentControl({
3101
- ...yield* prepareDeployment(input),
3102
- ...input.automationFactory ? { automationFactory: input.automationFactory } : {},
3103
- ...input.backgroundSessionFactory ? { backgroundSessionFactory: input.backgroundSessionFactory } : {},
3104
- endpointFactory: input.endpointFactory
3105
- });
3106
- });
3107
- }
3108
- function makeDeploymentControl(input) {
3109
- const endpointById = new Map(input.endpointSlots.map((slot) => [slot.definition.id, slot]));
3110
- const allSlots = [
3111
- ...input.endpointSlots,
3112
- ...input.automationSlots,
3113
- ...input.backgroundSessionSlot ? [input.backgroundSessionSlot] : []
3114
- ];
3115
- let lifecycle = "stopped";
3116
- const refreshObservedState = () => {
3117
- let degraded = false;
3118
- for (const slot of allSlots) {
3119
- if (slot.lifecycle !== "running") continue;
3120
- const observed = observeRunning(slot);
3121
- if (!observed.running) {
3122
- slot.lifecycle = transitionDeploymentComponentLifecycle(slot.lifecycle, "degraded");
3123
- slot.error = observed.error ?? "component stopped running after startup";
3124
- degraded = true;
3125
- }
3126
- }
3127
- if (degraded && lifecycle === "running") lifecycle = transitionDeploymentControlLifecycle(lifecycle, "degraded");
3128
- };
3129
- const canRunIntake = () => {
3130
- refreshObservedState();
3131
- return lifecycle === "running" || lifecycle === "degraded";
3132
- };
3133
- const handleEndpoint = (endpointId, request) => Effect.suspend(() => {
3134
- const slot = endpointById.get(endpointId);
3135
- if (!slot) return Effect.fail(new RivusDeploymentDaemonLifecycleError(`unknown endpoint: ${endpointId}`));
3136
- const observed = observeRunning(slot);
3137
- if (!canRunIntake() || slot.lifecycle !== "running" || !observed.running) return Effect.fail(new RivusDeploymentDaemonLifecycleError(`endpoint ${endpointId} cannot accept intake while Deployment Control is ${lifecycle} and endpoint is ${slot.lifecycle}`));
3138
- return input.host.handleEndpoint(endpointId, request);
3139
- });
3140
- const isReady = () => allSlots.every((slot) => isSlotReady(slot));
3141
- const readinessFailure = () => {
3142
- const endpointIds = input.endpointSlots.filter((slot) => slot.definition.enabled && slot.definition.required && !isSlotReady(slot)).map((slot) => slot.definition.id);
3143
- if (endpointIds.length > 0) return new RivusDeploymentReadinessError(Object.freeze(endpointIds));
3144
- const automationIds = input.automationSlots.filter((slot) => slot.definition.enabled && slot.definition.required && !isSlotReady(slot)).map((slot) => slot.definition.id);
3145
- if (automationIds.length > 0) return new RivusDeploymentAutomationReadinessError(Object.freeze(automationIds));
3146
- if (input.backgroundSessionSlot?.definition.enabled && input.backgroundSessionSlot.definition.required && !isSlotReady(input.backgroundSessionSlot)) return new RivusDeploymentBackgroundSessionReadinessError();
3147
- };
3148
- const status = () => {
3149
- refreshObservedState();
3150
- return Object.freeze({
3151
- agents: input.deployment.agents,
3152
- automations: Object.freeze(input.automationSlots.map((slot) => ({
3153
- ...componentStatus(slot),
3154
- agentId: slot.definition.agentId,
3155
- automationId: slot.definition.id
3156
- }))),
3157
- ...input.backgroundSessionSlot ? { backgroundSessions: backgroundStatus(input.backgroundSessionSlot) } : {},
3158
- defaultAgentId: input.deployment.manifest.defaultAgentId,
3159
- defaultEndpointId: input.deployment.manifest.defaultEndpointId,
3160
- endpoints: Object.freeze(input.endpointSlots.map((slot) => ({
3161
- ...componentStatus(slot),
3162
- agentId: slot.definition.agentId,
3163
- endpointId: slot.definition.id
3164
- }))),
3165
- lifecycle,
3166
- plugins: input.deployment.plugins,
3167
- ready: isReady(),
3168
- running: lifecycle === "running" || lifecycle === "degraded"
3169
- });
3170
- };
3171
- return {
3172
- deployment: input.deployment,
3173
- handleDefault: (request) => handleEndpoint(input.deployment.manifest.defaultEndpointId, request),
3174
- handleEndpoint,
3175
- runDefaultAgent: (request) => input.host.handleEndpoint(input.deployment.manifest.defaultEndpointId, request),
3176
- running: canRunIntake,
3177
- start: () => Effect.suspend(() => {
3178
- refreshObservedState();
3179
- if (lifecycle === "running") return Effect.void;
3180
- if (lifecycle === "degraded") {
3181
- const failure = readinessFailure();
3182
- return failure ? Effect.fail(failure) : Effect.void;
3183
- }
3184
- if (lifecycle !== "stopped") return Effect.fail(new RivusDeploymentDaemonLifecycleError(`cannot start deployment daemon while ${lifecycle}`));
3185
- lifecycle = transitionDeploymentControlLifecycle(lifecycle, "starting");
3186
- return Effect.gen(function* () {
3187
- let degraded = input.deployment.plugins.some(({ status: pluginStatus }) => pluginStatus === "failed");
3188
- for (const slot of input.endpointSlots) degraded = (yield* startSlot(slot, slot.agentEnabled, `endpoint ${slot.definition.id}`, () => Effect.gen(function* () {
3189
- const instance = yield* input.host.resolveEndpoint(slot.definition.id);
3190
- return yield* input.endpointFactory.create({
3191
- agentId: slot.definition.agentId,
3192
- cancel: (request) => input.host.cancelEndpoint(slot.definition.id, request),
3193
- definition: slot.definition,
3194
- endpointId: slot.definition.id,
3195
- handle: (request) => handleEndpoint(slot.definition.id, request),
3196
- instanceId: instance.instanceId,
3197
- ...input.definitions.get(slot.definition.agentId)?.projectSpaceId ? { projectSpaceId: input.definitions.get(slot.definition.agentId).projectSpaceId } : {},
3198
- steer: (request) => input.host.steerEndpoint(slot.definition.id, request)
3199
- });
3200
- }))) || degraded;
3201
- for (const slot of input.automationSlots) {
3202
- const resolvedDefinition = slot.resolvedDefinition;
3203
- const deliverySlot = endpointById.get(slot.definition.delivery.endpointId);
3204
- const deliveryRunning = observeRunning(deliverySlot).running && deliverySlot.lifecycle === "running";
3205
- degraded = (yield* startSlot(slot, slot.agentEnabled && resolvedDefinition !== void 0 && deliveryRunning, `automation ${slot.definition.id}`, () => Effect.gen(function* () {
3206
- if (!input.automationFactory) return yield* Effect.fail(new RivusDeploymentDaemonLifecycleError("deployment bootstrap does not provide Automation adapters"));
3207
- if (!resolvedDefinition) return yield* Effect.fail(new RivusDeploymentDaemonLifecycleError(`automation definition not resolved: ${slot.definition.id}`));
3208
- const instance = yield* input.host.resolveAutomation(slot.definition.id);
3209
- return yield* input.automationFactory.create({
3210
- automationId: slot.definition.id,
3211
- definition: resolvedDefinition,
3212
- deliveryEndpoint: deliverySlot.definition,
3213
- instanceId: instance.instanceId,
3214
- run: (request) => input.host.handleAutomation(slot.definition.id, {
3215
- invocation: {
3216
- allowedActorOpenIds: [],
3217
- automationId: slot.definition.id,
3218
- endpointId: slot.definition.delivery.endpointId,
3219
- kind: "automation",
3220
- sourceMessageId: request.tickId,
3221
- tenantKey: "automation",
3222
- tickId: request.tickId
3223
- },
3224
- sessionKey: request.sessionKey,
3225
- text: request.text
3226
- })
3227
- });
3228
- }))) || degraded;
3229
- }
3230
- if (input.backgroundSessionSlot) {
3231
- const slot = input.backgroundSessionSlot;
3232
- degraded = (yield* startSlot(slot, slot.agentEnabled, "Background Session", () => Effect.gen(function* () {
3233
- if (!input.backgroundSessionFactory) return yield* Effect.fail(new RivusDeploymentDaemonLifecycleError("deployment bootstrap does not provide Background Session adapters"));
3234
- return yield* input.backgroundSessionFactory.create({
3235
- agentIds: [...input.definitions.keys()].filter((agentId) => input.deployment.agents.some((agent) => agent.agentId === agentId && agent.status === "enabled")),
3236
- cancel: (request) => input.host.cancelBackgroundSession(request.agentId, request),
3237
- config: slot.definition,
3238
- run: (request) => input.host.handleBackgroundSession(request.agentId, {
3239
- ...request.invocation ? { invocation: request.invocation } : {},
3240
- ...request.onUpdate ? { onUpdate: request.onUpdate } : {},
3241
- sessionKey: request.sessionKey,
3242
- text: request.text
3243
- })
3244
- });
3245
- }))) || degraded;
3246
- }
3247
- lifecycle = transitionDeploymentControlLifecycle(lifecycle, degraded ? "degraded" : "running");
3248
- const failure = readinessFailure();
3249
- if (failure) return yield* Effect.fail(failure);
3250
- });
3251
- }),
3252
- status,
3253
- stop: () => Effect.suspend(() => {
3254
- if (lifecycle !== "stopped" && lifecycle !== "running" && lifecycle !== "degraded" && lifecycle !== "cleanup-required") return Effect.fail(new RivusDeploymentDaemonLifecycleError(`cannot stop deployment daemon while ${lifecycle}`));
3255
- lifecycle = transitionDeploymentControlLifecycle(lifecycle, "stopping");
3256
- return Effect.gen(function* () {
3257
- const errors = [];
3258
- yield* stopSlots(input.automationSlots, errors);
3259
- if (input.backgroundSessionSlot) yield* stopSlots([input.backgroundSessionSlot], errors);
3260
- yield* stopSlots(input.endpointSlots, errors);
3261
- const hostExit = yield* Effect.exit(input.host.dispose());
3262
- if (Exit.isFailure(hostExit)) errors.push(Cause.squash(hostExit.cause));
3263
- lifecycle = transitionDeploymentControlLifecycle(lifecycle, errors.length === 0 ? "stopped" : "cleanup-required");
3264
- if (errors.length === 1) return yield* Effect.fail(errors[0]);
3265
- if (errors.length > 1) return yield* Effect.fail(new AggregateError(errors, "deployment daemon cleanup failed"));
3266
- });
3267
- })
3268
- };
3269
- }
3270
- function startSlot(slot, available, label, create) {
3271
- return Effect.gen(function* () {
3272
- if (!slot.definition.enabled) {
3273
- slot.lifecycle = "disabled";
3274
- return false;
3275
- }
3276
- if (!available) {
3277
- slot.lifecycle = "degraded";
3278
- slot.error = `${label} is unavailable`;
3279
- return true;
3280
- }
3281
- if (slot.lifecycle !== "stopped") return yield* Effect.fail(new RivusDeploymentDaemonLifecycleError(`cannot start ${label} while ${slot.lifecycle}`));
3282
- slot.lifecycle = transitionDeploymentComponentLifecycle(slot.lifecycle, "starting");
3283
- delete slot.error;
3284
- const started = yield* Effect.gen(function* () {
3285
- slot.adapter ??= yield* create();
3286
- yield* slot.adapter.start();
3287
- if (!(yield* Effect.try({
3288
- try: () => slot.adapter.running(),
3289
- catch: toDeploymentFailure
3290
- }))) return yield* Effect.fail(/* @__PURE__ */ new Error(`${label} start completed but the adapter is not running`));
3291
- }).pipe(Effect.exit);
3292
- if (Exit.isFailure(started)) {
3293
- slot.error = formatDeploymentFailure(Cause.squash(started.cause));
3294
- slot.lifecycle = transitionDeploymentComponentLifecycle(slot.lifecycle, "degraded");
3295
- return true;
3296
- }
3297
- slot.lifecycle = transitionDeploymentComponentLifecycle(slot.lifecycle, "running");
3298
- return false;
3299
- });
3300
- }
3301
- function stopSlots(slots, errors) {
3302
- return Effect.gen(function* () {
3303
- for (const slot of [...slots].reverse()) {
3304
- if (slot.lifecycle === "stopped" || slot.lifecycle === "disabled") continue;
3305
- slot.lifecycle = transitionDeploymentComponentLifecycle(slot.lifecycle, "stopping");
3306
- if (!slot.adapter) {
3307
- slot.lifecycle = transitionDeploymentComponentLifecycle(slot.lifecycle, slot.definition.enabled && slot.agentEnabled ? "stopped" : "disabled");
3308
- delete slot.error;
3309
- continue;
3310
- }
3311
- const stopped = yield* Effect.gen(function* () {
3312
- yield* slot.adapter.stop();
3313
- if (yield* Effect.try({
3314
- try: () => slot.adapter.running(),
3315
- catch: toDeploymentFailure
3316
- })) return yield* Effect.fail(/* @__PURE__ */ new Error("component stop completed but the adapter is still running"));
3317
- }).pipe(Effect.exit);
3318
- if (Exit.isFailure(stopped)) {
3319
- const error = Cause.squash(stopped.cause);
3320
- errors.push(error);
3321
- slot.error = formatDeploymentFailure(error);
3322
- slot.lifecycle = transitionDeploymentComponentLifecycle(slot.lifecycle, "cleanup-required");
3323
- continue;
3324
- }
3325
- slot.lifecycle = transitionDeploymentComponentLifecycle(slot.lifecycle, slot.definition.enabled && slot.agentEnabled ? "stopped" : "disabled");
3326
- delete slot.error;
3327
- }
3328
- });
3329
- }
3330
- function componentStatus(slot) {
3331
- const observed = observeRunning(slot);
3332
- return Object.freeze({
3333
- enabled: slot.definition.enabled,
3334
- ...slot.error ? { error: slot.error } : {},
3335
- lifecycle: slot.lifecycle,
3336
- required: slot.definition.required,
3337
- running: slot.lifecycle === "running" && observed.running
3338
- });
3339
- }
3340
- function backgroundStatus(slot) {
3341
- let supervisor;
3342
- try {
3343
- supervisor = slot.adapter?.status?.();
3344
- } catch (error) {
3345
- slot.error = formatDeploymentFailure(error);
3346
- }
3347
- return Object.freeze({
3348
- ...componentStatus(slot),
3349
- ...supervisor ? { supervisor } : {}
3350
- });
3351
- }
3352
- function isSlotReady(slot) {
3353
- return isRequiredDeploymentComponentReady({
3354
- enabled: slot.definition.enabled,
3355
- lifecycle: slot.lifecycle,
3356
- required: slot.definition.required,
3357
- running: observeRunning(slot).running
3358
- });
3359
- }
3360
- function observeRunning(slot) {
3361
- if (!slot.adapter) return { running: false };
3362
- try {
3363
- return { running: slot.adapter.running() };
3364
- } catch (error) {
3365
- return {
3366
- error: formatDeploymentFailure(error),
3367
- running: false
3368
- };
3369
- }
3370
- }
3371
- //#endregion
3372
- //#region src/adapters/deployment/project-space/node-project-space-resolver.ts
3373
- async function resolveRivusProjectSpace(input) {
3374
- validateRivusProjectSpaceDeployment(input.declaration);
3375
- const root = await resolveContainedDirectory(await resolveExistingDirectory(input.deploymentRoot, "deployment root"), input.declaration.root, "Project Space root");
3376
- const workingDirectory = await resolveContainedDirectory(root, input.declaration.workingDirectory, "Project Space working directory");
3377
- const skillPaths = await Promise.all(input.declaration.skills.sources.map((source) => resolveContainedPath(root, source, "Project Space Skill source")));
3378
- await Promise.all(skillPaths.map((path) => assertTreeContainsNoSymbolicLinks(path)));
3379
- return Object.freeze({
3380
- id: input.declaration.id,
3381
- revision: createStableId("project-space", {
3382
- id: input.declaration.id,
3383
- root,
3384
- skillPaths,
3385
- workingDirectory
3386
- }),
3387
- root,
3388
- skillPaths: Object.freeze(skillPaths),
3389
- workingDirectory
3390
- });
3391
- }
3392
- async function assertTreeContainsNoSymbolicLinks(path) {
3393
- const metadata = await lstat(path);
3394
- if (metadata.isSymbolicLink()) throw new InvalidRivusProjectSpace(`Project Space Skill source contains a symbolic link: ${path}`);
3395
- if (!metadata.isDirectory()) return;
3396
- for (const entry of await readdir(path, { withFileTypes: true })) {
3397
- const child = resolve(path, entry.name);
3398
- if (entry.isSymbolicLink()) throw new InvalidRivusProjectSpace(`Project Space Skill source contains a symbolic link: ${child}`);
3399
- if (entry.isDirectory()) await assertTreeContainsNoSymbolicLinks(child);
3400
- }
3401
- }
3402
- async function resolveContainedDirectory(base, path, owner) {
3403
- const resolved = await resolveContainedPath(base, path, owner);
3404
- if (!(await stat(resolved)).isDirectory()) throw new InvalidRivusProjectSpace(`${owner} must be a directory: ${path}`);
3405
- return resolved;
3406
- }
3407
- async function resolveContainedPath(base, path, owner) {
3408
- validateRelativePath(path, owner);
3409
- const candidate = resolve(base, path);
3410
- assertContained(base, candidate, owner);
3411
- let resolved;
3412
- try {
3413
- resolved = await realpath(candidate);
3414
- } catch (cause) {
3415
- throw new InvalidRivusProjectSpace(`${owner} does not exist: ${path}`, { cause });
3416
- }
3417
- assertContained(base, resolved, owner);
3418
- const metadata = await stat(resolved);
3419
- if (!metadata.isDirectory() && !metadata.isFile()) throw new InvalidRivusProjectSpace(`${owner} must be a file or directory: ${path}`);
3420
- return resolved;
3421
- }
3422
- async function resolveExistingDirectory(path, owner) {
3423
- let resolved;
3424
- try {
3425
- resolved = await realpath(path);
3426
- } catch (cause) {
3427
- throw new InvalidRivusProjectSpace(`${owner} does not exist`, { cause });
3428
- }
3429
- if (!(await stat(resolved)).isDirectory()) throw new InvalidRivusProjectSpace(`${owner} must be a directory`);
3430
- return resolved;
3431
- }
3432
- function validateRelativePath(path, owner) {
3433
- if (!path.trim() || path.includes("\0") || isAbsolute(path)) throw new InvalidRivusProjectSpace(`${owner} must be a non-empty relative path`);
3434
- }
3435
- function assertContained(base, candidate, owner) {
3436
- if (isPathWithin(base, candidate)) return;
3437
- throw new InvalidRivusProjectSpace(`${owner} escapes its trusted root`);
3438
- }
3439
- //#endregion
3440
- //#region src/adapters/cli/prompt/local-cli-invocation.ts
3441
- function createLocalCliAgentInvocation(input) {
3442
- const conversationId = trimOptional(input.env.RIVUS_LOCAL_CONVERSATION_ID);
3443
- return {
3444
- allowedActorOpenIds: [],
3445
- endpointId: input.endpointId,
3446
- kind: "local-cli",
3447
- memory: {
3448
- audience: "private",
3449
- ...conversationId ? { conversationId } : {},
3450
- ...input.projectSpaceId ? { projectId: input.projectSpaceId } : {},
3451
- subjectId: trimOptional(input.env.RIVUS_LOCAL_SUBJECT_ID) ?? "local-operator",
3452
- tenantId: trimOptional(input.env.RIVUS_MEMORY_TENANT_ID) ?? "local"
3453
- },
3454
- sourceMessageId: `cli:${randomUUID()}`,
3455
- tenantKey: "local"
3456
- };
3457
- }
3458
- function trimOptional(value) {
3459
- return value?.trim() || void 0;
3460
- }
3461
- //#endregion
3462
- //#region src/bootstrap/deployment/rivus-deployment-cli-process.ts
3463
- async function createRivusDeploymentCliProcess(factory, context) {
3464
- const adapters = await factory(context);
3465
- let adaptersDisposed = false;
3466
- let recoveryControlPromise;
3467
- const disposeAdapters = async () => {
3468
- if (adaptersDisposed) return;
3469
- adaptersDisposed = true;
3470
- await adapters.dispose?.();
3471
- };
3472
- let daemon;
3473
- try {
3474
- daemon = await runDeploymentProcessEffect(loadRivusDeploymentManifest(context.manifestPath).pipe(Effect.flatMap((manifest) => {
3475
- const assemblyInput = {
3476
- ...createProcessDeploymentControlPorts({
3477
- ...adapters.createAutomation ? { createAutomation: adapters.createAutomation } : {},
3478
- ...adapters.createBackgroundSession ? { createBackgroundSession: adapters.createBackgroundSession } : {},
3479
- createEndpoint: adapters.createEndpoint,
3480
- createRuntime: adapters.createRuntime
3481
- }, manifest.backgroundSessions?.enabled === true, runDeploymentProcessEffect),
3482
- deploymentRoot: dirname(context.manifestPath),
3483
- ...adapters.initialInstanceRecords ? { initialInstanceRecords: adapters.initialInstanceRecords } : {},
3484
- manifest,
3485
- pluginLoader: createNodeRivusPluginModuleLoader(context.pluginPackageManifestPath ? { packageManifestPath: context.pluginPackageManifestPath } : {}),
3486
- projectSpaceResolver: { resolve: (input) => Effect.tryPromise({
3487
- try: () => resolveRivusProjectSpace(input),
3488
- catch: toError
3489
- }) }
3490
- };
3491
- return resolveRivusDeployment({
3492
- ...assemblyInput,
3493
- createStableId,
3494
- deepFreeze
3495
- }).pipe(Effect.flatMap((deployment) => createRivusDeploymentControl({
3496
- ...assemblyInput,
3497
- deployment,
3498
- digest: createSha256Digest
3499
- })));
3500
- })));
3501
- } catch (constructionError) {
3502
- try {
3503
- await disposeAdapters();
3504
- } catch (disposeError) {
3505
- throw new AggregateError([constructionError, disposeError], "deployment construction and cleanup failed");
3506
- }
3507
- throw constructionError;
3508
- }
3509
- const start = async () => {
3510
- if (adaptersDisposed) throw new Error("Rivus deployment process cannot restart after its adapters have been disposed");
3511
- try {
3512
- await runDeploymentProcessEffect(daemon.start());
3513
- } catch (startError) {
3514
- const cleanupErrors = [];
3515
- try {
3516
- await runDeploymentProcessEffect(daemon.stop());
3517
- } catch (error) {
3518
- cleanupErrors.push(error);
3519
- }
3520
- try {
3521
- await disposeAdapters();
3522
- } catch (error) {
3523
- cleanupErrors.push(error);
3524
- }
3525
- if (cleanupErrors.length > 0) throw new AggregateError([startError, ...cleanupErrors], "deployment startup and cleanup failed");
3526
- throw startError;
3527
- }
3528
- };
3529
- const stop = async () => {
3530
- const errors = [];
3531
- try {
3532
- await runDeploymentProcessEffect(daemon.stop());
3533
- } catch (error) {
3534
- errors.push(error);
3535
- }
3536
- try {
3537
- await disposeAdapters();
3538
- } catch (error) {
3539
- errors.push(error);
3540
- }
3541
- if (errors.length === 1) throw errors[0];
3542
- if (errors.length > 1) throw new AggregateError(errors, "deployment and adapter cleanup failed");
3543
- };
3544
- const process = {
3545
- defaultSessionKey: `local:${daemon.deployment.manifest.defaultAgentId}:cli`,
3546
- openRecoveryControl: () => Effect.tryPromise({
3547
- try: async () => {
3548
- if (adaptersDisposed) throw new Error("Rivus deployment process cannot open Recovery Control after disposal");
3549
- if (!adapters.createRecoveryControl) throw new Error("Deployment bootstrap does not expose Recovery Control");
3550
- recoveryControlPromise ??= Promise.resolve().then(() => adapters.createRecoveryControl());
3551
- try {
3552
- return await recoveryControlPromise;
3553
- } catch (error) {
3554
- recoveryControlPromise = void 0;
3555
- throw error;
3556
- }
3557
- },
3558
- catch: (error) => error
3559
- }),
3560
- running: () => daemon.running(),
3561
- start: () => Effect.tryPromise({
3562
- try: start,
3563
- catch: (error) => error
3564
- }),
3565
- status: () => Effect.sync(() => daemon.status()),
3566
- stop: () => Effect.tryPromise({
3567
- try: stop,
3568
- catch: (error) => error
3569
- }),
3570
- promptText: (command) => Effect.tryPromise({
3571
- try: async () => {
3572
- if (adaptersDisposed) throw new Error("Rivus deployment process cannot restart after its adapters have been disposed");
3573
- const projectSpaceId = daemon.deployment.definitions.find(({ agentId }) => agentId === daemon.deployment.manifest.defaultAgentId)?.projectSpaceId;
3574
- return readPromptFinalText(await runDeploymentProcessEffect(daemon.runDefaultAgent({
3575
- invocation: createLocalCliAgentInvocation({
3576
- endpointId: daemon.deployment.manifest.defaultEndpointId,
3577
- env: context.env,
3578
- ...projectSpaceId ? { projectSpaceId } : {}
3579
- }),
3580
- sessionKey: command.sessionKey,
3581
- text: command.text
3582
- })));
3583
- },
3584
- catch: (error) => error
3585
- })
3586
- };
3587
- const replayReceiveMessage = adapters.replayReceiveMessage;
3588
- if (replayReceiveMessage) process.replayReceiveMessage = (payload, options) => Effect.tryPromise({
3589
- try: async () => {
3590
- if (!daemon.running()) await start();
3591
- },
3592
- catch: (error) => error
3593
- }).pipe(Effect.flatMap(() => replayReceiveMessage((input) => runDeploymentProcessEffect(daemon.handleDefault(toEffectAgentRuntimeInput(input))), payload, options)));
3594
- return process;
3595
- }
3596
- function readPromptFinalText(result) {
3597
- if (typeof result === "string") return result;
3598
- if (typeof result === "object" && result !== null && "finalText" in result && typeof result.finalText === "string") return result.finalText;
3599
- throw new Error("Default deployment prompt result must be a string or contain finalText");
3600
- }
3601
- function toError(error) {
3602
- return error instanceof Error ? error : new Error(String(error));
3603
- }
3604
- //#endregion
3605
- //#region src/bootstrap/daemon/rivus-daemon-cli.ts
3606
- function runRivusDaemonCli(options) {
3607
- return Effect.gen(function* () {
3608
- const parsed = parseRivusDaemonArguments(options.argv);
3609
- if (parsed.help) {
3610
- options.stdout.write(RIVUS_DAEMON_USAGE);
3611
- return 0;
3612
- }
3613
- if (parsed.error) {
3614
- options.stderr.write(formatRivusDaemonUsageError(parsed.error));
3615
- return 1;
3616
- }
3617
- if (parsed.statusUrl) {
3618
- const statusUrl = parsed.statusUrl;
3619
- const readStatus = () => Effect.tryPromise({
3620
- try: () => fetchLiveStatus(statusUrl),
3621
- catch: (error) => error
3622
- });
3623
- const status = parsed.waitReceive ? yield* waitForReceiveStatus(readStatus, parsed.waitReceive, createWaitReceiveOptions(parsed)) : yield* readStatus();
3624
- options.stdout.write(formatRivusDaemonJson(status));
3625
- return 0;
3626
- }
3627
- if (parsed.printOpenClawEnvPath) {
3628
- const openClawConfigPath = parsed.printOpenClawEnvPath;
3629
- const result = yield* Effect.tryPromise({
3630
- try: () => loadRivusEnvFromOpenClawConfig(openClawConfigPath, parsed.piApiKeyFile),
3631
- catch: (error) => error
3632
- });
3633
- for (const warning of result.warnings) options.stderr.write(formatRivusDaemonWarning(warning));
3634
- options.stdout.write(formatRivusEnvFile(result.env));
3635
- return 0;
3636
- }
3637
- const env = yield* Effect.tryPromise({
3638
- try: () => loadCliEnv(parsed.envFilePath, options.env),
3639
- catch: (error) => error
3640
- });
3641
- const bootstrapSpecifier = parsed.bootstrap ?? env.RIVUS_BOOTSTRAP_MODULE?.trim();
3642
- if (!parsed.checkConfig && !bootstrapSpecifier) {
3643
- options.stderr.write(formatRivusDaemonUsageError("Missing --bootstrap <module> or RIVUS_BOOTSTRAP_MODULE"));
3644
- return 1;
3645
- }
3646
- let legacyConfig;
3647
- if (parsed.manifestPath) {
3648
- if (parsed.checkConfig) {
3649
- const manifestPath = parsed.manifestPath;
3650
- const manifest = yield* loadRivusDeploymentManifest(manifestPath);
3651
- yield* Effect.try({
3652
- try: () => validateRivusDeploymentManifest(manifest),
3653
- catch: (error) => error
3654
- });
3655
- options.stdout.write(formatRivusDaemonJson(toRedactedDeploymentManifest(manifest)));
3656
- return 0;
3657
- }
3658
- } else {
3659
- const configExit = yield* Effect.exit(loadRivusDaemonConfig(env));
3660
- if (configExit._tag === "Failure") {
3661
- options.stderr.write(formatRivusDaemonLine(configExit.cause.toString()));
3662
- return 1;
3663
- }
3664
- legacyConfig = configExit.value;
3665
- if (parsed.checkConfig) {
3666
- options.stdout.write(formatRivusDaemonJson(toRedactedConfig(legacyConfig)));
3667
- return 0;
3668
- }
3669
- }
3670
- if (!bootstrapSpecifier) {
3671
- options.stderr.write(formatRivusDaemonUsageError("Missing --bootstrap <module> or RIVUS_BOOTSTRAP_MODULE"));
3672
- return 1;
3673
- }
3674
- const loadBootstrap = options.loadBootstrap ?? ((specifier) => import(specifier));
3675
- const module = yield* Effect.tryPromise({
3676
- try: () => loadBootstrap(bootstrapSpecifier),
3677
- catch: (error) => error
3678
- });
3679
- const daemon = yield* Effect.tryPromise({
3680
- try: () => parsed.manifestPath ? createCliDeploymentDaemon(module.createRivusDeploymentAdapters, {
3681
- argv: options.argv,
3682
- env,
3683
- ...parsed.envFilePath ? { envFilePath: parsed.envFilePath } : {},
3684
- environmentOverrides: options.env,
3685
- manifestPath: parsed.manifestPath,
3686
- ...options.pluginPackageManifestPath ? { pluginPackageManifestPath: options.pluginPackageManifestPath } : {}
3687
- }) : createCliLegacyDaemon(module, {
3688
- argv: options.argv,
3689
- config: legacyConfig,
3690
- env
3691
- }),
3692
- catch: (error) => error
3693
- });
3694
- if (parsed.recoveryCommand) {
3695
- const recoveryCommand = parsed.recoveryCommand;
3696
- return yield* runOneShotDaemon(daemon, () => Effect.gen(function* () {
3697
- if (!hasRecoveryRunner(daemon)) {
3698
- options.stderr.write(formatRivusDaemonCapabilityError("recovery"));
3699
- return 1;
3700
- }
3701
- const control = yield* daemon.openRecoveryControl();
3702
- const result = yield* Effect.tryPromise({
3703
- try: () => runRivusRecoveryCliCommand(control, recoveryCommand),
3704
- catch: (error) => error
3705
- });
3706
- options.stdout.write(formatRivusDaemonJson(result));
3707
- return 0;
3708
- }));
3709
- }
3710
- if (parsed.status) return yield* runStatusOneShot(options, daemon, (statusDaemon) => Effect.gen(function* () {
3711
- const status = yield* statusDaemon.status();
3712
- options.stdout.write(formatRivusDaemonJson(status));
3713
- return 0;
3714
- }));
3715
- if (parsed.prompt !== void 0) {
3716
- const prompt = parsed.prompt;
3717
- return yield* runOneShotDaemon(daemon, () => Effect.gen(function* () {
3718
- if (!hasPromptRunner(daemon)) {
3719
- options.stderr.write(formatRivusDaemonCapabilityError("prompt"));
3720
- return 1;
3721
- }
3722
- const text = yield* daemon.promptText({
3723
- sessionKey: parsed.sessionKey ?? daemon.defaultSessionKey ?? `local:${legacyConfig.agentId}:cli`,
3724
- text: prompt
3725
- });
3726
- options.stdout.write(formatRivusDaemonLine(text));
3727
- return 0;
3728
- }));
3729
- }
3730
- if (parsed.replayFeishuText !== void 0) {
3731
- const replayFeishuText = parsed.replayFeishuText;
3732
- return yield* runReplayOneShot(options, daemon, () => Effect.sync(() => createSyntheticFeishuTextReplayPayload(replayFeishuText, {
3733
- ...parsed.feishuReplayChatId !== void 0 ? { chatId: parsed.feishuReplayChatId } : {},
3734
- ...parsed.feishuReplayMessageId !== void 0 ? { messageId: parsed.feishuReplayMessageId } : {},
3735
- ...parsed.feishuReplayTenantKey !== void 0 ? { tenantKey: parsed.feishuReplayTenantKey } : {},
3736
- ...parsed.feishuReplayThreadId !== void 0 ? { threadId: parsed.feishuReplayThreadId } : {}
3737
- })), { sideEffects: "disabled" });
3738
- }
3739
- if (parsed.replayFeishuEventPath !== void 0) {
3740
- const replayFeishuEventPath = parsed.replayFeishuEventPath;
3741
- return yield* runReplayOneShot(options, daemon, () => Effect.tryPromise({
3742
- try: () => readFeishuReceiveMessagePayload(replayFeishuEventPath),
3743
- catch: (error) => error
3744
- }));
3745
- }
3746
- if (parsed.waitReceive) {
3747
- const waitReceive = parsed.waitReceive;
3748
- return yield* runStatusOneShot(options, daemon, (statusDaemon) => Effect.gen(function* () {
3749
- yield* Effect.try({
3750
- try: () => installCliShutdownController(options, statusDaemon),
3751
- catch: (error) => error
3752
- });
3753
- yield* statusDaemon.start();
3754
- const status = yield* waitForReceiveStatus(() => statusDaemon.status(), waitReceive, createWaitReceiveOptions(parsed));
3755
- options.stdout.write(formatRivusDaemonJson(status));
3756
- return 0;
3757
- }));
3758
- }
3759
- yield* Effect.try({
3760
- try: () => installCliShutdownController(options, daemon),
3761
- catch: (error) => error
3762
- });
3763
- yield* daemon.start();
3764
- options.stdout.write(RIVUS_DAEMON_STARTED_MESSAGE);
3765
- return 0;
3766
- }).pipe(Effect.catchAll((error) => Effect.sync(() => {
3767
- options.stderr.write(formatRivusDaemonLine(formatRivusDaemonError(error)));
3768
- return 1;
3769
- })));
3770
- }
3771
- async function createCliLegacyDaemon(module, context) {
3772
- const factory = module.createRivusDaemonProcess ?? module.default;
3773
- if (!factory) throw new Error("Bootstrap module must export createRivusDaemonProcess(context) or a default factory");
3774
- return factory(context);
3775
- }
3776
- async function createCliDeploymentDaemon(factory, context) {
3777
- if (!factory) throw new Error("Manifest bootstrap module must export createRivusDeploymentAdapters(context)");
3778
- return createRivusDeploymentCliProcess(factory, context);
3779
- }
3780
- function runOneShotDaemon(daemon, action) {
3781
- return Effect.exit(Effect.suspend(action)).pipe(Effect.flatMap((exit) => daemon.stop().pipe(Effect.flatMap(() => exit._tag === "Success" ? Effect.succeed(exit.value) : Effect.failCause(exit.cause)))));
3782
- }
3783
- function runStatusOneShot(options, daemon, action) {
3784
- return runOneShotDaemon(daemon, () => Effect.gen(function* () {
3785
- if (!hasStatusReporter(daemon)) {
3786
- options.stderr.write(formatRivusDaemonCapabilityError("status"));
3787
- return 1;
3788
- }
3789
- return yield* action(daemon);
3790
- }));
3791
- }
3792
- function runReplayOneShot(options, daemon, createPayload, replayOptions) {
3793
- return runOneShotDaemon(daemon, () => Effect.gen(function* () {
3794
- if (!hasFeishuReplayRunner(daemon)) {
3795
- options.stderr.write(formatRivusDaemonCapabilityError("replay"));
3796
- return 1;
3797
- }
3798
- const payload = yield* createPayload();
3799
- const result = replayOptions === void 0 ? yield* daemon.replayReceiveMessage(payload) : yield* daemon.replayReceiveMessage(payload, replayOptions);
3800
- options.stdout.write(formatRivusDaemonJson(result));
3801
- return 0;
3802
- }));
3803
- }
3804
- function createWaitReceiveOptions(parsed) {
3805
- return {
3806
- ...parsed.waitReceiveMessageId ? { messageId: parsed.waitReceiveMessageId } : {},
3807
- ...parsed.waitReceiveObservedAfter ? { observedAfter: parsed.waitReceiveObservedAfter } : {},
3808
- pollMs: parsed.waitPollMs ?? 500,
3809
- ...parsed.waitReceiveText ? { text: parsed.waitReceiveText } : {},
3810
- timeoutMs: parsed.waitTimeoutMs ?? 3e4
3811
- };
3812
- }
3813
- function installCliShutdownController(options, daemon) {
3814
- createRivusDaemonShutdownController({
3815
- daemon,
3816
- onError: (error, signal) => {
3817
- options.stderr.write(formatRivusDaemonShutdownFailure(signal, formatRivusDaemonError(error)));
3818
- options.exitAfterSignal?.(1);
3819
- },
3820
- onStopped: () => {
3821
- options.exitAfterSignal?.(0);
3822
- },
3823
- signalSource: options.signalSource
3824
- }).install();
3825
- }
3826
- function hasStatusReporter(daemon) {
3827
- return typeof daemon.status === "function";
3828
- }
3829
- function hasPromptRunner(daemon) {
3830
- return typeof daemon.promptText === "function";
3831
- }
3832
- function hasFeishuReplayRunner(daemon) {
3833
- return typeof daemon.replayReceiveMessage === "function";
3834
- }
3835
- function hasRecoveryRunner(daemon) {
3836
- return typeof daemon.openRecoveryControl === "function";
3837
- }
3838
- //#endregion
3839
- export { validateRivusDeploymentManifest as A, createRuntimeCache as B, createStableId as C, RivusDeploymentManifestError as D, createAgentHostRuntimePool as E, FeishuEndpointCredentialError as F, loadRivusDaemonConfig as G, invokeRuntimeControl as H, resolveFeishuEndpointCredentials as I, migrateRivusModelManagementConfig as J, installRivusModelManagementCliLauncher as K, toEffectAgentRuntime as L, OpenClawEnvImportError as M, createRivusEnvFromOpenClawConfig as N, loadRivusDeploymentManifest as O, formatRivusEnvFile as P, toEffectAgentRuntimeInput as R, createEffectAgentInstanceRegistry as S, AgentRuntimeDisposed as T, runDeploymentProcessEffect as U, disposeRuntimeCacheEntries as V, RivusDaemonConfigError as W, loadMergedLocalEnvFile as X, parseRivusModelManagementHomeConfig as Y, resolveRivusPluginModule as _, RivusDeploymentBackgroundSessionReadinessError as a, createEffectAgentHost as b, RivusDeploymentDaemonLifecycleError as c, createNodeRivusPluginModuleLoader as d, loadNodeRivusPluginModule as f, findTrustedPackageRoot as g, isPathWithin as h, RivusDeploymentAutomationReadinessError as i, createRivusDaemonShutdownController as j, InvalidRivusProjectSpace as k, RivusPluginLoadError as l, validateTrustedModulePath as m, createRivusDeploymentCliProcess as n, RivusDeploymentReadinessError as o, resolveNodeRivusPluginModulePath as p, installRivusRuntimeManagementSkill as q, resolveRivusProjectSpace as r, createRivusDeploymentControl as s, runRivusDaemonCli as t, resolveRivusDeployment as u, createProcessDeploymentControlPorts as v, AgentInstanceBusy as w, AgentInstanceConflict as x, InvalidAgentHostBinding as y, toProcessAgentRuntimeInput as z };