@rivus/agent 0.5.1 → 0.6.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.
@@ -313,6 +313,331 @@ function required$1(env, variable) {
313
313
  return value;
314
314
  }
315
315
  //#endregion
316
+ //#region src/domain/card-presentation.ts
317
+ var CardPresentationTransitionDenied = class extends Error {
318
+ name = "CardPresentationTransitionDenied";
319
+ };
320
+ const STREAMING_STATUSES = /* @__PURE__ */ new Set(["streaming", "active"]);
321
+ function createCardPresentationChain(binding) {
322
+ assertTimestamp(binding.createdAt, "presentation creation time");
323
+ assertTimestamp(binding.leaseDeadlineAt, "presentation lease deadline");
324
+ return {
325
+ activeGeneration: 0,
326
+ activePresentationId: binding.presentationId,
327
+ presentations: [{
328
+ cardId: binding.cardId,
329
+ createdAt: binding.createdAt,
330
+ ...binding.elementId === void 0 ? {} : { elementId: binding.elementId },
331
+ generation: 0,
332
+ leaseDeadlineAt: binding.leaseDeadlineAt,
333
+ presentationId: binding.presentationId,
334
+ runId: binding.runId,
335
+ sourceMessageId: binding.sourceMessageId,
336
+ status: "streaming"
337
+ }],
338
+ revision: 1,
339
+ runId: binding.runId
340
+ };
341
+ }
342
+ function activeCardPresentation(chain) {
343
+ const presentation = chain.presentations.find((candidate) => candidate.presentationId === chain.activePresentationId);
344
+ if (!presentation) throw new CardPresentationTransitionDenied(`presentation chain ${chain.runId} has no active presentation`);
345
+ return presentation;
346
+ }
347
+ function acceptsCardPresentationProgress(chain) {
348
+ return STREAMING_STATUSES.has(activeCardPresentation(chain).status);
349
+ }
350
+ function isCardPresentationHandoffDue(chain, now) {
351
+ const presentation = activeCardPresentation(chain);
352
+ return STREAMING_STATUSES.has(presentation.status) && Date.parse(presentation.leaseDeadlineAt) <= Date.parse(now);
353
+ }
354
+ function beginCardPresentationHandoff(chain, handoffAt) {
355
+ assertTimestamp(handoffAt, "handoff time");
356
+ const presentation = activeCardPresentation(chain);
357
+ if (presentation.status === "handoff-pending") return chain;
358
+ if (!STREAMING_STATUSES.has(presentation.status)) throw new CardPresentationTransitionDenied(`presentation ${presentation.presentationId} is ${presentation.status} and cannot start a handoff`);
359
+ return replaceActivePresentation(chain, {
360
+ ...presentation,
361
+ handoffAt,
362
+ status: "handoff-pending"
363
+ });
364
+ }
365
+ function completeCardPresentationHandoff(chain, successor) {
366
+ assertTimestamp(successor.createdAt, "successor creation time");
367
+ assertTimestamp(successor.leaseDeadlineAt, "successor lease deadline");
368
+ const predecessor = activeCardPresentation(chain);
369
+ if (predecessor.status !== "handoff-pending") throw new CardPresentationTransitionDenied(`presentation ${predecessor.presentationId} is ${predecessor.status} and cannot adopt a successor`);
370
+ if (predecessor.successorPresentationId !== void 0) throw new CardPresentationTransitionDenied(`presentation ${predecessor.presentationId} already has successor ${predecessor.successorPresentationId}`);
371
+ const generation = chain.activeGeneration + 1;
372
+ return {
373
+ ...chain,
374
+ activeGeneration: generation,
375
+ activePresentationId: successor.presentationId,
376
+ presentations: [...chain.presentations.map((candidate) => candidate.presentationId === predecessor.presentationId ? {
377
+ ...predecessor,
378
+ status: "closed",
379
+ successorPresentationId: successor.presentationId
380
+ } : candidate), {
381
+ cardId: successor.cardId,
382
+ createdAt: successor.createdAt,
383
+ ...successor.elementId === void 0 ? {} : { elementId: successor.elementId },
384
+ generation,
385
+ leaseDeadlineAt: successor.leaseDeadlineAt,
386
+ predecessorPresentationId: predecessor.presentationId,
387
+ presentationId: successor.presentationId,
388
+ runId: chain.runId,
389
+ sourceMessageId: predecessor.sourceMessageId,
390
+ status: "active"
391
+ }],
392
+ revision: chain.revision + 1
393
+ };
394
+ }
395
+ function failCardPresentationHandoff(chain, failedAt) {
396
+ assertTimestamp(failedAt, "handoff failure time");
397
+ const presentation = activeCardPresentation(chain);
398
+ if (presentation.status !== "handoff-pending") throw new CardPresentationTransitionDenied(`presentation ${presentation.presentationId} is ${presentation.status} and has no pending handoff`);
399
+ return replaceActivePresentation(chain, {
400
+ ...presentation,
401
+ handoffFailedAt: failedAt,
402
+ status: "closed"
403
+ });
404
+ }
405
+ function compensateCardPresentationHandoff(chain, compensatedAt) {
406
+ return activeCardPresentation(chain).status === "handoff-pending" ? failCardPresentationHandoff(chain, compensatedAt) : chain;
407
+ }
408
+ function markCardPresentationTerminal(chain, terminalReceiptId) {
409
+ if (terminalReceiptId.trim().length === 0) throw new CardPresentationTransitionDenied("terminal receipt id must not be empty");
410
+ const presentation = activeCardPresentation(chain);
411
+ if (presentation.status === "terminal") return chain;
412
+ return replaceActivePresentation(chain, {
413
+ ...presentation,
414
+ status: "terminal",
415
+ terminalReceiptId
416
+ });
417
+ }
418
+ function replaceActivePresentation(chain, presentation) {
419
+ return {
420
+ ...chain,
421
+ presentations: chain.presentations.map((candidate) => candidate.presentationId === presentation.presentationId ? presentation : candidate),
422
+ revision: chain.revision + 1
423
+ };
424
+ }
425
+ function assertTimestamp(value, label) {
426
+ if (Number.isNaN(Date.parse(value))) throw new CardPresentationTransitionDenied(`${label} must be an ISO timestamp`);
427
+ }
428
+ //#endregion
429
+ //#region src/application/feishu/feishu-card-rollover.ts
430
+ const DEFAULT_CARD_STREAM_LEASE_MS = 51e4;
431
+ function createFeishuCardRollover(options) {
432
+ const leaseMs = options.leaseMs ?? 51e4;
433
+ if (!Number.isSafeInteger(leaseMs) || leaseMs < 1) throw new Error("Feishu card stream lease must be a positive integer");
434
+ const counters = createCounters();
435
+ const liveRuns = /* @__PURE__ */ new Map();
436
+ const semaphore = Effect.unsafeMakeSemaphore(1);
437
+ const exclusive = (effect) => semaphore.withPermits(1)(effect);
438
+ const record = (event, at) => {
439
+ counters[event.type] += 1;
440
+ options.observe?.({
441
+ ...event,
442
+ at: at.toISOString()
443
+ });
444
+ };
445
+ const recordNow = (event) => options.clock.now.pipe(Effect.map((at) => record(event, at)));
446
+ const leaseDeadline = (at) => new Date(at.getTime() + leaseMs).toISOString();
447
+ const presentationId = (runId, generation) => `${runId}#${generation}`;
448
+ const publishProgress = (action) => Effect.suspend(() => {
449
+ const chain = options.store.chain(action.runId);
450
+ if (chain && acceptsCardPresentationProgress(chain)) return options.publisher.publish(action);
451
+ return recordNow({
452
+ ...chain ? {
453
+ generation: chain.activeGeneration,
454
+ presentationId: chain.activePresentationId
455
+ } : {},
456
+ runId: action.runId,
457
+ type: "stale_update_dropped"
458
+ });
459
+ });
460
+ const publishTerminal = (action) => options.publisher.publish(action).pipe(Effect.tapError((error) => recordNow({
461
+ error,
462
+ runId: action.runId,
463
+ type: "terminal_delivery_failed"
464
+ })), Effect.tap(() => options.store.markTerminal({
465
+ runId: action.runId,
466
+ terminalReceiptId: `${action.runId}:${action.type}`
467
+ }).pipe(Effect.catchAll((error) => recordNow({
468
+ error,
469
+ runId: action.runId,
470
+ type: "presentation_write_failed"
471
+ })))), Effect.ensuring(Effect.sync(() => {
472
+ liveRuns.delete(action.runId);
473
+ })));
474
+ const publishAction = (action) => {
475
+ switch (action.type) {
476
+ case "update_text": return publishProgress(action);
477
+ case "handoff": return options.publisher.publish(action);
478
+ default: return publishTerminal(action);
479
+ }
480
+ };
481
+ const runHandoff = (runId) => Effect.gen(function* () {
482
+ const run = liveRuns.get(runId);
483
+ if (!run) return {
484
+ reason: "not-live",
485
+ status: "skipped"
486
+ };
487
+ const startedAt = yield* options.clock.now;
488
+ const chain = options.store.chain(runId);
489
+ if (!chain || !isCardPresentationHandoffDue(chain, startedAt.toISOString())) return {
490
+ reason: "not-due",
491
+ status: "skipped"
492
+ };
493
+ const start = yield* options.store.beginHandoff({
494
+ handoffAt: startedAt.toISOString(),
495
+ runId
496
+ }).pipe(Effect.catchAll(() => Effect.succeed(void 0)));
497
+ if (!start) return {
498
+ reason: "not-streaming",
499
+ status: "skipped"
500
+ };
501
+ if (!start.started) return {
502
+ reason: "already-pending",
503
+ status: "skipped"
504
+ };
505
+ const predecessor = start.presentation;
506
+ const generation = predecessor.generation + 1;
507
+ record({
508
+ cardId: predecessor.cardId,
509
+ generation,
510
+ presentationId: predecessor.presentationId,
511
+ runId,
512
+ sourceMessageId: predecessor.sourceMessageId,
513
+ type: "handoff_started"
514
+ }, startedAt);
515
+ const successorId = presentationId(runId, generation);
516
+ const created = yield* options.createSuccessor({
517
+ generation,
518
+ presentationId: successorId,
519
+ run
520
+ }).pipe(Effect.map((target) => ({ target })), Effect.catchAll((error) => options.store.failHandoff({
521
+ failedAt: startedAt.toISOString(),
522
+ runId
523
+ }).pipe(Effect.catchAll((storeError) => recordNow({
524
+ error: storeError,
525
+ runId,
526
+ type: "presentation_write_failed"
527
+ })), Effect.flatMap(() => recordNow({
528
+ cardId: predecessor.cardId,
529
+ error,
530
+ generation,
531
+ presentationId: predecessor.presentationId,
532
+ runId,
533
+ type: "handoff_failed"
534
+ })), Effect.as({ error }))));
535
+ if ("error" in created) return {
536
+ error: created.error,
537
+ status: "failed"
538
+ };
539
+ yield* options.publisher.publish({
540
+ runId,
541
+ type: "handoff"
542
+ }).pipe(Effect.catchAll((error) => recordNow({
543
+ cardId: predecessor.cardId,
544
+ error,
545
+ generation,
546
+ runId,
547
+ type: "handoff_notice_failed"
548
+ })));
549
+ const adoptedAt = yield* options.clock.now;
550
+ const presentation = yield* options.store.completeHandoff({
551
+ runId,
552
+ successor: {
553
+ cardId: created.target.cardId,
554
+ createdAt: adoptedAt.toISOString(),
555
+ ...created.target.elementId === void 0 ? {} : { elementId: created.target.elementId },
556
+ leaseDeadlineAt: leaseDeadline(adoptedAt),
557
+ presentationId: successorId
558
+ }
559
+ });
560
+ record({
561
+ cardId: predecessor.cardId,
562
+ generation,
563
+ presentationId: successorId,
564
+ runId,
565
+ sourceMessageId: predecessor.sourceMessageId,
566
+ successorCardId: created.target.cardId,
567
+ type: "handoff_succeeded"
568
+ }, adoptedAt);
569
+ return {
570
+ presentation,
571
+ status: "rolled-over"
572
+ };
573
+ });
574
+ return {
575
+ bindRun: (run, target) => exclusive(Effect.gen(function* () {
576
+ const createdAt = yield* options.clock.now;
577
+ const presentation = yield* options.store.bind({
578
+ cardId: target.cardId,
579
+ createdAt: createdAt.toISOString(),
580
+ ...target.elementId === void 0 ? {} : { elementId: target.elementId },
581
+ leaseDeadlineAt: leaseDeadline(createdAt),
582
+ presentationId: presentationId(run.runId, 0),
583
+ runId: run.runId,
584
+ sourceMessageId: run.messageId
585
+ });
586
+ liveRuns.set(run.runId, run);
587
+ return presentation;
588
+ })),
589
+ dueRunIds: (now) => {
590
+ const nowIso = now.toISOString();
591
+ return [...liveRuns.keys()].filter((runId) => {
592
+ const chain = options.store.chain(runId);
593
+ return chain !== void 0 && isCardPresentationHandoffDue(chain, nowIso);
594
+ });
595
+ },
596
+ flush: (runId) => exclusive(options.publisher.flush(runId)),
597
+ handoff: (runId) => exclusive(runHandoff(runId)),
598
+ observeStreamClosed: (cardId) => {
599
+ counters.stream_closed += 1;
600
+ options.observe?.({
601
+ cardId,
602
+ type: "stream_closed"
603
+ });
604
+ },
605
+ publish: (action) => exclusive(publishAction(action)),
606
+ recover: () => exclusive(Effect.gen(function* () {
607
+ const compensatedAt = yield* options.clock.now;
608
+ const compensated = yield* options.store.compensateInterruptedHandoffs(compensatedAt.toISOString());
609
+ for (const chain of compensated) record({
610
+ generation: chain.activeGeneration,
611
+ presentationId: chain.activePresentationId,
612
+ runId: chain.runId,
613
+ type: "handoff_failed"
614
+ }, compensatedAt);
615
+ return { compensated: compensated.length };
616
+ })),
617
+ status: () => ({
618
+ counters: { ...counters },
619
+ leaseMs,
620
+ live: [...liveRuns.keys()].flatMap((runId) => {
621
+ const chain = options.store.chain(runId);
622
+ if (!chain) return [];
623
+ return chain.presentations.filter((presentation) => presentation.presentationId === chain.activePresentationId);
624
+ })
625
+ })
626
+ };
627
+ }
628
+ function createCounters() {
629
+ return {
630
+ handoff_failed: 0,
631
+ handoff_notice_failed: 0,
632
+ handoff_started: 0,
633
+ handoff_succeeded: 0,
634
+ presentation_write_failed: 0,
635
+ stale_update_dropped: 0,
636
+ stream_closed: 0,
637
+ terminal_delivery_failed: 0
638
+ };
639
+ }
640
+ //#endregion
316
641
  //#region src/infrastructure/config/rivus-deployment-manifest.ts
317
642
  var RivusDeploymentManifestError = class extends Error {
318
643
  manifestPath;
@@ -397,6 +722,7 @@ function parseManifest(value) {
397
722
  exactKeys(endpoint, [
398
723
  "agentId",
399
724
  "baseUrl",
725
+ "cardStreamLeaseMs",
400
726
  "credentialRef",
401
727
  "enabled",
402
728
  "experimental",
@@ -405,12 +731,13 @@ function parseManifest(value) {
405
731
  "required",
406
732
  "sessionNamespace",
407
733
  "streamMinIntervalMs"
408
- ], `manifest.endpoints[${index}]`, ["experimental"]);
734
+ ], `manifest.endpoints[${index}]`, ["cardStreamLeaseMs", "experimental"]);
409
735
  const experimental = endpoint.experimental === void 0 ? void 0 : record(endpoint.experimental, `manifest.endpoints[${index}].experimental`);
410
736
  if (experimental) exactKeys(experimental, ["cotMessages"], `manifest.endpoints[${index}].experimental`);
411
737
  return Object.freeze({
412
738
  agentId: string(endpoint.agentId, `manifest.endpoints[${index}].agentId`),
413
739
  baseUrl: string(endpoint.baseUrl, `manifest.endpoints[${index}].baseUrl`),
740
+ cardStreamLeaseMs: endpoint.cardStreamLeaseMs === void 0 ? DEFAULT_CARD_STREAM_LEASE_MS : positiveInteger(endpoint.cardStreamLeaseMs, `manifest.endpoints[${index}].cardStreamLeaseMs`),
414
741
  credentialRef: string(endpoint.credentialRef, `manifest.endpoints[${index}].credentialRef`),
415
742
  enabled: boolean(endpoint.enabled, `manifest.endpoints[${index}].enabled`),
416
743
  ...experimental ? { experimental: Object.freeze({ cotMessages: boolean(experimental.cotMessages, `manifest.endpoints[${index}].experimental.cotMessages`) }) } : {},
@@ -561,6 +888,7 @@ function loadRivusDaemonConfig(env, options = {}) {
561
888
  const appId = yield* required(env, "FEISHU_APP_ID");
562
889
  const appSecret = yield* required(env, "FEISHU_APP_SECRET");
563
890
  const streamMinIntervalMs = yield* optionalPositiveInteger(env.FEISHU_STREAM_MIN_INTERVAL_MS, "FEISHU_STREAM_MIN_INTERVAL_MS", DEFAULT_STREAM_MIN_INTERVAL_MS$1);
891
+ const cardStreamLeaseMs = yield* optionalPositiveInteger(env.FEISHU_CARD_STREAM_LEASE_MS, "FEISHU_CARD_STREAM_LEASE_MS", DEFAULT_CARD_STREAM_LEASE_MS);
564
892
  const thinkingLevel = yield* optionalThinkingLevel(env.PI_THINKING_LEVEL);
565
893
  const apiKey = yield* optionalPiApiKey(env, options.readTextFile ?? readUtf8File);
566
894
  const baseUrl = optional(env.PI_BASE_URL);
@@ -571,6 +899,7 @@ function loadRivusDaemonConfig(env, options = {}) {
571
899
  appId,
572
900
  appSecret,
573
901
  baseUrl: optional(env.FEISHU_BASE_URL) ?? DEFAULT_FEISHU_BASE_URL$1,
902
+ cardStreamLeaseMs,
574
903
  streamMinIntervalMs
575
904
  },
576
905
  pi: {
@@ -1670,6 +1999,7 @@ Options:
1670
1999
  Environment:
1671
2000
  RIVUS_BOOTSTRAP_MODULE may be used instead of --bootstrap.
1672
2001
  FEISHU_APP_ID and FEISHU_APP_SECRET are required by the default config loader.
2002
+ FEISHU_CARD_STREAM_LEASE_MS shortens or extends the proactive CardKit rollover threshold.
1673
2003
  PI_API_KEY_FILE may point to a local BYOK key file instead of PI_API_KEY.
1674
2004
  `;
1675
2005
  const DEFAULT_WAIT_RECEIVE_TIMEOUT_MS = 3e4;
@@ -2587,6 +2917,7 @@ function toRedactedConfig(config) {
2587
2917
  appIdPresent: Boolean(config.feishu.appId),
2588
2918
  appSecretPresent: Boolean(config.feishu.appSecret),
2589
2919
  baseUrl: config.feishu.baseUrl,
2920
+ cardStreamLeaseMs: config.feishu.cardStreamLeaseMs,
2590
2921
  streamMinIntervalMs: config.feishu.streamMinIntervalMs
2591
2922
  },
2592
2923
  pi: {
@@ -2760,4 +3091,4 @@ function hasRecoveryRunner(daemon) {
2760
3091
  return typeof daemon.openRecoveryControl === "function";
2761
3092
  }
2762
3093
  //#endregion
2763
- export { loadMergedLocalEnvFile as A, loadRivusDaemonConfig as C, loadRivusDeploymentManifest as D, RivusDeploymentManifestError as E, loadRivusDeployment as M, validateRivusDeploymentManifest as N, FeishuEndpointCredentialError as O, RivusDaemonConfigError as S, resolveNodeRivusPluginModulePath as T, createStableId as _, InvalidRivusProjectSpace as a, createRivusEnvFromOpenClawConfig as b, RivusDeploymentReadinessError as c, createRivusAgentHost as d, AgentInstanceBusy as f, createAgentInstanceRegistry as g, AgentInstanceConflict as h, resolveRivusProjectSpace as i, RivusPluginLoadError as j, resolveFeishuEndpointCredentials as k, createRivusDeploymentDaemon as l, createAgentRuntimePool as m, createRivusDeploymentCliProcess as n, RivusDeploymentAutomationReadinessError as o, AgentRuntimeDisposed as p, createConfiguredRivusDeploymentDaemon as r, RivusDeploymentDaemonLifecycleError as s, runRivusDaemonCli as t, InvalidRivusEndpointBinding as u, createRivusDaemonShutdownController as v, loadNodeRivusPluginModule as w, formatRivusEnvFile as x, OpenClawEnvImportError as y };
3094
+ 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 };
@@ -16,14 +16,14 @@ import {
16
16
  createAgentHarnessPooledRuntime,
17
17
  createRivusMemoryToolDescriptor,
18
18
  createConfiguredFeishuAutomationCardSender,
19
- createConfiguredFeishuCardKitPublisher,
20
- createConfiguredFeishuCardKitTargetPreparation,
19
+ createConfiguredFeishuCardRolloverRuntime,
21
20
  createConfiguredFeishuHumanInteractionPresenter,
22
21
  createConfiguredFeishuOpenApiClient,
23
22
  createConfiguredFeishuTextReplySender,
24
23
  createFeishuCotPublisher,
25
24
  createFeishuDeploymentEndpoint,
26
25
  createFeishuPresentationPreparation,
26
+ createSystemClock,
27
27
  createFeishuCardDeliveryReconciler,
28
28
  createHumanInteractionEndpointRegistry,
29
29
  createHumanInteractionService,
@@ -43,7 +43,6 @@ import {
43
43
  createRoutedHumanInteractionToolApprovalService,
44
44
  createSessionScheduler,
45
45
  createScheduledAutomation,
46
- createSystemClock,
47
46
  createToolBroker,
48
47
  createUuidRunIds,
49
48
  createWorkspaceRootHandle,
@@ -61,6 +60,7 @@ import {
61
60
  type CreateRivusDeploymentAutomationInput,
62
61
  type CreateRivusDeploymentRuntimeInput,
63
62
  type ConfiguredFeishuOpenApiResponse,
63
+ type FeishuAgentRunPreparation,
64
64
  type FeishuWebSocketClient,
65
65
  type RivusDaemonConfig,
66
66
  type RivusDeploymentBootstrapContext,
@@ -106,6 +106,7 @@ export async function createRivusDeploymentAdapters(context: RivusDeploymentBoot
106
106
  feishu: {
107
107
  ...credentials,
108
108
  baseUrl: input.deliveryEndpoint.baseUrl,
109
+ cardStreamLeaseMs: input.deliveryEndpoint.cardStreamLeaseMs,
109
110
  streamMinIntervalMs: input.deliveryEndpoint.streamMinIntervalMs
110
111
  },
111
112
  pi: {}
@@ -176,6 +177,7 @@ export async function createRivusDeploymentAdapters(context: RivusDeploymentBoot
176
177
  feishu: {
177
178
  ...credentials,
178
179
  baseUrl: input.definition.baseUrl,
180
+ cardStreamLeaseMs: input.definition.cardStreamLeaseMs,
179
181
  streamMinIntervalMs: input.definition.streamMinIntervalMs
180
182
  },
181
183
  pi: {}
@@ -198,14 +200,20 @@ export async function createRivusDeploymentAdapters(context: RivusDeploymentBoot
198
200
  filePath: join(endpointState, "human-interactions.jsonl")
199
201
  })
200
202
  });
201
- const publisher = createConfiguredFeishuCardKitPublisher({
203
+ const cardRollover = createConfiguredFeishuCardRolloverRuntime({
202
204
  agentName: input.agentId,
205
+ cardTargets,
203
206
  client: openApiClient,
207
+ clock: createSystemClock(),
204
208
  config,
205
209
  ledger: cardLedger,
206
- resolveTarget: (runId) => cardTargets.resolveTarget(runId),
207
- sleep
210
+ onError: (error) => {
211
+ console.error(`Feishu card rollover failed for endpoint ${input.endpointId}`, error);
212
+ },
213
+ sleep,
214
+ title: input.agentId
208
215
  });
216
+ const publisher = cardRollover.rollover;
209
217
  const endpointEvents = createJsonlAgentEventLog({
210
218
  filePath: join(STATE_DIR, "instances", input.instanceId, "agent-events.jsonl")
211
219
  });
@@ -220,15 +228,11 @@ export async function createRivusDeploymentAdapters(context: RivusDeploymentBoot
220
228
  client: openApiClient,
221
229
  config
222
230
  });
223
- const prepareCardTarget = createConfiguredFeishuCardKitTargetPreparation({
224
- client: openApiClient,
225
- config,
226
- registry: cardTargets,
227
- title: input.agentId
228
- });
231
+ const prepareCardTarget = (run: FeishuAgentRunPreparation) => cardRollover.prepareRun(run);
229
232
  const endpoint = createFeishuDeploymentEndpoint({
230
233
  agentId: input.agentId,
231
234
  botOpenId,
235
+ cardRollover: cardRollover.transport,
232
236
  cancel: input.cancel,
233
237
  endpointId: input.endpointId,
234
238
  eventDispatcher: createLazyFeishuWebSocketEventDispatcher(() => new Lark.EventDispatcher({})),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rivus/agent",
3
- "version": "0.5.1",
3
+ "version": "0.6.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",