@rivus/agent 0.13.2 → 0.14.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/README.md +17 -18
  2. package/dist/acp.d.ts +40 -40
  3. package/dist/acp.js +71 -31
  4. package/dist/bootstrap/pi-feishu.d.ts +20 -0
  5. package/dist/bootstrap/pi-feishu.js +596 -0
  6. package/dist/{agent-loop.d.ts → chunks/agent-loop.d.ts} +293 -44
  7. package/dist/chunks/agent-loop.js +1272 -0
  8. package/dist/chunks/api.d.ts +70 -0
  9. package/dist/chunks/api.js +471 -0
  10. package/dist/{rivus-plugin.d.ts → chunks/api2.d.ts} +271 -120
  11. package/dist/chunks/api2.js +1331 -0
  12. package/dist/chunks/api3.d.ts +402 -0
  13. package/dist/chunks/index.d.ts +3662 -0
  14. package/dist/chunks/module.js +267 -0
  15. package/dist/chunks/pi-skill-tool.js +460 -0
  16. package/dist/chunks/pi-tool-proxy.d.ts +188 -0
  17. package/dist/chunks/pi.js +329 -0
  18. package/dist/{rivus-daemon-cli.js → chunks/rivus-daemon-cli.js} +2197 -1526
  19. package/dist/{rivus-plugin-testkit.d.ts → chunks/rivus-plugin-testkit.d.ts} +1 -1
  20. package/dist/{rivus-plugin-testkit.js → chunks/rivus-plugin-testkit.js} +11 -3
  21. package/dist/chunks/sha256-digest.js +12 -0
  22. package/dist/chunks/spi.d.ts +1 -0
  23. package/dist/chunks/spi.js +2 -0
  24. package/dist/chunks/src.js +9897 -0
  25. package/dist/cli.js +604 -95
  26. package/dist/index.d.ts +8 -3645
  27. package/dist/index.js +9 -10483
  28. package/dist/mcp.d.ts +3 -38
  29. package/dist/mcp.js +4 -114
  30. package/dist/pi.d.ts +95 -9
  31. package/dist/pi.js +3 -146
  32. package/dist/testing/index.d.ts +1 -1
  33. package/dist/testing/index.js +1 -1
  34. package/examples/pi-feishu-deployment.bootstrap.ts +45 -54
  35. package/examples/pi-feishu.bootstrap.ts +53 -37
  36. package/examples/rivus-starter.plugin.mjs +3 -1
  37. package/package.json +12 -14
  38. package/dist/agent-loop.js +0 -121
  39. package/dist/agent-memory.d.ts +0 -100
  40. package/dist/agent-memory.js +0 -114
  41. package/dist/background-session-authority.js +0 -224
  42. package/dist/background-session-input.js +0 -45
  43. package/dist/background-session-service.d.ts +0 -291
  44. package/dist/pi-tool-proxy.d.ts +0 -197
  45. package/dist/rivus-plugin-registry.js +0 -215
  46. package/dist/tool-input-digest.js +0 -128
@@ -1,328 +1,18 @@
1
- import { t as MEMORY_SCOPES } from "./agent-memory.js";
2
- import { f as narrowBackgroundSessionDefinition } from "./background-session-authority.js";
3
- import { n as resolveRivusAgentDefinition, r as deepFreeze, t as createRivusPluginCatalog } from "./rivus-plugin-registry.js";
1
+ import { a as deepFreeze, f as isRivusRuntimeToolId, t as restrictRivusAgentDefinitionGrants, v as MEMORY_SCOPES } from "./api.js";
2
+ import { t as createSha256Digest } from "./sha256-digest.js";
3
+ import { X as narrowBackgroundSessionDefinition } from "./api2.js";
4
+ import { n as createRivusHostToolDescriptorProvider, t as createRivusAgentCatalog } from "./module.js";
4
5
  import { createRequire } from "node:module";
5
- import { Effect } from "effect";
6
+ import { Cause, Deferred, Effect, Either, Exit, Option } from "effect";
6
7
  import { createHash, randomUUID } from "node:crypto";
7
8
  import { lstat, open, readFile, readdir, realpath, stat } from "node:fs/promises";
8
- import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
9
+ import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
9
10
  import { pathToFileURL } from "node:url";
10
11
  import { constants } from "node:fs";
11
- //#region src/application/plugin/rivus-automation-runtime-definition.ts
12
- function resolveRivusAutomationRuntimeDefinition(definition, requestedToolIds, requestedSkillIds) {
13
- const toolIds = Object.freeze([...new Set(requestedToolIds)].sort());
14
- const toolsById = new Map(definition.tools.map((tool) => [tool.id, tool]));
15
- const tools = Object.freeze(toolIds.map((toolId) => {
16
- const tool = toolsById.get(toolId);
17
- if (!tool) throw new Error(`Automation Runtime requests ungranted tool: ${toolId}`);
18
- return tool;
19
- }));
20
- const skillIds = Object.freeze([...new Set(requestedSkillIds)].sort());
21
- const skillsById = new Map(definition.skills.map((skill) => [skill.id, skill]));
22
- const skills = Object.freeze(skillIds.map((skillId) => {
23
- const skill = skillsById.get(skillId);
24
- if (!skill) throw new Error(`Automation Runtime requests ungranted skill: ${skillId}`);
25
- return skill;
26
- }));
27
- return deepFreeze({
28
- ...definition,
29
- memory: {
30
- scopes: [],
31
- tool: false
32
- },
33
- skillGrantSet: {
34
- revision: createHash("sha256").update(JSON.stringify({
35
- parentRevision: definition.skillGrantSet.revision,
36
- skillIds
37
- })).digest("hex"),
38
- skillIds
39
- },
40
- skills,
41
- toolGrantSet: {
42
- revision: createHash("sha256").update(JSON.stringify({
43
- parentRevision: definition.toolGrantSet.revision,
44
- toolIds
45
- })).digest("hex"),
46
- toolIds
47
- },
48
- tools
49
- });
50
- }
51
- //#endregion
52
- //#region src/application/plugin/rivus-plugin-loader.ts
53
- var RivusPluginLoadError = class extends Error {
54
- pluginId;
55
- moduleSpecifier;
56
- name = "RivusPluginLoadError";
57
- constructor(pluginId, moduleSpecifier, message, options) {
58
- super(message, options);
59
- this.pluginId = pluginId;
60
- this.moduleSpecifier = moduleSpecifier;
61
- }
62
- };
63
- async function loadRivusDeployment(options) {
64
- validateRivusDeploymentManifest(options.manifest);
65
- const catalog = createRivusPluginCatalog();
66
- const pluginStatuses = [];
67
- const statusByPlugin = /* @__PURE__ */ new Map();
68
- for (const declaration of options.manifest.plugins) try {
69
- const plugin = await resolvePluginExport(await options.loadModule({
70
- deploymentRoot: options.deploymentRoot,
71
- module: declaration.module,
72
- pluginId: declaration.id
73
- }));
74
- if (plugin.manifest.id !== declaration.id) throw new Error(`plugin manifest id ${plugin.manifest.id} does not match declaration ${declaration.id}`);
75
- catalog.registerPlugin(plugin);
76
- const status = Object.freeze({
77
- id: declaration.id,
78
- module: declaration.module,
79
- required: declaration.required,
80
- status: "loaded",
81
- version: plugin.manifest.version
82
- });
83
- pluginStatuses.push(status);
84
- statusByPlugin.set(declaration.id, status);
85
- } catch (cause) {
86
- const message = cause instanceof Error ? cause.message : String(cause);
87
- if (declaration.required) throw new RivusPluginLoadError(declaration.id, declaration.module, `required plugin ${declaration.id} failed to load: ${message}`, { cause });
88
- const status = Object.freeze({
89
- error: message,
90
- id: declaration.id,
91
- module: declaration.module,
92
- required: false,
93
- status: "failed"
94
- });
95
- pluginStatuses.push(status);
96
- statusByPlugin.set(declaration.id, status);
97
- }
98
- const agentStatuses = [];
99
- const definitions = [];
100
- for (const agent of options.manifest.agents) {
101
- const pluginStatus = statusByPlugin.get(agent.pluginId);
102
- if (pluginStatus.status === "failed") {
103
- agentStatuses.push(Object.freeze({
104
- agentId: agent.agentId,
105
- pluginId: agent.pluginId,
106
- profileId: agent.profileId,
107
- reason: `plugin ${agent.pluginId} is unavailable: ${pluginStatus.error}`,
108
- status: "disabled"
109
- }));
110
- continue;
111
- }
112
- try {
113
- const baseDefinition = resolveRivusAgentDefinition(catalog, agent, { backgroundSessions: options.manifest.backgroundSessions?.enabled === true });
114
- const projectSpace = agent.projectSpaceId ? options.manifest.projectSpaces?.find(({ id }) => id === agent.projectSpaceId) : void 0;
115
- const definition = projectSpace ? deepFreeze({
116
- ...baseDefinition,
117
- projectSpaceRevision: `project-space-declaration:${JSON.stringify(projectSpace)}`
118
- }) : baseDefinition;
119
- definitions.push(definition);
120
- agentStatuses.push(Object.freeze({
121
- agentId: agent.agentId,
122
- definition,
123
- pluginId: agent.pluginId,
124
- profileId: agent.profileId,
125
- status: "enabled"
126
- }));
127
- } catch (cause) {
128
- const declaration = options.manifest.plugins.find(({ id }) => id === agent.pluginId);
129
- const message = cause instanceof Error ? cause.message : String(cause);
130
- if (declaration.required) throw new RivusPluginLoadError(declaration.id, declaration.module, `deployment ${agent.agentId} failed to resolve: ${message}`, { cause });
131
- agentStatuses.push(Object.freeze({
132
- agentId: agent.agentId,
133
- pluginId: agent.pluginId,
134
- profileId: agent.profileId,
135
- reason: message,
136
- status: "disabled"
137
- }));
138
- }
139
- }
140
- const agentStatusById = new Map(agentStatuses.map((agent) => [agent.agentId, agent]));
141
- const automationTemplates = new Map(catalog.snapshot().automations.map((template) => [template.id, template]));
142
- const automationDefinitions = [];
143
- for (const automation of options.manifest.automations ?? []) {
144
- const agent = agentStatusById.get(automation.agentId);
145
- if (!agent || agent.status === "disabled" || !agent.definition) continue;
146
- const deliveryEndpoint = options.manifest.endpoints.find(({ id }) => id === automation.delivery.endpointId);
147
- const presentationAgent = agentStatusById.get(deliveryEndpoint.agentId);
148
- if (!presentationAgent || presentationAgent.status === "disabled" || !presentationAgent.definition) continue;
149
- const template = automationTemplates.get(automation.templateId);
150
- if (!template) throw new Error(`automation ${automation.id} references unknown template: ${automation.templateId}`);
151
- if (template.pluginId !== agent.pluginId || template.profileId !== agent.profileId) throw new Error(`automation ${automation.id} template is not owned by agent profile ${agent.profileId}`);
152
- automationDefinitions.push(deepFreeze({
153
- ...automation,
154
- runtimeDefinition: resolveRivusAutomationRuntimeDefinition(agent.definition, template.requestedToolIds, template.requestedSkillIds),
155
- template
156
- }));
157
- }
158
- return Object.freeze({
159
- agents: Object.freeze(agentStatuses),
160
- automationDefinitions: Object.freeze(automationDefinitions),
161
- catalog,
162
- definitions: Object.freeze(definitions),
163
- manifest: options.manifest,
164
- plugins: Object.freeze(pluginStatuses)
165
- });
166
- }
167
- function validateRivusDeploymentManifest(manifest) {
168
- const projectSpaceIds = /* @__PURE__ */ new Set();
169
- for (const projectSpace of manifest.projectSpaces ?? []) {
170
- if (projectSpaceIds.has(projectSpace.id)) throw new Error(`duplicate project space: ${projectSpace.id}`);
171
- projectSpaceIds.add(projectSpace.id);
172
- validateRelativeProjectPath(projectSpace.root, `project space ${projectSpace.id} root`);
173
- validateRelativeProjectPath(projectSpace.workingDirectory, `project space ${projectSpace.id} working directory`);
174
- if (projectSpace.skills.sources.length === 0) throw new Error(`project space ${projectSpace.id} must declare at least one Skill source`);
175
- const sources = /* @__PURE__ */ new Set();
176
- for (const source of projectSpace.skills.sources) {
177
- validateRelativeProjectPath(source, `project space ${projectSpace.id} Skill source`);
178
- if (sources.has(source)) throw new Error(`project space ${projectSpace.id} contains duplicate Skill source: ${source}`);
179
- sources.add(source);
180
- }
181
- }
182
- const pluginIds = /* @__PURE__ */ new Set();
183
- for (const plugin of manifest.plugins) {
184
- if (pluginIds.has(plugin.id)) throw new Error(`duplicate plugin declaration: ${plugin.id}`);
185
- validateModuleSpecifier(plugin.module);
186
- pluginIds.add(plugin.id);
187
- }
188
- const agentIds = /* @__PURE__ */ new Set();
189
- const agentById = /* @__PURE__ */ new Map();
190
- for (const agent of manifest.agents) {
191
- if (agentIds.has(agent.agentId)) throw new Error(`duplicate agent deployment: ${agent.agentId}`);
192
- agentIds.add(agent.agentId);
193
- if (!pluginIds.has(agent.pluginId)) throw new Error(`agent ${agent.agentId} references undeclared plugin: ${agent.pluginId}`);
194
- if (agent.projectSpaceId && !projectSpaceIds.has(agent.projectSpaceId)) throw new Error(`agent ${agent.agentId} references unknown project space: ${agent.projectSpaceId}`);
195
- agentById.set(agent.agentId, agent);
196
- }
197
- const automationIds = /* @__PURE__ */ new Set();
198
- for (const automation of manifest.automations ?? []) {
199
- if (automationIds.has(automation.id)) throw new Error(`duplicate automation binding: ${automation.id}`);
200
- automationIds.add(automation.id);
201
- if (!agentById.get(automation.agentId)) throw new Error(`automation ${automation.id} references unknown agent: ${automation.agentId}`);
202
- const endpoint = manifest.endpoints.find(({ id }) => id === automation.delivery.endpointId);
203
- if (!endpoint) throw new Error(`automation ${automation.id} references unknown delivery endpoint: ${automation.delivery.endpointId}`);
204
- if (automation.enabled && !endpoint.enabled) throw new Error(`automation ${automation.id} delivery endpoint must be enabled`);
205
- }
206
- const endpointIds = /* @__PURE__ */ new Set();
207
- const sessionNamespaces = /* @__PURE__ */ new Set();
208
- for (const endpoint of manifest.endpoints) {
209
- if (endpointIds.has(endpoint.id)) throw new Error(`duplicate endpoint binding: ${endpoint.id}`);
210
- endpointIds.add(endpoint.id);
211
- if (sessionNamespaces.has(endpoint.sessionNamespace)) throw new Error(`duplicate endpoint session namespace: ${endpoint.sessionNamespace}`);
212
- sessionNamespaces.add(endpoint.sessionNamespace);
213
- const agent = agentById.get(endpoint.agentId);
214
- if (!agent) throw new Error(`endpoint ${endpoint.id} references unknown agent: ${endpoint.agentId}`);
215
- if (!agent.endpointIds.includes(endpoint.id)) throw new Error(`endpoint ${endpoint.id} is not declared by agent ${endpoint.agentId}`);
216
- }
217
- for (const agent of manifest.agents) for (const endpointId of agent.endpointIds) {
218
- const endpoint = manifest.endpoints.find(({ id }) => id === endpointId);
219
- if (!endpoint) throw new Error(`agent ${agent.agentId} references unknown endpoint: ${endpointId}`);
220
- if (endpoint.agentId !== agent.agentId) throw new Error(`endpoint ${endpointId} is bound to a different agent`);
221
- }
222
- const defaultAgent = agentById.get(manifest.defaultAgentId);
223
- if (!defaultAgent) throw new Error(`default agent does not exist: ${manifest.defaultAgentId}`);
224
- const defaultEndpoint = manifest.endpoints.find(({ id }) => id === manifest.defaultEndpointId);
225
- if (!defaultEndpoint) throw new Error(`default endpoint does not exist: ${manifest.defaultEndpointId}`);
226
- if (defaultEndpoint.agentId !== defaultAgent.agentId) throw new Error("default endpoint is not bound to the default agent");
227
- if (!defaultEndpoint.enabled) throw new Error("default endpoint must be enabled");
228
- if (manifest.backgroundSessions) validateBackgroundSessions(manifest.backgroundSessions);
229
- }
230
- function validateBackgroundSessions(config) {
231
- if (config.required && !config.enabled) throw new Error("backgroundSessions.required requires backgroundSessions.enabled");
232
- if (config.leaseRenewalIntervalMs >= config.leaseMs) throw new Error("backgroundSessions.leaseRenewalIntervalMs must be shorter than leaseMs");
233
- if (config.stepTimeoutMs <= 0 || config.maxConcurrentSessions <= 0 || config.leaseMs <= 0) throw new Error("backgroundSessions durations and concurrency must be positive");
234
- if (config.retryBackoffMs <= 0 || config.sessionLifetimeMs <= 0 || config.maxConsecutiveFailures <= 0) throw new Error("backgroundSessions retry and lifetime limits must be positive");
235
- }
236
- function validateRelativeProjectPath(value, owner) {
237
- if (value.trim() === "" || isAbsolute(value) || value.includes("\0")) throw new Error(`${owner} must be a non-empty relative path`);
238
- }
239
- function validateModuleSpecifier(moduleSpecifier) {
240
- if (moduleSpecifier.trim() === "" || isAbsolute(moduleSpecifier) || /^[a-z][a-z+.-]*:/i.test(moduleSpecifier) || moduleSpecifier.includes("\0")) throw new Error(`invalid plugin module specifier: ${moduleSpecifier}`);
241
- if ((moduleSpecifier.startsWith("./") || moduleSpecifier.startsWith("../")) && moduleSpecifier.split(/[\\/]/).includes("..")) throw new Error(`plugin module escapes deployment root: ${moduleSpecifier}`);
242
- }
243
- async function resolvePluginExport(module) {
244
- const candidate = "default" in module ? module.default : module;
245
- const plugin = typeof candidate === "function" ? await candidate() : candidate;
246
- if (plugin === null || typeof plugin !== "object" || !("manifest" in plugin) || !("register" in plugin) || typeof plugin.register !== "function") throw new Error("plugin module default export is not a RivusPlugin or factory");
247
- return plugin;
248
- }
249
- //#endregion
250
- //#region src/infrastructure/config/local-env-file.ts
251
- var LocalEnvFileError = class extends Error {
252
- constructor(message) {
253
- super(message);
254
- this.name = "LocalEnvFileError";
255
- }
256
- };
257
- async function loadMergedLocalEnvFile(filePath, overrideEnv) {
258
- return mergeRivusDaemonEnv(await loadLocalEnvFile(filePath), overrideEnv);
259
- }
260
- async function loadLocalEnvFile(filePath) {
261
- return parseLocalEnvFile(await readFile(filePath, "utf8"));
262
- }
263
- function parseLocalEnvFile(contents) {
264
- const env = {};
265
- const lines = contents.split(/\r?\n/);
266
- for (let index = 0; index < lines.length; index += 1) {
267
- const trimmed = lines[index].trim();
268
- if (!trimmed || trimmed.startsWith("#")) continue;
269
- const match = (trimmed.startsWith("export ") ? trimmed.slice(7).trimStart() : trimmed).match(/^([A-Za-z_][A-Za-z0-9_]*)=(.*)$/);
270
- if (!match) throw new LocalEnvFileError(`Invalid env file line ${index + 1}`);
271
- const key = match[1];
272
- const rawValue = match[2];
273
- env[key] = parseEnvValue(rawValue, index + 1);
274
- }
275
- return env;
276
- }
277
- function mergeRivusDaemonEnv(fileEnv, overrideEnv) {
278
- const merged = { ...fileEnv };
279
- for (const [key, value] of Object.entries(overrideEnv)) if (value !== void 0) merged[key] = value;
280
- return merged;
281
- }
282
- function parseEnvValue(rawValue, lineNumber) {
283
- const value = rawValue.trim();
284
- if (!value) return "";
285
- if (value.startsWith("'")) {
286
- if (!value.endsWith("'")) throw new LocalEnvFileError(`Invalid single-quoted env value on line ${lineNumber}`);
287
- return value.slice(1, -1).replaceAll("'\\''", "'");
288
- }
289
- if (value.startsWith("\"")) {
290
- if (!value.endsWith("\"")) throw new LocalEnvFileError(`Invalid double-quoted env value on line ${lineNumber}`);
291
- return unescapeDoubleQuotedValue(value.slice(1, -1));
292
- }
293
- return value;
294
- }
295
- function unescapeDoubleQuotedValue(value) {
296
- return value.replace(/\\(["\\nrt])/g, (_match, escaped) => {
297
- switch (escaped) {
298
- case "n": return "\n";
299
- case "r": return "\r";
300
- case "t": return " ";
301
- default: return escaped;
302
- }
303
- });
304
- }
305
- //#endregion
306
- //#region src/infrastructure/config/feishu-endpoint-credentials.ts
307
- var FeishuEndpointCredentialError = class extends Error {
308
- name = "FeishuEndpointCredentialError";
309
- };
310
- function resolveFeishuEndpointCredentials(credentialRef, env) {
311
- if (!credentialRef.startsWith("env:")) throw new FeishuEndpointCredentialError("Feishu endpoint credentialRef must use env:<PREFIX>");
312
- const prefix = credentialRef.slice(4);
313
- if (!/^[A-Z][A-Z0-9_]*$/.test(prefix)) throw new FeishuEndpointCredentialError(`Invalid environment prefix in credentialRef: ${credentialRef}`);
314
- return Object.freeze({
315
- appId: required$1(env, `${prefix}_APP_ID`),
316
- appSecret: required$1(env, `${prefix}_APP_SECRET`)
317
- });
318
- }
319
- function required$1(env, variable) {
320
- const value = env[variable]?.trim();
321
- if (!value) throw new FeishuEndpointCredentialError(`${variable} is required`);
322
- return value;
323
- }
12
+ //#region src/modules/run-presentation/domain/presentation/conversation-progress.ts
13
+ const DEFAULT_CONVERSATION_PROGRESS_DISPLAY = "collapsed";
324
14
  //#endregion
325
- //#region src/domain/card-presentation.ts
15
+ //#region src/modules/card-presentation/domain/presentation/card-presentation.ts
326
16
  var CardPresentationTransitionDenied = class extends Error {
327
17
  name = "CardPresentationTransitionDenied";
328
18
  };
@@ -424,6 +114,28 @@ function markCardPresentationTerminal(chain, terminalReceiptId) {
424
114
  terminalReceiptId
425
115
  });
426
116
  }
117
+ function validateCardPresentationChain(chain) {
118
+ if (chain.runId.trim().length === 0 || !Number.isSafeInteger(chain.activeGeneration) || chain.activeGeneration < 0 || chain.activePresentationId.trim().length === 0 || !Number.isSafeInteger(chain.revision) || chain.revision < 1 || chain.presentations.length === 0) throw new CardPresentationTransitionDenied("card presentation chain is invalid");
119
+ const presentationIds = /* @__PURE__ */ new Set();
120
+ let previous;
121
+ for (const [index, presentation] of chain.presentations.entries()) {
122
+ if (presentation.runId !== chain.runId || presentation.presentationId.trim().length === 0 || presentation.cardId.trim().length === 0 || typeof presentation.sourceMessageId !== "string" || !Number.isSafeInteger(presentation.generation) || presentation.generation !== index || !isCardPresentationStatus(presentation.status) || presentationIds.has(presentation.presentationId)) throw new CardPresentationTransitionDenied(`presentation chain ${chain.runId} contains an invalid entry`);
123
+ assertTimestamp(presentation.createdAt, "presentation creation time");
124
+ assertTimestamp(presentation.leaseDeadlineAt, "presentation lease deadline");
125
+ if (presentation.handoffAt !== void 0) assertTimestamp(presentation.handoffAt, "handoff time");
126
+ if (presentation.handoffFailedAt !== void 0) assertTimestamp(presentation.handoffFailedAt, "handoff failure time");
127
+ if (presentation.status === "terminal" && !presentation.terminalReceiptId?.trim()) throw new CardPresentationTransitionDenied(`terminal presentation ${presentation.presentationId} has no terminal receipt`);
128
+ if (presentation.status !== "terminal" && presentation.terminalReceiptId !== void 0) throw new CardPresentationTransitionDenied(`non-terminal presentation ${presentation.presentationId} carries a terminal receipt`);
129
+ if (previous === void 0) {
130
+ if (presentation.predecessorPresentationId !== void 0) throw new CardPresentationTransitionDenied(`first presentation ${presentation.presentationId} has a predecessor`);
131
+ } else if (presentation.predecessorPresentationId !== previous.presentationId || previous.successorPresentationId !== presentation.presentationId || previous.status !== "closed") throw new CardPresentationTransitionDenied(`presentation chain ${chain.runId} has inconsistent predecessor and successor links`);
132
+ previous = presentation;
133
+ presentationIds.add(presentation.presentationId);
134
+ }
135
+ const active = activeCardPresentation(chain);
136
+ if (chain.activeGeneration !== chain.presentations.length - 1 || active.generation !== chain.activeGeneration || active !== chain.presentations.at(-1) || active.successorPresentationId !== void 0) throw new CardPresentationTransitionDenied(`presentation chain ${chain.runId} has an invalid active generation`);
137
+ return chain;
138
+ }
427
139
  function replaceActivePresentation(chain, presentation) {
428
140
  return {
429
141
  ...chain,
@@ -434,358 +146,748 @@ function replaceActivePresentation(chain, presentation) {
434
146
  function assertTimestamp(value, label) {
435
147
  if (Number.isNaN(Date.parse(value))) throw new CardPresentationTransitionDenied(`${label} must be an ISO timestamp`);
436
148
  }
437
- //#endregion
438
- //#region src/domain/run-presentation.ts
439
- const RUN_PRESENTATION_SCHEMA_VERSION = 2;
440
- function hasInspectableRunProgress(presentation) {
441
- return presentation.steps.some((step) => step.kind === "skill" || step.kind === "tool");
149
+ function isCardPresentationStatus(value) {
150
+ return value === "streaming" || value === "handoff-pending" || value === "active" || value === "closed" || value === "terminal";
442
151
  }
443
152
  //#endregion
444
- //#region src/application/feishu/feishu-card-rollover.ts
445
- const DEFAULT_CARD_STREAM_LEASE_MS = 51e4;
446
- function createFeishuCardRollover(options) {
447
- const leaseMs = options.leaseMs ?? 51e4;
448
- if (!Number.isSafeInteger(leaseMs) || leaseMs < 1) throw new Error("Feishu card stream lease must be a positive integer");
449
- const counters = createCounters();
450
- const latestPresentationByRun = /* @__PURE__ */ new Map();
451
- const latestTextByRun = /* @__PURE__ */ new Map();
452
- const generationBaselineByRun = /* @__PURE__ */ new Map();
453
- const liveRuns = /* @__PURE__ */ new Map();
454
- const semaphore = Effect.unsafeMakeSemaphore(1);
455
- const exclusive = (effect) => semaphore.withPermits(1)(effect);
456
- const record = (event, at) => {
457
- counters[event.type] += 1;
458
- options.observe?.({
459
- ...event,
460
- at: at.toISOString()
461
- });
462
- };
463
- const recordNow = (event) => options.clock.now.pipe(Effect.map((at) => record(event, at)));
464
- const leaseDeadline = (at) => new Date(at.getTime() + leaseMs).toISOString();
465
- const presentationId = (runId, generation) => `${runId}#${generation}`;
466
- const publishProgress = (action) => Effect.suspend(() => {
467
- const chain = options.store.chain(action.runId);
468
- if (chain && acceptsCardPresentationProgress(chain)) {
469
- if (action.type === "update_text") latestTextByRun.set(action.runId, action.text);
470
- else {
471
- latestPresentationByRun.set(action.runId, action.presentation);
472
- latestTextByRun.set(action.runId, action.presentation.answer.text);
473
- }
474
- const run = liveRuns.get(action.runId);
475
- if (!run) return options.publisher.publish(action);
476
- const baseline = generationBaselineByRun.get(action.runId);
477
- const text = projectText(latestTextByRun.get(action.runId), baseline?.text);
478
- const presentation = projectPresentation(latestPresentationByRun.get(action.runId), baseline);
479
- return options.publisher.publish({
480
- ...presentation === void 0 ? {} : { presentation },
481
- runId: action.runId,
482
- sessionKey: run.sessionKey,
483
- ...text === void 0 ? {} : { text },
484
- type: "update_presentation"
485
- });
486
- }
487
- return recordNow({
488
- ...chain ? {
489
- generation: chain.activeGeneration,
490
- presentationId: chain.activePresentationId
491
- } : {},
492
- runId: action.runId,
493
- type: "stale_update_dropped"
494
- });
495
- });
496
- const publishTerminal = (action) => {
497
- const latestText = latestTextByRun.get(action.runId);
498
- const latestPresentation = latestPresentationByRun.get(action.runId);
499
- const withText = action.type === "cancel" && action.text === void 0 && latestText !== void 0 ? {
500
- ...action,
501
- text: latestText
502
- } : action;
503
- const projectedAction = projectTerminalAction((withText.type === "cancel" || withText.type === "fail" || withText.type === "finish") && withText.presentation === void 0 && latestPresentation !== void 0 && hasInspectableRunProgress(latestPresentation) ? {
504
- ...withText,
505
- presentation: latestPresentation
506
- } : withText, generationBaselineByRun.get(action.runId));
507
- return options.publisher.publish(projectedAction).pipe(Effect.tapError((error) => recordNow({
508
- error,
509
- runId: action.runId,
510
- type: "terminal_delivery_failed"
511
- })), Effect.tap(() => options.store.markTerminal({
512
- runId: action.runId,
513
- terminalReceiptId: `${action.runId}:${action.type}`
514
- }).pipe(Effect.catchAll((error) => recordNow({
515
- error,
516
- runId: action.runId,
517
- type: "presentation_write_failed"
518
- })))), Effect.ensuring(Effect.sync(() => {
519
- latestPresentationByRun.delete(action.runId);
520
- latestTextByRun.delete(action.runId);
521
- generationBaselineByRun.delete(action.runId);
522
- liveRuns.delete(action.runId);
523
- })));
524
- };
525
- const publishAction = (action) => {
526
- switch (action.type) {
527
- case "update_text":
528
- case "update_progress": return publishProgress(action);
529
- case "update_presentation": return options.publisher.publish(action);
530
- default: return publishTerminal(action);
531
- }
532
- };
533
- const runHandoff = (runId) => Effect.gen(function* () {
534
- const run = liveRuns.get(runId);
535
- if (!run) return {
536
- reason: "not-live",
537
- status: "skipped"
538
- };
539
- const startedAt = yield* options.clock.now;
540
- const chain = options.store.chain(runId);
541
- if (!chain || !isCardPresentationHandoffDue(chain, startedAt.toISOString())) return {
542
- reason: "not-due",
543
- status: "skipped"
544
- };
545
- const start = yield* options.store.beginHandoff({
546
- handoffAt: startedAt.toISOString(),
547
- runId
548
- }).pipe(Effect.catchAll(() => Effect.succeed(void 0)));
549
- if (!start) return {
550
- reason: "not-streaming",
551
- status: "skipped"
552
- };
553
- if (!start.started) return {
554
- reason: "already-pending",
555
- status: "skipped"
556
- };
557
- const predecessor = start.presentation;
558
- const generation = predecessor.generation + 1;
559
- record({
560
- cardId: predecessor.cardId,
561
- generation,
562
- presentationId: predecessor.presentationId,
563
- runId,
564
- sourceMessageId: predecessor.sourceMessageId,
565
- type: "handoff_started"
566
- }, startedAt);
567
- const successorId = presentationId(runId, generation);
568
- const created = yield* options.createSuccessor({
569
- generation,
570
- presentationId: successorId,
571
- run
572
- }).pipe(Effect.map((target) => ({ target })), Effect.catchAll((error) => options.store.failHandoff({
573
- failedAt: startedAt.toISOString(),
574
- runId
575
- }).pipe(Effect.catchAll((storeError) => recordNow({
576
- error: storeError,
577
- runId,
578
- type: "presentation_write_failed"
579
- })), Effect.flatMap(() => recordNow({
580
- cardId: predecessor.cardId,
581
- error,
582
- generation,
583
- presentationId: predecessor.presentationId,
584
- runId,
585
- type: "handoff_failed"
586
- })), Effect.as({ error }))));
587
- if ("error" in created) return {
588
- error: created.error,
589
- status: "failed"
590
- };
591
- const adoptedAt = yield* options.clock.now;
592
- const presentation = yield* options.store.completeHandoff({
593
- runId,
594
- successor: {
595
- cardId: created.target.cardId,
596
- createdAt: adoptedAt.toISOString(),
597
- ...created.target.elementId === void 0 ? {} : { elementId: created.target.elementId },
598
- leaseDeadlineAt: leaseDeadline(adoptedAt),
599
- presentationId: successorId
600
- }
601
- });
602
- record({
603
- cardId: predecessor.cardId,
604
- generation,
605
- presentationId: successorId,
606
- runId,
607
- sourceMessageId: predecessor.sourceMessageId,
608
- successorCardId: created.target.cardId,
609
- type: "handoff_succeeded"
610
- }, adoptedAt);
611
- generationBaselineByRun.set(runId, {
612
- ...latestPresentationByRun.has(runId) ? { presentation: latestPresentationByRun.get(runId) } : {},
613
- ...latestTextByRun.has(runId) ? { text: latestTextByRun.get(runId) } : {}
614
- });
615
- return {
616
- presentation,
617
- status: "rolled-over"
153
+ //#region src/modules/card-presentation/application/presentation/card-presentation-service.ts
154
+ var CardPresentationNotFound = class extends Error {
155
+ runId;
156
+ _tag = "CardPresentationNotFound";
157
+ name = "CardPresentationNotFound";
158
+ constructor(runId) {
159
+ super(`card presentation for run ${runId} was not found`);
160
+ this.runId = runId;
161
+ }
162
+ };
163
+ function createCardPresentationService(repository) {
164
+ const transition = (runId, apply) => repository.transact((current) => {
165
+ const chain = findChain(current, runId);
166
+ if (!chain) throw new CardPresentationNotFound(runId);
167
+ const next = apply(chain);
168
+ return next === chain ? { result: activeCardPresentation(chain) } : {
169
+ next: replaceChain(current, next),
170
+ result: activeCardPresentation(next)
618
171
  };
619
172
  });
620
173
  return {
621
- bindRun: (run, target) => exclusive(Effect.gen(function* () {
622
- const createdAt = yield* options.clock.now;
623
- const presentation = yield* options.store.bind({
624
- cardId: target.cardId,
625
- createdAt: createdAt.toISOString(),
626
- ...target.elementId === void 0 ? {} : { elementId: target.elementId },
627
- leaseDeadlineAt: leaseDeadline(createdAt),
628
- presentationId: presentationId(run.runId, 0),
629
- runId: run.runId,
630
- sourceMessageId: run.messageId
631
- });
632
- latestPresentationByRun.delete(run.runId);
633
- latestTextByRun.delete(run.runId);
634
- generationBaselineByRun.delete(run.runId);
635
- liveRuns.set(run.runId, run);
636
- return presentation;
637
- })),
638
- dueRunIds: (now) => {
639
- const nowIso = now.toISOString();
640
- return [...liveRuns.keys()].filter((runId) => {
641
- const chain = options.store.chain(runId);
642
- return chain !== void 0 && isCardPresentationHandoffDue(chain, nowIso);
643
- });
644
- },
645
- flush: (runId) => exclusive(options.publisher.flush(runId)),
646
- handoff: (runId) => exclusive(runHandoff(runId)),
647
- observeStreamClosed: (cardId) => {
648
- counters.stream_closed += 1;
649
- options.observe?.({
650
- cardId,
651
- type: "stream_closed"
174
+ bind: (binding) => repository.transact((current) => {
175
+ const chain = findChain(current, binding.runId);
176
+ if (chain && activeCardPresentation(chain).cardId === binding.cardId) return { result: activeCardPresentation(chain) };
177
+ const created = createCardPresentationChain(binding);
178
+ return {
179
+ next: replaceChain(current, created),
180
+ result: activeCardPresentation(created)
181
+ };
182
+ }),
183
+ beginHandoff: ({ handoffAt, runId }) => repository.transact((current) => {
184
+ const chain = findChain(current, runId);
185
+ if (!chain) throw new CardPresentationNotFound(runId);
186
+ const next = beginCardPresentationHandoff(chain, handoffAt);
187
+ const result = {
188
+ presentation: activeCardPresentation(next),
189
+ started: next !== chain
190
+ };
191
+ return next === chain ? { result } : {
192
+ next: replaceChain(current, next),
193
+ result
194
+ };
195
+ }),
196
+ chain: (runId) => findChain(repository.snapshot(), runId),
197
+ chains: () => repository.snapshot(),
198
+ compensateInterruptedHandoffs: (compensatedAt) => repository.transact((current) => {
199
+ const compensated = [];
200
+ const next = current.map((chain) => {
201
+ const candidate = compensateCardPresentationHandoff(chain, compensatedAt);
202
+ if (candidate !== chain) compensated.push(candidate);
203
+ return candidate;
652
204
  });
653
- },
654
- publish: (action) => exclusive(publishAction(action)),
655
- releaseRun: (runId) => Effect.sync(() => {
656
- latestTextByRun.delete(runId);
657
- latestPresentationByRun.delete(runId);
658
- generationBaselineByRun.delete(runId);
659
- liveRuns.delete(runId);
205
+ return compensated.length === 0 ? { result: [] } : {
206
+ next,
207
+ result: compensated
208
+ };
660
209
  }),
661
- recover: () => exclusive(Effect.gen(function* () {
662
- const compensatedAt = yield* options.clock.now;
663
- const compensated = yield* options.store.compensateInterruptedHandoffs(compensatedAt.toISOString());
664
- for (const chain of compensated) record({
665
- generation: chain.activeGeneration,
666
- presentationId: chain.activePresentationId,
667
- runId: chain.runId,
668
- type: "handoff_failed"
669
- }, compensatedAt);
670
- return { compensated: compensated.length };
671
- })),
672
- status: () => ({
673
- counters: { ...counters },
674
- leaseMs,
675
- live: [...liveRuns.keys()].flatMap((runId) => {
676
- const chain = options.store.chain(runId);
677
- if (!chain) return [];
678
- return chain.presentations.filter((presentation) => presentation.presentationId === chain.activePresentationId);
679
- })
680
- })
210
+ completeHandoff: ({ runId, successor }) => transition(runId, (chain) => completeCardPresentationHandoff(chain, successor)),
211
+ failHandoff: ({ failedAt, runId }) => transition(runId, (chain) => failCardPresentationHandoff(chain, failedAt)).pipe(Effect.asVoid),
212
+ markTerminal: ({ runId, terminalReceiptId }) => transition(runId, (chain) => markCardPresentationTerminal(chain, terminalReceiptId)).pipe(Effect.asVoid),
213
+ release: (runId) => repository.transact((current) => {
214
+ const next = current.filter((chain) => chain.runId !== runId);
215
+ return next.length === current.length ? { result: void 0 } : {
216
+ next,
217
+ result: void 0
218
+ };
219
+ }),
220
+ size: () => repository.snapshot().length
681
221
  };
682
222
  }
683
- function projectText(text, baseline) {
684
- if (text === void 0 || baseline === void 0 || baseline.length === 0) return text;
685
- if (text === baseline) return "";
686
- return text.startsWith(baseline) ? text.slice(baseline.length) : text;
223
+ function findChain(chains, runId) {
224
+ return chains.find((chain) => chain.runId === runId);
687
225
  }
688
- function projectPresentation(presentation, baseline) {
689
- if (!presentation || !baseline) return presentation;
690
- const previous = new Map(baseline.presentation?.steps.map((step) => [step.id, JSON.stringify(step)]) ?? []);
691
- let skippedCommittedBaselineText = false;
692
- const steps = presentation.steps.filter((step) => {
693
- if (!skippedCommittedBaselineText && step.kind === "assistant" && baseline.text?.trim() && step.text.trim() === baseline.text.trim() && !previous.has(step.id)) {
694
- skippedCommittedBaselineText = true;
695
- return false;
696
- }
697
- return previous.get(step.id) !== JSON.stringify(step);
698
- });
699
- const answer = projectText(presentation.answer.text, baseline.text) ?? "";
700
- if (steps.length === 0 && answer.trim() === "") return void 0;
701
- return {
702
- ...presentation,
703
- answer: {
704
- ...presentation.answer,
705
- text: answer
706
- },
707
- omittedStepCount: 0,
708
- steps,
709
- totalStepCount: steps.length,
710
- totalToolCallCount: steps.filter((step) => step.kind === "tool").length
711
- };
712
- }
713
- function projectTerminalAction(action, baseline) {
714
- if (!baseline || action.type !== "finish" && action.type !== "fail" && action.type !== "cancel") return action;
715
- const presentation = action.presentation ? projectPresentation(action.presentation, baseline) : void 0;
716
- if (action.type === "finish") return {
717
- ...presentation === void 0 ? {} : { presentation },
718
- runId: action.runId,
719
- text: projectText(action.text, baseline.text) ?? action.text,
720
- type: "finish"
721
- };
722
- if (action.type === "cancel") return {
723
- ...presentation === void 0 ? {} : { presentation },
724
- ...action.reason === void 0 ? {} : { reason: action.reason },
725
- runId: action.runId,
726
- ...action.text === void 0 ? {} : { text: projectText(action.text, baseline.text) ?? action.text },
727
- type: "cancel"
226
+ function replaceChain(chains, replacement) {
227
+ const index = chains.findIndex((chain) => chain.runId === replacement.runId);
228
+ if (index === -1) return [...chains, replacement];
229
+ return chains.map((chain, current) => current === index ? replacement : chain);
230
+ }
231
+ //#endregion
232
+ //#region src/modules/card-presentation/domain/delivery/card-delivery.ts
233
+ var CardDeliveryTransitionDenied = class extends Error {
234
+ name = "CardDeliveryTransitionDenied";
235
+ };
236
+ function reserveCardDeliverySequence(current, runId) {
237
+ const record = current ?? initialCardDeliveryRecord(runId);
238
+ assertCardDeliveryIdentity(record, runId);
239
+ if (record.terminalPublished) throw new CardDeliveryTransitionDenied(`card delivery for run ${runId} already has a terminal receipt`);
240
+ const next = {
241
+ ...record,
242
+ revision: record.revision + 1,
243
+ sequence: record.sequence + 1
728
244
  };
729
245
  return {
730
- errorMessage: action.errorMessage,
731
- ...presentation === void 0 ? {} : { presentation },
732
- runId: action.runId,
733
- type: "fail"
246
+ record: next,
247
+ sequence: next.sequence
734
248
  };
735
249
  }
736
- function createCounters() {
250
+ function markCardDeliveryTerminal(current, runId) {
251
+ const record = current ?? initialCardDeliveryRecord(runId);
252
+ assertCardDeliveryIdentity(record, runId);
253
+ return record.terminalPublished ? record : {
254
+ ...record,
255
+ revision: record.revision + 1,
256
+ terminalPublished: true
257
+ };
258
+ }
259
+ function validateCardDeliveryRecord(record) {
260
+ if (record.runId.trim().length === 0 || !Number.isSafeInteger(record.revision) || record.revision < 1 || !Number.isSafeInteger(record.sequence) || record.sequence < 0 || typeof record.terminalPublished !== "boolean") throw new CardDeliveryTransitionDenied("card delivery record is invalid");
261
+ return { ...record };
262
+ }
263
+ function validateCardDeliveryTransition(previous, next) {
264
+ validateCardDeliveryRecord(next);
265
+ if (previous === void 0) {
266
+ if (!(next.sequence === 1 && !next.terminalPublished || next.sequence === 0 && next.terminalPublished) || next.revision !== 1) throw new CardDeliveryTransitionDenied(`card delivery for run ${next.runId} has an invalid initial revision`);
267
+ return { ...next };
268
+ }
269
+ assertCardDeliveryIdentity(previous, next.runId);
270
+ if (next.revision !== previous.revision + 1) throw new CardDeliveryTransitionDenied(`card delivery for run ${next.runId} skipped a revision`);
271
+ if (previous.terminalPublished) throw new CardDeliveryTransitionDenied(`card delivery for run ${next.runId} changed after its terminal receipt`);
272
+ if (!(next.terminalPublished ? next.sequence === previous.sequence : next.sequence === previous.sequence + 1)) throw new CardDeliveryTransitionDenied(`card delivery for run ${next.runId} has an invalid sequence transition`);
273
+ return { ...next };
274
+ }
275
+ function initialCardDeliveryRecord(runId) {
276
+ if (runId.trim().length === 0) throw new CardDeliveryTransitionDenied("card delivery run id must not be empty");
737
277
  return {
738
- handoff_failed: 0,
739
- handoff_notice_failed: 0,
740
- handoff_started: 0,
741
- handoff_succeeded: 0,
742
- presentation_write_failed: 0,
743
- stale_update_dropped: 0,
744
- stream_closed: 0,
745
- terminal_delivery_failed: 0
278
+ revision: 0,
279
+ runId,
280
+ sequence: 0,
281
+ terminalPublished: false
746
282
  };
747
283
  }
748
- //#endregion
749
- //#region src/application/background-session/background-session-config.ts
750
- const DEFAULT_BACKGROUND_SESSION_STEP_TIMEOUT_MS = 300 * 1e3;
751
- const DEFAULT_BACKGROUND_SESSION_MAX_CONCURRENT_SESSIONS = 4;
752
- const DEFAULT_BACKGROUND_SESSION_LEASE_MS = 3e4;
753
- const DEFAULT_BACKGROUND_SESSION_LEASE_RENEWAL_INTERVAL_MS = 1e4;
754
- const DEFAULT_BACKGROUND_SESSION_MAX_CONSECUTIVE_FAILURES = 3;
755
- const DEFAULT_BACKGROUND_SESSION_RETRY_BACKOFF_MS = 3e4;
756
- const DEFAULT_BACKGROUND_SESSION_LIFETIME_MS = 1440 * 60 * 1e3;
757
- const DEFAULT_BACKGROUND_SESSION_SUPERVISOR_INTERVAL_MS = 1e3;
758
- function resolveBackgroundSessionSupervisorIntervalMs(leaseMs) {
759
- return Math.min(5e3, Math.max(200, Math.floor(leaseMs / 10)));
284
+ function assertCardDeliveryIdentity(record, runId) {
285
+ if (record.runId !== runId) throw new CardDeliveryTransitionDenied(`card delivery identity ${record.runId} does not match requested run ${runId}`);
760
286
  }
761
287
  //#endregion
762
- //#region src/domain/conversation-progress.ts
763
- const DEFAULT_CONVERSATION_PROGRESS_DISPLAY = "collapsed";
288
+ //#region src/modules/card-presentation/domain/presentation/card-presentation-lease.ts
289
+ const DEFAULT_CARD_STREAM_LEASE_MS$1 = 51e4;
290
+ function validateCardPresentationLeaseMs(leaseMs) {
291
+ if (!Number.isSafeInteger(leaseMs) || leaseMs < 1) throw new CardPresentationTransitionDenied("card presentation lease must be a positive integer");
292
+ return leaseMs;
293
+ }
294
+ function cardPresentationLeaseDeadline(at, leaseMs) {
295
+ const atMs = Date.parse(at);
296
+ if (Number.isNaN(atMs)) throw new CardPresentationTransitionDenied("card presentation lease start must be an ISO timestamp");
297
+ return new Date(atMs + validateCardPresentationLeaseMs(leaseMs)).toISOString();
298
+ }
764
299
  //#endregion
765
- //#region src/infrastructure/config/rivus-deployment-manifest.ts
766
- var RivusDeploymentManifestError = class extends Error {
767
- manifestPath;
768
- name = "RivusDeploymentManifestError";
769
- constructor(manifestPath, message, options) {
770
- super(message, options);
771
- this.manifestPath = manifestPath;
300
+ //#region src/platform/runtime/runtime-cache.ts
301
+ function createRuntimeCache() {
302
+ const entries = /* @__PURE__ */ new Map();
303
+ const serial = Effect.unsafeMakeSemaphore(1);
304
+ const reserve = (key) => serial.withPermits(1)(Effect.gen(function* () {
305
+ const current = entries.get(key);
306
+ if (current) return {
307
+ created: false,
308
+ entry: current
309
+ };
310
+ const entry = {
311
+ deferred: yield* Deferred.make(),
312
+ initializationStarted: false,
313
+ key
314
+ };
315
+ entries.set(key, entry);
316
+ return {
317
+ created: true,
318
+ entry
319
+ };
320
+ }));
321
+ const start = (entry, create) => Effect.uninterruptible(Effect.gen(function* () {
322
+ if (!(yield* serial.withPermits(1)(Effect.sync(() => {
323
+ if (entry.initializationStarted) return false;
324
+ entry.initializationStarted = true;
325
+ return true;
326
+ })))) return;
327
+ const initialization = create().pipe(Effect.tapError(() => serial.withPermits(1)(Effect.sync(() => {
328
+ if (entries.get(entry.key) === entry) entries.delete(entry.key);
329
+ }))), Effect.exit, Effect.flatMap((exit) => Deferred.done(entry.deferred, exit)), Effect.asVoid);
330
+ yield* Effect.forkDaemon(initialization);
331
+ }));
332
+ const getOrCreate = (key, create) => Effect.uninterruptibleMask((restore) => Effect.gen(function* () {
333
+ const selected = yield* reserve(key);
334
+ yield* start(selected.entry, create);
335
+ return {
336
+ entry: selected.entry,
337
+ runtime: yield* restore(Deferred.await(selected.entry.deferred))
338
+ };
339
+ }));
340
+ return {
341
+ drain: () => serial.withPermits(1)(Effect.sync(() => {
342
+ const drained = Object.freeze([...entries.values()]);
343
+ entries.clear();
344
+ return drained;
345
+ })),
346
+ getExisting: (key) => Effect.gen(function* () {
347
+ const entry = yield* serial.withPermits(1)(Effect.sync(() => entries.get(key)));
348
+ if (!entry) return void 0;
349
+ return {
350
+ entry,
351
+ runtime: yield* Deferred.await(entry.deferred)
352
+ };
353
+ }),
354
+ getOrCreate,
355
+ isCurrent: (key, entry) => serial.withPermits(1)(Effect.sync(() => entries.get(key) === entry)),
356
+ reserve,
357
+ size: () => serial.withPermits(1)(Effect.sync(() => entries.size)),
358
+ start
359
+ };
360
+ }
361
+ function disposeRuntimeCacheEntries(options) {
362
+ const disposal = Effect.gen(function* () {
363
+ const completions = yield* Effect.forEach(options.entries, (entry) => Effect.gen(function* () {
364
+ const completion = yield* Deferred.make();
365
+ yield* Effect.forkDaemon(Deferred.await(entry.deferred).pipe(Effect.flatMap(options.dispose), Effect.exit, Effect.flatMap((exit) => Deferred.succeed(completion, exit)), Effect.asVoid));
366
+ return completion;
367
+ }), { concurrency: "unbounded" });
368
+ const failures = (yield* Effect.forEach(completions, Deferred.await, { concurrency: "unbounded" })).filter(Exit.isFailure).map(({ cause }) => Cause.squash(cause));
369
+ if (failures.length > 0) return yield* Effect.fail(new AggregateError(failures, options.failureMessage));
370
+ });
371
+ return options.timeout ? disposal.pipe(Effect.timeoutFail({
372
+ duration: options.timeout.milliseconds,
373
+ onTimeout: options.timeout.onTimeout
374
+ })) : disposal;
375
+ }
376
+ function invokeRuntimeControl(runtime, control) {
377
+ return runtime.pipe(Effect.flatMap((selected) => selected ? control(selected) ?? Effect.succeed(false) : Effect.succeed(false)));
378
+ }
379
+ //#endregion
380
+ //#region src/adapters/agent/runtime/process-agent-runtime-adapter.ts
381
+ function toEffectAgentRuntimeInput(input) {
382
+ const onUpdate = input.onUpdate;
383
+ return {
384
+ ...runtimeInputFields(input),
385
+ ...onUpdate ? { onUpdate: (update) => Effect.tryPromise({
386
+ try: async () => onUpdate(update),
387
+ catch: (failure) => failure
388
+ }) } : {}
389
+ };
390
+ }
391
+ function toProcessAgentRuntimeInput(input, runEffect) {
392
+ const onUpdate = input.onUpdate;
393
+ return {
394
+ ...runtimeInputFields(input),
395
+ ...onUpdate ? { onUpdate: (update) => runEffect(onUpdate(update)) } : {}
396
+ };
397
+ }
398
+ function toEffectAgentRuntime(runtime, runEffect) {
399
+ const cancel = runtime.cancel?.bind(runtime);
400
+ const dispose = runtime.dispose?.bind(runtime);
401
+ const steer = runtime.steer?.bind(runtime);
402
+ return {
403
+ ...runtime.concurrency ? { concurrency: runtime.concurrency } : {},
404
+ ...cancel ? { cancel: (input) => Effect.tryPromise({
405
+ try: () => cancel(input),
406
+ catch: (failure) => failure
407
+ }) } : {},
408
+ ...dispose ? { dispose: () => Effect.tryPromise({
409
+ try: async () => dispose(),
410
+ catch: (failure) => failure
411
+ }) } : {},
412
+ run: (input) => Effect.tryPromise({
413
+ try: () => runtime.run(toProcessAgentRuntimeInput(input, runEffect)),
414
+ catch: (failure) => failure
415
+ }),
416
+ ...steer ? { steer: (input) => Effect.tryPromise({
417
+ try: () => steer(input),
418
+ catch: (failure) => failure
419
+ }) } : {}
420
+ };
421
+ }
422
+ function runtimeInputFields(input) {
423
+ return {
424
+ ...input.invocation ? { invocation: input.invocation } : {},
425
+ ...input.payload === void 0 ? {} : { payload: input.payload },
426
+ sessionKey: input.sessionKey,
427
+ text: input.text
428
+ };
429
+ }
430
+ //#endregion
431
+ //#region src/platform/home/config/runtime/local-env-file.ts
432
+ var LocalEnvFileError = class extends Error {
433
+ constructor(message) {
434
+ super(message);
435
+ this.name = "LocalEnvFileError";
772
436
  }
773
437
  };
774
- async function loadRivusDeploymentManifest(manifestPath, options = {}) {
775
- const maxBytes = options.maxBytes ?? 1024 * 1024;
776
- try {
777
- const metadata = await stat(manifestPath);
778
- if (!metadata.isFile()) throw new Error("deployment manifest must be a regular file");
779
- if (metadata.size > maxBytes) throw new Error(`deployment manifest exceeds ${maxBytes} byte limit`);
780
- const bytes = await readFile(manifestPath);
781
- const text = new TextDecoder("utf-8", { fatal: true }).decode(bytes);
782
- return parseManifest(JSON.parse(text));
783
- } catch (cause) {
784
- if (cause instanceof RivusDeploymentManifestError) throw cause;
785
- throw new RivusDeploymentManifestError(manifestPath, `failed to load Rivus deployment manifest: ${cause instanceof Error ? cause.message : String(cause)}`, { cause });
438
+ async function loadMergedLocalEnvFile(filePath, overrideEnv) {
439
+ return mergeRivusDaemonEnv(await loadLocalEnvFile(filePath), overrideEnv);
440
+ }
441
+ async function loadLocalEnvFile(filePath) {
442
+ return parseLocalEnvFile(await readFile(filePath, "utf8"));
443
+ }
444
+ function parseLocalEnvFile(contents) {
445
+ const env = {};
446
+ const lines = contents.split(/\r?\n/);
447
+ for (let index = 0; index < lines.length; index += 1) {
448
+ const trimmed = lines[index].trim();
449
+ if (!trimmed || trimmed.startsWith("#")) continue;
450
+ const match = (trimmed.startsWith("export ") ? trimmed.slice(7).trimStart() : trimmed).match(/^([A-Za-z_][A-Za-z0-9_]*)=(.*)$/);
451
+ if (!match) throw new LocalEnvFileError(`Invalid env file line ${index + 1}`);
452
+ const key = match[1];
453
+ const rawValue = match[2];
454
+ env[key] = parseEnvValue(rawValue, index + 1);
455
+ }
456
+ return env;
457
+ }
458
+ function mergeRivusDaemonEnv(fileEnv, overrideEnv) {
459
+ const merged = { ...fileEnv };
460
+ for (const [key, value] of Object.entries(overrideEnv)) if (value !== void 0) merged[key] = value;
461
+ return merged;
462
+ }
463
+ function parseEnvValue(rawValue, lineNumber) {
464
+ const value = rawValue.trim();
465
+ if (!value) return "";
466
+ if (value.startsWith("'")) {
467
+ if (!value.endsWith("'")) throw new LocalEnvFileError(`Invalid single-quoted env value on line ${lineNumber}`);
468
+ return value.slice(1, -1).replaceAll("'\\''", "'");
469
+ }
470
+ if (value.startsWith("\"")) {
471
+ if (!value.endsWith("\"")) throw new LocalEnvFileError(`Invalid double-quoted env value on line ${lineNumber}`);
472
+ return unescapeDoubleQuotedValue(value.slice(1, -1));
473
+ }
474
+ return value;
475
+ }
476
+ function unescapeDoubleQuotedValue(value) {
477
+ return value.replace(/\\(["\\nrt])/g, (_match, escaped) => {
478
+ switch (escaped) {
479
+ case "n": return "\n";
480
+ case "r": return "\r";
481
+ case "t": return " ";
482
+ default: return escaped;
483
+ }
484
+ });
485
+ }
486
+ //#endregion
487
+ //#region src/platform/home/config/runtime/rivus-daemon-config.ts
488
+ var RivusDaemonConfigError = class {
489
+ variable;
490
+ message;
491
+ _tag = "RivusDaemonConfigError";
492
+ constructor(variable, message) {
493
+ this.variable = variable;
494
+ this.message = message;
495
+ }
496
+ };
497
+ const DEFAULT_AGENT_ID$1 = "main";
498
+ const DEFAULT_CARD_STREAM_LEASE_MS = 51e4;
499
+ const DEFAULT_FEISHU_BASE_URL$1 = "https://open.feishu.cn";
500
+ const DEFAULT_STREAM_MIN_INTERVAL_MS$1 = 200;
501
+ const THINKING_LEVELS$1 = /* @__PURE__ */ new Set([
502
+ "off",
503
+ "minimal",
504
+ "low",
505
+ "medium",
506
+ "high",
507
+ "xhigh"
508
+ ]);
509
+ function loadRivusDaemonConfig(env, options = {}) {
510
+ return Effect.gen(function* () {
511
+ const appId = yield* required$1(env, "FEISHU_APP_ID");
512
+ const appSecret = yield* required$1(env, "FEISHU_APP_SECRET");
513
+ const streamMinIntervalMs = yield* optionalPositiveInteger(env.FEISHU_STREAM_MIN_INTERVAL_MS, "FEISHU_STREAM_MIN_INTERVAL_MS", DEFAULT_STREAM_MIN_INTERVAL_MS$1);
514
+ const cardStreamLeaseMs = yield* optionalPositiveInteger(env.FEISHU_CARD_STREAM_LEASE_MS, "FEISHU_CARD_STREAM_LEASE_MS", DEFAULT_CARD_STREAM_LEASE_MS);
515
+ const thinkingLevel = yield* optionalThinkingLevel(env.PI_THINKING_LEVEL);
516
+ const apiKey = yield* optionalPiApiKey(env, options.readTextFile ?? readUtf8File);
517
+ const baseUrl = optional(env.PI_BASE_URL);
518
+ const model = optional(env.PI_MODEL);
519
+ return {
520
+ agentId: optional(env.RIVUS_AGENT_ID) ?? DEFAULT_AGENT_ID$1,
521
+ feishu: {
522
+ appId,
523
+ appSecret,
524
+ baseUrl: optional(env.FEISHU_BASE_URL) ?? DEFAULT_FEISHU_BASE_URL$1,
525
+ cardStreamLeaseMs,
526
+ streamMinIntervalMs
527
+ },
528
+ pi: {
529
+ ...apiKey ? { apiKey } : {},
530
+ ...baseUrl ? { baseUrl } : {},
531
+ ...model ? { model } : {},
532
+ ...thinkingLevel ? { thinkingLevel } : {}
533
+ }
534
+ };
535
+ });
536
+ }
537
+ function optional(value) {
538
+ const trimmed = value?.trim();
539
+ return trimmed ? trimmed : void 0;
540
+ }
541
+ function readUtf8File(path) {
542
+ return readFile(path, "utf8");
543
+ }
544
+ function optionalPiApiKey(env, readTextFile) {
545
+ const inlineApiKey = optional(env.PI_API_KEY);
546
+ const apiKeyFile = optional(env.PI_API_KEY_FILE);
547
+ if (inlineApiKey && apiKeyFile) return Effect.fail(new RivusDaemonConfigError("PI_API_KEY", "PI_API_KEY and PI_API_KEY_FILE cannot both be set"));
548
+ if (inlineApiKey) return Effect.succeed(inlineApiKey);
549
+ if (!apiKeyFile) return Effect.succeed(void 0);
550
+ return Effect.tryPromise({
551
+ try: async () => readTextFile(apiKeyFile),
552
+ catch: (error) => new RivusDaemonConfigError("PI_API_KEY_FILE", `PI_API_KEY_FILE could not be read: ${formatConfigError(error)}`)
553
+ }).pipe(Effect.flatMap((contents) => {
554
+ const apiKey = optional(contents);
555
+ return apiKey ? Effect.succeed(apiKey) : Effect.fail(new RivusDaemonConfigError("PI_API_KEY_FILE", "PI_API_KEY_FILE must not be empty"));
556
+ }));
557
+ }
558
+ function formatConfigError(error) {
559
+ return error instanceof Error ? error.message : String(error);
560
+ }
561
+ function optionalPositiveInteger(value, variable, fallback) {
562
+ const normalized = optional(value);
563
+ if (!normalized) return Effect.succeed(fallback);
564
+ if (/^[1-9]\d*$/.test(normalized)) return Effect.succeed(Number(normalized));
565
+ return Effect.fail(new RivusDaemonConfigError(variable, `${variable} must be a positive integer`));
566
+ }
567
+ function required$1(env, variable) {
568
+ const value = optional(env[variable]);
569
+ if (value) return Effect.succeed(value);
570
+ return Effect.fail(new RivusDaemonConfigError(variable, `${variable} is required`));
571
+ }
572
+ function optionalThinkingLevel(value) {
573
+ const normalized = optional(value);
574
+ if (!normalized) return Effect.succeed(void 0);
575
+ if (THINKING_LEVELS$1.has(normalized)) return Effect.succeed(normalized);
576
+ return Effect.fail(new RivusDaemonConfigError("PI_THINKING_LEVEL", "PI_THINKING_LEVEL must be one of off, minimal, low, medium, high, xhigh"));
577
+ }
578
+ //#endregion
579
+ //#region src/adapters/feishu/config/feishu-endpoint-credentials.ts
580
+ var FeishuEndpointCredentialError = class extends Error {
581
+ name = "FeishuEndpointCredentialError";
582
+ };
583
+ function resolveFeishuEndpointCredentials(credentialRef, env) {
584
+ if (!credentialRef.startsWith("env:")) throw new FeishuEndpointCredentialError("Feishu endpoint credentialRef must use env:<PREFIX>");
585
+ const prefix = credentialRef.slice(4);
586
+ if (!/^[A-Z][A-Z0-9_]*$/.test(prefix)) throw new FeishuEndpointCredentialError(`Invalid environment prefix in credentialRef: ${credentialRef}`);
587
+ return Object.freeze({
588
+ appId: required(env, `${prefix}_APP_ID`),
589
+ appSecret: required(env, `${prefix}_APP_SECRET`)
590
+ });
591
+ }
592
+ function required(env, variable) {
593
+ const value = env[variable]?.trim();
594
+ if (!value) throw new FeishuEndpointCredentialError(`${variable} is required`);
595
+ return value;
596
+ }
597
+ //#endregion
598
+ //#region src/adapters/openclaw/config/openclaw-env-import.ts
599
+ var OpenClawEnvImportError = class extends Error {
600
+ constructor(message) {
601
+ super(message);
602
+ this.name = "OpenClawEnvImportError";
603
+ }
604
+ };
605
+ const DEFAULT_AGENT_ID = "main";
606
+ const DEFAULT_FEISHU_BASE_URL = "https://open.feishu.cn";
607
+ const DEFAULT_LARK_BASE_URL = "https://open.larksuite.com";
608
+ const DEFAULT_STREAM_MIN_INTERVAL_MS = 200;
609
+ const THINKING_LEVELS = /* @__PURE__ */ new Set([
610
+ "off",
611
+ "minimal",
612
+ "low",
613
+ "medium",
614
+ "high",
615
+ "xhigh"
616
+ ]);
617
+ const ENV_FILE_ORDER = [
618
+ "FEISHU_APP_ID",
619
+ "FEISHU_APP_SECRET",
620
+ "FEISHU_BASE_URL",
621
+ "FEISHU_STREAM_MIN_INTERVAL_MS",
622
+ "RIVUS_AGENT_ID",
623
+ "PI_API_KEY_FILE",
624
+ "PI_BASE_URL",
625
+ "PI_MODEL",
626
+ "PI_THINKING_LEVEL"
627
+ ];
628
+ function createRivusEnvFromOpenClawConfig(openClawConfig, options = {}) {
629
+ const config = asRecord(openClawConfig, "OpenClaw config");
630
+ const feishu = asRecord(readPath(config, ["channels", "feishu"]), "channels.feishu");
631
+ const appId = requiredString(feishu, "appId", "channels.feishu.appId");
632
+ const appSecret = requiredString(feishu, "appSecret", "channels.feishu.appSecret");
633
+ const modelReference = findPrimaryModelReference(config);
634
+ const providerId = modelReference ? parseProviderId(modelReference) : void 0;
635
+ const provider = providerId ? optionalRecord(readPath(config, [
636
+ "models",
637
+ "providers",
638
+ providerId
639
+ ])) : void 0;
640
+ const providerBaseUrl = provider ? optionalString(provider.baseUrl) : void 0;
641
+ const thinkingLevel = modelReference ? findThinkingLevel(config, modelReference) : void 0;
642
+ const warnings = thinkingLevel?.warning ? [thinkingLevel.warning] : [];
643
+ return {
644
+ env: {
645
+ FEISHU_APP_ID: appId,
646
+ FEISHU_APP_SECRET: appSecret,
647
+ FEISHU_BASE_URL: options.feishuBaseUrl ?? inferFeishuBaseUrl(optionalString(feishu.domain)),
648
+ FEISHU_STREAM_MIN_INTERVAL_MS: String(options.streamMinIntervalMs ?? DEFAULT_STREAM_MIN_INTERVAL_MS),
649
+ RIVUS_AGENT_ID: findAgentId(config),
650
+ ...options.piApiKeyFile ? { PI_API_KEY_FILE: options.piApiKeyFile } : {},
651
+ ...providerBaseUrl ? { PI_BASE_URL: providerBaseUrl } : {},
652
+ ...modelReference ? { PI_MODEL: modelReference } : {},
653
+ ...thinkingLevel?.level ? { PI_THINKING_LEVEL: thinkingLevel.level } : {}
654
+ },
655
+ warnings
656
+ };
657
+ }
658
+ function formatRivusEnvFile(env) {
659
+ 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`;
660
+ }
661
+ function findAgentId(config) {
662
+ const agents = optionalRecord(config.agents);
663
+ return optionalString(optionalRecord((Array.isArray(agents?.list) ? agents.list : [])[0])?.id) ?? DEFAULT_AGENT_ID;
664
+ }
665
+ function findPrimaryModelReference(config) {
666
+ const primary = optionalString(readPath(config, [
667
+ "agents",
668
+ "defaults",
669
+ "model",
670
+ "primary"
671
+ ]));
672
+ if (primary) return primary;
673
+ const providers = optionalRecord(readPath(config, ["models", "providers"]));
674
+ if (!providers) return;
675
+ for (const [providerId, provider] of Object.entries(providers)) {
676
+ const model = firstModelId(optionalRecord(provider));
677
+ if (model) return `${providerId}/${model}`;
786
678
  }
787
679
  }
788
- function parseManifest(value) {
680
+ function firstModelId(provider) {
681
+ return optionalString(optionalRecord((Array.isArray(provider?.models) ? provider.models : [])[0])?.id);
682
+ }
683
+ function parseProviderId(modelReference) {
684
+ const separator = modelReference.indexOf("/");
685
+ return separator > 0 ? modelReference.slice(0, separator) : void 0;
686
+ }
687
+ function findThinkingLevel(config, modelReference) {
688
+ const providerId = parseProviderId(modelReference);
689
+ const modelId = readModelId(modelReference);
690
+ const rawLevel = [
691
+ readPath(config, [
692
+ "agents",
693
+ "defaults",
694
+ "models",
695
+ modelReference,
696
+ "thinkingLevel"
697
+ ]),
698
+ readPath(config, [
699
+ "agents",
700
+ "defaults",
701
+ "models",
702
+ modelReference,
703
+ "thinkLevel"
704
+ ]),
705
+ readPath(config, [
706
+ "agents",
707
+ "defaults",
708
+ "models",
709
+ modelReference,
710
+ "reasoningLevel"
711
+ ]),
712
+ ...providerId && modelId ? readProviderModelThinkingCandidates(config, providerId, modelId) : []
713
+ ].map(optionalString).find(Boolean);
714
+ if (!rawLevel) return;
715
+ if (THINKING_LEVELS.has(rawLevel)) return { level: rawLevel };
716
+ return { warning: `Unsupported OpenClaw thinking level '${rawLevel}' was ignored` };
717
+ }
718
+ function readProviderModelThinkingCandidates(config, providerId, modelId) {
719
+ const providerModels = readPath(config, [
720
+ "models",
721
+ "providers",
722
+ providerId,
723
+ "models"
724
+ ]);
725
+ if (!Array.isArray(providerModels)) return [];
726
+ const model = providerModels.map(optionalRecord).find((candidate) => optionalString(candidate?.id) === modelId);
727
+ if (!model) return [];
728
+ return [
729
+ model.thinkingLevel,
730
+ model.thinkLevel,
731
+ model.reasoningLevel,
732
+ readPath(model, ["reasoning", "level"]),
733
+ readPath(model, ["reasoning", "thinkingLevel"]),
734
+ readPath(model, ["reasoning", "thinkLevel"])
735
+ ];
736
+ }
737
+ function readModelId(modelReference) {
738
+ const separator = modelReference.indexOf("/");
739
+ return separator >= 0 && separator < modelReference.length - 1 ? modelReference.slice(separator + 1) : void 0;
740
+ }
741
+ function inferFeishuBaseUrl(domain) {
742
+ return domain === "lark" ? DEFAULT_LARK_BASE_URL : DEFAULT_FEISHU_BASE_URL;
743
+ }
744
+ function quoteEnvValue(value) {
745
+ return `'${value.replaceAll("'", "'\\''")}'`;
746
+ }
747
+ function requiredString(record, key, path) {
748
+ const value = optionalString(record[key]);
749
+ if (!value) throw new OpenClawEnvImportError(`${path} is required`);
750
+ return value;
751
+ }
752
+ function optionalString(value) {
753
+ return typeof value === "string" && value.trim() ? value.trim() : void 0;
754
+ }
755
+ function asRecord(value, path) {
756
+ const record = optionalRecord(value);
757
+ if (!record) throw new OpenClawEnvImportError(`${path} must be an object`);
758
+ return record;
759
+ }
760
+ function optionalRecord(value) {
761
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
762
+ }
763
+ function readPath(record, path) {
764
+ let value = record;
765
+ for (const segment of path) {
766
+ const current = optionalRecord(value);
767
+ if (!current) return;
768
+ value = current[segment];
769
+ }
770
+ return value;
771
+ }
772
+ //#endregion
773
+ //#region src/platform/daemon/process/rivus-daemon-shutdown-controller.ts
774
+ const DEFAULT_SIGNALS = ["SIGINT", "SIGTERM"];
775
+ function createRivusDaemonShutdownController(options) {
776
+ let shutdown;
777
+ const handle = (signal) => {
778
+ shutdown ??= Effect.runPromise(options.daemon.stop()).then(() => options.onStopped?.(signal)).catch(async (error) => {
779
+ await options.onError?.(error, signal);
780
+ throw error;
781
+ });
782
+ return shutdown;
783
+ };
784
+ return {
785
+ handle,
786
+ install: () => {
787
+ for (const signal of options.signals ?? DEFAULT_SIGNALS) options.signalSource.on(signal, () => {
788
+ handle(signal);
789
+ });
790
+ },
791
+ stopping: () => shutdown !== void 0
792
+ };
793
+ }
794
+ //#endregion
795
+ //#region src/modules/project-space/application/workspace/workspace-instructions.ts
796
+ var InvalidWorkspaceRoot = class extends Error {
797
+ name = "InvalidWorkspaceRoot";
798
+ };
799
+ var WorkspaceInstructionsSourceError = class extends Error {
800
+ relativePath;
801
+ reason;
802
+ name = "WorkspaceInstructionsSourceError";
803
+ constructor(relativePath, reason) {
804
+ super(`${relativePath}: ${reason}`);
805
+ this.relativePath = relativePath;
806
+ this.reason = reason;
807
+ }
808
+ };
809
+ function validateWorkspaceInstructionsBudget(maxBytes) {
810
+ if (!Number.isSafeInteger(maxBytes) || maxBytes < 0) throw new InvalidWorkspaceRoot("workspace instructions maxBytes must be a non-negative integer");
811
+ }
812
+ function assembleWorkspaceInstructions(input) {
813
+ validateWorkspaceInstructionsBudget(input.maxBytes);
814
+ const diagnostics = [...input.diagnostics];
815
+ const selected = [];
816
+ let usedBytes = 0;
817
+ for (const source of [...input.sources].reverse()) if (usedBytes + source.bytes <= input.maxBytes) {
818
+ selected.push(source);
819
+ usedBytes += source.bytes;
820
+ } else diagnostics.push({
821
+ code: "workspace-instructions-source-omitted",
822
+ reason: `source exceeds remaining byte budget of ${input.maxBytes - usedBytes}`,
823
+ relativePath: source.relativePath
824
+ });
825
+ selected.reverse();
826
+ const presented = new Set(input.presentedDigests ?? []);
827
+ const unseen = selected.filter((source) => !presented.has(source.digest));
828
+ const refreshRequired = input.operation === "mutate" && input.targetPath !== void 0 && unseen.length > 0;
829
+ if (refreshRequired) diagnostics.push({
830
+ code: "workspace-instructions-refresh-required",
831
+ reason: `mutation target has ${unseen.length} workspace instruction source(s) not yet presented`
832
+ });
833
+ const view = {
834
+ content: selected.map((source) => source.content).join("\n"),
835
+ diagnostics: Object.freeze(diagnostics),
836
+ digest: createSha256Digest(JSON.stringify(selected.map(({ digest }) => digest))),
837
+ refreshRequired,
838
+ sources: Object.freeze(selected),
839
+ truncated: selected.length !== input.sources.length || input.diagnostics.length > 0
840
+ };
841
+ return Object.freeze(view);
842
+ }
843
+ //#endregion
844
+ //#region src/platform/identity/stable-id.ts
845
+ function createStableId(prefix, value) {
846
+ return `${prefix}:${createHash("sha256").update(JSON.stringify(value)).digest("hex")}`;
847
+ }
848
+ //#endregion
849
+ //#region src/modules/project-space/application/space/project-space-declaration.ts
850
+ function createRivusProjectSpaceDeclarationRevision(declaration) {
851
+ return createStableId("project-space-declaration", {
852
+ id: declaration.id,
853
+ root: declaration.root,
854
+ skills: { sources: declaration.skills.sources },
855
+ workingDirectory: declaration.workingDirectory
856
+ });
857
+ }
858
+ //#endregion
859
+ //#region src/modules/project-space/domain/space/project-space.ts
860
+ var InvalidRivusProjectSpace = class extends Error {
861
+ name = "InvalidRivusProjectSpace";
862
+ };
863
+ function validateRivusProjectSpaceDeployments(declarations) {
864
+ const projectSpaceIds = /* @__PURE__ */ new Set();
865
+ for (const declaration of declarations) {
866
+ if (projectSpaceIds.has(declaration.id)) throw new InvalidRivusProjectSpace(`duplicate project space: ${declaration.id}`);
867
+ projectSpaceIds.add(declaration.id);
868
+ validateRivusProjectSpaceDeployment(declaration);
869
+ }
870
+ return projectSpaceIds;
871
+ }
872
+ function validateRivusProjectSpaceDeployment(declaration) {
873
+ validateRelativeProjectPath(declaration.root, `project space ${declaration.id} root`);
874
+ validateRelativeProjectPath(declaration.workingDirectory, `project space ${declaration.id} working directory`);
875
+ const sources = /* @__PURE__ */ new Set();
876
+ for (const source of declaration.skills.sources) {
877
+ validateRelativeProjectPath(source, `project space ${declaration.id} Skill source`);
878
+ if (sources.has(source)) throw new InvalidRivusProjectSpace(`project space ${declaration.id} contains duplicate Skill source: ${source}`);
879
+ sources.add(source);
880
+ }
881
+ }
882
+ function validateRelativeProjectPath(value, owner) {
883
+ if (value.trim() === "" || isAbsoluteProjectPath(value) || value.includes("\0")) throw new InvalidRivusProjectSpace(`${owner} must be a non-empty relative path`);
884
+ }
885
+ function isAbsoluteProjectPath(value) {
886
+ return value.startsWith("/") || value.startsWith("\\") || /^[a-z]:[\\/]/i.test(value);
887
+ }
888
+ //#endregion
889
+ //#region src/modules/deployment-control/domain/manifest/deployment-manifest.ts
890
+ function parseRivusDeploymentManifest(value) {
789
891
  const root = record(value, "manifest");
790
892
  exactKeys(root, [
791
893
  "agents",
@@ -823,15 +925,22 @@ function parseManifest(value) {
823
925
  "pluginId",
824
926
  "profileId",
825
927
  "projectSpaceId",
928
+ "runtimeTools",
826
929
  "skills",
827
930
  "tools"
828
- ], `manifest.agents[${index}]`, ["memory", "projectSpaceId"]);
931
+ ], `manifest.agents[${index}]`, [
932
+ "memory",
933
+ "projectSpaceId",
934
+ "runtimeTools"
935
+ ]);
829
936
  const memory = agent.memory === void 0 ? void 0 : record(agent.memory, `manifest.agents[${index}].memory`);
830
937
  if (memory) exactKeys(memory, ["scopes", "tool"], `manifest.agents[${index}].memory`);
831
938
  const skills = record(agent.skills, `manifest.agents[${index}].skills`);
832
939
  exactKeys(skills, ["allow"], `manifest.agents[${index}].skills`);
833
940
  const tools = record(agent.tools, `manifest.agents[${index}].tools`);
834
941
  exactKeys(tools, ["allow"], `manifest.agents[${index}].tools`);
942
+ const runtimeTools = agent.runtimeTools === void 0 ? void 0 : record(agent.runtimeTools, `manifest.agents[${index}].runtimeTools`);
943
+ if (runtimeTools) exactKeys(runtimeTools, ["allow"], `manifest.agents[${index}].runtimeTools`);
835
944
  return Object.freeze({
836
945
  agentId: string(agent.agentId, `manifest.agents[${index}].agentId`),
837
946
  endpointIds: Object.freeze(array(agent.endpointIds, `manifest.agents[${index}].endpointIds`).map((item, itemIndex) => string(item, `manifest.agents[${index}].endpointIds[${itemIndex}]`))),
@@ -842,6 +951,7 @@ function parseManifest(value) {
842
951
  pluginId: string(agent.pluginId, `manifest.agents[${index}].pluginId`),
843
952
  profileId: string(agent.profileId, `manifest.agents[${index}].profileId`),
844
953
  ...agent.projectSpaceId === void 0 ? {} : { projectSpaceId: string(agent.projectSpaceId, `manifest.agents[${index}].projectSpaceId`) },
954
+ ...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`)) }) } : {},
845
955
  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}]`))) }),
846
956
  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}]`))) })
847
957
  });
@@ -871,7 +981,7 @@ function parseManifest(value) {
871
981
  return Object.freeze({
872
982
  agentId: string(endpoint.agentId, `manifest.endpoints[${index}].agentId`),
873
983
  baseUrl: string(endpoint.baseUrl, `manifest.endpoints[${index}].baseUrl`),
874
- cardStreamLeaseMs: endpoint.cardStreamLeaseMs === void 0 ? DEFAULT_CARD_STREAM_LEASE_MS : positiveInteger(endpoint.cardStreamLeaseMs, `manifest.endpoints[${index}].cardStreamLeaseMs`),
984
+ cardStreamLeaseMs: endpoint.cardStreamLeaseMs === void 0 ? DEFAULT_CARD_STREAM_LEASE_MS$1 : positiveInteger(endpoint.cardStreamLeaseMs, `manifest.endpoints[${index}].cardStreamLeaseMs`),
875
985
  credentialRef: string(endpoint.credentialRef, `manifest.endpoints[${index}].credentialRef`),
876
986
  enabled: boolean(endpoint.enabled, `manifest.endpoints[${index}].enabled`),
877
987
  ...experimental ? { experimental: Object.freeze({ cotMessages: boolean(experimental.cotMessages, `manifest.endpoints[${index}].experimental.cotMessages`) }) } : {},
@@ -945,6 +1055,67 @@ function parseManifest(value) {
945
1055
  projectSpaces: Object.freeze(projectSpaces)
946
1056
  });
947
1057
  }
1058
+ function validateRivusDeploymentManifest(manifest) {
1059
+ const projectSpaceIds = validateRivusProjectSpaceDeployments(manifest.projectSpaces ?? []);
1060
+ const pluginIds = /* @__PURE__ */ new Set();
1061
+ for (const plugin of manifest.plugins) {
1062
+ if (pluginIds.has(plugin.id)) throw new Error(`duplicate plugin declaration: ${plugin.id}`);
1063
+ validateModuleSpecifier(plugin.module);
1064
+ pluginIds.add(plugin.id);
1065
+ }
1066
+ const agentIds = /* @__PURE__ */ new Set();
1067
+ const agentById = /* @__PURE__ */ new Map();
1068
+ for (const agent of manifest.agents) {
1069
+ if (agentIds.has(agent.agentId)) throw new Error(`duplicate agent deployment: ${agent.agentId}`);
1070
+ agentIds.add(agent.agentId);
1071
+ if (!pluginIds.has(agent.pluginId)) throw new Error(`agent ${agent.agentId} references undeclared plugin: ${agent.pluginId}`);
1072
+ if (agent.projectSpaceId && !projectSpaceIds.has(agent.projectSpaceId)) throw new Error(`agent ${agent.agentId} references unknown project space: ${agent.projectSpaceId}`);
1073
+ agentById.set(agent.agentId, agent);
1074
+ }
1075
+ const endpointIds = /* @__PURE__ */ new Set();
1076
+ const sessionNamespaces = /* @__PURE__ */ new Set();
1077
+ for (const endpoint of manifest.endpoints) {
1078
+ if (endpointIds.has(endpoint.id)) throw new Error(`duplicate endpoint binding: ${endpoint.id}`);
1079
+ endpointIds.add(endpoint.id);
1080
+ if (sessionNamespaces.has(endpoint.sessionNamespace)) throw new Error(`duplicate endpoint session namespace: ${endpoint.sessionNamespace}`);
1081
+ sessionNamespaces.add(endpoint.sessionNamespace);
1082
+ const agent = agentById.get(endpoint.agentId);
1083
+ if (!agent) throw new Error(`endpoint ${endpoint.id} references unknown agent: ${endpoint.agentId}`);
1084
+ if (!agent.endpointIds.includes(endpoint.id)) throw new Error(`endpoint ${endpoint.id} is not declared by agent ${endpoint.agentId}`);
1085
+ }
1086
+ for (const agent of manifest.agents) for (const endpointId of agent.endpointIds) {
1087
+ const endpoint = manifest.endpoints.find(({ id }) => id === endpointId);
1088
+ if (!endpoint) throw new Error(`agent ${agent.agentId} references unknown endpoint: ${endpointId}`);
1089
+ if (endpoint.agentId !== agent.agentId) throw new Error(`endpoint ${endpointId} is bound to a different agent`);
1090
+ }
1091
+ const automationIds = /* @__PURE__ */ new Set();
1092
+ for (const automation of manifest.automations ?? []) {
1093
+ if (automationIds.has(automation.id)) throw new Error(`duplicate automation binding: ${automation.id}`);
1094
+ automationIds.add(automation.id);
1095
+ if (!agentById.has(automation.agentId)) throw new Error(`automation ${automation.id} references unknown agent: ${automation.agentId}`);
1096
+ const endpoint = manifest.endpoints.find(({ id }) => id === automation.delivery.endpointId);
1097
+ if (!endpoint) throw new Error(`automation ${automation.id} references unknown delivery endpoint: ${automation.delivery.endpointId}`);
1098
+ if (automation.enabled && !endpoint.enabled) throw new Error(`automation ${automation.id} delivery endpoint must be enabled`);
1099
+ }
1100
+ const defaultAgent = agentById.get(manifest.defaultAgentId);
1101
+ if (!defaultAgent) throw new Error(`default agent does not exist: ${manifest.defaultAgentId}`);
1102
+ const defaultEndpoint = manifest.endpoints.find(({ id }) => id === manifest.defaultEndpointId);
1103
+ if (!defaultEndpoint) throw new Error(`default endpoint does not exist: ${manifest.defaultEndpointId}`);
1104
+ if (defaultEndpoint.agentId !== defaultAgent.agentId) throw new Error("default endpoint is not bound to the default agent");
1105
+ if (!defaultEndpoint.enabled) throw new Error("default endpoint must be enabled");
1106
+ if (manifest.backgroundSessions) validateBackgroundSessions(manifest.backgroundSessions);
1107
+ }
1108
+ function validateBackgroundSessions(config) {
1109
+ if (config.required && !config.enabled) throw new Error("backgroundSessions.required requires backgroundSessions.enabled");
1110
+ if (config.leaseRenewalIntervalMs >= config.leaseMs) throw new Error("backgroundSessions.leaseRenewalIntervalMs must be shorter than leaseMs");
1111
+ if (config.stepTimeoutMs <= 0 || config.maxConcurrentSessions <= 0 || config.leaseMs <= 0) throw new Error("backgroundSessions durations and concurrency must be positive");
1112
+ if (config.retryBackoffMs <= 0 || config.sessionLifetimeMs <= 0 || config.maxConsecutiveFailures <= 0) throw new Error("backgroundSessions retry and lifetime limits must be positive");
1113
+ }
1114
+ function validateModuleSpecifier(moduleSpecifier) {
1115
+ const absolutePath = moduleSpecifier.startsWith("/") || moduleSpecifier.startsWith("\\\\") || /^[a-zA-Z]:[\\/]/.test(moduleSpecifier);
1116
+ if (moduleSpecifier.trim() === "" || absolutePath || /^[a-z][a-z+.-]*:/i.test(moduleSpecifier) || moduleSpecifier.includes("\0")) throw new Error(`invalid plugin module specifier: ${moduleSpecifier}`);
1117
+ if ((moduleSpecifier.startsWith("./") || moduleSpecifier.startsWith("../")) && moduleSpecifier.split(/[\\/]/).includes("..")) throw new Error(`plugin module escapes deployment root: ${moduleSpecifier}`);
1118
+ }
948
1119
  function parseBackgroundSessions(value) {
949
1120
  exactKeys(value, [
950
1121
  "enabled",
@@ -967,14 +1138,14 @@ function parseBackgroundSessions(value) {
967
1138
  ]);
968
1139
  return Object.freeze({
969
1140
  enabled: boolean(value.enabled, "manifest.backgroundSessions.enabled"),
970
- required: boolean(value.required, "manifest.backgroundSessions.required"),
971
- stepTimeoutMs: positiveInteger(value.stepTimeoutMs ?? 3e5, "manifest.backgroundSessions.stepTimeoutMs"),
972
- maxConcurrentSessions: positiveInteger(value.maxConcurrentSessions ?? 4, "manifest.backgroundSessions.maxConcurrentSessions"),
973
1141
  leaseMs: positiveInteger(value.leaseMs ?? 3e4, "manifest.backgroundSessions.leaseMs"),
974
1142
  leaseRenewalIntervalMs: positiveInteger(value.leaseRenewalIntervalMs ?? 1e4, "manifest.backgroundSessions.leaseRenewalIntervalMs"),
975
1143
  maxConsecutiveFailures: positiveInteger(value.maxConsecutiveFailures ?? 3, "manifest.backgroundSessions.maxConsecutiveFailures"),
1144
+ maxConcurrentSessions: positiveInteger(value.maxConcurrentSessions ?? 4, "manifest.backgroundSessions.maxConcurrentSessions"),
1145
+ required: boolean(value.required, "manifest.backgroundSessions.required"),
976
1146
  retryBackoffMs: positiveInteger(value.retryBackoffMs ?? 3e4, "manifest.backgroundSessions.retryBackoffMs"),
977
- sessionLifetimeMs: positiveInteger(value.sessionLifetimeMs ?? 864e5, "manifest.backgroundSessions.sessionLifetimeMs")
1147
+ sessionLifetimeMs: positiveInteger(value.sessionLifetimeMs ?? 864e5, "manifest.backgroundSessions.sessionLifetimeMs"),
1148
+ stepTimeoutMs: positiveInteger(value.stepTimeoutMs ?? 3e5, "manifest.backgroundSessions.stepTimeoutMs")
978
1149
  });
979
1150
  }
980
1151
  function record(value, path) {
@@ -1009,6 +1180,19 @@ function progressDisplay(value, path) {
1009
1180
  if (value !== "hidden" && value !== "collapsed" && value !== "expanded") throw new Error(`${path} must be hidden, collapsed, or expanded`);
1010
1181
  return value;
1011
1182
  }
1183
+ function runtimeToolId(value, path) {
1184
+ const id = string(value, path);
1185
+ if (!isRivusRuntimeToolId(id)) throw new Error(`${path} must be read, bash, edit, write, grep, find, or ls`);
1186
+ return id;
1187
+ }
1188
+ function uniqueRuntimeTools(ids, path) {
1189
+ const result = /* @__PURE__ */ new Set();
1190
+ for (const id of ids) {
1191
+ if (result.has(id)) throw new Error(`${path} contains duplicate Runtime Tool: ${id}`);
1192
+ result.add(id);
1193
+ }
1194
+ return [...result];
1195
+ }
1012
1196
  function automationTargetType(value, path) {
1013
1197
  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`);
1014
1198
  return value;
@@ -1020,353 +1204,708 @@ function exactKeys(value, allowed, path, optional = []) {
1020
1204
  if (missing) throw new Error(`${path} is missing required field: ${missing}`);
1021
1205
  }
1022
1206
  //#endregion
1023
- //#region src/infrastructure/plugin/node-rivus-plugin-module-loader.ts
1024
- async function loadNodeRivusPluginModule(request) {
1025
- return await import(pathToFileURL(await resolveNodeRivusPluginModulePath(request)).href);
1026
- }
1027
- async function resolveNodeRivusPluginModulePath(request) {
1028
- const deploymentRoot = await realpath(request.deploymentRoot);
1029
- const resolvedRealpath = await realpath(createRequire(join(deploymentRoot, "package.json")).resolve(request.module));
1030
- if (!isWithin(deploymentRoot, resolvedRealpath)) throw new Error(`plugin module ${request.module} resolves outside deployment root: ${resolvedRealpath}`);
1031
- return resolvedRealpath;
1207
+ //#region src/modules/deployment-control/application/failure/deployment-failure.ts
1208
+ function formatDeploymentFailure(failure) {
1209
+ return failure instanceof Error ? failure.message : String(failure);
1032
1210
  }
1033
- function isWithin(root, candidate) {
1034
- const child = relative(root, candidate);
1035
- return child === "" || !child.startsWith(`..${sep}`) && child !== ".." && !isAbsolute(child);
1211
+ function toDeploymentFailure(failure) {
1212
+ return failure instanceof Error ? failure : new Error(String(failure));
1036
1213
  }
1037
1214
  //#endregion
1038
- //#region src/infrastructure/config/rivus-daemon-config.ts
1039
- var RivusDaemonConfigError = class {
1040
- variable;
1041
- message;
1042
- _tag = "RivusDaemonConfigError";
1043
- constructor(variable, message) {
1044
- this.variable = variable;
1045
- this.message = message;
1046
- }
1215
+ //#region src/modules/deployment-control/domain/lifecycle/deployment-lifecycle.ts
1216
+ var InvalidDeploymentLifecycleTransition = class extends Error {
1217
+ name = "InvalidDeploymentLifecycleTransition";
1047
1218
  };
1048
- const DEFAULT_AGENT_ID$1 = "main";
1049
- const DEFAULT_FEISHU_BASE_URL$1 = "https://open.feishu.cn";
1050
- const DEFAULT_STREAM_MIN_INTERVAL_MS$1 = 200;
1051
- const THINKING_LEVELS$1 = /* @__PURE__ */ new Set([
1052
- "off",
1053
- "minimal",
1054
- "low",
1055
- "medium",
1056
- "high",
1057
- "xhigh"
1058
- ]);
1059
- function loadRivusDaemonConfig(env, options = {}) {
1060
- return Effect.gen(function* () {
1061
- const appId = yield* required(env, "FEISHU_APP_ID");
1062
- const appSecret = yield* required(env, "FEISHU_APP_SECRET");
1063
- const streamMinIntervalMs = yield* optionalPositiveInteger(env.FEISHU_STREAM_MIN_INTERVAL_MS, "FEISHU_STREAM_MIN_INTERVAL_MS", DEFAULT_STREAM_MIN_INTERVAL_MS$1);
1064
- const cardStreamLeaseMs = yield* optionalPositiveInteger(env.FEISHU_CARD_STREAM_LEASE_MS, "FEISHU_CARD_STREAM_LEASE_MS", DEFAULT_CARD_STREAM_LEASE_MS);
1065
- const thinkingLevel = yield* optionalThinkingLevel(env.PI_THINKING_LEVEL);
1066
- const apiKey = yield* optionalPiApiKey(env, options.readTextFile ?? readUtf8File);
1067
- const baseUrl = optional(env.PI_BASE_URL);
1068
- const model = optional(env.PI_MODEL);
1069
- return {
1070
- agentId: optional(env.RIVUS_AGENT_ID) ?? DEFAULT_AGENT_ID$1,
1071
- feishu: {
1072
- appId,
1073
- appSecret,
1074
- baseUrl: optional(env.FEISHU_BASE_URL) ?? DEFAULT_FEISHU_BASE_URL$1,
1075
- cardStreamLeaseMs,
1076
- streamMinIntervalMs
1077
- },
1078
- pi: {
1079
- ...apiKey ? { apiKey } : {},
1080
- ...baseUrl ? { baseUrl } : {},
1081
- ...model ? { model } : {},
1082
- ...thinkingLevel ? { thinkingLevel } : {}
1083
- }
1084
- };
1085
- });
1086
- }
1087
- function optional(value) {
1088
- const trimmed = value?.trim();
1089
- return trimmed ? trimmed : void 0;
1090
- }
1091
- function readUtf8File(path) {
1092
- return readFile(path, "utf8");
1093
- }
1094
- function optionalPiApiKey(env, readTextFile) {
1095
- const inlineApiKey = optional(env.PI_API_KEY);
1096
- const apiKeyFile = optional(env.PI_API_KEY_FILE);
1097
- if (inlineApiKey && apiKeyFile) return Effect.fail(new RivusDaemonConfigError("PI_API_KEY", "PI_API_KEY and PI_API_KEY_FILE cannot both be set"));
1098
- if (inlineApiKey) return Effect.succeed(inlineApiKey);
1099
- if (!apiKeyFile) return Effect.succeed(void 0);
1100
- return Effect.tryPromise({
1101
- try: async () => readTextFile(apiKeyFile),
1102
- catch: (error) => new RivusDaemonConfigError("PI_API_KEY_FILE", `PI_API_KEY_FILE could not be read: ${formatConfigError(error)}`)
1103
- }).pipe(Effect.flatMap((contents) => {
1104
- const apiKey = optional(contents);
1105
- return apiKey ? Effect.succeed(apiKey) : Effect.fail(new RivusDaemonConfigError("PI_API_KEY_FILE", "PI_API_KEY_FILE must not be empty"));
1106
- }));
1107
- }
1108
- function formatConfigError(error) {
1109
- return error instanceof Error ? error.message : String(error);
1110
- }
1111
- function optionalPositiveInteger(value, variable, fallback) {
1112
- const normalized = optional(value);
1113
- if (!normalized) return Effect.succeed(fallback);
1114
- if (/^[1-9]\d*$/.test(normalized)) return Effect.succeed(Number(normalized));
1115
- return Effect.fail(new RivusDaemonConfigError(variable, `${variable} must be a positive integer`));
1116
- }
1117
- function required(env, variable) {
1118
- const value = optional(env[variable]);
1119
- if (value) return Effect.succeed(value);
1120
- return Effect.fail(new RivusDaemonConfigError(variable, `${variable} is required`));
1121
- }
1122
- function optionalThinkingLevel(value) {
1123
- const normalized = optional(value);
1124
- if (!normalized) return Effect.succeed(void 0);
1125
- if (THINKING_LEVELS$1.has(normalized)) return Effect.succeed(normalized);
1126
- return Effect.fail(new RivusDaemonConfigError("PI_THINKING_LEVEL", "PI_THINKING_LEVEL must be one of off, minimal, low, medium, high, xhigh"));
1219
+ const componentTransitions = Object.freeze({
1220
+ "cleanup-required": ["cleanup-required", "stopping"],
1221
+ degraded: ["degraded", "stopping"],
1222
+ disabled: ["disabled"],
1223
+ running: [
1224
+ "degraded",
1225
+ "running",
1226
+ "stopping"
1227
+ ],
1228
+ starting: ["degraded", "running"],
1229
+ stopped: ["starting", "stopped"],
1230
+ stopping: [
1231
+ "cleanup-required",
1232
+ "disabled",
1233
+ "stopped"
1234
+ ]
1235
+ });
1236
+ const controlTransitions = Object.freeze({
1237
+ "cleanup-required": ["cleanup-required", "stopping"],
1238
+ degraded: ["degraded", "stopping"],
1239
+ running: [
1240
+ "degraded",
1241
+ "running",
1242
+ "stopping"
1243
+ ],
1244
+ starting: ["degraded", "running"],
1245
+ stopped: [
1246
+ "starting",
1247
+ "stopped",
1248
+ "stopping"
1249
+ ],
1250
+ stopping: ["cleanup-required", "stopped"]
1251
+ });
1252
+ function transitionDeploymentComponentLifecycle(current, next) {
1253
+ if (!componentTransitions[current].includes(next)) throw new InvalidDeploymentLifecycleTransition(`cannot transition deployment component from ${current} to ${next}`);
1254
+ return next;
1255
+ }
1256
+ function transitionDeploymentControlLifecycle(current, next) {
1257
+ if (!controlTransitions[current].includes(next)) throw new InvalidDeploymentLifecycleTransition(`cannot transition Deployment Control from ${current} to ${next}`);
1258
+ return next;
1259
+ }
1260
+ function isRequiredDeploymentComponentReady(input) {
1261
+ return !input.enabled || !input.required || input.lifecycle === "running" && input.running;
1127
1262
  }
1128
1263
  //#endregion
1129
- //#region src/infrastructure/config/openclaw-env-import.ts
1130
- var OpenClawEnvImportError = class extends Error {
1131
- constructor(message) {
1132
- super(message);
1133
- this.name = "OpenClawEnvImportError";
1134
- }
1264
+ //#region src/modules/deployment-control/application/lifecycle/deployment-control.ts
1265
+ var RivusDeploymentDaemonLifecycleError = class extends Error {
1266
+ name = "RivusDeploymentDaemonLifecycleError";
1135
1267
  };
1136
- const DEFAULT_AGENT_ID = "main";
1137
- const DEFAULT_FEISHU_BASE_URL = "https://open.feishu.cn";
1138
- const DEFAULT_LARK_BASE_URL = "https://open.larksuite.com";
1139
- const DEFAULT_STREAM_MIN_INTERVAL_MS = 200;
1140
- const THINKING_LEVELS = /* @__PURE__ */ new Set([
1141
- "off",
1142
- "minimal",
1143
- "low",
1144
- "medium",
1145
- "high",
1146
- "xhigh"
1147
- ]);
1148
- const ENV_FILE_ORDER = [
1149
- "FEISHU_APP_ID",
1150
- "FEISHU_APP_SECRET",
1151
- "FEISHU_BASE_URL",
1152
- "FEISHU_STREAM_MIN_INTERVAL_MS",
1153
- "RIVUS_AGENT_ID",
1154
- "PI_API_KEY_FILE",
1155
- "PI_BASE_URL",
1156
- "PI_MODEL",
1157
- "PI_THINKING_LEVEL"
1158
- ];
1159
- function createRivusEnvFromOpenClawConfig(openClawConfig, options = {}) {
1160
- const config = asRecord(openClawConfig, "OpenClaw config");
1161
- const feishu = asRecord(readPath(config, ["channels", "feishu"]), "channels.feishu");
1162
- const appId = requiredString(feishu, "appId", "channels.feishu.appId");
1163
- const appSecret = requiredString(feishu, "appSecret", "channels.feishu.appSecret");
1164
- const modelReference = findPrimaryModelReference(config);
1165
- const providerId = modelReference ? parseProviderId(modelReference) : void 0;
1166
- const provider = providerId ? optionalRecord(readPath(config, [
1167
- "models",
1168
- "providers",
1169
- providerId
1170
- ])) : void 0;
1171
- const providerBaseUrl = provider ? optionalString(provider.baseUrl) : void 0;
1172
- const thinkingLevel = modelReference ? findThinkingLevel(config, modelReference) : void 0;
1173
- const warnings = thinkingLevel?.warning ? [thinkingLevel.warning] : [];
1174
- return {
1175
- env: {
1176
- FEISHU_APP_ID: appId,
1177
- FEISHU_APP_SECRET: appSecret,
1178
- FEISHU_BASE_URL: options.feishuBaseUrl ?? inferFeishuBaseUrl(optionalString(feishu.domain)),
1179
- FEISHU_STREAM_MIN_INTERVAL_MS: String(options.streamMinIntervalMs ?? DEFAULT_STREAM_MIN_INTERVAL_MS),
1180
- RIVUS_AGENT_ID: findAgentId(config),
1181
- ...options.piApiKeyFile ? { PI_API_KEY_FILE: options.piApiKeyFile } : {},
1182
- ...providerBaseUrl ? { PI_BASE_URL: providerBaseUrl } : {},
1183
- ...modelReference ? { PI_MODEL: modelReference } : {},
1184
- ...thinkingLevel?.level ? { PI_THINKING_LEVEL: thinkingLevel.level } : {}
1185
- },
1186
- warnings
1187
- };
1188
- }
1189
- function formatRivusEnvFile(env) {
1190
- 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`;
1191
- }
1192
- function findAgentId(config) {
1193
- const agents = optionalRecord(config.agents);
1194
- return optionalString(optionalRecord((Array.isArray(agents?.list) ? agents.list : [])[0])?.id) ?? DEFAULT_AGENT_ID;
1195
- }
1196
- function findPrimaryModelReference(config) {
1197
- const primary = optionalString(readPath(config, [
1198
- "agents",
1199
- "defaults",
1200
- "model",
1201
- "primary"
1202
- ]));
1203
- if (primary) return primary;
1204
- const providers = optionalRecord(readPath(config, ["models", "providers"]));
1205
- if (!providers) return;
1206
- for (const [providerId, provider] of Object.entries(providers)) {
1207
- const model = firstModelId(optionalRecord(provider));
1208
- if (model) return `${providerId}/${model}`;
1209
- }
1210
- }
1211
- function firstModelId(provider) {
1212
- return optionalString(optionalRecord((Array.isArray(provider?.models) ? provider.models : [])[0])?.id);
1213
- }
1214
- function parseProviderId(modelReference) {
1215
- const separator = modelReference.indexOf("/");
1216
- return separator > 0 ? modelReference.slice(0, separator) : void 0;
1217
- }
1218
- function findThinkingLevel(config, modelReference) {
1219
- const providerId = parseProviderId(modelReference);
1220
- const modelId = readModelId(modelReference);
1221
- const rawLevel = [
1222
- readPath(config, [
1223
- "agents",
1224
- "defaults",
1225
- "models",
1226
- modelReference,
1227
- "thinkingLevel"
1228
- ]),
1229
- readPath(config, [
1230
- "agents",
1231
- "defaults",
1232
- "models",
1233
- modelReference,
1234
- "thinkLevel"
1235
- ]),
1236
- readPath(config, [
1237
- "agents",
1238
- "defaults",
1239
- "models",
1240
- modelReference,
1241
- "reasoningLevel"
1242
- ]),
1243
- ...providerId && modelId ? readProviderModelThinkingCandidates(config, providerId, modelId) : []
1244
- ].map(optionalString).find(Boolean);
1245
- if (!rawLevel) return;
1246
- if (THINKING_LEVELS.has(rawLevel)) return { level: rawLevel };
1247
- return { warning: `Unsupported OpenClaw thinking level '${rawLevel}' was ignored` };
1268
+ var RivusDeploymentReadinessError = class extends Error {
1269
+ endpointIds;
1270
+ name = "RivusDeploymentReadinessError";
1271
+ constructor(endpointIds) {
1272
+ super(`required endpoint startup failed: ${endpointIds.join(", ")}`);
1273
+ this.endpointIds = endpointIds;
1274
+ }
1275
+ };
1276
+ var RivusDeploymentAutomationReadinessError = class extends Error {
1277
+ automationIds;
1278
+ name = "RivusDeploymentAutomationReadinessError";
1279
+ constructor(automationIds) {
1280
+ super(`required automation startup failed: ${automationIds.join(", ")}`);
1281
+ this.automationIds = automationIds;
1282
+ }
1283
+ };
1284
+ var RivusDeploymentBackgroundSessionReadinessError = class extends Error {
1285
+ name = "RivusDeploymentBackgroundSessionReadinessError";
1286
+ constructor() {
1287
+ super("required Background Session startup failed");
1288
+ }
1289
+ };
1290
+ function createRivusDeploymentControl(input) {
1291
+ return Effect.gen(function* () {
1292
+ const deployment = input.deployment;
1293
+ if ((deployment.manifest.projectSpaces?.length ?? 0) > 0 && !input.projectSpaceResolver) return yield* Effect.fail(new RivusDeploymentDaemonLifecycleError("deployment bootstrap does not provide a Project Space resolver"));
1294
+ const projectSpaces = /* @__PURE__ */ new Map();
1295
+ for (const declaration of deployment.manifest.projectSpaces ?? []) {
1296
+ const resolved = yield* input.projectSpaceResolver.resolve({
1297
+ declaration,
1298
+ deploymentRoot: input.deploymentRoot
1299
+ }).pipe(Effect.mapError(toDeploymentFailure));
1300
+ projectSpaces.set(resolved.id, resolved);
1301
+ }
1302
+ const definitions = yield* Effect.try({
1303
+ try: () => new Map(deployment.definitions.map((definition) => {
1304
+ const resolved = bindResolvedProjectSpaceRevision(definition, projectSpaces);
1305
+ return [resolved.agentId, resolved];
1306
+ })),
1307
+ catch: toDeploymentFailure
1308
+ });
1309
+ const automationDefinitions = yield* Effect.try({
1310
+ try: () => new Map(deployment.automationDefinitions.map((definition) => [definition.id, Object.freeze({
1311
+ ...definition,
1312
+ runtimeDefinition: bindResolvedProjectSpaceRevision(definition.runtimeDefinition, projectSpaces)
1313
+ })])),
1314
+ catch: toDeploymentFailure
1315
+ });
1316
+ const effectiveDeployment = Object.freeze({
1317
+ ...deployment,
1318
+ agents: Object.freeze(deployment.agents.map((agent) => {
1319
+ if (!agent.definition) return agent;
1320
+ const definition = definitions.get(agent.agentId);
1321
+ return definition ? Object.freeze({
1322
+ ...agent,
1323
+ definition
1324
+ }) : agent;
1325
+ })),
1326
+ automationDefinitions: Object.freeze([...automationDefinitions.values()]),
1327
+ definitions: Object.freeze([...definitions.values()])
1328
+ });
1329
+ const backgroundConfig = deployment.manifest.backgroundSessions;
1330
+ const backgroundDefinitions = /* @__PURE__ */ new Map();
1331
+ if (backgroundConfig?.enabled) for (const agent of effectiveDeployment.agents) {
1332
+ if (agent.status !== "enabled" || !agent.definition) continue;
1333
+ const narrowed = yield* Effect.try({
1334
+ try: () => narrowBackgroundSessionDefinition(definitions.get(agent.agentId)),
1335
+ catch: toDeploymentFailure
1336
+ });
1337
+ backgroundDefinitions.set(agent.agentId, narrowed);
1338
+ }
1339
+ const agentStatuses = new Map(effectiveDeployment.agents.map((agent) => [agent.agentId, agent]));
1340
+ const endpointSlots = deployment.manifest.endpoints.map((definition) => {
1341
+ return createSlot(definition, agentStatuses.get(definition.agentId)?.status === "enabled");
1342
+ });
1343
+ const automationSlots = (deployment.manifest.automations ?? []).map((definition) => {
1344
+ const agentEnabled = agentStatuses.get(definition.agentId)?.status === "enabled";
1345
+ const resolvedDefinition = automationDefinitions.get(definition.id);
1346
+ return {
1347
+ ...createSlot(definition, agentEnabled && resolvedDefinition !== void 0),
1348
+ agentEnabled,
1349
+ ...resolvedDefinition ? { resolvedDefinition } : {}
1350
+ };
1351
+ });
1352
+ const backgroundSessionSlot = backgroundConfig ? createSlot(backgroundConfig, backgroundDefinitions.size > 0) : void 0;
1353
+ const host = yield* input.agentHostFactory.create({
1354
+ automations: automationSlots.filter((slot) => slot.definition.enabled && slot.agentEnabled && slot.resolvedDefinition !== void 0).map((slot) => ({
1355
+ definition: slot.resolvedDefinition.runtimeDefinition,
1356
+ id: slot.definition.id
1357
+ })),
1358
+ backgroundSessions: [...backgroundDefinitions].map(([agentId, definition]) => ({
1359
+ agentId,
1360
+ definition
1361
+ })),
1362
+ definitions: [...definitions.values()],
1363
+ endpoints: endpointSlots.filter((slot) => slot.definition.enabled && slot.agentEnabled).map((slot) => ({
1364
+ agentId: slot.definition.agentId,
1365
+ id: slot.definition.id
1366
+ })),
1367
+ ...input.initialInstanceRecords ? { initialInstanceRecords: input.initialInstanceRecords } : {},
1368
+ runtimeFactory: { create: (instance) => {
1369
+ 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);
1370
+ if (!definition) return Effect.fail(new RivusDeploymentDaemonLifecycleError(`runtime instance references unknown agent: ${instance.agentId}`));
1371
+ const projectSpace = definition.projectSpaceId ? projectSpaces.get(definition.projectSpaceId) : void 0;
1372
+ return input.runtimeFactory.create({
1373
+ ...instance,
1374
+ catalog: effectiveDeployment.catalog,
1375
+ definition,
1376
+ ...projectSpace ? { projectSpace } : {}
1377
+ });
1378
+ } }
1379
+ }).pipe(Effect.mapError(toDeploymentFailure));
1380
+ return makeDeploymentControl({
1381
+ ...input.automationFactory ? { automationFactory: input.automationFactory } : {},
1382
+ automationSlots,
1383
+ ...input.backgroundSessionFactory ? { backgroundSessionFactory: input.backgroundSessionFactory } : {},
1384
+ ...backgroundSessionSlot ? { backgroundSessionSlot } : {},
1385
+ definitions,
1386
+ deployment: effectiveDeployment,
1387
+ endpointFactory: input.endpointFactory,
1388
+ endpointSlots,
1389
+ host
1390
+ });
1391
+ });
1248
1392
  }
1249
- function readProviderModelThinkingCandidates(config, providerId, modelId) {
1250
- const providerModels = readPath(config, [
1251
- "models",
1252
- "providers",
1253
- providerId,
1254
- "models"
1255
- ]);
1256
- if (!Array.isArray(providerModels)) return [];
1257
- const model = providerModels.map(optionalRecord).find((candidate) => optionalString(candidate?.id) === modelId);
1258
- if (!model) return [];
1259
- return [
1260
- model.thinkingLevel,
1261
- model.thinkLevel,
1262
- model.reasoningLevel,
1263
- readPath(model, ["reasoning", "level"]),
1264
- readPath(model, ["reasoning", "thinkingLevel"]),
1265
- readPath(model, ["reasoning", "thinkLevel"])
1393
+ function makeDeploymentControl(input) {
1394
+ const endpointById = new Map(input.endpointSlots.map((slot) => [slot.definition.id, slot]));
1395
+ const allSlots = [
1396
+ ...input.endpointSlots,
1397
+ ...input.automationSlots,
1398
+ ...input.backgroundSessionSlot ? [input.backgroundSessionSlot] : []
1266
1399
  ];
1400
+ let lifecycle = "stopped";
1401
+ const refreshObservedState = () => {
1402
+ let degraded = false;
1403
+ for (const slot of allSlots) {
1404
+ if (slot.lifecycle !== "running") continue;
1405
+ const observed = observeRunning(slot);
1406
+ if (!observed.running) {
1407
+ slot.lifecycle = transitionDeploymentComponentLifecycle(slot.lifecycle, "degraded");
1408
+ slot.error = observed.error ?? "component stopped running after startup";
1409
+ degraded = true;
1410
+ }
1411
+ }
1412
+ if (degraded && lifecycle === "running") lifecycle = transitionDeploymentControlLifecycle(lifecycle, "degraded");
1413
+ };
1414
+ const canRunIntake = () => {
1415
+ refreshObservedState();
1416
+ return lifecycle === "running" || lifecycle === "degraded";
1417
+ };
1418
+ const handleEndpoint = (endpointId, request) => Effect.suspend(() => {
1419
+ const slot = endpointById.get(endpointId);
1420
+ if (!slot) return Effect.fail(new RivusDeploymentDaemonLifecycleError(`unknown endpoint: ${endpointId}`));
1421
+ const observed = observeRunning(slot);
1422
+ 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}`));
1423
+ return input.host.handleEndpoint(endpointId, request);
1424
+ });
1425
+ const isReady = () => allSlots.every((slot) => isSlotReady(slot));
1426
+ const readinessFailure = () => {
1427
+ const endpointIds = input.endpointSlots.filter((slot) => slot.definition.enabled && slot.definition.required && !isSlotReady(slot)).map((slot) => slot.definition.id);
1428
+ if (endpointIds.length > 0) return new RivusDeploymentReadinessError(Object.freeze(endpointIds));
1429
+ const automationIds = input.automationSlots.filter((slot) => slot.definition.enabled && slot.definition.required && !isSlotReady(slot)).map((slot) => slot.definition.id);
1430
+ if (automationIds.length > 0) return new RivusDeploymentAutomationReadinessError(Object.freeze(automationIds));
1431
+ if (input.backgroundSessionSlot?.definition.enabled && input.backgroundSessionSlot.definition.required && !isSlotReady(input.backgroundSessionSlot)) return new RivusDeploymentBackgroundSessionReadinessError();
1432
+ };
1433
+ const status = () => {
1434
+ refreshObservedState();
1435
+ return Object.freeze({
1436
+ agents: input.deployment.agents,
1437
+ automations: Object.freeze(input.automationSlots.map((slot) => ({
1438
+ ...componentStatus(slot),
1439
+ agentId: slot.definition.agentId,
1440
+ automationId: slot.definition.id
1441
+ }))),
1442
+ ...input.backgroundSessionSlot ? { backgroundSessions: backgroundStatus(input.backgroundSessionSlot) } : {},
1443
+ defaultAgentId: input.deployment.manifest.defaultAgentId,
1444
+ defaultEndpointId: input.deployment.manifest.defaultEndpointId,
1445
+ endpoints: Object.freeze(input.endpointSlots.map((slot) => ({
1446
+ ...componentStatus(slot),
1447
+ agentId: slot.definition.agentId,
1448
+ endpointId: slot.definition.id
1449
+ }))),
1450
+ lifecycle,
1451
+ plugins: input.deployment.plugins,
1452
+ ready: isReady(),
1453
+ running: lifecycle === "running" || lifecycle === "degraded"
1454
+ });
1455
+ };
1456
+ return {
1457
+ deployment: input.deployment,
1458
+ handleDefault: (request) => handleEndpoint(input.deployment.manifest.defaultEndpointId, request),
1459
+ handleEndpoint,
1460
+ runDefaultAgent: (request) => input.host.handleEndpoint(input.deployment.manifest.defaultEndpointId, request),
1461
+ running: canRunIntake,
1462
+ start: () => Effect.suspend(() => {
1463
+ refreshObservedState();
1464
+ if (lifecycle === "running") return Effect.void;
1465
+ if (lifecycle === "degraded") {
1466
+ const failure = readinessFailure();
1467
+ return failure ? Effect.fail(failure) : Effect.void;
1468
+ }
1469
+ if (lifecycle !== "stopped") return Effect.fail(new RivusDeploymentDaemonLifecycleError(`cannot start deployment daemon while ${lifecycle}`));
1470
+ lifecycle = transitionDeploymentControlLifecycle(lifecycle, "starting");
1471
+ return Effect.gen(function* () {
1472
+ let degraded = input.deployment.plugins.some(({ status: pluginStatus }) => pluginStatus === "failed");
1473
+ for (const slot of input.endpointSlots) degraded = (yield* startSlot(slot, slot.agentEnabled, `endpoint ${slot.definition.id}`, () => Effect.gen(function* () {
1474
+ const instance = yield* input.host.resolveEndpoint(slot.definition.id);
1475
+ return yield* input.endpointFactory.create({
1476
+ agentId: slot.definition.agentId,
1477
+ cancel: (request) => input.host.cancelEndpoint(slot.definition.id, request),
1478
+ definition: slot.definition,
1479
+ endpointId: slot.definition.id,
1480
+ handle: (request) => handleEndpoint(slot.definition.id, request),
1481
+ instanceId: instance.instanceId,
1482
+ ...input.definitions.get(slot.definition.agentId)?.projectSpaceId ? { projectSpaceId: input.definitions.get(slot.definition.agentId).projectSpaceId } : {},
1483
+ steer: (request) => input.host.steerEndpoint(slot.definition.id, request)
1484
+ });
1485
+ }))) || degraded;
1486
+ for (const slot of input.automationSlots) {
1487
+ const resolvedDefinition = slot.resolvedDefinition;
1488
+ const deliverySlot = endpointById.get(slot.definition.delivery.endpointId);
1489
+ const deliveryRunning = observeRunning(deliverySlot).running && deliverySlot.lifecycle === "running";
1490
+ degraded = (yield* startSlot(slot, slot.agentEnabled && resolvedDefinition !== void 0 && deliveryRunning, `automation ${slot.definition.id}`, () => Effect.gen(function* () {
1491
+ if (!input.automationFactory) return yield* Effect.fail(new RivusDeploymentDaemonLifecycleError("deployment bootstrap does not provide Automation adapters"));
1492
+ if (!resolvedDefinition) return yield* Effect.fail(new RivusDeploymentDaemonLifecycleError(`automation definition not resolved: ${slot.definition.id}`));
1493
+ const instance = yield* input.host.resolveAutomation(slot.definition.id);
1494
+ return yield* input.automationFactory.create({
1495
+ automationId: slot.definition.id,
1496
+ definition: resolvedDefinition,
1497
+ deliveryEndpoint: deliverySlot.definition,
1498
+ instanceId: instance.instanceId,
1499
+ run: (request) => input.host.handleAutomation(slot.definition.id, {
1500
+ invocation: {
1501
+ allowedActorOpenIds: [],
1502
+ automationId: slot.definition.id,
1503
+ endpointId: slot.definition.delivery.endpointId,
1504
+ kind: "automation",
1505
+ sourceMessageId: request.tickId,
1506
+ tenantKey: "automation",
1507
+ tickId: request.tickId
1508
+ },
1509
+ sessionKey: request.sessionKey,
1510
+ text: request.text
1511
+ })
1512
+ });
1513
+ }))) || degraded;
1514
+ }
1515
+ if (input.backgroundSessionSlot) {
1516
+ const slot = input.backgroundSessionSlot;
1517
+ degraded = (yield* startSlot(slot, slot.agentEnabled, "Background Session", () => Effect.gen(function* () {
1518
+ if (!input.backgroundSessionFactory) return yield* Effect.fail(new RivusDeploymentDaemonLifecycleError("deployment bootstrap does not provide Background Session adapters"));
1519
+ return yield* input.backgroundSessionFactory.create({
1520
+ agentIds: [...input.definitions.keys()].filter((agentId) => input.deployment.agents.some((agent) => agent.agentId === agentId && agent.status === "enabled")),
1521
+ cancel: (request) => input.host.cancelBackgroundSession(request.agentId, request),
1522
+ config: slot.definition,
1523
+ run: (request) => input.host.handleBackgroundSession(request.agentId, {
1524
+ ...request.invocation ? { invocation: request.invocation } : {},
1525
+ ...request.onUpdate ? { onUpdate: request.onUpdate } : {},
1526
+ sessionKey: request.sessionKey,
1527
+ text: request.text
1528
+ })
1529
+ });
1530
+ }))) || degraded;
1531
+ }
1532
+ lifecycle = transitionDeploymentControlLifecycle(lifecycle, degraded ? "degraded" : "running");
1533
+ const failure = readinessFailure();
1534
+ if (failure) return yield* Effect.fail(failure);
1535
+ });
1536
+ }),
1537
+ status,
1538
+ stop: () => Effect.suspend(() => {
1539
+ if (lifecycle !== "stopped" && lifecycle !== "running" && lifecycle !== "degraded" && lifecycle !== "cleanup-required") return Effect.fail(new RivusDeploymentDaemonLifecycleError(`cannot stop deployment daemon while ${lifecycle}`));
1540
+ lifecycle = transitionDeploymentControlLifecycle(lifecycle, "stopping");
1541
+ return Effect.gen(function* () {
1542
+ const errors = [];
1543
+ yield* stopSlots(input.automationSlots, errors);
1544
+ if (input.backgroundSessionSlot) yield* stopSlots([input.backgroundSessionSlot], errors);
1545
+ yield* stopSlots(input.endpointSlots, errors);
1546
+ const hostExit = yield* Effect.exit(input.host.dispose());
1547
+ if (Exit.isFailure(hostExit)) errors.push(Cause.squash(hostExit.cause));
1548
+ lifecycle = transitionDeploymentControlLifecycle(lifecycle, errors.length === 0 ? "stopped" : "cleanup-required");
1549
+ if (errors.length === 1) return yield* Effect.fail(errors[0]);
1550
+ if (errors.length > 1) return yield* Effect.fail(new AggregateError(errors, "deployment daemon cleanup failed"));
1551
+ });
1552
+ })
1553
+ };
1267
1554
  }
1268
- function readModelId(modelReference) {
1269
- const separator = modelReference.indexOf("/");
1270
- return separator >= 0 && separator < modelReference.length - 1 ? modelReference.slice(separator + 1) : void 0;
1271
- }
1272
- function inferFeishuBaseUrl(domain) {
1273
- return domain === "lark" ? DEFAULT_LARK_BASE_URL : DEFAULT_FEISHU_BASE_URL;
1555
+ function createSlot(definition, agentEnabled) {
1556
+ return {
1557
+ agentEnabled,
1558
+ definition,
1559
+ ...!definition.enabled ? { lifecycle: "disabled" } : agentEnabled ? { lifecycle: "stopped" } : {
1560
+ error: "component is unavailable",
1561
+ lifecycle: "degraded"
1562
+ }
1563
+ };
1274
1564
  }
1275
- function quoteEnvValue(value) {
1276
- return `'${value.replaceAll("'", "'\\''")}'`;
1565
+ function startSlot(slot, available, label, create) {
1566
+ return Effect.gen(function* () {
1567
+ if (!slot.definition.enabled) {
1568
+ slot.lifecycle = "disabled";
1569
+ return false;
1570
+ }
1571
+ if (!available) {
1572
+ slot.lifecycle = "degraded";
1573
+ slot.error = `${label} is unavailable`;
1574
+ return true;
1575
+ }
1576
+ if (slot.lifecycle !== "stopped") return yield* Effect.fail(new RivusDeploymentDaemonLifecycleError(`cannot start ${label} while ${slot.lifecycle}`));
1577
+ slot.lifecycle = transitionDeploymentComponentLifecycle(slot.lifecycle, "starting");
1578
+ delete slot.error;
1579
+ const started = yield* Effect.gen(function* () {
1580
+ slot.adapter ??= yield* create();
1581
+ yield* slot.adapter.start();
1582
+ if (!(yield* Effect.try({
1583
+ try: () => slot.adapter.running(),
1584
+ catch: toDeploymentFailure
1585
+ }))) return yield* Effect.fail(/* @__PURE__ */ new Error(`${label} start completed but the adapter is not running`));
1586
+ }).pipe(Effect.exit);
1587
+ if (Exit.isFailure(started)) {
1588
+ slot.error = formatDeploymentFailure(Cause.squash(started.cause));
1589
+ slot.lifecycle = transitionDeploymentComponentLifecycle(slot.lifecycle, "degraded");
1590
+ return true;
1591
+ }
1592
+ slot.lifecycle = transitionDeploymentComponentLifecycle(slot.lifecycle, "running");
1593
+ return false;
1594
+ });
1277
1595
  }
1278
- function requiredString(record, key, path) {
1279
- const value = optionalString(record[key]);
1280
- if (!value) throw new OpenClawEnvImportError(`${path} is required`);
1281
- return value;
1596
+ function stopSlots(slots, errors) {
1597
+ return Effect.gen(function* () {
1598
+ for (const slot of [...slots].reverse()) {
1599
+ if (slot.lifecycle === "stopped" || slot.lifecycle === "disabled") continue;
1600
+ slot.lifecycle = transitionDeploymentComponentLifecycle(slot.lifecycle, "stopping");
1601
+ if (!slot.adapter) {
1602
+ slot.lifecycle = transitionDeploymentComponentLifecycle(slot.lifecycle, slot.definition.enabled && slot.agentEnabled ? "stopped" : "disabled");
1603
+ delete slot.error;
1604
+ continue;
1605
+ }
1606
+ const stopped = yield* Effect.gen(function* () {
1607
+ yield* slot.adapter.stop();
1608
+ if (yield* Effect.try({
1609
+ try: () => slot.adapter.running(),
1610
+ catch: toDeploymentFailure
1611
+ })) return yield* Effect.fail(/* @__PURE__ */ new Error("component stop completed but the adapter is still running"));
1612
+ }).pipe(Effect.exit);
1613
+ if (Exit.isFailure(stopped)) {
1614
+ const error = Cause.squash(stopped.cause);
1615
+ errors.push(error);
1616
+ slot.error = formatDeploymentFailure(error);
1617
+ slot.lifecycle = transitionDeploymentComponentLifecycle(slot.lifecycle, "cleanup-required");
1618
+ continue;
1619
+ }
1620
+ slot.lifecycle = transitionDeploymentComponentLifecycle(slot.lifecycle, slot.definition.enabled && slot.agentEnabled ? "stopped" : "disabled");
1621
+ delete slot.error;
1622
+ }
1623
+ });
1282
1624
  }
1283
- function optionalString(value) {
1284
- return typeof value === "string" && value.trim() ? value.trim() : void 0;
1625
+ function componentStatus(slot) {
1626
+ const observed = observeRunning(slot);
1627
+ return Object.freeze({
1628
+ enabled: slot.definition.enabled,
1629
+ ...slot.error ? { error: slot.error } : {},
1630
+ lifecycle: slot.lifecycle,
1631
+ required: slot.definition.required,
1632
+ running: slot.lifecycle === "running" && observed.running
1633
+ });
1285
1634
  }
1286
- function asRecord(value, path) {
1287
- const record = optionalRecord(value);
1288
- if (!record) throw new OpenClawEnvImportError(`${path} must be an object`);
1289
- return record;
1635
+ function backgroundStatus(slot) {
1636
+ let supervisor;
1637
+ try {
1638
+ supervisor = slot.adapter?.status?.();
1639
+ } catch (error) {
1640
+ slot.error = formatDeploymentFailure(error);
1641
+ }
1642
+ return Object.freeze({
1643
+ ...componentStatus(slot),
1644
+ ...supervisor ? { supervisor } : {}
1645
+ });
1290
1646
  }
1291
- function optionalRecord(value) {
1292
- return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
1647
+ function isSlotReady(slot) {
1648
+ return isRequiredDeploymentComponentReady({
1649
+ enabled: slot.definition.enabled,
1650
+ lifecycle: slot.lifecycle,
1651
+ required: slot.definition.required,
1652
+ running: observeRunning(slot).running
1653
+ });
1293
1654
  }
1294
- function readPath(record, path) {
1295
- let value = record;
1296
- for (const segment of path) {
1297
- const current = optionalRecord(value);
1298
- if (!current) return;
1299
- value = current[segment];
1655
+ function observeRunning(slot) {
1656
+ if (!slot.adapter) return { running: false };
1657
+ try {
1658
+ return { running: slot.adapter.running() };
1659
+ } catch (error) {
1660
+ return {
1661
+ error: formatDeploymentFailure(error),
1662
+ running: false
1663
+ };
1300
1664
  }
1301
- return value;
1665
+ }
1666
+ function bindResolvedProjectSpaceRevision(definition, projectSpaces) {
1667
+ if (!definition.projectSpaceId) return definition;
1668
+ const projectSpace = projectSpaces.get(definition.projectSpaceId);
1669
+ if (!projectSpace) throw new RivusDeploymentDaemonLifecycleError(`agent ${definition.agentId} references unresolved Project Space: ${definition.projectSpaceId}`);
1670
+ return Object.freeze({
1671
+ ...definition,
1672
+ projectSpaceRevision: projectSpace.revision
1673
+ });
1302
1674
  }
1303
1675
  //#endregion
1304
- //#region src/application/daemon/rivus-daemon-shutdown-controller.ts
1305
- const DEFAULT_SIGNALS = ["SIGINT", "SIGTERM"];
1306
- function createRivusDaemonShutdownController(options) {
1307
- let shutdown;
1308
- const handle = (signal) => {
1309
- shutdown ??= Effect.runPromise(options.daemon.stop()).then(() => options.onStopped?.(signal)).catch(async (error) => {
1310
- await options.onError?.(error, signal);
1311
- throw error;
1676
+ //#region src/modules/deployment-control/application/resolution/automation-runtime-definition.ts
1677
+ function resolveRivusAutomationRuntimeDefinition(definition, requestedToolIds, requestedSkillIds) {
1678
+ return restrictRivusAgentDefinitionGrants(definition, {
1679
+ memory: {
1680
+ scopes: [],
1681
+ tool: false
1682
+ },
1683
+ runtimeToolIds: [],
1684
+ skillIds: requestedSkillIds,
1685
+ toolIds: requestedToolIds
1686
+ });
1687
+ }
1688
+ //#endregion
1689
+ //#region src/modules/deployment-control/application/resolution/deployment-resolution.ts
1690
+ var RivusPluginLoadError = class extends Error {
1691
+ pluginId;
1692
+ moduleSpecifier;
1693
+ name = "RivusPluginLoadError";
1694
+ constructor(pluginId, moduleSpecifier, message, options) {
1695
+ super(message, options);
1696
+ this.pluginId = pluginId;
1697
+ this.moduleSpecifier = moduleSpecifier;
1698
+ }
1699
+ };
1700
+ function resolveRivusDeployment(input) {
1701
+ return Effect.gen(function* () {
1702
+ yield* Effect.try({
1703
+ try: () => validateRivusDeploymentManifest(input.manifest),
1704
+ catch: toDeploymentFailure
1312
1705
  });
1313
- return shutdown;
1314
- };
1315
- return {
1316
- handle,
1317
- install: () => {
1318
- for (const signal of options.signals ?? DEFAULT_SIGNALS) options.signalSource.on(signal, () => {
1319
- handle(signal);
1706
+ const catalog = input.agentCatalog.createPluginCatalog();
1707
+ const pluginStatuses = [];
1708
+ const statusByPlugin = /* @__PURE__ */ new Map();
1709
+ for (const declaration of input.manifest.plugins) {
1710
+ const loaded = yield* input.pluginLoader.load({
1711
+ deploymentRoot: input.deploymentRoot,
1712
+ module: declaration.module,
1713
+ pluginId: declaration.id
1714
+ }).pipe(Effect.flatMap((plugin) => Effect.try({
1715
+ try: () => {
1716
+ if (plugin.manifest.id !== declaration.id) throw new Error(`plugin manifest id ${plugin.manifest.id} does not match declaration ${declaration.id}`);
1717
+ catalog.registerPlugin(plugin);
1718
+ return plugin;
1719
+ },
1720
+ catch: toDeploymentFailure
1721
+ })), Effect.either);
1722
+ if (Either.isLeft(loaded)) {
1723
+ const message = formatDeploymentFailure(loaded.left);
1724
+ if (declaration.required) return yield* Effect.fail(new RivusPluginLoadError(declaration.id, declaration.module, `required plugin ${declaration.id} failed to load: ${message}`, { cause: loaded.left }));
1725
+ const status = Object.freeze({
1726
+ error: message,
1727
+ id: declaration.id,
1728
+ module: declaration.module,
1729
+ required: false,
1730
+ status: "failed"
1731
+ });
1732
+ pluginStatuses.push(status);
1733
+ statusByPlugin.set(declaration.id, status);
1734
+ continue;
1735
+ }
1736
+ const status = Object.freeze({
1737
+ id: declaration.id,
1738
+ module: declaration.module,
1739
+ required: declaration.required,
1740
+ status: "loaded",
1741
+ version: loaded.right.manifest.version
1320
1742
  });
1321
- },
1322
- stopping: () => shutdown !== void 0
1323
- };
1743
+ pluginStatuses.push(status);
1744
+ statusByPlugin.set(declaration.id, status);
1745
+ }
1746
+ const agentStatuses = [];
1747
+ const definitions = [];
1748
+ for (const agent of input.manifest.agents) {
1749
+ const pluginStatus = statusByPlugin.get(agent.pluginId);
1750
+ if (pluginStatus.status === "failed") {
1751
+ agentStatuses.push(Object.freeze({
1752
+ agentId: agent.agentId,
1753
+ pluginId: agent.pluginId,
1754
+ profileId: agent.profileId,
1755
+ reason: `plugin ${agent.pluginId} is unavailable: ${pluginStatus.error}`,
1756
+ status: "disabled"
1757
+ }));
1758
+ continue;
1759
+ }
1760
+ const resolved = yield* Effect.try({
1761
+ try: () => {
1762
+ const baseDefinition = input.agentCatalog.resolve(catalog, agent);
1763
+ const projectSpace = agent.projectSpaceId ? input.manifest.projectSpaces?.find(({ id }) => id === agent.projectSpaceId) : void 0;
1764
+ return projectSpace ? deepFreeze({
1765
+ ...baseDefinition,
1766
+ projectSpaceRevision: createRivusProjectSpaceDeclarationRevision(projectSpace)
1767
+ }) : baseDefinition;
1768
+ },
1769
+ catch: toDeploymentFailure
1770
+ }).pipe(Effect.either);
1771
+ if (Either.isLeft(resolved)) {
1772
+ const declaration = input.manifest.plugins.find(({ id }) => id === agent.pluginId);
1773
+ 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 }));
1774
+ agentStatuses.push(Object.freeze({
1775
+ agentId: agent.agentId,
1776
+ pluginId: agent.pluginId,
1777
+ profileId: agent.profileId,
1778
+ reason: resolved.left.message,
1779
+ status: "disabled"
1780
+ }));
1781
+ continue;
1782
+ }
1783
+ definitions.push(resolved.right);
1784
+ agentStatuses.push(Object.freeze({
1785
+ agentId: agent.agentId,
1786
+ definition: resolved.right,
1787
+ pluginId: agent.pluginId,
1788
+ profileId: agent.profileId,
1789
+ status: "enabled"
1790
+ }));
1791
+ }
1792
+ const agentStatusById = new Map(agentStatuses.map((agent) => [agent.agentId, agent]));
1793
+ const automationTemplates = new Map(catalog.snapshot().automations.map((template) => [template.id, template]));
1794
+ const automationDefinitions = [];
1795
+ for (const automation of input.manifest.automations ?? []) {
1796
+ const agent = agentStatusById.get(automation.agentId);
1797
+ if (!agent || agent.status === "disabled" || !agent.definition) continue;
1798
+ const deliveryEndpoint = input.manifest.endpoints.find(({ id }) => id === automation.delivery.endpointId);
1799
+ const presentationAgent = agentStatusById.get(deliveryEndpoint.agentId);
1800
+ if (!presentationAgent || presentationAgent.status === "disabled" || !presentationAgent.definition) continue;
1801
+ const template = automationTemplates.get(automation.templateId);
1802
+ if (!template) return yield* Effect.fail(/* @__PURE__ */ new Error(`automation ${automation.id} references unknown template: ${automation.templateId}`));
1803
+ 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}`));
1804
+ const definition = yield* Effect.try({
1805
+ try: () => deepFreeze({
1806
+ ...automation,
1807
+ runtimeDefinition: resolveRivusAutomationRuntimeDefinition(agent.definition, template.requestedToolIds, template.requestedSkillIds),
1808
+ template
1809
+ }),
1810
+ catch: toDeploymentFailure
1811
+ });
1812
+ automationDefinitions.push(definition);
1813
+ }
1814
+ return Object.freeze({
1815
+ agents: Object.freeze(agentStatuses),
1816
+ automationDefinitions: Object.freeze(automationDefinitions),
1817
+ catalog,
1818
+ definitions: Object.freeze(definitions),
1819
+ manifest: input.manifest,
1820
+ plugins: Object.freeze(pluginStatuses)
1821
+ });
1822
+ });
1324
1823
  }
1325
1824
  //#endregion
1326
- //#region src/application/support/stable-id.ts
1327
- function createStableId(prefix, value) {
1328
- return `${prefix}:${createHash("sha256").update(JSON.stringify(value)).digest("hex")}`;
1825
+ //#region src/adapters/deployment/manifest/node-rivus-deployment-manifest.ts
1826
+ var RivusDeploymentManifestError = class extends Error {
1827
+ manifestPath;
1828
+ name = "RivusDeploymentManifestError";
1829
+ constructor(manifestPath, message, options) {
1830
+ super(message, options);
1831
+ this.manifestPath = manifestPath;
1832
+ }
1833
+ };
1834
+ function loadRivusDeploymentManifest(manifestPath, options = {}) {
1835
+ const maxBytes = options.maxBytes ?? 1024 * 1024;
1836
+ return Effect.tryPromise({
1837
+ try: async () => {
1838
+ const metadata = await stat(manifestPath);
1839
+ if (!metadata.isFile()) throw new Error("deployment manifest must be a regular file");
1840
+ if (metadata.size > maxBytes) throw new Error(`deployment manifest exceeds ${maxBytes} byte limit`);
1841
+ const bytes = await readFile(manifestPath);
1842
+ const text = new TextDecoder("utf-8", { fatal: true }).decode(bytes);
1843
+ return parseRivusDeploymentManifest(JSON.parse(text));
1844
+ },
1845
+ catch: (cause) => cause instanceof RivusDeploymentManifestError ? cause : new RivusDeploymentManifestError(manifestPath, `failed to load Rivus deployment manifest: ${cause instanceof Error ? cause.message : String(cause)}`, { cause })
1846
+ });
1329
1847
  }
1330
1848
  //#endregion
1331
- //#region src/application/host/agent-instance-registry.ts
1849
+ //#region src/modules/agent-host/domain/instance/agent-instance.ts
1332
1850
  var AgentInstanceConflict = class extends Error {
1333
1851
  name = "AgentInstanceConflict";
1334
1852
  };
1335
- function createAgentInstanceRegistry(options = {}) {
1853
+ function agentInstanceBindingKey(binding, agentId) {
1854
+ const bindingId = binding.kind === "endpoint" ? binding.endpointId : binding.kind === "automation" ? binding.automationId : binding.agentId;
1855
+ return `${binding.kind}:${bindingId}:${agentId}`;
1856
+ }
1857
+ function createAgentInstanceRecord(input) {
1858
+ return Object.freeze({
1859
+ agentId: input.agentId,
1860
+ binding: Object.freeze({ ...input.binding }),
1861
+ bindingKey: agentInstanceBindingKey(input.binding, input.agentId),
1862
+ instanceId: input.instanceId,
1863
+ runtimeGenerationId: input.runtimeGenerationId
1864
+ });
1865
+ }
1866
+ //#endregion
1867
+ //#region src/modules/agent-host/application/registry/agent-instance-registry.ts
1868
+ function createEffectAgentInstanceRegistry(identity, options = {}) {
1336
1869
  const records = /* @__PURE__ */ new Map();
1337
1870
  for (const record of options.initialRecords ?? []) {
1871
+ if (agentInstanceBindingKey(record.binding, record.agentId) !== record.bindingKey) throw new AgentInstanceConflict(`invalid binding key: ${record.bindingKey}`);
1338
1872
  if (records.has(record.bindingKey)) throw new AgentInstanceConflict(`duplicate binding: ${record.bindingKey}`);
1339
- records.set(record.bindingKey, Object.freeze({ ...record }));
1873
+ records.set(record.bindingKey, Object.freeze({
1874
+ ...record,
1875
+ binding: Object.freeze({ ...record.binding })
1876
+ }));
1340
1877
  }
1341
- const resolveBinding = (binding, definition) => {
1342
- const runtimeGenerationId = createStableId("generation", {
1343
- agentId: definition.agentId,
1344
- profileRevision: definition.profileRevision,
1345
- projectSpaceId: definition.projectSpaceId ?? null,
1346
- projectSpaceRevision: definition.projectSpaceRevision ?? null,
1347
- skillGrantRevision: definition.skillGrantSet.revision,
1348
- toolGrantRevision: definition.toolGrantSet.revision
1349
- });
1350
- const bindingId = binding.kind === "endpoint" ? binding.endpointId : binding.kind === "automation" ? binding.automationId : binding.agentId;
1351
- const bindingKey = `${binding.kind}:${bindingId}:${definition.agentId}`;
1352
- const existing = records.get(bindingKey);
1353
- if (existing) {
1354
- if (existing.runtimeGenerationId !== runtimeGenerationId) throw new AgentInstanceConflict(`binding ${bindingKey} belongs to a different runtime generation`);
1355
- return existing;
1356
- }
1357
- const record = Object.freeze({
1358
- agentId: definition.agentId,
1359
- binding: Object.freeze({ ...binding }),
1360
- bindingKey,
1361
- instanceId: createStableId("instance", {
1362
- bindingKey,
1878
+ const resolveBinding = (binding, definition) => Effect.try({
1879
+ try: () => {
1880
+ const runtimeGenerationId = identity.createRuntimeGenerationId({
1881
+ agentId: definition.agentId,
1882
+ profileRevision: definition.profileRevision,
1883
+ projectSpaceId: definition.projectSpaceId ?? null,
1884
+ projectSpaceRevision: definition.projectSpaceRevision ?? null,
1885
+ runtimeToolGrantRevision: definition.runtimeToolGrantSet.revision,
1886
+ skillGrantRevision: definition.skillGrantSet.revision,
1887
+ toolGrantRevision: definition.toolGrantSet.revision
1888
+ });
1889
+ const bindingKey = agentInstanceBindingKey(binding, definition.agentId);
1890
+ const existing = records.get(bindingKey);
1891
+ if (existing) {
1892
+ if (existing.runtimeGenerationId !== runtimeGenerationId) throw new AgentInstanceConflict(`binding ${bindingKey} belongs to a different runtime generation`);
1893
+ return existing;
1894
+ }
1895
+ const record = createAgentInstanceRecord({
1896
+ agentId: definition.agentId,
1897
+ binding,
1898
+ instanceId: identity.createInstanceId({
1899
+ bindingKey,
1900
+ runtimeGenerationId
1901
+ }),
1363
1902
  runtimeGenerationId
1364
- }),
1365
- runtimeGenerationId
1366
- });
1367
- records.set(bindingKey, record);
1368
- return record;
1369
- };
1903
+ });
1904
+ records.set(bindingKey, record);
1905
+ return record;
1906
+ },
1907
+ catch: (error) => error instanceof AgentInstanceConflict ? error : new AgentInstanceConflict(String(error))
1908
+ });
1370
1909
  return {
1371
1910
  resolveAutomation: (automationId, definition) => resolveBinding({
1372
1911
  automationId,
@@ -1380,425 +1919,529 @@ function createAgentInstanceRegistry(options = {}) {
1380
1919
  endpointId,
1381
1920
  kind: "endpoint"
1382
1921
  }, definition),
1383
- snapshot: () => Object.freeze([...records.values()])
1922
+ snapshot: () => Effect.sync(() => Object.freeze([...records.values()]))
1384
1923
  };
1385
1924
  }
1386
1925
  //#endregion
1387
- //#region src/application/host/agent-runtime-pool.ts
1388
- var AgentInstanceBusy = class extends Error {
1389
- name = "AgentInstanceBusy";
1926
+ //#region src/modules/agent-host/application/routing/agent-host.ts
1927
+ var InvalidAgentHostBinding = class extends Error {
1928
+ name = "InvalidAgentHostBinding";
1390
1929
  };
1391
- var AgentRuntimeDisposed = class extends Error {
1392
- name = "AgentRuntimeDisposed";
1393
- };
1394
- const DEFAULT_RUNTIME_DISPOSE_TIMEOUT_MS = 1e4;
1395
- function createAgentRuntimePool(options) {
1396
- const runtimes = /* @__PURE__ */ new Map();
1397
- const active = /* @__PURE__ */ new Map();
1398
- const resolveRuntime = (instance) => {
1399
- const current = runtimes.get(instance.instanceId);
1400
- if (current) return current;
1401
- let created;
1402
- created = Promise.resolve(options.createRuntime(instance)).catch((error) => {
1403
- if (runtimes.get(instance.instanceId) === created) runtimes.delete(instance.instanceId);
1404
- throw error;
1405
- });
1406
- runtimes.set(instance.instanceId, created);
1407
- return created;
1408
- };
1409
- return {
1410
- registry: options.registry,
1411
- cancel: async (instance, input) => {
1412
- const runtime = runtimes.get(instance.instanceId);
1413
- return runtime ? (await runtime).cancel?.(input) ?? false : false;
1414
- },
1415
- steer: async (instance, input) => {
1416
- const runtime = runtimes.get(instance.instanceId);
1417
- return runtime ? (await runtime).steer?.(input) ?? false : false;
1418
- },
1419
- disposeAll: async () => {
1420
- const pendingRuntimes = [...runtimes.values()];
1421
- runtimes.clear();
1422
- active.clear();
1423
- await withTimeout(Promise.all(pendingRuntimes.map(async (runtime) => {
1424
- await (await runtime).dispose?.();
1425
- })), options.disposeTimeoutMs ?? DEFAULT_RUNTIME_DISPOSE_TIMEOUT_MS, "Agent runtime disposal timed out");
1426
- },
1427
- run: async (instance, input) => {
1428
- const pendingRuntime = resolveRuntime(instance);
1429
- const runtime = await pendingRuntime;
1430
- if (runtimes.get(instance.instanceId) !== pendingRuntime) throw new AgentRuntimeDisposed(`agent runtime was disposed before run start: ${instance.instanceId}`);
1431
- if (runtime.concurrency === "managed") return runtime.run(input);
1432
- if (active.has(instance.instanceId)) throw new AgentInstanceBusy(`agent instance already has an active run: ${instance.instanceId}`);
1433
- const activeToken = Symbol(instance.instanceId);
1434
- active.set(instance.instanceId, activeToken);
1435
- try {
1436
- return await runtime.run(input);
1437
- } finally {
1438
- if (active.get(instance.instanceId) === activeToken) active.delete(instance.instanceId);
1439
- }
1440
- }
1441
- };
1442
- }
1443
- async function withTimeout(promise, timeoutMs, message) {
1444
- let timeout;
1445
- try {
1446
- return await Promise.race([promise, new Promise((_resolve, reject) => {
1447
- timeout = setTimeout(() => reject(/* @__PURE__ */ new Error(`${message} after ${timeoutMs}ms`)), timeoutMs);
1448
- })]);
1449
- } finally {
1450
- if (timeout) clearTimeout(timeout);
1451
- }
1930
+ function createEffectAgentHost(options) {
1931
+ return Effect.gen(function* () {
1932
+ const definitions = new Map(options.definitions.map((definition) => [definition.agentId, definition]));
1933
+ const endpoints = /* @__PURE__ */ new Map();
1934
+ const automations = /* @__PURE__ */ new Map();
1935
+ const backgroundSessions = /* @__PURE__ */ new Map();
1936
+ for (const endpoint of options.endpoints) {
1937
+ if (endpoints.has(endpoint.id)) return yield* Effect.fail(new InvalidAgentHostBinding(`duplicate endpoint: ${endpoint.id}`));
1938
+ const definition = definitions.get(endpoint.agentId);
1939
+ if (!definition) return yield* Effect.fail(new InvalidAgentHostBinding(`unknown endpoint agent: ${endpoint.agentId}`));
1940
+ if (!definition.endpointIds.includes(endpoint.id)) return yield* Effect.fail(new InvalidAgentHostBinding(`endpoint ${endpoint.id} is not declared by ${endpoint.agentId}`));
1941
+ endpoints.set(endpoint.id, yield* options.registry.resolveEndpoint(endpoint.id, definition));
1942
+ }
1943
+ for (const automation of options.automations ?? []) {
1944
+ if (automations.has(automation.id)) return yield* Effect.fail(new InvalidAgentHostBinding(`duplicate automation: ${automation.id}`));
1945
+ if (!definitions.has(automation.definition.agentId)) return yield* Effect.fail(new InvalidAgentHostBinding(`unknown automation agent: ${automation.definition.agentId}`));
1946
+ automations.set(automation.id, yield* options.registry.resolveAutomation(automation.id, automation.definition));
1947
+ }
1948
+ for (const backgroundSession of options.backgroundSessions ?? []) {
1949
+ if (backgroundSession.definition.agentId !== backgroundSession.agentId) return yield* Effect.fail(new InvalidAgentHostBinding(`background session ${backgroundSession.agentId} cannot bind definition ${backgroundSession.definition.agentId}`));
1950
+ if (backgroundSessions.has(backgroundSession.agentId)) return yield* Effect.fail(new InvalidAgentHostBinding(`duplicate background session agent: ${backgroundSession.agentId}`));
1951
+ if (!definitions.has(backgroundSession.agentId)) return yield* Effect.fail(new InvalidAgentHostBinding(`unknown background session agent: ${backgroundSession.agentId}`));
1952
+ backgroundSessions.set(backgroundSession.agentId, yield* options.registry.resolveBackgroundSession(backgroundSession.agentId, backgroundSession.definition));
1953
+ }
1954
+ const resolve = (records, id, message) => {
1955
+ const instance = records.get(id);
1956
+ return instance ? Effect.succeed(instance) : Effect.fail(new InvalidAgentHostBinding(message));
1957
+ };
1958
+ const resolveEndpoint = (endpointId) => resolve(endpoints, endpointId, `unknown endpoint: ${endpointId}`);
1959
+ const resolveAutomation = (automationId) => resolve(automations, automationId, `unknown automation: ${automationId}`);
1960
+ const resolveBackgroundSession = (agentId) => resolve(backgroundSessions, agentId, `unknown background session agent: ${agentId}`);
1961
+ return {
1962
+ cancelBackgroundSession: (agentId, input) => resolveBackgroundSession(agentId).pipe(Effect.flatMap((instance) => options.runtime.cancel(instance, input))),
1963
+ cancelEndpoint: (endpointId, input) => resolveEndpoint(endpointId).pipe(Effect.flatMap((instance) => options.runtime.cancel(instance, input))),
1964
+ dispose: () => options.runtime.disposeAll(),
1965
+ handleAutomation: (automationId, input) => resolveAutomation(automationId).pipe(Effect.flatMap((instance) => options.runtime.run(instance, input))),
1966
+ handleBackgroundSession: (agentId, input) => resolveBackgroundSession(agentId).pipe(Effect.flatMap((instance) => options.runtime.run(instance, input))),
1967
+ handleEndpoint: (endpointId, input) => resolveEndpoint(endpointId).pipe(Effect.flatMap((instance) => options.runtime.run(instance, input))),
1968
+ resolveAutomation,
1969
+ resolveBackgroundSession,
1970
+ resolveEndpoint,
1971
+ steerEndpoint: (endpointId, input) => resolveEndpoint(endpointId).pipe(Effect.flatMap((instance) => options.runtime.steer(instance, input)))
1972
+ };
1973
+ });
1452
1974
  }
1453
1975
  //#endregion
1454
- //#region src/application/host/rivus-agent-host.ts
1455
- var InvalidRivusEndpointBinding = class extends Error {
1456
- name = "InvalidRivusEndpointBinding";
1976
+ //#region src/platform/runtime/runtime-pool.ts
1977
+ var RuntimeResourceBusy = class extends Error {
1978
+ name = "RuntimeResourceBusy";
1457
1979
  };
1458
- function createRivusAgentHost(options) {
1459
- const definitions = new Map(options.definitions.map((definition) => [definition.agentId, definition]));
1460
- const endpoints = /* @__PURE__ */ new Map();
1461
- const automations = /* @__PURE__ */ new Map();
1462
- const backgroundSessions = /* @__PURE__ */ new Map();
1463
- for (const endpoint of options.endpoints) {
1464
- if (endpoints.has(endpoint.id)) throw new InvalidRivusEndpointBinding(`duplicate endpoint: ${endpoint.id}`);
1465
- const definition = definitions.get(endpoint.agentId);
1466
- if (!definition) throw new InvalidRivusEndpointBinding(`unknown endpoint agent: ${endpoint.agentId}`);
1467
- if (!definition.endpointIds.includes(endpoint.id)) throw new InvalidRivusEndpointBinding(`endpoint ${endpoint.id} is not declared by ${endpoint.agentId}`);
1468
- endpoints.set(endpoint.id, options.runtimePool.registry.resolveEndpoint(endpoint.id, definition));
1469
- }
1470
- for (const automation of options.automations ?? []) {
1471
- if (automations.has(automation.id)) throw new InvalidRivusEndpointBinding(`duplicate automation: ${automation.id}`);
1472
- if (!definitions.has(automation.definition.agentId)) throw new InvalidRivusEndpointBinding(`unknown automation agent: ${automation.definition.agentId}`);
1473
- automations.set(automation.id, options.runtimePool.registry.resolveAutomation(automation.id, automation.definition));
1474
- }
1475
- for (const backgroundSession of options.backgroundSessions ?? []) {
1476
- if (backgroundSessions.has(backgroundSession.agentId)) throw new InvalidRivusEndpointBinding(`duplicate background session agent: ${backgroundSession.agentId}`);
1477
- if (!definitions.has(backgroundSession.agentId)) throw new InvalidRivusEndpointBinding(`unknown background session agent: ${backgroundSession.agentId}`);
1478
- backgroundSessions.set(backgroundSession.agentId, options.runtimePool.registry.resolveBackgroundSession(backgroundSession.agentId, backgroundSession.definition));
1479
- }
1480
- const resolveEndpoint = (endpointId) => {
1481
- const instance = endpoints.get(endpointId);
1482
- if (!instance) throw new InvalidRivusEndpointBinding(`unknown endpoint: ${endpointId}`);
1483
- return instance;
1484
- };
1485
- const resolveAutomation = (automationId) => {
1486
- const instance = automations.get(automationId);
1487
- if (!instance) throw new InvalidRivusEndpointBinding(`unknown automation: ${automationId}`);
1488
- return instance;
1489
- };
1490
- const resolveBackgroundSession = (agentId) => {
1491
- const instance = backgroundSessions.get(agentId);
1492
- if (!instance) throw new InvalidRivusEndpointBinding(`unknown background session agent: ${agentId}`);
1493
- return instance;
1494
- };
1980
+ var RuntimeResourceDisposed = class extends Error {
1981
+ name = "RuntimeResourceDisposed";
1982
+ };
1983
+ var RuntimePoolDisposalTimedOut = class extends Error {
1984
+ name = "RuntimePoolDisposalTimedOut";
1985
+ };
1986
+ const DEFAULT_RUNTIME_DISPOSE_TIMEOUT_MS = 1e4;
1987
+ function createEffectRuntimePool(options) {
1988
+ const runtimes = createRuntimeCache();
1989
+ const active = /* @__PURE__ */ new Map();
1990
+ const lifecycle = Effect.unsafeMakeSemaphore(1);
1991
+ const existingRuntime = (instance) => Effect.gen(function* () {
1992
+ const selected = yield* runtimes.getExisting(instance.instanceId);
1993
+ if (!selected || !(yield* runtimes.isCurrent(instance.instanceId, selected.entry))) return void 0;
1994
+ return selected.runtime;
1995
+ });
1495
1996
  return {
1496
- cancelBackgroundSession: (agentId, input) => options.runtimePool.cancel(resolveBackgroundSession(agentId), input),
1497
- cancelEndpoint: (endpointId, input) => options.runtimePool.cancel(resolveEndpoint(endpointId), input),
1498
- steerEndpoint: (endpointId, input) => options.runtimePool.steer(resolveEndpoint(endpointId), input),
1499
- handleAutomation: (automationId, input) => options.runtimePool.run(resolveAutomation(automationId), input),
1500
- handleBackgroundSession: (agentId, input) => options.runtimePool.run(resolveBackgroundSession(agentId), input),
1501
- handleEndpoint: (endpointId, input) => options.runtimePool.run(resolveEndpoint(endpointId), input),
1502
- resolveAutomation,
1503
- resolveBackgroundSession,
1504
- resolveEndpoint
1997
+ cancel: (instance, input) => invokeRuntimeControl(existingRuntime(instance), (runtime) => runtime.cancel?.(input)),
1998
+ disposeAll: () => Effect.gen(function* () {
1999
+ const entries = yield* runtimes.drain();
2000
+ yield* lifecycle.withPermits(1)(Effect.sync(() => active.clear()));
2001
+ yield* disposeRuntimeCacheEntries({
2002
+ dispose: (runtime) => runtime.dispose?.() ?? Effect.void,
2003
+ entries,
2004
+ failureMessage: "runtime pool disposal failed",
2005
+ timeout: {
2006
+ milliseconds: options.disposeTimeoutMs ?? DEFAULT_RUNTIME_DISPOSE_TIMEOUT_MS,
2007
+ onTimeout: () => new RuntimePoolDisposalTimedOut(`runtime pool disposal timed out after ${options.disposeTimeoutMs ?? DEFAULT_RUNTIME_DISPOSE_TIMEOUT_MS}ms`)
2008
+ }
2009
+ });
2010
+ }),
2011
+ run: (instance, input) => Effect.gen(function* () {
2012
+ const selected = yield* runtimes.getOrCreate(instance.instanceId, () => options.createRuntime(instance));
2013
+ if (!(yield* runtimes.isCurrent(instance.instanceId, selected.entry))) return yield* Effect.fail(new RuntimeResourceDisposed(`runtime was disposed before run start: ${instance.instanceId}`));
2014
+ if (selected.runtime.concurrency === "managed") return yield* selected.runtime.run(input);
2015
+ const token = yield* lifecycle.withPermits(1)(Effect.gen(function* () {
2016
+ if (!(yield* runtimes.isCurrent(instance.instanceId, selected.entry))) return yield* Effect.fail(new RuntimeResourceDisposed(`runtime was disposed before run start: ${instance.instanceId}`));
2017
+ if (active.has(instance.instanceId)) return yield* Effect.fail(new RuntimeResourceBusy(`runtime already has an active run: ${instance.instanceId}`));
2018
+ const activeToken = Symbol(instance.instanceId);
2019
+ active.set(instance.instanceId, activeToken);
2020
+ return activeToken;
2021
+ }));
2022
+ return yield* selected.runtime.run(input).pipe(Effect.ensuring(lifecycle.withPermits(1)(Effect.sync(() => {
2023
+ if (active.get(instance.instanceId) === token) active.delete(instance.instanceId);
2024
+ }))));
2025
+ }),
2026
+ steer: (instance, input) => invokeRuntimeControl(existingRuntime(instance), (runtime) => runtime.steer?.(input))
1505
2027
  };
1506
2028
  }
1507
2029
  //#endregion
1508
- //#region src/application/deployment/rivus-deployment-daemon.ts
1509
- var RivusDeploymentDaemonLifecycleError = class extends Error {
1510
- name = "RivusDeploymentDaemonLifecycleError";
1511
- };
1512
- var RivusDeploymentReadinessError = class extends Error {
1513
- endpointIds;
1514
- name = "RivusDeploymentReadinessError";
1515
- constructor(endpointIds) {
1516
- super(`required endpoint startup failed: ${endpointIds.join(", ")}`);
1517
- this.endpointIds = endpointIds;
1518
- }
2030
+ //#region src/modules/agent-host/application/runtime/agent-host-runtime.ts
2031
+ var AgentInstanceBusy = class extends Error {
2032
+ name = "AgentInstanceBusy";
1519
2033
  };
1520
- var RivusDeploymentAutomationReadinessError = class extends Error {
1521
- automationIds;
1522
- name = "RivusDeploymentAutomationReadinessError";
1523
- constructor(automationIds) {
1524
- super(`required automation startup failed: ${automationIds.join(", ")}`);
1525
- this.automationIds = automationIds;
1526
- }
2034
+ var AgentRuntimeDisposed = class extends Error {
2035
+ name = "AgentRuntimeDisposed";
1527
2036
  };
1528
- async function createRivusDeploymentDaemon(options) {
1529
- const deployment = await loadRivusDeployment(options);
1530
- if ((deployment.manifest.projectSpaces?.length ?? 0) > 0 && !options.resolveProjectSpace) throw new RivusDeploymentDaemonLifecycleError("deployment bootstrap does not provide a Project Space resolver");
1531
- const projectSpaces = new Map(await Promise.all((deployment.manifest.projectSpaces ?? []).map(async (declaration) => {
1532
- const projectSpace = await options.resolveProjectSpace({
1533
- declaration,
1534
- deploymentRoot: options.deploymentRoot
1535
- });
1536
- return [projectSpace.id, projectSpace];
1537
- })));
1538
- const definitions = new Map(deployment.definitions.map((definition) => [definition.agentId, definition]));
1539
- const automationDefinitions = new Map(deployment.automationDefinitions.map((definition) => [definition.id, definition]));
1540
- const backgroundSessionsConfig = deployment.manifest.backgroundSessions;
1541
- const backgroundDefinitions = /* @__PURE__ */ new Map();
1542
- if (backgroundSessionsConfig?.enabled) for (const agent of deployment.agents) {
1543
- if (agent.status !== "enabled" || !agent.definition) continue;
1544
- backgroundDefinitions.set(agent.agentId, narrowBackgroundSessionDefinition(agent.definition));
1545
- }
1546
- const runtimePool = createAgentRuntimePool({
1547
- createRuntime: (instance) => {
1548
- 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);
1549
- if (!definition) throw new RivusDeploymentDaemonLifecycleError(`runtime instance references unknown agent: ${instance.agentId}`);
1550
- const projectSpace = definition.projectSpaceId ? projectSpaces.get(definition.projectSpaceId) : void 0;
1551
- return options.createRuntime({
1552
- ...instance,
1553
- catalog: deployment.catalog,
1554
- definition,
1555
- ...projectSpace ? { projectSpace } : {}
1556
- });
1557
- },
1558
- registry: createAgentInstanceRegistry(options.initialInstanceRecords ? { initialRecords: options.initialInstanceRecords } : {})
2037
+ //#endregion
2038
+ //#region src/modules/agent-host/infrastructure/runtime/effect-agent-runtime-pool.ts
2039
+ function createAgentHostRuntimePool(factory, disposeTimeoutMs) {
2040
+ const pool = createEffectRuntimePool({
2041
+ createRuntime: (instance) => factory.create(instance),
2042
+ ...disposeTimeoutMs === void 0 ? {} : { disposeTimeoutMs }
1559
2043
  });
1560
- const agentStatuses = new Map(deployment.agents.map((agent) => [agent.agentId, agent]));
1561
- const slots = deployment.manifest.endpoints.map((definition) => {
1562
- const agentEnabled = agentStatuses.get(definition.agentId)?.status === "enabled";
1563
- return {
1564
- agentEnabled,
1565
- definition,
1566
- lifecycle: definition.enabled && agentEnabled ? "stopped" : "disabled"
1567
- };
2044
+ return {
2045
+ cancel: (instance, input) => pool.cancel(instance, input),
2046
+ disposeAll: () => pool.disposeAll().pipe(Effect.mapError(mapRuntimePoolError)),
2047
+ run: (instance, input) => pool.run(instance, input).pipe(Effect.mapError(mapRuntimePoolError)),
2048
+ steer: (instance, input) => pool.steer(instance, input)
2049
+ };
2050
+ }
2051
+ function mapRuntimePoolError(error) {
2052
+ if (error instanceof RuntimeResourceBusy) return new AgentInstanceBusy(error.message.replace(/^runtime /, "agent instance "));
2053
+ if (error instanceof RuntimeResourceDisposed) return new AgentRuntimeDisposed(error.message.replace(/^runtime /, "agent runtime "));
2054
+ if (error instanceof RuntimePoolDisposalTimedOut) return new AgentRuntimeDisposed(error.message.replace(/^runtime pool /, "agent runtime "));
2055
+ return error;
2056
+ }
2057
+ //#endregion
2058
+ //#region src/modules/agent-host/module.ts
2059
+ function createAgentInstanceRegistryModule(options = {}) {
2060
+ return createEffectAgentInstanceRegistry({
2061
+ createInstanceId: (input) => createStableId("instance", input),
2062
+ createRuntimeGenerationId: (input) => createStableId("generation", input)
2063
+ }, options);
2064
+ }
2065
+ function createAgentHostFromRuntimePort(options) {
2066
+ return createEffectAgentHost(options);
2067
+ }
2068
+ function createAgentHostRuntimePort(factory, disposeTimeoutMs) {
2069
+ return createAgentHostRuntimePool(factory, disposeTimeoutMs);
2070
+ }
2071
+ function createAgentHostModule(options) {
2072
+ const registry = createAgentInstanceRegistryModule(options.initialInstanceRecords ? { initialRecords: options.initialInstanceRecords } : {});
2073
+ const runtime = createAgentHostRuntimePort(options.runtimeFactory, options.disposeTimeoutMs);
2074
+ return createEffectAgentHost({
2075
+ ...options.automations ? { automations: options.automations } : {},
2076
+ ...options.backgroundSessions ? { backgroundSessions: options.backgroundSessions } : {},
2077
+ definitions: options.definitions,
2078
+ endpoints: options.endpoints,
2079
+ registry,
2080
+ runtime
1568
2081
  });
1569
- const automationSlots = (deployment.manifest.automations ?? []).map((definition) => {
1570
- const agentEnabled = agentStatuses.get(definition.agentId)?.status === "enabled";
1571
- const resolvedDefinition = automationDefinitions.get(definition.id);
1572
- return {
1573
- agentEnabled,
1574
- definition,
1575
- ...resolvedDefinition ? { resolvedDefinition } : {},
1576
- lifecycle: definition.enabled && agentEnabled ? "stopped" : "disabled"
1577
- };
2082
+ }
2083
+ //#endregion
2084
+ //#region src/adapters/deployment/bootstrap/process-deployment-adapters.ts
2085
+ function createProcessDeploymentControlPorts(factories, backgroundSessions, runEffect) {
2086
+ return {
2087
+ agentCatalog: createRivusAgentCatalog({ toolDescriptorProviders: [createRivusHostToolDescriptorProvider({ backgroundSessions })] }),
2088
+ agentHostFactory: { create: (input) => createAgentHostModule({
2089
+ automations: input.automations,
2090
+ backgroundSessions: input.backgroundSessions,
2091
+ definitions: input.definitions,
2092
+ endpoints: input.endpoints,
2093
+ ...input.initialInstanceRecords ? { initialInstanceRecords: input.initialInstanceRecords } : {},
2094
+ runtimeFactory: input.runtimeFactory
2095
+ }) },
2096
+ ...factories.createAutomation ? { automationFactory: { create: (input) => adaptAutomationFactory(factories.createAutomation, input, runEffect) } } : {},
2097
+ ...factories.createBackgroundSession ? { backgroundSessionFactory: { create: (input) => adaptBackgroundSessionFactory(factories.createBackgroundSession, input, runEffect) } } : {},
2098
+ endpointFactory: { create: (input) => adaptEndpointFactory(factories.createEndpoint, input, runEffect) },
2099
+ runtimeFactory: { create: (input) => Effect.tryPromise({
2100
+ try: () => Promise.resolve(factories.createRuntime(input)),
2101
+ catch: toError$3
2102
+ }).pipe(Effect.map((runtime) => toDeploymentRuntime(runtime, runEffect))) }
2103
+ };
2104
+ }
2105
+ function adaptEndpointFactory(create, input, runEffect) {
2106
+ return Effect.tryPromise({
2107
+ try: async () => {
2108
+ return toLifecycleAdapter(await create({
2109
+ agentId: input.agentId,
2110
+ cancel: (request) => runEffect(input.cancel(request)),
2111
+ definition: input.definition,
2112
+ endpointId: input.endpointId,
2113
+ handle: (request) => runEffect(input.handle(toEffectProcessAgentRuntimeInput(request))),
2114
+ instanceId: input.instanceId,
2115
+ ...input.projectSpaceId ? { projectSpaceId: input.projectSpaceId } : {},
2116
+ steer: (request) => runEffect(input.steer(request))
2117
+ }));
2118
+ },
2119
+ catch: toError$3
1578
2120
  });
1579
- const backgroundSessionSlot = backgroundSessionsConfig ? {
1580
- agentEnabled: backgroundDefinitions.size > 0,
1581
- definition: backgroundSessionsConfig,
1582
- lifecycle: backgroundSessionsConfig.enabled && backgroundDefinitions.size > 0 ? "stopped" : "disabled"
1583
- } : void 0;
1584
- const host = createRivusAgentHost({
1585
- automations: automationSlots.filter((slot) => slot.definition.enabled && slot.agentEnabled && slot.resolvedDefinition !== void 0).map((slot) => ({
1586
- definition: slot.resolvedDefinition.runtimeDefinition,
1587
- id: slot.definition.id
1588
- })),
1589
- ...backgroundSessionSlot ? { backgroundSessions: [...backgroundDefinitions].map(([agentId, definition]) => ({
1590
- agentId,
1591
- definition
1592
- })) } : {},
1593
- definitions: deployment.definitions,
1594
- endpoints: slots.filter((slot) => slot.definition.enabled && slot.agentEnabled).map((slot) => ({
1595
- agentId: slot.definition.agentId,
1596
- id: slot.definition.id
2121
+ }
2122
+ function adaptAutomationFactory(create, input, runEffect) {
2123
+ return Effect.tryPromise({
2124
+ try: async () => toLifecycleAdapter(await create({
2125
+ automationId: input.automationId,
2126
+ definition: input.definition,
2127
+ deliveryEndpoint: input.deliveryEndpoint,
2128
+ instanceId: input.instanceId,
2129
+ run: (request) => runEffect(input.run(request))
1597
2130
  })),
1598
- runtimePool
1599
- });
1600
- const slotById = new Map(slots.map((slot) => [slot.definition.id, slot]));
1601
- let lifecycle = "stopped";
1602
- const canRunIntake = () => lifecycle === "running" || lifecycle === "degraded";
1603
- const canRunEndpointIntake = (slot) => (canRunIntake() || lifecycle === "starting") && (slot.lifecycle === "running" || slot.lifecycle === "starting") && (slot.adapter?.running() ?? false);
1604
- const handleEndpoint = async (endpointId, input) => {
1605
- const slot = slotById.get(endpointId);
1606
- if (!slot) throw new RivusDeploymentDaemonLifecycleError(`unknown endpoint: ${endpointId}`);
1607
- if (!canRunEndpointIntake(slot)) throw new RivusDeploymentDaemonLifecycleError(`endpoint ${endpointId} cannot accept intake while ${slot.lifecycle}`);
1608
- return host.handleEndpoint(endpointId, input);
1609
- };
1610
- const endpointStatus = (slot) => Object.freeze({
1611
- ...componentStatus(slot),
1612
- agentId: slot.definition.agentId,
1613
- endpointId: slot.definition.id
1614
- });
1615
- const automationStatus = (slot) => Object.freeze({
1616
- ...componentStatus(slot),
1617
- agentId: slot.definition.agentId,
1618
- automationId: slot.definition.id
1619
- });
1620
- const backgroundSessionStatus = (slot) => Object.freeze({
1621
- ...componentStatus(slot),
1622
- ...slot.adapter?.status ? { supervisor: slot.adapter.status() } : {}
2131
+ catch: toError$3
1623
2132
  });
1624
- const allSlots = [
1625
- ...slots,
1626
- ...automationSlots,
1627
- ...backgroundSessionSlot ? [backgroundSessionSlot] : []
1628
- ];
1629
- const isReady = () => allSlots.every(isSlotReady);
1630
- const failedRequiredEndpointIds = () => slots.filter((slot) => slot.definition.enabled && slot.definition.required && slot.lifecycle !== "running").map((slot) => slot.definition.id);
1631
- const failedRequiredAutomationIds = () => automationSlots.filter((slot) => slot.definition.enabled && slot.definition.required && slot.lifecycle !== "running").map((slot) => slot.definition.id);
1632
- const assertRequiredReadiness = () => {
1633
- const endpointIds = failedRequiredEndpointIds();
1634
- if (endpointIds.length > 0) throw new RivusDeploymentReadinessError(Object.freeze(endpointIds));
1635
- const automationIds = failedRequiredAutomationIds();
1636
- if (automationIds.length > 0) throw new RivusDeploymentAutomationReadinessError(Object.freeze(automationIds));
1637
- };
1638
- const status = () => Object.freeze({
1639
- agents: deployment.agents,
1640
- automations: Object.freeze(automationSlots.map(automationStatus)),
1641
- ...backgroundSessionSlot ? { backgroundSessions: backgroundSessionStatus(backgroundSessionSlot) } : {},
1642
- defaultAgentId: deployment.manifest.defaultAgentId,
1643
- defaultEndpointId: deployment.manifest.defaultEndpointId,
1644
- endpoints: Object.freeze(slots.map(endpointStatus)),
1645
- lifecycle,
1646
- plugins: deployment.plugins,
1647
- ready: isReady(),
1648
- running: canRunIntake()
2133
+ }
2134
+ function adaptBackgroundSessionFactory(create, input, runEffect) {
2135
+ return Effect.tryPromise({
2136
+ try: async () => {
2137
+ const adapter = await create({
2138
+ agentIds: input.agentIds,
2139
+ cancel: (request) => runEffect(input.cancel(request)),
2140
+ config: input.config,
2141
+ run: (request) => runEffect(input.run({
2142
+ agentId: request.agentId,
2143
+ invocation: request.invocation,
2144
+ ...request.onUpdate ? { onUpdate: (update) => Effect.tryPromise({
2145
+ try: async () => request.onUpdate(update),
2146
+ catch: toError$3
2147
+ }) } : {},
2148
+ sessionKey: request.sessionKey,
2149
+ text: request.text
2150
+ }))
2151
+ });
2152
+ return {
2153
+ ...toLifecycleAdapter(adapter),
2154
+ ...adapter.status ? { status: () => adapter.status() } : {}
2155
+ };
2156
+ },
2157
+ catch: toError$3
1649
2158
  });
2159
+ }
2160
+ function toDeploymentRuntime(runtime, runEffect) {
2161
+ const adapted = toEffectAgentRuntime(runtime, runEffect);
2162
+ const cancel = adapted.cancel?.bind(adapted);
2163
+ const dispose = adapted.dispose?.bind(adapted);
2164
+ const steer = adapted.steer?.bind(adapted);
1650
2165
  return {
1651
- deployment,
1652
- handleDefault: (input) => handleEndpoint(deployment.manifest.defaultEndpointId, input),
1653
- handleEndpoint,
1654
- runDefaultAgent: (input) => host.handleEndpoint(deployment.manifest.defaultEndpointId, input),
1655
- running: canRunIntake,
1656
- start: async () => {
1657
- if (lifecycle === "running") return;
1658
- if (lifecycle === "degraded") {
1659
- assertRequiredReadiness();
1660
- return;
1661
- }
1662
- if (lifecycle !== "stopped") throw new RivusDeploymentDaemonLifecycleError(`cannot start deployment daemon while ${lifecycle}`);
1663
- lifecycle = "starting";
1664
- let degraded = false;
1665
- for (const slot of slots) {
1666
- const slotDegraded = await startSlot(slot, slot.agentEnabled, () => options.createEndpoint({
1667
- agentId: slot.definition.agentId,
1668
- cancel: (input) => host.cancelEndpoint(slot.definition.id, input),
1669
- definition: slot.definition,
1670
- endpointId: slot.definition.id,
1671
- handle: (input) => handleEndpoint(slot.definition.id, input),
1672
- instanceId: host.resolveEndpoint(slot.definition.id).instanceId,
1673
- steer: (input) => host.steerEndpoint(slot.definition.id, input),
1674
- ...definitions.get(slot.definition.agentId)?.projectSpaceId ? { projectSpaceId: definitions.get(slot.definition.agentId).projectSpaceId } : {}
1675
- }));
1676
- degraded ||= slotDegraded;
1677
- }
1678
- for (const slot of automationSlots) {
1679
- const resolvedDefinition = slot.resolvedDefinition;
1680
- const deliverySlot = slotById.get(slot.definition.delivery.endpointId);
1681
- const presentationReady = deliverySlot.lifecycle === "running" && (deliverySlot.adapter?.running() ?? false);
1682
- const slotDegraded = await startSlot(slot, slot.agentEnabled && resolvedDefinition !== void 0 && presentationReady, async () => {
1683
- if (!options.createAutomation) throw new RivusDeploymentDaemonLifecycleError("deployment bootstrap does not provide Automation adapters");
1684
- if (!resolvedDefinition) throw new RivusDeploymentDaemonLifecycleError(`automation definition not resolved: ${slot.definition.id}`);
1685
- return options.createAutomation({
1686
- automationId: slot.definition.id,
1687
- definition: resolvedDefinition,
1688
- deliveryEndpoint: deliverySlot.definition,
1689
- instanceId: host.resolveAutomation(slot.definition.id).instanceId,
1690
- run: (input) => host.handleAutomation(slot.definition.id, {
1691
- invocation: {
1692
- allowedActorOpenIds: [],
1693
- automationId: slot.definition.id,
1694
- endpointId: slot.definition.delivery.endpointId,
1695
- kind: "automation",
1696
- sourceMessageId: input.tickId,
1697
- tenantKey: "automation",
1698
- tickId: input.tickId
1699
- },
1700
- sessionKey: input.sessionKey,
1701
- text: input.text
1702
- })
1703
- });
1704
- });
1705
- degraded ||= slotDegraded;
1706
- }
1707
- if (backgroundSessionSlot) {
1708
- const slotDegraded = await startSlot(backgroundSessionSlot, true, async () => {
1709
- if (!options.createBackgroundSession) throw new RivusDeploymentDaemonLifecycleError("deployment bootstrap does not provide Background Session adapters");
1710
- return options.createBackgroundSession({
1711
- agentIds: [...backgroundDefinitions.keys()],
1712
- cancel: (input) => host.cancelBackgroundSession(input.agentId, input),
1713
- config: backgroundSessionSlot.definition,
1714
- run: (input) => host.handleBackgroundSession(input.agentId, input)
1715
- });
1716
- });
1717
- degraded ||= slotDegraded;
1718
- }
1719
- lifecycle = degraded ? "degraded" : "running";
1720
- assertRequiredReadiness();
1721
- },
1722
- status,
1723
- stop: async () => {
1724
- if (lifecycle === "stopped") {
1725
- await runtimePool.disposeAll();
1726
- return;
1727
- }
1728
- if (lifecycle !== "running" && lifecycle !== "degraded" && lifecycle !== "cleanup-required") throw new RivusDeploymentDaemonLifecycleError(`cannot stop deployment daemon while ${lifecycle}`);
1729
- lifecycle = "stopping";
1730
- const errors = [];
1731
- await stopSlots(automationSlots, errors);
1732
- if (backgroundSessionSlot) await stopSlots([backgroundSessionSlot], errors);
1733
- await stopSlots(slots, errors);
1734
- try {
1735
- await runtimePool.disposeAll();
1736
- } catch (error) {
1737
- errors.push(error);
1738
- }
1739
- lifecycle = errors.length === 0 ? "stopped" : "cleanup-required";
1740
- if (errors.length === 1) throw errors[0];
1741
- if (errors.length > 1) throw new AggregateError(errors, "deployment daemon cleanup failed");
1742
- }
2166
+ ...adapted.concurrency ? { concurrency: adapted.concurrency } : {},
2167
+ ...cancel ? { cancel: (input) => cancel(input).pipe(Effect.mapError(toError$3)) } : {},
2168
+ ...dispose ? { dispose: () => dispose().pipe(Effect.mapError(toError$3)) } : {},
2169
+ run: (input) => adapted.run(input).pipe(Effect.mapError(toError$3)),
2170
+ ...steer ? { steer: (input) => steer(input).pipe(Effect.mapError(toError$3)) } : {}
1743
2171
  };
1744
2172
  }
1745
- function componentStatus(slot) {
2173
+ function toLifecycleAdapter(adapter) {
1746
2174
  return {
1747
- enabled: slot.definition.enabled,
1748
- ...slot.error ? { error: slot.error } : {},
1749
- lifecycle: slot.lifecycle,
1750
- required: slot.definition.required,
1751
- running: slot.lifecycle === "running" && (slot.adapter?.running() ?? false)
2175
+ running: () => adapter.running(),
2176
+ start: () => Effect.tryPromise({
2177
+ try: () => Promise.resolve(adapter.start()),
2178
+ catch: toError$3
2179
+ }),
2180
+ stop: () => Effect.tryPromise({
2181
+ try: () => Promise.resolve(adapter.stop()),
2182
+ catch: toError$3
2183
+ })
1752
2184
  };
1753
2185
  }
1754
- function isSlotReady(slot) {
1755
- return !slot.definition.enabled || !slot.definition.required || slot.lifecycle === "running" && (slot.adapter?.running() ?? false);
2186
+ function toEffectProcessAgentRuntimeInput(input) {
2187
+ return toEffectAgentRuntimeInput(input);
1756
2188
  }
1757
- async function startSlot(slot, available, create) {
1758
- if (!slot.definition.enabled) {
1759
- slot.lifecycle = "disabled";
1760
- return false;
2189
+ function toError$3(error) {
2190
+ return error instanceof Error ? error : new Error(String(error));
2191
+ }
2192
+ //#endregion
2193
+ //#region src/adapters/deployment/plugin/rivus-plugin-module.ts
2194
+ function resolveRivusPluginModule(module) {
2195
+ const candidate = "default" in module ? module.default : module;
2196
+ return (typeof candidate === "function" ? Effect.tryPromise({
2197
+ try: () => Promise.resolve(candidate()),
2198
+ catch: toError$2
2199
+ }) : 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"))));
2200
+ }
2201
+ function isRivusPlugin(value) {
2202
+ return value !== null && typeof value === "object" && "manifest" in value && "register" in value && typeof value.register === "function";
2203
+ }
2204
+ function toError$2(error) {
2205
+ return error instanceof Error ? error : new Error(String(error));
2206
+ }
2207
+ //#endregion
2208
+ //#region src/adapters/deployment/plugin/trusted-package-root.ts
2209
+ function findTrustedPackageRoot(packageManifestPath) {
2210
+ return Effect.tryPromise({
2211
+ try: async () => {
2212
+ const packageRoot = dirname(await realpath(packageManifestPath));
2213
+ let current = packageRoot;
2214
+ for (;;) {
2215
+ if (basename(current) === "node_modules") return current;
2216
+ const parent = dirname(current);
2217
+ if (parent === current) return packageRoot;
2218
+ current = parent;
2219
+ }
2220
+ },
2221
+ catch: (error) => error instanceof Error ? error : new Error(String(error))
2222
+ });
2223
+ }
2224
+ //#endregion
2225
+ //#region src/platform/filesystem/path-boundary.ts
2226
+ function isPathWithin(root, candidate) {
2227
+ const child = relative(root, candidate);
2228
+ return child === "" || !child.startsWith(`..${sep}`) && child !== ".." && !isAbsolute(child);
2229
+ }
2230
+ function assertPathWithin(root, candidate, message) {
2231
+ if (!isPathWithin(root, candidate)) throw new Error(message);
2232
+ }
2233
+ //#endregion
2234
+ //#region src/adapters/deployment/plugin/trusted-module-path.ts
2235
+ function validateTrustedModulePath(trustedRoot, resolvedPath, subject) {
2236
+ return Effect.try({
2237
+ try: () => {
2238
+ assertPathWithin(trustedRoot, resolvedPath, `${subject} resolves outside trusted module root: ${resolvedPath}`);
2239
+ return resolvedPath;
2240
+ },
2241
+ catch: (failure) => failure instanceof Error ? failure : new Error(String(failure))
2242
+ });
2243
+ }
2244
+ //#endregion
2245
+ //#region src/adapters/deployment/plugin/node-rivus-plugin-module-loader.ts
2246
+ function createNodeRivusPluginModuleLoader(options = {}) {
2247
+ return { load: (request) => loadNodeRivusPluginModule(request, options).pipe(Effect.flatMap(resolveRivusPluginModule)) };
2248
+ }
2249
+ function loadNodeRivusPluginModule(request, options = {}) {
2250
+ return resolveNodeRivusPluginModulePath(request, options).pipe(Effect.flatMap((resolvedRealpath) => Effect.tryPromise({
2251
+ try: () => import(pathToFileURL(resolvedRealpath).href),
2252
+ catch: toError$1
2253
+ })));
2254
+ }
2255
+ function resolveNodeRivusPluginModulePath(request, options = {}) {
2256
+ return Effect.gen(function* () {
2257
+ const deploymentRoot = yield* Effect.tryPromise({
2258
+ try: () => realpath(request.deploymentRoot),
2259
+ catch: toError$1
2260
+ });
2261
+ const relativeModule = request.module.startsWith("./") || request.module.startsWith("../");
2262
+ const resolutionManifest = relativeModule ? join(deploymentRoot, "package.json") : options.packageManifestPath ?? join(deploymentRoot, "package.json");
2263
+ const resolved = yield* Effect.try({
2264
+ try: () => createRequire(resolutionManifest).resolve(request.module),
2265
+ catch: toError$1
2266
+ });
2267
+ const resolvedRealpath = yield* Effect.tryPromise({
2268
+ try: () => realpath(resolved),
2269
+ catch: toError$1
2270
+ });
2271
+ return yield* validateTrustedModulePath(relativeModule ? deploymentRoot : options.packageManifestPath ? yield* findTrustedPackageRoot(options.packageManifestPath) : deploymentRoot, resolvedRealpath, `plugin module ${request.module}`);
2272
+ });
2273
+ }
2274
+ function toError$1(error) {
2275
+ return error instanceof Error ? error : new Error(String(error));
2276
+ }
2277
+ //#endregion
2278
+ //#region src/modules/deployment-control/module.ts
2279
+ function resolveRivusDeploymentModule(options) {
2280
+ return resolveRivusDeployment(options);
2281
+ }
2282
+ function createRivusDeploymentControlModule(options) {
2283
+ return Effect.gen(function* () {
2284
+ const deployment = yield* resolveRivusDeploymentModule(options);
2285
+ return yield* createRivusDeploymentControl({
2286
+ agentHostFactory: options.agentHostFactory,
2287
+ ...options.automationFactory ? { automationFactory: options.automationFactory } : {},
2288
+ ...options.backgroundSessionFactory ? { backgroundSessionFactory: options.backgroundSessionFactory } : {},
2289
+ deployment,
2290
+ deploymentRoot: options.deploymentRoot,
2291
+ endpointFactory: options.endpointFactory,
2292
+ ...options.initialInstanceRecords ? { initialInstanceRecords: options.initialInstanceRecords } : {},
2293
+ ...options.projectSpaceResolver ? { projectSpaceResolver: options.projectSpaceResolver } : {},
2294
+ runtimeFactory: options.runtimeFactory
2295
+ });
2296
+ });
2297
+ }
2298
+ //#endregion
2299
+ //#region src/modules/project-space/application/workspace/workspace-instructions-resolver.ts
2300
+ function createWorkspaceInstructionsResolver(source) {
2301
+ return { resolve: (request) => Effect.gen(function* () {
2302
+ yield* Effect.try({
2303
+ catch: (error) => error,
2304
+ try: () => validateWorkspaceInstructionsBudget(request.maxBytes)
2305
+ });
2306
+ const loaded = yield* source.load({
2307
+ maxBytes: request.maxBytes,
2308
+ ...request.targetPath !== void 0 ? { targetPath: request.targetPath } : {},
2309
+ workingDirectory: request.workingDirectory,
2310
+ workspaceRoot: request.workspaceRoot
2311
+ });
2312
+ return assembleWorkspaceInstructions({
2313
+ diagnostics: loaded.diagnostics,
2314
+ maxBytes: request.maxBytes,
2315
+ ...request.operation ? { operation: request.operation } : {},
2316
+ ...request.presentedDigests ? { presentedDigests: request.presentedDigests } : {},
2317
+ sources: loaded.sources,
2318
+ ...request.targetPath !== void 0 ? { targetPath: request.targetPath } : {}
2319
+ });
2320
+ }) };
2321
+ }
2322
+ //#endregion
2323
+ //#region src/modules/project-space/infrastructure/workspace/node-workspace-root-registry.ts
2324
+ const workspaceRoots = /* @__PURE__ */ new WeakMap();
2325
+ async function createWorkspaceRootHandle(rootPath) {
2326
+ const resolved = await realpath(rootPath);
2327
+ if (!(await stat(resolved)).isDirectory()) throw new InvalidWorkspaceRoot("workspace root must be a directory");
2328
+ const handle = Object.freeze({ id: `workspace:${createHash("sha256").update(resolved).digest("hex")}` });
2329
+ workspaceRoots.set(handle, resolved);
2330
+ return handle;
2331
+ }
2332
+ function resolveWorkspaceRootPath(handle) {
2333
+ const rootPath = workspaceRoots.get(handle);
2334
+ if (!rootPath) throw new InvalidWorkspaceRoot("workspace root handle was not created by this host");
2335
+ return rootPath;
2336
+ }
2337
+ //#endregion
2338
+ //#region src/modules/project-space/infrastructure/instructions/agents-md-instructions-source.ts
2339
+ function createAgentsMdInstructionsSource() {
2340
+ return { load: (request) => Effect.tryPromise({
2341
+ catch: (error) => error,
2342
+ try: () => loadAgentsMdInstructions(request)
2343
+ }) };
2344
+ }
2345
+ async function loadAgentsMdInstructions(request) {
2346
+ const rootPath = resolveWorkspaceRootPath(request.workspaceRoot);
2347
+ const workingDirectory = await resolveBoundDirectory(rootPath, request.workingDirectory, false);
2348
+ const targetDirectory = request.targetPath ? await resolveBoundDirectory(rootPath, request.targetPath, true) : workingDirectory;
2349
+ if (!isPathWithin(rootPath, workingDirectory) || !isPathWithin(rootPath, targetDirectory)) throw new InvalidWorkspaceRoot("workspace path escapes the bound root");
2350
+ const discovered = [];
2351
+ const diagnostics = [];
2352
+ for (const directory of directoriesFromRoot(rootPath, targetDirectory)) {
2353
+ const result = await readSource(rootPath, join(directory, "AGENTS.md"), request.maxBytes);
2354
+ if (result?.source) discovered.push(result.source);
2355
+ if (result?.diagnostic) diagnostics.push(result.diagnostic);
2356
+ }
2357
+ return Object.freeze({
2358
+ diagnostics: Object.freeze(diagnostics),
2359
+ sources: Object.freeze(discovered)
2360
+ });
2361
+ }
2362
+ async function resolveBoundDirectory(rootPath, path, useParentForFile) {
2363
+ validateRelativePath$1(path);
2364
+ const candidate = resolve(rootPath, path || ".");
2365
+ if (!isPathWithin(rootPath, candidate)) throw new InvalidWorkspaceRoot("workspace path escapes the bound root");
2366
+ let existing = candidate;
2367
+ let metadata;
2368
+ while (isPathWithin(rootPath, existing)) try {
2369
+ metadata = await stat(existing);
2370
+ break;
2371
+ } catch (error) {
2372
+ if (!isMissing(error)) throw error;
2373
+ if (existing === rootPath) break;
2374
+ existing = dirname(existing);
1761
2375
  }
1762
- if (!available) {
1763
- slot.lifecycle = "disabled";
1764
- return slot.definition.required;
2376
+ if (!metadata) throw new InvalidWorkspaceRoot(`workspace path does not have an existing parent: ${path}`);
2377
+ const resolvedExisting = await realpath(existing);
2378
+ if (!isPathWithin(rootPath, resolvedExisting)) throw new InvalidWorkspaceRoot("workspace path resolves outside the bound root");
2379
+ if (existing !== candidate) {
2380
+ if (!useParentForFile) throw new InvalidWorkspaceRoot(`working directory does not exist: ${path}`);
2381
+ return resolvedExisting;
1765
2382
  }
1766
- slot.lifecycle = "starting";
1767
- delete slot.error;
2383
+ if (metadata.isDirectory()) return resolvedExisting;
2384
+ if (!useParentForFile || !metadata.isFile()) throw new InvalidWorkspaceRoot(`workspace path must resolve to a directory: ${path}`);
2385
+ return dirname(resolvedExisting);
2386
+ }
2387
+ async function readSource(rootPath, sourcePath, maxBytes) {
2388
+ let metadata;
1768
2389
  try {
1769
- slot.adapter ??= await create();
1770
- await slot.adapter.start();
1771
- slot.lifecycle = "running";
1772
- return false;
2390
+ metadata = await lstat(sourcePath);
1773
2391
  } catch (error) {
1774
- slot.error = error instanceof Error ? error.message : String(error);
1775
- slot.lifecycle = "degraded";
1776
- return true;
2392
+ if (isMissing(error)) return void 0;
2393
+ throw error;
1777
2394
  }
1778
- }
1779
- async function stopSlots(slots, errors) {
1780
- for (const slot of [...slots].reverse()) {
1781
- if (!slot.adapter || slot.lifecycle === "stopped" || slot.lifecycle === "disabled") continue;
1782
- slot.lifecycle = "stopping";
1783
- try {
1784
- await slot.adapter.stop();
1785
- slot.lifecycle = slot.definition.enabled && slot.agentEnabled ? "stopped" : "disabled";
1786
- delete slot.error;
1787
- } catch (error) {
1788
- errors.push(error);
1789
- slot.error = error instanceof Error ? error.message : String(error);
1790
- slot.lifecycle = "cleanup-required";
1791
- }
2395
+ const relativePath = toRelative(rootPath, sourcePath);
2396
+ if (metadata.isSymbolicLink() || !metadata.isFile()) throw new WorkspaceInstructionsSourceError(relativePath, "source must be a regular non-symlink file");
2397
+ if (metadata.size > maxBytes) return { diagnostic: {
2398
+ code: "workspace-instructions-source-omitted",
2399
+ reason: `source size ${metadata.size} exceeds byte budget ${maxBytes}`,
2400
+ relativePath
2401
+ } };
2402
+ const resolvedSource = await realpath(sourcePath);
2403
+ if (!isPathWithin(rootPath, resolvedSource)) throw new WorkspaceInstructionsSourceError(relativePath, "source resolves outside workspace root");
2404
+ const bytes = await readFile(resolvedSource);
2405
+ let content;
2406
+ try {
2407
+ content = new TextDecoder("utf-8", { fatal: true }).decode(bytes);
2408
+ } catch {
2409
+ throw new WorkspaceInstructionsSourceError(relativePath, "source is not valid UTF-8");
2410
+ }
2411
+ return { source: Object.freeze({
2412
+ bytes: bytes.byteLength,
2413
+ content,
2414
+ digest: createSourceDigest(bytes),
2415
+ relativePath
2416
+ }) };
2417
+ }
2418
+ function validateRelativePath$1(path) {
2419
+ if (isAbsolute(path) || path.split(/[\\/]/).includes("..")) throw new InvalidWorkspaceRoot("workspace path must be relative and cannot contain ..");
2420
+ }
2421
+ function directoriesFromRoot(rootPath, targetDirectory) {
2422
+ const suffix = relative(rootPath, targetDirectory);
2423
+ const directories = [rootPath];
2424
+ if (!suffix) return directories;
2425
+ let current = rootPath;
2426
+ for (const segment of suffix.split(sep)) {
2427
+ current = join(current, segment);
2428
+ directories.push(current);
1792
2429
  }
2430
+ return directories;
2431
+ }
2432
+ function toRelative(rootPath, path) {
2433
+ return relative(rootPath, path).split(sep).join("/");
2434
+ }
2435
+ function createSourceDigest(bytes) {
2436
+ return `sha256:${createHash("sha256").update(bytes).digest("hex")}`;
2437
+ }
2438
+ function isMissing(error) {
2439
+ return error instanceof Error && "code" in error && error.code === "ENOENT";
1793
2440
  }
1794
2441
  //#endregion
1795
- //#region src/domain/project-space.ts
1796
- var InvalidRivusProjectSpace = class extends Error {
1797
- name = "InvalidRivusProjectSpace";
1798
- };
1799
- //#endregion
1800
- //#region src/infrastructure/workspace/rivus-project-space.ts
2442
+ //#region src/modules/project-space/infrastructure/space/node-project-space-resolver.ts
1801
2443
  async function resolveRivusProjectSpace(input) {
2444
+ validateRivusProjectSpaceDeployment(input.declaration);
1802
2445
  const root = await resolveContainedDirectory(await resolveExistingDirectory(input.deploymentRoot, "deployment root"), input.declaration.root, "Project Space root");
1803
2446
  const workingDirectory = await resolveContainedDirectory(root, input.declaration.workingDirectory, "Project Space working directory");
1804
2447
  const skillPaths = await Promise.all(input.declaration.skills.sources.map((source) => resolveContainedPath(root, source, "Project Space Skill source")));
@@ -1860,25 +2503,21 @@ function validateRelativePath(path, owner) {
1860
2503
  if (!path.trim() || path.includes("\0") || isAbsolute(path)) throw new InvalidRivusProjectSpace(`${owner} must be a non-empty relative path`);
1861
2504
  }
1862
2505
  function assertContained(base, candidate, owner) {
1863
- const relation = relative(base, candidate);
1864
- if (relation === "" || !relation.startsWith("..") && !isAbsolute(relation)) return;
2506
+ if (isPathWithin(base, candidate)) return;
1865
2507
  throw new InvalidRivusProjectSpace(`${owner} escapes its trusted root`);
1866
2508
  }
1867
2509
  //#endregion
1868
- //#region src/infrastructure/runtime/configured-rivus-deployment-daemon.ts
1869
- async function createConfiguredRivusDeploymentDaemon(options) {
1870
- const manifest = await loadRivusDeploymentManifest(options.manifestPath, options.manifestOptions);
1871
- return createRivusDeploymentDaemon({
1872
- ...options.createAutomation ? { createAutomation: options.createAutomation } : {},
1873
- ...options.createBackgroundSession ? { createBackgroundSession: options.createBackgroundSession } : {},
1874
- createEndpoint: options.createEndpoint,
1875
- createRuntime: options.createRuntime,
1876
- deploymentRoot: dirname(options.manifestPath),
1877
- ...options.initialInstanceRecords ? { initialInstanceRecords: options.initialInstanceRecords } : {},
1878
- loadModule: loadNodeRivusPluginModule,
1879
- manifest,
1880
- resolveProjectSpace: (input) => resolveRivusProjectSpace(input)
1881
- });
2510
+ //#region src/modules/project-space/module.ts
2511
+ function createAgentsMdInstructionsResolver() {
2512
+ return createWorkspaceInstructionsResolver(createAgentsMdInstructionsSource());
2513
+ }
2514
+ //#endregion
2515
+ //#region src/composition/runtime/deployment-process-effect-runner.ts
2516
+ async function runDeploymentProcessEffect(effect) {
2517
+ const exit = await Effect.runPromiseExit(effect);
2518
+ if (Exit.isSuccess(exit)) return exit.value;
2519
+ const failure = Cause.failureOption(exit.cause);
2520
+ throw Option.isSome(failure) ? failure.value : Cause.squash(exit.cause);
1882
2521
  }
1883
2522
  //#endregion
1884
2523
  //#region src/composition/rivus-deployment-cli-process.ts
@@ -1893,14 +2532,24 @@ async function createRivusDeploymentCliProcess(factory, context) {
1893
2532
  };
1894
2533
  let daemon;
1895
2534
  try {
1896
- daemon = await createConfiguredRivusDeploymentDaemon({
1897
- ...adapters.createAutomation ? { createAutomation: adapters.createAutomation } : {},
1898
- ...adapters.createBackgroundSession ? { createBackgroundSession: adapters.createBackgroundSession } : {},
1899
- createEndpoint: adapters.createEndpoint,
1900
- createRuntime: adapters.createRuntime,
1901
- ...adapters.initialInstanceRecords ? { initialInstanceRecords: adapters.initialInstanceRecords } : {},
1902
- manifestPath: context.manifestPath
1903
- });
2535
+ daemon = await runDeploymentProcessEffect(loadRivusDeploymentManifest(context.manifestPath).pipe(Effect.flatMap((manifest) => {
2536
+ return createRivusDeploymentControlModule({
2537
+ ...createProcessDeploymentControlPorts({
2538
+ ...adapters.createAutomation ? { createAutomation: adapters.createAutomation } : {},
2539
+ ...adapters.createBackgroundSession ? { createBackgroundSession: adapters.createBackgroundSession } : {},
2540
+ createEndpoint: adapters.createEndpoint,
2541
+ createRuntime: adapters.createRuntime
2542
+ }, manifest.backgroundSessions?.enabled === true, runDeploymentProcessEffect),
2543
+ deploymentRoot: dirname(context.manifestPath),
2544
+ ...adapters.initialInstanceRecords ? { initialInstanceRecords: adapters.initialInstanceRecords } : {},
2545
+ manifest,
2546
+ pluginLoader: createNodeRivusPluginModuleLoader(context.pluginPackageManifestPath ? { packageManifestPath: context.pluginPackageManifestPath } : {}),
2547
+ projectSpaceResolver: { resolve: (input) => Effect.tryPromise({
2548
+ try: () => resolveRivusProjectSpace(input),
2549
+ catch: toError
2550
+ }) }
2551
+ });
2552
+ })));
1904
2553
  } catch (constructionError) {
1905
2554
  try {
1906
2555
  await disposeAdapters();
@@ -1912,11 +2561,11 @@ async function createRivusDeploymentCliProcess(factory, context) {
1912
2561
  const start = async () => {
1913
2562
  if (adaptersDisposed) throw new Error("Rivus deployment process cannot restart after its adapters have been disposed");
1914
2563
  try {
1915
- await daemon.start();
2564
+ await runDeploymentProcessEffect(daemon.start());
1916
2565
  } catch (startError) {
1917
2566
  const cleanupErrors = [];
1918
2567
  try {
1919
- await daemon.stop();
2568
+ await runDeploymentProcessEffect(daemon.stop());
1920
2569
  } catch (error) {
1921
2570
  cleanupErrors.push(error);
1922
2571
  }
@@ -1932,7 +2581,7 @@ async function createRivusDeploymentCliProcess(factory, context) {
1932
2581
  const stop = async () => {
1933
2582
  const errors = [];
1934
2583
  try {
1935
- await daemon.stop();
2584
+ await runDeploymentProcessEffect(daemon.stop());
1936
2585
  } catch (error) {
1937
2586
  errors.push(error);
1938
2587
  }
@@ -1973,7 +2622,7 @@ async function createRivusDeploymentCliProcess(factory, context) {
1973
2622
  promptText: (command) => Effect.tryPromise({
1974
2623
  try: async () => {
1975
2624
  if (adaptersDisposed) throw new Error("Rivus deployment process cannot restart after its adapters have been disposed");
1976
- return readPromptFinalText(await daemon.runDefaultAgent({
2625
+ return readPromptFinalText(await runDeploymentProcessEffect(daemon.runDefaultAgent({
1977
2626
  invocation: {
1978
2627
  allowedActorOpenIds: [],
1979
2628
  endpointId: daemon.deployment.manifest.defaultEndpointId,
@@ -1990,7 +2639,7 @@ async function createRivusDeploymentCliProcess(factory, context) {
1990
2639
  },
1991
2640
  sessionKey: command.sessionKey,
1992
2641
  text: command.text
1993
- }));
2642
+ })));
1994
2643
  },
1995
2644
  catch: (error) => error
1996
2645
  })
@@ -2001,7 +2650,7 @@ async function createRivusDeploymentCliProcess(factory, context) {
2001
2650
  if (!daemon.running()) await start();
2002
2651
  },
2003
2652
  catch: (error) => error
2004
- }).pipe(Effect.flatMap(() => replayReceiveMessage((input) => daemon.handleDefault(input), payload, options)));
2653
+ }).pipe(Effect.flatMap(() => replayReceiveMessage((input) => runDeploymentProcessEffect(daemon.handleDefault(toEffectProcessAgentRuntimeInput(input))), payload, options)));
2005
2654
  return process;
2006
2655
  }
2007
2656
  function readPromptFinalText(result) {
@@ -2009,6 +2658,9 @@ function readPromptFinalText(result) {
2009
2658
  if (typeof result === "object" && result !== null && "finalText" in result && typeof result.finalText === "string") return result.finalText;
2010
2659
  throw new Error("Default deployment prompt result must be a string or contain finalText");
2011
2660
  }
2661
+ function toError(error) {
2662
+ return error instanceof Error ? error : new Error(String(error));
2663
+ }
2012
2664
  //#endregion
2013
2665
  //#region src/composition/rivus-recovery-cli.ts
2014
2666
  function createRivusRecoveryCliParser() {
@@ -2178,6 +2830,10 @@ const USAGE = `Usage: rivus --bootstrap <module> [--manifest <rivus.config.json>
2178
2830
  Starts a local Rivus Agent daemon from an injected bootstrap module.
2179
2831
 
2180
2832
  Project commands:
2833
+ rivus setup [directory]
2834
+ rivus start
2835
+ rivus status
2836
+ rivus check-config
2181
2837
  rivus init [directory]
2182
2838
  rivus doctor [directory] [--env-file <path>]
2183
2839
 
@@ -2233,6 +2889,7 @@ Options:
2233
2889
  --help Show this help
2234
2890
 
2235
2891
  Environment:
2892
+ RIVUS_HOME selects Rivus Home (default ~/.rivus-agent) for Home commands.
2236
2893
  RIVUS_BOOTSTRAP_MODULE may be used instead of --bootstrap.
2237
2894
  FEISHU_APP_ID and FEISHU_APP_SECRET are required by the default config loader.
2238
2895
  FEISHU_CARD_STREAM_LEASE_MS shortens or extends the proactive CardKit rollover threshold.
@@ -2241,7 +2898,7 @@ Environment:
2241
2898
  const DEFAULT_WAIT_RECEIVE_TIMEOUT_MS = 3e4;
2242
2899
  const DEFAULT_WAIT_RECEIVE_POLL_MS = 500;
2243
2900
  function runRivusDaemonCli(options) {
2244
- return Effect.promise(async () => {
2901
+ return Effect.gen(function* () {
2245
2902
  const parsed = parseArgs(options.argv);
2246
2903
  if (parsed.help) {
2247
2904
  options.stdout.write(USAGE);
@@ -2251,36 +2908,38 @@ function runRivusDaemonCli(options) {
2251
2908
  options.stderr.write(`${parsed.error}\n\n${USAGE}`);
2252
2909
  return 1;
2253
2910
  }
2254
- if (parsed.statusUrl) try {
2255
- const status = parsed.waitReceive ? await waitForLiveReceiveStatus(parsed.statusUrl, parsed.waitReceive, {
2911
+ if (parsed.statusUrl) {
2912
+ const statusUrl = parsed.statusUrl;
2913
+ const readStatus = () => Effect.tryPromise({
2914
+ try: () => fetchLiveStatus(statusUrl),
2915
+ catch: (error) => error
2916
+ });
2917
+ const status = parsed.waitReceive ? yield* waitForReceiveStatus(readStatus, parsed.waitReceive, {
2256
2918
  ...parsed.waitReceiveMessageId ? { messageId: parsed.waitReceiveMessageId } : {},
2257
2919
  ...parsed.waitReceiveObservedAfter ? { observedAfter: parsed.waitReceiveObservedAfter } : {},
2258
2920
  pollMs: parsed.waitPollMs ?? DEFAULT_WAIT_RECEIVE_POLL_MS,
2259
2921
  ...parsed.waitReceiveText ? { text: parsed.waitReceiveText } : {},
2260
2922
  timeoutMs: parsed.waitTimeoutMs ?? DEFAULT_WAIT_RECEIVE_TIMEOUT_MS
2261
- }) : await fetchLiveStatus(parsed.statusUrl);
2923
+ }) : yield* readStatus();
2262
2924
  options.stdout.write(`${JSON.stringify(status, null, 2)}\n`);
2263
2925
  return 0;
2264
- } catch (error) {
2265
- options.stderr.write(`${formatCliError(error)}\n`);
2266
- return 1;
2267
2926
  }
2268
- if (parsed.printOpenClawEnvPath) try {
2269
- const result = createRivusEnvFromOpenClawConfig(JSON.parse(await readFile(parsed.printOpenClawEnvPath, "utf8")), parsed.piApiKeyFile ? { piApiKeyFile: parsed.piApiKeyFile } : {});
2927
+ if (parsed.printOpenClawEnvPath) {
2928
+ const openClawConfigPath = parsed.printOpenClawEnvPath;
2929
+ const result = yield* Effect.tryPromise({
2930
+ try: async () => {
2931
+ return createRivusEnvFromOpenClawConfig(JSON.parse(await readFile(openClawConfigPath, "utf8")), parsed.piApiKeyFile ? { piApiKeyFile: parsed.piApiKeyFile } : {});
2932
+ },
2933
+ catch: (error) => error
2934
+ });
2270
2935
  for (const warning of result.warnings) options.stderr.write(`Warning: ${warning}\n`);
2271
2936
  options.stdout.write(formatRivusEnvFile(result.env));
2272
2937
  return 0;
2273
- } catch (error) {
2274
- options.stderr.write(`${formatCliError(error)}\n`);
2275
- return 1;
2276
- }
2277
- let env;
2278
- try {
2279
- env = await loadCliEnv(parsed.envFilePath, options.env);
2280
- } catch (error) {
2281
- options.stderr.write(`${formatCliError(error)}\n`);
2282
- return 1;
2283
2938
  }
2939
+ const env = yield* Effect.tryPromise({
2940
+ try: () => loadCliEnv(parsed.envFilePath, options.env),
2941
+ catch: (error) => error
2942
+ });
2284
2943
  const bootstrapSpecifier = parsed.bootstrap ?? env.RIVUS_BOOTSTRAP_MODULE?.trim();
2285
2944
  if (!parsed.checkConfig && !bootstrapSpecifier) {
2286
2945
  options.stderr.write(`Missing --bootstrap <module> or RIVUS_BOOTSTRAP_MODULE\n\n${USAGE}`);
@@ -2288,17 +2947,18 @@ function runRivusDaemonCli(options) {
2288
2947
  }
2289
2948
  let legacyConfig;
2290
2949
  if (parsed.manifestPath) {
2291
- if (parsed.checkConfig) try {
2292
- const manifest = await loadRivusDeploymentManifest(parsed.manifestPath);
2293
- validateRivusDeploymentManifest(manifest);
2950
+ if (parsed.checkConfig) {
2951
+ const manifestPath = parsed.manifestPath;
2952
+ const manifest = yield* loadRivusDeploymentManifest(manifestPath);
2953
+ yield* Effect.try({
2954
+ try: () => validateRivusDeploymentManifest(manifest),
2955
+ catch: (error) => error
2956
+ });
2294
2957
  options.stdout.write(`${JSON.stringify(toRedactedDeploymentManifest(manifest), null, 2)}\n`);
2295
2958
  return 0;
2296
- } catch (error) {
2297
- options.stderr.write(`${formatCliError(error)}\n`);
2298
- return 1;
2299
2959
  }
2300
2960
  } else {
2301
- const configExit = await Effect.runPromiseExit(loadRivusDaemonConfig(env));
2961
+ const configExit = yield* Effect.exit(loadRivusDaemonConfig(env));
2302
2962
  if (configExit._tag === "Failure") {
2303
2963
  options.stderr.write(`${configExit.cause.toString()}\n`);
2304
2964
  return 1;
@@ -2313,113 +2973,132 @@ function runRivusDaemonCli(options) {
2313
2973
  options.stderr.write(`Missing --bootstrap <module> or RIVUS_BOOTSTRAP_MODULE\n\n${USAGE}`);
2314
2974
  return 1;
2315
2975
  }
2316
- try {
2317
- const module = await (options.loadBootstrap ?? ((specifier) => import(specifier)))(bootstrapSpecifier);
2318
- const daemon = parsed.manifestPath ? await createCliDeploymentDaemon(module.createRivusDeploymentAdapters, {
2976
+ const loadBootstrap = options.loadBootstrap ?? ((specifier) => import(specifier));
2977
+ const module = yield* Effect.tryPromise({
2978
+ try: () => loadBootstrap(bootstrapSpecifier),
2979
+ catch: (error) => error
2980
+ });
2981
+ const daemon = yield* Effect.tryPromise({
2982
+ try: () => parsed.manifestPath ? createCliDeploymentDaemon(module.createRivusDeploymentAdapters, {
2319
2983
  argv: options.argv,
2320
2984
  env,
2321
- manifestPath: parsed.manifestPath
2322
- }) : await createCliLegacyDaemon(module, {
2985
+ manifestPath: parsed.manifestPath,
2986
+ ...options.pluginPackageManifestPath ? { pluginPackageManifestPath: options.pluginPackageManifestPath } : {}
2987
+ }) : createCliLegacyDaemon(module, {
2323
2988
  argv: options.argv,
2324
2989
  config: legacyConfig,
2325
2990
  env
2326
- });
2327
- if (parsed.recoveryCommand) {
2328
- const recoveryCommand = parsed.recoveryCommand;
2329
- return await runOneShotDaemon(daemon, async () => {
2330
- if (!hasRecoveryRunner(daemon)) {
2331
- options.stderr.write(`Bootstrap daemon does not expose openRecoveryControl()\n`);
2332
- return 1;
2333
- }
2334
- const result = await runRivusRecoveryCliCommand(await Effect.runPromise(daemon.openRecoveryControl()), recoveryCommand);
2335
- options.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
2336
- return 0;
2991
+ }),
2992
+ catch: (error) => error
2993
+ });
2994
+ if (parsed.recoveryCommand) {
2995
+ const recoveryCommand = parsed.recoveryCommand;
2996
+ return yield* runOneShotDaemon(daemon, () => Effect.gen(function* () {
2997
+ if (!hasRecoveryRunner(daemon)) {
2998
+ options.stderr.write(`Bootstrap daemon does not expose openRecoveryControl()\n`);
2999
+ return 1;
3000
+ }
3001
+ const control = yield* daemon.openRecoveryControl();
3002
+ const result = yield* Effect.tryPromise({
3003
+ try: () => runRivusRecoveryCliCommand(control, recoveryCommand),
3004
+ catch: (error) => error
2337
3005
  });
3006
+ options.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
3007
+ return 0;
3008
+ }));
3009
+ }
3010
+ if (parsed.status) return yield* runOneShotDaemon(daemon, () => Effect.gen(function* () {
3011
+ if (!hasStatusReporter(daemon)) {
3012
+ options.stderr.write(`Bootstrap daemon does not expose status()\n`);
3013
+ return 1;
2338
3014
  }
2339
- if (parsed.status) return await runOneShotDaemon(daemon, async () => {
2340
- if (!hasStatusReporter(daemon)) {
2341
- options.stderr.write(`Bootstrap daemon does not expose status()\n`);
3015
+ const status = yield* daemon.status();
3016
+ options.stdout.write(`${JSON.stringify(status, null, 2)}\n`);
3017
+ return 0;
3018
+ }));
3019
+ if (parsed.prompt !== void 0) {
3020
+ const prompt = parsed.prompt;
3021
+ return yield* runOneShotDaemon(daemon, () => Effect.gen(function* () {
3022
+ if (!hasPromptRunner(daemon)) {
3023
+ options.stderr.write(`Bootstrap daemon does not expose promptText(command)\n`);
2342
3024
  return 1;
2343
3025
  }
2344
- const status = await Effect.runPromise(daemon.status());
2345
- options.stdout.write(`${JSON.stringify(status, null, 2)}\n`);
3026
+ const text = yield* daemon.promptText({
3027
+ sessionKey: parsed.sessionKey ?? daemon.defaultSessionKey ?? `local:${legacyConfig.agentId}:cli`,
3028
+ text: prompt
3029
+ });
3030
+ options.stdout.write(`${text}\n`);
2346
3031
  return 0;
2347
- });
2348
- if (parsed.prompt !== void 0) {
2349
- const prompt = parsed.prompt;
2350
- return await runOneShotDaemon(daemon, async () => {
2351
- if (!hasPromptRunner(daemon)) {
2352
- options.stderr.write(`Bootstrap daemon does not expose promptText(command)\n`);
2353
- return 1;
2354
- }
2355
- const text = await Effect.runPromise(daemon.promptText({
2356
- sessionKey: parsed.sessionKey ?? daemon.defaultSessionKey ?? `local:${legacyConfig.agentId}:cli`,
2357
- text: prompt
2358
- }));
2359
- options.stdout.write(`${text}\n`);
2360
- return 0;
3032
+ }));
3033
+ }
3034
+ if (parsed.replayFeishuText !== void 0) {
3035
+ const replayFeishuText = parsed.replayFeishuText;
3036
+ return yield* runOneShotDaemon(daemon, () => Effect.gen(function* () {
3037
+ if (!hasFeishuReplayRunner(daemon)) {
3038
+ options.stderr.write(`Bootstrap daemon does not expose replayReceiveMessage(payload)\n`);
3039
+ return 1;
3040
+ }
3041
+ const payload = createSyntheticFeishuTextReplayPayload(replayFeishuText, {
3042
+ ...parsed.feishuReplayChatId !== void 0 ? { chatId: parsed.feishuReplayChatId } : {},
3043
+ ...parsed.feishuReplayMessageId !== void 0 ? { messageId: parsed.feishuReplayMessageId } : {},
3044
+ ...parsed.feishuReplayTenantKey !== void 0 ? { tenantKey: parsed.feishuReplayTenantKey } : {},
3045
+ ...parsed.feishuReplayThreadId !== void 0 ? { threadId: parsed.feishuReplayThreadId } : {}
2361
3046
  });
2362
- }
2363
- if (parsed.replayFeishuText !== void 0) {
2364
- const replayFeishuText = parsed.replayFeishuText;
2365
- return await runOneShotDaemon(daemon, async () => {
2366
- if (!hasFeishuReplayRunner(daemon)) {
2367
- options.stderr.write(`Bootstrap daemon does not expose replayReceiveMessage(payload)\n`);
2368
- return 1;
2369
- }
2370
- const payload = createSyntheticFeishuTextReplayPayload(replayFeishuText, {
2371
- ...parsed.feishuReplayChatId !== void 0 ? { chatId: parsed.feishuReplayChatId } : {},
2372
- ...parsed.feishuReplayMessageId !== void 0 ? { messageId: parsed.feishuReplayMessageId } : {},
2373
- ...parsed.feishuReplayTenantKey !== void 0 ? { tenantKey: parsed.feishuReplayTenantKey } : {},
2374
- ...parsed.feishuReplayThreadId !== void 0 ? { threadId: parsed.feishuReplayThreadId } : {}
2375
- });
2376
- const result = await Effect.runPromise(daemon.replayReceiveMessage(payload, { sideEffects: "disabled" }));
2377
- options.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
2378
- return 0;
3047
+ const result = yield* daemon.replayReceiveMessage(payload, { sideEffects: "disabled" });
3048
+ options.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
3049
+ return 0;
3050
+ }));
3051
+ }
3052
+ if (parsed.replayFeishuEventPath !== void 0) {
3053
+ const replayFeishuEventPath = parsed.replayFeishuEventPath;
3054
+ return yield* runOneShotDaemon(daemon, () => Effect.gen(function* () {
3055
+ if (!hasFeishuReplayRunner(daemon)) {
3056
+ options.stderr.write(`Bootstrap daemon does not expose replayReceiveMessage(payload)\n`);
3057
+ return 1;
3058
+ }
3059
+ const payload = yield* Effect.tryPromise({
3060
+ try: () => readFeishuReceiveMessagePayload(replayFeishuEventPath),
3061
+ catch: (error) => error
2379
3062
  });
2380
- }
2381
- if (parsed.replayFeishuEventPath !== void 0) {
2382
- const replayFeishuEventPath = parsed.replayFeishuEventPath;
2383
- return await runOneShotDaemon(daemon, async () => {
2384
- if (!hasFeishuReplayRunner(daemon)) {
2385
- options.stderr.write(`Bootstrap daemon does not expose replayReceiveMessage(payload)\n`);
2386
- return 1;
2387
- }
2388
- const payload = await readFeishuReceiveMessagePayload(replayFeishuEventPath);
2389
- const result = await Effect.runPromise(daemon.replayReceiveMessage(payload));
2390
- options.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
2391
- return 0;
3063
+ const result = yield* daemon.replayReceiveMessage(payload);
3064
+ options.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
3065
+ return 0;
3066
+ }));
3067
+ }
3068
+ if (parsed.waitReceive) {
3069
+ const waitReceive = parsed.waitReceive;
3070
+ return yield* runOneShotDaemon(daemon, () => Effect.gen(function* () {
3071
+ if (!hasStatusReporter(daemon)) {
3072
+ options.stderr.write(`Bootstrap daemon does not expose status()\n`);
3073
+ return 1;
3074
+ }
3075
+ yield* Effect.try({
3076
+ try: () => installCliShutdownController(options, daemon),
3077
+ catch: (error) => error
2392
3078
  });
2393
- }
2394
- if (parsed.waitReceive) {
2395
- const waitReceive = parsed.waitReceive;
2396
- return await runOneShotDaemon(daemon, async () => {
2397
- if (!hasStatusReporter(daemon)) {
2398
- options.stderr.write(`Bootstrap daemon does not expose status()\n`);
2399
- return 1;
2400
- }
2401
- installCliShutdownController(options, daemon);
2402
- await Effect.runPromise(daemon.start());
2403
- const status = await waitForReceiveStatus(() => Effect.runPromise(daemon.status()), waitReceive, {
2404
- ...parsed.waitReceiveMessageId ? { messageId: parsed.waitReceiveMessageId } : {},
2405
- ...parsed.waitReceiveObservedAfter ? { observedAfter: parsed.waitReceiveObservedAfter } : {},
2406
- pollMs: parsed.waitPollMs ?? DEFAULT_WAIT_RECEIVE_POLL_MS,
2407
- ...parsed.waitReceiveText ? { text: parsed.waitReceiveText } : {},
2408
- timeoutMs: parsed.waitTimeoutMs ?? DEFAULT_WAIT_RECEIVE_TIMEOUT_MS
2409
- });
2410
- options.stdout.write(`${JSON.stringify(status, null, 2)}\n`);
2411
- return 0;
3079
+ yield* daemon.start();
3080
+ const status = yield* waitForReceiveStatus(() => daemon.status(), waitReceive, {
3081
+ ...parsed.waitReceiveMessageId ? { messageId: parsed.waitReceiveMessageId } : {},
3082
+ ...parsed.waitReceiveObservedAfter ? { observedAfter: parsed.waitReceiveObservedAfter } : {},
3083
+ pollMs: parsed.waitPollMs ?? DEFAULT_WAIT_RECEIVE_POLL_MS,
3084
+ ...parsed.waitReceiveText ? { text: parsed.waitReceiveText } : {},
3085
+ timeoutMs: parsed.waitTimeoutMs ?? DEFAULT_WAIT_RECEIVE_TIMEOUT_MS
2412
3086
  });
2413
- }
2414
- installCliShutdownController(options, daemon);
2415
- await Effect.runPromise(daemon.start());
2416
- options.stdout.write("Rivus Agent daemon started\n");
2417
- return 0;
2418
- } catch (error) {
2419
- options.stderr.write(`${formatCliError(error)}\n`);
2420
- return 1;
3087
+ options.stdout.write(`${JSON.stringify(status, null, 2)}\n`);
3088
+ return 0;
3089
+ }));
2421
3090
  }
2422
- });
3091
+ yield* Effect.try({
3092
+ try: () => installCliShutdownController(options, daemon),
3093
+ catch: (error) => error
3094
+ });
3095
+ yield* daemon.start();
3096
+ options.stdout.write("Rivus Agent daemon started\n");
3097
+ return 0;
3098
+ }).pipe(Effect.catchAll((error) => Effect.sync(() => {
3099
+ options.stderr.write(`${formatCliError(error)}\n`);
3100
+ return 1;
3101
+ })));
2423
3102
  }
2424
3103
  async function createCliLegacyDaemon(module, context) {
2425
3104
  const factory = module.createRivusDaemonProcess ?? module.default;
@@ -2430,12 +3109,8 @@ async function createCliDeploymentDaemon(factory, context) {
2430
3109
  if (!factory) throw new Error("Manifest bootstrap module must export createRivusDeploymentAdapters(context)");
2431
3110
  return createRivusDeploymentCliProcess(factory, context);
2432
3111
  }
2433
- async function runOneShotDaemon(daemon, action) {
2434
- try {
2435
- return await action();
2436
- } finally {
2437
- await Effect.runPromise(daemon.stop());
2438
- }
3112
+ function runOneShotDaemon(daemon, action) {
3113
+ 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)))));
2439
3114
  }
2440
3115
  function installCliShutdownController(options, daemon) {
2441
3116
  createRivusDaemonShutdownController({
@@ -3258,23 +3933,22 @@ async function fetchLiveStatus(url) {
3258
3933
  if (!response.ok) throw new Error(`Status request failed with HTTP ${response.status}`);
3259
3934
  return response.json();
3260
3935
  }
3261
- async function waitForLiveReceiveStatus(url, target, options) {
3262
- return waitForReceiveStatus(() => fetchLiveStatus(url), target, options);
3263
- }
3264
- async function waitForReceiveStatus(readStatus, target, options) {
3265
- const startedAt = Date.now();
3266
- let lastStatus;
3267
- while (true) {
3268
- const status = await readStatus();
3269
- lastStatus = status;
3270
- if (hasReceiveObservation(status, target, {
3271
- ...options.messageId ? { messageId: options.messageId } : {},
3272
- ...options.observedAfter ? { observedAfter: options.observedAfter } : {},
3273
- ...options.text ? { text: options.text } : {}
3274
- })) return status;
3275
- if (Date.now() - startedAt >= options.timeoutMs) throw new ReceiveWaitTimeoutError(target, lastStatus, options.messageId, options.observedAfter, options.text);
3276
- await sleep(options.pollMs);
3277
- }
3936
+ function waitForReceiveStatus(readStatus, target, options) {
3937
+ return Effect.suspend(() => {
3938
+ const startedAt = Date.now();
3939
+ let lastStatus;
3940
+ const poll = () => readStatus().pipe(Effect.flatMap((status) => {
3941
+ lastStatus = status;
3942
+ if (hasReceiveObservation(status, target, {
3943
+ ...options.messageId ? { messageId: options.messageId } : {},
3944
+ ...options.observedAfter ? { observedAfter: options.observedAfter } : {},
3945
+ ...options.text ? { text: options.text } : {}
3946
+ })) return Effect.succeed(status);
3947
+ if (Date.now() - startedAt >= options.timeoutMs) return Effect.fail(new ReceiveWaitTimeoutError(target, lastStatus, options.messageId, options.observedAfter, options.text));
3948
+ return Effect.sleep(options.pollMs).pipe(Effect.flatMap(poll));
3949
+ }));
3950
+ return poll();
3951
+ });
3278
3952
  }
3279
3953
  function hasReceiveObservation(status, target, criteria) {
3280
3954
  const receive = readRecord(status)?.receive;
@@ -3311,9 +3985,6 @@ function readObservedAtMs(value) {
3311
3985
  return Number.isNaN(time) ? void 0 : time;
3312
3986
  }
3313
3987
  }
3314
- function sleep(ms) {
3315
- return new Promise((resolve) => setTimeout(resolve, ms));
3316
- }
3317
3988
  function parseWaitReceive(value) {
3318
3989
  return value === "accepted" || value === "handled" ? value : void 0;
3319
3990
  }
@@ -3338,4 +4009,4 @@ function hasRecoveryRunner(daemon) {
3338
4009
  return typeof daemon.openRecoveryControl === "function";
3339
4010
  }
3340
4011
  //#endregion
3341
- export { resolveFeishuEndpointCredentials as $, DEFAULT_BACKGROUND_SESSION_LEASE_RENEWAL_INTERVAL_MS as A, RUN_PRESENTATION_SCHEMA_VERSION as B, loadRivusDaemonConfig as C, loadRivusDeploymentManifest as D, RivusDeploymentManifestError as E, DEFAULT_BACKGROUND_SESSION_STEP_TIMEOUT_MS as F, beginCardPresentationHandoff as G, CardPresentationTransitionDenied as H, DEFAULT_BACKGROUND_SESSION_SUPERVISOR_INTERVAL_MS as I, createCardPresentationChain as J, compensateCardPresentationHandoff as K, resolveBackgroundSessionSupervisorIntervalMs as L, DEFAULT_BACKGROUND_SESSION_MAX_CONCURRENT_SESSIONS as M, DEFAULT_BACKGROUND_SESSION_MAX_CONSECUTIVE_FAILURES as N, DEFAULT_CONVERSATION_PROGRESS_DISPLAY as O, DEFAULT_BACKGROUND_SESSION_RETRY_BACKOFF_MS as P, FeishuEndpointCredentialError as Q, DEFAULT_CARD_STREAM_LEASE_MS as R, RivusDaemonConfigError as S, resolveNodeRivusPluginModulePath as T, acceptsCardPresentationProgress as U, hasInspectableRunProgress as V, activeCardPresentation as W, isCardPresentationHandoffDue as X, failCardPresentationHandoff as Y, markCardPresentationTerminal as Z, createStableId as _, InvalidRivusProjectSpace as a, createRivusEnvFromOpenClawConfig as b, RivusDeploymentReadinessError as c, createRivusAgentHost as d, loadMergedLocalEnvFile as et, AgentInstanceBusy as f, createAgentInstanceRegistry as g, AgentInstanceConflict as h, resolveRivusProjectSpace as i, DEFAULT_BACKGROUND_SESSION_LIFETIME_MS as j, DEFAULT_BACKGROUND_SESSION_LEASE_MS as k, createRivusDeploymentDaemon as l, createAgentRuntimePool as m, createRivusDeploymentCliProcess as n, loadRivusDeployment as nt, RivusDeploymentAutomationReadinessError as o, AgentRuntimeDisposed as p, completeCardPresentationHandoff as q, createConfiguredRivusDeploymentDaemon as r, validateRivusDeploymentManifest as rt, RivusDeploymentDaemonLifecycleError as s, runRivusDaemonCli as t, RivusPluginLoadError as tt, InvalidRivusEndpointBinding as u, createRivusDaemonShutdownController as v, loadNodeRivusPluginModule as w, formatRivusEnvFile as x, OpenClawEnvImportError as y, createFeishuCardRollover as z };
4012
+ export { validateCardPresentationLeaseMs as $, RivusDeploymentReadinessError as A, FeishuEndpointCredentialError as B, AgentInstanceConflict as C, RivusDeploymentAutomationReadinessError as D, RivusPluginLoadError as E, WorkspaceInstructionsSourceError as F, toEffectAgentRuntime as G, RivusDaemonConfigError as H, createRivusDaemonShutdownController as I, createRuntimeCache as J, toEffectAgentRuntimeInput as K, OpenClawEnvImportError as L, InvalidRivusProjectSpace as M, createStableId as N, RivusDeploymentBackgroundSessionReadinessError as O, InvalidWorkspaceRoot as P, cardPresentationLeaseDeadline as Q, createRivusEnvFromOpenClawConfig as R, InvalidAgentHostBinding as S, loadRivusDeploymentManifest as T, loadRivusDaemonConfig as U, resolveFeishuEndpointCredentials as V, loadMergedLocalEnvFile as W, invokeRuntimeControl as X, disposeRuntimeCacheEntries as Y, DEFAULT_CARD_STREAM_LEASE_MS$1 as Z, createAgentHostFromRuntimePort as _, createWorkspaceRootHandle as a, createCardPresentationService as at, AgentInstanceBusy as b, createNodeRivusPluginModuleLoader as c, activeCardPresentation as ct, validateTrustedModulePath as d, validateCardPresentationChain as dt, markCardDeliveryTerminal as et, isPathWithin as f, DEFAULT_CONVERSATION_PROGRESS_DISPLAY as ft, toEffectProcessAgentRuntimeInput as g, createProcessDeploymentControlPorts as h, resolveRivusProjectSpace as i, CardPresentationNotFound as it, validateRivusDeploymentManifest as j, RivusDeploymentDaemonLifecycleError as k, loadNodeRivusPluginModule as l, createCardPresentationChain as lt, resolveRivusPluginModule as m, createRivusDeploymentCliProcess as n, validateCardDeliveryRecord as nt, createRivusDeploymentControlModule as o, CardPresentationTransitionDenied as ot, findTrustedPackageRoot as p, toProcessAgentRuntimeInput as q, createAgentsMdInstructionsResolver as r, validateCardDeliveryTransition as rt, resolveRivusDeploymentModule as s, acceptsCardPresentationProgress as st, runRivusDaemonCli as t, reserveCardDeliverySequence as tt, resolveNodeRivusPluginModulePath as u, isCardPresentationHandoffDue as ut, createAgentHostRuntimePort as v, RivusDeploymentManifestError as w, AgentRuntimeDisposed as x, createAgentInstanceRegistryModule as y, formatRivusEnvFile as z };