@rivus/agent 0.12.7 → 0.13.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.
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@ import { a as createAgentLoopSkillExecutionStart, c as createAgentLoopToolExecut
2
2
  import { a as createMemoryNamespace, c as InvalidRivusPlugin, d as requiresToolApproval, i as RIVUS_MEMORY_TOOL_VERSION, l as RIVUS_PLUGIN_API_VERSION, n as RIVUS_MEMORY_TOOL_ID, o as createRivusMemoryToolContract, r as RIVUS_MEMORY_TOOL_PLUGIN_ID, s as restrictMemoryScopesForAudience, t as MEMORY_SCOPES, u as RivusToolInputRejected } from "./agent-memory.js";
3
3
  import { a as BACKGROUND_SESSION_TOOL_VERSION, c as createBackgroundSessionStepSourceMessageId, d as isBackgroundSessionToolId, f as narrowBackgroundSessionDefinition, i as BACKGROUND_SESSION_TOOL_PLUGIN_ID, l as createBackgroundSessionToolContracts, n as BACKGROUND_SESSION_START_TOOL_ID, o as backgroundSessionToolIds, r as BACKGROUND_SESSION_TOOL_IDS, s as createBackgroundSessionKey, t as BACKGROUND_SESSION_SESSION_KEY_PREFIX, u as extendBackgroundSessionDefinition } from "./background-session-authority.js";
4
4
  import { n as resolveRivusAgentDefinition, t as createRivusPluginCatalog } from "./rivus-plugin-registry.js";
5
- import { $ as loadRivusDeployment, A as DEFAULT_BACKGROUND_SESSION_LIFETIME_MS, B as acceptsCardPresentationProgress, C as loadRivusDaemonConfig, D as loadRivusDeploymentManifest, E as RivusDeploymentManifestError, F as DEFAULT_BACKGROUND_SESSION_SUPERVISOR_INTERVAL_MS, G as createCardPresentationChain, H as beginCardPresentationHandoff, I as resolveBackgroundSessionSupervisorIntervalMs, J as markCardPresentationTerminal, K as failCardPresentationHandoff, L as DEFAULT_CARD_STREAM_LEASE_MS, M as DEFAULT_BACKGROUND_SESSION_MAX_CONSECUTIVE_FAILURES, N as DEFAULT_BACKGROUND_SESSION_RETRY_BACKOFF_MS, O as DEFAULT_BACKGROUND_SESSION_LEASE_MS, P as DEFAULT_BACKGROUND_SESSION_STEP_TIMEOUT_MS, Q as RivusPluginLoadError, R as createFeishuCardRollover, S as RivusDaemonConfigError, U as compensateCardPresentationHandoff, V as activeCardPresentation, W as completeCardPresentationHandoff, X as resolveFeishuEndpointCredentials, Y as FeishuEndpointCredentialError, _ as createStableId, a as InvalidRivusProjectSpace, b as createRivusEnvFromOpenClawConfig, c as RivusDeploymentReadinessError, d as createRivusAgentHost, et as validateRivusDeploymentManifest, f as AgentInstanceBusy, g as createAgentInstanceRegistry, h as AgentInstanceConflict, i as resolveRivusProjectSpace, j as DEFAULT_BACKGROUND_SESSION_MAX_CONCURRENT_SESSIONS, k as DEFAULT_BACKGROUND_SESSION_LEASE_RENEWAL_INTERVAL_MS, l as createRivusDeploymentDaemon, m as createAgentRuntimePool, n as createRivusDeploymentCliProcess, o as RivusDeploymentAutomationReadinessError, p as AgentRuntimeDisposed, q as isCardPresentationHandoffDue, r as createConfiguredRivusDeploymentDaemon, s as RivusDeploymentDaemonLifecycleError, t as runRivusDaemonCli, u as InvalidRivusEndpointBinding, v as createRivusDaemonShutdownController, w as loadNodeRivusPluginModule, x as formatRivusEnvFile, y as OpenClawEnvImportError, z as CardPresentationTransitionDenied } from "./rivus-daemon-cli.js";
5
+ import { $ as resolveFeishuEndpointCredentials, A as DEFAULT_BACKGROUND_SESSION_LEASE_RENEWAL_INTERVAL_MS, B as RUN_PRESENTATION_SCHEMA_VERSION, C as loadRivusDaemonConfig, D as loadRivusDeploymentManifest, E as RivusDeploymentManifestError, F as DEFAULT_BACKGROUND_SESSION_STEP_TIMEOUT_MS, G as beginCardPresentationHandoff, H as CardPresentationTransitionDenied, I as DEFAULT_BACKGROUND_SESSION_SUPERVISOR_INTERVAL_MS, J as createCardPresentationChain, K as compensateCardPresentationHandoff, L as resolveBackgroundSessionSupervisorIntervalMs, M as DEFAULT_BACKGROUND_SESSION_MAX_CONCURRENT_SESSIONS, N as DEFAULT_BACKGROUND_SESSION_MAX_CONSECUTIVE_FAILURES, O as DEFAULT_CONVERSATION_PROGRESS_DISPLAY, P as DEFAULT_BACKGROUND_SESSION_RETRY_BACKOFF_MS, Q as FeishuEndpointCredentialError, R as DEFAULT_CARD_STREAM_LEASE_MS, S as RivusDaemonConfigError, U as acceptsCardPresentationProgress, V as hasInspectableRunProgress, W as activeCardPresentation, X as isCardPresentationHandoffDue, Y as failCardPresentationHandoff, Z as markCardPresentationTerminal, _ as createStableId, a as InvalidRivusProjectSpace, b as createRivusEnvFromOpenClawConfig, c as RivusDeploymentReadinessError, d as createRivusAgentHost, f as AgentInstanceBusy, g as createAgentInstanceRegistry, h as AgentInstanceConflict, i as resolveRivusProjectSpace, j as DEFAULT_BACKGROUND_SESSION_LIFETIME_MS, k as DEFAULT_BACKGROUND_SESSION_LEASE_MS, l as createRivusDeploymentDaemon, m as createAgentRuntimePool, n as createRivusDeploymentCliProcess, nt as loadRivusDeployment, o as RivusDeploymentAutomationReadinessError, p as AgentRuntimeDisposed, q as completeCardPresentationHandoff, r as createConfiguredRivusDeploymentDaemon, rt as validateRivusDeploymentManifest, s as RivusDeploymentDaemonLifecycleError, t as runRivusDaemonCli, tt as RivusPluginLoadError, u as InvalidRivusEndpointBinding, v as createRivusDaemonShutdownController, w as loadNodeRivusPluginModule, x as formatRivusEnvFile, y as OpenClawEnvImportError, z as createFeishuCardRollover } from "./rivus-daemon-cli.js";
6
6
  import { a as InvalidInvocationAuthority, i as normalizeStableJson, n as InvalidToolInput, o as createInvocationAuthority, r as createToolInputDigest, s as resolveInvocationAuthority, t as InvalidStableJson } from "./tool-input-digest.js";
7
7
  import { a as readBackgroundSessionWaitInput, i as readBackgroundSessionString, n as readBackgroundSessionObject, r as readBackgroundSessionPhase, t as readBackgroundSessionInteger } from "./background-session-input.js";
8
8
  import { n as assertRivusPluginConforms, r as createFakeRivusPlugin, t as RivusPluginConformanceError } from "./rivus-plugin-testkit.js";
@@ -28,67 +28,421 @@ function createSerialExecutor() {
28
28
  } };
29
29
  }
30
30
  //#endregion
31
+ //#region src/application/presentation/presented-value.ts
32
+ const DEFAULT_MAX_COLLECTION_ITEMS = 20;
33
+ const DEFAULT_MAX_DEPTH = 5;
34
+ const DEFAULT_MAX_NODES = 100;
35
+ const DEFAULT_MAX_STRING_CHARACTERS = 800;
36
+ const SENSITIVE_KEY = /authorization|cookie|credential|password|passwd|private[-_]?key|secret|session[-_]?token|(?:^|[-_])token(?:$|[-_])|api[-_]?key|access[-_]?(?:key|token)|refresh[-_]?token|client[-_]?secret/i;
37
+ const WORKING_DIRECTORY_KEY = /^(?:cwd|workdir|workingDirectory|workspace|workspaceRoot)$/i;
38
+ const INLINE_SECRET = /\b(Bearer\s+)[^\s,;]+|\b(?:gh[opusr]_|sk-|xox[baprs]-)[A-Za-z0-9_-]{8,}/gi;
39
+ const NAMED_SECRET_ARGUMENT = /((?:--?|\b)(?:authorization|password|passwd|secret|token|api[-_]?key|access[-_]?token)(?:\s+|=))([^\s,;]+)/gi;
40
+ const SECRET_ENVIRONMENT_ASSIGNMENT = /\b([A-Z0-9_]*(?:PASSWORD|SECRET|TOKEN|API_KEY|ACCESS_KEY)=)([^\s,;]+)/g;
41
+ function createPresentedValue(value, options = {}) {
42
+ return present(value, {
43
+ ancestors: /* @__PURE__ */ new Set(),
44
+ depth: 0,
45
+ maxCollectionItems: options.maxCollectionItems ?? DEFAULT_MAX_COLLECTION_ITEMS,
46
+ maxDepth: options.maxDepth ?? DEFAULT_MAX_DEPTH,
47
+ nodeBudget: { remaining: options.maxNodes ?? DEFAULT_MAX_NODES },
48
+ maxStringCharacters: options.maxStringCharacters ?? DEFAULT_MAX_STRING_CHARACTERS
49
+ });
50
+ }
51
+ function present(value, context, key) {
52
+ if (key && SENSITIVE_KEY.test(key)) return {
53
+ kind: "redacted",
54
+ reason: "sensitive-key"
55
+ };
56
+ if (context.nodeBudget.remaining <= 0) return {
57
+ kind: "unsupported",
58
+ valueType: "node-budget"
59
+ };
60
+ context.nodeBudget.remaining -= 1;
61
+ if (value === null) return { kind: "null" };
62
+ if (typeof value === "boolean") return {
63
+ kind: "boolean",
64
+ value
65
+ };
66
+ if (typeof value === "number") return Number.isFinite(value) ? {
67
+ kind: "number",
68
+ value
69
+ } : {
70
+ kind: "unsupported",
71
+ valueType: "number"
72
+ };
73
+ if (typeof value === "string") return presentText(value, context.maxStringCharacters, key);
74
+ if (typeof value !== "object") return {
75
+ kind: "unsupported",
76
+ valueType: typeof value
77
+ };
78
+ if (context.depth >= context.maxDepth) return {
79
+ kind: "unsupported",
80
+ valueType: "max-depth"
81
+ };
82
+ if (context.ancestors.has(value)) return {
83
+ kind: "unsupported",
84
+ valueType: "circular"
85
+ };
86
+ context.ancestors.add(value);
87
+ const nested = {
88
+ ...context,
89
+ depth: context.depth + 1
90
+ };
91
+ try {
92
+ if (Array.isArray(value)) {
93
+ const visibleLength = Math.min(value.length, context.maxCollectionItems);
94
+ const items = Array.from({ length: visibleLength }, (_, index) => Object.hasOwn(value, index) ? present(value[index], nested) : {
95
+ kind: "unsupported",
96
+ valueType: "array-hole"
97
+ });
98
+ return {
99
+ items,
100
+ kind: "list",
101
+ omittedItems: Math.max(0, value.length - items.length)
102
+ };
103
+ }
104
+ if (value instanceof Error) return {
105
+ entries: [{
106
+ key: "name",
107
+ value: present(value.name, nested, "name")
108
+ }, {
109
+ key: "message",
110
+ value: present(value.message, nested, "message")
111
+ }],
112
+ kind: "record",
113
+ omittedEntries: 0
114
+ };
115
+ const prototype = Object.getPrototypeOf(value);
116
+ if (prototype !== Object.prototype && prototype !== null) return {
117
+ kind: "unsupported",
118
+ valueType: prototype?.constructor?.name ?? "object"
119
+ };
120
+ const allEntries = Object.entries(value);
121
+ const entries = allEntries.slice(0, context.maxCollectionItems).map(([entryKey, entryValue]) => ({
122
+ key: safeKey(entryKey),
123
+ value: present(entryValue, nested, entryKey)
124
+ }));
125
+ return {
126
+ entries,
127
+ kind: "record",
128
+ omittedEntries: Math.max(0, allEntries.length - entries.length)
129
+ };
130
+ } finally {
131
+ context.ancestors.delete(value);
132
+ }
133
+ }
134
+ function presentText(value, maxCharacters, key) {
135
+ if (INLINE_SECRET.test(value)) {
136
+ INLINE_SECRET.lastIndex = 0;
137
+ value = value.replace(INLINE_SECRET, (_match, bearerPrefix) => bearerPrefix ? `${bearerPrefix}<redacted>` : "<redacted>");
138
+ }
139
+ INLINE_SECRET.lastIndex = 0;
140
+ value = value.replace(NAMED_SECRET_ARGUMENT, "$1<redacted>").replace(SECRET_ENVIRONMENT_ASSIGNMENT, "$1<redacted>");
141
+ value = normalizeMachinePaths(value, key);
142
+ const preview = value.slice(0, maxCharacters);
143
+ return {
144
+ kind: "text",
145
+ omittedCharacters: Math.max(0, value.length - preview.length),
146
+ value: preview
147
+ };
148
+ }
149
+ function normalizeMachinePaths(value, key) {
150
+ if (key && WORKING_DIRECTORY_KEY.test(key)) return "<workspace>";
151
+ return value.replace(/\/(?:Users|home)\/[^/\s]+/g, "<home>").replace(/\/(?:private\/)?tmp\/[^\s'";]*/g, (path) => `<tmp>/${path.split("/").at(-1) || "item"}`);
152
+ }
153
+ function safeKey(value) {
154
+ return value.replace(/[^\p{L}\p{N}._:/-]+/gu, " ").trim().slice(0, 80) || "unknown";
155
+ }
156
+ //#endregion
157
+ //#region src/application/presentation/run-presentation-projector.ts
158
+ function createRunPresentationProjector() {
159
+ const runs = /* @__PURE__ */ new Map();
160
+ const stateFor = (runId) => {
161
+ const existing = runs.get(runId);
162
+ if (existing) return existing;
163
+ const created = {
164
+ answer: "",
165
+ answerStartedAt: void 0,
166
+ assistantSequence: 0,
167
+ currentSkillId: void 0,
168
+ order: [],
169
+ phase: "running",
170
+ revision: 0,
171
+ steps: /* @__PURE__ */ new Map()
172
+ };
173
+ runs.set(runId, created);
174
+ return created;
175
+ };
176
+ const snapshot = (runId) => {
177
+ const state = runs.get(runId);
178
+ if (!state) return void 0;
179
+ const visibleKeys = state.order.slice(-8);
180
+ return {
181
+ answer: {
182
+ format: "markdown",
183
+ text: state.answer
184
+ },
185
+ omittedStepCount: Math.max(0, state.order.length - visibleKeys.length),
186
+ phase: state.phase,
187
+ revision: state.revision,
188
+ runId,
189
+ schemaVersion: 2,
190
+ steps: visibleKeys.map((key) => state.steps.get(key)).filter((step) => step !== void 0),
191
+ totalStepCount: state.order.length,
192
+ totalToolCallCount: [...state.steps.values()].filter((step) => step.kind === "tool").length
193
+ };
194
+ };
195
+ const mutate = (runId, mutation) => {
196
+ const state = stateFor(runId);
197
+ mutation(state);
198
+ state.revision += 1;
199
+ return snapshot(runId);
200
+ };
201
+ const upsert = (state, step) => {
202
+ if (!state.steps.has(step.id)) state.order.push(step.id);
203
+ state.steps.set(step.id, step);
204
+ };
205
+ const commitAssistantNarration = (state, endedAt) => {
206
+ const text = state.answer.trim();
207
+ if (!text) return;
208
+ const id = `assistant:${state.assistantSequence + 1}`;
209
+ state.assistantSequence += 1;
210
+ const startedAt = state.answerStartedAt ?? endedAt.toISOString();
211
+ upsert(state, {
212
+ durationMs: elapsed(startedAt, endedAt),
213
+ endedAt: endedAt.toISOString(),
214
+ id,
215
+ kind: "assistant",
216
+ label: "过程说明",
217
+ startedAt,
218
+ status: "completed",
219
+ text
220
+ });
221
+ state.answer = "";
222
+ state.answerStartedAt = void 0;
223
+ };
224
+ return {
225
+ apply: (event, canonicalText) => {
226
+ switch (event.type) {
227
+ case "agent_run_accepted":
228
+ runs.set(event.runId, {
229
+ answer: "",
230
+ answerStartedAt: void 0,
231
+ assistantSequence: 0,
232
+ currentSkillId: void 0,
233
+ order: [],
234
+ phase: "running",
235
+ revision: 1,
236
+ steps: /* @__PURE__ */ new Map()
237
+ });
238
+ return snapshot(event.runId);
239
+ case "agent_turn_started": return mutate(event.runId, (state) => {
240
+ state.answer = "";
241
+ state.answerStartedAt = void 0;
242
+ state.currentSkillId = void 0;
243
+ });
244
+ case "agent_model_execution_started": return mutate(event.runId, (state) => {
245
+ state.answer = "";
246
+ state.answerStartedAt = void 0;
247
+ state.currentSkillId = void 0;
248
+ upsert(state, {
249
+ id: `model:${event.modelCallId}`,
250
+ kind: "model",
251
+ label: "分析问题",
252
+ model: safeIdentifier(event.model),
253
+ provider: safeIdentifier(event.provider),
254
+ startedAt: event.occurredAt.toISOString(),
255
+ status: "running"
256
+ });
257
+ });
258
+ case "agent_model_execution_ended": return mutate(event.runId, (state) => {
259
+ const id = `model:${event.modelCallId}`;
260
+ upsert(state, {
261
+ ...completedTiming(state, id, event.occurredAt),
262
+ id,
263
+ kind: "model",
264
+ label: "分析问题",
265
+ model: safeIdentifier(event.responseModel ?? event.model),
266
+ provider: safeIdentifier(event.provider),
267
+ status: event.errorMessage ? "failed" : "completed"
268
+ });
269
+ });
270
+ case "agent_skill_execution_started": return mutate(event.runId, (state) => {
271
+ commitAssistantNarration(state, event.occurredAt);
272
+ state.currentSkillId = safeIdentifier(event.skillId);
273
+ upsert(state, {
274
+ id: `skill:${event.skillCallId}`,
275
+ kind: "skill",
276
+ label: "读取技能",
277
+ skillId: safeIdentifier(event.skillId),
278
+ startedAt: event.occurredAt.toISOString(),
279
+ status: "running"
280
+ });
281
+ });
282
+ case "agent_skill_execution_ended": return mutate(event.runId, (state) => {
283
+ const id = `skill:${event.skillCallId}`;
284
+ upsert(state, {
285
+ ...completedTiming(state, id, event.occurredAt),
286
+ id,
287
+ kind: "skill",
288
+ label: "读取技能",
289
+ skillId: safeIdentifier(event.skillId),
290
+ status: event.isError ? "failed" : "completed"
291
+ });
292
+ });
293
+ case "agent_tool_execution_started": return mutate(event.runId, (state) => {
294
+ commitAssistantNarration(state, event.occurredAt);
295
+ upsert(state, runningToolStep(state, event));
296
+ });
297
+ case "agent_tool_execution_updated": return mutate(event.runId, (state) => {
298
+ upsert(state, runningToolStep(state, event));
299
+ });
300
+ case "agent_tool_execution_ended": return mutate(event.runId, (state) => {
301
+ const id = `tool:${event.toolCallId}`;
302
+ const existing = state.steps.get(id);
303
+ const startedAt = existing?.startedAt ?? event.occurredAt.toISOString();
304
+ const input = existing?.kind === "tool" ? existing.input : createPresentedValue(void 0);
305
+ upsert(state, {
306
+ durationMs: elapsed(startedAt, event.occurredAt),
307
+ endedAt: event.occurredAt.toISOString(),
308
+ ...event.isError ? { error: createPresentedValue(event.result) } : { output: createPresentedValue(event.result) },
309
+ id,
310
+ input,
311
+ kind: "tool",
312
+ label: "执行工具",
313
+ ...existing?.kind === "tool" && existing.skillId ? { skillId: existing.skillId } : {},
314
+ startedAt,
315
+ status: event.isError ? "failed" : "completed",
316
+ toolName: safeIdentifier(event.toolName)
317
+ });
318
+ });
319
+ case "assistant_text_delta": return mutate(event.runId, (state) => {
320
+ state.answerStartedAt ??= event.occurredAt.toISOString();
321
+ state.answer += event.delta;
322
+ });
323
+ case "assistant_thinking_delta":
324
+ case "agent_turn_completed": return snapshot(event.runId);
325
+ case "agent_run_completed": {
326
+ const result = finish(event.runId, "completed", "回答已完成", event.occurredAt, canonicalText, event.finalText, stateFor, mutate, upsert);
327
+ runs.delete(event.runId);
328
+ return result;
329
+ }
330
+ case "agent_run_failed": {
331
+ const result = finish(event.runId, "failed", "运行未完成", event.occurredAt, void 0, canonicalText, stateFor, mutate, upsert);
332
+ runs.delete(event.runId);
333
+ return result;
334
+ }
335
+ case "agent_run_cancelled": {
336
+ const result = finish(event.runId, "cancelled", "运行已取消", event.occurredAt, void 0, canonicalText, stateFor, mutate, upsert);
337
+ runs.delete(event.runId);
338
+ return result;
339
+ }
340
+ }
341
+ },
342
+ snapshot
343
+ };
344
+ }
345
+ function runningToolStep(state, event) {
346
+ const id = `tool:${event.toolCallId}`;
347
+ const existing = state.steps.get(id);
348
+ const skillId = existing?.kind === "tool" ? existing.skillId : state.currentSkillId;
349
+ const output = event.type === "agent_tool_execution_updated" ? createPresentedValue(event.partialResult) : existing?.kind === "tool" ? existing.output : void 0;
350
+ return {
351
+ id,
352
+ input: createPresentedValue(event.input),
353
+ kind: "tool",
354
+ label: "执行工具",
355
+ ...output === void 0 ? {} : { output },
356
+ ...skillId ? { skillId } : {},
357
+ startedAt: existing?.startedAt ?? event.occurredAt.toISOString(),
358
+ status: "running",
359
+ toolName: safeIdentifier(event.toolName)
360
+ };
361
+ }
362
+ function finish(runId, phase, label, occurredAt, preferredText, fallbackText, stateFor, mutate, upsert) {
363
+ stateFor(runId);
364
+ const startedAt = occurredAt.toISOString();
365
+ return mutate(runId, (state) => {
366
+ state.phase = phase;
367
+ state.answer = preferredText?.trim() || state.answer.trim() || fallbackText?.trim() || state.answer;
368
+ for (const step of state.steps.values()) if (step.status === "running") state.steps.set(step.id, finishRunningStep(step, occurredAt, phase === "completed" ? "completed" : "failed"));
369
+ upsert(state, {
370
+ endedAt: occurredAt.toISOString(),
371
+ id: `response:${phase}`,
372
+ kind: "response",
373
+ label,
374
+ startedAt,
375
+ status: phase === "completed" ? "completed" : "failed"
376
+ });
377
+ });
378
+ }
379
+ function finishRunningStep(step, occurredAt, status) {
380
+ return {
381
+ ...step,
382
+ durationMs: elapsed(step.startedAt, occurredAt),
383
+ endedAt: occurredAt.toISOString(),
384
+ status
385
+ };
386
+ }
387
+ function completedTiming(state, id, occurredAt) {
388
+ const startedAt = state.steps.get(id)?.startedAt ?? occurredAt.toISOString();
389
+ return {
390
+ durationMs: elapsed(startedAt, occurredAt),
391
+ endedAt: occurredAt.toISOString(),
392
+ startedAt
393
+ };
394
+ }
395
+ function elapsed(startedAt, endedAt) {
396
+ return Math.max(0, endedAt.getTime() - Date.parse(startedAt));
397
+ }
398
+ function safeIdentifier(value) {
399
+ return value.replace(/[^\p{L}\p{N}._:/-]+/gu, " ").trim().slice(0, 80) || "unknown";
400
+ }
401
+ //#endregion
31
402
  //#region src/application/feishu/feishu-stream-projector.ts
32
403
  function createFeishuStreamProjector() {
33
- const currentTextByRun = /* @__PURE__ */ new Map();
34
- const lastTextByRun = /* @__PURE__ */ new Map();
35
- const update = (runId, text) => [{
36
- runId,
37
- text,
38
- type: "update_text"
39
- }];
40
- const clear = (runId) => {
41
- currentTextByRun.delete(runId);
42
- lastTextByRun.delete(runId);
43
- };
404
+ const presentations = createRunPresentationProjector();
44
405
  const applyEvent = (event, canonicalText) => {
406
+ const presentation = presentations.apply(event, canonicalText);
45
407
  switch (event.type) {
46
408
  case "agent_run_accepted":
47
- currentTextByRun.set(event.runId, "");
48
- lastTextByRun.set(event.runId, "");
49
- return [];
50
409
  case "agent_turn_started":
51
- currentTextByRun.set(event.runId, "");
52
- lastTextByRun.set(event.runId, "");
53
- return [];
410
+ case "assistant_thinking_delta":
411
+ case "agent_turn_completed": return [];
54
412
  case "agent_model_execution_started":
55
- currentTextByRun.set(event.runId, "");
56
- return update(event.runId, "正在分析…");
57
- case "agent_skill_execution_started": return update(event.runId, `正在读取技能 \`${presentProgressIdentifier(event.skillId)}\`…`);
58
- case "agent_tool_execution_started": return update(event.runId, `正在执行工具 \`${presentProgressIdentifier(event.toolName)}\`…`);
59
- case "agent_tool_execution_ended": return update(event.runId, event.isError ? "工具执行失败,正在调整方案…" : "已取得工具结果,正在继续分析…");
60
- case "assistant_text_delta": {
61
- const text = `${currentTextByRun.get(event.runId) ?? ""}${event.delta}`;
62
- currentTextByRun.set(event.runId, text);
63
- if (text.trim()) lastTextByRun.set(event.runId, text);
64
- return update(event.runId, text);
65
- }
66
- case "agent_run_completed": {
67
- const currentText = currentTextByRun.get(event.runId)?.trim();
68
- const lastText = lastTextByRun.get(event.runId)?.trim();
69
- const text = currentText || lastText || canonicalText || event.finalText;
70
- clear(event.runId);
71
- return [{
72
- runId: event.runId,
73
- text,
74
- type: "finish"
75
- }];
76
- }
77
- case "agent_run_failed":
78
- clear(event.runId);
79
- return [{
80
- errorMessage: event.errorMessage,
81
- runId: event.runId,
82
- type: "fail"
83
- }];
84
- case "agent_run_cancelled":
85
- clear(event.runId);
86
- return [{
87
- ...event.reason ? { reason: event.reason } : {},
88
- runId: event.runId,
89
- type: "cancel"
90
- }];
91
- default: return [];
413
+ case "agent_model_execution_ended":
414
+ case "agent_skill_execution_started":
415
+ case "agent_skill_execution_ended":
416
+ case "agent_tool_execution_started":
417
+ case "agent_tool_execution_updated":
418
+ case "agent_tool_execution_ended": return presentation ? [{
419
+ presentation,
420
+ runId: event.runId,
421
+ type: "update_progress"
422
+ }] : [];
423
+ case "assistant_text_delta": return [{
424
+ runId: event.runId,
425
+ text: presentation?.answer.text ?? event.delta,
426
+ type: "update_text"
427
+ }];
428
+ case "agent_run_completed": return [{
429
+ ...hasMeaningfulProgress(presentation) ? { presentation } : {},
430
+ runId: event.runId,
431
+ text: presentation?.answer.text.trim() || canonicalText?.trim() || event.finalText,
432
+ type: "finish"
433
+ }];
434
+ case "agent_run_failed": return [{
435
+ errorMessage: event.errorMessage,
436
+ ...hasMeaningfulProgress(presentation) ? { presentation } : {},
437
+ runId: event.runId,
438
+ type: "fail"
439
+ }];
440
+ case "agent_run_cancelled": return [{
441
+ ...hasMeaningfulProgress(presentation) ? { presentation } : {},
442
+ ...event.reason ? { reason: event.reason } : {},
443
+ runId: event.runId,
444
+ type: "cancel"
445
+ }];
92
446
  }
93
447
  };
94
448
  return {
@@ -102,8 +456,8 @@ function createFeishuStreamProjector() {
102
456
  } : event, state.finalText) : applyEvent(event, state.finalText)
103
457
  };
104
458
  }
105
- function presentProgressIdentifier(value) {
106
- return value.replace(/[^\p{L}\p{N}._:/-]+/gu, " ").trim().slice(0, 80) || "unknown";
459
+ function hasMeaningfulProgress(presentation) {
460
+ return presentation !== void 0 && hasInspectableRunProgress(presentation);
107
461
  }
108
462
  //#endregion
109
463
  //#region src/application/feishu/feishu-card-delivery-ledger.ts
@@ -146,7 +500,7 @@ function createFeishuCardDeliveryReconciler(options) {
146
500
  return { reconcile: () => Effect.gen(function* () {
147
501
  const projector = createFeishuStreamProjector();
148
502
  const terminalByRun = /* @__PURE__ */ new Map();
149
- for (const event of options.events) for (const action of projector.apply(event)) if (action.type !== "update_text" && action.type !== "handoff") terminalByRun.set(action.runId, action);
503
+ for (const event of options.events) for (const action of projector.apply(event)) if (action.type === "cancel" || action.type === "fail" || action.type === "finish") terminalByRun.set(action.runId, action);
150
504
  let repaired = 0;
151
505
  let skipped = 0;
152
506
  for (const [runId, action] of terminalByRun) {
@@ -884,7 +1238,10 @@ function evolveAgentRun(state, event) {
884
1238
  ...state,
885
1239
  thinkingText: `${state.thinkingText ?? ""}${event.delta}`
886
1240
  };
887
- case "agent_model_execution_started":
1241
+ case "agent_model_execution_started": return {
1242
+ ...state,
1243
+ finalText: ""
1244
+ };
888
1245
  case "agent_model_execution_ended": return state;
889
1246
  case "agent_skill_execution_started": return {
890
1247
  ...state,
@@ -2519,9 +2876,16 @@ function createFeishuAgentDaemon(options) {
2519
2876
  messageId: inbound.messageId,
2520
2877
  runId: result.runId
2521
2878
  };
2522
- }).pipe(Effect.catchAll((error) => Effect.sync(() => {
2523
- if (dedupeMessageId !== void 0) seenMessageIds.delete(dedupeMessageId);
2524
- }).pipe(Effect.flatMap(() => Effect.fail(error)))));
2879
+ }).pipe(Effect.catchAll((error) => {
2880
+ if (error instanceof AgentRunCancelled) return Effect.succeed({
2881
+ cancelled: true,
2882
+ messageId: dedupeMessageId ?? payload.event.message.message_id,
2883
+ runId: error.runId
2884
+ });
2885
+ return Effect.sync(() => {
2886
+ if (dedupeMessageId !== void 0) seenMessageIds.delete(dedupeMessageId);
2887
+ }).pipe(Effect.flatMap(() => Effect.fail(error)));
2888
+ }));
2525
2889
  }
2526
2890
  };
2527
2891
  }
@@ -3328,43 +3692,225 @@ function createFeishuCardRolloverSupervisor(options) {
3328
3692
  };
3329
3693
  }
3330
3694
  //#endregion
3695
+ //#region src/infrastructure/feishu/feishu-run-presentation-renderer.ts
3696
+ function renderFeishuRunProgress(presentation) {
3697
+ const visible = presentation.steps.filter(isVisibleStep);
3698
+ const running = [...visible].reverse().find((step) => step.status === "running");
3699
+ const title = visible.length === 0 ? "执行过程 · 展开查看" : presentation.phase === "running" && running ? `执行过程 · ${runningLabel(running)}` : `执行过程 · ${visible.length} 段 · ${presentation.totalToolCallCount} 次工具调用`;
3700
+ return {
3701
+ markdown: [...presentation.omittedStepCount > 0 ? [`> 另有 ${presentation.omittedStepCount} 个较早步骤未展示`] : [], ...renderProgressSteps(visible)].join("\n\n") || "正在准备执行…",
3702
+ title
3703
+ };
3704
+ }
3705
+ function renderFeishuRunTimeline(presentation, options) {
3706
+ const visible = presentation.steps.filter(isVisibleStep);
3707
+ const elements = [];
3708
+ for (let index = 0; index < visible.length;) {
3709
+ const step = visible[index];
3710
+ if (step.kind !== "tool") {
3711
+ elements.push(...renderTimelineStep(step, options.expanded));
3712
+ index += 1;
3713
+ continue;
3714
+ }
3715
+ const tools = [];
3716
+ while (index < visible.length && visible[index]?.kind === "tool") {
3717
+ tools.push(visible[index]);
3718
+ index += 1;
3719
+ }
3720
+ elements.push(renderToolSequence(tools, options.expanded));
3721
+ }
3722
+ return {
3723
+ elements,
3724
+ inspectable: visible.some((step) => step.kind === "skill" || step.kind === "tool")
3725
+ };
3726
+ }
3727
+ function renderFeishuVisibleAnswer(presentation, currentText) {
3728
+ const committed = presentation?.steps.filter((step) => step.kind === "assistant").map((step) => step.text.trim()).filter(Boolean) ?? [];
3729
+ const current = currentText.trim();
3730
+ return [...committed, ...current ? [current] : []].join("\n\n");
3731
+ }
3732
+ function renderProgressSteps(steps) {
3733
+ const rendered = [];
3734
+ for (let index = 0; index < steps.length;) {
3735
+ const step = steps[index];
3736
+ if (step.kind !== "tool") {
3737
+ rendered.push(renderStep(step));
3738
+ index += 1;
3739
+ continue;
3740
+ }
3741
+ const tools = [];
3742
+ while (index < steps.length && steps[index]?.kind === "tool") {
3743
+ tools.push(steps[index]);
3744
+ index += 1;
3745
+ }
3746
+ rendered.push(tools.map(renderToolSummary).join("\n"));
3747
+ }
3748
+ return rendered;
3749
+ }
3750
+ function renderToolSequence(tools, expanded) {
3751
+ if (tools.length === 1) return {
3752
+ content: renderToolSummary(tools[0]),
3753
+ tag: "markdown"
3754
+ };
3755
+ return collapsiblePanel({
3756
+ content: tools.map(renderToolSummary).join("\n"),
3757
+ expanded: expanded || tools.some((tool) => tool.status === "running"),
3758
+ title: toolSequenceTitle(tools)
3759
+ });
3760
+ }
3761
+ function toolSequenceTitle(tools) {
3762
+ if ([...tools].reverse().find((tool) => tool.status === "running")) return `执行过程 · ${tools.length} 个工具 · 进行中`;
3763
+ if (tools.some((tool) => tool.status === "failed")) return `执行过程 · ${tools.length} 个工具 · 有失败`;
3764
+ const totalDuration = tools.reduce((total, tool) => total + (tool.durationMs ?? 0), 0);
3765
+ return totalDuration > 0 ? `执行过程 · ${tools.length} 个工具 · 已完成 · ${formatDuration$1(totalDuration)}` : `执行过程 · ${tools.length} 个工具 · 已完成`;
3766
+ }
3767
+ function renderToolSummary(step) {
3768
+ const skill = step.skillId ? ` · ${escapeInline(step.skillId)}` : "";
3769
+ const duration = step.durationMs === void 0 ? "" : ` · ${formatDuration$1(step.durationMs)}`;
3770
+ return `- ${statusIcon(step.status)} **${escapeInline(toolAction(step.toolName))}**${skill}${duration}`;
3771
+ }
3772
+ function renderTimelineStep(step, expandCompleted) {
3773
+ switch (step.kind) {
3774
+ case "assistant": return [{
3775
+ content: step.text,
3776
+ margin: "0px",
3777
+ tag: "markdown",
3778
+ text_align: "left",
3779
+ text_size: "normal"
3780
+ }];
3781
+ case "skill": return [collapsiblePanel({
3782
+ content: skillDetails(step),
3783
+ expanded: step.status === "running" || expandCompleted,
3784
+ title: `${step.skillId} · ${step.status === "running" ? "读取中" : step.status === "failed" ? "读取失败" : "已读取"}`
3785
+ })];
3786
+ case "tool": return [{
3787
+ content: renderToolSummary(step),
3788
+ tag: "markdown"
3789
+ }];
3790
+ case "model":
3791
+ case "response": return [];
3792
+ }
3793
+ }
3794
+ function collapsiblePanel(options) {
3795
+ return {
3796
+ background_color: options.expanded ? "blue-50" : "grey-50",
3797
+ elements: [{
3798
+ content: options.content,
3799
+ tag: "markdown",
3800
+ text_align: "left",
3801
+ text_size: "normal"
3802
+ }],
3803
+ expanded: options.expanded,
3804
+ header: {
3805
+ expanded_title: {
3806
+ content: options.title,
3807
+ tag: "plain_text",
3808
+ text_align: "left"
3809
+ },
3810
+ icon: {
3811
+ color: "grey",
3812
+ tag: "standard_icon",
3813
+ token: "down-small-ccm_outlined"
3814
+ },
3815
+ icon_expanded_angle: -180,
3816
+ icon_position: "right",
3817
+ padding: "8px 12px 8px 12px",
3818
+ title: {
3819
+ content: options.title,
3820
+ tag: "plain_text",
3821
+ text_align: "left"
3822
+ },
3823
+ width: "fill"
3824
+ },
3825
+ margin: "8px 0px 0px 0px",
3826
+ padding: "12px 12px 12px 12px",
3827
+ tag: "collapsible_panel"
3828
+ };
3829
+ }
3830
+ function skillDetails(step) {
3831
+ const duration = step.durationMs === void 0 ? "" : ` · ${formatDuration$1(step.durationMs)}`;
3832
+ return [`**${statusLabel(step.status)}**${duration}`, `这一步:读取并应用技能 \`${escapeInline(step.skillId)}\` 的任务说明。`].join("\n\n");
3833
+ }
3834
+ function statusLabel(status) {
3835
+ switch (status) {
3836
+ case "completed": return "已完成";
3837
+ case "failed": return "未完成";
3838
+ case "running": return "进行中";
3839
+ }
3840
+ }
3841
+ function toolAction(toolName) {
3842
+ const normalized = toolName.toLowerCase();
3843
+ const executable = normalized.split(/[\\/]/u).at(-1) ?? normalized;
3844
+ if (executable === "bash" || executable === "exec" || executable === "exec_command") return "执行命令";
3845
+ if (normalized.includes("read") && normalized.includes("file")) return "读取文件";
3846
+ if (normalized.includes("write") && normalized.includes("file")) return "写入文件";
3847
+ if (normalized.includes("search") || normalized === "rg") return "搜索内容";
3848
+ if (normalized.includes("fetch") || normalized.includes("http")) return "访问服务";
3849
+ return `调用 ${toolName.replace(/^.*[\\/]/u, "")}`;
3850
+ }
3851
+ function renderStep(step) {
3852
+ const duration = step.durationMs === void 0 ? "" : ` · ${formatDuration$1(step.durationMs)}`;
3853
+ switch (step.kind) {
3854
+ case "assistant": return step.text;
3855
+ case "model": return `- ${statusIcon(step.status)} ${step.label}${duration}`;
3856
+ case "skill": return `- ${statusIcon(step.status)} ${step.label} \`${escapeInline(step.skillId)}\`${duration}`;
3857
+ case "response": return `- ${statusIcon(step.status)} ${step.label}${duration}`;
3858
+ case "tool": return renderToolSummary(step);
3859
+ }
3860
+ }
3861
+ function isVisibleStep(step) {
3862
+ return step.kind === "assistant" || step.kind === "skill" || step.kind === "tool";
3863
+ }
3864
+ function runningLabel(step) {
3865
+ switch (step.kind) {
3866
+ case "assistant": return "正在说明进展";
3867
+ case "model": return "正在分析";
3868
+ case "skill": return "正在读取技能";
3869
+ case "tool": return `正在调用 ${step.toolName}`;
3870
+ case "response": return "正在整理回答";
3871
+ }
3872
+ }
3873
+ function statusIcon(status) {
3874
+ switch (status) {
3875
+ case "completed": return "✅";
3876
+ case "failed": return "⚠️";
3877
+ case "running": return "⏳";
3878
+ }
3879
+ }
3880
+ function formatDuration$1(milliseconds) {
3881
+ if (milliseconds < 1e3) return `${milliseconds} 毫秒`;
3882
+ if (milliseconds < 6e4) {
3883
+ const seconds = milliseconds / 1e3;
3884
+ return `${Number(seconds.toFixed(seconds < 10 ? 1 : 0))} 秒`;
3885
+ }
3886
+ return `${Math.floor(milliseconds / 6e4)} 分 ${Math.floor(milliseconds % 6e4 / 1e3)} 秒`;
3887
+ }
3888
+ function escapeInline(value) {
3889
+ return value.replace(/`/g, "ˋ");
3890
+ }
3891
+ //#endregion
3331
3892
  //#region src/infrastructure/feishu/feishu-agent-run-card.ts
3332
3893
  const FEISHU_AGENT_CARD_ELEMENT_ID = "rivus_agent_answer";
3894
+ const FEISHU_AGENT_CARD_PROGRESS_ELEMENT_ID = "rivus_agent_progress";
3333
3895
  function createFeishuAgentRunCard(input) {
3334
3896
  const presentation = resolveRunPresentation(input);
3335
3897
  return {
3336
- body: { elements: [
3337
- {
3338
- content: presentation.content,
3339
- element_id: input.status === "running" ? input.elementId ?? "rivus_agent_answer" : FEISHU_AGENT_CARD_ELEMENT_ID,
3340
- tag: "markdown"
3341
- },
3342
- {
3343
- content: `_${presentation.note}_`,
3344
- tag: "markdown"
3345
- },
3346
- ...input.status === "running" && input.cancelButton !== false ? [{
3347
- behaviors: [{
3348
- type: "callback",
3349
- value: {
3350
- rivus_action: "cancel_run",
3351
- run_id: input.runId,
3352
- session_key: input.sessionKey
3353
- }
3354
- }],
3355
- tag: "button",
3356
- text: {
3357
- content: "停止生成",
3358
- tag: "plain_text"
3359
- },
3360
- type: "danger"
3361
- }] : []
3362
- ] },
3898
+ body: {
3899
+ direction: "vertical",
3900
+ elements: [
3901
+ ...createRunElements("progressDisplay" in input ? input.progressDisplay : void 0, "presentation" in input ? input.presentation : void 0, presentation.content, input),
3902
+ { tag: "hr" },
3903
+ createRunFooter(input, presentation.note)
3904
+ ],
3905
+ padding: "18px 18px 16px 18px",
3906
+ vertical_spacing: "small"
3907
+ },
3363
3908
  config: {
3364
3909
  streaming_mode: input.status === "running",
3365
3910
  update_multi: true
3366
3911
  },
3367
3912
  header: {
3913
+ padding: "12px 18px 12px 18px",
3368
3914
  template: presentation.template,
3369
3915
  title: {
3370
3916
  content: `${input.agentName} · ${presentation.title}`,
@@ -3374,19 +3920,68 @@ function createFeishuAgentRunCard(input) {
3374
3920
  schema: "2.0"
3375
3921
  };
3376
3922
  }
3923
+ function createRunFooter(input, note) {
3924
+ const footerText = footerTextElement(note);
3925
+ if (input.status !== "running") return footerRow([footerText, footerTextElement("_Rivus_")]);
3926
+ if (input.cancelButton === false) return footerText;
3927
+ return footerRow([footerText, {
3928
+ behaviors: [{
3929
+ type: "callback",
3930
+ value: {
3931
+ rivus_action: "cancel_run",
3932
+ run_id: input.runId,
3933
+ session_key: input.sessionKey
3934
+ }
3935
+ }],
3936
+ tag: "button",
3937
+ text: {
3938
+ content: "停止",
3939
+ tag: "plain_text"
3940
+ },
3941
+ type: "danger"
3942
+ }]);
3943
+ }
3944
+ function footerRow(elements) {
3945
+ return {
3946
+ columns: [{
3947
+ elements: [elements[0]],
3948
+ tag: "column",
3949
+ vertical_align: "center",
3950
+ weight: 1,
3951
+ width: "weighted"
3952
+ }, ...elements.length > 1 ? [{
3953
+ elements: [elements[1]],
3954
+ horizontal_align: "right",
3955
+ tag: "column",
3956
+ vertical_align: "center",
3957
+ width: "auto"
3958
+ }] : []],
3959
+ flex_mode: "none",
3960
+ horizontal_align: "left",
3961
+ horizontal_spacing: "small",
3962
+ margin: "12px 0px 0px 0px",
3963
+ tag: "column_set"
3964
+ };
3965
+ }
3966
+ function footerTextElement(content) {
3967
+ return {
3968
+ tag: "div",
3969
+ text: {
3970
+ content,
3971
+ tag: "lark_md",
3972
+ text_align: "left",
3973
+ text_color: "grey",
3974
+ text_size: "notation"
3975
+ }
3976
+ };
3977
+ }
3377
3978
  function resolveRunPresentation(input) {
3378
3979
  switch (input.status) {
3379
3980
  case "running": return {
3380
- content: input.text || "正在思考…",
3381
- note: input.cancelButton === false ? `回答将实时更新;如需停止,请发送 \`/cancel ${input.runId}\`` : input.generation ? "接续上一条消息,回答将实时更新" : "回答将实时更新",
3382
- template: "blue",
3383
- title: input.generation ? "正在处理(续)" : "正在处理"
3384
- };
3385
- case "handoff": return {
3386
- content: "这是同一任务的续卡。任务仍在后台处理,进度将继续显示在下一条消息。",
3387
- note: "本卡片已停止更新;请查看下一条消息,不会创建第二个任务",
3981
+ content: input.text || "正在生成回答…",
3982
+ note: input.cancelButton === false ? `${progressNote(input.progressDisplay, input.presentation)}如需停止,请发送 \`/cancel ${input.runId}\`` : progressNote(input.progressDisplay, input.presentation),
3388
3983
  template: "blue",
3389
- title: "同一任务已续卡"
3984
+ title: "正在处理"
3390
3985
  };
3391
3986
  case "completed": return {
3392
3987
  content: input.text || "已完成。",
@@ -3403,6 +3998,29 @@ function resolveRunPresentation(input) {
3403
3998
  case "cancelled": return resolveCancellationPresentation(input.reason, input.text);
3404
3999
  }
3405
4000
  }
4001
+ function createRunElements(display, presentation, currentText, input) {
4002
+ const mode = display ?? "collapsed";
4003
+ const inspectable = presentation !== void 0 && hasInspectableRunProgress(presentation);
4004
+ const elementId = input.status === "running" ? input.elementId ?? "rivus_agent_answer" : void 0;
4005
+ if (mode === "hidden" || !inspectable) return [answerElement(renderFeishuVisibleAnswer(presentation, currentText) || currentText, elementId)];
4006
+ const timeline = renderFeishuRunTimeline(presentation, { expanded: mode === "expanded" });
4007
+ const current = currentText.trim();
4008
+ return [...timeline.elements, ...current || timeline.elements.length === 0 ? [answerElement(current || "正在生成回答…", elementId)] : elementId ? [answerElement("", elementId)] : []];
4009
+ }
4010
+ function answerElement(content, elementId) {
4011
+ return {
4012
+ content,
4013
+ ...elementId ? { element_id: elementId } : {},
4014
+ tag: "markdown"
4015
+ };
4016
+ }
4017
+ function progressNote(display, presentation) {
4018
+ return (display ?? "collapsed") === "hidden" || !presentation || !hasInspectableRunProgress(presentation) || !hasExpandableToolSequence(presentation) ? "内容实时更新;" : "内容实时更新;连续工具可展开;";
4019
+ }
4020
+ function hasExpandableToolSequence(presentation) {
4021
+ const visible = presentation.steps.filter((step) => step.kind === "assistant" || step.kind === "skill" || step.kind === "tool");
4022
+ return visible.some((step, index) => step.kind === "tool" && visible[index + 1]?.kind === "tool");
4023
+ }
3406
4024
  function resolveCancellationPresentation(reason, text) {
3407
4025
  const timeoutMs = readRunTimeoutMs(reason);
3408
4026
  if (timeoutMs !== void 0) return {
@@ -3513,59 +4131,83 @@ function createFeishuCardKitOpenApiClient(options) {
3513
4131
  const agentName = options.agentName ?? "Rivus Agent";
3514
4132
  const streamingClosedCards = /* @__PURE__ */ new Set();
3515
4133
  const call = (request) => options.client.request(request).pipe(Effect.asVoid);
3516
- const updateTerminal = (request, status) => updateWholeCard(baseUrl, agentName, request, status, call).pipe(Effect.tap(() => Effect.sync(() => streamingClosedCards.delete(request.cardId))));
4134
+ const updateTerminal = (request, status) => updateWholeCard(baseUrl, agentName, request, status, call, options.progressDisplay).pipe(Effect.tap(() => Effect.sync(() => streamingClosedCards.delete(request.cardId))));
4135
+ const publishStreamingUpdate = (cardId, update) => {
4136
+ if (streamingClosedCards.has(cardId)) return Effect.void;
4137
+ return update.pipe(Effect.catchAll((error) => {
4138
+ if (!isStreamingClosed(error)) return Effect.fail(error);
4139
+ return Effect.sync(() => {
4140
+ streamingClosedCards.add(cardId);
4141
+ options.onStreamingClosed?.(cardId);
4142
+ });
4143
+ }));
4144
+ };
3517
4145
  return {
3518
4146
  cancel: (request) => updateTerminal(request, "cancelled"),
3519
4147
  fail: (request) => updateTerminal(request, "failed"),
3520
4148
  finish: (request) => updateTerminal(request, "completed"),
3521
- handoff: (request) => Effect.sync(() => streamingClosedCards.add(request.cardId)).pipe(Effect.flatMap(() => updateWholeCard(baseUrl, agentName, request, "handoff", call))),
3522
- updateText: (request) => {
3523
- if (streamingClosedCards.has(request.cardId)) return Effect.void;
3524
- return call({
3525
- body: {
3526
- content: request.content,
3527
- sequence: request.sequence
3528
- },
3529
- method: "PUT",
3530
- url: `${baseUrl}/open-apis/cardkit/v1/cards/${encodeURIComponent(request.cardId)}/elements/${encodeURIComponent(request.elementId)}/content`
3531
- }).pipe(Effect.catchAll((error) => {
3532
- if (!isStreamingClosed(error)) return Effect.fail(error);
3533
- return Effect.sync(() => {
3534
- streamingClosedCards.add(request.cardId);
3535
- options.onStreamingClosed?.(request.cardId);
3536
- });
3537
- }));
3538
- }
4149
+ updatePresentation: (request) => publishStreamingUpdate(request.cardId, updateRunningCard(baseUrl, agentName, request, call, options.progressDisplay, options.cancelButton)),
4150
+ updateText: (request) => publishStreamingUpdate(request.cardId, call({
4151
+ body: {
4152
+ content: request.content,
4153
+ sequence: request.sequence
4154
+ },
4155
+ method: "PUT",
4156
+ url: `${baseUrl}/open-apis/cardkit/v1/cards/${encodeURIComponent(request.cardId)}/elements/${encodeURIComponent(request.elementId)}/content`
4157
+ }))
3539
4158
  };
3540
4159
  }
4160
+ function updateRunningCard(baseUrl, agentName, request, call, progressDisplay, cancelButton) {
4161
+ const card = createFeishuAgentRunCard({
4162
+ agentName,
4163
+ ...cancelButton === void 0 ? {} : { cancelButton },
4164
+ ...request.presentation === void 0 ? {} : { presentation: request.presentation },
4165
+ ...progressDisplay === void 0 ? {} : { progressDisplay },
4166
+ runId: request.runId,
4167
+ sessionKey: request.sessionKey,
4168
+ status: "running",
4169
+ ...request.text === void 0 ? {} : { text: request.text }
4170
+ });
4171
+ return replaceCard(baseUrl, request.cardId, request.sequence, card, call);
4172
+ }
3541
4173
  function isStreamingClosed(error) {
3542
4174
  return error instanceof FeishuOpenApiError && (error.code === 200850 || error.code === 300309);
3543
4175
  }
3544
- function updateWholeCard(baseUrl, agentName, request, status, call) {
4176
+ function updateWholeCard(baseUrl, agentName, request, status, call, progressDisplay) {
3545
4177
  const requestText = "text" in request ? request.text : void 0;
4178
+ const requestPresentation = "presentation" in request ? request.presentation : void 0;
3546
4179
  const card = status === "completed" ? createFeishuAgentRunCard({
3547
4180
  agentName,
4181
+ ...requestPresentation === void 0 ? {} : { presentation: requestPresentation },
4182
+ ...progressDisplay === void 0 ? {} : { progressDisplay },
3548
4183
  status,
3549
4184
  text: requestText ?? ""
3550
4185
  }) : status === "cancelled" ? createFeishuAgentRunCard({
3551
4186
  agentName,
4187
+ ...requestPresentation === void 0 ? {} : { presentation: requestPresentation },
4188
+ ...progressDisplay === void 0 ? {} : { progressDisplay },
3552
4189
  ..."reason" in request && request.reason ? { reason: request.reason } : {},
3553
4190
  status,
3554
4191
  ...requestText === void 0 ? {} : { text: requestText }
3555
4192
  }) : createFeishuAgentRunCard({
3556
4193
  agentName,
4194
+ ...requestPresentation === void 0 ? {} : { presentation: requestPresentation },
4195
+ ...progressDisplay === void 0 ? {} : { progressDisplay },
3557
4196
  status
3558
4197
  });
4198
+ return replaceCard(baseUrl, request.cardId, request.sequence, card, call);
4199
+ }
4200
+ function replaceCard(baseUrl, cardId, sequence, card, call) {
3559
4201
  return call({
3560
4202
  body: {
3561
4203
  card: {
3562
4204
  data: JSON.stringify(card),
3563
4205
  type: "card_json"
3564
4206
  },
3565
- sequence: request.sequence
4207
+ sequence
3566
4208
  },
3567
4209
  method: "PUT",
3568
- url: `${baseUrl}/open-apis/cardkit/v1/cards/${encodeURIComponent(request.cardId)}`
4210
+ url: `${baseUrl}/open-apis/cardkit/v1/cards/${encodeURIComponent(cardId)}`
3569
4211
  });
3570
4212
  }
3571
4213
  //#endregion
@@ -3574,6 +4216,7 @@ function createFeishuCardKitPublisher(options) {
3574
4216
  const ledger = options.ledger ?? createFeishuCardDeliveryLedger();
3575
4217
  return { publish: (action) => Effect.gen(function* () {
3576
4218
  const target = yield* options.resolveTarget(action.runId);
4219
+ if (action.type === "update_progress" && !target.progressElementId) return;
3577
4220
  const sequence = yield* ledger.reserveSequence(action.runId);
3578
4221
  switch (action.type) {
3579
4222
  case "update_text": return yield* options.client.updateText({
@@ -3582,14 +4225,24 @@ function createFeishuCardKitPublisher(options) {
3582
4225
  elementId: target.elementId,
3583
4226
  sequence
3584
4227
  });
3585
- case "handoff": return yield* options.client.handoff({
4228
+ case "update_progress": return yield* options.client.updateText({
3586
4229
  cardId: target.cardId,
4230
+ content: renderFeishuRunProgress(action.presentation).markdown,
4231
+ elementId: target.progressElementId,
4232
+ sequence
4233
+ });
4234
+ case "update_presentation": return yield* options.client.updatePresentation({
4235
+ cardId: target.cardId,
4236
+ ...action.presentation === void 0 ? {} : { presentation: action.presentation },
4237
+ runId: action.runId,
3587
4238
  sequence,
4239
+ sessionKey: action.sessionKey,
3588
4240
  ...action.text === void 0 ? {} : { text: action.text }
3589
4241
  });
3590
4242
  case "finish":
3591
4243
  yield* options.client.finish({
3592
4244
  cardId: target.cardId,
4245
+ ...action.presentation === void 0 ? {} : { presentation: action.presentation },
3593
4246
  sequence,
3594
4247
  text: action.text
3595
4248
  });
@@ -3598,12 +4251,14 @@ function createFeishuCardKitPublisher(options) {
3598
4251
  yield* options.client.fail({
3599
4252
  cardId: target.cardId,
3600
4253
  errorMessage: action.errorMessage,
4254
+ ...action.presentation === void 0 ? {} : { presentation: action.presentation },
3601
4255
  sequence
3602
4256
  });
3603
4257
  return yield* ledger.markTerminal(action.runId);
3604
4258
  case "cancel":
3605
4259
  yield* options.client.cancel({
3606
4260
  cardId: target.cardId,
4261
+ ...action.presentation === void 0 ? {} : { presentation: action.presentation },
3607
4262
  ...action.reason ? { reason: action.reason } : {},
3608
4263
  sequence,
3609
4264
  ...action.text === void 0 ? {} : { text: action.text }
@@ -3616,7 +4271,9 @@ function createFeishuCardKitPublisher(options) {
3616
4271
  //#region src/infrastructure/feishu/feishu-coalescing-publisher.ts
3617
4272
  function createCoalescingFeishuPublisher(options) {
3618
4273
  const pendingTextByRun = /* @__PURE__ */ new Map();
3619
- const flushOne = (runId, text) => Effect.gen(function* () {
4274
+ const pendingProgressByRun = /* @__PURE__ */ new Map();
4275
+ const pendingPresentationByRun = /* @__PURE__ */ new Map();
4276
+ const flushText = (runId, text) => Effect.gen(function* () {
3620
4277
  pendingTextByRun.delete(runId);
3621
4278
  yield* options.publisher.publish({
3622
4279
  runId,
@@ -3624,24 +4281,58 @@ function createCoalescingFeishuPublisher(options) {
3624
4281
  type: "update_text"
3625
4282
  });
3626
4283
  });
4284
+ const flushProgress = (runId, presentation) => Effect.gen(function* () {
4285
+ pendingProgressByRun.delete(runId);
4286
+ yield* options.publisher.publish({
4287
+ presentation,
4288
+ runId,
4289
+ type: "update_progress"
4290
+ });
4291
+ });
3627
4292
  const flushRun = (runId) => {
3628
4293
  const text = pendingTextByRun.get(runId);
3629
- return text === void 0 ? Effect.void : flushOne(runId, text);
4294
+ const progress = pendingProgressByRun.get(runId);
4295
+ const presentation = pendingPresentationByRun.get(runId);
4296
+ return Effect.gen(function* () {
4297
+ if (presentation !== void 0) {
4298
+ pendingPresentationByRun.delete(runId);
4299
+ yield* options.publisher.publish(presentation);
4300
+ pendingProgressByRun.delete(runId);
4301
+ pendingTextByRun.delete(runId);
4302
+ return;
4303
+ }
4304
+ if (progress !== void 0) yield* flushProgress(runId, progress);
4305
+ if (text !== void 0) yield* flushText(runId, text);
4306
+ });
3630
4307
  };
3631
4308
  return {
3632
4309
  flush: (runId) => runId === void 0 ? Effect.gen(function* () {
3633
- for (const [pendingRunId, text] of Array.from(pendingTextByRun)) yield* flushOne(pendingRunId, text);
4310
+ const runIds = /* @__PURE__ */ new Set([
4311
+ ...pendingPresentationByRun.keys(),
4312
+ ...pendingProgressByRun.keys(),
4313
+ ...pendingTextByRun.keys()
4314
+ ]);
4315
+ for (const pendingRunId of runIds) yield* flushRun(pendingRunId);
3634
4316
  }) : flushRun(runId),
3635
4317
  publish: (action) => {
3636
4318
  switch (action.type) {
3637
4319
  case "update_text": return Effect.sync(() => {
3638
4320
  pendingTextByRun.set(action.runId, action.text);
3639
4321
  });
3640
- case "handoff": return options.publisher.publish(action);
4322
+ case "update_progress": return Effect.sync(() => {
4323
+ pendingProgressByRun.set(action.runId, action.presentation);
4324
+ });
4325
+ case "update_presentation": return Effect.sync(() => {
4326
+ pendingPresentationByRun.set(action.runId, action);
4327
+ });
3641
4328
  case "finish":
3642
4329
  case "fail":
3643
4330
  case "cancel": return Effect.gen(function* () {
3644
- yield* Effect.sync(() => pendingTextByRun.delete(action.runId));
4331
+ yield* Effect.sync(() => {
4332
+ pendingProgressByRun.delete(action.runId);
4333
+ pendingTextByRun.delete(action.runId);
4334
+ pendingPresentationByRun.delete(action.runId);
4335
+ });
3645
4336
  yield* options.publisher.publish(action);
3646
4337
  });
3647
4338
  }
@@ -3666,8 +4357,10 @@ function createConfiguredFeishuCardKitPublisher(options) {
3666
4357
  client: createFeishuCardKitOpenApiClient({
3667
4358
  ...options.agentName === void 0 ? {} : { agentName: options.agentName },
3668
4359
  baseUrl: options.config.feishu.baseUrl,
4360
+ ...options.cancelButton === void 0 ? {} : { cancelButton: options.cancelButton },
3669
4361
  client: options.client,
3670
- ...options.onStreamingClosed === void 0 ? {} : { onStreamingClosed: options.onStreamingClosed }
4362
+ ...options.onStreamingClosed === void 0 ? {} : { onStreamingClosed: options.onStreamingClosed },
4363
+ ...options.progressDisplay === void 0 ? {} : { progressDisplay: options.progressDisplay }
3671
4364
  }),
3672
4365
  ...options.ledger ? { ledger: options.ledger } : {},
3673
4366
  resolveTarget: options.resolveTarget
@@ -3694,6 +4387,7 @@ function createConfiguredFeishuCardKitTargetCreator(options) {
3694
4387
  client: options.client,
3695
4388
  elementId: options.elementId ?? DEFAULT_ELEMENT_ID,
3696
4389
  ...options.initialContent === void 0 ? {} : { initialContent: options.initialContent },
4390
+ ...options.progressDisplay === void 0 ? {} : { progressDisplay: options.progressDisplay },
3697
4391
  ...options.title === void 0 ? {} : { title: options.title }
3698
4392
  });
3699
4393
  }
@@ -3708,6 +4402,8 @@ function createFeishuCardKitOpenApiTargetCreator(options) {
3708
4402
  agentName: title,
3709
4403
  elementId: options.elementId,
3710
4404
  ...createOptions?.generation === void 0 ? {} : { generation: createOptions.generation },
4405
+ ...createOptions?.initialPresentation === void 0 ? {} : { presentation: createOptions.initialPresentation },
4406
+ ...options.progressDisplay === void 0 ? {} : { progressDisplay: options.progressDisplay },
3711
4407
  runId: run.runId,
3712
4408
  sessionKey: run.sessionKey,
3713
4409
  status: "running",
@@ -3750,14 +4446,20 @@ const MIN_SUPERVISOR_INTERVAL_MS = 200;
3750
4446
  const MAX_SUPERVISOR_INTERVAL_MS = 5e3;
3751
4447
  function createConfiguredFeishuCardRolloverRuntime(options) {
3752
4448
  const leaseMs = options.config.feishu.cardStreamLeaseMs;
4449
+ const progressDisplay = options.progressDisplay ?? "collapsed";
3753
4450
  let rollover;
3754
4451
  const publisher = createConfiguredFeishuCardKitPublisher({
3755
4452
  ...options.agentName === void 0 ? {} : { agentName: options.agentName },
4453
+ ...options.cancelButton === void 0 ? {} : { cancelButton: options.cancelButton },
3756
4454
  client: options.client,
3757
4455
  config: options.config,
3758
4456
  ...options.ledger ? { ledger: options.ledger } : {},
3759
4457
  onStreamingClosed: (cardId) => rollover?.observeStreamClosed(cardId),
3760
- resolveTarget: (runId) => options.cardTargets.resolveTarget(runId),
4458
+ progressDisplay,
4459
+ resolveTarget: (runId) => options.cardTargets.resolveTarget(runId).pipe(Effect.map((target) => progressDisplay === "hidden" ? target : {
4460
+ ...target,
4461
+ progressElementId: FEISHU_AGENT_CARD_PROGRESS_ELEMENT_ID
4462
+ })),
3761
4463
  sleep: options.sleep
3762
4464
  });
3763
4465
  const creator = createConfiguredFeishuCardKitTargetCreator({
@@ -3766,13 +4468,14 @@ function createConfiguredFeishuCardRolloverRuntime(options) {
3766
4468
  config: options.config,
3767
4469
  ...options.elementId === void 0 ? {} : { elementId: options.elementId },
3768
4470
  ...options.initialContent === void 0 ? {} : { initialContent: options.initialContent },
4471
+ progressDisplay,
3769
4472
  ...options.title === void 0 ? {} : { title: options.title }
3770
4473
  });
3771
4474
  const cardRollover = createFeishuCardRollover({
3772
4475
  clock: options.clock,
3773
- createSuccessor: ({ generation, run, text }) => creator.createTarget(run, {
4476
+ createSuccessor: ({ generation, run }) => creator.createTarget(run, {
3774
4477
  generation,
3775
- ...text === void 0 ? {} : { initialContent: text }
4478
+ initialContent: ""
3776
4479
  }),
3777
4480
  leaseMs,
3778
4481
  ...options.observe ? { observe: options.observe } : {},
@@ -3903,7 +4606,6 @@ function createConfiguredRivusDaemonBootstrap(options) {
3903
4606
  });
3904
4607
  const cardRollover = createConfiguredFeishuCardRolloverRuntime({
3905
4608
  cardTargets: options.cardTargets,
3906
- cancelButton: false,
3907
4609
  client: openApiClient,
3908
4610
  clock: options.clock,
3909
4611
  config: options.config,
@@ -5051,6 +5753,114 @@ function createPiSessionRegistry(options) {
5051
5753
  };
5052
5754
  }
5053
5755
  //#endregion
5756
+ //#region src/domain/automation-presentation.ts
5757
+ const AUTOMATION_PRESENTATION_SCHEMA_VERSION = 1;
5758
+ var InvalidAutomationPresentation = class extends Error {
5759
+ name = "InvalidAutomationPresentation";
5760
+ };
5761
+ function createAutomationPresentation(input) {
5762
+ const title = requiredText(input.title, "title", 120);
5763
+ const meta = (input.meta ?? []).map((value, index) => requiredText(value, `meta[${index}]`, 80));
5764
+ if (meta.length > 6) throw new InvalidAutomationPresentation("Automation presentation meta exceeds 6 items");
5765
+ if (input.sections.length > 12) throw new InvalidAutomationPresentation("Automation presentation exceeds 12 sections");
5766
+ const sections = input.sections.map((section, sectionIndex) => {
5767
+ const id = requiredIdentifier(section.id, `sections[${sectionIndex}].id`);
5768
+ const sectionTitle = requiredText(section.title, `sections[${sectionIndex}].title`, 80);
5769
+ if (section.items.length > 40) throw new InvalidAutomationPresentation(`Automation presentation section '${id}' exceeds 40 items`);
5770
+ return Object.freeze({
5771
+ id,
5772
+ items: Object.freeze(section.items.map((item, itemIndex) => Object.freeze({
5773
+ headline: requiredText(item.headline, `sections[${sectionIndex}].items[${itemIndex}].headline`, 240),
5774
+ ...item.note ? { note: requiredText(item.note, `sections[${sectionIndex}].items[${itemIndex}].note`, 400) } : {},
5775
+ sources: Object.freeze((item.sources ?? []).map((source, sourceIndex) => Object.freeze({
5776
+ label: requiredText(source.label, `sections[${sectionIndex}].items[${itemIndex}].sources[${sourceIndex}].label`, 60),
5777
+ url: safeHttpUrl(source.url, `sections[${sectionIndex}].items[${itemIndex}].sources[${sourceIndex}].url`)
5778
+ })))
5779
+ }))),
5780
+ title: sectionTitle
5781
+ });
5782
+ });
5783
+ return Object.freeze({
5784
+ ...input.footnote ? { footnote: requiredText(input.footnote, "footnote", 240) } : {},
5785
+ ...input.kind === void 0 ? {} : { kind: input.kind },
5786
+ meta: Object.freeze(meta),
5787
+ schemaVersion: 1,
5788
+ sections: Object.freeze(sections),
5789
+ ...input.summary ? { summary: requiredText(input.summary, "summary", 800) } : {},
5790
+ title
5791
+ });
5792
+ }
5793
+ function readAutomationPresentation(value) {
5794
+ const record = objectValue(value, "Automation presentation");
5795
+ if (record.schemaVersion !== 1) throw new InvalidAutomationPresentation(`Automation presentation schemaVersion must be 1`);
5796
+ const meta = arrayValue(record.meta, "Automation presentation meta").map((item, index) => stringValue(item, `Automation presentation meta[${index}]`));
5797
+ const sections = arrayValue(record.sections, "Automation presentation sections").map((sectionValue, sectionIndex) => {
5798
+ const section = objectValue(sectionValue, `Automation presentation sections[${sectionIndex}]`);
5799
+ return {
5800
+ id: stringValue(section.id, `Automation presentation sections[${sectionIndex}].id`),
5801
+ items: arrayValue(section.items, `Automation presentation sections[${sectionIndex}].items`).map((itemValue, itemIndex) => {
5802
+ const item = objectValue(itemValue, `Automation presentation sections[${sectionIndex}].items[${itemIndex}]`);
5803
+ return {
5804
+ headline: stringValue(item.headline, `Automation presentation sections[${sectionIndex}].items[${itemIndex}].headline`),
5805
+ ...item.note === void 0 ? {} : { note: stringValue(item.note, `Automation presentation sections[${sectionIndex}].items[${itemIndex}].note`) },
5806
+ sources: arrayValue(item.sources, `Automation presentation sections[${sectionIndex}].items[${itemIndex}].sources`).map((sourceValue, sourceIndex) => {
5807
+ const source = objectValue(sourceValue, `Automation presentation sections[${sectionIndex}].items[${itemIndex}].sources[${sourceIndex}]`);
5808
+ return {
5809
+ label: stringValue(source.label, "Automation presentation source label"),
5810
+ url: stringValue(source.url, "Automation presentation source URL")
5811
+ };
5812
+ })
5813
+ };
5814
+ }),
5815
+ title: stringValue(section.title, `Automation presentation sections[${sectionIndex}].title`)
5816
+ };
5817
+ });
5818
+ return createAutomationPresentation({
5819
+ ...record.footnote === void 0 ? {} : { footnote: stringValue(record.footnote, "Automation presentation footnote") },
5820
+ meta,
5821
+ ...record.kind === void 0 ? {} : { kind: automationPresentationKind(record.kind) },
5822
+ sections,
5823
+ ...record.summary === void 0 ? {} : { summary: stringValue(record.summary, "Automation presentation summary") },
5824
+ title: stringValue(record.title, "Automation presentation title")
5825
+ });
5826
+ }
5827
+ function automationPresentationKind(value) {
5828
+ if (value === "daily-ai" || value === "generic" || value === "industry" || value === "news" || value === "subscriptions") return value;
5829
+ throw new InvalidAutomationPresentation("Automation presentation kind is invalid");
5830
+ }
5831
+ function requiredText(value, path, maxLength) {
5832
+ const normalized = value.trim();
5833
+ if (!normalized) throw new InvalidAutomationPresentation(`Automation presentation ${path} is empty`);
5834
+ if (normalized.length > maxLength) throw new InvalidAutomationPresentation(`Automation presentation ${path} exceeds ${maxLength} characters`);
5835
+ return normalized;
5836
+ }
5837
+ function requiredIdentifier(value, path) {
5838
+ const normalized = requiredText(value, path, 80);
5839
+ if (!/^[a-z0-9][a-z0-9._-]*$/i.test(normalized)) throw new InvalidAutomationPresentation(`Automation presentation ${path} is not a stable identifier`);
5840
+ return normalized;
5841
+ }
5842
+ function safeHttpUrl(value, path) {
5843
+ try {
5844
+ const parsed = new URL(value);
5845
+ if (parsed.protocol !== "https:" && parsed.protocol !== "http:") throw new Error("unsupported protocol");
5846
+ return parsed.toString();
5847
+ } catch {
5848
+ throw new InvalidAutomationPresentation(`Automation presentation ${path} must be an HTTP(S) URL`);
5849
+ }
5850
+ }
5851
+ function objectValue(value, path) {
5852
+ if (!value || typeof value !== "object" || Array.isArray(value)) throw new InvalidAutomationPresentation(`${path} must be an object`);
5853
+ return value;
5854
+ }
5855
+ function arrayValue(value, path) {
5856
+ if (!Array.isArray(value)) throw new InvalidAutomationPresentation(`${path} must be an array`);
5857
+ return value;
5858
+ }
5859
+ function stringValue(value, path) {
5860
+ if (typeof value !== "string") throw new InvalidAutomationPresentation(`${path} must be a string`);
5861
+ return value;
5862
+ }
5863
+ //#endregion
5054
5864
  //#region src/application/feishu/feishu-card-presentation-store.ts
5055
5865
  var FeishuCardPresentationNotFound = class {
5056
5866
  runId;
@@ -8665,6 +9475,7 @@ async function executeDue(options, schedule, at, claim) {
8665
9475
  record = await options.repository.put({
8666
9476
  ...record,
8667
9477
  body: result.body,
9478
+ ...result.presentation === void 0 ? {} : { presentation: result.presentation },
8668
9479
  revision: record.revision + 1,
8669
9480
  runId: result.runId,
8670
9481
  status: "generated"
@@ -8682,7 +9493,8 @@ async function executeDue(options, schedule, at, claim) {
8682
9493
  if (!record.body) throw new Error(`Automation ${options.automationId} generated an empty delivery body`);
8683
9494
  const delivered = await options.deliver({
8684
9495
  body: record.body,
8685
- idempotencyKey: record.tickId
9496
+ idempotencyKey: record.tickId,
9497
+ ...record.presentation === void 0 ? {} : { presentation: record.presentation }
8686
9498
  });
8687
9499
  return options.repository.put({
8688
9500
  ...record,
@@ -8779,6 +9591,7 @@ function readRecord(value) {
8779
9591
  ...typeof value.error === "string" ? { error: value.error } : {},
8780
9592
  mandateId: value.mandateId,
8781
9593
  occurrence: value.occurrence,
9594
+ ...value.presentation === void 0 ? {} : { presentation: readAutomationPresentation(value.presentation) },
8782
9595
  ...typeof value.providerMessageId === "string" ? { providerMessageId: value.providerMessageId } : {},
8783
9596
  revision: value.revision,
8784
9597
  ...typeof value.runId === "string" ? { runId: value.runId } : {},
@@ -9176,7 +9989,7 @@ function createConfiguredFeishuAutomationCardSender(options) {
9176
9989
  const baseUrl = options.config.feishu.baseUrl.replace(/\/$/, "");
9177
9990
  return { send: (input) => sendFeishuInteractiveCard(options.client, {
9178
9991
  body: {
9179
- content: JSON.stringify(createAutomationCard(input.markdown)),
9992
+ content: JSON.stringify(createAutomationCard(input.markdown, input.presentation)),
9180
9993
  msg_type: "interactive",
9181
9994
  receive_id: input.receiveId,
9182
9995
  uuid: deliveryUuid(input.idempotencyKey)
@@ -9185,21 +9998,98 @@ function createConfiguredFeishuAutomationCardSender(options) {
9185
9998
  url: `${baseUrl}/open-apis/im/v1/messages?receive_id_type=${encodeURIComponent(input.receiveIdType)}`
9186
9999
  }, "Feishu send failed") };
9187
10000
  }
9188
- function createAutomationCard(markdown) {
10001
+ function createAutomationCard(markdown, structured) {
10002
+ if (structured) return createStructuredAutomationCard(structured);
9189
10003
  const presentation = splitPresentation(markdown);
10004
+ return automationCard(presentation.title, [{
10005
+ content: presentation.content,
10006
+ tag: "markdown"
10007
+ }, {
10008
+ content: "_由 Rivus 自动生成_",
10009
+ tag: "markdown"
10010
+ }]);
10011
+ }
10012
+ function createStructuredAutomationCard(presentation) {
10013
+ const elements = [];
10014
+ if (presentation.meta.length > 0) elements.push({
10015
+ margin: "0px 0px 4px 0px",
10016
+ tag: "div",
10017
+ text: {
10018
+ content: presentation.meta.map(escapeMarkdown).join(" · "),
10019
+ tag: "lark_md",
10020
+ text_color: "grey",
10021
+ text_size: "notation"
10022
+ }
10023
+ });
10024
+ if (presentation.meta.length > 0 && (presentation.summary || presentation.sections.length > 0)) elements.push({ tag: "hr" });
10025
+ if (presentation.summary) {
10026
+ const summaryHeading = presentation.kind === "subscriptions" ? "先看这两条" : "今日概览";
10027
+ const summary = presentation.summary.trim();
10028
+ elements.push({
10029
+ content: summary === summaryHeading ? `**${summaryHeading}**` : `**${summaryHeading}**\n\n${escapeMarkdown(summary)}`,
10030
+ tag: "markdown",
10031
+ text_size: "normal"
10032
+ });
10033
+ }
10034
+ presentation.sections.forEach((section, index) => {
10035
+ if (presentation.summary || index > 0) elements.push({ tag: "hr" });
10036
+ elements.push({
10037
+ content: `**${escapeMarkdown(section.title)}**`,
10038
+ margin: "4px 0px 0px 0px",
10039
+ tag: "markdown",
10040
+ text_size: "heading"
10041
+ });
10042
+ for (const item of section.items) elements.push(renderDigestItem(item));
10043
+ });
10044
+ if (presentation.footnote) {
10045
+ elements.push({ tag: "hr" });
10046
+ elements.push({
10047
+ tag: "div",
10048
+ text: {
10049
+ content: escapeMarkdown(presentation.footnote),
10050
+ tag: "lark_md",
10051
+ text_color: "grey",
10052
+ text_size: "notation"
10053
+ }
10054
+ });
10055
+ }
10056
+ elements.push({
10057
+ margin: "12px 0px 0px 0px",
10058
+ tag: "div",
10059
+ text: {
10060
+ content: "Rivus",
10061
+ tag: "lark_md",
10062
+ text_align: "right",
10063
+ text_color: "grey",
10064
+ text_size: "notation"
10065
+ }
10066
+ });
10067
+ return automationCard(presentation.title, elements);
10068
+ }
10069
+ function renderDigestItem(item) {
10070
+ const sources = item.sources.map((source) => `[${escapeMarkdown(source.label)} ↗](${source.url})`).join(" ");
10071
+ const note = item.note ? `\n<font color='grey'>${escapeMarkdown(item.note)}</font>` : "";
9190
10072
  return {
9191
- body: { elements: [{
9192
- content: presentation.content,
9193
- tag: "markdown"
9194
- }, {
9195
- content: "_由 Rivus 自动生成_",
9196
- tag: "markdown"
9197
- }] },
10073
+ content: `• ${escapeMarkdown(item.headline)}${sources ? ` ${sources}` : ""}${note}`,
10074
+ margin: "4px 0px 0px 0px",
10075
+ tag: "markdown",
10076
+ text_size: "normal"
10077
+ };
10078
+ }
10079
+ function automationCard(title, elements) {
10080
+ return {
10081
+ body: {
10082
+ direction: "vertical",
10083
+ elements,
10084
+ padding: "18px 18px 16px 18px",
10085
+ vertical_spacing: "small"
10086
+ },
9198
10087
  config: { update_multi: true },
9199
10088
  header: {
10089
+ padding: "12px 18px 12px 18px",
9200
10090
  template: "blue",
9201
10091
  title: {
9202
- content: presentation.title,
10092
+ content: title,
9203
10093
  tag: "plain_text"
9204
10094
  }
9205
10095
  },
@@ -9222,6 +10112,9 @@ function splitPresentation(markdown) {
9222
10112
  function deliveryUuid(idempotencyKey) {
9223
10113
  return `rivus-${createHash("sha256").update(idempotencyKey).digest("hex").slice(0, 32)}`;
9224
10114
  }
10115
+ function escapeMarkdown(value) {
10116
+ return value.replace(/([\\`*_{}[\]|])/g, "\\$1");
10117
+ }
9225
10118
  //#endregion
9226
10119
  //#region src/application/interaction/human-interaction-repository.ts
9227
10120
  var HumanInteractionRepositoryError = class extends Error {
@@ -9575,4 +10468,4 @@ function validateDecisionInput(input) {
9575
10468
  if (input.recommendedOptionId && !optionIds.includes(input.recommendedOptionId)) throw new Error("recommended user decision option must be available");
9576
10469
  }
9577
10470
  //#endregion
9578
- export { AgentContextBudgetExceeded, AgentEventHandlerFailed, AgentEventLogStoreError, AgentEventSinkFailed, AgentHarnessBusy, AgentInstanceBusy, AgentInstanceConflict, AgentLoopFailed, AgentMemoryError, AgentRunCancelled, AgentRuntimeDisposed, AutomationMandateError, BACKGROUND_SESSION_DELIVERY_JSONL_VERSION, BACKGROUND_SESSION_JSONL_VERSION, BACKGROUND_SESSION_SESSION_KEY_PREFIX, BACKGROUND_SESSION_START_TOOL_ID, BACKGROUND_SESSION_TOOL_IDS, BACKGROUND_SESSION_TOOL_PLUGIN_ID, BACKGROUND_SESSION_TOOL_VERSION, BackgroundSessionCallerDenied, BackgroundSessionDeliveryConflict, BackgroundSessionRepositoryConflict, BackgroundSessionRepositoryCorrupted, BackgroundSessionTransitionDenied, CardPresentationTransitionDenied, CompactionError, DEFAULT_BACKGROUND_SESSION_LEASE_MS, DEFAULT_BACKGROUND_SESSION_LEASE_RENEWAL_INTERVAL_MS, DEFAULT_BACKGROUND_SESSION_LIFETIME_MS, DEFAULT_BACKGROUND_SESSION_MAX_CONCURRENT_SESSIONS, DEFAULT_BACKGROUND_SESSION_MAX_CONSECUTIVE_FAILURES, DEFAULT_BACKGROUND_SESSION_RETRY_BACKOFF_MS, DEFAULT_BACKGROUND_SESSION_STEP_TIMEOUT_MS, DEFAULT_BACKGROUND_SESSION_SUPERVISOR_INTERVAL_MS, DEFAULT_CARD_STREAM_LEASE_MS, DelegationDenied, DeliveryOutboxError, FeishuCardPresentationNotFound, FeishuCardTargetNotFound, FeishuCardTargetRegistryStoreError, FeishuCotProtocolError, FeishuEndpointCredentialError, FeishuOpenApiError, FeishuTenantAccessTokenError, HumanInteractionRepositoryError, HumanInteractionTransitionDenied, InvalidFeishuCardAction, InvalidFeishuMessageContent, InvalidFeishuSessionReference, InvalidFeishuTopicContext, InvalidInvocationAuthority, InvalidProjectSkillCatalog, InvalidRecoveryAction, InvalidRivusEndpointBinding, InvalidRivusPlugin, InvalidRivusProjectSpace, InvalidStableJson, InvalidToolInput, InvalidWorkspaceRoot, LangfuseTelemetryConfigError, MEMORY_SCOPES, OpenClawEnvImportError, PluginStateConflict, RIVUS_MEMORY_TOOL_ID, RIVUS_MEMORY_TOOL_PLUGIN_ID, RIVUS_MEMORY_TOOL_VERSION, RIVUS_PLUGIN_API_VERSION, RivusDaemonConfigError, RivusDeploymentAutomationReadinessError, RivusDeploymentDaemonLifecycleError, RivusDeploymentManifestError, RivusDeploymentReadinessError, RivusPluginConformanceError, RivusPluginLoadError, RivusToolInputRejected, SessionSchedulerCapacityExceeded, SessionSchedulerDisposed, ToolInvocationDenied, UnsupportedFeishuCardAction, UnsupportedFeishuMessage, WorkspaceInstructionsSourceError, acceptsCardPresentationProgress, activeCardPresentation, appendBackgroundSessionInput, assembleAgentContext, assertRivusPluginConforms, backgroundSessionToolIds, claimBackgroundSession, commitAutomationOutcome, completeBackgroundSessionStep, completeBackgroundSessionStop, composeFeishuTopicPrompt, createAgentCommandFromFeishuCardAction, createAgentCommandFromFeishuMessage, createAgentConversationMessages, createAgentDomainEventHandler, createAgentDomainEventSink, createAgentDomainEventSinkFromCallback, createAgentHarness, createAgentHarnessClient, createAgentHarnessPooledRuntime, createAgentInstanceRegistry, createAgentLoopFromCallback, createAgentLoopModelExecutionEnd, createAgentLoopModelExecutionStart, createAgentLoopPromptTransformer, createAgentLoopSkillExecutionEnd, createAgentLoopSkillExecutionStart, createAgentLoopTextDelta, createAgentLoopThinkingDelta, createAgentLoopToolExecutionEnd, createAgentLoopToolExecutionStart, createAgentLoopToolExecutionUpdate, createAgentLoopTurnStart, createAgentMemoryService, createAgentRunUpdateHandler, createAgentRuntime, createAgentRuntimePool, createAgentTranscriptMessages, createAgentTranscriptTurn, createAgentsMdInstructionsProvider, createAsyncIterableAgentLoop, createAutomationMandateStore, createBackgroundSession, createBackgroundSessionCard, createBackgroundSessionDeliveryStore, createBackgroundSessionHostTools, createBackgroundSessionKey, createBackgroundSessionRepository, createBackgroundSessionService, createBackgroundSessionStepSourceMessageId, createBackgroundSessionSupervisor, createBackgroundSessionToolContracts, createCoalescingFeishuPublisher, createCompactionService, createCompositeRivusDaemonTransport, createConfiguredFeishuAutomationCardSender, createConfiguredFeishuBackgroundSessionDelivery, createConfiguredFeishuCardKitPublisher, createConfiguredFeishuCardKitTargetCreator, createConfiguredFeishuCardRolloverRuntime, createConfiguredFeishuHumanInteractionPresenter, createConfiguredFeishuMessageReactionSender, createConfiguredFeishuOpenApiClient, createConfiguredFeishuTextReplySender, createConfiguredRivusDaemonBootstrap, createConfiguredRivusDeploymentDaemon, createDailyAutomationSchedule, createDefaultAgentHarness, createDefaultAgentHarnessClient, createDefaultAgentHarnessClientFromCallback, createDefaultAgentHarnessClientFromTextCallback, createDefaultAgentHarnessFromCallback, createDefaultAgentHarnessFromTextCallback, createDefaultAgentRuntime, createDefaultAgentRuntimeFromCallback, createDefaultAgentRuntimeFromTextCallback, createDelegationService, createDeliveryOutbox, createEventAgentLoop, createFakeRivusPlugin, createFeishuAgentDaemon, createFeishuAgentRunCard, createFeishuAgentRuntime, createFeishuCardActionCallbackResponse, createFeishuCardActionErrorResponse, createFeishuCardDeliveryLedger, createFeishuCardDeliveryReconciler, createFeishuCardKitOpenApiClient, createFeishuCardKitOpenApiTargetCreator, createFeishuCardKitPublisher, createFeishuCardPresentationStore, createFeishuCardRollover, createFeishuCardRolloverSupervisor, createFeishuCardTargetPreparation, createFeishuConversationId, createFeishuCotPublisher, createFeishuDeploymentEndpoint, createFeishuEventHandlers, createFeishuHumanInteractionCard, createFeishuHumanInteractionPresenter, createFeishuInboxRepository, createFeishuMessageQueue, createFeishuMessageWorker, createFeishuOpenApiClient, createFeishuPeriodicFlush, createFeishuPresentationPreparation, createFeishuSessionKey, createFeishuSessionStore, createFeishuStreamProjector, createFeishuTenantAccessTokenProvider, createFeishuTopicContextResolver, createFeishuWebSocketDaemon, createFeishuWorkerLoop, createFixedClock, createHumanInteractionEndpointRegistry, createHumanInteractionService, createHumanInteractionToolApprovalGateway, createInMemoryFeishuCardTargetRegistry, createInMemoryHumanInteractionRepository, createInvocationAuthority, createJsonFetchRequest, createJsonFileFeishuCardTargetRegistry, createJsonlAgentEventLog, createJsonlHumanInteractionRepository, createLangfuseAgentTelemetry, createLazyFeishuWebSocketEventDispatcher, createMemoryNamespace, createOpenTelemetryAgentEventSink, createOpenTelemetryAgentTelemetry, createPiAgentLoop, createPiSdkAgentLoop, createPiSessionRegistry, createPluginStateStore, createProjectMemoryPromptPreparer, createRateLimitedFeishuPublisher, createRecoveryAction, createRecoveryControl, createRivusAgentHost, createRivusDaemonProcess, createRivusDaemonShutdownController, createRivusDaemonStatusHttpServer, createRivusDaemonStatusReporter, createRivusDeploymentCliProcess, createRivusDeploymentDaemon, createRivusEnvFromOpenClawConfig, createRivusMemoryTool, createRivusMemoryToolContract, createRivusMemoryToolDescriptor, createRivusPluginCatalog, createRoutedHumanInteractionToolApprovalService, createScheduledAutomation, createSequenceRunIds, createSessionScheduler, createSubagentCoordinator, createSystemClock, createTelemetryContentRedactor, createTestClock, createTextAgentLoop, createTextAgentLoopFromCallback, createToolBroker, createToolInputDigest, createToolOperationLedger, createUuidRunIds, createWorkspaceRootHandle, describeFeishuMessageIntake, evolveAgentRun, extendBackgroundSessionDefinition, failBackgroundSessionStep, formatRivusEnvFile, initialAgentRunState, intersectToolIds, isAgentToolExecutionEvent, isAssistantTextDeltaEvent, isAssistantThinkingDeltaEvent, isBackgroundSessionDue, isBackgroundSessionLeaseExpired, isBackgroundSessionState, isBackgroundSessionTerminalPhase, isBackgroundSessionToolId, isCardPresentationHandoffDue, isTerminalAgentDomainEvent, isTerminalAgentRunPhase, loadNodeRivusPluginModule, loadRivusDaemonConfig, loadRivusDeployment, loadRivusDeploymentManifest, narrowBackgroundSessionDefinition, normalizeStableJson, openJsonAutomationTickRepository, openJsonFeishuSessionStore, openJsonlAgentMemoryService, openJsonlBackgroundSessionDeliveryStore, openJsonlBackgroundSessionRepository, openJsonlFeishuCardDeliveryLedger, openJsonlFeishuInboxRepository, openJsonlRecoveryControl, openJsonlToolOperationLedger, parkBackgroundSessionForReconciliation, progressDeliveryId, readFeishuMessageContent, releaseBackgroundSessionLease, renewBackgroundSessionLease, replayAgentHistory, replayAgentTranscript, requestBackgroundSessionStop, requeueInterruptedBackgroundSessionStep, requiresToolApproval, resolveBackgroundSessionReconciliation, resolveBackgroundSessionSupervisorIntervalMs, resolveFeishuDeliveryChatId, resolveFeishuEndpointCredentials, resolveLangfuseTelemetryConfig, resolveRivusAgentDefinition, resolveRivusProjectSpace, restoreAgentHistory, restoreConfiguredRivusDaemonBootstrap, restrictMemoryScopesForAudience, runRivusDaemonCli, sessionIdFromSessionKey, shouldAcceptFeishuEndpointMessage, suspendBackgroundSession, terminalDeliveryId, transitionHumanInteraction, validateProjectSkillCatalog, validateProjectSkillCommand, validateRivusDeploymentManifest };
10471
+ export { AUTOMATION_PRESENTATION_SCHEMA_VERSION, AgentContextBudgetExceeded, AgentEventHandlerFailed, AgentEventLogStoreError, AgentEventSinkFailed, AgentHarnessBusy, AgentInstanceBusy, AgentInstanceConflict, AgentLoopFailed, AgentMemoryError, AgentRunCancelled, AgentRuntimeDisposed, AutomationMandateError, BACKGROUND_SESSION_DELIVERY_JSONL_VERSION, BACKGROUND_SESSION_JSONL_VERSION, BACKGROUND_SESSION_SESSION_KEY_PREFIX, BACKGROUND_SESSION_START_TOOL_ID, BACKGROUND_SESSION_TOOL_IDS, BACKGROUND_SESSION_TOOL_PLUGIN_ID, BACKGROUND_SESSION_TOOL_VERSION, BackgroundSessionCallerDenied, BackgroundSessionDeliveryConflict, BackgroundSessionRepositoryConflict, BackgroundSessionRepositoryCorrupted, BackgroundSessionTransitionDenied, CardPresentationTransitionDenied, CompactionError, DEFAULT_BACKGROUND_SESSION_LEASE_MS, DEFAULT_BACKGROUND_SESSION_LEASE_RENEWAL_INTERVAL_MS, DEFAULT_BACKGROUND_SESSION_LIFETIME_MS, DEFAULT_BACKGROUND_SESSION_MAX_CONCURRENT_SESSIONS, DEFAULT_BACKGROUND_SESSION_MAX_CONSECUTIVE_FAILURES, DEFAULT_BACKGROUND_SESSION_RETRY_BACKOFF_MS, DEFAULT_BACKGROUND_SESSION_STEP_TIMEOUT_MS, DEFAULT_BACKGROUND_SESSION_SUPERVISOR_INTERVAL_MS, DEFAULT_CARD_STREAM_LEASE_MS, DEFAULT_CONVERSATION_PROGRESS_DISPLAY, DelegationDenied, DeliveryOutboxError, FEISHU_AGENT_CARD_ELEMENT_ID, FEISHU_AGENT_CARD_PROGRESS_ELEMENT_ID, FeishuCardPresentationNotFound, FeishuCardTargetNotFound, FeishuCardTargetRegistryStoreError, FeishuCotProtocolError, FeishuEndpointCredentialError, FeishuOpenApiError, FeishuTenantAccessTokenError, HumanInteractionRepositoryError, HumanInteractionTransitionDenied, InvalidAutomationPresentation, InvalidFeishuCardAction, InvalidFeishuMessageContent, InvalidFeishuSessionReference, InvalidFeishuTopicContext, InvalidInvocationAuthority, InvalidProjectSkillCatalog, InvalidRecoveryAction, InvalidRivusEndpointBinding, InvalidRivusPlugin, InvalidRivusProjectSpace, InvalidStableJson, InvalidToolInput, InvalidWorkspaceRoot, LangfuseTelemetryConfigError, MEMORY_SCOPES, OpenClawEnvImportError, PluginStateConflict, RIVUS_MEMORY_TOOL_ID, RIVUS_MEMORY_TOOL_PLUGIN_ID, RIVUS_MEMORY_TOOL_VERSION, RIVUS_PLUGIN_API_VERSION, RUN_PRESENTATION_SCHEMA_VERSION, RivusDaemonConfigError, RivusDeploymentAutomationReadinessError, RivusDeploymentDaemonLifecycleError, RivusDeploymentManifestError, RivusDeploymentReadinessError, RivusPluginConformanceError, RivusPluginLoadError, RivusToolInputRejected, SessionSchedulerCapacityExceeded, SessionSchedulerDisposed, ToolInvocationDenied, UnsupportedFeishuCardAction, UnsupportedFeishuMessage, WorkspaceInstructionsSourceError, acceptsCardPresentationProgress, activeCardPresentation, appendBackgroundSessionInput, assembleAgentContext, assertRivusPluginConforms, backgroundSessionToolIds, claimBackgroundSession, commitAutomationOutcome, completeBackgroundSessionStep, completeBackgroundSessionStop, composeFeishuTopicPrompt, createAgentCommandFromFeishuCardAction, createAgentCommandFromFeishuMessage, createAgentConversationMessages, createAgentDomainEventHandler, createAgentDomainEventSink, createAgentDomainEventSinkFromCallback, createAgentHarness, createAgentHarnessClient, createAgentHarnessPooledRuntime, createAgentInstanceRegistry, createAgentLoopFromCallback, createAgentLoopModelExecutionEnd, createAgentLoopModelExecutionStart, createAgentLoopPromptTransformer, createAgentLoopSkillExecutionEnd, createAgentLoopSkillExecutionStart, createAgentLoopTextDelta, createAgentLoopThinkingDelta, createAgentLoopToolExecutionEnd, createAgentLoopToolExecutionStart, createAgentLoopToolExecutionUpdate, createAgentLoopTurnStart, createAgentMemoryService, createAgentRunUpdateHandler, createAgentRuntime, createAgentRuntimePool, createAgentTranscriptMessages, createAgentTranscriptTurn, createAgentsMdInstructionsProvider, createAsyncIterableAgentLoop, createAutomationMandateStore, createAutomationPresentation, createBackgroundSession, createBackgroundSessionCard, createBackgroundSessionDeliveryStore, createBackgroundSessionHostTools, createBackgroundSessionKey, createBackgroundSessionRepository, createBackgroundSessionService, createBackgroundSessionStepSourceMessageId, createBackgroundSessionSupervisor, createBackgroundSessionToolContracts, createCoalescingFeishuPublisher, createCompactionService, createCompositeRivusDaemonTransport, createConfiguredFeishuAutomationCardSender, createConfiguredFeishuBackgroundSessionDelivery, createConfiguredFeishuCardKitPublisher, createConfiguredFeishuCardKitTargetCreator, createConfiguredFeishuCardRolloverRuntime, createConfiguredFeishuHumanInteractionPresenter, createConfiguredFeishuMessageReactionSender, createConfiguredFeishuOpenApiClient, createConfiguredFeishuTextReplySender, createConfiguredRivusDaemonBootstrap, createConfiguredRivusDeploymentDaemon, createDailyAutomationSchedule, createDefaultAgentHarness, createDefaultAgentHarnessClient, createDefaultAgentHarnessClientFromCallback, createDefaultAgentHarnessClientFromTextCallback, createDefaultAgentHarnessFromCallback, createDefaultAgentHarnessFromTextCallback, createDefaultAgentRuntime, createDefaultAgentRuntimeFromCallback, createDefaultAgentRuntimeFromTextCallback, createDelegationService, createDeliveryOutbox, createEventAgentLoop, createFakeRivusPlugin, createFeishuAgentDaemon, createFeishuAgentRunCard, createFeishuAgentRuntime, createFeishuCardActionCallbackResponse, createFeishuCardActionErrorResponse, createFeishuCardDeliveryLedger, createFeishuCardDeliveryReconciler, createFeishuCardKitOpenApiClient, createFeishuCardKitOpenApiTargetCreator, createFeishuCardKitPublisher, createFeishuCardPresentationStore, createFeishuCardRollover, createFeishuCardRolloverSupervisor, createFeishuCardTargetPreparation, createFeishuConversationId, createFeishuCotPublisher, createFeishuDeploymentEndpoint, createFeishuEventHandlers, createFeishuHumanInteractionCard, createFeishuHumanInteractionPresenter, createFeishuInboxRepository, createFeishuMessageQueue, createFeishuMessageWorker, createFeishuOpenApiClient, createFeishuPeriodicFlush, createFeishuPresentationPreparation, createFeishuSessionKey, createFeishuSessionStore, createFeishuStreamProjector, createFeishuTenantAccessTokenProvider, createFeishuTopicContextResolver, createFeishuWebSocketDaemon, createFeishuWorkerLoop, createFixedClock, createHumanInteractionEndpointRegistry, createHumanInteractionService, createHumanInteractionToolApprovalGateway, createInMemoryFeishuCardTargetRegistry, createInMemoryHumanInteractionRepository, createInvocationAuthority, createJsonFetchRequest, createJsonFileFeishuCardTargetRegistry, createJsonlAgentEventLog, createJsonlHumanInteractionRepository, createLangfuseAgentTelemetry, createLazyFeishuWebSocketEventDispatcher, createMemoryNamespace, createOpenTelemetryAgentEventSink, createOpenTelemetryAgentTelemetry, createPiAgentLoop, createPiSdkAgentLoop, createPiSessionRegistry, createPluginStateStore, createPresentedValue, createProjectMemoryPromptPreparer, createRateLimitedFeishuPublisher, createRecoveryAction, createRecoveryControl, createRivusAgentHost, createRivusDaemonProcess, createRivusDaemonShutdownController, createRivusDaemonStatusHttpServer, createRivusDaemonStatusReporter, createRivusDeploymentCliProcess, createRivusDeploymentDaemon, createRivusEnvFromOpenClawConfig, createRivusMemoryTool, createRivusMemoryToolContract, createRivusMemoryToolDescriptor, createRivusPluginCatalog, createRoutedHumanInteractionToolApprovalService, createRunPresentationProjector, createScheduledAutomation, createSequenceRunIds, createSessionScheduler, createSubagentCoordinator, createSystemClock, createTelemetryContentRedactor, createTestClock, createTextAgentLoop, createTextAgentLoopFromCallback, createToolBroker, createToolInputDigest, createToolOperationLedger, createUuidRunIds, createWorkspaceRootHandle, describeFeishuMessageIntake, evolveAgentRun, extendBackgroundSessionDefinition, failBackgroundSessionStep, formatRivusEnvFile, hasInspectableRunProgress, initialAgentRunState, intersectToolIds, isAgentToolExecutionEvent, isAssistantTextDeltaEvent, isAssistantThinkingDeltaEvent, isBackgroundSessionDue, isBackgroundSessionLeaseExpired, isBackgroundSessionState, isBackgroundSessionTerminalPhase, isBackgroundSessionToolId, isCardPresentationHandoffDue, isTerminalAgentDomainEvent, isTerminalAgentRunPhase, loadNodeRivusPluginModule, loadRivusDaemonConfig, loadRivusDeployment, loadRivusDeploymentManifest, narrowBackgroundSessionDefinition, normalizeStableJson, openJsonAutomationTickRepository, openJsonFeishuSessionStore, openJsonlAgentMemoryService, openJsonlBackgroundSessionDeliveryStore, openJsonlBackgroundSessionRepository, openJsonlFeishuCardDeliveryLedger, openJsonlFeishuInboxRepository, openJsonlRecoveryControl, openJsonlToolOperationLedger, parkBackgroundSessionForReconciliation, progressDeliveryId, readAutomationPresentation, readFeishuMessageContent, releaseBackgroundSessionLease, renewBackgroundSessionLease, replayAgentHistory, replayAgentTranscript, requestBackgroundSessionStop, requeueInterruptedBackgroundSessionStep, requiresToolApproval, resolveBackgroundSessionReconciliation, resolveBackgroundSessionSupervisorIntervalMs, resolveFeishuDeliveryChatId, resolveFeishuEndpointCredentials, resolveLangfuseTelemetryConfig, resolveRivusAgentDefinition, resolveRivusProjectSpace, restoreAgentHistory, restoreConfiguredRivusDaemonBootstrap, restrictMemoryScopesForAudience, runRivusDaemonCli, sessionIdFromSessionKey, shouldAcceptFeishuEndpointMessage, suspendBackgroundSession, terminalDeliveryId, transitionHumanInteraction, validateProjectSkillCatalog, validateProjectSkillCommand, validateRivusDeploymentManifest };