@rivus/agent 0.6.2 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,5 @@
1
1
  import { t as MEMORY_SCOPES } from "./agent-memory.js";
2
+ import { f as narrowBackgroundSessionDefinition } from "./background-session-authority.js";
2
3
  import { n as resolveRivusAgentDefinition, r as deepFreeze, t as createRivusPluginCatalog } from "./rivus-plugin-registry.js";
3
4
  import { createRequire } from "node:module";
4
5
  import { Effect } from "effect";
@@ -109,7 +110,7 @@ async function loadRivusDeployment(options) {
109
110
  continue;
110
111
  }
111
112
  try {
112
- const baseDefinition = resolveRivusAgentDefinition(catalog, agent);
113
+ const baseDefinition = resolveRivusAgentDefinition(catalog, agent, { backgroundSessions: options.manifest.backgroundSessions?.enabled === true });
113
114
  const projectSpace = agent.projectSpaceId ? options.manifest.projectSpaces?.find(({ id }) => id === agent.projectSpaceId) : void 0;
114
115
  const definition = projectSpace ? deepFreeze({
115
116
  ...baseDefinition,
@@ -224,6 +225,13 @@ function validateRivusDeploymentManifest(manifest) {
224
225
  if (!defaultEndpoint) throw new Error(`default endpoint does not exist: ${manifest.defaultEndpointId}`);
225
226
  if (defaultEndpoint.agentId !== defaultAgent.agentId) throw new Error("default endpoint is not bound to the default agent");
226
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");
227
235
  }
228
236
  function validateRelativeProjectPath(value, owner) {
229
237
  if (value.trim() === "" || isAbsolute(value) || value.includes("\0")) throw new Error(`${owner} must be a non-empty relative path`);
@@ -656,6 +664,19 @@ function createCounters() {
656
664
  };
657
665
  }
658
666
  //#endregion
667
+ //#region src/application/background-session/background-session-config.ts
668
+ const DEFAULT_BACKGROUND_SESSION_STEP_TIMEOUT_MS = 300 * 1e3;
669
+ const DEFAULT_BACKGROUND_SESSION_MAX_CONCURRENT_SESSIONS = 4;
670
+ const DEFAULT_BACKGROUND_SESSION_LEASE_MS = 3e4;
671
+ const DEFAULT_BACKGROUND_SESSION_LEASE_RENEWAL_INTERVAL_MS = 1e4;
672
+ const DEFAULT_BACKGROUND_SESSION_MAX_CONSECUTIVE_FAILURES = 3;
673
+ const DEFAULT_BACKGROUND_SESSION_RETRY_BACKOFF_MS = 3e4;
674
+ const DEFAULT_BACKGROUND_SESSION_LIFETIME_MS = 1440 * 60 * 1e3;
675
+ const DEFAULT_BACKGROUND_SESSION_SUPERVISOR_INTERVAL_MS = 1e3;
676
+ function resolveBackgroundSessionSupervisorIntervalMs(leaseMs) {
677
+ return Math.min(5e3, Math.max(200, Math.floor(leaseMs / 10)));
678
+ }
679
+ //#endregion
659
680
  //#region src/infrastructure/config/rivus-deployment-manifest.ts
660
681
  var RivusDeploymentManifestError = class extends Error {
661
682
  manifestPath;
@@ -684,12 +705,17 @@ function parseManifest(value) {
684
705
  exactKeys(root, [
685
706
  "agents",
686
707
  "automations",
708
+ "backgroundSessions",
687
709
  "defaultAgentId",
688
710
  "defaultEndpointId",
689
711
  "endpoints",
690
712
  "plugins",
691
713
  "projectSpaces"
692
- ], "manifest", ["automations", "projectSpaces"]);
714
+ ], "manifest", [
715
+ "automations",
716
+ "backgroundSessions",
717
+ "projectSpaces"
718
+ ]);
693
719
  const plugins = array(root.plugins, "manifest.plugins").map((entry, index) => {
694
720
  const plugin = record(entry, `manifest.plugins[${index}]`);
695
721
  exactKeys(plugin, [
@@ -816,9 +842,11 @@ function parseManifest(value) {
816
842
  workingDirectory: string(projectSpace.workingDirectory, `manifest.projectSpaces[${index}].workingDirectory`)
817
843
  });
818
844
  });
845
+ const backgroundSessions = root.backgroundSessions === void 0 ? void 0 : parseBackgroundSessions(record(root.backgroundSessions, "manifest.backgroundSessions"));
819
846
  return Object.freeze({
820
847
  agents: Object.freeze(agents),
821
848
  automations: Object.freeze(automations),
849
+ ...backgroundSessions ? { backgroundSessions } : {},
822
850
  defaultAgentId: string(root.defaultAgentId, "manifest.defaultAgentId"),
823
851
  defaultEndpointId: string(root.defaultEndpointId, "manifest.defaultEndpointId"),
824
852
  endpoints: Object.freeze(endpoints),
@@ -826,6 +854,38 @@ function parseManifest(value) {
826
854
  projectSpaces: Object.freeze(projectSpaces)
827
855
  });
828
856
  }
857
+ function parseBackgroundSessions(value) {
858
+ exactKeys(value, [
859
+ "enabled",
860
+ "leaseMs",
861
+ "leaseRenewalIntervalMs",
862
+ "maxConsecutiveFailures",
863
+ "maxConcurrentSessions",
864
+ "required",
865
+ "retryBackoffMs",
866
+ "sessionLifetimeMs",
867
+ "stepTimeoutMs"
868
+ ], "manifest.backgroundSessions", [
869
+ "leaseMs",
870
+ "leaseRenewalIntervalMs",
871
+ "maxConsecutiveFailures",
872
+ "maxConcurrentSessions",
873
+ "retryBackoffMs",
874
+ "sessionLifetimeMs",
875
+ "stepTimeoutMs"
876
+ ]);
877
+ return Object.freeze({
878
+ enabled: boolean(value.enabled, "manifest.backgroundSessions.enabled"),
879
+ required: boolean(value.required, "manifest.backgroundSessions.required"),
880
+ stepTimeoutMs: positiveInteger(value.stepTimeoutMs ?? 3e5, "manifest.backgroundSessions.stepTimeoutMs"),
881
+ maxConcurrentSessions: positiveInteger(value.maxConcurrentSessions ?? 4, "manifest.backgroundSessions.maxConcurrentSessions"),
882
+ leaseMs: positiveInteger(value.leaseMs ?? 3e4, "manifest.backgroundSessions.leaseMs"),
883
+ leaseRenewalIntervalMs: positiveInteger(value.leaseRenewalIntervalMs ?? 1e4, "manifest.backgroundSessions.leaseRenewalIntervalMs"),
884
+ maxConsecutiveFailures: positiveInteger(value.maxConsecutiveFailures ?? 3, "manifest.backgroundSessions.maxConsecutiveFailures"),
885
+ retryBackoffMs: positiveInteger(value.retryBackoffMs ?? 3e4, "manifest.backgroundSessions.retryBackoffMs"),
886
+ sessionLifetimeMs: positiveInteger(value.sessionLifetimeMs ?? 864e5, "manifest.backgroundSessions.sessionLifetimeMs")
887
+ });
888
+ }
829
889
  function record(value, path) {
830
890
  if (value === null || typeof value !== "object" || Array.isArray(value)) throw new Error(`${path} must be an object`);
831
891
  return value;
@@ -1192,7 +1252,7 @@ function createAgentInstanceRegistry(options = {}) {
1192
1252
  skillGrantRevision: definition.skillGrantSet.revision,
1193
1253
  toolGrantRevision: definition.toolGrantSet.revision
1194
1254
  });
1195
- const bindingId = binding.kind === "endpoint" ? binding.endpointId : binding.automationId;
1255
+ const bindingId = binding.kind === "endpoint" ? binding.endpointId : binding.kind === "automation" ? binding.automationId : binding.agentId;
1196
1256
  const bindingKey = `${binding.kind}:${bindingId}:${definition.agentId}`;
1197
1257
  const existing = records.get(bindingKey);
1198
1258
  if (existing) {
@@ -1217,6 +1277,10 @@ function createAgentInstanceRegistry(options = {}) {
1217
1277
  automationId,
1218
1278
  kind: "automation"
1219
1279
  }, definition),
1280
+ resolveBackgroundSession: (agentId, definition) => resolveBinding({
1281
+ agentId,
1282
+ kind: "background-session"
1283
+ }, definition),
1220
1284
  resolveEndpoint: (endpointId, definition) => resolveBinding({
1221
1285
  endpointId,
1222
1286
  kind: "endpoint"
@@ -1296,6 +1360,7 @@ function createRivusAgentHost(options) {
1296
1360
  const definitions = new Map(options.definitions.map((definition) => [definition.agentId, definition]));
1297
1361
  const endpoints = /* @__PURE__ */ new Map();
1298
1362
  const automations = /* @__PURE__ */ new Map();
1363
+ const backgroundSessions = /* @__PURE__ */ new Map();
1299
1364
  for (const endpoint of options.endpoints) {
1300
1365
  if (endpoints.has(endpoint.id)) throw new InvalidRivusEndpointBinding(`duplicate endpoint: ${endpoint.id}`);
1301
1366
  const definition = definitions.get(endpoint.agentId);
@@ -1308,6 +1373,11 @@ function createRivusAgentHost(options) {
1308
1373
  if (!definitions.has(automation.definition.agentId)) throw new InvalidRivusEndpointBinding(`unknown automation agent: ${automation.definition.agentId}`);
1309
1374
  automations.set(automation.id, options.runtimePool.registry.resolveAutomation(automation.id, automation.definition));
1310
1375
  }
1376
+ for (const backgroundSession of options.backgroundSessions ?? []) {
1377
+ if (backgroundSessions.has(backgroundSession.agentId)) throw new InvalidRivusEndpointBinding(`duplicate background session agent: ${backgroundSession.agentId}`);
1378
+ if (!definitions.has(backgroundSession.agentId)) throw new InvalidRivusEndpointBinding(`unknown background session agent: ${backgroundSession.agentId}`);
1379
+ backgroundSessions.set(backgroundSession.agentId, options.runtimePool.registry.resolveBackgroundSession(backgroundSession.agentId, backgroundSession.definition));
1380
+ }
1311
1381
  const resolveEndpoint = (endpointId) => {
1312
1382
  const instance = endpoints.get(endpointId);
1313
1383
  if (!instance) throw new InvalidRivusEndpointBinding(`unknown endpoint: ${endpointId}`);
@@ -1318,11 +1388,19 @@ function createRivusAgentHost(options) {
1318
1388
  if (!instance) throw new InvalidRivusEndpointBinding(`unknown automation: ${automationId}`);
1319
1389
  return instance;
1320
1390
  };
1391
+ const resolveBackgroundSession = (agentId) => {
1392
+ const instance = backgroundSessions.get(agentId);
1393
+ if (!instance) throw new InvalidRivusEndpointBinding(`unknown background session agent: ${agentId}`);
1394
+ return instance;
1395
+ };
1321
1396
  return {
1397
+ cancelBackgroundSession: (agentId, input) => options.runtimePool.cancel(resolveBackgroundSession(agentId), input),
1322
1398
  cancelEndpoint: (endpointId, input) => options.runtimePool.cancel(resolveEndpoint(endpointId), input),
1323
1399
  handleAutomation: (automationId, input) => options.runtimePool.run(resolveAutomation(automationId), input),
1400
+ handleBackgroundSession: (agentId, input) => options.runtimePool.run(resolveBackgroundSession(agentId), input),
1324
1401
  handleEndpoint: (endpointId, input) => options.runtimePool.run(resolveEndpoint(endpointId), input),
1325
1402
  resolveAutomation,
1403
+ resolveBackgroundSession,
1326
1404
  resolveEndpoint
1327
1405
  };
1328
1406
  }
@@ -1359,9 +1437,15 @@ async function createRivusDeploymentDaemon(options) {
1359
1437
  })));
1360
1438
  const definitions = new Map(deployment.definitions.map((definition) => [definition.agentId, definition]));
1361
1439
  const automationDefinitions = new Map(deployment.automationDefinitions.map((definition) => [definition.id, definition]));
1440
+ const backgroundSessionsConfig = deployment.manifest.backgroundSessions;
1441
+ const backgroundDefinitions = /* @__PURE__ */ new Map();
1442
+ if (backgroundSessionsConfig?.enabled) for (const agent of deployment.agents) {
1443
+ if (agent.status !== "enabled" || !agent.definition) continue;
1444
+ backgroundDefinitions.set(agent.agentId, narrowBackgroundSessionDefinition(agent.definition));
1445
+ }
1362
1446
  const runtimePool = createAgentRuntimePool({
1363
1447
  createRuntime: (instance) => {
1364
- const definition = instance.binding.kind === "automation" ? automationDefinitions.get(instance.binding.automationId)?.runtimeDefinition : definitions.get(instance.agentId);
1448
+ 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);
1365
1449
  if (!definition) throw new RivusDeploymentDaemonLifecycleError(`runtime instance references unknown agent: ${instance.agentId}`);
1366
1450
  const projectSpace = definition.projectSpaceId ? projectSpaces.get(definition.projectSpaceId) : void 0;
1367
1451
  return options.createRuntime({
@@ -1392,11 +1476,20 @@ async function createRivusDeploymentDaemon(options) {
1392
1476
  lifecycle: definition.enabled && agentEnabled ? "stopped" : "disabled"
1393
1477
  };
1394
1478
  });
1479
+ const backgroundSessionSlot = backgroundSessionsConfig ? {
1480
+ agentEnabled: backgroundDefinitions.size > 0,
1481
+ definition: backgroundSessionsConfig,
1482
+ lifecycle: backgroundSessionsConfig.enabled && backgroundDefinitions.size > 0 ? "stopped" : "disabled"
1483
+ } : void 0;
1395
1484
  const host = createRivusAgentHost({
1396
1485
  automations: automationSlots.filter((slot) => slot.definition.enabled && slot.agentEnabled && slot.resolvedDefinition !== void 0).map((slot) => ({
1397
1486
  definition: slot.resolvedDefinition.runtimeDefinition,
1398
1487
  id: slot.definition.id
1399
1488
  })),
1489
+ ...backgroundSessionSlot ? { backgroundSessions: [...backgroundDefinitions].map(([agentId, definition]) => ({
1490
+ agentId,
1491
+ definition
1492
+ })) } : {},
1400
1493
  definitions: deployment.definitions,
1401
1494
  endpoints: slots.filter((slot) => slot.definition.enabled && slot.agentEnabled).map((slot) => ({
1402
1495
  agentId: slot.definition.agentId,
@@ -1424,7 +1517,15 @@ async function createRivusDeploymentDaemon(options) {
1424
1517
  agentId: slot.definition.agentId,
1425
1518
  automationId: slot.definition.id
1426
1519
  });
1427
- const allSlots = [...slots, ...automationSlots];
1520
+ const backgroundSessionStatus = (slot) => Object.freeze({
1521
+ ...componentStatus(slot),
1522
+ ...slot.adapter?.status ? { supervisor: slot.adapter.status() } : {}
1523
+ });
1524
+ const allSlots = [
1525
+ ...slots,
1526
+ ...automationSlots,
1527
+ ...backgroundSessionSlot ? [backgroundSessionSlot] : []
1528
+ ];
1428
1529
  const isReady = () => allSlots.every(isSlotReady);
1429
1530
  const failedRequiredEndpointIds = () => slots.filter((slot) => slot.definition.enabled && slot.definition.required && slot.lifecycle !== "running").map((slot) => slot.definition.id);
1430
1531
  const failedRequiredAutomationIds = () => automationSlots.filter((slot) => slot.definition.enabled && slot.definition.required && slot.lifecycle !== "running").map((slot) => slot.definition.id);
@@ -1437,6 +1538,7 @@ async function createRivusDeploymentDaemon(options) {
1437
1538
  const status = () => Object.freeze({
1438
1539
  agents: deployment.agents,
1439
1540
  automations: Object.freeze(automationSlots.map(automationStatus)),
1541
+ ...backgroundSessionSlot ? { backgroundSessions: backgroundSessionStatus(backgroundSessionSlot) } : {},
1440
1542
  defaultAgentId: deployment.manifest.defaultAgentId,
1441
1543
  defaultEndpointId: deployment.manifest.defaultEndpointId,
1442
1544
  endpoints: Object.freeze(slots.map(endpointStatus)),
@@ -1501,6 +1603,18 @@ async function createRivusDeploymentDaemon(options) {
1501
1603
  });
1502
1604
  degraded ||= slotDegraded;
1503
1605
  }
1606
+ if (backgroundSessionSlot) {
1607
+ const slotDegraded = await startSlot(backgroundSessionSlot, true, async () => {
1608
+ if (!options.createBackgroundSession) throw new RivusDeploymentDaemonLifecycleError("deployment bootstrap does not provide Background Session adapters");
1609
+ return options.createBackgroundSession({
1610
+ agentIds: [...backgroundDefinitions.keys()],
1611
+ cancel: (input) => host.cancelBackgroundSession(input.agentId, input),
1612
+ config: backgroundSessionSlot.definition,
1613
+ run: (input) => host.handleBackgroundSession(input.agentId, input)
1614
+ });
1615
+ });
1616
+ degraded ||= slotDegraded;
1617
+ }
1504
1618
  lifecycle = degraded ? "degraded" : "running";
1505
1619
  assertRequiredReadiness();
1506
1620
  },
@@ -1514,6 +1628,7 @@ async function createRivusDeploymentDaemon(options) {
1514
1628
  lifecycle = "stopping";
1515
1629
  const errors = [];
1516
1630
  await stopSlots(automationSlots, errors);
1631
+ if (backgroundSessionSlot) await stopSlots([backgroundSessionSlot], errors);
1517
1632
  await stopSlots(slots, errors);
1518
1633
  try {
1519
1634
  await runtimePool.disposeAll();
@@ -1654,6 +1769,7 @@ async function createConfiguredRivusDeploymentDaemon(options) {
1654
1769
  const manifest = await loadRivusDeploymentManifest(options.manifestPath, options.manifestOptions);
1655
1770
  return createRivusDeploymentDaemon({
1656
1771
  ...options.createAutomation ? { createAutomation: options.createAutomation } : {},
1772
+ ...options.createBackgroundSession ? { createBackgroundSession: options.createBackgroundSession } : {},
1657
1773
  createEndpoint: options.createEndpoint,
1658
1774
  createRuntime: options.createRuntime,
1659
1775
  deploymentRoot: dirname(options.manifestPath),
@@ -2968,6 +3084,17 @@ function toRedactedDeploymentManifest(manifest) {
2968
3084
  templateId,
2969
3085
  timeZone
2970
3086
  })),
3087
+ ...manifest.backgroundSessions ? { backgroundSessions: {
3088
+ enabled: manifest.backgroundSessions.enabled,
3089
+ leaseMs: manifest.backgroundSessions.leaseMs,
3090
+ leaseRenewalIntervalMs: manifest.backgroundSessions.leaseRenewalIntervalMs,
3091
+ maxConsecutiveFailures: manifest.backgroundSessions.maxConsecutiveFailures,
3092
+ maxConcurrentSessions: manifest.backgroundSessions.maxConcurrentSessions,
3093
+ required: manifest.backgroundSessions.required,
3094
+ retryBackoffMs: manifest.backgroundSessions.retryBackoffMs,
3095
+ sessionLifetimeMs: manifest.backgroundSessions.sessionLifetimeMs,
3096
+ stepTimeoutMs: manifest.backgroundSessions.stepTimeoutMs
3097
+ } } : {},
2971
3098
  defaultAgentId: manifest.defaultAgentId,
2972
3099
  defaultEndpointId: manifest.defaultEndpointId,
2973
3100
  endpoints: manifest.endpoints.map(({ agentId, credentialRef, enabled, id, required, sessionNamespace }) => ({
@@ -3109,4 +3236,4 @@ function hasRecoveryRunner(daemon) {
3109
3236
  return typeof daemon.openRecoveryControl === "function";
3110
3237
  }
3111
3238
  //#endregion
3112
- export { CardPresentationTransitionDenied as A, FeishuEndpointCredentialError as B, loadRivusDaemonConfig as C, loadRivusDeploymentManifest as D, RivusDeploymentManifestError as E, completeCardPresentationHandoff as F, validateRivusDeploymentManifest as G, loadMergedLocalEnvFile as H, createCardPresentationChain as I, failCardPresentationHandoff as L, activeCardPresentation as M, beginCardPresentationHandoff as N, DEFAULT_CARD_STREAM_LEASE_MS as O, compensateCardPresentationHandoff as P, isCardPresentationHandoffDue as R, RivusDaemonConfigError as S, resolveNodeRivusPluginModulePath as T, RivusPluginLoadError as U, resolveFeishuEndpointCredentials as V, loadRivusDeployment as W, createStableId as _, InvalidRivusProjectSpace as a, createRivusEnvFromOpenClawConfig as b, RivusDeploymentReadinessError as c, createRivusAgentHost as d, AgentInstanceBusy as f, createAgentInstanceRegistry as g, AgentInstanceConflict as h, resolveRivusProjectSpace as i, acceptsCardPresentationProgress as j, createFeishuCardRollover as k, createRivusDeploymentDaemon as l, createAgentRuntimePool as m, createRivusDeploymentCliProcess as n, RivusDeploymentAutomationReadinessError as o, AgentRuntimeDisposed as p, createConfiguredRivusDeploymentDaemon as r, RivusDeploymentDaemonLifecycleError as s, runRivusDaemonCli as t, InvalidRivusEndpointBinding as u, createRivusDaemonShutdownController as v, loadNodeRivusPluginModule as w, formatRivusEnvFile as x, OpenClawEnvImportError as y, markCardPresentationTerminal as z };
3239
+ export { loadRivusDeployment as $, DEFAULT_BACKGROUND_SESSION_LIFETIME_MS as A, acceptsCardPresentationProgress as B, loadRivusDaemonConfig as C, loadRivusDeploymentManifest as D, RivusDeploymentManifestError as E, DEFAULT_BACKGROUND_SESSION_SUPERVISOR_INTERVAL_MS as F, createCardPresentationChain as G, beginCardPresentationHandoff as H, resolveBackgroundSessionSupervisorIntervalMs as I, markCardPresentationTerminal as J, failCardPresentationHandoff as K, DEFAULT_CARD_STREAM_LEASE_MS as L, DEFAULT_BACKGROUND_SESSION_MAX_CONSECUTIVE_FAILURES as M, DEFAULT_BACKGROUND_SESSION_RETRY_BACKOFF_MS as N, DEFAULT_BACKGROUND_SESSION_LEASE_MS as O, DEFAULT_BACKGROUND_SESSION_STEP_TIMEOUT_MS as P, RivusPluginLoadError as Q, createFeishuCardRollover as R, RivusDaemonConfigError as S, resolveNodeRivusPluginModulePath as T, compensateCardPresentationHandoff as U, activeCardPresentation as V, completeCardPresentationHandoff as W, resolveFeishuEndpointCredentials as X, FeishuEndpointCredentialError as Y, loadMergedLocalEnvFile as Z, createStableId as _, InvalidRivusProjectSpace as a, createRivusEnvFromOpenClawConfig as b, RivusDeploymentReadinessError as c, createRivusAgentHost as d, validateRivusDeploymentManifest as et, AgentInstanceBusy as f, createAgentInstanceRegistry as g, AgentInstanceConflict as h, resolveRivusProjectSpace as i, DEFAULT_BACKGROUND_SESSION_MAX_CONCURRENT_SESSIONS as j, DEFAULT_BACKGROUND_SESSION_LEASE_RENEWAL_INTERVAL_MS as k, createRivusDeploymentDaemon as l, createAgentRuntimePool as m, createRivusDeploymentCliProcess as n, RivusDeploymentAutomationReadinessError as o, AgentRuntimeDisposed as p, isCardPresentationHandoffDue as q, createConfiguredRivusDeploymentDaemon as r, RivusDeploymentDaemonLifecycleError as s, runRivusDaemonCli as t, InvalidRivusEndpointBinding as u, createRivusDaemonShutdownController as v, loadNodeRivusPluginModule as w, formatRivusEnvFile as x, OpenClawEnvImportError as y, CardPresentationTransitionDenied as z };
@@ -1,4 +1,5 @@
1
1
  import { c as InvalidRivusPlugin, o as createRivusMemoryToolContract, r as RIVUS_MEMORY_TOOL_PLUGIN_ID, t as MEMORY_SCOPES } from "./agent-memory.js";
2
+ import { u as extendBackgroundSessionDefinition } from "./background-session-authority.js";
2
3
  import { createHash } from "node:crypto";
3
4
  //#region src/application/plugin/deep-freeze.ts
4
5
  function deepFreeze(value) {
@@ -70,7 +71,7 @@ function createRivusPluginCatalog() {
70
71
  })
71
72
  };
72
73
  }
73
- function resolveRivusAgentDefinition(catalog, deployment) {
74
+ function resolveRivusAgentDefinition(catalog, deployment, options = {}) {
74
75
  const snapshot = catalog.snapshot();
75
76
  const plugin = snapshot.plugins.find((candidate) => candidate.id === deployment.pluginId);
76
77
  if (!plugin) throw new InvalidRivusPlugin(`unknown deployment plugin: ${deployment.pluginId}`);
@@ -135,7 +136,7 @@ function resolveRivusAgentDefinition(catalog, deployment) {
135
136
  }),
136
137
  skillIds
137
138
  });
138
- return deepFreeze({
139
+ const definition = deepFreeze({
139
140
  agentId: deployment.agentId,
140
141
  endpointIds: [...deployment.endpointIds],
141
142
  memory: {
@@ -153,6 +154,8 @@ function resolveRivusAgentDefinition(catalog, deployment) {
153
154
  toolGrantSet,
154
155
  tools
155
156
  });
157
+ if (options.backgroundSessions === true) return extendBackgroundSessionDefinition(definition);
158
+ return definition;
156
159
  }
157
160
  function validateMemoryScopes(scopes, owner) {
158
161
  const result = /* @__PURE__ */ new Set();
@@ -16,6 +16,12 @@ interface RivusPluginManifest {
16
16
  interface RivusToolExecutor {
17
17
  execute(input: unknown, context: RivusToolExecutionContext): unknown;
18
18
  }
19
+ interface RivusToolExecutionOrigin {
20
+ readonly endpointId: string;
21
+ readonly tenantKey: string;
22
+ readonly conversationId?: string;
23
+ readonly allowedActorOpenIds: ReadonlyArray<string>;
24
+ }
19
25
  interface RivusToolExecutionContext {
20
26
  readonly agentId: string;
21
27
  readonly instanceId: string;
@@ -27,6 +33,8 @@ interface RivusToolExecutionContext {
27
33
  readonly toolId: string;
28
34
  readonly toolVersion: string;
29
35
  readonly sessionKey: string;
36
+ readonly origin?: RivusToolExecutionOrigin;
37
+ readonly sourceMessageId?: string;
30
38
  }
31
39
  interface RivusToolFactoryContext {
32
40
  readonly toolId: string;
@@ -64,6 +64,7 @@ var InvalidInvocationAuthority = class extends Error {
64
64
  };
65
65
  function createInvocationAuthority(authority) {
66
66
  if (!authority.sourceMessageId.trim()) throw new InvalidInvocationAuthority("invocation authority requires a trusted source message id");
67
+ if (authority.endpointId !== void 0 && !authority.endpointId.trim()) throw new InvalidInvocationAuthority("invocation authority requires a trusted endpoint id");
67
68
  const reference = Object.freeze({ id: `authority:${randomUUID()}` });
68
69
  const memory = authority.memory ? Object.freeze({
69
70
  ...authority.memory,
@@ -71,6 +72,7 @@ function createInvocationAuthority(authority) {
71
72
  }) : void 0;
72
73
  authorities.set(reference, Object.freeze({
73
74
  ...authority,
75
+ ...authority.allowedActorOpenIds ? { allowedActorOpenIds: Object.freeze([...authority.allowedActorOpenIds]) } : {},
74
76
  ...memory ? { memory } : {}
75
77
  }));
76
78
  return reference;
@@ -1,5 +1,5 @@
1
1
  import { mkdir, readFile, writeFile } from "node:fs/promises";
2
- import { createHash } from "node:crypto";
2
+ import { createHash, randomUUID } from "node:crypto";
3
3
  import { join, relative } from "node:path";
4
4
  import { Effect } from "effect";
5
5
  import * as Lark from "@larksuiteoapi/node-sdk";
@@ -14,8 +14,13 @@ import {
14
14
  createAgentsMdInstructionsProvider,
15
15
  createAgentHarness,
16
16
  createAgentHarnessPooledRuntime,
17
+ createBackgroundSessionHostTools,
18
+ createBackgroundSessionService,
19
+ createBackgroundSessionStepSourceMessageId,
20
+ createBackgroundSessionSupervisor,
17
21
  createRivusMemoryToolDescriptor,
18
22
  createConfiguredFeishuAutomationCardSender,
23
+ createConfiguredFeishuBackgroundSessionDelivery,
19
24
  createConfiguredFeishuCardRolloverRuntime,
20
25
  createConfiguredFeishuHumanInteractionPresenter,
21
26
  createConfiguredFeishuOpenApiClient,
@@ -43,6 +48,10 @@ import {
43
48
  createToolBroker,
44
49
  createUuidRunIds,
45
50
  createWorkspaceRootHandle,
51
+ loadRivusDeploymentManifest,
52
+ openJsonlBackgroundSessionDeliveryStore,
53
+ openJsonlBackgroundSessionRepository,
54
+ resolveBackgroundSessionSupervisorIntervalMs,
46
55
  openJsonlFeishuCardDeliveryLedger,
47
56
  openJsonlFeishuInboxRepository,
48
57
  openJsonlAgentMemoryService,
@@ -53,13 +62,16 @@ import {
53
62
  resolveLangfuseTelemetryConfig,
54
63
  validateProjectSkillCatalog,
55
64
  validateProjectSkillCommand,
65
+ type CreateRivusDeploymentBackgroundSessionInput,
56
66
  type CreateRivusDeploymentEndpointInput,
57
67
  type CreateRivusDeploymentAutomationInput,
58
68
  type CreateRivusDeploymentRuntimeInput,
59
69
  type ConfiguredFeishuOpenApiResponse,
60
70
  type FeishuAgentRunPreparation,
71
+ type FeishuBackgroundSessionDelivery,
61
72
  type FeishuWebSocketClient,
62
73
  type RivusDaemonConfig,
74
+ type RivusDeploymentBackgroundSession,
63
75
  type RivusDeploymentBootstrapContext,
64
76
  type RivusThinkingLevel
65
77
  } from "@rivus/agent";
@@ -94,6 +106,144 @@ export async function createRivusDeploymentAdapters(context: RivusDeploymentBoot
94
106
  request: (input) => request(input).pipe(Effect.map((response) => response as ConfiguredFeishuOpenApiResponse))
95
107
  });
96
108
  const interactionRegistry = createHumanInteractionEndpointRegistry();
109
+ const manifest = await loadRivusDeploymentManifest(context.manifestPath);
110
+ const backgroundSessionsConfig = manifest.backgroundSessions;
111
+ const sessionRepository = backgroundSessionsConfig?.enabled
112
+ ? await openJsonlBackgroundSessionRepository({
113
+ filePath: join(STATE_DIR, "background-sessions", "sessions.jsonl")
114
+ })
115
+ : undefined;
116
+ const sessionDeliveries = backgroundSessionsConfig?.enabled
117
+ ? await openJsonlBackgroundSessionDeliveryStore({
118
+ filePath: join(STATE_DIR, "background-sessions", "deliveries.jsonl")
119
+ })
120
+ : undefined;
121
+ const deliveryClients = new Map<string, FeishuBackgroundSessionDelivery>();
122
+ const backgroundService = backgroundSessionsConfig?.enabled
123
+ ? createBackgroundSessionService({
124
+ clock: { now: () => new Date().toISOString() },
125
+ deliveries: sessionDeliveries!,
126
+ repository: sessionRepository!
127
+ })
128
+ : undefined;
129
+ const createBackgroundSessionAdapter = (
130
+ input: CreateRivusDeploymentBackgroundSessionInput
131
+ ): RivusDeploymentBackgroundSession => {
132
+ const resolveDeliverySender = async (endpointId: string): Promise<FeishuBackgroundSessionDelivery> => {
133
+ const existing = deliveryClients.get(endpointId);
134
+ if (existing) return existing;
135
+ const endpoint = manifest.endpoints.find(({ id }) => id === endpointId);
136
+ if (!endpoint) throw new Error(`background session delivery endpoint not found: ${endpointId}`);
137
+ const credentials = resolveFeishuEndpointCredentials(endpoint.credentialRef, context.env);
138
+ const config: RivusDaemonConfig = {
139
+ agentId: endpoint.agentId,
140
+ feishu: {
141
+ ...credentials,
142
+ baseUrl: endpoint.baseUrl,
143
+ cardStreamLeaseMs: endpoint.cardStreamLeaseMs,
144
+ streamMinIntervalMs: endpoint.streamMinIntervalMs
145
+ },
146
+ pi: {}
147
+ };
148
+ const sender = createConfiguredFeishuBackgroundSessionDelivery({
149
+ client: createOpenApiClient(config),
150
+ config
151
+ });
152
+ deliveryClients.set(endpointId, sender);
153
+ return sender;
154
+ };
155
+ const supervisor = createBackgroundSessionSupervisor({
156
+ clock: { now: () => new Date().toISOString() },
157
+ config: {
158
+ intervalMs: resolveBackgroundSessionSupervisorIntervalMs(input.config.leaseMs),
159
+ leaseMs: input.config.leaseMs,
160
+ leaseRenewalIntervalMs: input.config.leaseRenewalIntervalMs,
161
+ maxConcurrentSessions: input.config.maxConcurrentSessions,
162
+ maxConsecutiveFailures: input.config.maxConsecutiveFailures,
163
+ retryBackoffMs: input.config.retryBackoffMs,
164
+ sessionLifetimeMs: input.config.sessionLifetimeMs
165
+ },
166
+ deliveries: sessionDeliveries!,
167
+ deliver: async (delivery) => {
168
+ const session = await sessionRepository!.get(delivery.sessionId);
169
+ if (!session) throw new Error(`background session not found for delivery: ${delivery.sessionId}`);
170
+ if (!session.origin.conversationId) {
171
+ throw new Error(`background session has no delivery conversation: ${delivery.sessionId}`);
172
+ }
173
+ const sender = await resolveDeliverySender(session.origin.endpointId);
174
+ return sender.deliver({
175
+ chatId: session.origin.conversationId,
176
+ deliveryId: delivery.deliveryId,
177
+ displayName: session.displayName,
178
+ kind: delivery.kind,
179
+ sessionId: session.sessionId,
180
+ text: delivery.text
181
+ });
182
+ },
183
+ onError: (error) => {
184
+ console.error("Background session supervisor failed", error);
185
+ },
186
+ repository: sessionRepository!,
187
+ runStep: async ({ session, signal, wakeText }) => {
188
+ let runId: string | undefined;
189
+ const invocation = {
190
+ allowedActorOpenIds: session.origin.allowedActorOpenIds,
191
+ endpointId: session.origin.endpointId,
192
+ kind: "background-session" as const,
193
+ ...(session.origin.memory ? { memory: session.origin.memory } : {}),
194
+ sessionId: session.sessionId,
195
+ sourceMessageId: createBackgroundSessionStepSourceMessageId(session.sessionId, session.stepCount + 1),
196
+ tenantKey: session.origin.tenantKey
197
+ };
198
+ const abortPromise = new Promise<never>((_resolve, reject) => {
199
+ signal.addEventListener(
200
+ "abort",
201
+ () => {
202
+ if (runId) {
203
+ void input.cancel({
204
+ agentId: session.authority.agentId,
205
+ reason: "background session step aborted",
206
+ runId,
207
+ sessionKey: session.authority.sessionKey
208
+ });
209
+ }
210
+ reject(new Error("background session step aborted"));
211
+ },
212
+ { once: true }
213
+ );
214
+ });
215
+ const runPromise = input
216
+ .run({
217
+ agentId: session.authority.agentId,
218
+ invocation,
219
+ onUpdate: (update) => {
220
+ if (update.event.type === "agent_run_accepted" && !runId) {
221
+ runId = update.event.runId;
222
+ }
223
+ },
224
+ sessionKey: session.authority.sessionKey,
225
+ text: wakeText
226
+ })
227
+ .then((result) => readStepRunResult(result));
228
+ return Promise.race([runPromise, abortPromise]);
229
+ },
230
+ sleep
231
+ });
232
+ let running = false;
233
+ return {
234
+ running: () => running,
235
+ status: () => supervisor.status(),
236
+ start: async () => {
237
+ await Effect.runPromise(supervisor.recover());
238
+ await Effect.runPromise(supervisor.start());
239
+ running = true;
240
+ },
241
+ stop: async () => {
242
+ await Effect.runPromise(supervisor.stop());
243
+ running = false;
244
+ }
245
+ };
246
+ };
97
247
  return {
98
248
  dispose: () => telemetry?.shutdown(),
99
249
  createRecoveryControl: () =>
@@ -101,6 +251,9 @@ export async function createRivusDeploymentAdapters(context: RivusDeploymentBoot
101
251
  endpointsDirectory: join(STATE_DIR, "endpoints"),
102
252
  instancesDirectory: join(STATE_DIR, "instances")
103
253
  }),
254
+ createBackgroundSession: backgroundSessionsConfig?.enabled
255
+ ? (input: CreateRivusDeploymentBackgroundSessionInput) => createBackgroundSessionAdapter(input)
256
+ : undefined,
104
257
  createAutomation: async (input: CreateRivusDeploymentAutomationInput) => {
105
258
  const credentials = resolveFeishuEndpointCredentials(input.deliveryEndpoint.credentialRef, context.env);
106
259
  const config: RivusDaemonConfig = {
@@ -278,7 +431,18 @@ export async function createRivusDeploymentAdapters(context: RivusDeploymentBoot
278
431
  const broker = createToolBroker({
279
432
  approvals: createRoutedHumanInteractionToolApprovalService(interactionRegistry),
280
433
  catalog: input.catalog,
281
- ...(input.definition.memory.tool ? { hostTools: [createRivusMemoryToolDescriptor({ memory })] } : {}),
434
+ hostTools: [
435
+ ...(input.definition.memory.tool ? [createRivusMemoryToolDescriptor({ memory })] : []),
436
+ ...(backgroundService
437
+ ? [
438
+ ...createBackgroundSessionHostTools({
439
+ createSessionId: () => `bg-${randomUUID()}`,
440
+ definition: input.definition,
441
+ service: backgroundService
442
+ })
443
+ ]
444
+ : [])
445
+ ],
282
446
  operations: await openJsonlToolOperationLedger({ filePath: join(instanceState, "tool-operations.jsonl") }),
283
447
  policy: { current: async () => ({ epoch: 1, revokedToolIds: [] }) }
284
448
  });
@@ -374,10 +538,14 @@ export async function createRivusDeploymentAdapters(context: RivusDeploymentBoot
374
538
  eventSinks: telemetry ? [eventLog, telemetry.sink] : [eventLog],
375
539
  initialEvents: eventsForSession(initialEvents, sessionKey),
376
540
  loop,
377
- runIds: createUuidRunIds()
541
+ runIds: createUuidRunIds(),
542
+ ...(input.binding.kind === "background-session" && backgroundSessionsConfig
543
+ ? { runTimeoutMs: backgroundSessionsConfig.stepTimeoutMs }
544
+ : {})
378
545
  })
379
546
  ),
380
- maxConcurrentSessions: 4,
547
+ maxConcurrentSessions:
548
+ input.binding.kind === "background-session" ? (backgroundSessionsConfig?.maxConcurrentSessions ?? 4) : 4,
381
549
  maxQueuedRuns: 32
382
550
  });
383
551
  return {
@@ -439,6 +607,21 @@ function readAutomationRunResult(result: unknown): { readonly body: string; read
439
607
  throw new Error("Scheduled Automation Agent Run did not produce a runId and final text");
440
608
  }
441
609
 
610
+ function readStepRunResult(result: unknown): { readonly finalText: string; readonly runId: string } {
611
+ if (
612
+ result !== null &&
613
+ typeof result === "object" &&
614
+ "finalText" in result &&
615
+ typeof result.finalText === "string" &&
616
+ "runId" in result &&
617
+ typeof result.runId === "string" &&
618
+ result.runId.trim() !== ""
619
+ ) {
620
+ return { finalText: result.finalText, runId: result.runId };
621
+ }
622
+ throw new Error("Background session Agent Run did not produce a runId and final text");
623
+ }
624
+
442
625
  function createLazyFeishuWebSocketClient(
443
626
  credentials: {
444
627
  readonly appId: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rivus/agent",
3
- "version": "0.6.2",
3
+ "version": "0.8.0",
4
4
  "description": "A local agent daemon core built around a usable agent harness and domain events.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -41,6 +41,10 @@
41
41
  "./testing": {
42
42
  "types": "./dist/testing/index.d.ts",
43
43
  "import": "./dist/testing/index.js"
44
+ },
45
+ "./mcp": {
46
+ "types": "./dist/mcp.d.ts",
47
+ "import": "./dist/mcp.js"
44
48
  }
45
49
  },
46
50
  "files": [